[llvm] [RISCV] Avoid early split for fixed-length VECTOR_DEINTERLEAVE (PR #226453)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 30 18:30:11 PDT 2026


https://github.com/compilersutra updated https://github.com/llvm/llvm-project/pull/226453

>From e104d66ecc0215db16a1dcc8ac834f7602f43d5a Mon Sep 17 00:00:00 2001
From: compilersutra <osc at compilersutra.com>
Date: Fri, 25 Sep 2026 17:00:01 +0530
Subject: [PATCH 1/5] [RISCV] Avoid early split for fixed-length
 VECTOR_DEINTERLEAVE

When the LMUL=8 limit forces a stack+vlseg path, keep the operands
intact and issue legal-sized vlseg chunks instead of splitting first.

Fixes #222938
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 139 ++++++++++++++
 .../RISCV/rvv/vector-deinterleave-fixed.ll    | 176 +++++++-----------
 2 files changed, 206 insertions(+), 109 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index bd994fc36a7043..a56f7e625bd241 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14799,6 +14799,145 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
   // If concatenating would exceed LMUL=8, we need to split.
   if ((ContainerVecVT.getSizeInBits().getKnownMinValue() * Factor) >
       (8 * RISCV::RVVBitsPerBlock)) {
+    // Fixed-length store+vlseg path: keep operands intact, spill them
+    // contiguously, then issue legal-sized vlseg at consecutive offsets.
+    // Avoids extract/concat tax from splitting operands first (#222938).
+    // Scalable keeps the shared recursive split below.
+    if (IsFixedVector) {
+      const unsigned NumElts = VecVT.getVectorNumElements();
+      MVT ElemVT = VecVT.getVectorElementType();
+
+      // Largest per-field element count with EMUL * NFIELDS <= 8.
+      // Only consider power-of-two lengths: useRVVForFixedLengthVectorVT
+      // rejects non-pow2 types, and MVT::getVectorVT may be invalid for
+      // arbitrary lengths.
+      unsigned ChunkElts = 0;
+      for (unsigned K = 1u << Log2_32(NumElts); K >= 1; K /= 2) {
+        MVT ChunkVT = MVT::getVectorVT(ElemVT, K);
+        if (!ChunkVT.isValid() || !ChunkVT.isFixedLengthVector() ||
+            !useRVVForFixedLengthVectorVT(ChunkVT))
+          continue;
+        MVT ChunkContainer = getContainerForFixedLengthVector(ChunkVT);
+        if (ChunkContainer.getSizeInBits().getKnownMinValue() * Factor <=
+            (8 * RISCV::RVVBitsPerBlock)) {
+          ChunkElts = K;
+          break;
+        }
+      }
+      assert(ChunkElts != 0 && "expected a legal fixed-length vlseg chunk");
+
+      MVT XLenVT = Subtarget.getXLenVT();
+      auto &MF = DAG.getMachineFunction();
+      SDValue Chain = DAG.getEntryNode();
+      Align Alignment = DAG.getReducedAlign(VecVT, /*UseABI=*/false);
+
+      ElementCount ActualConcatEC = VecVT.getVectorElementCount() * Factor;
+      EVT ConcatEVT =
+          EVT::getVectorVT(*DAG.getContext(), ElemVT, ActualConcatEC);
+      SDValue StackPtr =
+          DAG.CreateStackTemporary(ConcatEVT.getStoreSize(), Alignment);
+      auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+      MachinePointerInfo PtrInfo =
+          MachinePointerInfo::getFixedStack(MF, FrameIndex);
+
+      TypeSize VecSize = VecVT.getStoreSize();
+      SDValue BasePtr = StackPtr;
+      MachinePointerInfo PI = PtrInfo;
+      SmallVector<SDValue, 8> Tokens(Factor);
+      for (auto [Idx, FieldOp] : enumerate(Op->op_values())) {
+        if (Idx) {
+          BasePtr = DAG.getObjectPtrOffset(DL, BasePtr, VecSize);
+          PI = PI.getWithOffset(VecSize);
+        }
+        Tokens[Idx] = DAG.getStore(Chain, DL, FieldOp, BasePtr, PI, Alignment);
+      }
+      Chain = DAG.getTokenFactor(DL, Tokens);
+
+      const unsigned ElemBytes = VecVT.getScalarStoreSize();
+
+      static const Intrinsic::ID VlsegIntrinsicsIds[] = {
+          Intrinsic::riscv_vlseg2_mask, Intrinsic::riscv_vlseg3_mask,
+          Intrinsic::riscv_vlseg4_mask, Intrinsic::riscv_vlseg5_mask,
+          Intrinsic::riscv_vlseg6_mask, Intrinsic::riscv_vlseg7_mask,
+          Intrinsic::riscv_vlseg8_mask};
+
+      SmallVector<SmallVector<SDValue, 4>, 8> Parts(Factor);
+      MVT ChunkVT = MVT::getVectorVT(ElemVT, ChunkElts);
+      MVT ChunkContainer = getContainerForFixedLengthVector(ChunkVT);
+      MVT WideVT =
+          MVT::getVectorVT(ElemVT, ChunkElts * PowerOf2Ceil(Factor));
+      assert(WideVT.isValid() && WideVT.isFixedLengthVector() &&
+             "expected legal wide passthru VT for chunked vlseg");
+      MVT WideContainer = getContainerForFixedLengthVector(WideVT);
+
+      for (unsigned Offset = 0; Offset < NumElts; Offset += ChunkElts) {
+        unsigned ThisChunkElts = std::min(ChunkElts, NumElts - Offset);
+
+        SDValue ChunkPtr = DAG.getObjectPtrOffset(
+            DL, StackPtr,
+            TypeSize::getFixed(static_cast<uint64_t>(Offset) * Factor *
+                               ElemBytes));
+        MachinePointerInfo ChunkPI = PtrInfo.getWithOffset(
+            static_cast<uint64_t>(Offset) * Factor * ElemBytes);
+
+        SDValue Mask, VL;
+        std::tie(Mask, VL) =
+            getDefaultVLOps(ChunkVT, ChunkContainer, DL, DAG, Subtarget);
+        // Partial final chunk: keep the legal (pow2) container, shrink VL.
+        if (ThisChunkElts != ChunkElts)
+          VL = DAG.getConstant(ThisChunkElts, DL, XLenVT);
+
+        SDValue Passthru = DAG.getUNDEF(WideContainer);
+
+        SDValue LoadOps[] = {
+            Chain,
+            DAG.getTargetConstant(VlsegIntrinsicsIds[Factor - 2], DL, XLenVT),
+            Passthru,
+            ChunkPtr,
+            Mask,
+            VL,
+            DAG.getTargetConstant(
+                RISCVVType::TAIL_AGNOSTIC | RISCVVType::MASK_AGNOSTIC, DL,
+                XLenVT),
+            DAG.getTargetConstant(Log2_64(VecVT.getScalarSizeInBits()), DL,
+                                  XLenVT)};
+
+        unsigned Sz = Factor * ChunkContainer.getVectorMinNumElements() *
+                      ChunkContainer.getScalarSizeInBits();
+        EVT VecTupTy = MVT::getRISCVVectorTupleVT(Sz, Factor);
+
+        SDValue Load = DAG.getMemIntrinsicNode(
+            ISD::INTRINSIC_W_CHAIN, DL, DAG.getVTList({VecTupTy, MVT::Other}),
+            LoadOps, ElemVT, ChunkPI, Alignment, MachineMemOperand::MOLoad,
+            LocationSize::beforeOrAfterPointer());
+        Chain = Load.getValue(1);
+
+        for (unsigned i = 0; i != Factor; ++i) {
+          SDValue FieldRes =
+              DAG.getNode(RISCVISD::TUPLE_EXTRACT, DL, ChunkContainer, Load,
+                          DAG.getTargetConstant(i, DL, MVT::i32));
+          SDValue Fixed = convertFromScalableVector(ChunkVT, FieldRes, DAG,
+                                                    Subtarget);
+          if (ThisChunkElts != ChunkElts) {
+            EVT NarrowVT =
+                EVT::getVectorVT(*DAG.getContext(), ElemVT, ThisChunkElts);
+            Fixed = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, NarrowVT, Fixed,
+                                DAG.getVectorIdxConstant(0, DL));
+          }
+          Parts[i].push_back(Fixed);
+        }
+      }
+
+      SmallVector<SDValue, 8> Res(Factor);
+      for (unsigned i = 0; i != Factor; ++i) {
+        if (Parts[i].size() == 1)
+          Res[i] = Parts[i][0];
+        else
+          Res[i] = DAG.getNode(ISD::CONCAT_VECTORS, DL, VecVT, Parts[i]);
+      }
+      return DAG.getMergeValues(Res, DL);
+    }
+
     SmallVector<SDValue, 8> Ops(Factor * 2);
     for (unsigned i = 0; i != Factor; ++i) {
       auto [OpLo, OpHi] = DAG.SplitVectorOperand(Op.getNode(), i);
diff --git a/llvm/test/CodeGen/RISCV/rvv/vector-deinterleave-fixed.ll b/llvm/test/CodeGen/RISCV/rvv/vector-deinterleave-fixed.ll
index bce071e988f0ef..1ea3769b7f621b 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vector-deinterleave-fixed.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vector-deinterleave-fixed.ll
@@ -231,38 +231,38 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; RV32-NEXT:    sw s0, 632(sp) # 4-byte Folded Spill
 ; RV32-NEXT:    addi s0, sp, 640
 ; RV32-NEXT:    andi sp, sp, -128
-; RV32-NEXT:    addi a1, sp, 252
+; RV32-NEXT:    addi a1, sp, 60
 ; RV32-NEXT:    vsetivli zero, 1, e32, mf2, ta, ma
 ; RV32-NEXT:    vse32.v v23, (a1)
-; RV32-NEXT:    addi a1, sp, 248
+; RV32-NEXT:    addi a1, sp, 56
 ; RV32-NEXT:    vse32.v v22, (a1)
-; RV32-NEXT:    addi a1, sp, 244
+; RV32-NEXT:    addi a1, sp, 52
 ; RV32-NEXT:    vse32.v v21, (a1)
-; RV32-NEXT:    addi a1, sp, 240
+; RV32-NEXT:    addi a1, sp, 48
 ; RV32-NEXT:    vse32.v v20, (a1)
-; RV32-NEXT:    addi a1, sp, 236
+; RV32-NEXT:    addi a1, sp, 44
 ; RV32-NEXT:    vse32.v v19, (a1)
-; RV32-NEXT:    addi a1, sp, 232
+; RV32-NEXT:    addi a1, sp, 40
 ; RV32-NEXT:    vse32.v v18, (a1)
-; RV32-NEXT:    addi a1, sp, 228
+; RV32-NEXT:    addi a1, sp, 36
 ; RV32-NEXT:    vse32.v v17, (a1)
-; RV32-NEXT:    addi a1, sp, 224
+; RV32-NEXT:    addi a1, sp, 32
 ; RV32-NEXT:    vse32.v v16, (a1)
-; RV32-NEXT:    addi a1, sp, 220
+; RV32-NEXT:    addi a1, sp, 28
 ; RV32-NEXT:    vse32.v v15, (a1)
-; RV32-NEXT:    addi a1, sp, 216
+; RV32-NEXT:    addi a1, sp, 24
 ; RV32-NEXT:    vse32.v v14, (a1)
-; RV32-NEXT:    addi a1, sp, 212
+; RV32-NEXT:    addi a1, sp, 20
 ; RV32-NEXT:    vse32.v v13, (a1)
-; RV32-NEXT:    addi a1, sp, 208
+; RV32-NEXT:    addi a1, sp, 16
 ; RV32-NEXT:    vse32.v v12, (a1)
-; RV32-NEXT:    addi a1, sp, 204
+; RV32-NEXT:    addi a1, sp, 12
 ; RV32-NEXT:    vse32.v v11, (a1)
-; RV32-NEXT:    addi a1, sp, 200
+; RV32-NEXT:    addi a1, sp, 8
 ; RV32-NEXT:    vse32.v v10, (a1)
-; RV32-NEXT:    addi a1, sp, 196
+; RV32-NEXT:    addi a1, sp, 4
 ; RV32-NEXT:    vse32.v v9, (a1)
-; RV32-NEXT:    addi a1, sp, 192
+; RV32-NEXT:    mv a1, sp
 ; RV32-NEXT:    vse32.v v8, (a1)
 ; RV32-NEXT:    addi a2, a0, 64
 ; RV32-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
@@ -276,33 +276,19 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; RV32-NEXT:    vse32.v v8, (a3)
 ; RV32-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; RV32-NEXT:    vle32.v v8, (a0)
-; RV32-NEXT:    addi a3, sp, 64
-; RV32-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; RV32-NEXT:    vse32.v v8, (a3)
-; RV32-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
-; RV32-NEXT:    vle32.v v12, (a1)
-; RV32-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; RV32-NEXT:    vslidedown.vi v16, v12, 8
-; RV32-NEXT:    mv a1, sp
-; RV32-NEXT:    vslidedown.vi v8, v8, 8
-; RV32-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; RV32-NEXT:    vse32.v v12, (a1)
-; RV32-NEXT:    addi a3, sp, 32
-; RV32-NEXT:    vse32.v v16, (a3)
-; RV32-NEXT:    addi a3, sp, 96
+; RV32-NEXT:    addi a3, sp, 128
 ; RV32-NEXT:    vse32.v v8, (a3)
+; RV32-NEXT:    vle32.v v8, (a1)
+; RV32-NEXT:    addi a1, sp, 64
+; RV32-NEXT:    vse32.v v8, (a1)
 ; RV32-NEXT:    lw a0, 112(a0)
 ; RV32-NEXT:    sw a0, 432(sp)
-; RV32-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; RV32-NEXT:    vle32.v v8, (a2)
-; RV32-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; RV32-NEXT:    vslidedown.vi v12, v8, 8
-; RV32-NEXT:    addi a0, sp, 128
-; RV32-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
+; RV32-NEXT:    addi a0, sp, 192
 ; RV32-NEXT:    vse32.v v8, (a0)
 ; RV32-NEXT:    addi a0, sp, 160
-; RV32-NEXT:    vse32.v v12, (a0)
-; RV32-NEXT:    vlseg3e32.v v20, (a3)
+; RV32-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
+; RV32-NEXT:    vlseg3e32.v v20, (a0)
 ; RV32-NEXT:    vlseg3e32.v v12, (a1)
 ; RV32-NEXT:    vmv4r.v v28, v20
 ; RV32-NEXT:    vmv2r.v v30, v8
@@ -329,37 +315,37 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; RV64-NEXT:    addi a1, a0, 112
 ; RV64-NEXT:    vsetivli zero, 1, e32, mf2, ta, ma
 ; RV64-NEXT:    vle32.v v24, (a1)
-; RV64-NEXT:    addi a1, sp, 252
+; RV64-NEXT:    addi a1, sp, 60
 ; RV64-NEXT:    vse32.v v23, (a1)
-; RV64-NEXT:    addi a1, sp, 248
+; RV64-NEXT:    addi a1, sp, 56
 ; RV64-NEXT:    vse32.v v22, (a1)
-; RV64-NEXT:    addi a1, sp, 244
+; RV64-NEXT:    addi a1, sp, 52
 ; RV64-NEXT:    vse32.v v21, (a1)
-; RV64-NEXT:    addi a1, sp, 240
+; RV64-NEXT:    addi a1, sp, 48
 ; RV64-NEXT:    vse32.v v20, (a1)
-; RV64-NEXT:    addi a1, sp, 236
+; RV64-NEXT:    addi a1, sp, 44
 ; RV64-NEXT:    vse32.v v19, (a1)
-; RV64-NEXT:    addi a1, sp, 232
+; RV64-NEXT:    addi a1, sp, 40
 ; RV64-NEXT:    vse32.v v18, (a1)
-; RV64-NEXT:    addi a1, sp, 228
+; RV64-NEXT:    addi a1, sp, 36
 ; RV64-NEXT:    vse32.v v17, (a1)
-; RV64-NEXT:    addi a1, sp, 224
+; RV64-NEXT:    addi a1, sp, 32
 ; RV64-NEXT:    vse32.v v16, (a1)
-; RV64-NEXT:    addi a1, sp, 220
+; RV64-NEXT:    addi a1, sp, 28
 ; RV64-NEXT:    vse32.v v15, (a1)
-; RV64-NEXT:    addi a1, sp, 216
+; RV64-NEXT:    addi a1, sp, 24
 ; RV64-NEXT:    vse32.v v14, (a1)
-; RV64-NEXT:    addi a1, sp, 212
+; RV64-NEXT:    addi a1, sp, 20
 ; RV64-NEXT:    vse32.v v13, (a1)
-; RV64-NEXT:    addi a1, sp, 208
+; RV64-NEXT:    addi a1, sp, 16
 ; RV64-NEXT:    vse32.v v12, (a1)
-; RV64-NEXT:    addi a1, sp, 204
+; RV64-NEXT:    addi a1, sp, 12
 ; RV64-NEXT:    vse32.v v11, (a1)
-; RV64-NEXT:    addi a1, sp, 200
+; RV64-NEXT:    addi a1, sp, 8
 ; RV64-NEXT:    vse32.v v10, (a1)
-; RV64-NEXT:    addi a1, sp, 196
+; RV64-NEXT:    addi a1, sp, 4
 ; RV64-NEXT:    vse32.v v9, (a1)
-; RV64-NEXT:    addi a1, sp, 192
+; RV64-NEXT:    mv a1, sp
 ; RV64-NEXT:    vse32.v v8, (a1)
 ; RV64-NEXT:    addi a2, a0, 64
 ; RV64-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
@@ -376,30 +362,16 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; RV64-NEXT:    vse32.v v24, (a3)
 ; RV64-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; RV64-NEXT:    vle32.v v8, (a0)
+; RV64-NEXT:    addi a0, sp, 128
+; RV64-NEXT:    vse32.v v8, (a0)
+; RV64-NEXT:    vle32.v v8, (a1)
 ; RV64-NEXT:    addi a0, sp, 64
-; RV64-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
 ; RV64-NEXT:    vse32.v v8, (a0)
-; RV64-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
-; RV64-NEXT:    vle32.v v12, (a1)
-; RV64-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; RV64-NEXT:    vslidedown.vi v16, v12, 8
-; RV64-NEXT:    mv a0, sp
-; RV64-NEXT:    vslidedown.vi v8, v8, 8
-; RV64-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; RV64-NEXT:    vse32.v v12, (a0)
-; RV64-NEXT:    addi a1, sp, 32
-; RV64-NEXT:    vse32.v v16, (a1)
-; RV64-NEXT:    addi a1, sp, 96
-; RV64-NEXT:    vse32.v v8, (a1)
-; RV64-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; RV64-NEXT:    vle32.v v8, (a2)
-; RV64-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; RV64-NEXT:    vslidedown.vi v12, v8, 8
-; RV64-NEXT:    addi a2, sp, 128
+; RV64-NEXT:    addi a1, sp, 192
+; RV64-NEXT:    vse32.v v8, (a1)
+; RV64-NEXT:    addi a1, sp, 160
 ; RV64-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; RV64-NEXT:    vse32.v v8, (a2)
-; RV64-NEXT:    addi a2, sp, 160
-; RV64-NEXT:    vse32.v v12, (a2)
 ; RV64-NEXT:    vlseg3e32.v v20, (a1)
 ; RV64-NEXT:    vlseg3e32.v v12, (a0)
 ; RV64-NEXT:    vmv4r.v v28, v20
@@ -427,37 +399,37 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; ZVZIP-NEXT:    addi a1, a0, 112
 ; ZVZIP-NEXT:    vsetivli zero, 1, e32, mf2, ta, ma
 ; ZVZIP-NEXT:    vle32.v v24, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 252
+; ZVZIP-NEXT:    addi a1, sp, 60
 ; ZVZIP-NEXT:    vse32.v v23, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 248
+; ZVZIP-NEXT:    addi a1, sp, 56
 ; ZVZIP-NEXT:    vse32.v v22, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 244
+; ZVZIP-NEXT:    addi a1, sp, 52
 ; ZVZIP-NEXT:    vse32.v v21, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 240
+; ZVZIP-NEXT:    addi a1, sp, 48
 ; ZVZIP-NEXT:    vse32.v v20, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 236
+; ZVZIP-NEXT:    addi a1, sp, 44
 ; ZVZIP-NEXT:    vse32.v v19, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 232
+; ZVZIP-NEXT:    addi a1, sp, 40
 ; ZVZIP-NEXT:    vse32.v v18, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 228
+; ZVZIP-NEXT:    addi a1, sp, 36
 ; ZVZIP-NEXT:    vse32.v v17, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 224
+; ZVZIP-NEXT:    addi a1, sp, 32
 ; ZVZIP-NEXT:    vse32.v v16, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 220
+; ZVZIP-NEXT:    addi a1, sp, 28
 ; ZVZIP-NEXT:    vse32.v v15, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 216
+; ZVZIP-NEXT:    addi a1, sp, 24
 ; ZVZIP-NEXT:    vse32.v v14, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 212
+; ZVZIP-NEXT:    addi a1, sp, 20
 ; ZVZIP-NEXT:    vse32.v v13, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 208
+; ZVZIP-NEXT:    addi a1, sp, 16
 ; ZVZIP-NEXT:    vse32.v v12, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 204
+; ZVZIP-NEXT:    addi a1, sp, 12
 ; ZVZIP-NEXT:    vse32.v v11, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 200
+; ZVZIP-NEXT:    addi a1, sp, 8
 ; ZVZIP-NEXT:    vse32.v v10, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 196
+; ZVZIP-NEXT:    addi a1, sp, 4
 ; ZVZIP-NEXT:    vse32.v v9, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 192
+; ZVZIP-NEXT:    mv a1, sp
 ; ZVZIP-NEXT:    vse32.v v8, (a1)
 ; ZVZIP-NEXT:    addi a2, a0, 64
 ; ZVZIP-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
@@ -474,30 +446,16 @@ define {<15 x i32>, <15 x i32>, <15 x i32>} @vector_deinterleave3_v15i32_v45i32(
 ; ZVZIP-NEXT:    vse32.v v24, (a3)
 ; ZVZIP-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; ZVZIP-NEXT:    vle32.v v8, (a0)
+; ZVZIP-NEXT:    addi a0, sp, 128
+; ZVZIP-NEXT:    vse32.v v8, (a0)
+; ZVZIP-NEXT:    vle32.v v8, (a1)
 ; ZVZIP-NEXT:    addi a0, sp, 64
-; ZVZIP-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
 ; ZVZIP-NEXT:    vse32.v v8, (a0)
-; ZVZIP-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
-; ZVZIP-NEXT:    vle32.v v12, (a1)
-; ZVZIP-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; ZVZIP-NEXT:    vslidedown.vi v16, v12, 8
-; ZVZIP-NEXT:    mv a0, sp
-; ZVZIP-NEXT:    vslidedown.vi v8, v8, 8
-; ZVZIP-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; ZVZIP-NEXT:    vse32.v v12, (a0)
-; ZVZIP-NEXT:    addi a1, sp, 32
-; ZVZIP-NEXT:    vse32.v v16, (a1)
-; ZVZIP-NEXT:    addi a1, sp, 96
-; ZVZIP-NEXT:    vse32.v v8, (a1)
-; ZVZIP-NEXT:    vsetivli zero, 16, e32, m4, ta, ma
 ; ZVZIP-NEXT:    vle32.v v8, (a2)
-; ZVZIP-NEXT:    vsetivli zero, 8, e32, m4, ta, ma
-; ZVZIP-NEXT:    vslidedown.vi v12, v8, 8
-; ZVZIP-NEXT:    addi a2, sp, 128
+; ZVZIP-NEXT:    addi a1, sp, 192
+; ZVZIP-NEXT:    vse32.v v8, (a1)
+; ZVZIP-NEXT:    addi a1, sp, 160
 ; ZVZIP-NEXT:    vsetivli zero, 8, e32, m2, ta, ma
-; ZVZIP-NEXT:    vse32.v v8, (a2)
-; ZVZIP-NEXT:    addi a2, sp, 160
-; ZVZIP-NEXT:    vse32.v v12, (a2)
 ; ZVZIP-NEXT:    vlseg3e32.v v20, (a1)
 ; ZVZIP-NEXT:    vlseg3e32.v v12, (a0)
 ; ZVZIP-NEXT:    vmv4r.v v28, v20

>From c757a52fb0bf417cfa8d6f2e72d178e624f76ed2 Mon Sep 17 00:00:00 2001
From: compilersutra <osc at compilersutra.com>
Date: Fri, 25 Sep 2026 17:18:42 +0530
Subject: [PATCH 2/5] [RISCV] clang-format fixed-length VECTOR_DEINTERLEAVE
 path

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index a56f7e625bd241..eb50356e48a9bf 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14864,8 +14864,7 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
       SmallVector<SmallVector<SDValue, 4>, 8> Parts(Factor);
       MVT ChunkVT = MVT::getVectorVT(ElemVT, ChunkElts);
       MVT ChunkContainer = getContainerForFixedLengthVector(ChunkVT);
-      MVT WideVT =
-          MVT::getVectorVT(ElemVT, ChunkElts * PowerOf2Ceil(Factor));
+      MVT WideVT = MVT::getVectorVT(ElemVT, ChunkElts * PowerOf2Ceil(Factor));
       assert(WideVT.isValid() && WideVT.isFixedLengthVector() &&
              "expected legal wide passthru VT for chunked vlseg");
       MVT WideContainer = getContainerForFixedLengthVector(WideVT);
@@ -14896,9 +14895,9 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
             ChunkPtr,
             Mask,
             VL,
-            DAG.getTargetConstant(
-                RISCVVType::TAIL_AGNOSTIC | RISCVVType::MASK_AGNOSTIC, DL,
-                XLenVT),
+            DAG.getTargetConstant(RISCVVType::TAIL_AGNOSTIC |
+                                      RISCVVType::MASK_AGNOSTIC,
+                                  DL, XLenVT),
             DAG.getTargetConstant(Log2_64(VecVT.getScalarSizeInBits()), DL,
                                   XLenVT)};
 
@@ -14916,8 +14915,8 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
           SDValue FieldRes =
               DAG.getNode(RISCVISD::TUPLE_EXTRACT, DL, ChunkContainer, Load,
                           DAG.getTargetConstant(i, DL, MVT::i32));
-          SDValue Fixed = convertFromScalableVector(ChunkVT, FieldRes, DAG,
-                                                    Subtarget);
+          SDValue Fixed =
+              convertFromScalableVector(ChunkVT, FieldRes, DAG, Subtarget);
           if (ThisChunkElts != ChunkElts) {
             EVT NarrowVT =
                 EVT::getVectorVT(*DAG.getContext(), ElemVT, ThisChunkElts);

>From 9f64093a835d28ce51314901bbb235c6c36baa91 Mon Sep 17 00:00:00 2001
From: compilersutra <osc at compilersutra.com>
Date: Fri, 25 Sep 2026 20:08:29 +0530
Subject: [PATCH 3/5] [RISCV] Share spill/vlseg helpers for VECTOR_DEINTERLEAVE

Reuse common fixed spill and vlseg reload helpers for the LMUL>8
chunked path and the normal store+vlseg lowering.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 256 +++++++++-----------
 1 file changed, 109 insertions(+), 147 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index eb50356e48a9bf..e48221430cdfdc 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14778,6 +14778,86 @@ static SDValue widenVectorOpsToi8(SDValue N, const SDLoc &DL,
   return TruncVals.front();
 }
 
+namespace {
+/// Spill fixed-length VECTOR_DEINTERLEAVE operands contiguously to a stack
+/// temporary. Operands are consecutive slices of the interleaved input, so
+/// storing them in order reconstructs that layout for a following vlseg.
+static std::tuple<SDValue, SDValue, MachinePointerInfo, Align>
+spillFixedDeinterleaveOperands(SDValue Op, const SDLoc &DL, SelectionDAG &DAG,
+                               MVT VecVT, unsigned Factor) {
+  MVT ElemVT = VecVT.getVectorElementType();
+  auto &MF = DAG.getMachineFunction();
+  SDValue Chain = DAG.getEntryNode();
+  Align Alignment = DAG.getReducedAlign(VecVT, /*UseABI=*/false);
+  ElementCount ActualConcatEC = VecVT.getVectorElementCount() * Factor;
+  EVT ConcatEVT = EVT::getVectorVT(*DAG.getContext(), ElemVT, ActualConcatEC);
+  SDValue StackPtr =
+      DAG.CreateStackTemporary(ConcatEVT.getStoreSize(), Alignment);
+  auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
+  MachinePointerInfo PtrInfo =
+      MachinePointerInfo::getFixedStack(MF, FrameIndex);
+
+  TypeSize VecSize = VecVT.getStoreSize();
+  SDValue BasePtr = StackPtr;
+  MachinePointerInfo PI = PtrInfo;
+  SmallVector<SDValue, 8> Tokens(Factor);
+  for (auto [Idx, FieldOp] : enumerate(Op->op_values())) {
+    if (Idx) {
+      BasePtr = DAG.getObjectPtrOffset(DL, BasePtr, VecSize);
+      PI = PI.getWithOffset(VecSize);
+    }
+    Tokens[Idx] = DAG.getStore(Chain, DL, FieldOp, BasePtr, PI, Alignment);
+  }
+  return {DAG.getTokenFactor(DL, Tokens), StackPtr, PtrInfo, Alignment};
+}
+
+/// Reload Factor deinterleaved fields from Ptr with a masked vlseg.
+/// Optionally convert each field from the scalable container to FixedVT.
+static SDValue vlsegDeinterleaveFields(
+    SelectionDAG &DAG, const RISCVSubtarget &Subtarget, const SDLoc &DL,
+    unsigned Factor, MVT ContainerFieldVT, EVT MemEltVT, SDValue Chain,
+    SDValue Ptr, MachinePointerInfo PtrInfo, Align Alignment, SDValue Mask,
+    SDValue VL, MVT PassthruVT, std::optional<MVT> FixedVT,
+    SmallVectorImpl<SDValue> &Fields) {
+  MVT XLenVT = Subtarget.getXLenVT();
+  static const Intrinsic::ID VlsegIntrinsicsIds[] = {
+      Intrinsic::riscv_vlseg2_mask, Intrinsic::riscv_vlseg3_mask,
+      Intrinsic::riscv_vlseg4_mask, Intrinsic::riscv_vlseg5_mask,
+      Intrinsic::riscv_vlseg6_mask, Intrinsic::riscv_vlseg7_mask,
+      Intrinsic::riscv_vlseg8_mask};
+
+  SDValue LoadOps[] = {
+      Chain,
+      DAG.getTargetConstant(VlsegIntrinsicsIds[Factor - 2], DL, XLenVT),
+      DAG.getUNDEF(PassthruVT),
+      Ptr,
+      Mask,
+      VL,
+      DAG.getTargetConstant(RISCVVType::TAIL_AGNOSTIC |
+                                RISCVVType::MASK_AGNOSTIC,
+                            DL, XLenVT),
+      DAG.getTargetConstant(Log2_64(MemEltVT.getScalarSizeInBits()), DL,
+                            XLenVT)};
+
+  unsigned Sz = Factor * ContainerFieldVT.getVectorMinNumElements() *
+                ContainerFieldVT.getScalarSizeInBits();
+  EVT VecTupTy = MVT::getRISCVVectorTupleVT(Sz, Factor);
+  SDValue Load = DAG.getMemIntrinsicNode(
+      ISD::INTRINSIC_W_CHAIN, DL, DAG.getVTList({VecTupTy, MVT::Other}),
+      LoadOps, MemEltVT, PtrInfo, Alignment, MachineMemOperand::MOLoad,
+      LocationSize::beforeOrAfterPointer());
+
+  for (unsigned i = 0; i != Factor; ++i) {
+    SDValue Field = DAG.getNode(RISCVISD::TUPLE_EXTRACT, DL, ContainerFieldVT,
+                                Load, DAG.getTargetConstant(i, DL, MVT::i32));
+    if (FixedVT)
+      Field = convertFromScalableVector(*FixedVT, Field, DAG, Subtarget);
+    Fields.push_back(Field);
+  }
+  return Load.getValue(1);
+}
+} // namespace
+
 SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
                                                       SelectionDAG &DAG) const {
   SDLoc DL(Op);
@@ -14799,18 +14879,12 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
   // If concatenating would exceed LMUL=8, we need to split.
   if ((ContainerVecVT.getSizeInBits().getKnownMinValue() * Factor) >
       (8 * RISCV::RVVBitsPerBlock)) {
-    // Fixed-length store+vlseg path: keep operands intact, spill them
-    // contiguously, then issue legal-sized vlseg at consecutive offsets.
-    // Avoids extract/concat tax from splitting operands first (#222938).
-    // Scalable keeps the shared recursive split below.
+    // Fixed-length: spill operands once, then reuse the shared vlseg helper on
+    // legal-sized chunks (#222938). Scalable keeps the recursive split below.
     if (IsFixedVector) {
       const unsigned NumElts = VecVT.getVectorNumElements();
       MVT ElemVT = VecVT.getVectorElementType();
 
-      // Largest per-field element count with EMUL * NFIELDS <= 8.
-      // Only consider power-of-two lengths: useRVVForFixedLengthVectorVT
-      // rejects non-pow2 types, and MVT::getVectorVT may be invalid for
-      // arbitrary lengths.
       unsigned ChunkElts = 0;
       for (unsigned K = 1u << Log2_32(NumElts); K >= 1; K /= 2) {
         MVT ChunkVT = MVT::getVectorVT(ElemVT, K);
@@ -14826,97 +14900,39 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
       }
       assert(ChunkElts != 0 && "expected a legal fixed-length vlseg chunk");
 
-      MVT XLenVT = Subtarget.getXLenVT();
-      auto &MF = DAG.getMachineFunction();
-      SDValue Chain = DAG.getEntryNode();
-      Align Alignment = DAG.getReducedAlign(VecVT, /*UseABI=*/false);
-
-      ElementCount ActualConcatEC = VecVT.getVectorElementCount() * Factor;
-      EVT ConcatEVT =
-          EVT::getVectorVT(*DAG.getContext(), ElemVT, ActualConcatEC);
-      SDValue StackPtr =
-          DAG.CreateStackTemporary(ConcatEVT.getStoreSize(), Alignment);
-      auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
-      MachinePointerInfo PtrInfo =
-          MachinePointerInfo::getFixedStack(MF, FrameIndex);
-
-      TypeSize VecSize = VecVT.getStoreSize();
-      SDValue BasePtr = StackPtr;
-      MachinePointerInfo PI = PtrInfo;
-      SmallVector<SDValue, 8> Tokens(Factor);
-      for (auto [Idx, FieldOp] : enumerate(Op->op_values())) {
-        if (Idx) {
-          BasePtr = DAG.getObjectPtrOffset(DL, BasePtr, VecSize);
-          PI = PI.getWithOffset(VecSize);
-        }
-        Tokens[Idx] = DAG.getStore(Chain, DL, FieldOp, BasePtr, PI, Alignment);
-      }
-      Chain = DAG.getTokenFactor(DL, Tokens);
-
-      const unsigned ElemBytes = VecVT.getScalarStoreSize();
-
-      static const Intrinsic::ID VlsegIntrinsicsIds[] = {
-          Intrinsic::riscv_vlseg2_mask, Intrinsic::riscv_vlseg3_mask,
-          Intrinsic::riscv_vlseg4_mask, Intrinsic::riscv_vlseg5_mask,
-          Intrinsic::riscv_vlseg6_mask, Intrinsic::riscv_vlseg7_mask,
-          Intrinsic::riscv_vlseg8_mask};
+      auto [Chain, StackPtr, PtrInfo, Alignment] =
+          spillFixedDeinterleaveOperands(Op, DL, DAG, VecVT, Factor);
 
-      SmallVector<SmallVector<SDValue, 4>, 8> Parts(Factor);
+      MVT XLenVT = Subtarget.getXLenVT();
       MVT ChunkVT = MVT::getVectorVT(ElemVT, ChunkElts);
       MVT ChunkContainer = getContainerForFixedLengthVector(ChunkVT);
       MVT WideVT = MVT::getVectorVT(ElemVT, ChunkElts * PowerOf2Ceil(Factor));
       assert(WideVT.isValid() && WideVT.isFixedLengthVector() &&
              "expected legal wide passthru VT for chunked vlseg");
       MVT WideContainer = getContainerForFixedLengthVector(WideVT);
+      const unsigned ElemBytes = VecVT.getScalarStoreSize();
 
+      SmallVector<SmallVector<SDValue, 4>, 8> Parts(Factor);
       for (unsigned Offset = 0; Offset < NumElts; Offset += ChunkElts) {
         unsigned ThisChunkElts = std::min(ChunkElts, NumElts - Offset);
-
+        uint64_t ByteOff =
+            static_cast<uint64_t>(Offset) * Factor * ElemBytes;
         SDValue ChunkPtr = DAG.getObjectPtrOffset(
-            DL, StackPtr,
-            TypeSize::getFixed(static_cast<uint64_t>(Offset) * Factor *
-                               ElemBytes));
-        MachinePointerInfo ChunkPI = PtrInfo.getWithOffset(
-            static_cast<uint64_t>(Offset) * Factor * ElemBytes);
+            DL, StackPtr, TypeSize::getFixed(ByteOff));
+        MachinePointerInfo ChunkPI = PtrInfo.getWithOffset(ByteOff);
 
         SDValue Mask, VL;
         std::tie(Mask, VL) =
             getDefaultVLOps(ChunkVT, ChunkContainer, DL, DAG, Subtarget);
-        // Partial final chunk: keep the legal (pow2) container, shrink VL.
         if (ThisChunkElts != ChunkElts)
           VL = DAG.getConstant(ThisChunkElts, DL, XLenVT);
 
-        SDValue Passthru = DAG.getUNDEF(WideContainer);
-
-        SDValue LoadOps[] = {
-            Chain,
-            DAG.getTargetConstant(VlsegIntrinsicsIds[Factor - 2], DL, XLenVT),
-            Passthru,
-            ChunkPtr,
-            Mask,
-            VL,
-            DAG.getTargetConstant(RISCVVType::TAIL_AGNOSTIC |
-                                      RISCVVType::MASK_AGNOSTIC,
-                                  DL, XLenVT),
-            DAG.getTargetConstant(Log2_64(VecVT.getScalarSizeInBits()), DL,
-                                  XLenVT)};
-
-        unsigned Sz = Factor * ChunkContainer.getVectorMinNumElements() *
-                      ChunkContainer.getScalarSizeInBits();
-        EVT VecTupTy = MVT::getRISCVVectorTupleVT(Sz, Factor);
-
-        SDValue Load = DAG.getMemIntrinsicNode(
-            ISD::INTRINSIC_W_CHAIN, DL, DAG.getVTList({VecTupTy, MVT::Other}),
-            LoadOps, ElemVT, ChunkPI, Alignment, MachineMemOperand::MOLoad,
-            LocationSize::beforeOrAfterPointer());
-        Chain = Load.getValue(1);
-
+        SmallVector<SDValue, 8> Fields;
+        Chain = vlsegDeinterleaveFields(
+            DAG, Subtarget, DL, Factor, ChunkContainer, ElemVT, Chain, ChunkPtr,
+            ChunkPI, Alignment, Mask, VL, WideContainer, ChunkVT, Fields);
         for (unsigned i = 0; i != Factor; ++i) {
-          SDValue FieldRes =
-              DAG.getNode(RISCVISD::TUPLE_EXTRACT, DL, ChunkContainer, Load,
-                          DAG.getTargetConstant(i, DL, MVT::i32));
-          SDValue Fixed =
-              convertFromScalableVector(ChunkVT, FieldRes, DAG, Subtarget);
+          SDValue Fixed = Fields[i];
           if (ThisChunkElts != ChunkElts) {
             EVT NarrowVT =
                 EVT::getVectorVT(*DAG.getContext(), ElemVT, ThisChunkElts);
@@ -15026,43 +15042,20 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
   // Store with unit-stride store and load it back with segmented load.
   SDValue Mask, VL;
   MVT XLenVT = Subtarget.getXLenVT();
-  auto &MF = DAG.getMachineFunction();
-  SDValue Chain = DAG.getEntryNode();
-  Align Alignment = DAG.getReducedAlign(VecVT, /*UseABI=*/false);
+  SDValue Chain;
+  Align Alignment;
   SDValue StackPtr;
   MachinePointerInfo PtrInfo;
   if (IsFixedVector) {
-    // Calculating the stack size.
-    ElementCount ActualConcatEC = VecVT.getVectorElementCount() * Factor;
-    EVT ConcatEVT = EVT::getVectorVT(
-        *DAG.getContext(), VecVT.getVectorElementType(), ActualConcatEC);
-    StackPtr = DAG.CreateStackTemporary(ConcatEVT.getStoreSize(), Alignment);
-    auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
-    PtrInfo = MachinePointerInfo::getFixedStack(MF, FrameIndex);
-
-    // If this is a fixed vector, instead of using the concat vector, we simply
-    // store each fixed vector operand directly onto the stack, individually.
-    // The reason being that if the fixed vector is (much) smaller than the
-    // container vector, we will be wasting space on stack.
-    TypeSize VecSize = VecVT.getStoreSize();
-    SDValue BasePtr = StackPtr;
-    MachinePointerInfo PI = PtrInfo;
-    SmallVector<SDValue, 8> Tokens(Factor);
-    for (auto [Idx, FieldOp] : enumerate(Op->op_values())) {
-      if (Idx) {
-        // Advance the pointer.
-        BasePtr = DAG.getObjectPtrOffset(DL, BasePtr, VecSize);
-        PI = PI.getWithOffset(VecSize);
-      }
-      Tokens[Idx] = DAG.getStore(Chain, DL, FieldOp, BasePtr, PI, Alignment);
-    }
-    Chain = DAG.getTokenFactor(DL, Tokens);
-
-    // Calculating Mask and VL for later usages.
+    std::tie(Chain, StackPtr, PtrInfo, Alignment) =
+        spillFixedDeinterleaveOperands(Op, DL, DAG, VecVT, Factor);
     std::tie(Mask, VL) =
         getDefaultVLOps(VecVT, ContainerVecVT, DL, DAG, Subtarget);
     ConcatVT = getContainerForFixedLengthVector(ConcatVT);
   } else {
+    auto &MF = DAG.getMachineFunction();
+    Chain = DAG.getEntryNode();
+    Alignment = DAG.getReducedAlign(VecVT, /*UseABI=*/false);
     std::tie(Mask, VL) = getDefaultScalableVLOps(VecVT, DL, DAG, Subtarget);
     StackPtr = DAG.CreateStackTemporary(ConcatVT.getStoreSize(), Alignment);
     auto FrameIndex = cast<FrameIndexSDNode>(StackPtr.getNode())->getIndex();
@@ -15079,43 +15072,12 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
   }
 
   // Load it back with segmented load.
-  SDValue Passthru = DAG.getUNDEF(ConcatVT);
-  static const Intrinsic::ID VlsegIntrinsicsIds[] = {
-      Intrinsic::riscv_vlseg2_mask, Intrinsic::riscv_vlseg3_mask,
-      Intrinsic::riscv_vlseg4_mask, Intrinsic::riscv_vlseg5_mask,
-      Intrinsic::riscv_vlseg6_mask, Intrinsic::riscv_vlseg7_mask,
-      Intrinsic::riscv_vlseg8_mask};
-
-  SDValue LoadOps[] = {
-      Chain,
-      DAG.getTargetConstant(VlsegIntrinsicsIds[Factor - 2], DL, XLenVT),
-      Passthru,
-      StackPtr,
-      Mask,
-      VL,
-      DAG.getTargetConstant(
-          RISCVVType::TAIL_AGNOSTIC | RISCVVType::MASK_AGNOSTIC, DL, XLenVT),
-      DAG.getTargetConstant(Log2_64(VecVT.getScalarSizeInBits()), DL, XLenVT)};
-
-  unsigned Sz = Factor * ContainerVecVT.getVectorMinNumElements() *
-                ContainerVecVT.getScalarSizeInBits();
-  EVT VecTupTy = MVT::getRISCVVectorTupleVT(Sz, Factor);
-
-  SDValue Load = DAG.getMemIntrinsicNode(
-      ISD::INTRINSIC_W_CHAIN, DL, DAG.getVTList({VecTupTy, MVT::Other}),
-      LoadOps, ConcatVT.getVectorElementType(), PtrInfo, Alignment,
-      MachineMemOperand::MOLoad, LocationSize::beforeOrAfterPointer());
-
-  SmallVector<SDValue, 8> Res(Factor);
-
-  for (unsigned i = 0U; i < Factor; ++i) {
-    SDValue FieldRes =
-        DAG.getNode(RISCVISD::TUPLE_EXTRACT, DL, ContainerVecVT, Load,
-                    DAG.getTargetConstant(i, DL, MVT::i32));
-    if (IsFixedVector)
-      FieldRes = convertFromScalableVector(VecVT, FieldRes, DAG, Subtarget);
-    Res[i] = FieldRes;
-  }
+  SmallVector<SDValue, 8> Res;
+  vlsegDeinterleaveFields(
+      DAG, Subtarget, DL, Factor, ContainerVecVT,
+      ConcatVT.getVectorElementType(), Chain, StackPtr, PtrInfo, Alignment,
+      Mask, VL, ConcatVT,
+      IsFixedVector ? std::optional<MVT>(VecVT) : std::nullopt, Res);
 
   return DAG.getMergeValues(Res, DL);
 }

>From fd1619e198c57f77c0b88a61835bb794477d99cd Mon Sep 17 00:00:00 2001
From: compilersutra <osc at compilersutra.com>
Date: Fri, 25 Sep 2026 20:49:07 +0530
Subject: [PATCH 4/5] [RISCV] clang-format shared VECTOR_DEINTERLEAVE helpers

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e48221430cdfdc..e9fe448bec3f0a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14833,9 +14833,8 @@ static SDValue vlsegDeinterleaveFields(
       Ptr,
       Mask,
       VL,
-      DAG.getTargetConstant(RISCVVType::TAIL_AGNOSTIC |
-                                RISCVVType::MASK_AGNOSTIC,
-                            DL, XLenVT),
+      DAG.getTargetConstant(
+          RISCVVType::TAIL_AGNOSTIC | RISCVVType::MASK_AGNOSTIC, DL, XLenVT),
       DAG.getTargetConstant(Log2_64(MemEltVT.getScalarSizeInBits()), DL,
                             XLenVT)};
 
@@ -14915,10 +14914,9 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
       SmallVector<SmallVector<SDValue, 4>, 8> Parts(Factor);
       for (unsigned Offset = 0; Offset < NumElts; Offset += ChunkElts) {
         unsigned ThisChunkElts = std::min(ChunkElts, NumElts - Offset);
-        uint64_t ByteOff =
-            static_cast<uint64_t>(Offset) * Factor * ElemBytes;
-        SDValue ChunkPtr = DAG.getObjectPtrOffset(
-            DL, StackPtr, TypeSize::getFixed(ByteOff));
+        uint64_t ByteOff = static_cast<uint64_t>(Offset) * Factor * ElemBytes;
+        SDValue ChunkPtr =
+            DAG.getObjectPtrOffset(DL, StackPtr, TypeSize::getFixed(ByteOff));
         MachinePointerInfo ChunkPI = PtrInfo.getWithOffset(ByteOff);
 
         SDValue Mask, VL;

>From 137ed2987e4df16c2f85ad8a14eda49c26ac095d Mon Sep 17 00:00:00 2001
From: compilersutra <osc at compilersutra.com>
Date: Thu, 1 Oct 2026 06:33:16 +0530
Subject: [PATCH 5/5] [RISCV] Address review on fixed VECTOR_DEINTERLEAVE

Drop the extra anonymous namespace, the redundant fixed-vector check,
and use structured bindings plus getExtractSubvector.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index e9fe448bec3f0a..6b49ad38734073 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -14778,7 +14778,6 @@ static SDValue widenVectorOpsToi8(SDValue N, const SDLoc &DL,
   return TruncVals.front();
 }
 
-namespace {
 /// Spill fixed-length VECTOR_DEINTERLEAVE operands contiguously to a stack
 /// temporary. Operands are consecutive slices of the interleaved input, so
 /// storing them in order reconstructs that layout for a following vlseg.
@@ -14855,7 +14854,6 @@ static SDValue vlsegDeinterleaveFields(
   }
   return Load.getValue(1);
 }
-} // namespace
 
 SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
                                                       SelectionDAG &DAG) const {
@@ -14887,8 +14885,7 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
       unsigned ChunkElts = 0;
       for (unsigned K = 1u << Log2_32(NumElts); K >= 1; K /= 2) {
         MVT ChunkVT = MVT::getVectorVT(ElemVT, K);
-        if (!ChunkVT.isValid() || !ChunkVT.isFixedLengthVector() ||
-            !useRVVForFixedLengthVectorVT(ChunkVT))
+        if (!ChunkVT.isValid() || !useRVVForFixedLengthVectorVT(ChunkVT))
           continue;
         MVT ChunkContainer = getContainerForFixedLengthVector(ChunkVT);
         if (ChunkContainer.getSizeInBits().getKnownMinValue() * Factor <=
@@ -14919,8 +14916,7 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
             DAG.getObjectPtrOffset(DL, StackPtr, TypeSize::getFixed(ByteOff));
         MachinePointerInfo ChunkPI = PtrInfo.getWithOffset(ByteOff);
 
-        SDValue Mask, VL;
-        std::tie(Mask, VL) =
+        auto [Mask, VL] =
             getDefaultVLOps(ChunkVT, ChunkContainer, DL, DAG, Subtarget);
         if (ThisChunkElts != ChunkElts)
           VL = DAG.getConstant(ThisChunkElts, DL, XLenVT);
@@ -14934,8 +14930,7 @@ SDValue RISCVTargetLowering::lowerVECTOR_DEINTERLEAVE(SDValue Op,
           if (ThisChunkElts != ChunkElts) {
             EVT NarrowVT =
                 EVT::getVectorVT(*DAG.getContext(), ElemVT, ThisChunkElts);
-            Fixed = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, NarrowVT, Fixed,
-                                DAG.getVectorIdxConstant(0, DL));
+            Fixed = DAG.getExtractSubvector(DL, NarrowVT, Fixed, 0);
           }
           Parts[i].push_back(Fixed);
         }



More information about the llvm-commits mailing list