-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[llvm][ConstraintElimination]Insert ConditionFact into loop header in case of monotonic induction variables #112080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
b0da99d
ef9c027
3a5592a
de54a16
4bc49ac
cff3656
bcc8bf4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -186,9 +186,12 @@ struct State { | |
LoopInfo &LI; | ||
ScalarEvolution &SE; | ||
SmallVector<FactOrCheck, 64> WorkList; | ||
bool AddInductionInfoIntoHeader = false; | ||
|
||
State(DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE) | ||
: DT(DT), LI(LI), SE(SE) {} | ||
State(DominatorTree &DT, LoopInfo &LI, ScalarEvolution &SE, | ||
bool AddInductionInfoIntoHeader = false) | ||
: DT(DT), LI(LI), SE(SE), | ||
AddInductionInfoIntoHeader(AddInductionInfoIntoHeader) {} | ||
|
||
/// Process block \p BB and add known facts to work-list. | ||
void addInfoFor(BasicBlock &BB); | ||
|
@@ -197,6 +200,8 @@ struct State { | |
/// controlling the loop header. | ||
void addInfoForInductions(BasicBlock &BB); | ||
|
||
void addConditionFactsIntoLoopHeader(BasicBlock &BB); | ||
|
||
/// Returns true if we can add a known condition from BB to its successor | ||
/// block Succ. | ||
bool canAddSuccessor(BasicBlock &BB, BasicBlock *Succ) const { | ||
|
@@ -900,7 +905,62 @@ static void dumpConstraint(ArrayRef<int64_t> C, | |
} | ||
#endif | ||
|
||
// For monotonically decreasing/increasing variables in the loop, | ||
// this will insert ConditionFact PN >= StartingValue or PN <= StartingValue | ||
// associated with the loop header, where PN is the corresponding PHi node. | ||
void State::addConditionFactsIntoLoopHeader(BasicBlock &BB) { | ||
auto *L = LI.getLoopFor(&BB); | ||
if (!L || L->getHeader() != &BB) | ||
return; | ||
DomTreeNode *DTN = DT.getNode(&BB); | ||
for (PHINode &PN : L->getHeader()->phis()) { | ||
if (PN.getNumIncomingValues() != 2 || PN.getParent() != &BB || | ||
!SE.isSCEVable(PN.getType())) | ||
continue; | ||
auto *AR = dyn_cast_or_null<SCEVAddRecExpr>(SE.getSCEV(&PN)); | ||
BasicBlock *LoopPred = L->getLoopPredecessor(); | ||
if (!AR || AR->getLoop() != L || !LoopPred) | ||
return; | ||
const SCEV *StartSCEV = AR->getStart(); | ||
Value *StartValue = nullptr; | ||
if (auto *C = dyn_cast<SCEVConstant>(StartSCEV)) { | ||
StartValue = C->getValue(); | ||
} else { | ||
StartValue = PN.getIncomingValueForBlock(LoopPred); | ||
assert(SE.getSCEV(StartValue) == StartSCEV && "inconsistent start value"); | ||
} | ||
auto IncUnsigned = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_UGT); | ||
auto IncSigned = SE.getMonotonicPredicateType(AR, CmpInst::ICMP_SGT); | ||
|
||
// Monotonically Increasing | ||
bool MonotonicallyIncreasingUnsigned = | ||
IncUnsigned && *IncUnsigned == ScalarEvolution::MonotonicallyIncreasing; | ||
bool MonotonicallyIncreasingSigned = | ||
IncSigned && *IncSigned == ScalarEvolution::MonotonicallyIncreasing; | ||
if (MonotonicallyIncreasingUnsigned) | ||
WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_UGE, | ||
&PN, StartValue)); | ||
if (MonotonicallyIncreasingSigned) | ||
WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SGE, | ||
&PN, StartValue)); | ||
|
||
// Monotonically Decreasing | ||
bool MonotonicallyDecreasingUnsigned = | ||
IncUnsigned && *IncUnsigned == ScalarEvolution::MonotonicallyDecreasing; | ||
bool MonotonicallyDecreasingSigned = | ||
IncSigned && *IncSigned == ScalarEvolution::MonotonicallyDecreasing; | ||
if (MonotonicallyDecreasingUnsigned) | ||
WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_ULE, | ||
&PN, StartValue)); | ||
if (MonotonicallyDecreasingSigned) | ||
WorkList.push_back(FactOrCheck::getConditionFact(DTN, CmpInst::ICMP_SLE, | ||
&PN, StartValue)); | ||
} | ||
} | ||
|
||
void State::addInfoForInductions(BasicBlock &BB) { | ||
if (AddInductionInfoIntoHeader) | ||
addConditionFactsIntoLoopHeader(BB); | ||
auto *L = LI.getLoopFor(&BB); | ||
if (!L || L->getHeader() != &BB) | ||
return; | ||
|
@@ -1358,7 +1418,7 @@ static std::optional<bool> checkCondition(CmpInst::Predicate Pred, Value *A, | |
LLVM_DEBUG(dbgs() << "Checking " << *CheckInst << "\n"); | ||
|
||
auto R = Info.getConstraintForSolving(Pred, A, B); | ||
if (R.empty() || !R.isValid(Info)){ | ||
if (R.empty() || !R.isValid(Info)) { | ||
LLVM_DEBUG(dbgs() << " failed to decompose condition\n"); | ||
return std::nullopt; | ||
} | ||
|
@@ -1671,6 +1731,16 @@ tryToSimplifyOverflowMath(IntrinsicInst *II, ConstraintInfo &Info, | |
return Changed; | ||
} | ||
|
||
static unsigned int getNumConditionalBranches(Function &F) { | ||
unsigned int NumCondBranches = 0; | ||
for (BasicBlock &BB : F) { | ||
BranchInst *BranchInstr = dyn_cast_or_null<BranchInst>(BB.getTerminator()); | ||
if (BranchInstr && BranchInstr->isConditional()) | ||
NumCondBranches++; | ||
} | ||
return NumCondBranches; | ||
} | ||
|
||
static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, | ||
ScalarEvolution &SE, | ||
OptimizationRemarkEmitter &ORE) { | ||
|
@@ -1680,7 +1750,9 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, | |
for (Value &Arg : F.args()) | ||
FunctionArgs.push_back(&Arg); | ||
ConstraintInfo Info(F.getDataLayout(), FunctionArgs); | ||
State S(DT, LI, SE); | ||
unsigned int NumCondBranches = getNumConditionalBranches(F); | ||
State S(DT, LI, SE, | ||
/* AddInductionInfoIntoHeader= */ NumCondBranches < MaxRows / 5); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was not aware of the existing Doing a whole walk over the function to count conditional branches feels somewhat heavy and we potentially get an amount of facts in the number of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I added this condition is to avoid regression in some edge cases (see analysis by @dtcxzyw). If we add extra constrains in loop headers and reach MaxRows limit, some constraints are dropped that are not otherwise. I am happy to remove it if we are OK with those regressions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It just feels like a lot of trouble (or rather a lot of memory read) to go through the whole function just to count the number of conditional branches... So I am wondering if there is ways to limit this without having an extra pass over the whole function... |
||
std::unique_ptr<Module> ReproducerModule( | ||
DumpReproducers ? new Module(F.getName(), F.getContext()) : nullptr); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 | ||
; RUN: opt < %s -passes=constraint-elimination -S | FileCheck %s | ||
|
||
define i32 @foo() { | ||
; CHECK-LABEL: define i32 @foo() { | ||
; CHECK-NEXT: init: | ||
; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL:.*]] | ||
; CHECK: outer.loop.control: | ||
; CHECK-NEXT: [[X_0:%.*]] = phi i32 [ 0, [[INIT:%.*]] ], [ [[X_OUTER:%.*]], %[[OUTER_LOOP_INC:.*]] ] | ||
; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 [[X_0]], 10 | ||
; CHECK-NEXT: br i1 [[TMP0]], label %[[INNER_LOOP_CONTROL:.*]], label %[[EXIT:.*]] | ||
; CHECK: inner.loop.control: | ||
; CHECK-NEXT: [[X_1:%.*]] = phi i32 [ [[X_0]], %[[OUTER_LOOP_CONTROL]] ], [ [[X_INNER:%.*]], %[[INNER_LOOP_BODY:.*]] ] | ||
; CHECK-NEXT: br i1 false, label %[[INNER_LOOP_BODY]], label %[[OUTER_LOOP_INC]] | ||
; CHECK: inner.loop.body: | ||
; CHECK-NEXT: [[X_INNER]] = add nsw i32 [[X_1]], -1 | ||
; CHECK-NEXT: br label %[[INNER_LOOP_CONTROL]] | ||
; CHECK: outer.loop.inc: | ||
; CHECK-NEXT: [[X_OUTER]] = add nsw i32 [[X_1]], 2 | ||
; CHECK-NEXT: br label %[[OUTER_LOOP_CONTROL]] | ||
; CHECK: exit: | ||
; CHECK-NEXT: ret i32 [[X_0]] | ||
; | ||
init: | ||
br label %outer.loop.control | ||
|
||
outer.loop.control: ; preds = %init, %outer.loop.inc | ||
%x.0 = phi i32 [ 0, %init ], [ %x.outer, %outer.loop.inc ] | ||
%0 = icmp slt i32 %x.0, 10 | ||
br i1 %0, label %inner.loop.control, label %exit | ||
|
||
inner.loop.control: ; preds = %outer.loop.control, %inner.loop.body | ||
%x.1 = phi i32 [ %x.0, %outer.loop.control ], [ %x.inner, %inner.loop.body ] | ||
%1 = icmp sgt i32 %x.1, 20 | ||
br i1 %1, label %inner.loop.body, label %outer.loop.inc | ||
|
||
inner.loop.body: ; preds = %inner.loop.control | ||
%x.inner = add nsw i32 %x.1, -1 | ||
br label %inner.loop.control | ||
|
||
outer.loop.inc: ; preds = %inner.loop.control | ||
%x.outer = add nsw i32 %x.1, 2 | ||
br label %outer.loop.control | ||
|
||
exit: ; preds = %1 | ||
ret i32 %x.0 | ||
} |
Uh oh!
There was an error while loading. Please reload this page.