diff --git a/flang/lib/Lower/IntrinsicCall.cpp b/flang/lib/Lower/IntrinsicCall.cpp index 9450d98309bb9..be32c4d04a502 100644 --- a/flang/lib/Lower/IntrinsicCall.cpp +++ b/flang/lib/Lower/IntrinsicCall.cpp @@ -545,6 +545,16 @@ struct IntrinsicLibrary { using Generator = std::variant; + /// All generators can be outlined. This will build a function named + /// "fir."+ + "." + and generate the + /// intrinsic implementation inside instead of at the intrinsic call sites. + /// This can be used to keep the FIR more readable. Only one function will + /// be generated for all the similar calls in a program. + /// If the Generator is nullptr, the wrapper uses genRuntimeCall. + template + mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name, + mlir::Type resultType, + llvm::ArrayRef args); template fir::ExtendedValue outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, @@ -1007,9 +1017,15 @@ static constexpr RuntimeFunction llvmIntrinsics[] = { // ceil is used for CEILING but is different, it returns a real. {"ceil", "llvm.ceil.f32", genF32F32FuncType}, {"ceil", "llvm.ceil.f64", genF64F64FuncType}, + {"exp", "llvm.exp.f32", genF32F32FuncType}, + {"exp", "llvm.exp.f64", genF64F64FuncType}, // llvm.floor is used for FLOOR, but returns real. {"floor", "llvm.floor.f32", genF32F32FuncType}, {"floor", "llvm.floor.f64", genF64F64FuncType}, + {"log", "llvm.log.f32", genF32F32FuncType}, + {"log", "llvm.log.f64", genF64F64FuncType}, + {"log10", "llvm.log10.f32", genF32F32FuncType}, + {"log10", "llvm.log10.f64", genF64F64FuncType}, {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>}, {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>}, {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>}, @@ -1349,6 +1365,8 @@ fir::ExtendedValue IntrinsicLibrary::genElementalCall( scalarArgs.emplace_back(fir::getBase(arg)); else fir::emitFatalError(loc, "nonscalar intrinsic argument"); + if (outline) + return outlineInWrapper(generator, name, resultType, scalarArgs); return invokeGenerator(generator, resultType, scalarArgs); } @@ -1559,6 +1577,12 @@ mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator, } /// Helpers to detect absent optional (not yet supported in outlining). +bool static hasAbsentOptional(llvm::ArrayRef args) { + for (const mlir::Value &arg : args) + if (!arg) + return true; + return false; +} bool static hasAbsentOptional(llvm::ArrayRef args) { for (const fir::ExtendedValue &arg : args) if (!fir::getBase(arg)) @@ -1566,6 +1590,25 @@ bool static hasAbsentOptional(llvm::ArrayRef args) { return false; } +template +mlir::Value +IntrinsicLibrary::outlineInWrapper(GeneratorType generator, + llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args) { + if (hasAbsentOptional(args)) { + // TODO: absent optional in outlining is an issue: we cannot just ignore + // them. Needs a better interface here. The issue is that we cannot easily + // tell that a value is optional or not here if it is presents. And if it is + // absent, we cannot tell what it type should be. + TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + + " with absent optional argument"); + } + + mlir::FunctionType funcType = getFunctionType(resultType, args, builder); + mlir::FuncOp wrapper = getWrapper(generator, name, funcType); + return builder.create(loc, wrapper, args).getResult(0); +} + template fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper( GeneratorType generator, llvm::StringRef name, diff --git a/flang/test/Lower/Intrinsics/exp.f90 b/flang/test/Lower/Intrinsics/exp.f90 new file mode 100644 index 0000000000000..c8778784cbaff --- /dev/null +++ b/flang/test/Lower/Intrinsics/exp.f90 @@ -0,0 +1,62 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s +! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s + +! CHECK-LABEL: exp_testr +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine exp_testr(a, b) + real :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.exp.f32.f32(%[[A]]) : (f32) -> f32 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = exp(a) +end subroutine + +! CHECK-LABEL: exp_testd +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine exp_testd(a, b) + real(kind=8) :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.exp.f64.f64(%[[A]]) : (f64) -> f64 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = exp(a) +end subroutine + +! CHECK-LABEL: exp_testc +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref> {{.*}}, %[[BREF:.*]]: !fir.ref> {{.*}}) +subroutine exp_testc(a, b) + complex :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref> +! CHECK: %[[RES:.*]] = fir.call @fir.exp.z4.z4(%[[A]]) : (!fir.complex<4>) -> !fir.complex<4> +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref> + b = exp(a) +end subroutine + +! CHECK-LABEL: exp_testcd +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref> {{.*}}, %[[BREF:.*]]: !fir.ref> {{.*}}) +subroutine exp_testcd(a, b) + complex(kind=8) :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref> +! CHECK: %[[RES:.*]] = fir.call @fir.exp.z8.z8(%[[A]]) : (!fir.complex<8>) -> !fir.complex<8> +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref> + b = exp(a) +end subroutine + +! CHECK-LABEL: private @fir.exp.f32.f32 +! CHECK-SAME: (%[[ARG32_OUTLINE:.*]]: f32) -> f32 +! CHECK: %[[RESULT32_OUTLINE:.*]] = fir.call @__fs_exp_1(%[[ARG32_OUTLINE]]) : (f32) -> f32 +! CHECK: return %[[RESULT32_OUTLINE]] : f32 + +! CHECK-LABEL: private @fir.exp.f64.f64 +! CHECK-SAME: (%[[ARG64_OUTLINE:.*]]: f64) -> f64 +! CHECK: %[[RESULT64_OUTLINE:.*]] = fir.call @__fd_exp_1(%[[ARG64_OUTLINE]]) : (f64) -> f64 +! CHECK: return %[[RESULT64_OUTLINE]] : f64 + +! CHECK-LABEL: private @fir.exp.z4.z4 +! CHECK-SAME: (%[[ARG32_OUTLINE]]: !fir.complex<4>) -> !fir.complex<4> +! CHECK: %[[RESULT32_OUTLINE]] = fir.call @__fc_exp_1(%[[ARG32_OUTLINE]]) : (!fir.complex<4>) -> !fir.complex<4> +! CHECK: return %[[RESULT32_OUTLINE]] : !fir.complex<4> + +! CHECK-LABEL: private @fir.exp.z8.z8 +! CHECK-SAME: (%[[ARG64_OUTLINE]]: !fir.complex<8>) -> !fir.complex<8> +! CHECK: %[[RESULT64_OUTLINE]] = fir.call @__fz_exp_1(%[[ARG64_OUTLINE]]) : (!fir.complex<8>) -> !fir.complex<8> +! CHECK: return %[[RESULT64_OUTLINE]] : !fir.complex<8> diff --git a/flang/test/Lower/Intrinsics/log.f90 b/flang/test/Lower/Intrinsics/log.f90 new file mode 100644 index 0000000000000..ae5f21ca96f69 --- /dev/null +++ b/flang/test/Lower/Intrinsics/log.f90 @@ -0,0 +1,92 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s +! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s + +! CHECK-LABEL: log_testr +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine log_testr(a, b) + real :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.log.f32.f32(%[[A]]) : (f32) -> f32 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = log(a) +end subroutine + +! CHECK-LABEL: log_testd +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine log_testd(a, b) + real(kind=8) :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.log.f64.f64(%[[A]]) : (f64) -> f64 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = log(a) +end subroutine + +! CHECK-LABEL: log_testc +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref> {{.*}}, %[[BREF:.*]]: !fir.ref> {{.*}}) +subroutine log_testc(a, b) + complex :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref> +! CHECK: %[[RES:.*]] = fir.call @fir.log.z4.z4(%[[A]]) : (!fir.complex<4>) -> !fir.complex<4> +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref> + b = log(a) +end subroutine + +! CHECK-LABEL: log_testcd +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref> {{.*}}, %[[BREF:.*]]: !fir.ref> {{.*}}) +subroutine log_testcd(a, b) + complex(kind=8) :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref> +! CHECK: %[[RES:.*]] = fir.call @fir.log.z8.z8(%[[A]]) : (!fir.complex<8>) -> !fir.complex<8> +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref> + b = log(a) +end subroutine + +! CHECK-LABEL: log10_testr +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine log10_testr(a, b) + real :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.log10.f32.f32(%[[A]]) : (f32) -> f32 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = log10(a) +end subroutine + +! CHECK-LABEL: log10_testd +! CHECK-SAME: (%[[AREF:.*]]: !fir.ref {{.*}}, %[[BREF:.*]]: !fir.ref {{.*}}) +subroutine log10_testd(a, b) + real(kind=8) :: a, b +! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref +! CHECK: %[[RES:.*]] = fir.call @fir.log10.f64.f64(%[[A]]) : (f64) -> f64 +! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref + b = log10(a) +end subroutine + +! CHECK-LABEL: private @fir.log.f32.f32 +! CHECK-SAME: (%[[ARG32_OUTLINE:.*]]: f32) -> f32 +! CHECK: %[[RESULT32_OUTLINE:.*]] = fir.call @__fs_log_1(%[[ARG32_OUTLINE]]) : (f32) -> f32 +! CHECK: return %[[RESULT32_OUTLINE]] : f32 + +! CHECK-LABEL: private @fir.log.f64.f64 +! CHECK-SAME: (%[[ARG64_OUTLINE:.*]]: f64) -> f64 +! CHECK: %[[RESULT64_OUTLINE:.*]] = fir.call @__fd_log_1(%[[ARG64_OUTLINE]]) : (f64) -> f64 +! CHECK: return %[[RESULT64_OUTLINE]] : f64 + +! CHECK-LABEL: private @fir.log.z4.z4 +! CHECK-SAME: (%[[ARG32_OUTLINE]]: !fir.complex<4>) -> !fir.complex<4> +! CHECK: %[[RESULT32_OUTLINE]] = fir.call @__fc_log_1(%[[ARG32_OUTLINE]]) : (!fir.complex<4>) -> !fir.complex<4> +! CHECK: return %[[RESULT32_OUTLINE]] : !fir.complex<4> + +! CHECK-LABEL: private @fir.log.z8.z8 +! CHECK-SAME: (%[[ARG64_OUTLINE]]: !fir.complex<8>) -> !fir.complex<8> +! CHECK: %[[RESULT64_OUTLINE]] = fir.call @__fz_log_1(%[[ARG64_OUTLINE]]) : (!fir.complex<8>) -> !fir.complex<8> +! CHECK: return %[[RESULT64_OUTLINE]] : !fir.complex<8> + +! CHECK-LABEL: private @fir.log10.f32.f32 +! CHECK-SAME: (%[[ARG32_OUTLINE:.*]]: f32) -> f32 +! CHECK: %[[RESULT32_OUTLINE:.*]] = fir.call @__fs_log10_1(%[[ARG32_OUTLINE]]) : (f32) -> f32 +! CHECK: return %[[RESULT32_OUTLINE]] : f32 + +! CHECK-LABEL: private @fir.log10.f64.f64 +! CHECK-SAME: (%[[ARG64_OUTLINE:.*]]: f64) -> f64 +! CHECK: %[[RESULT64_OUTLINE:.*]] = fir.call @__fd_log10_1(%[[ARG64_OUTLINE]]) : (f64) -> f64 +! CHECK: return %[[RESULT64_OUTLINE]] : f64 diff --git a/flang/test/Lower/llvm-math.f90 b/flang/test/Lower/llvm-math.f90 index 661c9b19a845b..fe2c67c20cdb0 100644 --- a/flang/test/Lower/llvm-math.f90 +++ b/flang/test/Lower/llvm-math.f90 @@ -23,3 +23,69 @@ SUBROUTINE POWF_WRAPPER(IN, IN2, OUT) ! 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 + + SUBROUTINE EXP_WRAPPER(IN, OUT) + DOUBLE PRECISION IN + OUT = DEXP(IN) + RETURN + END + +! CHECK: func private @fir.exp.f64.f64(%arg0: f64) +! CHECK-NEXT: %0 = fir.call @llvm.exp.f64(%arg0) : (f64) -> f64 +! CHECK-NEXT: return %0 : f64 +! CHECK-NEXT: } + + SUBROUTINE LOG_WRAPPER(IN, OUT) + DOUBLE PRECISION IN, OUT + OUT = DLOG(IN) + RETURN + END + +! CHECK: func private @fir.log.f64.f64(%arg0: f64) +! CHECK-NEXT: %0 = fir.call @llvm.log.f64(%arg0) : (f64) -> f64 +! CHECK-NEXT: return %0 : f64 +! CHECK-NEXT: } + + SUBROUTINE LOG10_WRAPPER(IN, OUT) + DOUBLE PRECISION IN, OUT + OUT = DLOG10(IN) + RETURN + END + +! CHECK: func private @fir.log10.f64.f64(%arg0: f64) +! CHECK-NEXT: %0 = fir.call @llvm.log10.f64(%arg0) : (f64) -> f64 +! CHECK-NEXT: return %0 : f64 +! CHECK-NEXT: } + + SUBROUTINE EXPF_WRAPPER(IN, OUT) + REAL IN + OUT = EXP(IN) + RETURN + END + +! CHECK: func private @fir.exp.f32.f32(%arg0: f32) +! CHECK-NEXT: %0 = fir.call @llvm.exp.f32(%arg0) : (f32) -> f32 +! CHECK-NEXT: return %0 : f32 +! CHECK-NEXT: } + + SUBROUTINE LOGF_WRAPPER(IN, OUT) + REAL IN, OUT + OUT = LOG(IN) + RETURN + END + +! CHECK: func private @fir.log.f32.f32(%arg0: f32) +! CHECK-NEXT: %0 = fir.call @llvm.log.f32(%arg0) : (f32) -> f32 +! CHECK-NEXT: return %0 : f32 +! CHECK-NEXT: } + + SUBROUTINE LOG10F_WRAPPER(IN, OUT) + REAL IN, OUT + OUT = LOG10(IN) + RETURN + END + +! CHECK: func private @fir.log10.f32.f32(%arg0: f32) +! CHECK-NEXT: %0 = fir.call @llvm.log10.f32(%arg0) : (f32) -> f32 +! CHECK-NEXT: return %0 : f32 +! CHECK-NEXT: }