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