[flang-commits] [clang] [flang] [flang] Diagnose integer MOD/MODULO when the divisor is zero (PR #222790)
via flang-commits
flang-commits at lists.llvm.org
Tue Sep 15 11:24:14 PDT 2026
https://github.com/junfengd-nv updated https://github.com/llvm/llvm-project/pull/222790
>From 2159a1b1068f3d9e9a771092f45e97224493ea91 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Thu, 10 Sep 2026 14:25:57 -0700
Subject: [PATCH 1/3] [flang] Diagnose integer MOD/MODULO when the divisor is
zero
Inlined integer remainder is undefined for a zero second argument, so
direct calls and unrestricted-intrinsic wrappers could yield a garbage
value. Emit a P==0 check that reports the same fatal error as the
runtime IntMod, and keep the inlined rem when P is a known nonzero
constant.
---
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 27 +++++++++++++--
.../forall-pointer-assignment-scheduling.f90 | 2 ++
flang/test/Lower/Intrinsics/mod.f90 | 34 +++++++++++++++++++
flang/test/Lower/dummy-procedure.f90 | 21 ++++++++++++
4 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index ddd083ef029e8..c4c47fc4b8c52 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -6916,6 +6916,26 @@ static mlir::Value genFastMod(fir::FirOpBuilder &builder, mlir::Location loc,
return subResult;
}
+/// A zero divisor makes the inlined integer remainder undefined. Guard it
+/// with a test that reports the same fatal error as the runtime IntMod.
+/// A divisor known to be nonzero needs no test.
+static void genIntegerZeroDivisorCheck(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Value p,
+ bool isModulo) {
+ if (std::optional<llvm::APInt> constantP = fir::getIntIfConstant(p))
+ if (!constantP->isZero())
+ return;
+ mlir::Value zero = builder.createIntegerConstant(loc, p.getType(), 0);
+ mlir::Value isZero = mlir::arith::CmpIOp::create(
+ builder, loc, mlir::arith::CmpIPredicate::eq, p, zero);
+ builder.genIfThen(loc, isZero)
+ .genThen([&]() {
+ fir::runtime::genReportFatalUserError(
+ builder, loc, isModulo ? "MODULO with P==0" : "MOD with P==0");
+ })
+ .end();
+}
+
mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args) {
auto mod = builder.getModule();
@@ -6931,8 +6951,10 @@ mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
return builder.createUnsigned<mlir::arith::RemUIOp>(loc, signlessType,
args[0], args[1]);
}
- if (mlir::isa<mlir::IntegerType>(resultType))
+ if (mlir::isa<mlir::IntegerType>(resultType)) {
+ genIntegerZeroDivisorCheck(builder, loc, args[1], /*isModulo=*/false);
return mlir::arith::RemSIOp::create(builder, loc, args[0], args[1]);
+ }
if (resultType.isFloat() && useFastRealMod) {
// Treat MOD as an approximate function and code-gen inline code
@@ -6949,8 +6971,6 @@ mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType,
// MODULO
mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args) {
- // TODO: we'd better generate a runtime call here, when runtime error
- // checking is needed (to detect 0 divisor) or when precise math is requested.
assert(args.size() == 2);
// No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR.
// In the meantime, use a simple inlined implementation based on truncated
@@ -6968,6 +6988,7 @@ mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType,
args[0], args[1]);
}
if (mlir::isa<mlir::IntegerType>(resultType)) {
+ genIntegerZeroDivisorCheck(builder, loc, args[1], /*isModulo=*/true);
auto remainder =
mlir::arith::RemSIOp::create(builder, loc, args[0], args[1]);
auto argXor = mlir::arith::XOrIOp::create(builder, loc, args[0], args[1]);
diff --git a/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90 b/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
index 73b147a25048f..7b6d0b74f5a5d 100644
--- a/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
+++ b/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
@@ -80,6 +80,8 @@ subroutine test_need_to_save_lhs_and_rhs(n, a)
forall(i=1:n) a(a(n+1-i)%p%i)%p => a(modulo(-2*i, n+1))%p
end subroutine
! CHECK: ------------ scheduling forall in _QMforall_pointersPtest_need_to_save_lhs_and_rhs ------------
+! CHECK-NEXT: unknown effect: fir.call @_FortranAReportFatalUserError
+! CHECK-NEXT: unknown effect: fir.call @_FortranAReportFatalUserError
! CHECK-NEXT: conflict: R/W
! CHECK-NEXT: run 1 save : forall/region_assign1/rhs
! CHECK-NEXT: conflict: R/W
diff --git a/flang/test/Lower/Intrinsics/mod.f90 b/flang/test/Lower/Intrinsics/mod.f90
index 5bc81d923b800..83952b12c6e1a 100644
--- a/flang/test/Lower/Intrinsics/mod.f90
+++ b/flang/test/Lower/Intrinsics/mod.f90
@@ -36,3 +36,37 @@ subroutine mod_testr16(r, a, p)
! CHECK-KIND16: fir.call @_FortranAModReal16(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {{.*}}: (f128, f128, !fir.ref<i8>, i32) -> f128
r = mod(a, p)
end subroutine
+
+! A divisor that is not a known nonzero constant is tested, and a fatal
+! error is reported instead of the inlined remainder yielding an undefined value.
+! CHECK-LABEL: func @_QPmod_testi4(
+subroutine mod_testi4(r, a, p)
+ integer(4) :: r, a, p
+! CHECK: %[[A:.*]] = fir.declare{{.*}}a"
+! CHECK: %[[P:.*]] = fir.declare{{.*}}p"
+! CHECK: %[[A_LOAD:.*]] = fir.load %[[A]]
+! CHECK: %[[P_LOAD:.*]] = fir.load %[[P]]
+! CHECK: %[[ISZERO:.*]] = arith.cmpi eq, %[[P_LOAD]], %c0{{.*}} : i32
+! CHECK: fir.if %[[ISZERO]] {
+! CHECK: fir.call @_FortranAReportFatalUserError
+! CHECK: }
+! CHECK: arith.remsi %[[A_LOAD]], %[[P_LOAD]] : i32
+ r = mod(a, p)
+end subroutine
+
+! CHECK-LABEL: func @_QPmod_testi8(
+subroutine mod_testi8(r, a, p)
+ integer(8) :: r, a, p
+! CHECK: fir.call @_FortranAReportFatalUserError
+! CHECK: arith.remsi %{{.*}}, %{{.*}} : i64
+ r = mod(a, p)
+end subroutine
+
+! A constant nonzero divisor keeps the inlined remainder with no test.
+! CHECK-LABEL: func @_QPmod_testi4_constant(
+subroutine mod_testi4_constant(r, a)
+ integer(4) :: r, a
+! CHECK-NOT: fir.call @_FortranAReportFatalUserError
+! CHECK: arith.remsi %{{.*}}, %c8{{.*}} : i32
+ r = mod(a, 8)
+end subroutine
diff --git a/flang/test/Lower/dummy-procedure.f90 b/flang/test/Lower/dummy-procedure.f90
index 0359897258d2f..79042f97cce9c 100644
--- a/flang/test/Lower/dummy-procedure.f90
+++ b/flang/test/Lower/dummy-procedure.f90
@@ -113,6 +113,16 @@ subroutine test_atan2()
call foo_atan2(atan2)
end subroutine
+! Integer MOD wrapper must diagnose a zero divisor (direct and dummy-proc uses).
+! CHECK-LABEL: func.func @_QPtest_mod
+subroutine test_mod()
+ intrinsic :: mod
+ ! CHECK: %[[f:.*]] = fir.address_of(@fir.mod.i32.ref_i32.ref_i32) : (!fir.ref<i32>, !fir.ref<i32>) -> i32
+ ! CHECK: %[[fcast:.*]] = fir.emboxproc %[[f]] : ((!fir.ref<i32>, !fir.ref<i32>) -> i32) -> !fir.boxproc<() -> ()>
+ ! CHECK: fir.call @_QPfoo_mod(%[[fcast]]) {{.*}}: (!fir.boxproc<() -> ()>) -> ()
+ call foo_mod(mod)
+end subroutine
+
! Intrinsic implemented inlined
! CHECK-LABEL: func.func @_QPtest_aimag
subroutine test_aimag()
@@ -166,6 +176,17 @@ subroutine todo3(dummy_proc)
! CHECK: %[[atan2:.*]] = math.atan2 %[[xload]], %[[yload]] fastmath<contract> : f32
! CHECK: return %[[atan2]] : f32
+! CHECK-LABEL: func.func private @fir.mod.i32.ref_i32.ref_i32(
+! CHECK-SAME: %[[a:.*]]: !fir.ref<i32>, %[[p:.*]]: !fir.ref<i32>) -> i32
+ ! CHECK-DAG: %[[aload:.*]] = fir.load %[[a]] : !fir.ref<i32>
+ ! CHECK-DAG: %[[pload:.*]] = fir.load %[[p]] : !fir.ref<i32>
+ ! CHECK: %[[iszero:.*]] = arith.cmpi eq, %[[pload]], %c0{{.*}} : i32
+ ! CHECK: fir.if %[[iszero]] {
+ ! CHECK: fir.call @_FortranAReportFatalUserError
+ ! CHECK: }
+ ! CHECK: %[[res:.*]] = arith.remsi %[[aload]], %[[pload]] : i32
+ ! CHECK: return %[[res]] : i32
+
!CHECK-LABEL: func.func private @fir.aimag.f32.ref_z32(%arg0: !fir.ref<complex<f32>>)
!CHECK: %[[load:.*]] = fir.load %arg0
!CHECK: %[[imag:.*]] = fir.extract_value %[[load]], [1 : index] : (complex<f32>) -> f32
>From 6567ca64ce348ea5ba1c7fdf93c771f97e180995 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Mon, 14 Sep 2026 12:39:05 -0700
Subject: [PATCH 2/3] [flang] Gate integer MOD/MODULO zero checks behind
-fcheck-integer-mod-zero
Keep the default inlined remainder, and only insert the P==0 fatal check
when the new opt-in flag is set so the extra branch is not paid always.
---
clang/include/clang/Options/FlangOptions.td | 4 ++
clang/lib/Driver/ToolChains/Flang.cpp | 4 ++
flang/include/flang/Support/LangOptions.def | 2 +
flang/lib/Frontend/CompilerInvocation.cpp | 3 ++
flang/lib/Frontend/FrontendActions.cpp | 8 ++++
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 5 +++
flang/test/Driver/check-integer-mod-zero.f90 | 12 ++++++
.../forall-pointer-assignment-scheduling.f90 | 2 -
flang/test/Lower/Intrinsics/mod.f90 | 38 ++++++++++---------
flang/test/Lower/Intrinsics/modulo.f90 | 7 ++++
flang/test/Lower/dummy-procedure.f90 | 7 +++-
11 files changed, 72 insertions(+), 20 deletions(-)
create mode 100644 flang/test/Driver/check-integer-mod-zero.f90
diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index 8fd0d78a2659c..2511bad109e29 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -315,6 +315,10 @@ def ffast_real_mod : Flag<["-"], "ffast-real-mod">, Group<f_Group>,
def fno_fast_real_mod : Flag<["-"], "fno-fast-real-mod">, Group<f_Group>,
HelpText<"Disable optimization of MOD for REAL types in presence of -ffast-math">;
+def fcheck_integer_mod_zero
+ : Flag<["-"], "fcheck-integer-mod-zero">, Group<f_Group>,
+ HelpText<"Check for a zero divisor in integer MOD and MODULO">;
+
defm fp_sum_reassociation
: BoolOptionWithoutMarshalling<
"f", "fp-sum-reassociation",
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 0e6858d3bb645..a0517f9343414 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1367,6 +1367,10 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
// not skipped by the -ffast-math fast path in addFloatingPointOptions().
addIEEEFPModesOptions(D, Args, CmdArgs, Triple);
+ // Integer MOD/MODULO zero-divisor check. Forwarded here with -ffpe-trap=
+ // rather than in addFloatingPointOptions() so -ffast-math does not drop it.
+ Args.AddLastArg(CmdArgs, options::OPT_fcheck_integer_mod_zero);
+
// Add target args, features, etc.
addTargetOptions(Args, CmdArgs, JA.getOffloadingArch(),
JA.getOffloadingDeviceKind());
diff --git a/flang/include/flang/Support/LangOptions.def b/flang/include/flang/Support/LangOptions.def
index 80d2302cc8c72..ecdf4518ea1fe 100644
--- a/flang/include/flang/Support/LangOptions.def
+++ b/flang/include/flang/Support/LangOptions.def
@@ -69,6 +69,8 @@ LANGOPT(OpenMPSimd, 1, false)
LANGOPT(NoReallocateLHS, 1, false)
/// Enable fast MOD operations for REAL
LANGOPT(FastRealMod, 1, false)
+/// Check for a zero divisor in integer MOD and MODULO.
+LANGOPT(CheckIntegerModZero, 1, false)
/// Bitmask of floating-point exceptions to trap on (from -ffpe-trap=)
LANGOPT(FPExceptionTraps, 8, 0)
LANGOPT(VScaleMin, 32, 0) ///< Minimum vscale range value
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index b76b38ff6497f..dd8a0f6ee2fc6 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1586,6 +1586,9 @@ static bool parseFloatingPointArgs(CompilerInvocation &invoc,
opts.FastRealMod = false;
}
+ if (args.getLastArg(clang::options::OPT_fcheck_integer_mod_zero))
+ opts.CheckIntegerModZero = true;
+
// Set the initial IEEE floating point modes
setIEEEFPModesArgs(opts, args);
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index da3c1de7e9903..76122094c2994 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -292,6 +292,14 @@ bool CodeGenAction::beginSourceFileAction() {
mlir::BoolAttr::get(mod.getContext(), true));
}
+ if (ci.getInvocation().getLangOpts().CheckIntegerModZero) {
+ mlir::ModuleOp mod = lb.getModule();
+ mod.getOperation()->setAttr(
+ mlir::StringAttr::get(mod.getContext(),
+ llvm::Twine{"fir.check_integer_mod_zero"}),
+ mlir::BoolAttr::get(mod.getContext(), true));
+ }
+
// Create a parse tree and lower it to FIR
parseAndLowerTree(ci, lb);
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index c4c47fc4b8c52..2d40a86d9d91a 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -6922,6 +6922,11 @@ static mlir::Value genFastMod(fir::FirOpBuilder &builder, mlir::Location loc,
static void genIntegerZeroDivisorCheck(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value p,
bool isModulo) {
+ mlir::ModuleOp mod = builder.getModule();
+ auto checkEnabled =
+ mod->getAttrOfType<mlir::BoolAttr>("fir.check_integer_mod_zero");
+ if (!checkEnabled || !checkEnabled.getValue())
+ return;
if (std::optional<llvm::APInt> constantP = fir::getIntIfConstant(p))
if (!constantP->isZero())
return;
diff --git a/flang/test/Driver/check-integer-mod-zero.f90 b/flang/test/Driver/check-integer-mod-zero.f90
new file mode 100644
index 0000000000000..5f695b962d28f
--- /dev/null
+++ b/flang/test/Driver/check-integer-mod-zero.f90
@@ -0,0 +1,12 @@
+! Test driver handling of -fcheck-integer-mod-zero.
+
+! RUN: %flang -fcheck-integer-mod-zero -### %s 2>&1 \
+! RUN: | FileCheck %s --check-prefix=ENABLE
+! ENABLE: "-fc1"
+! ENABLE-SAME: "-fcheck-integer-mod-zero"
+
+! RUN: %flang -### %s 2>&1 | FileCheck %s --check-prefix=DEFAULT
+! DEFAULT: "-fc1"
+! DEFAULT-NOT: "check-integer-mod-zero"
+
+end
diff --git a/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90 b/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
index 7b6d0b74f5a5d..73b147a25048f 100644
--- a/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
+++ b/flang/test/HLFIR/order_assignments/forall-pointer-assignment-scheduling.f90
@@ -80,8 +80,6 @@ subroutine test_need_to_save_lhs_and_rhs(n, a)
forall(i=1:n) a(a(n+1-i)%p%i)%p => a(modulo(-2*i, n+1))%p
end subroutine
! CHECK: ------------ scheduling forall in _QMforall_pointersPtest_need_to_save_lhs_and_rhs ------------
-! CHECK-NEXT: unknown effect: fir.call @_FortranAReportFatalUserError
-! CHECK-NEXT: unknown effect: fir.call @_FortranAReportFatalUserError
! CHECK-NEXT: conflict: R/W
! CHECK-NEXT: run 1 save : forall/region_assign1/rhs
! CHECK-NEXT: conflict: R/W
diff --git a/flang/test/Lower/Intrinsics/mod.f90 b/flang/test/Lower/Intrinsics/mod.f90
index 83952b12c6e1a..a88df834a2894 100644
--- a/flang/test/Lower/Intrinsics/mod.f90
+++ b/flang/test/Lower/Intrinsics/mod.f90
@@ -1,4 +1,5 @@
! RUN: bbc -emit-fir %s -o - | FileCheck %s --check-prefixes=CHECK%if target=x86_64{{.*}} %{,CHECK-KIND10%}%if flang-supports-f128-math %{,CHECK-KIND16%}
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s --check-prefix=CHECK-MOD-ZERO
! CHECK-LABEL: func @_QPmod_testr4(
subroutine mod_testr4(r, a, p)
@@ -37,36 +38,39 @@ subroutine mod_testr16(r, a, p)
r = mod(a, p)
end subroutine
-! A divisor that is not a known nonzero constant is tested, and a fatal
-! error is reported instead of the inlined remainder yielding an undefined value.
+! By default, integer MOD remains an unchecked inlined remainder.
! CHECK-LABEL: func @_QPmod_testi4(
+! CHECK-NOT: fir.call @_FortranAReportFatalUserError
+! CHECK: arith.remsi %{{.*}}, %{{.*}} : i32
+
+! With -fcheck-integer-mod-zero, a divisor that is not a known nonzero
+! constant is tested and a fatal error is reported.
+! CHECK-MOD-ZERO-LABEL: func @_QPmod_testi4(
subroutine mod_testi4(r, a, p)
integer(4) :: r, a, p
-! CHECK: %[[A:.*]] = fir.declare{{.*}}a"
-! CHECK: %[[P:.*]] = fir.declare{{.*}}p"
-! CHECK: %[[A_LOAD:.*]] = fir.load %[[A]]
-! CHECK: %[[P_LOAD:.*]] = fir.load %[[P]]
-! CHECK: %[[ISZERO:.*]] = arith.cmpi eq, %[[P_LOAD]], %c0{{.*}} : i32
-! CHECK: fir.if %[[ISZERO]] {
-! CHECK: fir.call @_FortranAReportFatalUserError
-! CHECK: }
-! CHECK: arith.remsi %[[A_LOAD]], %[[P_LOAD]] : i32
+! CHECK-MOD-ZERO: %[[A:.*]] = fir.load %{{.*}} : !fir.ref<i32>
+! CHECK-MOD-ZERO: %[[P:.*]] = fir.load %{{.*}} : !fir.ref<i32>
+! CHECK-MOD-ZERO: %[[ISZERO:.*]] = arith.cmpi eq, %[[P]], %c0{{.*}} : i32
+! CHECK-MOD-ZERO: fir.if %[[ISZERO]] {
+! CHECK-MOD-ZERO: fir.call @_FortranAReportFatalUserError
+! CHECK-MOD-ZERO: }
+! CHECK-MOD-ZERO: arith.remsi %[[A]], %[[P]] : i32
r = mod(a, p)
end subroutine
-! CHECK-LABEL: func @_QPmod_testi8(
+! CHECK-MOD-ZERO-LABEL: func @_QPmod_testi8(
subroutine mod_testi8(r, a, p)
integer(8) :: r, a, p
-! CHECK: fir.call @_FortranAReportFatalUserError
-! CHECK: arith.remsi %{{.*}}, %{{.*}} : i64
+! CHECK-MOD-ZERO: fir.call @_FortranAReportFatalUserError
+! CHECK-MOD-ZERO: arith.remsi %{{.*}}, %{{.*}} : i64
r = mod(a, p)
end subroutine
! A constant nonzero divisor keeps the inlined remainder with no test.
-! CHECK-LABEL: func @_QPmod_testi4_constant(
+! CHECK-MOD-ZERO-LABEL: func @_QPmod_testi4_constant(
subroutine mod_testi4_constant(r, a)
integer(4) :: r, a
-! CHECK-NOT: fir.call @_FortranAReportFatalUserError
-! CHECK: arith.remsi %{{.*}}, %c8{{.*}} : i32
+! CHECK-MOD-ZERO-NOT: fir.call @_FortranAReportFatalUserError
+! CHECK-MOD-ZERO: arith.remsi %{{.*}}, %c8{{.*}} : i32
r = mod(a, 8)
end subroutine
diff --git a/flang/test/Lower/Intrinsics/modulo.f90 b/flang/test/Lower/Intrinsics/modulo.f90
index 0cb91f3862f20..14458f30f6ebc 100644
--- a/flang/test/Lower/Intrinsics/modulo.f90
+++ b/flang/test/Lower/Intrinsics/modulo.f90
@@ -1,5 +1,6 @@
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s -check-prefixes=HONORINF,ALL
! RUN: %flang_fc1 -menable-no-infs -emit-hlfir %s -o - | FileCheck %s -check-prefixes=CHECK,ALL,%if flang-supports-f128-math %{F128%} %else %{F64%}
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s -check-prefix=CHECK-MOD-ZERO
! ALL-LABEL: func @_QPmodulo_testr(
! ALL-SAME: %[[arg0:.*]]: !fir.ref<f64>{{.*}}, %[[arg1:.*]]: !fir.ref<f64>{{.*}}, %[[arg2:.*]]: !fir.ref<f64>{{.*}}) {
@@ -25,6 +26,7 @@ subroutine modulo_testr(r, a, p)
end subroutine
! ALL-LABEL: func @_QPmodulo_testi(
+! CHECK-MOD-ZERO-LABEL: func @_QPmodulo_testi(
! ALL-SAME: %[[arg0:.*]]: !fir.ref<i64>{{.*}}, %[[arg1:.*]]: !fir.ref<i64>{{.*}}, %[[arg2:.*]]: !fir.ref<i64>{{.*}}) {
subroutine modulo_testi(r, a, p)
integer(8) :: r, a, p
@@ -33,6 +35,11 @@ subroutine modulo_testi(r, a, p)
! ALL: %[[r_decl:.*]]:2 = hlfir.declare %[[arg0]] {{.*}} {uniq_name = "_QFmodulo_testiEr"} : (!fir.ref<i64>, !fir.dscope) -> (!fir.ref<i64>, !fir.ref<i64>)
! ALL-DAG: %[[a:.*]] = fir.load %[[a_decl]]#0 : !fir.ref<i64>
! ALL-DAG: %[[p:.*]] = fir.load %[[p_decl]]#0 : !fir.ref<i64>
+ ! CHECK-MOD-ZERO: arith.cmpi eq, %{{.*}}, %c0{{.*}} : i64
+ ! CHECK-MOD-ZERO: fir.if %{{.*}} {
+ ! CHECK-MOD-ZERO: fir.call @_FortranAReportFatalUserError
+ ! CHECK-MOD-ZERO: }
+ ! CHECK-MOD-ZERO: arith.remsi
! ALL-DAG: %[[rem:.*]] = arith.remsi %[[a]], %[[p]] : i64
! ALL-DAG: %[[argXor:.*]] = arith.xori %[[a]], %[[p]] : i64
! ALL-DAG: %[[signDifferent:.*]] = arith.cmpi slt, %[[argXor]], %c0{{.*}} : i64
diff --git a/flang/test/Lower/dummy-procedure.f90 b/flang/test/Lower/dummy-procedure.f90
index 79042f97cce9c..60fe656d98ad2 100644
--- a/flang/test/Lower/dummy-procedure.f90
+++ b/flang/test/Lower/dummy-procedure.f90
@@ -1,4 +1,5 @@
-! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s --check-prefix=NO-MOD-ZERO
! Test dummy procedures
@@ -187,6 +188,10 @@ subroutine todo3(dummy_proc)
! CHECK: %[[res:.*]] = arith.remsi %[[aload]], %[[pload]] : i32
! CHECK: return %[[res]] : i32
+! NO-MOD-ZERO-LABEL: func.func private @fir.mod.i32.ref_i32.ref_i32(
+ ! NO-MOD-ZERO-NOT: fir.call @_FortranAReportFatalUserError
+ ! NO-MOD-ZERO: arith.remsi
+
!CHECK-LABEL: func.func private @fir.aimag.f32.ref_z32(%arg0: !fir.ref<complex<f32>>)
!CHECK: %[[load:.*]] = fir.load %arg0
!CHECK: %[[imag:.*]] = fir.extract_value %[[load]], [1 : index] : (complex<f32>) -> f32
>From d969105b414451d0f4c7fadda875ef010338b736 Mon Sep 17 00:00:00 2001
From: Junfeng Dong <junfengd at nvidia.com>
Date: Tue, 15 Sep 2026 10:41:36 -0700
Subject: [PATCH 3/3] [flang] Rename -fcheck-integer-mod-zero-divisor and share
the FIR attr name
Use a more specific driver flag and a constexpr StringRef in FIRAttr.h
so the module attribute is not a duplicated magic string.
---
clang/include/clang/Options/FlangOptions.td | 4 ++--
clang/lib/Driver/ToolChains/Flang.cpp | 2 +-
flang/include/flang/Optimizer/Dialect/FIRAttr.h | 5 +++++
flang/include/flang/Support/LangOptions.def | 2 +-
flang/lib/Frontend/CompilerInvocation.cpp | 4 ++--
flang/lib/Frontend/FrontendActions.cpp | 5 +++--
flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 4 ++--
flang/test/Driver/check-integer-mod-zero-divisor.f90 | 12 ++++++++++++
flang/test/Driver/check-integer-mod-zero.f90 | 12 ------------
flang/test/Lower/Intrinsics/mod.f90 | 4 ++--
flang/test/Lower/Intrinsics/modulo.f90 | 2 +-
flang/test/Lower/dummy-procedure.f90 | 2 +-
12 files changed, 32 insertions(+), 26 deletions(-)
create mode 100644 flang/test/Driver/check-integer-mod-zero-divisor.f90
delete mode 100644 flang/test/Driver/check-integer-mod-zero.f90
diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index 2511bad109e29..a456c3c888e28 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -315,8 +315,8 @@ def ffast_real_mod : Flag<["-"], "ffast-real-mod">, Group<f_Group>,
def fno_fast_real_mod : Flag<["-"], "fno-fast-real-mod">, Group<f_Group>,
HelpText<"Disable optimization of MOD for REAL types in presence of -ffast-math">;
-def fcheck_integer_mod_zero
- : Flag<["-"], "fcheck-integer-mod-zero">, Group<f_Group>,
+def fcheck_integer_mod_zero_divisor
+ : Flag<["-"], "fcheck-integer-mod-zero-divisor">, Group<f_Group>,
HelpText<"Check for a zero divisor in integer MOD and MODULO">;
defm fp_sum_reassociation
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index a0517f9343414..cc5d136d98dbf 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -1369,7 +1369,7 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
// Integer MOD/MODULO zero-divisor check. Forwarded here with -ffpe-trap=
// rather than in addFloatingPointOptions() so -ffast-math does not drop it.
- Args.AddLastArg(CmdArgs, options::OPT_fcheck_integer_mod_zero);
+ Args.AddLastArg(CmdArgs, options::OPT_fcheck_integer_mod_zero_divisor);
// Add target args, features, etc.
addTargetOptions(Args, CmdArgs, JA.getOffloadingArch(),
diff --git a/flang/include/flang/Optimizer/Dialect/FIRAttr.h b/flang/include/flang/Optimizer/Dialect/FIRAttr.h
index 7c071e5d391e6..d92516fed8a3b 100644
--- a/flang/include/flang/Optimizer/Dialect/FIRAttr.h
+++ b/flang/include/flang/Optimizer/Dialect/FIRAttr.h
@@ -161,6 +161,11 @@ class RealAttr
llvm::APFloat getValue() const;
};
+/// Module attribute set when -fcheck-integer-mod-zero-divisor is enabled.
+static constexpr llvm::StringRef getCheckIntegerModZeroDivisorAttrName() {
+ return "fir.check_integer_mod_zero_divisor";
+}
+
mlir::Attribute parseFirAttribute(FIROpsDialect *dialect,
mlir::DialectAsmParser &parser,
mlir::Type type);
diff --git a/flang/include/flang/Support/LangOptions.def b/flang/include/flang/Support/LangOptions.def
index ecdf4518ea1fe..09988ec3214d0 100644
--- a/flang/include/flang/Support/LangOptions.def
+++ b/flang/include/flang/Support/LangOptions.def
@@ -70,7 +70,7 @@ LANGOPT(NoReallocateLHS, 1, false)
/// Enable fast MOD operations for REAL
LANGOPT(FastRealMod, 1, false)
/// Check for a zero divisor in integer MOD and MODULO.
-LANGOPT(CheckIntegerModZero, 1, false)
+LANGOPT(CheckIntegerModZeroDivisor, 1, false)
/// Bitmask of floating-point exceptions to trap on (from -ffpe-trap=)
LANGOPT(FPExceptionTraps, 8, 0)
LANGOPT(VScaleMin, 32, 0) ///< Minimum vscale range value
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index dd8a0f6ee2fc6..231f490457459 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1586,8 +1586,8 @@ static bool parseFloatingPointArgs(CompilerInvocation &invoc,
opts.FastRealMod = false;
}
- if (args.getLastArg(clang::options::OPT_fcheck_integer_mod_zero))
- opts.CheckIntegerModZero = true;
+ if (args.getLastArg(clang::options::OPT_fcheck_integer_mod_zero_divisor))
+ opts.CheckIntegerModZeroDivisor = true;
// Set the initial IEEE floating point modes
setIEEEFPModesArgs(opts, args);
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 76122094c2994..93177b2fb3473 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -17,6 +17,7 @@
#include "flang/Frontend/ParserActions.h"
#include "flang/Lower/Bridge.h"
#include "flang/Lower/Support/Verifier.h"
+#include "flang/Optimizer/Dialect/FIRAttr.h"
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
#include "flang/Optimizer/Dialect/Support/KindMapping.h"
#include "flang/Optimizer/Passes/Pipelines.h"
@@ -292,11 +293,11 @@ bool CodeGenAction::beginSourceFileAction() {
mlir::BoolAttr::get(mod.getContext(), true));
}
- if (ci.getInvocation().getLangOpts().CheckIntegerModZero) {
+ if (ci.getInvocation().getLangOpts().CheckIntegerModZeroDivisor) {
mlir::ModuleOp mod = lb.getModule();
mod.getOperation()->setAttr(
mlir::StringAttr::get(mod.getContext(),
- llvm::Twine{"fir.check_integer_mod_zero"}),
+ fir::getCheckIntegerModZeroDivisorAttrName()),
mlir::BoolAttr::get(mod.getContext(), true));
}
diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
index 2d40a86d9d91a..9408dc213e2e1 100644
--- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
+++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp
@@ -6923,8 +6923,8 @@ static void genIntegerZeroDivisorCheck(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value p,
bool isModulo) {
mlir::ModuleOp mod = builder.getModule();
- auto checkEnabled =
- mod->getAttrOfType<mlir::BoolAttr>("fir.check_integer_mod_zero");
+ auto checkEnabled = mod->getAttrOfType<mlir::BoolAttr>(
+ fir::getCheckIntegerModZeroDivisorAttrName());
if (!checkEnabled || !checkEnabled.getValue())
return;
if (std::optional<llvm::APInt> constantP = fir::getIntIfConstant(p))
diff --git a/flang/test/Driver/check-integer-mod-zero-divisor.f90 b/flang/test/Driver/check-integer-mod-zero-divisor.f90
new file mode 100644
index 0000000000000..6f18085611104
--- /dev/null
+++ b/flang/test/Driver/check-integer-mod-zero-divisor.f90
@@ -0,0 +1,12 @@
+! Test driver handling of -fcheck-integer-mod-zero-divisor.
+
+! RUN: %flang -fcheck-integer-mod-zero-divisor -### %s 2>&1 \
+! RUN: | FileCheck %s --check-prefix=ENABLE
+! ENABLE: "-fc1"
+! ENABLE-SAME: "-fcheck-integer-mod-zero-divisor"
+
+! RUN: %flang -### %s 2>&1 | FileCheck %s --check-prefix=DEFAULT
+! DEFAULT: "-fc1"
+! DEFAULT-NOT: "check-integer-mod-zero-divisor"
+
+end
diff --git a/flang/test/Driver/check-integer-mod-zero.f90 b/flang/test/Driver/check-integer-mod-zero.f90
deleted file mode 100644
index 5f695b962d28f..0000000000000
--- a/flang/test/Driver/check-integer-mod-zero.f90
+++ /dev/null
@@ -1,12 +0,0 @@
-! Test driver handling of -fcheck-integer-mod-zero.
-
-! RUN: %flang -fcheck-integer-mod-zero -### %s 2>&1 \
-! RUN: | FileCheck %s --check-prefix=ENABLE
-! ENABLE: "-fc1"
-! ENABLE-SAME: "-fcheck-integer-mod-zero"
-
-! RUN: %flang -### %s 2>&1 | FileCheck %s --check-prefix=DEFAULT
-! DEFAULT: "-fc1"
-! DEFAULT-NOT: "check-integer-mod-zero"
-
-end
diff --git a/flang/test/Lower/Intrinsics/mod.f90 b/flang/test/Lower/Intrinsics/mod.f90
index a88df834a2894..a78344267fbb6 100644
--- a/flang/test/Lower/Intrinsics/mod.f90
+++ b/flang/test/Lower/Intrinsics/mod.f90
@@ -1,5 +1,5 @@
! RUN: bbc -emit-fir %s -o - | FileCheck %s --check-prefixes=CHECK%if target=x86_64{{.*}} %{,CHECK-KIND10%}%if flang-supports-f128-math %{,CHECK-KIND16%}
-! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s --check-prefix=CHECK-MOD-ZERO
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero-divisor %s -o - | FileCheck %s --check-prefix=CHECK-MOD-ZERO
! CHECK-LABEL: func @_QPmod_testr4(
subroutine mod_testr4(r, a, p)
@@ -43,7 +43,7 @@ subroutine mod_testr16(r, a, p)
! CHECK-NOT: fir.call @_FortranAReportFatalUserError
! CHECK: arith.remsi %{{.*}}, %{{.*}} : i32
-! With -fcheck-integer-mod-zero, a divisor that is not a known nonzero
+! With -fcheck-integer-mod-zero-divisor, a divisor that is not a known nonzero
! constant is tested and a fatal error is reported.
! CHECK-MOD-ZERO-LABEL: func @_QPmod_testi4(
subroutine mod_testi4(r, a, p)
diff --git a/flang/test/Lower/Intrinsics/modulo.f90 b/flang/test/Lower/Intrinsics/modulo.f90
index 14458f30f6ebc..b3f98a895643f 100644
--- a/flang/test/Lower/Intrinsics/modulo.f90
+++ b/flang/test/Lower/Intrinsics/modulo.f90
@@ -1,6 +1,6 @@
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s -check-prefixes=HONORINF,ALL
! RUN: %flang_fc1 -menable-no-infs -emit-hlfir %s -o - | FileCheck %s -check-prefixes=CHECK,ALL,%if flang-supports-f128-math %{F128%} %else %{F64%}
-! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s -check-prefix=CHECK-MOD-ZERO
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero-divisor %s -o - | FileCheck %s -check-prefix=CHECK-MOD-ZERO
! ALL-LABEL: func @_QPmodulo_testr(
! ALL-SAME: %[[arg0:.*]]: !fir.ref<f64>{{.*}}, %[[arg1:.*]]: !fir.ref<f64>{{.*}}, %[[arg2:.*]]: !fir.ref<f64>{{.*}}) {
diff --git a/flang/test/Lower/dummy-procedure.f90 b/flang/test/Lower/dummy-procedure.f90
index 60fe656d98ad2..ad0f35b77088e 100644
--- a/flang/test/Lower/dummy-procedure.f90
+++ b/flang/test/Lower/dummy-procedure.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fcheck-integer-mod-zero-divisor %s -o - | FileCheck %s
! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s --check-prefix=NO-MOD-ZERO
! Test dummy procedures
More information about the flang-commits
mailing list