1 //===-- RTBuilder.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 /// \file
10 /// This file defines some C++17 template classes that are used to convert the
11 /// signatures of plain old C functions into a model that can be used to
12 /// generate MLIR calls to those functions. This can be used to autogenerate
13 /// tables at compiler compile-time to call runtime support code.
14 ///
15 //===----------------------------------------------------------------------===//
16 
17 #ifndef FORTRAN_LOWER_RTBUILDER_H
18 #define FORTRAN_LOWER_RTBUILDER_H
19 
20 #include "flang/Lower/ConvertType.h"
21 #include "flang/Optimizer/Dialect/FIRType.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/MLIRContext.h"
24 #include "llvm/ADT/SmallVector.h"
25 #include <functional>
26 
27 // List the runtime headers we want to be able to dissect
28 #include "../../runtime/io-api.h"
29 
30 namespace Fortran::lower {
31 
32 using TypeBuilderFunc = mlir::Type (*)(mlir::MLIRContext *);
33 using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
34 
35 //===----------------------------------------------------------------------===//
36 // Type builder models
37 //===----------------------------------------------------------------------===//
38 
39 /// Return a function that returns the type signature model for the type `T`
40 /// when provided an MLIRContext*. This allows one to translate C(++) function
41 /// signatures from runtime header files to MLIR signatures into a static table
42 /// at compile-time.
43 ///
44 /// For example, when `T` is `int`, return a function that returns the MLIR
45 /// standard type `i32` when `sizeof(int)` is 4.
46 template <typename T>
47 static constexpr TypeBuilderFunc getModel();
48 template <>
49 constexpr TypeBuilderFunc getModel<int>() {
50   return [](mlir::MLIRContext *context) -> mlir::Type {
51     return mlir::IntegerType::get(8 * sizeof(int), context);
52   };
53 }
54 template <>
55 constexpr TypeBuilderFunc getModel<int &>() {
56   return [](mlir::MLIRContext *context) -> mlir::Type {
57     TypeBuilderFunc f{getModel<int>()};
58     return fir::ReferenceType::get(f(context));
59   };
60 }
61 template <>
62 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::Iostat>() {
63   return [](mlir::MLIRContext *context) -> mlir::Type {
64     return mlir::IntegerType::get(8 * sizeof(Fortran::runtime::io::Iostat),
65                                   context);
66   };
67 }
68 template <>
69 constexpr TypeBuilderFunc getModel<char *>() {
70   return [](mlir::MLIRContext *context) -> mlir::Type {
71     return fir::ReferenceType::get(mlir::IntegerType::get(8, context));
72   };
73 }
74 template <>
75 constexpr TypeBuilderFunc getModel<const char *>() {
76   return getModel<char *>();
77 }
78 template <>
79 constexpr TypeBuilderFunc getModel<const char16_t *>() {
80   return [](mlir::MLIRContext *context) -> mlir::Type {
81     return fir::ReferenceType::get(mlir::IntegerType::get(16, context));
82   };
83 }
84 template <>
85 constexpr TypeBuilderFunc getModel<const char32_t *>() {
86   return [](mlir::MLIRContext *context) -> mlir::Type {
87     return fir::ReferenceType::get(mlir::IntegerType::get(32, context));
88   };
89 }
90 template <>
91 constexpr TypeBuilderFunc getModel<void **>() {
92   return [](mlir::MLIRContext *context) -> mlir::Type {
93     return fir::ReferenceType::get(
94         fir::PointerType::get(mlir::IntegerType::get(8, context)));
95   };
96 }
97 template <>
98 constexpr TypeBuilderFunc getModel<std::int64_t>() {
99   return [](mlir::MLIRContext *context) -> mlir::Type {
100     return mlir::IntegerType::get(64, context);
101   };
102 }
103 template <>
104 constexpr TypeBuilderFunc getModel<std::int64_t &>() {
105   return [](mlir::MLIRContext *context) -> mlir::Type {
106     TypeBuilderFunc f{getModel<std::int64_t>()};
107     return fir::ReferenceType::get(f(context));
108   };
109 }
110 template <>
111 constexpr TypeBuilderFunc getModel<std::size_t>() {
112   return [](mlir::MLIRContext *context) -> mlir::Type {
113     return mlir::IntegerType::get(8 * sizeof(std::size_t), context);
114   };
115 }
116 template <>
117 constexpr TypeBuilderFunc getModel<Fortran::runtime::io::IoStatementState *>() {
118   return getModel<char *>();
119 }
120 template <>
121 constexpr TypeBuilderFunc getModel<double>() {
122   return [](mlir::MLIRContext *context) -> mlir::Type {
123     return mlir::FloatType::getF64(context);
124   };
125 }
126 template <>
127 constexpr TypeBuilderFunc getModel<double &>() {
128   return [](mlir::MLIRContext *context) -> mlir::Type {
129     TypeBuilderFunc f{getModel<double>()};
130     return fir::ReferenceType::get(f(context));
131   };
132 }
133 template <>
134 constexpr TypeBuilderFunc getModel<float>() {
135   return [](mlir::MLIRContext *context) -> mlir::Type {
136     return mlir::FloatType::getF32(context);
137   };
138 }
139 template <>
140 constexpr TypeBuilderFunc getModel<float &>() {
141   return [](mlir::MLIRContext *context) -> mlir::Type {
142     TypeBuilderFunc f{getModel<float>()};
143     return fir::ReferenceType::get(f(context));
144   };
145 }
146 template <>
147 constexpr TypeBuilderFunc getModel<bool>() {
148   return [](mlir::MLIRContext *context) -> mlir::Type {
149     return mlir::IntegerType::get(1, context);
150   };
151 }
152 template <>
153 constexpr TypeBuilderFunc getModel<bool &>() {
154   return [](mlir::MLIRContext *context) -> mlir::Type {
155     TypeBuilderFunc f{getModel<bool>()};
156     return fir::ReferenceType::get(f(context));
157   };
158 }
159 
160 template <>
161 constexpr TypeBuilderFunc getModel<const Fortran::runtime::Descriptor &>() {
162   return [](mlir::MLIRContext *context) -> mlir::Type {
163     return fir::BoxType::get(mlir::NoneType::get(context));
164   };
165 }
166 template <>
167 constexpr TypeBuilderFunc getModel<const Fortran::runtime::NamelistGroup &>() {
168   return [](mlir::MLIRContext *context) -> mlir::Type {
169     // FIXME: a namelist group must be some well-defined data structure, use a
170     // tuple as a proxy for the moment
171     return mlir::TupleType::get(context);
172   };
173 }
174 template <>
175 constexpr TypeBuilderFunc getModel<void>() {
176   return [](mlir::MLIRContext *context) -> mlir::Type {
177     return mlir::NoneType::get(context);
178   };
179 }
180 
181 template <typename...>
182 struct RuntimeTableKey;
183 template <typename RT, typename... ATs>
184 struct RuntimeTableKey<RT(ATs...)> {
185   static constexpr FuncTypeBuilderFunc getTypeModel() {
186     return [](mlir::MLIRContext *ctxt) {
187       TypeBuilderFunc ret = getModel<RT>();
188       std::array<TypeBuilderFunc, sizeof...(ATs)> args = {getModel<ATs>()...};
189       mlir::Type retTy = ret(ctxt);
190       llvm::SmallVector<mlir::Type, sizeof...(ATs)> argTys;
191       for (auto f : args)
192         argTys.push_back(f(ctxt));
193       return mlir::FunctionType::get(argTys, {retTy}, ctxt);
194     };
195   }
196 };
197 
198 //===----------------------------------------------------------------------===//
199 // Runtime table building (constexpr folded)
200 //===----------------------------------------------------------------------===//
201 
202 template <char... Cs>
203 using RuntimeIdentifier = std::integer_sequence<char, Cs...>;
204 
205 namespace details {
206 template <typename T, T... As, T... Bs>
207 static constexpr std::integer_sequence<T, As..., Bs...>
208 concat(std::integer_sequence<T, As...>, std::integer_sequence<T, Bs...>) {
209   return {};
210 }
211 template <typename T, T... As, T... Bs, typename... Cs>
212 static constexpr auto concat(std::integer_sequence<T, As...>,
213                              std::integer_sequence<T, Bs...>, Cs...) {
214   return concat(std::integer_sequence<T, As..., Bs...>{}, Cs{}...);
215 }
216 template <typename T>
217 static constexpr std::integer_sequence<T> concat(std::integer_sequence<T>) {
218   return {};
219 }
220 template <typename T, T a>
221 static constexpr auto filterZero(std::integer_sequence<T, a>) {
222   if constexpr (a != 0) {
223     return std::integer_sequence<T, a>{};
224   } else {
225     return std::integer_sequence<T>{};
226   }
227 }
228 template <typename T, T... b>
229 static constexpr auto filter(std::integer_sequence<T, b...>) {
230   if constexpr (sizeof...(b) > 0) {
231     return details::concat(filterZero(std::integer_sequence<T, b>{})...);
232   } else {
233     return std::integer_sequence<T>{};
234   }
235 }
236 } // namespace details
237 
238 template <typename...>
239 struct RuntimeTableEntry;
240 template <typename KT, char... Cs>
241 struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
242   static constexpr FuncTypeBuilderFunc getTypeModel() {
243     return RuntimeTableKey<KT>::getTypeModel();
244   }
245   static constexpr const char name[sizeof...(Cs) + 1] = {Cs..., '\0'};
246 };
247 
248 #undef E
249 #define E(L, I) (I < sizeof(L) / sizeof(*L) ? L[I] : 0)
250 #define QuoteKey(X) #X
251 #define MacroExpandKey(X)                                                      \
252   E(X, 0), E(X, 1), E(X, 2), E(X, 3), E(X, 4), E(X, 5), E(X, 6), E(X, 7),      \
253       E(X, 8), E(X, 9), E(X, 10), E(X, 11), E(X, 12), E(X, 13), E(X, 14),      \
254       E(X, 15), E(X, 16), E(X, 17), E(X, 18), E(X, 19), E(X, 20), E(X, 21),    \
255       E(X, 22), E(X, 23), E(X, 24), E(X, 25), E(X, 26), E(X, 27), E(X, 28),    \
256       E(X, 29), E(X, 30), E(X, 31), E(X, 32), E(X, 33), E(X, 34), E(X, 35),    \
257       E(X, 36), E(X, 37), E(X, 38), E(X, 39), E(X, 40), E(X, 41), E(X, 42),    \
258       E(X, 43), E(X, 44), E(X, 45), E(X, 46), E(X, 47), E(X, 48), E(X, 49)
259 #define ExpandKey(X) MacroExpandKey(QuoteKey(X))
260 #define FullSeq(X) std::integer_sequence<char, ExpandKey(X)>
261 #define AsSequence(X) decltype(Fortran::lower::details::filter(FullSeq(X){}))
262 #define mkKey(X)                                                               \
263   Fortran::lower::RuntimeTableEntry<                                           \
264       Fortran::lower::RuntimeTableKey<decltype(X)>, AsSequence(X)>
265 
266 } // namespace Fortran::lower
267 
268 #endif // FORTRAN_LOWER_RTBUILDER_H
269