Skip to content

Commit ae3fdf6

Browse files
committed
[ConstriantElim] Add non poison values recursively
1 parent 0e92ae9 commit ae3fdf6

File tree

6 files changed

+143
-126
lines changed

6 files changed

+143
-126
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,88 @@ bool isGuaranteedToExecuteForEveryIteration(const Instruction *I,
719719
/// getGuaranteedNonPoisonOp.
720720
bool propagatesPoison(const Use &PoisonOp);
721721

722+
/// Enumerates all operands of \p I that are guaranteed to not be undef or
723+
/// poison. If the callback \p Handle returns true, stop processing and return
724+
/// true. Otherwise, return false.
725+
template <typename CallableT>
726+
bool handleGuaranteedWellDefinedOps(const Instruction *I,
727+
const CallableT &Handle) {
728+
switch (I->getOpcode()) {
729+
case Instruction::Store:
730+
if (Handle(cast<StoreInst>(I)->getPointerOperand()))
731+
return true;
732+
break;
733+
734+
case Instruction::Load:
735+
if (Handle(cast<LoadInst>(I)->getPointerOperand()))
736+
return true;
737+
break;
738+
739+
// Since dereferenceable attribute imply noundef, atomic operations
740+
// also implicitly have noundef pointers too
741+
case Instruction::AtomicCmpXchg:
742+
if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
743+
return true;
744+
break;
745+
746+
case Instruction::AtomicRMW:
747+
if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
748+
return true;
749+
break;
750+
751+
case Instruction::Call:
752+
case Instruction::Invoke: {
753+
const CallBase *CB = cast<CallBase>(I);
754+
if (CB->isIndirectCall() && Handle(CB->getCalledOperand()))
755+
return true;
756+
for (unsigned i = 0; i < CB->arg_size(); ++i)
757+
if ((CB->paramHasAttr(i, Attribute::NoUndef) ||
758+
CB->paramHasAttr(i, Attribute::Dereferenceable) ||
759+
CB->paramHasAttr(i, Attribute::DereferenceableOrNull)) &&
760+
Handle(CB->getArgOperand(i)))
761+
return true;
762+
break;
763+
}
764+
case Instruction::Ret:
765+
if (I->getFunction()->hasRetAttribute(Attribute::NoUndef) &&
766+
Handle(I->getOperand(0)))
767+
return true;
768+
break;
769+
case Instruction::Switch:
770+
if (Handle(cast<SwitchInst>(I)->getCondition()))
771+
return true;
772+
break;
773+
case Instruction::Br: {
774+
auto *BR = cast<BranchInst>(I);
775+
if (BR->isConditional() && Handle(BR->getCondition()))
776+
return true;
777+
break;
778+
}
779+
default:
780+
break;
781+
}
782+
783+
return false;
784+
}
785+
786+
/// Enumerates all operands of \p I that are guaranteed to not be poison.
787+
template <typename CallableT>
788+
bool handleGuaranteedNonPoisonOps(const Instruction *I,
789+
const CallableT &Handle) {
790+
if (handleGuaranteedWellDefinedOps(I, Handle))
791+
return true;
792+
switch (I->getOpcode()) {
793+
// Divisors of these operations are allowed to be partially undef.
794+
case Instruction::UDiv:
795+
case Instruction::SDiv:
796+
case Instruction::URem:
797+
case Instruction::SRem:
798+
return Handle(I->getOperand(1));
799+
default:
800+
return false;
801+
}
802+
}
803+
722804
/// Return true if the given instruction must trigger undefined behavior
723805
/// when I is executed with any operands which appear in KnownPoison holding
724806
/// a poison value at the point of execution.

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -8213,88 +8213,6 @@ bool llvm::propagatesPoison(const Use &PoisonOp) {
82138213
}
82148214
}
82158215

