[llvm] [VectorCombine] Fold Deinterleave/Interleave Pairs (PR #211022)
Jacob Crawley via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 7 08:56:54 PDT 2026
https://github.com/jacob-crawley updated https://github.com/llvm/llvm-project/pull/211022
>From b7a4e30e03c4285b5ed954035072f8d5bdaa9037 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Tue, 21 Jul 2026 14:53:59 +0000
Subject: [PATCH 01/13] [VectorCombine] Fold interleave of high-half
truncations
Fold an interleave of shifted and truncated deinterleave results into a
deinterleave of the original vector at half the element width.
This replaces a deinterlave/interleave sequence and shift/truncates
with a single half-width deinterleave, enabling improved SVE codegen on
AArch64 targets.
---
.../Transforms/Vectorize/VectorCombine.cpp | 105 +++++++++
.../AArch64/sve-vectorcombine-interleave.ll | 65 ++++++
.../VectorCombine/fold-interleave.ll | 209 ++++++++++++++++++
3 files changed, 379 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll
create mode 100644 llvm/test/Transforms/VectorCombine/fold-interleave.ll
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 741bd4460a5ab..759c826b6e7a7 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -155,6 +155,7 @@ class VectorCombine {
bool foldEquivalentReductionCmp(Instruction &I);
bool foldReduceAddCmpZero(Instruction &I);
bool foldSelectShuffle(Instruction &I, bool FromReduction = false);
+ bool foldInterleaveOfHighHalfTruncs(Instruction &I);
bool foldInterleaveIntrinsics(Instruction &I);
bool foldDeinterleaveIntrinsics(Instruction &I);
bool foldBitcastOfVPLoad(Instruction &I);
@@ -5882,6 +5883,108 @@ bool VectorCombine::foldInsExtVectorToShuffle(Instruction &I) {
return true;
}
+/// Fold an interleave of shifted, truncated deinterleave results into a
+/// deinterleave of the original vector at half the element width, directly
+/// selecting the high half of each source element.
+///
+/// ```
+/// %d = call { <vscale x 2 x i16>, ... }
+/// @llvm.vector.deinterleave4(<vscale x 8 x i16> %x)
+/// %d0 = extractvalue { <vscale x 2 x i16>, ... } %d, 0
+/// %d1 = extractvalue { <vscale x 2 x i16>, ... } %d, 1
+/// %d2 = extractvalue { <vscale x 2 x i16>, ... } %d, 2
+/// %d3 = extractvalue { <vscale x 2 x i16>, ... } %d, 3
+/// %s0 = lshr <vscale x 2 x i16> %d0, splat (i16 8)
+/// %s1 = lshr <vscale x 2 x i16> %d1, splat (i16 8)
+/// %s2 = lshr <vscale x 2 x i16> %d2, splat (i16 8)
+/// %s3 = lshr <vscale x 2 x i16> %d3, splat (i16 8)
+/// %t0 = trunc <vscale x 2 x i16> %s0 to <vscale x 2 x i8>
+/// %t1 = trunc <vscale x 2 x i16> %s1 to <vscale x 2 x i8>
+/// %t2 = trunc <vscale x 2 x i16> %s2 to <vscale x 2 x i8>
+/// %t3 = trunc <vscale x 2 x i16> %s3 to <vscale x 2 x i8>
+/// %r = call <vscale x 8 x i8> @llvm.vector.interleave4(%t0, %t1, %t2, %t3)
+/// ```
+/// becomes:
+///
+/// ```
+/// %bc = bitcast <vscale x 8 x i16> %x to <vscale x 16 x i8>
+/// %d = call { <vscale x 8 x i8>, <vscale x 8 x i8> }
+/// @llvm.vector.deinterleave2(<vscale x 16 x i8> %bc)
+/// %r = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } %d, 1
+/// ```
+bool VectorCombine::foldInterleaveOfHighHalfTruncs(Instruction &I) {
+ auto *Interleave = dyn_cast<IntrinsicInst>(&I);
+ if (!Interleave || Interleave->hasOperandBundles())
+ return false;
+
+ unsigned Factor = getInterleaveIntrinsicFactor(Interleave->getIntrinsicID());
+ if (!Factor || Interleave->arg_size() != Factor)
+ return false;
+
+ auto *InterleaveTy = dyn_cast<VectorType>(Interleave->getType());
+ if (!InterleaveTy || !InterleaveTy->getElementType()->isIntegerTy())
+ return false;
+
+ unsigned DstEltBits = InterleaveTy->getScalarSizeInBits();
+ Intrinsic::ID DeinterleaveID = Intrinsic::getDeinterleaveIntrinsicID(Factor);
+ IntrinsicInst *Deinterleave = nullptr;
+
+ for (unsigned Idx = 0; Idx != Factor; ++Idx) {
+ auto *Trunc = dyn_cast<TruncInst>(Interleave->getArgOperand(Idx));
+ if (!Trunc || !Trunc->hasOneUse())
+ return false;
+
+ auto *Shift = dyn_cast<BinaryOperator>(Trunc->getOperand(0));
+ if (!Shift || !Shift->hasOneUse() ||
+ (Shift->getOpcode() != Instruction::LShr &&
+ Shift->getOpcode() != Instruction::AShr) ||
+ !match(Shift->getOperand(1), m_SpecificInt(DstEltBits)))
+ return false;
+
+ // Each interleave operand must come from the corresponding deinterleave
+ // result.
+ auto *Extract = dyn_cast<ExtractValueInst>(Shift->getOperand(0));
+ if (!Extract || !Extract->hasOneUse() || Extract->getNumIndices() != 1 ||
+ *Extract->idx_begin() != Idx)
+ return false;
+
+ auto *DI = dyn_cast<IntrinsicInst>(Extract->getAggregateOperand());
+ if (!DI || DI->hasOperandBundles() ||
+ DI->getIntrinsicID() != DeinterleaveID)
+ return false;
+
+ if (!Deinterleave)
+ Deinterleave = DI;
+ else if (DI != Deinterleave)
+ return false;
+ }
+
+ // Require the entire matched tree to become dead, otherwise the replacement
+ // will add another shuffle sequence to the remaining operations.
+ if (!Deinterleave || !Deinterleave->hasNUses(Factor))
+ return false;
+
+ Value *Source = Deinterleave->getArgOperand(0);
+ auto *SourceTy = dyn_cast<VectorType>(Source->getType());
+ if (!SourceTy ||
+ SourceTy->getElementCount() != InterleaveTy->getElementCount() ||
+ !SourceTy->getElementType()->isIntegerTy() ||
+ SourceTy->getScalarSizeInBits() != 2 * DstEltBits)
+ return false;
+
+ auto *BitcastTy = VectorType::getDoubleElementsVectorType(InterleaveTy);
+ Value *Bitcast = Builder.CreateBitCast(Source, BitcastTy);
+ Value *NewDeinterleave = Builder.CreateIntrinsic(
+ Intrinsic::vector_deinterleave2, {BitcastTy}, {Bitcast});
+ // A vector bitcast orders each element's low half first on little-endian
+ // targets and its high half first on big-endian targets.
+ unsigned HighHalfIndex = DL->isLittleEndian() ? 1 : 0;
+ Value *HighHalves =
+ Builder.CreateExtractValue(NewDeinterleave, HighHalfIndex);
+ replaceValue(I, *HighHalves);
+ return true;
+}
+
/// If we're interleaving 2 constant splats, for instance `<vscale x 8 x i32>
/// <splat of 666>` and `<vscale x 8 x i32> <splat of 777>`, we can create a
/// larger splat `<vscale x 8 x i64> <splat of ((777 << 32) | 666)>` first
@@ -6476,6 +6579,8 @@ bool VectorCombine::run() {
return true;
if (scalarizeVPIntrinsic(I))
return true;
+ if (foldInterleaveOfHighHalfTruncs(I))
+ return true;
if (foldInterleaveIntrinsics(I))
return true;
if (foldBitcastOfVPLoad(I))
diff --git a/llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll b/llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll
new file mode 100644
index 0000000000000..731bb482bcb34
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll
@@ -0,0 +1,65 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=vector-combine -mattr=+sve %s -o - | llc -mattr=+sve -o - | FileCheck %s
+
+target triple = "aarch64"
+
+define void @high_half_trunc_interleave4(ptr %src, ptr %dst) {
+; CHECK-LABEL: high_half_trunc_interleave4:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldr z0, [x0, #1, mul vl]
+; CHECK-NEXT: ldr z1, [x0]
+; CHECK-NEXT: uzp2 z0.b, z1.b, z0.b
+; CHECK-NEXT: str z0, [x1]
+; CHECK-NEXT: ret
+ %x = load <vscale x 16 x i16>, ptr %src, align 2
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %x)
+ %d0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %d1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %d2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %d3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %s0 = lshr <vscale x 4 x i16> %d0, splat (i16 8)
+ %s1 = lshr <vscale x 4 x i16> %d1, splat (i16 8)
+ %s2 = lshr <vscale x 4 x i16> %d2, splat (i16 8)
+ %s3 = lshr <vscale x 4 x i16> %d3, splat (i16 8)
+ %t0 = trunc <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
+ %t1 = trunc <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
+ %t2 = trunc <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
+ %t3 = trunc <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
+ %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
+ store <vscale x 16 x i8> %r, ptr %dst, align 1
+ ret void
+}
+
+; Keep the load and bitcast in separate blocks so VectorCombine
+; exposes a byte deinterleave2, which can be selected as a ld2b.
+define void @high_half_trunc_interleave4_byte_load(ptr %src, ptr %dst) {
+; CHECK-LABEL: high_half_trunc_interleave4_byte_load:
+; CHECK: // %bb.0: // %entry
+; CHECK-NEXT: ptrue p0.b
+; CHECK-NEXT: ld2b { z0.b, z1.b }, p0/z, [x0]
+; CHECK-NEXT: str z1, [x1]
+; CHECK-NEXT: ret
+entry:
+ %bytes = load <vscale x 32 x i8>, ptr %src, align 2
+ br label %body
+
+body:
+ %x = bitcast <vscale x 32 x i8> %bytes to <vscale x 16 x i16>
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %x)
+ %d0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %d1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %d2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %d3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %s0 = lshr <vscale x 4 x i16> %d0, splat (i16 8)
+ %s1 = lshr <vscale x 4 x i16> %d1, splat (i16 8)
+ %s2 = lshr <vscale x 4 x i16> %d2, splat (i16 8)
+ %s3 = lshr <vscale x 4 x i16> %d3, splat (i16 8)
+ %t0 = trunc <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
+ %t1 = trunc <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
+ %t2 = trunc <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
+ %t3 = trunc <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
+ %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
+ store <vscale x 16 x i8> %r, ptr %dst, align 1
+ ret void
+}
+
diff --git a/llvm/test/Transforms/VectorCombine/fold-interleave.ll b/llvm/test/Transforms/VectorCombine/fold-interleave.ll
new file mode 100644
index 0000000000000..efe51333c024a
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/fold-interleave.ll
@@ -0,0 +1,209 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=vector-combine -S %s | FileCheck %s
+; RUN: opt -passes=vector-combine -data-layout=E -S %s | FileCheck %s --check-prefixes=BE
+
+define <vscale x 8 x i8> @lshr_trunc_interleave4(<vscale x 8 x i16> %x) {
+; CHECK-LABEL: define <vscale x 8 x i8> @lshr_trunc_interleave4(
+; CHECK-SAME: <vscale x 8 x i16> [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <vscale x 8 x i16> [[X]] to <vscale x 16 x i8>
+; CHECK-NEXT: [[TMP2:%.*]] = call { <vscale x 8 x i8>, <vscale x 8 x i8> } @llvm.vector.deinterleave2.nxv16i8(<vscale x 16 x i8> [[TMP1]])
+; CHECK-NEXT: [[R:%.*]] = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } [[TMP2]], 1
+; CHECK-NEXT: ret <vscale x 8 x i8> [[R]]
+;
+; BE-LABEL: define <vscale x 8 x i8> @lshr_trunc_interleave4(
+; BE-SAME: <vscale x 8 x i16> [[X:%.*]]) {
+; BE-NEXT: [[TMP1:%.*]] = bitcast <vscale x 8 x i16> [[X]] to <vscale x 16 x i8>
+; BE-NEXT: [[TMP2:%.*]] = call { <vscale x 8 x i8>, <vscale x 8 x i8> } @llvm.vector.deinterleave2.nxv16i8(<vscale x 16 x i8> [[TMP1]])
+; BE-NEXT: [[R:%.*]] = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } [[TMP2]], 0
+; BE-NEXT: ret <vscale x 8 x i8> [[R]]
+;
+ %d = call { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } @llvm.vector.deinterleave4.nxv8i16(<vscale x 8 x i16> %x)
+ %d0 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 1
+ %d2 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 2
+ %d3 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 3
+ %s0 = lshr <vscale x 2 x i16> %d0, splat (i16 8)
+ %s1 = lshr <vscale x 2 x i16> %d1, splat (i16 8)
+ %s2 = lshr <vscale x 2 x i16> %d2, splat (i16 8)
+ %s3 = lshr <vscale x 2 x i16> %d3, splat (i16 8)
+ %t0 = trunc <vscale x 2 x i16> %s0 to <vscale x 2 x i8>
+ %t1 = trunc <vscale x 2 x i16> %s1 to <vscale x 2 x i8>
+ %t2 = trunc <vscale x 2 x i16> %s2 to <vscale x 2 x i8>
+ %t3 = trunc <vscale x 2 x i16> %s3 to <vscale x 2 x i8>
+ %r = call <vscale x 8 x i8> @llvm.vector.interleave4.nxv8i8(<vscale x 2 x i8> %t0, <vscale x 2 x i8> %t1, <vscale x 2 x i8> %t2, <vscale x 2 x i8> %t3)
+ ret <vscale x 8 x i8> %r
+}
+
+define <8 x i8> @fixed_lshr_trunc_interleave4(<8 x i16> %x) {
+; CHECK-LABEL: define <8 x i8> @fixed_lshr_trunc_interleave4(
+; CHECK-SAME: <8 x i16> [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[X]] to <16 x i8>
+; CHECK-NEXT: [[TMP2:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.vector.deinterleave2.v16i8(<16 x i8> [[TMP1]])
+; CHECK-NEXT: [[R:%.*]] = extractvalue { <8 x i8>, <8 x i8> } [[TMP2]], 1
+; CHECK-NEXT: ret <8 x i8> [[R]]
+;
+; BE-LABEL: define <8 x i8> @fixed_lshr_trunc_interleave4(
+; BE-SAME: <8 x i16> [[X:%.*]]) {
+; BE-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[X]] to <16 x i8>
+; BE-NEXT: [[TMP2:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.vector.deinterleave2.v16i8(<16 x i8> [[TMP1]])
+; BE-NEXT: [[R:%.*]] = extractvalue { <8 x i8>, <8 x i8> } [[TMP2]], 0
+; BE-NEXT: ret <8 x i8> [[R]]
+;
+ %d = call { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } @llvm.vector.deinterleave4.v8i16(<8 x i16> %x)
+ %d0 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 0
+ %d1 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 1
+ %d2 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 2
+ %d3 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 3
+ %s0 = lshr <2 x i16> %d0, splat (i16 8)
+ %s1 = lshr <2 x i16> %d1, splat (i16 8)
+ %s2 = lshr <2 x i16> %d2, splat (i16 8)
+ %s3 = lshr <2 x i16> %d3, splat (i16 8)
+ %t0 = trunc <2 x i16> %s0 to <2 x i8>
+ %t1 = trunc <2 x i16> %s1 to <2 x i8>
+ %t2 = trunc <2 x i16> %s2 to <2 x i8>
+ %t3 = trunc <2 x i16> %s3 to <2 x i8>
+ %r = call <8 x i8> @llvm.vector.interleave4.v8i8(<2 x i8> %t0, <2 x i8> %t1, <2 x i8> %t2, <2 x i8> %t3)
+ ret <8 x i8> %r
+}
+
+define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(<vscale x 4 x i32> %x) {
+; CHECK-LABEL: define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <vscale x 4 x i32> [[X]] to <vscale x 8 x i16>
+; CHECK-NEXT: [[TMP2:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[TMP1]])
+; CHECK-NEXT: [[R:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[TMP2]], 1
+; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+; BE-LABEL: define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(
+; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; BE-NEXT: [[TMP1:%.*]] = bitcast <vscale x 4 x i32> [[X]] to <vscale x 8 x i16>
+; BE-NEXT: [[TMP2:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[TMP1]])
+; BE-NEXT: [[R:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[TMP2]], 0
+; BE-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
+ %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %s0 = lshr exact <vscale x 2 x i32> %d0, splat (i32 16)
+ %s1 = ashr exact <vscale x 2 x i32> %d1, splat (i32 16)
+ %t0 = trunc nuw <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
+ %t1 = trunc nuw <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
+ %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
+ ret <vscale x 4 x i16> %r
+}
+
+; Negative test - the fold should only be applied if the shifts are equal.
+define <vscale x 4 x i16> @different_shift_amounts(<vscale x 4 x i32> %x) {
+; CHECK-LABEL: define <vscale x 4 x i16> @different_shift_amounts(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 15)
+; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
+; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+; BE-LABEL: define <vscale x 4 x i16> @different_shift_amounts(
+; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 15)
+; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
+; BE-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
+ %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
+ %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 15)
+ %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
+ %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
+ %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
+ ret <vscale x 4 x i16> %r
+}
+
+; Negative test - the fold shouldn't be generated as the deitnerleaved fields are passed in the wrong order.
+define <vscale x 4 x i16> @reordered_fields(<vscale x 4 x i32> %x) {
+; CHECK-LABEL: define <vscale x 4 x i16> @reordered_fields(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
+; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T1]], <vscale x 2 x i16> [[T0]])
+; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+; BE-LABEL: define <vscale x 4 x i16> @reordered_fields(
+; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
+; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T1]], <vscale x 2 x i16> [[T0]])
+; BE-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
+ %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
+ %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 16)
+ %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
+ %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
+ %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t1, <vscale x 2 x i16> %t0)
+ ret <vscale x 4 x i16> %r
+}
+
+; Negative test - the fold shouldn't be generated as the trunc has more than one use.
+define <vscale x 4 x i16> @extra_trunc_use(
+; CHECK-LABEL: define <vscale x 4 x i16> @extra_trunc_use(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]], ptr [[DST:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
+; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; CHECK-NEXT: store <vscale x 2 x i16> [[T0]], ptr [[DST]], align 4
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
+; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+; BE-LABEL: define <vscale x 4 x i16> @extra_trunc_use(
+; BE-SAME: <vscale x 4 x i32> [[X:%.*]], ptr [[DST:%.*]]) {
+; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
+; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; BE-NEXT: store <vscale x 2 x i16> [[T0]], ptr [[DST]], align 4
+; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
+; BE-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+ <vscale x 4 x i32> %x, ptr %dst) {
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
+ %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
+ %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 16)
+ %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
+ %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
+ store <vscale x 2 x i16> %t0, ptr %dst
+ %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
+ ret <vscale x 4 x i16> %r
+}
+
>From e0afad0945f658b52be79b25b19c35f413078f1a Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Tue, 28 Jul 2026 15:06:13 +0000
Subject: [PATCH 02/13] Generalise the Vector Combine to fold away
deinterleave/interleave pairs.
---
.../Transforms/Vectorize/VectorCombine.cpp | 291 ++++++---
.../AArch64/deinterleave-interleave-pairs.ll | 590 ++++++++++++++++++
.../AArch64/sve-vectorcombine-interleave.ll | 17 +-
.../VectorCombine/fold-interleave.ll | 209 -------
4 files changed, 808 insertions(+), 299 deletions(-)
create mode 100644 llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll
rename llvm/test/{CodeGen => Transforms/VectorCombine}/AArch64/sve-vectorcombine-interleave.ll (89%)
delete mode 100644 llvm/test/Transforms/VectorCombine/fold-interleave.ll
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 759c826b6e7a7..d949ae1d6600b 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -155,7 +155,6 @@ class VectorCombine {
bool foldEquivalentReductionCmp(Instruction &I);
bool foldReduceAddCmpZero(Instruction &I);
bool foldSelectShuffle(Instruction &I, bool FromReduction = false);
- bool foldInterleaveOfHighHalfTruncs(Instruction &I);
bool foldInterleaveIntrinsics(Instruction &I);
bool foldDeinterleaveIntrinsics(Instruction &I);
bool foldBitcastOfVPLoad(Instruction &I);
@@ -163,6 +162,7 @@ class VectorCombine {
bool shrinkType(Instruction &I);
bool shrinkLoadForShuffles(Instruction &I);
bool shrinkPhiOfShuffles(Instruction &I);
+ bool foldDeinterleaveInterleavePair(Instruction &I);
void replaceValue(Instruction &Old, Value &New, bool Erase = true) {
LLVM_DEBUG(dbgs() << "VC: Replacing: " << Old << '\n');
@@ -5883,105 +5883,227 @@ bool VectorCombine::foldInsExtVectorToShuffle(Instruction &I) {
return true;
}
-/// Fold an interleave of shifted, truncated deinterleave results into a
-/// deinterleave of the original vector at half the element width, directly
-/// selecting the high half of each source element.
+/// Fold away a matched pair of vector.deinterleave/interleave intrinsics
+/// with a chain of elementwise operations on each between the
+/// deinterleave and interleave.
///
-/// ```
-/// %d = call { <vscale x 2 x i16>, ... }
-/// @llvm.vector.deinterleave4(<vscale x 8 x i16> %x)
-/// %d0 = extractvalue { <vscale x 2 x i16>, ... } %d, 0
-/// %d1 = extractvalue { <vscale x 2 x i16>, ... } %d, 1
-/// %d2 = extractvalue { <vscale x 2 x i16>, ... } %d, 2
-/// %d3 = extractvalue { <vscale x 2 x i16>, ... } %d, 3
-/// %s0 = lshr <vscale x 2 x i16> %d0, splat (i16 8)
-/// %s1 = lshr <vscale x 2 x i16> %d1, splat (i16 8)
-/// %s2 = lshr <vscale x 2 x i16> %d2, splat (i16 8)
-/// %s3 = lshr <vscale x 2 x i16> %d3, splat (i16 8)
-/// %t0 = trunc <vscale x 2 x i16> %s0 to <vscale x 2 x i8>
-/// %t1 = trunc <vscale x 2 x i16> %s1 to <vscale x 2 x i8>
-/// %t2 = trunc <vscale x 2 x i16> %s2 to <vscale x 2 x i8>
-/// %t3 = trunc <vscale x 2 x i16> %s3 to <vscale x 2 x i8>
-/// %r = call <vscale x 8 x i8> @llvm.vector.interleave4(%t0, %t1, %t2, %t3)
-/// ```
-/// becomes:
+/// For example:
+/// ```
+/// %d = call { <2 x i16>, <2 x i16> } @deinterleave2.v4i16(<4 x i16> %v)
+/// %f0 = extractvalue { <2 x i16>, <2 x i16> } %d, 0
+/// %f1 = extractvalue { <2 x i16>, <2 x i16> } %d, 1
///
-/// ```
-/// %bc = bitcast <vscale x 8 x i16> %x to <vscale x 16 x i8>
-/// %d = call { <vscale x 8 x i8>, <vscale x 8 x i8> }
-/// @llvm.vector.deinterleave2(<vscale x 16 x i8> %bc)
-/// %r = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } %d, 1
-/// ```
-bool VectorCombine::foldInterleaveOfHighHalfTruncs(Instruction &I) {
- auto *Interleave = dyn_cast<IntrinsicInst>(&I);
- if (!Interleave || Interleave->hasOperandBundles())
- return false;
+/// %u0 = add <2 x i16> %f0, splat (i16 3)
+/// %u1 = add <2 x i16> %f1, splat (i16 3)
+///
+/// %r = call <4 x i16> @interleave2.v4i16(<2 x i16> %u0, <2 x i16> %u1)
+/// ```
+/// Folds to:
+/// ```
+/// %r = add <4 x i16> %v, splat (i16 3)
+/// ```
+bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
+ auto *Deinterleave = dyn_cast<IntrinsicInst>(&I);
+ if (!Deinterleave)
+ return false;
+
+ unsigned Factor =
+ getDeinterleaveIntrinsicFactor(Deinterleave->getIntrinsicID());
+ if (!Factor || Deinterleave->hasOperandBundles() ||
+ !Deinterleave->hasNUndroppableUses(Factor))
+ return false;
+
+ const Intrinsic::ID InterleaveIID =
+ Intrinsic::getInterleaveIntrinsicID(Factor);
+
+ // Collect one extract for each deinterleaved field.
+ SmallVector<Instruction *, 8> CurrentInsts(Factor, nullptr);
+ for (Use &U : Deinterleave->uses()) {
+ if (U.getUser()->isDroppable())
+ continue;
- unsigned Factor = getInterleaveIntrinsicFactor(Interleave->getIntrinsicID());
- if (!Factor || Interleave->arg_size() != Factor)
- return false;
+ auto *Extract = dyn_cast<ExtractValueInst>(U.getUser());
+ if (!Extract || Extract->getNumIndices() != 1)
+ return false;
- auto *InterleaveTy = dyn_cast<VectorType>(Interleave->getType());
- if (!InterleaveTy || !InterleaveTy->getElementType()->isIntegerTy())
- return false;
+ unsigned Index = *Extract->idx_begin();
+ if (Index >= Factor || CurrentInsts[Index])
+ return false;
- unsigned DstEltBits = InterleaveTy->getScalarSizeInBits();
- Intrinsic::ID DeinterleaveID = Intrinsic::getDeinterleaveIntrinsicID(Factor);
- IntrinsicInst *Deinterleave = nullptr;
+ CurrentInsts[Index] = Extract;
+ }
- for (unsigned Idx = 0; Idx != Factor; ++Idx) {
- auto *Trunc = dyn_cast<TruncInst>(Interleave->getArgOperand(Idx));
- if (!Trunc || !Trunc->hasOneUse())
- return false;
+ // Stores a chain steps operations with the preceding operand.
+ struct ElementwiseStep {
+ SmallVector<Instruction *, 8> Insts;
+ unsigned ChainOperand;
+ };
- auto *Shift = dyn_cast<BinaryOperator>(Trunc->getOperand(0));
- if (!Shift || !Shift->hasOneUse() ||
- (Shift->getOpcode() != Instruction::LShr &&
- Shift->getOpcode() != Instruction::AShr) ||
- !match(Shift->getOperand(1), m_SpecificInt(DstEltBits)))
- return false;
+ SmallVector<ElementwiseStep, 4> Steps;
+ IntrinsicInst *Interleave = nullptr;
+ unsigned NumVisited = 0;
- // Each interleave operand must come from the corresponding deinterleave
- // result.
- auto *Extract = dyn_cast<ExtractValueInst>(Shift->getOperand(0));
- if (!Extract || !Extract->hasOneUse() || Extract->getNumIndices() != 1 ||
- *Extract->idx_begin() != Idx)
+ auto getNumDataOperands = [](Instruction *Inst) -> unsigned {
+ if (auto *II = dyn_cast<IntrinsicInst>(Inst))
+ return II->arg_size();
+ return Inst->getNumOperands();
+ };
+
+ auto isSupportedElementwise = [&](Instruction *Inst) {
+ auto *ResultTy = dyn_cast<VectorType>(Inst->getType());
+ if (!ResultTy)
return false;
- auto *DI = dyn_cast<IntrinsicInst>(Extract->getAggregateOperand());
- if (!DI || DI->hasOperandBundles() ||
- DI->getIntrinsicID() != DeinterleaveID)
+ if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
+ if (II->hasOperandBundles() || II->isConvergent() ||
+ !isTriviallyVectorizable(II->getIntrinsicID()))
+ return false;
+ } else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
+ SelectInst, FreezeInst>(Inst)) {
return false;
+ }
+
+ // Reject operations such as element-count-changing bitcasts.
+ for (unsigned Op = 0, E = getNumDataOperands(Inst); Op != E; ++Op) {
+ auto *OperandTy = dyn_cast<VectorType>(Inst->getOperand(Op)->getType());
+ if (OperandTy &&
+ OperandTy->getElementCount() != ResultTy->getElementCount())
+ return false;
+ }
+
+ return true;
+ };
+
+ // Follow the chains until they reach the matching interleave.
+ while (NumVisited + Factor <= MaxInstrsToScan) {
+ NumVisited += Factor;
+
+ SmallVector<Instruction *, 8> NextInsts;
+ SmallVector<unsigned, 8> OperandNumbers;
+ NextInsts.reserve(Factor);
+ OperandNumbers.reserve(Factor);
+
+ for (Instruction *Current : CurrentInsts) {
+ Use *U = Current->getSingleUndroppableUse();
+ auto *Next = U ? dyn_cast<Instruction>(U->getUser()) : nullptr;
+ if (!Next)
+ return false;
+
+ NextInsts.push_back(Next);
+ OperandNumbers.push_back(U->getOperandNo());
+ }
+
+ // Check whether every chain has reached the same interleave.
+ if (auto *II = dyn_cast<IntrinsicInst>(NextInsts.front());
+ II && II->getIntrinsicID() == InterleaveIID) {
+ if (II->hasOperandBundles() || II->arg_size() != Factor)
+ return false;
- if (!Deinterleave)
- Deinterleave = DI;
- else if (DI != Deinterleave)
+ for (unsigned Index = 0; Index != Factor; ++Index)
+ if (NextInsts[Index] != II || OperandNumbers[Index] != Index)
+ return false;
+
+ Interleave = II;
+ break;
+ }
+
+ Instruction *FirstInst = NextInsts.front();
+ unsigned ChainOperand = OperandNumbers.front();
+
+ if (!isSupportedElementwise(FirstInst) ||
+ ChainOperand >= getNumDataOperands(FirstInst))
return false;
+
+ for (unsigned Index = 1; Index != Factor; ++Index) {
+ Instruction *Inst = NextInsts[Index];
+ if (OperandNumbers[Index] != ChainOperand ||
+ !FirstInst->isSameOperationAs(Inst))
+ return false;
+ }
+
+ // Non-chain operands must be either the same scalar or splats of that
+ // scalar.
+ auto getSplatOrScalar = [](Value *V) -> Value * {
+ return isa<VectorType>(V->getType()) ? getSplatValue(V) : V;
+ };
+
+ for (unsigned Op = 0, E = getNumDataOperands(FirstInst); Op != E; ++Op) {
+ if (Op == ChainOperand)
+ continue;
+
+ Value *CommonValue = getSplatOrScalar(FirstInst->getOperand(Op));
+ if (!CommonValue || any_of(drop_begin(NextInsts), [&](Instruction *Inst) {
+ return getSplatOrScalar(Inst->getOperand(Op)) != CommonValue;
+ }))
+ return false;
+ }
+
+ CurrentInsts.assign(NextInsts.begin(), NextInsts.end());
+
+ Steps.push_back(ElementwiseStep{std::move(NextInsts), ChainOperand});
}
- // Require the entire matched tree to become dead, otherwise the replacement
- // will add another shuffle sequence to the remaining operations.
- if (!Deinterleave || !Deinterleave->hasNUses(Factor))
+ if (!Interleave)
return false;
- Value *Source = Deinterleave->getArgOperand(0);
- auto *SourceTy = dyn_cast<VectorType>(Source->getType());
- if (!SourceTy ||
- SourceTy->getElementCount() != InterleaveTy->getElementCount() ||
- !SourceTy->getElementType()->isIntegerTy() ||
- SourceTy->getScalarSizeInBits() != 2 * DstEltBits)
- return false;
+ // Rebuild the matched elementwise chain at the original vector width.
+ Builder.SetInsertPoint(Interleave);
- auto *BitcastTy = VectorType::getDoubleElementsVectorType(InterleaveTy);
- Value *Bitcast = Builder.CreateBitCast(Source, BitcastTy);
- Value *NewDeinterleave = Builder.CreateIntrinsic(
- Intrinsic::vector_deinterleave2, {BitcastTy}, {Bitcast});
- // A vector bitcast orders each element's low half first on little-endian
- // targets and its high half first on big-endian targets.
- unsigned HighHalfIndex = DL->isLittleEndian() ? 1 : 0;
- Value *HighHalves =
- Builder.CreateExtractValue(NewDeinterleave, HighHalfIndex);
- replaceValue(I, *HighHalves);
+ Value *WideValue = Deinterleave->getArgOperand(0);
+
+ ElementCount WideEC =
+ cast<VectorType>(Deinterleave->getArgOperand(0)->getType())
+ ->getElementCount();
+ bool DropTruncFlags = false;
+ for (const ElementwiseStep &Step : Steps) {
+ Instruction *NarrowInst = Step.Insts.front();
+
+ unsigned NumOperands = getNumDataOperands(NarrowInst);
+ SmallVector<Value *, 4> NewOperands;
+ NewOperands.reserve(NumOperands);
+
+ for (unsigned Op = 0; Op != NumOperands; ++Op) {
+ Value *Operand = NarrowInst->getOperand(Op);
+
+ if (Op == Step.ChainOperand)
+ Operand = WideValue;
+ else if (isa<VectorType>(Operand->getType()))
+ Operand = Builder.CreateVectorSplat(WideEC, getSplatValue(Operand));
+ NewOperands.push_back(Operand);
+ }
+
+ auto *WideResultTy =
+ VectorType::get(NarrowInst->getType()->getScalarType(), WideEC);
+
+ Value *NewValue;
+ if (isa<BinaryOperator, UnaryOperator>(NarrowInst)) {
+ NewValue = Builder.CreateNAryOp(NarrowInst->getOpcode(), NewOperands);
+ } else if (auto *Cast = dyn_cast<CastInst>(NarrowInst)) {
+ NewValue =
+ Builder.CreateCast(Cast->getOpcode(), NewOperands[0], WideResultTy);
+ } else if (auto *Cmp = dyn_cast<CmpInst>(NarrowInst)) {
+ NewValue = Builder.CreateCmp(Cmp->getPredicate(), NewOperands[0],
+ NewOperands[1]);
+ } else if (isa<SelectInst>(NarrowInst)) {
+ NewValue =
+ Builder.CreateSelect(NewOperands[0], NewOperands[1], NewOperands[2]);
+ } else if (isa<FreezeInst>(NarrowInst)) {
+ NewValue = Builder.CreateFreeze(NewOperands[0]);
+ } else if (auto *II = dyn_cast<IntrinsicInst>(NarrowInst)) {
+ NewValue = Builder.CreateIntrinsic(WideResultTy, II->getIntrinsicID(),
+ NewOperands);
+ } else {
+ llvm_unreachable("Unsupported instruction");
+ }
+
+ SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
+ propagateIRFlags(NewValue, NarrowInsts);
+
+ WideValue = NewValue;
+ }
+
+ assert(WideValue->getType() == Interleave->getType());
+ replaceValue(*Interleave, *WideValue);
return true;
}
@@ -6055,6 +6177,9 @@ bool VectorCombine::foldInterleaveIntrinsics(Instruction &I) {
/// %merge1 = bitcast <vscale x 16 x i16> %f1 to <vscale x 8 x i32>
/// ```
bool VectorCombine::foldDeinterleaveIntrinsics(Instruction &I) {
+ if (foldDeinterleaveInterleavePair(I))
+ return true;
+
// This pattern involves bitcast that is not compatible with big endian.
if (DL->isBigEndian())
return false;
@@ -6579,8 +6704,6 @@ bool VectorCombine::run() {
return true;
if (scalarizeVPIntrinsic(I))
return true;
- if (foldInterleaveOfHighHalfTruncs(I))
- return true;
if (foldInterleaveIntrinsics(I))
return true;
if (foldBitcastOfVPLoad(I))
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll
new file mode 100644
index 0000000000000..6bb7d5220d963
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll
@@ -0,0 +1,590 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=vector-combine %s -S -o - | FileCheck %s
+
+target triple = "aarch64-unknown-linux-gnu"
+
+define <vscale x 12 x i16> @deinterleave3_interleave3_direct(<vscale x 12 x i16> %v) {
+; SCAN3-LABEL: define <vscale x 12 x i16> @deinterleave3_interleave3_direct(
+; SCAN3-SAME: <vscale x 12 x i16> [[V:%.*]]) {
+; SCAN3-NEXT: ret <vscale x 12 x i16> [[V]]
+; CHECK-LABEL: define <vscale x 12 x i16> @deinterleave3_interleave3_direct(
+; CHECK-SAME: <vscale x 12 x i16> [[V:%.*]]) {
+; CHECK-NEXT: ret <vscale x 12 x i16> [[V]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave3.nxv12i16(<vscale x 12 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %r = call <vscale x 12 x i16> @llvm.vector.interleave3.nxv12i16(<vscale x 4 x i16> %f0, <vscale x 4 x i16> %f1, <vscale x 4 x i16> %f2)
+ ret <vscale x 12 x i16> %r
+}
+
+define <vscale x 16 x i8> @deinterleave4_lshr_trunc_interleave4(<vscale x 16 x i16> %v) {
+; CHECK-LABEL: define <vscale x 16 x i8> @deinterleave4_lshr_trunc_interleave4(
+; CHECK-SAME: <vscale x 16 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr <vscale x 16 x i16> [[V]], splat (i16 8)
+; CHECK-NEXT: [[R:%.*]] = trunc nuw <vscale x 16 x i16> [[TMP1]] to <vscale x 16 x i8>
+; CHECK-NEXT: ret <vscale x 16 x i8> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %s0 = lshr <vscale x 4 x i16> %f0, splat (i16 8)
+ %t0 = trunc nuw <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
+ %s1 = lshr <vscale x 4 x i16> %f1, splat (i16 8)
+ %t1 = trunc nuw <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
+ %s2 = lshr <vscale x 4 x i16> %f2, splat (i16 8)
+ %t2 = trunc nuw <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
+ %s3 = lshr <vscale x 4 x i16> %f3, splat (i16 8)
+ %t3 = trunc nuw <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
+ %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
+ ret <vscale x 16 x i8> %r
+}
+
+define <vscale x 16 x i16> @deinterleave4_five_step_chain_interleave4(<vscale x 16 x i32> %v) {
+; CHECK-LABEL: define <vscale x 16 x i16> @deinterleave4_five_step_chain_interleave4(
+; CHECK-SAME: <vscale x 16 x i32> [[V:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = and <vscale x 16 x i32> [[V]], splat (i32 65535)
+; CHECK-NEXT: [[TMP2:%.*]] = lshr <vscale x 16 x i32> [[TMP1]], splat (i32 3)
+; CHECK-NEXT: [[TMP3:%.*]] = xor <vscale x 16 x i32> [[TMP2]], splat (i32 90)
+; CHECK-NEXT: [[TMP4:%.*]] = add <vscale x 16 x i32> [[TMP3]], splat (i32 7)
+; CHECK-NEXT: [[R:%.*]] = trunc <vscale x 16 x i32> [[TMP4]] to <vscale x 16 x i16>
+; CHECK-NEXT: ret <vscale x 16 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } @llvm.vector.deinterleave4.nxv16i32(<vscale x 16 x i32> %v)
+ %f0 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 3
+ %a0 = and <vscale x 4 x i32> %f0, splat (i32 65535)
+ %a1 = and <vscale x 4 x i32> %f1, splat (i32 65535)
+ %a2 = and <vscale x 4 x i32> %f2, splat (i32 65535)
+ %a3 = and <vscale x 4 x i32> %f3, splat (i32 65535)
+ %b0 = lshr <vscale x 4 x i32> %a0, splat (i32 3)
+ %b1 = lshr <vscale x 4 x i32> %a1, splat (i32 3)
+ %b2 = lshr <vscale x 4 x i32> %a2, splat (i32 3)
+ %b3 = lshr <vscale x 4 x i32> %a3, splat (i32 3)
+ %c0 = xor <vscale x 4 x i32> %b0, splat (i32 90)
+ %c1 = xor <vscale x 4 x i32> %b1, splat (i32 90)
+ %c2 = xor <vscale x 4 x i32> %b2, splat (i32 90)
+ %c3 = xor <vscale x 4 x i32> %b3, splat (i32 90)
+ %d0 = add <vscale x 4 x i32> %c0, splat (i32 7)
+ %d1 = add <vscale x 4 x i32> %c1, splat (i32 7)
+ %d2 = add <vscale x 4 x i32> %c2, splat (i32 7)
+ %d3 = add <vscale x 4 x i32> %c3, splat (i32 7)
+ %e0 = trunc <vscale x 4 x i32> %d0 to <vscale x 4 x i16>
+ %e1 = trunc <vscale x 4 x i32> %d1 to <vscale x 4 x i16>
+ %e2 = trunc <vscale x 4 x i32> %d2 to <vscale x 4 x i16>
+ %e3 = trunc <vscale x 4 x i32> %d3 to <vscale x 4 x i16>
+ %r = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> %e0, <vscale x 4 x i16> %e1, <vscale x 4 x i16> %e2, <vscale x 4 x i16> %e3)
+ ret <vscale x 16 x i16> %r
+}
+
+define <vscale x 14 x i16> @deinterleave7_sub_interleave7_lhs_splat(<vscale x 14 x i16> %v) {
+; CHECK-LABEL: define <vscale x 14 x i16> @deinterleave7_sub_interleave7_lhs_splat(
+; CHECK-SAME: <vscale x 14 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = sub <vscale x 14 x i16> splat (i16 1023), [[V]]
+; CHECK-NEXT: ret <vscale x 14 x i16> [[R]]
+;
+ %d = call { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } @llvm.vector.deinterleave7.nxv14i16(<vscale x 14 x i16> %v)
+ %f0 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 2
+ %f3 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 3
+ %f4 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 4
+ %f5 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 5
+ %f6 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 6
+ %u0 = sub <vscale x 2 x i16> splat (i16 1023), %f0
+ %u1 = sub <vscale x 2 x i16> splat (i16 1023), %f1
+ %u2 = sub <vscale x 2 x i16> splat (i16 1023), %f2
+ %u3 = sub <vscale x 2 x i16> splat (i16 1023), %f3
+ %u4 = sub <vscale x 2 x i16> splat (i16 1023), %f4
+ %u5 = sub <vscale x 2 x i16> splat (i16 1023), %f5
+ %u6 = sub <vscale x 2 x i16> splat (i16 1023), %f6
+ %r = call <vscale x 14 x i16> @llvm.vector.interleave7.nxv14i16(<vscale x 2 x i16> %u0, <vscale x 2 x i16> %u1, <vscale x 2 x i16> %u2, <vscale x 2 x i16> %u3, <vscale x 2 x i16> %u4, <vscale x 2 x i16> %u5, <vscale x 2 x i16> %u6)
+ ret <vscale x 14 x i16> %r
+}
+
+define <vscale x 16 x i32> @deinterleave8_and_interleave8(<vscale x 16 x i32> %v) {
+; CHECK-LABEL: define <vscale x 16 x i32> @deinterleave8_and_interleave8(
+; CHECK-SAME: <vscale x 16 x i32> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = and <vscale x 16 x i32> [[V]], splat (i32 255)
+; CHECK-NEXT: ret <vscale x 16 x i32> [[R]]
+;
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave8.nxv16i32(<vscale x 16 x i32> %v)
+ %f0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %f1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %f2 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 2
+ %f3 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 3
+ %f4 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 4
+ %f5 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 5
+ %f6 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 6
+ %f7 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 7
+ %u0 = and <vscale x 2 x i32> %f0, splat (i32 255)
+ %u1 = and <vscale x 2 x i32> %f1, splat (i32 255)
+ %u2 = and <vscale x 2 x i32> %f2, splat (i32 255)
+ %u3 = and <vscale x 2 x i32> %f3, splat (i32 255)
+ %u4 = and <vscale x 2 x i32> %f4, splat (i32 255)
+ %u5 = and <vscale x 2 x i32> %f5, splat (i32 255)
+ %u6 = and <vscale x 2 x i32> %f6, splat (i32 255)
+ %u7 = and <vscale x 2 x i32> %f7, splat (i32 255)
+ %r = call <vscale x 16 x i32> @llvm.vector.interleave8.nxv16i32(<vscale x 2 x i32> %u0, <vscale x 2 x i32> %u1, <vscale x 2 x i32> %u2, <vscale x 2 x i32> %u3, <vscale x 2 x i32> %u4, <vscale x 2 x i32> %u5, <vscale x 2 x i32> %u6, <vscale x 2 x i32> %u7)
+ ret <vscale x 16 x i32> %r
+}
+
+define <vscale x 16 x i16> @deinterleave4_zext_interleave4(<vscale x 16 x i8> %v) {
+; CHECK-LABEL: define <vscale x 16 x i16> @deinterleave4_zext_interleave4(
+; CHECK-SAME: <vscale x 16 x i8> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = zext <vscale x 16 x i8> [[V]] to <vscale x 16 x i16>
+; CHECK-NEXT: ret <vscale x 16 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8> } @llvm.vector.deinterleave4.nxv16i8(<vscale x 16 x i8> %v)
+ %f0 = extractvalue { <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8>, <vscale x 4 x i8> } %d, 3
+ %u0 = zext <vscale x 4 x i8> %f0 to <vscale x 4 x i16>
+ %u1 = zext <vscale x 4 x i8> %f1 to <vscale x 4 x i16>
+ %u2 = zext <vscale x 4 x i8> %f2 to <vscale x 4 x i16>
+ %u3 = zext <vscale x 4 x i8> %f3 to <vscale x 4 x i16>
+ %r = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1, <vscale x 4 x i16> %u2, <vscale x 4 x i16> %u3)
+ ret <vscale x 16 x i16> %r
+}
+
+define <16 x i8> @fixed_deinterleave4_lshr_trunc_interleave4(<16 x i16> %v) {
+; CHECK-LABEL: define <16 x i8> @fixed_deinterleave4_lshr_trunc_interleave4(
+; CHECK-SAME: <16 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = lshr <16 x i16> [[V]], splat (i16 8)
+; CHECK-NEXT: [[R:%.*]] = trunc <16 x i16> [[TMP1]] to <16 x i8>
+; CHECK-NEXT: ret <16 x i8> [[R]]
+;
+ %d = call { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } @llvm.vector.deinterleave4.v16i16(<16 x i16> %v)
+ %f0 = extractvalue { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } %d, 0
+ %f1 = extractvalue { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } %d, 1
+ %f2 = extractvalue { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } %d, 2
+ %f3 = extractvalue { <4 x i16>, <4 x i16>, <4 x i16>, <4 x i16> } %d, 3
+ %s0 = lshr <4 x i16> %f0, splat (i16 8)
+ %t0 = trunc <4 x i16> %s0 to <4 x i8>
+ %s1 = lshr <4 x i16> %f1, splat (i16 8)
+ %t1 = trunc <4 x i16> %s1 to <4 x i8>
+ %s2 = lshr <4 x i16> %f2, splat (i16 8)
+ %t2 = trunc <4 x i16> %s2 to <4 x i8>
+ %s3 = lshr <4 x i16> %f3, splat (i16 8)
+ %t3 = trunc <4 x i16> %s3 to <4 x i8>
+ %r = call <16 x i8> @llvm.vector.interleave4.v16i8(<4 x i8> %t0, <4 x i8> %t1, <4 x i8> %t2, <4 x i8> %t3)
+ ret <16 x i8> %r
+}
+
+define <vscale x 8 x i16> @deinterleave2_add_separate_splats_interleave2(<vscale x 8 x i16> %v, i16 %x) {
+; CHECK-LABEL: define <vscale x 8 x i16> @deinterleave2_add_separate_splats_interleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]], i16 [[X:%.*]]) {
+; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 8 x i16> poison, i16 [[X]], i64 0
+; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <vscale x 8 x i16> [[DOTSPLATINSERT]], <vscale x 8 x i16> poison, <vscale x 8 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = add <vscale x 8 x i16> [[V]], [[DOTSPLAT]]
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %insert0 = insertelement <vscale x 4 x i16> poison, i16 %x, i64 0
+ %splat0 = shufflevector <vscale x 4 x i16> %insert0, <vscale x 4 x i16> poison, <vscale x 4 x i32> zeroinitializer
+ %insert1 = insertelement <vscale x 4 x i16> poison, i16 %x, i64 0
+ %splat1 = shufflevector <vscale x 4 x i16> %insert1, <vscale x 4 x i16> poison, <vscale x 4 x i32> zeroinitializer
+ %u0 = add <vscale x 4 x i16> %f0, %splat0
+ %u1 = add <vscale x 4 x i16> %f1, %splat1
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+define <vscale x 8 x double> @deinterleave2_fpext_interleave2(<vscale x 8 x float> %v) {
+; CHECK-LABEL: define <vscale x 8 x double> @deinterleave2_fpext_interleave2(
+; CHECK-SAME: <vscale x 8 x float> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = fpext <vscale x 8 x float> [[V]] to <vscale x 8 x double>
+; CHECK-NEXT: ret <vscale x 8 x double> [[R]]
+;
+ %d = call { <vscale x 4 x float>, <vscale x 4 x float> } @llvm.vector.deinterleave2.nxv8f32(<vscale x 8 x float> %v)
+ %f0 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 1
+ %u0 = fpext <vscale x 4 x float> %f0 to <vscale x 4 x double>
+ %u1 = fpext <vscale x 4 x float> %f1 to <vscale x 4 x double>
+ %r = call <vscale x 8 x double> @llvm.vector.interleave2.nxv8f64(<vscale x 4 x double> %u0, <vscale x 4 x double> %u1)
+ ret <vscale x 8 x double> %r
+}
+
+define <vscale x 8 x float> @deinterleave2_fneg_interleave2(<vscale x 8 x float> %v) {
+; CHECK-LABEL: define <vscale x 8 x float> @deinterleave2_fneg_interleave2(
+; CHECK-SAME: <vscale x 8 x float> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = fneg <vscale x 8 x float> [[V]]
+; CHECK-NEXT: ret <vscale x 8 x float> [[R]]
+;
+ %d = call { <vscale x 4 x float>, <vscale x 4 x float> } @llvm.vector.deinterleave2.nxv8f32(<vscale x 8 x float> %v)
+ %f0 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 1
+ %u0 = fneg <vscale x 4 x float> %f0
+ %u1 = fneg <vscale x 4 x float> %f1
+ %r = call <vscale x 8 x float> @llvm.vector.interleave2.nxv8f32(<vscale x 4 x float> %u0, <vscale x 4 x float> %u1)
+ ret <vscale x 8 x float> %r
+}
+
+define <vscale x 8 x float> @deinterleave2_bitcast_interleave2(<vscale x 8 x i32> %v) {
+; CHECK-LABEL: define <vscale x 8 x float> @deinterleave2_bitcast_interleave2(
+; CHECK-SAME: <vscale x 8 x i32> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = bitcast <vscale x 8 x i32> [[V]] to <vscale x 8 x float>
+; CHECK-NEXT: ret <vscale x 8 x float> [[R]]
+;
+ %d = call { <vscale x 4 x i32>, <vscale x 4 x i32> } @llvm.vector.deinterleave2.nxv8i32(<vscale x 8 x i32> %v)
+ %f0 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i32>, <vscale x 4 x i32> } %d, 1
+ %u0 = bitcast <vscale x 4 x i32> %f0 to <vscale x 4 x float>
+ %u1 = bitcast <vscale x 4 x i32> %f1 to <vscale x 4 x float>
+ %r = call <vscale x 8 x float> @llvm.vector.interleave2.nxv8f32(<vscale x 4 x float> %u0, <vscale x 4 x float> %u1)
+ ret <vscale x 8 x float> %r
+}
+
+define <vscale x 8 x i1> @deinterleave2_icmp_interleave2(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i1> @deinterleave2_icmp_interleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = icmp eq <vscale x 8 x i16> [[V]], zeroinitializer
+; CHECK-NEXT: ret <vscale x 8 x i1> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = icmp eq <vscale x 4 x i16> %f0, zeroinitializer
+ %u1 = icmp eq <vscale x 4 x i16> %f1, zeroinitializer
+ %r = call <vscale x 8 x i1> @llvm.vector.interleave2.nxv8i1(<vscale x 4 x i1> %u0, <vscale x 4 x i1> %u1)
+ ret <vscale x 8 x i1> %r
+}
+
+define <vscale x 8 x i16> @deinterleave2_select_interleave2(<vscale x 8 x i1> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @deinterleave2_select_interleave2(
+; CHECK-SAME: <vscale x 8 x i1> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = select <vscale x 8 x i1> [[V]], <vscale x 8 x i16> zeroinitializer, <vscale x 8 x i16> splat (i16 1)
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i1>, <vscale x 4 x i1> } @llvm.vector.deinterleave2.nxv8i1(<vscale x 8 x i1> %v)
+ %f0 = extractvalue { <vscale x 4 x i1>, <vscale x 4 x i1> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i1>, <vscale x 4 x i1> } %d, 1
+ %u0 = select <vscale x 4 x i1> %f0, <vscale x 4 x i16> zeroinitializer, <vscale x 4 x i16> splat (i16 1)
+ %u1 = select <vscale x 4 x i1> %f1, <vscale x 4 x i16> zeroinitializer, <vscale x 4 x i16> splat (i16 1)
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+define <vscale x 8 x i16> @deinterleave2_freeze_interleave2(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @deinterleave2_freeze_interleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = freeze <vscale x 8 x i16> [[V]]
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = freeze <vscale x 4 x i16> %f0
+ %u1 = freeze <vscale x 4 x i16> %f1
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+define <vscale x 8 x i16> @deinterleave2_abs_interleave2(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @deinterleave2_abs_interleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.abs.nxv8i16(<vscale x 8 x i16> [[V]], i1 false)
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> %f0, i1 false)
+ %u1 = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> %f1, i1 false)
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+define <vscale x 8 x i16> @deinterleave2_intersect_flags_interleave2(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @deinterleave2_intersect_flags_interleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = add <vscale x 8 x i16> [[V]], splat (i16 1)
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = add nuw <vscale x 4 x i16> %f0, splat (i16 1)
+ %u1 = add <vscale x 4 x i16> %f1, splat (i16 1)
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+define <vscale x 16 x i16> @control_flow_sdiv(<vscale x 16 x i16> %v, i1 %cond, i16 %splat_value) {
+; CHECK-LABEL: define <vscale x 16 x i16> @control_flow_sdiv(
+; CHECK-SAME: <vscale x 16 x i16> [[V:%.*]], i1 [[COND:%.*]], i16 [[SPLAT_VALUE:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br i1 [[COND]], label %[[THEN:.*]], label %[[ELSE:.*]]
+; CHECK: [[THEN]]:
+; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement <vscale x 16 x i16> poison, i16 [[SPLAT_VALUE]], i64 0
+; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector <vscale x 16 x i16> [[DOTSPLATINSERT]], <vscale x 16 x i16> poison, <vscale x 16 x i32> zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = sdiv <vscale x 16 x i16> [[V]], [[DOTSPLAT]]
+; CHECK-NEXT: ret <vscale x 16 x i16> [[R]]
+; CHECK: [[ELSE]]:
+; CHECK-NEXT: ret <vscale x 16 x i16> zeroinitializer
+;
+entry:
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %insert = insertelement <vscale x 4 x i16> poison, i16 %splat_value, i64 0
+ %splat = shufflevector <vscale x 4 x i16> %insert, <vscale x 4 x i16> poison, <vscale x 4 x i32> zeroinitializer
+ br i1 %cond, label %then, label %else
+
+then:
+ %a0 = sdiv <vscale x 4 x i16> %f0, %splat
+ %a1 = sdiv <vscale x 4 x i16> %f1, %splat
+ %a2 = sdiv <vscale x 4 x i16> %f2, %splat
+ %a3 = sdiv <vscale x 4 x i16> %f3, %splat
+ %r = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> %a0, <vscale x 4 x i16> %a1, <vscale x 4 x i16> %a2, <vscale x 4 x i16> %a3)
+ ret <vscale x 16 x i16> %r
+
+else:
+ ret <vscale x 16 x i16> zeroinitializer
+}
+
+; Negative test: operand bundles on the deinterleave must be preserved.
+define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]]) [ "deopt"(i32 0) ]
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> [[F0]], <vscale x 4 x i16> [[F1]])
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v) [ "deopt"(i32 0) ]
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %f0, <vscale x 4 x i16> %f1)
+ ret <vscale x 8 x i16> %r
+}
+
+; Negative test: one chain uses a different shift amount, so the fold must not happen.
+define <vscale x 16 x i8> @negative_deinterleave4_mismatched_shift_amount(<vscale x 16 x i16> %v) {
+; CHECK-LABEL: define <vscale x 16 x i8> @negative_deinterleave4_mismatched_shift_amount(
+; CHECK-SAME: <vscale x 16 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[F2:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 2
+; CHECK-NEXT: [[F3:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 3
+; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 4 x i16> [[F0]], splat (i16 8)
+; CHECK-NEXT: [[T0:%.*]] = trunc nuw <vscale x 4 x i16> [[S0]] to <vscale x 4 x i8>
+; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 4 x i16> [[F1]], splat (i16 8)
+; CHECK-NEXT: [[T1:%.*]] = trunc nuw <vscale x 4 x i16> [[S1]] to <vscale x 4 x i8>
+; CHECK-NEXT: [[S2:%.*]] = lshr <vscale x 4 x i16> [[F2]], splat (i16 7)
+; CHECK-NEXT: [[T2:%.*]] = trunc nuw <vscale x 4 x i16> [[S2]] to <vscale x 4 x i8>
+; CHECK-NEXT: [[S3:%.*]] = lshr <vscale x 4 x i16> [[F3]], splat (i16 8)
+; CHECK-NEXT: [[T3:%.*]] = trunc nuw <vscale x 4 x i16> [[S3]] to <vscale x 4 x i8>
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> [[T0]], <vscale x 4 x i8> [[T1]], <vscale x 4 x i8> [[T2]], <vscale x 4 x i8> [[T3]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %s0 = lshr <vscale x 4 x i16> %f0, splat (i16 8)
+ %t0 = trunc nuw <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
+ %s1 = lshr <vscale x 4 x i16> %f1, splat (i16 8)
+ %t1 = trunc nuw <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
+ %s2 = lshr <vscale x 4 x i16> %f2, splat (i16 7)
+ %t2 = trunc nuw <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
+ %s3 = lshr <vscale x 4 x i16> %f3, splat (i16 8)
+ %t3 = trunc nuw <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
+ %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
+ ret <vscale x 16 x i8> %r
+}
+
+; Negative test - the fold shouldn't be generated as the deitnerleaved fields are passed in the wrong order.
+define <vscale x 4 x i16> @negative_deinterleave2_reordered_fields(<vscale x 4 x i32> %x) {
+; CHECK-LABEL: define <vscale x 4 x i16> @negative_deinterleave2_reordered_fields(
+; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
+; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
+; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
+; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
+; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
+; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T1]], <vscale x 2 x i16> [[T0]])
+; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
+;
+ %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
+ %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
+ %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
+ %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
+ %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 16)
+ %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
+ %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
+ %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t1, <vscale x 2 x i16> %t0)
+ ret <vscale x 4 x i16> %r
+}
+
+; Negative test: reusing one extracted field and skipping another means the fold must not happen.
+define <vscale x 16 x i16> @negative_deinterleave4_duplicate_extract_operand(<vscale x 16 x i16> %v) {
+; CHECK-LABEL: define <vscale x 16 x i16> @negative_deinterleave4_duplicate_extract_operand(
+; CHECK-SAME: <vscale x 16 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[F2:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 2
+; CHECK-NEXT: [[F0_DUP:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> [[F0]], <vscale x 4 x i16> [[F1]], <vscale x 4 x i16> [[F2]], <vscale x 4 x i16> [[F0_DUP]])
+; CHECK-NEXT: ret <vscale x 16 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %f0.dup = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %r = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> %f0, <vscale x 4 x i16> %f1, <vscale x 4 x i16> %f2, <vscale x 4 x i16> %f0.dup)
+ ret <vscale x 16 x i16> %r
+}
+
+; Negative test: changing the extracted-value operand position in one chain means the fold must not happen.
+define <vscale x 16 x i16> @negative_deinterleave4_mismatched_operand_position(<vscale x 16 x i16> %v) {
+; CHECK-LABEL: define <vscale x 16 x i16> @negative_deinterleave4_mismatched_operand_position(
+; CHECK-SAME: <vscale x 16 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[F2:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 2
+; CHECK-NEXT: [[F3:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 3
+; CHECK-NEXT: [[U0:%.*]] = sub <vscale x 4 x i16> [[F0]], splat (i16 5)
+; CHECK-NEXT: [[U1:%.*]] = sub <vscale x 4 x i16> splat (i16 5), [[F1]]
+; CHECK-NEXT: [[U2:%.*]] = sub <vscale x 4 x i16> splat (i16 5), [[F2]]
+; CHECK-NEXT: [[U3:%.*]] = sub <vscale x 4 x i16> splat (i16 5), [[F3]]
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> [[U0]], <vscale x 4 x i16> [[U1]], <vscale x 4 x i16> [[U2]], <vscale x 4 x i16> [[U3]])
+; CHECK-NEXT: ret <vscale x 16 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %f2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
+ %f3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
+ %u0 = sub <vscale x 4 x i16> %f0, splat (i16 5)
+ %u1 = sub <vscale x 4 x i16> splat (i16 5), %f1
+ %u2 = sub <vscale x 4 x i16> splat (i16 5), %f2
+ %u3 = sub <vscale x 4 x i16> splat (i16 5), %f3
+ %r = call <vscale x 16 x i16> @llvm.vector.interleave4.nxv16i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1, <vscale x 4 x i16> %u2, <vscale x 4 x i16> %u3)
+ ret <vscale x 16 x i16> %r
+}
+
+; Negative test: compare predicates are part of the operation and must match.
+define <vscale x 8 x i1> @negative_deinterleave2_mismatched_predicates(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i1> @negative_deinterleave2_mismatched_predicates(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U0:%.*]] = icmp eq <vscale x 4 x i16> [[F0]], zeroinitializer
+; CHECK-NEXT: [[U1:%.*]] = icmp ne <vscale x 4 x i16> [[F1]], zeroinitializer
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i1> @llvm.vector.interleave2.nxv8i1(<vscale x 4 x i1> [[U0]], <vscale x 4 x i1> [[U1]])
+; CHECK-NEXT: ret <vscale x 8 x i1> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = icmp eq <vscale x 4 x i16> %f0, zeroinitializer
+ %u1 = icmp ne <vscale x 4 x i16> %f1, zeroinitializer
+ %r = call <vscale x 8 x i1> @llvm.vector.interleave2.nxv8i1(<vscale x 4 x i1> %u0, <vscale x 4 x i1> %u1)
+ ret <vscale x 8 x i1> %r
+}
+
+; Negative test: non-data intrinsic operands must match across every chain.
+define <vscale x 8 x i16> @negative_deinterleave2_mismatched_intrinsic_scalar(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_mismatched_intrinsic_scalar(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U0:%.*]] = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> [[F0]], i1 false)
+; CHECK-NEXT: [[U1:%.*]] = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> [[F1]], i1 true)
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> [[U0]], <vscale x 4 x i16> [[U1]])
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> %f0, i1 false)
+ %u1 = call <vscale x 4 x i16> @llvm.abs.nxv4i16(<vscale x 4 x i16> %f1, i1 true)
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+; Negative test: a bitcast that changes the element count is not elementwise.
+define <vscale x 16 x i8> @negative_deinterleave2_element_count_changing_bitcast(<vscale x 8 x i16> %v) {
+; CHECK-LABEL: define <vscale x 16 x i8> @negative_deinterleave2_element_count_changing_bitcast(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U0:%.*]] = bitcast <vscale x 4 x i16> [[F0]] to <vscale x 8 x i8>
+; CHECK-NEXT: [[U1:%.*]] = bitcast <vscale x 4 x i16> [[F1]] to <vscale x 8 x i8>
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 16 x i8> @llvm.vector.interleave2.nxv16i8(<vscale x 8 x i8> [[U0]], <vscale x 8 x i8> [[U1]])
+; CHECK-NEXT: ret <vscale x 16 x i8> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = bitcast <vscale x 4 x i16> %f0 to <vscale x 8 x i8>
+ %u1 = bitcast <vscale x 4 x i16> %f1 to <vscale x 8 x i8>
+ %r = call <vscale x 16 x i8> @llvm.vector.interleave2.nxv16i8(<vscale x 8 x i8> %u0, <vscale x 8 x i8> %u1)
+ ret <vscale x 16 x i8> %r
+}
+
+; Negative test: arbitrary vector operands cannot be widened like splats.
+define <vscale x 8 x i16> @negative_deinterleave2_non_splat_vector_operand(<vscale x 8 x i16> %v, <vscale x 4 x i16> %x0, <vscale x 4 x i16> %x1) {
+; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_non_splat_vector_operand(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]], <vscale x 4 x i16> [[X0:%.*]], <vscale x 4 x i16> [[X1:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U0:%.*]] = add <vscale x 4 x i16> [[F0]], [[X0]]
+; CHECK-NEXT: [[U1:%.*]] = add <vscale x 4 x i16> [[F1]], [[X1]]
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> [[U0]], <vscale x 4 x i16> [[U1]])
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = add <vscale x 4 x i16> %f0, %x0
+ %u1 = add <vscale x 4 x i16> %f1, %x1
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
+
+; Negative test: the fold must not remove an intermediate value with another use.
+define <vscale x 8 x i16> @negative_deinterleave2_extra_intermediate_use(<vscale x 8 x i16> %v, ptr %p) {
+; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_extra_intermediate_use(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U0:%.*]] = add <vscale x 4 x i16> [[F0]], splat (i16 1)
+; CHECK-NEXT: [[U1:%.*]] = add <vscale x 4 x i16> [[F1]], splat (i16 1)
+; CHECK-NEXT: store <vscale x 4 x i16> [[U0]], ptr [[P]], align 8
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> [[U0]], <vscale x 4 x i16> [[U1]])
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u0 = add <vscale x 4 x i16> %f0, splat (i16 1)
+ %u1 = add <vscale x 4 x i16> %f1, splat (i16 1)
+ store <vscale x 4 x i16> %u0, ptr %p
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
+ ret <vscale x 8 x i16> %r
+}
diff --git a/llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll b/llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
similarity index 89%
rename from llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll
rename to llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
index 731bb482bcb34..11537a5291cbc 100644
--- a/llvm/test/CodeGen/AArch64/sve-vectorcombine-interleave.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
@@ -6,9 +6,11 @@ target triple = "aarch64"
define void @high_half_trunc_interleave4(ptr %src, ptr %dst) {
; CHECK-LABEL: high_half_trunc_interleave4:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldr z0, [x0, #1, mul vl]
-; CHECK-NEXT: ldr z1, [x0]
-; CHECK-NEXT: uzp2 z0.b, z1.b, z0.b
+; CHECK-NEXT: ldr z0, [x0]
+; CHECK-NEXT: ldr z1, [x0, #1, mul vl]
+; CHECK-NEXT: lsr z1.h, z1.h, #8
+; CHECK-NEXT: lsr z0.h, z0.h, #8
+; CHECK-NEXT: uzp1 z0.b, z0.b, z1.b
; CHECK-NEXT: str z0, [x1]
; CHECK-NEXT: ret
%x = load <vscale x 16 x i16>, ptr %src, align 2
@@ -35,9 +37,12 @@ define void @high_half_trunc_interleave4(ptr %src, ptr %dst) {
define void @high_half_trunc_interleave4_byte_load(ptr %src, ptr %dst) {
; CHECK-LABEL: high_half_trunc_interleave4_byte_load:
; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: ptrue p0.b
-; CHECK-NEXT: ld2b { z0.b, z1.b }, p0/z, [x0]
-; CHECK-NEXT: str z1, [x1]
+; CHECK-NEXT: ldr z0, [x0]
+; CHECK-NEXT: ldr z1, [x0, #1, mul vl]
+; CHECK-NEXT: lsr z1.h, z1.h, #8
+; CHECK-NEXT: lsr z0.h, z0.h, #8
+; CHECK-NEXT: uzp1 z0.b, z0.b, z1.b
+; CHECK-NEXT: str z0, [x1]
; CHECK-NEXT: ret
entry:
%bytes = load <vscale x 32 x i8>, ptr %src, align 2
diff --git a/llvm/test/Transforms/VectorCombine/fold-interleave.ll b/llvm/test/Transforms/VectorCombine/fold-interleave.ll
deleted file mode 100644
index efe51333c024a..0000000000000
--- a/llvm/test/Transforms/VectorCombine/fold-interleave.ll
+++ /dev/null
@@ -1,209 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=vector-combine -S %s | FileCheck %s
-; RUN: opt -passes=vector-combine -data-layout=E -S %s | FileCheck %s --check-prefixes=BE
-
-define <vscale x 8 x i8> @lshr_trunc_interleave4(<vscale x 8 x i16> %x) {
-; CHECK-LABEL: define <vscale x 8 x i8> @lshr_trunc_interleave4(
-; CHECK-SAME: <vscale x 8 x i16> [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = bitcast <vscale x 8 x i16> [[X]] to <vscale x 16 x i8>
-; CHECK-NEXT: [[TMP2:%.*]] = call { <vscale x 8 x i8>, <vscale x 8 x i8> } @llvm.vector.deinterleave2.nxv16i8(<vscale x 16 x i8> [[TMP1]])
-; CHECK-NEXT: [[R:%.*]] = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } [[TMP2]], 1
-; CHECK-NEXT: ret <vscale x 8 x i8> [[R]]
-;
-; BE-LABEL: define <vscale x 8 x i8> @lshr_trunc_interleave4(
-; BE-SAME: <vscale x 8 x i16> [[X:%.*]]) {
-; BE-NEXT: [[TMP1:%.*]] = bitcast <vscale x 8 x i16> [[X]] to <vscale x 16 x i8>
-; BE-NEXT: [[TMP2:%.*]] = call { <vscale x 8 x i8>, <vscale x 8 x i8> } @llvm.vector.deinterleave2.nxv16i8(<vscale x 16 x i8> [[TMP1]])
-; BE-NEXT: [[R:%.*]] = extractvalue { <vscale x 8 x i8>, <vscale x 8 x i8> } [[TMP2]], 0
-; BE-NEXT: ret <vscale x 8 x i8> [[R]]
-;
- %d = call { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } @llvm.vector.deinterleave4.nxv8i16(<vscale x 8 x i16> %x)
- %d0 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 0
- %d1 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 1
- %d2 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 2
- %d3 = extractvalue { <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16>, <vscale x 2 x i16> } %d, 3
- %s0 = lshr <vscale x 2 x i16> %d0, splat (i16 8)
- %s1 = lshr <vscale x 2 x i16> %d1, splat (i16 8)
- %s2 = lshr <vscale x 2 x i16> %d2, splat (i16 8)
- %s3 = lshr <vscale x 2 x i16> %d3, splat (i16 8)
- %t0 = trunc <vscale x 2 x i16> %s0 to <vscale x 2 x i8>
- %t1 = trunc <vscale x 2 x i16> %s1 to <vscale x 2 x i8>
- %t2 = trunc <vscale x 2 x i16> %s2 to <vscale x 2 x i8>
- %t3 = trunc <vscale x 2 x i16> %s3 to <vscale x 2 x i8>
- %r = call <vscale x 8 x i8> @llvm.vector.interleave4.nxv8i8(<vscale x 2 x i8> %t0, <vscale x 2 x i8> %t1, <vscale x 2 x i8> %t2, <vscale x 2 x i8> %t3)
- ret <vscale x 8 x i8> %r
-}
-
-define <8 x i8> @fixed_lshr_trunc_interleave4(<8 x i16> %x) {
-; CHECK-LABEL: define <8 x i8> @fixed_lshr_trunc_interleave4(
-; CHECK-SAME: <8 x i16> [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[X]] to <16 x i8>
-; CHECK-NEXT: [[TMP2:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.vector.deinterleave2.v16i8(<16 x i8> [[TMP1]])
-; CHECK-NEXT: [[R:%.*]] = extractvalue { <8 x i8>, <8 x i8> } [[TMP2]], 1
-; CHECK-NEXT: ret <8 x i8> [[R]]
-;
-; BE-LABEL: define <8 x i8> @fixed_lshr_trunc_interleave4(
-; BE-SAME: <8 x i16> [[X:%.*]]) {
-; BE-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[X]] to <16 x i8>
-; BE-NEXT: [[TMP2:%.*]] = call { <8 x i8>, <8 x i8> } @llvm.vector.deinterleave2.v16i8(<16 x i8> [[TMP1]])
-; BE-NEXT: [[R:%.*]] = extractvalue { <8 x i8>, <8 x i8> } [[TMP2]], 0
-; BE-NEXT: ret <8 x i8> [[R]]
-;
- %d = call { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } @llvm.vector.deinterleave4.v8i16(<8 x i16> %x)
- %d0 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 0
- %d1 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 1
- %d2 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 2
- %d3 = extractvalue { <2 x i16>, <2 x i16>, <2 x i16>, <2 x i16> } %d, 3
- %s0 = lshr <2 x i16> %d0, splat (i16 8)
- %s1 = lshr <2 x i16> %d1, splat (i16 8)
- %s2 = lshr <2 x i16> %d2, splat (i16 8)
- %s3 = lshr <2 x i16> %d3, splat (i16 8)
- %t0 = trunc <2 x i16> %s0 to <2 x i8>
- %t1 = trunc <2 x i16> %s1 to <2 x i8>
- %t2 = trunc <2 x i16> %s2 to <2 x i8>
- %t3 = trunc <2 x i16> %s3 to <2 x i8>
- %r = call <8 x i8> @llvm.vector.interleave4.v8i8(<2 x i8> %t0, <2 x i8> %t1, <2 x i8> %t2, <2 x i8> %t3)
- ret <8 x i8> %r
-}
-
-define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(<vscale x 4 x i32> %x) {
-; CHECK-LABEL: define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(
-; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; CHECK-NEXT: [[TMP1:%.*]] = bitcast <vscale x 4 x i32> [[X]] to <vscale x 8 x i16>
-; CHECK-NEXT: [[TMP2:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[TMP1]])
-; CHECK-NEXT: [[R:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[TMP2]], 1
-; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
-;
-; BE-LABEL: define <vscale x 4 x i16> @mixed_shifts_trunc_interleave2(
-; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; BE-NEXT: [[TMP1:%.*]] = bitcast <vscale x 4 x i32> [[X]] to <vscale x 8 x i16>
-; BE-NEXT: [[TMP2:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[TMP1]])
-; BE-NEXT: [[R:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[TMP2]], 0
-; BE-NEXT: ret <vscale x 4 x i16> [[R]]
-;
- %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
- %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
- %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
- %s0 = lshr exact <vscale x 2 x i32> %d0, splat (i32 16)
- %s1 = ashr exact <vscale x 2 x i32> %d1, splat (i32 16)
- %t0 = trunc nuw <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
- %t1 = trunc nuw <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
- %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
- ret <vscale x 4 x i16> %r
-}
-
-; Negative test - the fold should only be applied if the shifts are equal.
-define <vscale x 4 x i16> @different_shift_amounts(<vscale x 4 x i32> %x) {
-; CHECK-LABEL: define <vscale x 4 x i16> @different_shift_amounts(
-; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 15)
-; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
-; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
-;
-; BE-LABEL: define <vscale x 4 x i16> @different_shift_amounts(
-; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 15)
-; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
-; BE-NEXT: ret <vscale x 4 x i16> [[R]]
-;
- %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
- %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
- %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
- %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
- %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 15)
- %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
- %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
- %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
- ret <vscale x 4 x i16> %r
-}
-
-; Negative test - the fold shouldn't be generated as the deitnerleaved fields are passed in the wrong order.
-define <vscale x 4 x i16> @reordered_fields(<vscale x 4 x i32> %x) {
-; CHECK-LABEL: define <vscale x 4 x i16> @reordered_fields(
-; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
-; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T1]], <vscale x 2 x i16> [[T0]])
-; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
-;
-; BE-LABEL: define <vscale x 4 x i16> @reordered_fields(
-; BE-SAME: <vscale x 4 x i32> [[X:%.*]]) {
-; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
-; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T1]], <vscale x 2 x i16> [[T0]])
-; BE-NEXT: ret <vscale x 4 x i16> [[R]]
-;
- %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
- %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
- %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
- %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
- %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 16)
- %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
- %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
- %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t1, <vscale x 2 x i16> %t0)
- ret <vscale x 4 x i16> %r
-}
-
-; Negative test - the fold shouldn't be generated as the trunc has more than one use.
-define <vscale x 4 x i16> @extra_trunc_use(
-; CHECK-LABEL: define <vscale x 4 x i16> @extra_trunc_use(
-; CHECK-SAME: <vscale x 4 x i32> [[X:%.*]], ptr [[DST:%.*]]) {
-; CHECK-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; CHECK-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; CHECK-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; CHECK-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; CHECK-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
-; CHECK-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; CHECK-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; CHECK-NEXT: store <vscale x 2 x i16> [[T0]], ptr [[DST]], align 4
-; CHECK-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
-; CHECK-NEXT: ret <vscale x 4 x i16> [[R]]
-;
-; BE-LABEL: define <vscale x 4 x i16> @extra_trunc_use(
-; BE-SAME: <vscale x 4 x i32> [[X:%.*]], ptr [[DST:%.*]]) {
-; BE-NEXT: [[D:%.*]] = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> [[X]])
-; BE-NEXT: [[D0:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 0
-; BE-NEXT: [[D1:%.*]] = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } [[D]], 1
-; BE-NEXT: [[S0:%.*]] = lshr <vscale x 2 x i32> [[D0]], splat (i32 16)
-; BE-NEXT: [[S1:%.*]] = lshr <vscale x 2 x i32> [[D1]], splat (i32 16)
-; BE-NEXT: [[T0:%.*]] = trunc <vscale x 2 x i32> [[S0]] to <vscale x 2 x i16>
-; BE-NEXT: [[T1:%.*]] = trunc <vscale x 2 x i32> [[S1]] to <vscale x 2 x i16>
-; BE-NEXT: store <vscale x 2 x i16> [[T0]], ptr [[DST]], align 4
-; BE-NEXT: [[R:%.*]] = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> [[T0]], <vscale x 2 x i16> [[T1]])
-; BE-NEXT: ret <vscale x 4 x i16> [[R]]
-;
- <vscale x 4 x i32> %x, ptr %dst) {
- %d = call { <vscale x 2 x i32>, <vscale x 2 x i32> } @llvm.vector.deinterleave2.nxv4i32(<vscale x 4 x i32> %x)
- %d0 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 0
- %d1 = extractvalue { <vscale x 2 x i32>, <vscale x 2 x i32> } %d, 1
- %s0 = lshr <vscale x 2 x i32> %d0, splat (i32 16)
- %s1 = lshr <vscale x 2 x i32> %d1, splat (i32 16)
- %t0 = trunc <vscale x 2 x i32> %s0 to <vscale x 2 x i16>
- %t1 = trunc <vscale x 2 x i32> %s1 to <vscale x 2 x i16>
- store <vscale x 2 x i16> %t0, ptr %dst
- %r = call <vscale x 4 x i16> @llvm.vector.interleave2.nxv4i16(<vscale x 2 x i16> %t0, <vscale x 2 x i16> %t1)
- ret <vscale x 4 x i16> %r
-}
-
>From 12cdae57230d52aa450b3c3babfa7b7e491f32c3 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Thu, 30 Jul 2026 13:42:06 +0000
Subject: [PATCH 03/13] Rm unused variable
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index d949ae1d6600b..4f00d48add34a 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6054,7 +6054,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
ElementCount WideEC =
cast<VectorType>(Deinterleave->getArgOperand(0)->getType())
->getElementCount();
- bool DropTruncFlags = false;
+
for (const ElementwiseStep &Step : Steps) {
Instruction *NarrowInst = Step.Insts.front();
>From 4bddb1d734017e55e017d45cfc06b7299d4cd0f6 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Thu, 30 Jul 2026 14:26:21 +0000
Subject: [PATCH 04/13] move tests
---
.../AArch64/sve-vectorcombine-interleave.ll | 70 -------------------
.../deinterleave-interleave-pairs.ll | 0
2 files changed, 70 deletions(-)
delete mode 100644 llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
rename llvm/test/Transforms/VectorCombine/{AArch64 => }/deinterleave-interleave-pairs.ll (100%)
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll b/llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
deleted file mode 100644
index 11537a5291cbc..0000000000000
--- a/llvm/test/Transforms/VectorCombine/AArch64/sve-vectorcombine-interleave.ll
+++ /dev/null
@@ -1,70 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes=vector-combine -mattr=+sve %s -o - | llc -mattr=+sve -o - | FileCheck %s
-
-target triple = "aarch64"
-
-define void @high_half_trunc_interleave4(ptr %src, ptr %dst) {
-; CHECK-LABEL: high_half_trunc_interleave4:
-; CHECK: // %bb.0:
-; CHECK-NEXT: ldr z0, [x0]
-; CHECK-NEXT: ldr z1, [x0, #1, mul vl]
-; CHECK-NEXT: lsr z1.h, z1.h, #8
-; CHECK-NEXT: lsr z0.h, z0.h, #8
-; CHECK-NEXT: uzp1 z0.b, z0.b, z1.b
-; CHECK-NEXT: str z0, [x1]
-; CHECK-NEXT: ret
- %x = load <vscale x 16 x i16>, ptr %src, align 2
- %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %x)
- %d0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
- %d1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
- %d2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
- %d3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
- %s0 = lshr <vscale x 4 x i16> %d0, splat (i16 8)
- %s1 = lshr <vscale x 4 x i16> %d1, splat (i16 8)
- %s2 = lshr <vscale x 4 x i16> %d2, splat (i16 8)
- %s3 = lshr <vscale x 4 x i16> %d3, splat (i16 8)
- %t0 = trunc <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
- %t1 = trunc <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
- %t2 = trunc <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
- %t3 = trunc <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
- %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
- store <vscale x 16 x i8> %r, ptr %dst, align 1
- ret void
-}
-
-; Keep the load and bitcast in separate blocks so VectorCombine
-; exposes a byte deinterleave2, which can be selected as a ld2b.
-define void @high_half_trunc_interleave4_byte_load(ptr %src, ptr %dst) {
-; CHECK-LABEL: high_half_trunc_interleave4_byte_load:
-; CHECK: // %bb.0: // %entry
-; CHECK-NEXT: ldr z0, [x0]
-; CHECK-NEXT: ldr z1, [x0, #1, mul vl]
-; CHECK-NEXT: lsr z1.h, z1.h, #8
-; CHECK-NEXT: lsr z0.h, z0.h, #8
-; CHECK-NEXT: uzp1 z0.b, z0.b, z1.b
-; CHECK-NEXT: str z0, [x1]
-; CHECK-NEXT: ret
-entry:
- %bytes = load <vscale x 32 x i8>, ptr %src, align 2
- br label %body
-
-body:
- %x = bitcast <vscale x 32 x i8> %bytes to <vscale x 16 x i16>
- %d = call { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave4.nxv16i16(<vscale x 16 x i16> %x)
- %d0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
- %d1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
- %d2 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 2
- %d3 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 3
- %s0 = lshr <vscale x 4 x i16> %d0, splat (i16 8)
- %s1 = lshr <vscale x 4 x i16> %d1, splat (i16 8)
- %s2 = lshr <vscale x 4 x i16> %d2, splat (i16 8)
- %s3 = lshr <vscale x 4 x i16> %d3, splat (i16 8)
- %t0 = trunc <vscale x 4 x i16> %s0 to <vscale x 4 x i8>
- %t1 = trunc <vscale x 4 x i16> %s1 to <vscale x 4 x i8>
- %t2 = trunc <vscale x 4 x i16> %s2 to <vscale x 4 x i8>
- %t3 = trunc <vscale x 4 x i16> %s3 to <vscale x 4 x i8>
- %r = call <vscale x 16 x i8> @llvm.vector.interleave4.nxv16i8(<vscale x 4 x i8> %t0, <vscale x 4 x i8> %t1, <vscale x 4 x i8> %t2, <vscale x 4 x i8> %t3)
- store <vscale x 16 x i8> %r, ptr %dst, align 1
- ret void
-}
-
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
similarity index 100%
rename from llvm/test/Transforms/VectorCombine/AArch64/deinterleave-interleave-pairs.ll
rename to llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
>From 19f87170bad9997eb1ea3a11a9c785bf372357bb Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Tue, 4 Aug 2026 13:08:04 +0000
Subject: [PATCH 05/13] Add debug info and fixup checks
---
.../Transforms/Vectorize/VectorCombine.cpp | 13 ++++++++----
.../deinterleave-interleave-pairs.ll | 20 +++++++++++++++++++
2 files changed, 29 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 4f00d48add34a..1e67d94e371d6 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -5955,7 +5955,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
return false;
if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
- if (II->hasOperandBundles() || II->isConvergent() ||
+ if (II->hasOperandBundles() ||
!isTriviallyVectorizable(II->getIntrinsicID()))
return false;
} else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
@@ -5996,7 +5996,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
// Check whether every chain has reached the same interleave.
if (auto *II = dyn_cast<IntrinsicInst>(NextInsts.front());
II && II->getIntrinsicID() == InterleaveIID) {
- if (II->hasOperandBundles() || II->arg_size() != Factor)
+ if (II->hasOperandBundles())
return false;
for (unsigned Index = 0; Index != Factor; ++Index)
@@ -6010,8 +6010,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
Instruction *FirstInst = NextInsts.front();
unsigned ChainOperand = OperandNumbers.front();
- if (!isSupportedElementwise(FirstInst) ||
- ChainOperand >= getNumDataOperands(FirstInst))
+ if (!isSupportedElementwise(FirstInst))
return false;
for (unsigned Index = 1; Index != Factor; ++Index) {
@@ -6058,6 +6057,9 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
for (const ElementwiseStep &Step : Steps) {
Instruction *NarrowInst = Step.Insts.front();
+ Builder.SetInsertPoint(NarrowInst);
+ Builder.SetCurrentDebugLocation(NarrowInst->getDebugLoc());
+
unsigned NumOperands = getNumDataOperands(NarrowInst);
SmallVector<Value *, 4> NewOperands;
NewOperands.reserve(NumOperands);
@@ -6099,6 +6101,9 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
propagateIRFlags(NewValue, NarrowInsts);
+ if (auto *NewInst = dyn_cast<Instruction>(NewValue))
+ propagateMetadata(NewInst, NarrowInsts);
+
WideValue = NewValue;
}
diff --git a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
index 6bb7d5220d963..480d42fb5ef69 100644
--- a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
+++ b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
@@ -353,6 +353,23 @@ else:
ret <vscale x 16 x i16> zeroinitializer
}
+define <8 x float> @fpmath_metadata(<8 x float> %v) {
+; CHECK-LABEL: define <8 x float> @fpmath_metadata(
+; CHECK-SAME: <8 x float> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = fadd <8 x float> [[V]], splat (float 1.000000e+00), !fpmath [[META0:![0-9]+]]
+; CHECK-NEXT: ret <8 x float> [[R]]
+;
+ %d = call { <4 x float>, <4 x float> }
+ @llvm.vector.deinterleave2.v8f32(<8 x float> %v)
+ %f0 = extractvalue { <4 x float>, <4 x float> } %d, 0
+ %f1 = extractvalue { <4 x float>, <4 x float> } %d, 1
+ %u0 = fadd <4 x float> %f0, splat (float 1.0), !fpmath !{float 2.5}
+ %u1 = fadd <4 x float> %f1, splat (float 1.0), !fpmath !{float 2.5}
+ %r = call <8 x float>
+ @llvm.vector.interleave2.v8f32(<4 x float> %u0, <4 x float> %u1)
+ ret <8 x float> %r
+}
+
; Negative test: operand bundles on the deinterleave must be preserved.
define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(<vscale x 8 x i16> %v) {
; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(
@@ -588,3 +605,6 @@ define <vscale x 8 x i16> @negative_deinterleave2_extra_intermediate_use(<vscale
%r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
ret <vscale x 8 x i16> %r
}
+;.
+; CHECK: [[META0]] = !{float 2.500000e+00}
+;.
>From 2c9f474d80df7034b6bf8e3a3b104bfafec8e079 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Tue, 4 Aug 2026 13:27:08 +0000
Subject: [PATCH 06/13] Change fpmath_metadata test to scalable vectors
---
.../deinterleave-interleave-pairs.ll | 28 +++++++++----------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
index 480d42fb5ef69..1903b3c24a545 100644
--- a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
+++ b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
@@ -353,21 +353,21 @@ else:
ret <vscale x 16 x i16> zeroinitializer
}
-define <8 x float> @fpmath_metadata(<8 x float> %v) {
-; CHECK-LABEL: define <8 x float> @fpmath_metadata(
-; CHECK-SAME: <8 x float> [[V:%.*]]) {
-; CHECK-NEXT: [[R:%.*]] = fadd <8 x float> [[V]], splat (float 1.000000e+00), !fpmath [[META0:![0-9]+]]
-; CHECK-NEXT: ret <8 x float> [[R]]
+define <vscale x 8 x float> @fpmath_metadata(<vscale x 8 x float> %v) {
+; CHECK-LABEL: define <vscale x 8 x float> @fpmath_metadata(
+; CHECK-SAME: <vscale x 8 x float> [[V:%.*]]) {
+; CHECK-NEXT: [[R:%.*]] = fadd <vscale x 8 x float> [[V]], splat (float 1.000000e+00), !fpmath [[META0:![0-9]+]]
+; CHECK-NEXT: ret <vscale x 8 x float> [[R]]
;
- %d = call { <4 x float>, <4 x float> }
- @llvm.vector.deinterleave2.v8f32(<8 x float> %v)
- %f0 = extractvalue { <4 x float>, <4 x float> } %d, 0
- %f1 = extractvalue { <4 x float>, <4 x float> } %d, 1
- %u0 = fadd <4 x float> %f0, splat (float 1.0), !fpmath !{float 2.5}
- %u1 = fadd <4 x float> %f1, splat (float 1.0), !fpmath !{float 2.5}
- %r = call <8 x float>
- @llvm.vector.interleave2.v8f32(<4 x float> %u0, <4 x float> %u1)
- ret <8 x float> %r
+ %d = call { <vscale x 4 x float>, <vscale x 4 x float> }
+ @llvm.vector.deinterleave2.nxv8f32(<vscale x 8 x float> %v)
+ %f0 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x float>, <vscale x 4 x float> } %d, 1
+ %u0 = fadd <vscale x 4 x float> %f0, splat (float 1.0), !fpmath !{float 2.5}
+ %u1 = fadd <vscale x 4 x float> %f1, splat (float 1.0), !fpmath !{float 2.5}
+ %r = call <vscale x 8 x float>
+ @llvm.vector.interleave2.nxv8f32(<vscale x 4 x float> %u0, <vscale x 4 x float> %u1)
+ ret <vscale x 8 x float> %r
}
; Negative test: operand bundles on the deinterleave must be preserved.
>From d703fed0516710d13057b820015058b5ec56ae8a Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Tue, 4 Aug 2026 16:04:06 +0000
Subject: [PATCH 07/13] cast NewValue to Instruction directly
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 1e67d94e371d6..1654f048471d9 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6047,7 +6047,6 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
// Rebuild the matched elementwise chain at the original vector width.
Builder.SetInsertPoint(Interleave);
-
Value *WideValue = Deinterleave->getArgOperand(0);
ElementCount WideEC =
@@ -6100,9 +6099,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
propagateIRFlags(NewValue, NarrowInsts);
-
- if (auto *NewInst = dyn_cast<Instruction>(NewValue))
- propagateMetadata(NewInst, NarrowInsts);
+ propagateMetadata(cast<Instruction>(NewValue), NarrowInsts);
WideValue = NewValue;
}
>From 3d906ae9e67d62fa292303dbfeea5fe8290f9d88 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Wed, 5 Aug 2026 09:00:02 +0000
Subject: [PATCH 08/13] revert new value cast
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 1654f048471d9..3f86e4ae92477 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6046,7 +6046,6 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
return false;
// Rebuild the matched elementwise chain at the original vector width.
- Builder.SetInsertPoint(Interleave);
Value *WideValue = Deinterleave->getArgOperand(0);
ElementCount WideEC =
@@ -6099,7 +6098,9 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
propagateIRFlags(NewValue, NarrowInsts);
- propagateMetadata(cast<Instruction>(NewValue), NarrowInsts);
+
+ if (auto *NewInst = dyn_cast<Instruction>(NewValue))
+ propagateMetadata(NewInst, NarrowInsts);
WideValue = NewValue;
}
>From 26c1b5079505884795ee839ebdee4917a109bb84 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Wed, 5 Aug 2026 09:50:52 +0000
Subject: [PATCH 09/13] add icmp test
---
.../VectorCombine/deinterleave-interleave-pairs.ll | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
index 1903b3c24a545..e2489a78e14af 100644
--- a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
+++ b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
@@ -370,6 +370,20 @@ define <vscale x 8 x float> @fpmath_metadata(<vscale x 8 x float> %v) {
ret <vscale x 8 x float> %r
}
+define <8 x i1> @icmp_i1_fields(<8 x i1> %v) {
+; CHECK-LABEL: define <8 x i1> @icmp_i1_fields(
+; CHECK-SAME: <8 x i1> [[V:%.*]]) {
+; CHECK-NEXT: ret <8 x i1> [[V]]
+;
+ %d = call { <4 x i1>, <4 x i1> } @llvm.vector.deinterleave2.v8i1(<8 x i1> %v)
+ %f0 = extractvalue { <4 x i1>, <4 x i1> } %d, 0
+ %f1 = extractvalue { <4 x i1>, <4 x i1> } %d, 1
+ %u0 = icmp eq <4 x i1> %f0, splat (i1 true)
+ %u1 = icmp eq <4 x i1> %f1, splat (i1 true)
+ %r = call <8 x i1> @llvm.vector.interleave2.v8i1(<4 x i1> %u0, <4 x i1> %u1)
+ ret <8 x i1> %r
+}
+
; Negative test: operand bundles on the deinterleave must be preserved.
define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(<vscale x 8 x i16> %v) {
; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(
>From 6f520843fbaefe5dce0a04df6e627419410c761b Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Thu, 6 Aug 2026 13:10:06 +0000
Subject: [PATCH 10/13] refactor to use nextInsts and add comments
---
.../Transforms/Vectorize/VectorCombine.cpp | 56 ++++++++++---------
1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 3f86e4ae92477..0c0da4318e09f 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -5958,70 +5958,76 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
if (II->hasOperandBundles() ||
!isTriviallyVectorizable(II->getIntrinsicID()))
return false;
+ assert(!getInterleaveIntrinsicFactor(II->getIntrinsicID()) &&
+ "vector.interleave must not be treated as a trivially "
+ "vectorizable operation.");
} else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
SelectInst, FreezeInst>(Inst)) {
return false;
}
- // Reject operations such as element-count-changing bitcasts.
+ // Reject operations that change the element-count.
for (unsigned Op = 0, E = getNumDataOperands(Inst); Op != E; ++Op) {
auto *OperandTy = dyn_cast<VectorType>(Inst->getOperand(Op)->getType());
- if (OperandTy &&
- OperandTy->getElementCount() != ResultTy->getElementCount())
+ if (any_of(Inst->operands(), [&](Value *Operand) {
+ auto *OperandTy = dyn_cast<VectorType>(Operand->getType());
+ return OperandTy &&
+ OperandTy->getElementCount() != ResultTy->getElementCount();
+ }))
return false;
}
return true;
};
- // Follow the chains until they reach the matching interleave.
+ // Traverse the Factor use chains with a breadth-first search.
+ // At each level, expect every chain to perform the same operation with the
+ // preceding chain value at the same operand position, until they all reach
+ // the matching interleave.
+ SmallVector<unsigned, 8> OperandNumbers(CurrentInsts.size());
while (NumVisited + Factor <= MaxInstrsToScan) {
NumVisited += Factor;
- SmallVector<Instruction *, 8> NextInsts;
- SmallVector<unsigned, 8> OperandNumbers;
- NextInsts.reserve(Factor);
- OperandNumbers.reserve(Factor);
-
- for (Instruction *Current : CurrentInsts) {
+ for (auto [Current, OpNumber] : zip_equal(CurrentInsts, OperandNumbers)) {
Use *U = Current->getSingleUndroppableUse();
auto *Next = U ? dyn_cast<Instruction>(U->getUser()) : nullptr;
if (!Next)
return false;
- NextInsts.push_back(Next);
- OperandNumbers.push_back(U->getOperandNo());
+ Current = Next;
+ OpNumber = U->getOperandNo();
}
// Check whether every chain has reached the same interleave.
- if (auto *II = dyn_cast<IntrinsicInst>(NextInsts.front());
+ if (auto *II = dyn_cast<IntrinsicInst>(CurrentInsts.front());
II && II->getIntrinsicID() == InterleaveIID) {
if (II->hasOperandBundles())
return false;
for (unsigned Index = 0; Index != Factor; ++Index)
- if (NextInsts[Index] != II || OperandNumbers[Index] != Index)
+ if (CurrentInsts[Index] != II || OperandNumbers[Index] != Index)
return false;
Interleave = II;
break;
}
- Instruction *FirstInst = NextInsts.front();
+ Instruction *FirstInst = CurrentInsts.front();
unsigned ChainOperand = OperandNumbers.front();
if (!isSupportedElementwise(FirstInst))
return false;
for (unsigned Index = 1; Index != Factor; ++Index) {
- Instruction *Inst = NextInsts[Index];
+ Instruction *Inst = CurrentInsts[Index];
if (OperandNumbers[Index] != ChainOperand ||
!FirstInst->isSameOperationAs(Inst))
return false;
}
// Non-chain operands must be either the same scalar or splats of that
- // scalar.
+ // scalr. This intentionally rejects differing poison/undef or non-splat
+ // vector operands between chains.
auto getSplatOrScalar = [](Value *V) -> Value * {
return isa<VectorType>(V->getType()) ? getSplatValue(V) : V;
};
@@ -6030,16 +6036,15 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
if (Op == ChainOperand)
continue;
- Value *CommonValue = getSplatOrScalar(FirstInst->getOperand(Op));
- if (!CommonValue || any_of(drop_begin(NextInsts), [&](Instruction *Inst) {
- return getSplatOrScalar(Inst->getOperand(Op)) != CommonValue;
- }))
+ auto SplatOrScalars = map_range(CurrentInsts, [&](Instruction *Inst) {
+ return getSplatOrScalar(Inst->getOperand(Op));
+ });
+
+ if (!*SplatOrScalars.begin() || !all_equal(SplatOrScalars))
return false;
}
- CurrentInsts.assign(NextInsts.begin(), NextInsts.end());
-
- Steps.push_back(ElementwiseStep{std::move(NextInsts), ChainOperand});
+ Steps.push_back(ElementwiseStep{CurrentInsts, ChainOperand});
}
if (!Interleave)
@@ -6049,8 +6054,7 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
Value *WideValue = Deinterleave->getArgOperand(0);
ElementCount WideEC =
- cast<VectorType>(Deinterleave->getArgOperand(0)->getType())
- ->getElementCount();
+ cast<VectorType>(WideValue->getType())->getElementCount();
for (const ElementwiseStep &Step : Steps) {
Instruction *NarrowInst = Step.Insts.front();
>From 6b5ff64c081e8a33c72291f32e9886e35c9b6354 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Thu, 6 Aug 2026 13:30:22 +0000
Subject: [PATCH 11/13] add new tests
---
.../deinterleave-interleave-pairs.ll | 41 ++++++++++++++++++-
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
index e2489a78e14af..952d743bda70a 100644
--- a/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
+++ b/llvm/test/Transforms/VectorCombine/deinterleave-interleave-pairs.ll
@@ -1,8 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes=vector-combine %s -S -o - | FileCheck %s
-target triple = "aarch64-unknown-linux-gnu"
-
define <vscale x 12 x i16> @deinterleave3_interleave3_direct(<vscale x 12 x i16> %v) {
; SCAN3-LABEL: define <vscale x 12 x i16> @deinterleave3_interleave3_direct(
; SCAN3-SAME: <vscale x 12 x i16> [[V:%.*]]) {
@@ -384,6 +382,24 @@ define <8 x i1> @icmp_i1_fields(<8 x i1> %v) {
ret <8 x i1> %r
}
+define <4 x i32> @deinterleave2_select_scalar_condition_interleave2(
+; CHECK-LABEL: define <4 x i32> @deinterleave2_select_scalar_condition_interleave2(
+; CHECK-SAME: <4 x i32> [[V:%.*]], i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[COND:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: [[R:%.*]] = select i1 [[COND]], <4 x i32> [[V]], <4 x i32> splat (i32 7)
+; CHECK-NEXT: ret <4 x i32> [[R]]
+;
+ <4 x i32> %v, i32 %x, i32 %y) {
+ %cond = icmp eq i32 %x, %y
+ %d = call { <2 x i32>, <2 x i32> } @llvm.vector.deinterleave2.v4i32(<4 x i32> %v)
+ %f0 = extractvalue { <2 x i32>, <2 x i32> } %d, 0
+ %f1 = extractvalue { <2 x i32>, <2 x i32> } %d, 1
+ %u0 = select i1 %cond, <2 x i32> %f0, <2 x i32> splat (i32 7)
+ %u1 = select i1 %cond, <2 x i32> %f1, <2 x i32> splat (i32 7)
+ %r = call <4 x i32> @llvm.vector.interleave2.v4i32(<2 x i32> %u0, <2 x i32> %u1)
+ ret <4 x i32> %r
+}
+
; Negative test: operand bundles on the deinterleave must be preserved.
define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(<vscale x 8 x i16> %v) {
; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2_operand_bundle(
@@ -619,6 +635,27 @@ define <vscale x 8 x i16> @negative_deinterleave2_extra_intermediate_use(<vscale
%r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u0, <vscale x 4 x i16> %u1)
ret <vscale x 8 x i16> %r
}
+
+; Negative test: merging the chains at an intermediate instruction prevents the elementwise-chain fold.
+define <vscale x 8 x i16> @negative_deinterleave2(
+; CHECK-LABEL: define <vscale x 8 x i16> @negative_deinterleave2(
+; CHECK-SAME: <vscale x 8 x i16> [[V:%.*]]) {
+; CHECK-NEXT: [[D:%.*]] = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> [[V]])
+; CHECK-NEXT: [[F0:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 0
+; CHECK-NEXT: [[F1:%.*]] = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } [[D]], 1
+; CHECK-NEXT: [[U:%.*]] = add <vscale x 4 x i16> [[F0]], [[F1]]
+; CHECK-NEXT: [[R:%.*]] = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> [[U]], <vscale x 4 x i16> [[U]])
+; CHECK-NEXT: ret <vscale x 8 x i16> [[R]]
+;
+ <vscale x 8 x i16> %v) {
+ %d = call { <vscale x 4 x i16>, <vscale x 4 x i16> } @llvm.vector.deinterleave2.nxv8i16(<vscale x 8 x i16> %v)
+ %f0 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 0
+ %f1 = extractvalue { <vscale x 4 x i16>, <vscale x 4 x i16> } %d, 1
+ %u = add <vscale x 4 x i16> %f0, %f1
+ %r = call <vscale x 8 x i16> @llvm.vector.interleave2.nxv8i16(<vscale x 4 x i16> %u, <vscale x 4 x i16> %u)
+ ret <vscale x 8 x i16> %r
+}
+
;.
; CHECK: [[META0]] = !{float 2.500000e+00}
;.
>From 2bccca2c04e9685f3c664bcc2522d9d847142e28 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Thu, 6 Aug 2026 14:58:04 +0000
Subject: [PATCH 12/13] Add class with helper functions to the fold
---
.../Transforms/Vectorize/VectorCombine.cpp | 295 ++++++++++--------
1 file changed, 161 insertions(+), 134 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 0c0da4318e09f..d7396783b97f5 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -5883,6 +5883,164 @@ bool VectorCombine::foldInsExtVectorToShuffle(Instruction &I) {
return true;
}
+namespace {
+
+class InterleavedElementwiseChain {
+ struct ElementwiseStep {
+ SmallVector<Instruction *, 8> Insts;
+ unsigned ChainOperand;
+
+ ElementwiseStep(ArrayRef<Instruction *> Insts, unsigned ChainOperand)
+ : Insts(Insts.begin(), Insts.end()), ChainOperand(ChainOperand) {}
+ };
+
+ IRBuilderBase &Builder;
+ Value *Root;
+ SmallVector<ElementwiseStep, 4> Steps;
+
+ static unsigned getNumDataOperands(Instruction *Inst) {
+ if (auto *II = dyn_cast<IntrinsicInst>(Inst))
+ return II->arg_size();
+ return Inst->getNumOperands();
+ }
+
+ static Value *getSplatOrScalar(Value *V) {
+ return isa<VectorType>(V->getType()) ? getSplatValue(V) : V;
+ }
+
+ static bool isSupportedElementwise(Instruction *Inst) {
+ auto *ResultTy = dyn_cast<VectorType>(Inst->getType());
+ if (!ResultTy)
+ return false;
+
+ if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
+ if (II->hasOperandBundles() ||
+ !isTriviallyVectorizable(II->getIntrinsicID()))
+ return false;
+ assert(!getInterleaveIntrinsicFactor(II->getIntrinsicID()) &&
+ "vector.interleave must not be treated as a trivially "
+ "vectorizable operation.");
+ } else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
+ SelectInst, FreezeInst>(Inst)) {
+ return false;
+ }
+
+ // Reject operations that change the element-count.
+ for (unsigned Op = 0, E = getNumDataOperands(Inst); Op != E; ++Op) {
+ auto *OperandTy = dyn_cast<VectorType>(Inst->getOperand(Op)->getType());
+ if (OperandTy &&
+ OperandTy->getElementCount() != ResultTy->getElementCount())
+ return false;
+ }
+
+ return true;
+ }
+
+ Value *createWideInstruction(Instruction *NarrowInst,
+ ArrayRef<Value *> NewOperands,
+ VectorType *WideResultTy) {
+ if (isa<BinaryOperator, UnaryOperator>(NarrowInst))
+ return Builder.CreateNAryOp(NarrowInst->getOpcode(), NewOperands);
+ if (auto *Cast = dyn_cast<CastInst>(NarrowInst))
+ return Builder.CreateCast(Cast->getOpcode(), NewOperands[0],
+ WideResultTy);
+ if (auto *Cmp = dyn_cast<CmpInst>(NarrowInst))
+ return Builder.CreateCmp(Cmp->getPredicate(), NewOperands[0],
+ NewOperands[1]);
+ if (isa<SelectInst>(NarrowInst))
+ return Builder.CreateSelect(NewOperands[0], NewOperands[1],
+ NewOperands[2]);
+ if (isa<FreezeInst>(NarrowInst))
+ return Builder.CreateFreeze(NewOperands[0]);
+ if (auto *II = dyn_cast<IntrinsicInst>(NarrowInst))
+ return Builder.CreateIntrinsic(WideResultTy, II->getIntrinsicID(),
+ NewOperands);
+ llvm_unreachable("Unsupported instruction");
+ }
+
+public:
+ InterleavedElementwiseChain(IRBuilderBase &Builder, Value *Root)
+ : Builder(Builder), Root(Root) {}
+
+ bool visitInstLevel(ArrayRef<Instruction *> Insts,
+ ArrayRef<unsigned> OperandNumbers) {
+ if (Insts.empty() || Insts.size() != OperandNumbers.size())
+ return false;
+
+ Instruction *FirstInst = Insts.front();
+ unsigned ChainOperand = OperandNumbers.front();
+
+ if (!isSupportedElementwise(FirstInst))
+ return false;
+
+ for (unsigned Index = 1; Index != Insts.size(); ++Index)
+ if (OperandNumbers[Index] != ChainOperand ||
+ !FirstInst->isSameOperationAs(Insts[Index]))
+ return false;
+
+ // Non-chain operands must be either the same scalar or splats of that
+ // scalar. This intentionally rejects differing poison/undef or non-splat
+ // vector operands between chains.
+ for (unsigned Op = 0, E = getNumDataOperands(FirstInst); Op != E; ++Op) {
+ if (Op == ChainOperand)
+ continue;
+
+ Value *CommonValue = getSplatOrScalar(FirstInst->getOperand(Op));
+ if (!CommonValue || any_of(drop_begin(Insts), [&](Instruction *Inst) {
+ return getSplatOrScalar(Inst->getOperand(Op)) != CommonValue;
+ }))
+ return false;
+ }
+
+ Steps.emplace_back(Insts, ChainOperand);
+ return true;
+ }
+
+ Value *widenInstructions() {
+ Value *WideValue = Root;
+ ElementCount WideEC =
+ cast<VectorType>(WideValue->getType())->getElementCount();
+
+ for (const ElementwiseStep &Step : Steps) {
+ Instruction *NarrowInst = Step.Insts.front();
+
+ Builder.SetInsertPoint(NarrowInst);
+ Builder.SetCurrentDebugLocation(NarrowInst->getDebugLoc());
+
+ unsigned NumOperands = getNumDataOperands(NarrowInst);
+ SmallVector<Value *, 4> NewOperands;
+ NewOperands.reserve(NumOperands);
+
+ for (unsigned Op = 0; Op != NumOperands; ++Op) {
+ Value *Operand = NarrowInst->getOperand(Op);
+
+ if (Op == Step.ChainOperand)
+ Operand = WideValue;
+ else if (isa<VectorType>(Operand->getType()))
+ Operand = Builder.CreateVectorSplat(WideEC, getSplatValue(Operand));
+ NewOperands.push_back(Operand);
+ }
+
+ auto *WideResultTy =
+ VectorType::get(NarrowInst->getType()->getScalarType(), WideEC);
+ Value *NewValue =
+ createWideInstruction(NarrowInst, NewOperands, WideResultTy);
+
+ SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
+ propagateIRFlags(NewValue, NarrowInsts);
+
+ if (auto *NewInst = dyn_cast<Instruction>(NewValue))
+ propagateMetadata(NewInst, NarrowInsts);
+
+ WideValue = NewValue;
+ }
+
+ return WideValue;
+ }
+};
+
+} // namespace
+
/// Fold away a matched pair of vector.deinterleave/interleave intrinsics
/// with a chain of elementwise operations on each between the
/// deinterleave and interleave.
@@ -5933,53 +6091,10 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
CurrentInsts[Index] = Extract;
}
- // Stores a chain steps operations with the preceding operand.
- struct ElementwiseStep {
- SmallVector<Instruction *, 8> Insts;
- unsigned ChainOperand;
- };
-
- SmallVector<ElementwiseStep, 4> Steps;
+ InterleavedElementwiseChain Chain(Builder, Deinterleave->getArgOperand(0));
IntrinsicInst *Interleave = nullptr;
unsigned NumVisited = 0;
- auto getNumDataOperands = [](Instruction *Inst) -> unsigned {
- if (auto *II = dyn_cast<IntrinsicInst>(Inst))
- return II->arg_size();
- return Inst->getNumOperands();
- };
-
- auto isSupportedElementwise = [&](Instruction *Inst) {
- auto *ResultTy = dyn_cast<VectorType>(Inst->getType());
- if (!ResultTy)
- return false;
-
- if (auto *II = dyn_cast<IntrinsicInst>(Inst)) {
- if (II->hasOperandBundles() ||
- !isTriviallyVectorizable(II->getIntrinsicID()))
- return false;
- assert(!getInterleaveIntrinsicFactor(II->getIntrinsicID()) &&
- "vector.interleave must not be treated as a trivially "
- "vectorizable operation.");
- } else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
- SelectInst, FreezeInst>(Inst)) {
- return false;
- }
-
- // Reject operations that change the element-count.
- for (unsigned Op = 0, E = getNumDataOperands(Inst); Op != E; ++Op) {
- auto *OperandTy = dyn_cast<VectorType>(Inst->getOperand(Op)->getType());
- if (any_of(Inst->operands(), [&](Value *Operand) {
- auto *OperandTy = dyn_cast<VectorType>(Operand->getType());
- return OperandTy &&
- OperandTy->getElementCount() != ResultTy->getElementCount();
- }))
- return false;
- }
-
- return true;
- };
-
// Traverse the Factor use chains with a breadth-first search.
// At each level, expect every chain to perform the same operation with the
// preceding chain value at the same operand position, until they all reach
@@ -6012,103 +6127,15 @@ bool VectorCombine::foldDeinterleaveInterleavePair(Instruction &I) {
break;
}
- Instruction *FirstInst = CurrentInsts.front();
- unsigned ChainOperand = OperandNumbers.front();
-
- if (!isSupportedElementwise(FirstInst))
+ if (!Chain.visitInstLevel(CurrentInsts, OperandNumbers))
return false;
-
- for (unsigned Index = 1; Index != Factor; ++Index) {
- Instruction *Inst = CurrentInsts[Index];
- if (OperandNumbers[Index] != ChainOperand ||
- !FirstInst->isSameOperationAs(Inst))
- return false;
- }
-
- // Non-chain operands must be either the same scalar or splats of that
- // scalr. This intentionally rejects differing poison/undef or non-splat
- // vector operands between chains.
- auto getSplatOrScalar = [](Value *V) -> Value * {
- return isa<VectorType>(V->getType()) ? getSplatValue(V) : V;
- };
-
- for (unsigned Op = 0, E = getNumDataOperands(FirstInst); Op != E; ++Op) {
- if (Op == ChainOperand)
- continue;
-
- auto SplatOrScalars = map_range(CurrentInsts, [&](Instruction *Inst) {
- return getSplatOrScalar(Inst->getOperand(Op));
- });
-
- if (!*SplatOrScalars.begin() || !all_equal(SplatOrScalars))
- return false;
- }
-
- Steps.push_back(ElementwiseStep{CurrentInsts, ChainOperand});
}
if (!Interleave)
return false;
// Rebuild the matched elementwise chain at the original vector width.
- Value *WideValue = Deinterleave->getArgOperand(0);
-
- ElementCount WideEC =
- cast<VectorType>(WideValue->getType())->getElementCount();
-
- for (const ElementwiseStep &Step : Steps) {
- Instruction *NarrowInst = Step.Insts.front();
-
- Builder.SetInsertPoint(NarrowInst);
- Builder.SetCurrentDebugLocation(NarrowInst->getDebugLoc());
-
- unsigned NumOperands = getNumDataOperands(NarrowInst);
- SmallVector<Value *, 4> NewOperands;
- NewOperands.reserve(NumOperands);
-
- for (unsigned Op = 0; Op != NumOperands; ++Op) {
- Value *Operand = NarrowInst->getOperand(Op);
-
- if (Op == Step.ChainOperand)
- Operand = WideValue;
- else if (isa<VectorType>(Operand->getType()))
- Operand = Builder.CreateVectorSplat(WideEC, getSplatValue(Operand));
- NewOperands.push_back(Operand);
- }
-
- auto *WideResultTy =
- VectorType::get(NarrowInst->getType()->getScalarType(), WideEC);
-
- Value *NewValue;
- if (isa<BinaryOperator, UnaryOperator>(NarrowInst)) {
- NewValue = Builder.CreateNAryOp(NarrowInst->getOpcode(), NewOperands);
- } else if (auto *Cast = dyn_cast<CastInst>(NarrowInst)) {
- NewValue =
- Builder.CreateCast(Cast->getOpcode(), NewOperands[0], WideResultTy);
- } else if (auto *Cmp = dyn_cast<CmpInst>(NarrowInst)) {
- NewValue = Builder.CreateCmp(Cmp->getPredicate(), NewOperands[0],
- NewOperands[1]);
- } else if (isa<SelectInst>(NarrowInst)) {
- NewValue =
- Builder.CreateSelect(NewOperands[0], NewOperands[1], NewOperands[2]);
- } else if (isa<FreezeInst>(NarrowInst)) {
- NewValue = Builder.CreateFreeze(NewOperands[0]);
- } else if (auto *II = dyn_cast<IntrinsicInst>(NarrowInst)) {
- NewValue = Builder.CreateIntrinsic(WideResultTy, II->getIntrinsicID(),
- NewOperands);
- } else {
- llvm_unreachable("Unsupported instruction");
- }
-
- SmallVector<Value *, 8> NarrowInsts(Step.Insts.begin(), Step.Insts.end());
- propagateIRFlags(NewValue, NarrowInsts);
-
- if (auto *NewInst = dyn_cast<Instruction>(NewValue))
- propagateMetadata(NewInst, NarrowInsts);
-
- WideValue = NewValue;
- }
-
+ Value *WideValue = Chain.widenInstructions();
assert(WideValue->getType() == Interleave->getType());
replaceValue(*Interleave, *WideValue);
return true;
>From b5df4ecd8f4f777c7993662786403f8b17418001 Mon Sep 17 00:00:00 2001
From: Jacob Crawley <jacob.crawley at arm.com>
Date: Fri, 7 Aug 2026 15:54:38 +0000
Subject: [PATCH 13/13] rm assert and add comments
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index d7396783b97f5..342b0b894777c 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -5917,9 +5917,6 @@ class InterleavedElementwiseChain {
if (II->hasOperandBundles() ||
!isTriviallyVectorizable(II->getIntrinsicID()))
return false;
- assert(!getInterleaveIntrinsicFactor(II->getIntrinsicID()) &&
- "vector.interleave must not be treated as a trivially "
- "vectorizable operation.");
} else if (!isa<BinaryOperator, UnaryOperator, CastInst, CmpInst,
SelectInst, FreezeInst>(Inst)) {
return false;
@@ -5962,6 +5959,8 @@ class InterleavedElementwiseChain {
InterleavedElementwiseChain(IRBuilderBase &Builder, Value *Root)
: Builder(Builder), Root(Root) {}
+ /// Visit a list of instructions and check if they can be rewritten as a
+ /// single wider instruction. If so, the list is appened to \p Steps.
bool visitInstLevel(ArrayRef<Instruction *> Insts,
ArrayRef<unsigned> OperandNumbers) {
if (Insts.empty() || Insts.size() != OperandNumbers.size())
@@ -5996,6 +5995,8 @@ class InterleavedElementwiseChain {
return true;
}
+ /// Go through all the collected \p Steps and rewrite each of them as a wide
+ /// instruction.
Value *widenInstructions() {
Value *WideValue = Root;
ElementCount WideEC =
More information about the llvm-commits
mailing list