[llvm] [SelectionDAG] Fold VECREDUCE_ADD of a constant BUILD_VECTOR (PR #207560)

Ankit Kumar Tiwari via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 5 00:30:09 PDT 2026


https://github.com/ankit-cybertron created https://github.com/llvm/llvm-project/pull/207560


This PR implements constant folding for ISD::VECREDUCE_ADD when the input is a BUILD_VECTOR of integer constants. The fold computes the sum at compile time using APInt arithmetic and returns a folded constant scalar, instead of emitting real vector materialization and reduction instructions for a value that's already known.

Folding is skipped if any element is undef or opaque, since the result can't be assumed at compile time in that case.

#206743

>From 66d774d8868c6673b8f52c1dc731d5ce96d333bd Mon Sep 17 00:00:00 2001
From: Ankit Kumar Tiwari <ankit.cybertron at gmail.com>
Date: Sun, 5 Jul 2026 11:50:29 +0530
Subject: [PATCH] [SelectionDAG] Fold VECREDUCE_ADD of a constant BUILD_VECTOR

---
 .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 18 +++++++
 .../CodeGen/RISCV/rvv/fold-binary-reduce.ll   |  8 +--
 .../RISCV/rvv/vecreduce-add-constant-fold.ll  | 53 +++++++++++++++++++
 3 files changed, 72 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/CodeGen/RISCV/rvv/vecreduce-add-constant-fold.ll

diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index c542f8e7cc20b..a6a5c29e1b716 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -7114,6 +7114,7 @@ SDValue SelectionDAG::getNode(unsigned Opcode, const SDLoc &DL, EVT VT,
   case ISD::CTTZ_ZERO_POISON:
   case ISD::CTPOP:
   case ISD::CTLS:
+  case ISD::VECREDUCE_ADD:
   case ISD::STEP_VECTOR: {
     SDValue Ops = {N1};
     if (SDValue Fold = FoldConstantArithmetic(Opcode, DL, VT, Ops))
@@ -7785,6 +7786,23 @@ SDValue SelectionDAG::FoldConstantArithmetic(unsigned Opcode, const SDLoc &DL,
     // Early-out if we failed to constant fold a bitcast.
     if (Opcode == ISD::BITCAST)
       return SDValue();
+
+    // Constant fold VECREDUCE_ADD with a BUILD_VECTOR of integer constants.
+    if (Opcode == ISD::VECREDUCE_ADD && N1.getOpcode() == ISD::BUILD_VECTOR &&
+        ISD::isBuildVectorOfConstantSDNodes(N1.getNode())) {
+      unsigned EltBits = N1.getValueType().getScalarSizeInBits();
+      APInt Acc = APInt::getZero(EltBits);
+      bool HasUndef = false;
+      for (SDValue Elt : N1->op_values()) {
+        if (Elt.isUndef() || cast<ConstantSDNode>(Elt)->isOpaque()) {
+          HasUndef = true;
+          break;
+        }
+        Acc += cast<ConstantSDNode>(Elt)->getAPIntValue().trunc(EltBits);
+      }
+      if (!HasUndef)
+        return getConstant(Acc.zextOrTrunc(VT.getSizeInBits()), DL, VT);
+    }
   }
 
   // Handle binops special cases.
diff --git a/llvm/test/CodeGen/RISCV/rvv/fold-binary-reduce.ll b/llvm/test/CodeGen/RISCV/rvv/fold-binary-reduce.ll
index 134bea04b58fc..5a1ce80eb9468 100644
--- a/llvm/test/CodeGen/RISCV/rvv/fold-binary-reduce.ll
+++ b/llvm/test/CodeGen/RISCV/rvv/fold-binary-reduce.ll
@@ -327,13 +327,7 @@ entry:
 define void @crash(<2 x i32> %0) {
 ; CHECK-LABEL: crash:
 ; CHECK:       # %bb.0: # %entry
-; CHECK-NEXT:    vsetivli zero, 4, e16, mf2, ta, ma
-; CHECK-NEXT:    vmv.v.i v9, 0
-; CHECK-NEXT:    vsetvli zero, zero, e32, m1, ta, ma
-; CHECK-NEXT:    vmv.x.s a0, v8
-; CHECK-NEXT:    vmv.s.x v8, a0
-; CHECK-NEXT:    vsetvli zero, zero, e16, mf2, ta, ma
-; CHECK-NEXT:    vredsum.vs v8, v9, v8
+; CHECK-NEXT:    vsetivli zero, 1, e32, m1, ta, ma
 ; CHECK-NEXT:    vmv.x.s a0, v8
 ; CHECK-NEXT:    sb a0, 0(zero)
 ; CHECK-NEXT:    ret
diff --git a/llvm/test/CodeGen/RISCV/rvv/vecreduce-add-constant-fold.ll b/llvm/test/CodeGen/RISCV/rvv/vecreduce-add-constant-fold.ll
new file mode 100644
index 0000000000000..c45ead8152995
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/rvv/vecreduce-add-constant-fold.ll
@@ -0,0 +1,53 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mtriple=riscv64 -mattr=+v -verify-machineinstrs < %s | FileCheck %s
+
+; Case 1: all-constant vector -> should fold to a plain constant (10)
+define i32 @test_const() {
+; CHECK-LABEL: test_const:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 10
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>)
+  ret i32 %r
+}
+
+; Case 2: vector containing an undef element -> should NOT fold
+define i32 @test_undef() {
+; CHECK-LABEL: test_undef:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 4, e32, m1, ta, ma
+; CHECK-NEXT:    vmv.s.x v8, zero
+; CHECK-NEXT:    vid.v v9
+; CHECK-NEXT:    vadd.vi v9, v9, 1
+; CHECK-NEXT:    vredsum.vs v8, v9, v8
+; CHECK-NEXT:    vmv.x.s a0, v8
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> <i32 1, i32 2, i32 undef, i32 4>)
+  ret i32 %r
+}
+
+; Case 3: 8-element vector, sum = 360
+define i32 @test_const_wide() {
+; CHECK-LABEL: test_const_wide:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a0, 360
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.vector.reduce.add.v8i32(<8 x i32> <i32 10, i32 20, i32 30, i32 40, i32 50, i32 60, i32 70, i32 80>)
+  ret i32 %r
+}
+
+; Case 4: non-constant input -> should NOT fold
+define i32 @test_nonconst(<4 x i32> %v) {
+; CHECK-LABEL: test_nonconst:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    vsetivli zero, 4, e32, m1, ta, ma
+; CHECK-NEXT:    vmv.s.x v9, zero
+; CHECK-NEXT:    vredsum.vs v8, v8, v9
+; CHECK-NEXT:    vmv.x.s a0, v8
+; CHECK-NEXT:    ret
+  %r = call i32 @llvm.vector.reduce.add.v4i32(<4 x i32> %v)
+  ret i32 %r
+}
+
+declare i32 @llvm.vector.reduce.add.v4i32(<4 x i32>)
+declare i32 @llvm.vector.reduce.add.v8i32(<8 x i32>)



More information about the llvm-commits mailing list