Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,8 @@ Currently, only the following parameter attributes are defined:
function, returning a pointer to allocated storage disjoint from the
storage for any other object accessible to the caller.

.. _captures_attr:

``captures(...)``
This attribute restricts the ways in which the callee may capture the
pointer. This is not a valid attribute for return values. This attribute
Expand Down Expand Up @@ -7543,6 +7545,33 @@ The number of bytes known to be dereferenceable is specified by the integer
value in the metadata node. This is analogous to the ''dereferenceable_or_null''
attribute on parameters and return values.

'``captures``' Metadata
^^^^^^^^^^^^^^^^^^^^^^^

The ``!captures`` metadata can only be applied to ``store`` instructions with
a pointer-typed value operand. It restricts the capturing behavior of the store
value operand in the same way the ``captures(...)`` attribute would do on a
call. See the :ref:`pointer capture section <pointercapture>` for a detailed
discussion of capture semantics.

The ``!captures`` metadata accepts a non-empty list of strings from the same
set as the :ref:`captures attribute <captures_attr>`:
``!"address"``, ``!"address_is_null"``, ``!"provenance"`` and
``!"read_provenance"``. ``!"none"`` is not supported.

For example ``store ptr %x, ptr %y, !captures !{!"address"}`` indicates that
the copy of pointer ``%x`` stored to location ``%y`` will only be used to
inspect its integral address value, and not dereferenced. Dereferencing the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this may be somewhat target-specific as well, though, longer term, could one envision the frontend emitting, e.g., !"address" when storing a pointer to a non-integral address space (and would that imply reading the visibile bits)? Not specific to the change, but didn't find any mention either in \pointercapture on provenance when it comes to non-integral pointers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depends a bit on what kind of non-integral pointer we'd talking about here, but if it's about GC pointers, then you'd probably want the opposite, that is !captures !{!"provenance"} to indicate that accesses through the captured pointer are possible, but the address will not be inspected? Assuming the language does in fact not expose pointer addresses. I haven't thought too carefully on whether this would be safe. (Though I don't think we currently have any transforms that are able to take advantage of this.)

pointer would result in undefined behavior.

Similarly ``store ptr %x, ptr %y, !captures !{!"address", !"read_provenance"}``
indicates that while reads through the stored pointer are allowed, writes would
result in undefined behavior.

The ``!captures`` attribute makes no statement about other uses of ``%x``, or
uses of the stored-to memory location after it has been overwritten with a
different value.

.. _llvm.loop:

'``llvm.loop``'
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/FixedMetadataKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)
8 changes: 8 additions & 0 deletions llvm/include/llvm/IR/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@

