[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