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