[llvm] [RISCV] Simplify the insertelement 0, x, x in vector.reduce with an initial value to 0 (PR #193709)
Liao Chunyu via llvm-commits
llvm-commits at lists.llvm.org
Thu Apr 23 03:23:56 PDT 2026
https://github.com/ChunyuLiao updated https://github.com/llvm/llvm-project/pull/193709
>From c66813c3ba4a8e59f0c78642db0ed9a7a5ed3e12 Mon Sep 17 00:00:00 2001
From: Liao Chunyu <chunyu at iscas.ac.cn>
Date: Tue, 21 Apr 2026 09:34:25 +0000
Subject: [PATCH 1/5] simplyInsertElementForReduction
---
llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp | 86 +++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
index 4ce5432d67fcf..39de9456708a3 100644
--- a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
@@ -50,6 +50,7 @@ class RISCVCodeGenPrepare : public InstVisitor<RISCVCodeGenPrepare, bool> {
bool visitIntrinsicInst(IntrinsicInst &I);
bool expandVPStrideLoad(IntrinsicInst &I);
bool expandMulReduction(IntrinsicInst &I);
+ bool simplyInsertElementForReduction(IntrinsicInst &I);
bool widenVPMerge(Instruction *I);
bool visitFreezeInst(FreezeInst &BO);
};
@@ -233,6 +234,9 @@ bool RISCVCodeGenPrepare::visitIntrinsicInst(IntrinsicInst &I) {
if (expandMulReduction(I))
return true;
+ if (simplyInsertElementForReduction(I))
+ return true;
+
if (widenVPMerge(&I))
return true;
@@ -264,6 +268,88 @@ bool RISCVCodeGenPrepare::visitIntrinsicInst(IntrinsicInst &I) {
return true;
}
+bool RISCVCodeGenPrepare::simplyInsertElementForReduction(IntrinsicInst &II) {
+ auto Opcode = II.getIntrinsicID();
+ if (Opcode != Intrinsic::vector_reduce_xor &&
+ Opcode != Intrinsic::vector_reduce_add &&
+ Opcode != Intrinsic::vector_reduce_or)
+ return false;
+
+ Value *C, *X, *Y;
+ Value *Op0 = II.getArgOperand(0);
+ using namespace PatternMatch;
+ switch (Opcode) {
+ case Intrinsic::vector_reduce_xor:
+ if (!match(Op0, m_Intrinsic<Intrinsic::vp_merge>(
+ m_One(), m_OneUse(m_Xor(m_Value(X), m_Value(Y))),
+ m_Value(C))) ||
+ (X != C && Y != C))
+ return false;
+ break;
+ case Intrinsic::vector_reduce_add:
+ if (!match(Op0, m_Intrinsic<Intrinsic::vp_merge>(
+ m_One(), m_OneUse(m_Add(m_Value(X), m_Value(Y))),
+ m_Value(C))) ||
+ (X != C && Y != C))
+ return false;
+ break;
+ case Intrinsic::vector_reduce_or:
+ if (!match(Op0, m_Intrinsic<Intrinsic::vp_merge>(
+ m_One(), m_OneUse(m_Or(m_Value(X), m_Value(Y))),
+ m_Value(C))) ||
+ (X != C && Y != C))
+ return false;
+ break;
+ default:
+ return false;
+ };
+ auto *PHI = dyn_cast<PHINode>(C);
+ if (!PHI || !PHI->hasNUses(2) || PHI->getNumIncomingValues() != 2)
+ return false;
+
+ auto isScalar = [&](const Value *X) -> llvm::Value * {
+ if (auto *E = dyn_cast<ConstantExpr>(X);
+ E && E->getOpcode() == Instruction::InsertElement &&
+ match(E->getOperand(0), m_Zero()))
+ return E->getOperand(1);
+ if (auto *I = dyn_cast<Instruction>(X);
+ I && I->getOpcode() == Instruction::InsertElement &&
+ match(I->getOperand(0), m_Zero()))
+ return I->getOperand(1);
+ return nullptr;
+ };
+
+ auto *Phi0 = PHI->getIncomingValue(0);
+ auto *Phi1 = PHI->getIncomingValue(1);
+ Value *Scalar;
+ if (Scalar = isScalar(Phi0); Scalar && Phi1 == Op0)
+ PHI->setIncomingValue(0, ConstantInt::get(PHI->getType(), 0));
+ else if (Scalar = isScalar(Phi1); Scalar && Phi0 == Op0)
+ PHI->setIncomingValue(1, ConstantInt::get(PHI->getType(), 0));
+ else
+ return false;
+
+ IRBuilder<> Builder(&II);
+ Value *NewOp = Builder.CreateIntrinsic(Opcode, Op0->getType(), Op0);
+ Value *NewScalar;
+ switch (Opcode) {
+ case Intrinsic::vector_reduce_xor:
+ NewScalar = Builder.CreateXor(NewOp, Scalar);
+ break;
+ case Intrinsic::vector_reduce_add:
+ NewScalar = Builder.CreateAdd(NewOp, Scalar);
+ break;
+ case Intrinsic::vector_reduce_or:
+ NewScalar = Builder.CreateOr(NewOp, Scalar);
+ break;
+ default:
+ break;
+ };
+ II.replaceAllUsesWith(NewScalar);
+ II.eraseFromParent();
+ return true;
+}
+
// Partially expand a vector_reduce_mul wider than M1 to reduce the
// number of vsetvlis required when VLEN is exactly known, and
// reducing register pressure in all cases.
>From 44b775d8f05767f47b4a4991bc8216cbf7966086 Mon Sep 17 00:00:00 2001
From: Liao Chunyu <chunyu at iscas.ac.cn>
Date: Thu, 23 Apr 2026 07:01:03 +0000
Subject: [PATCH 2/5] init testcase
---
.../RISCV/rvv/reduction-insertelemet-init.ll | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
diff --git a/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll b/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
new file mode 100644
index 0000000000000..264184903e71d
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
@@ -0,0 +1,104 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv32 -mattr=+v -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc -mtriple=riscv64 -mattr=+v -verify-machineinstrs < %s | FileCheck %s --check-prefixes=CHECK,RV64
+;
+; testcase from:
+; int reduc_plus (int *a, int n) {
+; int r = 13;
+; for (int i = 0; i < n; ++i)
+; r += a[i];
+; return r;
+; }
+
+
+define dso_local signext i32 @reduc_plus(ptr noundef readonly captures(none) %0, i64 noundef %1, <vscale x 4 x i32> %a10, i32 %vl) {
+; RV32-LABEL: reduc_plus:
+; RV32: # %bb.0:
+; RV32-NEXT: beqz a2, .LBB0_3
+; RV32-NEXT: # %bb.1:
+; RV32-NEXT: srli a0, a2, 31
+; RV32-NEXT: beqz a0, .LBB0_4
+; RV32-NEXT: .LBB0_2:
+; RV32-NEXT: li a0, 13
+; RV32-NEXT: ret
+; RV32-NEXT: .LBB0_3:
+; RV32-NEXT: seqz a0, a1
+; RV32-NEXT: bnez a0, .LBB0_2
+; RV32-NEXT: .LBB0_4: # %.preheader
+; RV32-NEXT: vsetvli a0, zero, e32, m1, ta, ma
+; RV32-NEXT: vmv.v.i v12, 0
+; RV32-NEXT: li a0, 13
+; RV32-NEXT: vsetvli zero, zero, e32, m1, tu, ma
+; RV32-NEXT: vmv.s.x v12, a0
+; RV32-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; RV32-NEXT: vmv.v.i v10, 0
+; RV32-NEXT: vmv1r.v v10, v12
+; RV32-NEXT: .LBB0_5: # =>This Inner Loop Header: Depth=1
+; RV32-NEXT: sltu a0, a1, a3
+; RV32-NEXT: sub a1, a1, a3
+; RV32-NEXT: sub a2, a2, a0
+; RV32-NEXT: or a0, a1, a2
+; RV32-NEXT: vsetvli zero, a3, e32, m2, tu, ma
+; RV32-NEXT: vadd.vv v10, v8, v10
+; RV32-NEXT: bnez a0, .LBB0_5
+; RV32-NEXT: # %bb.6:
+; RV32-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; RV32-NEXT: vmv.s.x v8, zero
+; RV32-NEXT: vredsum.vs v8, v10, v8
+; RV32-NEXT: vmv.x.s a0, v8
+; RV32-NEXT: ret
+;
+; RV64-LABEL: reduc_plus:
+; RV64: # %bb.0:
+; RV64-NEXT: blez a1, .LBB0_4
+; RV64-NEXT: # %bb.1: # %.preheader
+; RV64-NEXT: neg a0, a1
+; RV64-NEXT: vsetvli a1, zero, e32, m1, ta, ma
+; RV64-NEXT: vmv.v.i v12, 0
+; RV64-NEXT: li a1, 13
+; RV64-NEXT: vsetvli a3, zero, e32, m2, ta, ma
+; RV64-NEXT: vmv.v.i v10, 0
+; RV64-NEXT: vsetvli zero, zero, e32, m2, tu, ma
+; RV64-NEXT: vmv.s.x v12, a1
+; RV64-NEXT: slli a1, a2, 32
+; RV64-NEXT: vmv1r.v v10, v12
+; RV64-NEXT: srli a1, a1, 32
+; RV64-NEXT: .LBB0_2: # =>This Inner Loop Header: Depth=1
+; RV64-NEXT: add a0, a0, a1
+; RV64-NEXT: vsetvli zero, a1, e32, m2, tu, ma
+; RV64-NEXT: vadd.vv v10, v8, v10
+; RV64-NEXT: bnez a0, .LBB0_2
+; RV64-NEXT: # %bb.3:
+; RV64-NEXT: vsetvli a0, zero, e32, m2, ta, ma
+; RV64-NEXT: vmv.s.x v8, zero
+; RV64-NEXT: vredsum.vs v8, v10, v8
+; RV64-NEXT: vmv.x.s a0, v8
+; RV64-NEXT: ret
+; RV64-NEXT: .LBB0_4:
+; RV64-NEXT: li a0, 13
+; RV64-NEXT: ret
+ %3 = icmp sgt i64 %1, 0
+ br i1 %3, label %4, label %19
+
+4:
+ %5 = phi i64 [ %14, %4 ], [ 0, %2 ]
+ %6 = phi <vscale x 4 x i32> [ %12, %4 ], [ insertelement (<vscale x 4 x i32> zeroinitializer, i32 13, i32 0), %2 ]
+ %7 = phi i64 [ %15, %4 ], [ %1, %2 ]
+ %11 = add <vscale x 4 x i32> %a10, %6
+ %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1> splat (i1 true), <vscale x 4 x i32> %11, <vscale x 4 x i32> %6, i32 %vl)
+ %13 = zext i32 %vl to i64
+ %14 = add i64 %5, %13
+ %15 = sub nuw i64 %7, %13
+ %16 = icmp eq i64 %15, 0
+ br i1 %16, label %17, label %4
+
+17:
+ %18 = tail call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %12)
+ br label %19
+
+19:
+ %20 = phi i32 [ 13, %2 ], [ %18, %17 ]
+ ret i32 %20
+}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; CHECK: {{.*}}
>From 981e4809f870e3e714a69eac1387c9c77f51324a Mon Sep 17 00:00:00 2001
From: Liao Chunyu <chunyu at iscas.ac.cn>
Date: Thu, 23 Apr 2026 07:03:16 +0000
Subject: [PATCH 3/5] update testcase
---
.../RISCV/rvv/reduction-insertelemet-init.ll | 22 +++++--------------
1 file changed, 6 insertions(+), 16 deletions(-)
diff --git a/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll b/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
index 264184903e71d..6b269a8ef21a8 100644
--- a/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/reduction-insertelemet-init.ll
@@ -25,14 +25,8 @@ define dso_local signext i32 @reduc_plus(ptr noundef readonly captures(none) %0,
; RV32-NEXT: seqz a0, a1
; RV32-NEXT: bnez a0, .LBB0_2
; RV32-NEXT: .LBB0_4: # %.preheader
-; RV32-NEXT: vsetvli a0, zero, e32, m1, ta, ma
-; RV32-NEXT: vmv.v.i v12, 0
-; RV32-NEXT: li a0, 13
-; RV32-NEXT: vsetvli zero, zero, e32, m1, tu, ma
-; RV32-NEXT: vmv.s.x v12, a0
; RV32-NEXT: vsetvli a0, zero, e32, m2, ta, ma
; RV32-NEXT: vmv.v.i v10, 0
-; RV32-NEXT: vmv1r.v v10, v12
; RV32-NEXT: .LBB0_5: # =>This Inner Loop Header: Depth=1
; RV32-NEXT: sltu a0, a1, a3
; RV32-NEXT: sub a1, a1, a3
@@ -42,8 +36,9 @@ define dso_local signext i32 @reduc_plus(ptr noundef readonly captures(none) %0,
; RV32-NEXT: vadd.vv v10, v8, v10
; RV32-NEXT: bnez a0, .LBB0_5
; RV32-NEXT: # %bb.6:
+; RV32-NEXT: vsetivli zero, 1, e32, m1, ta, ma
+; RV32-NEXT: vmv.v.i v8, 13
; RV32-NEXT: vsetvli a0, zero, e32, m2, ta, ma
-; RV32-NEXT: vmv.s.x v8, zero
; RV32-NEXT: vredsum.vs v8, v10, v8
; RV32-NEXT: vmv.x.s a0, v8
; RV32-NEXT: ret
@@ -53,24 +48,19 @@ define dso_local signext i32 @reduc_plus(ptr noundef readonly captures(none) %0,
; RV64-NEXT: blez a1, .LBB0_4
; RV64-NEXT: # %bb.1: # %.preheader
; RV64-NEXT: neg a0, a1
-; RV64-NEXT: vsetvli a1, zero, e32, m1, ta, ma
-; RV64-NEXT: vmv.v.i v12, 0
-; RV64-NEXT: li a1, 13
-; RV64-NEXT: vsetvli a3, zero, e32, m2, ta, ma
-; RV64-NEXT: vmv.v.i v10, 0
-; RV64-NEXT: vsetvli zero, zero, e32, m2, tu, ma
-; RV64-NEXT: vmv.s.x v12, a1
; RV64-NEXT: slli a1, a2, 32
-; RV64-NEXT: vmv1r.v v10, v12
; RV64-NEXT: srli a1, a1, 32
+; RV64-NEXT: vsetvli a2, zero, e32, m2, ta, ma
+; RV64-NEXT: vmv.v.i v10, 0
; RV64-NEXT: .LBB0_2: # =>This Inner Loop Header: Depth=1
; RV64-NEXT: add a0, a0, a1
; RV64-NEXT: vsetvli zero, a1, e32, m2, tu, ma
; RV64-NEXT: vadd.vv v10, v8, v10
; RV64-NEXT: bnez a0, .LBB0_2
; RV64-NEXT: # %bb.3:
+; RV64-NEXT: vsetivli zero, 1, e32, m1, ta, ma
+; RV64-NEXT: vmv.v.i v8, 13
; RV64-NEXT: vsetvli a0, zero, e32, m2, ta, ma
-; RV64-NEXT: vmv.s.x v8, zero
; RV64-NEXT: vredsum.vs v8, v10, v8
; RV64-NEXT: vmv.x.s a0, v8
; RV64-NEXT: ret
>From 2958408a8a48a2bf545882288028eb66fd0b83a1 Mon Sep 17 00:00:00 2001
From: Liao Chunyu <chunyu at iscas.ac.cn>
Date: Thu, 23 Apr 2026 10:01:10 +0000
Subject: [PATCH 4/5] add comment
---
llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
index 39de9456708a3..89c8ccd341b70 100644
--- a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
@@ -268,6 +268,28 @@ bool RISCVCodeGenPrepare::visitIntrinsicInst(IntrinsicInst &I) {
return true;
}
+// For reduction operations with an initial value, ir:
+// %6 = phi <vscale x 4 x i32> [ %12, %4 ],
+// [ insertelement (<vscale x 4 x i32> zeroinitializer, i32 13, i32 0), %2 ]
+// %11 = add <vscale x 4 x i32> %a10, %6
+// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1> splat (i1 true),
+// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6, i32 %vl)
+//
+//bb:
+// %18 = tail call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %12)
+//
+// insertelement (<vscale x 2 x i64> zeroinitializer, i64 13, i32 0),
+// which generates multiple vmv instructions. By initializing with zeroinitializer and adding
+// the initial value after the reduction, some vmv instructions can be avoided. ir:
+//
+// %6 = phi <vscale x 4 x i32> [ %12, %4 ], [zeroinitializer, %2 ]
+// %11 = add <vscale x 4 x i32> %a10, %6
+// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1> splat (i1 true),
+// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6, i32 %vl)
+//
+//bb:
+// %18 = tail call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %12)
+// %19 = add i32 %18, 13
bool RISCVCodeGenPrepare::simplyInsertElementForReduction(IntrinsicInst &II) {
auto Opcode = II.getIntrinsicID();
if (Opcode != Intrinsic::vector_reduce_xor &&
>From c2679c60b28449d3d6593372f3043f507c8960ad Mon Sep 17 00:00:00 2001
From: Liao Chunyu <chunyu at iscas.ac.cn>
Date: Thu, 23 Apr 2026 10:23:38 +0000
Subject: [PATCH 5/5] format
---
llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp | 24 ++++++++++++-------
1 file changed, 15 insertions(+), 9 deletions(-)
diff --git a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
index 89c8ccd341b70..ab03b0a2a6257 100644
--- a/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
+++ b/llvm/lib/Target/RISCV/RISCVCodeGenPrepare.cpp
@@ -270,24 +270,30 @@ bool RISCVCodeGenPrepare::visitIntrinsicInst(IntrinsicInst &I) {
// For reduction operations with an initial value, ir:
// %6 = phi <vscale x 4 x i32> [ %12, %4 ],
-// [ insertelement (<vscale x 4 x i32> zeroinitializer, i32 13, i32 0), %2 ]
+// [ insertelement (<vscale x 4 x i32> zeroinitializer, i32 13, i32 0),
+// %2 ]
// %11 = add <vscale x 4 x i32> %a10, %6
-// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1> splat (i1 true),
-// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6, i32 %vl)
+// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1>
+// splat (i1 true),
+// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6,
+// i32 %vl)
//
-//bb:
+// bb:
// %18 = tail call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %12)
//
// insertelement (<vscale x 2 x i64> zeroinitializer, i64 13, i32 0),
-// which generates multiple vmv instructions. By initializing with zeroinitializer and adding
-// the initial value after the reduction, some vmv instructions can be avoided. ir:
+// which generates multiple vmv instructions. By initializing with
+// zeroinitializer and adding the initial value after the reduction, some vmv
+// instructions can be avoided. ir:
//
// %6 = phi <vscale x 4 x i32> [ %12, %4 ], [zeroinitializer, %2 ]
// %11 = add <vscale x 4 x i32> %a10, %6
-// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1> splat (i1 true),
-// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6, i32 %vl)
+// %12 = tail call <vscale x 4 x i32> @llvm.vp.merge.nxv4i32(<vscale x 4 x i1>
+// splat (i1 true),
+// <vscale x 4 x i32> %11, <vscale x 4 x i32> %6,
+// i32 %vl)
//
-//bb:
+// bb:
// %18 = tail call i32 @llvm.vector.reduce.add.nxv4i32(<vscale x 4 x i32> %12)
// %19 = add i32 %18, 13
bool RISCVCodeGenPrepare::simplyInsertElementForReduction(IntrinsicInst &II) {
More information about the llvm-commits
mailing list