8216-
/// Enumerates all operands of \p I that are guaranteed to not be undef or
8217-
/// poison. If the callback \p Handle returns true, stop processing and return
8218-
/// true. Otherwise, return false.
8219-
template <typename CallableT>
8220-
static bool handleGuaranteedWellDefinedOps(const Instruction *I,
8221-
const CallableT &Handle) {
8222-
switch (I->getOpcode()) {
8223-
case Instruction::Store:
8224-
if (Handle(cast<StoreInst>(I)->getPointerOperand()))
8225-
return true;
8226-
break;
8227-
8228-
case Instruction::Load:
8229-
if (Handle(cast<LoadInst>(I)->getPointerOperand()))
8230-
return true;
8231-
break;
8232-
8233-
// Since dereferenceable attribute imply noundef, atomic operations
8234-
// also implicitly have noundef pointers too
8235-
case Instruction::AtomicCmpXchg:
8236-
if (Handle(cast<AtomicCmpXchgInst>(I)->getPointerOperand()))
8237-
return true;
8238-
break;
8239-
8240-
case Instruction::AtomicRMW:
8241-
if (Handle(cast<AtomicRMWInst>(I)->getPointerOperand()))
8242-
return true;
8243-
break;
8244-
8245-
case Instruction::Call:
8246-
case Instruction::Invoke: {
8247-
const CallBase *CB = cast<CallBase>(I);
8248-
if (CB->isIndirectCall() && Handle(CB->getCalledOperand()))
8249-
return true;
8250-
for (unsigned i = 0; i < CB->arg_size(); ++i)
8251-
if ((CB->paramHasAttr(i, Attribute::NoUndef) ||
8252-
CB->paramHasAttr(i, Attribute::Dereferenceable) ||
8253-
CB->paramHasAttr(i, Attribute::DereferenceableOrNull)) &&
8254-
Handle(CB->getArgOperand(i)))
8255-
return true;
8256-
break;
8257-
}
8258-
case Instruction::Ret:
8259-
if (I->getFunction()->hasRetAttribute(Attribute::NoUndef) &&
8260-
Handle(I->getOperand(0)))
8261-
return true;
8262-
break;
8263-
case Instruction::Switch:
8264-
if (Handle(cast<SwitchInst>(I)->getCondition()))
8265-
return true;
8266-
break;
8267-
case Instruction::Br: {
8268-
auto *BR = cast<BranchInst>(I);
8269-
if (BR->isConditional() && Handle(BR->getCondition()))
8270-
return true;
8271-
break;
8272-
}
8273-
default:
8274-
break;
8275-
}
8276-
8277-
return false;
8278-
}
8279-
8280-
/// Enumerates all operands of \p I that are guaranteed to not be poison.
8281-
template <typename CallableT>
8282-
static bool handleGuaranteedNonPoisonOps(const Instruction *I,
8283-
const CallableT &Handle) {
8284-
if (handleGuaranteedWellDefinedOps(I, Handle))
8285-
return true;
8286-
switch (I->getOpcode()) {
8287-
// Divisors of these operations are allowed to be partially undef.
8288-
case Instruction::UDiv:
8289-
case Instruction::SDiv:
8290-
case Instruction::URem:
8291-
case Instruction::SRem:
8292-
return Handle(I->getOperand(1));
8293-
default:
8294-
return false;
8295-
}
8296-
}
8297-
82988216
bool llvm::mustTriggerUB(const Instruction *I,
82998217
const SmallPtrSetImpl<const Value *> &KnownPoison) {
83008218
return handleGuaranteedNonPoisonOps(

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,46 @@ void State::addInfoForInductions(BasicBlock &BB) {
10831083
}
10841084
}
10851085

1086+
static void addNonPoisonIntrinsicInstFact(
1087+
IntrinsicInst *II,
1088+
function_ref<void(CmpPredicate, Value *, Value *)> AddFact) {
1089+
Intrinsic::ID IID = II->getIntrinsicID();
1090+
switch (IID) {
1091+
case Intrinsic::umin:
1092+
case Intrinsic::umax:
1093+
case Intrinsic::smin:
1094+
case Intrinsic::smax: {
1095+
ICmpInst::Predicate Pred =
1096+
ICmpInst::getNonStrictPredicate(MinMaxIntrinsic::getPredicate(IID));
1097+
AddFact(Pred, II, II->getArgOperand(0));
1098+
AddFact(Pred, II, II->getArgOperand(1));
1099+
break;
1100+
}
1101+
case Intrinsic::abs: {
1102+
if (cast<ConstantInt>(II->getArgOperand(1))->isOne())
1103+
AddFact(CmpInst::ICMP_SGE, II, ConstantInt::get(II->getType(), 0));
1104+
AddFact(CmpInst::ICMP_SGE, II, II->getArgOperand(0));
1105+
break;
1106+
}
1107+
default:
1108+
break;
1109+
}
1110+
}
1111+
1112+
static void addNonPoisonValueFactRecursive(
1113+
Value *V, function_ref<void(CmpPredicate, Value *, Value *)> AddFact) {
1114+
if (auto *II = dyn_cast<IntrinsicInst>(V))
1115+
addNonPoisonIntrinsicInstFact(II, AddFact);
1116+
1117+
if (auto *I = dyn_cast<Instruction>(V)) {
1118+
if (I->getOpcode() == Instruction::PHI)
1119+
return;
1120+
for (auto &Op : I->operands())
1121+
if (impliesPoison(Op, V))
1122+
addNonPoisonValueFactRecursive(Op.get(), AddFact);
1123+
}
1124+
}
1125+
10861126
void State::addInfoFor(BasicBlock &BB) {
10871127
addInfoForInductions(BB);
10881128

@@ -1134,6 +1174,18 @@ void State::addInfoFor(BasicBlock &BB) {
11341174
break;
11351175
}
11361176

1177+
if (GuaranteedToExecute) {
1178+
auto AddFact = [&](CmpPredicate Pred, Value *A, Value *B) {
1179+
WorkList.emplace_back(
1180+
FactOrCheck::getConditionFact(DT.getNode(&BB), Pred, A, B));
1181+
};
1182+
1183+
handleGuaranteedWellDefinedOps(&I, [&](const Value *Op) {
1184+
addNonPoisonValueFactRecursive(const_cast<Value *>(Op), AddFact);
1185+
return false;
1186+
});
1187+
}
1188+
11371189
GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I);
11381190
}
11391191

