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