[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