Files
Madhur Amilkanthwar e6673bd037 [SLP][Modularization][NFC] Extract type and constant helpers into SLPUtils (1/3) (#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
2026-07-13 09:27:26 +05:30

187 lines
5.9 KiB
C++

//===- 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