[llvm] [LangRef] Clarify pointer capture spec (PR #194647)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 29 03:36:58 PDT 2026
================
@@ -3683,9 +3683,53 @@ memory before the call, the call may capture two components of the pointer:
rules <pointeraliasing>`. We further distinguish whether only read accesses
are allowed, or both reads and writes.
+These two cases are discussed in more detail in the following.
+
+Provenance capture
+^^^^^^^^^^^^^^^^^^
+
+If an argument does not capture the provenance the pointer, accesses that are
+based on the argument and are performed after the function returns (or unwinds)
+cause undefined behavior. In the case where only read provenance is captured,
+only stores cause undefined behavior.
+
+More formally, an argument that does not capture provenance will return a new
+pointer with the same address and a derived provenance, where the derived
+provenance initially has the same permissions as the original, but all access
+permissions are removed once the function returns (or unwinds). Or in the case
+of only capturing read provenance, the permissions are changed to only allow
+read accesses.
+
+This means that the following code is well-defined in isolation:
+
+.. code-block:: llvm
+
+ define void @f(ptr captures(address) %a, ptr %b) {
+ store ptr %a, ptr %b
+ ret void
+ }
+
+Even though the pointer is stored in another location that persists past the
+return of the function, this does not cause undefined behavior in itself. It
+depends on whether/how the pointer will be used in the future.
+
+.. code-block:: llvm
+
+ call void @f(ptr captures(address) %a, ptr %b)
+ %a2 = load ptr, ptr %b ; This is still well-defined
+ load i64, ptr %a2 ; This causes undefined behavior
+
+In this example, the persisted pointer is accessed after the return of the
+function, which causes undefined behavior.
+
+Address capture
+^^^^^^^^^^^^^^^
+
+The address of the pointer is considered to be captured if the function can
+exhibit different behavior based on the address of the pointer.
----------------
nikic wrote:
After thinking about this a bit more, I think my ideal here would be that even the observable behavior of the function may change, and the attribute is just a statement that such changes in behavior are allowed -- in a similar way that you might see comparisons between lifetime-disjoint stack allocations evaluate to either true or false, and both behaviors are allowed.
One of the main things that address-capture controls are things like call slot optimization, and I'd like `captures(none)` to be well-defined even in cases where the call can in fact observe whether or not an intermediate allocation has been elided, but we want to convey that both options are legal.
But I'm not sure how to best specify that.
https://github.com/llvm/llvm-project/pull/194647
More information about the llvm-commits
mailing list