[DAE] Adjust param/arg attributes when changing parameter to undef

In DeadArgumentElimination pass, if a function's argument is never used, corresponding caller's parameter can be changed to undef. If the param/arg has attribute noundef or other related attributes, LLVM LangRef(https://llvm.org/docs/LangRef.html#parameter-attributes) says its behavior is undefined. SimplifyCFG(D97244) takes advantage of this behavior and does bad transformation on valid code.

To avoid this undefined behavior when change caller's parameter to undef, this patch removes noundef attribute and other attributes imply noundef on param/arg.

Differential Revision: https://reviews.llvm.org/D98899
This commit is contained in:
Guozhi Wei 2021-03-25 14:50:18 -07:00
parent 4f5e92cc05
commit 3240910f00
8 changed files with 58 additions and 1 deletions

View File

@ -521,6 +521,12 @@ public:
return removeAttributes(C, ArgNo + FirstArgIndex, AttrsToRemove); 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 /// Remove all attributes at the specified arg index from this
/// attribute list. Returns a new list because attribute lists are immutable. /// attribute list. Returns a new list because attribute lists are immutable.
LLVM_NODISCARD AttributeList removeParamAttributes(LLVMContext &C, LLVM_NODISCARD AttributeList removeParamAttributes(LLVMContext &C,

View File

@ -426,6 +426,10 @@ public:
/// removes the attribute from the list of attributes. /// removes the attribute from the list of attributes.
void removeParamAttrs(unsigned ArgNo, const AttrBuilder &Attrs); 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. /// check if an attributes is in the list of attributes.
bool hasAttribute(unsigned i, Attribute::AttrKind Kind) const { bool hasAttribute(unsigned i, Attribute::AttrKind Kind) const {
return getAttributes().hasAttribute(i, Kind); return getAttributes().hasAttribute(i, Kind);

View File

@ -1555,6 +1555,15 @@ public:
setAttributes(PAL); 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. /// adds the dereferenceable attribute to the list of attributes.
void addDereferenceableAttr(unsigned i, uint64_t Bytes) { void addDereferenceableAttr(unsigned i, uint64_t Bytes) {
AttributeList PAL = getAttributes(); AttributeList PAL = getAttributes();

View File

@ -1454,6 +1454,17 @@ AttributeList AttributeList::removeAttributes(LLVMContext &C,
return getImpl(C, AttrSets); 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, AttributeList AttributeList::addDereferenceableAttr(LLVMContext &C,
unsigned Index, unsigned Index,
uint64_t Bytes) const { uint64_t Bytes) const {

View File

@ -562,6 +562,12 @@ void Function::removeParamAttrs(unsigned ArgNo, const AttrBuilder &Attrs) {
setAttributes(PAL); 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) { void Function::addDereferenceableAttr(unsigned i, uint64_t Bytes) {
AttributeList PAL = getAttributes(); AttributeList PAL = getAttributes();
PAL = PAL.addDereferenceableAttr(getContext(), i, Bytes); PAL = PAL.addDereferenceableAttr(getContext(), i, Bytes);

View File

@ -295,6 +295,7 @@ bool DeadArgumentEliminationPass::RemoveDeadArgumentsFromCallers(Function &Fn) {
Changed = true; Changed = true;
} }
UnusedArgs.push_back(Arg.getArgNo()); UnusedArgs.push_back(Arg.getArgNo());
Fn.removeParamUndefImplyingAttrs(Arg.getArgNo());
} }
} }
@ -312,6 +313,8 @@ bool DeadArgumentEliminationPass::RemoveDeadArgumentsFromCallers(Function &Fn) {
Value *Arg = CB->getArgOperand(ArgNo); Value *Arg = CB->getArgOperand(ArgNo);
CB->setArgOperand(ArgNo, UndefValue::get(Arg->getType())); CB->setArgOperand(ArgNo, UndefValue::get(Arg->getType()));
CB->removeParamUndefImplyingAttrs(ArgNo);
++NumArgumentsReplacedWithUndef; ++NumArgumentsReplacedWithUndef;
Changed = true; Changed = true;
} }

View File

@ -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
}

View File

@ -37,7 +37,7 @@ done:
define i32 @compute(i8* noundef nonnull %ptr, i32 %x) #1 { define i32 @compute(i8* noundef nonnull %ptr, i32 %x) #1 {
; CHECK-LABEL: define {{[^@]+}}@compute ; 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]] ; CHECK-NEXT: ret i32 [[X]]
; ;
ret i32 %x ret i32 %x