[llvm] [flang-rt] Consider the signedness of zero for PRODUCT (PR #226918)

Yusuke MINATO via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 28 01:12:30 PDT 2026


https://github.com/yus3710-fj created https://github.com/llvm/llvm-project/pull/226918

Currently, the result of PRODUCT can be calculated in three places:

1. constant folding in Semantics
2. the runtime library
3. inlined code

Only the runtime ignored the signedness of zero. This patch fixes this discrepancy.

>From 4959f2fbb9ae777fe17e67fd329f1a9b9a65c6f8 Mon Sep 17 00:00:00 2001
From: Yusuke MINATO <minato.yusuke at fujitsu.com>
Date: Fri, 7 Aug 2026 14:59:18 +0900
Subject: [PATCH 1/2] [flang][Runtime] Consider the signedness of zero for
 PRODUCT

Currently, the result of PRODUCT can be calculated in three places:

1. constant folding in Semantics
2. the runtime library
3. inlined code

Only the runtime ignored the signedness of zero. This patch fixes this discrepancy.
---
 flang-rt/lib/runtime/product.cpp | 67 +++++++++++++++++++-------------
 1 file changed, 40 insertions(+), 27 deletions(-)

diff --git a/flang-rt/lib/runtime/product.cpp b/flang-rt/lib/runtime/product.cpp
index 02fdc2bfcd576..8d5ee8bd93777 100644
--- a/flang-rt/lib/runtime/product.cpp
+++ b/flang-rt/lib/runtime/product.cpp
@@ -16,9 +16,9 @@
 #include <complex>
 
 namespace Fortran::runtime {
-template <typename INTERMEDIATE> class NonComplexProductAccumulator {
+template <typename INTERMEDIATE> class IntegerProductAccumulator {
 public:
-  explicit RT_API_ATTRS NonComplexProductAccumulator(const Descriptor &array)
+  explicit RT_API_ATTRS IntegerProductAccumulator(const Descriptor &array)
       : array_{array} {}
   RT_API_ATTRS void Reinitialize() { product_ = 1; }
   template <typename A>
@@ -36,6 +36,28 @@ template <typename INTERMEDIATE> class NonComplexProductAccumulator {
   INTERMEDIATE product_{1};
 };
 
+template <typename INTERMEDIATE> class RealProductAccumulator {
+public:
+  explicit RT_API_ATTRS RealProductAccumulator(const Descriptor &array)
+      : array_{array} {}
+  RT_API_ATTRS void Reinitialize() { product_ = 1; }
+  template <typename A>
+  RT_API_ATTRS void GetResult(A *p, int /*zeroBasedDim*/ = -1) const {
+    *p = static_cast<A>(product_);
+  }
+  template <typename A>
+  RT_API_ATTRS bool AccumulateAt(const SubscriptValue at[]) {
+    product_ *= *array_.Element<A>(at);
+    // If nsz (No Signed Zeros) flag is set, we could cut off the product early
+    // when we hit a zero.
+    return true;
+  }
+
+private:
+  const Descriptor &array_;
+  INTERMEDIATE product_{1};
+};
+
 template <typename PART> class ComplexProductAccumulator {
 public:
   explicit RT_API_ATTRS ComplexProductAccumulator(const Descriptor &array)
@@ -64,25 +86,25 @@ RT_EXT_API_GROUP_BEGIN
 CppTypeFor<TypeCategory::Integer, 1> RTDEF(ProductInteger1)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Integer, 1>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
+      IntegerProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Integer, 2> RTDEF(ProductInteger2)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Integer, 2>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
+      IntegerProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Integer, 4> RTDEF(ProductInteger4)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Integer, 4>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
+      IntegerProductAccumulator<CppTypeFor<TypeCategory::Integer, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Integer, 8> RTDEF(ProductInteger8)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Integer, 8>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x},
+      IntegerProductAccumulator<CppTypeFor<TypeCategory::Integer, 8>>{x},
       "PRODUCT");
 }
 #ifdef __SIZEOF_INT128__
@@ -90,8 +112,7 @@ CppTypeFor<TypeCategory::Integer, 16> RTDEF(ProductInteger16)(
     const Descriptor &x, const char *source, int line, int dim,
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Integer, 16>(x, source, line, dim,
-      mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
+      mask, IntegerProductAccumulator<CppTypeFor<TypeCategory::Integer, 16>>{x},
       "PRODUCT");
 }
 #endif
@@ -100,32 +121,28 @@ CppTypeFor<TypeCategory::Unsigned, 1> RTDEF(ProductUnsigned1)(
     const Descriptor &x, const char *source, int line, int dim,
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Unsigned, 1>(x, source, line, dim,
-      mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
+      mask, IntegerProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Unsigned, 2> RTDEF(ProductUnsigned2)(
     const Descriptor &x, const char *source, int line, int dim,
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Unsigned, 2>(x, source, line, dim,
-      mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
+      mask, IntegerProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Unsigned, 4> RTDEF(ProductUnsigned4)(
     const Descriptor &x, const char *source, int line, int dim,
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Unsigned, 4>(x, source, line, dim,
-      mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
+      mask, IntegerProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 4>>{x},
       "PRODUCT");
 }
 CppTypeFor<TypeCategory::Unsigned, 8> RTDEF(ProductUnsigned8)(
     const Descriptor &x, const char *source, int line, int dim,
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Unsigned, 8>(x, source, line, dim,
-      mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 8>>{x},
+      mask, IntegerProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 8>>{x},
       "PRODUCT");
 }
 #ifdef __SIZEOF_INT128__
@@ -134,7 +151,7 @@ CppTypeFor<TypeCategory::Unsigned, 16> RTDEF(ProductUnsigned16)(
     const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Unsigned, 16>(x, source, line, dim,
       mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 16>>{x},
+      IntegerProductAccumulator<CppTypeFor<TypeCategory::Unsigned, 16>>{x},
       "PRODUCT");
 }
 #endif
@@ -143,29 +160,25 @@ CppTypeFor<TypeCategory::Unsigned, 16> RTDEF(ProductUnsigned16)(
 CppTypeFor<TypeCategory::Real, 4> RTDEF(ProductReal4)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Real, 4>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 4>>{x},
-      "PRODUCT");
+      RealProductAccumulator<CppTypeFor<TypeCategory::Real, 4>>{x}, "PRODUCT");
 }
 CppTypeFor<TypeCategory::Real, 8> RTDEF(ProductReal8)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Real, 8>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x},
-      "PRODUCT");
+      RealProductAccumulator<CppTypeFor<TypeCategory::Real, 8>>{x}, "PRODUCT");
 }
 #if HAS_FLOAT80
 CppTypeFor<TypeCategory::Real, 10> RTDEF(ProductReal10)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Real, 10>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x},
