Skip to content

Commit 94d9a4f

Browse files
committed
[flang] Rework host runtime folding and enable REAL(2) folding with it.
- Rework the host runtime table so that it is constexpr to avoid having to construct it and to store/propagate it. - Make the interface simpler (remove many templates and a file) - Enable 16bits float folding using 32bits float host runtime - Move StaticMultimapView into its own header to use it for host folding Reviewed By: klausler, PeteSteinfeld Differential Revision: https://reviews.llvm.org/D88981
1 parent 41d85fe commit 94d9a4f

File tree

13 files changed

+626
-865
lines changed

13 files changed

+626
-865
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//===-- include/flang/Common/static-multimap-view.h -------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef FORTRAN_COMMON_STATIC_MULTIMAP_VIEW_H_
10+
#define FORTRAN_COMMON_STATIC_MULTIMAP_VIEW_H_
11+
#include <algorithm>
12+
#include <utility>
13+
14+
/// StaticMultimapView is a constexpr friendly multimap implementation over
15+
/// sorted constexpr arrays. As the View name suggests, it does not duplicate
16+
/// the sorted array but only brings range and search concepts over it. It
17+
/// mainly erases the array size from the type and ensures the array is sorted
18+
/// at compile time. When C++20 brings std::span and constexpr std::is_sorted,
19+
/// this can most likely be replaced by those.
20+
21+
namespace Fortran::common {
22+
23+
template <typename V> class StaticMultimapView {
24+
public:
25+
using Key = typename V::Key;
26+
using const_iterator = const V *;
27+
28+
constexpr const_iterator begin() const { return begin_; }
29+
constexpr const_iterator end() const { return end_; }
30+
// Be sure to static_assert(map.Verify(), "must be sorted"); for
31+
// every instance constexpr created. Sadly this cannot be done in
32+
// the ctor since there is no way to know whether the ctor is actually
33+
// called at compile time or not.
34+
template <std::size_t N>
35+
constexpr StaticMultimapView(const V (&array)[N])
36+
: begin_{&array[0]}, end_{&array[0] + N} {}
37+
38+
// std::equal_range will be constexpr in C++20 only, so far there is actually
39+
// no need for equal_range to be constexpr anyway.
40+
std::pair<const_iterator, const_iterator> equal_range(const Key &key) const {
41+
return std::equal_range(begin_, end_, key);
42+
}
43+
44+
// Check that the array is sorted. This used to assert at compile time that
45+
// the array is indeed sorted. When C++20 is required for flang,
46+
// std::is_sorted can be used here since it will be constexpr.
47+
constexpr bool Verify() const {
48+
const V *lastSeen{begin_};
49+
bool isSorted{true};
50+
for (const auto *x{begin_}; x != end_; ++x) {
51+
isSorted &= lastSeen->key <= x->key;
52+
lastSeen = x;
53+
}
54+
return isSorted;
55+
}
56+
57+
private:
58+
const_iterator begin_{nullptr};
59+
const_iterator end_{nullptr};
60+
};
61+
} // namespace Fortran::common
62+
#endif // FORTRAN_COMMON_STATIC_MULTIMAP_VIEW_H_

flang/include/flang/Evaluate/common.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
#ifndef FORTRAN_EVALUATE_COMMON_H_
1010
#define FORTRAN_EVALUATE_COMMON_H_
1111

12-
#include "intrinsics-library.h"
1312
#include "flang/Common/Fortran.h"
1413
#include "flang/Common/default-kinds.h"
1514
#include "flang/Common/enum-set.h"
@@ -237,9 +236,6 @@ class FoldingContext {
237236
bool flushSubnormalsToZero() const { return flushSubnormalsToZero_; }
238237
bool bigEndian() const { return bigEndian_; }
239238
const semantics::DerivedTypeSpec *pdtInstance() const { return pdtInstance_; }
240-
const HostIntrinsicProceduresLibrary &hostIntrinsicsLibrary() const {
241-
return hostIntrinsicsLibrary_;
242-
}
243239
const evaluate::IntrinsicProcTable &intrinsics() const { return intrinsics_; }
244240

245241
ConstantSubscript &StartImpliedDo(parser::CharBlock, ConstantSubscript = 1);
@@ -264,7 +260,6 @@ class FoldingContext {
264260
bool bigEndian_{false};
265261
const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
266262
std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
267-
HostIntrinsicProceduresLibrary hostIntrinsicsLibrary_;
268263
};
269264

270265
void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);

