diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Numeric.h b/flang/include/flang/Optimizer/Builder/Runtime/Numeric.h --- a/flang/include/flang/Optimizer/Builder/Runtime/Numeric.h +++ b/flang/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/flang/lib/Lower/IntrinsicCall.cpp b/flang/lib/Lower/IntrinsicCall.cpp --- a/flang/lib/Lower/IntrinsicCall.cpp +++ b/flang/lib/Lower/IntrinsicCall.cpp @@ -3393,11 +3393,9 @@ if (resultType.isa()) return builder.create(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/flang/lib/Optimizer/Builder/Runtime/Numeric.cpp b/flang/lib/Optimizer/Builder/Runtime/Numeric.cpp --- a/flang/lib/Optimizer/Builder/Runtime/Numeric.cpp +++ b/flang/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(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(loc, builder); + else if (fltTy.isF64()) + func = fir::runtime::getRuntimeFunc(loc, builder); + else if (fltTy.isF80()) + func = fir::runtime::getRuntimeFunc(loc, builder); + else if (fltTy.isF128()) + func = fir::runtime::getRuntimeFunc(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(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/flang/test/Lower/Intrinsics/mod.f90 b/flang/test/Lower/Intrinsics/mod.f90 new file mode 100644 --- /dev/null +++ b/flang/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{{.*}}, %[[arg1:.*]]: !fir.ref{{.*}}, %[[arg2:.*]]: !fir.ref{{.*}}) { +subroutine mod_testr4(r, a, p) + real(4) :: r, a, p +! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref +! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref +! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32 +! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref>) -> !fir.ref +! CHECK: fir.call @_FortranAModReal4(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f32, f32, !fir.ref, i32) -> f32 + r = mod(a, p) +end subroutine + +! CHECK-LABEL: func @_QPmod_testr8( +! CHECK-SAME: %[[arg0:.*]]: !fir.ref{{.*}}, %[[arg1:.*]]: !fir.ref{{.*}}, %[[arg2:.*]]: !fir.ref{{.*}}) { +subroutine mod_testr8(r, a, p) + real(8) :: r, a, p +! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref +! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref +! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32 +! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref>) -> !fir.ref +! CHECK: fir.call @_FortranAModReal8(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f64, f64, !fir.ref, i32) -> f64 + r = mod(a, p) +end subroutine + +! CHECK-LABEL: func @_QPmod_testr10( +! CHECK-SAME: %[[arg0:.*]]: !fir.ref{{.*}}, %[[arg1:.*]]: !fir.ref{{.*}}, %[[arg2:.*]]: !fir.ref{{.*}}) { +subroutine mod_testr10(r, a, p) + real(10) :: r, a, p +! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref +! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref +! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32 +! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref>) -> !fir.ref +! CHECK: fir.call @_FortranAModReal10(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f80, f80, !fir.ref, i32) -> f80 + r = mod(a, p) +end subroutine + +! CHECK-LABEL: func @_QPmod_testr16( +! CHECK-SAME: %[[arg0:.*]]: !fir.ref{{.*}}, %[[arg1:.*]]: !fir.ref{{.*}}, %[[arg2:.*]]: !fir.ref{{.*}}) { +subroutine mod_testr16(r, a, p) + real(16) :: r, a, p +! CHECK: %[[V1:.*]] = fir.load %[[arg1]] : !fir.ref +! CHECK: %[[V2:.*]] = fir.load %[[arg2]] : !fir.ref +! CHECK: %[[FILE:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +! CHECK: %[[LINE:.*]] = arith.constant {{[0-9]*}} : i32 +! CHECK: %[[FILEARG:.*]] = fir.convert %[[FILE]] : (!fir.ref>) -> !fir.ref +! CHECK: fir.call @_FortranAModReal16(%[[V1]], %[[V2]], %[[FILEARG]], %[[LINE]]) : (f128, f128, !fir.ref, i32) -> f128 + r = mod(a, p) +end subroutine