-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[SSAUpdaterBulk] Add PHI simplification pass. #132004
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
Conversation
@llvm/pr-subscribers-backend-webassembly @llvm/pr-subscribers-llvm-transforms Author: Valery Pykhtin (vpykhtin) ChangesThis patch, along with #131762, enhances SSAUpdaterBulk to function as a near drop-in replacement for SSAUpdater, see #130611. I opted to implement phi optimization as a separate step to maintain the current behavior of Full diff: https://github.com/llvm/llvm-project/pull/132004.diff 2 Files Affected:
diff --git a/llvm/include/llvm/Transforms/Utils/SSAUpdaterBulk.h b/llvm/include/llvm/Transforms/Utils/SSAUpdaterBulk.h
index ad24cb454d5e7..6894e4fa0a357 100644
--- a/llvm/include/llvm/Transforms/Utils/SSAUpdaterBulk.h
+++ b/llvm/include/llvm/Transforms/Utils/SSAUpdaterBulk.h
@@ -79,6 +79,30 @@ class SSAUpdaterBulk {
/// vector.
void RewriteAllUses(DominatorTree *DT,
SmallVectorImpl<PHINode *> *InsertedPHIs = nullptr);
+
+ /// Rewrite all uses and simplify the inserted PHI nodes.
+ /// Use this method to preserve behavior when replacing SSAUpdater.
+ void RewriteAndSimplifyAllUses(DominatorTree *DT) {
+ SmallVector<PHINode *, 8> NewPHIs;
+ RewriteAllUses(DT, &NewPHIs);
+ simplifyPass(NewPHIs);
+ }
+
+ /// Same as previous, but return inserted PHI nodes in InsertedPHIs.
+ void RewriteAndSimplifyAllUses(DominatorTree *DT,
+ SmallVectorImpl<PHINode *> &InsertedPHIs) {
+ RewriteAllUses(DT, &InsertedPHIs);
+ simplifyPass(InsertedPHIs);
+ // Remove nullptrs from the worklist
+ InsertedPHIs.erase(
+ std::remove(InsertedPHIs.begin(), InsertedPHIs.end(), nullptr),
+ InsertedPHIs.end());
+ }
+
+private:
+ /// Perform a single pass of simplification over the worklist of PHIs.
+ /// Pointers to replaced phis are nulled out.
+ static bool simplifyPass(SmallVectorImpl<PHINode *> &Worklist);
};
} // end namespace llvm
diff --git a/llvm/lib/Transforms/Utils/SSAUpdaterBulk.cpp b/llvm/lib/Transforms/Utils/SSAUpdaterBulk.cpp
index cad7ff64c01fb..dac964b092ec5 100644
--- a/llvm/lib/Transforms/Utils/SSAUpdaterBulk.cpp
+++ b/llvm/lib/Transforms/Utils/SSAUpdaterBulk.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Utils/SSAUpdaterBulk.h"
+#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/IteratedDominanceFrontier.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Dominators.h"
@@ -182,3 +183,49 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
}
}
}
+
+static bool isEquivalentPHI(PHINode *PHI1, PHINode *PHI2) {
+ if (PHI1->getNumIncomingValues() != PHI2->getNumIncomingValues())
+ return false;
+
+ unsigned I = 0, NumValues = PHI1->getNumIncomingValues();
+ for (; I != NumValues; ++I) {
+ if (PHI1->getIncomingBlock(I) != PHI2->getIncomingBlock(I))
+ break;
+ if (PHI1->getIncomingValue(I) != PHI2->getIncomingValue(I))
+ return false;
+ }
+ // TODO: add procesing if phis have different order of incoming values.
+ return I == NumValues;
+}
+
+bool SSAUpdaterBulk::simplifyPass(SmallVectorImpl<PHINode *> &Worklist) {
+ if (Worklist.empty())
+ return false;
+
+ auto findEquivalentPHI = [](PHINode *PHI) -> Value * {
+ BasicBlock *BB = PHI->getParent();
+ for (auto &OtherPHI : BB->phis()) {
+ if (PHI != &OtherPHI && isEquivalentPHI(PHI, &OtherPHI)) {
+ return &OtherPHI;
+ }
+ }
+ return nullptr;
+ };
+
+ const DataLayout &DL = Worklist.front()->getParent()->getDataLayout();
+ bool Change = false;
+ for (PHINode *&PHI : Worklist) {
+ Value *Replacement = simplifyInstruction(PHI, DL);
+ if (!Replacement)
+ Replacement = findEquivalentPHI(PHI);
+
+ if (Replacement) {
+ PHI->replaceAllUsesWith(Replacement);
+ PHI->eraseFromParent();
+ PHI = nullptr; // Mark as removed
+ Change = true;
+ }
+ }
+ return Change;
+}
|
} | ||
} | ||
return nullptr; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just use EliminateDuplicatePHINodes here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The EliminateDuplicatePHINodes function has two main issues. First, it replaces later PHI nodes with earlier ones, leading to unexpected behavior where existing PHI nodes are replaced with new ones. Second, it tests all PHI nodes instead of focusing solely on the newly added ones, which would be more efficient.
Do we have some uses that don't want the simplification behavior? If not, I'd rather change the behavior of the existing methods. |
I don't know, it's very likely that everybody would prefer optimizing version. In this case it would be easier to add EliminateDuplicatePHINodes to the end of RewriteAllUses as I need to collect the set of BB to process. |
The |
✅ With the latest revision this PR passed the C/C++ code formatter. |
I peeked into |
Hi @nikic, I believe this is ready for submission in its current state. I chose to keep this code as a separate 'pass' rather than integrating it directly into RewriteAllUses for two reasons:
|
Used Turned on optimizations by default, got some failures but they're mostly reorder of phi's. |
Current implementation is incorrect: I cannot move new phis to the end because they can be used by existing phis if the user added 'variable' as incoming value for an existed phi. |
Maybe I'm misunderstanding what you mean here, but phi's are all evaluated at the same time, not in order. If a phi refers to another phi, it always refers to the value of the phi at a previous cycle iteration. |
Sorry, false alarm, otherwise phis wouldn't be able to reference itself. I'm preparing new patch as a stacked graphite PR that would include this patch and its' use in AMDGPU backend. |
return false; | ||
|
||
const DataLayout &DL = Worklist.front()->getParent()->getDataLayout(); | ||
bool Change = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool Change = false; | |
bool Changed = false; |
nit: More typical naming for this flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will address this in the new stacker PR.
if (Value *Replacement = simplifyInstruction(PHI, DL)) { | ||
PHI->replaceAllUsesWith(Replacement); | ||
PHI->eraseFromParent(); | ||
PHI = nullptr; // Mark as removed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could also iterate over Worklist using erase_if instead to avoid leaving behind nullptrs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, but I don't see much sense since this is internal routine and we're know what we're doing after that :)
This patch, along with #131762, enhances SSAUpdaterBulk to function as a near drop-in replacement for SSAUpdater, see #130611.
I opted to implement phi optimization as a separate step to maintain the current behavior of
SSAUpdaterBulk::RewriteAllUses
while ensuring the implementation remains straightforward.