Skip to content

Commit 0e02541

Browse files
authored
[SSAUpdaterBulk] Add PHI simplification pass. (#150936)
This optimization is performed as a separate pass over newly inserted PHI nodes to simplify and deduplicate them. By processing PHIs separately, we avoid the complexity of tracking reference bookkeeping needed to update BBValueInfo structures during insertion.
1 parent 32fffe5 commit 0e02541

File tree

3 files changed

+324
-7
lines changed

3 files changed

+324
-7
lines changed

llvm/include/llvm/Transforms/Utils/SSAUpdaterBulk.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#ifndef LLVM_TRANSFORMS_UTILS_SSAUPDATERBULK_H
1414
#define LLVM_TRANSFORMS_UTILS_SSAUPDATERBULK_H
1515

16-
#include "llvm/ADT/DenseMap.h"
1716
#include "llvm/ADT/StringRef.h"
1817
#include "llvm/IR/PredIteratorCache.h"
1918
#include "llvm/Support/Compiler.h"
@@ -79,6 +78,10 @@ class SSAUpdaterBulk {
7978
LLVM_ABI void
8079
RewriteAllUses(DominatorTree *DT,
8180
SmallVectorImpl<PHINode *> *InsertedPHIs = nullptr);
81+
82+
/// Rewrite all uses and simplify the inserted PHI nodes.
83+
/// Use this method to preserve behavior when replacing SSAUpdater.
84+
void RewriteAndOptimizeAllUses(DominatorTree &DT);
8285
};
8386

8487
} // end namespace llvm

llvm/lib/Transforms/Utils/SSAUpdaterBulk.cpp

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "llvm/Transforms/Utils/SSAUpdaterBulk.h"
14+
#include "llvm/Analysis/InstructionSimplify.h"
1415
#include "llvm/Analysis/IteratedDominanceFrontier.h"
1516
#include "llvm/IR/BasicBlock.h"
1617
#include "llvm/IR/Dominators.h"
1718
#include "llvm/IR/IRBuilder.h"
18-
#include "llvm/IR/Instructions.h"
1919
#include "llvm/IR/Use.h"
2020
#include "llvm/IR/Value.h"
2121

