[llvm] [InstCombine] Iterative replacement in PtrReplacer (PR #137215)
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Mon May 5 10:50:21 PDT 2025
================
@@ -270,80 +268,131 @@ class PointerReplacer {
} // end anonymous namespace
bool PointerReplacer::collectUsers() {
- if (!collectUsersRecursive(Root))
- return false;
-
- // Ensure that all outstanding (indirect) users of I
- // are inserted into the Worklist. Return false
- // otherwise.
- return llvm::set_is_subset(ValuesToRevisit, Worklist);
-}
+ SmallVector<Instruction *> Worklist;
+ SmallSetVector<Instruction *, 4> ValuesToRevisit;
+
+ auto PushUsersToWorklist = [&](Instruction *Inst) {
+ for (auto *U : Inst->users()) {
+ if (auto *I = dyn_cast<Instruction>(U)) {
+ if (!isAvailable(I) && !ValuesToRevisit.contains(I))
+ Worklist.emplace_back(I);
+ }
+ }
+ };
-bool PointerReplacer::collectUsersRecursive(Instruction &I) {
- for (auto *U : I.users()) {
- auto *Inst = cast<Instruction>(&*U);
+ PushUsersToWorklist(&Root);
+ while (!Worklist.empty()) {
+ auto *Inst = Worklist.pop_back_val();
+ if (!Inst)
+ return false;
+ if (isAvailable(Inst))
+ continue;
if (auto *Load = dyn_cast<LoadInst>(Inst)) {
if (Load->isVolatile())
return false;
- Worklist.insert(Load);
+ UsersToReplace.insert(Load);
} else if (auto *PHI = dyn_cast<PHINode>(Inst)) {
// All incoming values must be instructions for replacability
if (any_of(PHI->incoming_values(),
[](Value *V) { return !isa<Instruction>(V); }))
return false;
- // If at least one incoming value of the PHI is not in Worklist,
- // store the PHI for revisiting and skip this iteration of the
- // loop.
- if (any_of(PHI->incoming_values(), [this](Value *V) {
- return !isAvailable(cast<Instruction>(V));
- })) {
- ValuesToRevisit.insert(Inst);
+ // If all incoming values are available, mark this PHI as
+ // replacable and push it's users into the worklist.
+ if (all_of(PHI->incoming_values(),
+ [&](Value *V) { return isAvailable(cast<Instruction>(V)); })) {
+ UsersToReplace.insert(PHI);
+ PushUsersToWorklist(PHI);
continue;
}
- Worklist.insert(PHI);
- if (!collectUsersRecursive(*PHI))
- return false;
- } else if (auto *SI = dyn_cast<SelectInst>(Inst)) {
- if (!isa<Instruction>(SI->getTrueValue()) ||
- !isa<Instruction>(SI->getFalseValue()))
+ // Not all incoming values are available. If this PHI was already
+ // visited prior to this iteration, return false.
+ if (!ValuesToRevisit.insert(PHI))
return false;
- if (!isAvailable(cast<Instruction>(SI->getTrueValue())) ||
- !isAvailable(cast<Instruction>(SI->getFalseValue()))) {
- ValuesToRevisit.insert(Inst);
- continue;
+ // Push PHI back into the stack, followed by unavailable
+ // incoming values.
+ Worklist.emplace_back(PHI);
+ for (unsigned Idx = 0; Idx < PHI->getNumIncomingValues(); ++Idx) {
+ auto *IncomingValue = cast<Instruction>(PHI->getIncomingValue(Idx));
+ if (UsersToReplace.contains(IncomingValue))
+ continue;
+ if (!ValuesToRevisit.insert(IncomingValue))
+ return false;
+ Worklist.emplace_back(IncomingValue);
}
- Worklist.insert(SI);
- if (!collectUsersRecursive(*SI))
- return false;
- } else if (isa<GetElementPtrInst>(Inst)) {
- Worklist.insert(Inst);
- if (!collectUsersRecursive(*Inst))
+ } else if (auto *SI = dyn_cast<SelectInst>(Inst)) {
+ auto *TrueInst = dyn_cast<Instruction>(SI->getTrueValue());
+ auto *FalseInst = dyn_cast<Instruction>(SI->getFalseValue());
+ if (!TrueInst || !FalseInst)
return false;
+
+ UsersToReplace.insert(SI);
+ PushUsersToWorklist(SI);
+ } else if (auto *GEP = dyn_cast<GetElementPtrInst>(Inst)) {
+ UsersToReplace.insert(GEP);
+ PushUsersToWorklist(GEP);
} else if (auto *MI = dyn_cast<MemTransferInst>(Inst)) {
if (MI->isVolatile())
return false;
- Worklist.insert(Inst);
+ UsersToReplace.insert(Inst);
} else if (isEqualOrValidAddrSpaceCast(Inst, FromAS)) {
- Worklist.insert(Inst);
- if (!collectUsersRecursive(*Inst))
- return false;
+ UsersToReplace.insert(Inst);
+ PushUsersToWorklist(Inst);
} else if (Inst->isLifetimeStartOrEnd()) {
continue;
} else {
// TODO: For arbitrary uses with address space mismatches, should we check
// if we can introduce a valid addrspacecast?
- LLVM_DEBUG(dbgs() << "Cannot handle pointer user: " << *U << '\n');
+ LLVM_DEBUG(dbgs() << "Cannot handle pointer user: " << *Inst << '\n');
return false;
}
}
- return true;
+ return llvm::set_is_subset(ValuesToRevisit, UsersToReplace);
}
-Value *PointerReplacer::getReplacement(Value *V) { return WorkMap.lookup(V); }
+void PointerReplacer::replacePointer(Value *V) {
+#ifndef NDEBUG
+ auto *PT = cast<PointerType>(Root.getType());
+ auto *NT = cast<PointerType>(V->getType());
+ assert(PT != NT && "Invalid usage");
+#endif
+
+ WorkMap[&Root] = V;
+ SmallVector<Instruction *> Worklist;
+ SetVector<Instruction *> PostOrderWorklist;
+ SmallPtrSet<Instruction *, 4> Visited;
+
+ // Perform a postorder traversal of the users of Root.
+ Worklist.push_back(&Root);
+ while (!Worklist.empty()) {
+ Instruction *I = Worklist.back();
+
+ // If I has not been processed before, push each of its
+ // replacable users into the worklist.
+ if (Visited.insert(I).second) {
+ for (auto *U : I->users()) {
+ assert(isa<Instruction>(U) &&
+ "User should not have been"
+ " collected as it is not an instruction.");
+ auto *UserInst = cast<Instruction>(U);
+ if (UsersToReplace.contains(UserInst))
+ Worklist.push_back(UserInst);
+ }
+ // Otherwise, users of I have already been pushed into
+ // the PostOrderWorklist. Push I as well.
+ } else {
+ PostOrderWorklist.insert(I);
+ Worklist.pop_back();
+ }
+ }
+
+ // Replace pointers in reverse-postorder.
+ for (Instruction *I : llvm::reverse(PostOrderWorklist))
----------------
arsenm wrote:
```suggestion
for (Instruction *I : reverse(PostOrderWorklist))
```
https://github.com/llvm/llvm-project/pull/137215
More information about the llvm-commits
mailing list