diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index b52eb395d5e82..678762a756d4c 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -2658,7 +2658,8 @@ fn -> other_fn -> other_fn ; fn is norecurse how much of the frame is cleared: - `"used"` clears every stack slot the function used. - - `"sensitive"` clears the slots that sensitivity metadata identifies, + - `"sensitive"` clears the slots that + {ref}`sensitivity metadata ` identifies, together with every slot whose contents cannot be traced back to a source-level object. The second half is not optional: spill slots, the callee-save area, and alignment padding can all hold copies of data that @@ -8012,6 +8013,57 @@ call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) %d, !"nvvm.l2_prefetch_size", !"128B" } ``` +(md_sensitive)= + +#### '`sensitive`' Metadata + +`sensitive` metadata may be attached to an `alloca` to record that the object +it allocates holds data that should not be left readable in the stack frame +once the function is done with it. It identifies the objects that the +`"sensitive"` mode of the `"zeroize-stack"` function attribute clears. + +This metadata is only used as a flag, so the associated node must be empty. Its +presence on the `alloca` is the entire signal; the contents of the node are +reserved for future use. + +```llvm +%round_keys = alloca [176 x i8], align 16, !sensitive !0 + +... +!0 = !{} +``` + +The metadata is a hint that lets a function clear less of its frame than it +would otherwise have to. It is not itself part of any guarantee, and no +guarantee is conditioned on it being present, accurate, or complete. The +obligation to clear a frame comes from the `"zeroize-stack"` attribute alone, +and the `"sensitive"` mode of that attribute clears every frame object whose +contents cannot be traced back to a source-level object — spill slots, the +callee-save area, alignment padding — whether or not anything is marked. + +Transforms must respect the one-directional rule this sets up: + +- Dropping the metadata is always permitted. A transform is never required to + preserve or propagate it in order to be correct, and no correctness argument + may rest on it having survived. Where the marked set has stopped describing + the frame, because a marked object was split, merged, replaced, or promoted + and the metadata did not follow, the response is to clear more, falling back + at the limit to clearing every stack slot the function used. It is never to + clear less. +- Attaching the metadata to an object that was not marked before is likewise + permitted, and can only widen what is cleared. +- The absence of the metadata on a frame object is not a statement that the + object is insensitive. A transform may not conclude from an object being + unmarked that its contents need not be cleared, and may not shrink the set of + objects a function clears on the strength of what is or is not marked. + Clearing only the marked objects together with the objects of unknown + provenance is the request that the `"sensitive"` mode expresses; it is not a + licence for a transform to narrow that set further. + +Whether an individual transform propagates this metadata across the objects it +creates is therefore a question of precision rather than of correctness, and is +specified separately. + (llvm.loop)= #### '`llvm.loop`' diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index 350adfc27bef4..f415e8bf70fe6 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -67,3 +67,4 @@ LLVM_FIXED_MD_KIND(MD_mem_cache_hint, "mem.cache_hint", 52) LLVM_FIXED_MD_KIND(MD_block_uniformity_profile, "block.uniformity.profile", 53) LLVM_FIXED_MD_KIND(MD_callgraph, "callgraph", 54) LLVM_FIXED_MD_KIND(MD_metadata_section_kind, "metadata_section_kind", 55) +LLVM_FIXED_MD_KIND(MD_sensitive, "sensitive", 56) diff --git a/llvm/test/Assembler/sensitive-metadata.ll b/llvm/test/Assembler/sensitive-metadata.ll new file mode 100644 index 0000000000000..70af29adfdc8a --- /dev/null +++ b/llvm/test/Assembler/sensitive-metadata.ll @@ -0,0 +1,31 @@ +; Textual and bitcode round-trips of !sensitive on an alloca. +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +define void @marked() { +; CHECK-LABEL: define void @marked() { +; CHECK-NEXT: [[ROUND_KEYS:%.*]] = alloca [176 x i8], align 16, !sensitive [[META0:![0-9]+]] +; CHECK-NEXT: ret void +; + %round_keys = alloca [176 x i8], align 16, !sensitive !0 + ret void +} + +; The metadata is a flag, so every marked object shares the one empty node. +define void @marked_twice() { +; CHECK-LABEL: define void @marked_twice() { +; CHECK-NEXT: [[KEY:%.*]] = alloca i64, align 8, !sensitive [[META0]] +; CHECK-NEXT: [[NONCE:%.*]] = alloca i64, align 8, !sensitive [[META0]] +; CHECK-NEXT: [[SCRATCH:%.*]] = alloca i64, align 8 +; CHECK-NEXT: ret void +; + %key = alloca i64, align 8, !sensitive !0 + %nonce = alloca i64, align 8, !sensitive !0 + %scratch = alloca i64, align 8 + ret void +} + +!0 = !{} +;. +; CHECK: [[META0]] = !{} +;.