diff options
author | Antonio Frighetto <me@antoniofrighetto.com> | 2025-07-02 09:23:22 +0200 |
---|---|---|
committer | Antonio Frighetto <me@antoniofrighetto.com> | 2025-07-02 09:29:36 +0200 |
commit | f1cc0b607b03548028db3ca57bb057b2599b1711 (patch) | |
tree | bb9c8f0c81f52d3cead1c23ba0a31fde611d697f /llvm | |
parent | d5608d6751315f2e34a0445ca7f5be44a1520463 (diff) | |
download | llvm-f1cc0b607b03548028db3ca57bb057b2599b1711.zip llvm-f1cc0b607b03548028db3ca57bb057b2599b1711.tar.gz llvm-f1cc0b607b03548028db3ca57bb057b2599b1711.tar.bz2 |
[IR] Introduce `dead_on_return` attribute
Add `dead_on_return` attribute, which is meant to be taken advantage
by the frontend, and states that the memory pointed to by the argument
is dead upon function return. As with `byval`, it is supposed to be
used for passing aggregates by value. The difference lies in the ABI:
`byval` implies that the pointer is explicitly passed as argument to
the callee (during codegen the copy is emitted as per byval contract),
whereas a `dead_on_return`-marked argument implies that the copy
already exists in the IR, is located at a specific stack offset within
the caller, and this memory will not be read further by the caller upon
callee return – or otherwise poison, if read before being written.
RFC: https://discourse.llvm.org/t/rfc-add-dead-on-return-attribute/86871.
Diffstat (limited to 'llvm')
-rw-r--r-- | llvm/docs/LangRef.rst | 17 | ||||
-rw-r--r-- | llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 | ||||
-rw-r--r-- | llvm/include/llvm/IR/Argument.h | 3 | ||||
-rw-r--r-- | llvm/include/llvm/IR/Attributes.td | 3 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/IR/Attributes.cpp | 3 | ||||
-rw-r--r-- | llvm/lib/IR/Function.cpp | 6 | ||||
-rw-r--r-- | llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp | 6 | ||||
-rw-r--r-- | llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 | ||||
-rw-r--r-- | llvm/test/Bitcode/attributes.ll | 5 | ||||
-rw-r--r-- | llvm/test/Transforms/DeadStoreElimination/simple.ll | 35 | ||||
-rw-r--r-- | llvm/test/Verifier/dead-on-return.ll | 7 |
13 files changed, 86 insertions, 5 deletions
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index d77c659..2a45f4f 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1741,6 +1741,23 @@ Currently, only the following parameter attributes are defined: This attribute cannot be applied to return values. +``dead_on_return`` + This attribute indicates that the memory pointed to by the argument is dead + upon function return, both upon normal return and if the calls unwinds, meaning + that the caller will not depend on its contents. Stores that would be observable + either on the return path or on the unwind path may be elided. + + Specifically, the behavior is as-if any memory written through the pointer + during the execution of the function is overwritten with a poison value + upon function return. The caller may access the memory, but any load + not preceded by a store will return poison. + + This attribute does not imply aliasing properties. For pointer arguments that + do not alias other memory locations, ``noalias`` attribute may be used in + conjunction. Conversely, this attribute always implies ``dead_on_unwind``. + + This attribute cannot be applied to return values. + ``range(<ty> <a>, <b>)`` This attribute expresses the possible range of the parameter or return value. If the value is not in the specified range, it is converted to poison. diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index b362a88..dc78eb4 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -798,6 +798,7 @@ enum AttributeKindCodes { ATTR_KIND_NO_DIVERGENCE_SOURCE = 100, ATTR_KIND_SANITIZE_TYPE = 101, ATTR_KIND_CAPTURES = 102, + ATTR_KIND_DEAD_ON_RETURN = 103, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Argument.h b/llvm/include/llvm/IR/Argument.h index 60854b17..b9a73b3 100644 --- a/llvm/include/llvm/IR/Argument.h +++ b/llvm/include/llvm/IR/Argument.h @@ -78,6 +78,9 @@ public: /// Return true if this argument has the byval attribute. LLVM_ABI bool hasByValAttr() const; + /// Return true if this argument has the dead_on_return attribute. + LLVM_ABI bool hasDeadOnReturnAttr() const; + /// Return true if this argument has the byref attribute. LLVM_ABI bool hasByRefAttr() const; diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index 0bcd15e..1128539 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -198,6 +198,9 @@ def NoFree : EnumAttr<"nofree", IntersectAnd, [FnAttr, ParamAttr]>; /// Argument is dead if the call unwinds. def DeadOnUnwind : EnumAttr<"dead_on_unwind", IntersectAnd, [ParamAttr]>; +/// Argument is dead upon function return. +def DeadOnReturn : EnumAttr<"dead_on_return", IntersectAnd, [ParamAttr]>; + /// Disable implicit floating point insts. def NoImplicitFloat : EnumAttr<"noimplicitfloat", IntersectPreserve, [FnAttr]>; diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index c6549e7..de7bf9b 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2244,6 +2244,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::NoExt; case bitc::ATTR_KIND_CAPTURES: return Attribute::Captures; + case bitc::ATTR_KIND_DEAD_ON_RETURN: + return Attribute::DeadOnReturn; } } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 2a2dd08..da00eec 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -938,6 +938,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_NO_EXT; case Attribute::Captures: return bitc::ATTR_KIND_CAPTURES; + case Attribute::DeadOnReturn: + return bitc::ATTR_KIND_DEAD_ON_RETURN; case Attribute::EndAttrKinds: llvm_unreachable("Can not encode end-attribute kinds marker."); case Attribute::None: diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index bfb32ff..d1fbcb9 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -2424,7 +2424,8 @@ AttributeMask AttributeFuncs::typeIncompatible(Type *Ty, AttributeSet AS, .addAttribute(Attribute::Writable) .addAttribute(Attribute::DeadOnUnwind) .addAttribute(Attribute::Initializes) - .addAttribute(Attribute::Captures); + .addAttribute(Attribute::Captures) + .addAttribute(Attribute::DeadOnReturn); if (ASK & ASK_UNSAFE_TO_DROP) Incompatible.addAttribute(Attribute::Nest) .addAttribute(Attribute::SwiftError) diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index 3e7fcbb..7a03663 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -130,6 +130,12 @@ bool Argument::hasByValAttr() const { return hasAttribute(Attribute::ByVal); } +bool Argument::hasDeadOnReturnAttr() const { + if (!getType()->isPointerTy()) + return false; + return hasAttribute(Attribute::DeadOnReturn); +} + bool Argument::hasByRefAttr() const { if (!getType()->isPointerTy()) return false; diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index 4a2eb92..85dd9a1 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -1017,10 +1017,10 @@ struct DSEState { } } - // Treat byval or inalloca arguments the same as Allocas, stores to them are - // dead at the end of the function. + // Treat byval, inalloca or dead on return arguments the same as Allocas, + // stores to them are dead at the end of the function. for (Argument &AI : F.args()) - if (AI.hasPassPointeeByValueCopyAttr()) + if (AI.hasPassPointeeByValueCopyAttr() || AI.hasDeadOnReturnAttr()) InvisibleToCallerAfterRet.insert({&AI, true}); // Collect whether there is any irreducible control flow in the function. diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index 242cf6d..e28a1b4 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -1020,6 +1020,7 @@ Function *CodeExtractor::constructFunctionDeclaration( case Attribute::EndAttrKinds: case Attribute::EmptyKey: case Attribute::TombstoneKey: + case Attribute::DeadOnReturn: llvm_unreachable("Not a function attribute"); } diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index 7dd86a8..8c1a763 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -567,6 +567,11 @@ define void @captures(ptr captures(address) %p) { ret void } +; CHECK: define void @dead_on_return(ptr dead_on_return %p) +define void @dead_on_return(ptr dead_on_return %p) { + ret void +} + ; CHECK: attributes #0 = { noreturn } ; CHECK: attributes #1 = { nounwind } ; CHECK: attributes #2 = { memory(none) } diff --git a/llvm/test/Transforms/DeadStoreElimination/simple.ll b/llvm/test/Transforms/DeadStoreElimination/simple.ll index f8e594b..6c04e15 100644 --- a/llvm/test/Transforms/DeadStoreElimination/simple.ll +++ b/llvm/test/Transforms/DeadStoreElimination/simple.ll @@ -425,7 +425,7 @@ define ptr @test25(ptr %p) nounwind { ; CHECK-NEXT: [[P_4:%.*]] = getelementptr i8, ptr [[P:%.*]], i64 4 ; CHECK-NEXT: [[TMP:%.*]] = load i8, ptr [[P_4]], align 1 ; CHECK-NEXT: store i8 0, ptr [[P_4]], align 1 -; CHECK-NEXT: [[Q:%.*]] = call ptr @strdup(ptr [[P]]) #[[ATTR13:[0-9]+]] +; CHECK-NEXT: [[Q:%.*]] = call ptr @strdup(ptr [[P]]) #[[ATTR14:[0-9]+]] ; CHECK-NEXT: store i8 [[TMP]], ptr [[P_4]], align 1 ; CHECK-NEXT: ret ptr [[Q]] ; @@ -855,3 +855,36 @@ bb: store ptr null, ptr null, align 8 ret void } + +define void @test_dead_on_return(ptr dead_on_return %p) { +; CHECK-LABEL: @test_dead_on_return( +; CHECK-NEXT: ret void +; + store i8 0, ptr %p + ret void +} + +define void @test_dead_on_return_maythrow(ptr dead_on_return %p) { +; CHECK-LABEL: @test_dead_on_return_maythrow( +; CHECK-NEXT: call void @maythrow() +; CHECK-NEXT: ret void +; + store i8 0, ptr %p + call void @maythrow() + ret void +} + +define ptr @test_dead_on_return_ptr_returned(ptr dead_on_return %p) { +; CHECK-LABEL: @test_dead_on_return_ptr_returned( +; CHECK-NEXT: [[LOCAL_VAR:%.*]] = alloca ptr, align 8 +; CHECK-NEXT: call void @opaque(ptr [[LOCAL_VAR]]) +; CHECK-NEXT: ret ptr [[P:%.*]] +; + %local.var = alloca ptr + call void @opaque(ptr %local.var) + store ptr %local.var, ptr %p + ret ptr %p +} + +declare void @opaque(ptr) +declare void @maythrow() memory(none) diff --git a/llvm/test/Verifier/dead-on-return.ll b/llvm/test/Verifier/dead-on-return.ll new file mode 100644 index 0000000..eb1d67f --- /dev/null +++ b/llvm/test/Verifier/dead-on-return.ll @@ -0,0 +1,7 @@ +; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s + +; CHECK: Attribute 'dead_on_return' applied to incompatible type! +; CHECK-NEXT: ptr @arg_not_pointer +define void @arg_not_pointer(i32 dead_on_return %arg) { + ret void +} |