@@ -1374,32 +1426,6 @@ static void generateReproducer(CmpInst *Cond, Module *M,
13741426
assert(!verifyFunction(*F, &dbgs()));
13751427
}
13761428

1377-
static void addNonPoisonIntrinsicInstFact(
1378-
IntrinsicInst *II,
1379-
function_ref<void(CmpPredicate, Value *, Value *)> AddFact) {
1380-
Intrinsic::ID IID = II->getIntrinsicID();
1381-
switch (IID) {
1382-
case Intrinsic::umin:
1383-
case Intrinsic::umax:
1384-
case Intrinsic::smin:
1385-
case Intrinsic::smax: {
1386-
ICmpInst::Predicate Pred =
1387-
ICmpInst::getNonStrictPredicate(MinMaxIntrinsic::getPredicate(IID));
1388-
AddFact(Pred, II, II->getArgOperand(0));
1389-
AddFact(Pred, II, II->getArgOperand(1));
1390-
break;
1391-
}
1392-
case Intrinsic::abs: {
1393-
if (cast<ConstantInt>(II->getArgOperand(1))->isOne())
1394-
AddFact(CmpInst::ICMP_SGE, II, ConstantInt::get(II->getType(), 0));
1395-
AddFact(CmpInst::ICMP_SGE, II, II->getArgOperand(0));
1396-
break;
1397-
}
1398-
default:
1399-
break;
1400-
}
1401-
}
1402-
14031429
static void
14041430
removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
14051431
SmallVectorImpl<StackEntry> &DFSInStack,
@@ -1428,10 +1454,8 @@ static std::optional<bool> checkCondition(CmpInst::Predicate Pred, Value *A,
14281454
Info.addFact(Pred, A, B, 0, 0, DFSInStack);
14291455
};
14301456

1431-
if (auto *II = dyn_cast<IntrinsicInst>(A))
1432-
addNonPoisonIntrinsicInstFact(II, AddFact);
1433-
if (auto *II = dyn_cast<IntrinsicInst>(B))
1434-
addNonPoisonIntrinsicInstFact(II, AddFact);
1457+
addNonPoisonValueFactRecursive(A, AddFact);
1458+
addNonPoisonValueFactRecursive(B, AddFact);
14351459

