diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -521,6 +521,12 @@ 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 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -426,6 +426,10 @@ /// 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 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1555,6 +1555,15 @@ 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 --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -1454,6 +1454,17 @@ 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 --- a/llvm/lib/IR/Function.cpp +++ b/llvm/lib/IR/Function.cpp @@ -562,6 +562,12 @@ 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 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -295,6 +295,7 @@ Changed = true; } UnusedArgs.push_back(Arg.getArgNo()); + Fn.removeParamUndefImplyingAttrs(Arg.getArgNo()); } } @@ -312,6 +313,8 @@ 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 --- /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 --- a/llvm/test/Transforms/InstCombine/unused-nonnull.ll +++ b/llvm/test/Transforms/InstCombine/unused-nonnull.ll @@ -37,7 +37,7 @@ 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