[llvm] [SLP][Modularization][NFC] Extract type and constant helpers into SLPUtils (1/3) (PR #206881)
Madhur Amilkanthwar via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 9 06:02:50 PDT 2026
https://github.com/madhur13490 updated https://github.com/llvm/llvm-project/pull/206881
>From 587acd7a5085ba9e7ba17fcd0ec7819d93d78d59 Mon Sep 17 00:00:00 2001
From: Madhur Amilkanthwar <madhura at nvidia.com>
Date: Thu, 28 May 2026 04:58:40 -0700
Subject: [PATCH] [SLP][NFC] Extract type and constant helpers into SLPUtils
(1/3)
As we discussed on RFC:
https://discourse.llvm.org/t/modularizing-slpvectorizer-cpp/90922
This patch introduces the SLPVectorizer/ subdirectory and adds
SLPUtils.{h,cpp} under namespace llvm::slpvectorizer, then moves the
type and constant query helpers into it:
- isConstant
- isVectorLikeInstWithConstOps
- isSplat
- allConstant
- allSameBlock
- allSameType
- allSameOpcode
- getNumElements
- getPartNumElems
- getNumElems
- getInsertExtractIndex
- getExtractIndex
---
llvm/lib/Transforms/Vectorize/CMakeLists.txt | 1 +
.../Transforms/Vectorize/SLPVectorizer.cpp | 181 +----------------
.../Vectorize/SLPVectorizer/SLPUtils.cpp | 186 ++++++++++++++++++
.../Vectorize/SLPVectorizer/SLPUtils.h | 85 ++++++++
4 files changed, 273 insertions(+), 180 deletions(-)
create mode 100644 llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.cpp
create mode 100644 llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.h
diff --git a/llvm/lib/Transforms/Vectorize/CMakeLists.txt b/llvm/lib/Transforms/Vectorize/CMakeLists.txt
index d081747b821a4..6e26203d957cb 100644
--- a/llvm/lib/Transforms/Vectorize/CMakeLists.txt
+++ b/llvm/lib/Transforms/Vectorize/CMakeLists.txt
@@ -23,6 +23,7 @@ add_llvm_component_library(LLVMVectorize
SandboxVectorizer/Scheduler.cpp
SandboxVectorizer/SeedCollector.cpp
SandboxVectorizer/VecUtils.cpp
+ SLPVectorizer/SLPUtils.cpp
SLPVectorizer.cpp
Vectorize.cpp
VectorCombine.cpp
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
index 30fa465e5178a..5b391363bcbb4 100644
--- a/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp
@@ -17,6 +17,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/Vectorize/SLPVectorizer.h"
+#include "SLPVectorizer/SLPUtils.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/PriorityQueue.h"
@@ -343,15 +344,6 @@ static Type *getValueType(Value *V, bool LookThroughCmp = false) {
return V->getType();
}
-/// \returns the number of elements for Ty.
-static unsigned getNumElements(Type *Ty) {
- assert(!isa<ScalableVectorType>(Ty) &&
- "ScalableVectorType is not supported.");
- if (isVectorizedTy(Ty))
- return getVectorizedTypeVF(Ty).getFixedValue();
- return 1;
-}
-
/// \returns the vector type of ScalarTy based on vectorization factor.
static Type *getWidenedType(Type *ScalarTy, unsigned VF) {
if (VF == 1 && !isVectorizedTy(ScalarTy)) {
@@ -516,47 +508,6 @@ static SmallVector<int> calculateShufflevectorMask(ArrayRef<Value *> VL) {
return Mask;
}
-/// \returns True if the value is a constant (but not globals/constant
-/// expressions).
-static bool isConstant(Value *V) {
- return isa<Constant>(V) && !isa<ConstantExpr, GlobalValue>(V);
-}
-
-/// Checks if \p V is one of vector-like instructions, i.e. undef,
-/// insertelement/extractelement with constant indices for fixed vector type or
-/// extractvalue instruction.
-static bool isVectorLikeInstWithConstOps(Value *V) {
- if (!isa<InsertElementInst, InsertValueInst, ExtractElementInst>(V) &&
- !isa<ExtractValueInst, UndefValue>(V))
- return false;
- auto *I = dyn_cast<Instruction>(V);
- if (!I || isa<ExtractValueInst>(I))
- return true;
- if (isa<ExtractElementInst>(I))
- return isa<FixedVectorType>(I->getOperand(0)->getType()) &&
- isConstant(I->getOperand(1));
- if (isa<InsertElementInst>(I))
- return isa<FixedVectorType>(I->getOperand(0)->getType()) &&
- isConstant(I->getOperand(2));
- assert(isa<InsertValueInst>(I) && "Expected InsertValueInst");
- return true;
-}
-
-/// Returns power-of-2 number of elements in a single register (part), given the
-/// total number of elements \p Size and number of registers (parts) \p
-/// NumParts.
-static unsigned getPartNumElems(unsigned Size, unsigned NumParts) {
- return std::min<unsigned>(Size, bit_ceil(divideCeil(Size, NumParts)));
-}
-
-/// Returns correct remaining number of elements, considering total amount \p
-/// Size, (power-of-2 number) of elements in a single register \p PartNumElems
-/// and current register (part) \p Part.
-static unsigned getNumElems(unsigned Size, unsigned PartNumElems,
- unsigned Part) {
- return std::min<unsigned>(PartNumElems, Size - Part * PartNumElems);
-}
-
#if !defined(NDEBUG)
/// Print a short descriptor of the instruction bundle suitable for debug output.
static std::string shortBundleName(ArrayRef<Value *> VL, int Idx = -1) {
@@ -569,55 +520,6 @@ static std::string shortBundleName(ArrayRef<Value *> VL, int Idx = -1) {
}
#endif
-/// \returns true if all of the instructions in \p VL are in the same block or
-/// false otherwise.
-static bool allSameBlock(ArrayRef<Value *> VL) {
- auto *It = find_if(VL, IsaPred<Instruction>);
- if (It == VL.end())
- return false;
- Instruction *I0 = cast<Instruction>(*It);
- if (all_of(VL, isVectorLikeInstWithConstOps))
- return true;
-
- BasicBlock *BB = I0->getParent();
- for (Value *V : iterator_range(It, VL.end())) {
- if (isa<PoisonValue>(V))
- continue;
- auto *II = dyn_cast<Instruction>(V);
- if (!II)
- return false;
-
- if (BB != II->getParent())
- return false;
- }
- return true;
-}
-
-/// \returns True if all of the values in \p VL are constants (but not
-/// globals/constant expressions).
-static bool allConstant(ArrayRef<Value *> VL) {
- // Constant expressions and globals can't be vectorized like normal integer/FP
- // constants.
- return all_of(VL, isConstant);
-}
-
-/// \returns True if all of the values in \p VL are identical or some of them
-/// are UndefValue.
-static bool isSplat(ArrayRef<Value *> VL) {
- Value *FirstNonUndef = nullptr;
- for (Value *V : VL) {
- if (isa<UndefValue>(V))
- continue;
- if (!FirstNonUndef) {
- FirstNonUndef = V;
- continue;
- }
- if (V != FirstNonUndef)
- return false;
- }
- return FirstNonUndef != nullptr;
-}
-
/// \returns True if \p I is commutative, handles CmpInst and BinaryOperator.
/// For BinaryOperator, it also checks if \p InstWithUses is used in specific
/// patterns that make it effectively commutative (like equality comparisons
@@ -710,33 +612,6 @@ static unsigned getNumberOfPotentiallyCommutativeOps(Instruction *I) {
return I->getNumOperands();
}
-template <typename T>
-static std::optional<unsigned> getInsertExtractIndex(const Value *Inst,
- unsigned Offset) {
- static_assert(std::is_same_v<T, InsertElementInst> ||
- std::is_same_v<T, ExtractElementInst>,
- "unsupported T");
- const auto *IE = dyn_cast<T>(Inst);
- if (!IE)
- return std::nullopt;
- // InsertElement: result is the vector, index is op 2.
- // ExtractElement: result is scalar, vector is op 0, index is op 1.
- constexpr bool IsInsert = std::is_same_v<T, InsertElementInst>;
- Type *VecTy = IsInsert ? IE->getType() : IE->getOperand(0)->getType();
- const auto *VT = dyn_cast<FixedVectorType>(VecTy);
- if (!VT)
- return std::nullopt;
- const auto *CI = dyn_cast<ConstantInt>(IE->getOperand(IsInsert ? 2 : 1));
- if (!CI)
- return std::nullopt;
- if (CI->getValue().uge(VT->getNumElements()))
- return std::nullopt;
- unsigned Index = Offset;
- Index *= VT->getNumElements();
- Index += CI->getZExtValue();
- return Index;
-}
-
/// \returns inserting or extracting index of InsertElement, ExtractElement or
/// InsertValue instruction, using Offset as base offset for index.
/// \returns std::nullopt if the index is not an immediate.
@@ -769,28 +644,6 @@ static std::optional<unsigned> getElementIndex(const Value *Inst,
return Index;
}
-/// \returns true if all of the values in \p VL use the same opcode.
-/// For comparison instructions, also checks if predicates match.
-/// PoisonValues are considered matching.
-/// Interchangeable instructions are not considered.
-static bool allSameOpcode(ArrayRef<Value *> VL) {
- auto *It = find_if(VL, IsaPred<Instruction>);
- if (It == VL.end())
- return true;
- Instruction *MainOp = cast<Instruction>(*It);
- unsigned Opcode = MainOp->getOpcode();
- bool IsCmpOp = isa<CmpInst>(MainOp);
- CmpInst::Predicate BasePred = IsCmpOp ? cast<CmpInst>(MainOp)->getPredicate()
- : CmpInst::BAD_ICMP_PREDICATE;
- return std::all_of(It, VL.end(), [&](Value *V) {
- if (auto *CI = dyn_cast<CmpInst>(V))
- return BasePred == CI->getPredicate();
- if (auto *I = dyn_cast<Instruction>(V))
- return I->getOpcode() == Opcode;
- return isa<PoisonValue>(V);
- });
-}
-
namespace {
/// Specifies the way the mask should be analyzed for undefs/poisonous elements
/// in the shuffle mask.
@@ -986,31 +839,6 @@ isFixedVectorShuffle(ArrayRef<Value *> VL, SmallVectorImpl<int> &Mask,
: TargetTransformInfo::SK_PermuteSingleSrc;
}
-/// \returns True if Extract{Value,Element} instruction extracts element Idx.
-static std::optional<unsigned> getExtractIndex(const Instruction *E) {
- unsigned Opcode = E->getOpcode();
- assert((Opcode == Instruction::ExtractElement ||
- Opcode == Instruction::ExtractValue) &&
- "Expected extractelement or extractvalue instruction.");
- if (Opcode == Instruction::ExtractElement) {
- auto *CI = dyn_cast<ConstantInt>(E->getOperand(1));
- if (!CI)
- return std::nullopt;
- // Check if the index is out of bound - we can get the source vector from
- // operand 0
- unsigned Idx = CI->getZExtValue();
- auto *EE = cast<ExtractElementInst>(E);
- const unsigned VF = ::getNumElements(EE->getVectorOperandType());
- if (Idx >= VF)
- return std::nullopt;
- return Idx;
- }
- auto *EI = cast<ExtractValueInst>(E);
- if (EI->getNumIndices() != 1)
- return std::nullopt;
- return *EI->idx_begin();
-}
-
/// Checks if the provided value does not require scheduling. It does not
/// require scheduling if this is not an instruction or it is an instruction
/// that does not read/write memory and all operands are either not instructions
@@ -1853,13 +1681,6 @@ static InstructionsState getSameOpcode(ArrayRef<Value *> VL,
return S;
}
-/// \returns true if all of the values in \p VL have the same type or false
-/// otherwise.
-static bool allSameType(ArrayRef<Value *> VL) {
- Type *Ty = VL.consume_front()->getType();
- return all_of(VL, [&](Value *V) { return V->getType() == Ty; });
-}
-
/// \returns True if in-tree use also needs extract. This refers to
/// possible scalar operand in vectorized instruction.
static bool doesInTreeUserNeedToExtract(Value *Scalar, Instruction *UserInst,
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.cpp b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.cpp
new file mode 100644
index 0000000000000..d4f3415a04c4a
--- /dev/null
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.cpp
@@ -0,0 +1,186 @@
+//===- SLPUtils.cpp - SLP Vectorizer free utility helpers -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SLPUtils.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/MathExtras.h"
+
+#include <type_traits>
+
+using namespace llvm;
+
+namespace llvm::slpvectorizer {
+
+bool isConstant(Value *V) {
+ return isa<Constant>(V) && !isa<ConstantExpr, GlobalValue>(V);
+}
+
+bool isVectorLikeInstWithConstOps(Value *V) {
+ if (!isa<InsertElementInst, InsertValueInst, ExtractElementInst>(V) &&
+ !isa<ExtractValueInst, UndefValue>(V))
+ return false;
+ auto *I = dyn_cast<Instruction>(V);
+ if (!I || isa<ExtractValueInst>(I))
+ return true;
+ if (isa<ExtractElementInst>(I))
+ return isa<FixedVectorType>(I->getOperand(0)->getType()) &&
+ isConstant(I->getOperand(1));
+ if (isa<InsertElementInst>(I))
+ return isa<FixedVectorType>(I->getOperand(0)->getType()) &&
+ isConstant(I->getOperand(2));
+ assert(isa<InsertValueInst>(I) && "Expected InsertValueInst");
+ return true;
+}
+
+unsigned getNumElements(Type *Ty) {
+ assert(!isa<ScalableVectorType>(Ty) &&
+ "ScalableVectorType is not supported.");
+ if (isVectorizedTy(Ty))
+ return getVectorizedTypeVF(Ty).getFixedValue();
+ return 1;
+}
+
+unsigned getPartNumElems(unsigned Size, unsigned NumParts) {
+ return std::min<unsigned>(Size, bit_ceil(divideCeil(Size, NumParts)));
+}
+
+unsigned getNumElems(unsigned Size, unsigned PartNumElems, unsigned Part) {
+ return std::min<unsigned>(PartNumElems, Size - Part * PartNumElems);
+}
+
+bool allSameBlock(ArrayRef<Value *> VL) {
+ auto *It = find_if(VL, IsaPred<Instruction>);
+ if (It == VL.end())
+ return false;
+ Instruction *I0 = cast<Instruction>(*It);
+ if (all_of(VL, isVectorLikeInstWithConstOps))
+ return true;
+
+ BasicBlock *BB = I0->getParent();
+ for (Value *V : iterator_range(It, VL.end())) {
+ if (isa<PoisonValue>(V))
+ continue;
+ auto *II = dyn_cast<Instruction>(V);
+ if (!II)
+ return false;
+
+ if (BB != II->getParent())
+ return false;
+ }
+ return true;
+}
+
+bool allConstant(ArrayRef<Value *> VL) {
+ // Constant expressions and globals can't be vectorized like normal integer/FP
+ // constants.
+ return all_of(VL, isConstant);
+}
+
+bool isSplat(ArrayRef<Value *> VL) {
+ Value *FirstNonUndef = nullptr;
+ for (Value *V : VL) {
+ if (isa<UndefValue>(V))
+ continue;
+ if (!FirstNonUndef) {
+ FirstNonUndef = V;
+ continue;
+ }
+ if (V != FirstNonUndef)
+ return false;
+ }
+ return FirstNonUndef != nullptr;
+}
+
+bool allSameOpcode(ArrayRef<Value *> VL) {
+ auto *It = find_if(VL, IsaPred<Instruction>);
+ if (It == VL.end())
+ return true;
+ Instruction *MainOp = cast<Instruction>(*It);
+ unsigned Opcode = MainOp->getOpcode();
+ bool IsCmpOp = isa<CmpInst>(MainOp);
+ CmpInst::Predicate BasePred = IsCmpOp ? cast<CmpInst>(MainOp)->getPredicate()
+ : CmpInst::BAD_ICMP_PREDICATE;
+ return std::all_of(It, VL.end(), [&](Value *V) {
+ if (auto *CI = dyn_cast<CmpInst>(V))
+ return BasePred == CI->getPredicate();
+ if (auto *I = dyn_cast<Instruction>(V))
+ return I->getOpcode() == Opcode;
+ return isa<PoisonValue>(V);
+ });
+}
+
+std::optional<unsigned> getExtractIndex(const Instruction *E) {
+ unsigned Opcode = E->getOpcode();
+ assert((Opcode == Instruction::ExtractElement ||
+ Opcode == Instruction::ExtractValue) &&
+ "Expected extractelement or extractvalue instruction.");
+ if (Opcode == Instruction::ExtractElement) {
+ auto *CI = dyn_cast<ConstantInt>(E->getOperand(1));
+ if (!CI)
+ return std::nullopt;
+ // Check if the index is out of bound. We can get the source vector from
+ // operand 0.
+ unsigned Idx = CI->getZExtValue();
+ auto *EE = cast<ExtractElementInst>(E);
+ const unsigned VF = getNumElements(EE->getVectorOperandType());
+ if (Idx >= VF)
+ return std::nullopt;
+ return Idx;
+ }
+ auto *EI = cast<ExtractValueInst>(E);
+ if (EI->getNumIndices() != 1)
+ return std::nullopt;
+ return *EI->idx_begin();
+}
+
+bool allSameType(ArrayRef<Value *> VL) {
+ Type *Ty = VL.consume_front()->getType();
+ return all_of(VL, [&](Value *V) { return V->getType() == Ty; });
+}
+
+template <typename T>
+std::optional<unsigned> getInsertExtractIndex(const Value *Inst,
+ unsigned Offset) {
+ static_assert(std::is_same_v<T, InsertElementInst> ||
+ std::is_same_v<T, ExtractElementInst>,
+ "unsupported T");
+ const auto *IE = dyn_cast<T>(Inst);
+ if (!IE)
+ return std::nullopt;
+ // InsertElement: result is the vector, index is op 2.
+ // ExtractElement: result is scalar, vector is op 0, index is op 1.
+ constexpr bool IsInsert = std::is_same_v<T, InsertElementInst>;
+ Type *VecTy = IsInsert ? IE->getType() : IE->getOperand(0)->getType();
+ const auto *VT = dyn_cast<FixedVectorType>(VecTy);
+ if (!VT)
+ return std::nullopt;
+ const auto *CI = dyn_cast<ConstantInt>(IE->getOperand(IsInsert ? 2 : 1));
+ if (!CI)
+ return std::nullopt;
+ if (CI->getValue().uge(VT->getNumElements()))
+ return std::nullopt;
+ unsigned Index = Offset;
+ Index *= VT->getNumElements();
+ Index += CI->getZExtValue();
+ return Index;
+}
+
+// Only these two specializations are used; instantiate them here so the
+// definition can stay out of the header.
+template std::optional<unsigned>
+getInsertExtractIndex<InsertElementInst>(const Value *, unsigned);
+template std::optional<unsigned>
+getInsertExtractIndex<ExtractElementInst>(const Value *, unsigned);
+
+} // namespace llvm::slpvectorizer
diff --git a/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.h b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.h
new file mode 100644
index 0000000000000..7ce84348ff946
--- /dev/null
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.h
@@ -0,0 +1,85 @@
+//===- SLPUtils.h - SLP Vectorizer free utility helpers --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Internal header used by SLPVectorizer.cpp. It declares free helper
+// functions that do not depend on BoUpSLP, InstructionsState, or any other
+// SLP-private type. Splitting them out keeps SLPVectorizer.cpp focused on
+// the build / legality / cost / codegen pipeline.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPUTILS_H
+#define LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPUTILS_H
+
+#include "llvm/ADT/ArrayRef.h"
+
+#include <optional>
+
+namespace llvm {
+class Instruction;
+class Type;
+class Value;
+} // namespace llvm
+
+namespace llvm::slpvectorizer {
+
+/// \returns True if the value is a constant (but not globals/constant
+/// expressions).
+bool isConstant(Value *V);
+
+/// Checks if \p V is one of vector-like instructions, i.e. undef,
+/// insertelement/extractelement with constant indices for fixed vector type
+/// or extractvalue instruction.
+bool isVectorLikeInstWithConstOps(Value *V);
+
+/// \returns the number of elements for Ty.
+unsigned getNumElements(Type *Ty);
+
+/// Returns power-of-2 number of elements in a single register (part), given
+/// the total number of elements \p Size and number of registers (parts) \p
+/// NumParts.
+unsigned getPartNumElems(unsigned Size, unsigned NumParts);
+
+/// Returns correct remaining number of elements, considering total amount
+/// \p Size, (power-of-2 number) of elements in a single register
+/// \p PartNumElems and current register (part) \p Part.
+unsigned getNumElems(unsigned Size, unsigned PartNumElems, unsigned Part);
+
+/// \returns True if all of the instructions in \p VL are in the same block.
+bool allSameBlock(ArrayRef<Value *> VL);
+
+/// \returns True if all of the values in \p VL are constants (but not
+/// globals/constant expressions).
+bool allConstant(ArrayRef<Value *> VL);
+
+/// \returns True if all of the values in \p VL are identical or some of them
+/// are UndefValue.
+bool isSplat(ArrayRef<Value *> VL);
+
+/// \returns True if all of the values in \p VL use the same opcode.
+/// For comparison instructions, also checks if predicates match.
+/// PoisonValues are considered matching. Interchangeable instructions are
+/// not considered.
+bool allSameOpcode(ArrayRef<Value *> VL);
+
+/// \returns Optional element Idx for Extract{Value,Element} instructions.
+std::optional<unsigned> getExtractIndex(const Instruction *E);
+
+/// \returns True iff every value in \p VL has the same Type as the first.
+bool allSameType(ArrayRef<Value *> VL);
+
+/// \returns inserting or extracting index of InsertElement / ExtractElement
+/// instruction, using \p Offset as base offset for index. Only instantiated
+/// for InsertElementInst and ExtractElementInst (see SLPUtils.cpp).
+template <typename T>
+std::optional<unsigned> getInsertExtractIndex(const Value *Inst,
+ unsigned Offset);
+
+} // namespace llvm::slpvectorizer
+
+#endif // LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPUTILS_H
More information about the llvm-commits
mailing list