forked from OSchip/llvm-project
[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:
parent
4f5e92cc05
commit
3240910f00
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue