[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);
}
/// 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,

View File

@ -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);

View File

@ -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();

View File

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

View File

@ -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);

View File

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

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 {
; 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