[llvm] [RISCV] Fold vp.reverse of vp.load through binary ops (PR #205529)

Luke Lau via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 2 05:37:39 PDT 2026


https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/205529

>From 37c750196cb447486fde2fde7ea406adf404fe61 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 24 Jun 2026 18:33:56 +0800
Subject: [PATCH 1/8] Precommit tests

---
 .../RISCV/rvv/vp-combine-reverse-load.ll      | 136 ++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
index 9bf38753e5054..3d11c1d008611 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
@@ -161,3 +161,139 @@ define <vscale x 2 x float> @test_different_evl_splice(ptr %ptr, i32 zeroext %ev
   %splice = call <vscale x 2 x float> @llvm.vector.splice.right(<vscale x 2 x float> %rev, <vscale x 2 x float> poison, i32 %evl2)
   ret <vscale x 2 x float> %splice
 }
+
+define <vscale x 2 x float> @binop(ptr %ptr, i32 zeroext %evl) {
+; CHECK-LABEL: binop:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a2, 260096
+; CHECK-NEXT:    fmv.w.x fa5, a2
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vid.v v8
+; CHECK-NEXT:    vle32.v v9, (a0)
+; CHECK-NEXT:    addi a0, a1, -1
+; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vf v9, v9, fa5
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v10, v8, a0
+; CHECK-NEXT:    vrgather.vv v8, v9, v10
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load, splat (float 1.0)
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+define <vscale x 2 x float> @binop_nested(ptr %ptr, i32 zeroext %evl) {
+; CHECK-LABEL: binop_nested:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a2, 263168
+; CHECK-NEXT:    fmv.w.x fa5, a2
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vle32.v v8, (a0)
+; CHECK-NEXT:    lui a0, 260096
+; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfmul.vf v8, v8, fa5
+; CHECK-NEXT:    fmv.w.x fa5, a0
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vid.v v9
+; CHECK-NEXT:    addi a0, a1, -1
+; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vf v10, v8, fa5
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v9, v9, a0
+; CHECK-NEXT:    vrgather.vv v8, v10, v9
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fmul = fmul <vscale x 2 x float> %load, splat (float 3.0)
+  %fadd = fadd <vscale x 2 x float> %fmul, splat (float 1.0)
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+define <vscale x 2 x float> @binop_2loads(ptr %ptr1, ptr %ptr2, i32 zeroext %evl) {
+; CHECK-LABEL: binop_2loads:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
+; CHECK-NEXT:    vle32.v v8, (a0)
+; CHECK-NEXT:    vid.v v9
+; CHECK-NEXT:    vle32.v v10, (a1)
+; CHECK-NEXT:    addi a0, a2, -1
+; CHECK-NEXT:    vsetvli a1, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vv v10, v8, v10
+; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v9, v9, a0
+; CHECK-NEXT:    vrgather.vv v8, v10, v9
+; CHECK-NEXT:    ret
+  %load1 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr1, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %load2 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr2, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load1, %load2
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+define <vscale x 2 x float> @binop_2splats(float %f1, float %f2, i32 zeroext %evl) {
+; CHECK-LABEL: binop_2splats:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli zero, a0, e32, m1, ta, ma
+; CHECK-NEXT:    vid.v v8
+; CHECK-NEXT:    fadd.s fa5, fa0, fa1
+; CHECK-NEXT:    addi a1, a0, -1
+; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfmv.v.f v9, fa5
+; CHECK-NEXT:    vsetvli zero, a0, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v10, v8, a1
+; CHECK-NEXT:    vrgather.vv v8, v9, v10
+; CHECK-NEXT:    ret
+  %splat1.head = insertelement <vscale x 2 x float> poison, float %f1, i32 0
+  %splat1 = shufflevector <vscale x 2 x float> %splat1.head, <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer
+  %splat2.head = insertelement <vscale x 2 x float> poison, float %f2, i32 0
+  %splat2 = shufflevector <vscale x 2 x float> %splat2.head, <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer
+  %fadd = fadd <vscale x 2 x float> %splat1, %splat2
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+; Negative test, can't combine because a leaf isn't a splat.
+
+define <vscale x 2 x float> @binop_nonsplat(ptr %ptr, <vscale x 2 x float> %v, i32 zeroext %evl) {
+; CHECK-LABEL: binop_nonsplat:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vid.v v9
+; CHECK-NEXT:    vle32.v v10, (a0)
+; CHECK-NEXT:    addi a0, a1, -1
+; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vv v10, v10, v8
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v9, v9, a0
+; CHECK-NEXT:    vrgather.vv v8, v10, v9
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load, %v
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+; Negative test, can't combine because binary op has multiple uses.
+define <vscale x 2 x float> @binop_multiuse(ptr %ptr, i32 zeroext %evl) {
+; CHECK-LABEL: binop_multiuse:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    lui a2, 260096
+; CHECK-NEXT:    fmv.w.x fa5, a2
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vid.v v8
+; CHECK-NEXT:    vle32.v v9, (a0)
+; CHECK-NEXT:    addi a2, a1, -1
+; CHECK-NEXT:    vsetvli a3, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vf v9, v9, fa5
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v10, v8, a2
+; CHECK-NEXT:    vrgather.vv v8, v9, v10
+; CHECK-NEXT:    vs1r.v v9, (a0)
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load, splat (float 1.0)
+  store <vscale x 2 x float> %fadd, ptr %ptr
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}

>From 4198451d0fb1b432f5a4ca62c0885455b907a065 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 24 Jun 2026 18:42:21 +0800
Subject: [PATCH 2/8] [RISCV] Fold vp.reverse of vp.load through binary ops

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 110 +++++++++++-------
 .../RISCV/rvv/vp-combine-reverse-load.ll      |  68 +++++------
 2 files changed, 96 insertions(+), 82 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 7a2b9611683c6..8b8134158379d 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19649,61 +19649,85 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
   //
   // splice.right(reverse(vp.load(ADDR, REVMASK, EVL)), poison, EVL)
   // -> vp.strided.load(ADDR, -1, MASK, EVL)
-
-  // Check if its first operand is a vp.load.
+  //
+  // vp.reverse(binop(vp.load(ADDR, REVMASK, EVL), splat), EVL)
+  // -> binop(vp.strided.load(ADDR, -1, MASK, EVL), splat)
   using namespace SDPatternMatch;
   SDValue Op, EVL;
-  if (!sd_match(N,
-                m_ReverseEVL(m_OneUse(m_Value(Op, m_SpecificOpc(ISD::VP_LOAD))),
-                             m_Value(EVL))))
+  if (!sd_match(N, m_ReverseEVL(m_Value(Op), m_Value(EVL))))
     return SDValue();
 
-  auto *VPLoad = cast<VPLoadSDNode>(Op);
-
-  EVT LoadVT = VPLoad->getValueType(0);
-  // We do not have a strided_load version for masks, and the evl of vp.reverse
-  // and vp.load should always be the same.
-  if (!LoadVT.getVectorElementType().isByteSized() ||
-      EVL != VPLoad->getVectorLength())
-    return SDValue();
-
-  SDValue LoadMask = VPLoad->getMask();
-  // If Mask is all ones, then load is unmasked and can be reversed.
-  if (!isOneOrOneSplat(LoadMask)) {
-    // If the mask is not all ones, we can reverse the load if the mask was also
-    // reversed by a vp.reverse with the same EVL.
-    SDValue OrigMask;
-    if (!sd_match(LoadMask, m_ReverseEVL(m_Value(OrigMask), m_Specific(EVL))))
+  // Check that all leaves are splats or vp_loads, and collect the latter.
+  SmallVector<SDValue> Worklist = {Op};
+  SmallVector<VPLoadSDNode *> VPLoads;
+  while (!Worklist.empty()) {
+    SDValue X = Worklist.pop_back_val();
+    if (!X.hasOneUse())
+      return SDValue();
+    if (auto *VPLoad = dyn_cast<VPLoadSDNode>(X))
+      VPLoads.push_back(VPLoad);
+    else if (DAG.isSplatValue(X))
+      continue;
+    else if (DAG.getTargetLoweringInfo().isBinOp(X.getOpcode()))
+      append_range(Worklist, X->op_values());
+    else
       return SDValue();
-    LoadMask = OrigMask;
   }
 
-  // Base = LoadAddr + (NumElem - 1) * ElemWidthByte
-  SDLoc DL(N);
-  MVT XLenVT = Subtarget.getXLenVT();
-  SDValue NumElem = VPLoad->getVectorLength();
-  uint64_t ElemWidthByte = VPLoad->getValueType(0).getScalarSizeInBits() / 8;
+  SmallVector<SDValue> LoadMasks;
+  for (auto *VPLoad : VPLoads) {
+    EVT LoadVT = VPLoad->getValueType(0);
+    // We do not have a strided_load version for masks, and the evl of
+    // vp.reverse and vp.load should always be the same.
+    if (!LoadVT.getVectorElementType().isByteSized() ||
+        EVL != VPLoad->getVectorLength())
+      return SDValue();
 
-  SDValue Temp1 = DAG.getNode(ISD::SUB, DL, XLenVT, NumElem,
-                              DAG.getConstant(1, DL, XLenVT));
-  SDValue Temp2 = DAG.getNode(ISD::MUL, DL, XLenVT, Temp1,
-                              DAG.getConstant(ElemWidthByte, DL, XLenVT));
-  SDValue Base = DAG.getNode(ISD::ADD, DL, XLenVT, VPLoad->getBasePtr(), Temp2);
-  SDValue Stride = DAG.getSignedConstant(-ElemWidthByte, DL, XLenVT);
+    SDValue LoadMask = VPLoad->getMask();
+    // If Mask is all ones, then load is unmasked and can be reversed.
+    if (isOneOrOneSplat(LoadMask))
+      LoadMasks.push_back(LoadMask);
+    else {
+      // If the mask is not all ones, we can reverse the load if the mask was
+      // also reversed by a vp.reverse with the same EVL.
+      SDValue OrigMask;
+      if (!sd_match(LoadMask, m_ReverseEVL(m_Value(OrigMask), m_Specific(EVL))))
+        return SDValue();
+      LoadMasks.push_back(OrigMask);
+    }
+  }
 
-  MachineFunction &MF = DAG.getMachineFunction();
-  MachinePointerInfo PtrInfo(VPLoad->getAddressSpace());
-  MachineMemOperand *MMO = MF.getMachineMemOperand(
-      PtrInfo, VPLoad->getMemOperand()->getFlags(),
-      LocationSize::beforeOrAfterPointer(), VPLoad->getAlign());
+  // Reverse the vp_loads.
+  for (auto [VPLoad, LoadMask] : zip_equal(VPLoads, LoadMasks)) {
+    // Base = LoadAddr + (NumElem - 1) * ElemWidthByte
+    SDLoc DL(N);
+    MVT XLenVT = Subtarget.getXLenVT();
+    SDValue NumElem = VPLoad->getVectorLength();
+    uint64_t ElemWidthByte = VPLoad->getValueType(0).getScalarSizeInBits() / 8;
+
+    SDValue Temp1 = DAG.getNode(ISD::SUB, DL, XLenVT, NumElem,
+                                DAG.getConstant(1, DL, XLenVT));
+    SDValue Temp2 = DAG.getNode(ISD::MUL, DL, XLenVT, Temp1,
+                                DAG.getConstant(ElemWidthByte, DL, XLenVT));
+    SDValue Base =
+        DAG.getNode(ISD::ADD, DL, XLenVT, VPLoad->getBasePtr(), Temp2);
+    SDValue Stride = DAG.getSignedConstant(-ElemWidthByte, DL, XLenVT);
 
-  SDValue Ret = DAG.getStridedLoadVP(
-      LoadVT, DL, VPLoad->getChain(), Base, Stride, LoadMask,
-      VPLoad->getVectorLength(), MMO, VPLoad->isExpandingLoad());
+    MachineFunction &MF = DAG.getMachineFunction();
+    MachinePointerInfo PtrInfo(VPLoad->getAddressSpace());
+    MachineMemOperand *MMO = MF.getMachineMemOperand(
+        PtrInfo, VPLoad->getMemOperand()->getFlags(),
+        LocationSize::beforeOrAfterPointer(), VPLoad->getAlign());
 
-  DAG.ReplaceAllUsesOfValueWith(SDValue(VPLoad, 1), Ret.getValue(1));
+    SDValue Ret = DAG.getStridedLoadVP(
+        VPLoad->getValueType(0), DL, VPLoad->getChain(), Base, Stride, LoadMask,
+        VPLoad->getVectorLength(), MMO, VPLoad->isExpandingLoad());
+    DAG.ReplaceAllUsesWith(VPLoad, Ret.getNode());
+  }
 
-  return Ret;
+  // Remove the top level reverse.
+  (void)sd_match(N, m_ReverseEVL(m_Value(Op), m_Specific(EVL)));
+  return Op;
 }
 
 // Fold (i32 (bitcast (v4i8/v2i16 const_splat))) to a scalar i32 constant
diff --git a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
index 3d11c1d008611..ba2725af8250d 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
@@ -165,17 +165,16 @@ define <vscale x 2 x float> @test_different_evl_splice(ptr %ptr, i32 zeroext %ev
 define <vscale x 2 x float> @binop(ptr %ptr, i32 zeroext %evl) {
 ; CHECK-LABEL: binop:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a2, 260096
-; CHECK-NEXT:    fmv.w.x fa5, a2
-; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
-; CHECK-NEXT:    vid.v v8
-; CHECK-NEXT:    vle32.v v9, (a0)
-; CHECK-NEXT:    addi a0, a1, -1
-; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfadd.vf v9, v9, fa5
+; CHECK-NEXT:    slli a2, a1, 2
+; CHECK-NEXT:    add a0, a2, a0
+; CHECK-NEXT:    addi a0, a0, -4
+; CHECK-NEXT:    li a2, -4
 ; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
-; CHECK-NEXT:    vrsub.vx v10, v8, a0
-; CHECK-NEXT:    vrgather.vv v8, v9, v10
+; CHECK-NEXT:    vlse32.v v8, (a0), a2
+; CHECK-NEXT:    lui a0, 260096
+; CHECK-NEXT:    fmv.w.x fa5, a0
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vf v8, v8, fa5
 ; CHECK-NEXT:    ret
   %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
   %fadd = fadd <vscale x 2 x float> %load, splat (float 1.0)
@@ -186,22 +185,19 @@ define <vscale x 2 x float> @binop(ptr %ptr, i32 zeroext %evl) {
 define <vscale x 2 x float> @binop_nested(ptr %ptr, i32 zeroext %evl) {
 ; CHECK-LABEL: binop_nested:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    lui a2, 263168
-; CHECK-NEXT:    fmv.w.x fa5, a2
+; CHECK-NEXT:    slli a2, a1, 2
+; CHECK-NEXT:    add a0, a2, a0
+; CHECK-NEXT:    addi a0, a0, -4
+; CHECK-NEXT:    li a2, -4
 ; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
-; CHECK-NEXT:    vle32.v v8, (a0)
+; CHECK-NEXT:    vlse32.v v8, (a0), a2
+; CHECK-NEXT:    lui a0, 263168
+; CHECK-NEXT:    fmv.w.x fa5, a0
 ; CHECK-NEXT:    lui a0, 260096
-; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vsetvli a1, zero, e32, m1, ta, ma
 ; CHECK-NEXT:    vfmul.vf v8, v8, fa5
 ; CHECK-NEXT:    fmv.w.x fa5, a0
-; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
-; CHECK-NEXT:    vid.v v9
-; CHECK-NEXT:    addi a0, a1, -1
-; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfadd.vf v10, v8, fa5
-; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
-; CHECK-NEXT:    vrsub.vx v9, v9, a0
-; CHECK-NEXT:    vrgather.vv v8, v10, v9
+; CHECK-NEXT:    vfadd.vf v8, v8, fa5
 ; CHECK-NEXT:    ret
   %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
   %fmul = fmul <vscale x 2 x float> %load, splat (float 3.0)
@@ -213,16 +209,16 @@ define <vscale x 2 x float> @binop_nested(ptr %ptr, i32 zeroext %evl) {
 define <vscale x 2 x float> @binop_2loads(ptr %ptr1, ptr %ptr2, i32 zeroext %evl) {
 ; CHECK-LABEL: binop_2loads:
 ; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a3, a2, 2
+; CHECK-NEXT:    addi a3, a3, -4
+; CHECK-NEXT:    li a4, -4
+; CHECK-NEXT:    add a1, a1, a3
 ; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
-; CHECK-NEXT:    vle32.v v8, (a0)
-; CHECK-NEXT:    vid.v v9
-; CHECK-NEXT:    vle32.v v10, (a1)
-; CHECK-NEXT:    addi a0, a2, -1
-; CHECK-NEXT:    vsetvli a1, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfadd.vv v10, v8, v10
-; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
-; CHECK-NEXT:    vrsub.vx v9, v9, a0
-; CHECK-NEXT:    vrgather.vv v8, v10, v9
+; CHECK-NEXT:    vlse32.v v8, (a1), a4
+; CHECK-NEXT:    add a0, a0, a3
+; CHECK-NEXT:    vlse32.v v9, (a0), a4
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vv v8, v9, v8
 ; CHECK-NEXT:    ret
   %load1 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr1, <vscale x 2 x i1> splat (i1 true), i32 %evl)
   %load2 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr2, <vscale x 2 x i1> splat (i1 true), i32 %evl)
@@ -234,15 +230,9 @@ define <vscale x 2 x float> @binop_2loads(ptr %ptr1, ptr %ptr2, i32 zeroext %evl
 define <vscale x 2 x float> @binop_2splats(float %f1, float %f2, i32 zeroext %evl) {
 ; CHECK-LABEL: binop_2splats:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    vsetvli zero, a0, e32, m1, ta, ma
-; CHECK-NEXT:    vid.v v8
 ; CHECK-NEXT:    fadd.s fa5, fa0, fa1
-; CHECK-NEXT:    addi a1, a0, -1
-; CHECK-NEXT:    vsetvli a2, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfmv.v.f v9, fa5
-; CHECK-NEXT:    vsetvli zero, a0, e32, m1, ta, ma
-; CHECK-NEXT:    vrsub.vx v10, v8, a1
-; CHECK-NEXT:    vrgather.vv v8, v9, v10
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfmv.v.f v8, fa5
 ; CHECK-NEXT:    ret
   %splat1.head = insertelement <vscale x 2 x float> poison, float %f1, i32 0
   %splat1 = shufflevector <vscale x 2 x float> %splat1.head, <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer

>From c49b85a316f3ad8e35df6d5260b8c16998ff909a Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Wed, 24 Jun 2026 23:13:25 +0800
Subject: [PATCH 3/8] Check SDNode has one use, not SDValue

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 8b8134158379d..855ff27fd9423 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19662,7 +19662,7 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
   SmallVector<VPLoadSDNode *> VPLoads;
   while (!Worklist.empty()) {
     SDValue X = Worklist.pop_back_val();
-    if (!X.hasOneUse())
+    if (!X->hasOneUse())
       return SDValue();
     if (auto *VPLoad = dyn_cast<VPLoadSDNode>(X))
       VPLoads.push_back(VPLoad);

>From 6ad90b26797491814cf5b246eecc33bf6f800fc4 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Thu, 25 Jun 2026 15:52:03 +0800
Subject: [PATCH 4/8] Handle multiple uses but single user, ignoring chain

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 18 +++++++--
 .../RISCV/rvv/vp-combine-reverse-load.ll      | 40 +++++++++++++++++++
 2 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 855ff27fd9423..7a1429db61e02 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19641,6 +19641,15 @@ static auto m_ReverseEVL = [](auto X, auto EVL) {
                  m_Node(ISD::EXPERIMENTAL_VP_REVERSE, X, m_Value(), EVL));
 };
 
+/// Returns true if there is one node that uses the SDValue \p X.
+static bool hasOneUser(SDValue X) {
+  auto Uses = make_filter_range(X->uses(), [&X](SDUse &U) {
+    return U.get() == X;
+  });
+  auto Users = map_range(Uses, [](SDUse &U) { return U.getUser(); });
+  return all_equal(Users);
+}
+
 static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
                                         const RISCVSubtarget &Subtarget) {
   // Fold:
@@ -19662,13 +19671,14 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
   SmallVector<VPLoadSDNode *> VPLoads;
   while (!Worklist.empty()) {
     SDValue X = Worklist.pop_back_val();
-    if (!X->hasOneUse())
+    if (!hasOneUser(X))
       return SDValue();
     if (auto *VPLoad = dyn_cast<VPLoadSDNode>(X))
       VPLoads.push_back(VPLoad);
     else if (DAG.isSplatValue(X))
       continue;
-    else if (DAG.getTargetLoweringInfo().isBinOp(X.getOpcode()))
+    else if (DAG.getTargetLoweringInfo().isBinOp(X.getOpcode()) &&
+             X->getNumValues() == 1)
       append_range(Worklist, X->op_values());
     else
       return SDValue();
@@ -19685,9 +19695,9 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
 
     SDValue LoadMask = VPLoad->getMask();
     // If Mask is all ones, then load is unmasked and can be reversed.
-    if (isOneOrOneSplat(LoadMask))
+    if (isOneOrOneSplat(LoadMask)) {
       LoadMasks.push_back(LoadMask);
-    else {
+    } else {
       // If the mask is not all ones, we can reverse the load if the mask was
       // also reversed by a vp.reverse with the same EVL.
       SDValue OrigMask;
diff --git a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
index ba2725af8250d..3b26a05f2adc3 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
@@ -182,6 +182,46 @@ define <vscale x 2 x float> @binop(ptr %ptr, i32 zeroext %evl) {
   ret <vscale x 2 x float> %rev
 }
 
+define <vscale x 2 x float> @binop_2uses_1user(ptr %ptr, i32 zeroext %evl) {
+; CHECK-LABEL: binop_2uses_1user:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a2, a1, 2
+; CHECK-NEXT:    add a0, a2, a0
+; CHECK-NEXT:    addi a0, a0, -4
+; CHECK-NEXT:    li a2, -4
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vlse32.v v8, (a0), a2
+; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vv v8, v8, v8
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load, %load
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  ret <vscale x 2 x float> %rev
+}
+
+define <vscale x 2 x float> @binop_chain(ptr %ptr, i32 zeroext %evl) {
+; CHECK-LABEL: binop_chain:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a2, a1, 2
+; CHECK-NEXT:    add a2, a2, a0
+; CHECK-NEXT:    addi a2, a2, -4
+; CHECK-NEXT:    li a3, -4
+; CHECK-NEXT:    vsetvli zero, a1, e32, m1, ta, ma
+; CHECK-NEXT:    vlse32.v v8, (a2), a3
+; CHECK-NEXT:    lui a1, 260096
+; CHECK-NEXT:    fmv.w.x fa5, a1
+; CHECK-NEXT:    vsetvli a1, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vf v8, v8, fa5
+; CHECK-NEXT:    vs1r.v v8, (a0)
+; CHECK-NEXT:    ret
+  %load = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  %fadd = fadd <vscale x 2 x float> %load, splat (float 1.0)
+  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
+  store <vscale x 2 x float> %rev, ptr %ptr
+  ret <vscale x 2 x float> %rev
+}
+
 define <vscale x 2 x float> @binop_nested(ptr %ptr, i32 zeroext %evl) {
 ; CHECK-LABEL: binop_nested:
 ; CHECK:       # %bb.0:

>From 10aa5de379d341fe4634872dfa1f3393982631b7 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Thu, 25 Jun 2026 15:59:38 +0800
Subject: [PATCH 5/8] Pull out stride and temp2 from loop

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 7a1429db61e02..88622b68b76b5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19708,20 +19708,18 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
   }
 
   // Reverse the vp_loads.
+  SDLoc DL(N);
+  MVT XLenVT = Subtarget.getXLenVT();
+  uint64_t ElemWidthByte = N->getValueType(0).getScalarSizeInBits() / 8;
+  SDValue Temp1 =
+      DAG.getNode(ISD::SUB, DL, XLenVT, EVL, DAG.getConstant(1, DL, XLenVT));
+  SDValue Temp2 = DAG.getNode(ISD::MUL, DL, XLenVT, Temp1,
+                              DAG.getConstant(ElemWidthByte, DL, XLenVT));
+  SDValue Stride = DAG.getSignedConstant(-ElemWidthByte, DL, XLenVT);
   for (auto [VPLoad, LoadMask] : zip_equal(VPLoads, LoadMasks)) {
     // Base = LoadAddr + (NumElem - 1) * ElemWidthByte
-    SDLoc DL(N);
-    MVT XLenVT = Subtarget.getXLenVT();
-    SDValue NumElem = VPLoad->getVectorLength();
-    uint64_t ElemWidthByte = VPLoad->getValueType(0).getScalarSizeInBits() / 8;
-
-    SDValue Temp1 = DAG.getNode(ISD::SUB, DL, XLenVT, NumElem,
-                                DAG.getConstant(1, DL, XLenVT));
-    SDValue Temp2 = DAG.getNode(ISD::MUL, DL, XLenVT, Temp1,
-                                DAG.getConstant(ElemWidthByte, DL, XLenVT));
     SDValue Base =
         DAG.getNode(ISD::ADD, DL, XLenVT, VPLoad->getBasePtr(), Temp2);
-    SDValue Stride = DAG.getSignedConstant(-ElemWidthByte, DL, XLenVT);
 
     MachineFunction &MF = DAG.getMachineFunction();
     MachinePointerInfo PtrInfo(VPLoad->getAddressSpace());

>From 931785a18cb3623b7699dc50e147ac7a1d026544 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Thu, 25 Jun 2026 16:00:39 +0800
Subject: [PATCH 6/8] clang-format

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

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 88622b68b76b5..581021a8633ce 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19643,9 +19643,8 @@ static auto m_ReverseEVL = [](auto X, auto EVL) {
 
 /// Returns true if there is one node that uses the SDValue \p X.
 static bool hasOneUser(SDValue X) {
-  auto Uses = make_filter_range(X->uses(), [&X](SDUse &U) {
-    return U.get() == X;
-  });
+  auto Uses =
+      make_filter_range(X->uses(), [&X](SDUse &U) { return U.get() == X; });
   auto Users = map_range(Uses, [](SDUse &U) { return U.getUser(); });
   return all_equal(Users);
 }

>From 040b1ad11fbe252edaeb032fa12575acc5b6060a Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Fri, 26 Jun 2026 01:00:34 +0800
Subject: [PATCH 7/8] Restrict to just a single vp_load

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp   | 87 ++++++++++---------
 .../RISCV/rvv/vp-combine-reverse-load.ll      | 36 +++-----
 2 files changed, 55 insertions(+), 68 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 581021a8633ce..ba10733bf57fa 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19649,6 +19649,9 @@ static bool hasOneUser(SDValue X) {
   return all_equal(Users);
 }
 
+// TODO: A vlse.v is not necessarily faster than a vrgather.vv on all uarchs.
+// Remove once a cost model driven transform is implemented in the loop
+// vectorizer.
 static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
                                         const RISCVSubtarget &Subtarget) {
   // Fold:
@@ -19665,16 +19668,18 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
   if (!sd_match(N, m_ReverseEVL(m_Value(Op), m_Value(EVL))))
     return SDValue();
 
+  VPLoadSDNode *VPLoad = nullptr;
   // Check that all leaves are splats or vp_loads, and collect the latter.
   SmallVector<SDValue> Worklist = {Op};
-  SmallVector<VPLoadSDNode *> VPLoads;
   while (!Worklist.empty()) {
     SDValue X = Worklist.pop_back_val();
     if (!hasOneUser(X))
       return SDValue();
-    if (auto *VPLoad = dyn_cast<VPLoadSDNode>(X))
-      VPLoads.push_back(VPLoad);
-    else if (DAG.isSplatValue(X))
+    if (auto *VPL = dyn_cast<VPLoadSDNode>(X)) {
+      if (VPLoad && VPLoad != VPL)
+        return SDValue();
+      VPLoad = VPL;
+    } else if (DAG.isSplatValue(X))
       continue;
     else if (DAG.getTargetLoweringInfo().isBinOp(X.getOpcode()) &&
              X->getNumValues() == 1)
@@ -19682,58 +19687,54 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
     else
       return SDValue();
   }
+  if (!VPLoad)
+    return SDValue();
 
-  SmallVector<SDValue> LoadMasks;
-  for (auto *VPLoad : VPLoads) {
-    EVT LoadVT = VPLoad->getValueType(0);
-    // We do not have a strided_load version for masks, and the evl of
-    // vp.reverse and vp.load should always be the same.
-    if (!LoadVT.getVectorElementType().isByteSized() ||
-        EVL != VPLoad->getVectorLength())
-      return SDValue();
+  EVT LoadVT = VPLoad->getValueType(0);
+  // We do not have a strided_load version for masks, and the evl of vp.reverse
+  // and vp.load should always be the same.
+  if (!LoadVT.getVectorElementType().isByteSized() ||
+      EVL != VPLoad->getVectorLength())
+    return SDValue();
 
-    SDValue LoadMask = VPLoad->getMask();
-    // If Mask is all ones, then load is unmasked and can be reversed.
-    if (isOneOrOneSplat(LoadMask)) {
-      LoadMasks.push_back(LoadMask);
-    } else {
-      // If the mask is not all ones, we can reverse the load if the mask was
-      // also reversed by a vp.reverse with the same EVL.
-      SDValue OrigMask;
-      if (!sd_match(LoadMask, m_ReverseEVL(m_Value(OrigMask), m_Specific(EVL))))
-        return SDValue();
-      LoadMasks.push_back(OrigMask);
-    }
+  SDValue LoadMask = VPLoad->getMask();
+  // If Mask is all ones, then load is unmasked and can be reversed.
+  if (!isOneOrOneSplat(LoadMask)) {
+    // If the mask is not all ones, we can reverse the load if the mask was also
+    // reversed by a vp.reverse with the same EVL.
+    SDValue OrigMask;
+    if (!sd_match(LoadMask, m_ReverseEVL(m_Value(OrigMask), m_Specific(EVL))))
+      return SDValue();
+    LoadMask = OrigMask;
   }
 
-  // Reverse the vp_loads.
+  // Base = LoadAddr + (NumElem - 1) * ElemWidthByte
   SDLoc DL(N);
   MVT XLenVT = Subtarget.getXLenVT();
-  uint64_t ElemWidthByte = N->getValueType(0).getScalarSizeInBits() / 8;
-  SDValue Temp1 =
-      DAG.getNode(ISD::SUB, DL, XLenVT, EVL, DAG.getConstant(1, DL, XLenVT));
+  SDValue NumElem = VPLoad->getVectorLength();
+  uint64_t ElemWidthByte = VPLoad->getValueType(0).getScalarSizeInBits() / 8;
+
+  SDValue Temp1 = DAG.getNode(ISD::SUB, DL, XLenVT, NumElem,
+                              DAG.getConstant(1, DL, XLenVT));
   SDValue Temp2 = DAG.getNode(ISD::MUL, DL, XLenVT, Temp1,
                               DAG.getConstant(ElemWidthByte, DL, XLenVT));
+  SDValue Base = DAG.getNode(ISD::ADD, DL, XLenVT, VPLoad->getBasePtr(), Temp2);
   SDValue Stride = DAG.getSignedConstant(-ElemWidthByte, DL, XLenVT);
-  for (auto [VPLoad, LoadMask] : zip_equal(VPLoads, LoadMasks)) {
-    // Base = LoadAddr + (NumElem - 1) * ElemWidthByte
-    SDValue Base =
-        DAG.getNode(ISD::ADD, DL, XLenVT, VPLoad->getBasePtr(), Temp2);
 
-    MachineFunction &MF = DAG.getMachineFunction();
-    MachinePointerInfo PtrInfo(VPLoad->getAddressSpace());
-    MachineMemOperand *MMO = MF.getMachineMemOperand(
-        PtrInfo, VPLoad->getMemOperand()->getFlags(),
-        LocationSize::beforeOrAfterPointer(), VPLoad->getAlign());
+  MachineFunction &MF = DAG.getMachineFunction();
+  MachinePointerInfo PtrInfo(VPLoad->getAddressSpace());
+  MachineMemOperand *MMO = MF.getMachineMemOperand(
+      PtrInfo, VPLoad->getMemOperand()->getFlags(),
+      LocationSize::beforeOrAfterPointer(), VPLoad->getAlign());
 
-    SDValue Ret = DAG.getStridedLoadVP(
-        VPLoad->getValueType(0), DL, VPLoad->getChain(), Base, Stride, LoadMask,
-        VPLoad->getVectorLength(), MMO, VPLoad->isExpandingLoad());
-    DAG.ReplaceAllUsesWith(VPLoad, Ret.getNode());
-  }
+  SDValue Ret = DAG.getStridedLoadVP(
+      LoadVT, DL, VPLoad->getChain(), Base, Stride, LoadMask,
+      VPLoad->getVectorLength(), MMO, VPLoad->isExpandingLoad());
+
+  DAG.ReplaceAllUsesWith(VPLoad, Ret.getNode());
 
   // Remove the top level reverse.
-  (void)sd_match(N, m_ReverseEVL(m_Value(Op), m_Specific(EVL)));
+  (void)sd_match(N, m_ReverseEVL(m_Value(Op), m_Value()));
   return Op;
 }
 
diff --git a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
index 3b26a05f2adc3..b7235b78f6175 100644
--- a/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vp-combine-reverse-load.ll
@@ -246,19 +246,21 @@ define <vscale x 2 x float> @binop_nested(ptr %ptr, i32 zeroext %evl) {
   ret <vscale x 2 x float> %rev
 }
 
+; Negative test, we don't want to create more than one vlse.v
+
 define <vscale x 2 x float> @binop_2loads(ptr %ptr1, ptr %ptr2, i32 zeroext %evl) {
 ; CHECK-LABEL: binop_2loads:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a3, a2, 2
-; CHECK-NEXT:    addi a3, a3, -4
-; CHECK-NEXT:    li a4, -4
-; CHECK-NEXT:    add a1, a1, a3
 ; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
-; CHECK-NEXT:    vlse32.v v8, (a1), a4
-; CHECK-NEXT:    add a0, a0, a3
-; CHECK-NEXT:    vlse32.v v9, (a0), a4
-; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfadd.vv v8, v9, v8
+; CHECK-NEXT:    vle32.v v8, (a0)
+; CHECK-NEXT:    vid.v v9
+; CHECK-NEXT:    vle32.v v10, (a1)
+; CHECK-NEXT:    addi a0, a2, -1
+; CHECK-NEXT:    vsetvli a1, zero, e32, m1, ta, ma
+; CHECK-NEXT:    vfadd.vv v10, v8, v10
+; CHECK-NEXT:    vsetvli zero, a2, e32, m1, ta, ma
+; CHECK-NEXT:    vrsub.vx v9, v9, a0
+; CHECK-NEXT:    vrgather.vv v8, v10, v9
 ; CHECK-NEXT:    ret
   %load1 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr1, <vscale x 2 x i1> splat (i1 true), i32 %evl)
   %load2 = call <vscale x 2 x float> @llvm.vp.load(ptr %ptr2, <vscale x 2 x i1> splat (i1 true), i32 %evl)
@@ -267,22 +269,6 @@ define <vscale x 2 x float> @binop_2loads(ptr %ptr1, ptr %ptr2, i32 zeroext %evl
   ret <vscale x 2 x float> %rev
 }
 
-define <vscale x 2 x float> @binop_2splats(float %f1, float %f2, i32 zeroext %evl) {
-; CHECK-LABEL: binop_2splats:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    fadd.s fa5, fa0, fa1
-; CHECK-NEXT:    vsetvli a0, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vfmv.v.f v8, fa5
-; CHECK-NEXT:    ret
-  %splat1.head = insertelement <vscale x 2 x float> poison, float %f1, i32 0
-  %splat1 = shufflevector <vscale x 2 x float> %splat1.head, <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer
-  %splat2.head = insertelement <vscale x 2 x float> poison, float %f2, i32 0
-  %splat2 = shufflevector <vscale x 2 x float> %splat2.head, <vscale x 2 x float> poison, <vscale x 2 x i32> zeroinitializer
-  %fadd = fadd <vscale x 2 x float> %splat1, %splat2
-  %rev = call <vscale x 2 x float> @llvm.experimental.vp.reverse(<vscale x 2 x float> %fadd, <vscale x 2 x i1> splat (i1 true), i32 %evl)
-  ret <vscale x 2 x float> %rev
-}
-
 ; Negative test, can't combine because a leaf isn't a splat.
 
 define <vscale x 2 x float> @binop_nonsplat(ptr %ptr, <vscale x 2 x float> %v, i32 zeroext %evl) {

>From a1d085b5d9823c059f5c2c092f279718ad07c590 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Mon, 29 Jun 2026 16:46:33 +0800
Subject: [PATCH 8/8] Update comment

---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 18a22f5048bdb..a362a3b0eebfc 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19736,7 +19736,7 @@ static SDValue performReverseEVLCombine(SDNode *N, SelectionDAG &DAG,
     return SDValue();
 
   VPLoadSDNode *VPLoad = nullptr;
-  // Check that all leaves are splats or vp_loads, and collect the latter.
+  // Find the single vp_load and check all other leaves are splats.
   SmallVector<SDValue> Worklist = {Op};
   while (!Worklist.empty()) {
     SDValue X = Worklist.pop_back_val();



More information about the llvm-commits mailing list