| //===- 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/ADT/Sequence.h" |
| #include "llvm/Analysis/ValueTracking.h" |
| #include "llvm/Analysis/VectorUtils.h" |
| #include "llvm/IR/Constants.h" |
| #include "llvm/IR/DerivedTypes.h" |
| #include "llvm/IR/Instructions.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 <algorithm> |
| #include <string> |
| #include <type_traits> |
| |
| 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) { |
| auto *I = dyn_cast<Instruction>(V); |
| // Non-instructions are vector-like only if they are undef. |
| if (!I) |
| return isa<UndefValue>(V); |
| switch (I->getOpcode()) { |
| case Instruction::ExtractValue: |
| case Instruction::InsertValue: |
| return true; |
| case Instruction::ExtractElement: |
| return isa<FixedVectorType>(I->getOperand(0)->getType()) && |
| isConstant(I->getOperand(1)); |
| case Instruction::InsertElement: |
| return isa<FixedVectorType>(I->getOperand(0)->getType()) && |
| isConstant(I->getOperand(2)); |
| default: |
| return false; |
| } |
| } |
| |
| 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); |
| } |
| |
| #if !defined(NDEBUG) |
| std::string shortBundleName(ArrayRef<Value *> VL, int Idx) { |
| std::string Result; |
| raw_string_ostream OS(Result); |
| if (Idx >= 0) |
| OS << "Idx: " << Idx << ", "; |
| OS << "n=" << VL.size() << " [" << *VL.front() << ", ..]"; |
| return Result; |
| } |
| #endif |
| |
| 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 isCommutative(const Instruction *I, const Value *ValWithUses, |
| bool IsCopyable) { |
| if (auto *Cmp = dyn_cast<CmpInst>(I)) |
| return Cmp->isCommutative(); |
| if (auto *BO = dyn_cast<BinaryOperator>(I)) |
| return BO->isCommutative() || |
| (BO->getOpcode() == Instruction::Sub && ValWithUses->hasUseList() && |
| !ValWithUses->hasNUsesOrMore(UsesLimit) && |
| all_of( |
| ValWithUses->uses(), |
| [&](const Use &U) { |
| // Commutative, if icmp eq/ne sub, 0 |
| CmpPredicate Pred; |
| if (match(U.getUser(), |
| m_ICmp(Pred, m_Specific(U.get()), m_Zero())) && |
| (Pred == ICmpInst::ICMP_EQ || Pred == ICmpInst::ICMP_NE)) |
| return true; |
| // Commutative, if abs(sub nsw, true) or abs(sub, false). |
| ConstantInt *Flag; |
| auto *I = dyn_cast<BinaryOperator>(U.get()); |
| return match(U.getUser(), |
| m_Intrinsic<Intrinsic::abs>( |
| m_Specific(U.get()), m_ConstantInt(Flag))) && |
| ((!IsCopyable && I && !I->hasNoSignedWrap()) || |
| Flag->isOne()); |
| })) || |
| (BO->getOpcode() == Instruction::FSub && ValWithUses->hasUseList() && |
| !ValWithUses->hasNUsesOrMore(UsesLimit) && |
| all_of(ValWithUses->uses(), [](const Use &U) { |
| return match(U.getUser(), |
| m_Intrinsic<Intrinsic::fabs>(m_Specific(U.get()))); |
| })); |
| return I->isCommutative(); |
| } |
| |
| bool isCommutative(const Instruction *I) { return isCommutative(I, I); } |
| |
| bool isCommutableOperand(const Instruction *I, Value *ValWithUses, unsigned Op, |
| bool IsCopyable) { |
| assert(isCommutative(I, ValWithUses, IsCopyable) && |
| "The instruction is not commutative."); |
| if (isa<CmpInst>(I)) |
| return true; |
| if (auto *BO = dyn_cast<BinaryOperator>(I)) { |
| switch (BO->getOpcode()) { |
| case Instruction::Sub: |
| case Instruction::FSub: |
| return true; |
| default: |
| break; |
| } |
| } |
| return I->isCommutableOperand(Op); |
| } |
| |
| unsigned getNumberOfPotentiallyCommutativeOps(Instruction *I) { |
| if (isa<IntrinsicInst>(I) && isCommutative(I)) { |
| // IntrinsicInst::isCommutative returns true if swapping the first "two" |
| // arguments to the intrinsic produces the same result. |
| constexpr unsigned IntrinsicNumOperands = 2; |
| return IntrinsicNumOperands; |
| } |
| return I->getNumOperands(); |
| } |
| |
| std::optional<unsigned> getElementIndex(const Value *Inst, unsigned Offset) { |
| if (auto Index = getInsertExtractIndex<InsertElementInst>(Inst, Offset)) |
| return Index; |
| if (auto Index = getInsertExtractIndex<ExtractElementInst>(Inst, Offset)) |
| return Index; |
| |
| unsigned Index = Offset; |
| |
| const auto *IV = dyn_cast<InsertValueInst>(Inst); |
| if (!IV) |
| return std::nullopt; |
| |
| Type *CurrentType = IV->getType(); |
| for (unsigned I : IV->indices()) { |
| if (const auto *ST = dyn_cast<StructType>(CurrentType)) { |
| Index *= ST->getNumElements(); |
| CurrentType = ST->getElementType(I); |
| } else if (const auto *AT = dyn_cast<ArrayType>(CurrentType)) { |
| Index *= AT->getNumElements(); |
| CurrentType = AT->getElementType(); |
| } else { |
| return std::nullopt; |
| } |
| Index += I; |
| } |
| return Index; |
| } |
| |
| 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 all_of(make_range(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(); |
| } |
| |
| void inversePermutation(ArrayRef<unsigned> Indices, |
| SmallVectorImpl<int> &Mask) { |
| Mask.clear(); |
| const unsigned E = Indices.size(); |
| Mask.resize(E, PoisonMaskElem); |
| for (unsigned I = 0; I < E; ++I) |
| Mask[Indices[I]] = I; |
| } |
| |
| void reorderScalars(SmallVectorImpl<Value *> &Scalars, ArrayRef<int> Mask) { |
| assert(!Mask.empty() && "Expected non-empty mask."); |
| SmallVector<Value *> Prev(Scalars.size(), |
| PoisonValue::get(Scalars.front()->getType())); |
| Prev.swap(Scalars); |
| for (unsigned I = 0, E = Prev.size(); I < E; ++I) |
| if (Mask[I] != PoisonMaskElem) |
| Scalars[Mask[I]] = Prev[I]; |
| } |
| |
| bool allSameType(ArrayRef<Value *> VL) { |
| assert(!VL.empty() && "Expected non-empty list of values."); |
| 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); |
| |
| bool areAllOperandsNonInsts(Value *V) { |
| auto *I = dyn_cast<Instruction>(V); |
| if (!I) |
| return true; |
| return !mayHaveNonDefUseDependency(*I) && |
| all_of(I->operands(), [I](Value *V) { |
| auto *IO = dyn_cast<Instruction>(V); |
| if (!IO) |
| return true; |
| return isa<PHINode>(IO) || IO->getParent() != I->getParent(); |
| }); |
| } |
| |
| bool isUsedOutsideBlock(Value *V) { |
| auto *I = dyn_cast<Instruction>(V); |
| if (!I) |
| return true; |
| // Limits the number of uses to save compile time. |
| return !I->mayReadOrWriteMemory() && !I->hasNUsesOrMore(UsesLimit) && |
| all_of(I->users(), [I](User *U) { |
| auto *IU = dyn_cast<Instruction>(U); |
| if (!IU) |
| return true; |
| return IU->getParent() != I->getParent() || isa<PHINode>(IU); |
| }); |
| } |
| |
| bool doesNotNeedToBeScheduled(Value *V) { |
| return areAllOperandsNonInsts(V) && isUsedOutsideBlock(V); |
| } |
| |
| bool doesNotNeedToSchedule(ArrayRef<Value *> VL) { |
| return !VL.empty() && |
| (all_of(VL, isUsedOutsideBlock) || all_of(VL, areAllOperandsNonInsts)); |
| } |
| |
| void transformScalarShuffleIndiciesToVector(unsigned VecTyNumElements, |
| SmallVectorImpl<int> &Mask) { |
| // The ShuffleBuilder implementation use shufflevector to splat an "element". |
| // But the element have different meaning for SLP (scalar) and REVEC |
| // (vector). We need to expand Mask into masks which shufflevector can use |
| // directly. |
| SmallVector<int> NewMask(Mask.size() * VecTyNumElements); |
| for (unsigned I : seq<unsigned>(Mask.size())) |
| for (auto [J, MaskV] : enumerate(MutableArrayRef(NewMask).slice( |
| I * VecTyNumElements, VecTyNumElements))) |
| MaskV = Mask[I] == PoisonMaskElem ? PoisonMaskElem |
| : Mask[I] * VecTyNumElements + J; |
| Mask.swap(NewMask); |
| } |
| |
| unsigned getShufflevectorNumGroups(ArrayRef<Value *> VL) { |
| if (VL.empty()) |
| return 0; |
| if (!all_of(VL, IsaPred<ShuffleVectorInst>)) |
| return 0; |
| auto *SV = cast<ShuffleVectorInst>(VL.front()); |
| unsigned SVNumElements = |
| cast<FixedVectorType>(SV->getOperand(0)->getType())->getNumElements(); |
| unsigned ShuffleMaskSize = SV->getShuffleMask().size(); |
| if (SVNumElements % ShuffleMaskSize != 0) |
| return 0; |
| unsigned GroupSize = SVNumElements / ShuffleMaskSize; |
| if (GroupSize == 0 || (VL.size() % GroupSize) != 0) |
| return 0; |
| unsigned NumGroup = 0; |
| for (size_t I = 0, E = VL.size(); I != E; I += GroupSize) { |
| auto *SV = cast<ShuffleVectorInst>(VL[I]); |
| Value *Src = SV->getOperand(0); |
| ArrayRef<Value *> Group = VL.slice(I, GroupSize); |
| SmallBitVector ExpectedIndex(GroupSize); |
| if (!all_of(Group, [&](Value *V) { |
| auto *SV = cast<ShuffleVectorInst>(V); |
| // From the same source. |
| if (SV->getOperand(0) != Src) |
| return false; |
| int Index; |
| if (!SV->isExtractSubvectorMask(Index)) |
| return false; |
| ExpectedIndex.set(Index / ShuffleMaskSize); |
| return true; |
| })) |
| return 0; |
| if (!ExpectedIndex.all()) |
| return 0; |
| ++NumGroup; |
| } |
| assert(NumGroup == (VL.size() / GroupSize) && "Unexpected number of groups"); |
| return NumGroup; |
| } |
| |
| SmallVector<int> calculateShufflevectorMask(ArrayRef<Value *> VL) { |
| assert(getShufflevectorNumGroups(VL) && "Not supported shufflevector usage."); |
| auto *SV = cast<ShuffleVectorInst>(VL.front()); |
| unsigned SVNumElements = |
| cast<FixedVectorType>(SV->getOperand(0)->getType())->getNumElements(); |
| SmallVector<int> Mask; |
| unsigned AccumulateLength = 0; |
| for (Value *V : VL) { |
| auto *SV = cast<ShuffleVectorInst>(V); |
| for (int M : SV->getShuffleMask()) |
| Mask.push_back(M == PoisonMaskElem ? PoisonMaskElem |
| : AccumulateLength + M); |
| AccumulateLength += SVNumElements; |
| } |
| return Mask; |
| } |
| |
| SmallBitVector buildUseMask(int VF, ArrayRef<int> Mask, UseMask MaskArg) { |
| SmallBitVector UseMask(VF, true); |
| for (auto [Idx, Value] : enumerate(Mask)) { |
| if (Value == PoisonMaskElem) { |
| if (MaskArg == UseMask::UndefsAsMask) |
| UseMask.reset(Idx); |
| continue; |
| } |
| if (MaskArg == UseMask::FirstArg && Value < VF) |
| UseMask.reset(Value); |
| else if (MaskArg == UseMask::SecondArg && Value >= VF) |
| UseMask.reset(Value - VF); |
| } |
| return UseMask; |
| } |
| |
| template <bool IsPoisonOnly> |
| SmallBitVector isUndefVector(const Value *V, const SmallBitVector &UseMask) { |
| SmallBitVector Res(UseMask.empty() ? 1 : UseMask.size(), true); |
| using T = std::conditional_t<IsPoisonOnly, PoisonValue, UndefValue>; |
| if (isa<T>(V)) |
| return Res; |
| auto *VecTy = dyn_cast<FixedVectorType>(V->getType()); |
| if (!VecTy) |
| return Res.reset(); |
| auto *C = dyn_cast<Constant>(V); |
| if (!C) { |
| if (!UseMask.empty()) { |
| const Value *Base = V; |
| while (auto *II = dyn_cast<InsertElementInst>(Base)) { |
| Base = II->getOperand(0); |
| if (isa<T>(II->getOperand(1))) |
| continue; |
| std::optional<unsigned> Idx = getElementIndex(II); |
| if (!Idx) { |
| Res.reset(); |
| return Res; |
| } |
| if (*Idx < UseMask.size() && !UseMask.test(*Idx)) |
| Res.reset(*Idx); |
| } |
| // TODO: Add analysis for shuffles here too. |
| if (V == Base) { |
| Res.reset(); |
| } else { |
| SmallBitVector SubMask(UseMask.size(), false); |
| Res &= isUndefVector<IsPoisonOnly>(Base, SubMask); |
| } |
| } else { |
| Res.reset(); |
| } |
| return Res; |
| } |
| for (unsigned I = 0, E = VecTy->getNumElements(); I != E; ++I) { |
| if (Constant *Elem = C->getAggregateElement(I)) |
| if (!isa<T>(Elem) && |
| (UseMask.empty() || (I < UseMask.size() && !UseMask.test(I)))) |
| Res.reset(I); |
| } |
| return Res; |
| } |
| |
| template SmallBitVector isUndefVector<false>(const Value *, |
| const SmallBitVector &); |
| template SmallBitVector isUndefVector<true>(const Value *, |
| const SmallBitVector &); |
| |
| bool doesInTreeUserNeedToExtract(Value *Scalar, Instruction *UserInst, |
| TargetLibraryInfo *TLI, |
| const TargetTransformInfo *TTI) { |
| if (!UserInst) |
| return false; |
| unsigned Opcode = UserInst->getOpcode(); |
| switch (Opcode) { |
| case Instruction::Load: { |
| LoadInst *LI = cast<LoadInst>(UserInst); |
| return (LI->getPointerOperand() == Scalar); |
| } |
| case Instruction::Store: { |
| StoreInst *SI = cast<StoreInst>(UserInst); |
| return (SI->getPointerOperand() == Scalar); |
| } |
| case Instruction::Call: { |
| CallInst *CI = cast<CallInst>(UserInst); |
| Intrinsic::ID ID = getVectorIntrinsicIDForCall(CI, TLI); |
| return any_of(enumerate(CI->args()), [&](auto &&Arg) { |
| return isVectorIntrinsicWithScalarOpAtArg(ID, Arg.index(), TTI) && |
| Arg.value().get() == Scalar; |
| }); |
| } |
| default: |
| return false; |
| } |
| } |
| |
| MemoryLocation getLocation(Instruction *I) { |
| if (StoreInst *SI = dyn_cast<StoreInst>(I)) |
| return MemoryLocation::get(SI); |
| if (LoadInst *LI = dyn_cast<LoadInst>(I)) |
| return MemoryLocation::get(LI); |
| return MemoryLocation(); |
| } |
| |
| bool isSimple(Instruction *I) { |
| if (LoadInst *LI = dyn_cast<LoadInst>(I)) |
| return LI->isSimple(); |
| if (StoreInst *SI = dyn_cast<StoreInst>(I)) |
| return SI->isSimple(); |
| if (MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) |
| return !MI->isVolatile(); |
| return true; |
| } |
| |
| void addMask(SmallVectorImpl<int> &Mask, ArrayRef<int> SubMask, |
| bool ExtendingManyInputs) { |
| if (SubMask.empty()) |
| return; |
| assert( |
| (!ExtendingManyInputs || SubMask.size() > Mask.size() || |
| // Check if input scalars were extended to match the size of other node. |
| (SubMask.size() == Mask.size() && Mask.back() == PoisonMaskElem)) && |
| "SubMask with many inputs support must be larger than the mask."); |
| if (Mask.empty()) { |
| Mask.append(SubMask.begin(), SubMask.end()); |
| return; |
| } |
| SmallVector<int> NewMask(SubMask.size(), PoisonMaskElem); |
| int TermValue = std::min(Mask.size(), SubMask.size()); |
| for (int I = 0, E = SubMask.size(); I < E; ++I) { |
| if (SubMask[I] == PoisonMaskElem || |
| (!ExtendingManyInputs && |
| (SubMask[I] >= TermValue || Mask[SubMask[I]] >= TermValue))) |
| continue; |
| NewMask[I] = Mask[SubMask[I]]; |
| } |
| Mask.swap(NewMask); |
| } |
| |
| void fixupOrderingIndices(MutableArrayRef<unsigned> Order) { |
| const size_t Sz = Order.size(); |
| SmallBitVector UnusedIndices(Sz, /*t=*/true); |
| SmallBitVector MaskedIndices(Sz); |
| for (unsigned I = 0; I < Sz; ++I) { |
| if (Order[I] < Sz) |
| UnusedIndices.reset(Order[I]); |
| else |
| MaskedIndices.set(I); |
| } |
| if (MaskedIndices.none()) |
| return; |
| assert(UnusedIndices.count() == MaskedIndices.count() && |
| "Non-synced masked/available indices."); |
| int Idx = UnusedIndices.find_first(); |
| int MIdx = MaskedIndices.find_first(); |
| while (MIdx >= 0) { |
| assert(Idx >= 0 && "Indices must be synced."); |
| Order[MIdx] = Idx; |
| Idx = UnusedIndices.find_next(Idx); |
| MIdx = MaskedIndices.find_next(MIdx); |
| } |
| } |
| |
| SmallBitVector getAltInstrMask(ArrayRef<Value *> VL, Type *ScalarTy, |
| unsigned Opcode0, unsigned Opcode1) { |
| unsigned ScalarTyNumElements = getNumElements(ScalarTy); |
| SmallBitVector OpcodeMask(VL.size() * ScalarTyNumElements, false); |
| for (unsigned Lane : seq<unsigned>(VL.size())) { |
| if (isa<PoisonValue>(VL[Lane])) |
| continue; |
| if (cast<Instruction>(VL[Lane])->getOpcode() == Opcode1) |
| OpcodeMask.set(Lane * ScalarTyNumElements, |
| Lane * ScalarTyNumElements + ScalarTyNumElements); |
| } |
| return OpcodeMask; |
| } |
| |
| SmallVector<Constant *> replicateMask(ArrayRef<Constant *> Val, unsigned VF) { |
| assert(none_of(Val, [](Constant *C) { return C->getType()->isVectorTy(); }) && |
| "Expected scalar constants."); |
| SmallVector<Constant *> NewVal(Val.size() * VF); |
| for (auto [I, V] : enumerate(Val)) |
| std::fill_n(NewVal.begin() + I * VF, VF, V); |
| return NewVal; |
| } |
| |
| } // namespace llvm::slpvectorizer |