[llvm] [CodeGen] Add scalarization for vector (de)interleave (PR #212547)
Kamlesh Kumar via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 28 09:55:12 PDT 2026
https://github.com/kamleshbhalui created https://github.com/llvm/llvm-project/pull/212547
None
>From 4947aec1dc69a2ac6e22f59f1eab0db1b128901d Mon Sep 17 00:00:00 2001
From: Kamlesh Kumar <kamlesh.kumar at arm.com>
Date: Tue, 28 Jul 2026 16:25:01 +0000
Subject: [PATCH] [CodeGen] Add scalarization for vector (de)interleave
---
llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h | 2 +
.../SelectionDAG/LegalizeVectorTypes.cpp | 17 ++
...-vector-interleave-deinterleave-no-neon.ll | 156 ++++++++++++++++++
3 files changed, 175 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/fixed-vector-interleave-deinterleave-no-neon.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
index d4d56a9563f71..dbf4ca0a821c0 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeTypes.h
@@ -859,6 +859,8 @@ class LLVM_LIBRARY_VISIBILITY DAGTypeLegalizer {
SDValue ScalarizeVecRes_LOAD(LoadSDNode *N);
SDValue ScalarizeVecRes_ATOMIC_LOAD(AtomicSDNode *N);
SDValue ScalarizeVecRes_SCALAR_TO_VECTOR(SDNode *N);
+ SDValue ScalarizeVecRes_VECTOR_INTERLEAVE_DEINTERLEAVE(SDNode *N,
+ unsigned ResNo);
SDValue ScalarizeVecRes_VSELECT(SDNode *N);
SDValue ScalarizeVecRes_SELECT(SDNode *N);
SDValue ScalarizeVecRes_SELECT_CC(SDNode *N);
diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
index c71352fb20817..6527c1ce6a310 100644
--- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorTypes.cpp
@@ -84,6 +84,10 @@ void DAGTypeLegalizer::ScalarizeVectorResult(SDNode *N, unsigned ResNo) {
break;
case ISD::LOAD: R = ScalarizeVecRes_LOAD(cast<LoadSDNode>(N));break;
case ISD::SCALAR_TO_VECTOR: R = ScalarizeVecRes_SCALAR_TO_VECTOR(N); break;
+ case ISD::VECTOR_DEINTERLEAVE:
+ case ISD::VECTOR_INTERLEAVE:
+ R = ScalarizeVecRes_VECTOR_INTERLEAVE_DEINTERLEAVE(N, ResNo);
+ break;
case ISD::SIGN_EXTEND_INREG: R = ScalarizeVecRes_InregOp(N); break;
case ISD::VSELECT: R = ScalarizeVecRes_VSELECT(N); break;
case ISD::SELECT: R = ScalarizeVecRes_SELECT(N); break;
@@ -669,6 +673,19 @@ SDValue DAGTypeLegalizer::ScalarizeVecRes_SCALAR_TO_VECTOR(SDNode *N) {
return InOp;
}
+SDValue DAGTypeLegalizer::ScalarizeVecRes_VECTOR_INTERLEAVE_DEINTERLEAVE(
+ SDNode *N, unsigned ResNo) {
+ assert(N->getNumValues() == N->getNumOperands() &&
+ "Expected one result per operand");
+
+ // Interleaving or deinterleaving one-element vectors leaves each result
+ // equal to the corresponding operand.
+ for (unsigned I = 0; I != N->getNumValues(); ++I)
+ if (I != ResNo)
+ SetScalarizedVector(SDValue(N, I), GetScalarizedVector(N->getOperand(I)));
+ return GetScalarizedVector(N->getOperand(ResNo));
+}
+
SDValue DAGTypeLegalizer::ScalarizeVecRes_VSELECT(SDNode *N) {
SDValue Cond = N->getOperand(0);
EVT OpVT = Cond.getValueType();
diff --git a/llvm/test/CodeGen/AArch64/fixed-vector-interleave-deinterleave-no-neon.ll b/llvm/test/CodeGen/AArch64/fixed-vector-interleave-deinterleave-no-neon.ll
new file mode 100644
index 0000000000000..4688e9cb9a102
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/fixed-vector-interleave-deinterleave-no-neon.ll
@@ -0,0 +1,156 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc -mtriple=aarch64-none-linux-gnu -mattr=-neon < %s | FileCheck %s
+
+define void @interleave2_v2f32(ptr %dst, ptr %a, ptr %b) {
+; CHECK-LABEL: interleave2_v2f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldp s3, s0, [x2]
+; CHECK-NEXT: ldp s2, s1, [x1]
+; CHECK-NEXT: stp s1, s0, [x0, #8]
+; CHECK-NEXT: stp s2, s3, [x0]
+; CHECK-NEXT: ret
+ %vec0 = load <2 x float>, ptr %a, align 4
+ %vec1 = load <2 x float>, ptr %b, align 4
+ %interleaved = call <4 x float> @llvm.vector.interleave2.v2f32(
+ <2 x float> %vec0, <2 x float> %vec1)
+ store <4 x float> %interleaved, ptr %dst, align 4
+ ret void
+}
+
+define void @interleave3_v2f32(ptr %dst, ptr %a, ptr %b, ptr %c) {
+; CHECK-LABEL: interleave3_v2f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldp w11, w8, [x3]
+; CHECK-NEXT: ldp w12, w9, [x2]
+; CHECK-NEXT: ldp w13, w10, [x1]
+; CHECK-NEXT: orr x8, x9, x8, lsl #32
+; CHECK-NEXT: orr x9, x11, x10, lsl #32
+; CHECK-NEXT: orr x10, x13, x12, lsl #32
+; CHECK-NEXT: stp x9, x8, [x0, #8]
+; CHECK-NEXT: str x10, [x0]
+; CHECK-NEXT: ret
+ %vec0 = load <2 x float>, ptr %a, align 4
+ %vec1 = load <2 x float>, ptr %b, align 4
+ %vec2 = load <2 x float>, ptr %c, align 4
+ %interleaved = call <6 x float> @llvm.vector.interleave3.v2f32(
+ <2 x float> %vec0, <2 x float> %vec1, <2 x float> %vec2)
+ store <6 x float> %interleaved, ptr %dst, align 4
+ ret void
+}
+
+define void @interleave4_v2f32(
+; CHECK-LABEL: interleave4_v2f32:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldp s0, s1, [x4]
+; CHECK-NEXT: ldp s2, s3, [x1]
+; CHECK-NEXT: ldp s4, s5, [x3]
+; CHECK-NEXT: ldp s6, s7, [x2]
+; CHECK-NEXT: stp s5, s1, [x0, #24]
+; CHECK-NEXT: stp s3, s7, [x0, #16]
+; CHECK-NEXT: stp s4, s0, [x0, #8]
+; CHECK-NEXT: stp s2, s6, [x0]
+; CHECK-NEXT: ret
+ ptr %dst, ptr %a, ptr %b, ptr %c, ptr %d) {
+ %vec0 = load <2 x float>, ptr %a, align 4
+ %vec1 = load <2 x float>, ptr %b, align 4
+ %vec2 = load <2 x float>, ptr %c, align 4
+ %vec3 = load <2 x float>, ptr %d, align 4
+ %interleaved = call <8 x float> @llvm.vector.interleave4.v2f32(
+ <2 x float> %vec0, <2 x float> %vec1,
+ <2 x float> %vec2, <2 x float> %vec3)
+ store <8 x float> %interleaved, ptr %dst, align 4
+ ret void
+}
+
+define void @deinterleave2_v2i16(ptr %dst0, ptr %dst1, ptr %src) {
+; CHECK-LABEL: deinterleave2_v2i16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldrh w8, [x2, #4]
+; CHECK-NEXT: ldrh w9, [x2]
+; CHECK-NEXT: ldrh w10, [x2, #2]
+; CHECK-NEXT: ldrh w11, [x2, #6]
+; CHECK-NEXT: strh w8, [x0, #2]
+; CHECK-NEXT: strh w9, [x0]
+; CHECK-NEXT: strh w11, [x1, #2]
+; CHECK-NEXT: strh w10, [x1]
+; CHECK-NEXT: ret
+ %vec = load <4 x i16>, ptr %src, align 2
+ %deinterleaved = call {<2 x i16>, <2 x i16>}
+ @llvm.vector.deinterleave2.v4i16(<4 x i16> %vec)
+ %vec0 = extractvalue {<2 x i16>, <2 x i16>} %deinterleaved, 0
+ %vec1 = extractvalue {<2 x i16>, <2 x i16>} %deinterleaved, 1
+ store <2 x i16> %vec0, ptr %dst0, align 2
+ store <2 x i16> %vec1, ptr %dst1, align 2
+ ret void
+}
+
+define void @deinterleave3_v2i16(
+; CHECK-LABEL: deinterleave3_v2i16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldr x8, [x3]
+; CHECK-NEXT: ldr w10, [x3, #8]
+; CHECK-NEXT: lsr x9, x8, #48
+; CHECK-NEXT: strh w8, [x0]
+; CHECK-NEXT: lsr w11, w8, #16
+; CHECK-NEXT: lsr x8, x8, #32
+; CHECK-NEXT: strh w9, [x0, #2]
+; CHECK-NEXT: lsr w9, w10, #16
+; CHECK-NEXT: strh w10, [x1, #2]
+; CHECK-NEXT: strh w11, [x1]
+; CHECK-NEXT: strh w9, [x2, #2]
+; CHECK-NEXT: strh w8, [x2]
+; CHECK-NEXT: ret
+ ptr %dst0, ptr %dst1, ptr %dst2, ptr %src) {
+ %vec = load <6 x i16>, ptr %src, align 2
+ %deinterleaved = call {<2 x i16>, <2 x i16>, <2 x i16>}
+ @llvm.vector.deinterleave3.v6i16(<6 x i16> %vec)
+ %vec0 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 0
+ %vec1 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 1
+ %vec2 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 2
+ store <2 x i16> %vec0, ptr %dst0, align 2
+ store <2 x i16> %vec1, ptr %dst1, align 2
+ store <2 x i16> %vec2, ptr %dst2, align 2
+ ret void
+}
+
+define void @deinterleave4_v2i16(
+; CHECK-LABEL: deinterleave4_v2i16:
+; CHECK: // %bb.0:
+; CHECK-NEXT: ldrh w8, [x4, #6]
+; CHECK-NEXT: ldrh w9, [x4, #14]
+; CHECK-NEXT: ldrh w10, [x4, #8]
+; CHECK-NEXT: ldrh w11, [x4, #4]
+; CHECK-NEXT: ldrh w12, [x4, #12]
+; CHECK-NEXT: ldrh w13, [x4]
+; CHECK-NEXT: ldrh w14, [x4, #2]
+; CHECK-NEXT: ldrh w15, [x4, #10]
+; CHECK-NEXT: strh w10, [x0, #2]
+; CHECK-NEXT: strh w13, [x0]
+; CHECK-NEXT: strh w15, [x1, #2]
+; CHECK-NEXT: strh w14, [x1]
+; CHECK-NEXT: strh w12, [x2, #2]
+; CHECK-NEXT: strh w11, [x2]
+; CHECK-NEXT: strh w9, [x3, #2]
+; CHECK-NEXT: strh w8, [x3]
+; CHECK-NEXT: ret
+ ptr %dst0, ptr %dst1, ptr %dst2, ptr %dst3, ptr %src) {
+ %vec = load <8 x i16>, ptr %src, align 2
+ %deinterleaved = call {<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>}
+ @llvm.vector.deinterleave4.v8i16(<8 x i16> %vec)
+ %vec0 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 0
+ %vec1 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 1
+ %vec2 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 2
+ %vec3 = extractvalue {<2 x i16>, <2 x i16>, <2 x i16>, <2 x i16>}
+ %deinterleaved, 3
+ store <2 x i16> %vec0, ptr %dst0, align 2
+ store <2 x i16> %vec1, ptr %dst1, align 2
+ store <2 x i16> %vec2, ptr %dst2, align 2
+ store <2 x i16> %vec3, ptr %dst3, align 2
+ ret void
+}
More information about the llvm-commits
mailing list