Skip to content

Commit 9e43ada

Browse files
committed
ConstraintElimination: use dry-run to upper-bound cols
1 parent 1a2b1b0 commit 9e43ada

File tree

2 files changed

+165
-27
lines changed

2 files changed

+165
-27
lines changed

llvm/include/llvm/Analysis/ConstraintSystem.h

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,10 @@ class ConstraintSystem {
7171
ConstraintSystem() : Constraints(6), View(Constraints) {}
7272

7373
// This constructor is used by ConstraintElimination, inside ConstraintInfo.
74-
// Unfortunately, due to calls to addFact, that adds local variables, it is
75-
// impossible to know how many local variables there are in advance.
76-
// ConstraintElimination has a fixed upper-bound on the number of columns,
77-
// configurable as a cl::opt, so use that number, and don't add the constraint
78-
// if it exceeds that number.
79-
ConstraintSystem(ArrayRef<Value *> FunctionArgs, size_t NCols)
74+
// ConstraintElimination upper-bounds the number of columns using a dry run.
75+
ConstraintSystem(ArrayRef<Value *> FunctionArgs, size_t NRows, size_t NCols)
8076
: Constraints(NCols), View(Constraints) {
77+
Constraints.reserve(NRows);
8178
NumVariables += FunctionArgs.size();
8279
for (auto *Arg : FunctionArgs) {
8380
Value2Index.insert({Arg, Value2Index.size() + 1});
@@ -109,6 +106,11 @@ class ConstraintSystem {
109106
continue;
110107
NewRow.emplace_back(C, Idx);
111108
}
109+
110+
// There is no correctness issue if we don't add a constraint.
111+
if (NewRow.size() > Constraints.getNumCols())
112+
return false;
113+
112114
if (View.empty())
113115
NumVariables = R.size();
114116

@@ -127,11 +129,6 @@ class ConstraintSystem {
127129
if (all_of(ArrayRef(R).drop_front(1), [](int64_t C) { return C == 0; }))
128130
return false;
129131

130-
// There is no correctness issue if we don't add a constraint, for whatever
131-
// reason.
132-
if (R.size() > Constraints.getNumCols())
133-
return false;
134-
135132
NumVariables = std::max(R.size(), NumVariables);
136133
return addVariableRow(R);
137134
}

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 157 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "llvm/Transforms/Utils/Cloning.h"
4141
#include "llvm/Transforms/Utils/ValueMapper.h"
4242

43+
#include <cstdint>
4344
#include <optional>
4445
#include <string>
4546

@@ -57,7 +58,7 @@ static cl::opt<unsigned>
5758
cl::desc("Maximum number of rows to keep in constraint system"));
5859

5960
static cl::opt<unsigned> MaxColumns(
60-
"constraint-elimination-max-cols", cl::init(16), cl::Hidden,
61+
"constraint-elimination-max-cols", cl::init(64), cl::Hidden,
6162
cl::desc("Maximum number of columns to keep in constraint system"));
6263

6364
static cl::opt<bool> DumpReproducers(
@@ -269,16 +270,16 @@ struct ConstraintTy {
269270
/// based on signed-ness, certain conditions can be transferred between the two
270271
/// systems.
271272
class ConstraintInfo {
272-
273273
ConstraintSystem UnsignedCS;
274274
ConstraintSystem SignedCS;
275275

276276
const DataLayout &DL;
277277

278278
public:
279-
ConstraintInfo(const DataLayout &DL, ArrayRef<Value *> FunctionArgs)
280-
: UnsignedCS(FunctionArgs, MaxColumns),
281-
SignedCS(FunctionArgs, MaxColumns), DL(DL) {
279+
ConstraintInfo(const DataLayout &DL, ArrayRef<Value *> FunctionArgs,
280+
unsigned MaxRows, unsigned MaxColumns)
281+
: UnsignedCS(FunctionArgs, MaxRows, MaxColumns),
282+
SignedCS(FunctionArgs, MaxRows, MaxColumns), DL(DL) {
282283
auto &Value2Index = getValue2Index(false);
283284
// Add Arg > -1 constraints to unsigned system for all function arguments.
284285
for (Value *Arg : FunctionArgs) {
@@ -307,6 +308,7 @@ class ConstraintInfo {
307308
void popLastNVariables(bool Signed, unsigned N) {
308309
getCS(Signed).popLastNVariables(N);
309310
}
311+
const DataLayout &getDataLayout() const { return DL; }
310312

311313
bool doesHold(CmpInst::Predicate Pred, Value *A, Value *B) const;
312314

@@ -1495,7 +1497,7 @@ removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
14951497
/// Check if either the first condition of an AND or OR is implied by the
14961498
/// (negated in case of OR) second condition or vice versa.
14971499
static bool checkOrAndOpImpliedByOther(
1498-
FactOrCheck &CB, ConstraintInfo &Info, Module *ReproducerModule,
1500+
const FactOrCheck &CB, ConstraintInfo &Info, Module *ReproducerModule,
14991501
SmallVectorImpl<ReproducerEntry> &ReproducerCondStack,
15001502
SmallVectorImpl<StackEntry> &DFSInStack) {
15011503

@@ -1675,21 +1677,85 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info,
16751677
return Changed;
16761678
}
16771679

1678-
static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
1679-
ScalarEvolution &SE,
1680-
OptimizationRemarkEmitter &ORE) {
1681-
bool Changed = false;
1680+
/// Performs a dry run of AddFact, computing a conservative estimate of the
1681+
/// number of new variables introduced.
1682+
static void dryRunAddFact(CmpInst::Predicate Pred, Value *A, Value *B,
1683+
const ConstraintInfo &Info, unsigned &EstimatedRows,
1684+
unsigned &EstimatedColumns) {
1685+
auto UpdateEstimate = [&Info, &EstimatedRows, &EstimatedColumns](
1686+
CmpInst::Predicate Pred, Value *A, Value *B) {
1687+
SmallVector<Value *> NewVars;
1688+
auto R = Info.getConstraint(Pred, A, B, NewVars);
1689+
1690+
// We offset it by 1 due to logic in addFact.
1691+
unsigned NewEstimate =
1692+
count_if(R.Coefficients, [](int64_t C) { return C != 0; }) + 1;
1693+
1694+
++EstimatedRows;
1695+
EstimatedColumns = std::max(EstimatedColumns, NewEstimate);
1696+
};
1697+
1698+
UpdateEstimate(Pred, A, B);
1699+
1700+
// What follows is a dry-run of transferToOtherSystem.
1701+
auto IsKnownNonNegative = [&Info](Value *V) {
1702+
return Info.doesHold(CmpInst::ICMP_SGE, V,
1703+
ConstantInt::get(V->getType(), 0)) ||
1704+
isKnownNonNegative(V, Info.getDataLayout(),
1705+
/*Depth=*/MaxAnalysisRecursionDepth - 1);
1706+
};
1707+
1708+
if (!A->getType()->isIntegerTy())
1709+
return;
1710+
1711+
switch (Pred) {
1712+
default:
1713+
break;
1714+
case CmpInst::ICMP_ULT:
1715+
case CmpInst::ICMP_ULE:
1716+
if (IsKnownNonNegative(B)) {
1717+
UpdateEstimate(CmpInst::ICMP_SGE, A, ConstantInt::get(B->getType(), 0));
1718+
UpdateEstimate(CmpInst::getSignedPredicate(Pred), A, B);
1719+
}
1720+
break;
1721+
case CmpInst::ICMP_UGE:
1722+
case CmpInst::ICMP_UGT:
1723+
if (IsKnownNonNegative(A)) {
1724+
UpdateEstimate(CmpInst::ICMP_SGE, B, ConstantInt::get(B->getType(), 0));
1725+
UpdateEstimate(CmpInst::getSignedPredicate(Pred), A, B);
1726+
}
1727+
break;
1728+
case CmpInst::ICMP_SLT:
1729+
if (IsKnownNonNegative(A))
1730+
UpdateEstimate(CmpInst::ICMP_ULT, A, B);
1731+
break;
1732+
case CmpInst::ICMP_SGT:
1733+
if (Info.doesHold(CmpInst::ICMP_SGE, B, ConstantInt::get(B->getType(), -1)))
1734+
UpdateEstimate(CmpInst::ICMP_UGE, A, ConstantInt::get(B->getType(), 0));
1735+
if (IsKnownNonNegative(B))
1736+
UpdateEstimate(CmpInst::ICMP_UGT, A, B);
1737+
break;
1738+
case CmpInst::ICMP_SGE:
1739+
if (IsKnownNonNegative(B))
1740+
UpdateEstimate(CmpInst::ICMP_UGE, A, B);
1741+
break;
1742+
}
1743+
}
1744+
1745+
/// Performs a dry run of the transform, computing a conservative estimate of
1746+
/// the total number of columns we need in the underlying storage.
1747+
static std::tuple<State, unsigned, unsigned>
1748+
dryRun(Function &F, DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE) {
16821749
DT.updateDFSNumbers();
16831750
SmallVector<Value *> FunctionArgs;
16841751
for (Value &Arg : F.args())
16851752
FunctionArgs.push_back(&Arg);
1686-
ConstraintInfo Info(F.getDataLayout(), FunctionArgs);
16871753
State S(DT, LI, SE);
1688-
std::unique_ptr<Module> ReproducerModule(
1689-
DumpReproducers ? new Module(F.getName(), F.getContext()) : nullptr);
1754+
unsigned EstimatedColumns = FunctionArgs.size() + 1;
1755+
unsigned EstimatedRows = 1;
1756+
ConstraintInfo Info(F.getDataLayout(), FunctionArgs, EstimatedRows,
1757+
EstimatedColumns);
16901758

1691-
// First, collect conditions implied by branches and blocks with their
1692-
// Dominator DFS in and out numbers.
16931759
for (BasicBlock &BB : F) {
16941760
if (!DT.getNode(&BB))
16951761
continue;
@@ -1729,12 +1795,87 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
17291795
return A.NumIn < B.NumIn;
17301796
});
17311797

1798+
for (const FactOrCheck &CB : S.WorkList) {
1799+
ICmpInst::Predicate Pred;
1800+
Value *A, *B;
1801+
if (CB.isCheck()) {
1802+
// What follows is a dry-run of checkOrAndOpImpliedByOther, without
1803+
// assuming that instructions have been simplified, as they would have
1804+
// during the course of normal operation.
1805+
auto *ContextInst = CB.getContextInst();
1806+
if (auto *Cmp =
1807+
dyn_cast_or_null<ICmpInst>(CB.getInstructionToSimplify())) {
1808+
unsigned OtherOpIdx = ContextInst->getOperand(0) == Cmp ? 1 : 0;
1809+
if (match(ContextInst, m_LogicalOp()) &&
1810+
match(ContextInst->getOperand(OtherOpIdx),
1811+
m_ICmp(Pred, m_Value(A), m_Value(B)))) {
1812+
if (match(ContextInst, m_LogicalOr()))
1813+
Pred = CmpInst::getInversePredicate(Pred);
1814+
dryRunAddFact(Pred, A, B, Info, EstimatedRows, EstimatedColumns);
1815+
}
1816+
}
1817+
continue;
1818+
}
1819+
if (!CB.isConditionFact()) {
1820+
Value *X;
1821+
if (match(CB.Inst, m_Intrinsic<Intrinsic::abs>(m_Value(X)))) {
1822+
if (cast<ConstantInt>(CB.Inst->getOperand(1))->isOne())
1823+
dryRunAddFact(CmpInst::ICMP_SGE, CB.Inst,
1824+
ConstantInt::get(CB.Inst->getType(), 0), Info,
1825+
EstimatedRows, EstimatedColumns);
1826+
dryRunAddFact(CmpInst::ICMP_SGE, CB.Inst, X, Info, EstimatedRows,
1827+
EstimatedColumns);
1828+
continue;
1829+
}
1830+
1831+
if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(CB.Inst)) {
1832+
Pred = ICmpInst::getNonStrictPredicate(MinMax->getPredicate());
1833+
dryRunAddFact(Pred, MinMax, MinMax->getLHS(), Info, EstimatedRows,
1834+
EstimatedColumns);
1835+
dryRunAddFact(Pred, MinMax, MinMax->getRHS(), Info, EstimatedRows,
1836+
EstimatedColumns);
1837+
continue;
1838+
}
1839+
}
1840+
1841+
if (CB.isConditionFact()) {
1842+
Pred = CB.Cond.Pred;
1843+
A = CB.Cond.Op0;
1844+
B = CB.Cond.Op1;
1845+
} else {
1846+
bool Matched = match(CB.Inst, m_Intrinsic<Intrinsic::assume>(
1847+
m_ICmp(Pred, m_Value(A), m_Value(B))));
1848+
(void)Matched;
1849+
assert(Matched && "Must have an assume intrinsic with a icmp operand");
1850+
}
1851+
dryRunAddFact(Pred, A, B, Info, EstimatedRows, EstimatedColumns);
1852+
}
1853+
return {S, EstimatedRows, EstimatedColumns};
1854+
}
1855+
1856+
static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
1857+
ScalarEvolution &SE,
1858+
OptimizationRemarkEmitter &ORE) {
1859+
bool Changed = false;
1860+
DT.updateDFSNumbers();
1861+
SmallVector<Value *> FunctionArgs;
1862+
for (Value &Arg : F.args())
1863+
FunctionArgs.push_back(&Arg);
1864+
const auto &[S, EstimatedRows, EstimatedColumns] = dryRun(F, DT, LI, SE);
1865+
if (EstimatedRows > MaxRows || EstimatedColumns > MaxColumns)
1866+
return false;
1867+
1868+
ConstraintInfo Info(F.getDataLayout(), FunctionArgs, EstimatedRows,
1869+
EstimatedColumns);
1870+
std::unique_ptr<Module> ReproducerModule(
1871+
DumpReproducers ? new Module(F.getName(), F.getContext()) : nullptr);
1872+
17321873
SmallVector<Instruction *> ToRemove;
17331874

17341875
// Finally, process ordered worklist and eliminate implied conditions.
17351876
SmallVector<StackEntry, 16> DFSInStack;
17361877
SmallVector<ReproducerEntry> ReproducerCondStack;
1737-
for (FactOrCheck &CB : S.WorkList) {
1878+
for (const FactOrCheck &CB : S.WorkList) {
17381879
// First, pop entries from the stack that are out-of-scope for CB. Remove
17391880
// the corresponding entry from the constraint system.
17401881
while (!DFSInStack.empty()) {

0 commit comments

Comments
 (0)