nocapture: affects aliasing or also numerical value?

Hi,

I have a question about the meaning of the nocapture attribute:

In Rust, safe code can use pointers as, say, HashMap keys (by
reference equality) without regard to their lifetimes - of course, a
dead pointer might be reference-equal to other pointers, but users
should not face any other trouble with that (of course, dead pointers
can't be dereferenced).

Rust also has "anonymous &T" arguments, which are pointers that can't
be dereferenced after the function returns. However, because pointers
can be used as HashMap keys without regard to their lifetimes, their
identity can be inserted into an HashMap.

Now, we want to transfer this information to LLVM, which is quite
similar to the `nocapture` attribute.

However, according to the documentation for `nocapture`, "This
indicates that the callee does not make any copies of the pointer that
outlive the callee itself.".

This might mean that the pointer becomes some sort of indeterminate
value after the function returns - aka using it as a HashMap key would
be trouble. Or it could just mean that the pointer can't be
dereferenced after the function returns, which is the Rust semantics.

Does `nocapture` have Rust-compatible semantics, and if not, is there
some other way to emulate these semantics?

Thanks,
- Ariel

Hi,

I have a question about the meaning of the nocapture attribute:

In Rust, safe code can use pointers as, say, HashMap keys (by
reference equality) without regard to their lifetimes - of course, a
dead pointer might be reference-equal to other pointers, but users
should not face any other trouble with that (of course, dead pointers
can't be dereferenced).

Rust also has "anonymous &T" arguments, which are pointers that can't
be dereferenced after the function returns.

Do you mean "dereferenced in the called function"?
or dereferenced in the callee?

However, because pointers
can be used as HashMap keys without regard to their lifetimes, their
identity can be inserted into an HashMap.

Now, we want to transfer this information to LLVM, which is quite
similar to the `nocapture` attribute.

However, according to the documentation for `nocapture`, "This
indicates that the callee does not make any copies of the pointer that
outlive the callee itself.".

This might mean that the pointer becomes some sort of indeterminate
value after the function returns - aka using it as a HashMap key would
be trouble.

No, because that would not be a copy becoming indeterminate, since this is
SSA.
Remember this is SSA, so the original value passed to the function can only
have been assigned once.

Or it could just mean that the pointer can't be
dereferenced after the function returns, which is the Rust semantics.

No, it doesn't mean that either.
The semantics are precisely:
"the *called* function does not store this pointer in a place that, after
function return, it may be accessed by the called function or any other
function that the called function used"

To be concrete, if this is the entire function:
declare func(nocapture i8*);

define bar() {
a_1 = pointer to foo
call func(a_1)
*a_1 = 5
}
is legal.

Additionally

define bar() {
a_1 = pointer to foo
call func(a_1)
call func2()
}
func2 cannot access a_1

Note:

define bar() {
a_1 = pointer to foo
call func(a_1)
a_1 = different thing
}

is impossible, because a_1 is an ssa value

it would be:
define bar() {
a_1 = pointer to foo
call func(a_1)
a_2 = different thing
}

and a_2 is a different pointer than a_1.

Note, if our first example was:
declare func(nocapture i8*);

define bar(i8* a_1 ) {
call func(a_1)
}

bar would also be nocapture of a_1, and could be marked as such.

Does `nocapture` have Rust-compatible semantics, and if not, is there
some other way to emulate these semantics?

If your semantic is that, after the call to func, a_1 is actually *dead*,
the way to emulate that semantic would be to use llvm.lifetime.end on it.