Skip to content

Commit 86fb1b9

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

File tree

6 files changed

+142
-126
lines changed

6 files changed

+142
-126
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

+82
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

-82
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

+57-38
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "llvm/Analysis/ScalarEvolution.h"
2424
#include "llvm/Analysis/ScalarEvolutionExpressions.h"
2525
#include "llvm/Analysis/ValueTracking.h"
26+
#include "llvm/IR/CmpPredicate.h"
2627
#include "llvm/IR/DataLayout.h"
2728
#include "llvm/IR/Dominators.h"
2829
#include "llvm/IR/Function.h"
@@ -1083,6 +1084,44 @@ void State::addInfoForInductions(BasicBlock &BB) {
10831084
}
10841085
}
10851086

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

@@ -1134,6 +1173,18 @@ void State::addInfoFor(BasicBlock &BB) {
11341173
break;
11351174
}
11361175

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

@@ -1374,32 +1425,6 @@ static void generateReproducer(CmpInst *Cond, Module *M,
13741425
assert(!verifyFunction(*F, &dbgs()));
13751426
}
13761427

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-
14031428
static void
14041429
removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
14051430
SmallVectorImpl<StackEntry> &DFSInStack,
@@ -1428,10 +1453,8 @@ static std::optional<bool> checkCondition(CmpInst::Predicate Pred, Value *A,
14281453
Info.addFact(Pred, A, B, 0, 0, DFSInStack);
14291454
};
14301455

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

14361459
auto R = Info.getConstraintForSolving(Pred, A, B);
14371460
if (R.empty() || !R.isValid(Info)){
@@ -1602,10 +1625,8 @@ static bool checkOrAndOpImpliedByOther(
16021625
Info.addFact(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack);
16031626
};
16041627

1605-
if (auto *II = dyn_cast<IntrinsicInst>(LHS))
1606-
addNonPoisonIntrinsicInstFact(II, AddFact);
1607-
if (auto *II = dyn_cast<IntrinsicInst>(RHS))
1608-
addNonPoisonIntrinsicInstFact(II, AddFact);
1628+
addNonPoisonValueFactRecursive(LHS, AddFact);
1629+
addNonPoisonValueFactRecursive(RHS, AddFact);
16091630
continue;
16101631
}
16111632
if (IsOr ? match(Val, m_LogicalOr(m_Value(LHS), m_Value(RHS)))
@@ -1939,10 +1960,8 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
19391960
}
19401961
AddFact(Pred, A, B);
19411962
// 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);
1963+
addNonPoisonValueFactRecursive(A, AddFact);
1964+
addNonPoisonValueFactRecursive(B, AddFact);
19461965
}
19471966

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

llvm/test/Transforms/ConstraintElimination/abs.ll

+1-2
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

+1-2
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

+1-2
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)