[llvm] [VectorCombine] Fold deinterleave2 with smaller effective element size (PR #192121)
Luke Lau via llvm-commits
llvm-commits at lists.llvm.org
Sat Apr 18 03:08:52 PDT 2026
================
@@ -5531,6 +5532,138 @@ bool VectorCombine::foldInterleaveIntrinsics(Instruction &I) {
return true;
}
+bool VectorCombine::foldDeinterleaveIntrinsics(Instruction &I) {
+ using namespace PatternMatch;
+ Value *DeinterleavedVal;
+ if (!match(&I, m_Deinterleave2(m_Value(DeinterleavedVal))))
+ return false;
+
+ // Given this sequence:
+ // ```
+ // %d = llvm.vector.deinterleave2 <vscale x 16 x i32> %v
+ // %f0 = extractvalue { <vscale x 8 x i32>, <vscale x 8 x i32> } %d, 0
+ // %f1 = extractvalue { <vscale x 8 x i32>, <vscale x 8 x i32> } %d, 1
+ //
+ // %low0 = and <vscale x 8 x i32> %f0, splat (i32 65535)
+ // %low1 = shl <vscale x 8 x i32> %f1, splat (i32 16)
+ // %merge0 = or disjoint <vscale x 8 x i32> %low0, %low1
+ //
+ // %high0 = and <vscale x 8 x i32> %f1, splat (i32 -65536)
+ // %high1 = lshr <vscale x 8 x i32> %f0, splat (i32 16)
+ // %merge1 = or disjoint <vscale x 8 x i32> %high0, %high1
+ // ```
+ // It is actually just de-interleaving a 16-bit vector with double the
+ // vector length. More generally speaking, it's de-interleaving on a vector
+ // with half the element width as the original vector.
+ //
+ // Therefore, we can turn it into:
+ // ```
+ // %narrow.v = bitcast <vscale x 16 x i32> %v to <vscale x 32 x i16>
+ // %d = llvm.vector.deinterleave2 <vscale x 32 x i16> %narrow.v
+ // %f0 = extractvalue { <vscale x 16 x i16>, <vscale x 16 x i16> } %d, 0
+ // %f1 = extractvalue { <vscale x 16 x i16>, <vscale x 16 x i16> } %d, 1
+ //
+ // %merge0 = bitcast <vscale x 16 x i16> %f0 to <vscale x 8 x i32>
+ // %merge1 = bitcast <vscale x 16 x i16> %f1 to <vscale x 8 x i32>
+ // ```
+ VectorType *VecTy = cast<VectorType>(DeinterleavedVal->getType());
+ IntegerType *ElementTy = dyn_cast<IntegerType>(VecTy->getElementType());
+ if (!ElementTy)
+ return false;
+ unsigned ElementWidth = ElementTy->getBitWidth();
+ assert(isPowerOf2_32(ElementWidth));
+ unsigned HalfElementWidth = ElementWidth / 2;
+ APInt LoMask(ElementWidth, 0);
+ LoMask.setLowBits(HalfElementWidth);
+ APInt HiMask(ElementWidth, 0);
+ HiMask.setHighBits(HalfElementWidth);
+
+ if (!I.hasNUses(2))
+ return false;
+ std::array<ExtractValueInst *, 2> OrigFields{};
+ for (User *Usr : I.users()) {
+ auto *E = dyn_cast<ExtractValueInst>(Usr);
+ // The detinerleave result can only be used by extractions.
+ if (!E || E->getNumIndices() != 1)
+ return false;
+ unsigned Idx = *E->idx_begin();
+ // A single field cannot be extracted more than once.
+ if (Idx >= 2 || OrigFields[Idx] || !E->hasNUses(2))
+ return false;
+ OrigFields[Idx] = E;
+ }
+
+ // Find the OR (merge instruction) first.
+ SmallVector<PossiblyDisjointInst *, 2> MergeInsts;
+ for (auto *FieldUsr : OrigFields[0]->users()) {
+ if (!FieldUsr->hasOneUse())
+ return false;
+ auto *Merge = dyn_cast<PossiblyDisjointInst>(*FieldUsr->user_begin());
+ if (!Merge)
+ return false;
+ MergeInsts.push_back(Merge);
+ }
+ assert(MergeInsts.size() == 2);
+
+ // Pattern match bottom-up from the merge instructions.
+ auto MatchMerge = [&](void) -> bool {
+ return match(MergeInsts[0],
+ m_c_Or(m_And(m_Specific(OrigFields[0]), m_SpecificInt(LoMask)),
+ m_Shl(m_Specific(OrigFields[1]),
+ m_SpecificInt(HalfElementWidth)))) &&
+ match(MergeInsts[1],
+ m_c_Or(m_And(m_Specific(OrigFields[1]), m_SpecificInt(HiMask)),
+ m_LShr(m_Specific(OrigFields[0]),
+ m_SpecificInt(HalfElementWidth))));
+ };
+ if (!MatchMerge()) {
+ std::swap(MergeInsts[0], MergeInsts[1]);
+ if (!MatchMerge())
+ return false;
+ }
+
+ // Profitibility check.
----------------
lukel97 wrote:
Nit
```suggestion
// Profitability check.
```
https://github.com/llvm/llvm-project/pull/192121
More information about the llvm-commits
mailing list