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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
