[llvm-dev] [RFC] Introduce the `!nocapture` metadata and "nocapture_use" operand bundle

Johannes Doerfert via llvm-dev llvm-dev at lists.llvm.org
Thu Jan 7 16:20:44 PST 2021

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.


∽ Johannes

More information about the llvm-dev mailing list