[flang-commits] [flang] [flang] Build 128-bit MIN/MAX identity constants from APInt (PR #228513)
via flang-commits
flang-commits at lists.llvm.org
Fri Oct 2 11:45:59 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-openmp
Author: Eugene Epshteyn (eugeneepshteyn)
<details>
<summary>Changes</summary>
The MIN/MAX reduction identities were computed with `APInt::getSigned{Min,Max}Value(bits).getSExtValue()`
and passed to `createIntegerConstant(int64_t)`. For INTEGER(16) that value does not fit in `int64_t`.
An assertions build aborts at `-O1` on any `MINVAL`/`MAXVAL`/`MINLOC`/`MAXLOC` of an INTEGER(16)
array, and on OpenMP `reduction(min:)`/`reduction(max:)` of INTEGER(16) at any `-O`. A release
build zero-extends the low 64 bits instead. The identity becomes `2**64-1` or `0`, so empty and
fully masked reductions return the wrong value.
This adds an `APInt` overload of `FirOpBuilder::createIntegerConstant` and uses it at the four
affected sites: `SimplifyHLFIRIntrinsics`, the MAXVAL and MINLOC/MAXLOC `init` lambdas in
`SimplifyIntrinsics`, and `ReductionProcessor::getReductionInitValue`.
Fixes #<!-- -->228509
Assisted-by: AI
---
Full diff: https://github.com/llvm/llvm-project/pull/228513.diff
11 Files Affected:
- (modified) flang/include/flang/Optimizer/Builder/FIRBuilder.h (+7)
- (modified) flang/lib/Lower/Support/ReductionProcessor.cpp (+4-4)
- (modified) flang/lib/Optimizer/Builder/FIRBuilder.cpp (+18)
- (modified) flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp (+3-4)
- (modified) flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp (+4-5)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-maxloc.fir (+8)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir (+15)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-minloc.fir (+8)
- (modified) flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir (+15)
- (added) flang/test/Lower/OpenMP/wsloop-reduction-min-max-i128.f90 (+23)
- (modified) flang/test/Transforms/simplifyintrinsics.fir (+27)
``````````diff
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index a29e3bf7063a5..3857d2b73c4ee 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -195,6 +195,13 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType,
std::int64_t i);
+ /// Create an integer constant of \p integerType with value \p value. The
+ /// bit width of \p value must match the width of \p integerType. Use this
+ /// for values that do not fit in std::int64_t, such as the limits of
+ /// 128-bit integers.
+ mlir::Value createIntegerConstant(mlir::Location loc, mlir::Type integerType,
+ const llvm::APInt &value);
+
/// Create an integer of \p integerType where all the bits have been set to
/// ones. Safe to use regardless of integerType bitwidth.
mlir::Value createAllOnesInteger(mlir::Location loc, mlir::Type integerType);
diff --git a/flang/lib/Lower/Support/ReductionProcessor.cpp b/flang/lib/Lower/Support/ReductionProcessor.cpp
index f36834fecc914..ae483128fe7df 100644
--- a/flang/lib/Lower/Support/ReductionProcessor.cpp
+++ b/flang/lib/Lower/Support/ReductionProcessor.cpp
@@ -317,8 +317,8 @@ ReductionProcessor::getReductionInitValue(mlir::Location loc, mlir::Type type,
loc, type, llvm::APFloat::getLargest(sem, /*Negative=*/true));
}
unsigned bits = type.getIntOrFloatBitWidth();
- int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
- return builder.createIntegerConstant(loc, type, minInt);
+ return builder.createIntegerConstant(loc, type,
+ llvm::APInt::getSignedMinValue(bits));
}
case ReductionIdentifier::MIN: {
if (auto ty = mlir::dyn_cast<mlir::FloatType>(type)) {
@@ -327,8 +327,8 @@ ReductionProcessor::getReductionInitValue(mlir::Location loc, mlir::Type type,
loc, type, llvm::APFloat::getLargest(sem, /*Negative=*/false));
}
unsigned bits = type.getIntOrFloatBitWidth();
- int64_t maxInt = llvm::APInt::getSignedMaxValue(bits).getSExtValue();
- return builder.createIntegerConstant(loc, type, maxInt);
+ return builder.createIntegerConstant(loc, type,
+ llvm::APInt::getSignedMaxValue(bits));
}
case ReductionIdentifier::IOR: {
unsigned bits = type.getIntOrFloatBitWidth();
diff --git a/flang/lib/Optimizer/Builder/FIRBuilder.cpp b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
index b808dbfa267cf..7f03f3f050d5f 100644
--- a/flang/lib/Optimizer/Builder/FIRBuilder.cpp
+++ b/flang/lib/Optimizer/Builder/FIRBuilder.cpp
@@ -167,6 +167,24 @@ mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
return createConvert(loc, ty, cstValue);
}
+mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
+ mlir::Type ty,
+ const llvm::APInt &cst) {
+ auto intType = mlir::cast<mlir::IntegerType>(ty);
+ assert(intType.getWidth() == cst.getBitWidth() && "bit width mismatch");
+ // Signed and unsigned constants must be encoded as signless
+ // arith.constant followed by fir.convert cast.
+ mlir::Type cstType = ty;
+ if (intType.isUnsigned())
+ cstType = mlir::IntegerType::get(getContext(), intType.getWidth());
+ else if (intType.isSigned())
+ TODO(loc, "signed integer constant");
+
+ mlir::Value cstValue = mlir::arith::ConstantOp::create(
+ *this, loc, cstType, getIntegerAttr(cstType, cst));
+ return createConvert(loc, ty, cstValue);
+}
+
mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc,
mlir::Type ty) {
if (mlir::isa<mlir::IndexType>(ty))
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
index 0e28a91afbd90..17ae3260163a4 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
@@ -371,10 +371,9 @@ static mlir::Value genMinMaxInitValue(mlir::Location loc,
return builder.createRealConstant(loc, type, limit);
}
unsigned bits = type.getIntOrFloatBitWidth();
- int64_t limitInt = IS_MAX
- ? llvm::APInt::getSignedMinValue(bits).getSExtValue()
- : llvm::APInt::getSignedMaxValue(bits).getSExtValue();
- return builder.createIntegerConstant(loc, type, limitInt);
+ llvm::APInt limit = IS_MAX ? llvm::APInt::getSignedMinValue(bits)
+ : llvm::APInt::getSignedMaxValue(bits);
+ return builder.createIntegerConstant(loc, type, limit);
}
/// Generate a comparison of an array element value \p elem
diff --git a/flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp b/flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp
index 6e1608c3c5877..78cb92ed9a8fa 100644
--- a/flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp
+++ b/flang/lib/Optimizer/Transforms/SimplifyIntrinsics.cpp
@@ -418,8 +418,8 @@ static void genRuntimeMaxvalBody(fir::FirOpBuilder &builder,
loc, elementType, llvm::APFloat::getLargest(sem, /*Negative=*/true));
}
unsigned bits = elementType.getIntOrFloatBitWidth();
- int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
- return builder.createIntegerConstant(loc, elementType, minInt);
+ return builder.createIntegerConstant(loc, elementType,
+ llvm::APInt::getSignedMinValue(bits));
};
auto genBodyOp = [](fir::FirOpBuilder builder, mlir::Location loc,
@@ -667,9 +667,8 @@ static void genRuntimeMinMaxlocBody(fir::FirOpBuilder &builder,
return builder.createRealConstant(loc, elementType, limit);
}
unsigned bits = elementType.getIntOrFloatBitWidth();
- int64_t initValue = (isMax ? llvm::APInt::getSignedMinValue(bits)
- : llvm::APInt::getSignedMaxValue(bits))
- .getSExtValue();
+ llvm::APInt initValue = isMax ? llvm::APInt::getSignedMinValue(bits)
+ : llvm::APInt::getSignedMaxValue(bits);
return builder.createIntegerConstant(loc, elementType, initValue);
};
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-maxloc.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxloc.fir
index b285945027afb..c4f40663a78d1 100644
--- a/flang/test/HLFIR/simplify-hlfir-intrinsics-maxloc.fir
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxloc.fir
@@ -483,3 +483,11 @@ func.func @test_back(%input: !hlfir.expr<?xi32>) -> !hlfir.expr<1xi32> {
}
// CHECK-LABEL: func.func @test_back(
// CHECK: hlfir.maxloc
+
+// The INTEGER(16) identity must not be truncated to 64 bits.
+func.func @test_i128_mask(%input: !hlfir.expr<?xi128>, %mask: !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32> {
+ %0 = hlfir.maxloc %input mask %mask : (!hlfir.expr<?xi128>, !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32>
+ return %0 : !hlfir.expr<1xi32>
+}
+// CHECK-LABEL: func.func @test_i128_mask(
+// CHECK: arith.constant -170141183460469231731687303715884105728 : i128
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir
index def0f46a4fe9e..639d9e3890419 100644
--- a/flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-maxval.fir
@@ -308,3 +308,18 @@ func.func @test_simple_int(%input: !hlfir.expr<?xi32>) -> i32 {
// CHECK-LABEL: func.func @test_simple_int(
// CHECK: fir.do_loop{{.*}}unordered
// CHECK: arith.maxsi %{{.*}}, %{{.*}} : i32
+
+// The INTEGER(16) identity must not be truncated to 64 bits.
+func.func @test_i128_mask(%input: !hlfir.expr<?xi128>, %mask: !hlfir.expr<?x!fir.logical<4>>) -> i128 {
+ %0 = hlfir.maxval %input mask %mask : (!hlfir.expr<?xi128>, !hlfir.expr<?x!fir.logical<4>>) -> i128
+ return %0 : i128
+}
+// CHECK-LABEL: func.func @test_i128_mask(
+// CHECK: arith.constant -170141183460469231731687303715884105728 : i128
+
+func.func @test_i128_var_nomask(%input: !fir.box<!fir.array<?xi128>>) -> i128 {
+ %0 = hlfir.maxval %input : (!fir.box<!fir.array<?xi128>>) -> i128
+ return %0 : i128
+}
+// CHECK-LABEL: func.func @test_i128_var_nomask(
+// CHECK: arith.constant -170141183460469231731687303715884105728 : i128
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-minloc.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-minloc.fir
index b9a7195b5f139..0653dbcaee8d4 100644
--- a/flang/test/HLFIR/simplify-hlfir-intrinsics-minloc.fir
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-minloc.fir
@@ -483,3 +483,11 @@ func.func @test_back(%input: !hlfir.expr<?xi32>) -> !hlfir.expr<1xi32> {
}
// CHECK-LABEL: func.func @test_back(
// CHECK: hlfir.minloc
+
+// The INTEGER(16) identity must not be truncated to 64 bits.
+func.func @test_i128_mask(%input: !hlfir.expr<?xi128>, %mask: !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32> {
+ %0 = hlfir.minloc %input mask %mask : (!hlfir.expr<?xi128>, !hlfir.expr<?x!fir.logical<4>>) -> !hlfir.expr<1xi32>
+ return %0 : !hlfir.expr<1xi32>
+}
+// CHECK-LABEL: func.func @test_i128_mask(
+// CHECK: arith.constant 170141183460469231731687303715884105727 : i128
diff --git a/flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir b/flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir
index a61843c6d6fbb..f6541bbb25c50 100644
--- a/flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir
+++ b/flang/test/HLFIR/simplify-hlfir-intrinsics-minval.fir
@@ -308,3 +308,18 @@ func.func @test_simple_int(%input: !hlfir.expr<?xi32>) -> i32 {
// CHECK-LABEL: func.func @test_simple_int(
// CHECK: fir.do_loop{{.*}}unordered
// CHECK: arith.minsi %{{.*}}, %{{.*}} : i32
+
+// The INTEGER(16) identity must not be truncated to 64 bits.
+func.func @test_i128_mask(%input: !hlfir.expr<?xi128>, %mask: !hlfir.expr<?x!fir.logical<4>>) -> i128 {
+ %0 = hlfir.minval %input mask %mask : (!hlfir.expr<?xi128>, !hlfir.expr<?x!fir.logical<4>>) -> i128
+ return %0 : i128
+}
+// CHECK-LABEL: func.func @test_i128_mask(
+// CHECK: arith.constant 170141183460469231731687303715884105727 : i128
+
+func.func @test_i128_var_nomask(%input: !fir.box<!fir.array<?xi128>>) -> i128 {
+ %0 = hlfir.minval %input : (!fir.box<!fir.array<?xi128>>) -> i128
+ return %0 : i128
+}
+// CHECK-LABEL: func.func @test_i128_var_nomask(
+// CHECK: arith.constant 170141183460469231731687303715884105727 : i128
diff --git a/flang/test/Lower/OpenMP/wsloop-reduction-min-max-i128.f90 b/flang/test/Lower/OpenMP/wsloop-reduction-min-max-i128.f90
new file mode 100644
index 0000000000000..84aa104e4cb2a
--- /dev/null
+++ b/flang/test/Lower/OpenMP/wsloop-reduction-min-max-i128.f90
@@ -0,0 +1,23 @@
+! Check that the MIN and MAX reduction identities for INTEGER(16) are the
+! full 128-bit limits rather than values truncated to 64 bits.
+
+! RUN: bbc -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+! CHECK-LABEL: omp.declare_reduction @max_i128 : i128 init {
+! CHECK: %[[MIN:.*]] = arith.constant -170141183460469231731687303715884105728 : i128
+! CHECK: omp.yield(%[[MIN]] : i128)
+
+! CHECK-LABEL: omp.declare_reduction @min_i128 : i128 init {
+! CHECK: %[[MAX:.*]] = arith.constant 170141183460469231731687303715884105727 : i128
+! CHECK: omp.yield(%[[MAX]] : i128)
+
+subroutine reduce_i128(a, n, r, q)
+ integer :: n, i
+ integer(16) :: a(n), r, q
+ !$omp parallel do reduction(min:r) reduction(max:q)
+ do i = 1, n
+ r = min(r, a(i))
+ q = max(q, a(i))
+ end do
+end subroutine
diff --git a/flang/test/Transforms/simplifyintrinsics.fir b/flang/test/Transforms/simplifyintrinsics.fir
index e9e4264187d8b..bbb29a2e69492 100644
--- a/flang/test/Transforms/simplifyintrinsics.fir
+++ b/flang/test/Transforms/simplifyintrinsics.fir
@@ -2590,3 +2590,30 @@ func.func @_QPtestmaxloc_works1d_scalarmask_f64(%arg0: !fir.ref<!fir.array<10xf6
// CHECK: fir.store %[[BOX_OUTARR]] to %[[REF_BOX_OUTARR]] : !fir.ref<!fir.box<!fir.heap<!fir.array<1xi32>>>>
// CHECK: return
// CHECK: }
+
+// -----
+
+// Call to MAXVAL with 1D I128 array is replaced, and the INTEGER(16) identity
+// is not truncated to 64 bits.
+module attributes {fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.target_triple = "native"} {
+ func.func @maxval_1d_array_i128(%arg0: !fir.ref<!fir.array<10xi128>>) -> i128 {
+ %c10 = arith.constant 10 : index
+ %1 = fir.shape %c10 : (index) -> !fir.shape<1>
+ %2 = fir.embox %arg0(%1) : (!fir.ref<!fir.array<10xi128>>, !fir.shape<1>) -> !fir.box<!fir.array<10xi128>>
+ %3 = fir.absent !fir.box<i1>
+ %c0 = arith.constant 0 : index
+ %4 = fir.zero_bits !fir.ref<i8>
+ %c5_i32 = arith.constant 5 : i32
+ %5 = fir.convert %2 : (!fir.box<!fir.array<10xi128>>) -> !fir.box<none>
+ %6 = fir.convert %c0 : (index) -> i32
+ %7 = fir.convert %3 : (!fir.box<i1>) -> !fir.box<none>
+ %8 = fir.call @_FortranAMaxvalInteger16(%5, %4, %c5_i32, %6, %7) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i128
+ return %8 : i128
+ }
+ func.func private @_FortranAMaxvalInteger16(!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i128 attributes {fir.runtime}
+}
+
+// CHECK-LABEL: func.func @maxval_1d_array_i128(
+// CHECK: fir.call @_FortranAMaxvalInteger16x1_simplified(
+// CHECK-LABEL: func.func private @_FortranAMaxvalInteger16x1_simplified(
+// CHECK: arith.constant -170141183460469231731687303715884105728 : i128
``````````
</details>
https://github.com/llvm/llvm-project/pull/228513
More information about the flang-commits
mailing list