@@ -112,7 +112,7 @@ struct BBValueInfo {
112112
void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
113113
SmallVectorImpl<PHINode *> *InsertedPHIs) {
114114
DenseMap<BasicBlock *, BBValueInfo> BBInfos;
115-
for (auto &R : Rewrites) {
115+
for (RewriteInfo &R : Rewrites) {
116116
BBInfos.clear();
117117

118118
// Compute locations for new phi-nodes.
@@ -145,7 +145,7 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
145145
BBInfos[BB].LiveOutValue = V;
146146

147147
// We've computed IDF, now insert new phi-nodes there.
148-
for (auto *FrontierBB : IDFBlocks) {
148+
for (BasicBlock *FrontierBB : IDFBlocks) {
149149
IRBuilder<> B(FrontierBB, FrontierBB->begin());
150150
PHINode *PN = B.CreatePHI(R.Ty, 0, R.Name);
151151
BBInfos[FrontierBB].LiveInValue = PN;
@@ -156,7 +156,7 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
156156
// IsLiveOut indicates whether we are computing live-out values (true) or
157157
// live-in values (false).
158158
auto ComputeValue = [&](BasicBlock *BB, bool IsLiveOut) -> Value * {
159-
auto *BBInfo = &BBInfos[BB];
159+
BBValueInfo *BBInfo = &BBInfos[BB];
160160

161161
if (IsLiveOut && BBInfo->LiveOutValue)
162162
return BBInfo->LiveOutValue;
@@ -187,7 +187,7 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
187187
if (!V)
188188
V = UndefValue::get(R.Ty);
189189

190-
for (auto *BBInfo : Stack)
190+
for (BBValueInfo *BBInfo : Stack)
191191
// Loop above can insert new entries into the BBInfos map: assume the
192192
// map shouldn't grow due to [1] and BBInfo references are valid.
193193
BBInfo->LiveInValue = V;
@@ -196,7 +196,7 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
196196
};
197197

198198
// Fill in arguments of the inserted PHIs.
199-
for (auto *BB : IDFBlocks) {
199+
for (BasicBlock *BB : IDFBlocks) {
200200
auto *PHI = cast<PHINode>(&BB->front());
201201
for (BasicBlock *Pred : PredCache.get(BB))
202202
PHI->addIncoming(ComputeValue(Pred, /*IsLiveOut=*/true), Pred);
@@ -222,3 +222,97 @@ void SSAUpdaterBulk::RewriteAllUses(DominatorTree *DT,
222222
}
223223
}
224224
}
225+
226+
// Perform a single pass of simplification over the worklist of PHIs.
227+
// This should be called after RewriteAllUses() because simplifying PHIs
228+
// immediately after creation would require updating all references to those
229+
// PHIs in the BBValueInfo structures, which would necessitate additional
230+
// reference tracking overhead.
231+
static void simplifyPass(MutableArrayRef<PHINode *> Worklist,
232+
const DataLayout &DL) {
233+
for (PHINode *&PHI : Worklist) {
234+
if (Value *Simplified = simplifyInstruction(PHI, DL)) {
235+
PHI->replaceAllUsesWith(Simplified);
236+
PHI->eraseFromParent();
237+
PHI = nullptr; // Mark as removed.
238+
}
239+
}
240+
}
241+
242+
#ifndef NDEBUG // Should this be under EXPENSIVE_CHECKS?
243+
// New PHI nodes should not reference one another but they may reference
244+
// themselves or existing PHI nodes, and existing PHI nodes may reference new
245+
// PHI nodes.
246+
static bool
247+
PHIAreRefEachOther(const iterator_range<BasicBlock::phi_iterator> NewPHIs) {
248+
SmallPtrSet<PHINode *, 8> NewPHISet;
249+
for (PHINode &PN : NewPHIs)
250+
NewPHISet.insert(&PN);
251+
for (PHINode &PHI : NewPHIs) {
252+
for (Value *V : PHI.incoming_values()) {
253+
PHINode *IncPHI = dyn_cast<PHINode>(V);
254+
if (IncPHI && IncPHI != &PHI && NewPHISet.contains(IncPHI))
255+
return true;
256+
}
257+
}
258+
return false;
259+
}
260+
#endif
261+
262+
static bool replaceIfIdentical(PHINode &PHI, PHINode &ReplPHI) {
263+
if (!PHI.isIdenticalToWhenDefined(&ReplPHI))
264+
return false;
265+
PHI.replaceAllUsesWith(&ReplPHI);
266+
PHI.eraseFromParent();
267+
return true;
268+
}
269+
270+
bool EliminateNewDuplicatePHINodes(BasicBlock *BB,
271+
BasicBlock::phi_iterator FirstExistingPN) {
272+
auto NewPHIs = make_range(BB->phis().begin(), FirstExistingPN);
273+
assert(!PHIAreRefEachOther(NewPHIs));
274+
275+
// Deduplicate new PHIs first to reduce the number of comparisons on the
276+
// following new -> existing pass.
277+
bool Changed = false;
278+
for (auto I = BB->phis().begin(); I != FirstExistingPN; ++I) {
279+
for (auto J = std::next(I); J != FirstExistingPN;) {
280+
Changed |= replaceIfIdentical(*J++, *I);
281+
}
282+
}
283+
284+
// Iterate over existing PHIs and replace identical new PHIs.
285+
for (PHINode &ExistingPHI : make_range(FirstExistingPN, BB->phis().end())) {
286+
auto I = BB->phis().begin();
287+
assert(I != FirstExistingPN); // Should be at least one new PHI.
288+
do {
289+
Changed |= replaceIfIdentical(*I++, ExistingPHI);
290+
} while (I != FirstExistingPN);
291+
if (BB->phis().begin() == FirstExistingPN)
292+
return Changed;
293+
}
294+
return Changed;
295+
}
296+
297+
static void deduplicatePass(ArrayRef<PHINode *> Worklist) {
298+
SmallDenseMap<BasicBlock *, unsigned> BBs;
299+
for (PHINode *PHI : Worklist) {
300+
if (PHI)
301+
++BBs[PHI->getParent()];
302+
}
303+
304+
for (auto [BB, NumNewPHIs] : BBs) {
305+
auto FirstExistingPN = std::next(BB->phis().begin(), NumNewPHIs);
306+
EliminateNewDuplicatePHINodes(BB, FirstExistingPN);
307+
}
308+
}
309+
310+
void SSAUpdaterBulk::RewriteAndOptimizeAllUses(DominatorTree &DT) {
311+
SmallVector<PHINode *, 4> PHIs;
312+
RewriteAllUses(&DT, &PHIs);
313+
if (PHIs.empty())
314+
return;
315+
316+
simplifyPass(PHIs, PHIs.front()->getParent()->getDataLayout());
317+
deduplicatePass(PHIs);
318+
}

llvm/unittests/Transforms/Utils/SSAUpdaterBulkTest.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,223 @@ TEST(SSAUpdaterBulk, TwoBBLoop) {
308308
EXPECT_EQ(Phi->getIncomingValueForBlock(Entry), ConstantInt::get(I32Ty, 0));
309309
EXPECT_EQ(Phi->getIncomingValueForBlock(Loop), I);
310310
}
311+
312+
TEST(SSAUpdaterBulk, SimplifyPHIs) {
313+
const char *IR = R"(
314+
define void @main(i32 %val, i1 %cond) {
315+
entry:
316+
br i1 %cond, label %left, label %right
317+
left:
318+
%add = add i32 %val, 1
319+
br label %exit
320+
right:
321+
%sub = sub i32 %val, 1
322+
br label %exit
323+
exit:
324+
%phi = phi i32 [ %sub, %right ], [ %add, %left ]
325+
%cmp = icmp slt i32 0, 42
326+
ret void
327+
}
328+
)";
329+
330+
llvm::LLVMContext Context;
331+
llvm::SMDiagnostic Err;
332+
std::unique_ptr<llvm::Module> M = llvm::parseAssemblyString(IR, Err, Context);
333+
ASSERT_NE(M, nullptr) << "Failed to parse IR: " << Err.getMessage();
334+
335+
Function *F = M->getFunction("main");
336+
auto *Entry = &F->getEntryBlock();
337+
auto *Left = Entry->getTerminator()->getSuccessor(0);
338+
auto *Right = Entry->getTerminator()->getSuccessor(1);
339+
auto *Exit = Left->getSingleSuccessor();
340+
auto *Val = &*F->arg_begin();
341+
auto *Phi = &Exit->front();
342+
auto *Cmp = &*std::next(Exit->begin());
343+
auto *Add = &Left->front();
344+
auto *Sub = &Right->front();
345+
346+
SSAUpdaterBulk Updater;
347+
Type *I32Ty = Type::getInt32Ty(Context);
348+
349+
// Use %val directly instead of creating a phi.
350+
unsigned ValVar = Updater.AddVariable("Val", I32Ty);
351+
Updater.AddAvailableValue(ValVar, Left, Val);
352+
Updater.AddAvailableValue(ValVar, Right, Val);
353+
Updater.AddUse(ValVar, &Cmp->getOperandUse(0));
354+
355+
// Use existing %phi for %add and %sub values.
356+
unsigned AddSubVar = Updater.AddVariable("AddSub", I32Ty);
357+
Updater.AddAvailableValue(AddSubVar, Left, Add);
358+
Updater.AddAvailableValue(AddSubVar, Right, Sub);
359+
Updater.AddUse(AddSubVar, &Cmp->getOperandUse(1));
360+
361+
auto ExitSizeBefore = Exit->size();
362+
DominatorTree DT(*F);
363+
Updater.RewriteAndOptimizeAllUses(DT);
364+
365+
// Output for Exit->dump():
366+
// exit: ; preds = %right, %left
367+
// %phi = phi i32 [ %sub, %right ], [ %add, %left ]
368+
// %cmp = icmp slt i32 %val, %phi
369+
// ret void
370+
371+
ASSERT_EQ(Exit->size(), ExitSizeBefore);
372+
ASSERT_EQ(&Exit->front(), Phi);
373+
EXPECT_EQ(Val, Cmp->getOperand(0));
374+
EXPECT_EQ(Phi, Cmp->getOperand(1));
375+
}
376+
377+
bool EliminateNewDuplicatePHINodes(BasicBlock *BB,
378+
BasicBlock::phi_iterator FirstExistingPN);
379+
380+
// Helper to run both versions on the same input.
381+
static void RunEliminateNewDuplicatePHINode(
382+
const char *AsmText,
383+
std::function<void(BasicBlock &,
384+
bool(BasicBlock *BB, BasicBlock::phi_iterator))>
385+
Check) {
386+
LLVMContext C;
387+
388+
SMDiagnostic Err;
389+
std::unique_ptr<Module> M = parseAssemblyString(AsmText, Err, C);
390+
if (!M) {
391+
Err.print("UtilsTests", errs());
392+
return;
393+
}
394+
395+
Function *F = M->getFunction("main");
396+
auto BBIt = std::find_if(F->begin(), F->end(), [](const BasicBlock &Block) {
397+
return Block.getName() == "testbb";
398+
});
399+
ASSERT_NE(BBIt, F->end());
400+
Check(*BBIt, EliminateNewDuplicatePHINodes);
401+
}
402+
403+
static BasicBlock::phi_iterator getPhiIt(BasicBlock &BB, unsigned Idx) {
404+
return std::next(BB.phis().begin(), Idx);
405+
}
406+
407+
static PHINode *getPhi(BasicBlock &BB, unsigned Idx) {
408+
return &*getPhiIt(BB, Idx);
409+
}
410+
411+
static int getNumPHIs(BasicBlock &BB) {
412+
return std::distance(BB.phis().begin(), BB.phis().end());
413+
}
414+
415+
TEST(SSAUpdaterBulk, EliminateNewDuplicatePHINodes_OrderExisting) {
416+
RunEliminateNewDuplicatePHINode(R"(
417+
define void @main() {
418+
entry:
419+
br label %testbb
420+
testbb:
421+
%np0 = phi i32 [ 1, %entry ]
422+
%np1 = phi i32 [ 1, %entry ]
423+
%ep0 = phi i32 [ 1, %entry ]
424+
%ep1 = phi i32 [ 1, %entry ]
425+
%u = add i32 %np0, %np1
426+
ret void
427+
}
428+
)", [](BasicBlock &BB, auto *ENDPN) {
429+
AssertingVH<PHINode> EP0 = getPhi(BB, 2);
430+
AssertingVH<PHINode> EP1 = getPhi(BB, 3);
431+
EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
432+
// Expected:
433+
// %ep0 = phi i32 [ 1, %entry ]
434+
// %ep1 = phi i32 [ 1, %entry ]
435+
// %u = add i32 %ep0, %ep0
436+
EXPECT_EQ(getNumPHIs(BB), 2);
437+
Instruction &Add = *BB.getFirstNonPHIIt();
438+
EXPECT_EQ(Add.getOperand(0), EP0);
439+
EXPECT_EQ(Add.getOperand(1), EP0);
440+
(void)EP1; // Avoid "unused" warning.
441+
});
442+
}
443+
444+
TEST(SSAUpdaterBulk, EliminateNewDuplicatePHINodes_OrderNew) {
445+
RunEliminateNewDuplicatePHINode(R"(
446+
define void @main() {
447+
entry:
448+
br label %testbb
449+
testbb:
450+
%np0 = phi i32 [ 1, %entry ]
451+
%np1 = phi i32 [ 1, %entry ]
452+
%ep0 = phi i32 [ 2, %entry ]
453+
%ep1 = phi i32 [ 2, %entry ]
454+
%u = add i32 %np0, %np1
455+
ret void
456+
}
457+
)", [](BasicBlock &BB, auto *ENDPN) {
458+
AssertingVH<PHINode> NP0 = getPhi(BB, 0);
459+
AssertingVH<PHINode> EP0 = getPhi(BB, 2);
460+
AssertingVH<PHINode> EP1 = getPhi(BB, 3);
461+
EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
462+
// Expected:
463+
// %np0 = phi i32 [ 1, %entry ]
464+
// %ep0 = phi i32 [ 2, %entry ]
465+
// %ep1 = phi i32 [ 2, %entry ]
466+
// %u = add i32 %np0, %np0
467+
EXPECT_EQ(getNumPHIs(BB), 3);
468+
Instruction &Add = *BB.getFirstNonPHIIt();
469+
EXPECT_EQ(Add.getOperand(0), NP0);
470+
EXPECT_EQ(Add.getOperand(1), NP0);
471+
(void)EP0;
472+
(void)EP1; // Avoid "unused" warning.
473+
});
474+
}
475+
476+
TEST(SSAUpdaterBulk, EliminateNewDuplicatePHINodes_NewRefExisting) {
477+
RunEliminateNewDuplicatePHINode(R"(
478+
define void @main() {
479+
entry:
480+
br label %testbb
481+
testbb:
482+
%np0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
483+
%np1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
484+
%ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
485+
%ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
486+
%u = add i32 %np0, %np1
487+
br label %testbb
488+
}
489+
)", [](BasicBlock &BB, auto *ENDPN) {
490+
AssertingVH<PHINode> EP0 = getPhi(BB, 2);
491+
AssertingVH<PHINode> EP1 = getPhi(BB, 3);
492+
EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
493+
// Expected:
494+
// %ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
495+
// %ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
496+
// %u = add i32 %ep0, %ep1
497+
EXPECT_EQ(getNumPHIs(BB), 2);
498+
Instruction &Add = *BB.getFirstNonPHIIt();
499+
EXPECT_EQ(Add.getOperand(0), EP0);
500+
EXPECT_EQ(Add.getOperand(1), EP1);
501+
});
502+
}
503+
504+
TEST(SSAUpdaterBulk, EliminateNewDuplicatePHINodes_ExistingRefNew) {
505+
RunEliminateNewDuplicatePHINode(R"(
506+
define void @main() {
507+
entry:
508+
br label %testbb
509+
testbb:
510+
%np0 = phi i32 [ 1, %entry ], [ %np0, %testbb ]
511+
%np1 = phi i32 [ 1, %entry ], [ %np1, %testbb ]
512+
%ep0 = phi i32 [ 1, %entry ], [ %np0, %testbb ]
513+
%ep1 = phi i32 [ 1, %entry ], [ %np1, %testbb ]
514+
%u = add i32 %np0, %np1
515+
br label %testbb
516+
}
517+
)", [](BasicBlock &BB, auto *ENDPN) {
518+
AssertingVH<PHINode> EP0 = getPhi(BB, 2);
519+
AssertingVH<PHINode> EP1 = getPhi(BB, 3);
520+
EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
521+
// Expected:
522+
// %ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
523+
// %ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
524+
// %u = add i32 %ep0, %ep1
525+
EXPECT_EQ(getNumPHIs(BB), 2);
526+
Instruction &Add = *BB.getFirstNonPHIIt();
527+
EXPECT_EQ(Add.getOperand(0), EP0);
528+
EXPECT_EQ(Add.getOperand(1), EP1);
529+
});
530+
}

0 commit comments

Comments
 (0)