[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:
Shinji Okumura
2020-08-07 11:40:53 +09:00
parent 39cbcbe1b1
commit f13f2e16f0
2 changed files with 169 additions and 1 deletions

View File

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

View File

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