[llvm] [SelectionDAG] Fold VECREDUCE_MUL of a constant BUILD_VECTOR (PR #210126)
Ankit Kumar Tiwari via llvm-commits
llvm-commits at lists.llvm.org
Fri Jul 17 00:09:54 PDT 2026
https://github.com/ankit-cybertron updated https://github.com/llvm/llvm-project/pull/210126
>From e5463cb0280a9837bc33f3ae17ede85b11c465df Mon Sep 17 00:00:00 2001
From: Ankit Kumar Tiwari <ankit.cybertron at gmail.com>
Date: Thu, 16 Jul 2026 23:23:04 +0530
Subject: [PATCH 1/3] Add Base Test for AArch64
---
.../AArch64/vecreduce-mul-constant-fold.ll | 76 +++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100644 llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll
diff --git a/llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll b/llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll
new file mode 100644
index 0000000000000..664975cf1ca23
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll
@@ -0,0 +1,76 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s
+
+; all-constant vector -> should fold to a plain constant (24)
+define i32 @test_const() {
+; CHECK-LABEL: test_const:
+; CHECK: // %bb.0:
+; CHECK-NEXT: movi v0.4s, #24
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>)
+ ret i32 %r
+}
+
+; 8-element vector, product = 40320
+define i32 @test_const_wide() {
+; CHECK-LABEL: test_const_wide:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w0, #40320 // =0x9d80
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v8i32(<8 x i32> <i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8>)
+ ret i32 %r
+}
+
+; non-constant input -> should NOT fold
+define i32 @test_nonconst(<4 x i32> %v) {
+; CHECK-LABEL: test_nonconst:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov d1, v0.d[1]
+; CHECK-NEXT: mul v0.2s, v0.2s, v1.2s
+; CHECK-NEXT: mul v0.2s, v0.2s, v0.s[1]
+; CHECK-NEXT: fmov w0, s0
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> %v)
+ ret i32 %r
+}
+
+; vector containing a poison element -> return poison
+define i32 @test_poison() {
+; CHECK-LABEL: test_poison:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w0, wzr
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 poison, i32 4>)
+ ret i32 %r
+}
+
+; vector containing zero and poison -> 0
+define i32 @test_zero_and_poison() {
+; CHECK-LABEL: test_zero_and_poison:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w0, wzr
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 0, i32 2, i32 poison, i32 4>)
+ ret i32 %r
+}
+
+; vector containing an undef element -> should NOT fold
+define i32 @test_undef() {
+; CHECK-LABEL: test_undef:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w0, wzr
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 undef, i32 4>)
+ ret i32 %r
+}
+
+; vector containing zero and undef -> 0
+define i32 @test_zero_and_undef() {
+; CHECK-LABEL: test_zero_and_undef:
+; CHECK: // %bb.0:
+; CHECK-NEXT: mov w0, wzr
+; CHECK-NEXT: ret
+ %r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 0, i32 2, i32 undef, i32 4>)
+ ret i32 %r
+}
>From 2f517bf3087a8d302f29d931ab2ec8a1722b6c9a Mon Sep 17 00:00:00 2001
From: Ankit Kumar Tiwari <ankit.cybertron at gmail.com>
Date: Fri, 17 Jul 2026 12:29:11 +0530
Subject: [PATCH 2/3] Base test for RISCV
---
.../rvv}/vecreduce-mul-constant-fold.ll | 44 +++++++++----------
1 file changed, 22 insertions(+), 22 deletions(-)
rename llvm/test/CodeGen/{AArch64 => RISCV/rvv}/vecreduce-mul-constant-fold.ll (68%)
diff --git a/llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll b/llvm/test/CodeGen/RISCV/rvv/vecreduce-mul-constant-fold.ll
similarity index 68%
rename from llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll
rename to llvm/test/CodeGen/RISCV/rvv/vecreduce-mul-constant-fold.ll
index 664975cf1ca23..f8dbee99c9f90 100644
--- a/llvm/test/CodeGen/AArch64/vecreduce-mul-constant-fold.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/vecreduce-mul-constant-fold.ll
@@ -1,12 +1,11 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=aarch64 -verify-machineinstrs < %s | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=riscv64 -mattr=+v -verify-machineinstrs < %s | FileCheck %s
; all-constant vector -> should fold to a plain constant (24)
define i32 @test_const() {
; CHECK-LABEL: test_const:
-; CHECK: // %bb.0:
-; CHECK-NEXT: movi v0.4s, #24
-; CHECK-NEXT: fmov w0, s0
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 24
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>)
ret i32 %r
@@ -15,8 +14,9 @@ define i32 @test_const() {
; 8-element vector, product = 40320
define i32 @test_const_wide() {
; CHECK-LABEL: test_const_wide:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov w0, #40320 // =0x9d80
+; CHECK: # %bb.0:
+; CHECK-NEXT: lui a0, 10
+; CHECK-NEXT: addi a0, a0, -640
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v8i32(<8 x i32> <i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8>)
ret i32 %r
@@ -25,11 +25,13 @@ define i32 @test_const_wide() {
; non-constant input -> should NOT fold
define i32 @test_nonconst(<4 x i32> %v) {
; CHECK-LABEL: test_nonconst:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov d1, v0.d[1]
-; CHECK-NEXT: mul v0.2s, v0.2s, v1.2s
-; CHECK-NEXT: mul v0.2s, v0.2s, v0.s[1]
-; CHECK-NEXT: fmov w0, s0
+; CHECK: # %bb.0:
+; CHECK-NEXT: vsetivli zero, 4, e32, m1, ta, ma
+; CHECK-NEXT: vslidedown.vi v9, v8, 2
+; CHECK-NEXT: vmul.vv v8, v8, v9
+; CHECK-NEXT: vrgather.vi v9, v8, 1
+; CHECK-NEXT: vmul.vv v8, v8, v9
+; CHECK-NEXT: vmv.x.s a0, v8
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> %v)
ret i32 %r
@@ -38,18 +40,16 @@ define i32 @test_nonconst(<4 x i32> %v) {
; vector containing a poison element -> return poison
define i32 @test_poison() {
; CHECK-LABEL: test_poison:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov w0, wzr
+; CHECK: # %bb.0:
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 poison, i32 4>)
ret i32 %r
}
-; vector containing zero and poison -> 0
+; vector containing zero and poison -> return poison
define i32 @test_zero_and_poison() {
; CHECK-LABEL: test_zero_and_poison:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov w0, wzr
+; CHECK: # %bb.0:
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 0, i32 2, i32 poison, i32 4>)
ret i32 %r
@@ -58,18 +58,18 @@ define i32 @test_zero_and_poison() {
; vector containing an undef element -> should NOT fold
define i32 @test_undef() {
; CHECK-LABEL: test_undef:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov w0, wzr
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 0
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 1, i32 2, i32 undef, i32 4>)
ret i32 %r
}
-; vector containing zero and undef -> 0
+; vector containing zero and undef -> should NOT fold
define i32 @test_zero_and_undef() {
; CHECK-LABEL: test_zero_and_undef:
-; CHECK: // %bb.0:
-; CHECK-NEXT: mov w0, wzr
+; CHECK: # %bb.0:
+; CHECK-NEXT: li a0, 0
; CHECK-NEXT: ret
%r = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> <i32 0, i32 2, i32 undef, i32 4>)
ret i32 %r
>From 2378fbf1904305ac06eaa9faaaf342e36fd0b0fd Mon Sep 17 00:00:00 2001
From: Ankit Kumar Tiwari <ankit.cybertron at gmail.com>
Date: Fri, 17 Jul 2026 12:37:53 +0530
Subject: [PATCH 3/3] Adds constant folding for VECREDUCE_MUL
---
llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 9719d603e1869..3bd86c8e29c1c 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -7130,6 +7130,7 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
case ISD::CTPOP:
case ISD::CTLS:
case ISD::VECREDUCE_ADD:
+ case ISD::VECREDUCE_MUL:
case ISD::STEP_VECTOR: {
SDValue Ops = {N1};
if (SDValue Fold = FoldConstantArithmetic(Opcode, DL, VT, Ops))
@@ -7815,6 +7816,22 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
}
EVT EltVT = N1.getValueType().getScalarType();
return getAnyExtOrTrunc(getConstant(Acc, DL, EltVT), DL, VT);
+ }
+
+ // Constant fold VECREDUCE_MUL with a BUILD_VECTOR of integer constants.
+ if (Opcode == ISD::VECREDUCE_MUL &&
+ ISD::isBuildVectorOfConstantSDNodes(N1.getNode())) {
+ unsigned EltBits = N1.getValueType().getScalarSizeInBits();
+ APInt Acc(EltBits, 1);
+ for (SDValue Elt : N1->op_values()) {
+ if (Elt.getOpcode() == ISD::POISON)
+ return getPOISON(VT);
+ if (Elt.isUndef() || cast<ConstantSDNode>(Elt)->isOpaque())
+ return SDValue();
+ Acc *= cast<ConstantSDNode>(Elt)->getAPIntValue().trunc(EltBits);
+ }
+ EVT EltVT = N1.getValueType().getScalarType();
+ return getAnyExtOrTrunc(getConstant(Acc, DL, EltVT), DL, VT);
}
}
More information about the llvm-commits
mailing list