[Attributor] Check violation of returned position nonnull and noundef attribute in AAUndefinedBehavior
This patch is a follow up of D84733. If a function has noundef attribute in returned position, instructions that return undef or poison value cause UB. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D85178
This commit is contained in:
@@ -2038,6 +2038,37 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior {
|
||||
return true;
|
||||
};
|
||||
|
||||
auto InspectReturnInstForUB =
|
||||
[&](Value &V, const SmallSetVector<ReturnInst *, 4> RetInsts) {
|
||||
// Check if a return instruction always cause UB or not
|
||||
// Note: It is guaranteed that the returned position of the anchor
|
||||
// scope has noundef attribute when this is called.
|
||||
|
||||
// When the returned position has noundef attriubte, UB occur in the
|
||||
// following cases.
|
||||
// (1) Returned value is known to be undef.
|
||||
// (2) The value is known to be a null pointer and the returned
|
||||
// position has nonnull attribute (because the returned value is
|
||||
// poison).
|
||||
// Note: This callback is not called for a dead returned value because
|
||||
// such values are ignored in
|
||||
// checkForAllReturnedValuesAndReturnedInsts.
|
||||
bool FoundUB = false;
|
||||
if (isa<UndefValue>(V)) {
|
||||
FoundUB = true;
|
||||
} else {
|
||||
auto &NonNullAA = A.getAAFor<AANonNull>(
|
||||
*this, IRPosition::returned(*getAnchorScope()));
|
||||
if (NonNullAA.isKnownNonNull() && isa<ConstantPointerNull>(V))
|
||||
FoundUB = true;
|
||||
}
|
||||
|
||||
if (FoundUB)
|
||||
for (ReturnInst *RI : RetInsts)
|
||||
KnownUBInsts.insert(RI);
|
||||
return true;
|
||||
};
|
||||
|
||||
A.checkForAllInstructions(InspectMemAccessInstForUB, *this,
|
||||
{Instruction::Load, Instruction::Store,
|
||||
Instruction::AtomicCmpXchg,
|
||||
@@ -2046,6 +2077,13 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior {
|
||||
A.checkForAllInstructions(InspectBrInstForUB, *this, {Instruction::Br},
|
||||
/* CheckBBLivenessOnly */ true);
|
||||
A.checkForAllCallLikeInstructions(InspectCallSiteForUB, *this);
|
||||
|
||||
// If the returned position of the anchor scope has noundef attriubte, check
|
||||
// all returned instructions.
|
||||
// TODO: If AANoUndef is implemented, ask it here.
|
||||
if (IRPosition::returned(*getAnchorScope()).hasAttr({Attribute::NoUndef}))
|
||||
A.checkForAllReturnedValuesAndReturnInsts(InspectReturnInstForUB, *this);
|
||||
|
||||
if (NoUBPrevSize != AssumedNoUBInsts.size() ||
|
||||
UBPrevSize != KnownUBInsts.size())
|
||||
return ChangeStatus::CHANGED;
|
||||
|
||||
@@ -580,7 +580,9 @@ define i32 @foo() {
|
||||
ret i32 %X
|
||||
}
|
||||
|
||||
; Tests for nonnull attribute violation.
|
||||
; Tests for nonnull noundef attribute violation.
|
||||
;
|
||||
; Tests for argument position
|
||||
|
||||
define void @arg_nonnull_1(i32* nonnull %a) {
|
||||
; IS__TUNIT____: Function Attrs: argmemonly nofree nosync nounwind willreturn writeonly
|
||||
@@ -874,3 +876,131 @@ f:
|
||||
ret:
|
||||
ret void
|
||||
}
|
||||
|
||||
; Tests for returned position
|
||||
|
||||
define nonnull i32* @returned_nonnnull(i32 %c) {
|
||||
; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_nonnnull
|
||||
; IS__TUNIT____-SAME: (i32 [[C:%.*]])
|
||||
; IS__TUNIT____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__TUNIT____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__TUNIT____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__TUNIT____-NEXT: ]
|
||||
; IS__TUNIT____: onzero:
|
||||
; IS__TUNIT____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__TUNIT____-NEXT: ret i32* [[PTR]]
|
||||
; IS__TUNIT____: onone:
|
||||
; IS__TUNIT____-NEXT: ret i32* null
|
||||
; IS__TUNIT____: ondefault:
|
||||
; IS__TUNIT____-NEXT: ret i32* undef
|
||||
;
|
||||
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_nonnnull
|
||||
; IS__CGSCC____-SAME: (i32 [[C:%.*]])
|
||||
; IS__CGSCC____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__CGSCC____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__CGSCC____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__CGSCC____-NEXT: ]
|
||||
; IS__CGSCC____: onzero:
|
||||
; IS__CGSCC____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__CGSCC____-NEXT: ret i32* [[PTR]]
|
||||
; IS__CGSCC____: onone:
|
||||
; IS__CGSCC____-NEXT: ret i32* null
|
||||
; IS__CGSCC____: ondefault:
|
||||
; IS__CGSCC____-NEXT: ret i32* undef
|
||||
;
|
||||
switch i32 %c, label %ondefault [ i32 0, label %onzero
|
||||
i32 1, label %onone ]
|
||||
onzero:
|
||||
%ptr = alloca i32
|
||||
ret i32* %ptr
|
||||
onone:
|
||||
ret i32* null
|
||||
ondefault:
|
||||
ret i32* undef
|
||||
}
|
||||
|
||||
define noundef i32* @returned_noundef(i32 %c) {
|
||||
; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_noundef
|
||||
; IS__TUNIT____-SAME: (i32 [[C:%.*]])
|
||||
; IS__TUNIT____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__TUNIT____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__TUNIT____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__TUNIT____-NEXT: ]
|
||||
; IS__TUNIT____: onzero:
|
||||
; IS__TUNIT____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__TUNIT____-NEXT: ret i32* [[PTR]]
|
||||
; IS__TUNIT____: onone:
|
||||
; IS__TUNIT____-NEXT: ret i32* null
|
||||
; IS__TUNIT____: ondefault:
|
||||
; IS__TUNIT____-NEXT: unreachable
|
||||
;
|
||||
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_noundef
|
||||
; IS__CGSCC____-SAME: (i32 [[C:%.*]])
|
||||
; IS__CGSCC____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__CGSCC____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__CGSCC____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__CGSCC____-NEXT: ]
|
||||
; IS__CGSCC____: onzero:
|
||||
; IS__CGSCC____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__CGSCC____-NEXT: ret i32* [[PTR]]
|
||||
; IS__CGSCC____: onone:
|
||||
; IS__CGSCC____-NEXT: ret i32* null
|
||||
; IS__CGSCC____: ondefault:
|
||||
; IS__CGSCC____-NEXT: unreachable
|
||||
;
|
||||
switch i32 %c, label %ondefault [ i32 0, label %onzero
|
||||
i32 1, label %onone ]
|
||||
onzero:
|
||||
%ptr = alloca i32
|
||||
ret i32* %ptr
|
||||
onone:
|
||||
ret i32* null
|
||||
ondefault:
|
||||
ret i32* undef
|
||||
}
|
||||
|
||||
define nonnull noundef i32* @returned_nonnnull_noundef(i32 %c) {
|
||||
; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn
|
||||
; IS__TUNIT____-LABEL: define {{[^@]+}}@returned_nonnnull_noundef
|
||||
; IS__TUNIT____-SAME: (i32 [[C:%.*]])
|
||||
; IS__TUNIT____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__TUNIT____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__TUNIT____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__TUNIT____-NEXT: ]
|
||||
; IS__TUNIT____: onzero:
|
||||
; IS__TUNIT____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__TUNIT____-NEXT: ret i32* [[PTR]]
|
||||
; IS__TUNIT____: onone:
|
||||
; IS__TUNIT____-NEXT: unreachable
|
||||
; IS__TUNIT____: ondefault:
|
||||
; IS__TUNIT____-NEXT: unreachable
|
||||
;
|
||||
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
|
||||
; IS__CGSCC____-LABEL: define {{[^@]+}}@returned_nonnnull_noundef
|
||||
; IS__CGSCC____-SAME: (i32 [[C:%.*]])
|
||||
; IS__CGSCC____-NEXT: switch i32 [[C]], label [[ONDEFAULT:%.*]] [
|
||||
; IS__CGSCC____-NEXT: i32 0, label [[ONZERO:%.*]]
|
||||
; IS__CGSCC____-NEXT: i32 1, label [[ONONE:%.*]]
|
||||
; IS__CGSCC____-NEXT: ]
|
||||
; IS__CGSCC____: onzero:
|
||||
; IS__CGSCC____-NEXT: [[PTR:%.*]] = alloca i32, align 4
|
||||
; IS__CGSCC____-NEXT: ret i32* [[PTR]]
|
||||
; IS__CGSCC____: onone:
|
||||
; IS__CGSCC____-NEXT: unreachable
|
||||
; IS__CGSCC____: ondefault:
|
||||
; IS__CGSCC____-NEXT: unreachable
|
||||
;
|
||||
switch i32 %c, label %ondefault [ i32 0, label %onzero
|
||||
i32 1, label %onone ]
|
||||
onzero:
|
||||
%ptr = alloca i32
|
||||
ret i32* %ptr
|
||||
onone:
|
||||
ret i32* null
|
||||
ondefault:
|
||||
ret i32* undef
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user