Skip to content

Commit

Permalink
[ConstraintElim] Store conditional facts as (Predicate, Op0, Op1).
Browse files Browse the repository at this point in the history
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
  • Loading branch information
fhahn committed Aug 30, 2023
1 parent 5b3f41c commit 4a5bcbd
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 59 deletions.
137 changes: 82 additions & 55 deletions llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -101,37 +111,45 @@ 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) {
return FactOrCheck(EntryTy::InstFact, DTN, Inst);
}

static FactOrCheck getInstFact(DomTreeNode *DTN, Instruction *Inst,
bool Not = false) {
return FactOrCheck(EntryTy::InstFact, DTN, Inst, Not);
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) {
return FactOrCheck(DTN, U);
}

static FactOrCheck getCheck(DomTreeNode *DTN, CallInst *CI) {
return FactOrCheck(EntryTy::InstCheck, DTN, CI, false);
return FactOrCheck(EntryTy::InstCheck, DTN, CI);
}

bool isCheck() const {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -346,17 +354,17 @@ struct Decomposition {
} // namespace

static Decomposition decompose(Value *V,
SmallVectorImpl<PreconditionTy> &Preconditions,
SmallVectorImpl<ConditionTy> &Preconditions,
bool IsSigned, const DataLayout &DL);

static bool canUseSExt(ConstantInt *CI) {
const APInt &Val = CI->getValue();
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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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);
});
}
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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)))) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand All @@ -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);
Expand All @@ -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()) {
Expand Down
8 changes: 4 additions & 4 deletions llvm/test/Transforms/ConstraintElimination/debug.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down

0 comments on commit 4a5bcbd

Please sign in to comment.