[llvm] [VPlan] Add vputils::reconstructSSA (PR #212209)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 24 03:40:02 PDT 2026
https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/212209
>From 032d55446433d3437a94dc9d47d3aeec39029ef6 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 27 Jul 2026 16:54:22 +0800
Subject: [PATCH 01/10] [VPlan] Add vputils::reconstructSSA
Split off from #201784
Adds a simple implementation of the algorithm described in "Simple and Efficient SSA Construction" by Braun et al. https://www.researchgate.net/publication/236997796_Simple_and_Efficient_Construction_of_Static_Single_Assignment_Form
This can be used to reconstruct SSA form whenever the control flow graph is changed, which #201784 plans to use to model predicated early exiting conditions.
Added three unit tests for now which should exercise all the code paths, including what happens when there's a cycle.
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 38 ++++++++
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 6 ++
.../Transforms/Vectorize/VPlanTest.cpp | 93 +++++++++++++++++++
3 files changed, 137 insertions(+)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 0bcd4dd8aba5b..487f965c65916 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1143,3 +1143,41 @@ void vputils::detail::pullOutPermutationsImpl(
}
}
}
+
+/// Implements the algorithm described in "Simple and Efficient Construction of
+/// Static Single Assignment Form" by Braun et al.
+static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
+ DenseMap<VPBasicBlock *, VPValue *> &Defs) {
+ if (VPValue *Def = Defs.lookup(VPBB))
+ return Def;
+ assert(VPBB->getNumPredecessors() && "Not all paths have def");
+
+ if (VPBlockBase *Pred = VPBB->getSinglePredecessor())
+ return reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
+
+ // Multiple predecessors, create a join.
+ Type *Ty = Defs.begin()->second->getScalarType();
+ auto *Phi = new VPPhi({}, {}, DebugLoc::getUnknown(), "", Ty);
+ VPBB->insert(Phi, VPBB->getFirstNonPhi());
+ Defs[VPBB] = Phi;
+ for (auto *Pred : VPBB->predecessors())
+ Phi->addIncoming(reconstructSSAImpl(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);
+ Phi->eraseFromParent();
+ Defs[VPBB] = Common;
+ return Common;
+ }
+
+ return Phi;
+}
+
+VPValue *vputils::reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs,
+ VPBasicBlock *VPBB) {
+ assert(!Defs.empty() && "Defs shouldn't be empty");
+ return reconstructSSAImpl(VPBB, Defs);
+}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 8c0690a82cf95..88de0d4ee7dc4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -214,6 +214,12 @@ SmallVector<VPUser *> collectUsersRecursively(VPValue *V);
VPIRValue *tryToFoldLiveIns(VPSingleDefRecipe &R, ArrayRef<VPValue *> Operands,
const DataLayout &DL);
+/// Insert phis to reconstruct SSA for a single value starting from \p VPBB. \p
+/// Defs is a map of definitions at specific blocks. Returns the
+/// reconstructed value at VPBB. Use if the CFG has been modified such that a
+/// def no longer dominates all its uses.
+VPValue *reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs,
+ VPBasicBlock *VPBB);
namespace detail {
/// Template-independent implementation for pullOutPermutations.
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index f10435dfb2da6..d8fba47428389 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -1898,6 +1898,99 @@ TEST_F(VPUtilsTest, IsUniformAcrossVFsAndUFsForSingleScalarOpcodes) {
EXPECT_FALSE(vputils::isUniformAcrossVFsAndUFs(FirstActiveLaneNonUniform));
}
+TEST_F(VPUtilsTest, ReconstructSSA) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // / \
+ // VPBB2 VPBB3
+ // \ /
+ // VPBB4
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB4);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+ auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB2->appendRecipe(Def2);
+
+ auto *Res = cast<VPPhi>(
+ vputils::reconstructSSA({{VPBB1, Def1}, {VPBB2, Def2}}, VPBB4));
+ EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def2);
+ EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Def1);
+}
+
+TEST_F(VPUtilsTest, ReconstructSSAFold) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // / \
+ // VPBB2 VPBB3
+ // \ /
+ // VPBB4
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB4);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def);
+
+ // Check that phis with all equal incoming values are folded away.
+ EXPECT_EQ(vputils::reconstructSSA({{VPBB2, Def}, {VPBB3, Def}}, VPBB4), Def);
+}
+
+TEST_F(VPUtilsTest, ReconstructSSACycle) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // |
+ // VPBB2
+ // / | ^
+ // VPBB3 | |
+ // \ | /
+ // VPBB4
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB4, VPBB2);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+ auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB3->appendRecipe(Def2);
+
+ auto *Phi1 = cast<VPPhi>(
+ vputils::reconstructSSA({{VPBB1, Def1}, {VPBB3, Def2}}, VPBB4));
+ EXPECT_EQ(Phi1->getIncomingValueForBlock(VPBB3), Def2);
+ EXPECT_TRUE(isa<VPPhi>(Phi1->getIncomingValueForBlock(VPBB2)));
+
+ auto *Phi2 = cast<VPPhi>(Phi1->getIncomingValueForBlock(VPBB2));
+ EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB4), Phi1);
+ EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB1), Def1);
+}
+
TEST_F(VPBasicBlockTest, VPRegionValueClonePropagatesMaterialized) {
VPlan &Plan = getPlan();
VPBasicBlock *Preheader = Plan.getEntry();
>From bab0cc606489d7b4de592fb7af6fd8aa328ebacf Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 28 Jul 2026 01:47:39 +0800
Subject: [PATCH 02/10] Add test with poison and test with multiple phis
---
.../Transforms/Vectorize/VPlanTest.cpp | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index d8fba47428389..17d6a176dc490 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -1928,6 +1928,79 @@ TEST_F(VPUtilsTest, ReconstructSSA) {
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Def1);
}
+TEST_F(VPUtilsTest, ReconstructSSAPoisonExample) {
+ // Test that the resulting phi isn't affected by the parent block of any
+ // definition. The resulting value in VPBB4 should be Def1 on any path that
+ // goes through VPPB2, and poison otherwise.
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // / \
+ // VPBB2 VPBB3
+ // \ /
+ // VPBB4
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB4);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPValue *Poison = Plan.getPoison(C->getScalarType());
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+
+ auto *Res = cast<VPPhi>(
+ vputils::reconstructSSA({{VPBB1, Poison}, {VPBB2, Def1}}, VPBB4));
+ EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def1);
+ EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Poison);
+}
+
+TEST_F(VPUtilsTest, ReconstructSSAMultiplePhis) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB4 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB5 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB6 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // / \
+ // VPBB2 VPBB3
+ // \ / \
+ // VPBB4 VPBB5
+ // \ /
+ // VPBB6
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB4);
+ VPBlockUtils::connectBlocks(VPBB3, VPBB5);
+ VPBlockUtils::connectBlocks(VPBB4, VPBB6);
+ VPBlockUtils::connectBlocks(VPBB5, VPBB6);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB2->appendRecipe(Def2);
+ auto *Def3 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB3->appendRecipe(Def3);
+
+ auto *Phi6 = cast<VPPhi>(
+ vputils::reconstructSSA({{VPBB2, Def2}, {VPBB3, Def3}}, VPBB6));
+ EXPECT_EQ(Phi6->getIncomingValueForBlock(VPBB5), Def3);
+ EXPECT_TRUE(isa<VPPhi>(Phi6->getIncomingValueForBlock(VPBB4)));
+
+ auto *Phi4 = cast<VPPhi>(Phi6->getIncomingValueForBlock(VPBB4));
+ EXPECT_EQ(Phi4->getIncomingValueForBlock(VPBB2), Def2);
+ EXPECT_EQ(Phi4->getIncomingValueForBlock(VPBB3), Def3);
+}
+
TEST_F(VPUtilsTest, ReconstructSSAFold) {
VPlan &Plan = getPlan();
VPBasicBlock *VPBB1 = Plan.getEntry();
>From b23526e7ae8eb8c7a633b3bde837b9c00e8fec86 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Tue, 28 Jul 2026 12:09:09 +0800
Subject: [PATCH 03/10] Add comment
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 487f965c65916..82fb8140d9d55 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1150,6 +1150,8 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
DenseMap<VPBasicBlock *, VPValue *> &Defs) {
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())
>From 48eec68e282ea6e1569a6a143b45337c10a2247d Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 17:12:36 +0800
Subject: [PATCH 04/10] Add LLVM_ABI_FOR_TEST
---
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index cb4cc7b9cc277..3705680c85d7b 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -225,8 +225,9 @@ VPIRValue *tryToFoldLiveIns(VPSingleDefRecipe &R, ArrayRef<VPValue *> Operands,
/// Defs is a map of definitions at specific blocks. Returns the
/// reconstructed value at VPBB. Use if the CFG has been modified such that a
/// def no longer dominates all its uses.
-VPValue *reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs,
- VPBasicBlock *VPBB);
+LLVM_ABI_FOR_TEST VPValue *
+reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs, VPBasicBlock *VPBB);
+
namespace detail {
/// Template-independent implementation for pullOutPermutations.
>From ff658fb650f6aafa9f2c5c0348695981f0a83255 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 17:46:42 +0800
Subject: [PATCH 05/10] Reorder arguments
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 4 ++--
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 2 +-
llvm/unittests/Transforms/Vectorize/VPlanTest.cpp | 10 +++++-----
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 97a6dbc4724ad..4ec87df214f55 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1196,8 +1196,8 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
return Phi;
}
-VPValue *vputils::reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs,
- VPBasicBlock *VPBB) {
+VPValue *vputils::reconstructSSA(VPBasicBlock *VPBB,
+ DenseMap<VPBasicBlock *, VPValue *> Defs) {
assert(!Defs.empty() && "Defs shouldn't be empty");
return reconstructSSAImpl(VPBB, Defs);
}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 3705680c85d7b..0d3cffd50fb31 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -226,7 +226,7 @@ VPIRValue *tryToFoldLiveIns(VPSingleDefRecipe &R, ArrayRef<VPValue *> Operands,
/// reconstructed value at VPBB. Use if the CFG has been modified such that a
/// def no longer dominates all its uses.
LLVM_ABI_FOR_TEST VPValue *
-reconstructSSA(DenseMap<VPBasicBlock *, VPValue *> Defs, VPBasicBlock *VPBB);
+reconstructSSA(VPBasicBlock *VPBB, DenseMap<VPBasicBlock *, VPValue *> Defs);
namespace detail {
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 7b7e2699661ea..73813c0c78355 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -1925,7 +1925,7 @@ TEST_F(VPUtilsTest, ReconstructSSA) {
VPBB2->appendRecipe(Def2);
auto *Res = cast<VPPhi>(
- vputils::reconstructSSA({{VPBB1, Def1}, {VPBB2, Def2}}, VPBB4));
+ vputils::reconstructSSA(VPBB4, {{VPBB1, Def1}, {VPBB2, Def2}}));
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def2);
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Def1);
}
@@ -1957,7 +1957,7 @@ TEST_F(VPUtilsTest, ReconstructSSAPoisonExample) {
VPBB1->appendRecipe(Def1);
auto *Res = cast<VPPhi>(
- vputils::reconstructSSA({{VPBB1, Poison}, {VPBB2, Def1}}, VPBB4));
+ vputils::reconstructSSA(VPBB4, {{VPBB1, Poison}, {VPBB2, Def1}}));
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def1);
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Poison);
}
@@ -1994,7 +1994,7 @@ TEST_F(VPUtilsTest, ReconstructSSAMultiplePhis) {
VPBB3->appendRecipe(Def3);
auto *Phi6 = cast<VPPhi>(
- vputils::reconstructSSA({{VPBB2, Def2}, {VPBB3, Def3}}, VPBB6));
+ vputils::reconstructSSA(VPBB6, {{VPBB2, Def2}, {VPBB3, Def3}}));
EXPECT_EQ(Phi6->getIncomingValueForBlock(VPBB5), Def3);
EXPECT_TRUE(isa<VPPhi>(Phi6->getIncomingValueForBlock(VPBB4)));
@@ -2026,7 +2026,7 @@ TEST_F(VPUtilsTest, ReconstructSSAFold) {
VPBB1->appendRecipe(Def);
// Check that phis with all equal incoming values are folded away.
- EXPECT_EQ(vputils::reconstructSSA({{VPBB2, Def}, {VPBB3, Def}}, VPBB4), Def);
+ EXPECT_EQ(vputils::reconstructSSA(VPBB4, {{VPBB2, Def}, {VPBB3, Def}}), Def);
}
TEST_F(VPUtilsTest, ReconstructSSACycle) {
@@ -2057,7 +2057,7 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
VPBB3->appendRecipe(Def2);
auto *Phi1 = cast<VPPhi>(
- vputils::reconstructSSA({{VPBB1, Def1}, {VPBB3, Def2}}, VPBB4));
+ vputils::reconstructSSA(VPBB4, {{VPBB1, Def1}, {VPBB3, Def2}}));
EXPECT_EQ(Phi1->getIncomingValueForBlock(VPBB3), Def2);
EXPECT_TRUE(isa<VPPhi>(Phi1->getIncomingValueForBlock(VPBB2)));
>From ec23daa3031e8f96cc5ef5529dae7e05a4af013e Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 19:22:26 +0800
Subject: [PATCH 06/10] Handle cycles and add test for duplicate predecessors
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 8 ++--
.../Transforms/Vectorize/VPlanTest.cpp | 38 +++++++++++++++++++
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 4ec87df214f55..823c1f0c5a1fa 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1173,9 +1173,10 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
assert(VPBB->getNumPredecessors() && "Not all paths have def");
if (VPBlockBase *Pred = VPBB->getSinglePredecessor())
- return reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
+ if (Pred != VPBB)
+ reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
- // Multiple predecessors, create a join.
+ // Create a join over the predecessors.
Type *Ty = Defs.begin()->second->getScalarType();
auto *Phi = new VPPhi({}, {}, DebugLoc::getUnknown(), "", Ty);
VPBB->insert(Phi, VPBB->getFirstNonPhi());
@@ -1188,7 +1189,8 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
if (all_equal(Phi->incoming_values())) {
VPValue *Common = Phi->getIncomingValue(0);
Phi->replaceAllUsesWith(Common);
- Phi->eraseFromParent();
+ if (Phi->getNumUsers() == 0)
+ Phi->eraseFromParent();
Defs[VPBB] = Common;
return Common;
}
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 73813c0c78355..1005e11f35f8a 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -2066,6 +2066,44 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB1), Def1);
}
+TEST_F(VPUtilsTest, ReconstructSSAUnreachableCycle) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+
+ // VPBB1 VPBB2 <-+
+ // | |
+ // +-----+
+ VPBlockUtils::connectBlocks(VPBB2, VPBB2);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+
+ EXPECT_TRUE(isa<VPPhi>(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}})));
+}
+
+TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+
+ // VPBB1
+ // / \
+ // \ /
+ // VPBB2
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+
+ EXPECT_EQ(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}}), Def1);
+}
+
TEST_F(VPBasicBlockTest, VPRegionValueClonePropagatesMaterialized) {
VPlan &Plan = getPlan();
VPBasicBlock *Preheader = Plan.getEntry();
>From e0e0f81ef2fa95c4a653d10d203672efbc400007 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 22:33:06 +0800
Subject: [PATCH 07/10] Don't try to handle cycles, just assert
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 9 ++++-----
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 2 +-
.../Transforms/Vectorize/VPlanTest.cpp | 18 ------------------
3 files changed, 5 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 823c1f0c5a1fa..18dfaa9a19cb2 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1173,10 +1173,9 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
assert(VPBB->getNumPredecessors() && "Not all paths have def");
if (VPBlockBase *Pred = VPBB->getSinglePredecessor())
- if (Pred != VPBB)
- reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
+ return reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
- // Create a join over the predecessors.
+ // Multiple predecessors, create a join.
Type *Ty = Defs.begin()->second->getScalarType();
auto *Phi = new VPPhi({}, {}, DebugLoc::getUnknown(), "", Ty);
VPBB->insert(Phi, VPBB->getFirstNonPhi());
@@ -1189,8 +1188,7 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
if (all_equal(Phi->incoming_values())) {
VPValue *Common = Phi->getIncomingValue(0);
Phi->replaceAllUsesWith(Common);
- if (Phi->getNumUsers() == 0)
- Phi->eraseFromParent();
+ Phi->eraseFromParent();
Defs[VPBB] = Common;
return Common;
}
@@ -1201,5 +1199,6 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
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");
return reconstructSSAImpl(VPBB, Defs);
}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 0d3cffd50fb31..0a4fd48fb500e 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -224,7 +224,7 @@ VPIRValue *tryToFoldLiveIns(VPSingleDefRecipe &R, ArrayRef<VPValue *> Operands,
/// Insert phis to reconstruct SSA for a single value starting from \p VPBB. \p
/// Defs is a map of definitions at specific blocks. Returns the
/// reconstructed value at VPBB. Use if the CFG has been modified such that a
-/// def no longer dominates all its uses.
+/// def no longer dominates all its uses. VPBB must be reachable from the entry.
LLVM_ABI_FOR_TEST VPValue *
reconstructSSA(VPBasicBlock *VPBB, DenseMap<VPBasicBlock *, VPValue *> Defs);
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 1005e11f35f8a..1dd06bff88662 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -2066,24 +2066,6 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB1), Def1);
}
-TEST_F(VPUtilsTest, ReconstructSSAUnreachableCycle) {
- VPlan &Plan = getPlan();
- VPBasicBlock *VPBB1 = Plan.getEntry();
- VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
-
- // VPBB1 VPBB2 <-+
- // | |
- // +-----+
- VPBlockUtils::connectBlocks(VPBB2, VPBB2);
-
- VPValue *C = Plan.getConstantInt(32, 1);
- VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
- auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
- VPBB1->appendRecipe(Def1);
-
- EXPECT_TRUE(isa<VPPhi>(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}})));
-}
-
TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
VPlan &Plan = getPlan();
VPBasicBlock *VPBB1 = Plan.getEntry();
>From c3f7675f31b9da804fe7ba9b6056c76858ab8384 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 23:24:10 +0800
Subject: [PATCH 08/10] Add back test with EXPECT_DEATH
---
.../Transforms/Vectorize/VPlanTest.cpp | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 1dd06bff88662..501f454880302 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -2066,6 +2066,25 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB1), Def1);
}
+TEST_F(VPUtilsTest, ReconstructSSAUnreachableCycle) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+
+ // VPBB1 VPBB2 <-+
+ // | |
+ // +-----+
+ VPBlockUtils::connectBlocks(VPBB2, VPBB2);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+
+ EXPECT_DEATH(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}}),
+ "VPlan without any entry node without predecessors");
+}
+
TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
VPlan &Plan = getPlan();
VPBasicBlock *VPBB1 = Plan.getEntry();
>From 0c4a5c8f095613fd295fe8451a538ba94cbcad76 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 19 Aug 2026 23:39:53 +0800
Subject: [PATCH 09/10] Flesh out duplicate predecessor tests
---
.../Transforms/Vectorize/VPlanTest.cpp | 39 +++++++++++++++++--
1 file changed, 35 insertions(+), 4 deletions(-)
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 501f454880302..22a40198e67fc 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -2089,19 +2089,50 @@ TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
VPlan &Plan = getPlan();
VPBasicBlock *VPBB1 = Plan.getEntry();
VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+
+ // VPBB1 VPPB2
+ // / \ /
+ // \ / /
+ // VPBB3
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB3);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+ auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB2->appendRecipe(Def2);
+
+ auto *Phi = cast<VPPhi>(
+ vputils::reconstructSSA(VPBB3, {{VPBB1, Def1}, {VPBB2, Def2}}));
+ EXPECT_EQ(Phi->getIncomingValue(0), Def1);
+ EXPECT_EQ(Phi->getIncomingValue(1), Def1);
+ EXPECT_EQ(Phi->getIncomingValue(2), Def2);
+}
+
+TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessorAllEqual) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
// VPBB1
- // / \
- // \ /
- // VPBB2
- VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ // / \ \
+ // / \ \
+ // VPBB2 VPBB3
VPBlockUtils::connectBlocks(VPBB1, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
VPValue *C = Plan.getConstantInt(32, 1);
VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB1->appendRecipe(Def1);
+ EXPECT_EQ(vputils::reconstructSSA(VPBB3, {{VPBB1, Def1}}), Def1);
EXPECT_EQ(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}}), Def1);
}
>From cf604e841b5f88ea963abb03d3c2d7d719969a01 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 24 Aug 2026 18:38:00 +0800
Subject: [PATCH 10/10] Address review comments
* Use reference for densemap
* Pass phi default flags
* Document plain-cfg only
* Add EXPECT_DEATH def guard
* Assert every block is reachable
---
llvm/lib/Transforms/Vectorize/VPlanUtils.cpp | 24 +++----
llvm/lib/Transforms/Vectorize/VPlanUtils.h | 6 +-
.../Transforms/Vectorize/VPlanTest.cpp | 66 ++++++++++++++-----
3 files changed, 63 insertions(+), 33 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
index 5e9241910dd9b..2de0e354de5b4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.cpp
@@ -1221,10 +1221,12 @@ void vputils::detail::pullOutPermutationsImpl(
}
}
-/// Implements the algorithm described in "Simple and Efficient Construction of
-/// Static Single Assignment Form" by Braun et al.
-static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
- DenseMap<VPBasicBlock *, VPValue *> &Defs) {
+// 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
@@ -1232,15 +1234,16 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
assert(VPBB->getNumPredecessors() && "Not all paths have def");
if (VPBlockBase *Pred = VPBB->getSinglePredecessor())
- return reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs);
+ return reconstructSSA(cast<VPBasicBlock>(Pred), Defs);
// Multiple predecessors, create a join.
Type *Ty = Defs.begin()->second->getScalarType();
- auto *Phi = new VPPhi({}, {}, DebugLoc::getUnknown(), "", Ty);
+ 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(reconstructSSAImpl(cast<VPBasicBlock>(Pred), Defs));
+ Phi->addIncoming(reconstructSSA(cast<VPBasicBlock>(Pred), Defs));
// Fold away trivial phis.
// TODO: Remove phi users which have become trivial too.
@@ -1254,10 +1257,3 @@ static VPValue *reconstructSSAImpl(VPBasicBlock *VPBB,
return Phi;
}
-
-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");
- return reconstructSSAImpl(VPBB, Defs);
-}
diff --git a/llvm/lib/Transforms/Vectorize/VPlanUtils.h b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
index 8d89b711c9315..e082fe68acf85 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanUtils.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanUtils.h
@@ -224,9 +224,11 @@ VPIRValue *tryToFoldLiveIns(VPSingleDefRecipe &R, ArrayRef<VPValue *> Operands,
/// Insert phis to reconstruct SSA for a single value starting from \p VPBB. \p
/// Defs is a map of definitions at specific blocks. Returns the
/// reconstructed value at VPBB. Use if the CFG has been modified such that a
-/// def no longer dominates all its uses. VPBB must be reachable from the entry.
+/// def no longer dominates all its uses. Every block leading to VPBB must be
+/// reachable from the entry and the plan must be plain-CFG (not contain any
+/// regions).
LLVM_ABI_FOR_TEST VPValue *
-reconstructSSA(VPBasicBlock *VPBB, DenseMap<VPBasicBlock *, VPValue *> Defs);
+reconstructSSA(VPBasicBlock *VPBB, DenseMap<VPBasicBlock *, VPValue *> &Defs);
namespace detail {
diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 22a40198e67fc..f046b4e9cc842 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -1924,8 +1924,8 @@ TEST_F(VPUtilsTest, ReconstructSSA) {
auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB2->appendRecipe(Def2);
- auto *Res = cast<VPPhi>(
- vputils::reconstructSSA(VPBB4, {{VPBB1, Def1}, {VPBB2, Def2}}));
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}, {VPBB2, Def2}};
+ auto *Res = cast<VPPhi>(vputils::reconstructSSA(VPBB4, Defs));
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def2);
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Def1);
}
@@ -1956,8 +1956,8 @@ TEST_F(VPUtilsTest, ReconstructSSAPoisonExample) {
auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB1->appendRecipe(Def1);
- auto *Res = cast<VPPhi>(
- vputils::reconstructSSA(VPBB4, {{VPBB1, Poison}, {VPBB2, Def1}}));
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Poison}, {VPBB2, Def1}};
+ auto *Res = cast<VPPhi>(vputils::reconstructSSA(VPBB4, Defs));
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB2), Def1);
EXPECT_EQ(Res->getIncomingValueForBlock(VPBB3), Poison);
}
@@ -1993,8 +1993,8 @@ TEST_F(VPUtilsTest, ReconstructSSAMultiplePhis) {
auto *Def3 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB3->appendRecipe(Def3);
- auto *Phi6 = cast<VPPhi>(
- vputils::reconstructSSA(VPBB6, {{VPBB2, Def2}, {VPBB3, Def3}}));
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB2, Def2}, {VPBB3, Def3}};
+ auto *Phi6 = cast<VPPhi>(vputils::reconstructSSA(VPBB6, Defs));
EXPECT_EQ(Phi6->getIncomingValueForBlock(VPBB5), Def3);
EXPECT_TRUE(isa<VPPhi>(Phi6->getIncomingValueForBlock(VPBB4)));
@@ -2026,7 +2026,8 @@ TEST_F(VPUtilsTest, ReconstructSSAFold) {
VPBB1->appendRecipe(Def);
// Check that phis with all equal incoming values are folded away.
- EXPECT_EQ(vputils::reconstructSSA(VPBB4, {{VPBB2, Def}, {VPBB3, Def}}), Def);
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB2, Def}, {VPBB3, Def}};
+ EXPECT_EQ(vputils::reconstructSSA(VPBB4, Defs), Def);
}
TEST_F(VPUtilsTest, ReconstructSSACycle) {
@@ -2056,8 +2057,8 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB3->appendRecipe(Def2);
- auto *Phi1 = cast<VPPhi>(
- vputils::reconstructSSA(VPBB4, {{VPBB1, Def1}, {VPBB3, Def2}}));
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}, {VPBB3, Def2}};
+ auto *Phi1 = cast<VPPhi>(vputils::reconstructSSA(VPBB4, Defs));
EXPECT_EQ(Phi1->getIncomingValueForBlock(VPBB3), Def2);
EXPECT_TRUE(isa<VPPhi>(Phi1->getIncomingValueForBlock(VPBB2)));
@@ -2066,6 +2067,7 @@ TEST_F(VPUtilsTest, ReconstructSSACycle) {
EXPECT_EQ(Phi2->getIncomingValueForBlock(VPBB1), Def1);
}
+#if defined(GTEST_HAS_DEATH_TEST) && !defined(NDEBUG)
TEST_F(VPUtilsTest, ReconstructSSAUnreachableCycle) {
VPlan &Plan = getPlan();
VPBasicBlock *VPBB1 = Plan.getEntry();
@@ -2081,9 +2083,35 @@ TEST_F(VPUtilsTest, ReconstructSSAUnreachableCycle) {
auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB1->appendRecipe(Def1);
- EXPECT_DEATH(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}}),
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}};
+ EXPECT_DEATH(vputils::reconstructSSA(VPBB2, Defs),
+ "VPlan without any entry node without predecessors");
+}
+
+TEST_F(VPUtilsTest, ReconstructSSAUnreachableCyclePredecessor) {
+ VPlan &Plan = getPlan();
+ VPBasicBlock *VPBB1 = Plan.getEntry();
+ VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
+ VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
+
+ // VPBB1 VPBB2 <-+
+ // \ / | |
+ // \ / +-----+
+ // VPBB3
+ VPBlockUtils::connectBlocks(VPBB2, VPBB2);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB2, VPBB3);
+
+ VPValue *C = Plan.getConstantInt(32, 1);
+ VPIRFlags AddFlags = VPIRFlags::getDefaultFlags(Instruction::Add);
+ auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
+ VPBB1->appendRecipe(Def1);
+
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}};
+ EXPECT_DEATH(vputils::reconstructSSA(VPBB3, Defs),
"VPlan without any entry node without predecessors");
}
+#endif
TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
VPlan &Plan = getPlan();
@@ -2091,12 +2119,14 @@ TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
VPBasicBlock *VPBB3 = Plan.createVPBasicBlock("");
- // VPBB1 VPPB2
- // / \ /
- // \ / /
+ // VPBB1
+ // / \ \
+ // | | VPBB2
+ // \ / /
// VPBB3
VPBlockUtils::connectBlocks(VPBB1, VPBB3);
VPBlockUtils::connectBlocks(VPBB1, VPBB3);
+ VPBlockUtils::connectBlocks(VPBB1, VPBB2);
VPBlockUtils::connectBlocks(VPBB2, VPBB3);
VPValue *C = Plan.getConstantInt(32, 1);
@@ -2106,8 +2136,8 @@ TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessor) {
auto *Def2 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB2->appendRecipe(Def2);
- auto *Phi = cast<VPPhi>(
- vputils::reconstructSSA(VPBB3, {{VPBB1, Def1}, {VPBB2, Def2}}));
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}, {VPBB2, Def2}};
+ auto *Phi = cast<VPPhi>(vputils::reconstructSSA(VPBB3, Defs));
EXPECT_EQ(Phi->getIncomingValue(0), Def1);
EXPECT_EQ(Phi->getIncomingValue(1), Def1);
EXPECT_EQ(Phi->getIncomingValue(2), Def2);
@@ -2132,8 +2162,10 @@ TEST_F(VPUtilsTest, ReconstructSSADuplicatePredecessorAllEqual) {
auto *Def1 = new VPInstruction(Instruction::Add, {C, C}, AddFlags);
VPBB1->appendRecipe(Def1);
- EXPECT_EQ(vputils::reconstructSSA(VPBB3, {{VPBB1, Def1}}), Def1);
- EXPECT_EQ(vputils::reconstructSSA(VPBB2, {{VPBB1, Def1}}), Def1);
+ DenseMap<VPBasicBlock *, VPValue *> Defs = {{VPBB1, Def1}};
+ EXPECT_EQ(vputils::reconstructSSA(VPBB3, Defs), Def1);
+ Defs = {{VPBB1, Def1}};
+ EXPECT_EQ(vputils::reconstructSSA(VPBB2, Defs), Def1);
}
TEST_F(VPBasicBlockTest, VPRegionValueClonePropagatesMaterialized) {
More information about the llvm-commits
mailing list