diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h index 1e43d903360e..d21d65bc4e79 100644 --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -521,6 +521,12 @@ public: return removeAttributes(C, ArgNo + FirstArgIndex, AttrsToRemove); } + /// Remove noundef attribute and other attributes that imply undefined + /// behavior if a `undef` or `poison` value is passed from this attribute + /// list. Returns a new list because attribute lists are immutable. + LLVM_NODISCARD AttributeList + removeParamUndefImplyingAttributes(LLVMContext &C, unsigned ArgNo) const; + /// Remove all attributes at the specified arg index from this /// attribute list. Returns a new list because attribute lists are immutable. LLVM_NODISCARD AttributeList removeParamAttributes(LLVMContext &C, diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index b3a1b6c03618..ab20cc4b68c8 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -426,6 +426,10 @@ public: /// removes the attribute from the list of attributes. void removeParamAttrs(unsigned ArgNo, const AttrBuilder &Attrs); + /// removes noundef and other attributes that imply undefined behavior if a + /// `undef` or `poison` value is passed from the list of attributes. + void removeParamUndefImplyingAttrs(unsigned ArgNo); + /// check if an attributes is in the list of attributes. bool hasAttribute(unsigned i, Attribute::AttrKind Kind) const { return getAttributes().hasAttribute(i, Kind); diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 86e86c454d8f..f218bc4cd36f 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1555,6 +1555,15 @@ public: setAttributes(PAL); } + /// Removes noundef and other attributes that imply undefined behavior if a + /// `undef` or `poison` value is passed from the given argument. + void removeParamUndefImplyingAttrs(unsigned ArgNo) { + assert(ArgNo < getNumArgOperands() && "Out of bounds"); + AttributeList PAL = getAttributes(); + PAL = PAL.removeParamUndefImplyingAttributes(getContext(), ArgNo); + setAttributes(PAL); + } + /// adds the dereferenceable attribute to the list of attributes. void addDereferenceableAttr(unsigned i, uint64_t Bytes) { AttributeList PAL = getAttributes(); diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp index 4c087c967b01..831186a49fca 100644 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1454,6 +1454,17 @@ AttributeList AttributeList::removeAttributes(LLVMContext &C, return getImpl(C, AttrSets); } +AttributeList +AttributeList::removeParamUndefImplyingAttributes(LLVMContext &C, + unsigned ArgNo) const { + AttrBuilder B; + B.addAttribute(Attribute::NoUndef); + B.addAttribute(Attribute::NonNull); + B.addDereferenceableAttr(1); + B.addDereferenceableOrNullAttr(1); + return removeParamAttributes(C, ArgNo, B); +} + AttributeList AttributeList::addDereferenceableAttr(LLVMContext &C, unsigned Index, uint64_t Bytes) const { diff --git a/llvm/lib/IR/Function.cpp b/llvm/lib/IR/Function.cpp index ab8d425ef44c..7389ec6858ed 100644 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -562,6 +562,12 @@ void Function::removeParamAttrs(unsigned ArgNo, const AttrBuilder &Attrs) { setAttributes(PAL); } +void Function::removeParamUndefImplyingAttrs(unsigned ArgNo) { + AttributeList PAL = getAttributes(); + PAL = PAL.removeParamUndefImplyingAttributes(getContext(), ArgNo); + setAttributes(PAL); +} + void Function::addDereferenceableAttr(unsigned i, uint64_t Bytes) { AttributeList PAL = getAttributes(); PAL = PAL.addDereferenceableAttr(getContext(), i, Bytes); diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index 0b763e423fe0..3154d2468c64 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -295,6 +295,7 @@ bool DeadArgumentEliminationPass::RemoveDeadArgumentsFromCallers(Function &Fn) { Changed = true; } UnusedArgs.push_back(Arg.getArgNo()); + Fn.removeParamUndefImplyingAttrs(Arg.getArgNo()); } } @@ -312,6 +313,8 @@ bool DeadArgumentEliminationPass::RemoveDeadArgumentsFromCallers(Function &Fn) { Value *Arg = CB->getArgOperand(ArgNo); CB->setArgOperand(ArgNo, UndefValue::get(Arg->getType())); + CB->removeParamUndefImplyingAttrs(ArgNo); + ++NumArgumentsReplacedWithUndef; Changed = true; } diff --git a/llvm/test/Transforms/DeadArgElim/NoundefAttrs.ll b/llvm/test/Transforms/DeadArgElim/NoundefAttrs.ll new file mode 100644 index 000000000000..576f972fc87a --- /dev/null +++ b/llvm/test/Transforms/DeadArgElim/NoundefAttrs.ll @@ -0,0 +1,18 @@ +; RUN: opt -deadargelim -S < %s | FileCheck %s + +; If caller is changed to pass in undef, noundef and related attributes +; should be deleted. + + +; CHECK: define i64 @bar(i64* %0, i64 %1) +define i64 @bar(i64* nonnull dereferenceable(8) %0, i64 %1) { +entry: + %2 = add i64 %1, 8 + ret i64 %2 +} + +define i64 @foo(i64* %p, i64 %v) { +; CHECK: %retval = call i64 @bar(i64* undef, i64 %v) + %retval = call i64 @bar(i64* nonnull dereferenceable(8) %p, i64 %v) + ret i64 %retval +} diff --git a/llvm/test/Transforms/InstCombine/unused-nonnull.ll b/llvm/test/Transforms/InstCombine/unused-nonnull.ll index 74173ad4592d..0f6ef7299980 100644 --- a/llvm/test/Transforms/InstCombine/unused-nonnull.ll +++ b/llvm/test/Transforms/InstCombine/unused-nonnull.ll @@ -37,7 +37,7 @@ done: define i32 @compute(i8* noundef nonnull %ptr, i32 %x) #1 { ; CHECK-LABEL: define {{[^@]+}}@compute -; CHECK-SAME: (i8* nocapture noundef nonnull readnone [[PTR:%.*]], i32 returned [[X:%.*]]) local_unnamed_addr #1 +; CHECK-SAME: (i8* nocapture readnone [[PTR:%.*]], i32 returned [[X:%.*]]) local_unnamed_addr #1 ; CHECK-NEXT: ret i32 [[X]] ; ret i32 %x