1 //===-- IntrinsicCall.cpp -------------------------------------------------===// 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 // Helper routines for constructing the FIR dialect of MLIR. As FIR is a 10 // dialect of MLIR, it makes extensive use of MLIR interfaces and MLIR's coding 11 // style (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this 12 // module. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "flang/Lower/IntrinsicCall.h" 17 #include "flang/Common/static-multimap-view.h" 18 #include "flang/Lower/Mangler.h" 19 #include "flang/Lower/Runtime.h" 20 #include "flang/Lower/StatementContext.h" 21 #include "flang/Lower/SymbolMap.h" 22 #include "flang/Lower/Todo.h" 23 #include "flang/Optimizer/Builder/Character.h" 24 #include "flang/Optimizer/Builder/Complex.h" 25 #include "flang/Optimizer/Builder/FIRBuilder.h" 26 #include "flang/Optimizer/Builder/MutableBox.h" 27 #include "flang/Optimizer/Builder/Runtime/Inquiry.h" 28 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" 29 #include "flang/Optimizer/Builder/Runtime/Reduction.h" 30 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 31 #include "flang/Optimizer/Support/FatalError.h" 32 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 33 #include "llvm/Support/CommandLine.h" 34 35 #define DEBUG_TYPE "flang-lower-intrinsic" 36 37 #define PGMATH_DECLARE 38 #include "flang/Evaluate/pgmath.h.inc" 39 40 /// Enums used to templatize and share lowering of MIN and MAX. 41 enum class Extremum { Min, Max }; 42 43 // There are different ways to deal with NaNs in MIN and MAX. 44 // Known existing behaviors are listed below and can be selected for 45 // f18 MIN/MAX implementation. 46 enum class ExtremumBehavior { 47 // Note: the Signaling/quiet aspect of NaNs in the behaviors below are 48 // not described because there is no way to control/observe such aspect in 49 // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this 50 // aspect that are therefore currently not enforced. In the descriptions 51 // below, NaNs can be signaling or quite. Returned NaNs may be signaling 52 // if one of the input NaN was signaling but it cannot be guaranteed either. 53 // Existing compilers using an IEEE behavior (gfortran) also do not fulfill 54 // signaling/quiet requirements. 55 IeeeMinMaximumNumber, 56 // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): 57 // If one of the argument is and number and the other is NaN, return the 58 // number. If both arguements are NaN, return NaN. 59 // Compilers: gfortran. 60 IeeeMinMaximum, 61 // IEEE minimum/maximum behavior (754-2019, section 9.6): 62 // If one of the argument is NaN, return NaN. 63 MinMaxss, 64 // x86 minss/maxss behavior: 65 // If the second argument is a number and the other is NaN, return the number. 66 // In all other cases where at least one operand is NaN, return NaN. 67 // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. 68 PgfortranLlvm, 69 // "Opposite of" x86 minss/maxss behavior: 70 // If the first argument is a number and the other is NaN, return the 71 // number. 72 // In all other cases where at least one operand is NaN, return NaN. 73 // Compilers: xlf (only for MIN), and pgfortran (with llvm). 74 IeeeMinMaxNum 75 // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): 76 // TODO: Not implemented. 77 // It is the only behavior where the signaling/quiet aspect of a NaN argument 78 // impacts if the result should be NaN or the argument that is a number. 79 // LLVM/MLIR do not provide ways to observe this aspect, so it is not 80 // possible to implement it without some target dependent runtime. 81 }; 82 83 /// This file implements lowering of Fortran intrinsic procedures. 84 /// Intrinsics are lowered to a mix of FIR and MLIR operations as 85 /// well as call to runtime functions or LLVM intrinsics. 86 87 /// Lowering of intrinsic procedure calls is based on a map that associates 88 /// Fortran intrinsic generic names to FIR generator functions. 89 /// All generator functions are member functions of the IntrinsicLibrary class 90 /// and have the same interface. 91 /// If no generator is given for an intrinsic name, a math runtime library 92 /// is searched for an implementation and, if a runtime function is found, 93 /// a call is generated for it. LLVM intrinsics are handled as a math 94 /// runtime library here. 95 96 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() { 97 return fir::UnboxedValue{}; 98 } 99 100 /// Test if an ExtendedValue is absent. 101 static bool isAbsent(const fir::ExtendedValue &exv) { 102 return !fir::getBase(exv); 103 } 104 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) { 105 return args.size() <= argIndex || isAbsent(args[argIndex]); 106 } 107 108 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that 109 /// take a DIM argument. 110 template <typename FD> 111 static fir::ExtendedValue 112 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 113 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 114 llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg, 115 mlir::Value mask, int rank) { 116 117 // Create mutable fir.box to be passed to the runtime for the result. 118 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 119 fir::MutableBoxValue resultMutableBox = 120 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 121 mlir::Value resultIrBox = 122 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 123 124 mlir::Value dim = 125 isAbsent(dimArg) 126 ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 127 : fir::getBase(dimArg); 128 funcDim(builder, loc, resultIrBox, array, dim, mask); 129 130 fir::ExtendedValue res = 131 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 132 return res.match( 133 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 134 // Add cleanup code 135 assert(stmtCtx); 136 fir::FirOpBuilder *bldr = &builder; 137 mlir::Value temp = box.getAddr(); 138 stmtCtx->attachCleanup( 139 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 140 return box; 141 }, 142 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 143 // Add cleanup code 144 assert(stmtCtx); 145 fir::FirOpBuilder *bldr = &builder; 146 mlir::Value temp = box.getAddr(); 147 stmtCtx->attachCleanup( 148 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 149 return box; 150 }, 151 [&](const auto &) -> fir::ExtendedValue { 152 fir::emitFatalError(loc, errMsg); 153 }); 154 } 155 156 /// Process calls to Product, Sum intrinsic functions 157 template <typename FN, typename FD> 158 static fir::ExtendedValue 159 genProdOrSum(FN func, FD funcDim, mlir::Type resultType, 160 fir::FirOpBuilder &builder, mlir::Location loc, 161 Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg, 162 llvm::ArrayRef<fir::ExtendedValue> args) { 163 164 assert(args.size() == 3); 165 166 // Handle required array argument 167 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 168 mlir::Value array = fir::getBase(arryTmp); 169 int rank = arryTmp.rank(); 170 assert(rank >= 1); 171 172 // Handle optional mask argument 173 auto mask = isAbsent(args[2]) 174 ? builder.create<fir::AbsentOp>( 175 loc, fir::BoxType::get(builder.getI1Type())) 176 : builder.createBox(loc, args[2]); 177 178 bool absentDim = isAbsent(args[1]); 179 180 // We call the type specific versions because the result is scalar 181 // in the case below. 182 if (absentDim || rank == 1) { 183 mlir::Type ty = array.getType(); 184 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty); 185 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 186 if (fir::isa_complex(eleTy)) { 187 mlir::Value result = builder.createTemporary(loc, eleTy); 188 func(builder, loc, array, mask, result); 189 return builder.create<fir::LoadOp>(loc, result); 190 } 191 auto resultBox = builder.create<fir::AbsentOp>( 192 loc, fir::BoxType::get(builder.getI1Type())); 193 return func(builder, loc, array, mask, resultBox); 194 } 195 // Handle Product/Sum cases that have an array result. 196 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 197 args[1], mask, rank); 198 } 199 200 // TODO error handling -> return a code or directly emit messages ? 201 struct IntrinsicLibrary { 202 203 // Constructors. 204 explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc, 205 Fortran::lower::StatementContext *stmtCtx = nullptr) 206 : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {} 207 IntrinsicLibrary() = delete; 208 IntrinsicLibrary(const IntrinsicLibrary &) = delete; 209 210 /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg 211 /// and expected result type \p resultType. 212 fir::ExtendedValue genIntrinsicCall(llvm::StringRef name, 213 llvm::Optional<mlir::Type> resultType, 214 llvm::ArrayRef<fir::ExtendedValue> arg); 215 216 /// Search a runtime function that is associated to the generic intrinsic name 217 /// and whose signature matches the intrinsic arguments and result types. 218 /// If no such runtime function is found but a runtime function associated 219 /// with the Fortran generic exists and has the same number of arguments, 220 /// conversions will be inserted before and/or after the call. This is to 221 /// mainly to allow 16 bits float support even-though little or no math 222 /// runtime is currently available for it. 223 mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, 224 llvm::ArrayRef<mlir::Value>); 225 226 using RuntimeCallGenerator = std::function<mlir::Value( 227 fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>; 228 RuntimeCallGenerator 229 getRuntimeCallGenerator(llvm::StringRef name, 230 mlir::FunctionType soughtFuncType); 231 232 /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in 233 /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation 234 /// if the argument is an integer, into llvm intrinsics if the argument is 235 /// real and to the `hypot` math routine if the argument is of complex type. 236 mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>); 237 fir::ExtendedValue genAssociated(mlir::Type, 238 llvm::ArrayRef<fir::ExtendedValue>); 239 fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 240 template <Extremum, ExtremumBehavior> 241 mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>); 242 /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments 243 /// in the llvm::ArrayRef. 244 mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>); 245 fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 246 fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 247 fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 248 fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 249 250 /// Define the different FIR generators that can be mapped to intrinsic to 251 /// generate the related code. 252 using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); 253 using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum); 254 using Generator = std::variant<ElementalGenerator, ExtendedGenerator>; 255 256 template <typename GeneratorType> 257 fir::ExtendedValue 258 outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, 259 llvm::Optional<mlir::Type> resultType, 260 llvm::ArrayRef<fir::ExtendedValue> args); 261 262 template <typename GeneratorType> 263 mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name, 264 mlir::FunctionType, bool loadRefArguments = false); 265 266 /// Generate calls to ElementalGenerator, handling the elemental aspects 267 template <typename GeneratorType> 268 fir::ExtendedValue 269 genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, 270 llvm::ArrayRef<fir::ExtendedValue> args, bool outline); 271 272 /// Helper to invoke code generator for the intrinsics given arguments. 273 mlir::Value invokeGenerator(ElementalGenerator generator, 274 mlir::Type resultType, 275 llvm::ArrayRef<mlir::Value> args); 276 mlir::Value invokeGenerator(RuntimeCallGenerator generator, 277 mlir::Type resultType, 278 llvm::ArrayRef<mlir::Value> args); 279 mlir::Value invokeGenerator(ExtendedGenerator generator, 280 mlir::Type resultType, 281 llvm::ArrayRef<mlir::Value> args); 282 283 /// Add clean-up for \p temp to the current statement context; 284 void addCleanUpForTemp(mlir::Location loc, mlir::Value temp); 285 /// Helper function for generating code clean-up for result descriptors 286 fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 287 mlir::Type resultType, 288 llvm::StringRef errMsg); 289 290 fir::FirOpBuilder &builder; 291 mlir::Location loc; 292 Fortran::lower::StatementContext *stmtCtx; 293 }; 294 295 struct IntrinsicDummyArgument { 296 const char *name = nullptr; 297 Fortran::lower::LowerIntrinsicArgAs lowerAs = 298 Fortran::lower::LowerIntrinsicArgAs::Value; 299 bool handleDynamicOptional = false; 300 }; 301 302 struct Fortran::lower::IntrinsicArgumentLoweringRules { 303 /// There is no more than 7 non repeated arguments in Fortran intrinsics. 304 IntrinsicDummyArgument args[7]; 305 constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } 306 }; 307 308 /// Structure describing what needs to be done to lower intrinsic "name". 309 struct IntrinsicHandler { 310 const char *name; 311 IntrinsicLibrary::Generator generator; 312 // The following may be omitted in the table below. 313 Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {}; 314 bool isElemental = true; 315 }; 316 317 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value; 318 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box; 319 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired; 320 using I = IntrinsicLibrary; 321 322 /// Flag to indicate that an intrinsic argument has to be handled as 323 /// being dynamically optional (e.g. special handling when actual 324 /// argument is an optional variable in the current scope). 325 static constexpr bool handleDynamicOptional = true; 326 327 /// Table that drives the fir generation depending on the intrinsic. 328 /// one to one mapping with Fortran arguments. If no mapping is 329 /// defined here for a generic intrinsic, genRuntimeCall will be called 330 /// to look for a match in the runtime a emit a call. Note that the argument 331 /// lowering rules for an intrinsic need to be provided only if at least one 332 /// argument must not be lowered by value. In which case, the lowering rules 333 /// should be provided for all the intrinsic arguments for completeness. 334 static constexpr IntrinsicHandler handlers[]{ 335 {"abs", &I::genAbs}, 336 {"associated", 337 &I::genAssociated, 338 {{{"pointer", asInquired}, {"target", asInquired}}}, 339 /*isElemental=*/false}, 340 {"char", &I::genChar}, 341 {"iand", &I::genIand}, 342 {"sum", 343 &I::genSum, 344 {{{"array", asBox}, 345 {"dim", asValue}, 346 {"mask", asBox, handleDynamicOptional}}}, 347 /*isElemental=*/false}, 348 {"ubound", 349 &I::genUbound, 350 {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}}, 351 /*isElemental=*/false}, 352 }; 353 354 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { 355 auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { 356 return name.compare(handler.name) > 0; 357 }; 358 auto result = 359 std::lower_bound(std::begin(handlers), std::end(handlers), name, compare); 360 return result != std::end(handlers) && result->name == name ? result 361 : nullptr; 362 } 363 364 //===----------------------------------------------------------------------===// 365 // Math runtime description and matching utility 366 //===----------------------------------------------------------------------===// 367 368 /// Command line option to modify math runtime version used to implement 369 /// intrinsics. 370 enum MathRuntimeVersion { fastVersion, llvmOnly }; 371 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion( 372 "math-runtime", llvm::cl::desc("Select math runtime version:"), 373 llvm::cl::values( 374 clEnumValN(fastVersion, "fast", "use pgmath fast runtime"), 375 clEnumValN(llvmOnly, "llvm", 376 "only use LLVM intrinsics (may be incomplete)")), 377 llvm::cl::init(fastVersion)); 378 379 struct RuntimeFunction { 380 // llvm::StringRef comparison operator are not constexpr, so use string_view. 381 using Key = std::string_view; 382 // Needed for implicit compare with keys. 383 constexpr operator Key() const { return key; } 384 Key key; // intrinsic name 385 llvm::StringRef symbol; 386 fir::runtime::FuncTypeBuilderFunc typeGenerator; 387 }; 388 389 #define RUNTIME_STATIC_DESCRIPTION(name, func) \ 390 {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()}, 391 static constexpr RuntimeFunction pgmathFast[] = { 392 #define PGMATH_FAST 393 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 394 #include "flang/Evaluate/pgmath.h.inc" 395 }; 396 397 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) { 398 mlir::Type t = mlir::FloatType::getF32(context); 399 return mlir::FunctionType::get(context, {t}, {t}); 400 } 401 402 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) { 403 mlir::Type t = mlir::FloatType::getF64(context); 404 return mlir::FunctionType::get(context, {t}, {t}); 405 } 406 407 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) { 408 auto t = mlir::FloatType::getF32(context); 409 return mlir::FunctionType::get(context, {t, t}, {t}); 410 } 411 412 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) { 413 auto t = mlir::FloatType::getF64(context); 414 return mlir::FunctionType::get(context, {t, t}, {t}); 415 } 416 417 // TODO : Fill-up this table with more intrinsic. 418 // Note: These are also defined as operations in LLVM dialect. See if this 419 // can be use and has advantages. 420 static constexpr RuntimeFunction llvmIntrinsics[] = { 421 {"abs", "llvm.fabs.f32", genF32F32FuncType}, 422 {"abs", "llvm.fabs.f64", genF64F64FuncType}, 423 {"pow", "llvm.pow.f32", genF32F32F32FuncType}, 424 {"pow", "llvm.pow.f64", genF64F64F64FuncType}, 425 }; 426 427 // This helper class computes a "distance" between two function types. 428 // The distance measures how many narrowing conversions of actual arguments 429 // and result of "from" must be made in order to use "to" instead of "from". 430 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is 431 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means 432 // if no implementation of ACOS(REAL(10)) is available, it is better to use 433 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)). 434 // Note that this is not a symmetric distance and the order of "from" and "to" 435 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it 436 // may be safe to replace foo by bar, but not the opposite. 437 class FunctionDistance { 438 public: 439 FunctionDistance() : infinite{true} {} 440 441 FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { 442 unsigned nInputs = from.getNumInputs(); 443 unsigned nResults = from.getNumResults(); 444 if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { 445 infinite = true; 446 } else { 447 for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i) 448 addArgumentDistance(from.getInput(i), to.getInput(i)); 449 for (decltype(nResults) i = 0; i < nResults && !infinite; ++i) 450 addResultDistance(to.getResult(i), from.getResult(i)); 451 } 452 } 453 454 /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be 455 /// false if both d1 and d2 are infinite. This implies that 456 /// d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1) 457 bool isSmallerThan(const FunctionDistance &d) const { 458 return !infinite && 459 (d.infinite || std::lexicographical_compare( 460 conversions.begin(), conversions.end(), 461 d.conversions.begin(), d.conversions.end())); 462 } 463 464 bool isLosingPrecision() const { 465 return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0; 466 } 467 468 bool isInfinite() const { return infinite; } 469 470 private: 471 enum class Conversion { Forbidden, None, Narrow, Extend }; 472 473 void addArgumentDistance(mlir::Type from, mlir::Type to) { 474 switch (conversionBetweenTypes(from, to)) { 475 case Conversion::Forbidden: 476 infinite = true; 477 break; 478 case Conversion::None: 479 break; 480 case Conversion::Narrow: 481 conversions[narrowingArg]++; 482 break; 483 case Conversion::Extend: 484 conversions[nonNarrowingArg]++; 485 break; 486 } 487 } 488 489 void addResultDistance(mlir::Type from, mlir::Type to) { 490 switch (conversionBetweenTypes(from, to)) { 491 case Conversion::Forbidden: 492 infinite = true; 493 break; 494 case Conversion::None: 495 break; 496 case Conversion::Narrow: 497 conversions[nonExtendingResult]++; 498 break; 499 case Conversion::Extend: 500 conversions[extendingResult]++; 501 break; 502 } 503 } 504 505 // Floating point can be mlir::FloatType or fir::real 506 static unsigned getFloatingPointWidth(mlir::Type t) { 507 if (auto f{t.dyn_cast<mlir::FloatType>()}) 508 return f.getWidth(); 509 // FIXME: Get width another way for fir.real/complex 510 // - use fir/KindMapping.h and llvm::Type 511 // - or use evaluate/type.h 512 if (auto r{t.dyn_cast<fir::RealType>()}) 513 return r.getFKind() * 4; 514 if (auto cplx{t.dyn_cast<fir::ComplexType>()}) 515 return cplx.getFKind() * 4; 516 llvm_unreachable("not a floating-point type"); 517 } 518 519 static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { 520 if (from == to) 521 return Conversion::None; 522 523 if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) { 524 if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) { 525 return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow 526 : Conversion::Extend; 527 } 528 } 529 530 if (fir::isa_real(from) && fir::isa_real(to)) { 531 return getFloatingPointWidth(from) > getFloatingPointWidth(to) 532 ? Conversion::Narrow 533 : Conversion::Extend; 534 } 535 536 if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) { 537 if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) { 538 return getFloatingPointWidth(fromCplxTy) > 539 getFloatingPointWidth(toCplxTy) 540 ? Conversion::Narrow 541 : Conversion::Extend; 542 } 543 } 544 // Notes: 545 // - No conversion between character types, specialization of runtime 546 // functions should be made instead. 547 // - It is not clear there is a use case for automatic conversions 548 // around Logical and it may damage hidden information in the physical 549 // storage so do not do it. 550 return Conversion::Forbidden; 551 } 552 553 // Below are indexes to access data in conversions. 554 // The order in data does matter for lexicographical_compare 555 enum { 556 narrowingArg = 0, // usually bad 557 extendingResult, // usually bad 558 nonExtendingResult, // usually ok 559 nonNarrowingArg, // usually ok 560 dataSize 561 }; 562 563 std::array<int, dataSize> conversions = {}; 564 bool infinite = false; // When forbidden conversion or wrong argument number 565 }; 566 567 /// Build mlir::FuncOp from runtime symbol description and add 568 /// fir.runtime attribute. 569 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder, 570 const RuntimeFunction &runtime) { 571 mlir::FuncOp function = builder.addNamedFunction( 572 loc, runtime.symbol, runtime.typeGenerator(builder.getContext())); 573 function->setAttr("fir.runtime", builder.getUnitAttr()); 574 return function; 575 } 576 577 /// Select runtime function that has the smallest distance to the intrinsic 578 /// function type and that will not imply narrowing arguments or extending the 579 /// result. 580 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 581 mlir::FuncOp searchFunctionInLibrary( 582 mlir::Location loc, fir::FirOpBuilder &builder, 583 const Fortran::common::StaticMultimapView<RuntimeFunction> &lib, 584 llvm::StringRef name, mlir::FunctionType funcType, 585 const RuntimeFunction **bestNearMatch, 586 FunctionDistance &bestMatchDistance) { 587 std::pair<const RuntimeFunction *, const RuntimeFunction *> range = 588 lib.equal_range(name); 589 for (auto iter = range.first; iter != range.second && iter; ++iter) { 590 const RuntimeFunction &impl = *iter; 591 mlir::FunctionType implType = impl.typeGenerator(builder.getContext()); 592 if (funcType == implType) 593 return getFuncOp(loc, builder, impl); // exact match 594 595 FunctionDistance distance(funcType, implType); 596 if (distance.isSmallerThan(bestMatchDistance)) { 597 *bestNearMatch = &impl; 598 bestMatchDistance = std::move(distance); 599 } 600 } 601 return {}; 602 } 603 604 /// Search runtime for the best runtime function given an intrinsic name 605 /// and interface. The interface may not be a perfect match in which case 606 /// the caller is responsible to insert argument and return value conversions. 607 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 608 static mlir::FuncOp getRuntimeFunction(mlir::Location loc, 609 fir::FirOpBuilder &builder, 610 llvm::StringRef name, 611 mlir::FunctionType funcType) { 612 const RuntimeFunction *bestNearMatch = nullptr; 613 FunctionDistance bestMatchDistance{}; 614 mlir::FuncOp match; 615 using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>; 616 static constexpr RtMap pgmathF(pgmathFast); 617 static_assert(pgmathF.Verify() && "map must be sorted"); 618 if (mathRuntimeVersion == fastVersion) { 619 match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, 620 &bestNearMatch, bestMatchDistance); 621 } else { 622 assert(mathRuntimeVersion == llvmOnly && "unknown math runtime"); 623 } 624 if (match) 625 return match; 626 627 // Go through llvm intrinsics if not exact match in libpgmath or if 628 // mathRuntimeVersion == llvmOnly 629 static constexpr RtMap llvmIntr(llvmIntrinsics); 630 static_assert(llvmIntr.Verify() && "map must be sorted"); 631 if (mlir::FuncOp exactMatch = 632 searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType, 633 &bestNearMatch, bestMatchDistance)) 634 return exactMatch; 635 636 if (bestNearMatch != nullptr) { 637 if (bestMatchDistance.isLosingPrecision()) { 638 // Using this runtime version requires narrowing the arguments 639 // or extending the result. It is not numerically safe. There 640 // is currently no quad math library that was described in 641 // lowering and could be used here. Emit an error and continue 642 // generating the code with the narrowing cast so that the user 643 // can get a complete list of the problematic intrinsic calls. 644 std::string message("TODO: no math runtime available for '"); 645 llvm::raw_string_ostream sstream(message); 646 if (name == "pow") { 647 assert(funcType.getNumInputs() == 2 && 648 "power operator has two arguments"); 649 sstream << funcType.getInput(0) << " ** " << funcType.getInput(1); 650 } else { 651 sstream << name << "("; 652 if (funcType.getNumInputs() > 0) 653 sstream << funcType.getInput(0); 654 for (mlir::Type argType : funcType.getInputs().drop_front()) 655 sstream << ", " << argType; 656 sstream << ")"; 657 } 658 sstream << "'"; 659 mlir::emitError(loc, message); 660 } 661 return getFuncOp(loc, builder, *bestNearMatch); 662 } 663 return {}; 664 } 665 666 /// Helpers to get function type from arguments and result type. 667 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType, 668 llvm::ArrayRef<mlir::Value> arguments, 669 fir::FirOpBuilder &builder) { 670 llvm::SmallVector<mlir::Type> argTypes; 671 for (mlir::Value arg : arguments) 672 argTypes.push_back(arg.getType()); 673 llvm::SmallVector<mlir::Type> resTypes; 674 if (resultType) 675 resTypes.push_back(*resultType); 676 return mlir::FunctionType::get(builder.getModule().getContext(), argTypes, 677 resTypes); 678 } 679 680 /// fir::ExtendedValue to mlir::Value translation layer 681 682 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder, 683 mlir::Location loc) { 684 assert(val && "optional unhandled here"); 685 mlir::Type type = val.getType(); 686 mlir::Value base = val; 687 mlir::IndexType indexType = builder.getIndexType(); 688 llvm::SmallVector<mlir::Value> extents; 689 690 fir::factory::CharacterExprHelper charHelper{builder, loc}; 691 // FIXME: we may want to allow non character scalar here. 692 if (charHelper.isCharacterScalar(type)) 693 return charHelper.toExtendedValue(val); 694 695 if (auto refType = type.dyn_cast<fir::ReferenceType>()) 696 type = refType.getEleTy(); 697 698 if (auto arrayType = type.dyn_cast<fir::SequenceType>()) { 699 type = arrayType.getEleTy(); 700 for (fir::SequenceType::Extent extent : arrayType.getShape()) { 701 if (extent == fir::SequenceType::getUnknownExtent()) 702 break; 703 extents.emplace_back( 704 builder.createIntegerConstant(loc, indexType, extent)); 705 } 706 // Last extent might be missing in case of assumed-size. If more extents 707 // could not be deduced from type, that's an error (a fir.box should 708 // have been used in the interface). 709 if (extents.size() + 1 < arrayType.getShape().size()) 710 mlir::emitError(loc, "cannot retrieve array extents from type"); 711 } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) { 712 fir::emitFatalError(loc, "not yet implemented: descriptor or derived type"); 713 } 714 715 if (!extents.empty()) 716 return fir::ArrayBoxValue{base, extents}; 717 return base; 718 } 719 720 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder, 721 mlir::Location loc) { 722 if (const fir::CharBoxValue *charBox = val.getCharBox()) { 723 mlir::Value buffer = charBox->getBuffer(); 724 if (buffer.getType().isa<fir::BoxCharType>()) 725 return buffer; 726 return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar( 727 buffer, charBox->getLen()); 728 } 729 730 // FIXME: need to access other ExtendedValue variants and handle them 731 // properly. 732 return fir::getBase(val); 733 } 734 735 //===----------------------------------------------------------------------===// 736 // IntrinsicLibrary 737 //===----------------------------------------------------------------------===// 738 739 /// Emit a TODO error message for as yet unimplemented intrinsics. 740 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) { 741 TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name)); 742 } 743 744 template <typename GeneratorType> 745 fir::ExtendedValue IntrinsicLibrary::genElementalCall( 746 GeneratorType generator, llvm::StringRef name, mlir::Type resultType, 747 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 748 llvm::SmallVector<mlir::Value> scalarArgs; 749 for (const fir::ExtendedValue &arg : args) 750 if (arg.getUnboxed() || arg.getCharBox()) 751 scalarArgs.emplace_back(fir::getBase(arg)); 752 else 753 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 754 return invokeGenerator(generator, resultType, scalarArgs); 755 } 756 757 template <> 758 fir::ExtendedValue 759 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>( 760 ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType, 761 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 762 for (const fir::ExtendedValue &arg : args) 763 if (!arg.getUnboxed() && !arg.getCharBox()) 764 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 765 if (outline) 766 return outlineInExtendedWrapper(generator, name, resultType, args); 767 return std::invoke(generator, *this, resultType, args); 768 } 769 770 static fir::ExtendedValue 771 invokeHandler(IntrinsicLibrary::ElementalGenerator generator, 772 const IntrinsicHandler &handler, 773 llvm::Optional<mlir::Type> resultType, 774 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 775 IntrinsicLibrary &lib) { 776 assert(resultType && "expect elemental intrinsic to be functions"); 777 return lib.genElementalCall(generator, handler.name, *resultType, args, 778 outline); 779 } 780 781 static fir::ExtendedValue 782 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator, 783 const IntrinsicHandler &handler, 784 llvm::Optional<mlir::Type> resultType, 785 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 786 IntrinsicLibrary &lib) { 787 assert(resultType && "expect intrinsic function"); 788 if (handler.isElemental) 789 return lib.genElementalCall(generator, handler.name, *resultType, args, 790 outline); 791 if (outline) 792 return lib.outlineInExtendedWrapper(generator, handler.name, *resultType, 793 args); 794 return std::invoke(generator, lib, *resultType, args); 795 } 796 797 fir::ExtendedValue 798 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name, 799 llvm::Optional<mlir::Type> resultType, 800 llvm::ArrayRef<fir::ExtendedValue> args) { 801 if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) { 802 bool outline = false; 803 return std::visit( 804 [&](auto &generator) -> fir::ExtendedValue { 805 return invokeHandler(generator, *handler, resultType, args, outline, 806 *this); 807 }, 808 handler->generator); 809 } 810 811 if (!resultType) 812 // Subroutine should have a handler, they are likely missing for now. 813 crashOnMissingIntrinsic(loc, name); 814 815 // Try the runtime if no special handler was defined for the 816 // intrinsic being called. Maths runtime only has numerical elemental. 817 // No optional arguments are expected at this point, the code will 818 // crash if it gets absent optional. 819 820 // FIXME: using toValue to get the type won't work with array arguments. 821 llvm::SmallVector<mlir::Value> mlirArgs; 822 for (const fir::ExtendedValue &extendedVal : args) { 823 mlir::Value val = toValue(extendedVal, builder, loc); 824 if (!val) 825 // If an absent optional gets there, most likely its handler has just 826 // not yet been defined. 827 crashOnMissingIntrinsic(loc, name); 828 mlirArgs.emplace_back(val); 829 } 830 mlir::FunctionType soughtFuncType = 831 getFunctionType(*resultType, mlirArgs, builder); 832 833 IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator = 834 getRuntimeCallGenerator(name, soughtFuncType); 835 return genElementalCall(runtimeCallGenerator, name, *resultType, args, 836 /* outline */ true); 837 } 838 839 mlir::Value 840 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator, 841 mlir::Type resultType, 842 llvm::ArrayRef<mlir::Value> args) { 843 return std::invoke(generator, *this, resultType, args); 844 } 845 846 mlir::Value 847 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator, 848 mlir::Type resultType, 849 llvm::ArrayRef<mlir::Value> args) { 850 return generator(builder, loc, args); 851 } 852 853 mlir::Value 854 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator, 855 mlir::Type resultType, 856 llvm::ArrayRef<mlir::Value> args) { 857 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 858 for (mlir::Value arg : args) 859 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 860 auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs); 861 return toValue(extendedResult, builder, loc); 862 } 863 864 template <typename GeneratorType> 865 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator, 866 llvm::StringRef name, 867 mlir::FunctionType funcType, 868 bool loadRefArguments) { 869 std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType); 870 mlir::FuncOp function = builder.getNamedFunction(wrapperName); 871 if (!function) { 872 // First time this wrapper is needed, build it. 873 function = builder.createFunction(loc, wrapperName, funcType); 874 function->setAttr("fir.intrinsic", builder.getUnitAttr()); 875 auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; 876 auto linkage = 877 mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage); 878 function->setAttr("llvm.linkage", linkage); 879 function.addEntryBlock(); 880 881 // Create local context to emit code into the newly created function 882 // This new function is not linked to a source file location, only 883 // its calls will be. 884 auto localBuilder = 885 std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap()); 886 localBuilder->setInsertionPointToStart(&function.front()); 887 // Location of code inside wrapper of the wrapper is independent from 888 // the location of the intrinsic call. 889 mlir::Location localLoc = localBuilder->getUnknownLoc(); 890 llvm::SmallVector<mlir::Value> localArguments; 891 for (mlir::BlockArgument bArg : function.front().getArguments()) { 892 auto refType = bArg.getType().dyn_cast<fir::ReferenceType>(); 893 if (loadRefArguments && refType) { 894 auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg); 895 localArguments.push_back(loaded); 896 } else { 897 localArguments.push_back(bArg); 898 } 899 } 900 901 IntrinsicLibrary localLib{*localBuilder, localLoc}; 902 903 assert(funcType.getNumResults() == 1 && 904 "expect one result for intrinsic function wrapper type"); 905 mlir::Type resultType = funcType.getResult(0); 906 auto result = 907 localLib.invokeGenerator(generator, resultType, localArguments); 908 localBuilder->create<mlir::func::ReturnOp>(localLoc, result); 909 } else { 910 // Wrapper was already built, ensure it has the sought type 911 assert(function.getType() == funcType && 912 "conflict between intrinsic wrapper types"); 913 } 914 return function; 915 } 916 917 /// Helpers to detect absent optional (not yet supported in outlining). 918 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) { 919 for (const fir::ExtendedValue &arg : args) 920 if (!fir::getBase(arg)) 921 return true; 922 return false; 923 } 924 925 template <typename GeneratorType> 926 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper( 927 GeneratorType generator, llvm::StringRef name, 928 llvm::Optional<mlir::Type> resultType, 929 llvm::ArrayRef<fir::ExtendedValue> args) { 930 if (hasAbsentOptional(args)) 931 TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + 932 " with absent optional argument"); 933 llvm::SmallVector<mlir::Value> mlirArgs; 934 for (const auto &extendedVal : args) 935 mlirArgs.emplace_back(toValue(extendedVal, builder, loc)); 936 mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder); 937 mlir::FuncOp wrapper = getWrapper(generator, name, funcType); 938 auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs); 939 if (resultType) 940 return toExtendedValue(call.getResult(0), builder, loc); 941 // Subroutine calls 942 return mlir::Value{}; 943 } 944 945 IntrinsicLibrary::RuntimeCallGenerator 946 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name, 947 mlir::FunctionType soughtFuncType) { 948 mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType); 949 if (!funcOp) { 950 std::string buffer("not yet implemented: missing intrinsic lowering: "); 951 llvm::raw_string_ostream sstream(buffer); 952 sstream << name << "\nrequested type was: " << soughtFuncType << '\n'; 953 fir::emitFatalError(loc, buffer); 954 } 955 956 mlir::FunctionType actualFuncType = funcOp.getType(); 957 assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() && 958 actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() && 959 actualFuncType.getNumResults() == 1 && "Bad intrinsic match"); 960 961 return [funcOp, actualFuncType, 962 soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc, 963 llvm::ArrayRef<mlir::Value> args) { 964 llvm::SmallVector<mlir::Value> convertedArguments; 965 for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args)) 966 convertedArguments.push_back(builder.createConvert(loc, fst, snd)); 967 auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments); 968 mlir::Type soughtType = soughtFuncType.getResult(0); 969 return builder.createConvert(loc, soughtType, call.getResult(0)); 970 }; 971 } 972 973 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) { 974 assert(stmtCtx); 975 fir::FirOpBuilder *bldr = &builder; 976 stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 977 } 978 979 fir::ExtendedValue 980 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 981 mlir::Type resultType, 982 llvm::StringRef intrinsicName) { 983 fir::ExtendedValue res = 984 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 985 return res.match( 986 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 987 // Add cleanup code 988 addCleanUpForTemp(loc, box.getAddr()); 989 return box; 990 }, 991 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 992 // Add cleanup code 993 auto addr = 994 builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); 995 addCleanUpForTemp(loc, addr); 996 return box; 997 }, 998 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 999 // Add cleanup code 1000 addCleanUpForTemp(loc, box.getAddr()); 1001 return box; 1002 }, 1003 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 1004 // Add cleanup code 1005 addCleanUpForTemp(loc, tempAddr); 1006 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 1007 }, 1008 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1009 // Add cleanup code 1010 addCleanUpForTemp(loc, box.getAddr()); 1011 return box; 1012 }, 1013 [&](const auto &) -> fir::ExtendedValue { 1014 fir::emitFatalError(loc, "unexpected result for " + intrinsicName); 1015 }); 1016 } 1017 1018 //===----------------------------------------------------------------------===// 1019 // Code generators for the intrinsic 1020 //===----------------------------------------------------------------------===// 1021 1022 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name, 1023 mlir::Type resultType, 1024 llvm::ArrayRef<mlir::Value> args) { 1025 mlir::FunctionType soughtFuncType = 1026 getFunctionType(resultType, args, builder); 1027 return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args); 1028 } 1029 1030 // ABS 1031 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType, 1032 llvm::ArrayRef<mlir::Value> args) { 1033 assert(args.size() == 1); 1034 mlir::Value arg = args[0]; 1035 mlir::Type type = arg.getType(); 1036 if (fir::isa_real(type)) { 1037 // Runtime call to fp abs. An alternative would be to use mlir 1038 // math::AbsFOp but it does not support all fir floating point types. 1039 return genRuntimeCall("abs", resultType, args); 1040 } 1041 if (auto intType = type.dyn_cast<mlir::IntegerType>()) { 1042 // At the time of this implementation there is no abs op in mlir. 1043 // So, implement abs here without branching. 1044 mlir::Value shift = 1045 builder.createIntegerConstant(loc, intType, intType.getWidth() - 1); 1046 auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift); 1047 auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask); 1048 return builder.create<mlir::arith::SubIOp>(loc, xored, mask); 1049 } 1050 if (fir::isa_complex(type)) { 1051 // Use HYPOT to fulfill the no underflow/overflow requirement. 1052 auto parts = fir::factory::Complex{builder, loc}.extractParts(arg); 1053 llvm::SmallVector<mlir::Value> args = {parts.first, parts.second}; 1054 return genRuntimeCall("hypot", resultType, args); 1055 } 1056 llvm_unreachable("unexpected type in ABS argument"); 1057 } 1058 1059 // ASSOCIATED 1060 fir::ExtendedValue 1061 IntrinsicLibrary::genAssociated(mlir::Type resultType, 1062 llvm::ArrayRef<fir::ExtendedValue> args) { 1063 assert(args.size() == 2); 1064 auto *pointer = 1065 args[0].match([&](const fir::MutableBoxValue &x) { return &x; }, 1066 [&](const auto &) -> const fir::MutableBoxValue * { 1067 fir::emitFatalError(loc, "pointer not a MutableBoxValue"); 1068 }); 1069 const fir::ExtendedValue &target = args[1]; 1070 if (isAbsent(target)) 1071 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer); 1072 1073 mlir::Value targetBox = builder.createBox(loc, target); 1074 if (fir::valueHasFirAttribute(fir::getBase(target), 1075 fir::getOptionalAttrName())) { 1076 // Subtle: contrary to other intrinsic optional arguments, disassociated 1077 // POINTER and unallocated ALLOCATABLE actual argument are not considered 1078 // absent here. This is because ASSOCIATED has special requirements for 1079 // TARGET actual arguments that are POINTERs. There is no precise 1080 // requirements for ALLOCATABLEs, but all existing Fortran compilers treat 1081 // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED 1082 // to rerun false. The runtime deals with the disassociated/unallocated 1083 // case. Simply ensures that TARGET that are OPTIONAL get conditionally 1084 // emboxed here to convey the optional aspect to the runtime. 1085 auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 1086 fir::getBase(target)); 1087 auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType()); 1088 targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox, 1089 absentBox); 1090 } 1091 mlir::Value pointerBoxRef = 1092 fir::factory::getMutableIRBox(builder, loc, *pointer); 1093 auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef); 1094 return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox); 1095 } 1096 1097 // CHAR 1098 fir::ExtendedValue 1099 IntrinsicLibrary::genChar(mlir::Type type, 1100 llvm::ArrayRef<fir::ExtendedValue> args) { 1101 // Optional KIND argument. 1102 assert(args.size() >= 1); 1103 const mlir::Value *arg = args[0].getUnboxed(); 1104 // expect argument to be a scalar integer 1105 if (!arg) 1106 mlir::emitError(loc, "CHAR intrinsic argument not unboxed"); 1107 fir::factory::CharacterExprHelper helper{builder, loc}; 1108 fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind(); 1109 mlir::Value cast = helper.createSingletonFromCode(*arg, kind); 1110 mlir::Value len = 1111 builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1); 1112 return fir::CharBoxValue{cast, len}; 1113 } 1114 1115 // IAND 1116 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType, 1117 llvm::ArrayRef<mlir::Value> args) { 1118 assert(args.size() == 2); 1119 return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]); 1120 } 1121 1122 // Compare two FIR values and return boolean result as i1. 1123 template <Extremum extremum, ExtremumBehavior behavior> 1124 static mlir::Value createExtremumCompare(mlir::Location loc, 1125 fir::FirOpBuilder &builder, 1126 mlir::Value left, mlir::Value right) { 1127 static constexpr mlir::arith::CmpIPredicate integerPredicate = 1128 extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt 1129 : mlir::arith::CmpIPredicate::slt; 1130 static constexpr mlir::arith::CmpFPredicate orderedCmp = 1131 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT 1132 : mlir::arith::CmpFPredicate::OLT; 1133 mlir::Type type = left.getType(); 1134 mlir::Value result; 1135 if (fir::isa_real(type)) { 1136 // Note: the signaling/quit aspect of the result required by IEEE 1137 // cannot currently be obtained with LLVM without ad-hoc runtime. 1138 if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { 1139 // Return the number if one of the inputs is NaN and the other is 1140 // a number. 1141 auto leftIsResult = 1142 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1143 auto rightIsNan = builder.create<mlir::arith::CmpFOp>( 1144 loc, mlir::arith::CmpFPredicate::UNE, right, right); 1145 result = 1146 builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan); 1147 } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { 1148 // Always return NaNs if one the input is NaNs 1149 auto leftIsResult = 1150 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1151 auto leftIsNan = builder.create<mlir::arith::CmpFOp>( 1152 loc, mlir::arith::CmpFPredicate::UNE, left, left); 1153 result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan); 1154 } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { 1155 // If the left is a NaN, return the right whatever it is. 1156 result = 1157 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1158 } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { 1159 // If one of the operand is a NaN, return left whatever it is. 1160 static constexpr auto unorderedCmp = 1161 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT 1162 : mlir::arith::CmpFPredicate::ULT; 1163 result = 1164 builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right); 1165 } else { 1166 // TODO: ieeeMinNum/ieeeMaxNum 1167 static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, 1168 "ieeeMinNum/ieeeMaxNum behavior not implemented"); 1169 } 1170 } else if (fir::isa_integer(type)) { 1171 result = 1172 builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right); 1173 } else if (fir::isa_char(type)) { 1174 // TODO: ! character min and max is tricky because the result 1175 // length is the length of the longest argument! 1176 // So we may need a temp. 1177 TODO(loc, "CHARACTER min and max"); 1178 } 1179 assert(result && "result must be defined"); 1180 return result; 1181 } 1182 1183 // MIN and MAX 1184 template <Extremum extremum, ExtremumBehavior behavior> 1185 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type, 1186 llvm::ArrayRef<mlir::Value> args) { 1187 assert(args.size() >= 1); 1188 mlir::Value result = args[0]; 1189 for (auto arg : args.drop_front()) { 1190 mlir::Value mask = 1191 createExtremumCompare<extremum, behavior>(loc, builder, result, arg); 1192 result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg); 1193 } 1194 return result; 1195 } 1196 1197 // SUM 1198 fir::ExtendedValue 1199 IntrinsicLibrary::genSum(mlir::Type resultType, 1200 llvm::ArrayRef<fir::ExtendedValue> args) { 1201 return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType, 1202 builder, loc, stmtCtx, "unexpected result for Sum", args); 1203 } 1204 1205 // SIZE 1206 fir::ExtendedValue 1207 IntrinsicLibrary::genSize(mlir::Type resultType, 1208 llvm::ArrayRef<fir::ExtendedValue> args) { 1209 // Note that the value of the KIND argument is already reflected in the 1210 // resultType 1211 assert(args.size() == 3); 1212 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 1213 if (boxValue->hasAssumedRank()) 1214 TODO(loc, "SIZE intrinsic with assumed rank argument"); 1215 1216 // Get the ARRAY argument 1217 mlir::Value array = builder.createBox(loc, args[0]); 1218 1219 // The front-end rewrites SIZE without the DIM argument to 1220 // an array of SIZE with DIM in most cases, but it may not be 1221 // possible in some cases like when in SIZE(function_call()). 1222 if (isAbsent(args, 1)) 1223 return builder.createConvert(loc, resultType, 1224 fir::runtime::genSize(builder, loc, array)); 1225 1226 // Get the DIM argument. 1227 mlir::Value dim = fir::getBase(args[1]); 1228 if (!fir::isa_ref_type(dim.getType())) 1229 return builder.createConvert( 1230 loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim)); 1231 1232 mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim); 1233 return builder 1234 .genIfOp(loc, {resultType}, isDynamicallyAbsent, 1235 /*withElseRegion=*/true) 1236 .genThen([&]() { 1237 mlir::Value size = builder.createConvert( 1238 loc, resultType, fir::runtime::genSize(builder, loc, array)); 1239 builder.create<fir::ResultOp>(loc, size); 1240 }) 1241 .genElse([&]() { 1242 mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim); 1243 mlir::Value size = builder.createConvert( 1244 loc, resultType, 1245 fir::runtime::genSizeDim(builder, loc, array, dimValue)); 1246 builder.create<fir::ResultOp>(loc, size); 1247 }) 1248 .getResults()[0]; 1249 } 1250 1251 // LBOUND 1252 fir::ExtendedValue 1253 IntrinsicLibrary::genLbound(mlir::Type resultType, 1254 llvm::ArrayRef<fir::ExtendedValue> args) { 1255 // Calls to LBOUND that don't have the DIM argument, or for which 1256 // the DIM is a compile time constant, are folded to descriptor inquiries by 1257 // semantics. This function covers the situations where a call to the 1258 // runtime is required. 1259 assert(args.size() == 3); 1260 assert(!isAbsent(args[1])); 1261 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 1262 if (boxValue->hasAssumedRank()) 1263 TODO(loc, "LBOUND intrinsic with assumed rank argument"); 1264 1265 const fir::ExtendedValue &array = args[0]; 1266 mlir::Value box = array.match( 1267 [&](const fir::BoxValue &boxValue) -> mlir::Value { 1268 // This entity is mapped to a fir.box that may not contain the local 1269 // lower bound information if it is a dummy. Rebox it with the local 1270 // shape information. 1271 mlir::Value localShape = builder.createShape(loc, array); 1272 mlir::Value oldBox = boxValue.getAddr(); 1273 return builder.create<fir::ReboxOp>( 1274 loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{}); 1275 }, 1276 [&](const auto &) -> mlir::Value { 1277 // This a pointer/allocatable, or an entity not yet tracked with a 1278 // fir.box. For pointer/allocatable, createBox will forward the 1279 // descriptor that contains the correct lower bound information. For 1280 // other entities, a new fir.box will be made with the local lower 1281 // bounds. 1282 return builder.createBox(loc, array); 1283 }); 1284 1285 mlir::Value dim = fir::getBase(args[1]); 1286 return builder.createConvert( 1287 loc, resultType, 1288 fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim)); 1289 } 1290 1291 // UBOUND 1292 fir::ExtendedValue 1293 IntrinsicLibrary::genUbound(mlir::Type resultType, 1294 llvm::ArrayRef<fir::ExtendedValue> args) { 1295 assert(args.size() == 3 || args.size() == 2); 1296 if (args.size() == 3) { 1297 // Handle calls to UBOUND with the DIM argument, which return a scalar 1298 mlir::Value extent = fir::getBase(genSize(resultType, args)); 1299 mlir::Value lbound = fir::getBase(genLbound(resultType, args)); 1300 1301 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 1302 mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one); 1303 return builder.create<mlir::arith::AddIOp>(loc, ubound, extent); 1304 } else { 1305 // Handle calls to UBOUND without the DIM argument, which return an array 1306 mlir::Value kind = isAbsent(args[1]) 1307 ? builder.createIntegerConstant( 1308 loc, builder.getIndexType(), 1309 builder.getKindMap().defaultIntegerKind()) 1310 : fir::getBase(args[1]); 1311 1312 // Create mutable fir.box to be passed to the runtime for the result. 1313 mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1); 1314 fir::MutableBoxValue resultMutableBox = 1315 fir::factory::createTempMutableBox(builder, loc, type); 1316 mlir::Value resultIrBox = 1317 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1318 1319 fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]), 1320 kind); 1321 1322 return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND"); 1323 } 1324 return mlir::Value(); 1325 } 1326 1327 //===----------------------------------------------------------------------===// 1328 // Argument lowering rules interface 1329 //===----------------------------------------------------------------------===// 1330 1331 const Fortran::lower::IntrinsicArgumentLoweringRules * 1332 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) { 1333 if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName)) 1334 if (!handler->argLoweringRules.hasDefaultRules()) 1335 return &handler->argLoweringRules; 1336 return nullptr; 1337 } 1338 1339 /// Return how argument \p argName should be lowered given the rules for the 1340 /// intrinsic function. 1341 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs( 1342 mlir::Location loc, const IntrinsicArgumentLoweringRules &rules, 1343 llvm::StringRef argName) { 1344 for (const IntrinsicDummyArgument &arg : rules.args) { 1345 if (arg.name && arg.name == argName) 1346 return {arg.lowerAs, arg.handleDynamicOptional}; 1347 } 1348 fir::emitFatalError( 1349 loc, "internal: unknown intrinsic argument name in lowering '" + argName + 1350 "'"); 1351 } 1352 1353 //===----------------------------------------------------------------------===// 1354 // Public intrinsic call helpers 1355 //===----------------------------------------------------------------------===// 1356 1357 fir::ExtendedValue 1358 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, 1359 llvm::StringRef name, 1360 llvm::Optional<mlir::Type> resultType, 1361 llvm::ArrayRef<fir::ExtendedValue> args, 1362 Fortran::lower::StatementContext &stmtCtx) { 1363 return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall( 1364 name, resultType, args); 1365 } 1366 1367 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder, 1368 mlir::Location loc, 1369 llvm::ArrayRef<mlir::Value> args) { 1370 assert(args.size() > 0 && "max requires at least one argument"); 1371 return IntrinsicLibrary{builder, loc} 1372 .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(), 1373 args); 1374 } 1375 1376 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder, 1377 mlir::Location loc, mlir::Type type, 1378 mlir::Value x, mlir::Value y) { 1379 return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); 1380 } 1381