[llvm] Add EliminateNewDuplicatePHINodes function. (PR #135179)

via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 10 07:02:07 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Valery Pykhtin (vpykhtin)

<details>
<summary>Changes</summary>

While integrating PHI deduplication into SSAUpdaterBulk, an attempt was made
to utilize EliminateDuplicatePHINodes, but it revealed some drawbacks:

1. It removes all duplicate PHI nodes within a block.
2. It retains the first occurrence among duplicates, which would replace
   an existing PHI with the new one.
3. It restarts the process after each duplicate removal.

When adding new PHI nodes, points 2 and especially 1 are not expected - for
example, it may be redundant to deduplicate existing PHIs. Point 3 can be
addressed by leveraging the fact that new PHIs do not reference each other.

While it might be possible to extend EliminateDuplicatePHINodes with this
functionality, I opted to implement separate routines for clarity. For the same
reason, I omitted the ToRemove argument.

Though it might be overkill, I decided to implement two versions of the
routine, similar to EliminateDuplicatePHINodes - N^2 and hash-based - to
compare performance on blocks with a small number of PHIs and ensure that
both versions agree on the resulting order of PHIs while remaining simple
in implementation.

Output for perfomance test on AMD EPYC 7702, 2183 MHz:
```
[ RUN      ] Local.RaceEliminateNewDuplicatePHINodes
Num phis: 2  H: 170  N2: 17  Diff: 153  Ratio: 10.000
Num phis: 3  H: 196  N2: 28  Diff: 168  Ratio: 7.000
Num phis: 4  H: 219  N2: 42  Diff: 177  Ratio: 5.214
Num phis: 5  H: 243  N2: 56  Diff: 187  Ratio: 4.339
Num phis: 6  H: 274  N2: 84  Diff: 190  Ratio: 3.262
Num phis: 7  H: 312  N2: 114  Diff: 198  Ratio: 2.737
Num phis: 8  H: 350  N2: 146  Diff: 204  Ratio: 2.397
Num phis: 9  H: 368  N2: 170  Diff: 198  Ratio: 2.165
Num phis: 10  H: 399  N2: 211  Diff: 188  Ratio: 1.891
Num phis: 11  H: 431  N2: 240  Diff: 191  Ratio: 1.796
Num phis: 12  H: 457  N2: 289  Diff: 168  Ratio: 1.581
Num phis: 13  H: 478  N2: 319  Diff: 159  Ratio: 1.498
Num phis: 14  H: 510  N2: 376  Diff: 134  Ratio: 1.356
Num phis: 15  H: 552  N2: 412  Diff: 140  Ratio: 1.340
Num phis: 16  H: 581  N2: 495  Diff: 86  Ratio: 1.174
Num phis: 17  H: 606  N2: 535  Diff: 71  Ratio: 1.133
Num phis: 18  H: 650  N2: 626  Diff: 24  Ratio: 1.038
Num phis: 19  H: 674  N2: 671  Diff: 3  Ratio: 1.004
Num phis: 20  H: 703  N2: 767  Diff: -64  Ratio: 0.917
Num phis: 21  H: 727  N2: 816  Diff: -89  Ratio: 0.891
Num phis: 22  H: 754  N2: 1052  Diff: -298  Ratio: 0.717
Num phis: 23  H: 779  N2: 1114  Diff: -335  Ratio: 0.699
Num phis: 24  H: 814  N2: 1216  Diff: -402  Ratio: 0.669
Num phis: 25  H: 833  N2: 1280  Diff: -447  Ratio: 0.651
Num phis: 26  H: 843  N2: 1418  Diff: -575  Ratio: 0.594
Num phis: 27  H: 864  N2: 1487  Diff: -623  Ratio: 0.581
Num phis: 28  H: 894  N2: 1640  Diff: -746  Ratio: 0.545
Num phis: 29  H: 936  N2: 1712  Diff: -776  Ratio: 0.547
Num phis: 30  H: 988  N2: 1863  Diff: -875  Ratio: 0.530
Num phis: 31  H: 1003  N2: 1940  Diff: -937  Ratio: 0.517
Num phis: 32  H: 1050  N2: 2100  Diff: -1050  Ratio: 0.500
Num phis: 33  H: 1071  N2: 2183  Diff: -1112  Ratio: 0.491`
...
```

---
Full diff: https://github.com/llvm/llvm-project/pull/135179.diff


3 Files Affected:

- (modified) llvm/include/llvm/Transforms/Utils/Local.h (+16) 
- (modified) llvm/lib/Transforms/Utils/Local.cpp (+154-49) 
- (modified) llvm/unittests/Transforms/Utils/LocalTest.cpp (+200) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h
index db064e1f41f02..06b0bc3611cff 100644
--- a/llvm/include/llvm/Transforms/Utils/Local.h
+++ b/llvm/include/llvm/Transforms/Utils/Local.h
@@ -175,6 +175,22 @@ bool EliminateDuplicatePHINodes(BasicBlock *BB);
 bool EliminateDuplicatePHINodes(BasicBlock *BB,
                                 SmallPtrSetImpl<PHINode *> &ToRemove);
 
+/// Check for and eliminate duplicate PHI nodes in the block. This function is
+/// specifically designed for scenarios where new PHI nodes are inserted into
+/// the beginning of the block, such when SSAUpdaterBulk::RewriteAllUses. It
+/// compares the newly inserted PHI nodes against the existing ones and if a
+/// new PHI node is found to be a duplicate of an existing one, the new node is
+/// removed. Existing PHI nodes are left unmodified, even if they are
+/// duplicates. New nodes are also deleted if they are duplicates of each other.
+/// Similar to EliminateDuplicatePHINodes, this function assumes a consistent
+/// order for all incoming values across PHI nodes in the block. FirstExistedPN
+/// Points to the first existing PHI node in the block. Newly inserted PHI nodes
+/// should not reference one another. However, they may reference themselves or
+/// existing PHI nodes, and existing PHI nodes may reference the newly inserted
+/// PHI nodes.
+bool EliminateNewDuplicatePHINodes(BasicBlock *BB,
+                                   BasicBlock::phi_iterator FirstExistedPN);
+
 /// This function is used to do simplification of a CFG.  For example, it
 /// adjusts branches to branches to eliminate the extra hop, it eliminates
 /// unreachable basic blocks, and does other peephole optimization of the CFG.
diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp
index 2f3ea2266e07f..271d6a4c3bdf3 100644
--- a/llvm/lib/Transforms/Utils/Local.cpp
+++ b/llvm/lib/Transforms/Utils/Local.cpp
@@ -1419,65 +1419,65 @@ EliminateDuplicatePHINodesNaiveImpl(BasicBlock *BB,
   return Changed;
 }
 
-static bool
-EliminateDuplicatePHINodesSetBasedImpl(BasicBlock *BB,
-                                       SmallPtrSetImpl<PHINode *> &ToRemove) {
-  // This implementation doesn't currently consider undef operands
-  // specially. Theoretically, two phis which are identical except for
-  // one having an undef where the other doesn't could be collapsed.
+// This implementation doesn't currently consider undef operands
+// specially. Theoretically, two phis which are identical except for
+// one having an undef where the other doesn't could be collapsed.
 
-  struct PHIDenseMapInfo {
-    static PHINode *getEmptyKey() {
-      return DenseMapInfo<PHINode *>::getEmptyKey();
-    }
+struct PHIDenseMapInfo {
+  static PHINode *getEmptyKey() {
+    return DenseMapInfo<PHINode *>::getEmptyKey();
+  }
 
-    static PHINode *getTombstoneKey() {
-      return DenseMapInfo<PHINode *>::getTombstoneKey();
-    }
+  static PHINode *getTombstoneKey() {
+    return DenseMapInfo<PHINode *>::getTombstoneKey();
+  }
 
-    static bool isSentinel(PHINode *PN) {
-      return PN == getEmptyKey() || PN == getTombstoneKey();
-    }
+  static bool isSentinel(const PHINode *PN) {
+    return PN == getEmptyKey() || PN == getTombstoneKey();
+  }
 
-    // WARNING: this logic must be kept in sync with
-    //          Instruction::isIdenticalToWhenDefined()!
-    static unsigned getHashValueImpl(PHINode *PN) {
-      // Compute a hash value on the operands. Instcombine will likely have
-      // sorted them, which helps expose duplicates, but we have to check all
-      // the operands to be safe in case instcombine hasn't run.
-      return static_cast<unsigned>(hash_combine(
-          hash_combine_range(PN->value_op_begin(), PN->value_op_end()),
-          hash_combine_range(PN->block_begin(), PN->block_end())));
-    }
+  // WARNING: this logic must be kept in sync with
+  //          Instruction::isIdenticalToWhenDefined()!
+  static unsigned getHashValueImpl(const PHINode *PN) {
+    // Compute a hash value on the operands. Instcombine will likely have
+    // sorted them, which helps expose duplicates, but we have to check all
+    // the operands to be safe in case instcombine hasn't run.
+    return static_cast<unsigned>(hash_combine(
+        hash_combine_range(PN->value_op_begin(), PN->value_op_end()),
+        hash_combine_range(PN->block_begin(), PN->block_end())));
+  }
 
-    static unsigned getHashValue(PHINode *PN) {
+  static unsigned getHashValue(const PHINode *PN) {
 #ifndef NDEBUG
-      // If -phicse-debug-hash was specified, return a constant -- this
-      // will force all hashing to collide, so we'll exhaustively search
-      // the table for a match, and the assertion in isEqual will fire if
-      // there's a bug causing equal keys to hash differently.
-      if (PHICSEDebugHash)
-        return 0;
+    // If -phicse-debug-hash was specified, return a constant -- this
+    // will force all hashing to collide, so we'll exhaustively search
+    // the table for a match, and the assertion in isEqual will fire if
+    // there's a bug causing equal keys to hash differently.
+    if (PHICSEDebugHash)
+      return 0;
 #endif
-      return getHashValueImpl(PN);
-    }
+    return getHashValueImpl(PN);
+  }
 
-    static bool isEqualImpl(PHINode *LHS, PHINode *RHS) {
-      if (isSentinel(LHS) || isSentinel(RHS))
-        return LHS == RHS;
-      return LHS->isIdenticalTo(RHS);
-    }
+  static bool isEqualImpl(const PHINode *LHS, const PHINode *RHS) {
+    if (isSentinel(LHS) || isSentinel(RHS))
+      return LHS == RHS;
+    return LHS->isIdenticalTo(RHS);
+  }
 
-    static bool isEqual(PHINode *LHS, PHINode *RHS) {
-      // These comparisons are nontrivial, so assert that equality implies
-      // hash equality (DenseMap demands this as an invariant).
-      bool Result = isEqualImpl(LHS, RHS);
-      assert(!Result || (isSentinel(LHS) && LHS == RHS) ||
-             getHashValueImpl(LHS) == getHashValueImpl(RHS));
-      return Result;
-    }
-  };
+  static bool isEqual(const PHINode *LHS, const PHINode *RHS) {
+    // These comparisons are nontrivial, so assert that equality implies
+    // hash equality (DenseMap demands this as an invariant).
+    bool Result = isEqualImpl(LHS, RHS);
+    assert(!Result || (isSentinel(LHS) && LHS == RHS) ||
+           getHashValueImpl(LHS) == getHashValueImpl(RHS));
+    return Result;
+  }
+};
 
+static bool
+EliminateDuplicatePHINodesSetBasedImpl(BasicBlock *BB,
+                                       SmallPtrSetImpl<PHINode *> &ToRemove) {
   // Set of unique PHINodes.
   DenseSet<PHINode *, PHIDenseMapInfo> PHISet;
   PHISet.reserve(4 * PHICSENumPHISmallSize);
@@ -1524,6 +1524,111 @@ bool llvm::EliminateDuplicatePHINodes(BasicBlock *BB) {
   return Changed;
 }
 
+#ifndef NDEBUG // Should this be under EXPENSIVE_CHECKS?
+// New PHI nodes should not reference one another but they may reference
+// themselves or existing PHI nodes, and existing PHI nodes may reference new
+// PHI nodes.
+static bool
+PHIAreRefEachOther(const iterator_range<BasicBlock::phi_iterator> &NewPHIs) {
+  SmallPtrSet<PHINode *, 8> NewPHISet;
+  for (PHINode &PN : NewPHIs)
+    NewPHISet.insert(&PN);
+  for (PHINode &PHI : NewPHIs) {
+    for (Value *V : PHI.incoming_values()) {
+      PHINode *IncPHI = dyn_cast<PHINode>(V);
+      if (IncPHI && IncPHI != &PHI && NewPHISet.contains(IncPHI))
+        return true;
+    }
+  }
+  return false;
+}
+#endif
+
+bool EliminateNewDuplicatePHINodesN2(BasicBlock *BB,
+                                     BasicBlock::phi_iterator FirstExistedPN) {
+  auto ReplaceIfIdentical = [](PHINode &PHI, PHINode &ReplPHI) {
+    if (!PHI.isIdenticalToWhenDefined(&ReplPHI))
+      return false;
+    PHI.replaceAllUsesWith(&ReplPHI);
+    PHI.eraseFromParent();
+    return true;
+  };
+
+  // Deduplicate new PHIs first to reduce the number of comparisons on the
+  // following new -> existing pass.
+  bool Changed = false;
+  for (auto I = BB->phis().begin(); I != FirstExistedPN; ++I) {
+    for (auto J = std::next(I); J != FirstExistedPN;) {
+      Changed |= ReplaceIfIdentical(*J++, *I);
+    }
+  }
+
+  // Iterate over existing PHIs and replace identical new PHIs.
+  for (PHINode &ExistingPHI : make_range(FirstExistedPN, BB->phis().end())) {
+    auto I = BB->phis().begin();
+    assert(I != FirstExistedPN); // Should be at least one new PHI.
+    do {
+      Changed |= ReplaceIfIdentical(*I++, ExistingPHI);
+    } while (I != FirstExistedPN);
+    if (BB->phis().begin() == FirstExistedPN)
+      return Changed;
+  }
+  return Changed;
+}
+
+bool EliminateNewDuplicatePHINodesSet(BasicBlock *BB,
+                                      BasicBlock::phi_iterator FirstExistedPN) {
+  auto Replace = [](PHINode &PHI, PHINode &ReplPHI) {
+    PHI.replaceAllUsesWith(&ReplPHI);
+    PHI.eraseFromParent();
+    return true;
+  };
+
+  DenseSet<PHINode *, PHIDenseMapInfo> NewPHISet;
+  NewPHISet.reserve(4 * PHICSENumPHISmallSize);
+
+  // Deduplicate new PHIs, note that NewPHISet remains consistent because new
+  // PHIs are not reference each other.
+  bool Changed = false;
+  for (PHINode &NewPHI :
+       make_early_inc_range(make_range(BB->phis().begin(), FirstExistedPN))) {
+    auto [I, Inserted] = NewPHISet.insert(&NewPHI);
+    if (!Inserted)
+      Changed |= Replace(NewPHI, **I);
+  }
+
+  // Iterate over existing PHIs and replace matching new PHIs.
+  for (PHINode &ExistingPHI : make_range(FirstExistedPN, BB->phis().end())) {
+    assert(!NewPHISet.empty()); // Should be at least one new PHI.
+    auto I = NewPHISet.find(&ExistingPHI);
+    if (I == NewPHISet.end())
+      continue;
+    Changed |= Replace(**I, ExistingPHI);
+    NewPHISet.erase(I);
+    if (NewPHISet.empty())
+      return Changed;
+  }
+  return Changed;
+}
+
+bool llvm::EliminateNewDuplicatePHINodes(
+    BasicBlock *BB, BasicBlock::phi_iterator FirstExistedPN) {
+  
+  if (hasNItemsOrLess(BB->phis(), 1))
+    return false;
+
+  auto NewPHIs = make_range(BB->phis().begin(), FirstExistedPN);
+  assert(!PHIAreRefEachOther(NewPHIs));
+
+  // Both functions perform identical pass over existing PHIs and differ in time
+  // spent on new PHI duplicate check which depends on the number of new PHIs.
+  // Therefore make a choice based on the number of new PHIs and not the total
+  // number of PHIs in the block.
+  return hasNItemsOrLess(NewPHIs, PHICSENumPHISmallSize)
+             ? EliminateNewDuplicatePHINodesN2(BB, FirstExistedPN)
+             : EliminateNewDuplicatePHINodesSet(BB, FirstExistedPN);
+}
+
 Align llvm::tryEnforceAlignment(Value *V, Align PrefAlign,
                                 const DataLayout &DL) {
   V = V->stripPointerCasts();
diff --git a/llvm/unittests/Transforms/Utils/LocalTest.cpp b/llvm/unittests/Transforms/Utils/LocalTest.cpp
index 3c7a892c9d65a..f7b658b17b6ef 100644
--- a/llvm/unittests/Transforms/Utils/LocalTest.cpp
+++ b/llvm/unittests/Transforms/Utils/LocalTest.cpp
@@ -1362,3 +1362,203 @@ TEST(Local, ReplaceDbgVariableRecord) {
   // Teardown.
   RetInst->DebugMarker->eraseFromParent();
 }
+
+bool EliminateNewDuplicatePHINodesN2(BasicBlock *BB,
+                                     BasicBlock::phi_iterator FirstExistedPN);
+bool EliminateNewDuplicatePHINodesSet(BasicBlock *BB,
+                                      BasicBlock::phi_iterator FirstExistedPN);
+
+TEST(Local, RaceEliminateNewDuplicatePHINodes) {
+  GTEST_SKIP(); // Comment out to run this test manually.
+  using namespace std::chrono;
+
+  LLVMContext C;
+  IRBuilder<> B(C);
+  std::unique_ptr<Function> F(
+      Function::Create(FunctionType::get(B.getVoidTy(), false),
+                       GlobalValue::ExternalLinkage, "F"));
+  BasicBlock *Entry(BasicBlock::Create(C, "", F.get()));
+  BasicBlock *BB(BasicBlock::Create(C, "", F.get()));
+  BranchInst::Create(BB, Entry);
+  B.SetInsertPoint(BB);
+  auto *Ret = B.CreateRetVoid();
+  B.SetInsertPoint(Ret);
+
+  const unsigned NumPreds = 5;
+  for (unsigned Pass = 1; Pass < 64; ++Pass) {
+    auto *PHI = B.CreatePHI(Type::getInt32Ty(C), NumPreds);
+    for (unsigned I = 0; I < NumPreds; ++I)
+      PHI->addIncoming(B.getInt32(Pass), Entry);
+
+    if (Pass < 2)
+      continue;
+
+    auto FirstExistedPN = std::next(BB->phis().begin(), Pass / 2);
+    const unsigned NumRuns = 1000000;
+
+    outs() << "Num phis: " << Pass;
+    auto Start = high_resolution_clock::now();
+    for (unsigned Run = NumRuns; Run > 0; --Run)
+      EliminateNewDuplicatePHINodesSet(BB, FirstExistedPN);
+    auto End = high_resolution_clock::now();
+    auto TH = duration_cast<milliseconds>(End - Start).count();
+    outs() << "  H: " << TH;
+
+    Start = high_resolution_clock::now();
+    for (unsigned Run = NumRuns; Run > 0; --Run)
+      EliminateNewDuplicatePHINodesN2(BB, FirstExistedPN);
+    End = high_resolution_clock::now();
+    auto TN2 = duration_cast<milliseconds>(End - Start).count();
+
+    outs() << "  N2: " << TN2 << "  Diff: " << (TH - TN2)
+           << "  Ratio: " << format("%.3f", ((double)TH / TN2)) << "\n";
+  }
+}
+
+// Helper to run both versions on the same input.
+static void RunEliminateNewDuplicatePHINode(
+    const char *AsmText,
+    std::function<void(BasicBlock &,
+                       bool(BasicBlock *BB, BasicBlock::phi_iterator))>
+        Check) {
+  LLVMContext C;
+  for (int Pass = 0; Pass < 2; ++Pass) {
+    std::unique_ptr<Module> M = parseIR(C, AsmText);
+    Function *F = M->getFunction("main");
+    auto BBIt = std::find_if(F->begin(), F->end(), [](const BasicBlock &Block) {
+      return Block.getName() == "testbb";
+    });
+    ASSERT_NE(BBIt, F->end());
+    Check(*BBIt, Pass == 0 ? EliminateNewDuplicatePHINodesSet
+                           : EliminateNewDuplicatePHINodesN2);
+  }
+}
+
+static BasicBlock::phi_iterator getPhiIt(BasicBlock &BB, unsigned Idx) {
+  return std::next(BB.phis().begin(), Idx);
+}
+
+static PHINode *getPhi(BasicBlock &BB, unsigned Idx) {
+  return &*getPhiIt(BB, Idx);
+}
+
+static int getNumPHIs(BasicBlock &BB) {
+  return std::distance(BB.phis().begin(), BB.phis().end());
+}
+
+TEST(Local, EliminateNewDuplicatePHINodes_OrderExisting) {
+  RunEliminateNewDuplicatePHINode(R"(
+      define void @main() {
+      entry:
+          br label %testbb
+      testbb:
+          %np0 = phi i32 [ 1, %entry ]
+          %np1 = phi i32 [ 1, %entry ]
+          %ep0 = phi i32 [ 1, %entry ]
+          %ep1 = phi i32 [ 1, %entry ]
+          %u = add i32 %np0, %np1
+          ret void
+      }
+  )", [](BasicBlock &BB, auto *ENDPN) {
+    AssertingVH<PHINode> EP0 = getPhi(BB, 2);
+    AssertingVH<PHINode> EP1 = getPhi(BB, 3);
+    EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
+    // Expected:
+    //   %ep0 = phi i32 [ 1, %entry ]
+    //   %ep1 = phi i32 [ 1, %entry ]
+    //   %u = add i32 %ep0, %ep0
+    EXPECT_EQ(getNumPHIs(BB), 2);
+    Instruction &Add = *BB.getFirstNonPHIIt();
+    EXPECT_EQ(Add.getOperand(0), EP0);
+    EXPECT_EQ(Add.getOperand(1), EP0);
+    (void)EP1; // Avoid "unused" warning.
+  });
+}
+
+TEST(Local, EliminateNewDuplicatePHINodes_OrderNew) {
+  RunEliminateNewDuplicatePHINode(R"(
+      define void @main() {
+      entry:
+          br label %testbb
+      testbb:
+          %np0 = phi i32 [ 1, %entry ]
+          %np1 = phi i32 [ 1, %entry ]
+          %ep0 = phi i32 [ 2, %entry ]
+          %ep1 = phi i32 [ 2, %entry ]
+          %u = add i32 %np0, %np1
+          ret void
+      }
+  )", [](BasicBlock &BB, auto *ENDPN) {
+    AssertingVH<PHINode> NP0 = getPhi(BB, 0);
+    AssertingVH<PHINode> EP0 = getPhi(BB, 2);
+    AssertingVH<PHINode> EP1 = getPhi(BB, 3);
+    EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
+    // Expected:
+    //   %np0 = phi i32 [ 1, %entry ]
+    //   %ep0 = phi i32 [ 2, %entry ]
+    //   %ep1 = phi i32 [ 2, %entry ]
+    //   %u = add i32 %np0, %np0
+    EXPECT_EQ(getNumPHIs(BB), 3);
+    Instruction &Add = *BB.getFirstNonPHIIt();
+    EXPECT_EQ(Add.getOperand(0), NP0);
+    EXPECT_EQ(Add.getOperand(1), NP0);
+    (void)EP0;
+    (void)EP1; // Avoid "unused" warning.
+  });
+}
+
+TEST(Local, EliminateNewDuplicatePHINodes_NewRefExisting) {
+  RunEliminateNewDuplicatePHINode(R"(
+      define void @main() {
+      entry:
+          br label %testbb
+      testbb:
+          %np0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
+          %np1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
+          %ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
+          %ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
+          %u = add i32 %np0, %np1
+          br label %testbb
+      }
+  )", [](BasicBlock &BB, auto *ENDPN) {
+    AssertingVH<PHINode> EP0 = getPhi(BB, 2);
+    AssertingVH<PHINode> EP1 = getPhi(BB, 3);
+    EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
+    // Expected:
+    //   %ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
+    //   %ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
+    //   %u = add i32 %ep0, %ep1
+    EXPECT_EQ(getNumPHIs(BB), 2);
+    Instruction &Add = *BB.getFirstNonPHIIt();
+    EXPECT_EQ(Add.getOperand(0), EP0);
+    EXPECT_EQ(Add.getOperand(1), EP1);
+  });
+}
+
+TEST(Local, EliminateNewDuplicatePHINodes_ExistingRefNew) {
+  RunEliminateNewDuplicatePHINode(R"(
+      define void @main() {
+      entry:
+          br label %testbb
+      testbb:
+          %np0 = phi i32 [ 1, %entry ], [ %np0, %testbb ]
+          %np1 = phi i32 [ 1, %entry ], [ %np1, %testbb ]
+          %ep0 = phi i32 [ 1, %entry ], [ %np0, %testbb ]
+          %ep1 = phi i32 [ 1, %entry ], [ %np1, %testbb ]
+          %u = add i32 %np0, %np1
+          br label %testbb
+      }
+  )", [](BasicBlock &BB, auto *ENDPN) {
+    AssertingVH<PHINode> EP0 = getPhi(BB, 2);
+    AssertingVH<PHINode> EP1 = getPhi(BB, 3);
+    EXPECT_TRUE(ENDPN(&BB, getPhiIt(BB, 2)));
+    // Expected:
+    //   %ep0 = phi i32 [ 1, %entry ], [ %ep0, %testbb ]
+    //   %ep1 = phi i32 [ 1, %entry ], [ %ep1, %testbb ]
+    //   %u = add i32 %ep0, %ep1
+    EXPECT_EQ(getNumPHIs(BB), 2);
+    Instruction &Add = *BB.getFirstNonPHIIt();
+    EXPECT_EQ(Add.getOperand(0), EP0);
+    EXPECT_EQ(Add.getOperand(1), EP1);
+  });
+}
\ No newline at end of file

``````````

</details>


https://github.com/llvm/llvm-project/pull/135179


More information about the llvm-commits mailing list