Skip to content
Open
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
6 changes: 4 additions & 2 deletions llvm/docs/LangRef.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1842,11 +1842,13 @@ Currently, only the following parameter attributes are defined:

This attribute cannot be applied to return values.

``dead_on_return``
``dead_on_return(<n>)``
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.
either on the return path or on the unwind path may be elided. The number of
bytes known to be dead must be provided in parentheses. It is legal for the
number of bytes to be less than the size of the pointee type.

Specifically, the behavior is as-if any memory written through the pointer
during the execution of the function is overwritten with a poison value
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ namespace llvm {
bool parseOptionalAlignment(MaybeAlign &Alignment,
bool AllowParens = false);
bool parseOptionalCodeModel(CodeModel::Model &model);
bool parseOptionalDerefAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
bool parseOptionalAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes);
bool parseOptionalUWTableKind(UWTableKind &Kind);
bool parseAllocKind(AllocFnKind &Kind);
std::optional<MemoryEffects> parseMemoryAttr();
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/IR/Argument.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Argument final : public Value {
LLVM_ABI bool hasByValAttr() const;

/// Return true if this argument has the dead_on_return attribute.
LLVM_ABI bool hasDeadOnReturnAttr() const;
LLVM_ABI uint64_t getDeadOnReturnBytes() const;

/// Return true if this argument has the byref attribute.
LLVM_ABI bool hasByRefAttr() const;
Expand Down
12 changes: 12 additions & 0 deletions llvm/include/llvm/IR/Attributes.h
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,10 @@ class Attribute {
/// dereferenceable attribute.
LLVM_ABI uint64_t getDereferenceableBytes() const;

/// Returns the number of dead_on_return bytes from the
/// dead_on_return attribute.
LLVM_ABI uint64_t getDeadOnReturnBytes() const;

/// Returns the number of dereferenceable_or_null bytes from the
/// dereferenceable_or_null attribute.
LLVM_ABI uint64_t getDereferenceableOrNullBytes() const;
Expand Down Expand Up @@ -445,6 +449,7 @@ class AttributeSet {
LLVM_ABI MaybeAlign getAlignment() const;
LLVM_ABI MaybeAlign getStackAlignment() const;
LLVM_ABI uint64_t getDereferenceableBytes() const;
LLVM_ABI uint64_t getDeadOnReturnBytes() const;
LLVM_ABI uint64_t getDereferenceableOrNullBytes() const;
LLVM_ABI Type *getByValType() const;
LLVM_ABI Type *getStructRetType() const;
Expand Down Expand Up @@ -964,6 +969,9 @@ class AttributeList {
/// the return value.
LLVM_ABI uint64_t getRetDereferenceableOrNullBytes() const;

/// Get the number of dead_on_return bytes (or zero if unknown) of an arg.
LLVM_ABI uint64_t getDeadOnReturnBytes(unsigned Index) const;

/// Get the number of dereferenceable_or_null bytes (or zero if unknown) of an
/// arg.
LLVM_ABI uint64_t getParamDereferenceableOrNullBytes(unsigned ArgNo) const;
Expand Down Expand Up @@ -1242,6 +1250,10 @@ class AttrBuilder {
/// internally in Attribute.
LLVM_ABI AttrBuilder &addDereferenceableAttr(uint64_t Bytes);

/// This turns the number of dead_on_return bytes into the form used
/// internally in Attribute.
LLVM_ABI AttrBuilder &addDeadOnReturnAttr(uint64_t Bytes);

/// This turns the number of dereferenceable_or_null bytes into the
/// form used internally in Attribute.
LLVM_ABI AttrBuilder &addDereferenceableOrNullAttr(uint64_t Bytes);
Expand Down
2 changes: 1 addition & 1 deletion llvm/include/llvm/IR/Attributes.td
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ def NoFree : EnumAttr<"nofree", IntersectAnd, [FnAttr, ParamAttr]>;
def DeadOnUnwind : EnumAttr<"dead_on_unwind", IntersectAnd, [ParamAttr]>;

/// Argument is dead upon function return.
def DeadOnReturn : EnumAttr<"dead_on_return", IntersectAnd, [ParamAttr]>;
def DeadOnReturn : IntAttr<"dead_on_return", IntersectMin, [ParamAttr]>;

/// Disable implicit floating point insts.
def NoImplicitFloat : EnumAttr<"noimplicitfloat", IntersectPreserve, [FnAttr]>;
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,12 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> {
return AttributeSets.getParamDereferenceableBytes(ArgNo);
}

/// Extract the number of dead_on_return bytes for a parameter.
/// @param ArgNo Index of an argument, with 0 being the first function arg.
uint64_t getDeadOnReturnBytes(unsigned ArgNo) const {
return AttributeSets.getDeadOnReturnBytes(ArgNo);
}

/// Extract the number of dereferenceable_or_null bytes for a
/// parameter.
/// @param ArgNo AttributeList ArgNo, referring to an argument.
Expand Down
22 changes: 15 additions & 7 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1607,14 +1607,21 @@ bool LLParser::parseEnumAttribute(Attribute::AttrKind Attr, AttrBuilder &B,
}
case Attribute::Dereferenceable: {
uint64_t Bytes;
if (parseOptionalDerefAttrBytes(lltok::kw_dereferenceable, Bytes))
if (parseOptionalAttrBytes(lltok::kw_dereferenceable, Bytes))
return true;
B.addDereferenceableAttr(Bytes);
return false;
}
case Attribute::DeadOnReturn: {
uint64_t Bytes;
if (parseOptionalAttrBytes(lltok::kw_dead_on_return, Bytes))
return true;
B.addDeadOnReturnAttr(Bytes);
return false;
}
case Attribute::DereferenceableOrNull: {
uint64_t Bytes;
if (parseOptionalDerefAttrBytes(lltok::kw_dereferenceable_or_null, Bytes))
if (parseOptionalAttrBytes(lltok::kw_dereferenceable_or_null, Bytes))
return true;
B.addDereferenceableOrNullAttr(Bytes);
return false;
Expand Down Expand Up @@ -2468,15 +2475,16 @@ bool LLParser::parseOptionalCodeModel(CodeModel::Model &model) {
return false;
}

/// parseOptionalDerefAttrBytes
/// parseOptionalAttrBytes
/// ::= /* empty */
/// ::= AttrKind '(' 4 ')'
///
/// where AttrKind is either 'dereferenceable' or 'dereferenceable_or_null'.
bool LLParser::parseOptionalDerefAttrBytes(lltok::Kind AttrKind,
uint64_t &Bytes) {
/// where AttrKind is either 'dereferenceable', 'dereferenceable_or_null', or
/// 'dead_on_return'
bool LLParser::parseOptionalAttrBytes(lltok::Kind AttrKind, uint64_t &Bytes) {
assert((AttrKind == lltok::kw_dereferenceable ||
AttrKind == lltok::kw_dereferenceable_or_null) &&
AttrKind == lltok::kw_dereferenceable_or_null ||
AttrKind == lltok::kw_dead_on_return) &&
"contract!");

Bytes = 0;
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#include <cstddef>
#include <cstdint>
#include <deque>
#include <limits>
#include <map>
#include <memory>
#include <optional>
Expand Down Expand Up @@ -2384,6 +2385,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
B.addInAllocaAttr(nullptr);
else if (Kind == Attribute::UWTable)
B.addUWTableAttr(UWTableKind::Default);
else if (Kind == Attribute::DeadOnReturn)
B.addDeadOnReturnAttr(std::numeric_limits<uint64_t>::max());
else if (Attribute::isEnumAttrKind(Kind))
B.addAttribute(Kind);
else
Expand All @@ -2402,6 +2405,8 @@ Error BitcodeReader::parseAttributeGroupBlock() {
B.addDereferenceableAttr(Record[++i]);
else if (Kind == Attribute::DereferenceableOrNull)
B.addDereferenceableOrNullAttr(Record[++i]);
else if (Kind == Attribute::DeadOnReturn)
B.addDeadOnReturnAttr(Record[++i]);
else if (Kind == Attribute::AllocSize)
B.addAllocSizeAttrFromRawRepr(Record[++i]);
else if (Kind == Attribute::VScaleRange)
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/IR/AttributeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ class AttributeSetNode final
MaybeAlign getAlignment() const;
MaybeAlign getStackAlignment() const;
uint64_t getDereferenceableBytes() const;
uint64_t getDeadOnReturnBytes() const;
uint64_t getDereferenceableOrNullBytes() const;
std::optional<std::pair<unsigned, std::optional<unsigned>>> getAllocSizeArgs()
const;
Expand Down
31 changes: 31 additions & 0 deletions llvm/lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,13 @@ uint64_t Attribute::getDereferenceableBytes() const {
return pImpl->getValueAsInt();
}

uint64_t Attribute::getDeadOnReturnBytes() const {
assert(hasAttribute(Attribute::DeadOnReturn) &&
"Trying to get dead_on_return bytes from"
"from a parameter without such an attribute!");
return pImpl->getValueAsInt();
}

uint64_t Attribute::getDereferenceableOrNullBytes() const {
assert(hasAttribute(Attribute::DereferenceableOrNull) &&
"Trying to get dereferenceable bytes from "
Expand Down Expand Up @@ -574,6 +581,9 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
if (hasAttribute(Attribute::DereferenceableOrNull))
return AttrWithBytesToString("dereferenceable_or_null");

if (hasAttribute(Attribute::DeadOnReturn))
return AttrWithBytesToString("dead_on_return");

if (hasAttribute(Attribute::AllocSize)) {
unsigned ElemSize;
std::optional<unsigned> NumElems;
Expand Down Expand Up @@ -1162,6 +1172,10 @@ uint64_t AttributeSet::getDereferenceableBytes() const {
return SetNode ? SetNode->getDereferenceableBytes() : 0;
}

uint64_t AttributeSet::getDeadOnReturnBytes() const {
return SetNode ? SetNode->getDeadOnReturnBytes() : 0;
}

uint64_t AttributeSet::getDereferenceableOrNullBytes() const {
return SetNode ? SetNode->getDereferenceableOrNullBytes() : 0;
}
Expand Down Expand Up @@ -1366,6 +1380,12 @@ uint64_t AttributeSetNode::getDereferenceableBytes() const {
return 0;
}

uint64_t AttributeSetNode::getDeadOnReturnBytes() const {
if (auto A = findEnumAttribute(Attribute::DeadOnReturn))
return A->getDeadOnReturnBytes();
return 0;
}

uint64_t AttributeSetNode::getDereferenceableOrNullBytes() const {
if (auto A = findEnumAttribute(Attribute::DereferenceableOrNull))
return A->getDereferenceableOrNullBytes();
Expand Down Expand Up @@ -1983,6 +2003,10 @@ uint64_t AttributeList::getRetDereferenceableOrNullBytes() const {
return getRetAttrs().getDereferenceableOrNullBytes();
}

uint64_t AttributeList::getDeadOnReturnBytes(unsigned Index) const {
return getParamAttrs(Index).getDeadOnReturnBytes();
}

uint64_t
AttributeList::getParamDereferenceableOrNullBytes(unsigned Index) const {
return getParamAttrs(Index).getDereferenceableOrNullBytes();
Expand Down Expand Up @@ -2205,6 +2229,13 @@ AttrBuilder &AttrBuilder::addDereferenceableAttr(uint64_t Bytes) {
return addRawIntAttr(Attribute::Dereferenceable, Bytes);
}

AttrBuilder &AttrBuilder::addDeadOnReturnAttr(uint64_t Bytes) {
if (Bytes == 0)
return *this;

return addRawIntAttr(Attribute::DeadOnReturn, Bytes);
}

AttrBuilder &AttrBuilder::addDereferenceableOrNullAttr(uint64_t Bytes) {
if (Bytes == 0)
return *this;
Expand Down
7 changes: 3 additions & 4 deletions llvm/lib/IR/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,9 @@ bool Argument::hasByValAttr() const {
return hasAttribute(Attribute::ByVal);
}

bool Argument::hasDeadOnReturnAttr() const {
if (!getType()->isPointerTy())
return false;
return hasAttribute(Attribute::DeadOnReturn);
uint64_t Argument::getDeadOnReturnBytes() const {
assert(getType()->isPointerTy() && "Only pointers have dead_on_return bytes");
return getParent()->getDeadOnReturnBytes(getArgNo());
}

bool Argument::hasByRefAttr() const {
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,8 @@ struct DSEState {
// 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() || AI.hasDeadOnReturnAttr())
if (AI.hasPassPointeeByValueCopyAttr() ||
(AI.getType()->isPointerTy() && AI.getDeadOnReturnBytes() > 0))
InvisibleToCallerAfterRet.insert({&AI, true});

// Collect whether there is any irreducible control flow in the function.
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Bitcode/attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -577,8 +577,8 @@ 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) {
; CHECK: define void @dead_on_return(ptr dead_on_return(4) %p)
define void @dead_on_return(ptr dead_on_return(4) %p) {
ret void
}

Expand Down
7 changes: 7 additions & 0 deletions llvm/test/Bitcode/dead-on-return-upgrade.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
; RUN: llvm-dis -o - %s.bc | FileCheck %s

; CHECK: define void @test_dead_on_return_autoupgrade(ptr dead_on_return(18446744073709551615) %p) {

define void @test_dead_on_return_autoupgrade(ptr dead_on_return %p) {
ret void
}
Binary file added llvm/test/Bitcode/dead-on-return-upgrade.ll.bc
Binary file not shown.
6 changes: 3 additions & 3 deletions llvm/test/Transforms/DeadStoreElimination/simple.ll
Original file line number Diff line number Diff line change
Expand Up @@ -856,15 +856,15 @@ bb:
ret void
}

define void @test_dead_on_return(ptr dead_on_return %p) {
define void @test_dead_on_return(ptr dead_on_return(4) %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) {
define void @test_dead_on_return_maythrow(ptr dead_on_return(4) %p) {
; CHECK-LABEL: @test_dead_on_return_maythrow(
; CHECK-NEXT: call void @maythrow()
; CHECK-NEXT: ret void
Expand All @@ -874,7 +874,7 @@ define void @test_dead_on_return_maythrow(ptr dead_on_return %p) {
ret void
}

define ptr @test_dead_on_return_ptr_returned(ptr dead_on_return %p) {
define ptr @test_dead_on_return_ptr_returned(ptr dead_on_return(64) %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]])
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/Verifier/dead-on-return.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; RUN: not llvm-as -disable-output %s 2>&1 | FileCheck %s

; CHECK: Attribute 'dead_on_return' applied to incompatible type!
; CHECK: Attribute 'dead_on_return(4)' applied to incompatible type!
; CHECK-NEXT: ptr @arg_not_pointer
define void @arg_not_pointer(i32 dead_on_return %arg) {
define void @arg_not_pointer(i32 dead_on_return(4) %arg) {
ret void
}
Loading