Background
LLVM recently introduced pointers with external state as a specific case of non-integral pointers. This IR concept is used to represent pointer values for which some kind of out-of-band runtime information is required, a canonical example being the validity tag bits for CHERI capabilities.
One important constraint of this kind of pointer is that memory accesses to regions containing them must be properly typed:
When a pointer type has external state, all roundtrips via memory must be performed as loads and stores of the correct type since stores of other types may not propagate the external data. Therefore it is not legal to convert an existing load/store (or a llvm.memcpy / llvm.memmove intrinsic) of pointer types with external state to a load/store of an integer type with the same bitwidth, as that may drop the external state.
While the IR specification mentions that llvm.memcpy / llvm.memmove require special handling, no facility is currently provided in upstream LLVM to handle external-storage pointers in these builtins, other than completely disabling transformations that could produce them. This proposal aims to address that.
Proposed Change
We propose, based on implementation experience in the CHERI and CHERIoT LLVM downstreams, creating a new enum-valued call site attribute to LLVM IR, which may only be applied to llvm.memcpy, llvm.memmove, and their respective inline variants:
copy_external_pointer_state(true): Required preservation of external pointer state. Code generation must assume that the copied region may contain pointers with external state and take appropriate steps to preserve them.copy_external_pointer_state(false): No preservation of external pointer state is required. Code generation may assume that the copied region does not contain pointers with external state, and no extra steps need to be taken to preserve them.
Backends for targets with external state pointers may reject calls to llvm.memcpy/llvm.memmove that do not carry this annotation. As such, this is in-effect a tri-state value: attribute absent, copy_external_pointer_state(true), and copy_external_pointer_state(false).
Why is the attribute-absent case useful?
At first glance it may appear that the copy_external_pointer_state(true) could be made the default behavior, simplifying the implementation to a single optimization attribute and a conservative default. Unfortunately, our experience has been that this is an area with a high likelihood of regression: most transformations are written without regard to external pointer state targets.
As these regressions would otherwise manifest as difficult to track-down performance regressions, in the CHERI / CHERIoT downstreams we find it valuable to be able to distinguish the case of an omitted attribute (due to a new oblivious transformation) from an intentional conservative attribute. The CHERI backend asserts at compile time that all calls to llvm.memcpy/llvm.memmove have the copy_external_pointer_state attribute set in one direction or the other.
Does my frontend, optimizer, or backend need to care?
We will be upstreaming changes from the CHERI / CHERIoT downstreams to set, propagate, and preserve these attributes properly across Clang and all of the relevant LLVM IR optimizations. Backends that do not have pointers with external state should require no functional changes, and upstreamed support for CHERI / CHERIoT backends will naturally incorporate support for lowering the flag appropriately for them.