xref: /llvm-project/flang/include/flang/Optimizer/Builder/Runtime/RTBuilder.h (revision 12ba74e181bd6641b532e271f3bfabf53066b1c0)
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_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H
18 #define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H
19 
20 #include "flang/Common/Fortran.h"
21 #include "flang/Common/uint128.h"
22 #include "flang/Optimizer/Builder/FIRBuilder.h"
23 #include "flang/Optimizer/Dialect/FIRDialect.h"
24 #include "flang/Optimizer/Dialect/FIRType.h"
25 #include "flang/Runtime/reduce.h"
26 #include "mlir/IR/BuiltinTypes.h"
27 #include "mlir/IR/MLIRContext.h"
28 #include "llvm/ADT/SmallVector.h"
29 #include <cstdint>
30 #include <functional>
31 
32 #ifdef _WIN32
33 // On Windows* OS GetCurrentProcessId returns DWORD aka uint32_t
34 typedef std::uint32_t pid_t;
35 #endif
36 
37 // Incomplete type indicating C99 complex ABI in interfaces. Beware, _Complex
38 // and std::complex are layout compatible, but not compatible in all ABI call
39 // interfaces (e.g. X86 32 bits). _Complex is not standard C++, so do not use
40 // it here.
41 struct c_float_complex_t;
42 struct c_double_complex_t;
43 
44 namespace Fortran::runtime {
45 class Descriptor;
46 namespace typeInfo {
47 class DerivedType;
48 }
49 } // namespace Fortran::runtime
50 
51 namespace fir::runtime {
52 
53 using TypeBuilderFunc = mlir::Type (*)(mlir::MLIRContext *);
54 using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *);
55 
56 #define REDUCTION_REF_OPERATION_MODEL(T)                                       \
57   template <>                                                                  \
58   constexpr TypeBuilderFunc                                                    \
59   getModel<Fortran::runtime::ReferenceReductionOperation<T>>() {               \
60     return [](mlir::MLIRContext *context) -> mlir::Type {                      \
61       TypeBuilderFunc f{getModel<T>()};                                        \
62       auto refTy = fir::ReferenceType::get(f(context));                        \
63       return mlir::FunctionType::get(context, {refTy, refTy}, refTy);          \
64     };                                                                         \
65   }
66 
67 #define REDUCTION_VALUE_OPERATION_MODEL(T)                                     \
68   template <>                                                                  \
69   constexpr TypeBuilderFunc                                                    \
70   getModel<Fortran::runtime::ValueReductionOperation<T>>() {                   \
71     return [](mlir::MLIRContext *context) -> mlir::Type {                      \
72       TypeBuilderFunc f{getModel<T>()};                                        \
73       auto refTy = fir::ReferenceType::get(f(context));                        \
74       return mlir::FunctionType::get(context, {f(context), f(context)},        \
75                                      refTy);                                   \
76     };                                                                         \
77   }
78 
79 #define REDUCTION_CHAR_OPERATION_MODEL(T)                                      \
80   template <>                                                                  \
81   constexpr TypeBuilderFunc                                                    \
82   getModel<Fortran::runtime::ReductionCharOperation<T>>() {                    \
83     return [](mlir::MLIRContext *context) -> mlir::Type {                      \
84       TypeBuilderFunc f{getModel<T>()};                                        \
85       auto voidTy = fir::LLVMPointerType::get(                                 \
86           context, mlir::IntegerType::get(context, 8));                        \
87       auto size_tTy =                                                          \
88           mlir::IntegerType::get(context, 8 * sizeof(std::size_t));            \
89       auto refTy = fir::ReferenceType::get(f(context));                        \
90       return mlir::FunctionType::get(                                          \
91           context, {refTy, size_tTy, refTy, refTy, size_tTy, size_tTy},        \
92           voidTy);                                                             \
93     };                                                                         \
94   }
95 
96 //===----------------------------------------------------------------------===//
97 // Type builder models
98 //===----------------------------------------------------------------------===//
99 
100 // TODO: all usages of sizeof in this file assume build ==  host == target.
101 // This will need to be re-visited for cross compilation.
102 
103 /// Return a function that returns the type signature model for the type `T`
104 /// when provided an MLIRContext*. This allows one to translate C(++) function
105 /// signatures from runtime header files to MLIR signatures into a static table
106 /// at compile-time.
107 ///
108 /// For example, when `T` is `int`, return a function that returns the MLIR
109 /// standard type `i32` when `sizeof(int)` is 4.
110 template <typename T>
111 static constexpr TypeBuilderFunc getModel();
112 
113 template <>
114 constexpr TypeBuilderFunc getModel<unsigned int>() {
115   return [](mlir::MLIRContext *context) -> mlir::Type {
116     return mlir::IntegerType::get(context, 8 * sizeof(unsigned int));
117   };
118 }
119 template <>
120 constexpr TypeBuilderFunc getModel<short int>() {
121   return [](mlir::MLIRContext *context) -> mlir::Type {
122     return mlir::IntegerType::get(context, 8 * sizeof(short int));
123   };
124 }
125 template <>
126 constexpr TypeBuilderFunc getModel<short int *>() {
127   return [](mlir::MLIRContext *context) -> mlir::Type {
128     TypeBuilderFunc f{getModel<short int>()};
129     return fir::ReferenceType::get(f(context));
130   };
131 }
132 template <>
133 constexpr TypeBuilderFunc getModel<const short int *>() {
134   return getModel<short int *>();
135 }
136 template <>
137 constexpr TypeBuilderFunc getModel<int>() {
138   return [](mlir::MLIRContext *context) -> mlir::Type {
139     return mlir::IntegerType::get(context, 8 * sizeof(int));
140   };
141 }
142 template <>
143 constexpr TypeBuilderFunc getModel<int &>() {
144   return [](mlir::MLIRContext *context) -> mlir::Type {
145     TypeBuilderFunc f{getModel<int>()};
146     return fir::ReferenceType::get(f(context));
147   };
148 }
149 template <>
150 constexpr TypeBuilderFunc getModel<int *>() {
151   return getModel<int &>();
152 }
153 template <>
154 constexpr TypeBuilderFunc getModel<const int *>() {
155   return [](mlir::MLIRContext *context) -> mlir::Type {
156     TypeBuilderFunc f{getModel<int>()};
157     return fir::ReferenceType::get(f(context));
158   };
159 }
160 template <>
161 constexpr TypeBuilderFunc getModel<char *>() {
162   return [](mlir::MLIRContext *context) -> mlir::Type {
163     return fir::ReferenceType::get(mlir::IntegerType::get(context, 8));
164   };
165 }
166 template <>
167 constexpr TypeBuilderFunc getModel<const char *>() {
168   return getModel<char *>();
169 }
170 template <>
171 constexpr TypeBuilderFunc getModel<const char16_t *>() {
172   return [](mlir::MLIRContext *context) -> mlir::Type {
173     return fir::ReferenceType::get(mlir::IntegerType::get(context, 16));
174   };
175 }
176 template <>
177 constexpr TypeBuilderFunc getModel<const char32_t *>() {
178   return [](mlir::MLIRContext *context) -> mlir::Type {
179     return fir::ReferenceType::get(mlir::IntegerType::get(context, 32));
180   };
181 }
182 template <>
183 constexpr TypeBuilderFunc getModel<char>() {
184   return [](mlir::MLIRContext *context) -> mlir::Type {
185     return mlir::IntegerType::get(context, 8 * sizeof(char));
186   };
187 }
188 template <>
189 constexpr TypeBuilderFunc getModel<signed char>() {
190   return [](mlir::MLIRContext *context) -> mlir::Type {
191     return mlir::IntegerType::get(context, 8 * sizeof(signed char));
192   };
193 }
194 template <>
195 constexpr TypeBuilderFunc getModel<signed char *>() {
196   return [](mlir::MLIRContext *context) -> mlir::Type {
197     TypeBuilderFunc f{getModel<signed char>()};
198     return fir::ReferenceType::get(f(context));
199   };
200 }
201 template <>
202 constexpr TypeBuilderFunc getModel<const signed char *>() {
203   return getModel<signed char *>();
204 }
205 template <>
206 constexpr TypeBuilderFunc getModel<char16_t>() {
207   return [](mlir::MLIRContext *context) -> mlir::Type {
208     return mlir::IntegerType::get(context, 8 * sizeof(char16_t));
209   };
210 }
211 template <>
212 constexpr TypeBuilderFunc getModel<char16_t *>() {
213   return [](mlir::MLIRContext *context) -> mlir::Type {
214     TypeBuilderFunc f{getModel<char16_t>()};
215     return fir::ReferenceType::get(f(context));
216   };
217 }
218 template <>
219 constexpr TypeBuilderFunc getModel<char32_t>() {
220   return [](mlir::MLIRContext *context) -> mlir::Type {
221     return mlir::IntegerType::get(context, 8 * sizeof(char32_t));
222   };
223 }
224 template <>
225 constexpr TypeBuilderFunc getModel<char32_t *>() {
226   return [](mlir::MLIRContext *context) -> mlir::Type {
227     TypeBuilderFunc f{getModel<char32_t>()};
228     return fir::ReferenceType::get(f(context));
229   };
230 }
231 template <>
232 constexpr TypeBuilderFunc getModel<unsigned char>() {
233   return [](mlir::MLIRContext *context) -> mlir::Type {
234     return mlir::IntegerType::get(context, 8 * sizeof(unsigned char));
235   };
236 }
237 template <>
238 constexpr TypeBuilderFunc getModel<void *>() {
239   return [](mlir::MLIRContext *context) -> mlir::Type {
240     return fir::LLVMPointerType::get(context,
241                                      mlir::IntegerType::get(context, 8));
242   };
243 }
244 template <>
245 constexpr TypeBuilderFunc getModel<void (*)(int)>() {
246   return [](mlir::MLIRContext *context) -> mlir::Type {
247     return fir::LLVMPointerType::get(
248         context,
249         mlir::FunctionType::get(context, /*inputs=*/{}, /*results*/ {}));
250   };
251 }
252 template <>
253 constexpr TypeBuilderFunc getModel<void **>() {
254   return [](mlir::MLIRContext *context) -> mlir::Type {
255     return fir::ReferenceType::get(
256         fir::LLVMPointerType::get(context, mlir::IntegerType::get(context, 8)));
257   };
258 }
259 template <>
260 constexpr TypeBuilderFunc getModel<long>() {
261   return [](mlir::MLIRContext *context) -> mlir::Type {
262     return mlir::IntegerType::get(context, 8 * sizeof(long));
263   };
264 }
265 template <>
266 constexpr TypeBuilderFunc getModel<long &>() {
267   return [](mlir::MLIRContext *context) -> mlir::Type {
268     TypeBuilderFunc f{getModel<long>()};
269     return fir::ReferenceType::get(f(context));
270   };
271 }
272 template <>
273 constexpr TypeBuilderFunc getModel<long *>() {
274   return getModel<long &>();
275 }
276 template <>
277 constexpr TypeBuilderFunc getModel<const long *>() {
278   return getModel<long *>();
279 }
280 template <>
281 constexpr TypeBuilderFunc getModel<long long>() {
282   return [](mlir::MLIRContext *context) -> mlir::Type {
283     return mlir::IntegerType::get(context, 8 * sizeof(long long));
284   };
285 }
286 template <>
287 constexpr TypeBuilderFunc getModel<Fortran::common::int128_t>() {
288   return [](mlir::MLIRContext *context) -> mlir::Type {
289     return mlir::IntegerType::get(context,
290                                   8 * sizeof(Fortran::common::int128_t));
291   };
292 }
293 template <>
294 constexpr TypeBuilderFunc getModel<long long &>() {
295   return [](mlir::MLIRContext *context) -> mlir::Type {
296     TypeBuilderFunc f{getModel<long long>()};
297     return fir::ReferenceType::get(f(context));
298   };
299 }
300 template <>
301 constexpr TypeBuilderFunc getModel<long long *>() {
302   return getModel<long long &>();
303 }
304 template <>
305 constexpr TypeBuilderFunc getModel<const long long *>() {
306   return getModel<long long *>();
307 }
308 template <>
309 constexpr TypeBuilderFunc getModel<unsigned long>() {
310   return [](mlir::MLIRContext *context) -> mlir::Type {
311     return mlir::IntegerType::get(context, 8 * sizeof(unsigned long));
312   };
313 }
314 template <>
315 constexpr TypeBuilderFunc getModel<unsigned long long>() {
316   return [](mlir::MLIRContext *context) -> mlir::Type {
317     return mlir::IntegerType::get(context, 8 * sizeof(unsigned long long));
318   };
319 }
320 template <>
321 constexpr TypeBuilderFunc getModel<double>() {
322   return [](mlir::MLIRContext *context) -> mlir::Type {
323     return mlir::Float64Type::get(context);
324   };
325 }
326 template <>
327 constexpr TypeBuilderFunc getModel<double &>() {
328   return [](mlir::MLIRContext *context) -> mlir::Type {
329     TypeBuilderFunc f{getModel<double>()};
330     return fir::ReferenceType::get(f(context));
331   };
332 }
333 template <>
334 constexpr TypeBuilderFunc getModel<double *>() {
335   return getModel<double &>();
336 }
337 template <>
338 constexpr TypeBuilderFunc getModel<const double *>() {
339   return getModel<double *>();
340 }
341 template <>
342 constexpr TypeBuilderFunc getModel<long double>() {
343   return [](mlir::MLIRContext *context) -> mlir::Type {
344     // See TODO at the top of the file. This is configuring for the host system
345     // - it might be incorrect when cross-compiling!
346     constexpr size_t size = sizeof(long double);
347     static_assert(size == 16 || size == 10 || size == 8,
348                   "unsupported long double size");
349     if constexpr (size == 16)
350       return mlir::Float128Type::get(context);
351     if constexpr (size == 10)
352       return mlir::Float80Type::get(context);
353     if constexpr (size == 8)
354       return mlir::Float64Type::get(context);
355     llvm_unreachable("failed static assert");
356   };
357 }
358 template <>
359 constexpr TypeBuilderFunc getModel<long double *>() {
360   return [](mlir::MLIRContext *context) -> mlir::Type {
361     TypeBuilderFunc f{getModel<long double>()};
362     return fir::ReferenceType::get(f(context));
363   };
364 }
365 template <>
366 constexpr TypeBuilderFunc getModel<const long double *>() {
367   return getModel<long double *>();
368 }
369 template <>
370 constexpr TypeBuilderFunc getModel<float>() {
371   return [](mlir::MLIRContext *context) -> mlir::Type {
372     return mlir::Float32Type::get(context);
373   };
374 }
375 template <>
376 constexpr TypeBuilderFunc getModel<float &>() {
377   return [](mlir::MLIRContext *context) -> mlir::Type {
378     TypeBuilderFunc f{getModel<float>()};
379     return fir::ReferenceType::get(f(context));
380   };
381 }
382 template <>
383 constexpr TypeBuilderFunc getModel<float *>() {
384   return getModel<float &>();
385 }
386 template <>
387 constexpr TypeBuilderFunc getModel<const float *>() {
388   return getModel<float *>();
389 }
390 template <>
391 constexpr TypeBuilderFunc getModel<bool>() {
392   return [](mlir::MLIRContext *context) -> mlir::Type {
393     return mlir::IntegerType::get(context, 1);
394   };
395 }
396 template <>
397 constexpr TypeBuilderFunc getModel<bool &>() {
398   return [](mlir::MLIRContext *context) -> mlir::Type {
399     TypeBuilderFunc f{getModel<bool>()};
400     return fir::ReferenceType::get(f(context));
401   };
402 }
403 template <>
404 constexpr TypeBuilderFunc getModel<unsigned short>() {
405   return [](mlir::MLIRContext *context) -> mlir::Type {
406     return mlir::IntegerType::get(
407         context, 8 * sizeof(unsigned short),
408         mlir::IntegerType::SignednessSemantics::Unsigned);
409   };
410 }
411 template <>
412 constexpr TypeBuilderFunc getModel<unsigned char *>() {
413   return [](mlir::MLIRContext *context) -> mlir::Type {
414     return fir::ReferenceType::get(mlir::IntegerType::get(context, 8));
415   };
416 }
417 template <>
418 constexpr TypeBuilderFunc getModel<const unsigned char *>() {
419   return getModel<unsigned char *>();
420 }
421 template <>
422 constexpr TypeBuilderFunc getModel<unsigned short *>() {
423   return [](mlir::MLIRContext *context) -> mlir::Type {
424     return fir::ReferenceType::get(
425         mlir::IntegerType::get(context, 8 * sizeof(unsigned short)));
426   };
427 }
428 template <>
429 constexpr TypeBuilderFunc getModel<const unsigned short *>() {
430   return getModel<unsigned short *>();
431 }
432 template <>
433 constexpr TypeBuilderFunc getModel<unsigned *>() {
434   return getModel<int *>();
435 }
436 template <>
437 constexpr TypeBuilderFunc getModel<const unsigned *>() {
438   return getModel<unsigned *>();
439 }
440 template <>
441 constexpr TypeBuilderFunc getModel<unsigned long *>() {
442   return [](mlir::MLIRContext *context) -> mlir::Type {
443     return fir::ReferenceType::get(
444         mlir::IntegerType::get(context, 8 * sizeof(unsigned long)));
445   };
446 }
447 template <>
448 constexpr TypeBuilderFunc getModel<const unsigned long *>() {
449   return getModel<unsigned long *>();
450 }
451 template <>
452 constexpr TypeBuilderFunc getModel<unsigned long long *>() {
453   return [](mlir::MLIRContext *context) -> mlir::Type {
454     return fir::ReferenceType::get(
455         mlir::IntegerType::get(context, 8 * sizeof(unsigned long long)));
456   };
457 }
458 template <>
459 constexpr TypeBuilderFunc getModel<const unsigned long long *>() {
460   return getModel<unsigned long long *>();
461 }
462 template <>
463 constexpr TypeBuilderFunc getModel<Fortran::common::uint128_t>() {
464   return getModel<Fortran::common::int128_t>();
465 }
466 template <>
467 constexpr TypeBuilderFunc getModel<Fortran::common::int128_t *>() {
468   return [](mlir::MLIRContext *context) -> mlir::Type {
469     TypeBuilderFunc f{getModel<Fortran::common::int128_t>()};
470     return fir::ReferenceType::get(f(context));
471   };
472 }
473 template <>
474 constexpr TypeBuilderFunc getModel<Fortran::common::uint128_t *>() {
475   return getModel<Fortran::common::int128_t *>();
476 }
477 template <>
478 constexpr TypeBuilderFunc getModel<const Fortran::common::uint128_t *>() {
479   return getModel<Fortran::common::uint128_t *>();
480 }
481 
482 // getModel<std::complex<T>> are not implemented on purpose.
483 // Prefer passing/returning the complex by reference in the runtime to
484 // avoid ABI issues.
485 // C++ std::complex is not an intrinsic type, and while it is storage
486 // compatible with C/Fortran complex type, it follows the struct value passing
487 // ABI rule, which may differ from how C complex are passed on some platforms.
488 
489 template <>
490 constexpr TypeBuilderFunc getModel<std::complex<float> &>() {
491   return [](mlir::MLIRContext *context) -> mlir::Type {
492     mlir::Type floatTy = getModel<float>()(context);
493     return fir::ReferenceType::get(mlir::ComplexType::get(floatTy));
494   };
495 }
496 template <>
497 constexpr TypeBuilderFunc getModel<std::complex<float> *>() {
498   return getModel<std::complex<float> &>();
499 }
500 template <>
501 constexpr TypeBuilderFunc getModel<const std::complex<float> *>() {
502   return getModel<std::complex<float> *>();
503 }
504 template <>
505 constexpr TypeBuilderFunc getModel<std::complex<double> &>() {
506   return [](mlir::MLIRContext *context) -> mlir::Type {
507     mlir::Type floatTy = getModel<double>()(context);
508     return fir::ReferenceType::get(mlir::ComplexType::get(floatTy));
509   };
510 }
511 template <>
512 constexpr TypeBuilderFunc getModel<std::complex<double> *>() {
513   return getModel<std::complex<double> &>();
514 }
515 template <>
516 constexpr TypeBuilderFunc getModel<const std::complex<double> *>() {
517   return getModel<std::complex<double> *>();
518 }
519 template <>
520 constexpr TypeBuilderFunc getModel<c_float_complex_t>() {
521   return [](mlir::MLIRContext *context) -> mlir::Type {
522     mlir::Type floatTy = getModel<float>()(context);
523     return mlir::ComplexType::get(floatTy);
524   };
525 }
526 template <>
527 constexpr TypeBuilderFunc getModel<c_double_complex_t>() {
528   return [](mlir::MLIRContext *context) -> mlir::Type {
529     mlir::Type floatTy = getModel<double>()(context);
530     return mlir::ComplexType::get(floatTy);
531   };
532 }
533 template <>
534 constexpr TypeBuilderFunc getModel<const Fortran::runtime::Descriptor &>() {
535   return [](mlir::MLIRContext *context) -> mlir::Type {
536     return fir::BoxType::get(mlir::NoneType::get(context));
537   };
538 }
539 template <>
540 constexpr TypeBuilderFunc getModel<Fortran::runtime::Descriptor &>() {
541   return [](mlir::MLIRContext *context) -> mlir::Type {
542     return fir::ReferenceType::get(
543         fir::BoxType::get(mlir::NoneType::get(context)));
544   };
545 }
546 template <>
547 constexpr TypeBuilderFunc getModel<const Fortran::runtime::Descriptor *>() {
548   return getModel<const Fortran::runtime::Descriptor &>();
549 }
550 template <>
551 constexpr TypeBuilderFunc getModel<Fortran::runtime::Descriptor *>() {
552   return getModel<Fortran::runtime::Descriptor &>();
553 }
554 template <>
555 constexpr TypeBuilderFunc getModel<Fortran::common::TypeCategory>() {
556   return [](mlir::MLIRContext *context) -> mlir::Type {
557     return mlir::IntegerType::get(context,
558                                   sizeof(Fortran::common::TypeCategory) * 8);
559   };
560 }
561 template <>
562 constexpr TypeBuilderFunc
563 getModel<const Fortran::runtime::typeInfo::DerivedType &>() {
564   return [](mlir::MLIRContext *context) -> mlir::Type {
565     return fir::ReferenceType::get(mlir::NoneType::get(context));
566   };
567 }
568 template <>
569 constexpr TypeBuilderFunc
570 getModel<const Fortran::runtime::typeInfo::DerivedType *>() {
571   return [](mlir::MLIRContext *context) -> mlir::Type {
572     return fir::ReferenceType::get(mlir::NoneType::get(context));
573   };
574 }
575 template <>
576 constexpr TypeBuilderFunc getModel<void>() {
577   return [](mlir::MLIRContext *context) -> mlir::Type {
578     return mlir::NoneType::get(context);
579   };
580 }
581 
582 REDUCTION_REF_OPERATION_MODEL(std::int8_t)
583 REDUCTION_VALUE_OPERATION_MODEL(std::int8_t)
584 REDUCTION_REF_OPERATION_MODEL(std::int16_t)
585 REDUCTION_VALUE_OPERATION_MODEL(std::int16_t)
586 REDUCTION_REF_OPERATION_MODEL(std::int32_t)
587 REDUCTION_VALUE_OPERATION_MODEL(std::int32_t)
588 REDUCTION_REF_OPERATION_MODEL(std::int64_t)
589 REDUCTION_VALUE_OPERATION_MODEL(std::int64_t)
590 REDUCTION_REF_OPERATION_MODEL(Fortran::common::int128_t)
591 REDUCTION_VALUE_OPERATION_MODEL(Fortran::common::int128_t)
592 
593 REDUCTION_REF_OPERATION_MODEL(std::uint8_t)
594 REDUCTION_VALUE_OPERATION_MODEL(std::uint8_t)
595 REDUCTION_REF_OPERATION_MODEL(std::uint16_t)
596 REDUCTION_VALUE_OPERATION_MODEL(std::uint16_t)
597 REDUCTION_REF_OPERATION_MODEL(std::uint32_t)
598 REDUCTION_VALUE_OPERATION_MODEL(std::uint32_t)
599 REDUCTION_REF_OPERATION_MODEL(std::uint64_t)
600 REDUCTION_VALUE_OPERATION_MODEL(std::uint64_t)
601 REDUCTION_REF_OPERATION_MODEL(Fortran::common::uint128_t)
602 REDUCTION_VALUE_OPERATION_MODEL(Fortran::common::uint128_t)
603 
604 REDUCTION_REF_OPERATION_MODEL(float)
605 REDUCTION_VALUE_OPERATION_MODEL(float)
606 REDUCTION_REF_OPERATION_MODEL(double)
607 REDUCTION_VALUE_OPERATION_MODEL(double)
608 REDUCTION_REF_OPERATION_MODEL(long double)
609 REDUCTION_VALUE_OPERATION_MODEL(long double)
610 
611 // FIXME: the runtime is not using the correct ABIs when calling complex
612 // callbacks. lowering either need to create wrappers or just have an inline
613 // implementation for it. https://github.com/llvm/llvm-project/issues/110674
614 template <>
615 constexpr TypeBuilderFunc
616 getModel<Fortran::runtime::ValueReductionOperation<std::complex<float>>>() {
617   return [](mlir::MLIRContext *context) -> mlir::Type {
618     mlir::Type cplx = mlir::ComplexType::get(getModel<float>()(context));
619     auto refTy = fir::ReferenceType::get(cplx);
620     return mlir::FunctionType::get(context, {cplx, cplx}, refTy);
621   };
622 }
623 template <>
624 constexpr TypeBuilderFunc
625 getModel<Fortran::runtime::ValueReductionOperation<std::complex<double>>>() {
626   return [](mlir::MLIRContext *context) -> mlir::Type {
627     mlir::Type cplx = mlir::ComplexType::get(getModel<double>()(context));
628     auto refTy = fir::ReferenceType::get(cplx);
629     return mlir::FunctionType::get(context, {cplx, cplx}, refTy);
630   };
631 }
632 template <>
633 constexpr TypeBuilderFunc
634 getModel<Fortran::runtime::ReferenceReductionOperation<std::complex<float>>>() {
635   return [](mlir::MLIRContext *context) -> mlir::Type {
636     mlir::Type cplx = mlir::ComplexType::get(getModel<float>()(context));
637     auto refTy = fir::ReferenceType::get(cplx);
638     return mlir::FunctionType::get(context, {refTy, refTy}, refTy);
639   };
640 }
641 template <>
642 constexpr TypeBuilderFunc getModel<
643     Fortran::runtime::ReferenceReductionOperation<std::complex<double>>>() {
644   return [](mlir::MLIRContext *context) -> mlir::Type {
645     mlir::Type cplx = mlir::ComplexType::get(getModel<double>()(context));
646     auto refTy = fir::ReferenceType::get(cplx);
647     return mlir::FunctionType::get(context, {refTy, refTy}, refTy);
648   };
649 }
650 
651 REDUCTION_CHAR_OPERATION_MODEL(char)
652 REDUCTION_CHAR_OPERATION_MODEL(char16_t)
653 REDUCTION_CHAR_OPERATION_MODEL(char32_t)
654 
655 template <>
656 constexpr TypeBuilderFunc
657 getModel<Fortran::runtime::ReductionDerivedTypeOperation>() {
658   return [](mlir::MLIRContext *context) -> mlir::Type {
659     auto voidTy =
660         fir::LLVMPointerType::get(context, mlir::IntegerType::get(context, 8));
661     return mlir::FunctionType::get(context, {voidTy, voidTy, voidTy}, voidTy);
662   };
663 }
664 
665 template <typename...>
666 struct RuntimeTableKey;
667 template <typename RT, typename... ATs>
668 struct RuntimeTableKey<RT(ATs...)> {
669   static constexpr FuncTypeBuilderFunc getTypeModel() {
670     return [](mlir::MLIRContext *ctxt) {
671       TypeBuilderFunc ret = getModel<RT>();
672       std::array<TypeBuilderFunc, sizeof...(ATs)> args = {getModel<ATs>()...};
673       mlir::Type retTy = ret(ctxt);
674       llvm::SmallVector<mlir::Type, sizeof...(ATs)> argTys;
675       for (auto f : args)
676         argTys.push_back(f(ctxt));
677       if (mlir::isa<mlir::NoneType>(retTy))
678         return mlir::FunctionType::get(ctxt, argTys, {});
679       return mlir::FunctionType::get(ctxt, argTys, {retTy});
680     };
681   }
682 };
683 
684 //===----------------------------------------------------------------------===//
685 // Runtime table building (constexpr folded)
686 //===----------------------------------------------------------------------===//
687 
688 template <char... Cs>
689 using RuntimeIdentifier = std::integer_sequence<char, Cs...>;
690 
691 namespace details {
692 template <typename T, T... As, T... Bs>
693 static constexpr std::integer_sequence<T, As..., Bs...>
694 concat(std::integer_sequence<T, As...>, std::integer_sequence<T, Bs...>) {
695   return {};
696 }
697 template <typename T, T... As, T... Bs, typename... Cs>
698 static constexpr auto concat(std::integer_sequence<T, As...>,
699                              std::integer_sequence<T, Bs...>, Cs...) {
700   return concat(std::integer_sequence<T, As..., Bs...>{}, Cs{}...);
701 }
702 template <typename T>
703 static constexpr std::integer_sequence<T> concat(std::integer_sequence<T>) {
704   return {};
705 }
706 template <typename T, T a>
707 static constexpr auto filterZero(std::integer_sequence<T, a>) {
708   if constexpr (a != 0) {
709     return std::integer_sequence<T, a>{};
710   } else {
711     return std::integer_sequence<T>{};
712   }
713 }
714 template <typename T, T... b>
715 static constexpr auto filter(std::integer_sequence<T, b...>) {
716   if constexpr (sizeof...(b) > 0) {
717     return details::concat(filterZero(std::integer_sequence<T, b>{})...);
718   } else {
719     return std::integer_sequence<T>{};
720   }
721 }
722 } // namespace details
723 
724 template <typename...>
725 struct RuntimeTableEntry;
726 template <typename KT, char... Cs>
727 struct RuntimeTableEntry<RuntimeTableKey<KT>, RuntimeIdentifier<Cs...>> {
728   static constexpr FuncTypeBuilderFunc getTypeModel() {
729     return RuntimeTableKey<KT>::getTypeModel();
730   }
731   static constexpr const char name[sizeof...(Cs) + 1] = {Cs..., '\0'};
732 };
733 
734 /// These macros are used to create the RuntimeTableEntry for runtime function.
735 ///
736 /// For example the runtime function `SumReal4` will be expanded as shown below
737 /// (simplified version)
738 ///
739 /// ```
740 /// fir::runtime::RuntimeTableEntry<fir::runtime::RuntimeTableKey<
741 ///     decltype(_FortranASumReal4)>, "_FortranASumReal4"))>
742 /// ```
743 /// These entries are then used to generate the MLIR FunctionType that
744 /// correspond to the runtime function declaration in C++.
745 #undef FirE
746 #define FirE(L, I) (I < sizeof(L) / sizeof(*L) ? L[I] : 0)
747 #define FirQuoteKey(X) #X
748 #define ExpandAndQuoteKey(X) FirQuoteKey(X)
749 #define FirMacroExpandKey(X)                                                   \
750   FirE(X, 0), FirE(X, 1), FirE(X, 2), FirE(X, 3), FirE(X, 4), FirE(X, 5),      \
751       FirE(X, 6), FirE(X, 7), FirE(X, 8), FirE(X, 9), FirE(X, 10),             \
752       FirE(X, 11), FirE(X, 12), FirE(X, 13), FirE(X, 14), FirE(X, 15),         \
753       FirE(X, 16), FirE(X, 17), FirE(X, 18), FirE(X, 19), FirE(X, 20),         \
754       FirE(X, 21), FirE(X, 22), FirE(X, 23), FirE(X, 24), FirE(X, 25),         \
755       FirE(X, 26), FirE(X, 27), FirE(X, 28), FirE(X, 29), FirE(X, 30),         \
756       FirE(X, 31), FirE(X, 32), FirE(X, 33), FirE(X, 34), FirE(X, 35),         \
757       FirE(X, 36), FirE(X, 37), FirE(X, 38), FirE(X, 39), FirE(X, 40),         \
758       FirE(X, 41), FirE(X, 42), FirE(X, 43), FirE(X, 44), FirE(X, 45),         \
759       FirE(X, 46), FirE(X, 47), FirE(X, 48), FirE(X, 49)
760 #define FirExpandKey(X) FirMacroExpandKey(FirQuoteKey(X))
761 #define FirFullSeq(X) std::integer_sequence<char, FirExpandKey(X)>
762 #define FirAsSequence(X)                                                       \
763   decltype(fir::runtime::details::filter(FirFullSeq(X){}))
764 #define FirmkKey(X)                                                            \
765   fir::runtime::RuntimeTableEntry<fir::runtime::RuntimeTableKey<decltype(X)>,  \
766                                   FirAsSequence(X)>
767 #define mkRTKey(X) FirmkKey(RTNAME(X))
768 #define EXPAND_AND_QUOTE_KEY(S) ExpandAndQuoteKey(RTNAME(S))
769 
770 /// Get (or generate) the MLIR FuncOp for a given runtime function. Its template
771 /// argument is intended to be of the form: <mkRTKey(runtime function name)>.
772 template <typename RuntimeEntry>
773 static mlir::func::FuncOp getRuntimeFunc(mlir::Location loc,
774                                          fir::FirOpBuilder &builder) {
775   using namespace Fortran::runtime;
776   auto name = RuntimeEntry::name;
777   auto func = builder.getNamedFunction(name);
778   if (func)
779     return func;
780   auto funTy = RuntimeEntry::getTypeModel()(builder.getContext());
781   func = builder.createFunction(loc, name, funTy);
782   func->setAttr(FIROpsDialect::getFirRuntimeAttrName(), builder.getUnitAttr());
783   return func;
784 }
785 
786 namespace helper {
787 template <int N, typename A>
788 void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
789                      fir::FirOpBuilder &builder, mlir::Location loc,
790                      mlir::FunctionType fTy, A arg) {
791   result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
792 }
793 
794 template <int N, typename A, typename... As>
795 void createArguments(llvm::SmallVectorImpl<mlir::Value> &result,
796                      fir::FirOpBuilder &builder, mlir::Location loc,
797                      mlir::FunctionType fTy, A arg, As... args) {
798   result.emplace_back(builder.createConvert(loc, fTy.getInput(N), arg));
799   createArguments<N + 1>(result, builder, loc, fTy, args...);
800 }
801 } // namespace helper
802 
803 /// Create a SmallVector of arguments for a runtime call.
804 template <typename... As>
805 llvm::SmallVector<mlir::Value>
806 createArguments(fir::FirOpBuilder &builder, mlir::Location loc,
807                 mlir::FunctionType fTy, As... args) {
808   llvm::SmallVector<mlir::Value> result;
809   helper::createArguments<0>(result, builder, loc, fTy, args...);
810   return result;
811 }
812 
813 } // namespace fir::runtime
814 
815 #endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_RTBUILDER_H
816