[llvm] [VPlan] Add VPConstantInt for VPIRValues wrapping ConstantInts (NFC) (PR #175458)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 12 04:08:50 PST 2026


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

>From 102b6aaa3d80f663bed58f0453795f592c2dbbbf Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Sun, 11 Jan 2026 17:09:16 +0000
Subject: [PATCH 1/2] [VPlan] Add VPConstantInt for VPIRValues wrapping
 ConstantInts (NFC)

Follow-up to https://github.com/llvm/llvm-project/pull/174282: Introduce
a new VPConstantInt overlay for VPIRValue, to make it easier to check
and access constant int IR values.
---
 .../Transforms/Vectorize/LoopVectorize.cpp    |  3 +-
 llvm/lib/Transforms/Vectorize/VPlan.h         | 12 ++++---
 .../Transforms/Vectorize/VPlanAnalysis.cpp    |  4 +--
 .../Transforms/Vectorize/VPlanPatternMatch.h  | 18 +++-------
 .../lib/Transforms/Vectorize/VPlanRecipes.cpp | 35 +++++++------------
 llvm/lib/Transforms/Vectorize/VPlanValue.h    | 20 +++++++++++
 6 files changed, 48 insertions(+), 44 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index cdc6ecfa21bcb..2d321f6a32692 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -4276,8 +4276,7 @@ VectorizationFactor LoopVectorizationPlanner::selectVectorizationFactor() {
           }
           case VPInstruction::ActiveLaneMask: {
             unsigned Multiplier =
-                cast<ConstantInt>(VPI->getOperand(2)->getLiveInIRValue())
-                    ->getZExtValue();
+                cast<VPConstantInt>(VPI->getOperand(2))->getZExtValue();
             C += VPI->cost(VF * Multiplier, CostCtx);
             break;
           }
diff --git a/llvm/lib/Transforms/Vectorize/VPlan.h b/llvm/lib/Transforms/Vectorize/VPlan.h
index 83fe45bfd0bbf..e8dcdaf728b29 100644
--- a/llvm/lib/Transforms/Vectorize/VPlan.h
+++ b/llvm/lib/Transforms/Vectorize/VPlan.h
@@ -3103,8 +3103,8 @@ class VPExpressionRecipe : public VPSingleDefRecipe {
     assert(Red->getRecurrenceKind() == RecurKind::Add &&
            "Expected an add reduction");
     assert(getNumOperands() >= 3 && "Expected at least three operands");
-    [[maybe_unused]] auto *SubConst = dyn_cast<ConstantInt>(getOperand(2)->getLiveInIRValue());
-    assert(SubConst && SubConst->getValue() == 0 &&
+    [[maybe_unused]] auto *SubConst = dyn_cast<VPConstantInt>(getOperand(2));
+    assert(SubConst && SubConst->isZero() &&
            Sub->getOpcode() == Instruction::Sub && "Expected a negating sub");
   }
 
@@ -4506,8 +4506,12 @@ class VPlan {
   VPIRValue *getOrAddLiveIn(Value *V) {
     assert(V && "Trying to get or add the VPIRValue of a null Value");
     auto [It, Inserted] = LiveIns.try_emplace(V);
-    if (Inserted)
-      It->second = new VPIRValue(V);
+    if (Inserted) {
+      if (auto *CI = dyn_cast<ConstantInt>(V))
+        It->second = new VPConstantInt(CI);
+      else
+        It->second = new VPIRValue(V);
+    }
 
     assert(isa<VPIRValue>(It->second) &&
            "Only VPIRValues should be in mapping");
diff --git a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
index 27e7915a71675..20dcbff2389d4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanAnalysis.cpp
@@ -162,8 +162,8 @@ Type *VPTypeAnalysis::inferScalarTypeForRecipe(const VPWidenRecipe *R) {
   case Instruction::ExtractValue: {
     assert(R->getNumOperands() == 2 && "expected single level extractvalue");
     auto *StructTy = cast<StructType>(inferScalarType(R->getOperand(0)));
-    auto *CI = cast<ConstantInt>(R->getOperand(1)->getLiveInIRValue());
-    return StructTy->getTypeAtIndex(CI->getZExtValue());
+    return StructTy->getTypeAtIndex(
+        cast<VPConstantInt>(R->getOperand(1))->getZExtValue());
   }
   case Instruction::Select:
     return inferScalarType(R->getOperand(1));
diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
index 3732d009b9537..629becaf77ed0 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
@@ -109,17 +109,13 @@ template <typename Pred, unsigned BitWidth = 0> struct int_pred_ty {
     auto *VPI = dyn_cast<VPInstruction>(VPV);
     if (VPI && VPI->getOpcode() == VPInstruction::Broadcast)
       VPV = VPI->getOperand(0);
-    auto *IRV = dyn_cast<VPIRValue>(VPV);
-    if (!IRV)
-      return false;
-    assert(!IRV->getType()->isVectorTy() && "Unexpected vector live-in");
-    const auto *CI = dyn_cast<ConstantInt>(IRV->getValue());
+    auto *CI = dyn_cast<VPConstantInt>(VPV);
     if (!CI)
       return false;
 
-    if (BitWidth != 0 && CI->getBitWidth() != BitWidth)
+    if (BitWidth != 0 && CI->getAPInt().getBitWidth() != BitWidth)
       return false;
-    return P.isValue(CI->getValue());
+    return P.isValue(CI->getAPInt());
   }
 };
 
@@ -183,14 +179,10 @@ struct bind_apint {
   bind_apint(const APInt *&Res) : Res(Res) {}
 
   bool match(VPValue *VPV) const {
-    auto *IRV = dyn_cast<VPIRValue>(VPV);
-    if (!IRV)
-      return false;
-    assert(!IRV->getType()->isVectorTy() && "Unexpected vector live-in");
-    const auto *CI = dyn_cast<ConstantInt>(IRV->getValue());
+    auto *CI = dyn_cast<VPConstantInt>(VPV);
     if (!CI)
       return false;
-    Res = &CI->getValue();
+    Res = &CI->getAPInt();
     return true;
   }
 };
diff --git a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
index 2c0772320c3cf..5b1b6e702dc12 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanRecipes.cpp
@@ -393,7 +393,7 @@ VPUnrollPartAccessor<PartOpIdx>::getUnrollPartOperand(const VPUser &U) const {
 template <unsigned PartOpIdx>
 unsigned VPUnrollPartAccessor<PartOpIdx>::getUnrollPart(const VPUser &U) const {
   if (auto *UnrollPartOp = getUnrollPartOperand(U))
-    return cast<ConstantInt>(UnrollPartOp->getLiveInIRValue())->getZExtValue();
+    return cast<VPConstantInt>(UnrollPartOp)->getZExtValue();
   return 0;
 }
 
@@ -534,11 +534,8 @@ Value *VPInstruction::generate(VPTransformState &State) {
   }
   case Instruction::ExtractElement: {
     assert(State.VF.isVector() && "Only extract elements from vectors");
-    if (auto *IdxIRV = dyn_cast<VPIRValue>(getOperand(1))) {
-      unsigned IdxToExtract =
-          cast<ConstantInt>(IdxIRV->getValue())->getZExtValue();
-      return State.get(getOperand(0), VPLane(IdxToExtract));
-    }
+    if (auto *Idx = dyn_cast<VPConstantInt>(getOperand(1)))
+      return State.get(getOperand(0), VPLane(Idx->getZExtValue()));
     Value *Vec = State.get(getOperand(0));
     Value *Idx = State.get(getOperand(1), /*IsScalar=*/true);
     return Builder.CreateExtractElement(Vec, Idx, Name);
@@ -579,7 +576,7 @@ Value *VPInstruction::generate(VPTransformState &State) {
                                Name);
 
     ElementCount EC = State.VF.multiplyCoefficientBy(
-        cast<ConstantInt>(getOperand(2)->getLiveInIRValue())->getZExtValue());
+        cast<VPConstantInt>(getOperand(2))->getZExtValue());
     auto *PredTy = VectorType::get(Builder.getInt1Ty(), EC);
     return Builder.CreateIntrinsic(Intrinsic::get_active_lane_mask,
                                    {PredTy, ScalarTC->getType()},
@@ -696,7 +693,7 @@ Value *VPInstruction::generate(VPTransformState &State) {
     // If this start vector is scaled then it should produce a vector with fewer
     // elements than the VF.
     ElementCount VF = State.VF.divideCoefficientBy(
-        cast<ConstantInt>(getOperand(2)->getLiveInIRValue())->getZExtValue());
+        cast<VPConstantInt>(getOperand(2))->getZExtValue());
     auto *Iden = Builder.CreateVectorSplat(VF, State.get(getOperand(1), true));
     return Builder.CreateInsertElement(Iden, State.get(getOperand(0), true),
                                        Builder.getInt32(0));
@@ -1208,8 +1205,7 @@ InstructionCost VPInstruction::computeCost(ElementCount VF,
   }
   case VPInstruction::ActiveLaneMask: {
     Type *ArgTy = Ctx.Types.inferScalarType(getOperand(0));
-    unsigned Multiplier =
-        cast<ConstantInt>(getOperand(2)->getLiveInIRValue())->getZExtValue();
+    unsigned Multiplier = cast<VPConstantInt>(getOperand(2))->getZExtValue();
     Type *RetTy = toVectorTy(Type::getInt1Ty(Ctx.LLVMCtx), VF * Multiplier);
     IntrinsicCostAttributes Attrs(Intrinsic::get_active_lane_mask, RetTy,
                                   {ArgTy, ArgTy});
@@ -2005,12 +2001,9 @@ InstructionCost VPHistogramRecipe::computeCost(ElementCount VF,
   // a multiply, and add that into the cost.
   InstructionCost MulCost =
       Ctx.TTI.getArithmeticInstrCost(Instruction::Mul, VTy, Ctx.CostKind);
-  if (auto *IncAmountIRV = dyn_cast<VPIRValue>(IncAmt)) {
-    ConstantInt *CI = dyn_cast<ConstantInt>(IncAmountIRV->getValue());
-
-    if (CI && CI->getZExtValue() == 1)
+  if (auto *CI = dyn_cast<VPConstantInt>(IncAmt))
+    if (CI->isOne())
       MulCost = TTI::TCC_Free;
-  }
 
   // Find the cost of the histogram operation itself.
   Type *PtrTy = VectorType::get(AddressTy, VF);
@@ -2193,8 +2186,8 @@ void VPWidenRecipe::execute(VPTransformState &State) {
   case Instruction::ExtractValue: {
     assert(getNumOperands() == 2 && "expected single level extractvalue");
     Value *Op = State.get(getOperand(0));
-    auto *CI = cast<ConstantInt>(getOperand(1)->getLiveInIRValue());
-    Value *Extract = Builder.CreateExtractValue(Op, CI->getZExtValue());
+    Value *Extract = Builder.CreateExtractValue(
+        Op, cast<VPConstantInt>(getOperand(1))->getZExtValue());
     State.set(this, Extract);
     break;
   }
@@ -2362,12 +2355,8 @@ bool VPWidenIntOrFpInductionRecipe::isCanonical() const {
   // The step may be defined by a recipe in the preheader (e.g. if it requires
   // SCEV expansion), but for the canonical induction the step is required to be
   // 1, which is represented as live-in.
-  const VPIRValue *Step = dyn_cast<VPIRValue>(getStepValue());
-  if (!Step)
-    return false;
-  ;
-  auto *StepC = dyn_cast<ConstantInt>(Step->getValue());
-  auto *StartC = dyn_cast<ConstantInt>(getStartValue()->getValue());
+  auto *StepC = dyn_cast<VPConstantInt>(getStepValue());
+  auto *StartC = dyn_cast<VPConstantInt>(getStartValue());
   return StartC && StartC->isZero() && StepC && StepC->isOne() &&
          getScalarType() == getRegion()->getCanonicalIVType();
 }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanValue.h b/llvm/lib/Transforms/Vectorize/VPlanValue.h
index cae0633dbb185..0579aaf744ca6 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanValue.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanValue.h
@@ -24,6 +24,8 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/TinyPtrVector.h"
 #include "llvm/ADT/iterator_range.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/Compiler.h"
 
 namespace llvm {
@@ -197,6 +199,24 @@ struct VPIRValue : public VPValue {
   }
 };
 
+/// An overlay on VPIRValue for VPValues that wrap a ConstantInt. Provides
+/// convenient accessors for the underlying constant.
+struct VPConstantInt : public VPIRValue {
+  VPConstantInt(ConstantInt *CI) : VPIRValue(CI) {}
+
+  uint64_t getZExtValue() const { return getCI()->getZExtValue(); }
+  const APInt &getAPInt() const { return getCI()->getValue(); }
+  bool isZero() const { return getCI()->isZero(); }
+  bool isOne() const { return getCI()->isOne(); }
+
+  static bool classof(const VPValue *V) {
+    return isa<VPIRValue>(V) && isa<ConstantInt>(V->getUnderlyingValue());
+  }
+
+private:
+  ConstantInt *getCI() const { return cast<ConstantInt>(getValue()); }
+};
+
 /// A symbolic live-in VPValue, used for values like vector trip count, VF, and
 /// VFxUF.
 struct VPSymbolicValue : public VPValue {

>From f2fce6f27339ef3cc47d4c78d39a01a77eb22dc5 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 12 Jan 2026 12:05:54 +0000
Subject: [PATCH 2/2] !fixup address comments, some cleanup, thanks

---
 llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h |  2 +-
 llvm/lib/Transforms/Vectorize/VPlanValue.h        | 14 +++++++-------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
index f6c0c65ebb14e..0c7d4fd5ce4c8 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanPatternMatch.h
@@ -113,7 +113,7 @@ template <typename Pred, unsigned BitWidth = 0> struct int_pred_ty {
     if (!CI)
       return false;
 
-    if (BitWidth != 0 && CI->getAPInt().getBitWidth() != BitWidth)
+    if (BitWidth != 0 && CI->getBitWidth() != BitWidth)
       return false;
     return P.isValue(CI->getAPInt());
   }
diff --git a/llvm/lib/Transforms/Vectorize/VPlanValue.h b/llvm/lib/Transforms/Vectorize/VPlanValue.h
index 0579aaf744ca6..64b52d54526d4 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanValue.h
+++ b/llvm/lib/Transforms/Vectorize/VPlanValue.h
@@ -204,17 +204,17 @@ struct VPIRValue : public VPValue {
 struct VPConstantInt : public VPIRValue {
   VPConstantInt(ConstantInt *CI) : VPIRValue(CI) {}
 
-  uint64_t getZExtValue() const { return getCI()->getZExtValue(); }
-  const APInt &getAPInt() const { return getCI()->getValue(); }
-  bool isZero() const { return getCI()->isZero(); }
-  bool isOne() const { return getCI()->isOne(); }
-
   static bool classof(const VPValue *V) {
     return isa<VPIRValue>(V) && isa<ConstantInt>(V->getUnderlyingValue());
   }
 
-private:
-  ConstantInt *getCI() const { return cast<ConstantInt>(getValue()); }
+  bool isOne() const { return getAPInt().isOne(); }
+  bool isZero() const { return getAPInt().isZero(); }
+  const APInt &getAPInt() const {
+    return cast<ConstantInt>(getValue())->getValue();
+  }
+  unsigned getBitWidth() const { return getAPInt().getBitWidth(); }
+  uint64_t getZExtValue() const { return getAPInt().getZExtValue(); }
 };
 
 /// A symbolic live-in VPValue, used for values like vector trip count, VF, and



More information about the llvm-commits mailing list