flang/include/flang/Evaluate/intrinsics-library.h

Lines changed: 23 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -10,99 +10,36 @@
1010
#define FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_
1111

1212
// Defines structures to be used in F18 for folding intrinsic function with host
13-
// runtime libraries. To avoid unnecessary header circular dependencies, the
14-
// actual implementation of the templatized member function are defined in
15-
// intrinsics-library-templates.h The header at hand is meant to be included by
16-
// files that need to define intrinsic runtime data structure but that do not
17-
// use them directly. To actually use the runtime data structures,
18-
// intrinsics-library-templates.h must be included.
13+
// runtime libraries.
1914

2015
#include <functional>
21-
#include <map>
2216
#include <optional>
2317
#include <string>
2418
#include <vector>
2519

2620
namespace Fortran::evaluate {
2721
class FoldingContext;
28-
29-
using TypeCode = unsigned char;
30-
31-
template <typename TR, typename... TA> using FuncPointer = TR (*)(TA...);
32-
// This specific type signature prevents GCC complaining about function casts.
33-
using GenericFunctionPointer = void (*)(void);
34-
35-
enum class PassBy { Ref, Val };
36-
template <typename TA, PassBy Pass = PassBy::Ref> struct ArgumentInfo {
37-
using Type = TA;
38-
static constexpr PassBy pass{Pass};
39-
};
40-
41-
template <typename TR, typename... ArgInfo> struct Signature {
42-
// Note valid template argument are of form
43-
//<TR, ArgumentInfo<TA, PassBy>...> where TA and TR belong to RuntimeTypes.
44-
// RuntimeTypes is a type union defined in intrinsics-library-templates.h to
45-
// avoid circular dependencies. Argument of type void cannot be passed by
46-
// value. So far TR cannot be a pointer.
47-
const std::string name;
48-
};
49-
50-
struct IntrinsicProcedureRuntimeDescription {
51-
const std::string name;
52-
const TypeCode returnType;
53-
const std::vector<TypeCode> argumentsType;
54-
const std::vector<PassBy> argumentsPassedBy;
55-
const bool isElemental;
56-
const GenericFunctionPointer callable;
57-
// Construct from description using host independent types (RuntimeTypes)
58-
template <typename TR, typename... ArgInfo>
59-
IntrinsicProcedureRuntimeDescription(
60-
const Signature<TR, ArgInfo...> &signature, bool isElemental = false);
61-
};
62-
63-
// HostRuntimeIntrinsicProcedure allows host runtime function to be called for
64-
// constant folding.
65-
struct HostRuntimeIntrinsicProcedure : IntrinsicProcedureRuntimeDescription {
66-
// Construct from runtime pointer with host types (float, double....)
67-
template <typename HostTR, typename... HostTA>
68-
HostRuntimeIntrinsicProcedure(const std::string &name,
69-
FuncPointer<HostTR, HostTA...> func, bool isElemental = false);
70-
HostRuntimeIntrinsicProcedure(
71-
const IntrinsicProcedureRuntimeDescription &rteProc,
72-
GenericFunctionPointer handle)
73-
: IntrinsicProcedureRuntimeDescription{rteProc}, handle{handle} {}
74-
GenericFunctionPointer handle;
75-
};
76-
77-
// Defines a wrapper type that indirects calls to host runtime functions.
78-
// Valid ConstantContainer are Scalar (only for elementals) and Constant.
79-
template <template <typename> typename ConstantContainer, typename TR,
80-
typename... TA>
81-
using HostProcedureWrapper = std::function<ConstantContainer<TR>(
82-
FoldingContext &, ConstantContainer<TA>...)>;
83-
84-
// HostIntrinsicProceduresLibrary is a data structure that holds
85-
// HostRuntimeIntrinsicProcedure elements. It is meant for constant folding.
86-
// When queried for an intrinsic procedure, it can return a callable object that
87-
// implements this intrinsic if a host runtime function pointer for this
88-
// intrinsic was added to its data structure.
89-
class HostIntrinsicProceduresLibrary {
90-
public:
91-
HostIntrinsicProceduresLibrary();
92-
void AddProcedure(HostRuntimeIntrinsicProcedure &&sym) {
93-
const std::string name{sym.name};
94-
procedures_.insert(std::make_pair(name, std::move(sym)));
95-
}
96-
bool HasEquivalentProcedure(
97-
const IntrinsicProcedureRuntimeDescription &sym) const;
98-
template <template <typename> typename ConstantContainer, typename TR,
99-
typename... TA>
100-
std::optional<HostProcedureWrapper<ConstantContainer, TR, TA...>>
101-
GetHostProcedureWrapper(const std::string &name) const;
102-
103-
private:
104-
std::multimap<std::string, const HostRuntimeIntrinsicProcedure> procedures_;
105-
};
106-
22+
class DynamicType;
23+
struct SomeType;
24+
template <typename> class Expr;
25+
26+
// Define a callable type that is used to fold scalar intrinsic function using
27+
// host runtime. These callables are responsible for the conversions between
28+
// host types and Fortran abstract types (Scalar<T>). They also deal with
29+
// floating point environment (To set it up to match the Fortran compiling
30+
// options and to clean it up after the call). Floating point errors are
31+
// reported to the FoldingContext. For 16bits float types, 32bits float host
32+
// runtime plus conversions may be used to build the host wrappers if no 16bits
33+
// runtime is available. IEEE 128bits float may also be used for x87 float.
34+
// Potential conversion overflows are reported by the HostRuntimeWrapper in the
35+
// FoldingContext.
36+
using HostRuntimeWrapper = std::function<Expr<SomeType>(
37+
FoldingContext &, std::vector<Expr<SomeType>> &&)>;
38+
39+
// Returns the folder using host runtime given the intrinsic function name,
40+
// result and argument types. Nullopt if no host runtime is available for such
41+
// intrinsic function.
42+
std::optional<HostRuntimeWrapper> GetHostRuntimeWrapper(const std::string &name,
43+
DynamicType resultType, const std::vector<DynamicType> &argTypes);
10744
} // namespace Fortran::evaluate
10845
#endif // FORTRAN_EVALUATE_INTRINSICS_LIBRARY_H_

