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