Skip to content

Commit 545cdca

Browse files
authored
[SCCP] Improve worklist management (#146321)
SCCP currently stores instructions whose lattice value has changed in a worklist, and then updates their users in the main loop. This may result in instructions unnecessarily being visited multiple times (as an instruction will often use multiple other instructions). Additionally, we'd often redundantly visit instructions that were already visited when the containing block first became executable. Instead, change the worklist to directly store the instructions that need to be revisited. Additionally, do not add instructions to the worklist that will already be covered by the main basic block walk. This change is conceptually NFC, but is expected to produce minor differences in practice, because the visitation order interacts with the range widening limit.
1 parent 6f7370c commit 545cdca

File tree

1 file changed

+66
-101
lines changed

1 file changed

+66
-101
lines changed

llvm/lib/Transforms/Utils/SCCPSolver.cpp

Lines changed: 66 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,10 @@ void SCCPSolver::inferArgAttributes() const {
426426
class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
427427
const DataLayout &DL;
428428
std::function<const TargetLibraryInfo &(Function &)> GetTLI;
429-
SmallPtrSet<BasicBlock *, 8> BBExecutable; // The BBs that are executable.
429+
/// Basic blocks that are executable (but may not have been visited yet).
430+
SmallPtrSet<BasicBlock *, 8> BBExecutable;
431+
/// Basic blocks that are executable and have been visited at least once.
432+
SmallPtrSet<BasicBlock *, 8> BBVisited;
430433
DenseMap<Value *, ValueLatticeElement>
431434
ValueState; // The state each value is in.
432435

@@ -466,15 +469,14 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
466469
/// constants.
467470
SmallPtrSet<Function *, 16> TrackingIncomingArguments;
468471

469-
/// The reason for two worklists is that overdefined is the lowest state
470-
/// on the lattice, and moving things to overdefined as fast as possible
471-
/// makes SCCP converge much faster.
472-
///
473-
/// By having a separate worklist, we accomplish this because everything
474-
/// possibly overdefined will become overdefined at the soonest possible
475-
/// point.
476-
SmallVector<Value *, 64> OverdefinedInstWorkList;
477-
SmallVector<Value *, 64> InstWorkList;
472+
/// Worklist of instructions to re-visit. This only includes instructions
473+
/// in blocks that have already been visited at least once.
474+
SmallSetVector<Instruction *, 16> InstWorkList;
475+
476+
/// Current instruction while visiting a block for the first time, used to
477+
/// avoid unnecessary instruction worklist insertions. Null if an instruction
478+
/// is visited outside a whole-block visitation.
479+
Instruction *CurI = nullptr;
478480

479481
// The BasicBlock work list
480482
SmallVector<BasicBlock *, 64> BBWorkList;
@@ -497,12 +499,15 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
497499
return dyn_cast_or_null<ConstantInt>(getConstant(IV, Ty));
498500
}
499501

500-
// pushToWorkList - Helper for markConstant/markOverdefined
501-
void pushToWorkList(ValueLatticeElement &IV, Value *V);
502+
/// Push instruction \p I to the worklist.
503+
void pushToWorkList(Instruction *I);
504+
505+
/// Push users of value \p V to the worklist.
506+
void pushUsersToWorkList(Value *V);
502507

503-
// Helper to push \p V to the worklist, after updating it to \p IV. Also
504-
// prints a debug message with the updated value.
505-
void pushToWorkListMsg(ValueLatticeElement &IV, Value *V);
508+
/// Like pushUsersToWorkList(), but also prints a debug message with the
509+
/// updated value.
510+
void pushUsersToWorkListMsg(ValueLatticeElement &IV, Value *V);
506511

507512
// markConstant - Make a value be marked as "constant". If the value
508513
// is not already a constant, add it to the instruction work list so that
@@ -661,46 +666,9 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
661666
// successors are reachable from a given terminator instruction.
662667
void getFeasibleSuccessors(Instruction &TI, SmallVectorImpl<bool> &Succs);
663668

664-
// OperandChangedState - This method is invoked on all of the users of an
665-
// instruction that was just changed state somehow. Based on this
666-
// information, we need to update the specified user of this instruction.
667-
void operandChangedState(Instruction *I) {
668-
if (BBExecutable.count(I->getParent())) // Inst is executable?
669-
visit(*I);
670-
}
671-
672669
// Add U as additional user of V.
673670
void addAdditionalUser(Value *V, User *U) { AdditionalUsers[V].insert(U); }
674671

675-
// Mark I's users as changed, including AdditionalUsers.
676-
void markUsersAsChanged(Value *I) {
677-
// Functions include their arguments in the use-list. Changed function
678-
// values mean that the result of the function changed. We only need to
679-
// update the call sites with the new function result and do not have to
680-
// propagate the call arguments.
681-
if (isa<Function>(I)) {
682-
for (User *U : I->users()) {
683-
if (auto *CB = dyn_cast<CallBase>(U))
684-
handleCallResult(*CB);
685-
}
686-
} else {
687-
for (User *U : I->users())
688-
if (auto *UI = dyn_cast<Instruction>(U))
689-
operandChangedState(UI);
690-
}
691-
692-
auto Iter = AdditionalUsers.find(I);
693-
if (Iter != AdditionalUsers.end()) {
694-
// Copy additional users before notifying them of changes, because new
695-
// users may be added, potentially invalidating the iterator.
696-
SmallVector<Instruction *, 2> ToNotify;
697-
for (User *U : Iter->second)
698-
if (auto *UI = dyn_cast<Instruction>(U))
699-
ToNotify.push_back(UI);
700-
for (Instruction *UI : ToNotify)
701-
operandChangedState(UI);
702-
}
703-
}
704672
void handleCallOverdefined(CallBase &CB);
705673
void handleCallResult(CallBase &CB);
706674
void handleCallArguments(CallBase &CB);
@@ -985,27 +953,49 @@ bool SCCPInstVisitor::markBlockExecutable(BasicBlock *BB) {
985953
return true;
986954
}
987955

988-
void SCCPInstVisitor::pushToWorkList(ValueLatticeElement &IV, Value *V) {
989-
if (IV.isOverdefined()) {
990-
if (OverdefinedInstWorkList.empty() || OverdefinedInstWorkList.back() != V)
991-
OverdefinedInstWorkList.push_back(V);
956+
void SCCPInstVisitor::pushToWorkList(Instruction *I) {
957+
// If we're currently visiting a block, do not push any instructions in the
958+
// same blocks that are after the current one, as they will be visited
959+
// anyway. We do have to push updates to earlier instructions (e.g. phi
960+
// nodes or loads of tracked globals).
961+
if (CurI && I->getParent() == CurI->getParent() && !I->comesBefore(CurI))
992962
return;
963+
// Only push instructions in already visited blocks. Otherwise we'll handle
964+
// it when we visit the block for the first time.
965+
if (BBVisited.contains(I->getParent()))
966+
InstWorkList.insert(I);
967+
}
968+
969+
void SCCPInstVisitor::pushUsersToWorkList(Value *V) {
970+
for (User *U : V->users())
971+
if (auto *UI = dyn_cast<Instruction>(U))
972+
pushToWorkList(UI);
973+
974+
auto Iter = AdditionalUsers.find(V);
975+
if (Iter != AdditionalUsers.end()) {
976+
// Copy additional users before notifying them of changes, because new
977+
// users may be added, potentially invalidating the iterator.
978+
SmallVector<Instruction *, 2> ToNotify;
979+
for (User *U : Iter->second)
980+
if (auto *UI = dyn_cast<Instruction>(U))
981+
ToNotify.push_back(UI);
982+
for (Instruction *UI : ToNotify)
983+
pushToWorkList(UI);
993984
}
994-
if (InstWorkList.empty() || InstWorkList.back() != V)
995-
InstWorkList.push_back(V);
996985
}
997986

998-
void SCCPInstVisitor::pushToWorkListMsg(ValueLatticeElement &IV, Value *V) {
987+
void SCCPInstVisitor::pushUsersToWorkListMsg(ValueLatticeElement &IV,
988+
Value *V) {
999989
LLVM_DEBUG(dbgs() << "updated " << IV << ": " << *V << '\n');
1000-
pushToWorkList(IV, V);
990+
pushUsersToWorkList(V);
1001991
}
1002992

1003993
bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
1004994
Constant *C, bool MayIncludeUndef) {
1005995
if (!IV.markConstant(C, MayIncludeUndef))
1006996
return false;
1007997
LLVM_DEBUG(dbgs() << "markConstant: " << *C << ": " << *V << '\n');
1008-
pushToWorkList(IV, V);
998+
pushUsersToWorkList(V);
1009999
return true;
10101000
}
10111001

@@ -1014,7 +1004,7 @@ bool SCCPInstVisitor::markNotConstant(ValueLatticeElement &IV, Value *V,
10141004
if (!IV.markNotConstant(C))
10151005
return false;
10161006
LLVM_DEBUG(dbgs() << "markNotConstant: " << *C << ": " << *V << '\n');
1017-
pushToWorkList(IV, V);
1007+
pushUsersToWorkList(V);
10181008
return true;
10191009
}
10201010

@@ -1023,7 +1013,7 @@ bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
10231013
if (!IV.markConstantRange(CR))
10241014
return false;
10251015
LLVM_DEBUG(dbgs() << "markConstantRange: " << CR << ": " << *V << '\n');
1026-
pushToWorkList(IV, V);
1016+
pushUsersToWorkList(V);
10271017
return true;
10281018
}
10291019

@@ -1036,7 +1026,7 @@ bool SCCPInstVisitor::markOverdefined(ValueLatticeElement &IV, Value *V) {
10361026
<< "Function '" << F->getName() << "'\n";
10371027
else dbgs() << *V << '\n');
10381028
// Only instructions go on the work list
1039-
pushToWorkList(IV, V);
1029+
pushUsersToWorkList(V);
10401030
return true;
10411031
}
10421032

@@ -1144,7 +1134,7 @@ bool SCCPInstVisitor::mergeInValue(ValueLatticeElement &IV, Value *V,
11441134
ValueLatticeElement MergeWithV,
11451135
ValueLatticeElement::MergeOptions Opts) {
11461136
if (IV.mergeIn(MergeWithV, Opts)) {
1147-
pushToWorkList(IV, V);
1137+
pushUsersToWorkList(V);
11481138
LLVM_DEBUG(dbgs() << "Merged " << MergeWithV << " into " << *V << " : "
11491139
<< IV << "\n");
11501140
return true;
@@ -1164,7 +1154,7 @@ bool SCCPInstVisitor::markEdgeExecutable(BasicBlock *Source, BasicBlock *Dest) {
11641154
<< " -> " << Dest->getName() << '\n');
11651155

11661156
for (PHINode &PN : Dest->phis())
1167-
visitPHINode(PN);
1157+
pushToWorkList(&PN);
11681158
}
11691159
return true;
11701160
}
@@ -1540,7 +1530,7 @@ void SCCPInstVisitor::visitSelectInst(SelectInst &I) {
15401530
bool Changed = State.mergeIn(TVal);
15411531
Changed |= State.mergeIn(FVal);
15421532
if (Changed)
1543-
pushToWorkListMsg(State, &I);
1533+
pushUsersToWorkListMsg(State, &I);
15441534
}
15451535

15461536
// Handle Unary Operators.
@@ -2039,53 +2029,28 @@ void SCCPInstVisitor::handleCallResult(CallBase &CB) {
20392029

20402030
void SCCPInstVisitor::solve() {
20412031
// Process the work lists until they are empty!
2042-
while (!BBWorkList.empty() || !InstWorkList.empty() ||
2043-
!OverdefinedInstWorkList.empty()) {
2044-
// Process the overdefined instruction's work list first, which drives other
2045-
// things to overdefined more quickly.
2046-
while (!OverdefinedInstWorkList.empty()) {
2047-
Value *I = OverdefinedInstWorkList.pop_back_val();
2048-
Invalidated.erase(I);
2049-
2050-
LLVM_DEBUG(dbgs() << "\nPopped off OI-WL: " << *I << '\n');
2051-
2052-
// "I" got into the work list because it either made the transition from
2053-
// bottom to constant, or to overdefined.
2054-
//
2055-
// Anything on this worklist that is overdefined need not be visited
2056-
// since all of its users will have already been marked as overdefined
2057-
// Update all of the users of this instruction's value.
2058-
//
2059-
markUsersAsChanged(I);
2060-
}
2061-
2032+
while (!BBWorkList.empty() || !InstWorkList.empty()) {
20622033
// Process the instruction work list.
20632034
while (!InstWorkList.empty()) {
2064-
Value *I = InstWorkList.pop_back_val();
2035+
Instruction *I = InstWorkList.pop_back_val();
20652036
Invalidated.erase(I);
20662037

20672038
LLVM_DEBUG(dbgs() << "\nPopped off I-WL: " << *I << '\n');
20682039

2069-
// "I" got into the work list because it made the transition from undef to
2070-
// constant.
2071-
//
2072-
// Anything on this worklist that is overdefined need not be visited
2073-
// since all of its users will have already been marked as overdefined.
2074-
// Update all of the users of this instruction's value.
2075-
//
2076-
if (I->getType()->isStructTy() || !getValueState(I).isOverdefined())
2077-
markUsersAsChanged(I);
2040+
visit(I);
20782041
}
20792042

20802043
// Process the basic block work list.
20812044
while (!BBWorkList.empty()) {
20822045
BasicBlock *BB = BBWorkList.pop_back_val();
2046+
BBVisited.insert(BB);
20832047

20842048
LLVM_DEBUG(dbgs() << "\nPopped off BBWL: " << *BB << '\n');
2085-
2086-
// Notify all instructions in this basic block that they are newly
2087-
// executable.
2088-
visit(BB);
2049+
for (Instruction &I : *BB) {
2050+
CurI = &I;
2051+
visit(I);
2052+
}
2053+
CurI = nullptr;
20892054
}
20902055
}
20912056
}

0 commit comments

Comments
 (0)