[llvm] [VPlan] Add VPInst::getNumOperandsForOpcode, use to verify in ctor (NFC) (PR #142284)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 21 02:43:12 PDT 2025


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/142284

>From 2ab3df5245bf142386eaed027688467b07c3a8f5 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 31 May 2025 16:04:10 +0100
Subject: [PATCH 1/4] [VPlan] Add VPInst::getNumOperandsForOpcode, use to
 verify in ctor (NFC)

Add a new getNumOperandsForOpcode helper to determine the number of
operands from the opcode. For now, it is used to verify the number
operands at VPInstruction construction.

It returns -1 for a few opcodes where the number of operands cannot be
determined (GEP, Switch, PHI, Call).

This can also be used in a follow-up to determine if a VPInstruction is
masked based on the number of arguments.
---
 llvm/lib/Transforms/Vectorize/VPlan.h         |  7 +++
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 59 ++++++++++++++++++-
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 44f0b6d964a6e..bd6a3247abd7d 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -967,6 +967,13 @@ class VPInstruction : public VPRecipeWithIRFlags,
   /// value for lane \p Lane.
   Value *generatePerLane(VPTransformState &State, const VPLane &Lane);
 
+#if !defined(NDEBUG)
+  /// Return the number of operands determined by the opcode of the
+  /// VPInstruction. Returns -1 if the number of operands cannot be determined
+  /// directly by the opcode.
+  unsigned getNumOperandsForOpcode() const;
+#endif
+
 public:
   VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands, DebugLoc DL = {},
                 const Twine &Name = "")
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index a4831ea7c11f7..5c6e4aeaf3cad 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -413,8 +413,62 @@ VPInstruction::VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands,
       Opcode(Opcode), Name(Name.str()) {
   assert(flagsValidForOpcode(getOpcode()) &&
          "Set flags not supported for the provided opcode");
+  assert((getNumOperandsForOpcode() == -1u ||
+          getNumOperandsForOpcode() == getNumOperands()) &&
+         "number of operands does not match opcode");
 }
 
+#ifndef NDEBUG
+unsigned VPInstruction::getNumOperandsForOpcode() const {
+  if (Instruction::isUnaryOp(getOpcode()) || Instruction::isCast(getOpcode()))
+    return 1;
+
+  if (Instruction::isBinaryOp(getOpcode()))
+    return 2;
+
+  switch (getOpcode()) {
+  case VPInstruction::StepVector:
+    return 0;
+  case Instruction::Alloca:
+  case Instruction::ExtractValue:
+  case Instruction::Freeze:
+  case Instruction::Load:
+  case VPInstruction::AnyOf:
+  case VPInstruction::BranchOnCond:
+  case VPInstruction::CalculateTripCountMinusVF:
+  case VPInstruction::CanonicalIVIncrementForPart:
+  case VPInstruction::ExplicitVectorLength:
+  case VPInstruction::ExtractLastElement:
+  case VPInstruction::ExtractPenultimateElement:
+  case VPInstruction::FirstActiveLane:
+  case VPInstruction::Not:
+    return 1;
+
+  case Instruction::ICmp:
+  case Instruction::FCmp:
+  case Instruction::Store:
+  case VPInstruction::ActiveLaneMask:
+  case VPInstruction::BranchOnCount:
+  case VPInstruction::ComputeReductionResult:
+  case VPInstruction::FirstOrderRecurrenceSplice:
+  case VPInstruction::LogicalAnd:
+  case VPInstruction::WideIVStep:
+  case VPInstruction::PtrAdd:
+    return 2;
+  case Instruction::Select:
+  case VPInstruction::ComputeFindLastIVResult:
+    return 3;
+  case Instruction::Call:
+  case Instruction::PHI:
+  case Instruction::GetElementPtr:
+  case Instruction::Switch:
+    // Cannot determine the number of operands from the opcode.
+    return -1u;
+  }
+  llvm_unreachable("all cases should be handled above");
+}
+#endif
+
 bool VPInstruction::doesGeneratePerAllLanes() const {
   return Opcode == VPInstruction::PtrAdd && !vputils::onlyFirstLaneUsed(this);
 }
