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