mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-14 03:45:33 +00:00
[Flang] Lower the exp, log, log10 intrinsics
The intrinsic computes the exponent, log real and complex numbers and log10 for real numbers. By default they are lowered to runtime calls to libpgmath. kind=10 and 16 are not supported. With the llvm option, it can be lowered to llvm intrinsics (not all types .eg. complex are supported for llvm lowering). This is part of the upstreaming effort from the fir-dev branch in [1]. [1] https://github.com/flang-compiler/f18-llvm-project Reviewed By: PeteSteinfeld Differential Revision: https://reviews.llvm.org/D122132 Co-authored-by: Eric Schweitz <eschweitz@nvidia.com> Co-authored-by: William S Moses <gh@wsmoses.com>
This commit is contained in:
parent
73c0d05e6a
commit
db925d08de
@ -545,6 +545,16 @@ struct IntrinsicLibrary {
|
||||
using Generator =
|
||||
std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>;
|
||||
|
||||
/// All generators can be outlined. This will build a function named
|
||||
/// "fir."+ <generic name> + "." + <result type code> 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 <typename GeneratorType>
|
||||
mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name,
|
||||
mlir::Type resultType,
|
||||
llvm::ArrayRef<mlir::Value> args);
|
||||
template <typename GeneratorType>
|
||||
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<mlir::Value> args) {
|
||||
for (const mlir::Value &arg : args)
|
||||
if (!arg)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||
for (const fir::ExtendedValue &arg : args)
|
||||
if (!fir::getBase(arg))
|
||||
@ -1566,6 +1590,25 @@ bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename GeneratorType>
|
||||
mlir::Value
|
||||
IntrinsicLibrary::outlineInWrapper(GeneratorType generator,
|
||||
llvm::StringRef name, mlir::Type resultType,
|
||||
llvm::ArrayRef<mlir::Value> 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<fir::CallOp>(loc, wrapper, args).getResult(0);
|
||||
}
|
||||
|
||||
template <typename GeneratorType>
|
||||
fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
|
||||
GeneratorType generator, llvm::StringRef name,
|
||||
|
62
flang/test/Lower/Intrinsics/exp.f90
Normal file
62
flang/test/Lower/Intrinsics/exp.f90
Normal file
@ -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<f32> {{.*}}, %[[BREF:.*]]: !fir.ref<f32> {{.*}})
|
||||
subroutine exp_testr(a, b)
|
||||
real :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f32>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.exp.f32.f32(%[[A]]) : (f32) -> f32
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f32>
|
||||
b = exp(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: exp_testd
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<f64> {{.*}}, %[[BREF:.*]]: !fir.ref<f64> {{.*}})
|
||||
subroutine exp_testd(a, b)
|
||||
real(kind=8) :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f64>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.exp.f64.f64(%[[A]]) : (f64) -> f64
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f64>
|
||||
b = exp(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: exp_testc
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<!fir.complex<4>> {{.*}}, %[[BREF:.*]]: !fir.ref<!fir.complex<4>> {{.*}})
|
||||
subroutine exp_testc(a, b)
|
||||
complex :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<!fir.complex<4>>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.exp.z4.z4(%[[A]]) : (!fir.complex<4>) -> !fir.complex<4>
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<!fir.complex<4>>
|
||||
b = exp(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: exp_testcd
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<!fir.complex<8>> {{.*}}, %[[BREF:.*]]: !fir.ref<!fir.complex<8>> {{.*}})
|
||||
subroutine exp_testcd(a, b)
|
||||
complex(kind=8) :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<!fir.complex<8>>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.exp.z8.z8(%[[A]]) : (!fir.complex<8>) -> !fir.complex<8>
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<!fir.complex<8>>
|
||||
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>
|
92
flang/test/Lower/Intrinsics/log.f90
Normal file
92
flang/test/Lower/Intrinsics/log.f90
Normal file
@ -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<f32> {{.*}}, %[[BREF:.*]]: !fir.ref<f32> {{.*}})
|
||||
subroutine log_testr(a, b)
|
||||
real :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f32>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log.f32.f32(%[[A]]) : (f32) -> f32
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f32>
|
||||
b = log(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: log_testd
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<f64> {{.*}}, %[[BREF:.*]]: !fir.ref<f64> {{.*}})
|
||||
subroutine log_testd(a, b)
|
||||
real(kind=8) :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f64>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log.f64.f64(%[[A]]) : (f64) -> f64
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f64>
|
||||
b = log(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: log_testc
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<!fir.complex<4>> {{.*}}, %[[BREF:.*]]: !fir.ref<!fir.complex<4>> {{.*}})
|
||||
subroutine log_testc(a, b)
|
||||
complex :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<!fir.complex<4>>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log.z4.z4(%[[A]]) : (!fir.complex<4>) -> !fir.complex<4>
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<!fir.complex<4>>
|
||||
b = log(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: log_testcd
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<!fir.complex<8>> {{.*}}, %[[BREF:.*]]: !fir.ref<!fir.complex<8>> {{.*}})
|
||||
subroutine log_testcd(a, b)
|
||||
complex(kind=8) :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<!fir.complex<8>>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log.z8.z8(%[[A]]) : (!fir.complex<8>) -> !fir.complex<8>
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<!fir.complex<8>>
|
||||
b = log(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: log10_testr
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<f32> {{.*}}, %[[BREF:.*]]: !fir.ref<f32> {{.*}})
|
||||
subroutine log10_testr(a, b)
|
||||
real :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f32>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log10.f32.f32(%[[A]]) : (f32) -> f32
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f32>
|
||||
b = log10(a)
|
||||
end subroutine
|
||||
|
||||
! CHECK-LABEL: log10_testd
|
||||
! CHECK-SAME: (%[[AREF:.*]]: !fir.ref<f64> {{.*}}, %[[BREF:.*]]: !fir.ref<f64> {{.*}})
|
||||
subroutine log10_testd(a, b)
|
||||
real(kind=8) :: a, b
|
||||
! CHECK: %[[A:.*]] = fir.load %[[AREF:.*]] : !fir.ref<f64>
|
||||
! CHECK: %[[RES:.*]] = fir.call @fir.log10.f64.f64(%[[A]]) : (f64) -> f64
|
||||
! CHECK: fir.store %[[RES]] to %[[BREF]] : !fir.ref<f64>
|
||||
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
|
@ -23,3 +23,69 @@
|
||||
! CHECK-NEXT: %0 = fir.load %arg0 : !fir.ref<f32>
|
||||
! CHECK-NEXT: %1 = fir.load %arg1 : !fir.ref<f32>
|
||||
! 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: }
|
||||
|
Loading…
x
Reference in New Issue
Block a user