[llvm] [VPlan] Add vputils::reconstructSSA (PR #212209)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 13:59:30 PDT 2026


================
@@ -1366,3 +1366,40 @@ void vputils::detail::pullOutPermutationsImpl(
     }
   }
 }
+
+// Implements the algorithm described in "Simple and Efficient Construction of
+// Static Single Assignment Form" by Braun et al.
+VPValue *vputils::reconstructSSA(VPBasicBlock *VPBB,
+                                 DenseMap<VPBasicBlock *, VPValue *> &Defs) {
+  assert(!Defs.empty() && "Defs shouldn't be empty");
+  assert(VPBB->getPlan() && "VPBB isn't reachable from entry");
+  if (VPValue *Def = Defs.lookup(VPBB))
+    return Def;
+  // If the entry block is reached and there's still no def, then Defs is
+  // missing a definition that covers this path.
+  assert(VPBB->getNumPredecessors() && "Not all paths have def");
+
+  if (VPBlockBase *Pred = VPBB->getSinglePredecessor())
+    return reconstructSSA(cast<VPBasicBlock>(Pred), Defs);
+
+  // Multiple predecessors, create a join.
+  Type *Ty = Defs.begin()->second->getScalarType();
+  auto *Phi = new VPPhi({}, VPIRFlags::getDefaultFlags(Instruction::PHI, Ty),
+                        DebugLoc::getUnknown(), "", Ty);
+  VPBB->insert(Phi, VPBB->getFirstNonPhi());
+  Defs[VPBB] = Phi;
+  for (auto *Pred : VPBB->predecessors())
+    Phi->addIncoming(reconstructSSA(cast<VPBasicBlock>(Pred), Defs));
+
+  // Fold away trivial phis.
+  // TODO: Remove phi users which have become trivial too.
+  if (all_equal(Phi->incoming_values())) {
+    VPValue *Common = Phi->getIncomingValue(0);
+    Phi->replaceAllUsesWith(Common);
----------------
fhahn wrote:

I think there may be cases where we add `Phi` to `Defs` for a different block in one of the recursions above, if in the recursion we fold another phi to `Phi`.

Then we may end up with a pointer to the deleted `Phi` in `Defs`, which may trigger use-after-free. Could we just rely on using regular VPlan DCE? Or remove all dead phis at the end?

Here's a test case that should show use-after-free with UBSan (created with assistance of AI)

```
TEST_F(VPUtilsTest, ReconstructSSAStaleDefsCrash) {
  VPlan &Plan = getPlan();
  VPBasicBlock *Y = Plan.getEntry();
  VPBasicBlock *Pj = Plan.createVPBasicBlock("");
  VPBasicBlock *Pk = Plan.createVPBasicBlock("");
  VPBasicBlock *B = Plan.createVPBasicBlock("");
  VPBasicBlock *X = Plan.createVPBasicBlock("");
  VPBasicBlock *Xb = Plan.createVPBasicBlock("");
  VPBasicBlock *T = Plan.createVPBasicBlock("");

  VPBlockUtils::connectBlocks(Y, Pj);
  VPBlockUtils::connectBlocks(X, Pj);
  VPBlockUtils::connectBlocks(Pj, B);
  VPBlockUtils::connectBlocks(Pj, Pk);
  VPBlockUtils::connectBlocks(Pk, B);
  VPBlockUtils::connectBlocks(B, X);
  VPBlockUtils::connectBlocks(B, Xb);
  VPBlockUtils::connectBlocks(Xb, X);
  VPBlockUtils::connectBlocks(B, T);
  VPBlockUtils::connectBlocks(X, T);

  VPValue *C = Plan.getConstantInt(32, 1);
  VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
  auto *Def = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
  Y->appendRecipe(Def);

  DenseMap<VPBasicBlock *, VPValue *> Defs;
  Defs[Y] = Def;
  EXPECT_NE(vputils::reconstructSSA(T, Defs), nullptr);
}
```



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


More information about the llvm-commits mailing list