flang/lib/Evaluate/fold-complex.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ Expr<Type<TypeCategory::Complex, KIND>> FoldIntrinsicFunction(
2323
name == "atan" || name == "atanh" || name == "cos" || name == "cosh" ||
2424
name == "exp" || name == "log" || name == "sin" || name == "sinh" ||
2525
name == "sqrt" || name == "tan" || name == "tanh") {
26-
if (auto callable{context.hostIntrinsicsLibrary()
27-
.GetHostProcedureWrapper<Scalar, T, T>(name)}) {
26+
if (auto callable{GetHostRuntimeWrapper<T, T>(name)}) {
2827
return FoldElementalIntrinsic<T, T>(
2928
context, std::move(funcRef), *callable);
3029
} else {

flang/lib/Evaluate/fold-implementation.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "character.h"
1313
#include "host.h"
1414
#include "int-power.h"
15-
#include "intrinsics-library-templates.h"
1615
#include "flang/Common/indirection.h"
1716
#include "flang/Common/template.h"
1817
#include "flang/Common/unwrap.h"
@@ -22,6 +21,7 @@
2221
#include "flang/Evaluate/expression.h"
2322
#include "flang/Evaluate/fold.h"
2423
#include "flang/Evaluate/formatting.h"
24+
#include "flang/Evaluate/intrinsics-library.h"
2525
#include "flang/Evaluate/intrinsics.h"
2626
#include "flang/Evaluate/shape.h"
2727
#include "flang/Evaluate/tools.h"
@@ -70,6 +70,24 @@ template <typename T> class Folder {
7070
std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
7171
FoldingContext &, Subscript &, const NamedEntity &, int dim);
7272

73+
// Helper to use host runtime on scalars for folding.
74+
template <typename TR, typename... TA>
75+
std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
76+
GetHostRuntimeWrapper(const std::string &name) {
77+
std::vector<DynamicType> argTypes{TA{}.GetType()...};
78+
if (auto hostWrapper{GetHostRuntimeWrapper(name, TR{}.GetType(), argTypes)}) {
79+
return [hostWrapper](
80+
FoldingContext &context, Scalar<TA>... args) -> Scalar<TR> {
81+
std::vector<Expr<SomeType>> genericArgs{
82+
AsGenericExpr(Constant<TA>{args})...};
83+
return GetScalarConstantValue<TR>(
84+
(*hostWrapper)(context, std::move(genericArgs)))
85+
.value();
86+
};
87+
}
88+
return std::nullopt;
89+
}
90+
7391
// FoldOperation() rewrites expression tree nodes.
7492
// If there is any possibility that the rewritten node will
7593
// not have the same representation type, the result of
@@ -1410,8 +1428,7 @@ Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
14101428
}
14111429
return Expr<T>{Constant<T>{power.power}};
14121430
} else {
1413-
if (auto callable{context.hostIntrinsicsLibrary()
1414-
.GetHostProcedureWrapper<Scalar, T, T, T>("pow")}) {
1431+
if (auto callable{GetHostRuntimeWrapper<T, T, T>("pow")}) {
14151432
return Expr<T>{
14161433
Constant<T>{(*callable)(context, folded->first, folded->second)}};
14171434
} else {

flang/lib/Evaluate/fold-real.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
2929
name == "log_gamma" || name == "sin" || name == "sinh" ||
3030
name == "sqrt" || name == "tan" || name == "tanh") {
3131
CHECK(args.size() == 1);
32-
if (auto callable{context.hostIntrinsicsLibrary()
33-
.GetHostProcedureWrapper<Scalar, T, T>(name)}) {
32+
if (auto callable{GetHostRuntimeWrapper<T, T>(name)}) {
3433
return FoldElementalIntrinsic<T, T>(
3534
context, std::move(funcRef), *callable);
3635
} else {
@@ -44,9 +43,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
4443
name == "mod") {
4544
std::string localName{name == "atan" ? "atan2" : name};
4645
CHECK(args.size() == 2);
47-
if (auto callable{
48-
context.hostIntrinsicsLibrary()
49-
.GetHostProcedureWrapper<Scalar, T, T, T>(localName)}) {
46+
if (auto callable{GetHostRuntimeWrapper<T, T, T>(localName)}) {
5047
return FoldElementalIntrinsic<T, T, T>(
5148
context, std::move(funcRef), *callable);
5249
} else {
@@ -58,9 +55,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
5855
if (args.size() == 2) { // elemental
5956
// runtime functions use int arg
6057
using Int4 = Type<TypeCategory::Integer, 4>;
61-
if (auto callable{
62-
context.hostIntrinsicsLibrary()
63-
.GetHostProcedureWrapper<Scalar, T, Int4, T>(name)}) {
58+
if (auto callable{GetHostRuntimeWrapper<T, Int4, T>(name)}) {
6459
return FoldElementalIntrinsic<T, Int4, T>(
6560
context, std::move(funcRef), *callable);
6661
} else {
@@ -75,9 +70,7 @@ Expr<Type<TypeCategory::Real, KIND>> FoldIntrinsicFunction(
7570
return FoldElementalIntrinsic<T, T>(
7671
context, std::move(funcRef), &Scalar<T>::ABS);
7772
} else if (auto *z{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
78-
if (auto callable{
79-
context.hostIntrinsicsLibrary()
80-
.GetHostProcedureWrapper<Scalar, T, ComplexT>("abs")}) {
73+
if (auto callable{GetHostRuntimeWrapper<T, ComplexT>("abs")}) {
8174
return FoldElementalIntrinsic<T, ComplexT>(
8275
context, std::move(funcRef), *callable);
8376
} else {

0 commit comments

Comments
 (0)