[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