diff --git a/flang/include/flang/Lower/IntrinsicCall.h b/flang/include/flang/Lower/IntrinsicCall.h --- a/flang/include/flang/Lower/IntrinsicCall.h +++ b/flang/include/flang/Lower/IntrinsicCall.h @@ -78,6 +78,17 @@ /// Return place-holder for absent intrinsic arguments. fir::ExtendedValue getAbsentIntrinsicArgument(); + +//===----------------------------------------------------------------------===// +// Direct access to intrinsics that may be used by lowering outside +// of intrinsic call lowering. +//===----------------------------------------------------------------------===// + +/// Generate power function x**y with the given expected +/// result type. +mlir::Value genPow(fir::FirOpBuilder &, mlir::Location, mlir::Type resultType, + mlir::Value x, mlir::Value y); + } // namespace Fortran::lower #endif // FORTRAN_LOWER_INTRINSICCALL_H diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp --- a/flang/lib/Lower/ConvertExpr.cpp +++ b/flang/lib/Lower/ConvertExpr.cpp @@ -532,14 +532,20 @@ template ExtValue genval( const Fortran::evaluate::Power> &op) { - TODO(getLoc(), "genval Power"); + mlir::Type ty = converter.genType(TC, KIND); + mlir::Value lhs = genunbox(op.left()); + mlir::Value rhs = genunbox(op.right()); + return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); } template ExtValue genval( const Fortran::evaluate::RealToIntPower> &op) { - TODO(getLoc(), "genval RealToInt"); + mlir::Type ty = converter.genType(TC, KIND); + mlir::Value lhs = genunbox(op.left()); + mlir::Value rhs = genunbox(op.right()); + return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs); } template 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 @@ -148,6 +148,17 @@ // Math runtime description and matching utility //===----------------------------------------------------------------------===// +/// Command line option to modify math runtime version used to implement +/// intrinsics. +enum MathRuntimeVersion { fastVersion, llvmOnly }; +llvm::cl::opt mathRuntimeVersion( + "math-runtime", llvm::cl::desc("Select math runtime version:"), + llvm::cl::values( + clEnumValN(fastVersion, "fast", "use pgmath fast runtime"), + clEnumValN(llvmOnly, "llvm", + "only use LLVM intrinsics (may be incomplete)")), + llvm::cl::init(fastVersion)); + struct RuntimeFunction { // llvm::StringRef comparison operator are not constexpr, so use string_view. using Key = std::string_view; @@ -176,12 +187,24 @@ return mlir::FunctionType::get(context, {t}, {t}); } +static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) { + auto t = mlir::FloatType::getF32(context); + return mlir::FunctionType::get(context, {t, t}, {t}); +} + +static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) { + auto t = mlir::FloatType::getF64(context); + return mlir::FunctionType::get(context, {t, t}, {t}); +} + // TODO : Fill-up this table with more intrinsic. // Note: These are also defined as operations in LLVM dialect. See if this // can be use and has advantages. static constexpr RuntimeFunction llvmIntrinsics[] = { {"abs", "llvm.fabs.f32", genF32F32FuncType}, {"abs", "llvm.fabs.f64", genF64F64FuncType}, + {"pow", "llvm.pow.f32", genF32F32F32FuncType}, + {"pow", "llvm.pow.f64", genF64F64F64FuncType}, }; // This helper class computes a "distance" between two function types. @@ -373,8 +396,12 @@ using RtMap = Fortran::common::StaticMultimapView; static constexpr RtMap pgmathF(pgmathFast); static_assert(pgmathF.Verify() && "map must be sorted"); - match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, - &bestNearMatch, bestMatchDistance); + if (mathRuntimeVersion == fastVersion) { + match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, + &bestNearMatch, bestMatchDistance); + } else { + assert(mathRuntimeVersion == llvmOnly && "unknown math runtime"); + } if (match) return match; @@ -602,3 +629,9 @@ return IntrinsicLibrary{builder, loc}.genIntrinsicCall(name, resultType, args); } + +mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder, + mlir::Location loc, mlir::Type type, + mlir::Value x, mlir::Value y) { + return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); +} diff --git a/flang/test/Lower/llvm-math.f90 b/flang/test/Lower/llvm-math.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/llvm-math.f90 @@ -0,0 +1,25 @@ +! RUN: bbc -emit-fir %s -o - --math-runtime=llvm | FileCheck %s + + SUBROUTINE POW_WRAPPER(IN, IN2, OUT) + DOUBLE PRECISION IN, IN2 + OUT = IN ** IN2 + RETURN + END + +! CHECK-LABEL: func @_QPpow_wrapper( +! CHECK-SAME: %{{.*}}: !fir.ref{{.*}}, %{{.*}}: !fir.ref{{.*}}, %{{.*}}: !fir.ref{{.*}}) { +! CHECK-NEXT: %0 = fir.load %arg0 : !fir.ref +! CHECK-NEXT: %1 = fir.load %arg1 : !fir.ref +! CHECK-NEXT: %2 = fir.call @llvm.pow.f64(%0, %1) : (f64, f64) -> f64 + + SUBROUTINE POWF_WRAPPER(IN, IN2, OUT) + REAL IN, IN2 + OUT = IN ** IN2 + RETURN + END + +! CHECK-LABEL: func @_QPpowf_wrapper( +! CHECK-SAME: %{{.*}}: !fir.ref{{.*}}, %{{.*}}: !fir.ref{{.*}}, %{{.*}}: !fir.ref{{.*}}) { +! CHECK-NEXT: %0 = fir.load %arg0 : !fir.ref +! CHECK-NEXT: %1 = fir.load %arg1 : !fir.ref +! CHECK-NEXT: %2 = fir.call @llvm.pow.f32(%0, %1) : (f32, f32) -> f32 diff --git a/flang/test/Lower/power-operator.f90 b/flang/test/Lower/power-operator.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/power-operator.f90 @@ -0,0 +1,104 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s + +! Test power operation lowering + +! CHECK-LABEL: pow_r4_i4 +subroutine pow_r4_i4(x, y, z) + real :: x, z + integer :: y + z = x ** y + ! CHECK: call @__fs_powi_1 +end subroutine + +! CHECK-LABEL: pow_r4_r4 +subroutine pow_r4_r4(x, y, z) + real :: x, z, y + z = x ** y + ! CHECK: call @__fs_pow_1 +end subroutine + +! CHECK-LABEL: pow_r4_i8 +subroutine pow_r4_i8(x, y, z) + real :: x, z + integer(8) :: y + z = x ** y + ! CHECK: call @__fs_powk_1 +end subroutine + +! CHECK-LABEL: pow_r8_i4 +subroutine pow_r8_i4(x, y, z) + real(8) :: x, z + integer :: y + z = x ** y + ! CHECK: call @__fd_powi_1 +end subroutine + +! CHECK-LABEL: pow_r8_i8 +subroutine pow_r8_i8(x, y, z) + real(8) :: x, z + integer(8) :: y + z = x ** y + ! CHECK: call @__fd_powk_1 +end subroutine + +! CHECK-LABEL: pow_r8_r8 +subroutine pow_r8_r8(x, y, z) + real(8) :: x, z, y + z = x ** y + ! CHECK: call @__fd_pow_1 +end subroutine + +! CHECK-LABEL: pow_r4_r8 +subroutine pow_r4_r8(x, y, z) + real(4) :: x + real(8) :: z, y + z = x ** y + ! CHECK: %{{.*}} = fir.convert %{{.*}} : (f32) -> f64 + ! CHECK: call @__fd_pow_1 +end subroutine + +! CHECK-LABEL: pow_i4_i4 +subroutine pow_i4_i4(x, y, z) + integer(4) :: x, y, z + z = x ** y + ! CHECK: call @__mth_i_ipowi +end subroutine + +! CHECK-LABEL: pow_i8_i8 +subroutine pow_i8_i8(x, y, z) + integer(8) :: x, y, z + z = x ** y + ! CHECK: call @__mth_i_kpowk +end subroutine + +! CHECK-LABEL: pow_c4_i4 +subroutine pow_c4_i4(x, y, z) + complex :: x, z + integer :: y + z = x ** y + ! CHECK: call @__fc_powi_1 +end subroutine + +! CHECK-LABEL: pow_c4_i8 +subroutine pow_c4_i8(x, y, z) + complex :: x, z + integer(8) :: y + z = x ** y + ! CHECK: call @__fc_powk_1 +end subroutine + +! CHECK-LABEL: pow_c8_i4 +subroutine pow_c8_i4(x, y, z) + complex(8) :: x, z + integer :: y + z = x ** y + ! CHECK: call @__fz_powi_1 +end subroutine + +! CHECK-LABEL: pow_c8_i8 +subroutine pow_c8_i8(x, y, z) + complex(8) :: x, z + integer(8) :: y + z = x ** y + ! CHECK: call @__fz_powk_1 +end subroutine