[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
Tue Jun 30 20:54:16 PDT 2026
https://github.com/madhur13490 created https://github.com/llvm/llvm-project/pull/206881
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
>From e989f93ad4c465d6e08c0c4be15a5d0c413b9117 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 | 177 +-----------------
.../Vectorize/SLPVectorizer/SLPUtils.cpp | 154 +++++++++++++++
.../Vectorize/SLPVectorizer/SLPUtils.h | 117 ++++++++++++
4 files changed, 273 insertions(+), 176 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 a5337eb1eef61..fa2fb509a2513 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"
@@ -333,15 +334,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)) {
@@ -506,47 +498,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) {
@@ -559,55 +510,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
@@ -700,29 +602,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");
- int Index = Offset;
- if (const auto *IE = dyn_cast<T>(Inst)) {
- const auto *VT = dyn_cast<FixedVectorType>(IE->getType());
- if (!VT)
- return std::nullopt;
- const auto *CI = dyn_cast<ConstantInt>(IE->getOperand(2));
- if (!CI)
- return std::nullopt;
- if (CI->getValue().uge(VT->getNumElements()))
- return std::nullopt;
- Index *= VT->getNumElements();
- Index += CI->getZExtValue();
- return Index;
- }
- return std::nullopt;
-}
-
/// \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.
@@ -755,28 +634,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.
@@ -972,31 +829,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
@@ -1839,13 +1671,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..477250c93b06f
--- /dev/null
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.cpp
@@ -0,0 +1,154 @@
+//===- 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/ValueTracking.h"
+#include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/PatternMatch.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <bit>
+
+using namespace llvm;
+using namespace llvm::PatternMatch;
+
+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; });
+}
+
+} // 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..385946bda6c6f
--- /dev/null
+++ b/llvm/lib/Transforms/Vectorize/SLPVectorizer/SLPUtils.h
@@ -0,0 +1,117 @@
+//===- 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 "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Analysis/MemoryLocation.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Value.h"
+
+#include <optional>
+#include <string>
+#include <type_traits>
+
+namespace llvm {
+
+class Constant;
+class Instruction;
+class TargetLibraryInfo;
+class TargetTransformInfo;
+class Type;
+class Value;
+
+namespace 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
+/// or false otherwise.
+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 True if Extract{Value,Element} instruction extracts element Idx.
+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.
+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");
+ int Index = Offset;
+ if (const auto *IE = dyn_cast<T>(Inst)) {
+ const auto *VT = dyn_cast<FixedVectorType>(IE->getType());
+ if (!VT)
+ return std::nullopt;
+ const auto *CI = dyn_cast<ConstantInt>(IE->getOperand(2));
+ if (!CI)
+ return std::nullopt;
+ if (CI->getValue().uge(VT->getNumElements()))
+ return std::nullopt;
+ Index *= VT->getNumElements();
+ Index += CI->getZExtValue();
+ return Index;
+ }
+ return std::nullopt;
+}
+
+} // end namespace slpvectorizer
+} // end namespace llvm
+
+#endif // LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPUTILS_H
More information about the llvm-commits
mailing list