[flang] Removed dynamic loading feature for intrinsic folding

After more reflexion, dynamic loading brings to much uncertainty
regarding which library is actually being use for folding.
It is removed to avoid pushing people to use it.
A static linking to libpgmath will be provided in a later commit.

Original-commit: flang-compiler/f18@2161627d28
Tree-same-pre-rewrite: false
This commit is contained in:
Jean Perier 2019-03-21 10:08:57 -07:00 committed by GitHub
parent 9ce02da63a
commit ca0261b253
4 changed files with 9 additions and 188 deletions

View File

@ -37,9 +37,3 @@ target_link_libraries(FortranEvaluate
FortranParser FortranParser
m m
) )
IF(CMAKE_SYSTEM_NAME STREQUAL Linux)
target_link_libraries(FortranEvaluate
dl
)
endif()

View File

@ -168,12 +168,5 @@ HostIntrinsicProceduresLibrary::GetHostProcedureWrapper(
return std::nullopt; return std::nullopt;
} }
template<typename TR, typename... ArgInfo>
TargetRuntimeIntrinsicProcedure::TargetRuntimeIntrinsicProcedure(
const Signature<TR, ArgInfo...> &signature, const std::string &symbolName,
bool isElemental)
: IntrinsicProcedureRuntimeDescription{signature, isElemental},
symbol{symbolName} {}
} }
#endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_ #endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_TEMPLATES_H_

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// This file defines the runtime libraries for the target as well as a default // This file defines host runtimes functions that can be used for folding
// set of host rte functions that can be used for folding. // intrinsic functions.
// The default HostIntrinsicProceduresLibrary is built with <cmath> and // The default HostIntrinsicProceduresLibrary is built with <cmath> and
// <complex> functions that are guaranteed to exist from the C++ standard. // <complex> functions that are guaranteed to exist from the C++ standard.
@ -22,10 +22,6 @@
#include <cerrno> #include <cerrno>
#include <cfenv> #include <cfenv>
#include <sstream> #include <sstream>
#if defined(__APPLE__) || defined(__unix__)
#define IS_POSIX_COMPLIANT
#include <dlfcn.h>
#endif
namespace Fortran::evaluate { namespace Fortran::evaluate {
@ -55,48 +51,7 @@ bool HostIntrinsicProceduresLibrary::HasEquivalentProcedure(
return false; return false;
} }
void HostIntrinsicProceduresLibrary::LoadTargetIntrinsicProceduresLibrary( // Map numerical intrinsic to <cmath>/<complex> functions
const TargetIntrinsicProceduresLibrary &lib) {
if (dynamicallyLoadedLibraries.find(lib.name) !=
dynamicallyLoadedLibraries.end()) {
return; // already loaded
}
#ifdef IS_POSIX_COMPLIANT
void *handle = dlopen((lib.name + std::string{".so"}).c_str(), RTLD_LAZY);
if (!handle) {
return;
}
dynamicallyLoadedLibraries.insert(std::make_pair(lib.name, handle));
for (const auto &sym : lib.procedures) {
void *func{dlsym(handle, sym.second.symbol.c_str())};
auto error{dlerror()};
if (error) {
} else {
// Note: below is the only reinterpret_cast from an object pointer to
// function pointer As per C++11 and later rules on reinterpret_cast, it
// is implementation defined whether this is supported. POSIX mandates
// that such cast from function pointers to void* are defined. Hence this
// reinterpret_cast is and MUST REMAIN inside ifdef related to POSIX.
AddProcedure(HostRuntimeIntrinsicProcedure{
sym.second, reinterpret_cast<FuncPointer<void *>>(func)});
}
}
#else
// TODO: systems that do not support dlopen (e.g windows)
#endif
}
HostIntrinsicProceduresLibrary::~HostIntrinsicProceduresLibrary() {
for (auto iter{dynamicallyLoadedLibraries.begin()};
iter != dynamicallyLoadedLibraries.end(); ++iter) {
#ifdef IS_POSIX_COMPLIANT
(void)dlclose(iter->second);
#endif
}
}
// Map numerical intrinsic to <cmath>/<complex> functions (for host folding
// only)
// TODO mapping to <cmath> function to be tested.<cmath> func takes // TODO mapping to <cmath> function to be tested.<cmath> func takes
// real arg for n // real arg for n
@ -148,92 +103,9 @@ void AddLibmComplexHostProcedure(
} }
} }
// define mapping between numerical intrinsics and libpgmath symbols
enum class MathOption { Fast, Precise, Relaxed };
char constexpr inline EncodePgmMathOption(MathOption m) {
switch (m) {
case MathOption::Fast: return 'f';
case MathOption::Precise: return 'p';
case MathOption::Relaxed: return 'r';
}
return '\0'; // unreachable. Silencing bogus g++ warning
}
template<typename T> struct EncodePgmTypeHelper {};
template<> struct EncodePgmTypeHelper<Type<TypeCategory::Real, 4>> {
static constexpr char value{'s'};
};
template<> struct EncodePgmTypeHelper<Type<TypeCategory::Real, 8>> {
static constexpr char value{'d'};
};
template<> struct EncodePgmTypeHelper<Type<TypeCategory::Complex, 4>> {
static constexpr char value{'c'};
};
template<> struct EncodePgmTypeHelper<Type<TypeCategory::Complex, 8>> {
static constexpr char value{'z'};
};
template<typename T>
static constexpr char EncodePgmType{EncodePgmTypeHelper<T>::value};
template<typename T>
static std::string MakeLibpgmathName(const std::string &name, MathOption m) {
std::ostringstream stream;
stream << "__" << EncodePgmMathOption(m) << EncodePgmType<T> << "_" << name
<< "_1";
// TODO Take mask and vector length into account
return stream.str();
}
template<typename T>
static void AddLibpgmathTargetSymbols(
TargetIntrinsicProceduresLibrary &lib, MathOption opt) {
using F = Signature<T, ArgumentInfo<T, PassBy::Val>>;
const std::string oneArgFuncs[]{"acos", "asin", "atan", "cos", "cosh", "exp",
"log", "log10", "sin", "sinh", "tan", "tanh"};
for (const std::string &name : oneArgFuncs) {
lib.AddProcedure(TargetRuntimeIntrinsicProcedure{
F{name}, MakeLibpgmathName<T>(name, opt), true});
}
if constexpr (T::category == TypeCategory::Real) {
using F2 = Signature<T, ArgumentInfo<T, PassBy::Val>,
ArgumentInfo<T, PassBy::Val>>;
lib.AddProcedure(TargetRuntimeIntrinsicProcedure{
F2{"atan2"}, MakeLibpgmathName<T>("acos", opt), true});
} else {
const std::string oneArgCmplxFuncs[]{
"div", "sqrt"}; // for scalar, only complex available
for (const std::string &name : oneArgCmplxFuncs) {
lib.AddProcedure(TargetRuntimeIntrinsicProcedure{
F{name}, MakeLibpgmathName<T>(name, opt), true});
}
}
}
TargetIntrinsicProceduresLibrary BuildLibpgmTargetIntrinsicProceduresLibrary(
MathOption opt) {
TargetIntrinsicProceduresLibrary lib{"libpgmath"};
AddLibpgmathTargetSymbols<Type<TypeCategory::Real, 4>>(lib, opt);
AddLibpgmathTargetSymbols<Type<TypeCategory::Real, 8>>(lib, opt);
AddLibpgmathTargetSymbols<Type<TypeCategory::Complex, 4>>(lib, opt);
AddLibpgmathTargetSymbols<Type<TypeCategory::Complex, 8>>(lib, opt);
return lib;
}
// Defines which host runtime functions will be used for folding // Defines which host runtime functions will be used for folding
void HostIntrinsicProceduresLibrary::DefaultInit() { void HostIntrinsicProceduresLibrary::DefaultInit() {
// TODO: when linkage information is available, this needs to be modified to
// load runtime accordingly. For now, try loading libpgmath (libpgmath.so
// needs to be in a directory from LD_LIBRARY_PATH) and then add libm symbols
// when no equivalent symbols were already loaded
TargetIntrinsicProceduresLibrary libpgmath{
BuildLibpgmTargetIntrinsicProceduresLibrary(MathOption::Precise)};
LoadTargetIntrinsicProceduresLibrary(libpgmath);
AddLibmRealHostProcedure<float>(*this); AddLibmRealHostProcedure<float>(*this);
AddLibmRealHostProcedure<double>(*this); AddLibmRealHostProcedure<double>(*this);

View File

@ -15,17 +15,13 @@
#ifndef FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_ #ifndef FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_
#define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_ #define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_
// Defines structures to be used in F18 when dealing with the intrinsic // Defines structures to be used in F18 for folding intrinsic function with host
// procedures runtime. It abstracts both: // runtime libraries. To avoid unnecessary header circular dependencies, the
// - the target intrinsic procedure runtime to be used for code generation // actual implementation of the templatized member function are defined in
// - the host intrinsic runtime to be used for constant folding purposes.
// To avoid unnecessary header circular dependencies, the actual implementation
// of the templatized member function are defined in
// intrinsics-library-templates.h The header at hand is meant to be included by // intrinsics-library-templates.h The header at hand is meant to be included by
// files that need to define intrinsic runtime data structure but that do not // files that need to define intrinsic runtime data structure but that do not
// use them directly. To actually use the runtime data structures, // use them directly. To actually use the runtime data structures,
// intrinsics-library-templates.h must be included Note that // intrinsics-library-templates.h must be included.
// intrinsics-library-templates.h includes the header at hand.
#include <functional> #include <functional>
#include <map> #include <map>
@ -62,38 +58,12 @@ struct IntrinsicProcedureRuntimeDescription {
const std::vector<PassBy> argumentsPassedBy; const std::vector<PassBy> argumentsPassedBy;
const bool isElemental; const bool isElemental;
const FuncPointer<void *> callable; const FuncPointer<void *> callable;
// callable only usable by HostRuntimeIntrinsicProcedure but need to be
// created in case TargetRuntimeIntrinsicProcedure is dynamically loaded
// because creating it dynamically would be too complex
// Construct from description using host independent types (RuntimeTypes) // Construct from description using host independent types (RuntimeTypes)
template<typename TR, typename... ArgInfo> template<typename TR, typename... ArgInfo>
IntrinsicProcedureRuntimeDescription( IntrinsicProcedureRuntimeDescription(
const Signature<TR, ArgInfo...> &signature, bool isElemental = false); const Signature<TR, ArgInfo...> &signature, bool isElemental = false);
}; };
// TargetRuntimeIntrinsicProcedure holds target runtime information
// for an intrinsics procedure.
struct TargetRuntimeIntrinsicProcedure : IntrinsicProcedureRuntimeDescription {
// Construct from description using host independent types (RuntimeTypes)
// Note: passing ref/val also needs to be passed by template to build
// the callable
template<typename TR, typename... ArgInfo>
TargetRuntimeIntrinsicProcedure(const Signature<TR, ArgInfo...> &signature,
const std::string &symbolName, bool isElemental = false);
const std::string symbol;
};
struct TargetIntrinsicProceduresLibrary {
TargetIntrinsicProceduresLibrary(const std::string &name) : name{name} {}
void AddProcedure(TargetRuntimeIntrinsicProcedure &&sym) {
const std::string name{sym.name};
procedures.insert(std::make_pair(name, std::move(sym)));
}
const std::string name;
std::multimap<std::string, const TargetRuntimeIntrinsicProcedure> procedures;
};
// HostRuntimeIntrinsicProcedure allows host runtime function to be called for // HostRuntimeIntrinsicProcedure allows host runtime function to be called for
// constant folding. // constant folding.
struct HostRuntimeIntrinsicProcedure : IntrinsicProcedureRuntimeDescription { struct HostRuntimeIntrinsicProcedure : IntrinsicProcedureRuntimeDescription {
@ -119,9 +89,7 @@ using HostProcedureWrapper = std::function<ConstantContainer<TR>(
// HostRuntimeIntrinsicProcedure elements. It is meant for constant folding. // HostRuntimeIntrinsicProcedure elements. It is meant for constant folding.
// When queried for an intrinsic procedure, it can return a callable object that // When queried for an intrinsic procedure, it can return a callable object that
// implements this intrinsic if a host runtime function pointer for this // implements this intrinsic if a host runtime function pointer for this
// intrinsic was added to its data structure. It can also dynamically load // intrinsic was added to its data structure.
// function pointer from a TargetIntrinsicProceduresLibrary if the related
// library is available on the host.
struct HostIntrinsicProceduresLibrary { struct HostIntrinsicProceduresLibrary {
void AddProcedure(HostRuntimeIntrinsicProcedure &&sym) { void AddProcedure(HostRuntimeIntrinsicProcedure &&sym) {
const std::string name{sym.name}; const std::string name{sym.name};
@ -130,18 +98,12 @@ struct HostIntrinsicProceduresLibrary {
bool HasEquivalentProcedure( bool HasEquivalentProcedure(
const IntrinsicProcedureRuntimeDescription &sym) const; const IntrinsicProcedureRuntimeDescription &sym) const;
HostIntrinsicProceduresLibrary() { DefaultInit(); } HostIntrinsicProceduresLibrary() { DefaultInit(); }
~HostIntrinsicProceduresLibrary(); void DefaultInit();
void DefaultInit(); // Try loading libpgmath functions and then load
// functions from <cmath> and <complex>
void LoadTargetIntrinsicProceduresLibrary(
const TargetIntrinsicProceduresLibrary &lib);
template<template<typename> typename ConstantContainer, typename TR, template<template<typename> typename ConstantContainer, typename TR,
typename... TA> typename... TA>
std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>> std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>>
GetHostProcedureWrapper(const std::string &name); GetHostProcedureWrapper(const std::string &name);
std::multimap<std::string, const HostRuntimeIntrinsicProcedure> procedures; std::multimap<std::string, const HostRuntimeIntrinsicProcedure> procedures;
std::map<std::string, void *>
dynamicallyLoadedLibraries; // keep the handles for dlclose
}; };
} }