@@ -2706,7 +2760,10 @@ static void scalarizeInstruction(const Instruction *Instr,
 
   // Replace the operands of the cloned instructions with their scalar
   // equivalents in the new loop.
-  for (const auto &I : enumerate(RepRecipe->operands())) {
+  auto OpRange = RepRecipe->operands();
+  if (isa<CallBase>(Cloned))
+    OpRange = drop_end(OpRange);
+  for (const auto &I : enumerate(OpRange)) {
     auto InputLane = Lane;
     VPValue *Operand = I.value();
     if (vputils::isSingleScalar(Operand))

>From f58eaddb22100003f9db154252aa56072851a9b2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 6 Jun 2025 10:53:12 +0100
Subject: [PATCH 2/4] !fixup address comments, thanks

---
 llvm/lib/Transforms/Vectorize/VPlan.h         |  4 ++--
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 22 +++++++++----------
 2 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index a2f450a1a7c71..58639406bb7dc 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -970,9 +970,9 @@ class VPInstruction : public VPRecipeWithIRFlags,
 
 #if !defined(NDEBUG)
   /// Return the number of operands determined by the opcode of the
-  /// VPInstruction. Returns -1 if the number of operands cannot be determined
+  /// VPInstruction. Returns -1u if the number of operands cannot be determined
   /// directly by the opcode.
-  unsigned getNumOperandsForOpcode() const;
+  static unsigned getNumOperandsForOpcode(unsigned Opcode);
 #endif
 
 public:
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index aa519596f674b..b25119688fdc5 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -413,20 +413,20 @@ VPInstruction::VPInstruction(unsigned Opcode, ArrayRef<VPValue *> Operands,
       Opcode(Opcode), Name(Name.str()) {
   assert(flagsValidForOpcode(getOpcode()) &&
          "Set flags not supported for the provided opcode");
-  assert((getNumOperandsForOpcode() == -1u ||
-          getNumOperandsForOpcode() == getNumOperands()) &&
+  assert((getNumOperandsForOpcode(Opcode) == -1u ||
+          getNumOperandsForOpcode(Opcode) == getNumOperands()) &&
          "number of operands does not match opcode");
 }
 
 #ifndef NDEBUG
-unsigned VPInstruction::getNumOperandsForOpcode() const {
-  if (Instruction::isUnaryOp(getOpcode()) || Instruction::isCast(getOpcode()))
+unsigned VPInstruction::getNumOperandsForOpcode(unsigned Opcode) {
+  if (Instruction::isUnaryOp(Opcode) || Instruction::isCast(Opcode))
     return 1;
 
-  if (Instruction::isBinaryOp(getOpcode()))
+  if (Instruction::isBinaryOp(Opcode))
     return 2;
 
-  switch (getOpcode()) {
+  switch (Opcode) {
   case VPInstruction::StepVector:
     return 0;
   case Instruction::Alloca:
@@ -452,15 +452,16 @@ unsigned VPInstruction::getNumOperandsForOpcode() const {
   case VPInstruction::ComputeReductionResult:
   case VPInstruction::FirstOrderRecurrenceSplice:
   case VPInstruction::LogicalAnd:
-  case VPInstruction::WideIVStep:
   case VPInstruction::PtrAdd:
+  case VPInstruction::WideIVStep:
     return 2;
   case Instruction::Select:
+  case VPInstruction::ComputeAnyOfResult:
   case VPInstruction::ComputeFindLastIVResult:
     return 3;
   case Instruction::Call:
-  case Instruction::PHI:
   case Instruction::GetElementPtr:
+  case Instruction::PHI:
   case Instruction::Switch:
     // Cannot determine the number of operands from the opcode.
     return -1u;
@@ -2772,10 +2773,7 @@ static void scalarizeInstruction(const Instruction *Instr,
 
   // Replace the operands of the cloned instructions with their scalar
   // equivalents in the new loop.
-  auto OpRange = RepRecipe->operands();
-  if (isa<CallBase>(Cloned))
-    OpRange = drop_end(OpRange);
-  for (const auto &I : enumerate(OpRange)) {
+  for (const auto &I : enumerate(RepRecipe->operands())) {
     auto InputLane = Lane;
     VPValue *Operand = I.value();
     if (vputils::isSingleScalar(Operand))

>From e78eb441a64231539615c96b80df13ba2f1abff8 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 20 Jun 2025 18:37:40 +0100
Subject: [PATCH 3/4] !fixup handle updated opcodes.

---
 llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index c4698b8661ec7..0eb769c339efa 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -459,8 +459,10 @@ unsigned VPInstruction::getNumOperandsForOpcode(unsigned Opcode) {
     return 2;
   case Instruction::Select:
   case VPInstruction::ComputeAnyOfResult:
-  case VPInstruction::ComputeFindLastIVResult:
+  case VPInstruction::ReductionStartVector:
     return 3;
+  case VPInstruction::ComputeFindLastIVResult:
+    return 4;
   case Instruction::Call:
   case Instruction::GetElementPtr:
   case Instruction::PHI:

>From d2f5c265cfeb766b0ed5284689ff08df020e6006 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sat, 21 Jun 2025 10:42:20 +0100
Subject: [PATCH 4/4] !fixup update gtests constructing VPInstructions.

---
 .../Transforms/Vectorize/VPlanTest.cpp        | 39 +++++++++++--------
 1 file changed, 22 insertions(+), 17 deletions(-)

diff --git a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
index 8b4fd066aaa26..8012d9ed38706 100644
--- a/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
+++ b/llvm/unittests/Transforms/Vectorize/VPlanTest.cpp
@@ -706,13 +706,15 @@ TEST_F(VPBasicBlockTest, reassociateBlocks) {
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 TEST_F(VPBasicBlockTest, print) {
-  VPInstruction *TC = new VPInstruction(Instruction::Add, {});
+  VPInstruction *TC = new VPInstruction(Instruction::PHI, {});
   VPlan &Plan = getPlan(TC);
+  IntegerType *Int32 = IntegerType::get(C, 32);
+  VPValue *Val = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1));
   VPBasicBlock *VPBB0 = Plan.getEntry();
   VPBB0->appendRecipe(TC);
 
-  VPInstruction *I1 = new VPInstruction(Instruction::Add, {});
-  VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1});
+  VPInstruction *I1 = new VPInstruction(Instruction::Add, {Val, Val});
+  VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1, Val});
   VPInstruction *I3 = new VPInstruction(Instruction::Br, {I1, I2});
 
   VPBasicBlock *VPBB1 = Plan.createVPBasicBlock("");
@@ -722,7 +724,7 @@ TEST_F(VPBasicBlockTest, print) {
   VPBB1->setName("bb1");
 
   VPInstruction *I4 = new VPInstruction(Instruction::Mul, {I2, I1});
-  VPInstruction *I5 = new VPInstruction(Instruction::Ret, {I4});
+  VPInstruction *I5 = new VPInstruction(Instruction::Br, {I4});
   VPBasicBlock *VPBB2 = Plan.createVPBasicBlock("");
   VPBB2->appendRecipe(I4);
   VPBB2->appendRecipe(I5);
@@ -752,14 +754,14 @@ edge [fontname=Courier, fontsize=30]
 compound=true
   N0 [label =
     "preheader:\l" +
-    "  EMIT vp\<%1\> = add \l" +
+    "  EMIT-SCALAR vp\<%1\> = phi \l" +
     "Successor(s): bb1\l"
   ]
   N0 -> N1 [ label=""]
   N1 [label =
     "bb1:\l" +
-    "  EMIT vp\<%2\> = add \l" +
-    "  EMIT vp\<%3\> = sub vp\<%2\>\l" +
+    "  EMIT vp\<%2\> = add ir\<1\>, ir\<1\>\l" +
+    "  EMIT vp\<%3\> = sub vp\<%2\>, ir\<1\>\l" +
     "  EMIT br vp\<%2\>, vp\<%3\>\l" +
     "Successor(s): bb2\l"
   ]
@@ -767,7 +769,7 @@ compound=true
   N2 [label =
     "bb2:\l" +
     "  EMIT vp\<%5\> = mul vp\<%3\>, vp\<%2\>\l" +
-    "  EMIT ret vp\<%5\>\l" +
+    "  EMIT br vp\<%5\>\l" +
     "Successor(s): ir-bb\<scalar.header\>\l"
   ]
   N2 -> N3 [ label=""]
@@ -780,8 +782,8 @@ compound=true
   EXPECT_EQ(ExpectedStr, FullDump);
 
   const char *ExpectedBlock1Str = R"(bb1:
-  EMIT vp<%2> = add 
-  EMIT vp<%3> = sub vp<%2>
+  EMIT vp<%2> = add ir<1>, ir<1>
+  EMIT vp<%3> = sub vp<%2>, ir<1>
   EMIT br vp<%2>, vp<%3>
 Successor(s): bb2
 )";
@@ -793,7 +795,7 @@ Successor(s): bb2
   // Ensure that numbering is good when dumping the second block in isolation.
   const char *ExpectedBlock2Str = R"(bb2:
   EMIT vp<%5> = mul vp<%3>, vp<%2>
-  EMIT ret vp<%5>
+  EMIT br vp<%5>
 Successor(s): ir-bb<scalar.header>
 )";
   std::string Block2Dump;
@@ -909,9 +911,12 @@ TEST_F(VPBasicBlockTest, cloneAndPrint) {
   VPlan &Plan = getPlan(nullptr);
   VPBasicBlock *VPBB0 = Plan.getEntry();
 
-  VPInstruction *I1 = new VPInstruction(Instruction::Add, {});
-  VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1});
-  VPInstruction *I3 = new VPInstruction(Instruction::Br, {I1, I2});
+  IntegerType *Int32 = IntegerType::get(C, 32);
+  VPValue *Val = Plan.getOrAddLiveIn(ConstantInt::get(Int32, 1));
+
+  VPInstruction *I1 = new VPInstruction(Instruction::Add, {Val, Val});
+  VPInstruction *I2 = new VPInstruction(Instruction::Sub, {I1, Val});
+  VPInstruction *I3 = new VPInstruction(Instruction::Store, {I1, I2});
 
   VPBasicBlock *VPBB1 = Plan.createVPBasicBlock("");
   VPBB1->appendRecipe(I1);
@@ -932,9 +937,9 @@ compound=true
   N0 -> N1 [ label=""]
   N1 [label =
     "bb1:\l" +
-    "  EMIT vp\<%1\> = add \l" +
-    "  EMIT vp\<%2\> = sub vp\<%1\>\l" +
-    "  EMIT br vp\<%1\>, vp\<%2\>\l" +
+    "  EMIT vp\<%1\> = add ir\<1\>, ir\<1\>\l" +
+    "  EMIT vp\<%2\> = sub vp\<%1\>, ir\<1\>\l" +
+    "  EMIT store vp\<%1\>, vp\<%2\>\l" +
     "No successors\l"
   ]
 }



More information about the llvm-commits mailing list