-      "PRODUCT");
+      RealProductAccumulator<CppTypeFor<TypeCategory::Real, 10>>{x}, "PRODUCT");
 }
 #endif
 #if HAS_LDBL128 || HAS_FLOAT128
 CppTypeFor<TypeCategory::Real, 16> RTDEF(ProductReal16)(const Descriptor &x,
     const char *source, int line, int dim, const Descriptor *mask) {
   return GetTotalReduction<TypeCategory::Real, 16>(x, source, line, dim, mask,
-      NonComplexProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x},
-      "PRODUCT");
+      RealProductAccumulator<CppTypeFor<TypeCategory::Real, 16>>{x}, "PRODUCT");
 }
 #endif
 
@@ -204,8 +217,8 @@ void RTDEF(CppProductComplex16)(CppTypeFor<TypeCategory::Complex, 16> &result,
 
 void RTDEF(ProductDim)(Descriptor &result, const Descriptor &x, int dim,
     const char *source, int line, const Descriptor *mask) {
-  TypedPartialNumericReduction<NonComplexProductAccumulator,
-      NonComplexProductAccumulator, ComplexProductAccumulator,
+  TypedPartialNumericReduction<IntegerProductAccumulator,
+      RealProductAccumulator, ComplexProductAccumulator,
       /*MIN_REAL_KIND=*/4>(result, x, dim, source, line, mask, "PRODUCT");
 }
 

>From 7720a33e5ad6e55076c19222a88a6994a37d3a87 Mon Sep 17 00:00:00 2001
From: Yusuke MINATO <minato.yusuke at fujitsu.com>
Date: Mon, 7 Sep 2026 16:57:21 +0900
Subject: [PATCH 2/2] Add test

---
 flang-rt/unittests/Runtime/Reduction.cpp | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/flang-rt/unittests/Runtime/Reduction.cpp b/flang-rt/unittests/Runtime/Reduction.cpp
index 821d89f41ba7e..fd763f2faffa9 100644
--- a/flang-rt/unittests/Runtime/Reduction.cpp
+++ b/flang-rt/unittests/Runtime/Reduction.cpp
@@ -688,3 +688,27 @@ TEST(Reductions, InfSums) {
   auto t3{RTNAME(SumReal4)(*infMinusInf, __FILE__, __LINE__)};
   EXPECT_NE(t3, t3) << t3;
 }
+
+TEST(Reductions, NegZeroProduct) {
+  auto intVector{MakeArray<TypeCategory::Integer, 4>(
+      std::vector<int>{2}, std::vector<std::int32_t>{0, -1})};
+  int resultI4{RTNAME(ProductInteger4)(*intVector, __FILE__, __LINE__)};
+  EXPECT_EQ(resultI4, 0);
+  EXPECT_NE(resultI4, 1 << 31); // not -0 but -2^31 in two's complement
+  auto realVector{MakeArray<TypeCategory::Real, 4>(
+      std::vector<int>{2}, std::vector<float>{0.0f, -1.0f})};
+  float resultR4{RTNAME(ProductReal4)(*realVector, __FILE__, __LINE__)};
+  EXPECT_EQ(resultR4, 0.0f);
+  EXPECT_TRUE(std::signbit(resultR4));
+  auto complexVector1{MakeArray<TypeCategory::Complex, 4>(std::vector<int>{2},
+      std::vector<std::complex<float>>{{0.0f, 1.0f}, {-1.0f, 0.0f}})};
+  std::complex<float> resultC4;
+  RTNAME(CppProductComplex4)(resultC4, *complexVector1, __FILE__, __LINE__);
+  EXPECT_EQ(resultC4.real(), 0.0f);
+  EXPECT_TRUE(std::signbit(resultC4.real()));
+  auto complexVector2{MakeArray<TypeCategory::Complex, 4>(std::vector<int>{2},
+      std::vector<std::complex<float>>{{0.0f, 0.0f}, {-1.0f, -1.0f}})};
+  RTNAME(CppProductComplex4)(resultC4, *complexVector2, __FILE__, __LINE__);
+  EXPECT_EQ(resultC4.imag(), 0.0f);
+  EXPECT_TRUE(std::signbit(resultC4.imag()));
+}



More information about the llvm-commits mailing list