[ConstraintElim] Store conditional facts as (Predicate, Op0, Op1).

This allows to add facts even if no corresponding ICmp instruction
exists in the IR.

Reviewed By: nikic

Differential Revision: https://reviews.llvm.org/D158837
This commit is contained in:
Florian Hahn 2023-08-30 10:54:28 +01:00
parent 5b3f41c55d
commit 4a5bcbd560
No known key found for this signature in database
GPG Key ID: 9E54DEA47A8F4434
2 changed files with 86 additions and 59 deletions

View File

@ -83,6 +83,16 @@ static Instruction *getContextInstForUse(Use &U) {
}
namespace {
/// Struct to express a condition of the form %Op0 Pred %Op1.
struct ConditionTy {
CmpInst::Predicate Pred;
Value *Op0;
Value *Op1;
ConditionTy(CmpInst::Predicate Pred, Value *Op0, Value *Op1)
: Pred(Pred), Op0(Op0), Op1(Op1) {}
};
/// Represents either
/// * a condition that holds on entry to a block (=condition fact)
/// * an assume (=assume fact)
@ -101,29 +111,37 @@ struct FactOrCheck {
union {
Instruction *Inst;
Use *U;
ConditionTy Cond;
};
unsigned NumIn;
unsigned NumOut;
EntryTy Ty;
bool Not;
FactOrCheck(EntryTy Ty, DomTreeNode *DTN, Instruction *Inst, bool Not)
FactOrCheck(EntryTy Ty, DomTreeNode *DTN, Instruction *Inst)
: Inst(Inst), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
Ty(Ty), Not(Not) {}
Ty(Ty) {}
FactOrCheck(DomTreeNode *DTN, Use *U)
: U(U), NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()),
Ty(EntryTy::UseCheck), Not(false) {}
Ty(EntryTy::UseCheck) {}
static FactOrCheck getConditionFact(DomTreeNode *DTN, CmpInst *Inst,
bool Not = false) {
return FactOrCheck(EntryTy::ConditionFact, DTN, Inst, Not);
FactOrCheck(DomTreeNode *DTN, CmpInst::Predicate Pred, Value *Op0, Value *Op1)
: Cond(Pred, Op0, Op1), NumIn(DTN->getDFSNumIn()),
NumOut(DTN->getDFSNumOut()), Ty(EntryTy::ConditionFact) {}
static FactOrCheck getConditionFact(DomTreeNode *DTN, CmpInst::Predicate Pred,
Value *Op0, Value *Op1) {
return FactOrCheck(DTN, Pred, Op0, Op1);
}
static FactOrCheck getInstFact(DomTreeNode *DTN, Instruction *Inst,
bool Not = false) {
return FactOrCheck(EntryTy::InstFact, DTN, Inst, Not);
static FactOrCheck getInstFact(DomTreeNode *DTN, Instruction *Inst) {
return FactOrCheck(EntryTy::InstFact, DTN, Inst);
}
static FactOrCheck getFact(DomTreeNode *DTN, CmpInst::Predicate Pred,
Value *Op0, Value *Op1) {
return FactOrCheck(DTN, Pred, Op0, Op1);
}
static FactOrCheck getCheck(DomTreeNode *DTN, Use *U) {
@ -131,7 +149,7 @@ struct FactOrCheck {
}
static FactOrCheck getCheck(DomTreeNode *DTN, CallInst *CI) {
return FactOrCheck(EntryTy::InstCheck, DTN, CI, false);
return FactOrCheck(EntryTy::InstCheck, DTN, CI);
}
bool isCheck() const {
@ -188,19 +206,9 @@ struct StackEntry {
ValuesToRelease(ValuesToRelease) {}
};
/// Struct to express a pre-condition of the form %Op0 Pred %Op1.
struct PreconditionTy {
CmpInst::Predicate Pred;
Value *Op0;
Value *Op1;
PreconditionTy(CmpInst::Predicate Pred, Value *Op0, Value *Op1)
: Pred(Pred), Op0(Op0), Op1(Op1) {}
};
struct ConstraintTy {
SmallVector<int64_t, 8> Coefficients;
SmallVector<PreconditionTy, 2> Preconditions;
SmallVector<ConditionTy, 2> Preconditions;
SmallVector<SmallVector<int64_t, 8>> ExtraInfo;
@ -346,7 +354,7 @@ struct Decomposition {
} // namespace
static Decomposition decompose(Value *V,
SmallVectorImpl<PreconditionTy> &Preconditions,
SmallVectorImpl<ConditionTy> &Preconditions,
bool IsSigned, const DataLayout &DL);
static bool canUseSExt(ConstantInt *CI) {
@ -354,9 +362,9 @@ static bool canUseSExt(ConstantInt *CI) {
return Val.sgt(MinSignedConstraintValue) && Val.slt(MaxConstraintValue);
}
static Decomposition
decomposeGEP(GEPOperator &GEP, SmallVectorImpl<PreconditionTy> &Preconditions,
bool IsSigned, const DataLayout &DL) {
static Decomposition decomposeGEP(GEPOperator &GEP,
SmallVectorImpl<ConditionTy> &Preconditions,
bool IsSigned, const DataLayout &DL) {
// Do not reason about pointers where the index size is larger than 64 bits,
// as the coefficients used to encode constraints are 64 bit integers.
if (DL.getIndexTypeSizeInBits(GEP.getPointerOperand()->getType()) > 64)
@ -417,7 +425,7 @@ decomposeGEP(GEPOperator &GEP, SmallVectorImpl<PreconditionTy> &Preconditions,
// Variable } where Coefficient * Variable. The sum of the constant offset and
// pairs equals \p V.
static Decomposition decompose(Value *V,
SmallVectorImpl<PreconditionTy> &Preconditions,
SmallVectorImpl<ConditionTy> &Preconditions,
bool IsSigned, const DataLayout &DL) {
auto MergeResults = [&Preconditions, IsSigned, &DL](Value *A, Value *B,
@ -560,7 +568,7 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
Pred != CmpInst::ICMP_SLE && Pred != CmpInst::ICMP_SLT)
return {};
SmallVector<PreconditionTy, 4> Preconditions;
SmallVector<ConditionTy, 4> Preconditions;
bool IsSigned = CmpInst::isSigned(Pred);
auto &Value2Index = getValue2Index(IsSigned);
auto ADec = decompose(Op0->stripPointerCastsSameRepresentation(),
@ -670,7 +678,7 @@ ConstraintTy ConstraintInfo::getConstraintForSolving(CmpInst::Predicate Pred,
bool ConstraintTy::isValid(const ConstraintInfo &Info) const {
return Coefficients.size() > 0 &&
all_of(Preconditions, [&Info](const PreconditionTy &C) {
all_of(Preconditions, [&Info](const ConditionTy &C) {
return Info.doesHold(C.Pred, C.Op0, C.Op1);
});
}
@ -805,15 +813,16 @@ void State::addInfoFor(BasicBlock &BB) {
continue;
}
Value *Cond;
Value *A, *B;
CmpInst::Predicate Pred;
// For now, just handle assumes with a single compare as condition.
if (match(&I, m_Intrinsic<Intrinsic::assume>(m_Value(Cond))) &&
isa<ICmpInst>(Cond)) {
if (match(&I, m_Intrinsic<Intrinsic::assume>(
m_ICmp(Pred, m_Value(A), m_Value(B))))) {
if (GuaranteedToExecute) {
// The assume is guaranteed to execute when BB is entered, hence Cond
// holds on entry to BB.
WorkList.emplace_back(FactOrCheck::getConditionFact(
DT.getNode(I.getParent()), cast<CmpInst>(Cond)));
DT.getNode(I.getParent()), Pred, A, B));
} else {
WorkList.emplace_back(
FactOrCheck::getInstFact(DT.getNode(I.getParent()), &I));
@ -853,8 +862,11 @@ void State::addInfoFor(BasicBlock &BB) {
while (!CondWorkList.empty()) {
Value *Cur = CondWorkList.pop_back_val();
if (auto *Cmp = dyn_cast<ICmpInst>(Cur)) {
WorkList.emplace_back(
FactOrCheck::getConditionFact(DT.getNode(Successor), Cmp, IsOr));
WorkList.emplace_back(FactOrCheck::getConditionFact(
DT.getNode(Successor),
IsOr ? CmpInst::getInversePredicate(Cmp->getPredicate())
: Cmp->getPredicate(),
Cmp->getOperand(0), Cmp->getOperand(1)));
continue;
}
if (IsOr && match(Cur, m_LogicalOr(m_Value(Op0), m_Value(Op1)))) {
@ -876,11 +888,14 @@ void State::addInfoFor(BasicBlock &BB) {
if (!CmpI)
return;
if (canAddSuccessor(BB, Br->getSuccessor(0)))
WorkList.emplace_back(
FactOrCheck::getConditionFact(DT.getNode(Br->getSuccessor(0)), CmpI));
WorkList.emplace_back(FactOrCheck::getConditionFact(
DT.getNode(Br->getSuccessor(0)), CmpI->getPredicate(),
CmpI->getOperand(0), CmpI->getOperand(1)));
if (canAddSuccessor(BB, Br->getSuccessor(1)))
WorkList.emplace_back(FactOrCheck::getConditionFact(
DT.getNode(Br->getSuccessor(1)), CmpI, true));
DT.getNode(Br->getSuccessor(1)),
CmpInst::getInversePredicate(CmpI->getPredicate()), CmpI->getOperand(0),
CmpI->getOperand(1)));
}
namespace {
@ -1312,8 +1327,9 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
// transfer logic.
stable_sort(S.WorkList, [](const FactOrCheck &A, const FactOrCheck &B) {
auto HasNoConstOp = [](const FactOrCheck &B) {
return !isa<ConstantInt>(B.Inst->getOperand(0)) &&
!isa<ConstantInt>(B.Inst->getOperand(1));
Value *V0 = B.isConditionFact() ? B.Cond.Op0 : B.Inst->getOperand(0);
Value *V1 = B.isConditionFact() ? B.Cond.Op1 : B.Inst->getOperand(1);
return !isa<ConstantInt>(V0) && !isa<ConstantInt>(V1);
};
// If both entries have the same In numbers, conditional facts come first.
// Otherwise use the relative order in the basic block.
@ -1386,7 +1402,6 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
continue;
}
LLVM_DEBUG(dbgs() << "fact to add to the system: " << *CB.Inst << "\n");
auto AddFact = [&](CmpInst::Predicate Pred, Value *A, Value *B) {
if (Info.getCS(CmpInst::isSigned(Pred)).size() > MaxRows) {
LLVM_DEBUG(
@ -1395,6 +1410,14 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
return;
}
LLVM_DEBUG({
dbgs() << "Processing fact to add to the system: " << Pred << " ";
A->printAsOperand(dbgs());
dbgs() << ", ";
B->printAsOperand(dbgs(), false);
dbgs() << "\n";
});
Info.addFact(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack);
if (ReproducerModule && DFSInStack.size() > ReproducerCondStack.size())
ReproducerCondStack.emplace_back(Pred, A, B);
@ -1413,23 +1436,27 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT,
};
ICmpInst::Predicate Pred;
if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(CB.Inst)) {
Pred = ICmpInst::getNonStrictPredicate(MinMax->getPredicate());
AddFact(Pred, MinMax, MinMax->getLHS());
AddFact(Pred, MinMax, MinMax->getRHS());
continue;
if (!CB.isConditionFact()) {
if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(CB.Inst)) {
Pred = ICmpInst::getNonStrictPredicate(MinMax->getPredicate());
AddFact(Pred, MinMax, MinMax->getLHS());
AddFact(Pred, MinMax, MinMax->getRHS());
continue;
}
}
Value *A, *B;
Value *Cmp = CB.Inst;
match(Cmp, m_Intrinsic<Intrinsic::assume>(m_Value(Cmp)));
if (match(Cmp, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
// Use the inverse predicate if required.
if (CB.Not)
Pred = CmpInst::getInversePredicate(Pred);
AddFact(Pred, A, B);
Value *A = nullptr, *B = nullptr;
if (CB.isConditionFact()) {
Pred = CB.Cond.Pred;
A = CB.Cond.Op0;
B = CB.Cond.Op1;
} else {
bool Matched = match(CB.Inst, m_Intrinsic<Intrinsic::assume>(
m_ICmp(Pred, m_Value(A), m_Value(B))));
(void)Matched;
assert(Matched && "Must have an assume intrinsic with a icmp operand");
}
AddFact(Pred, A, B);
}
if (ReproducerModule && !ReproducerModule->functions().empty()) {

View File

@ -3,11 +3,11 @@
; REQUIRES: asserts
define i1 @test_and_ule(i4 %x, i4 %y, i4 %z) {
; CHECK: Processing fact to add to the system: %c.1 = icmp ule i4 %x, %y
; CHECK: Processing fact to add to the system: ule i4 %x, %y
; CHECK-NEXT: Adding 'ule %x, %y'
; CHECK-NEXT: constraint: %x + -1 * %y <= 0
; CHECK: Processing fact to add to the system: %c.2 = icmp ule i4 %y, %z
; CHECK: Processing fact to add to the system: ule i4 %y, %z
; CHECK-NEXT: Adding 'ule %y, %z'
; CHECK-NEXT: constraint: %y + -1 * %z <= 0
@ -33,11 +33,11 @@ exit:
}
define i1 @test_and_ugt(i4 %x, i4 %y, i4 %z) {
; CHECK: Processing fact to add to the system: %c.1 = icmp ugt i4 %x, %y
; CHECK: Processing fact to add to the system: ugt i4 %x, %y
; CHECK-NEXT: Adding 'ugt %x, %y'
; CHECK-NEXT: constraint: -1 * %x + %y <= -1
; CHECK: Processing fact to add to the system: %c.2 = icmp ugt i4 %y, %z
; CHECK: Processing fact to add to the system: ugt i4 %y, %z
; CHECK-NEXT: Adding 'ugt %y, %z'
; CHECK-NEXT: constraint: -1 * %y + %z <= -1