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,
// 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>},
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);
}
}
/// 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))
}
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,
llvm::Optional<mlir::Type> resultType,
--- /dev/null
+! 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>
--- /dev/null
+! 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
! 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: }