forked from OSchip/llvm-project
210 lines
8.0 KiB
C++
210 lines
8.0 KiB
C++
//===-- lib/Evaluate/intrinsics-library-templates.h -------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
|
|
#define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
|
|
|
|
// This header defines the actual implementation of the templatized member
|
|
// function of the structures defined in intrinsics-library.h. It should only be
|
|
// included if these member functions are used, else intrinsics-library.h is
|
|
// sufficient. This is to avoid circular dependencies. The below implementation
|
|
// cannot be defined in .cpp file because it would be too cumbersome to decide
|
|
// which version should be instantiated in a generic way.
|
|
|
|
#include "host.h"
|
|
#include "flang/Common/template.h"
|
|
#include "flang/Evaluate/intrinsics-library.h"
|
|
#include "flang/Evaluate/type.h"
|
|
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
|
|
namespace Fortran::evaluate {
|
|
|
|
// Define meaningful types for the runtime
|
|
using RuntimeTypes = evaluate::AllIntrinsicTypes;
|
|
|
|
template <typename T, typename... TT> struct IndexInTupleHelper {};
|
|
template <typename T, typename... TT>
|
|
struct IndexInTupleHelper<T, std::tuple<TT...>> {
|
|
static constexpr TypeCode value{common::TypeIndex<T, TT...>};
|
|
};
|
|
|
|
static_assert(
|
|
std::tuple_size_v<RuntimeTypes> < std::numeric_limits<TypeCode>::max(),
|
|
"TypeCode is too small");
|
|
template <typename T>
|
|
inline constexpr TypeCode typeCodeOf{
|
|
IndexInTupleHelper<T, RuntimeTypes>::value};
|
|
|
|
template <TypeCode n>
|
|
using RuntimeTypeOf = typename std::tuple_element_t<n, RuntimeTypes>;
|
|
|
|
template <typename TA, PassBy Pass>
|
|
using HostArgType = std::conditional_t<Pass == PassBy::Ref,
|
|
std::add_lvalue_reference_t<std::add_const_t<host::HostType<TA>>>,
|
|
host::HostType<TA>>;
|
|
|
|
template <typename TR, typename... ArgInfo>
|
|
using HostFuncPointer = FuncPointer<host::HostType<TR>,
|
|
HostArgType<typename ArgInfo::Type, ArgInfo::pass>...>;
|
|
|
|
// Software Subnormal Flushing helper.
|
|
template <typename T> struct Flusher {
|
|
// Only flush floating-points. Forward other scalars untouched.
|
|
static constexpr inline const Scalar<T> &FlushSubnormals(const Scalar<T> &x) {
|
|
return x;
|
|
}
|
|
};
|
|
template <int Kind> struct Flusher<Type<TypeCategory::Real, Kind>> {
|
|
using T = Type<TypeCategory::Real, Kind>;
|
|
static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) {
|
|
return x.FlushSubnormalToZero();
|
|
}
|
|
};
|
|
template <int Kind> struct Flusher<Type<TypeCategory::Complex, Kind>> {
|
|
using T = Type<TypeCategory::Complex, Kind>;
|
|
static constexpr inline Scalar<T> FlushSubnormals(const Scalar<T> &x) {
|
|
return x.FlushSubnormalToZero();
|
|
}
|
|
};
|
|
|
|
// Callable factory
|
|
template <typename TR, typename... ArgInfo> struct CallableHostWrapper {
|
|
static Scalar<TR> scalarCallable(FoldingContext &context,
|
|
HostFuncPointer<TR, ArgInfo...> func,
|
|
const Scalar<typename ArgInfo::Type> &...x) {
|
|
if constexpr (host::HostTypeExists<TR, typename ArgInfo::Type...>()) {
|
|
host::HostFloatingPointEnvironment hostFPE;
|
|
hostFPE.SetUpHostFloatingPointEnvironment(context);
|
|
host::HostType<TR> hostResult{};
|
|
Scalar<TR> result{};
|
|
if (context.flushSubnormalsToZero() &&
|
|
!hostFPE.hasSubnormalFlushingHardwareControl()) {
|
|
hostResult = func(host::CastFortranToHost<typename ArgInfo::Type>(
|
|
Flusher<typename ArgInfo::Type>::FlushSubnormals(x))...);
|
|
result = Flusher<TR>::FlushSubnormals(
|
|
host::CastHostToFortran<TR>(hostResult));
|
|
} else {
|
|
hostResult =
|
|
func(host::CastFortranToHost<typename ArgInfo::Type>(x)...);
|
|
result = host::CastHostToFortran<TR>(hostResult);
|
|
}
|
|
if (!hostFPE.hardwareFlagsAreReliable()) {
|
|
CheckFloatingPointIssues(hostFPE, result);
|
|
}
|
|
hostFPE.CheckAndRestoreFloatingPointEnvironment(context);
|
|
return result;
|
|
} else {
|
|
common::die("Internal error: Host does not supports this function type."
|
|
"This should not have been called for folding");
|
|
}
|
|
}
|
|
static constexpr inline auto MakeScalarCallable() { return &scalarCallable; }
|
|
|
|
static void CheckFloatingPointIssues(
|
|
host::HostFloatingPointEnvironment &hostFPE, const Scalar<TR> &x) {
|
|
if constexpr (TR::category == TypeCategory::Complex ||
|
|
TR::category == TypeCategory::Real) {
|
|
if (x.IsNotANumber()) {
|
|
hostFPE.SetFlag(RealFlag::InvalidArgument);
|
|
} else if (x.IsInfinite()) {
|
|
hostFPE.SetFlag(RealFlag::Overflow);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename TR, typename... TA>
|
|
inline GenericFunctionPointer ToGenericFunctionPointer(
|
|
FuncPointer<TR, TA...> f) {
|
|
return reinterpret_cast<GenericFunctionPointer>(f);
|
|
}
|
|
|
|
template <typename TR, typename... TA>
|
|
inline FuncPointer<TR, TA...> FromGenericFunctionPointer(
|
|
GenericFunctionPointer g) {
|
|
return reinterpret_cast<FuncPointer<TR, TA...>>(g);
|
|
}
|
|
|
|
template <typename TR, typename... ArgInfo>
|
|
IntrinsicProcedureRuntimeDescription::IntrinsicProcedureRuntimeDescription(
|
|
const Signature<TR, ArgInfo...> &signature, bool isElemental)
|
|
: name{signature.name}, returnType{typeCodeOf<TR>},
|
|
argumentsType{typeCodeOf<typename ArgInfo::Type>...},
|
|
argumentsPassedBy{ArgInfo::pass...}, isElemental{isElemental},
|
|
callable{ToGenericFunctionPointer(
|
|
CallableHostWrapper<TR, ArgInfo...>::MakeScalarCallable())} {}
|
|
|
|
template <typename HostTA> static constexpr inline PassBy PassByMethod() {
|
|
if constexpr (std::is_pointer_v<std::decay_t<HostTA>> ||
|
|
std::is_lvalue_reference_v<HostTA>) {
|
|
return PassBy::Ref;
|
|
}
|
|
return PassBy::Val;
|
|
}
|
|
|
|
template <typename HostTA>
|
|
using ArgInfoFromHostType =
|
|
ArgumentInfo<host::FortranType<std::remove_pointer_t<std::decay_t<HostTA>>>,
|
|
PassByMethod<HostTA>()>;
|
|
|
|
template <typename HostTR, typename... HostTA>
|
|
using SignatureFromHostFuncPointer =
|
|
Signature<host::FortranType<HostTR>, ArgInfoFromHostType<HostTA>...>;
|
|
|
|
template <typename HostTR, typename... HostTA>
|
|
HostRuntimeIntrinsicProcedure::HostRuntimeIntrinsicProcedure(
|
|
const std::string &name, FuncPointer<HostTR, HostTA...> func,
|
|
bool isElemental)
|
|
: IntrinsicProcedureRuntimeDescription(
|
|
SignatureFromHostFuncPointer<HostTR, HostTA...>{name}, isElemental),
|
|
handle{ToGenericFunctionPointer(func)} {}
|
|
|
|
template <template <typename> typename ConstantContainer, typename TR,
|
|
typename... TA>
|
|
std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>>
|
|
HostIntrinsicProceduresLibrary::GetHostProcedureWrapper(
|
|
const std::string &name) const {
|
|
if constexpr (host::HostTypeExists<TR, TA...>()) {
|
|
auto rteProcRange{procedures_.equal_range(name)};
|
|
const TypeCode resTypeCode{typeCodeOf<TR>};
|
|
const std::vector<TypeCode> argTypes{typeCodeOf<TA>...};
|
|
const size_t nargs{argTypes.size()};
|
|
for (auto iter{rteProcRange.first}; iter != rteProcRange.second; ++iter) {
|
|
if (nargs == iter->second.argumentsType.size() &&
|
|
resTypeCode == iter->second.returnType &&
|
|
(!std::is_same_v<ConstantContainer<TR>, Scalar<TR>> ||
|
|
iter->second.isElemental)) {
|
|
bool match{true};
|
|
int pos{0};
|
|
for (auto const &type : argTypes) {
|
|
if (type != iter->second.argumentsType[pos++]) {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
if (match) {
|
|
return {HostProcedureWrapper<ConstantContainer, TR, TA...>{
|
|
[=](FoldingContext &context,
|
|
const ConstantContainer<TA> &...args) {
|
|
auto callable{FromGenericFunctionPointer<ConstantContainer<TR>,
|
|
FoldingContext &, GenericFunctionPointer,
|
|
const ConstantContainer<TA> &...>(iter->second.callable)};
|
|
return callable(context, iter->second.handle, args...);
|
|
}}};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
} // namespace Fortran::evaluate
|
|
#endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_
|