[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