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