László Radnai via llvm-dev
2020-Aug-13 11:23 UTC
[llvm-dev] Deterministic function return attribute
Hi! I'm interested in what attributes in LLVM mean, specifically how to say that the result is always the same for the given input parameters. The main thing would be to merge two calls with the same parameters when the function is declared but not defined. (just like two stores). I'll call this property mergability. %1 := call @test(%0) %2 := call @test(%0) and the optimization would be something like (%2).replaceUsesWith((%1)). I think it's related to speculatable & readnone in LLVM, (if I understood well, it's the same as GCC's __attribute__((pure)), but I'm not sure whether there are edgecases where the mergability is not equivalent. I've seen somewhere that maybe malloc has these attributes, but it surely cannot be merged. This is because there is memory read/written that cannot be seen by LLVM (which is accepted by readnone). This would be a counter-example. So my question is: Is mergability equivalent to the readnone+speculatable attribute? If not, is there some attribute that should help? And also, does malloc have (or rather, could malloc have) these attributes? Thanks, László
Johannes Doerfert via llvm-dev
2020-Aug-13 16:15 UTC
[llvm-dev] Deterministic function return attribute
Hi László, On 8/13/20 6:23 AM, László Radnai via llvm-dev wrote: > Hi! > > I'm interested in what attributes in LLVM mean, specifically how to > say that the result is always the same for the given input parameters. > > The main thing would be to merge two calls with the same parameters > when the function is declared but not defined. (just like two stores). > I'll call this property mergability. > > %1 := call @test(%0) > %2 := call @test(%0) > > and the optimization would be something like (%2).replaceUsesWith((%1)). > > I think it's related to speculatable & readnone in LLVM, (if I > understood well, it's the same as GCC's __attribute__((pure)), but I'm > not sure whether there are edgecases where the mergability is not > equivalent. > > I've seen somewhere that maybe malloc has these attributes, but it > surely cannot be merged. This is because there is memory read/written > that cannot be seen by LLVM (which is accepted by readnone). This > would be a counter-example. > > So my question is: > Is mergability equivalent to the readnone+speculatable attribute? > If not, is there some attribute that should help? > > And also, does malloc have (or rather, could malloc have) these attributes? Some thoughts, you let me know if this is helpful: same input -> same output; this is basically an implication of `readnone`, or `readonly` without intermediate modifications. It is already happening as you would expect it to, I think in inst combine but I didn't check: https://godbolt.org/z/hnY71v `speculatable` means it is `readnone` and doesn't cause UB. As a consequence it is allowed to eagerly execute the function. `readnone` (aka `__attribute__((const))`) is not sufficient because of things like this: `int pure_div(int a, int b) { return a / b; }` While there is certainly no memory access or anything else that would make it not only depend on the arguments, you cannot hoist a call to `pure_div` out of a conditional like `if (b != 0) r = pure_div(a, b);`. `readnone` does *not* allow accesses to memory that "cannot be seen by LLVM". We have `inaccessiblememonly` for that. Please follow up with questions :) ~ Johannes > Thanks, László > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
László Radnai via llvm-dev
2020-Aug-13 22:21 UTC
[llvm-dev] Fwd: Deterministic function return attribute
(Sorry I clicked reply instead of reply to all) I'm fighting with my email client, I hope the quoted text contains what I want it to contain. ---------- Forwarded message ---------- From: László Radnai <radlaci97 at gmail.com> Date: Fri, 14 Aug 2020 00:11:35 +0200 Subject: Re: [llvm-dev] Deterministic function return attribute To: Johannes Doerfert <johannesdoerfert at gmail.com> Johannes, Thanks for clarifying. Your answer was really useful for me! I think I've mixed these up (and that explains why I haven't been able to find some things on the docs I've remembered...) Though one question interests me: what attributes can be given to a lazy-init singleton or memoized function (which do access memory, but does not change output and has no visible side-effects)? Examples: ``` static Type* data; Type* getXInstance(){ if (data==nullptr) data = new Type(); return data; } ``` or with `static Type* data` inside the function (I don't know whether LLVM makes a distinction, haven't had the time to check.) For the memoized function, an example: static int memo[100]; int fib(int n){ llvm.assume(0 <= n && n<=100); if (memo[n] != 0) { return memo[n]; } if (n == 0) { return memo[n] = 1; } return memo[n] = fib(n-1) + fib(n-2); } My goal would be the same: instruction combining. There are memory accesses, but considering(assuming) only this function accessey the memory and the output is deterministic (for both cases). Whether the optimization kicks in (as I will check later), the question is either (i) what attributes can communicate this property, or (ii) how can I achieve such optimizations? Is there some previous work on this? Is this available in gcc? I'm not even sure what would be needed for this property to hold, but memory access is too strong property. If it only accesses function-internal (does not alias with anything from any other function) memory...? Well, it could store state, not cache results... So it does not guarantee deterministic outputs... My motive (maybe clarifying this helps a bit): I'm interested in the internal workings of a compiler and how smart can a compiler can be, for fun. Secondarily, tocommunicate with the optimizer when writing in C. Thanks, László On 8/13/20, Johannes Doerfert <johannesdoerfert at gmail.com> wrote:> > Hi László, > > On 8/13/20 6:23 AM, László Radnai via llvm-dev wrote: > > Hi! > > > > I'm interested in what attributes in LLVM mean, specifically how to > > say that the result is always the same for the given input parameters. > > > > The main thing would be to merge two calls with the same parameters > > when the function is declared but not defined. (just like two stores). > > I'll call this property mergability. > > > > %1 := call @test(%0) > > %2 := call @test(%0) > > > > and the optimization would be something like (%2).replaceUsesWith((%1)). > > > > I think it's related to speculatable & readnone in LLVM, (if I > > understood well, it's the same as GCC's __attribute__((pure)), but I'm > > not sure whether there are edgecases where the mergability is not > > equivalent. > > > > I've seen somewhere that maybe malloc has these attributes, but it > > surely cannot be merged. This is because there is memory read/written > > that cannot be seen by LLVM (which is accepted by readnone). This > > would be a counter-example. > > > > So my question is: > > Is mergability equivalent to the readnone+speculatable attribute? > > If not, is there some attribute that should help? > > > > And also, does malloc have (or rather, could malloc have) these > attributes? > > Some thoughts, you let me know if this is helpful: > > same input -> same output; this is basically an implication of > `readnone`, or `readonly` without intermediate modifications. > It is already happening as you would expect it to, I think in inst > combine but I didn't check: https://godbolt.org/z/hnY71v > > `speculatable` means it is `readnone` and doesn't cause UB. As a > consequence it is allowed to eagerly execute the function. `readnone` > (aka `__attribute__((const))`) is not sufficient because of things like > this: `int pure_div(int a, int b) { return a / b; }` > While there is certainly no memory access or anything else that would > make it not only depend on the arguments, you cannot hoist a call to > `pure_div` out of a conditional like `if (b != 0) r = pure_div(a, b);`. > > `readnone` does *not* allow accesses to memory that "cannot be seen by > LLVM". We have `inaccessiblememonly` for that. > > Please follow up with questions :) > > ~ Johannes > > > > Thanks, László > > _______________________________________________ > > LLVM Developers mailing list > > llvm-dev at lists.llvm.org > > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev > >
Johannes Doerfert via llvm-dev
2020-Aug-14 01:41 UTC
[llvm-dev] Fwd: Deterministic function return attribute
Hi László, On 8/13/20 5:21 PM, László Radnai via llvm-dev wrote: > (Sorry I clicked reply instead of reply to all) > I'm fighting with my email client, I hope the quoted text contains > what I want it to contain. > > ---------- Forwarded message ---------- > From: László Radnai <radlaci97 at gmail.com> > Date: Fri, 14 Aug 2020 00:11:35 +0200 > Subject: Re: [llvm-dev] Deterministic function return attribute > To: Johannes Doerfert <johannesdoerfert at gmail.com> > > Johannes, > > Thanks for clarifying. Your answer was really useful for me! Glad to help, let's try to figure this one out too ;) > I think I've mixed these up (and that explains why I haven't been able > to find some things on the docs I've remembered...) Yeah, the docs,.. feel free to propose patches to improve them and put me as a reviewer ;) > Though one question interests me: what attributes can be given to a > lazy-init singleton or memoized function (which do access memory, but > does not change output and has no visible side-effects)? Short answer: None (right now). Long answer: There are optimizations that exploit such behavior (or at least similar behavior) already. IPSCCP and there is an Attributor patch that I lost track of a while ago. However, that does not mean we couldn't create an attribute for this. I'm confident there is a reasonable way to define it, the question is how we would use it. I guess we can teach inst combine and such passes about it, but then the question is: is it worth it? There are two cases two consider, and so far I'm unsure if either justifies this: 1) The function is a definition, so we analyze it, deduce the attribute, and passes simplify calls to it. So far, so good. Though, in this scenario it is likely that we inline the function. The attribute would be gone, but we can still optimize subsequent "bodies" as we basically see the assignment and the subsequent load + compare. 2) The function is a declaration, so no deduction is possible and we require the attribute to be provided in the input. Usefulness is given in this case but it is unclear to me if we can expect people to annotate their code. That said, I'm still hoping this will soon become a viable alternative to LTO: https://www.youtube.com/watch?v=elmio6AoyK0&list=PL_R5A0lGi1AAxLTNN21BA0w8CA_xDR0F8&index=14&t=6s > Examples: > > ``` > static Type* data; > > Type* getXInstance(){ > if (data==nullptr) > data = new Type(); > return data; > } > ``` > > or with `static Type* data` inside the function (I don't know whether > LLVM makes a distinction, haven't had the time to check.) > > For the memoized function, an example: > > static int memo[100]; > > int fib(int n){ > llvm.assume(0 <= n && n<=100); > if (memo[n] != 0) { > return memo[n]; > } > if (n == 0) { > return memo[n] = 1; > } > return memo[n] = fib(n-1) + fib(n-2); > } > > My goal would be the same: instruction combining. There are memory > accesses, but considering(assuming) only this function accessey the > memory and the output is deterministic (for both cases). > > Whether the optimization kicks in (as I will check later), the > question is either (i) what attributes can communicate this property, > or (ii) how can I achieve such optimizations? Is there some previous > work on this? Is this available in gcc? I don't know much about gcc, sorry ;) > I'm not even sure what would be needed for this property to hold, but > memory access is too strong property. > > If it only accesses function-internal (does not alias with anything > from any other function) memory...? Well, it could store state, not > cache results... So it does not guarantee deterministic outputs... First thing is to define what uses cases should be addressed. The above are two but we need to be precise about the surrounding code. Let's assume no other uses of the memory exist, then we derive nice properties for the functions. Though, it might be worth to consider defining properties for such kind of memory instead ;) I have ideas but I'll think about this first and let you know. Feel free to brainstorm ideas (on the list or just via email to me if you prefer). > My motive (maybe clarifying this helps a bit): I'm interested in the > internal workings of a compiler and how smart can a compiler can be, > for fun. Secondarily, tocommunicate with the optimizer when writing in > C. I would also like to improve the communication/interface, believe me... In addition to talk above, I'd also recommend to look at http://lists.llvm.org/pipermail/llvm-dev/2019-December/137632.html and the `assume` directive we added to OpenMP 5.1: https://www.openmp.org/wp-content/uploads/openmp-TR8.pdf ~ Johannes > Thanks, László > > On 8/13/20, Johannes Doerfert <johannesdoerfert at gmail.com> wrote: >> >> Hi László, >> >> On 8/13/20 6:23 AM, László Radnai via llvm-dev wrote: >> > Hi! >> > >> > I'm interested in what attributes in LLVM mean, specifically how to >> > say that the result is always the same for the given input parameters. >> > >> > The main thing would be to merge two calls with the same parameters >> > when the function is declared but not defined. (just like two stores). >> > I'll call this property mergability. >> > >> > %1 := call @test(%0) >> > %2 := call @test(%0) >> > >> > and the optimization would be something like (%2).replaceUsesWith((%1)). >> > >> > I think it's related to speculatable & readnone in LLVM, (if I >> > understood well, it's the same as GCC's __attribute__((pure)), but I'm >> > not sure whether there are edgecases where the mergability is not >> > equivalent. >> > >> > I've seen somewhere that maybe malloc has these attributes, but it >> > surely cannot be merged. This is because there is memory read/written >> > that cannot be seen by LLVM (which is accepted by readnone). This >> > would be a counter-example. >> > >> > So my question is: >> > Is mergability equivalent to the readnone+speculatable attribute? >> > If not, is there some attribute that should help? >> > >> > And also, does malloc have (or rather, could malloc have) these >> attributes? >> >> Some thoughts, you let me know if this is helpful: >> >> same input -> same output; this is basically an implication of >> `readnone`, or `readonly` without intermediate modifications. >> It is already happening as you would expect it to, I think in inst >> combine but I didn't check: https://godbolt.org/z/hnY71v >> >> `speculatable` means it is `readnone` and doesn't cause UB. As a >> consequence it is allowed to eagerly execute the function. `readnone` >> (aka `__attribute__((const))`) is not sufficient because of things like >> this: `int pure_div(int a, int b) { return a / b; }` >> While there is certainly no memory access or anything else that would >> make it not only depend on the arguments, you cannot hoist a call to >> `pure_div` out of a conditional like `if (b != 0) r = pure_div(a, b);`. >> >> `readnone` does *not* allow accesses to memory that "cannot be seen by >> LLVM". We have `inaccessiblememonly` for that. >> >> Please follow up with questions :) >> >> ~ Johannes >> >> >> > Thanks, László >> > _______________________________________________ >> > LLVM Developers mailing list >> > llvm-dev at lists.llvm.org >> > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >> >> > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev