[flang] Lower MOD to Fortran runtime call.

This change removes dependency on pgmath mod, and also allows
Fortran runtime to issue a diagnostic message in case of zero
denominator.

Differential Revision: https://reviews.llvm.org/D131192

GitOrigin-RevId: 1b9faafe911ef05c893553cb79cb35db063f1bb3
diff --git a/include/flang/Optimizer/Builder/Runtime/Numeric.h b/include/flang/Optimizer/Builder/Runtime/Numeric.h
index 552df43..c1a7247 100644
--- a/include/flang/Optimizer/Builder/Runtime/Numeric.h
+++ b/include/flang/Optimizer/Builder/Runtime/Numeric.h
@@ -26,6 +26,10 @@
 mlir::Value genFraction(fir::FirOpBuilder &builder, mlir::Location loc,
                         mlir::Value x);
 
+/// Generate call to Mod intrinsic runtime routine.
+mlir::Value genMod(fir::FirOpBuilder &builder, mlir::Location loc,
+                   mlir::Value a, mlir::Value p);
+
 /// Generate call to Nearest intrinsic runtime routine.
 mlir::Value genNearest(fir::FirOpBuilder &builder, mlir::Location loc,
                        mlir::Value x, mlir::Value s);
diff --git a/lib/Lower/IntrinsicCall.cpp b/lib/Lower/IntrinsicCall.cpp
index 5eb527f..2294780 100644
--- a/lib/Lower/IntrinsicCall.cpp
+++ b/lib/Lower/IntrinsicCall.cpp
@@ -3393,11 +3393,9 @@
   if (resultType.isa<mlir::IntegerType>())
     return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]);
 
-  // Use runtime. Note that mlir::arith::RemFOp implements floating point
-  // remainder, but it does not work with fir::Real type.
-  // TODO: consider using mlir::arith::RemFOp when possible, that may help
-  // folding and  optimizations.
-  return genRuntimeCall("mod", resultType, args);
+  // Use runtime.
+  return builder.createConvert(
+      loc, resultType, fir::runtime::genMod(builder, loc, args[0], args[1]));
 }
 
 // MODULO
diff --git a/lib/Optimizer/Builder/Runtime/Numeric.cpp b/lib/Optimizer/Builder/Runtime/Numeric.cpp
index 2f77c3b..cb4035d 100644
--- a/lib/Optimizer/Builder/Runtime/Numeric.cpp
+++ b/lib/Optimizer/Builder/Runtime/Numeric.cpp
@@ -90,6 +90,34 @@
   }
 };
 
+/// Placeholder for real*10 version of Mod Intrinsic
+struct ForcedMod10 {
+  static constexpr const char *name = ExpandAndQuoteKey(RTNAME(ModReal10));
+  static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
+    return [](mlir::MLIRContext *ctx) {
+      auto fltTy = mlir::FloatType::getF80(ctx);
+      auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
+      auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
+      return mlir::FunctionType::get(ctx, {fltTy, fltTy, strTy, intTy},
+                                     {fltTy});
+    };
+  }
+};
+
+/// Placeholder for real*16 version of Mod Intrinsic
+struct ForcedMod16 {
+  static constexpr const char *name = ExpandAndQuoteKey(RTNAME(ModReal16));
+  static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
+    return [](mlir::MLIRContext *ctx) {
+      auto fltTy = mlir::FloatType::getF128(ctx);
+      auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
+      auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
+      return mlir::FunctionType::get(ctx, {fltTy, fltTy, strTy, intTy},
+                                     {fltTy});
+    };
+  }
+};
+
 /// Placeholder for real*10 version of Nearest Intrinsic
 struct ForcedNearest10 {
   static constexpr const char *name = ExpandAndQuoteKey(RTNAME(Nearest10));
@@ -270,6 +298,38 @@
   return builder.create<fir::CallOp>(loc, func, args).getResult(0);
 }
 
+/// Generate call to Mod intrinsic runtime routine.
+mlir::Value fir::runtime::genMod(fir::FirOpBuilder &builder, mlir::Location loc,
+                                 mlir::Value a, mlir::Value p) {
+  mlir::func::FuncOp func;
+  mlir::Type fltTy = a.getType();
+
+  if (fltTy != p.getType())
+    fir::emitFatalError(loc, "arguments type mismatch in MOD");
+
+  if (fltTy.isF16())
+    TODO(loc, "support for REAL with KIND = 2 in MOD");
+  else if (fltTy.isF32())
+    func = fir::runtime::getRuntimeFunc<mkRTKey(ModReal4)>(loc, builder);
+  else if (fltTy.isF64())
+    func = fir::runtime::getRuntimeFunc<mkRTKey(ModReal8)>(loc, builder);
+  else if (fltTy.isF80())
+    func = fir::runtime::getRuntimeFunc<ForcedMod10>(loc, builder);
+  else if (fltTy.isF128())
+    func = fir::runtime::getRuntimeFunc<ForcedMod16>(loc, builder);
+  else
+    fir::emitFatalError(loc, "unsupported REAL kind in MOD");
+
+  auto funcTy = func.getFunctionType();
+  auto sourceFile = fir::factory::locationToFilename(builder, loc);
+  auto sourceLine =
+      fir::factory::locationToLineNo(builder, loc, funcTy.getInput(3));
+  auto args = fir::runtime::createArguments(builder, loc, funcTy, a, p,
+                                            sourceFile, sourceLine);
+
+  return builder.create<fir::CallOp>(loc, func, args).getResult(0);
+}
+
 /// Generate call to Nearest intrinsic runtime routine.
 mlir::Value fir::runtime::genNearest(fir::FirOpBuilder &builder,
                                      mlir::Location loc, mlir::Value x,
diff --git a/test/Lower/Intrinsics/mod.f90 b/test/Lower/Intrinsics/mod.f90
new file mode 100644
index 0000000..117d78c
--- /dev/null
+++ b/test/Lower/Intrinsics/mod.f90
@@ -0,0 +1,54 @@
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+
+! CHECK-LABEL: func @_QPmod_testr4(
+! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f32>{{.*}}, %[[arg1:.*]]: !fir.ref<f32>{{.*}}, %[[arg2:.*]]: !fir.ref<f32>{{.*}}) {
+subroutine mod_testr4(r, a, p)
+  real(4) :: r, a, p
+! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref<f32>
+! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref<f32>
+! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32
+! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+! CHECK: fir.call @_FortranAModReal4(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f32, f32, !fir.ref<i8>, i32) -> f32
+  r = mod(a, p)
+end subroutine
+
+! CHECK-LABEL: func @_QPmod_testr8(
+! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f64>{{.*}}, %[[arg1:.*]]: !fir.ref<f64>{{.*}}, %[[arg2:.*]]: !fir.ref<f64>{{.*}}) {
+subroutine mod_testr8(r, a, p)
+  real(8) :: r, a, p
+! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref<f64>
+! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref<f64>
+! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32
+! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+! CHECK: fir.call @_FortranAModReal8(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f64, f64, !fir.ref<i8>, i32) -> f64
+  r = mod(a, p)
+end subroutine
+
+! CHECK-LABEL: func @_QPmod_testr10(
+! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f80>{{.*}}, %[[arg1:.*]]: !fir.ref<f80>{{.*}}, %[[arg2:.*]]: !fir.ref<f80>{{.*}}) {
+subroutine mod_testr10(r, a, p)
+  real(10) :: r, a, p
+! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref<f80>
+! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref<f80>
+! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32
+! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+! CHECK: fir.call @_FortranAModReal10(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f80, f80, !fir.ref<i8>, i32) -> f80
+  r = mod(a, p)
+end subroutine
+
+! CHECK-LABEL: func @_QPmod_testr16(
+! CHECK-SAME: %[[arg0:.*]]: !fir.ref<f128>{{.*}}, %[[arg1:.*]]: !fir.ref<f128>{{.*}}, %[[arg2:.*]]: !fir.ref<f128>{{.*}}) {
+subroutine mod_testr16(r, a, p)
+  real(16) :: r, a, p
+! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref<f128>
+! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref<f128>
+! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1,{{.*}}>>
+! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32
+! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref<!fir.char<1,{{.*}}>>) -> !fir.ref<i8>
+! CHECK: fir.call @_FortranAModReal16(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f128, f128, !fir.ref<i8>, i32) -> f128
+  r = mod(a, p)
+end subroutine