Johannes Doerfert via llvm-dev
2021-Jan-08 00:20 UTC
[llvm-dev] [RFC] Introduce the `!nocapture` metadata and "nocapture_use" operand bundle
TL;DR: A pointer stored in memory is not necessarily captured, let's add a way to express this. Phab: https://reviews.llvm.org/D93189 --- Commit Message / Rational --- Runtime functions, as well as regular functions, might require a pointer to be passed in memory even though the memory is simply a means to pass (multiple) arguments. That is, the indirection through memory is only used on the call edge and not otherwise relevant. However, such pointers are currently assumed to escape as soon as they are stored in memory even if the callee only reloads them and use them in a "non-escaping" way. Generally, storing a pointer might not cause it to escape if all "uses of the memory" it is stored to all have the "nocapture" property. To allow optimizations in the presence of pointers stored to memory we introduce two new IR extensions. `!nocapture` metadata on stores and "nocapture_use" operand bundles for call(base) instructions. The former ensures that the store can be ignored for the purpose of escape analysis. The latter indicates that a call is using a pointer value but not capturing it. This is important as the call might still read or write the pointer and since the passing of the pointer through memory is not considered "capturing" with the "nocapture" metadata, we need to otherwise indicate the potential read/write. As an example use case where we can deduce `!nocapture` metadata, consider the following code: ``` struct Payload { int *a; double *b; }; int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg); int use(double); void fn(void *v) { Payload *p = (Payload*)(v); // Load the pointers from the payload and then dereference them, // this will not capture the pointers. int *a = p->a; double *b = p->b; *a = use(*b); } void foo(int *a, double *b) { Payload p = {a, b}; pthread_create(..., &fn, &p); } ``` Given the usage of the payload struct in `fn` we can conclude neither `a` nor `b` in are captured in `foo`, however we could not express this fact "locally" before. That is, we can deduce and annotate it for the arguments `a` and `b` but only since there is no other use (later on). Similarly, if the callee would not be known, we were not able to describe the "nocapture" behavior of the API. A follow up patch will introduce `!nocapture` metadata to stores generated during OpenMP lowering. This will, among other things, fix PR48475. I generally expect us to find more APIs that could benefit from the annotation in addition to the deduction we can do if we see the callee. --- As always, feedback is welcome. Feel free to look at the phab patch as well. Thanks, Johannes -- ────────── ∽ Johannes
Jameson Nash via llvm-dev
2021-Jan-08 18:25 UTC
[llvm-dev] [RFC] Introduce the `!nocapture` metadata and "nocapture_use" operand bundle
Hey Johannes,>From my recollection of past conversations with you, in the example with`foo`, shouldn't there be a `pthread_join` call also represented in the documentation, so that the lifetime of the child call is shown to not escape the scope of the parent stack frame? For the !nocapture/nocapture_use optimizations, what is the intended semantics in the presence of interprocedural analysis, such as inlining? It sounds like the !nocapture is tied to the lifetime of the alloca, but the last use of the value loaded is always, necessarily, going to come after the last use of the alloca (to load the value), and thus seems insufficient to cover the lifetime of the uses of that stored pointer. On Thu, Jan 7, 2021 at 7:21 PM Johannes Doerfert via llvm-dev < llvm-dev at lists.llvm.org> wrote:> TL;DR: A pointer stored in memory is not necessarily captured, let's add > a way to express this. > > Phab: https://reviews.llvm.org/D93189 > > --- Commit Message / Rational --- > > Runtime functions, as well as regular functions, might require a pointer > to be passed in memory even though the memory is simply a means to pass > (multiple) arguments. That is, the indirection through memory is only > used on the call edge and not otherwise relevant. However, such pointers > are currently assumed to escape as soon as they are stored in memory > even if the callee only reloads them and use them in a "non-escaping" way. > Generally, storing a pointer might not cause it to escape if all "uses of > the memory" it is stored to all have the "nocapture" property. > > To allow optimizations in the presence of pointers stored to memory we > introduce two new IR extensions. `!nocapture` metadata on stores and > "nocapture_use" operand bundles for call(base) instructions. The former > ensures that the store can be ignored for the purpose of escape > analysis. The latter indicates that a call is using a pointer value > but not capturing it. This is important as the call might still read > or write the pointer and since the passing of the pointer through > memory is not considered "capturing" with the "nocapture" metadata, > we need to otherwise indicate the potential read/write. > > As an example use case where we can deduce `!nocapture` metadata, > consider the following code: > > ``` > struct Payload { > int *a; > double *b; > }; > > int pthread_create(pthread_t *thread, const pthread_attr_t *attr, > void *(*start_routine) (void *), void *arg); > > int use(double); > > void fn(void *v) { > Payload *p = (Payload*)(v); > // Load the pointers from the payload and then dereference them, > // this will not capture the pointers. > int *a = p->a; > double *b = p->b; > *a = use(*b); > } > > void foo(int *a, double *b) { > Payload p = {a, b}; > pthread_create(..., &fn, &p); > } > ``` > > Given the usage of the payload struct in `fn` we can conclude neither > `a` nor `b` in are captured in `foo`, however we could not express this > fact "locally" before. That is, we can deduce and annotate it for the > arguments `a` and `b` but only since there is no other use (later on). > Similarly, if the callee would not be known, we were not able to > describe the "nocapture" behavior of the API. > > A follow up patch will introduce `!nocapture` metadata to stores > generated during OpenMP lowering. This will, among other things, fix > PR48475. I generally expect us to find more APIs that could benefit from > the annotation in addition to the deduction we can do if we see the callee. > > --- > > As always, feedback is welcome. Feel free to look at the phab patch as > well. > > Thanks, > Johannes > > > -- > ────────── > ∽ Johannes > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210108/7bdd36fb/attachment.html>
Artur Pilipenko via llvm-dev
2021-Jan-11 22:25 UTC
[llvm-dev] [RFC] Introduce the `!nocapture` metadata and "nocapture_use" operand bundle
I'm a bit confused with nocapture_use. I guess you need this because without it BasicAA would assume that the pointer is not accessed by the call at all. So, as a workaround you introduce a use which implicitly reads and writes. But this might be a more general problem. For example: a = new ... store a, ptr, !nocapture a' = load ptr ; Now you have 2 pointers to the same object (a' and a ) which BasicAA considers as no aliasing. v1 = load a store 5, a' v2 = load a We would happily replace v2 with v1 even though the memory was clobbered by the store through a’. Artur> On Jan 7, 2021, at 4:20 PM, Johannes Doerfert via llvm-dev <llvm-dev at lists.llvm.org> wrote: > > TL;DR: A pointer stored in memory is not necessarily captured, let's add a way to express this. > > Phab: https://reviews.llvm.org/D93189 > > --- Commit Message / Rational --- > > Runtime functions, as well as regular functions, might require a pointer > to be passed in memory even though the memory is simply a means to pass > (multiple) arguments. That is, the indirection through memory is only > used on the call edge and not otherwise relevant. However, such pointers > are currently assumed to escape as soon as they are stored in memory > even if the callee only reloads them and use them in a "non-escaping" way. > Generally, storing a pointer might not cause it to escape if all "uses of > the memory" it is stored to all have the "nocapture" property. > > To allow optimizations in the presence of pointers stored to memory we > introduce two new IR extensions. `!nocapture` metadata on stores and > "nocapture_use" operand bundles for call(base) instructions. The former > ensures that the store can be ignored for the purpose of escape > analysis. The latter indicates that a call is using a pointer value > but not capturing it. This is important as the call might still read > or write the pointer and since the passing of the pointer through > memory is not considered "capturing" with the "nocapture" metadata, > we need to otherwise indicate the potential read/write. > > As an example use case where we can deduce `!nocapture` metadata, > consider the following code: > > ``` > struct Payload { > int *a; > double *b; > }; > > int pthread_create(pthread_t *thread, const pthread_attr_t *attr, > void *(*start_routine) (void *), void *arg); > > int use(double); > > void fn(void *v) { > Payload *p = (Payload*)(v); > // Load the pointers from the payload and then dereference them, > // this will not capture the pointers. > int *a = p->a; > double *b = p->b; > *a = use(*b); > } > > void foo(int *a, double *b) { > Payload p = {a, b}; > pthread_create(..., &fn, &p); > } > ``` > > Given the usage of the payload struct in `fn` we can conclude neither > `a` nor `b` in are captured in `foo`, however we could not express this > fact "locally" before. That is, we can deduce and annotate it for the > arguments `a` and `b` but only since there is no other use (later on). > Similarly, if the callee would not be known, we were not able to > describe the "nocapture" behavior of the API. > > A follow up patch will introduce `!nocapture` metadata to stores > generated during OpenMP lowering. This will, among other things, fix > PR48475. I generally expect us to find more APIs that could benefit from > the annotation in addition to the deduction we can do if we see the callee. > > --- > > As always, feedback is welcome. Feel free to look at the phab patch as well. > > Thanks, > Johannes > > > -- > ────────── > ∽ Johannes > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev