Skip to content

Commit 63ca848

Browse files
authored
[IR] Introduce !captures metadata (#160913)
This introduces `!captures` metadata on stores, which looks like this: ``` store ptr %x, ptr %y, !captures !{!"address", !"read_provenance"} ``` The semantics are the same as replacing the store with a call like this: ``` call void @llvm.store(ptr captures(address, read_provenance) %x, ptr %y) ``` This metadata is intended for annotation by frontends -- it's not something we can feasibly infer at this point, as it would require analyzing uses of the pointer stored in memory. The motivating use case for this is Rust's `println!()` machinery, which involves storing a reference to the value inside a structure. This means that printing code (including conditional debugging code), can inhibit optimizations because the pointer escapes. With the new metadata we can annotate this as a read-only capture, which has less impact on optimizations.
1 parent d62776d commit 63ca848

File tree

10 files changed

+385
-1
lines changed

10 files changed

+385
-1
lines changed

llvm/docs/LangRef.rst

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,8 @@ Currently, only the following parameter attributes are defined:
14891489
function, returning a pointer to allocated storage disjoint from the
14901490
storage for any other object accessible to the caller.
14911491

1492+
.. _captures_attr:
1493+
14921494
``captures(...)``
14931495
This attribute restricts the ways in which the callee may capture the
14941496
pointer. This is not a valid attribute for return values. This attribute
@@ -7543,6 +7545,33 @@ The number of bytes known to be dereferenceable is specified by the integer
75437545
value in the metadata node. This is analogous to the ''dereferenceable_or_null''
75447546
attribute on parameters and return values.
75457547

7548+
'``captures``' Metadata
7549+
^^^^^^^^^^^^^^^^^^^^^^^
7550+
7551+
The ``!captures`` metadata can only be applied to ``store`` instructions with
7552+
a pointer-typed value operand. It restricts the capturing behavior of the store
7553+
value operand in the same way the ``captures(...)`` attribute would do on a
7554+
call. See the :ref:`pointer capture section <pointercapture>` for a detailed
7555+
discussion of capture semantics.
7556+
7557+
The ``!captures`` metadata accepts a non-empty list of strings from the same
7558+
set as the :ref:`captures attribute <captures_attr>`:
7559+
``!"address"``, ``!"address_is_null"``, ``!"provenance"`` and
7560+
``!"read_provenance"``. ``!"none"`` is not supported.
7561+
7562+
For example ``store ptr %x, ptr %y, !captures !{!"address"}`` indicates that
7563+
the copy of pointer ``%x`` stored to location ``%y`` will only be used to
7564+
inspect its integral address value, and not dereferenced. Dereferencing the
7565+
pointer would result in undefined behavior.
7566+
7567+
Similarly ``store ptr %x, ptr %y, !captures !{!"address", !"read_provenance"}``
7568+
indicates that while reads through the stored pointer are allowed, writes would
7569+
result in undefined behavior.
7570+
7571+
The ``!captures`` attribute makes no statement about other uses of ``%x``, or
7572+
uses of the stored-to memory location after it has been overwritten with a
7573+
different value.
7574+
75467575
.. _llvm.loop:
75477576

75487577
'``llvm.loop``'

llvm/include/llvm/IR/FixedMetadataKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40)
5555
LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41)
5656
LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42)
5757
LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43)
58+
LLVM_FIXED_MD_KIND(MD_captures, "captures", 44)

llvm/include/llvm/IR/Metadata.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
namespace llvm {
4343

44+
enum class CaptureComponents : uint8_t;
4445
class Module;
4546
class ModuleSlotTracker;
4647
class raw_ostream;
@@ -1480,6 +1481,13 @@ class MDNode : public Metadata {
14801481
LLVM_ABI static MDNode *getMergedCallsiteMetadata(MDNode *A, MDNode *B);
14811482
LLVM_ABI static MDNode *getMergedCalleeTypeMetadata(const MDNode *A,
14821483
const MDNode *B);
1484+
1485+
/// Convert !captures metadata to CaptureComponents. MD may be nullptr.
1486+
LLVM_ABI static CaptureComponents toCaptureComponents(const MDNode *MD);
1487+
/// Convert CaptureComponents to !captures metadata. The return value may be
1488+
/// nullptr.
1489+
LLVM_ABI static MDNode *fromCaptureComponents(LLVMContext &Ctx,
1490+
CaptureComponents CC);
14831491
};
14841492

14851493
/// Tuple of metadata.

llvm/lib/Analysis/CaptureTracking.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,8 +320,12 @@ UseCaptureInfo llvm::DetermineUseCaptureKind(const Use &U, const Value *Base) {
320320
return CaptureComponents::None;
321321
case Instruction::Store:
322322
// Stored the pointer - conservatively assume it may be captured.
323+
if (U.getOperandNo() == 0)
324+
return MDNode::toCaptureComponents(
325+
I->getMetadata(LLVMContext::MD_captures));
326+
323327
// Volatile stores make the address observable.
324-
if (U.getOperandNo() == 0 || cast<StoreInst>(I)->isVolatile())
328+
if (cast<StoreInst>(I)->isVolatile())
325329
return CaptureComponents::All;
326330
return CaptureComponents::None;
327331
case Instruction::AtomicRMW: {

llvm/lib/IR/Metadata.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
#include "llvm/Support/Casting.h"
4949
#include "llvm/Support/ErrorHandling.h"
5050
#include "llvm/Support/MathExtras.h"
51+
#include "llvm/Support/ModRef.h"
5152
#include <cassert>
5253
#include <cstddef>
5354
#include <cstdint>
@@ -1435,6 +1436,40 @@ MDNode *MDNode::getMostGenericAlignmentOrDereferenceable(MDNode *A, MDNode *B) {
14351436
return B;
14361437
}
14371438

1439+
CaptureComponents MDNode::toCaptureComponents(const MDNode *MD) {
1440+
if (!MD)
1441+
return CaptureComponents::All;
1442+
1443+
CaptureComponents CC = CaptureComponents::None;
1444+
for (Metadata *Op : MD->operands()) {
1445+
CaptureComponents Component =
1446+
StringSwitch<CaptureComponents>(cast<MDString>(Op)->getString())
1447+
.Case("address", CaptureComponents::Address)
1448+
.Case("address_is_null", CaptureComponents::AddressIsNull)
1449+
.Case("provenance", CaptureComponents::Provenance)
1450+
.Case("read_provenance", CaptureComponents::ReadProvenance);
1451+
CC |= Component;
1452+
}
1453+
return CC;
1454+
}
1455+
1456+
MDNode *MDNode::fromCaptureComponents(LLVMContext &Ctx, CaptureComponents CC) {
1457+
assert(!capturesNothing(CC) && "Can't encode captures(none)");
1458+
if (capturesAll(CC))
1459+
return nullptr;
1460+
1461+
SmallVector<Metadata *> Components;
1462+
if (capturesAddressIsNullOnly(CC))
1463+
Components.push_back(MDString::get(Ctx, "address_is_null"));
1464+
else if (capturesAddress(CC))
1465+
Components.push_back(MDString::get(Ctx, "address"));
1466+
if (capturesReadProvenanceOnly(CC))
1467+
Components.push_back(MDString::get(Ctx, "read_provenance"));
1468+
else if (capturesFullProvenance(CC))
1469+
Components.push_back(MDString::get(Ctx, "provenance"));
1470+
return MDNode::get(Ctx, Components);
1471+
}
1472+
14381473
//===----------------------------------------------------------------------===//
14391474
// NamedMDNode implementation.
14401475
//

llvm/lib/IR/Verifier.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,7 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
542542
void visitAliasScopeMetadata(const MDNode *MD);
543543
void visitAliasScopeListMetadata(const MDNode *MD);
544544
void visitAccessGroupMetadata(const MDNode *MD);
545+
void visitCapturesMetadata(Instruction &I, const MDNode *Captures);
545546

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

5377+
void Verifier::visitCapturesMetadata(Instruction &I, const MDNode *Captures) {
5378+
static const char *ValidArgs[] = {"address_is_null", "address",
5379+
"read_provenance", "provenance"};
5380+
5381+
auto *SI = dyn_cast<StoreInst>(&I);
5382+
Check(SI, "!captures metadata can only be applied to store instructions", &I);
5383+
Check(SI->getValueOperand()->getType()->isPointerTy(),
5384+
"!captures metadata can only be applied to store with value operand of "
5385+
"pointer type",
5386+
&I);
5387+
Check(Captures->getNumOperands() != 0, "!captures metadata cannot be empty",
5388+
&I);
5389+
5390+
for (Metadata *Op : Captures->operands()) {
5391+
auto *Str = dyn_cast<MDString>(Op);
5392+
Check(Str, "!captures metadata must be a list of strings", &I);
5393+
Check(is_contained(ValidArgs, Str->getString()),
5394+
"invalid entry in !captures metadata", &I, Str);
5395+
}
5396+
}
5397+
53765398
/// verifyInstruction - Verify that an instruction is well formed.
53775399
///
53785400
void Verifier::visitInstruction(Instruction &I) {
@@ -5600,6 +5622,9 @@ void Verifier::visitInstruction(Instruction &I) {
56005622
if (MDNode *Annotation = I.getMetadata(LLVMContext::MD_annotation))
56015623
visitAnnotationMetadata(Annotation);
56025624

5625+
if (MDNode *Captures = I.getMetadata(LLVMContext::MD_captures))
5626+
visitCapturesMetadata(I, Captures);
5627+
56035628
if (MDNode *N = I.getDebugLoc().getAsMDNode()) {
56045629
CheckDI(isa<DILocation>(N), "invalid !dbg metadata attachment", &I, N);
56055630
visitMDNode(*N, AreDebugLocsAllowed::Yes);

llvm/lib/Transforms/Utils/Local.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,6 +3025,12 @@ static void combineMetadata(Instruction *K, const Instruction *J,
30253025
// Preserve !nosanitize if both K and J have it.
30263026
K->setMetadata(Kind, JMD);
30273027
break;
3028+
case LLVMContext::MD_captures:
3029+
K->setMetadata(
3030+
Kind, MDNode::fromCaptureComponents(
3031+
K->getContext(), MDNode::toCaptureComponents(JMD) |
3032+
MDNode::toCaptureComponents(KMD)));
3033+
break;
30283034
}
30293035
}
30303036
// Set !invariant.group from J if J has it. If both instructions have it

llvm/test/Transforms/FunctionAttrs/nocapture.ll

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,5 +1398,73 @@ define void @assume_nonnull(ptr %p) {
13981398
ret void
13991399
}
14001400

1401+
define void @captures_metadata_address_is_null(ptr %x, ptr %y) {
1402+
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1403+
; FNATTRS-LABEL: define void @captures_metadata_address_is_null
1404+
; FNATTRS-SAME: (ptr captures(address_is_null) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
1405+
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META0:![0-9]+]]
1406+
; FNATTRS-NEXT: ret void
1407+
;
1408+
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1409+
; ATTRIBUTOR-LABEL: define void @captures_metadata_address_is_null
1410+
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
1411+
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META0:![0-9]+]]
1412+
; ATTRIBUTOR-NEXT: ret void
1413+
;
1414+
store ptr %x, ptr %y, !captures !{!"address_is_null"}
1415+
ret void
1416+
}
1417+
1418+
define void @captures_metadata_address(ptr %x, ptr %y) {
1419+
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1420+
; FNATTRS-LABEL: define void @captures_metadata_address
1421+
; FNATTRS-SAME: (ptr captures(address) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
1422+
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META1:![0-9]+]]
1423+
; FNATTRS-NEXT: ret void
1424+
;
1425+
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1426+
; ATTRIBUTOR-LABEL: define void @captures_metadata_address
1427+
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
1428+
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META1:![0-9]+]]
1429+
; ATTRIBUTOR-NEXT: ret void
1430+
;
1431+
store ptr %x, ptr %y, !captures !{!"address"}
1432+
ret void
1433+
}
1434+
1435+
define void @captures_metadata_address_read_provenance(ptr %x, ptr %y) {
1436+
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1437+
; FNATTRS-LABEL: define void @captures_metadata_address_read_provenance
1438+
; FNATTRS-SAME: (ptr captures(address, read_provenance) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
1439+
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META2:![0-9]+]]
1440+
; FNATTRS-NEXT: ret void
1441+
;
1442+
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1443+
; ATTRIBUTOR-LABEL: define void @captures_metadata_address_read_provenance
1444+
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
1445+
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META2:![0-9]+]]
1446+
; ATTRIBUTOR-NEXT: ret void
1447+
;
1448+
store ptr %x, ptr %y, !captures !{!"address", !"read_provenance"}
1449+
ret void
1450+
}
1451+
1452+
define void @captures_metadata_provenance(ptr %x, ptr %y) {
1453+
; FNATTRS: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1454+
; FNATTRS-LABEL: define void @captures_metadata_provenance
1455+
; FNATTRS-SAME: (ptr captures(provenance) [[X:%.*]], ptr writeonly captures(none) initializes((0, 8)) [[Y:%.*]]) #[[ATTR17]] {
1456+
; FNATTRS-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META3:![0-9]+]]
1457+
; FNATTRS-NEXT: ret void
1458+
;
1459+
; ATTRIBUTOR: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(argmem: write)
1460+
; ATTRIBUTOR-LABEL: define void @captures_metadata_provenance
1461+
; ATTRIBUTOR-SAME: (ptr nofree writeonly [[X:%.*]], ptr nofree nonnull writeonly captures(none) [[Y:%.*]]) #[[ATTR13]] {
1462+
; ATTRIBUTOR-NEXT: store ptr [[X]], ptr [[Y]], align 8, !captures [[META3:![0-9]+]]
1463+
; ATTRIBUTOR-NEXT: ret void
1464+
;
1465+
store ptr %x, ptr %y, !captures !{!"provenance"}
1466+
ret void
1467+
}
1468+
14011469
declare ptr @llvm.launder.invariant.group.p0(ptr)
14021470
declare ptr @llvm.strip.invariant.group.p0(ptr)

0 commit comments

Comments
 (0)