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