[llvm] [IR] Add `dead_on_return` attribute (PR #143271)
Antonio Frighetto via llvm-commits
llvm-commits at lists.llvm.org
Sat Jun 7 09:14:49 PDT 2025
https://github.com/antoniofrighetto updated https://github.com/llvm/llvm-project/pull/143271
>From 04889585578ff3680a2dff5792e25934525d0a8c Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 7 Jun 2025 15:29:18 +0200
Subject: [PATCH 1/2] [IR] Add `dead_on_return` attribute
Introduce `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, and the
pointee is invisible once the call completes. They however differ
in the ABI: with `byval` the pointer is explicitly passed as
argument to the callee and already points at the copy, whereas
`dead_on_return` implies the copy is located within the callee
stack frame.
---
llvm/docs/LangRef.rst | 14 ++++++++++++++
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 +
llvm/include/llvm/IR/Argument.h | 9 ++++++---
llvm/include/llvm/IR/Attributes.td | 3 +++
llvm/lib/Analysis/AliasAnalysis.cpp | 2 +-
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 ++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 ++
llvm/lib/IR/Attributes.cpp | 3 ++-
llvm/lib/IR/Function.cpp | 9 ++++++++-
llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 +
llvm/test/Bitcode/attributes.ll | 5 +++++
.../test/Transforms/DeadStoreElimination/simple.ll | 8 ++++++++
llvm/test/Verifier/dead-on-return.ll | 7 +++++++
13 files changed, 60 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/Verifier/dead-on-return.ll
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 0958f6a4b729b..af1ae52e4e821 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1741,6 +1741,20 @@ 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 normal function return.
+
+ It is similar to ``byval`` in the regard that it is generally used to pass
+ structs and arrays by value, and the memory is caller-invisible when the
+ function returns. However, unlike ``byval``, it is intended for ABIs where the
+ *callee* allocates the hidden copy, rather than the caller. Stores that would
+ only be visible on the normal return path may be optimized out. Likewise,
+ optimizations may assume that the pointer does not alias any memory that
+ outlives the call.
+
+ 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 b362a88963f6c..dc78eb4164acf 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 60854b17094bf..8e767a237c35b 100644
--- a/llvm/include/llvm/IR/Argument.h
+++ b/llvm/include/llvm/IR/Argument.h
@@ -78,6 +78,9 @@ class Argument final : public Value {
/// 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.
+ bool hasDeadOnReturnAttr() const;
+
/// Return true if this argument has the byref attribute.
LLVM_ABI bool hasByRefAttr() const;
@@ -87,9 +90,9 @@ class Argument final : public Value {
/// Return true if this argument has the swifterror attribute.
LLVM_ABI bool hasSwiftErrorAttr() const;
- /// Return true if this argument has the byval, inalloca, or preallocated
- /// attribute. These attributes represent arguments being passed by value,
- /// with an associated copy between the caller and callee
+ /// Return true if this argument has the byval, inalloca, preallocated or
+ /// dead_on_return attribute. These attributes represent arguments being
+ /// passed by value, with an associated copy between the caller and callee.
LLVM_ABI bool hasPassPointeeByValueCopyAttr() const;
/// If this argument satisfies has hasPassPointeeByValueAttr, return the
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index d488c5f419b82..6a6510efa5ec4 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/Analysis/AliasAnalysis.cpp b/llvm/lib/Analysis/AliasAnalysis.cpp
index 3ec009ca4adde..924d97af79a17 100644
--- a/llvm/lib/Analysis/AliasAnalysis.cpp
+++ b/llvm/lib/Analysis/AliasAnalysis.cpp
@@ -818,7 +818,7 @@ bool llvm::isNoAliasCall(const Value *V) {
static bool isNoAliasOrByValArgument(const Value *V) {
if (const Argument *A = dyn_cast<Argument>(V))
- return A->hasNoAliasAttr() || A->hasByValAttr();
+ return A->hasNoAliasAttr() || A->hasByValAttr() || A->hasDeadOnReturnAttr();
return false;
}
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index 105edb943eb7f..b6da841e948fe 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2246,6 +2246,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 fad8ebfad9f9a..e33dc1d729389 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -940,6 +940,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 ed485f9656996..78d00c05a90e7 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 dfffbbfcf5d2a..aa09d7fa44812 100644
--- a/llvm/lib/IR/Function.cpp
+++ b/llvm/lib/IR/Function.cpp
@@ -146,6 +146,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;
@@ -176,7 +182,8 @@ bool Argument::hasPassPointeeByValueCopyAttr() const {
AttributeList Attrs = getParent()->getAttributes();
return Attrs.hasParamAttr(getArgNo(), Attribute::ByVal) ||
Attrs.hasParamAttr(getArgNo(), Attribute::InAlloca) ||
- Attrs.hasParamAttr(getArgNo(), Attribute::Preallocated);
+ Attrs.hasParamAttr(getArgNo(), Attribute::Preallocated) ||
+ Attrs.hasParamAttr(getArgNo(), Attribute::DeadOnReturn);
}
bool Argument::hasPointeeInMemoryValueAttr() const {
diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
index c4894c90c127f..30f3e9ebab4e2 100644
--- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp
+++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp
@@ -1025,6 +1025,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 7dd86a8c0eb16..8c1a76365e1b4 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 f8e594b5626a0..761090427f941 100644
--- a/llvm/test/Transforms/DeadStoreElimination/simple.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/simple.ll
@@ -855,3 +855,11 @@ bb:
store ptr null, ptr null, align 8
ret void
}
+
+define void @test50(ptr dead_on_return %p) {
+; CHECK-LABEL: @test50(
+; CHECK-NEXT: ret void
+;
+ store i8 0, ptr %p
+ ret void
+}
diff --git a/llvm/test/Verifier/dead-on-return.ll b/llvm/test/Verifier/dead-on-return.ll
new file mode 100644
index 0000000000000..eb1d67fa319c2
--- /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
+}
>From b7be265abe89c82b84998f440543e6f7f36adb00 Mon Sep 17 00:00:00 2001
From: Antonio Frighetto <me at antoniofrighetto.com>
Date: Sat, 7 Jun 2025 18:13:47 +0200
Subject: [PATCH 2/2] !fixup fix wording
---
llvm/docs/LangRef.rst | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index af1ae52e4e821..43862532d8a86 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1748,8 +1748,8 @@ Currently, only the following parameter attributes are defined:
It is similar to ``byval`` in the regard that it is generally used to pass
structs and arrays by value, and the memory is caller-invisible when the
function returns. However, unlike ``byval``, it is intended for ABIs where the
- *callee* allocates the hidden copy, rather than the caller. Stores that would
- only be visible on the normal return path may be optimized out. Likewise,
+ *callee* explicitly allocates the temporary copy. Stores that would only be
+ visible on the normal return path may be optimized out. Likewise,
optimizations may assume that the pointer does not alias any memory that
outlives the call.
More information about the llvm-commits
mailing list