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/Optimizer/Builder/Character.h" 23 #include "flang/Optimizer/Builder/Complex.h" 24 #include "flang/Optimizer/Builder/FIRBuilder.h" 25 #include "flang/Optimizer/Builder/MutableBox.h" 26 #include "flang/Optimizer/Builder/Runtime/Character.h" 27 #include "flang/Optimizer/Builder/Runtime/Command.h" 28 #include "flang/Optimizer/Builder/Runtime/Inquiry.h" 29 #include "flang/Optimizer/Builder/Runtime/Numeric.h" 30 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" 31 #include "flang/Optimizer/Builder/Runtime/Reduction.h" 32 #include "flang/Optimizer/Builder/Runtime/Stop.h" 33 #include "flang/Optimizer/Builder/Runtime/Transformational.h" 34 #include "flang/Optimizer/Builder/Todo.h" 35 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 36 #include "flang/Optimizer/Support/FatalError.h" 37 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 38 #include "llvm/Support/CommandLine.h" 39 #include "llvm/Support/Debug.h" 40 41 #define DEBUG_TYPE "flang-lower-intrinsic" 42 43 #define PGMATH_DECLARE 44 #include "flang/Evaluate/pgmath.h.inc" 45 46 /// This file implements lowering of Fortran intrinsic procedures and Fortran 47 /// intrinsic module procedures. A call may be inlined with a mix of FIR and 48 /// MLIR operations, or as a call to a runtime function or LLVM intrinsic. 49 50 /// Lowering of intrinsic procedure calls is based on a map that associates 51 /// Fortran intrinsic generic names to FIR generator functions. 52 /// All generator functions are member functions of the IntrinsicLibrary class 53 /// and have the same interface. 54 /// If no generator is given for an intrinsic name, a math runtime library 55 /// is searched for an implementation and, if a runtime function is found, 56 /// a call is generated for it. LLVM intrinsics are handled as a math 57 /// runtime library here. 58 59 /// Enums used to templatize and share lowering of MIN and MAX. 60 enum class Extremum { Min, Max }; 61 62 // There are different ways to deal with NaNs in MIN and MAX. 63 // Known existing behaviors are listed below and can be selected for 64 // f18 MIN/MAX implementation. 65 enum class ExtremumBehavior { 66 // Note: the Signaling/quiet aspect of NaNs in the behaviors below are 67 // not described because there is no way to control/observe such aspect in 68 // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this 69 // aspect that are therefore currently not enforced. In the descriptions 70 // below, NaNs can be signaling or quite. Returned NaNs may be signaling 71 // if one of the input NaN was signaling but it cannot be guaranteed either. 72 // Existing compilers using an IEEE behavior (gfortran) also do not fulfill 73 // signaling/quiet requirements. 74 IeeeMinMaximumNumber, 75 // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): 76 // If one of the argument is and number and the other is NaN, return the 77 // number. If both arguements are NaN, return NaN. 78 // Compilers: gfortran. 79 IeeeMinMaximum, 80 // IEEE minimum/maximum behavior (754-2019, section 9.6): 81 // If one of the argument is NaN, return NaN. 82 MinMaxss, 83 // x86 minss/maxss behavior: 84 // If the second argument is a number and the other is NaN, return the number. 85 // In all other cases where at least one operand is NaN, return NaN. 86 // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. 87 PgfortranLlvm, 88 // "Opposite of" x86 minss/maxss behavior: 89 // If the first argument is a number and the other is NaN, return the 90 // number. 91 // In all other cases where at least one operand is NaN, return NaN. 92 // Compilers: xlf (only for MIN), and pgfortran (with llvm). 93 IeeeMinMaxNum 94 // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): 95 // TODO: Not implemented. 96 // It is the only behavior where the signaling/quiet aspect of a NaN argument 97 // impacts if the result should be NaN or the argument that is a number. 98 // LLVM/MLIR do not provide ways to observe this aspect, so it is not 99 // possible to implement it without some target dependent runtime. 100 }; 101 102 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() { 103 return fir::UnboxedValue{}; 104 } 105 106 /// Test if an ExtendedValue is absent. This is used to test if an intrinsic 107 /// argument are absent at compile time. 108 static bool isStaticallyAbsent(const fir::ExtendedValue &exv) { 109 return !fir::getBase(exv); 110 } 111 static bool isStaticallyAbsent(llvm::ArrayRef<fir::ExtendedValue> args, 112 size_t argIndex) { 113 return args.size() <= argIndex || isStaticallyAbsent(args[argIndex]); 114 } 115 static bool isStaticallyAbsent(llvm::ArrayRef<mlir::Value> args, 116 size_t argIndex) { 117 return args.size() <= argIndex || !args[argIndex]; 118 } 119 120 /// Test if an ExtendedValue is present. This is used to test if an intrinsic 121 /// argument is present at compile time. This does not imply that the related 122 /// value may not be an absent dummy optional, disassociated pointer, or a 123 /// deallocated allocatable. See `handleDynamicOptional` to deal with these 124 /// cases when it makes sense. 125 static bool isStaticallyPresent(const fir::ExtendedValue &exv) { 126 return !isStaticallyAbsent(exv); 127 } 128 129 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that 130 /// take a DIM argument. 131 template <typename FD> 132 static fir::ExtendedValue 133 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 134 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 135 llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg, 136 mlir::Value mask, int rank) { 137 138 // Create mutable fir.box to be passed to the runtime for the result. 139 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 140 fir::MutableBoxValue resultMutableBox = 141 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 142 mlir::Value resultIrBox = 143 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 144 145 mlir::Value dim = 146 isStaticallyAbsent(dimArg) 147 ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 148 : fir::getBase(dimArg); 149 funcDim(builder, loc, resultIrBox, array, dim, mask); 150 151 fir::ExtendedValue res = 152 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 153 return res.match( 154 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 155 // Add cleanup code 156 assert(stmtCtx); 157 fir::FirOpBuilder *bldr = &builder; 158 mlir::Value temp = box.getAddr(); 159 stmtCtx->attachCleanup( 160 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 161 return box; 162 }, 163 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 164 // Add cleanup code 165 assert(stmtCtx); 166 fir::FirOpBuilder *bldr = &builder; 167 mlir::Value temp = box.getAddr(); 168 stmtCtx->attachCleanup( 169 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 170 return box; 171 }, 172 [&](const auto &) -> fir::ExtendedValue { 173 fir::emitFatalError(loc, errMsg); 174 }); 175 } 176 177 /// Process calls to Product, Sum intrinsic functions 178 template <typename FN, typename FD> 179 static fir::ExtendedValue 180 genProdOrSum(FN func, FD funcDim, mlir::Type resultType, 181 fir::FirOpBuilder &builder, mlir::Location loc, 182 Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg, 183 llvm::ArrayRef<fir::ExtendedValue> args) { 184 185 assert(args.size() == 3); 186 187 // Handle required array argument 188 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 189 mlir::Value array = fir::getBase(arryTmp); 190 int rank = arryTmp.rank(); 191 assert(rank >= 1); 192 193 // Handle optional mask argument 194 auto mask = isStaticallyAbsent(args[2]) 195 ? builder.create<fir::AbsentOp>( 196 loc, fir::BoxType::get(builder.getI1Type())) 197 : builder.createBox(loc, args[2]); 198 199 bool absentDim = isStaticallyAbsent(args[1]); 200 201 // We call the type specific versions because the result is scalar 202 // in the case below. 203 if (absentDim || rank == 1) { 204 mlir::Type ty = array.getType(); 205 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty); 206 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 207 if (fir::isa_complex(eleTy)) { 208 mlir::Value result = builder.createTemporary(loc, eleTy); 209 func(builder, loc, array, mask, result); 210 return builder.create<fir::LoadOp>(loc, result); 211 } 212 auto resultBox = builder.create<fir::AbsentOp>( 213 loc, fir::BoxType::get(builder.getI1Type())); 214 return func(builder, loc, array, mask, resultBox); 215 } 216 // Handle Product/Sum cases that have an array result. 217 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 218 args[1], mask, rank); 219 } 220 221 /// Process calls to DotProduct 222 template <typename FN> 223 static fir::ExtendedValue 224 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder, 225 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 226 llvm::ArrayRef<fir::ExtendedValue> args) { 227 228 assert(args.size() == 2); 229 230 // Handle required vector arguments 231 mlir::Value vectorA = fir::getBase(args[0]); 232 mlir::Value vectorB = fir::getBase(args[1]); 233 234 mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType()) 235 .cast<fir::SequenceType>() 236 .getEleTy(); 237 if (fir::isa_complex(eleTy)) { 238 mlir::Value result = builder.createTemporary(loc, eleTy); 239 func(builder, loc, vectorA, vectorB, result); 240 return builder.create<fir::LoadOp>(loc, result); 241 } 242 243 auto resultBox = builder.create<fir::AbsentOp>( 244 loc, fir::BoxType::get(builder.getI1Type())); 245 return func(builder, loc, vectorA, vectorB, resultBox); 246 } 247 248 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions 249 template <typename FN, typename FD, typename FC> 250 static fir::ExtendedValue 251 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType, 252 fir::FirOpBuilder &builder, mlir::Location loc, 253 Fortran::lower::StatementContext *stmtCtx, 254 llvm::StringRef errMsg, 255 llvm::ArrayRef<fir::ExtendedValue> args) { 256 257 assert(args.size() == 3); 258 259 // Handle required array argument 260 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 261 mlir::Value array = fir::getBase(arryTmp); 262 int rank = arryTmp.rank(); 263 assert(rank >= 1); 264 bool hasCharacterResult = arryTmp.isCharacter(); 265 266 // Handle optional mask argument 267 auto mask = isStaticallyAbsent(args[2]) 268 ? builder.create<fir::AbsentOp>( 269 loc, fir::BoxType::get(builder.getI1Type())) 270 : builder.createBox(loc, args[2]); 271 272 bool absentDim = isStaticallyAbsent(args[1]); 273 274 // For Maxval/MinVal, we call the type specific versions of 275 // Maxval/Minval because the result is scalar in the case below. 276 if (!hasCharacterResult && (absentDim || rank == 1)) 277 return func(builder, loc, array, mask); 278 279 if (hasCharacterResult && (absentDim || rank == 1)) { 280 // Create mutable fir.box to be passed to the runtime for the result. 281 fir::MutableBoxValue resultMutableBox = 282 fir::factory::createTempMutableBox(builder, loc, resultType); 283 mlir::Value resultIrBox = 284 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 285 286 funcChar(builder, loc, resultIrBox, array, mask); 287 288 // Handle cleanup of allocatable result descriptor and return 289 fir::ExtendedValue res = 290 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 291 return res.match( 292 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 293 // Add cleanup code 294 assert(stmtCtx); 295 fir::FirOpBuilder *bldr = &builder; 296 mlir::Value temp = box.getAddr(); 297 stmtCtx->attachCleanup( 298 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 299 return box; 300 }, 301 [&](const auto &) -> fir::ExtendedValue { 302 fir::emitFatalError(loc, errMsg); 303 }); 304 } 305 306 // Handle Min/Maxval cases that have an array result. 307 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 308 args[1], mask, rank); 309 } 310 311 /// Process calls to Minloc, Maxloc intrinsic functions 312 template <typename FN, typename FD> 313 static fir::ExtendedValue genExtremumloc( 314 FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 315 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 316 llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) { 317 318 assert(args.size() == 5); 319 320 // Handle required array argument 321 mlir::Value array = builder.createBox(loc, args[0]); 322 unsigned rank = fir::BoxValue(array).rank(); 323 assert(rank >= 1); 324 325 // Handle optional mask argument 326 auto mask = isStaticallyAbsent(args[2]) 327 ? builder.create<fir::AbsentOp>( 328 loc, fir::BoxType::get(builder.getI1Type())) 329 : builder.createBox(loc, args[2]); 330 331 // Handle optional kind argument 332 auto kind = isStaticallyAbsent(args[3]) 333 ? builder.createIntegerConstant( 334 loc, builder.getIndexType(), 335 builder.getKindMap().defaultIntegerKind()) 336 : fir::getBase(args[3]); 337 338 // Handle optional back argument 339 auto back = isStaticallyAbsent(args[4]) ? builder.createBool(loc, false) 340 : fir::getBase(args[4]); 341 342 bool absentDim = isStaticallyAbsent(args[1]); 343 344 if (!absentDim && rank == 1) { 345 // If dim argument is present and the array is rank 1, then the result is 346 // a scalar (since the the result is rank-1 or 0). 347 // Therefore, we use a scalar result descriptor with Min/MaxlocDim(). 348 mlir::Value dim = fir::getBase(args[1]); 349 // Create mutable fir.box to be passed to the runtime for the result. 350 fir::MutableBoxValue resultMutableBox = 351 fir::factory::createTempMutableBox(builder, loc, resultType); 352 mlir::Value resultIrBox = 353 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 354 355 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 356 357 // Handle cleanup of allocatable result descriptor and return 358 fir::ExtendedValue res = 359 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 360 return res.match( 361 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 362 // Add cleanup code 363 assert(stmtCtx); 364 fir::FirOpBuilder *bldr = &builder; 365 stmtCtx->attachCleanup( 366 [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); }); 367 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 368 }, 369 [&](const auto &) -> fir::ExtendedValue { 370 fir::emitFatalError(loc, errMsg); 371 }); 372 } 373 374 // Note: The Min/Maxloc/val cases below have an array result. 375 376 // Create mutable fir.box to be passed to the runtime for the result. 377 mlir::Type resultArrayType = 378 builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1); 379 fir::MutableBoxValue resultMutableBox = 380 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 381 mlir::Value resultIrBox = 382 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 383 384 if (absentDim) { 385 // Handle min/maxloc/val case where there is no dim argument 386 // (calls Min/Maxloc()/MinMaxval() runtime routine) 387 func(builder, loc, resultIrBox, array, mask, kind, back); 388 } else { 389 // else handle min/maxloc case with dim argument (calls 390 // Min/Max/loc/val/Dim() runtime routine). 391 mlir::Value dim = fir::getBase(args[1]); 392 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 393 } 394 395 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 396 .match( 397 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 398 // Add cleanup code 399 assert(stmtCtx); 400 fir::FirOpBuilder *bldr = &builder; 401 mlir::Value temp = box.getAddr(); 402 stmtCtx->attachCleanup( 403 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 404 return box; 405 }, 406 [&](const auto &) -> fir::ExtendedValue { 407 fir::emitFatalError(loc, errMsg); 408 }); 409 } 410 411 // TODO error handling -> return a code or directly emit messages ? 412 struct IntrinsicLibrary { 413 414 // Constructors. 415 explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc, 416 Fortran::lower::StatementContext *stmtCtx = nullptr) 417 : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {} 418 IntrinsicLibrary() = delete; 419 IntrinsicLibrary(const IntrinsicLibrary &) = delete; 420 421 /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg 422 /// and expected result type \p resultType. 423 fir::ExtendedValue genIntrinsicCall(llvm::StringRef name, 424 llvm::Optional<mlir::Type> resultType, 425 llvm::ArrayRef<fir::ExtendedValue> arg); 426 427 /// Search a runtime function that is associated to the generic intrinsic name 428 /// and whose signature matches the intrinsic arguments and result types. 429 /// If no such runtime function is found but a runtime function associated 430 /// with the Fortran generic exists and has the same number of arguments, 431 /// conversions will be inserted before and/or after the call. This is to 432 /// mainly to allow 16 bits float support even-though little or no math 433 /// runtime is currently available for it. 434 mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, 435 llvm::ArrayRef<mlir::Value>); 436 437 using RuntimeCallGenerator = std::function<mlir::Value( 438 fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>; 439 RuntimeCallGenerator 440 getRuntimeCallGenerator(llvm::StringRef name, 441 mlir::FunctionType soughtFuncType); 442 443 /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in 444 /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation 445 /// if the argument is an integer, into llvm intrinsics if the argument is 446 /// real and to the `hypot` math routine if the argument is of complex type. 447 mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>); 448 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc, 449 mlir::Value, mlir::Value)> 450 fir::ExtendedValue genAdjustRtCall(mlir::Type, 451 llvm::ArrayRef<fir::ExtendedValue>); 452 mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>); 453 mlir::Value genAint(mlir::Type, llvm::ArrayRef<mlir::Value>); 454 fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 455 fir::ExtendedValue genAllocated(mlir::Type, 456 llvm::ArrayRef<fir::ExtendedValue>); 457 mlir::Value genAnint(mlir::Type, llvm::ArrayRef<mlir::Value>); 458 fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 459 fir::ExtendedValue 460 genCommandArgumentCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 461 fir::ExtendedValue genAssociated(mlir::Type, 462 llvm::ArrayRef<fir::ExtendedValue>); 463 mlir::Value genBtest(mlir::Type, llvm::ArrayRef<mlir::Value>); 464 mlir::Value genCeiling(mlir::Type, llvm::ArrayRef<mlir::Value>); 465 fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 466 template <mlir::arith::CmpIPredicate pred> 467 fir::ExtendedValue genCharacterCompare(mlir::Type, 468 llvm::ArrayRef<fir::ExtendedValue>); 469 mlir::Value genCmplx(mlir::Type, llvm::ArrayRef<mlir::Value>); 470 mlir::Value genConjg(mlir::Type, llvm::ArrayRef<mlir::Value>); 471 fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 472 void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>); 473 fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 474 void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>); 475 mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>); 476 fir::ExtendedValue genDotProduct(mlir::Type, 477 llvm::ArrayRef<fir::ExtendedValue>); 478 mlir::Value genDprod(mlir::Type, llvm::ArrayRef<mlir::Value>); 479 fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 480 void genExit(llvm::ArrayRef<fir::ExtendedValue>); 481 mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>); 482 template <Extremum, ExtremumBehavior> 483 mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>); 484 mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>); 485 mlir::Value genFraction(mlir::Type resultType, 486 mlir::ArrayRef<mlir::Value> args); 487 void genGetCommandArgument(mlir::ArrayRef<fir::ExtendedValue> args); 488 void genGetEnvironmentVariable(llvm::ArrayRef<fir::ExtendedValue>); 489 /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments 490 /// in the llvm::ArrayRef. 491 mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>); 492 mlir::Value genIbclr(mlir::Type, llvm::ArrayRef<mlir::Value>); 493 mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>); 494 mlir::Value genIbset(mlir::Type, llvm::ArrayRef<mlir::Value>); 495 fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 496 mlir::Value genIeeeIsFinite(mlir::Type, llvm::ArrayRef<mlir::Value>); 497 template <mlir::arith::CmpIPredicate pred> 498 fir::ExtendedValue genIeeeTypeCompare(mlir::Type, 499 llvm::ArrayRef<fir::ExtendedValue>); 500 mlir::Value genIeor(mlir::Type, llvm::ArrayRef<mlir::Value>); 501 fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 502 mlir::Value genIor(mlir::Type, llvm::ArrayRef<mlir::Value>); 503 mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>); 504 mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>); 505 fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 506 fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 507 fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 508 fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 509 fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 510 fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 511 fir::ExtendedValue genMerge(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 512 fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 513 fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 514 mlir::Value genMod(mlir::Type, llvm::ArrayRef<mlir::Value>); 515 mlir::Value genModulo(mlir::Type, llvm::ArrayRef<mlir::Value>); 516 void genMvbits(llvm::ArrayRef<fir::ExtendedValue>); 517 mlir::Value genNearest(mlir::Type, llvm::ArrayRef<mlir::Value>); 518 mlir::Value genNint(mlir::Type, llvm::ArrayRef<mlir::Value>); 519 mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>); 520 fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 521 fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 522 fir::ExtendedValue genPresent(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 523 fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 524 void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>); 525 void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>); 526 void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>); 527 fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 528 fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 529 mlir::Value genRRSpacing(mlir::Type resultType, 530 llvm::ArrayRef<mlir::Value> args); 531 mlir::Value genScale(mlir::Type, llvm::ArrayRef<mlir::Value>); 532 fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 533 mlir::Value genSetExponent(mlir::Type resultType, 534 llvm::ArrayRef<mlir::Value> args); 535 mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>); 536 fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 537 mlir::Value genSpacing(mlir::Type resultType, 538 llvm::ArrayRef<mlir::Value> args); 539 fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 540 fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 541 void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>); 542 fir::ExtendedValue genTransfer(mlir::Type, 543 llvm::ArrayRef<fir::ExtendedValue>); 544 fir::ExtendedValue genTranspose(mlir::Type, 545 llvm::ArrayRef<fir::ExtendedValue>); 546 fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 547 fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 548 fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 549 fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 550 /// Implement all conversion functions like DBLE, the first argument is 551 /// the value to convert. There may be an additional KIND arguments that 552 /// is ignored because this is already reflected in the result type. 553 mlir::Value genConversion(mlir::Type, llvm::ArrayRef<mlir::Value>); 554 555 /// Define the different FIR generators that can be mapped to intrinsic to 556 /// generate the related code. 557 using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); 558 using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim); 559 using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime); 560 using Generator = 561 std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>; 562 563 /// All generators can be outlined. This will build a function named 564 /// "fir."+ <generic name> + "." + <result type code> and generate the 565 /// intrinsic implementation inside instead of at the intrinsic call sites. 566 /// This can be used to keep the FIR more readable. Only one function will 567 /// be generated for all the similar calls in a program. 568 /// If the Generator is nullptr, the wrapper uses genRuntimeCall. 569 template <typename GeneratorType> 570 mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name, 571 mlir::Type resultType, 572 llvm::ArrayRef<mlir::Value> args); 573 template <typename GeneratorType> 574 fir::ExtendedValue 575 outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, 576 llvm::Optional<mlir::Type> resultType, 577 llvm::ArrayRef<fir::ExtendedValue> args); 578 579 template <typename GeneratorType> 580 mlir::func::FuncOp getWrapper(GeneratorType, llvm::StringRef name, 581 mlir::FunctionType, 582 bool loadRefArguments = false); 583 584 /// Generate calls to ElementalGenerator, handling the elemental aspects 585 template <typename GeneratorType> 586 fir::ExtendedValue 587 genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, 588 llvm::ArrayRef<fir::ExtendedValue> args, bool outline); 589 590 /// Helper to invoke code generator for the intrinsics given arguments. 591 mlir::Value invokeGenerator(ElementalGenerator generator, 592 mlir::Type resultType, 593 llvm::ArrayRef<mlir::Value> args); 594 mlir::Value invokeGenerator(RuntimeCallGenerator generator, 595 mlir::Type resultType, 596 llvm::ArrayRef<mlir::Value> args); 597 mlir::Value invokeGenerator(ExtendedGenerator generator, 598 mlir::Type resultType, 599 llvm::ArrayRef<mlir::Value> args); 600 mlir::Value invokeGenerator(SubroutineGenerator generator, 601 llvm::ArrayRef<mlir::Value> args); 602 603 /// Get pointer to unrestricted intrinsic. Generate the related unrestricted 604 /// intrinsic if it is not defined yet. 605 mlir::SymbolRefAttr 606 getUnrestrictedIntrinsicSymbolRefAttr(llvm::StringRef name, 607 mlir::FunctionType signature); 608 609 /// Add clean-up for \p temp to the current statement context; 610 void addCleanUpForTemp(mlir::Location loc, mlir::Value temp); 611 /// Helper function for generating code clean-up for result descriptors 612 fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 613 mlir::Type resultType, 614 llvm::StringRef errMsg); 615 616 fir::FirOpBuilder &builder; 617 mlir::Location loc; 618 Fortran::lower::StatementContext *stmtCtx; 619 }; 620 621 struct IntrinsicDummyArgument { 622 const char *name = nullptr; 623 Fortran::lower::LowerIntrinsicArgAs lowerAs = 624 Fortran::lower::LowerIntrinsicArgAs::Value; 625 bool handleDynamicOptional = false; 626 }; 627 628 struct Fortran::lower::IntrinsicArgumentLoweringRules { 629 /// There is no more than 7 non repeated arguments in Fortran intrinsics. 630 IntrinsicDummyArgument args[7]; 631 constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } 632 }; 633 634 /// Structure describing what needs to be done to lower intrinsic "name". 635 struct IntrinsicHandler { 636 const char *name; 637 IntrinsicLibrary::Generator generator; 638 // The following may be omitted in the table below. 639 Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {}; 640 bool isElemental = true; 641 /// Code heavy intrinsic can be outlined to make FIR 642 /// more readable. 643 bool outline = false; 644 }; 645 646 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value; 647 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr; 648 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box; 649 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired; 650 using I = IntrinsicLibrary; 651 652 /// Flag to indicate that an intrinsic argument has to be handled as 653 /// being dynamically optional (e.g. special handling when actual 654 /// argument is an optional variable in the current scope). 655 static constexpr bool handleDynamicOptional = true; 656 657 /// Table that drives the fir generation depending on the intrinsic. 658 /// one to one mapping with Fortran arguments. If no mapping is 659 /// defined here for a generic intrinsic, genRuntimeCall will be called 660 /// to look for a match in the runtime a emit a call. Note that the argument 661 /// lowering rules for an intrinsic need to be provided only if at least one 662 /// argument must not be lowered by value. In which case, the lowering rules 663 /// should be provided for all the intrinsic arguments for completeness. 664 static constexpr IntrinsicHandler handlers[]{ 665 {"abs", &I::genAbs}, 666 {"achar", &I::genChar}, 667 {"adjustl", 668 &I::genAdjustRtCall<fir::runtime::genAdjustL>, 669 {{{"string", asAddr}}}, 670 /*isElemental=*/true}, 671 {"adjustr", 672 &I::genAdjustRtCall<fir::runtime::genAdjustR>, 673 {{{"string", asAddr}}}, 674 /*isElemental=*/true}, 675 {"aimag", &I::genAimag}, 676 {"aint", &I::genAint}, 677 {"all", 678 &I::genAll, 679 {{{"mask", asAddr}, {"dim", asValue}}}, 680 /*isElemental=*/false}, 681 {"allocated", 682 &I::genAllocated, 683 {{{"array", asInquired}, {"scalar", asInquired}}}, 684 /*isElemental=*/false}, 685 {"anint", &I::genAnint}, 686 {"any", 687 &I::genAny, 688 {{{"mask", asAddr}, {"dim", asValue}}}, 689 /*isElemental=*/false}, 690 {"associated", 691 &I::genAssociated, 692 {{{"pointer", asInquired}, {"target", asInquired}}}, 693 /*isElemental=*/false}, 694 {"btest", &I::genBtest}, 695 {"ceiling", &I::genCeiling}, 696 {"char", &I::genChar}, 697 {"cmplx", 698 &I::genCmplx, 699 {{{"x", asValue}, {"y", asValue, handleDynamicOptional}}}}, 700 {"command_argument_count", &I::genCommandArgumentCount}, 701 {"conjg", &I::genConjg}, 702 {"count", 703 &I::genCount, 704 {{{"mask", asAddr}, {"dim", asValue}, {"kind", asValue}}}, 705 /*isElemental=*/false}, 706 {"cpu_time", 707 &I::genCpuTime, 708 {{{"time", asAddr}}}, 709 /*isElemental=*/false}, 710 {"cshift", 711 &I::genCshift, 712 {{{"array", asAddr}, {"shift", asAddr}, {"dim", asValue}}}, 713 /*isElemental=*/false}, 714 {"date_and_time", 715 &I::genDateAndTime, 716 {{{"date", asAddr, handleDynamicOptional}, 717 {"time", asAddr, handleDynamicOptional}, 718 {"zone", asAddr, handleDynamicOptional}, 719 {"values", asBox, handleDynamicOptional}}}, 720 /*isElemental=*/false}, 721 {"dble", &I::genConversion}, 722 {"dim", &I::genDim}, 723 {"dot_product", 724 &I::genDotProduct, 725 {{{"vector_a", asBox}, {"vector_b", asBox}}}, 726 /*isElemental=*/false}, 727 {"dprod", &I::genDprod}, 728 {"eoshift", 729 &I::genEoshift, 730 {{{"array", asBox}, 731 {"shift", asAddr}, 732 {"boundary", asBox, handleDynamicOptional}, 733 {"dim", asValue}}}, 734 /*isElemental=*/false}, 735 {"exit", 736 &I::genExit, 737 {{{"status", asValue, handleDynamicOptional}}}, 738 /*isElemental=*/false}, 739 {"exponent", &I::genExponent}, 740 {"floor", &I::genFloor}, 741 {"fraction", &I::genFraction}, 742 {"get_command_argument", 743 &I::genGetCommandArgument, 744 {{{"number", asValue}, 745 {"value", asBox, handleDynamicOptional}, 746 {"length", asAddr}, 747 {"status", asAddr}, 748 {"errmsg", asBox, handleDynamicOptional}}}, 749 /*isElemental=*/false}, 750 {"get_environment_variable", 751 &I::genGetEnvironmentVariable, 752 {{{"name", asBox}, 753 {"value", asBox, handleDynamicOptional}, 754 {"length", asAddr}, 755 {"status", asAddr}, 756 {"trim_name", asAddr}, 757 {"errmsg", asBox, handleDynamicOptional}}}, 758 /*isElemental=*/false}, 759 {"iachar", &I::genIchar}, 760 {"iand", &I::genIand}, 761 {"ibclr", &I::genIbclr}, 762 {"ibits", &I::genIbits}, 763 {"ibset", &I::genIbset}, 764 {"ichar", &I::genIchar}, 765 {"ieee_class_eq", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::eq>}, 766 {"ieee_class_ne", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::ne>}, 767 {"ieee_is_finite", &I::genIeeeIsFinite}, 768 {"ieee_round_eq", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::eq>}, 769 {"ieee_round_ne", &I::genIeeeTypeCompare<mlir::arith::CmpIPredicate::ne>}, 770 {"ieor", &I::genIeor}, 771 {"index", 772 &I::genIndex, 773 {{{"string", asAddr}, 774 {"substring", asAddr}, 775 {"back", asValue, handleDynamicOptional}, 776 {"kind", asValue}}}}, 777 {"ior", &I::genIor}, 778 {"ishft", &I::genIshft}, 779 {"ishftc", &I::genIshftc}, 780 {"lbound", 781 &I::genLbound, 782 {{{"array", asInquired}, {"dim", asValue}, {"kind", asValue}}}, 783 /*isElemental=*/false}, 784 {"len", 785 &I::genLen, 786 {{{"string", asInquired}, {"kind", asValue}}}, 787 /*isElemental=*/false}, 788 {"len_trim", &I::genLenTrim}, 789 {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>}, 790 {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>}, 791 {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>}, 792 {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>}, 793 {"matmul", 794 &I::genMatmul, 795 {{{"matrix_a", asAddr}, {"matrix_b", asAddr}}}, 796 /*isElemental=*/false}, 797 {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>}, 798 {"maxloc", 799 &I::genMaxloc, 800 {{{"array", asBox}, 801 {"dim", asValue}, 802 {"mask", asBox, handleDynamicOptional}, 803 {"kind", asValue}, 804 {"back", asValue, handleDynamicOptional}}}, 805 /*isElemental=*/false}, 806 {"maxval", 807 &I::genMaxval, 808 {{{"array", asBox}, 809 {"dim", asValue}, 810 {"mask", asBox, handleDynamicOptional}}}, 811 /*isElemental=*/false}, 812 {"merge", &I::genMerge}, 813 {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>}, 814 {"minloc", 815 &I::genMinloc, 816 {{{"array", asBox}, 817 {"dim", asValue}, 818 {"mask", asBox, handleDynamicOptional}, 819 {"kind", asValue}, 820 {"back", asValue, handleDynamicOptional}}}, 821 /*isElemental=*/false}, 822 {"minval", 823 &I::genMinval, 824 {{{"array", asBox}, 825 {"dim", asValue}, 826 {"mask", asBox, handleDynamicOptional}}}, 827 /*isElemental=*/false}, 828 {"mod", &I::genMod}, 829 {"modulo", &I::genModulo}, 830 {"mvbits", 831 &I::genMvbits, 832 {{{"from", asValue}, 833 {"frompos", asValue}, 834 {"len", asValue}, 835 {"to", asAddr}, 836 {"topos", asValue}}}}, 837 {"nearest", &I::genNearest}, 838 {"nint", &I::genNint}, 839 {"not", &I::genNot}, 840 {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false}, 841 {"pack", 842 &I::genPack, 843 {{{"array", asBox}, 844 {"mask", asBox}, 845 {"vector", asBox, handleDynamicOptional}}}, 846 /*isElemental=*/false}, 847 {"present", 848 &I::genPresent, 849 {{{"a", asInquired}}}, 850 /*isElemental=*/false}, 851 {"product", 852 &I::genProduct, 853 {{{"array", asBox}, 854 {"dim", asValue}, 855 {"mask", asBox, handleDynamicOptional}}}, 856 /*isElemental=*/false}, 857 {"random_init", 858 &I::genRandomInit, 859 {{{"repeatable", asValue}, {"image_distinct", asValue}}}, 860 /*isElemental=*/false}, 861 {"random_number", 862 &I::genRandomNumber, 863 {{{"harvest", asBox}}}, 864 /*isElemental=*/false}, 865 {"random_seed", 866 &I::genRandomSeed, 867 {{{"size", asBox}, {"put", asBox}, {"get", asBox}}}, 868 /*isElemental=*/false}, 869 {"repeat", 870 &I::genRepeat, 871 {{{"string", asAddr}, {"ncopies", asValue}}}, 872 /*isElemental=*/false}, 873 {"reshape", 874 &I::genReshape, 875 {{{"source", asBox}, 876 {"shape", asBox}, 877 {"pad", asBox, handleDynamicOptional}, 878 {"order", asBox, handleDynamicOptional}}}, 879 /*isElemental=*/false}, 880 {"rrspacing", &I::genRRSpacing}, 881 {"scale", 882 &I::genScale, 883 {{{"x", asValue}, {"i", asValue}}}, 884 /*isElemental=*/true}, 885 {"scan", 886 &I::genScan, 887 {{{"string", asAddr}, 888 {"set", asAddr}, 889 {"back", asValue, handleDynamicOptional}, 890 {"kind", asValue}}}, 891 /*isElemental=*/true}, 892 {"set_exponent", &I::genSetExponent}, 893 {"sign", &I::genSign}, 894 {"size", 895 &I::genSize, 896 {{{"array", asBox}, 897 {"dim", asAddr, handleDynamicOptional}, 898 {"kind", asValue}}}, 899 /*isElemental=*/false}, 900 {"spacing", &I::genSpacing}, 901 {"spread", 902 &I::genSpread, 903 {{{"source", asAddr}, {"dim", asValue}, {"ncopies", asValue}}}, 904 /*isElemental=*/false}, 905 {"sum", 906 &I::genSum, 907 {{{"array", asBox}, 908 {"dim", asValue}, 909 {"mask", asBox, handleDynamicOptional}}}, 910 /*isElemental=*/false}, 911 {"system_clock", 912 &I::genSystemClock, 913 {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}}, 914 /*isElemental=*/false}, 915 {"transfer", 916 &I::genTransfer, 917 {{{"source", asAddr}, {"mold", asAddr}, {"size", asValue}}}, 918 /*isElemental=*/false}, 919 {"transpose", 920 &I::genTranspose, 921 {{{"matrix", asAddr}}}, 922 /*isElemental=*/false}, 923 {"trim", &I::genTrim, {{{"string", asAddr}}}, /*isElemental=*/false}, 924 {"ubound", 925 &I::genUbound, 926 {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}}, 927 /*isElemental=*/false}, 928 {"unpack", 929 &I::genUnpack, 930 {{{"vector", asBox}, {"mask", asBox}, {"field", asBox}}}, 931 /*isElemental=*/false}, 932 {"verify", 933 &I::genVerify, 934 {{{"string", asAddr}, 935 {"set", asAddr}, 936 {"back", asValue, handleDynamicOptional}, 937 {"kind", asValue}}}, 938 /*isElemental=*/true}, 939 }; 940 941 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { 942 auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { 943 return name.compare(handler.name) > 0; 944 }; 945 auto result = 946 std::lower_bound(std::begin(handlers), std::end(handlers), name, compare); 947 return result != std::end(handlers) && result->name == name ? result 948 : nullptr; 949 } 950 951 /// To make fir output more readable for debug, one can outline all intrinsic 952 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag). 953 static llvm::cl::opt<bool> outlineAllIntrinsics( 954 "outline-intrinsics", 955 llvm::cl::desc( 956 "Lower all intrinsic procedure implementation in their own functions"), 957 llvm::cl::init(false)); 958 959 //===----------------------------------------------------------------------===// 960 // Math runtime description and matching utility 961 //===----------------------------------------------------------------------===// 962 963 /// Command line option to modify math runtime version used to implement 964 /// intrinsics. 965 enum MathRuntimeVersion { 966 fastVersion, 967 relaxedVersion, 968 preciseVersion, 969 llvmOnly 970 }; 971 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion( 972 "math-runtime", llvm::cl::desc("Select math runtime version:"), 973 llvm::cl::values( 974 clEnumValN(fastVersion, "fast", "use pgmath fast runtime"), 975 clEnumValN(relaxedVersion, "relaxed", "use pgmath relaxed runtime"), 976 clEnumValN(preciseVersion, "precise", "use pgmath precise runtime"), 977 clEnumValN(llvmOnly, "llvm", 978 "only use LLVM intrinsics (may be incomplete)")), 979 llvm::cl::init(fastVersion)); 980 981 struct RuntimeFunction { 982 // llvm::StringRef comparison operator are not constexpr, so use string_view. 983 using Key = std::string_view; 984 // Needed for implicit compare with keys. 985 constexpr operator Key() const { return key; } 986 Key key; // intrinsic name 987 llvm::StringRef symbol; 988 fir::runtime::FuncTypeBuilderFunc typeGenerator; 989 }; 990 991 #define RUNTIME_STATIC_DESCRIPTION(name, func) \ 992 {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()}, 993 static constexpr RuntimeFunction pgmathFast[] = { 994 #define PGMATH_FAST 995 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 996 #include "flang/Evaluate/pgmath.h.inc" 997 }; 998 static constexpr RuntimeFunction pgmathRelaxed[] = { 999 #define PGMATH_RELAXED 1000 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 1001 #include "flang/Evaluate/pgmath.h.inc" 1002 }; 1003 static constexpr RuntimeFunction pgmathPrecise[] = { 1004 #define PGMATH_PRECISE 1005 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 1006 #include "flang/Evaluate/pgmath.h.inc" 1007 }; 1008 1009 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) { 1010 mlir::Type t = mlir::FloatType::getF32(context); 1011 return mlir::FunctionType::get(context, {t}, {t}); 1012 } 1013 1014 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) { 1015 mlir::Type t = mlir::FloatType::getF64(context); 1016 return mlir::FunctionType::get(context, {t}, {t}); 1017 } 1018 1019 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) { 1020 auto t = mlir::FloatType::getF32(context); 1021 return mlir::FunctionType::get(context, {t, t}, {t}); 1022 } 1023 1024 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) { 1025 auto t = mlir::FloatType::getF64(context); 1026 return mlir::FunctionType::get(context, {t, t}, {t}); 1027 } 1028 1029 static mlir::FunctionType genF80F80F80FuncType(mlir::MLIRContext *context) { 1030 auto t = mlir::FloatType::getF80(context); 1031 return mlir::FunctionType::get(context, {t, t}, {t}); 1032 } 1033 1034 static mlir::FunctionType genF128F128F128FuncType(mlir::MLIRContext *context) { 1035 auto t = mlir::FloatType::getF128(context); 1036 return mlir::FunctionType::get(context, {t, t}, {t}); 1037 } 1038 1039 template <int Bits> 1040 static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) { 1041 auto t = mlir::FloatType::getF64(context); 1042 auto r = mlir::IntegerType::get(context, Bits); 1043 return mlir::FunctionType::get(context, {t}, {r}); 1044 } 1045 1046 template <int Bits> 1047 static mlir::FunctionType genIntF32FuncType(mlir::MLIRContext *context) { 1048 auto t = mlir::FloatType::getF32(context); 1049 auto r = mlir::IntegerType::get(context, Bits); 1050 return mlir::FunctionType::get(context, {t}, {r}); 1051 } 1052 1053 // TODO : Fill-up this table with more intrinsic. 1054 // Note: These are also defined as operations in LLVM dialect. See if this 1055 // can be use and has advantages. 1056 static constexpr RuntimeFunction llvmIntrinsics[] = { 1057 {"abs", "llvm.fabs.f32", genF32F32FuncType}, 1058 {"abs", "llvm.fabs.f64", genF64F64FuncType}, 1059 {"aint", "llvm.trunc.f32", genF32F32FuncType}, 1060 {"aint", "llvm.trunc.f64", genF64F64FuncType}, 1061 {"anint", "llvm.round.f32", genF32F32FuncType}, 1062 {"anint", "llvm.round.f64", genF64F64FuncType}, 1063 {"atan", "atanf", genF32F32FuncType}, 1064 {"atan", "atan", genF64F64FuncType}, 1065 // ceil is used for CEILING but is different, it returns a real. 1066 {"ceil", "llvm.ceil.f32", genF32F32FuncType}, 1067 {"ceil", "llvm.ceil.f64", genF64F64FuncType}, 1068 {"cos", "llvm.cos.f32", genF32F32FuncType}, 1069 {"cos", "llvm.cos.f64", genF64F64FuncType}, 1070 {"cosh", "coshf", genF32F32FuncType}, 1071 {"cosh", "cosh", genF64F64FuncType}, 1072 {"exp", "llvm.exp.f32", genF32F32FuncType}, 1073 {"exp", "llvm.exp.f64", genF64F64FuncType}, 1074 // llvm.floor is used for FLOOR, but returns real. 1075 {"floor", "llvm.floor.f32", genF32F32FuncType}, 1076 {"floor", "llvm.floor.f64", genF64F64FuncType}, 1077 {"log", "llvm.log.f32", genF32F32FuncType}, 1078 {"log", "llvm.log.f64", genF64F64FuncType}, 1079 {"log10", "llvm.log10.f32", genF32F32FuncType}, 1080 {"log10", "llvm.log10.f64", genF64F64FuncType}, 1081 {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>}, 1082 {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>}, 1083 {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>}, 1084 {"nint", "llvm.lround.i32.f32", genIntF32FuncType<32>}, 1085 {"pow", "llvm.pow.f32", genF32F32F32FuncType}, 1086 {"pow", "llvm.pow.f64", genF64F64F64FuncType}, 1087 {"sign", "llvm.copysign.f32", genF32F32F32FuncType}, 1088 {"sign", "llvm.copysign.f64", genF64F64F64FuncType}, 1089 {"sign", "llvm.copysign.f80", genF80F80F80FuncType}, 1090 {"sign", "llvm.copysign.f128", genF128F128F128FuncType}, 1091 {"sin", "llvm.sin.f32", genF32F32FuncType}, 1092 {"sin", "llvm.sin.f64", genF64F64FuncType}, 1093 {"sinh", "sinhf", genF32F32FuncType}, 1094 {"sinh", "sinh", genF64F64FuncType}, 1095 {"sqrt", "llvm.sqrt.f32", genF32F32FuncType}, 1096 {"sqrt", "llvm.sqrt.f64", genF64F64FuncType}, 1097 }; 1098 1099 // This helper class computes a "distance" between two function types. 1100 // The distance measures how many narrowing conversions of actual arguments 1101 // and result of "from" must be made in order to use "to" instead of "from". 1102 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is 1103 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means 1104 // if no implementation of ACOS(REAL(10)) is available, it is better to use 1105 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)). 1106 // Note that this is not a symmetric distance and the order of "from" and "to" 1107 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it 1108 // may be safe to replace foo by bar, but not the opposite. 1109 class FunctionDistance { 1110 public: 1111 FunctionDistance() : infinite{true} {} 1112 1113 FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { 1114 unsigned nInputs = from.getNumInputs(); 1115 unsigned nResults = from.getNumResults(); 1116 if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { 1117 infinite = true; 1118 } else { 1119 for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i) 1120 addArgumentDistance(from.getInput(i), to.getInput(i)); 1121 for (decltype(nResults) i = 0; i < nResults && !infinite; ++i) 1122 addResultDistance(to.getResult(i), from.getResult(i)); 1123 } 1124 } 1125 1126 /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be 1127 /// false if both d1 and d2 are infinite. This implies that 1128 /// d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1) 1129 bool isSmallerThan(const FunctionDistance &d) const { 1130 return !infinite && 1131 (d.infinite || std::lexicographical_compare( 1132 conversions.begin(), conversions.end(), 1133 d.conversions.begin(), d.conversions.end())); 1134 } 1135 1136 bool isLosingPrecision() const { 1137 return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0; 1138 } 1139 1140 bool isInfinite() const { return infinite; } 1141 1142 private: 1143 enum class Conversion { Forbidden, None, Narrow, Extend }; 1144 1145 void addArgumentDistance(mlir::Type from, mlir::Type to) { 1146 switch (conversionBetweenTypes(from, to)) { 1147 case Conversion::Forbidden: 1148 infinite = true; 1149 break; 1150 case Conversion::None: 1151 break; 1152 case Conversion::Narrow: 1153 conversions[narrowingArg]++; 1154 break; 1155 case Conversion::Extend: 1156 conversions[nonNarrowingArg]++; 1157 break; 1158 } 1159 } 1160 1161 void addResultDistance(mlir::Type from, mlir::Type to) { 1162 switch (conversionBetweenTypes(from, to)) { 1163 case Conversion::Forbidden: 1164 infinite = true; 1165 break; 1166 case Conversion::None: 1167 break; 1168 case Conversion::Narrow: 1169 conversions[nonExtendingResult]++; 1170 break; 1171 case Conversion::Extend: 1172 conversions[extendingResult]++; 1173 break; 1174 } 1175 } 1176 1177 // Floating point can be mlir::FloatType or fir::real 1178 static unsigned getFloatingPointWidth(mlir::Type t) { 1179 if (auto f{t.dyn_cast<mlir::FloatType>()}) 1180 return f.getWidth(); 1181 // FIXME: Get width another way for fir.real/complex 1182 // - use fir/KindMapping.h and llvm::Type 1183 // - or use evaluate/type.h 1184 if (auto r{t.dyn_cast<fir::RealType>()}) 1185 return r.getFKind() * 4; 1186 if (auto cplx{t.dyn_cast<fir::ComplexType>()}) 1187 return cplx.getFKind() * 4; 1188 llvm_unreachable("not a floating-point type"); 1189 } 1190 1191 static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { 1192 if (from == to) 1193 return Conversion::None; 1194 1195 if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) { 1196 if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) { 1197 return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow 1198 : Conversion::Extend; 1199 } 1200 } 1201 1202 if (fir::isa_real(from) && fir::isa_real(to)) { 1203 return getFloatingPointWidth(from) > getFloatingPointWidth(to) 1204 ? Conversion::Narrow 1205 : Conversion::Extend; 1206 } 1207 1208 if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) { 1209 if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) { 1210 return getFloatingPointWidth(fromCplxTy) > 1211 getFloatingPointWidth(toCplxTy) 1212 ? Conversion::Narrow 1213 : Conversion::Extend; 1214 } 1215 } 1216 // Notes: 1217 // - No conversion between character types, specialization of runtime 1218 // functions should be made instead. 1219 // - It is not clear there is a use case for automatic conversions 1220 // around Logical and it may damage hidden information in the physical 1221 // storage so do not do it. 1222 return Conversion::Forbidden; 1223 } 1224 1225 // Below are indexes to access data in conversions. 1226 // The order in data does matter for lexicographical_compare 1227 enum { 1228 narrowingArg = 0, // usually bad 1229 extendingResult, // usually bad 1230 nonExtendingResult, // usually ok 1231 nonNarrowingArg, // usually ok 1232 dataSize 1233 }; 1234 1235 std::array<int, dataSize> conversions = {}; 1236 bool infinite = false; // When forbidden conversion or wrong argument number 1237 }; 1238 1239 /// Build mlir::func::FuncOp from runtime symbol description and add 1240 /// fir.runtime attribute. 1241 static mlir::func::FuncOp getFuncOp(mlir::Location loc, 1242 fir::FirOpBuilder &builder, 1243 const RuntimeFunction &runtime) { 1244 mlir::func::FuncOp function = builder.addNamedFunction( 1245 loc, runtime.symbol, runtime.typeGenerator(builder.getContext())); 1246 function->setAttr("fir.runtime", builder.getUnitAttr()); 1247 return function; 1248 } 1249 1250 /// Select runtime function that has the smallest distance to the intrinsic 1251 /// function type and that will not imply narrowing arguments or extending the 1252 /// result. 1253 /// If nothing is found, the mlir::func::FuncOp will contain a nullptr. 1254 mlir::func::FuncOp searchFunctionInLibrary( 1255 mlir::Location loc, fir::FirOpBuilder &builder, 1256 const Fortran::common::StaticMultimapView<RuntimeFunction> &lib, 1257 llvm::StringRef name, mlir::FunctionType funcType, 1258 const RuntimeFunction **bestNearMatch, 1259 FunctionDistance &bestMatchDistance) { 1260 std::pair<const RuntimeFunction *, const RuntimeFunction *> range = 1261 lib.equal_range(name); 1262 for (auto iter = range.first; iter != range.second && iter; ++iter) { 1263 const RuntimeFunction &impl = *iter; 1264 mlir::FunctionType implType = impl.typeGenerator(builder.getContext()); 1265 if (funcType == implType) 1266 return getFuncOp(loc, builder, impl); // exact match 1267 1268 FunctionDistance distance(funcType, implType); 1269 if (distance.isSmallerThan(bestMatchDistance)) { 1270 *bestNearMatch = &impl; 1271 bestMatchDistance = std::move(distance); 1272 } 1273 } 1274 return {}; 1275 } 1276 1277 /// Search runtime for the best runtime function given an intrinsic name 1278 /// and interface. The interface may not be a perfect match in which case 1279 /// the caller is responsible to insert argument and return value conversions. 1280 /// If nothing is found, the mlir::func::FuncOp will contain a nullptr. 1281 static mlir::func::FuncOp getRuntimeFunction(mlir::Location loc, 1282 fir::FirOpBuilder &builder, 1283 llvm::StringRef name, 1284 mlir::FunctionType funcType) { 1285 const RuntimeFunction *bestNearMatch = nullptr; 1286 FunctionDistance bestMatchDistance; 1287 mlir::func::FuncOp match; 1288 using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>; 1289 static constexpr RtMap pgmathF(pgmathFast); 1290 static_assert(pgmathF.Verify() && "map must be sorted"); 1291 static constexpr RtMap pgmathR(pgmathRelaxed); 1292 static_assert(pgmathR.Verify() && "map must be sorted"); 1293 static constexpr RtMap pgmathP(pgmathPrecise); 1294 static_assert(pgmathP.Verify() && "map must be sorted"); 1295 if (mathRuntimeVersion == fastVersion) { 1296 match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, 1297 &bestNearMatch, bestMatchDistance); 1298 } else if (mathRuntimeVersion == relaxedVersion) { 1299 match = searchFunctionInLibrary(loc, builder, pgmathR, name, funcType, 1300 &bestNearMatch, bestMatchDistance); 1301 } else if (mathRuntimeVersion == preciseVersion) { 1302 match = searchFunctionInLibrary(loc, builder, pgmathP, name, funcType, 1303 &bestNearMatch, bestMatchDistance); 1304 } else { 1305 assert(mathRuntimeVersion == llvmOnly && "unknown math runtime"); 1306 } 1307 if (match) 1308 return match; 1309 1310 // Go through llvm intrinsics if not exact match in libpgmath or if 1311 // mathRuntimeVersion == llvmOnly 1312 static constexpr RtMap llvmIntr(llvmIntrinsics); 1313 static_assert(llvmIntr.Verify() && "map must be sorted"); 1314 if (mlir::func::FuncOp exactMatch = 1315 searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType, 1316 &bestNearMatch, bestMatchDistance)) 1317 return exactMatch; 1318 1319 if (bestNearMatch != nullptr) { 1320 if (bestMatchDistance.isLosingPrecision()) { 1321 // Using this runtime version requires narrowing the arguments 1322 // or extending the result. It is not numerically safe. There 1323 // is currently no quad math library that was described in 1324 // lowering and could be used here. Emit an error and continue 1325 // generating the code with the narrowing cast so that the user 1326 // can get a complete list of the problematic intrinsic calls. 1327 std::string message("TODO: no math runtime available for '"); 1328 llvm::raw_string_ostream sstream(message); 1329 if (name == "pow") { 1330 assert(funcType.getNumInputs() == 2 && 1331 "power operator has two arguments"); 1332 sstream << funcType.getInput(0) << " ** " << funcType.getInput(1); 1333 } else { 1334 sstream << name << "("; 1335 if (funcType.getNumInputs() > 0) 1336 sstream << funcType.getInput(0); 1337 for (mlir::Type argType : funcType.getInputs().drop_front()) 1338 sstream << ", " << argType; 1339 sstream << ")"; 1340 } 1341 sstream << "'"; 1342 mlir::emitError(loc, message); 1343 } 1344 return getFuncOp(loc, builder, *bestNearMatch); 1345 } 1346 return {}; 1347 } 1348 1349 /// Helpers to get function type from arguments and result type. 1350 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType, 1351 llvm::ArrayRef<mlir::Value> arguments, 1352 fir::FirOpBuilder &builder) { 1353 llvm::SmallVector<mlir::Type> argTypes; 1354 for (mlir::Value arg : arguments) 1355 argTypes.push_back(arg.getType()); 1356 llvm::SmallVector<mlir::Type> resTypes; 1357 if (resultType) 1358 resTypes.push_back(*resultType); 1359 return mlir::FunctionType::get(builder.getModule().getContext(), argTypes, 1360 resTypes); 1361 } 1362 1363 /// fir::ExtendedValue to mlir::Value translation layer 1364 1365 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder, 1366 mlir::Location loc) { 1367 assert(val && "optional unhandled here"); 1368 mlir::Type type = val.getType(); 1369 mlir::Value base = val; 1370 mlir::IndexType indexType = builder.getIndexType(); 1371 llvm::SmallVector<mlir::Value> extents; 1372 1373 fir::factory::CharacterExprHelper charHelper{builder, loc}; 1374 // FIXME: we may want to allow non character scalar here. 1375 if (charHelper.isCharacterScalar(type)) 1376 return charHelper.toExtendedValue(val); 1377 1378 if (auto refType = type.dyn_cast<fir::ReferenceType>()) 1379 type = refType.getEleTy(); 1380 1381 if (auto arrayType = type.dyn_cast<fir::SequenceType>()) { 1382 type = arrayType.getEleTy(); 1383 for (fir::SequenceType::Extent extent : arrayType.getShape()) { 1384 if (extent == fir::SequenceType::getUnknownExtent()) 1385 break; 1386 extents.emplace_back( 1387 builder.createIntegerConstant(loc, indexType, extent)); 1388 } 1389 // Last extent might be missing in case of assumed-size. If more extents 1390 // could not be deduced from type, that's an error (a fir.box should 1391 // have been used in the interface). 1392 if (extents.size() + 1 < arrayType.getShape().size()) 1393 mlir::emitError(loc, "cannot retrieve array extents from type"); 1394 } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) { 1395 fir::emitFatalError(loc, "not yet implemented: descriptor or derived type"); 1396 } 1397 1398 if (!extents.empty()) 1399 return fir::ArrayBoxValue{base, extents}; 1400 return base; 1401 } 1402 1403 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder, 1404 mlir::Location loc) { 1405 if (const fir::CharBoxValue *charBox = val.getCharBox()) { 1406 mlir::Value buffer = charBox->getBuffer(); 1407 auto buffTy = buffer.getType(); 1408 if (buffTy.isa<mlir::FunctionType>()) 1409 fir::emitFatalError( 1410 loc, "A character's buffer type cannot be a function type."); 1411 if (buffTy.isa<fir::BoxCharType>()) 1412 return buffer; 1413 return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar( 1414 buffer, charBox->getLen()); 1415 } 1416 1417 // FIXME: need to access other ExtendedValue variants and handle them 1418 // properly. 1419 return fir::getBase(val); 1420 } 1421 1422 //===----------------------------------------------------------------------===// 1423 // IntrinsicLibrary 1424 //===----------------------------------------------------------------------===// 1425 1426 static bool isIntrinsicModuleProcedure(llvm::StringRef name) { 1427 return name.startswith("c_") || name.startswith("compiler_") || 1428 name.startswith("ieee_"); 1429 } 1430 1431 /// Return the generic name of an intrinsic module procedure specific name. 1432 /// Remove any "__builtin_" prefix, and any specific suffix of the form 1433 /// {_[ail]?[0-9]+}*, such as _1 or _a4. 1434 llvm::StringRef genericName(llvm::StringRef specificName) { 1435 const std::string builtin = "__builtin_"; 1436 llvm::StringRef name = specificName.startswith(builtin) 1437 ? specificName.drop_front(builtin.size()) 1438 : specificName; 1439 size_t size = name.size(); 1440 if (isIntrinsicModuleProcedure(name)) 1441 while (isdigit(name[size - 1])) 1442 while (name[--size] != '_') 1443 ; 1444 return name.drop_back(name.size() - size); 1445 } 1446 1447 /// Generate a TODO error message for an as yet unimplemented intrinsic. 1448 void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) { 1449 if (isIntrinsicModuleProcedure(name)) 1450 TODO(loc, "intrinsic module procedure: " + llvm::Twine(name)); 1451 else 1452 TODO(loc, "intrinsic: " + llvm::Twine(name)); 1453 } 1454 1455 template <typename GeneratorType> 1456 fir::ExtendedValue IntrinsicLibrary::genElementalCall( 1457 GeneratorType generator, llvm::StringRef name, mlir::Type resultType, 1458 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1459 llvm::SmallVector<mlir::Value> scalarArgs; 1460 for (const fir::ExtendedValue &arg : args) 1461 if (arg.getUnboxed() || arg.getCharBox()) 1462 scalarArgs.emplace_back(fir::getBase(arg)); 1463 else 1464 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1465 if (outline) 1466 return outlineInWrapper(generator, name, resultType, scalarArgs); 1467 return invokeGenerator(generator, resultType, scalarArgs); 1468 } 1469 1470 template <> 1471 fir::ExtendedValue 1472 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>( 1473 ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType, 1474 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1475 for (const fir::ExtendedValue &arg : args) 1476 if (!arg.getUnboxed() && !arg.getCharBox()) 1477 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1478 if (outline) 1479 return outlineInExtendedWrapper(generator, name, resultType, args); 1480 return std::invoke(generator, *this, resultType, args); 1481 } 1482 1483 template <> 1484 fir::ExtendedValue 1485 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>( 1486 SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType, 1487 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1488 for (const fir::ExtendedValue &arg : args) 1489 if (!arg.getUnboxed() && !arg.getCharBox()) 1490 // fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1491 crashOnMissingIntrinsic(loc, name); 1492 if (outline) 1493 return outlineInExtendedWrapper(generator, name, resultType, args); 1494 std::invoke(generator, *this, args); 1495 return mlir::Value(); 1496 } 1497 1498 static fir::ExtendedValue 1499 invokeHandler(IntrinsicLibrary::ElementalGenerator generator, 1500 const IntrinsicHandler &handler, 1501 llvm::Optional<mlir::Type> resultType, 1502 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1503 IntrinsicLibrary &lib) { 1504 assert(resultType && "expect elemental intrinsic to be functions"); 1505 return lib.genElementalCall(generator, handler.name, *resultType, args, 1506 outline); 1507 } 1508 1509 static fir::ExtendedValue 1510 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator, 1511 const IntrinsicHandler &handler, 1512 llvm::Optional<mlir::Type> resultType, 1513 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1514 IntrinsicLibrary &lib) { 1515 assert(resultType && "expect intrinsic function"); 1516 if (handler.isElemental) 1517 return lib.genElementalCall(generator, handler.name, *resultType, args, 1518 outline); 1519 if (outline) 1520 return lib.outlineInExtendedWrapper(generator, handler.name, *resultType, 1521 args); 1522 return std::invoke(generator, lib, *resultType, args); 1523 } 1524 1525 static fir::ExtendedValue 1526 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator, 1527 const IntrinsicHandler &handler, 1528 llvm::Optional<mlir::Type> resultType, 1529 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1530 IntrinsicLibrary &lib) { 1531 if (handler.isElemental) 1532 return lib.genElementalCall(generator, handler.name, mlir::Type{}, args, 1533 outline); 1534 if (outline) 1535 return lib.outlineInExtendedWrapper(generator, handler.name, resultType, 1536 args); 1537 std::invoke(generator, lib, args); 1538 return mlir::Value{}; 1539 } 1540 1541 fir::ExtendedValue 1542 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName, 1543 llvm::Optional<mlir::Type> resultType, 1544 llvm::ArrayRef<fir::ExtendedValue> args) { 1545 llvm::StringRef name = genericName(specificName); 1546 if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) { 1547 bool outline = handler->outline || outlineAllIntrinsics; 1548 return std::visit( 1549 [&](auto &generator) -> fir::ExtendedValue { 1550 return invokeHandler(generator, *handler, resultType, args, outline, 1551 *this); 1552 }, 1553 handler->generator); 1554 } 1555 1556 if (!resultType) 1557 // Subroutine should have a handler, they are likely missing for now. 1558 crashOnMissingIntrinsic(loc, name); 1559 1560 // Try the runtime if no special handler was defined for the 1561 // intrinsic being called. Maths runtime only has numerical elemental. 1562 // No optional arguments are expected at this point, the code will 1563 // crash if it gets absent optional. 1564 1565 // FIXME: using toValue to get the type won't work with array arguments. 1566 llvm::SmallVector<mlir::Value> mlirArgs; 1567 for (const fir::ExtendedValue &extendedVal : args) { 1568 mlir::Value val = toValue(extendedVal, builder, loc); 1569 if (!val) 1570 // If an absent optional gets there, most likely its handler has just 1571 // not yet been defined. 1572 crashOnMissingIntrinsic(loc, name); 1573 mlirArgs.emplace_back(val); 1574 } 1575 mlir::FunctionType soughtFuncType = 1576 getFunctionType(*resultType, mlirArgs, builder); 1577 1578 IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator = 1579 getRuntimeCallGenerator(name, soughtFuncType); 1580 return genElementalCall(runtimeCallGenerator, name, *resultType, args, 1581 /* outline */ true); 1582 } 1583 1584 mlir::Value 1585 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator, 1586 mlir::Type resultType, 1587 llvm::ArrayRef<mlir::Value> args) { 1588 return std::invoke(generator, *this, resultType, args); 1589 } 1590 1591 mlir::Value 1592 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator, 1593 mlir::Type resultType, 1594 llvm::ArrayRef<mlir::Value> args) { 1595 return generator(builder, loc, args); 1596 } 1597 1598 mlir::Value 1599 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator, 1600 mlir::Type resultType, 1601 llvm::ArrayRef<mlir::Value> args) { 1602 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1603 for (mlir::Value arg : args) 1604 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1605 auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs); 1606 return toValue(extendedResult, builder, loc); 1607 } 1608 1609 mlir::Value 1610 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator, 1611 llvm::ArrayRef<mlir::Value> args) { 1612 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1613 for (mlir::Value arg : args) 1614 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1615 std::invoke(generator, *this, extendedArgs); 1616 return {}; 1617 } 1618 1619 template <typename GeneratorType> 1620 mlir::func::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator, 1621 llvm::StringRef name, 1622 mlir::FunctionType funcType, 1623 bool loadRefArguments) { 1624 std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType); 1625 mlir::func::FuncOp function = builder.getNamedFunction(wrapperName); 1626 if (!function) { 1627 // First time this wrapper is needed, build it. 1628 function = builder.createFunction(loc, wrapperName, funcType); 1629 function->setAttr("fir.intrinsic", builder.getUnitAttr()); 1630 auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; 1631 auto linkage = 1632 mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage); 1633 function->setAttr("llvm.linkage", linkage); 1634 function.addEntryBlock(); 1635 1636 // Create local context to emit code into the newly created function 1637 // This new function is not linked to a source file location, only 1638 // its calls will be. 1639 auto localBuilder = 1640 std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap()); 1641 localBuilder->setInsertionPointToStart(&function.front()); 1642 // Location of code inside wrapper of the wrapper is independent from 1643 // the location of the intrinsic call. 1644 mlir::Location localLoc = localBuilder->getUnknownLoc(); 1645 llvm::SmallVector<mlir::Value> localArguments; 1646 for (mlir::BlockArgument bArg : function.front().getArguments()) { 1647 auto refType = bArg.getType().dyn_cast<fir::ReferenceType>(); 1648 if (loadRefArguments && refType) { 1649 auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg); 1650 localArguments.push_back(loaded); 1651 } else { 1652 localArguments.push_back(bArg); 1653 } 1654 } 1655 1656 IntrinsicLibrary localLib{*localBuilder, localLoc}; 1657 1658 if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) { 1659 localLib.invokeGenerator(generator, localArguments); 1660 localBuilder->create<mlir::func::ReturnOp>(localLoc); 1661 } else { 1662 assert(funcType.getNumResults() == 1 && 1663 "expect one result for intrinsic function wrapper type"); 1664 mlir::Type resultType = funcType.getResult(0); 1665 auto result = 1666 localLib.invokeGenerator(generator, resultType, localArguments); 1667 localBuilder->create<mlir::func::ReturnOp>(localLoc, result); 1668 } 1669 } else { 1670 // Wrapper was already built, ensure it has the sought type 1671 assert(function.getFunctionType() == funcType && 1672 "conflict between intrinsic wrapper types"); 1673 } 1674 return function; 1675 } 1676 1677 /// Helpers to detect absent optional (not yet supported in outlining). 1678 bool static hasAbsentOptional(llvm::ArrayRef<mlir::Value> args) { 1679 for (const mlir::Value &arg : args) 1680 if (!arg) 1681 return true; 1682 return false; 1683 } 1684 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) { 1685 for (const fir::ExtendedValue &arg : args) 1686 if (!fir::getBase(arg)) 1687 return true; 1688 return false; 1689 } 1690 1691 template <typename GeneratorType> 1692 mlir::Value 1693 IntrinsicLibrary::outlineInWrapper(GeneratorType generator, 1694 llvm::StringRef name, mlir::Type resultType, 1695 llvm::ArrayRef<mlir::Value> args) { 1696 if (hasAbsentOptional(args)) { 1697 // TODO: absent optional in outlining is an issue: we cannot just ignore 1698 // them. Needs a better interface here. The issue is that we cannot easily 1699 // tell that a value is optional or not here if it is presents. And if it is 1700 // absent, we cannot tell what it type should be. 1701 TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + 1702 " with absent optional argument"); 1703 } 1704 1705 mlir::FunctionType funcType = getFunctionType(resultType, args, builder); 1706 mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType); 1707 return builder.create<fir::CallOp>(loc, wrapper, args).getResult(0); 1708 } 1709 1710 template <typename GeneratorType> 1711 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper( 1712 GeneratorType generator, llvm::StringRef name, 1713 llvm::Optional<mlir::Type> resultType, 1714 llvm::ArrayRef<fir::ExtendedValue> args) { 1715 if (hasAbsentOptional(args)) 1716 TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + 1717 " with absent optional argument"); 1718 llvm::SmallVector<mlir::Value> mlirArgs; 1719 for (const auto &extendedVal : args) 1720 mlirArgs.emplace_back(toValue(extendedVal, builder, loc)); 1721 mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder); 1722 mlir::func::FuncOp wrapper = getWrapper(generator, name, funcType); 1723 auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs); 1724 if (resultType) 1725 return toExtendedValue(call.getResult(0), builder, loc); 1726 // Subroutine calls 1727 return mlir::Value{}; 1728 } 1729 1730 IntrinsicLibrary::RuntimeCallGenerator 1731 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name, 1732 mlir::FunctionType soughtFuncType) { 1733 mlir::func::FuncOp funcOp = 1734 getRuntimeFunction(loc, builder, name, soughtFuncType); 1735 if (!funcOp) { 1736 std::string nameAndType; 1737 llvm::raw_string_ostream sstream(nameAndType); 1738 sstream << name << "\nrequested type: " << soughtFuncType; 1739 crashOnMissingIntrinsic(loc, nameAndType); 1740 } 1741 1742 mlir::FunctionType actualFuncType = funcOp.getFunctionType(); 1743 assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() && 1744 actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() && 1745 actualFuncType.getNumResults() == 1 && "Bad intrinsic match"); 1746 1747 return [funcOp, actualFuncType, 1748 soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc, 1749 llvm::ArrayRef<mlir::Value> args) { 1750 llvm::SmallVector<mlir::Value> convertedArguments; 1751 for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args)) 1752 convertedArguments.push_back(builder.createConvert(loc, fst, snd)); 1753 auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments); 1754 mlir::Type soughtType = soughtFuncType.getResult(0); 1755 return builder.createConvert(loc, soughtType, call.getResult(0)); 1756 }; 1757 } 1758 1759 mlir::SymbolRefAttr IntrinsicLibrary::getUnrestrictedIntrinsicSymbolRefAttr( 1760 llvm::StringRef name, mlir::FunctionType signature) { 1761 // Unrestricted intrinsics signature follows implicit rules: argument 1762 // are passed by references. But the runtime versions expect values. 1763 // So instead of duplicating the runtime, just have the wrappers loading 1764 // this before calling the code generators. 1765 bool loadRefArguments = true; 1766 mlir::func::FuncOp funcOp; 1767 if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) 1768 funcOp = std::visit( 1769 [&](auto generator) { 1770 return getWrapper(generator, name, signature, loadRefArguments); 1771 }, 1772 handler->generator); 1773 1774 if (!funcOp) { 1775 llvm::SmallVector<mlir::Type> argTypes; 1776 for (mlir::Type type : signature.getInputs()) { 1777 if (auto refType = type.dyn_cast<fir::ReferenceType>()) 1778 argTypes.push_back(refType.getEleTy()); 1779 else 1780 argTypes.push_back(type); 1781 } 1782 mlir::FunctionType soughtFuncType = 1783 builder.getFunctionType(argTypes, signature.getResults()); 1784 IntrinsicLibrary::RuntimeCallGenerator rtCallGenerator = 1785 getRuntimeCallGenerator(name, soughtFuncType); 1786 funcOp = getWrapper(rtCallGenerator, name, signature, loadRefArguments); 1787 } 1788 1789 return mlir::SymbolRefAttr::get(funcOp); 1790 } 1791 1792 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) { 1793 assert(stmtCtx); 1794 fir::FirOpBuilder *bldr = &builder; 1795 stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 1796 } 1797 1798 fir::ExtendedValue 1799 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 1800 mlir::Type resultType, 1801 llvm::StringRef intrinsicName) { 1802 fir::ExtendedValue res = 1803 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1804 return res.match( 1805 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1806 // Add cleanup code 1807 addCleanUpForTemp(loc, box.getAddr()); 1808 return box; 1809 }, 1810 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 1811 // Add cleanup code 1812 auto addr = 1813 builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); 1814 addCleanUpForTemp(loc, addr); 1815 return box; 1816 }, 1817 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 1818 // Add cleanup code 1819 addCleanUpForTemp(loc, box.getAddr()); 1820 return box; 1821 }, 1822 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 1823 // Add cleanup code 1824 addCleanUpForTemp(loc, tempAddr); 1825 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 1826 }, 1827 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1828 // Add cleanup code 1829 addCleanUpForTemp(loc, box.getAddr()); 1830 return box; 1831 }, 1832 [&](const auto &) -> fir::ExtendedValue { 1833 fir::emitFatalError(loc, "unexpected result for " + intrinsicName); 1834 }); 1835 } 1836 1837 //===----------------------------------------------------------------------===// 1838 // Code generators for the intrinsic 1839 //===----------------------------------------------------------------------===// 1840 1841 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name, 1842 mlir::Type resultType, 1843 llvm::ArrayRef<mlir::Value> args) { 1844 mlir::FunctionType soughtFuncType = 1845 getFunctionType(resultType, args, builder); 1846 return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args); 1847 } 1848 1849 mlir::Value IntrinsicLibrary::genConversion(mlir::Type resultType, 1850 llvm::ArrayRef<mlir::Value> args) { 1851 // There can be an optional kind in second argument. 1852 assert(args.size() >= 1); 1853 return builder.convertWithSemantics(loc, resultType, args[0]); 1854 } 1855 1856 // ABS 1857 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType, 1858 llvm::ArrayRef<mlir::Value> args) { 1859 assert(args.size() == 1); 1860 mlir::Value arg = args[0]; 1861 mlir::Type type = arg.getType(); 1862 if (fir::isa_real(type)) { 1863 // Runtime call to fp abs. An alternative would be to use mlir 1864 // math::AbsFOp but it does not support all fir floating point types. 1865 return genRuntimeCall("abs", resultType, args); 1866 } 1867 if (auto intType = type.dyn_cast<mlir::IntegerType>()) { 1868 // At the time of this implementation there is no abs op in mlir. 1869 // So, implement abs here without branching. 1870 mlir::Value shift = 1871 builder.createIntegerConstant(loc, intType, intType.getWidth() - 1); 1872 auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift); 1873 auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask); 1874 return builder.create<mlir::arith::SubIOp>(loc, xored, mask); 1875 } 1876 if (fir::isa_complex(type)) { 1877 // Use HYPOT to fulfill the no underflow/overflow requirement. 1878 auto parts = fir::factory::Complex{builder, loc}.extractParts(arg); 1879 llvm::SmallVector<mlir::Value> args = {parts.first, parts.second}; 1880 return genRuntimeCall("hypot", resultType, args); 1881 } 1882 llvm_unreachable("unexpected type in ABS argument"); 1883 } 1884 1885 // ADJUSTL & ADJUSTR 1886 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc, 1887 mlir::Value, mlir::Value)> 1888 fir::ExtendedValue 1889 IntrinsicLibrary::genAdjustRtCall(mlir::Type resultType, 1890 llvm::ArrayRef<fir::ExtendedValue> args) { 1891 assert(args.size() == 1); 1892 mlir::Value string = builder.createBox(loc, args[0]); 1893 // Create a mutable fir.box to be passed to the runtime for the result. 1894 fir::MutableBoxValue resultMutableBox = 1895 fir::factory::createTempMutableBox(builder, loc, resultType); 1896 mlir::Value resultIrBox = 1897 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1898 1899 // Call the runtime -- the runtime will allocate the result. 1900 CallRuntime(builder, loc, resultIrBox, string); 1901 1902 // Read result from mutable fir.box and add it to the list of temps to be 1903 // finalized by the StatementContext. 1904 fir::ExtendedValue res = 1905 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1906 return res.match( 1907 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1908 addCleanUpForTemp(loc, fir::getBase(box)); 1909 return box; 1910 }, 1911 [&](const auto &) -> fir::ExtendedValue { 1912 fir::emitFatalError(loc, "result of ADJUSTL is not a scalar character"); 1913 }); 1914 } 1915 1916 // AIMAG 1917 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType, 1918 llvm::ArrayRef<mlir::Value> args) { 1919 assert(args.size() == 1); 1920 return fir::factory::Complex{builder, loc}.extractComplexPart( 1921 args[0], true /* isImagPart */); 1922 } 1923 1924 // AINT 1925 mlir::Value IntrinsicLibrary::genAint(mlir::Type resultType, 1926 llvm::ArrayRef<mlir::Value> args) { 1927 assert(args.size() >= 1 && args.size() <= 2); 1928 // Skip optional kind argument to search the runtime; it is already reflected 1929 // in result type. 1930 return genRuntimeCall("aint", resultType, {args[0]}); 1931 } 1932 1933 // ALL 1934 fir::ExtendedValue 1935 IntrinsicLibrary::genAll(mlir::Type resultType, 1936 llvm::ArrayRef<fir::ExtendedValue> args) { 1937 1938 assert(args.size() == 2); 1939 // Handle required mask argument 1940 mlir::Value mask = builder.createBox(loc, args[0]); 1941 1942 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 1943 int rank = maskArry.rank(); 1944 assert(rank >= 1); 1945 1946 // Handle optional dim argument 1947 bool absentDim = isStaticallyAbsent(args[1]); 1948 mlir::Value dim = 1949 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1950 : fir::getBase(args[1]); 1951 1952 if (rank == 1 || absentDim) 1953 return builder.createConvert(loc, resultType, 1954 fir::runtime::genAll(builder, loc, mask, dim)); 1955 1956 // else use the result descriptor AllDim() intrinsic 1957 1958 // Create mutable fir.box to be passed to the runtime for the result. 1959 1960 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 1961 fir::MutableBoxValue resultMutableBox = 1962 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1963 mlir::Value resultIrBox = 1964 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1965 1966 // Call runtime. The runtime is allocating the result. 1967 fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim); 1968 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 1969 .match( 1970 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1971 addCleanUpForTemp(loc, box.getAddr()); 1972 return box; 1973 }, 1974 [&](const auto &) -> fir::ExtendedValue { 1975 fir::emitFatalError(loc, "Invalid result for ALL"); 1976 }); 1977 } 1978 1979 // ALLOCATED 1980 fir::ExtendedValue 1981 IntrinsicLibrary::genAllocated(mlir::Type resultType, 1982 llvm::ArrayRef<fir::ExtendedValue> args) { 1983 assert(args.size() == 1); 1984 return args[0].match( 1985 [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue { 1986 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x); 1987 }, 1988 [&](const auto &) -> fir::ExtendedValue { 1989 fir::emitFatalError(loc, 1990 "allocated arg not lowered to MutableBoxValue"); 1991 }); 1992 } 1993 1994 // ANINT 1995 mlir::Value IntrinsicLibrary::genAnint(mlir::Type resultType, 1996 llvm::ArrayRef<mlir::Value> args) { 1997 assert(args.size() >= 1 && args.size() <= 2); 1998 // Skip optional kind argument to search the runtime; it is already reflected 1999 // in result type. 2000 return genRuntimeCall("anint", resultType, {args[0]}); 2001 } 2002 2003 // ANY 2004 fir::ExtendedValue 2005 IntrinsicLibrary::genAny(mlir::Type resultType, 2006 llvm::ArrayRef<fir::ExtendedValue> args) { 2007 2008 assert(args.size() == 2); 2009 // Handle required mask argument 2010 mlir::Value mask = builder.createBox(loc, args[0]); 2011 2012 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 2013 int rank = maskArry.rank(); 2014 assert(rank >= 1); 2015 2016 // Handle optional dim argument 2017 bool absentDim = isStaticallyAbsent(args[1]); 2018 mlir::Value dim = 2019 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 2020 : fir::getBase(args[1]); 2021 2022 if (rank == 1 || absentDim) 2023 return builder.createConvert(loc, resultType, 2024 fir::runtime::genAny(builder, loc, mask, dim)); 2025 2026 // else use the result descriptor AnyDim() intrinsic 2027 2028 // Create mutable fir.box to be passed to the runtime for the result. 2029 2030 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 2031 fir::MutableBoxValue resultMutableBox = 2032 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2033 mlir::Value resultIrBox = 2034 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2035 2036 // Call runtime. The runtime is allocating the result. 2037 fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim); 2038 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 2039 .match( 2040 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 2041 addCleanUpForTemp(loc, box.getAddr()); 2042 return box; 2043 }, 2044 [&](const auto &) -> fir::ExtendedValue { 2045 fir::emitFatalError(loc, "Invalid result for ANY"); 2046 }); 2047 } 2048 2049 // ASSOCIATED 2050 fir::ExtendedValue 2051 IntrinsicLibrary::genAssociated(mlir::Type resultType, 2052 llvm::ArrayRef<fir::ExtendedValue> args) { 2053 assert(args.size() == 2); 2054 auto *pointer = 2055 args[0].match([&](const fir::MutableBoxValue &x) { return &x; }, 2056 [&](const auto &) -> const fir::MutableBoxValue * { 2057 fir::emitFatalError(loc, "pointer not a MutableBoxValue"); 2058 }); 2059 const fir::ExtendedValue &target = args[1]; 2060 if (isStaticallyAbsent(target)) 2061 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer); 2062 2063 mlir::Value targetBox = builder.createBox(loc, target); 2064 if (fir::valueHasFirAttribute(fir::getBase(target), 2065 fir::getOptionalAttrName())) { 2066 // Subtle: contrary to other intrinsic optional arguments, disassociated 2067 // POINTER and unallocated ALLOCATABLE actual argument are not considered 2068 // absent here. This is because ASSOCIATED has special requirements for 2069 // TARGET actual arguments that are POINTERs. There is no precise 2070 // requirements for ALLOCATABLEs, but all existing Fortran compilers treat 2071 // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED 2072 // to rerun false. The runtime deals with the disassociated/unallocated 2073 // case. Simply ensures that TARGET that are OPTIONAL get conditionally 2074 // emboxed here to convey the optional aspect to the runtime. 2075 auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 2076 fir::getBase(target)); 2077 auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType()); 2078 targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox, 2079 absentBox); 2080 } 2081 mlir::Value pointerBoxRef = 2082 fir::factory::getMutableIRBox(builder, loc, *pointer); 2083 auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef); 2084 return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox); 2085 } 2086 2087 // BTEST 2088 mlir::Value IntrinsicLibrary::genBtest(mlir::Type resultType, 2089 llvm::ArrayRef<mlir::Value> args) { 2090 // A conformant BTEST(I,POS) call satisfies: 2091 // POS >= 0 2092 // POS < BIT_SIZE(I) 2093 // Return: (I >> POS) & 1 2094 assert(args.size() == 2); 2095 mlir::Type argType = args[0].getType(); 2096 mlir::Value pos = builder.createConvert(loc, argType, args[1]); 2097 auto shift = builder.create<mlir::arith::ShRUIOp>(loc, args[0], pos); 2098 mlir::Value one = builder.createIntegerConstant(loc, argType, 1); 2099 auto res = builder.create<mlir::arith::AndIOp>(loc, shift, one); 2100 return builder.createConvert(loc, resultType, res); 2101 } 2102 2103 // CEILING 2104 mlir::Value IntrinsicLibrary::genCeiling(mlir::Type resultType, 2105 llvm::ArrayRef<mlir::Value> args) { 2106 // Optional KIND argument. 2107 assert(args.size() >= 1); 2108 mlir::Value arg = args[0]; 2109 // Use ceil that is not an actual Fortran intrinsic but that is 2110 // an llvm intrinsic that does the same, but return a floating 2111 // point. 2112 mlir::Value ceil = genRuntimeCall("ceil", arg.getType(), {arg}); 2113 return builder.createConvert(loc, resultType, ceil); 2114 } 2115 2116 // CHAR 2117 fir::ExtendedValue 2118 IntrinsicLibrary::genChar(mlir::Type type, 2119 llvm::ArrayRef<fir::ExtendedValue> args) { 2120 // Optional KIND argument. 2121 assert(args.size() >= 1); 2122 const mlir::Value *arg = args[0].getUnboxed(); 2123 // expect argument to be a scalar integer 2124 if (!arg) 2125 mlir::emitError(loc, "CHAR intrinsic argument not unboxed"); 2126 fir::factory::CharacterExprHelper helper{builder, loc}; 2127 fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind(); 2128 mlir::Value cast = helper.createSingletonFromCode(*arg, kind); 2129 mlir::Value len = 2130 builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1); 2131 return fir::CharBoxValue{cast, len}; 2132 } 2133 2134 // CMPLX 2135 mlir::Value IntrinsicLibrary::genCmplx(mlir::Type resultType, 2136 llvm::ArrayRef<mlir::Value> args) { 2137 assert(args.size() >= 1); 2138 fir::factory::Complex complexHelper(builder, loc); 2139 mlir::Type partType = complexHelper.getComplexPartType(resultType); 2140 mlir::Value real = builder.createConvert(loc, partType, args[0]); 2141 mlir::Value imag = isStaticallyAbsent(args, 1) 2142 ? builder.createRealZeroConstant(loc, partType) 2143 : builder.createConvert(loc, partType, args[1]); 2144 return fir::factory::Complex{builder, loc}.createComplex(resultType, real, 2145 imag); 2146 } 2147 2148 // COMMAND_ARGUMENT_COUNT 2149 fir::ExtendedValue IntrinsicLibrary::genCommandArgumentCount( 2150 mlir::Type resultType, llvm::ArrayRef<fir::ExtendedValue> args) { 2151 assert(args.size() == 0); 2152 assert(resultType == builder.getDefaultIntegerType() && 2153 "result type is not default integer kind type"); 2154 return builder.createConvert( 2155 loc, resultType, fir::runtime::genCommandArgumentCount(builder, loc)); 2156 ; 2157 } 2158 2159 // CONJG 2160 mlir::Value IntrinsicLibrary::genConjg(mlir::Type resultType, 2161 llvm::ArrayRef<mlir::Value> args) { 2162 assert(args.size() == 1); 2163 if (resultType != args[0].getType()) 2164 llvm_unreachable("argument type mismatch"); 2165 2166 mlir::Value cplx = args[0]; 2167 auto imag = fir::factory::Complex{builder, loc}.extractComplexPart( 2168 cplx, /*isImagPart=*/true); 2169 auto negImag = builder.create<mlir::arith::NegFOp>(loc, imag); 2170 return fir::factory::Complex{builder, loc}.insertComplexPart( 2171 cplx, negImag, /*isImagPart=*/true); 2172 } 2173 2174 // COUNT 2175 fir::ExtendedValue 2176 IntrinsicLibrary::genCount(mlir::Type resultType, 2177 llvm::ArrayRef<fir::ExtendedValue> args) { 2178 assert(args.size() == 3); 2179 2180 // Handle mask argument 2181 fir::BoxValue mask = builder.createBox(loc, args[0]); 2182 unsigned maskRank = mask.rank(); 2183 2184 assert(maskRank > 0); 2185 2186 // Handle optional dim argument 2187 bool absentDim = isStaticallyAbsent(args[1]); 2188 mlir::Value dim = 2189 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 2190 : fir::getBase(args[1]); 2191 2192 if (absentDim || maskRank == 1) { 2193 // Result is scalar if no dim argument or mask is rank 1. 2194 // So, call specialized Count runtime routine. 2195 return builder.createConvert( 2196 loc, resultType, 2197 fir::runtime::genCount(builder, loc, fir::getBase(mask), dim)); 2198 } 2199 2200 // Call general CountDim runtime routine. 2201 2202 // Handle optional kind argument 2203 bool absentKind = isStaticallyAbsent(args[2]); 2204 mlir::Value kind = absentKind ? builder.createIntegerConstant( 2205 loc, builder.getIndexType(), 2206 builder.getKindMap().defaultIntegerKind()) 2207 : fir::getBase(args[2]); 2208 2209 // Create mutable fir.box to be passed to the runtime for the result. 2210 mlir::Type type = builder.getVarLenSeqTy(resultType, maskRank - 1); 2211 fir::MutableBoxValue resultMutableBox = 2212 fir::factory::createTempMutableBox(builder, loc, type); 2213 2214 mlir::Value resultIrBox = 2215 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2216 2217 fir::runtime::genCountDim(builder, loc, resultIrBox, fir::getBase(mask), dim, 2218 kind); 2219 2220 // Handle cleanup of allocatable result descriptor and return 2221 fir::ExtendedValue res = 2222 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 2223 return res.match( 2224 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 2225 // Add cleanup code 2226 addCleanUpForTemp(loc, box.getAddr()); 2227 return box; 2228 }, 2229 [&](const auto &) -> fir::ExtendedValue { 2230 fir::emitFatalError(loc, "unexpected result for COUNT"); 2231 }); 2232 } 2233 2234 // CPU_TIME 2235 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) { 2236 assert(args.size() == 1); 2237 const mlir::Value *arg = args[0].getUnboxed(); 2238 assert(arg && "nonscalar cpu_time argument"); 2239 mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc); 2240 mlir::Value res2 = 2241 builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1); 2242 builder.create<fir::StoreOp>(loc, res2, *arg); 2243 } 2244 2245 // CSHIFT 2246 fir::ExtendedValue 2247 IntrinsicLibrary::genCshift(mlir::Type resultType, 2248 llvm::ArrayRef<fir::ExtendedValue> args) { 2249 assert(args.size() == 3); 2250 2251 // Handle required ARRAY argument 2252 fir::BoxValue arrayBox = builder.createBox(loc, args[0]); 2253 mlir::Value array = fir::getBase(arrayBox); 2254 unsigned arrayRank = arrayBox.rank(); 2255 2256 // Create mutable fir.box to be passed to the runtime for the result. 2257 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank); 2258 fir::MutableBoxValue resultMutableBox = 2259 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2260 mlir::Value resultIrBox = 2261 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2262 2263 if (arrayRank == 1) { 2264 // Vector case 2265 // Handle required SHIFT argument as a scalar 2266 const mlir::Value *shiftAddr = args[1].getUnboxed(); 2267 assert(shiftAddr && "nonscalar CSHIFT argument"); 2268 auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr); 2269 2270 fir::runtime::genCshiftVector(builder, loc, resultIrBox, array, shift); 2271 } else { 2272 // Non-vector case 2273 // Handle required SHIFT argument as an array 2274 mlir::Value shift = builder.createBox(loc, args[1]); 2275 2276 // Handle optional DIM argument 2277 mlir::Value dim = 2278 isStaticallyAbsent(args[2]) 2279 ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 2280 : fir::getBase(args[2]); 2281 fir::runtime::genCshift(builder, loc, resultIrBox, array, shift, dim); 2282 } 2283 return readAndAddCleanUp(resultMutableBox, resultType, "CSHIFT"); 2284 } 2285 2286 // DATE_AND_TIME 2287 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) { 2288 assert(args.size() == 4 && "date_and_time has 4 args"); 2289 llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3); 2290 for (unsigned i = 0; i < 3; ++i) 2291 if (const fir::CharBoxValue *charBox = args[i].getCharBox()) 2292 charArgs[i] = *charBox; 2293 2294 mlir::Value values = fir::getBase(args[3]); 2295 if (!values) 2296 values = builder.create<fir::AbsentOp>( 2297 loc, fir::BoxType::get(builder.getNoneType())); 2298 2299 Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1], 2300 charArgs[2], values); 2301 } 2302 2303 // DIM 2304 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType, 2305 llvm::ArrayRef<mlir::Value> args) { 2306 assert(args.size() == 2); 2307 if (resultType.isa<mlir::IntegerType>()) { 2308 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2309 auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]); 2310 auto cmp = builder.create<mlir::arith::CmpIOp>( 2311 loc, mlir::arith::CmpIPredicate::sgt, diff, zero); 2312 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 2313 } 2314 assert(fir::isa_real(resultType) && "Only expects real and integer in DIM"); 2315 mlir::Value zero = builder.createRealZeroConstant(loc, resultType); 2316 auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]); 2317 auto cmp = builder.create<mlir::arith::CmpFOp>( 2318 loc, mlir::arith::CmpFPredicate::OGT, diff, zero); 2319 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 2320 } 2321 2322 // DOT_PRODUCT 2323 fir::ExtendedValue 2324 IntrinsicLibrary::genDotProduct(mlir::Type resultType, 2325 llvm::ArrayRef<fir::ExtendedValue> args) { 2326 return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc, 2327 stmtCtx, args); 2328 } 2329 2330 // DPROD 2331 mlir::Value IntrinsicLibrary::genDprod(mlir::Type resultType, 2332 llvm::ArrayRef<mlir::Value> args) { 2333 assert(args.size() == 2); 2334 assert(fir::isa_real(resultType) && 2335 "Result must be double precision in DPROD"); 2336 mlir::Value a = builder.createConvert(loc, resultType, args[0]); 2337 mlir::Value b = builder.createConvert(loc, resultType, args[1]); 2338 return builder.create<mlir::arith::MulFOp>(loc, a, b); 2339 } 2340 2341 // EOSHIFT 2342 fir::ExtendedValue 2343 IntrinsicLibrary::genEoshift(mlir::Type resultType, 2344 llvm::ArrayRef<fir::ExtendedValue> args) { 2345 assert(args.size() == 4); 2346 2347 // Handle required ARRAY argument 2348 fir::BoxValue arrayBox = builder.createBox(loc, args[0]); 2349 mlir::Value array = fir::getBase(arrayBox); 2350 unsigned arrayRank = arrayBox.rank(); 2351 2352 // Create mutable fir.box to be passed to the runtime for the result. 2353 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank); 2354 fir::MutableBoxValue resultMutableBox = 2355 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2356 mlir::Value resultIrBox = 2357 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2358 2359 // Handle optional BOUNDARY argument 2360 mlir::Value boundary = 2361 isStaticallyAbsent(args[2]) 2362 ? builder.create<fir::AbsentOp>( 2363 loc, fir::BoxType::get(builder.getNoneType())) 2364 : builder.createBox(loc, args[2]); 2365 2366 if (arrayRank == 1) { 2367 // Vector case 2368 // Handle required SHIFT argument as a scalar 2369 const mlir::Value *shiftAddr = args[1].getUnboxed(); 2370 assert(shiftAddr && "nonscalar EOSHIFT SHIFT argument"); 2371 auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr); 2372 fir::runtime::genEoshiftVector(builder, loc, resultIrBox, array, shift, 2373 boundary); 2374 } else { 2375 // Non-vector case 2376 // Handle required SHIFT argument as an array 2377 mlir::Value shift = builder.createBox(loc, args[1]); 2378 2379 // Handle optional DIM argument 2380 mlir::Value dim = 2381 isStaticallyAbsent(args[3]) 2382 ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 2383 : fir::getBase(args[3]); 2384 fir::runtime::genEoshift(builder, loc, resultIrBox, array, shift, boundary, 2385 dim); 2386 } 2387 return readAndAddCleanUp(resultMutableBox, resultType, 2388 "unexpected result for EOSHIFT"); 2389 } 2390 2391 // EXIT 2392 void IntrinsicLibrary::genExit(llvm::ArrayRef<fir::ExtendedValue> args) { 2393 assert(args.size() == 1); 2394 2395 mlir::Value status = 2396 isStaticallyAbsent(args[0]) 2397 ? builder.createIntegerConstant(loc, builder.getDefaultIntegerType(), 2398 EXIT_SUCCESS) 2399 : fir::getBase(args[0]); 2400 2401 assert(status.getType() == builder.getDefaultIntegerType() && 2402 "STATUS parameter must be an INTEGER of default kind"); 2403 2404 fir::runtime::genExit(builder, loc, status); 2405 } 2406 2407 // EXPONENT 2408 mlir::Value IntrinsicLibrary::genExponent(mlir::Type resultType, 2409 llvm::ArrayRef<mlir::Value> args) { 2410 assert(args.size() == 1); 2411 2412 return builder.createConvert( 2413 loc, resultType, 2414 fir::runtime::genExponent(builder, loc, resultType, 2415 fir::getBase(args[0]))); 2416 } 2417 2418 // FLOOR 2419 mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType, 2420 llvm::ArrayRef<mlir::Value> args) { 2421 // Optional KIND argument. 2422 assert(args.size() >= 1); 2423 mlir::Value arg = args[0]; 2424 // Use LLVM floor that returns real. 2425 mlir::Value floor = genRuntimeCall("floor", arg.getType(), {arg}); 2426 return builder.createConvert(loc, resultType, floor); 2427 } 2428 2429 // FRACTION 2430 mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType, 2431 llvm::ArrayRef<mlir::Value> args) { 2432 assert(args.size() == 1); 2433 2434 return builder.createConvert( 2435 loc, resultType, 2436 fir::runtime::genFraction(builder, loc, fir::getBase(args[0]))); 2437 } 2438 2439 // GET_COMMAND_ARGUMENT 2440 void IntrinsicLibrary::genGetCommandArgument( 2441 llvm::ArrayRef<fir::ExtendedValue> args) { 2442 assert(args.size() == 5); 2443 mlir::Value number = fir::getBase(args[0]); 2444 const fir::ExtendedValue &value = args[1]; 2445 const fir::ExtendedValue &length = args[2]; 2446 const fir::ExtendedValue &status = args[3]; 2447 const fir::ExtendedValue &errmsg = args[4]; 2448 2449 if (!number) 2450 fir::emitFatalError(loc, "expected NUMBER parameter"); 2451 2452 if (isStaticallyPresent(value) || isStaticallyPresent(status) || 2453 isStaticallyPresent(errmsg)) { 2454 mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType()); 2455 mlir::Value valBox = 2456 isStaticallyPresent(value) 2457 ? fir::getBase(value) 2458 : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult(); 2459 mlir::Value errBox = 2460 isStaticallyPresent(errmsg) 2461 ? fir::getBase(errmsg) 2462 : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult(); 2463 mlir::Value stat = 2464 fir::runtime::genArgumentValue(builder, loc, number, valBox, errBox); 2465 if (isStaticallyPresent(status)) { 2466 mlir::Value statAddr = fir::getBase(status); 2467 mlir::Value statIsPresentAtRuntime = 2468 builder.genIsNotNullAddr(loc, statAddr); 2469 builder.genIfThen(loc, statIsPresentAtRuntime) 2470 .genThen( 2471 [&]() { builder.createStoreWithConvert(loc, stat, statAddr); }) 2472 .end(); 2473 } 2474 } 2475 if (isStaticallyPresent(length)) { 2476 mlir::Value lenAddr = fir::getBase(length); 2477 mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr); 2478 builder.genIfThen(loc, lenIsPresentAtRuntime) 2479 .genThen([&]() { 2480 mlir::Value len = 2481 fir::runtime::genArgumentLength(builder, loc, number); 2482 builder.createStoreWithConvert(loc, len, lenAddr); 2483 }) 2484 .end(); 2485 } 2486 } 2487 2488 // GET_ENVIRONMENT_VARIABLE 2489 void IntrinsicLibrary::genGetEnvironmentVariable( 2490 llvm::ArrayRef<fir::ExtendedValue> args) { 2491 assert(args.size() == 6); 2492 mlir::Value name = fir::getBase(args[0]); 2493 const fir::ExtendedValue &value = args[1]; 2494 const fir::ExtendedValue &length = args[2]; 2495 const fir::ExtendedValue &status = args[3]; 2496 const fir::ExtendedValue &trimName = args[4]; 2497 const fir::ExtendedValue &errmsg = args[5]; 2498 2499 // Handle optional TRIM_NAME argument 2500 mlir::Value trim; 2501 if (isStaticallyAbsent(trimName)) { 2502 trim = builder.createBool(loc, true); 2503 } else { 2504 mlir::Type i1Ty = builder.getI1Type(); 2505 mlir::Value trimNameAddr = fir::getBase(trimName); 2506 mlir::Value trimNameIsPresentAtRuntime = 2507 builder.genIsNotNullAddr(loc, trimNameAddr); 2508 trim = builder 2509 .genIfOp(loc, {i1Ty}, trimNameIsPresentAtRuntime, 2510 /*withElseRegion=*/true) 2511 .genThen([&]() { 2512 auto trimLoad = builder.create<fir::LoadOp>(loc, trimNameAddr); 2513 mlir::Value cast = builder.createConvert(loc, i1Ty, trimLoad); 2514 builder.create<fir::ResultOp>(loc, cast); 2515 }) 2516 .genElse([&]() { 2517 mlir::Value trueVal = builder.createBool(loc, true); 2518 builder.create<fir::ResultOp>(loc, trueVal); 2519 }) 2520 .getResults()[0]; 2521 } 2522 2523 if (isStaticallyPresent(value) || isStaticallyPresent(status) || 2524 isStaticallyPresent(errmsg)) { 2525 mlir::Type boxNoneTy = fir::BoxType::get(builder.getNoneType()); 2526 mlir::Value valBox = 2527 isStaticallyPresent(value) 2528 ? fir::getBase(value) 2529 : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult(); 2530 mlir::Value errBox = 2531 isStaticallyPresent(errmsg) 2532 ? fir::getBase(errmsg) 2533 : builder.create<fir::AbsentOp>(loc, boxNoneTy).getResult(); 2534 mlir::Value stat = fir::runtime::genEnvVariableValue(builder, loc, name, 2535 valBox, trim, errBox); 2536 if (isStaticallyPresent(status)) { 2537 mlir::Value statAddr = fir::getBase(status); 2538 mlir::Value statIsPresentAtRuntime = 2539 builder.genIsNotNullAddr(loc, statAddr); 2540 builder.genIfThen(loc, statIsPresentAtRuntime) 2541 .genThen( 2542 [&]() { builder.createStoreWithConvert(loc, stat, statAddr); }) 2543 .end(); 2544 } 2545 } 2546 2547 if (isStaticallyPresent(length)) { 2548 mlir::Value lenAddr = fir::getBase(length); 2549 mlir::Value lenIsPresentAtRuntime = builder.genIsNotNullAddr(loc, lenAddr); 2550 builder.genIfThen(loc, lenIsPresentAtRuntime) 2551 .genThen([&]() { 2552 mlir::Value len = 2553 fir::runtime::genEnvVariableLength(builder, loc, name, trim); 2554 builder.createStoreWithConvert(loc, len, lenAddr); 2555 }) 2556 .end(); 2557 } 2558 } 2559 2560 // IAND 2561 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType, 2562 llvm::ArrayRef<mlir::Value> args) { 2563 assert(args.size() == 2); 2564 auto arg0 = builder.createConvert(loc, resultType, args[0]); 2565 auto arg1 = builder.createConvert(loc, resultType, args[1]); 2566 return builder.create<mlir::arith::AndIOp>(loc, arg0, arg1); 2567 } 2568 2569 // IBCLR 2570 mlir::Value IntrinsicLibrary::genIbclr(mlir::Type resultType, 2571 llvm::ArrayRef<mlir::Value> args) { 2572 // A conformant IBCLR(I,POS) call satisfies: 2573 // POS >= 0 2574 // POS < BIT_SIZE(I) 2575 // Return: I & (!(1 << POS)) 2576 assert(args.size() == 2); 2577 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2578 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 2579 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2580 auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos); 2581 auto res = builder.create<mlir::arith::XOrIOp>(loc, ones, mask); 2582 return builder.create<mlir::arith::AndIOp>(loc, args[0], res); 2583 } 2584 2585 // IBITS 2586 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType, 2587 llvm::ArrayRef<mlir::Value> args) { 2588 // A conformant IBITS(I,POS,LEN) call satisfies: 2589 // POS >= 0 2590 // LEN >= 0 2591 // POS + LEN <= BIT_SIZE(I) 2592 // Return: LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN)) 2593 // For a conformant call, implementing (I >> POS) with a signed or an 2594 // unsigned shift produces the same result. For a nonconformant call, 2595 // the two choices may produce different results. 2596 assert(args.size() == 3); 2597 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2598 mlir::Value len = builder.createConvert(loc, resultType, args[2]); 2599 mlir::Value bitSize = builder.createIntegerConstant( 2600 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2601 auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len); 2602 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2603 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2604 auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount); 2605 auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos); 2606 auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask); 2607 auto lenIsZero = builder.create<mlir::arith::CmpIOp>( 2608 loc, mlir::arith::CmpIPredicate::eq, len, zero); 2609 return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2); 2610 } 2611 2612 // IBSET 2613 mlir::Value IntrinsicLibrary::genIbset(mlir::Type resultType, 2614 llvm::ArrayRef<mlir::Value> args) { 2615 // A conformant IBSET(I,POS) call satisfies: 2616 // POS >= 0 2617 // POS < BIT_SIZE(I) 2618 // Return: I | (1 << POS) 2619 assert(args.size() == 2); 2620 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2621 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 2622 auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos); 2623 return builder.create<mlir::arith::OrIOp>(loc, args[0], mask); 2624 } 2625 2626 // ICHAR 2627 fir::ExtendedValue 2628 IntrinsicLibrary::genIchar(mlir::Type resultType, 2629 llvm::ArrayRef<fir::ExtendedValue> args) { 2630 // There can be an optional kind in second argument. 2631 assert(args.size() == 2); 2632 const fir::CharBoxValue *charBox = args[0].getCharBox(); 2633 if (!charBox) 2634 llvm::report_fatal_error("expected character scalar"); 2635 2636 fir::factory::CharacterExprHelper helper{builder, loc}; 2637 mlir::Value buffer = charBox->getBuffer(); 2638 mlir::Type bufferTy = buffer.getType(); 2639 mlir::Value charVal; 2640 if (auto charTy = bufferTy.dyn_cast<fir::CharacterType>()) { 2641 assert(charTy.singleton()); 2642 charVal = buffer; 2643 } else { 2644 // Character is in memory, cast to fir.ref<char> and load. 2645 mlir::Type ty = fir::dyn_cast_ptrEleTy(bufferTy); 2646 if (!ty) 2647 llvm::report_fatal_error("expected memory type"); 2648 // The length of in the character type may be unknown. Casting 2649 // to a singleton ref is required before loading. 2650 fir::CharacterType eleType = helper.getCharacterType(ty); 2651 fir::CharacterType charType = 2652 fir::CharacterType::get(builder.getContext(), eleType.getFKind(), 1); 2653 mlir::Type toTy = builder.getRefType(charType); 2654 mlir::Value cast = builder.createConvert(loc, toTy, buffer); 2655 charVal = builder.create<fir::LoadOp>(loc, cast); 2656 } 2657 LLVM_DEBUG(llvm::dbgs() << "ichar(" << charVal << ")\n"); 2658 auto code = helper.extractCodeFromSingleton(charVal); 2659 if (code.getType() == resultType) 2660 return code; 2661 return builder.create<mlir::arith::ExtUIOp>(loc, resultType, code); 2662 } 2663 2664 // IEEE_CLASS_TYPE OPERATOR(==), OPERATOR(/=) 2665 // IEEE_ROUND_TYPE OPERATOR(==), OPERATOR(/=) 2666 template <mlir::arith::CmpIPredicate pred> 2667 fir::ExtendedValue 2668 IntrinsicLibrary::genIeeeTypeCompare(mlir::Type resultType, 2669 llvm::ArrayRef<fir::ExtendedValue> args) { 2670 assert(args.size() == 2); 2671 mlir::Value arg0 = fir::getBase(args[0]); 2672 mlir::Value arg1 = fir::getBase(args[1]); 2673 auto recType = 2674 fir::unwrapPassByRefType(arg0.getType()).dyn_cast<fir::RecordType>(); 2675 assert(recType.getTypeList().size() == 1 && "expected exactly one component"); 2676 auto [fieldName, fieldType] = recType.getTypeList().front(); 2677 mlir::Type fieldIndexType = fir::FieldType::get(recType.getContext()); 2678 mlir::Value field = builder.create<fir::FieldIndexOp>( 2679 loc, fieldIndexType, fieldName, recType, fir::getTypeParams(arg0)); 2680 mlir::Value left = builder.create<fir::LoadOp>( 2681 loc, fieldType, 2682 builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType), 2683 arg0, field)); 2684 mlir::Value right = builder.create<fir::LoadOp>( 2685 loc, fieldType, 2686 builder.create<fir::CoordinateOp>(loc, builder.getRefType(fieldType), 2687 arg1, field)); 2688 return builder.create<mlir::arith::CmpIOp>(loc, pred, left, right); 2689 } 2690 2691 // IEEE_IS_FINITE 2692 mlir::Value 2693 IntrinsicLibrary::genIeeeIsFinite(mlir::Type resultType, 2694 llvm::ArrayRef<mlir::Value> args) { 2695 // IEEE_IS_FINITE(X) is true iff exponent(X) is the max exponent of kind(X). 2696 assert(args.size() == 1); 2697 mlir::Value floatVal = fir::getBase(args[0]); 2698 mlir::FloatType floatType = floatVal.getType().dyn_cast<mlir::FloatType>(); 2699 int floatBits = floatType.getWidth(); 2700 mlir::Type intType = builder.getIntegerType( 2701 floatType.isa<mlir::Float80Type>() ? 128 : floatBits); 2702 mlir::Value intVal = 2703 builder.create<mlir::arith::BitcastOp>(loc, intType, floatVal); 2704 int significandBits; 2705 if (floatType.isa<mlir::Float32Type>()) 2706 significandBits = 23; 2707 else if (floatType.isa<mlir::Float64Type>()) 2708 significandBits = 52; 2709 else // problems elsewhere for other kinds 2710 TODO(loc, "intrinsic module procedure: ieee_is_finite"); 2711 mlir::Value significand = 2712 builder.createIntegerConstant(loc, intType, significandBits); 2713 int exponentBits = floatBits - 1 - significandBits; 2714 mlir::Value maxExponent = 2715 builder.createIntegerConstant(loc, intType, (1 << exponentBits) - 1); 2716 mlir::Value exponent = genIbits( 2717 intType, {intVal, significand, 2718 builder.createIntegerConstant(loc, intType, exponentBits)}); 2719 return builder.createConvert( 2720 loc, resultType, 2721 builder.create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::ne, 2722 exponent, maxExponent)); 2723 } 2724 2725 // IEOR 2726 mlir::Value IntrinsicLibrary::genIeor(mlir::Type resultType, 2727 llvm::ArrayRef<mlir::Value> args) { 2728 assert(args.size() == 2); 2729 return builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]); 2730 } 2731 2732 // INDEX 2733 fir::ExtendedValue 2734 IntrinsicLibrary::genIndex(mlir::Type resultType, 2735 llvm::ArrayRef<fir::ExtendedValue> args) { 2736 assert(args.size() >= 2 && args.size() <= 4); 2737 2738 mlir::Value stringBase = fir::getBase(args[0]); 2739 fir::KindTy kind = 2740 fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind( 2741 stringBase.getType()); 2742 mlir::Value stringLen = fir::getLen(args[0]); 2743 mlir::Value substringBase = fir::getBase(args[1]); 2744 mlir::Value substringLen = fir::getLen(args[1]); 2745 mlir::Value back = 2746 isStaticallyAbsent(args, 2) 2747 ? builder.createIntegerConstant(loc, builder.getI1Type(), 0) 2748 : fir::getBase(args[2]); 2749 if (isStaticallyAbsent(args, 3)) 2750 return builder.createConvert( 2751 loc, resultType, 2752 fir::runtime::genIndex(builder, loc, kind, stringBase, stringLen, 2753 substringBase, substringLen, back)); 2754 2755 // Call the descriptor-based Index implementation 2756 mlir::Value string = builder.createBox(loc, args[0]); 2757 mlir::Value substring = builder.createBox(loc, args[1]); 2758 auto makeRefThenEmbox = [&](mlir::Value b) { 2759 fir::LogicalType logTy = fir::LogicalType::get( 2760 builder.getContext(), builder.getKindMap().defaultLogicalKind()); 2761 mlir::Value temp = builder.createTemporary(loc, logTy); 2762 mlir::Value castb = builder.createConvert(loc, logTy, b); 2763 builder.create<fir::StoreOp>(loc, castb, temp); 2764 return builder.createBox(loc, temp); 2765 }; 2766 mlir::Value backOpt = isStaticallyAbsent(args, 2) 2767 ? builder.create<fir::AbsentOp>( 2768 loc, fir::BoxType::get(builder.getI1Type())) 2769 : makeRefThenEmbox(fir::getBase(args[2])); 2770 mlir::Value kindVal = isStaticallyAbsent(args, 3) 2771 ? builder.createIntegerConstant( 2772 loc, builder.getIndexType(), 2773 builder.getKindMap().defaultIntegerKind()) 2774 : fir::getBase(args[3]); 2775 // Create mutable fir.box to be passed to the runtime for the result. 2776 fir::MutableBoxValue mutBox = 2777 fir::factory::createTempMutableBox(builder, loc, resultType); 2778 mlir::Value resBox = fir::factory::getMutableIRBox(builder, loc, mutBox); 2779 // Call runtime. The runtime is allocating the result. 2780 fir::runtime::genIndexDescriptor(builder, loc, resBox, string, substring, 2781 backOpt, kindVal); 2782 // Read back the result from the mutable box. 2783 return readAndAddCleanUp(mutBox, resultType, "INDEX"); 2784 } 2785 2786 // IOR 2787 mlir::Value IntrinsicLibrary::genIor(mlir::Type resultType, 2788 llvm::ArrayRef<mlir::Value> args) { 2789 assert(args.size() == 2); 2790 return builder.create<mlir::arith::OrIOp>(loc, args[0], args[1]); 2791 } 2792 2793 // ISHFT 2794 mlir::Value IntrinsicLibrary::genIshft(mlir::Type resultType, 2795 llvm::ArrayRef<mlir::Value> args) { 2796 // A conformant ISHFT(I,SHIFT) call satisfies: 2797 // abs(SHIFT) <= BIT_SIZE(I) 2798 // Return: abs(SHIFT) >= BIT_SIZE(I) 2799 // ? 0 2800 // : SHIFT < 0 2801 // ? I >> abs(SHIFT) 2802 // : I << abs(SHIFT) 2803 assert(args.size() == 2); 2804 mlir::Value bitSize = builder.createIntegerConstant( 2805 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2806 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2807 mlir::Value shift = builder.createConvert(loc, resultType, args[1]); 2808 mlir::Value absShift = genAbs(resultType, {shift}); 2809 auto left = builder.create<mlir::arith::ShLIOp>(loc, args[0], absShift); 2810 auto right = builder.create<mlir::arith::ShRUIOp>(loc, args[0], absShift); 2811 auto shiftIsLarge = builder.create<mlir::arith::CmpIOp>( 2812 loc, mlir::arith::CmpIPredicate::sge, absShift, bitSize); 2813 auto shiftIsNegative = builder.create<mlir::arith::CmpIOp>( 2814 loc, mlir::arith::CmpIPredicate::slt, shift, zero); 2815 auto sel = 2816 builder.create<mlir::arith::SelectOp>(loc, shiftIsNegative, right, left); 2817 return builder.create<mlir::arith::SelectOp>(loc, shiftIsLarge, zero, sel); 2818 } 2819 2820 // ISHFTC 2821 mlir::Value IntrinsicLibrary::genIshftc(mlir::Type resultType, 2822 llvm::ArrayRef<mlir::Value> args) { 2823 // A conformant ISHFTC(I,SHIFT,SIZE) call satisfies: 2824 // SIZE > 0 2825 // SIZE <= BIT_SIZE(I) 2826 // abs(SHIFT) <= SIZE 2827 // if SHIFT > 0 2828 // leftSize = abs(SHIFT) 2829 // rightSize = SIZE - abs(SHIFT) 2830 // else [if SHIFT < 0] 2831 // leftSize = SIZE - abs(SHIFT) 2832 // rightSize = abs(SHIFT) 2833 // unchanged = SIZE == BIT_SIZE(I) ? 0 : (I >> SIZE) << SIZE 2834 // leftMaskShift = BIT_SIZE(I) - leftSize 2835 // rightMaskShift = BIT_SIZE(I) - rightSize 2836 // left = (I >> rightSize) & (-1 >> leftMaskShift) 2837 // right = (I & (-1 >> rightMaskShift)) << leftSize 2838 // Return: SHIFT == 0 || SIZE == abs(SHIFT) ? I : (unchanged | left | right) 2839 assert(args.size() == 3); 2840 mlir::Value bitSize = builder.createIntegerConstant( 2841 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2842 mlir::Value I = args[0]; 2843 mlir::Value shift = builder.createConvert(loc, resultType, args[1]); 2844 mlir::Value size = 2845 args[2] ? builder.createConvert(loc, resultType, args[2]) : bitSize; 2846 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2847 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2848 mlir::Value absShift = genAbs(resultType, {shift}); 2849 auto elseSize = builder.create<mlir::arith::SubIOp>(loc, size, absShift); 2850 auto shiftIsZero = builder.create<mlir::arith::CmpIOp>( 2851 loc, mlir::arith::CmpIPredicate::eq, shift, zero); 2852 auto shiftEqualsSize = builder.create<mlir::arith::CmpIOp>( 2853 loc, mlir::arith::CmpIPredicate::eq, absShift, size); 2854 auto shiftIsNop = 2855 builder.create<mlir::arith::OrIOp>(loc, shiftIsZero, shiftEqualsSize); 2856 auto shiftIsPositive = builder.create<mlir::arith::CmpIOp>( 2857 loc, mlir::arith::CmpIPredicate::sgt, shift, zero); 2858 auto leftSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive, 2859 absShift, elseSize); 2860 auto rightSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive, 2861 elseSize, absShift); 2862 auto hasUnchanged = builder.create<mlir::arith::CmpIOp>( 2863 loc, mlir::arith::CmpIPredicate::ne, size, bitSize); 2864 auto unchangedTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, I, size); 2865 auto unchangedTmp2 = 2866 builder.create<mlir::arith::ShLIOp>(loc, unchangedTmp1, size); 2867 auto unchanged = builder.create<mlir::arith::SelectOp>(loc, hasUnchanged, 2868 unchangedTmp2, zero); 2869 auto leftMaskShift = 2870 builder.create<mlir::arith::SubIOp>(loc, bitSize, leftSize); 2871 auto leftMask = 2872 builder.create<mlir::arith::ShRUIOp>(loc, ones, leftMaskShift); 2873 auto leftTmp = builder.create<mlir::arith::ShRUIOp>(loc, I, rightSize); 2874 auto left = builder.create<mlir::arith::AndIOp>(loc, leftTmp, leftMask); 2875 auto rightMaskShift = 2876 builder.create<mlir::arith::SubIOp>(loc, bitSize, rightSize); 2877 auto rightMask = 2878 builder.create<mlir::arith::ShRUIOp>(loc, ones, rightMaskShift); 2879 auto rightTmp = builder.create<mlir::arith::AndIOp>(loc, I, rightMask); 2880 auto right = builder.create<mlir::arith::ShLIOp>(loc, rightTmp, leftSize); 2881 auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, left); 2882 auto res = builder.create<mlir::arith::OrIOp>(loc, resTmp, right); 2883 return builder.create<mlir::arith::SelectOp>(loc, shiftIsNop, I, res); 2884 } 2885 2886 // LEN 2887 // Note that this is only used for an unrestricted intrinsic LEN call. 2888 // Other uses of LEN are rewritten as descriptor inquiries by the front-end. 2889 fir::ExtendedValue 2890 IntrinsicLibrary::genLen(mlir::Type resultType, 2891 llvm::ArrayRef<fir::ExtendedValue> args) { 2892 // Optional KIND argument reflected in result type and otherwise ignored. 2893 assert(args.size() == 1 || args.size() == 2); 2894 mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]); 2895 return builder.createConvert(loc, resultType, len); 2896 } 2897 2898 // LEN_TRIM 2899 fir::ExtendedValue 2900 IntrinsicLibrary::genLenTrim(mlir::Type resultType, 2901 llvm::ArrayRef<fir::ExtendedValue> args) { 2902 // Optional KIND argument reflected in result type and otherwise ignored. 2903 assert(args.size() == 1 || args.size() == 2); 2904 const fir::CharBoxValue *charBox = args[0].getCharBox(); 2905 if (!charBox) 2906 TODO(loc, "character array len_trim"); 2907 auto len = 2908 fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox); 2909 return builder.createConvert(loc, resultType, len); 2910 } 2911 2912 // LGE, LGT, LLE, LLT 2913 template <mlir::arith::CmpIPredicate pred> 2914 fir::ExtendedValue 2915 IntrinsicLibrary::genCharacterCompare(mlir::Type resultType, 2916 llvm::ArrayRef<fir::ExtendedValue> args) { 2917 assert(args.size() == 2); 2918 return fir::runtime::genCharCompare( 2919 builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]), 2920 fir::getBase(args[1]), fir::getLen(args[1])); 2921 } 2922 2923 // MATMUL 2924 fir::ExtendedValue 2925 IntrinsicLibrary::genMatmul(mlir::Type resultType, 2926 llvm::ArrayRef<fir::ExtendedValue> args) { 2927 assert(args.size() == 2); 2928 2929 // Handle required matmul arguments 2930 fir::BoxValue matrixTmpA = builder.createBox(loc, args[0]); 2931 mlir::Value matrixA = fir::getBase(matrixTmpA); 2932 fir::BoxValue matrixTmpB = builder.createBox(loc, args[1]); 2933 mlir::Value matrixB = fir::getBase(matrixTmpB); 2934 unsigned resultRank = 2935 (matrixTmpA.rank() == 1 || matrixTmpB.rank() == 1) ? 1 : 2; 2936 2937 // Create mutable fir.box to be passed to the runtime for the result. 2938 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, resultRank); 2939 fir::MutableBoxValue resultMutableBox = 2940 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2941 mlir::Value resultIrBox = 2942 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2943 // Call runtime. The runtime is allocating the result. 2944 fir::runtime::genMatmul(builder, loc, resultIrBox, matrixA, matrixB); 2945 // Read result from mutable fir.box and add it to the list of temps to be 2946 // finalized by the StatementContext. 2947 return readAndAddCleanUp(resultMutableBox, resultType, 2948 "unexpected result for MATMUL"); 2949 } 2950 2951 // MERGE 2952 fir::ExtendedValue 2953 IntrinsicLibrary::genMerge(mlir::Type, 2954 llvm::ArrayRef<fir::ExtendedValue> args) { 2955 assert(args.size() == 3); 2956 mlir::Value tsource = fir::getBase(args[0]); 2957 mlir::Value fsource = fir::getBase(args[1]); 2958 mlir::Value rawMask = fir::getBase(args[2]); 2959 mlir::Type type0 = fir::unwrapRefType(tsource.getType()); 2960 bool isCharRslt = fir::isa_char(type0); // result is same as first argument 2961 mlir::Value mask = builder.createConvert(loc, builder.getI1Type(), rawMask); 2962 // FSOURCE has the same type as TSOURCE, but they may not have the same MLIR 2963 // types (one can have dynamic length while the other has constant lengths, 2964 // or one may be a fir.logical<> while the other is an i1). Insert a cast to 2965 // fulfill mlir::SelectOp constraint that the MLIR types must be the same. 2966 mlir::Value fsourceCast = 2967 builder.createConvert(loc, tsource.getType(), fsource); 2968 auto rslt = 2969 builder.create<mlir::arith::SelectOp>(loc, mask, tsource, fsourceCast); 2970 if (isCharRslt) { 2971 // Need a CharBoxValue for character results 2972 const fir::CharBoxValue *charBox = args[0].getCharBox(); 2973 fir::CharBoxValue charRslt(rslt, charBox->getLen()); 2974 return charRslt; 2975 } 2976 return rslt; 2977 } 2978 2979 // MOD 2980 mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType, 2981 llvm::ArrayRef<mlir::Value> args) { 2982 assert(args.size() == 2); 2983 if (resultType.isa<mlir::IntegerType>()) 2984 return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]); 2985 2986 // Use runtime. Note that mlir::arith::RemFOp implements floating point 2987 // remainder, but it does not work with fir::Real type. 2988 // TODO: consider using mlir::arith::RemFOp when possible, that may help 2989 // folding and optimizations. 2990 return genRuntimeCall("mod", resultType, args); 2991 } 2992 2993 // MODULO 2994 mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType, 2995 llvm::ArrayRef<mlir::Value> args) { 2996 assert(args.size() == 2); 2997 // No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR. 2998 // In the meantime, use a simple inlined implementation based on truncated 2999 // modulo (MOD(A, P) implemented by RemIOp, RemFOp). This avoids making manual 3000 // division and multiplication from MODULO formula. 3001 // - If A/P > 0 or MOD(A,P)=0, then INT(A/P) = FLOOR(A/P), and MODULO = MOD. 3002 // - Otherwise, when A/P < 0 and MOD(A,P) !=0, then MODULO(A, P) = 3003 // A-FLOOR(A/P)*P = A-(INT(A/P)-1)*P = A-INT(A/P)*P+P = MOD(A,P)+P 3004 // Note that A/P < 0 if and only if A and P signs are different. 3005 if (resultType.isa<mlir::IntegerType>()) { 3006 auto remainder = 3007 builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]); 3008 auto argXor = builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]); 3009 mlir::Value zero = builder.createIntegerConstant(loc, argXor.getType(), 0); 3010 auto argSignDifferent = builder.create<mlir::arith::CmpIOp>( 3011 loc, mlir::arith::CmpIPredicate::slt, argXor, zero); 3012 auto remainderIsNotZero = builder.create<mlir::arith::CmpIOp>( 3013 loc, mlir::arith::CmpIPredicate::ne, remainder, zero); 3014 auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero, 3015 argSignDifferent); 3016 auto remPlusP = 3017 builder.create<mlir::arith::AddIOp>(loc, remainder, args[1]); 3018 return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP, 3019 remainder); 3020 } 3021 // Real case 3022 auto remainder = builder.create<mlir::arith::RemFOp>(loc, args[0], args[1]); 3023 mlir::Value zero = builder.createRealZeroConstant(loc, remainder.getType()); 3024 auto remainderIsNotZero = builder.create<mlir::arith::CmpFOp>( 3025 loc, mlir::arith::CmpFPredicate::UNE, remainder, zero); 3026 auto aLessThanZero = builder.create<mlir::arith::CmpFOp>( 3027 loc, mlir::arith::CmpFPredicate::OLT, args[0], zero); 3028 auto pLessThanZero = builder.create<mlir::arith::CmpFOp>( 3029 loc, mlir::arith::CmpFPredicate::OLT, args[1], zero); 3030 auto argSignDifferent = 3031 builder.create<mlir::arith::XOrIOp>(loc, aLessThanZero, pLessThanZero); 3032 auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero, 3033 argSignDifferent); 3034 auto remPlusP = builder.create<mlir::arith::AddFOp>(loc, remainder, args[1]); 3035 return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP, 3036 remainder); 3037 } 3038 3039 // MVBITS 3040 void IntrinsicLibrary::genMvbits(llvm::ArrayRef<fir::ExtendedValue> args) { 3041 // A conformant MVBITS(FROM,FROMPOS,LEN,TO,TOPOS) call satisfies: 3042 // FROMPOS >= 0 3043 // LEN >= 0 3044 // TOPOS >= 0 3045 // FROMPOS + LEN <= BIT_SIZE(FROM) 3046 // TOPOS + LEN <= BIT_SIZE(TO) 3047 // MASK = -1 >> (BIT_SIZE(FROM) - LEN) 3048 // TO = LEN == 0 ? TO : ((!(MASK << TOPOS)) & TO) | 3049 // (((FROM >> FROMPOS) & MASK) << TOPOS) 3050 assert(args.size() == 5); 3051 auto unbox = [&](fir::ExtendedValue exv) { 3052 const mlir::Value *arg = exv.getUnboxed(); 3053 assert(arg && "nonscalar mvbits argument"); 3054 return *arg; 3055 }; 3056 mlir::Value from = unbox(args[0]); 3057 mlir::Type resultType = from.getType(); 3058 mlir::Value frompos = builder.createConvert(loc, resultType, unbox(args[1])); 3059 mlir::Value len = builder.createConvert(loc, resultType, unbox(args[2])); 3060 mlir::Value toAddr = unbox(args[3]); 3061 assert(fir::dyn_cast_ptrEleTy(toAddr.getType()) == resultType && 3062 "mismatched mvbits types"); 3063 auto to = builder.create<fir::LoadOp>(loc, resultType, toAddr); 3064 mlir::Value topos = builder.createConvert(loc, resultType, unbox(args[4])); 3065 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 3066 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 3067 mlir::Value bitSize = builder.createIntegerConstant( 3068 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 3069 auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len); 3070 auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount); 3071 auto unchangedTmp1 = builder.create<mlir::arith::ShLIOp>(loc, mask, topos); 3072 auto unchangedTmp2 = 3073 builder.create<mlir::arith::XOrIOp>(loc, unchangedTmp1, ones); 3074 auto unchanged = builder.create<mlir::arith::AndIOp>(loc, unchangedTmp2, to); 3075 auto frombitsTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, from, frompos); 3076 auto frombitsTmp2 = 3077 builder.create<mlir::arith::AndIOp>(loc, frombitsTmp1, mask); 3078 auto frombits = builder.create<mlir::arith::ShLIOp>(loc, frombitsTmp2, topos); 3079 auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, frombits); 3080 auto lenIsZero = builder.create<mlir::arith::CmpIOp>( 3081 loc, mlir::arith::CmpIPredicate::eq, len, zero); 3082 auto res = builder.create<mlir::arith::SelectOp>(loc, lenIsZero, to, resTmp); 3083 builder.create<fir::StoreOp>(loc, res, toAddr); 3084 } 3085 3086 // NEAREST 3087 mlir::Value IntrinsicLibrary::genNearest(mlir::Type resultType, 3088 llvm::ArrayRef<mlir::Value> args) { 3089 assert(args.size() == 2); 3090 3091 mlir::Value realX = fir::getBase(args[0]); 3092 mlir::Value realS = fir::getBase(args[1]); 3093 3094 return builder.createConvert( 3095 loc, resultType, fir::runtime::genNearest(builder, loc, realX, realS)); 3096 } 3097 3098 // NINT 3099 mlir::Value IntrinsicLibrary::genNint(mlir::Type resultType, 3100 llvm::ArrayRef<mlir::Value> args) { 3101 assert(args.size() >= 1); 3102 // Skip optional kind argument to search the runtime; it is already reflected 3103 // in result type. 3104 return genRuntimeCall("nint", resultType, {args[0]}); 3105 } 3106 3107 // NOT 3108 mlir::Value IntrinsicLibrary::genNot(mlir::Type resultType, 3109 llvm::ArrayRef<mlir::Value> args) { 3110 assert(args.size() == 1); 3111 mlir::Value allOnes = builder.createIntegerConstant(loc, resultType, -1); 3112 return builder.create<mlir::arith::XOrIOp>(loc, args[0], allOnes); 3113 } 3114 3115 // NULL 3116 fir::ExtendedValue 3117 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) { 3118 // NULL() without MOLD must be handled in the contexts where it can appear 3119 // (see table 16.5 of Fortran 2018 standard). 3120 assert(args.size() == 1 && isStaticallyPresent(args[0]) && 3121 "MOLD argument required to lower NULL outside of any context"); 3122 const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>(); 3123 assert(mold && "MOLD must be a pointer or allocatable"); 3124 fir::BoxType boxType = mold->getBoxTy(); 3125 mlir::Value boxStorage = builder.createTemporary(loc, boxType); 3126 mlir::Value box = fir::factory::createUnallocatedBox( 3127 builder, loc, boxType, mold->nonDeferredLenParams()); 3128 builder.create<fir::StoreOp>(loc, box, boxStorage); 3129 return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {}); 3130 } 3131 3132 // PACK 3133 fir::ExtendedValue 3134 IntrinsicLibrary::genPack(mlir::Type resultType, 3135 llvm::ArrayRef<fir::ExtendedValue> args) { 3136 [[maybe_unused]] auto numArgs = args.size(); 3137 assert(numArgs == 2 || numArgs == 3); 3138 3139 // Handle required array argument 3140 mlir::Value array = builder.createBox(loc, args[0]); 3141 3142 // Handle required mask argument 3143 mlir::Value mask = builder.createBox(loc, args[1]); 3144 3145 // Handle optional vector argument 3146 mlir::Value vector = isStaticallyAbsent(args, 2) 3147 ? builder.create<fir::AbsentOp>( 3148 loc, fir::BoxType::get(builder.getI1Type())) 3149 : builder.createBox(loc, args[2]); 3150 3151 // Create mutable fir.box to be passed to the runtime for the result. 3152 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 1); 3153 fir::MutableBoxValue resultMutableBox = 3154 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 3155 mlir::Value resultIrBox = 3156 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3157 3158 fir::runtime::genPack(builder, loc, resultIrBox, array, mask, vector); 3159 3160 return readAndAddCleanUp(resultMutableBox, resultType, 3161 "unexpected result for PACK"); 3162 } 3163 3164 // PRESENT 3165 fir::ExtendedValue 3166 IntrinsicLibrary::genPresent(mlir::Type, 3167 llvm::ArrayRef<fir::ExtendedValue> args) { 3168 assert(args.size() == 1); 3169 return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 3170 fir::getBase(args[0])); 3171 } 3172 3173 // PRODUCT 3174 fir::ExtendedValue 3175 IntrinsicLibrary::genProduct(mlir::Type resultType, 3176 llvm::ArrayRef<fir::ExtendedValue> args) { 3177 return genProdOrSum(fir::runtime::genProduct, fir::runtime::genProductDim, 3178 resultType, builder, loc, stmtCtx, 3179 "unexpected result for Product", args); 3180 } 3181 3182 // RANDOM_INIT 3183 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) { 3184 assert(args.size() == 2); 3185 Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]), 3186 fir::getBase(args[1])); 3187 } 3188 3189 // RANDOM_NUMBER 3190 void IntrinsicLibrary::genRandomNumber( 3191 llvm::ArrayRef<fir::ExtendedValue> args) { 3192 assert(args.size() == 1); 3193 Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0])); 3194 } 3195 3196 // RANDOM_SEED 3197 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) { 3198 assert(args.size() == 3); 3199 for (int i = 0; i < 3; ++i) 3200 if (isStaticallyPresent(args[i])) { 3201 Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i])); 3202 return; 3203 } 3204 Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{}); 3205 } 3206 3207 // REPEAT 3208 fir::ExtendedValue 3209 IntrinsicLibrary::genRepeat(mlir::Type resultType, 3210 llvm::ArrayRef<fir::ExtendedValue> args) { 3211 assert(args.size() == 2); 3212 mlir::Value string = builder.createBox(loc, args[0]); 3213 mlir::Value ncopies = fir::getBase(args[1]); 3214 // Create mutable fir.box to be passed to the runtime for the result. 3215 fir::MutableBoxValue resultMutableBox = 3216 fir::factory::createTempMutableBox(builder, loc, resultType); 3217 mlir::Value resultIrBox = 3218 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3219 // Call runtime. The runtime is allocating the result. 3220 fir::runtime::genRepeat(builder, loc, resultIrBox, string, ncopies); 3221 // Read result from mutable fir.box and add it to the list of temps to be 3222 // finalized by the StatementContext. 3223 return readAndAddCleanUp(resultMutableBox, resultType, "REPEAT"); 3224 } 3225 3226 // RESHAPE 3227 fir::ExtendedValue 3228 IntrinsicLibrary::genReshape(mlir::Type resultType, 3229 llvm::ArrayRef<fir::ExtendedValue> args) { 3230 assert(args.size() == 4); 3231 3232 // Handle source argument 3233 mlir::Value source = builder.createBox(loc, args[0]); 3234 3235 // Handle shape argument 3236 mlir::Value shape = builder.createBox(loc, args[1]); 3237 assert(fir::BoxValue(shape).rank() == 1); 3238 mlir::Type shapeTy = shape.getType(); 3239 mlir::Type shapeArrTy = fir::dyn_cast_ptrOrBoxEleTy(shapeTy); 3240 auto resultRank = shapeArrTy.cast<fir::SequenceType>().getShape(); 3241 3242 assert(resultRank[0] != fir::SequenceType::getUnknownExtent() && 3243 "shape arg must have constant size"); 3244 3245 // Handle optional pad argument 3246 mlir::Value pad = isStaticallyAbsent(args[2]) 3247 ? builder.create<fir::AbsentOp>( 3248 loc, fir::BoxType::get(builder.getI1Type())) 3249 : builder.createBox(loc, args[2]); 3250 3251 // Handle optional order argument 3252 mlir::Value order = isStaticallyAbsent(args[3]) 3253 ? builder.create<fir::AbsentOp>( 3254 loc, fir::BoxType::get(builder.getI1Type())) 3255 : builder.createBox(loc, args[3]); 3256 3257 // Create mutable fir.box to be passed to the runtime for the result. 3258 mlir::Type type = builder.getVarLenSeqTy(resultType, resultRank[0]); 3259 fir::MutableBoxValue resultMutableBox = 3260 fir::factory::createTempMutableBox(builder, loc, type); 3261 3262 mlir::Value resultIrBox = 3263 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3264 3265 fir::runtime::genReshape(builder, loc, resultIrBox, source, shape, pad, 3266 order); 3267 3268 return readAndAddCleanUp(resultMutableBox, resultType, 3269 "unexpected result for RESHAPE"); 3270 } 3271 3272 // RRSPACING 3273 mlir::Value IntrinsicLibrary::genRRSpacing(mlir::Type resultType, 3274 llvm::ArrayRef<mlir::Value> args) { 3275 assert(args.size() == 1); 3276 3277 return builder.createConvert( 3278 loc, resultType, 3279 fir::runtime::genRRSpacing(builder, loc, fir::getBase(args[0]))); 3280 } 3281 3282 // SCALE 3283 mlir::Value IntrinsicLibrary::genScale(mlir::Type resultType, 3284 llvm::ArrayRef<mlir::Value> args) { 3285 assert(args.size() == 2); 3286 3287 mlir::Value realX = fir::getBase(args[0]); 3288 mlir::Value intI = fir::getBase(args[1]); 3289 3290 return builder.createConvert( 3291 loc, resultType, fir::runtime::genScale(builder, loc, realX, intI)); 3292 } 3293 3294 // SCAN 3295 fir::ExtendedValue 3296 IntrinsicLibrary::genScan(mlir::Type resultType, 3297 llvm::ArrayRef<fir::ExtendedValue> args) { 3298 3299 assert(args.size() == 4); 3300 3301 if (isStaticallyAbsent(args[3])) { 3302 // Kind not specified, so call scan/verify runtime routine that is 3303 // specialized on the kind of characters in string. 3304 3305 // Handle required string base arg 3306 mlir::Value stringBase = fir::getBase(args[0]); 3307 3308 // Handle required set string base arg 3309 mlir::Value setBase = fir::getBase(args[1]); 3310 3311 // Handle kind argument; it is the kind of character in this case 3312 fir::KindTy kind = 3313 fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind( 3314 stringBase.getType()); 3315 3316 // Get string length argument 3317 mlir::Value stringLen = fir::getLen(args[0]); 3318 3319 // Get set string length argument 3320 mlir::Value setLen = fir::getLen(args[1]); 3321 3322 // Handle optional back argument 3323 mlir::Value back = 3324 isStaticallyAbsent(args[2]) 3325 ? builder.createIntegerConstant(loc, builder.getI1Type(), 0) 3326 : fir::getBase(args[2]); 3327 3328 return builder.createConvert(loc, resultType, 3329 fir::runtime::genScan(builder, loc, kind, 3330 stringBase, stringLen, 3331 setBase, setLen, back)); 3332 } 3333 // else use the runtime descriptor version of scan/verify 3334 3335 // Handle optional argument, back 3336 auto makeRefThenEmbox = [&](mlir::Value b) { 3337 fir::LogicalType logTy = fir::LogicalType::get( 3338 builder.getContext(), builder.getKindMap().defaultLogicalKind()); 3339 mlir::Value temp = builder.createTemporary(loc, logTy); 3340 mlir::Value castb = builder.createConvert(loc, logTy, b); 3341 builder.create<fir::StoreOp>(loc, castb, temp); 3342 return builder.createBox(loc, temp); 3343 }; 3344 mlir::Value back = fir::isUnboxedValue(args[2]) 3345 ? makeRefThenEmbox(*args[2].getUnboxed()) 3346 : builder.create<fir::AbsentOp>( 3347 loc, fir::BoxType::get(builder.getI1Type())); 3348 3349 // Handle required string argument 3350 mlir::Value string = builder.createBox(loc, args[0]); 3351 3352 // Handle required set argument 3353 mlir::Value set = builder.createBox(loc, args[1]); 3354 3355 // Handle kind argument 3356 mlir::Value kind = fir::getBase(args[3]); 3357 3358 // Create result descriptor 3359 fir::MutableBoxValue resultMutableBox = 3360 fir::factory::createTempMutableBox(builder, loc, resultType); 3361 mlir::Value resultIrBox = 3362 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3363 3364 fir::runtime::genScanDescriptor(builder, loc, resultIrBox, string, set, back, 3365 kind); 3366 3367 // Handle cleanup of allocatable result descriptor and return 3368 return readAndAddCleanUp(resultMutableBox, resultType, "SCAN"); 3369 } 3370 3371 // SET_EXPONENT 3372 mlir::Value IntrinsicLibrary::genSetExponent(mlir::Type resultType, 3373 llvm::ArrayRef<mlir::Value> args) { 3374 assert(args.size() == 2); 3375 3376 return builder.createConvert( 3377 loc, resultType, 3378 fir::runtime::genSetExponent(builder, loc, fir::getBase(args[0]), 3379 fir::getBase(args[1]))); 3380 } 3381 3382 // SIGN 3383 mlir::Value IntrinsicLibrary::genSign(mlir::Type resultType, 3384 llvm::ArrayRef<mlir::Value> args) { 3385 assert(args.size() == 2); 3386 if (resultType.isa<mlir::IntegerType>()) { 3387 mlir::Value abs = genAbs(resultType, {args[0]}); 3388 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 3389 auto neg = builder.create<mlir::arith::SubIOp>(loc, zero, abs); 3390 auto cmp = builder.create<mlir::arith::CmpIOp>( 3391 loc, mlir::arith::CmpIPredicate::slt, args[1], zero); 3392 return builder.create<mlir::arith::SelectOp>(loc, cmp, neg, abs); 3393 } 3394 return genRuntimeCall("sign", resultType, args); 3395 } 3396 3397 // SIZE 3398 fir::ExtendedValue 3399 IntrinsicLibrary::genSize(mlir::Type resultType, 3400 llvm::ArrayRef<fir::ExtendedValue> args) { 3401 // Note that the value of the KIND argument is already reflected in the 3402 // resultType 3403 assert(args.size() == 3); 3404 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 3405 if (boxValue->hasAssumedRank()) 3406 TODO(loc, "SIZE intrinsic with assumed rank argument"); 3407 3408 // Get the ARRAY argument 3409 mlir::Value array = builder.createBox(loc, args[0]); 3410 3411 // The front-end rewrites SIZE without the DIM argument to 3412 // an array of SIZE with DIM in most cases, but it may not be 3413 // possible in some cases like when in SIZE(function_call()). 3414 if (isStaticallyAbsent(args, 1)) 3415 return builder.createConvert(loc, resultType, 3416 fir::runtime::genSize(builder, loc, array)); 3417 3418 // Get the DIM argument. 3419 mlir::Value dim = fir::getBase(args[1]); 3420 if (!fir::isa_ref_type(dim.getType())) 3421 return builder.createConvert( 3422 loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim)); 3423 3424 mlir::Value isDynamicallyAbsent = builder.genIsNullAddr(loc, dim); 3425 return builder 3426 .genIfOp(loc, {resultType}, isDynamicallyAbsent, 3427 /*withElseRegion=*/true) 3428 .genThen([&]() { 3429 mlir::Value size = builder.createConvert( 3430 loc, resultType, fir::runtime::genSize(builder, loc, array)); 3431 builder.create<fir::ResultOp>(loc, size); 3432 }) 3433 .genElse([&]() { 3434 mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim); 3435 mlir::Value size = builder.createConvert( 3436 loc, resultType, 3437 fir::runtime::genSizeDim(builder, loc, array, dimValue)); 3438 builder.create<fir::ResultOp>(loc, size); 3439 }) 3440 .getResults()[0]; 3441 } 3442 3443 static bool hasDefaultLowerBound(const fir::ExtendedValue &exv) { 3444 return exv.match( 3445 [](const fir::ArrayBoxValue &arr) { return arr.getLBounds().empty(); }, 3446 [](const fir::CharArrayBoxValue &arr) { 3447 return arr.getLBounds().empty(); 3448 }, 3449 [](const fir::BoxValue &arr) { return arr.getLBounds().empty(); }, 3450 [](const auto &) { return false; }); 3451 } 3452 3453 /// Compute the lower bound in dimension \p dim (zero based) of \p array 3454 /// taking care of returning one when the related extent is zero. 3455 static mlir::Value computeLBOUND(fir::FirOpBuilder &builder, mlir::Location loc, 3456 const fir::ExtendedValue &array, unsigned dim, 3457 mlir::Value zero, mlir::Value one) { 3458 assert(dim < array.rank() && "invalid dimension"); 3459 if (hasDefaultLowerBound(array)) 3460 return one; 3461 mlir::Value lb = fir::factory::readLowerBound(builder, loc, array, dim, one); 3462 if (dim + 1 == array.rank() && array.isAssumedSize()) 3463 return lb; 3464 mlir::Value extent = fir::factory::readExtent(builder, loc, array, dim); 3465 zero = builder.createConvert(loc, extent.getType(), zero); 3466 auto dimIsEmpty = builder.create<mlir::arith::CmpIOp>( 3467 loc, mlir::arith::CmpIPredicate::eq, extent, zero); 3468 one = builder.createConvert(loc, lb.getType(), one); 3469 return builder.create<mlir::arith::SelectOp>(loc, dimIsEmpty, one, lb); 3470 } 3471 3472 /// Create a fir.box to be passed to the LBOUND runtime. 3473 /// This ensure that local lower bounds of assumed shape are propagated and that 3474 /// a fir.box with equivalent LBOUNDs but an explicit shape is created for 3475 /// assumed size arrays to avoid undefined behaviors in codegen or the runtime. 3476 static mlir::Value createBoxForLBOUND(mlir::Location loc, 3477 fir::FirOpBuilder &builder, 3478 const fir::ExtendedValue &array) { 3479 if (!array.isAssumedSize()) 3480 return array.match( 3481 [&](const fir::BoxValue &boxValue) -> mlir::Value { 3482 // This entity is mapped to a fir.box that may not contain the local 3483 // lower bound information if it is a dummy. Rebox it with the local 3484 // shape information. 3485 mlir::Value localShape = builder.createShape(loc, array); 3486 mlir::Value oldBox = boxValue.getAddr(); 3487 return builder.create<fir::ReboxOp>(loc, oldBox.getType(), oldBox, 3488 localShape, 3489 /*slice=*/mlir::Value{}); 3490 }, 3491 [&](const auto &) -> mlir::Value { 3492 // This a pointer/allocatable, or an entity not yet tracked with a 3493 // fir.box. For pointer/allocatable, createBox will forward the 3494 // descriptor that contains the correct lower bound information. For 3495 // other entities, a new fir.box will be made with the local lower 3496 // bounds. 3497 return builder.createBox(loc, array); 3498 }); 3499 // Assumed sized are not meant to be emboxed. This could cause the undefined 3500 // extent cannot safely be understood by the runtime/codegen that will 3501 // consider that the dimension is empty and that the related LBOUND value must 3502 // be one. Pretend that the related extent is one to get the correct LBOUND 3503 // value. 3504 llvm::SmallVector<mlir::Value> shape = 3505 fir::factory::getExtents(loc, builder, array); 3506 assert(!shape.empty() && "assumed size must have at least one dimension"); 3507 shape.back() = builder.createIntegerConstant(loc, builder.getIndexType(), 1); 3508 auto safeToEmbox = array.match( 3509 [&](const fir::CharArrayBoxValue &x) -> fir::ExtendedValue { 3510 return fir::CharArrayBoxValue{x.getAddr(), x.getLen(), shape, 3511 x.getLBounds()}; 3512 }, 3513 [&](const fir::ArrayBoxValue &x) -> fir::ExtendedValue { 3514 return fir::ArrayBoxValue{x.getAddr(), shape, x.getLBounds()}; 3515 }, 3516 [&](const auto &) -> fir::ExtendedValue { 3517 fir::emitFatalError(loc, "not an assumed size array"); 3518 }); 3519 return builder.createBox(loc, safeToEmbox); 3520 } 3521 3522 // LBOUND 3523 fir::ExtendedValue 3524 IntrinsicLibrary::genLbound(mlir::Type resultType, 3525 llvm::ArrayRef<fir::ExtendedValue> args) { 3526 assert(args.size() == 2 || args.size() == 3); 3527 const fir::ExtendedValue &array = args[0]; 3528 if (const auto *boxValue = array.getBoxOf<fir::BoxValue>()) 3529 if (boxValue->hasAssumedRank()) 3530 TODO(loc, "LBOUND intrinsic with assumed rank argument"); 3531 3532 //===----------------------------------------------------------------------===// 3533 mlir::Type indexType = builder.getIndexType(); 3534 3535 // Semantics builds signatures for LBOUND calls as either 3536 // LBOUND(array, dim, [kind]) or LBOUND(array, [kind]). 3537 if (args.size() == 2 || isStaticallyAbsent(args, 1)) { 3538 // DIM is absent. 3539 mlir::Type lbType = fir::unwrapSequenceType(resultType); 3540 unsigned rank = array.rank(); 3541 mlir::Type lbArrayType = fir::SequenceType::get( 3542 {static_cast<fir::SequenceType::Extent>(array.rank())}, lbType); 3543 mlir::Value lbArray = builder.createTemporary(loc, lbArrayType); 3544 mlir::Type lbAddrType = builder.getRefType(lbType); 3545 mlir::Value one = builder.createIntegerConstant(loc, lbType, 1); 3546 mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0); 3547 for (unsigned dim = 0; dim < rank; ++dim) { 3548 mlir::Value lb = computeLBOUND(builder, loc, array, dim, zero, one); 3549 lb = builder.createConvert(loc, lbType, lb); 3550 auto index = builder.createIntegerConstant(loc, indexType, dim); 3551 auto lbAddr = 3552 builder.create<fir::CoordinateOp>(loc, lbAddrType, lbArray, index); 3553 builder.create<fir::StoreOp>(loc, lb, lbAddr); 3554 } 3555 mlir::Value lbArrayExtent = 3556 builder.createIntegerConstant(loc, indexType, rank); 3557 llvm::SmallVector<mlir::Value> extents{lbArrayExtent}; 3558 return fir::ArrayBoxValue{lbArray, extents}; 3559 } 3560 // DIM is present. 3561 mlir::Value dim = fir::getBase(args[1]); 3562 3563 // If it is a compile time constant, skip the runtime call. 3564 if (llvm::Optional<std::int64_t> cstDim = 3565 fir::factory::getIntIfConstant(dim)) { 3566 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 3567 mlir::Value zero = builder.createIntegerConstant(loc, indexType, 0); 3568 mlir::Value lb = computeLBOUND(builder, loc, array, *cstDim - 1, zero, one); 3569 return builder.createConvert(loc, resultType, lb); 3570 } 3571 3572 fir::ExtendedValue box = createBoxForLBOUND(loc, builder, array); 3573 return builder.createConvert( 3574 loc, resultType, 3575 fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim)); 3576 } 3577 3578 // UBOUND 3579 fir::ExtendedValue 3580 IntrinsicLibrary::genUbound(mlir::Type resultType, 3581 llvm::ArrayRef<fir::ExtendedValue> args) { 3582 assert(args.size() == 3 || args.size() == 2); 3583 if (args.size() == 3) { 3584 // Handle calls to UBOUND with the DIM argument, which return a scalar 3585 mlir::Value extent = fir::getBase(genSize(resultType, args)); 3586 mlir::Value lbound = fir::getBase(genLbound(resultType, args)); 3587 3588 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 3589 mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one); 3590 return builder.create<mlir::arith::AddIOp>(loc, ubound, extent); 3591 } else { 3592 // Handle calls to UBOUND without the DIM argument, which return an array 3593 mlir::Value kind = isStaticallyAbsent(args[1]) 3594 ? builder.createIntegerConstant( 3595 loc, builder.getIndexType(), 3596 builder.getKindMap().defaultIntegerKind()) 3597 : fir::getBase(args[1]); 3598 3599 // Create mutable fir.box to be passed to the runtime for the result. 3600 mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1); 3601 fir::MutableBoxValue resultMutableBox = 3602 fir::factory::createTempMutableBox(builder, loc, type); 3603 mlir::Value resultIrBox = 3604 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3605 3606 fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]), 3607 kind); 3608 3609 return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND"); 3610 } 3611 return mlir::Value(); 3612 } 3613 3614 // SPACING 3615 mlir::Value IntrinsicLibrary::genSpacing(mlir::Type resultType, 3616 llvm::ArrayRef<mlir::Value> args) { 3617 assert(args.size() == 1); 3618 3619 return builder.createConvert( 3620 loc, resultType, 3621 fir::runtime::genSpacing(builder, loc, fir::getBase(args[0]))); 3622 } 3623 3624 // SPREAD 3625 fir::ExtendedValue 3626 IntrinsicLibrary::genSpread(mlir::Type resultType, 3627 llvm::ArrayRef<fir::ExtendedValue> args) { 3628 3629 assert(args.size() == 3); 3630 3631 // Handle source argument 3632 mlir::Value source = builder.createBox(loc, args[0]); 3633 fir::BoxValue sourceTmp = source; 3634 unsigned sourceRank = sourceTmp.rank(); 3635 3636 // Handle Dim argument 3637 mlir::Value dim = fir::getBase(args[1]); 3638 3639 // Handle ncopies argument 3640 mlir::Value ncopies = fir::getBase(args[2]); 3641 3642 // Generate result descriptor 3643 mlir::Type resultArrayType = 3644 builder.getVarLenSeqTy(resultType, sourceRank + 1); 3645 fir::MutableBoxValue resultMutableBox = 3646 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 3647 mlir::Value resultIrBox = 3648 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3649 3650 fir::runtime::genSpread(builder, loc, resultIrBox, source, dim, ncopies); 3651 3652 return readAndAddCleanUp(resultMutableBox, resultType, 3653 "unexpected result for SPREAD"); 3654 } 3655 3656 // SUM 3657 fir::ExtendedValue 3658 IntrinsicLibrary::genSum(mlir::Type resultType, 3659 llvm::ArrayRef<fir::ExtendedValue> args) { 3660 return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType, 3661 builder, loc, stmtCtx, "unexpected result for Sum", args); 3662 } 3663 3664 // SYSTEM_CLOCK 3665 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) { 3666 assert(args.size() == 3); 3667 Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]), 3668 fir::getBase(args[1]), fir::getBase(args[2])); 3669 } 3670 3671 // TRANSFER 3672 fir::ExtendedValue 3673 IntrinsicLibrary::genTransfer(mlir::Type resultType, 3674 llvm::ArrayRef<fir::ExtendedValue> args) { 3675 3676 assert(args.size() >= 2); // args.size() == 2 when size argument is omitted. 3677 3678 // Handle source argument 3679 mlir::Value source = builder.createBox(loc, args[0]); 3680 3681 // Handle mold argument 3682 mlir::Value mold = builder.createBox(loc, args[1]); 3683 fir::BoxValue moldTmp = mold; 3684 unsigned moldRank = moldTmp.rank(); 3685 3686 bool absentSize = (args.size() == 2); 3687 3688 // Create mutable fir.box to be passed to the runtime for the result. 3689 mlir::Type type = (moldRank == 0 && absentSize) 3690 ? resultType 3691 : builder.getVarLenSeqTy(resultType, 1); 3692 fir::MutableBoxValue resultMutableBox = 3693 fir::factory::createTempMutableBox(builder, loc, type); 3694 3695 if (moldRank == 0 && absentSize) { 3696 // This result is a scalar in this case. 3697 mlir::Value resultIrBox = 3698 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3699 3700 Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold); 3701 } else { 3702 // The result is a rank one array in this case. 3703 mlir::Value resultIrBox = 3704 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3705 3706 if (absentSize) { 3707 Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold); 3708 } else { 3709 mlir::Value sizeArg = fir::getBase(args[2]); 3710 Fortran::lower::genTransferSize(builder, loc, resultIrBox, source, mold, 3711 sizeArg); 3712 } 3713 } 3714 return readAndAddCleanUp(resultMutableBox, resultType, 3715 "unexpected result for TRANSFER"); 3716 } 3717 3718 // TRANSPOSE 3719 fir::ExtendedValue 3720 IntrinsicLibrary::genTranspose(mlir::Type resultType, 3721 llvm::ArrayRef<fir::ExtendedValue> args) { 3722 3723 assert(args.size() == 1); 3724 3725 // Handle source argument 3726 mlir::Value source = builder.createBox(loc, args[0]); 3727 3728 // Create mutable fir.box to be passed to the runtime for the result. 3729 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 2); 3730 fir::MutableBoxValue resultMutableBox = 3731 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 3732 mlir::Value resultIrBox = 3733 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3734 // Call runtime. The runtime is allocating the result. 3735 fir::runtime::genTranspose(builder, loc, resultIrBox, source); 3736 // Read result from mutable fir.box and add it to the list of temps to be 3737 // finalized by the StatementContext. 3738 return readAndAddCleanUp(resultMutableBox, resultType, 3739 "unexpected result for TRANSPOSE"); 3740 } 3741 3742 // TRIM 3743 fir::ExtendedValue 3744 IntrinsicLibrary::genTrim(mlir::Type resultType, 3745 llvm::ArrayRef<fir::ExtendedValue> args) { 3746 assert(args.size() == 1); 3747 mlir::Value string = builder.createBox(loc, args[0]); 3748 // Create mutable fir.box to be passed to the runtime for the result. 3749 fir::MutableBoxValue resultMutableBox = 3750 fir::factory::createTempMutableBox(builder, loc, resultType); 3751 mlir::Value resultIrBox = 3752 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3753 // Call runtime. The runtime is allocating the result. 3754 fir::runtime::genTrim(builder, loc, resultIrBox, string); 3755 // Read result from mutable fir.box and add it to the list of temps to be 3756 // finalized by the StatementContext. 3757 return readAndAddCleanUp(resultMutableBox, resultType, "TRIM"); 3758 } 3759 3760 // Compare two FIR values and return boolean result as i1. 3761 template <Extremum extremum, ExtremumBehavior behavior> 3762 static mlir::Value createExtremumCompare(mlir::Location loc, 3763 fir::FirOpBuilder &builder, 3764 mlir::Value left, mlir::Value right) { 3765 static constexpr mlir::arith::CmpIPredicate integerPredicate = 3766 extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt 3767 : mlir::arith::CmpIPredicate::slt; 3768 static constexpr mlir::arith::CmpFPredicate orderedCmp = 3769 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT 3770 : mlir::arith::CmpFPredicate::OLT; 3771 mlir::Type type = left.getType(); 3772 mlir::Value result; 3773 if (fir::isa_real(type)) { 3774 // Note: the signaling/quit aspect of the result required by IEEE 3775 // cannot currently be obtained with LLVM without ad-hoc runtime. 3776 if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { 3777 // Return the number if one of the inputs is NaN and the other is 3778 // a number. 3779 auto leftIsResult = 3780 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 3781 auto rightIsNan = builder.create<mlir::arith::CmpFOp>( 3782 loc, mlir::arith::CmpFPredicate::UNE, right, right); 3783 result = 3784 builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan); 3785 } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { 3786 // Always return NaNs if one the input is NaNs 3787 auto leftIsResult = 3788 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 3789 auto leftIsNan = builder.create<mlir::arith::CmpFOp>( 3790 loc, mlir::arith::CmpFPredicate::UNE, left, left); 3791 result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan); 3792 } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { 3793 // If the left is a NaN, return the right whatever it is. 3794 result = 3795 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 3796 } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { 3797 // If one of the operand is a NaN, return left whatever it is. 3798 static constexpr auto unorderedCmp = 3799 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT 3800 : mlir::arith::CmpFPredicate::ULT; 3801 result = 3802 builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right); 3803 } else { 3804 // TODO: ieeeMinNum/ieeeMaxNum 3805 static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, 3806 "ieeeMinNum/ieeeMaxNum behavior not implemented"); 3807 } 3808 } else if (fir::isa_integer(type)) { 3809 result = 3810 builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right); 3811 } else if (fir::isa_char(type)) { 3812 // TODO: ! character min and max is tricky because the result 3813 // length is the length of the longest argument! 3814 // So we may need a temp. 3815 TODO(loc, "CHARACTER min and max"); 3816 } 3817 assert(result && "result must be defined"); 3818 return result; 3819 } 3820 3821 // UNPACK 3822 fir::ExtendedValue 3823 IntrinsicLibrary::genUnpack(mlir::Type resultType, 3824 llvm::ArrayRef<fir::ExtendedValue> args) { 3825 assert(args.size() == 3); 3826 3827 // Handle required vector argument 3828 mlir::Value vector = builder.createBox(loc, args[0]); 3829 3830 // Handle required mask argument 3831 fir::BoxValue maskBox = builder.createBox(loc, args[1]); 3832 mlir::Value mask = fir::getBase(maskBox); 3833 unsigned maskRank = maskBox.rank(); 3834 3835 // Handle required field argument 3836 mlir::Value field = builder.createBox(loc, args[2]); 3837 3838 // Create mutable fir.box to be passed to the runtime for the result. 3839 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, maskRank); 3840 fir::MutableBoxValue resultMutableBox = 3841 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 3842 mlir::Value resultIrBox = 3843 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3844 3845 fir::runtime::genUnpack(builder, loc, resultIrBox, vector, mask, field); 3846 3847 return readAndAddCleanUp(resultMutableBox, resultType, 3848 "unexpected result for UNPACK"); 3849 } 3850 3851 // VERIFY 3852 fir::ExtendedValue 3853 IntrinsicLibrary::genVerify(mlir::Type resultType, 3854 llvm::ArrayRef<fir::ExtendedValue> args) { 3855 3856 assert(args.size() == 4); 3857 3858 if (isStaticallyAbsent(args[3])) { 3859 // Kind not specified, so call scan/verify runtime routine that is 3860 // specialized on the kind of characters in string. 3861 3862 // Handle required string base arg 3863 mlir::Value stringBase = fir::getBase(args[0]); 3864 3865 // Handle required set string base arg 3866 mlir::Value setBase = fir::getBase(args[1]); 3867 3868 // Handle kind argument; it is the kind of character in this case 3869 fir::KindTy kind = 3870 fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind( 3871 stringBase.getType()); 3872 3873 // Get string length argument 3874 mlir::Value stringLen = fir::getLen(args[0]); 3875 3876 // Get set string length argument 3877 mlir::Value setLen = fir::getLen(args[1]); 3878 3879 // Handle optional back argument 3880 mlir::Value back = 3881 isStaticallyAbsent(args[2]) 3882 ? builder.createIntegerConstant(loc, builder.getI1Type(), 0) 3883 : fir::getBase(args[2]); 3884 3885 return builder.createConvert( 3886 loc, resultType, 3887 fir::runtime::genVerify(builder, loc, kind, stringBase, stringLen, 3888 setBase, setLen, back)); 3889 } 3890 // else use the runtime descriptor version of scan/verify 3891 3892 // Handle optional argument, back 3893 auto makeRefThenEmbox = [&](mlir::Value b) { 3894 fir::LogicalType logTy = fir::LogicalType::get( 3895 builder.getContext(), builder.getKindMap().defaultLogicalKind()); 3896 mlir::Value temp = builder.createTemporary(loc, logTy); 3897 mlir::Value castb = builder.createConvert(loc, logTy, b); 3898 builder.create<fir::StoreOp>(loc, castb, temp); 3899 return builder.createBox(loc, temp); 3900 }; 3901 mlir::Value back = fir::isUnboxedValue(args[2]) 3902 ? makeRefThenEmbox(*args[2].getUnboxed()) 3903 : builder.create<fir::AbsentOp>( 3904 loc, fir::BoxType::get(builder.getI1Type())); 3905 3906 // Handle required string argument 3907 mlir::Value string = builder.createBox(loc, args[0]); 3908 3909 // Handle required set argument 3910 mlir::Value set = builder.createBox(loc, args[1]); 3911 3912 // Handle kind argument 3913 mlir::Value kind = fir::getBase(args[3]); 3914 3915 // Create result descriptor 3916 fir::MutableBoxValue resultMutableBox = 3917 fir::factory::createTempMutableBox(builder, loc, resultType); 3918 mlir::Value resultIrBox = 3919 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 3920 3921 fir::runtime::genVerifyDescriptor(builder, loc, resultIrBox, string, set, 3922 back, kind); 3923 3924 // Handle cleanup of allocatable result descriptor and return 3925 return readAndAddCleanUp(resultMutableBox, resultType, "VERIFY"); 3926 } 3927 3928 // MAXLOC 3929 fir::ExtendedValue 3930 IntrinsicLibrary::genMaxloc(mlir::Type resultType, 3931 llvm::ArrayRef<fir::ExtendedValue> args) { 3932 return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim, 3933 resultType, builder, loc, stmtCtx, 3934 "unexpected result for Maxloc", args); 3935 } 3936 3937 // MAXVAL 3938 fir::ExtendedValue 3939 IntrinsicLibrary::genMaxval(mlir::Type resultType, 3940 llvm::ArrayRef<fir::ExtendedValue> args) { 3941 return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim, 3942 fir::runtime::genMaxvalChar, resultType, builder, loc, 3943 stmtCtx, "unexpected result for Maxval", args); 3944 } 3945 3946 // MINLOC 3947 fir::ExtendedValue 3948 IntrinsicLibrary::genMinloc(mlir::Type resultType, 3949 llvm::ArrayRef<fir::ExtendedValue> args) { 3950 return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim, 3951 resultType, builder, loc, stmtCtx, 3952 "unexpected result for Minloc", args); 3953 } 3954 3955 // MINVAL 3956 fir::ExtendedValue 3957 IntrinsicLibrary::genMinval(mlir::Type resultType, 3958 llvm::ArrayRef<fir::ExtendedValue> args) { 3959 return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim, 3960 fir::runtime::genMinvalChar, resultType, builder, loc, 3961 stmtCtx, "unexpected result for Minval", args); 3962 } 3963 3964 // MIN and MAX 3965 template <Extremum extremum, ExtremumBehavior behavior> 3966 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type, 3967 llvm::ArrayRef<mlir::Value> args) { 3968 assert(args.size() >= 1); 3969 mlir::Value result = args[0]; 3970 for (auto arg : args.drop_front()) { 3971 mlir::Value mask = 3972 createExtremumCompare<extremum, behavior>(loc, builder, result, arg); 3973 result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg); 3974 } 3975 return result; 3976 } 3977 3978 //===----------------------------------------------------------------------===// 3979 // Argument lowering rules interface 3980 //===----------------------------------------------------------------------===// 3981 3982 const Fortran::lower::IntrinsicArgumentLoweringRules * 3983 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) { 3984 if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName)) 3985 if (!handler->argLoweringRules.hasDefaultRules()) 3986 return &handler->argLoweringRules; 3987 return nullptr; 3988 } 3989 3990 /// Return how argument \p argName should be lowered given the rules for the 3991 /// intrinsic function. 3992 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs( 3993 const IntrinsicArgumentLoweringRules &rules, unsigned position) { 3994 assert(position < sizeof(rules.args) / sizeof(decltype(*rules.args)) && 3995 "invalid argument"); 3996 return {rules.args[position].lowerAs, 3997 rules.args[position].handleDynamicOptional}; 3998 } 3999 4000 //===----------------------------------------------------------------------===// 4001 // Public intrinsic call helpers 4002 //===----------------------------------------------------------------------===// 4003 4004 fir::ExtendedValue 4005 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, 4006 llvm::StringRef name, 4007 llvm::Optional<mlir::Type> resultType, 4008 llvm::ArrayRef<fir::ExtendedValue> args, 4009 Fortran::lower::StatementContext &stmtCtx) { 4010 return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall( 4011 name, resultType, args); 4012 } 4013 4014 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder, 4015 mlir::Location loc, 4016 llvm::ArrayRef<mlir::Value> args) { 4017 assert(args.size() > 0 && "max requires at least one argument"); 4018 return IntrinsicLibrary{builder, loc} 4019 .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(), 4020 args); 4021 } 4022 4023 mlir::Value Fortran::lower::genMin(fir::FirOpBuilder &builder, 4024 mlir::Location loc, 4025 llvm::ArrayRef<mlir::Value> args) { 4026 assert(args.size() > 0 && "min requires at least one argument"); 4027 return IntrinsicLibrary{builder, loc} 4028 .genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>(args[0].getType(), 4029 args); 4030 } 4031 4032 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder, 4033 mlir::Location loc, mlir::Type type, 4034 mlir::Value x, mlir::Value y) { 4035 return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); 4036 } 4037 4038 mlir::SymbolRefAttr Fortran::lower::getUnrestrictedIntrinsicSymbolRefAttr( 4039 fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name, 4040 mlir::FunctionType signature) { 4041 return IntrinsicLibrary{builder, loc}.getUnrestrictedIntrinsicSymbolRefAttr( 4042 name, signature); 4043 } 4044