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/Character.h" 28 #include "flang/Optimizer/Builder/Runtime/Inquiry.h" 29 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" 30 #include "flang/Optimizer/Builder/Runtime/Reduction.h" 31 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 32 #include "flang/Optimizer/Support/FatalError.h" 33 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 34 #include "llvm/Support/CommandLine.h" 35 36 #define DEBUG_TYPE "flang-lower-intrinsic" 37 38 #define PGMATH_DECLARE 39 #include "flang/Evaluate/pgmath.h.inc" 40 41 /// Enums used to templatize and share lowering of MIN and MAX. 42 enum class Extremum { Min, Max }; 43 44 // There are different ways to deal with NaNs in MIN and MAX. 45 // Known existing behaviors are listed below and can be selected for 46 // f18 MIN/MAX implementation. 47 enum class ExtremumBehavior { 48 // Note: the Signaling/quiet aspect of NaNs in the behaviors below are 49 // not described because there is no way to control/observe such aspect in 50 // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this 51 // aspect that are therefore currently not enforced. In the descriptions 52 // below, NaNs can be signaling or quite. Returned NaNs may be signaling 53 // if one of the input NaN was signaling but it cannot be guaranteed either. 54 // Existing compilers using an IEEE behavior (gfortran) also do not fulfill 55 // signaling/quiet requirements. 56 IeeeMinMaximumNumber, 57 // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): 58 // If one of the argument is and number and the other is NaN, return the 59 // number. If both arguements are NaN, return NaN. 60 // Compilers: gfortran. 61 IeeeMinMaximum, 62 // IEEE minimum/maximum behavior (754-2019, section 9.6): 63 // If one of the argument is NaN, return NaN. 64 MinMaxss, 65 // x86 minss/maxss behavior: 66 // If the second argument is a number and the other is NaN, return the number. 67 // In all other cases where at least one operand is NaN, return NaN. 68 // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. 69 PgfortranLlvm, 70 // "Opposite of" x86 minss/maxss behavior: 71 // If the first argument is a number and the other is NaN, return the 72 // number. 73 // In all other cases where at least one operand is NaN, return NaN. 74 // Compilers: xlf (only for MIN), and pgfortran (with llvm). 75 IeeeMinMaxNum 76 // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): 77 // TODO: Not implemented. 78 // It is the only behavior where the signaling/quiet aspect of a NaN argument 79 // impacts if the result should be NaN or the argument that is a number. 80 // LLVM/MLIR do not provide ways to observe this aspect, so it is not 81 // possible to implement it without some target dependent runtime. 82 }; 83 84 /// This file implements lowering of Fortran intrinsic procedures. 85 /// Intrinsics are lowered to a mix of FIR and MLIR operations as 86 /// well as call to runtime functions or LLVM intrinsics. 87 88 /// Lowering of intrinsic procedure calls is based on a map that associates 89 /// Fortran intrinsic generic names to FIR generator functions. 90 /// All generator functions are member functions of the IntrinsicLibrary class 91 /// and have the same interface. 92 /// If no generator is given for an intrinsic name, a math runtime library 93 /// is searched for an implementation and, if a runtime function is found, 94 /// a call is generated for it. LLVM intrinsics are handled as a math 95 /// runtime library here. 96 97 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() { 98 return fir::UnboxedValue{}; 99 } 100 101 /// Test if an ExtendedValue is absent. 102 static bool isAbsent(const fir::ExtendedValue &exv) { 103 return !fir::getBase(exv); 104 } 105 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) { 106 return args.size() <= argIndex || isAbsent(args[argIndex]); 107 } 108 109 /// Test if an ExtendedValue is present. 110 static bool isPresent(const fir::ExtendedValue &exv) { return !isAbsent(exv); } 111 112 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that 113 /// take a DIM argument. 114 template <typename FD> 115 static fir::ExtendedValue 116 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 117 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 118 llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg, 119 mlir::Value mask, int rank) { 120 121 // Create mutable fir.box to be passed to the runtime for the result. 122 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 123 fir::MutableBoxValue resultMutableBox = 124 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 125 mlir::Value resultIrBox = 126 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 127 128 mlir::Value dim = 129 isAbsent(dimArg) 130 ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 131 : fir::getBase(dimArg); 132 funcDim(builder, loc, resultIrBox, array, dim, mask); 133 134 fir::ExtendedValue res = 135 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 136 return res.match( 137 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 138 // Add cleanup code 139 assert(stmtCtx); 140 fir::FirOpBuilder *bldr = &builder; 141 mlir::Value temp = box.getAddr(); 142 stmtCtx->attachCleanup( 143 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 144 return box; 145 }, 146 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 147 // Add cleanup code 148 assert(stmtCtx); 149 fir::FirOpBuilder *bldr = &builder; 150 mlir::Value temp = box.getAddr(); 151 stmtCtx->attachCleanup( 152 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 153 return box; 154 }, 155 [&](const auto &) -> fir::ExtendedValue { 156 fir::emitFatalError(loc, errMsg); 157 }); 158 } 159 160 /// Process calls to Product, Sum intrinsic functions 161 template <typename FN, typename FD> 162 static fir::ExtendedValue 163 genProdOrSum(FN func, FD funcDim, mlir::Type resultType, 164 fir::FirOpBuilder &builder, mlir::Location loc, 165 Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg, 166 llvm::ArrayRef<fir::ExtendedValue> args) { 167 168 assert(args.size() == 3); 169 170 // Handle required array argument 171 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 172 mlir::Value array = fir::getBase(arryTmp); 173 int rank = arryTmp.rank(); 174 assert(rank >= 1); 175 176 // Handle optional mask argument 177 auto mask = isAbsent(args[2]) 178 ? builder.create<fir::AbsentOp>( 179 loc, fir::BoxType::get(builder.getI1Type())) 180 : builder.createBox(loc, args[2]); 181 182 bool absentDim = isAbsent(args[1]); 183 184 // We call the type specific versions because the result is scalar 185 // in the case below. 186 if (absentDim || rank == 1) { 187 mlir::Type ty = array.getType(); 188 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty); 189 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 190 if (fir::isa_complex(eleTy)) { 191 mlir::Value result = builder.createTemporary(loc, eleTy); 192 func(builder, loc, array, mask, result); 193 return builder.create<fir::LoadOp>(loc, result); 194 } 195 auto resultBox = builder.create<fir::AbsentOp>( 196 loc, fir::BoxType::get(builder.getI1Type())); 197 return func(builder, loc, array, mask, resultBox); 198 } 199 // Handle Product/Sum cases that have an array result. 200 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 201 args[1], mask, rank); 202 } 203 204 /// Process calls to DotProduct 205 template <typename FN> 206 static fir::ExtendedValue 207 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder, 208 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 209 llvm::ArrayRef<fir::ExtendedValue> args) { 210 211 assert(args.size() == 2); 212 213 // Handle required vector arguments 214 mlir::Value vectorA = fir::getBase(args[0]); 215 mlir::Value vectorB = fir::getBase(args[1]); 216 217 mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType()) 218 .cast<fir::SequenceType>() 219 .getEleTy(); 220 if (fir::isa_complex(eleTy)) { 221 mlir::Value result = builder.createTemporary(loc, eleTy); 222 func(builder, loc, vectorA, vectorB, result); 223 return builder.create<fir::LoadOp>(loc, result); 224 } 225 226 auto resultBox = builder.create<fir::AbsentOp>( 227 loc, fir::BoxType::get(builder.getI1Type())); 228 return func(builder, loc, vectorA, vectorB, resultBox); 229 } 230 231 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions 232 template <typename FN, typename FD, typename FC> 233 static fir::ExtendedValue 234 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType, 235 fir::FirOpBuilder &builder, mlir::Location loc, 236 Fortran::lower::StatementContext *stmtCtx, 237 llvm::StringRef errMsg, 238 llvm::ArrayRef<fir::ExtendedValue> args) { 239 240 assert(args.size() == 3); 241 242 // Handle required array argument 243 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 244 mlir::Value array = fir::getBase(arryTmp); 245 int rank = arryTmp.rank(); 246 assert(rank >= 1); 247 bool hasCharacterResult = arryTmp.isCharacter(); 248 249 // Handle optional mask argument 250 auto mask = isAbsent(args[2]) 251 ? builder.create<fir::AbsentOp>( 252 loc, fir::BoxType::get(builder.getI1Type())) 253 : builder.createBox(loc, args[2]); 254 255 bool absentDim = isAbsent(args[1]); 256 257 // For Maxval/MinVal, we call the type specific versions of 258 // Maxval/Minval because the result is scalar in the case below. 259 if (!hasCharacterResult && (absentDim || rank == 1)) 260 return func(builder, loc, array, mask); 261 262 if (hasCharacterResult && (absentDim || rank == 1)) { 263 // Create mutable fir.box to be passed to the runtime for the result. 264 fir::MutableBoxValue resultMutableBox = 265 fir::factory::createTempMutableBox(builder, loc, resultType); 266 mlir::Value resultIrBox = 267 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 268 269 funcChar(builder, loc, resultIrBox, array, mask); 270 271 // Handle cleanup of allocatable result descriptor and return 272 fir::ExtendedValue res = 273 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 274 return res.match( 275 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 276 // Add cleanup code 277 assert(stmtCtx); 278 fir::FirOpBuilder *bldr = &builder; 279 mlir::Value temp = box.getAddr(); 280 stmtCtx->attachCleanup( 281 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 282 return box; 283 }, 284 [&](const auto &) -> fir::ExtendedValue { 285 fir::emitFatalError(loc, errMsg); 286 }); 287 } 288 289 // Handle Min/Maxval cases that have an array result. 290 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 291 args[1], mask, rank); 292 } 293 294 /// Process calls to Minloc, Maxloc intrinsic functions 295 template <typename FN, typename FD> 296 static fir::ExtendedValue genExtremumloc( 297 FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 298 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 299 llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) { 300 301 assert(args.size() == 5); 302 303 // Handle required array argument 304 mlir::Value array = builder.createBox(loc, args[0]); 305 unsigned rank = fir::BoxValue(array).rank(); 306 assert(rank >= 1); 307 308 // Handle optional mask argument 309 auto mask = isAbsent(args[2]) 310 ? builder.create<fir::AbsentOp>( 311 loc, fir::BoxType::get(builder.getI1Type())) 312 : builder.createBox(loc, args[2]); 313 314 // Handle optional kind argument 315 auto kind = isAbsent(args[3]) ? builder.createIntegerConstant( 316 loc, builder.getIndexType(), 317 builder.getKindMap().defaultIntegerKind()) 318 : fir::getBase(args[3]); 319 320 // Handle optional back argument 321 auto back = isAbsent(args[4]) ? builder.createBool(loc, false) 322 : fir::getBase(args[4]); 323 324 bool absentDim = isAbsent(args[1]); 325 326 if (!absentDim && rank == 1) { 327 // If dim argument is present and the array is rank 1, then the result is 328 // a scalar (since the the result is rank-1 or 0). 329 // Therefore, we use a scalar result descriptor with Min/MaxlocDim(). 330 mlir::Value dim = fir::getBase(args[1]); 331 // Create mutable fir.box to be passed to the runtime for the result. 332 fir::MutableBoxValue resultMutableBox = 333 fir::factory::createTempMutableBox(builder, loc, resultType); 334 mlir::Value resultIrBox = 335 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 336 337 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 338 339 // Handle cleanup of allocatable result descriptor and return 340 fir::ExtendedValue res = 341 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 342 return res.match( 343 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 344 // Add cleanup code 345 assert(stmtCtx); 346 fir::FirOpBuilder *bldr = &builder; 347 stmtCtx->attachCleanup( 348 [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); }); 349 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 350 }, 351 [&](const auto &) -> fir::ExtendedValue { 352 fir::emitFatalError(loc, errMsg); 353 }); 354 } 355 356 // Note: The Min/Maxloc/val cases below have an array result. 357 358 // Create mutable fir.box to be passed to the runtime for the result. 359 mlir::Type resultArrayType = 360 builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1); 361 fir::MutableBoxValue resultMutableBox = 362 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 363 mlir::Value resultIrBox = 364 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 365 366 if (absentDim) { 367 // Handle min/maxloc/val case where there is no dim argument 368 // (calls Min/Maxloc()/MinMaxval() runtime routine) 369 func(builder, loc, resultIrBox, array, mask, kind, back); 370 } else { 371 // else handle min/maxloc case with dim argument (calls 372 // Min/Max/loc/val/Dim() runtime routine). 373 mlir::Value dim = fir::getBase(args[1]); 374 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 375 } 376 377 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 378 .match( 379 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 380 // Add cleanup code 381 assert(stmtCtx); 382 fir::FirOpBuilder *bldr = &builder; 383 mlir::Value temp = box.getAddr(); 384 stmtCtx->attachCleanup( 385 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 386 return box; 387 }, 388 [&](const auto &) -> fir::ExtendedValue { 389 fir::emitFatalError(loc, errMsg); 390 }); 391 } 392 393 // TODO error handling -> return a code or directly emit messages ? 394 struct IntrinsicLibrary { 395 396 // Constructors. 397 explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc, 398 Fortran::lower::StatementContext *stmtCtx = nullptr) 399 : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {} 400 IntrinsicLibrary() = delete; 401 IntrinsicLibrary(const IntrinsicLibrary &) = delete; 402 403 /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg 404 /// and expected result type \p resultType. 405 fir::ExtendedValue genIntrinsicCall(llvm::StringRef name, 406 llvm::Optional<mlir::Type> resultType, 407 llvm::ArrayRef<fir::ExtendedValue> arg); 408 409 /// Search a runtime function that is associated to the generic intrinsic name 410 /// and whose signature matches the intrinsic arguments and result types. 411 /// If no such runtime function is found but a runtime function associated 412 /// with the Fortran generic exists and has the same number of arguments, 413 /// conversions will be inserted before and/or after the call. This is to 414 /// mainly to allow 16 bits float support even-though little or no math 415 /// runtime is currently available for it. 416 mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, 417 llvm::ArrayRef<mlir::Value>); 418 419 using RuntimeCallGenerator = std::function<mlir::Value( 420 fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>; 421 RuntimeCallGenerator 422 getRuntimeCallGenerator(llvm::StringRef name, 423 mlir::FunctionType soughtFuncType); 424 425 /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in 426 /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation 427 /// if the argument is an integer, into llvm intrinsics if the argument is 428 /// real and to the `hypot` math routine if the argument is of complex type. 429 mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>); 430 mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>); 431 fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 432 fir::ExtendedValue genAllocated(mlir::Type, 433 llvm::ArrayRef<fir::ExtendedValue>); 434 fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 435 fir::ExtendedValue genAssociated(mlir::Type, 436 llvm::ArrayRef<fir::ExtendedValue>); 437 fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 438 mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>); 439 fir::ExtendedValue genDotProduct(mlir::Type, 440 llvm::ArrayRef<fir::ExtendedValue>); 441 template <mlir::arith::CmpIPredicate pred> 442 fir::ExtendedValue genCharacterCompare(mlir::Type, 443 llvm::ArrayRef<fir::ExtendedValue>); 444 void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>); 445 void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>); 446 template <Extremum, ExtremumBehavior> 447 mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>); 448 /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments 449 /// in the llvm::ArrayRef. 450 mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>); 451 mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>); 452 fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 453 fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 454 fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 455 fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 456 fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 457 fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 458 fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 459 fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 460 void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>); 461 void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>); 462 void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>); 463 fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 464 fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 465 void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>); 466 fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 467 468 /// Define the different FIR generators that can be mapped to intrinsic to 469 /// generate the related code. 470 using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); 471 using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum); 472 using SubroutineGenerator = decltype(&IntrinsicLibrary::genRandomInit); 473 using Generator = 474 std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>; 475 476 template <typename GeneratorType> 477 fir::ExtendedValue 478 outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, 479 llvm::Optional<mlir::Type> resultType, 480 llvm::ArrayRef<fir::ExtendedValue> args); 481 482 template <typename GeneratorType> 483 mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name, 484 mlir::FunctionType, bool loadRefArguments = false); 485 486 /// Generate calls to ElementalGenerator, handling the elemental aspects 487 template <typename GeneratorType> 488 fir::ExtendedValue 489 genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, 490 llvm::ArrayRef<fir::ExtendedValue> args, bool outline); 491 492 /// Helper to invoke code generator for the intrinsics given arguments. 493 mlir::Value invokeGenerator(ElementalGenerator generator, 494 mlir::Type resultType, 495 llvm::ArrayRef<mlir::Value> args); 496 mlir::Value invokeGenerator(RuntimeCallGenerator generator, 497 mlir::Type resultType, 498 llvm::ArrayRef<mlir::Value> args); 499 mlir::Value invokeGenerator(ExtendedGenerator generator, 500 mlir::Type resultType, 501 llvm::ArrayRef<mlir::Value> args); 502 mlir::Value invokeGenerator(SubroutineGenerator generator, 503 llvm::ArrayRef<mlir::Value> args); 504 505 /// Add clean-up for \p temp to the current statement context; 506 void addCleanUpForTemp(mlir::Location loc, mlir::Value temp); 507 /// Helper function for generating code clean-up for result descriptors 508 fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 509 mlir::Type resultType, 510 llvm::StringRef errMsg); 511 512 fir::FirOpBuilder &builder; 513 mlir::Location loc; 514 Fortran::lower::StatementContext *stmtCtx; 515 }; 516 517 struct IntrinsicDummyArgument { 518 const char *name = nullptr; 519 Fortran::lower::LowerIntrinsicArgAs lowerAs = 520 Fortran::lower::LowerIntrinsicArgAs::Value; 521 bool handleDynamicOptional = false; 522 }; 523 524 struct Fortran::lower::IntrinsicArgumentLoweringRules { 525 /// There is no more than 7 non repeated arguments in Fortran intrinsics. 526 IntrinsicDummyArgument args[7]; 527 constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } 528 }; 529 530 /// Structure describing what needs to be done to lower intrinsic "name". 531 struct IntrinsicHandler { 532 const char *name; 533 IntrinsicLibrary::Generator generator; 534 // The following may be omitted in the table below. 535 Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {}; 536 bool isElemental = true; 537 /// Code heavy intrinsic can be outlined to make FIR 538 /// more readable. 539 bool outline = false; 540 }; 541 542 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value; 543 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr; 544 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box; 545 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired; 546 using I = IntrinsicLibrary; 547 548 /// Flag to indicate that an intrinsic argument has to be handled as 549 /// being dynamically optional (e.g. special handling when actual 550 /// argument is an optional variable in the current scope). 551 static constexpr bool handleDynamicOptional = true; 552 553 /// Table that drives the fir generation depending on the intrinsic. 554 /// one to one mapping with Fortran arguments. If no mapping is 555 /// defined here for a generic intrinsic, genRuntimeCall will be called 556 /// to look for a match in the runtime a emit a call. Note that the argument 557 /// lowering rules for an intrinsic need to be provided only if at least one 558 /// argument must not be lowered by value. In which case, the lowering rules 559 /// should be provided for all the intrinsic arguments for completeness. 560 static constexpr IntrinsicHandler handlers[]{ 561 {"abs", &I::genAbs}, 562 {"aimag", &I::genAimag}, 563 {"all", 564 &I::genAll, 565 {{{"mask", asAddr}, {"dim", asValue}}}, 566 /*isElemental=*/false}, 567 {"allocated", 568 &I::genAllocated, 569 {{{"array", asInquired}, {"scalar", asInquired}}}, 570 /*isElemental=*/false}, 571 {"any", 572 &I::genAny, 573 {{{"mask", asAddr}, {"dim", asValue}}}, 574 /*isElemental=*/false}, 575 {"associated", 576 &I::genAssociated, 577 {{{"pointer", asInquired}, {"target", asInquired}}}, 578 /*isElemental=*/false}, 579 {"char", &I::genChar}, 580 {"cpu_time", 581 &I::genCpuTime, 582 {{{"time", asAddr}}}, 583 /*isElemental=*/false}, 584 {"date_and_time", 585 &I::genDateAndTime, 586 {{{"date", asAddr, handleDynamicOptional}, 587 {"time", asAddr, handleDynamicOptional}, 588 {"zone", asAddr, handleDynamicOptional}, 589 {"values", asBox, handleDynamicOptional}}}, 590 /*isElemental=*/false}, 591 {"dim", &I::genDim}, 592 {"dot_product", 593 &I::genDotProduct, 594 {{{"vector_a", asBox}, {"vector_b", asBox}}}, 595 /*isElemental=*/false}, 596 {"iand", &I::genIand}, 597 {"ibits", &I::genIbits}, 598 {"len", 599 &I::genLen, 600 {{{"string", asInquired}, {"kind", asValue}}}, 601 /*isElemental=*/false}, 602 {"len_trim", &I::genLenTrim}, 603 {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>}, 604 {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>}, 605 {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>}, 606 {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>}, 607 {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>}, 608 {"maxloc", 609 &I::genMaxloc, 610 {{{"array", asBox}, 611 {"dim", asValue}, 612 {"mask", asBox, handleDynamicOptional}, 613 {"kind", asValue}, 614 {"back", asValue, handleDynamicOptional}}}, 615 /*isElemental=*/false}, 616 {"maxval", 617 &I::genMaxval, 618 {{{"array", asBox}, 619 {"dim", asValue}, 620 {"mask", asBox, handleDynamicOptional}}}, 621 /*isElemental=*/false}, 622 {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>}, 623 {"minloc", 624 &I::genMinloc, 625 {{{"array", asBox}, 626 {"dim", asValue}, 627 {"mask", asBox, handleDynamicOptional}, 628 {"kind", asValue}, 629 {"back", asValue, handleDynamicOptional}}}, 630 /*isElemental=*/false}, 631 {"minval", 632 &I::genMinval, 633 {{{"array", asBox}, 634 {"dim", asValue}, 635 {"mask", asBox, handleDynamicOptional}}}, 636 /*isElemental=*/false}, 637 {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false}, 638 {"random_init", 639 &I::genRandomInit, 640 {{{"repeatable", asValue}, {"image_distinct", asValue}}}, 641 /*isElemental=*/false}, 642 {"random_number", 643 &I::genRandomNumber, 644 {{{"harvest", asBox}}}, 645 /*isElemental=*/false}, 646 {"random_seed", 647 &I::genRandomSeed, 648 {{{"size", asBox}, {"put", asBox}, {"get", asBox}}}, 649 /*isElemental=*/false}, 650 {"sum", 651 &I::genSum, 652 {{{"array", asBox}, 653 {"dim", asValue}, 654 {"mask", asBox, handleDynamicOptional}}}, 655 /*isElemental=*/false}, 656 {"system_clock", 657 &I::genSystemClock, 658 {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}}, 659 /*isElemental=*/false}, 660 {"ubound", 661 &I::genUbound, 662 {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}}, 663 /*isElemental=*/false}, 664 }; 665 666 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { 667 auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { 668 return name.compare(handler.name) > 0; 669 }; 670 auto result = 671 std::lower_bound(std::begin(handlers), std::end(handlers), name, compare); 672 return result != std::end(handlers) && result->name == name ? result 673 : nullptr; 674 } 675 676 /// To make fir output more readable for debug, one can outline all intrinsic 677 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag). 678 static llvm::cl::opt<bool> outlineAllIntrinsics( 679 "outline-intrinsics", 680 llvm::cl::desc( 681 "Lower all intrinsic procedure implementation in their own functions"), 682 llvm::cl::init(false)); 683 684 //===----------------------------------------------------------------------===// 685 // Math runtime description and matching utility 686 //===----------------------------------------------------------------------===// 687 688 /// Command line option to modify math runtime version used to implement 689 /// intrinsics. 690 enum MathRuntimeVersion { fastVersion, llvmOnly }; 691 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion( 692 "math-runtime", llvm::cl::desc("Select math runtime version:"), 693 llvm::cl::values( 694 clEnumValN(fastVersion, "fast", "use pgmath fast runtime"), 695 clEnumValN(llvmOnly, "llvm", 696 "only use LLVM intrinsics (may be incomplete)")), 697 llvm::cl::init(fastVersion)); 698 699 struct RuntimeFunction { 700 // llvm::StringRef comparison operator are not constexpr, so use string_view. 701 using Key = std::string_view; 702 // Needed for implicit compare with keys. 703 constexpr operator Key() const { return key; } 704 Key key; // intrinsic name 705 llvm::StringRef symbol; 706 fir::runtime::FuncTypeBuilderFunc typeGenerator; 707 }; 708 709 #define RUNTIME_STATIC_DESCRIPTION(name, func) \ 710 {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()}, 711 static constexpr RuntimeFunction pgmathFast[] = { 712 #define PGMATH_FAST 713 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 714 #include "flang/Evaluate/pgmath.h.inc" 715 }; 716 717 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) { 718 mlir::Type t = mlir::FloatType::getF32(context); 719 return mlir::FunctionType::get(context, {t}, {t}); 720 } 721 722 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) { 723 mlir::Type t = mlir::FloatType::getF64(context); 724 return mlir::FunctionType::get(context, {t}, {t}); 725 } 726 727 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) { 728 auto t = mlir::FloatType::getF32(context); 729 return mlir::FunctionType::get(context, {t, t}, {t}); 730 } 731 732 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) { 733 auto t = mlir::FloatType::getF64(context); 734 return mlir::FunctionType::get(context, {t, t}, {t}); 735 } 736 737 // TODO : Fill-up this table with more intrinsic. 738 // Note: These are also defined as operations in LLVM dialect. See if this 739 // can be use and has advantages. 740 static constexpr RuntimeFunction llvmIntrinsics[] = { 741 {"abs", "llvm.fabs.f32", genF32F32FuncType}, 742 {"abs", "llvm.fabs.f64", genF64F64FuncType}, 743 {"pow", "llvm.pow.f32", genF32F32F32FuncType}, 744 {"pow", "llvm.pow.f64", genF64F64F64FuncType}, 745 }; 746 747 // This helper class computes a "distance" between two function types. 748 // The distance measures how many narrowing conversions of actual arguments 749 // and result of "from" must be made in order to use "to" instead of "from". 750 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is 751 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means 752 // if no implementation of ACOS(REAL(10)) is available, it is better to use 753 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)). 754 // Note that this is not a symmetric distance and the order of "from" and "to" 755 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it 756 // may be safe to replace foo by bar, but not the opposite. 757 class FunctionDistance { 758 public: 759 FunctionDistance() : infinite{true} {} 760 761 FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { 762 unsigned nInputs = from.getNumInputs(); 763 unsigned nResults = from.getNumResults(); 764 if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { 765 infinite = true; 766 } else { 767 for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i) 768 addArgumentDistance(from.getInput(i), to.getInput(i)); 769 for (decltype(nResults) i = 0; i < nResults && !infinite; ++i) 770 addResultDistance(to.getResult(i), from.getResult(i)); 771 } 772 } 773 774 /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be 775 /// false if both d1 and d2 are infinite. This implies that 776 /// d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1) 777 bool isSmallerThan(const FunctionDistance &d) const { 778 return !infinite && 779 (d.infinite || std::lexicographical_compare( 780 conversions.begin(), conversions.end(), 781 d.conversions.begin(), d.conversions.end())); 782 } 783 784 bool isLosingPrecision() const { 785 return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0; 786 } 787 788 bool isInfinite() const { return infinite; } 789 790 private: 791 enum class Conversion { Forbidden, None, Narrow, Extend }; 792 793 void addArgumentDistance(mlir::Type from, mlir::Type to) { 794 switch (conversionBetweenTypes(from, to)) { 795 case Conversion::Forbidden: 796 infinite = true; 797 break; 798 case Conversion::None: 799 break; 800 case Conversion::Narrow: 801 conversions[narrowingArg]++; 802 break; 803 case Conversion::Extend: 804 conversions[nonNarrowingArg]++; 805 break; 806 } 807 } 808 809 void addResultDistance(mlir::Type from, mlir::Type to) { 810 switch (conversionBetweenTypes(from, to)) { 811 case Conversion::Forbidden: 812 infinite = true; 813 break; 814 case Conversion::None: 815 break; 816 case Conversion::Narrow: 817 conversions[nonExtendingResult]++; 818 break; 819 case Conversion::Extend: 820 conversions[extendingResult]++; 821 break; 822 } 823 } 824 825 // Floating point can be mlir::FloatType or fir::real 826 static unsigned getFloatingPointWidth(mlir::Type t) { 827 if (auto f{t.dyn_cast<mlir::FloatType>()}) 828 return f.getWidth(); 829 // FIXME: Get width another way for fir.real/complex 830 // - use fir/KindMapping.h and llvm::Type 831 // - or use evaluate/type.h 832 if (auto r{t.dyn_cast<fir::RealType>()}) 833 return r.getFKind() * 4; 834 if (auto cplx{t.dyn_cast<fir::ComplexType>()}) 835 return cplx.getFKind() * 4; 836 llvm_unreachable("not a floating-point type"); 837 } 838 839 static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { 840 if (from == to) 841 return Conversion::None; 842 843 if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) { 844 if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) { 845 return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow 846 : Conversion::Extend; 847 } 848 } 849 850 if (fir::isa_real(from) && fir::isa_real(to)) { 851 return getFloatingPointWidth(from) > getFloatingPointWidth(to) 852 ? Conversion::Narrow 853 : Conversion::Extend; 854 } 855 856 if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) { 857 if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) { 858 return getFloatingPointWidth(fromCplxTy) > 859 getFloatingPointWidth(toCplxTy) 860 ? Conversion::Narrow 861 : Conversion::Extend; 862 } 863 } 864 // Notes: 865 // - No conversion between character types, specialization of runtime 866 // functions should be made instead. 867 // - It is not clear there is a use case for automatic conversions 868 // around Logical and it may damage hidden information in the physical 869 // storage so do not do it. 870 return Conversion::Forbidden; 871 } 872 873 // Below are indexes to access data in conversions. 874 // The order in data does matter for lexicographical_compare 875 enum { 876 narrowingArg = 0, // usually bad 877 extendingResult, // usually bad 878 nonExtendingResult, // usually ok 879 nonNarrowingArg, // usually ok 880 dataSize 881 }; 882 883 std::array<int, dataSize> conversions = {}; 884 bool infinite = false; // When forbidden conversion or wrong argument number 885 }; 886 887 /// Build mlir::FuncOp from runtime symbol description and add 888 /// fir.runtime attribute. 889 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder, 890 const RuntimeFunction &runtime) { 891 mlir::FuncOp function = builder.addNamedFunction( 892 loc, runtime.symbol, runtime.typeGenerator(builder.getContext())); 893 function->setAttr("fir.runtime", builder.getUnitAttr()); 894 return function; 895 } 896 897 /// Select runtime function that has the smallest distance to the intrinsic 898 /// function type and that will not imply narrowing arguments or extending the 899 /// result. 900 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 901 mlir::FuncOp searchFunctionInLibrary( 902 mlir::Location loc, fir::FirOpBuilder &builder, 903 const Fortran::common::StaticMultimapView<RuntimeFunction> &lib, 904 llvm::StringRef name, mlir::FunctionType funcType, 905 const RuntimeFunction **bestNearMatch, 906 FunctionDistance &bestMatchDistance) { 907 std::pair<const RuntimeFunction *, const RuntimeFunction *> range = 908 lib.equal_range(name); 909 for (auto iter = range.first; iter != range.second && iter; ++iter) { 910 const RuntimeFunction &impl = *iter; 911 mlir::FunctionType implType = impl.typeGenerator(builder.getContext()); 912 if (funcType == implType) 913 return getFuncOp(loc, builder, impl); // exact match 914 915 FunctionDistance distance(funcType, implType); 916 if (distance.isSmallerThan(bestMatchDistance)) { 917 *bestNearMatch = &impl; 918 bestMatchDistance = std::move(distance); 919 } 920 } 921 return {}; 922 } 923 924 /// Search runtime for the best runtime function given an intrinsic name 925 /// and interface. The interface may not be a perfect match in which case 926 /// the caller is responsible to insert argument and return value conversions. 927 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 928 static mlir::FuncOp getRuntimeFunction(mlir::Location loc, 929 fir::FirOpBuilder &builder, 930 llvm::StringRef name, 931 mlir::FunctionType funcType) { 932 const RuntimeFunction *bestNearMatch = nullptr; 933 FunctionDistance bestMatchDistance{}; 934 mlir::FuncOp match; 935 using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>; 936 static constexpr RtMap pgmathF(pgmathFast); 937 static_assert(pgmathF.Verify() && "map must be sorted"); 938 if (mathRuntimeVersion == fastVersion) { 939 match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, 940 &bestNearMatch, bestMatchDistance); 941 } else { 942 assert(mathRuntimeVersion == llvmOnly && "unknown math runtime"); 943 } 944 if (match) 945 return match; 946 947 // Go through llvm intrinsics if not exact match in libpgmath or if 948 // mathRuntimeVersion == llvmOnly 949 static constexpr RtMap llvmIntr(llvmIntrinsics); 950 static_assert(llvmIntr.Verify() && "map must be sorted"); 951 if (mlir::FuncOp exactMatch = 952 searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType, 953 &bestNearMatch, bestMatchDistance)) 954 return exactMatch; 955 956 if (bestNearMatch != nullptr) { 957 if (bestMatchDistance.isLosingPrecision()) { 958 // Using this runtime version requires narrowing the arguments 959 // or extending the result. It is not numerically safe. There 960 // is currently no quad math library that was described in 961 // lowering and could be used here. Emit an error and continue 962 // generating the code with the narrowing cast so that the user 963 // can get a complete list of the problematic intrinsic calls. 964 std::string message("TODO: no math runtime available for '"); 965 llvm::raw_string_ostream sstream(message); 966 if (name == "pow") { 967 assert(funcType.getNumInputs() == 2 && 968 "power operator has two arguments"); 969 sstream << funcType.getInput(0) << " ** " << funcType.getInput(1); 970 } else { 971 sstream << name << "("; 972 if (funcType.getNumInputs() > 0) 973 sstream << funcType.getInput(0); 974 for (mlir::Type argType : funcType.getInputs().drop_front()) 975 sstream << ", " << argType; 976 sstream << ")"; 977 } 978 sstream << "'"; 979 mlir::emitError(loc, message); 980 } 981 return getFuncOp(loc, builder, *bestNearMatch); 982 } 983 return {}; 984 } 985 986 /// Helpers to get function type from arguments and result type. 987 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType, 988 llvm::ArrayRef<mlir::Value> arguments, 989 fir::FirOpBuilder &builder) { 990 llvm::SmallVector<mlir::Type> argTypes; 991 for (mlir::Value arg : arguments) 992 argTypes.push_back(arg.getType()); 993 llvm::SmallVector<mlir::Type> resTypes; 994 if (resultType) 995 resTypes.push_back(*resultType); 996 return mlir::FunctionType::get(builder.getModule().getContext(), argTypes, 997 resTypes); 998 } 999 1000 /// fir::ExtendedValue to mlir::Value translation layer 1001 1002 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder, 1003 mlir::Location loc) { 1004 assert(val && "optional unhandled here"); 1005 mlir::Type type = val.getType(); 1006 mlir::Value base = val; 1007 mlir::IndexType indexType = builder.getIndexType(); 1008 llvm::SmallVector<mlir::Value> extents; 1009 1010 fir::factory::CharacterExprHelper charHelper{builder, loc}; 1011 // FIXME: we may want to allow non character scalar here. 1012 if (charHelper.isCharacterScalar(type)) 1013 return charHelper.toExtendedValue(val); 1014 1015 if (auto refType = type.dyn_cast<fir::ReferenceType>()) 1016 type = refType.getEleTy(); 1017 1018 if (auto arrayType = type.dyn_cast<fir::SequenceType>()) { 1019 type = arrayType.getEleTy(); 1020 for (fir::SequenceType::Extent extent : arrayType.getShape()) { 1021 if (extent == fir::SequenceType::getUnknownExtent()) 1022 break; 1023 extents.emplace_back( 1024 builder.createIntegerConstant(loc, indexType, extent)); 1025 } 1026 // Last extent might be missing in case of assumed-size. If more extents 1027 // could not be deduced from type, that's an error (a fir.box should 1028 // have been used in the interface). 1029 if (extents.size() + 1 < arrayType.getShape().size()) 1030 mlir::emitError(loc, "cannot retrieve array extents from type"); 1031 } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) { 1032 fir::emitFatalError(loc, "not yet implemented: descriptor or derived type"); 1033 } 1034 1035 if (!extents.empty()) 1036 return fir::ArrayBoxValue{base, extents}; 1037 return base; 1038 } 1039 1040 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder, 1041 mlir::Location loc) { 1042 if (const fir::CharBoxValue *charBox = val.getCharBox()) { 1043 mlir::Value buffer = charBox->getBuffer(); 1044 if (buffer.getType().isa<fir::BoxCharType>()) 1045 return buffer; 1046 return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar( 1047 buffer, charBox->getLen()); 1048 } 1049 1050 // FIXME: need to access other ExtendedValue variants and handle them 1051 // properly. 1052 return fir::getBase(val); 1053 } 1054 1055 //===----------------------------------------------------------------------===// 1056 // IntrinsicLibrary 1057 //===----------------------------------------------------------------------===// 1058 1059 /// Emit a TODO error message for as yet unimplemented intrinsics. 1060 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) { 1061 TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name)); 1062 } 1063 1064 template <typename GeneratorType> 1065 fir::ExtendedValue IntrinsicLibrary::genElementalCall( 1066 GeneratorType generator, llvm::StringRef name, mlir::Type resultType, 1067 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1068 llvm::SmallVector<mlir::Value> scalarArgs; 1069 for (const fir::ExtendedValue &arg : args) 1070 if (arg.getUnboxed() || arg.getCharBox()) 1071 scalarArgs.emplace_back(fir::getBase(arg)); 1072 else 1073 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1074 return invokeGenerator(generator, resultType, scalarArgs); 1075 } 1076 1077 template <> 1078 fir::ExtendedValue 1079 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>( 1080 ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType, 1081 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1082 for (const fir::ExtendedValue &arg : args) 1083 if (!arg.getUnboxed() && !arg.getCharBox()) 1084 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1085 if (outline) 1086 return outlineInExtendedWrapper(generator, name, resultType, args); 1087 return std::invoke(generator, *this, resultType, args); 1088 } 1089 1090 template <> 1091 fir::ExtendedValue 1092 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>( 1093 SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType, 1094 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1095 for (const fir::ExtendedValue &arg : args) 1096 if (!arg.getUnboxed() && !arg.getCharBox()) 1097 // fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1098 crashOnMissingIntrinsic(loc, name); 1099 if (outline) 1100 return outlineInExtendedWrapper(generator, name, resultType, args); 1101 std::invoke(generator, *this, args); 1102 return mlir::Value(); 1103 } 1104 1105 static fir::ExtendedValue 1106 invokeHandler(IntrinsicLibrary::ElementalGenerator generator, 1107 const IntrinsicHandler &handler, 1108 llvm::Optional<mlir::Type> resultType, 1109 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1110 IntrinsicLibrary &lib) { 1111 assert(resultType && "expect elemental intrinsic to be functions"); 1112 return lib.genElementalCall(generator, handler.name, *resultType, args, 1113 outline); 1114 } 1115 1116 static fir::ExtendedValue 1117 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator, 1118 const IntrinsicHandler &handler, 1119 llvm::Optional<mlir::Type> resultType, 1120 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1121 IntrinsicLibrary &lib) { 1122 assert(resultType && "expect intrinsic function"); 1123 if (handler.isElemental) 1124 return lib.genElementalCall(generator, handler.name, *resultType, args, 1125 outline); 1126 if (outline) 1127 return lib.outlineInExtendedWrapper(generator, handler.name, *resultType, 1128 args); 1129 return std::invoke(generator, lib, *resultType, args); 1130 } 1131 1132 static fir::ExtendedValue 1133 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator, 1134 const IntrinsicHandler &handler, 1135 llvm::Optional<mlir::Type> resultType, 1136 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1137 IntrinsicLibrary &lib) { 1138 if (handler.isElemental) 1139 return lib.genElementalCall(generator, handler.name, mlir::Type{}, args, 1140 outline); 1141 if (outline) 1142 return lib.outlineInExtendedWrapper(generator, handler.name, resultType, 1143 args); 1144 std::invoke(generator, lib, args); 1145 return mlir::Value{}; 1146 } 1147 1148 fir::ExtendedValue 1149 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name, 1150 llvm::Optional<mlir::Type> resultType, 1151 llvm::ArrayRef<fir::ExtendedValue> args) { 1152 if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) { 1153 bool outline = handler->outline || outlineAllIntrinsics; 1154 return std::visit( 1155 [&](auto &generator) -> fir::ExtendedValue { 1156 return invokeHandler(generator, *handler, resultType, args, outline, 1157 *this); 1158 }, 1159 handler->generator); 1160 } 1161 1162 if (!resultType) 1163 // Subroutine should have a handler, they are likely missing for now. 1164 crashOnMissingIntrinsic(loc, name); 1165 1166 // Try the runtime if no special handler was defined for the 1167 // intrinsic being called. Maths runtime only has numerical elemental. 1168 // No optional arguments are expected at this point, the code will 1169 // crash if it gets absent optional. 1170 1171 // FIXME: using toValue to get the type won't work with array arguments. 1172 llvm::SmallVector<mlir::Value> mlirArgs; 1173 for (const fir::ExtendedValue &extendedVal : args) { 1174 mlir::Value val = toValue(extendedVal, builder, loc); 1175 if (!val) 1176 // If an absent optional gets there, most likely its handler has just 1177 // not yet been defined. 1178 crashOnMissingIntrinsic(loc, name); 1179 mlirArgs.emplace_back(val); 1180 } 1181 mlir::FunctionType soughtFuncType = 1182 getFunctionType(*resultType, mlirArgs, builder); 1183 1184 IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator = 1185 getRuntimeCallGenerator(name, soughtFuncType); 1186 return genElementalCall(runtimeCallGenerator, name, *resultType, args, 1187 /* outline */ true); 1188 } 1189 1190 mlir::Value 1191 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator, 1192 mlir::Type resultType, 1193 llvm::ArrayRef<mlir::Value> args) { 1194 return std::invoke(generator, *this, resultType, args); 1195 } 1196 1197 mlir::Value 1198 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator, 1199 mlir::Type resultType, 1200 llvm::ArrayRef<mlir::Value> args) { 1201 return generator(builder, loc, args); 1202 } 1203 1204 mlir::Value 1205 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator, 1206 mlir::Type resultType, 1207 llvm::ArrayRef<mlir::Value> args) { 1208 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1209 for (mlir::Value arg : args) 1210 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1211 auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs); 1212 return toValue(extendedResult, builder, loc); 1213 } 1214 1215 mlir::Value 1216 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator, 1217 llvm::ArrayRef<mlir::Value> args) { 1218 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1219 for (mlir::Value arg : args) 1220 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1221 std::invoke(generator, *this, extendedArgs); 1222 return {}; 1223 } 1224 1225 template <typename GeneratorType> 1226 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator, 1227 llvm::StringRef name, 1228 mlir::FunctionType funcType, 1229 bool loadRefArguments) { 1230 std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType); 1231 mlir::FuncOp function = builder.getNamedFunction(wrapperName); 1232 if (!function) { 1233 // First time this wrapper is needed, build it. 1234 function = builder.createFunction(loc, wrapperName, funcType); 1235 function->setAttr("fir.intrinsic", builder.getUnitAttr()); 1236 auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; 1237 auto linkage = 1238 mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage); 1239 function->setAttr("llvm.linkage", linkage); 1240 function.addEntryBlock(); 1241 1242 // Create local context to emit code into the newly created function 1243 // This new function is not linked to a source file location, only 1244 // its calls will be. 1245 auto localBuilder = 1246 std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap()); 1247 localBuilder->setInsertionPointToStart(&function.front()); 1248 // Location of code inside wrapper of the wrapper is independent from 1249 // the location of the intrinsic call. 1250 mlir::Location localLoc = localBuilder->getUnknownLoc(); 1251 llvm::SmallVector<mlir::Value> localArguments; 1252 for (mlir::BlockArgument bArg : function.front().getArguments()) { 1253 auto refType = bArg.getType().dyn_cast<fir::ReferenceType>(); 1254 if (loadRefArguments && refType) { 1255 auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg); 1256 localArguments.push_back(loaded); 1257 } else { 1258 localArguments.push_back(bArg); 1259 } 1260 } 1261 1262 IntrinsicLibrary localLib{*localBuilder, localLoc}; 1263 1264 if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) { 1265 localLib.invokeGenerator(generator, localArguments); 1266 localBuilder->create<mlir::func::ReturnOp>(localLoc); 1267 } else { 1268 assert(funcType.getNumResults() == 1 && 1269 "expect one result for intrinsic function wrapper type"); 1270 mlir::Type resultType = funcType.getResult(0); 1271 auto result = 1272 localLib.invokeGenerator(generator, resultType, localArguments); 1273 localBuilder->create<mlir::func::ReturnOp>(localLoc, result); 1274 } 1275 } else { 1276 // Wrapper was already built, ensure it has the sought type 1277 assert(function.getType() == funcType && 1278 "conflict between intrinsic wrapper types"); 1279 } 1280 return function; 1281 } 1282 1283 /// Helpers to detect absent optional (not yet supported in outlining). 1284 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) { 1285 for (const fir::ExtendedValue &arg : args) 1286 if (!fir::getBase(arg)) 1287 return true; 1288 return false; 1289 } 1290 1291 template <typename GeneratorType> 1292 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper( 1293 GeneratorType generator, llvm::StringRef name, 1294 llvm::Optional<mlir::Type> resultType, 1295 llvm::ArrayRef<fir::ExtendedValue> args) { 1296 if (hasAbsentOptional(args)) 1297 TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + 1298 " with absent optional argument"); 1299 llvm::SmallVector<mlir::Value> mlirArgs; 1300 for (const auto &extendedVal : args) 1301 mlirArgs.emplace_back(toValue(extendedVal, builder, loc)); 1302 mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder); 1303 mlir::FuncOp wrapper = getWrapper(generator, name, funcType); 1304 auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs); 1305 if (resultType) 1306 return toExtendedValue(call.getResult(0), builder, loc); 1307 // Subroutine calls 1308 return mlir::Value{}; 1309 } 1310 1311 IntrinsicLibrary::RuntimeCallGenerator 1312 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name, 1313 mlir::FunctionType soughtFuncType) { 1314 mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType); 1315 if (!funcOp) { 1316 std::string buffer("not yet implemented: missing intrinsic lowering: "); 1317 llvm::raw_string_ostream sstream(buffer); 1318 sstream << name << "\nrequested type was: " << soughtFuncType << '\n'; 1319 fir::emitFatalError(loc, buffer); 1320 } 1321 1322 mlir::FunctionType actualFuncType = funcOp.getType(); 1323 assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() && 1324 actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() && 1325 actualFuncType.getNumResults() == 1 && "Bad intrinsic match"); 1326 1327 return [funcOp, actualFuncType, 1328 soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc, 1329 llvm::ArrayRef<mlir::Value> args) { 1330 llvm::SmallVector<mlir::Value> convertedArguments; 1331 for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args)) 1332 convertedArguments.push_back(builder.createConvert(loc, fst, snd)); 1333 auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments); 1334 mlir::Type soughtType = soughtFuncType.getResult(0); 1335 return builder.createConvert(loc, soughtType, call.getResult(0)); 1336 }; 1337 } 1338 1339 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) { 1340 assert(stmtCtx); 1341 fir::FirOpBuilder *bldr = &builder; 1342 stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 1343 } 1344 1345 fir::ExtendedValue 1346 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 1347 mlir::Type resultType, 1348 llvm::StringRef intrinsicName) { 1349 fir::ExtendedValue res = 1350 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1351 return res.match( 1352 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1353 // Add cleanup code 1354 addCleanUpForTemp(loc, box.getAddr()); 1355 return box; 1356 }, 1357 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 1358 // Add cleanup code 1359 auto addr = 1360 builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); 1361 addCleanUpForTemp(loc, addr); 1362 return box; 1363 }, 1364 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 1365 // Add cleanup code 1366 addCleanUpForTemp(loc, box.getAddr()); 1367 return box; 1368 }, 1369 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 1370 // Add cleanup code 1371 addCleanUpForTemp(loc, tempAddr); 1372 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 1373 }, 1374 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1375 // Add cleanup code 1376 addCleanUpForTemp(loc, box.getAddr()); 1377 return box; 1378 }, 1379 [&](const auto &) -> fir::ExtendedValue { 1380 fir::emitFatalError(loc, "unexpected result for " + intrinsicName); 1381 }); 1382 } 1383 1384 //===----------------------------------------------------------------------===// 1385 // Code generators for the intrinsic 1386 //===----------------------------------------------------------------------===// 1387 1388 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name, 1389 mlir::Type resultType, 1390 llvm::ArrayRef<mlir::Value> args) { 1391 mlir::FunctionType soughtFuncType = 1392 getFunctionType(resultType, args, builder); 1393 return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args); 1394 } 1395 1396 // ABS 1397 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType, 1398 llvm::ArrayRef<mlir::Value> args) { 1399 assert(args.size() == 1); 1400 mlir::Value arg = args[0]; 1401 mlir::Type type = arg.getType(); 1402 if (fir::isa_real(type)) { 1403 // Runtime call to fp abs. An alternative would be to use mlir 1404 // math::AbsFOp but it does not support all fir floating point types. 1405 return genRuntimeCall("abs", resultType, args); 1406 } 1407 if (auto intType = type.dyn_cast<mlir::IntegerType>()) { 1408 // At the time of this implementation there is no abs op in mlir. 1409 // So, implement abs here without branching. 1410 mlir::Value shift = 1411 builder.createIntegerConstant(loc, intType, intType.getWidth() - 1); 1412 auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift); 1413 auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask); 1414 return builder.create<mlir::arith::SubIOp>(loc, xored, mask); 1415 } 1416 if (fir::isa_complex(type)) { 1417 // Use HYPOT to fulfill the no underflow/overflow requirement. 1418 auto parts = fir::factory::Complex{builder, loc}.extractParts(arg); 1419 llvm::SmallVector<mlir::Value> args = {parts.first, parts.second}; 1420 return genRuntimeCall("hypot", resultType, args); 1421 } 1422 llvm_unreachable("unexpected type in ABS argument"); 1423 } 1424 1425 // AIMAG 1426 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType, 1427 llvm::ArrayRef<mlir::Value> args) { 1428 assert(args.size() == 1); 1429 return fir::factory::Complex{builder, loc}.extractComplexPart( 1430 args[0], true /* isImagPart */); 1431 } 1432 1433 // ALL 1434 fir::ExtendedValue 1435 IntrinsicLibrary::genAll(mlir::Type resultType, 1436 llvm::ArrayRef<fir::ExtendedValue> args) { 1437 1438 assert(args.size() == 2); 1439 // Handle required mask argument 1440 mlir::Value mask = builder.createBox(loc, args[0]); 1441 1442 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 1443 int rank = maskArry.rank(); 1444 assert(rank >= 1); 1445 1446 // Handle optional dim argument 1447 bool absentDim = isAbsent(args[1]); 1448 mlir::Value dim = 1449 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1450 : fir::getBase(args[1]); 1451 1452 if (rank == 1 || absentDim) 1453 return builder.createConvert(loc, resultType, 1454 fir::runtime::genAll(builder, loc, mask, dim)); 1455 1456 // else use the result descriptor AllDim() intrinsic 1457 1458 // Create mutable fir.box to be passed to the runtime for the result. 1459 1460 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 1461 fir::MutableBoxValue resultMutableBox = 1462 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1463 mlir::Value resultIrBox = 1464 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1465 1466 // Call runtime. The runtime is allocating the result. 1467 fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim); 1468 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 1469 .match( 1470 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1471 addCleanUpForTemp(loc, box.getAddr()); 1472 return box; 1473 }, 1474 [&](const auto &) -> fir::ExtendedValue { 1475 fir::emitFatalError(loc, "Invalid result for ALL"); 1476 }); 1477 } 1478 1479 // ALLOCATED 1480 fir::ExtendedValue 1481 IntrinsicLibrary::genAllocated(mlir::Type resultType, 1482 llvm::ArrayRef<fir::ExtendedValue> args) { 1483 assert(args.size() == 1); 1484 return args[0].match( 1485 [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue { 1486 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x); 1487 }, 1488 [&](const auto &) -> fir::ExtendedValue { 1489 fir::emitFatalError(loc, 1490 "allocated arg not lowered to MutableBoxValue"); 1491 }); 1492 } 1493 1494 // ANY 1495 fir::ExtendedValue 1496 IntrinsicLibrary::genAny(mlir::Type resultType, 1497 llvm::ArrayRef<fir::ExtendedValue> args) { 1498 1499 assert(args.size() == 2); 1500 // Handle required mask argument 1501 mlir::Value mask = builder.createBox(loc, args[0]); 1502 1503 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 1504 int rank = maskArry.rank(); 1505 assert(rank >= 1); 1506 1507 // Handle optional dim argument 1508 bool absentDim = isAbsent(args[1]); 1509 mlir::Value dim = 1510 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1511 : fir::getBase(args[1]); 1512 1513 if (rank == 1 || absentDim) 1514 return builder.createConvert(loc, resultType, 1515 fir::runtime::genAny(builder, loc, mask, dim)); 1516 1517 // else use the result descriptor AnyDim() intrinsic 1518 1519 // Create mutable fir.box to be passed to the runtime for the result. 1520 1521 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 1522 fir::MutableBoxValue resultMutableBox = 1523 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1524 mlir::Value resultIrBox = 1525 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1526 1527 // Call runtime. The runtime is allocating the result. 1528 fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim); 1529 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 1530 .match( 1531 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1532 addCleanUpForTemp(loc, box.getAddr()); 1533 return box; 1534 }, 1535 [&](const auto &) -> fir::ExtendedValue { 1536 fir::emitFatalError(loc, "Invalid result for ANY"); 1537 }); 1538 } 1539 1540 // ASSOCIATED 1541 fir::ExtendedValue 1542 IntrinsicLibrary::genAssociated(mlir::Type resultType, 1543 llvm::ArrayRef<fir::ExtendedValue> args) { 1544 assert(args.size() == 2); 1545 auto *pointer = 1546 args[0].match([&](const fir::MutableBoxValue &x) { return &x; }, 1547 [&](const auto &) -> const fir::MutableBoxValue * { 1548 fir::emitFatalError(loc, "pointer not a MutableBoxValue"); 1549 }); 1550 const fir::ExtendedValue &target = args[1]; 1551 if (isAbsent(target)) 1552 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer); 1553 1554 mlir::Value targetBox = builder.createBox(loc, target); 1555 if (fir::valueHasFirAttribute(fir::getBase(target), 1556 fir::getOptionalAttrName())) { 1557 // Subtle: contrary to other intrinsic optional arguments, disassociated 1558 // POINTER and unallocated ALLOCATABLE actual argument are not considered 1559 // absent here. This is because ASSOCIATED has special requirements for 1560 // TARGET actual arguments that are POINTERs. There is no precise 1561 // requirements for ALLOCATABLEs, but all existing Fortran compilers treat 1562 // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED 1563 // to rerun false. The runtime deals with the disassociated/unallocated 1564 // case. Simply ensures that TARGET that are OPTIONAL get conditionally 1565 // emboxed here to convey the optional aspect to the runtime. 1566 auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 1567 fir::getBase(target)); 1568 auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType()); 1569 targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox, 1570 absentBox); 1571 } 1572 mlir::Value pointerBoxRef = 1573 fir::factory::getMutableIRBox(builder, loc, *pointer); 1574 auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef); 1575 return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox); 1576 } 1577 1578 // CHAR 1579 fir::ExtendedValue 1580 IntrinsicLibrary::genChar(mlir::Type type, 1581 llvm::ArrayRef<fir::ExtendedValue> args) { 1582 // Optional KIND argument. 1583 assert(args.size() >= 1); 1584 const mlir::Value *arg = args[0].getUnboxed(); 1585 // expect argument to be a scalar integer 1586 if (!arg) 1587 mlir::emitError(loc, "CHAR intrinsic argument not unboxed"); 1588 fir::factory::CharacterExprHelper helper{builder, loc}; 1589 fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind(); 1590 mlir::Value cast = helper.createSingletonFromCode(*arg, kind); 1591 mlir::Value len = 1592 builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1); 1593 return fir::CharBoxValue{cast, len}; 1594 } 1595 1596 // DIM 1597 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType, 1598 llvm::ArrayRef<mlir::Value> args) { 1599 assert(args.size() == 2); 1600 if (resultType.isa<mlir::IntegerType>()) { 1601 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 1602 auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]); 1603 auto cmp = builder.create<mlir::arith::CmpIOp>( 1604 loc, mlir::arith::CmpIPredicate::sgt, diff, zero); 1605 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 1606 } 1607 assert(fir::isa_real(resultType) && "Only expects real and integer in DIM"); 1608 mlir::Value zero = builder.createRealZeroConstant(loc, resultType); 1609 auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]); 1610 auto cmp = builder.create<mlir::arith::CmpFOp>( 1611 loc, mlir::arith::CmpFPredicate::OGT, diff, zero); 1612 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 1613 } 1614 1615 // DOT_PRODUCT 1616 fir::ExtendedValue 1617 IntrinsicLibrary::genDotProduct(mlir::Type resultType, 1618 llvm::ArrayRef<fir::ExtendedValue> args) { 1619 return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc, 1620 stmtCtx, args); 1621 } 1622 1623 // CPU_TIME 1624 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) { 1625 assert(args.size() == 1); 1626 const mlir::Value *arg = args[0].getUnboxed(); 1627 assert(arg && "nonscalar cpu_time argument"); 1628 mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc); 1629 mlir::Value res2 = 1630 builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1); 1631 builder.create<fir::StoreOp>(loc, res2, *arg); 1632 } 1633 1634 // DATE_AND_TIME 1635 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) { 1636 assert(args.size() == 4 && "date_and_time has 4 args"); 1637 llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3); 1638 for (unsigned i = 0; i < 3; ++i) 1639 if (const fir::CharBoxValue *charBox = args[i].getCharBox()) 1640 charArgs[i] = *charBox; 1641 1642 mlir::Value values = fir::getBase(args[3]); 1643 if (!values) 1644 values = builder.create<fir::AbsentOp>( 1645 loc, fir::BoxType::get(builder.getNoneType())); 1646 1647 Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1], 1648 charArgs[2], values); 1649 } 1650 1651 // IAND 1652 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType, 1653 llvm::ArrayRef<mlir::Value> args) { 1654 assert(args.size() == 2); 1655 return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]); 1656 } 1657 1658 // IBITS 1659 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType, 1660 llvm::ArrayRef<mlir::Value> args) { 1661 // A conformant IBITS(I,POS,LEN) call satisfies: 1662 // POS >= 0 1663 // LEN >= 0 1664 // POS + LEN <= BIT_SIZE(I) 1665 // Return: LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN)) 1666 // For a conformant call, implementing (I >> POS) with a signed or an 1667 // unsigned shift produces the same result. For a nonconformant call, 1668 // the two choices may produce different results. 1669 assert(args.size() == 3); 1670 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 1671 mlir::Value len = builder.createConvert(loc, resultType, args[2]); 1672 mlir::Value bitSize = builder.createIntegerConstant( 1673 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 1674 auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len); 1675 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 1676 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 1677 auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount); 1678 auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos); 1679 auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask); 1680 auto lenIsZero = builder.create<mlir::arith::CmpIOp>( 1681 loc, mlir::arith::CmpIPredicate::eq, len, zero); 1682 return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2); 1683 } 1684 1685 // LEN 1686 // Note that this is only used for an unrestricted intrinsic LEN call. 1687 // Other uses of LEN are rewritten as descriptor inquiries by the front-end. 1688 fir::ExtendedValue 1689 IntrinsicLibrary::genLen(mlir::Type resultType, 1690 llvm::ArrayRef<fir::ExtendedValue> args) { 1691 // Optional KIND argument reflected in result type and otherwise ignored. 1692 assert(args.size() == 1 || args.size() == 2); 1693 mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]); 1694 return builder.createConvert(loc, resultType, len); 1695 } 1696 1697 // LEN_TRIM 1698 fir::ExtendedValue 1699 IntrinsicLibrary::genLenTrim(mlir::Type resultType, 1700 llvm::ArrayRef<fir::ExtendedValue> args) { 1701 // Optional KIND argument reflected in result type and otherwise ignored. 1702 assert(args.size() == 1 || args.size() == 2); 1703 const fir::CharBoxValue *charBox = args[0].getCharBox(); 1704 if (!charBox) 1705 TODO(loc, "character array len_trim"); 1706 auto len = 1707 fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox); 1708 return builder.createConvert(loc, resultType, len); 1709 } 1710 1711 // LGE, LGT, LLE, LLT 1712 template <mlir::arith::CmpIPredicate pred> 1713 fir::ExtendedValue 1714 IntrinsicLibrary::genCharacterCompare(mlir::Type type, 1715 llvm::ArrayRef<fir::ExtendedValue> args) { 1716 assert(args.size() == 2); 1717 return fir::runtime::genCharCompare( 1718 builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]), 1719 fir::getBase(args[1]), fir::getLen(args[1])); 1720 } 1721 1722 // Compare two FIR values and return boolean result as i1. 1723 template <Extremum extremum, ExtremumBehavior behavior> 1724 static mlir::Value createExtremumCompare(mlir::Location loc, 1725 fir::FirOpBuilder &builder, 1726 mlir::Value left, mlir::Value right) { 1727 static constexpr mlir::arith::CmpIPredicate integerPredicate = 1728 extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt 1729 : mlir::arith::CmpIPredicate::slt; 1730 static constexpr mlir::arith::CmpFPredicate orderedCmp = 1731 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT 1732 : mlir::arith::CmpFPredicate::OLT; 1733 mlir::Type type = left.getType(); 1734 mlir::Value result; 1735 if (fir::isa_real(type)) { 1736 // Note: the signaling/quit aspect of the result required by IEEE 1737 // cannot currently be obtained with LLVM without ad-hoc runtime. 1738 if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { 1739 // Return the number if one of the inputs is NaN and the other is 1740 // a number. 1741 auto leftIsResult = 1742 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1743 auto rightIsNan = builder.create<mlir::arith::CmpFOp>( 1744 loc, mlir::arith::CmpFPredicate::UNE, right, right); 1745 result = 1746 builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan); 1747 } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { 1748 // Always return NaNs if one the input is NaNs 1749 auto leftIsResult = 1750 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1751 auto leftIsNan = builder.create<mlir::arith::CmpFOp>( 1752 loc, mlir::arith::CmpFPredicate::UNE, left, left); 1753 result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan); 1754 } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { 1755 // If the left is a NaN, return the right whatever it is. 1756 result = 1757 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 1758 } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { 1759 // If one of the operand is a NaN, return left whatever it is. 1760 static constexpr auto unorderedCmp = 1761 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT 1762 : mlir::arith::CmpFPredicate::ULT; 1763 result = 1764 builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right); 1765 } else { 1766 // TODO: ieeeMinNum/ieeeMaxNum 1767 static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, 1768 "ieeeMinNum/ieeeMaxNum behavior not implemented"); 1769 } 1770 } else if (fir::isa_integer(type)) { 1771 result = 1772 builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right); 1773 } else if (fir::isa_char(type)) { 1774 // TODO: ! character min and max is tricky because the result 1775 // length is the length of the longest argument! 1776 // So we may need a temp. 1777 TODO(loc, "CHARACTER min and max"); 1778 } 1779 assert(result && "result must be defined"); 1780 return result; 1781 } 1782 1783 // MAXLOC 1784 fir::ExtendedValue 1785 IntrinsicLibrary::genMaxloc(mlir::Type resultType, 1786 llvm::ArrayRef<fir::ExtendedValue> args) { 1787 return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim, 1788 resultType, builder, loc, stmtCtx, 1789 "unexpected result for Maxloc", args); 1790 } 1791 1792 // MAXVAL 1793 fir::ExtendedValue 1794 IntrinsicLibrary::genMaxval(mlir::Type resultType, 1795 llvm::ArrayRef<fir::ExtendedValue> args) { 1796 return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim, 1797 fir::runtime::genMaxvalChar, resultType, builder, loc, 1798 stmtCtx, "unexpected result for Maxval", args); 1799 } 1800 1801 // MINLOC 1802 fir::ExtendedValue 1803 IntrinsicLibrary::genMinloc(mlir::Type resultType, 1804 llvm::ArrayRef<fir::ExtendedValue> args) { 1805 return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim, 1806 resultType, builder, loc, stmtCtx, 1807 "unexpected result for Minloc", args); 1808 } 1809 1810 // MINVAL 1811 fir::ExtendedValue 1812 IntrinsicLibrary::genMinval(mlir::Type resultType, 1813 llvm::ArrayRef<fir::ExtendedValue> args) { 1814 return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim, 1815 fir::runtime::genMinvalChar, resultType, builder, loc, 1816 stmtCtx, "unexpected result for Minval", args); 1817 } 1818 1819 // MIN and MAX 1820 template <Extremum extremum, ExtremumBehavior behavior> 1821 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type, 1822 llvm::ArrayRef<mlir::Value> args) { 1823 assert(args.size() >= 1); 1824 mlir::Value result = args[0]; 1825 for (auto arg : args.drop_front()) { 1826 mlir::Value mask = 1827 createExtremumCompare<extremum, behavior>(loc, builder, result, arg); 1828 result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg); 1829 } 1830 return result; 1831 } 1832 1833 // NULL 1834 fir::ExtendedValue 1835 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) { 1836 // NULL() without MOLD must be handled in the contexts where it can appear 1837 // (see table 16.5 of Fortran 2018 standard). 1838 assert(args.size() == 1 && isPresent(args[0]) && 1839 "MOLD argument required to lower NULL outside of any context"); 1840 const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>(); 1841 assert(mold && "MOLD must be a pointer or allocatable"); 1842 fir::BoxType boxType = mold->getBoxTy(); 1843 mlir::Value boxStorage = builder.createTemporary(loc, boxType); 1844 mlir::Value box = fir::factory::createUnallocatedBox( 1845 builder, loc, boxType, mold->nonDeferredLenParams()); 1846 builder.create<fir::StoreOp>(loc, box, boxStorage); 1847 return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {}); 1848 } 1849 1850 // RANDOM_INIT 1851 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) { 1852 assert(args.size() == 2); 1853 Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]), 1854 fir::getBase(args[1])); 1855 } 1856 1857 // RANDOM_NUMBER 1858 void IntrinsicLibrary::genRandomNumber( 1859 llvm::ArrayRef<fir::ExtendedValue> args) { 1860 assert(args.size() == 1); 1861 Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0])); 1862 } 1863 1864 // RANDOM_SEED 1865 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) { 1866 assert(args.size() == 3); 1867 for (int i = 0; i < 3; ++i) 1868 if (isPresent(args[i])) { 1869 Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i])); 1870 return; 1871 } 1872 Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{}); 1873 } 1874 1875 // SUM 1876 fir::ExtendedValue 1877 IntrinsicLibrary::genSum(mlir::Type resultType, 1878 llvm::ArrayRef<fir::ExtendedValue> args) { 1879 return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType, 1880 builder, loc, stmtCtx, "unexpected result for Sum", args); 1881 } 1882 1883 // SYSTEM_CLOCK 1884 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) { 1885 assert(args.size() == 3); 1886 Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]), 1887 fir::getBase(args[1]), fir::getBase(args[2])); 1888 } 1889 1890 // SIZE 1891 fir::ExtendedValue 1892 IntrinsicLibrary::genSize(mlir::Type resultType, 1893 llvm::ArrayRef<fir::ExtendedValue> args) { 1894 // Note that the value of the KIND argument is already reflected in the 1895 // resultType 1896 assert(args.size() == 3); 1897 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 1898 if (boxValue->hasAssumedRank()) 1899 TODO(loc, "SIZE intrinsic with assumed rank argument"); 1900 1901 // Get the ARRAY argument 1902 mlir::Value array = builder.createBox(loc, args[0]); 1903 1904 // The front-end rewrites SIZE without the DIM argument to 1905 // an array of SIZE with DIM in most cases, but it may not be 1906 // possible in some cases like when in SIZE(function_call()). 1907 if (isAbsent(args, 1)) 1908 return builder.createConvert(loc, resultType, 1909 fir::runtime::genSize(builder, loc, array)); 1910 1911 // Get the DIM argument. 1912 mlir::Value dim = fir::getBase(args[1]); 1913 if (!fir::isa_ref_type(dim.getType())) 1914 return builder.createConvert( 1915 loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim)); 1916 1917 mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim); 1918 return builder 1919 .genIfOp(loc, {resultType}, isDynamicallyAbsent, 1920 /*withElseRegion=*/true) 1921 .genThen([&]() { 1922 mlir::Value size = builder.createConvert( 1923 loc, resultType, fir::runtime::genSize(builder, loc, array)); 1924 builder.create<fir::ResultOp>(loc, size); 1925 }) 1926 .genElse([&]() { 1927 mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim); 1928 mlir::Value size = builder.createConvert( 1929 loc, resultType, 1930 fir::runtime::genSizeDim(builder, loc, array, dimValue)); 1931 builder.create<fir::ResultOp>(loc, size); 1932 }) 1933 .getResults()[0]; 1934 } 1935 1936 // LBOUND 1937 fir::ExtendedValue 1938 IntrinsicLibrary::genLbound(mlir::Type resultType, 1939 llvm::ArrayRef<fir::ExtendedValue> args) { 1940 // Calls to LBOUND that don't have the DIM argument, or for which 1941 // the DIM is a compile time constant, are folded to descriptor inquiries by 1942 // semantics. This function covers the situations where a call to the 1943 // runtime is required. 1944 assert(args.size() == 3); 1945 assert(!isAbsent(args[1])); 1946 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 1947 if (boxValue->hasAssumedRank()) 1948 TODO(loc, "LBOUND intrinsic with assumed rank argument"); 1949 1950 const fir::ExtendedValue &array = args[0]; 1951 mlir::Value box = array.match( 1952 [&](const fir::BoxValue &boxValue) -> mlir::Value { 1953 // This entity is mapped to a fir.box that may not contain the local 1954 // lower bound information if it is a dummy. Rebox it with the local 1955 // shape information. 1956 mlir::Value localShape = builder.createShape(loc, array); 1957 mlir::Value oldBox = boxValue.getAddr(); 1958 return builder.create<fir::ReboxOp>( 1959 loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{}); 1960 }, 1961 [&](const auto &) -> mlir::Value { 1962 // This a pointer/allocatable, or an entity not yet tracked with a 1963 // fir.box. For pointer/allocatable, createBox will forward the 1964 // descriptor that contains the correct lower bound information. For 1965 // other entities, a new fir.box will be made with the local lower 1966 // bounds. 1967 return builder.createBox(loc, array); 1968 }); 1969 1970 mlir::Value dim = fir::getBase(args[1]); 1971 return builder.createConvert( 1972 loc, resultType, 1973 fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim)); 1974 } 1975 1976 // UBOUND 1977 fir::ExtendedValue 1978 IntrinsicLibrary::genUbound(mlir::Type resultType, 1979 llvm::ArrayRef<fir::ExtendedValue> args) { 1980 assert(args.size() == 3 || args.size() == 2); 1981 if (args.size() == 3) { 1982 // Handle calls to UBOUND with the DIM argument, which return a scalar 1983 mlir::Value extent = fir::getBase(genSize(resultType, args)); 1984 mlir::Value lbound = fir::getBase(genLbound(resultType, args)); 1985 1986 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 1987 mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one); 1988 return builder.create<mlir::arith::AddIOp>(loc, ubound, extent); 1989 } else { 1990 // Handle calls to UBOUND without the DIM argument, which return an array 1991 mlir::Value kind = isAbsent(args[1]) 1992 ? builder.createIntegerConstant( 1993 loc, builder.getIndexType(), 1994 builder.getKindMap().defaultIntegerKind()) 1995 : fir::getBase(args[1]); 1996 1997 // Create mutable fir.box to be passed to the runtime for the result. 1998 mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1); 1999 fir::MutableBoxValue resultMutableBox = 2000 fir::factory::createTempMutableBox(builder, loc, type); 2001 mlir::Value resultIrBox = 2002 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2003 2004 fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]), 2005 kind); 2006 2007 return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND"); 2008 } 2009 return mlir::Value(); 2010 } 2011 2012 //===----------------------------------------------------------------------===// 2013 // Argument lowering rules interface 2014 //===----------------------------------------------------------------------===// 2015 2016 const Fortran::lower::IntrinsicArgumentLoweringRules * 2017 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) { 2018 if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName)) 2019 if (!handler->argLoweringRules.hasDefaultRules()) 2020 return &handler->argLoweringRules; 2021 return nullptr; 2022 } 2023 2024 /// Return how argument \p argName should be lowered given the rules for the 2025 /// intrinsic function. 2026 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs( 2027 mlir::Location loc, const IntrinsicArgumentLoweringRules &rules, 2028 llvm::StringRef argName) { 2029 for (const IntrinsicDummyArgument &arg : rules.args) { 2030 if (arg.name && arg.name == argName) 2031 return {arg.lowerAs, arg.handleDynamicOptional}; 2032 } 2033 fir::emitFatalError( 2034 loc, "internal: unknown intrinsic argument name in lowering '" + argName + 2035 "'"); 2036 } 2037 2038 //===----------------------------------------------------------------------===// 2039 // Public intrinsic call helpers 2040 //===----------------------------------------------------------------------===// 2041 2042 fir::ExtendedValue 2043 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, 2044 llvm::StringRef name, 2045 llvm::Optional<mlir::Type> resultType, 2046 llvm::ArrayRef<fir::ExtendedValue> args, 2047 Fortran::lower::StatementContext &stmtCtx) { 2048 return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall( 2049 name, resultType, args); 2050 } 2051 2052 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder, 2053 mlir::Location loc, 2054 llvm::ArrayRef<mlir::Value> args) { 2055 assert(args.size() > 0 && "max requires at least one argument"); 2056 return IntrinsicLibrary{builder, loc} 2057 .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(), 2058 args); 2059 } 2060 2061 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder, 2062 mlir::Location loc, mlir::Type type, 2063 mlir::Value x, mlir::Value y) { 2064 return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); 2065 } 2066