[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