| //===- SLPShuffleAnalysis.h - SLP shuffle analysis base ---------*- 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 defines the base class for | |
| // shuffle cost estimation and shuffle instruction emission. It does not depend | |
| // on BoUpSLP or any other SLP-private type. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPSHUFFLEANALYSIS_H | |
| #define LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPSHUFFLEANALYSIS_H | |
| #include "SLPUtils.h" | |
| #include "llvm/ADT/ArrayRef.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/Sequence.h" | |
| #include "llvm/ADT/SmallBitVector.h" | |
| #include "llvm/ADT/SmallVector.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/DerivedTypes.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/Support/Casting.h" | |
| #include <algorithm> | |
| #include <cassert> | |
| namespace llvm::slpvectorizer { | |
| /// The base class for shuffle instruction emission and shuffle cost estimation. | |
| class BaseShuffleAnalysis { | |
| protected: | |
| Type *ScalarTy = nullptr; | |
| BaseShuffleAnalysis(Type *ScalarTy) : ScalarTy(ScalarTy) {} | |
| /// V is expected to be a vectorized value. | |
| /// When REVEC is disabled, there is no difference between VF and | |
| /// VNumElements. | |
| /// When REVEC is enabled, VF is VNumElements / ScalarTyNumElements. | |
| /// e.g., if ScalarTy is <4 x Ty> and V1 is <8 x Ty>, 2 is returned instead | |
| /// of 8. | |
| unsigned getVF(Value *V) const { | |
| assert(V && "V cannot be nullptr"); | |
| assert(isa<FixedVectorType>(V->getType()) && | |
| "V does not have FixedVectorType"); | |
| assert(ScalarTy && "ScalarTy cannot be nullptr"); | |
| unsigned ScalarTyNumElements = getNumElements(ScalarTy); | |
| unsigned VNumElements = | |
| cast<FixedVectorType>(V->getType())->getNumElements(); | |
| assert(VNumElements > ScalarTyNumElements && | |
| "the number of elements of V is not large enough"); | |
| assert(VNumElements % ScalarTyNumElements == 0 && | |
| "the number of elements of V is not a vectorized value"); | |
| return VNumElements / ScalarTyNumElements; | |
| } | |
| /// Checks if the mask is an identity mask. | |
| /// \param IsStrict if is true the function returns false if mask size does | |
| /// not match vector size. | |
| static bool isIdentityMask(ArrayRef<int> Mask, const FixedVectorType *VecTy, | |
| bool IsStrict) { | |
| int Limit = Mask.size(); | |
| int VF = VecTy->getNumElements(); | |
| int Index = -1; | |
| if (VF == Limit && ShuffleVectorInst::isIdentityMask(Mask, Limit)) | |
| return true; | |
| if (!IsStrict) { | |
| // Consider extract subvector starting from index 0. | |
| if (ShuffleVectorInst::isExtractSubvectorMask(Mask, VF, Index) && | |
| Index == 0) | |
| return true; | |
| // All VF-size submasks are identity (e.g. | |
| // <poison,poison,poison,poison,0,1,2,poison,poison,1,2,3> etc. for VF 4). | |
| if (Limit % VF == 0 && all_of(seq<int>(0, Limit / VF), [=](int Idx) { | |
| ArrayRef<int> Slice = Mask.slice(Idx * VF, VF); | |
| return all_of(Slice, equal_to(PoisonMaskElem)) || | |
| ShuffleVectorInst::isIdentityMask(Slice, VF); | |
| })) | |
| return true; | |
| } | |
| return false; | |
| } | |
| /// Tries to combine 2 different masks into single one. | |
| /// \param LocalVF Vector length of the permuted input vector. \p Mask may | |
| /// change the size of the vector, \p LocalVF is the original size of the | |
| /// shuffled vector. | |
| static void combineMasks(unsigned LocalVF, SmallVectorImpl<int> &Mask, | |
| ArrayRef<int> ExtMask) { | |
| unsigned VF = Mask.size(); | |
| SmallVector<int> NewMask(ExtMask.size(), PoisonMaskElem); | |
| for (int I = 0, Sz = ExtMask.size(); I < Sz; ++I) { | |
| if (ExtMask[I] == PoisonMaskElem) | |
| continue; | |
| int MaskedIdx = Mask[ExtMask[I] % VF]; | |
| NewMask[I] = | |
| MaskedIdx == PoisonMaskElem ? PoisonMaskElem : MaskedIdx % LocalVF; | |
| } | |
| Mask.swap(NewMask); | |
| } | |
| /// Looks through shuffles trying to reduce final number of shuffles in the | |
| /// code. The function looks through the previously emitted shuffle | |
| /// instructions and properly mark indices in mask as undef. | |
| /// For example, given the code | |
| /// \code | |
| /// %s1 = shufflevector <2 x ty> %0, poison, <1, 0> | |
| /// %s2 = shufflevector <2 x ty> %1, poison, <1, 0> | |
| /// \endcode | |
| /// and if need to emit shuffle of %s1 and %s2 with mask <1, 0, 3, 2>, it will | |
| /// look through %s1 and %s2 and select vectors %0 and %1 with mask | |
| /// <0, 1, 2, 3> for the shuffle. | |
| /// If 2 operands are of different size, the smallest one will be resized and | |
| /// the mask recalculated properly. | |
| /// For example, given the code | |
| /// \code | |
| /// %s1 = shufflevector <2 x ty> %0, poison, <1, 0, 1, 0> | |
| /// %s2 = shufflevector <2 x ty> %1, poison, <1, 0, 1, 0> | |
| /// \endcode | |
| /// and if need to emit shuffle of %s1 and %s2 with mask <1, 0, 5, 4>, it will | |
| /// look through %s1 and %s2 and select vectors %0 and %1 with mask | |
| /// <0, 1, 2, 3> for the shuffle. | |
| /// So, it tries to transform permutations to simple vector merge, if | |
| /// possible. | |
| /// \param V The input vector which must be shuffled using the given \p Mask. | |
| /// If the better candidate is found, \p V is set to this best candidate | |
| /// vector. | |
| /// \param Mask The input mask for the shuffle. If the best candidate is found | |
| /// during looking-through-shuffles attempt, it is updated accordingly. | |
| /// \param SinglePermute true if the shuffle operation is originally a | |
| /// single-value-permutation. In this case the look-through-shuffles procedure | |
| /// may look for resizing shuffles as the best candidates. | |
| /// \return true if the shuffle results in the non-resizing identity shuffle | |
| /// (and thus can be ignored), false - otherwise. | |
| static bool peekThroughShuffles(Value *&V, SmallVectorImpl<int> &Mask, | |
| bool SinglePermute) { | |
| Value *Op = V; | |
| ShuffleVectorInst *IdentityOp = nullptr; | |
| SmallVector<int> IdentityMask; | |
| while (auto *SV = dyn_cast<ShuffleVectorInst>(Op)) { | |
| // Exit if not a fixed vector type or changing size shuffle. | |
| auto *SVTy = dyn_cast<FixedVectorType>(SV->getType()); | |
| if (!SVTy) | |
| break; | |
| // Remember the identity or broadcast mask, if it is not a resizing | |
| // shuffle. If no better candidates are found, this Op and Mask will be | |
| // used in the final shuffle. | |
| if (isIdentityMask(Mask, SVTy, /*IsStrict=*/false)) { | |
| if (!IdentityOp || !SinglePermute || | |
| (isIdentityMask(Mask, SVTy, /*IsStrict=*/true) && | |
| !ShuffleVectorInst::isZeroEltSplatMask(IdentityMask, | |
| IdentityMask.size()))) { | |
| IdentityOp = SV; | |
| // Store current mask in the IdentityMask so later we did not lost | |
| // this info if IdentityOp is selected as the best candidate for the | |
| // permutation. | |
| IdentityMask.assign(Mask); | |
| } | |
| } | |
| // Remember the broadcast mask. If no better candidates are found, this Op | |
| // and Mask will be used in the final shuffle. | |
| // Zero splat can be used as identity too, since it might be used with | |
| // mask <0, 1, 2, ...>, i.e. identity mask without extra reshuffling. | |
| // E.g. if need to shuffle the vector with the mask <3, 1, 2, 0>, which is | |
| // expensive, the analysis founds out, that the source vector is just a | |
| // broadcast, this original mask can be transformed to identity mask <0, | |
| // 1, 2, 3>. | |
| // \code | |
| // %0 = shuffle %v, poison, zeroinitalizer | |
| // %res = shuffle %0, poison, <3, 1, 2, 0> | |
| // \endcode | |
| // may be transformed to | |
| // \code | |
| // %0 = shuffle %v, poison, zeroinitalizer | |
| // %res = shuffle %0, poison, <0, 1, 2, 3> | |
| // \endcode | |
| if (SV->isZeroEltSplat()) { | |
| IdentityOp = SV; | |
| IdentityMask.assign(Mask); | |
| } | |
| int LocalVF = Mask.size(); | |
| if (auto *SVOpTy = | |
| dyn_cast<FixedVectorType>(SV->getOperand(0)->getType())) | |
| LocalVF = SVOpTy->getNumElements(); | |
| SmallVector<int> ExtMask(Mask.size(), PoisonMaskElem); | |
| for (auto [Idx, I] : | |
| make_filter_range(enumerate(Mask), [&](const auto &P) { | |
| return P.value() != PoisonMaskElem && | |
| static_cast<unsigned>(P.value()) < | |
| SV->getShuffleMask().size(); | |
| })) | |
| ExtMask[Idx] = SV->getMaskValue(I); | |
| bool IsOp1Undef = isUndefVector</*isPoisonOnly=*/true>( | |
| SV->getOperand(0), | |
| buildUseMask(LocalVF, ExtMask, UseMask::FirstArg)) | |
| .all(); | |
| bool IsOp2Undef = isUndefVector</*isPoisonOnly=*/true>( | |
| SV->getOperand(1), | |
| buildUseMask(LocalVF, ExtMask, UseMask::SecondArg)) | |
| .all(); | |
| if (!IsOp1Undef && !IsOp2Undef) { | |
| // Update mask and mark undef elems. | |
| for (int &I : make_filter_range( | |
| Mask, [](int I) { return I != PoisonMaskElem; })) { | |
| if (SV->getMaskValue(I % SV->getShuffleMask().size()) == | |
| PoisonMaskElem) | |
| I = PoisonMaskElem; | |
| } | |
| break; | |
| } | |
| SmallVector<int> ShuffleMask(SV->getShuffleMask()); | |
| combineMasks(LocalVF, ShuffleMask, Mask); | |
| Mask.swap(ShuffleMask); | |
| if (IsOp2Undef) | |
| Op = SV->getOperand(0); | |
| else | |
| Op = SV->getOperand(1); | |
| } | |
| if (auto *OpTy = dyn_cast<FixedVectorType>(Op->getType()); | |
| !OpTy || !isIdentityMask(Mask, OpTy, SinglePermute) || | |
| ShuffleVectorInst::isZeroEltSplatMask(Mask, Mask.size())) { | |
| if (IdentityOp) { | |
| V = IdentityOp; | |
| assert(Mask.size() == IdentityMask.size() && | |
| "Expected masks of same sizes."); | |
| // Clear known poison elements. | |
| for (auto [I, Idx] : enumerate(Mask)) | |
| if (Idx == PoisonMaskElem) | |
| IdentityMask[I] = PoisonMaskElem; | |
| Mask.swap(IdentityMask); | |
| auto *Shuffle = dyn_cast<ShuffleVectorInst>(V); | |
| return SinglePermute && | |
| (isIdentityMask(Mask, cast<FixedVectorType>(V->getType()), | |
| /*IsStrict=*/true) || | |
| (Shuffle && Mask.size() == Shuffle->getShuffleMask().size() && | |
| Shuffle->isZeroEltSplat() && | |
| ShuffleVectorInst::isZeroEltSplatMask(Mask, Mask.size()) && | |
| all_of(enumerate(Mask), [&](const auto &P) { | |
| return P.value() == PoisonMaskElem || | |
| Shuffle->getShuffleMask()[P.index()] == 0; | |
| }))); | |
| } | |
| V = Op; | |
| return false; | |
| } | |
| V = Op; | |
| return true; | |
| } | |
| /// Smart shuffle instruction emission, walks through shuffles trees and | |
| /// tries to find the best matching vector for the actual shuffle | |
| /// instruction. | |
| template <typename T, typename ShuffleBuilderTy, typename... Args> | |
| static T createShuffle(Value *V1, Value *V2, ArrayRef<int> Mask, | |
| ShuffleBuilderTy &Builder, Type *ScalarTy, | |
| [[maybe_unused]] bool ReVec, Args... Arguments) { | |
| assert(V1 && "Expected at least one vector value."); | |
| unsigned ScalarTyNumElements = getNumElements(ScalarTy); | |
| SmallVector<int> NewMask(Mask); | |
| if (ScalarTyNumElements != 1) { | |
| assert(ReVec && "FixedVectorType is not expected."); | |
| transformScalarShuffleIndiciesToVector(ScalarTyNumElements, NewMask); | |
| Mask = NewMask; | |
| } | |
| if (V2) | |
| Builder.resizeToMatch(V1, V2); | |
| int VF = Mask.size(); | |
| if (auto *FTy = dyn_cast<FixedVectorType>(V1->getType())) | |
| VF = FTy->getNumElements(); | |
| if (V2 && !isUndefVector</*IsPoisonOnly=*/true>( | |
| V2, buildUseMask(VF, Mask, UseMask::SecondArg)) | |
| .all()) { | |
| // Peek through shuffles. | |
| Value *Op1 = V1; | |
| Value *Op2 = V2; | |
| int VF = | |
| cast<VectorType>(V1->getType())->getElementCount().getKnownMinValue(); | |
| SmallVector<int> CombinedMask1(Mask.size(), PoisonMaskElem); | |
| SmallVector<int> CombinedMask2(Mask.size(), PoisonMaskElem); | |
| for (int I = 0, E = Mask.size(); I < E; ++I) { | |
| if (Mask[I] < VF) | |
| CombinedMask1[I] = Mask[I]; | |
| else | |
| CombinedMask2[I] = Mask[I] - VF; | |
| } | |
| Value *PrevOp1; | |
| Value *PrevOp2; | |
| do { | |
| PrevOp1 = Op1; | |
| PrevOp2 = Op2; | |
| (void)peekThroughShuffles(Op1, CombinedMask1, /*SinglePermute=*/false); | |
| (void)peekThroughShuffles(Op2, CombinedMask2, /*SinglePermute=*/false); | |
| // Check if we have 2 resizing shuffles - need to peek through operands | |
| // again. | |
| if (auto *SV1 = dyn_cast<ShuffleVectorInst>(Op1)) | |
| if (auto *SV2 = dyn_cast<ShuffleVectorInst>(Op2)) { | |
| SmallVector<int> ExtMask1(Mask.size(), PoisonMaskElem); | |
| for (const auto &P : | |
| make_filter_range(enumerate(CombinedMask1), [](const auto &P) { | |
| return P.value() != PoisonMaskElem; | |
| })) | |
| ExtMask1[P.index()] = SV1->getMaskValue(P.value()); | |
| SmallBitVector UseMask1 = buildUseMask( | |
| cast<FixedVectorType>(SV1->getOperand(1)->getType()) | |
| ->getNumElements(), | |
| ExtMask1, UseMask::SecondArg); | |
| SmallVector<int> ExtMask2(CombinedMask2.size(), PoisonMaskElem); | |
| for (const auto &P : | |
| make_filter_range(enumerate(CombinedMask2), [](const auto &P) { | |
| return P.value() != PoisonMaskElem; | |
| })) | |
| ExtMask2[P.index()] = SV2->getMaskValue(P.value()); | |
| SmallBitVector UseMask2 = buildUseMask( | |
| cast<FixedVectorType>(SV2->getOperand(1)->getType()) | |
| ->getNumElements(), | |
| ExtMask2, UseMask::SecondArg); | |
| if (SV1->getOperand(0)->getType() == | |
| SV2->getOperand(0)->getType() && | |
| SV1->getOperand(0)->getType() != SV1->getType() && | |
| isUndefVector(SV1->getOperand(1), UseMask1).all() && | |
| isUndefVector(SV2->getOperand(1), UseMask2).all()) { | |
| Op1 = SV1->getOperand(0); | |
| Op2 = SV2->getOperand(0); | |
| SmallVector<int> ShuffleMask1(SV1->getShuffleMask()); | |
| int LocalVF = ShuffleMask1.size(); | |
| if (auto *FTy = dyn_cast<FixedVectorType>(Op1->getType())) | |
| LocalVF = FTy->getNumElements(); | |
| combineMasks(LocalVF, ShuffleMask1, CombinedMask1); | |
| CombinedMask1.swap(ShuffleMask1); | |
| SmallVector<int> ShuffleMask2(SV2->getShuffleMask()); | |
| LocalVF = ShuffleMask2.size(); | |
| if (auto *FTy = dyn_cast<FixedVectorType>(Op2->getType())) | |
| LocalVF = FTy->getNumElements(); | |
| combineMasks(LocalVF, ShuffleMask2, CombinedMask2); | |
| CombinedMask2.swap(ShuffleMask2); | |
| } | |
| } | |
| } while (PrevOp1 != Op1 || PrevOp2 != Op2); | |
| Builder.resizeToMatch(Op1, Op2); | |
| VF = std::max(cast<VectorType>(Op1->getType()) | |
| ->getElementCount() | |
| .getKnownMinValue(), | |
| cast<VectorType>(Op2->getType()) | |
| ->getElementCount() | |
| .getKnownMinValue()); | |
| for (int I = 0, E = Mask.size(); I < E; ++I) { | |
| if (CombinedMask2[I] != PoisonMaskElem) { | |
| assert(CombinedMask1[I] == PoisonMaskElem && | |
| "Expected undefined mask element"); | |
| CombinedMask1[I] = CombinedMask2[I] + (Op1 == Op2 ? 0 : VF); | |
| } | |
| } | |
| if (Op1 == Op2 && | |
| (ShuffleVectorInst::isIdentityMask(CombinedMask1, VF) || | |
| (ShuffleVectorInst::isZeroEltSplatMask(CombinedMask1, VF) && | |
| isa<ShuffleVectorInst>(Op1) && | |
| cast<ShuffleVectorInst>(Op1)->getShuffleMask() == | |
| ArrayRef(CombinedMask1)))) | |
| return Builder.createIdentity(Op1); | |
| return Builder.createShuffleVector( | |
| Op1, Op1 == Op2 ? PoisonValue::get(Op1->getType()) : Op2, | |
| CombinedMask1); | |
| } | |
| if (isa<PoisonValue>(V1)) | |
| return Builder.createPoison( | |
| cast<VectorType>(V1->getType())->getElementType(), Mask.size()); | |
| bool IsIdentity = peekThroughShuffles(V1, NewMask, /*SinglePermute=*/true); | |
| assert(V1 && "Expected non-null value after looking through shuffles."); | |
| if (!IsIdentity) | |
| return Builder.createShuffleVector(V1, NewMask, Arguments...); | |
| return Builder.createIdentity(V1); | |
| } | |
| /// Transforms mask \p CommonMask per given \p Mask to make proper set after | |
| /// shuffle emission. | |
| static void transformMaskAfterShuffle(MutableArrayRef<int> CommonMask, | |
| ArrayRef<int> Mask) { | |
| for (unsigned I : seq<unsigned>(CommonMask.size())) | |
| if (Mask[I] != PoisonMaskElem) | |
| CommonMask[I] = I; | |
| } | |
| }; | |
| } // namespace llvm::slpvectorizer | |
| #endif // LLVM_LIB_TRANSFORMS_VECTORIZE_SLPVECTORIZER_SLPSHUFFLEANALYSIS_H |