namespace llvm {

enum class CaptureComponents : uint8_t;
class Module;
class ModuleSlotTracker;
class raw_ostream;
Expand Down Expand Up @@ -1480,6 +1481,13 @@ class MDNode : public Metadata {
LLVM_ABI static MDNode *getMergedCallsiteMetadata(MDNode *A, MDNode *B);
LLVM_ABI static MDNode *getMergedCalleeTypeMetadata(const MDNode *A,
const MDNode *B);

/// Convert !captures metadata to CaptureComponents. MD may be nullptr.
LLVM_ABI static CaptureComponents toCaptureComponents(const MDNode *MD);
/// Convert CaptureComponents to !captures metadata. The return value may be
/// nullptr.
LLVM_ABI static MDNode *fromCaptureComponents(LLVMContext &Ctx,
CaptureComponents CC);
};

/// Tuple of metadata.
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Analysis/CaptureTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,12 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) {
return CaptureComponents::None;
case Instruction::Store:
// Stored the pointer - conservatively assume it may be captured.
if (U.getOperandNo() == 0)
return MDNode::toCaptureComponents(
I->getMetadata(LLVMContext::MD_captures));

// Volatile stores make the address observable.
if (U.getOperandNo() == 0 || cast<StoreInst>(I)->isVolatile())
if (cast<StoreInst>(I)->isVolatile())
return CaptureComponents::All;
return CaptureComponents::None;
case Instruction::AtomicRMW: {
Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/IR/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/ModRef.h"
#include <cassert>
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -1435,6 +1436,40 @@ MDNode *MDNode::getMostGenericAlignmentOrDereferenceable(MDNode *A, MDNode *B) {
return B;
}

CaptureComponents MDNode::toCaptureComponents(const MDNode *MD) {
if (!MD)
return CaptureComponents::All;

CaptureComponents CC = CaptureComponents::None;
for (Metadata *Op : MD->operands()) {
CaptureComponents Component =
StringSwitch<CaptureComponents>(cast<MDString>(Op)->getString())
.Case("address", CaptureComponents::Address)
.Case("address_is_null", CaptureComponents::AddressIsNull)
.Case("provenance", CaptureComponents::Provenance)
.Case("read_provenance", CaptureComponents::ReadProvenance);
CC |= Component;
}
return CC;
}

MDNode *MDNode::fromCaptureComponents(LLVMContext &Ctx, CaptureComponents CC) {
assert(!capturesNothing(CC) && "Can't encode captures(none)");
if (capturesAll(CC))
return nullptr;

SmallVector<Metadata *> Components;
if (capturesAddressIsNullOnly(CC))
Components.push_back(MDString::get(Ctx, "address_is_null"));
else if (capturesAddress(CC))
Components.push_back(MDString::get(Ctx, "address"));
if (capturesReadProvenanceOnly(CC))
Components.push_back(MDString::get(Ctx, "read_provenance"));
else if (capturesFullProvenance(CC))
Components.push_back(MDString::get(Ctx, "provenance"));
return MDNode::get(Ctx, Components);
}

//===----------------------------------------------------------------------===//
// NamedMDNode implementation.
//
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
void visitAliasScopeMetadata(const MDNode *MD);
void visitAliasScopeListMetadata(const MDNode *MD);
void visitAccessGroupMetadata(const MDNode *MD);
void visitCapturesMetadata(Instruction &I, const MDNode *Captures);

template <class Ty> bool isValidMetadataArray(const MDTuple &N);
#define HANDLE_SPECIALIZED_MDNODE_LEAF(CLASS) void visit##CLASS(const CLASS &N);
Expand Down Expand Up @@ -5373,6 +5374,27 @@ void Verifier::visitAccessGroupMetadata(const MDNode *MD) {
}
}

void Verifier::visitCapturesMetadata(Instruction &I, const MDNode *Captures) {
static const char *ValidArgs[] = {"address_is_null", "address",
"read_provenance", "provenance"};

auto *SI = dyn_cast<StoreInst>(&I);
Check(SI, "!captures metadata can only be applied to store instructions", &I);
Check(SI->getValueOperand()->getType()->isPointerTy(),
"!captures metadata can only be applied to store with value operand of "
"pointer type",
&I);
Check(Captures->getNumOperands() != 0, "!captures metadata cannot be empty",
&I);

for (Metadata *Op : Captures->operands()) {
auto *Str = dyn_cast<MDString>(Op);
Check(Str, "!captures metadata must be a list of strings", &I);
Check(is_contained(ValidArgs, Str->getString()),
"invalid entry in !captures metadata", &I, Str);
}
}

/// verifyInstruction - Verify that an instruction is well formed.
///
void Verifier::visitInstruction(Instruction &I) {
Expand Down Expand Up @@ -5600,6 +5622,9 @@ void Verifier::visitInstruction(Instruction &I) {
if (MDNode *Annotation = I.getMetadata(LLVMContext::MD_annotation))
visitAnnotationMetadata(Annotation);

if (MDNode *Captures = I.getMetadata(LLVMContext::MD_captures))
visitCapturesMetadata(I, Captures);

if (MDNode *N = I.getDebugLoc().getAsMDNode()) {
CheckDI(isa<DILocation>(N), "invalid !dbg metadata attachment", &I, N);
visitMDNode(*N, AreDebugLocsAllowed::Yes);
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Transforms/Utils/Local.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3025,6 +3025,12 @@ static void combineMetadata(Instruction *K, const Instruction *J,
// Preserve !nosanitize if both K and J have it.
K->setMetadata(Kind, JMD);
break;
case LLVMContext::MD_captures:
K->setMetadata(
Kind, MDNode::fromCaptureComponents(
K->getContext(), MDNode::toCaptureComponents(JMD) |
MDNode::toCaptureComponents(KMD)));
break;
}
}
// Set !invariant.group from J if J has it. If both instructions have it
Expand Down
68 changes: 68 additions & 0 deletions llvm/test/Transforms/FunctionAttrs/nocapture.ll
Original file line number Diff line number Diff line change
Expand Up @@ -1398,5 +1398,73 @@ define void @assume_nonnull(ptr %p) {
ret void
}

define void @captures_metadata_address_is_null(ptr %x, ptr %y) {
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; FNATTRS-LABEL: define void @captures_metadata_address_is_null
; FNATTRS-SAME: (ptr captures(address_is_null) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META0:![0-9]+]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; ATTRIBUTOR-LABEL: define void @captures_metadata_address_is_null
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META0:![0-9]+]]
; ATTRIBUTOR-NEXT: ret void
;
store ptr %x, ptr %y, !captures !{!"address_is_null"}
ret void
}

define void @captures_metadata_address(ptr %x, ptr %y) {
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; FNATTRS-LABEL: define void @captures_metadata_address
; FNATTRS-SAME: (ptr captures(address) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META1:![0-9]+]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; ATTRIBUTOR-LABEL: define void @captures_metadata_address
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META1:![0-9]+]]
; ATTRIBUTOR-NEXT: ret void
;
store ptr %x, ptr %y, !captures !{!"address"}
ret void
}

define void @captures_metadata_address_read_provenance(ptr %x, ptr %y) {
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; FNATTRS-LABEL: define void @captures_metadata_address_read_provenance
; FNATTRS-SAME: (ptr captures(address, read_provenance) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META2:![0-9]+]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; ATTRIBUTOR-LABEL: define void @captures_metadata_address_read_provenance
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully Attributor can be extended to infer the whole CaptureInfo too soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is tracked at #135610 -- but I see you're already assigned on that one ^^

; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META2:![0-9]+]]
; ATTRIBUTOR-NEXT: ret void
;
store ptr %x, ptr %y, !captures !{!"address", !"read_provenance"}
ret void
}

define void @captures_metadata_provenance(ptr %x, ptr %y) {
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; FNATTRS-LABEL: define void @captures_metadata_provenance
; FNATTRS-SAME: (ptr captures(provenance) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META3:![0-9]+]]
; FNATTRS-NEXT: ret void
;
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
; ATTRIBUTOR-LABEL: define void @captures_metadata_provenance
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META3:![0-9]+]]
; ATTRIBUTOR-NEXT: ret void
;
store ptr %x, ptr %y, !captures !{!"provenance"}
ret void
}

declare ptr @llvm.launder.invariant.group.p0(ptr)
declare ptr @llvm.strip.invariant.group.p0(ptr)
Loading