14361460
auto R = Info.getConstraintForSolving(Pred, A, B);
14371461
if (R.empty() || !R.isValid(Info)){
@@ -1602,10 +1626,8 @@ static bool checkOrAndOpImpliedByOther(
16021626
Info.addFact(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack);
16031627
};
16041628

1605-
if (auto *II = dyn_cast<IntrinsicInst>(LHS))
1606-
addNonPoisonIntrinsicInstFact(II, AddFact);
1607-
if (auto *II = dyn_cast<IntrinsicInst>(RHS))
1608-
addNonPoisonIntrinsicInstFact(II, AddFact);
1629+
addNonPoisonValueFactRecursive(LHS, AddFact);
1630+
addNonPoisonValueFactRecursive(RHS, AddFact);
16091631
continue;
16101632
}
16111633
if (IsOr ? match(Val, m_LogicalOr(m_Value(LHS), m_Value(RHS)))
@@ -1939,10 +1961,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
19391961
}
19401962
AddFact(Pred, A, B);
19411963
// Now both A and B is guaranteed not to be poison.
1942-
if (auto *II = dyn_cast<IntrinsicInst>(A))
1943-
addNonPoisonIntrinsicInstFact(II, AddFact);
1944-
if (auto *II = dyn_cast<IntrinsicInst>(B))
1945-
addNonPoisonIntrinsicInstFact(II, AddFact);
1964+
addNonPoisonValueFactRecursive(A, AddFact);
1965+
addNonPoisonValueFactRecursive(B, AddFact);
19461966
}
19471967

19481968
if (ReproducerModule && !ReproducerModule->functions().empty()) {

llvm/test/Transforms/ConstraintElimination/abs.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ define i1 @abs_plus_one(i32 %arg) {
2828
; CHECK-SAME: i32 [[ARG:%.*]]) {
2929
; CHECK-NEXT: [[ABS:%.*]] = tail call i32 @llvm.abs.i32(i32 [[ARG]], i1 true)
3030
; CHECK-NEXT: [[ABS_PLUS_ONE:%.*]] = add nsw i32 [[ABS]], 1
31-
; CHECK-NEXT: [[CMP:%.*]] = icmp sge i32 [[ABS_PLUS_ONE]], [[ARG]]
32-
; CHECK-NEXT: ret i1 [[CMP]]
31+
; CHECK-NEXT: ret i1 true
3332
;
3433
%abs = tail call i32 @llvm.abs.i32(i32 %arg, i1 true)
3534
%abs_plus_one = add nsw i32 %abs, 1

llvm/test/Transforms/ConstraintElimination/minmax.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,7 @@ define i1 @umax_uge_ugt_with_add_nuw(i32 %x, i32 %y) {
222222
; CHECK-NEXT: [[CMP:%.*]] = icmp uge i32 [[Y]], [[SUM]]
223223
; CHECK-NEXT: br i1 [[CMP]], label [[IF:%.*]], label [[END:%.*]]
224224
; CHECK: if:
225-
; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i32 [[Y]], [[X]]
226-
; CHECK-NEXT: ret i1 [[CMP2]]
225+
; CHECK-NEXT: ret i1 true
227226
; CHECK: end:
228227
; CHECK-NEXT: ret i1 false
229228
;

llvm/test/Transforms/ConstraintElimination/umin-result-may-be-poison.ll

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ define i1 @umin_not_used(i32 %arg) {
2222
define i1 @umin_poison_is_UB_via_call(i32 %arg) {
2323
; CHECK-LABEL: define i1 @umin_poison_is_UB_via_call(
2424
; CHECK-SAME: i32 [[ARG:%.*]]) {
25-
; CHECK-NEXT: [[ICMP:%.*]] = icmp slt i32 [[ARG]], 0
2625
; CHECK-NEXT: [[SHL:%.*]] = shl nuw nsw i32 [[ARG]], 3
2726
; CHECK-NEXT: [[MIN:%.*]] = call i32 @llvm.umin.i32(i32 [[SHL]], i32 80)
2827
; CHECK-NEXT: call void @noundef(i32 noundef [[MIN]])
2928
; CHECK-NEXT: [[CMP2:%.*]] = shl nuw nsw i32 [[ARG]], 3
30-
; CHECK-NEXT: ret i1 [[ICMP]]
29+
; CHECK-NEXT: ret i1 false
3130
;
3231
%icmp = icmp slt i32 %arg, 0
3332
%shl = shl nuw nsw i32 %arg, 3

0 commit comments

Comments
 (0)