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/Lower/Todo.h" 23 #include "flang/Optimizer/Builder/Character.h" 24 #include "flang/Optimizer/Builder/Complex.h" 25 #include "flang/Optimizer/Builder/FIRBuilder.h" 26 #include "flang/Optimizer/Builder/MutableBox.h" 27 #include "flang/Optimizer/Builder/Runtime/Character.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/Transformational.h" 33 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 34 #include "flang/Optimizer/Support/FatalError.h" 35 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 36 #include "llvm/Support/CommandLine.h" 37 #include "llvm/Support/Debug.h" 38 39 #define DEBUG_TYPE "flang-lower-intrinsic" 40 41 #define PGMATH_DECLARE 42 #include "flang/Evaluate/pgmath.h.inc" 43 44 /// This file implements lowering of Fortran intrinsic procedures. 45 /// Intrinsics are lowered to a mix of FIR and MLIR operations as 46 /// well as call to runtime functions or LLVM intrinsics. 47 48 /// Lowering of intrinsic procedure calls is based on a map that associates 49 /// Fortran intrinsic generic names to FIR generator functions. 50 /// All generator functions are member functions of the IntrinsicLibrary class 51 /// and have the same interface. 52 /// If no generator is given for an intrinsic name, a math runtime library 53 /// is searched for an implementation and, if a runtime function is found, 54 /// a call is generated for it. LLVM intrinsics are handled as a math 55 /// runtime library here. 56 57 /// Enums used to templatize and share lowering of MIN and MAX. 58 enum class Extremum { Min, Max }; 59 60 // There are different ways to deal with NaNs in MIN and MAX. 61 // Known existing behaviors are listed below and can be selected for 62 // f18 MIN/MAX implementation. 63 enum class ExtremumBehavior { 64 // Note: the Signaling/quiet aspect of NaNs in the behaviors below are 65 // not described because there is no way to control/observe such aspect in 66 // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this 67 // aspect that are therefore currently not enforced. In the descriptions 68 // below, NaNs can be signaling or quite. Returned NaNs may be signaling 69 // if one of the input NaN was signaling but it cannot be guaranteed either. 70 // Existing compilers using an IEEE behavior (gfortran) also do not fulfill 71 // signaling/quiet requirements. 72 IeeeMinMaximumNumber, 73 // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): 74 // If one of the argument is and number and the other is NaN, return the 75 // number. If both arguements are NaN, return NaN. 76 // Compilers: gfortran. 77 IeeeMinMaximum, 78 // IEEE minimum/maximum behavior (754-2019, section 9.6): 79 // If one of the argument is NaN, return NaN. 80 MinMaxss, 81 // x86 minss/maxss behavior: 82 // If the second argument is a number and the other is NaN, return the number. 83 // In all other cases where at least one operand is NaN, return NaN. 84 // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. 85 PgfortranLlvm, 86 // "Opposite of" x86 minss/maxss behavior: 87 // If the first argument is a number and the other is NaN, return the 88 // number. 89 // In all other cases where at least one operand is NaN, return NaN. 90 // Compilers: xlf (only for MIN), and pgfortran (with llvm). 91 IeeeMinMaxNum 92 // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): 93 // TODO: Not implemented. 94 // It is the only behavior where the signaling/quiet aspect of a NaN argument 95 // impacts if the result should be NaN or the argument that is a number. 96 // LLVM/MLIR do not provide ways to observe this aspect, so it is not 97 // possible to implement it without some target dependent runtime. 98 }; 99 100 fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() { 101 return fir::UnboxedValue{}; 102 } 103 104 /// Test if an ExtendedValue is absent. 105 static bool isAbsent(const fir::ExtendedValue &exv) { 106 return !fir::getBase(exv); 107 } 108 static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) { 109 return args.size() <= argIndex || isAbsent(args[argIndex]); 110 } 111 112 /// Test if an ExtendedValue is present. 113 static bool isPresent(const fir::ExtendedValue &exv) { return !isAbsent(exv); } 114 115 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions that 116 /// take a DIM argument. 117 template <typename FD> 118 static fir::ExtendedValue 119 genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 120 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 121 llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg, 122 mlir::Value mask, int rank) { 123 124 // Create mutable fir.box to be passed to the runtime for the result. 125 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 126 fir::MutableBoxValue resultMutableBox = 127 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 128 mlir::Value resultIrBox = 129 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 130 131 mlir::Value dim = 132 isAbsent(dimArg) 133 ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 134 : fir::getBase(dimArg); 135 funcDim(builder, loc, resultIrBox, array, dim, mask); 136 137 fir::ExtendedValue res = 138 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 139 return res.match( 140 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 141 // Add cleanup code 142 assert(stmtCtx); 143 fir::FirOpBuilder *bldr = &builder; 144 mlir::Value temp = box.getAddr(); 145 stmtCtx->attachCleanup( 146 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 147 return box; 148 }, 149 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 150 // Add cleanup code 151 assert(stmtCtx); 152 fir::FirOpBuilder *bldr = &builder; 153 mlir::Value temp = box.getAddr(); 154 stmtCtx->attachCleanup( 155 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 156 return box; 157 }, 158 [&](const auto &) -> fir::ExtendedValue { 159 fir::emitFatalError(loc, errMsg); 160 }); 161 } 162 163 /// Process calls to Product, Sum intrinsic functions 164 template <typename FN, typename FD> 165 static fir::ExtendedValue 166 genProdOrSum(FN func, FD funcDim, mlir::Type resultType, 167 fir::FirOpBuilder &builder, mlir::Location loc, 168 Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg, 169 llvm::ArrayRef<fir::ExtendedValue> args) { 170 171 assert(args.size() == 3); 172 173 // Handle required array argument 174 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 175 mlir::Value array = fir::getBase(arryTmp); 176 int rank = arryTmp.rank(); 177 assert(rank >= 1); 178 179 // Handle optional mask argument 180 auto mask = isAbsent(args[2]) 181 ? builder.create<fir::AbsentOp>( 182 loc, fir::BoxType::get(builder.getI1Type())) 183 : builder.createBox(loc, args[2]); 184 185 bool absentDim = isAbsent(args[1]); 186 187 // We call the type specific versions because the result is scalar 188 // in the case below. 189 if (absentDim || rank == 1) { 190 mlir::Type ty = array.getType(); 191 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty); 192 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy(); 193 if (fir::isa_complex(eleTy)) { 194 mlir::Value result = builder.createTemporary(loc, eleTy); 195 func(builder, loc, array, mask, result); 196 return builder.create<fir::LoadOp>(loc, result); 197 } 198 auto resultBox = builder.create<fir::AbsentOp>( 199 loc, fir::BoxType::get(builder.getI1Type())); 200 return func(builder, loc, array, mask, resultBox); 201 } 202 // Handle Product/Sum cases that have an array result. 203 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 204 args[1], mask, rank); 205 } 206 207 /// Process calls to DotProduct 208 template <typename FN> 209 static fir::ExtendedValue 210 genDotProd(FN func, mlir::Type resultType, fir::FirOpBuilder &builder, 211 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 212 llvm::ArrayRef<fir::ExtendedValue> args) { 213 214 assert(args.size() == 2); 215 216 // Handle required vector arguments 217 mlir::Value vectorA = fir::getBase(args[0]); 218 mlir::Value vectorB = fir::getBase(args[1]); 219 220 mlir::Type eleTy = fir::dyn_cast_ptrOrBoxEleTy(vectorA.getType()) 221 .cast<fir::SequenceType>() 222 .getEleTy(); 223 if (fir::isa_complex(eleTy)) { 224 mlir::Value result = builder.createTemporary(loc, eleTy); 225 func(builder, loc, vectorA, vectorB, result); 226 return builder.create<fir::LoadOp>(loc, result); 227 } 228 229 auto resultBox = builder.create<fir::AbsentOp>( 230 loc, fir::BoxType::get(builder.getI1Type())); 231 return func(builder, loc, vectorA, vectorB, resultBox); 232 } 233 234 /// Process calls to Maxval, Minval, Product, Sum intrinsic functions 235 template <typename FN, typename FD, typename FC> 236 static fir::ExtendedValue 237 genExtremumVal(FN func, FD funcDim, FC funcChar, mlir::Type resultType, 238 fir::FirOpBuilder &builder, mlir::Location loc, 239 Fortran::lower::StatementContext *stmtCtx, 240 llvm::StringRef errMsg, 241 llvm::ArrayRef<fir::ExtendedValue> args) { 242 243 assert(args.size() == 3); 244 245 // Handle required array argument 246 fir::BoxValue arryTmp = builder.createBox(loc, args[0]); 247 mlir::Value array = fir::getBase(arryTmp); 248 int rank = arryTmp.rank(); 249 assert(rank >= 1); 250 bool hasCharacterResult = arryTmp.isCharacter(); 251 252 // Handle optional mask argument 253 auto mask = isAbsent(args[2]) 254 ? builder.create<fir::AbsentOp>( 255 loc, fir::BoxType::get(builder.getI1Type())) 256 : builder.createBox(loc, args[2]); 257 258 bool absentDim = isAbsent(args[1]); 259 260 // For Maxval/MinVal, we call the type specific versions of 261 // Maxval/Minval because the result is scalar in the case below. 262 if (!hasCharacterResult && (absentDim || rank == 1)) 263 return func(builder, loc, array, mask); 264 265 if (hasCharacterResult && (absentDim || rank == 1)) { 266 // Create mutable fir.box to be passed to the runtime for the result. 267 fir::MutableBoxValue resultMutableBox = 268 fir::factory::createTempMutableBox(builder, loc, resultType); 269 mlir::Value resultIrBox = 270 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 271 272 funcChar(builder, loc, resultIrBox, array, mask); 273 274 // Handle cleanup of allocatable result descriptor and return 275 fir::ExtendedValue res = 276 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 277 return res.match( 278 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 279 // Add cleanup code 280 assert(stmtCtx); 281 fir::FirOpBuilder *bldr = &builder; 282 mlir::Value temp = box.getAddr(); 283 stmtCtx->attachCleanup( 284 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 285 return box; 286 }, 287 [&](const auto &) -> fir::ExtendedValue { 288 fir::emitFatalError(loc, errMsg); 289 }); 290 } 291 292 // Handle Min/Maxval cases that have an array result. 293 return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array, 294 args[1], mask, rank); 295 } 296 297 /// Process calls to Minloc, Maxloc intrinsic functions 298 template <typename FN, typename FD> 299 static fir::ExtendedValue genExtremumloc( 300 FN func, FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder, 301 mlir::Location loc, Fortran::lower::StatementContext *stmtCtx, 302 llvm::StringRef errMsg, llvm::ArrayRef<fir::ExtendedValue> args) { 303 304 assert(args.size() == 5); 305 306 // Handle required array argument 307 mlir::Value array = builder.createBox(loc, args[0]); 308 unsigned rank = fir::BoxValue(array).rank(); 309 assert(rank >= 1); 310 311 // Handle optional mask argument 312 auto mask = isAbsent(args[2]) 313 ? builder.create<fir::AbsentOp>( 314 loc, fir::BoxType::get(builder.getI1Type())) 315 : builder.createBox(loc, args[2]); 316 317 // Handle optional kind argument 318 auto kind = isAbsent(args[3]) ? builder.createIntegerConstant( 319 loc, builder.getIndexType(), 320 builder.getKindMap().defaultIntegerKind()) 321 : fir::getBase(args[3]); 322 323 // Handle optional back argument 324 auto back = isAbsent(args[4]) ? builder.createBool(loc, false) 325 : fir::getBase(args[4]); 326 327 bool absentDim = isAbsent(args[1]); 328 329 if (!absentDim && rank == 1) { 330 // If dim argument is present and the array is rank 1, then the result is 331 // a scalar (since the the result is rank-1 or 0). 332 // Therefore, we use a scalar result descriptor with Min/MaxlocDim(). 333 mlir::Value dim = fir::getBase(args[1]); 334 // Create mutable fir.box to be passed to the runtime for the result. 335 fir::MutableBoxValue resultMutableBox = 336 fir::factory::createTempMutableBox(builder, loc, resultType); 337 mlir::Value resultIrBox = 338 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 339 340 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 341 342 // Handle cleanup of allocatable result descriptor and return 343 fir::ExtendedValue res = 344 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 345 return res.match( 346 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 347 // Add cleanup code 348 assert(stmtCtx); 349 fir::FirOpBuilder *bldr = &builder; 350 stmtCtx->attachCleanup( 351 [=]() { bldr->create<fir::FreeMemOp>(loc, tempAddr); }); 352 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 353 }, 354 [&](const auto &) -> fir::ExtendedValue { 355 fir::emitFatalError(loc, errMsg); 356 }); 357 } 358 359 // Note: The Min/Maxloc/val cases below have an array result. 360 361 // Create mutable fir.box to be passed to the runtime for the result. 362 mlir::Type resultArrayType = 363 builder.getVarLenSeqTy(resultType, absentDim ? 1 : rank - 1); 364 fir::MutableBoxValue resultMutableBox = 365 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 366 mlir::Value resultIrBox = 367 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 368 369 if (absentDim) { 370 // Handle min/maxloc/val case where there is no dim argument 371 // (calls Min/Maxloc()/MinMaxval() runtime routine) 372 func(builder, loc, resultIrBox, array, mask, kind, back); 373 } else { 374 // else handle min/maxloc case with dim argument (calls 375 // Min/Max/loc/val/Dim() runtime routine). 376 mlir::Value dim = fir::getBase(args[1]); 377 funcDim(builder, loc, resultIrBox, array, dim, mask, kind, back); 378 } 379 380 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 381 .match( 382 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 383 // Add cleanup code 384 assert(stmtCtx); 385 fir::FirOpBuilder *bldr = &builder; 386 mlir::Value temp = box.getAddr(); 387 stmtCtx->attachCleanup( 388 [=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 389 return box; 390 }, 391 [&](const auto &) -> fir::ExtendedValue { 392 fir::emitFatalError(loc, errMsg); 393 }); 394 } 395 396 // TODO error handling -> return a code or directly emit messages ? 397 struct IntrinsicLibrary { 398 399 // Constructors. 400 explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc, 401 Fortran::lower::StatementContext *stmtCtx = nullptr) 402 : builder{builder}, loc{loc}, stmtCtx{stmtCtx} {} 403 IntrinsicLibrary() = delete; 404 IntrinsicLibrary(const IntrinsicLibrary &) = delete; 405 406 /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg 407 /// and expected result type \p resultType. 408 fir::ExtendedValue genIntrinsicCall(llvm::StringRef name, 409 llvm::Optional<mlir::Type> resultType, 410 llvm::ArrayRef<fir::ExtendedValue> arg); 411 412 /// Search a runtime function that is associated to the generic intrinsic name 413 /// and whose signature matches the intrinsic arguments and result types. 414 /// If no such runtime function is found but a runtime function associated 415 /// with the Fortran generic exists and has the same number of arguments, 416 /// conversions will be inserted before and/or after the call. This is to 417 /// mainly to allow 16 bits float support even-though little or no math 418 /// runtime is currently available for it. 419 mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, 420 llvm::ArrayRef<mlir::Value>); 421 422 using RuntimeCallGenerator = std::function<mlir::Value( 423 fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>; 424 RuntimeCallGenerator 425 getRuntimeCallGenerator(llvm::StringRef name, 426 mlir::FunctionType soughtFuncType); 427 428 /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in 429 /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation 430 /// if the argument is an integer, into llvm intrinsics if the argument is 431 /// real and to the `hypot` math routine if the argument is of complex type. 432 mlir::Value genAbs(mlir::Type, llvm::ArrayRef<mlir::Value>); 433 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc, 434 mlir::Value, mlir::Value)> 435 fir::ExtendedValue genAdjustRtCall(mlir::Type, 436 llvm::ArrayRef<fir::ExtendedValue>); 437 mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>); 438 fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 439 fir::ExtendedValue genAllocated(mlir::Type, 440 llvm::ArrayRef<fir::ExtendedValue>); 441 fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 442 fir::ExtendedValue genAssociated(mlir::Type, 443 llvm::ArrayRef<fir::ExtendedValue>); 444 fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 445 fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 446 template <mlir::arith::CmpIPredicate pred> 447 fir::ExtendedValue genCharacterCompare(mlir::Type, 448 llvm::ArrayRef<fir::ExtendedValue>); 449 void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>); 450 fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 451 void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>); 452 mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>); 453 fir::ExtendedValue genDotProduct(mlir::Type, 454 llvm::ArrayRef<fir::ExtendedValue>); 455 fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 456 mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>); 457 template <Extremum, ExtremumBehavior> 458 mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>); 459 mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>); 460 mlir::Value genFraction(mlir::Type resultType, 461 mlir::ArrayRef<mlir::Value> args); 462 /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments 463 /// in the llvm::ArrayRef. 464 mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>); 465 mlir::Value genIbclr(mlir::Type, llvm::ArrayRef<mlir::Value>); 466 mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>); 467 mlir::Value genIbset(mlir::Type, llvm::ArrayRef<mlir::Value>); 468 fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 469 mlir::Value genIeor(mlir::Type, llvm::ArrayRef<mlir::Value>); 470 mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>); 471 mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>); 472 fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 473 fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 474 fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 475 fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 476 fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 477 fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 478 fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 479 mlir::Value genMod(mlir::Type, llvm::ArrayRef<mlir::Value>); 480 mlir::Value genModulo(mlir::Type, llvm::ArrayRef<mlir::Value>); 481 mlir::Value genNint(mlir::Type, llvm::ArrayRef<mlir::Value>); 482 mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>); 483 fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 484 fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 485 fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 486 void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>); 487 void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>); 488 void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>); 489 fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 490 mlir::Value genSetExponent(mlir::Type resultType, 491 llvm::ArrayRef<mlir::Value> args); 492 fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 493 fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 494 void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>); 495 fir::ExtendedValue genTransfer(mlir::Type, 496 llvm::ArrayRef<fir::ExtendedValue>); 497 fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 498 fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 499 fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>); 500 501 /// Define the different FIR generators that can be mapped to intrinsic to 502 /// generate the related code. 503 using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); 504 using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum); 505 using SubroutineGenerator = decltype(&IntrinsicLibrary::genRandomInit); 506 using Generator = 507 std::variant<ElementalGenerator, ExtendedGenerator, SubroutineGenerator>; 508 509 template <typename GeneratorType> 510 fir::ExtendedValue 511 outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, 512 llvm::Optional<mlir::Type> resultType, 513 llvm::ArrayRef<fir::ExtendedValue> args); 514 515 template <typename GeneratorType> 516 mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name, 517 mlir::FunctionType, bool loadRefArguments = false); 518 519 /// Generate calls to ElementalGenerator, handling the elemental aspects 520 template <typename GeneratorType> 521 fir::ExtendedValue 522 genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, 523 llvm::ArrayRef<fir::ExtendedValue> args, bool outline); 524 525 /// Helper to invoke code generator for the intrinsics given arguments. 526 mlir::Value invokeGenerator(ElementalGenerator generator, 527 mlir::Type resultType, 528 llvm::ArrayRef<mlir::Value> args); 529 mlir::Value invokeGenerator(RuntimeCallGenerator generator, 530 mlir::Type resultType, 531 llvm::ArrayRef<mlir::Value> args); 532 mlir::Value invokeGenerator(ExtendedGenerator generator, 533 mlir::Type resultType, 534 llvm::ArrayRef<mlir::Value> args); 535 mlir::Value invokeGenerator(SubroutineGenerator generator, 536 llvm::ArrayRef<mlir::Value> args); 537 538 /// Add clean-up for \p temp to the current statement context; 539 void addCleanUpForTemp(mlir::Location loc, mlir::Value temp); 540 /// Helper function for generating code clean-up for result descriptors 541 fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 542 mlir::Type resultType, 543 llvm::StringRef errMsg); 544 545 fir::FirOpBuilder &builder; 546 mlir::Location loc; 547 Fortran::lower::StatementContext *stmtCtx; 548 }; 549 550 struct IntrinsicDummyArgument { 551 const char *name = nullptr; 552 Fortran::lower::LowerIntrinsicArgAs lowerAs = 553 Fortran::lower::LowerIntrinsicArgAs::Value; 554 bool handleDynamicOptional = false; 555 }; 556 557 struct Fortran::lower::IntrinsicArgumentLoweringRules { 558 /// There is no more than 7 non repeated arguments in Fortran intrinsics. 559 IntrinsicDummyArgument args[7]; 560 constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } 561 }; 562 563 /// Structure describing what needs to be done to lower intrinsic "name". 564 struct IntrinsicHandler { 565 const char *name; 566 IntrinsicLibrary::Generator generator; 567 // The following may be omitted in the table below. 568 Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {}; 569 bool isElemental = true; 570 /// Code heavy intrinsic can be outlined to make FIR 571 /// more readable. 572 bool outline = false; 573 }; 574 575 constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value; 576 constexpr auto asAddr = Fortran::lower::LowerIntrinsicArgAs::Addr; 577 constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box; 578 constexpr auto asInquired = Fortran::lower::LowerIntrinsicArgAs::Inquired; 579 using I = IntrinsicLibrary; 580 581 /// Flag to indicate that an intrinsic argument has to be handled as 582 /// being dynamically optional (e.g. special handling when actual 583 /// argument is an optional variable in the current scope). 584 static constexpr bool handleDynamicOptional = true; 585 586 /// Table that drives the fir generation depending on the intrinsic. 587 /// one to one mapping with Fortran arguments. If no mapping is 588 /// defined here for a generic intrinsic, genRuntimeCall will be called 589 /// to look for a match in the runtime a emit a call. Note that the argument 590 /// lowering rules for an intrinsic need to be provided only if at least one 591 /// argument must not be lowered by value. In which case, the lowering rules 592 /// should be provided for all the intrinsic arguments for completeness. 593 static constexpr IntrinsicHandler handlers[]{ 594 {"abs", &I::genAbs}, 595 {"adjustl", 596 &I::genAdjustRtCall<fir::runtime::genAdjustL>, 597 {{{"string", asAddr}}}, 598 /*isElemental=*/true}, 599 {"adjustr", 600 &I::genAdjustRtCall<fir::runtime::genAdjustR>, 601 {{{"string", asAddr}}}, 602 /*isElemental=*/true}, 603 {"aimag", &I::genAimag}, 604 {"all", 605 &I::genAll, 606 {{{"mask", asAddr}, {"dim", asValue}}}, 607 /*isElemental=*/false}, 608 {"allocated", 609 &I::genAllocated, 610 {{{"array", asInquired}, {"scalar", asInquired}}}, 611 /*isElemental=*/false}, 612 {"any", 613 &I::genAny, 614 {{{"mask", asAddr}, {"dim", asValue}}}, 615 /*isElemental=*/false}, 616 {"associated", 617 &I::genAssociated, 618 {{{"pointer", asInquired}, {"target", asInquired}}}, 619 /*isElemental=*/false}, 620 {"char", &I::genChar}, 621 {"count", 622 &I::genCount, 623 {{{"mask", asAddr}, {"dim", asValue}, {"kind", asValue}}}, 624 /*isElemental=*/false}, 625 {"cpu_time", 626 &I::genCpuTime, 627 {{{"time", asAddr}}}, 628 /*isElemental=*/false}, 629 {"cshift", 630 &I::genCshift, 631 {{{"array", asAddr}, {"shift", asAddr}, {"dim", asValue}}}, 632 /*isElemental=*/false}, 633 {"date_and_time", 634 &I::genDateAndTime, 635 {{{"date", asAddr, handleDynamicOptional}, 636 {"time", asAddr, handleDynamicOptional}, 637 {"zone", asAddr, handleDynamicOptional}, 638 {"values", asBox, handleDynamicOptional}}}, 639 /*isElemental=*/false}, 640 {"dim", &I::genDim}, 641 {"dot_product", 642 &I::genDotProduct, 643 {{{"vector_a", asBox}, {"vector_b", asBox}}}, 644 /*isElemental=*/false}, 645 {"eoshift", 646 &I::genEoshift, 647 {{{"array", asBox}, 648 {"shift", asAddr}, 649 {"boundary", asBox, handleDynamicOptional}, 650 {"dim", asValue}}}, 651 /*isElemental=*/false}, 652 {"exponent", &I::genExponent}, 653 {"floor", &I::genFloor}, 654 {"fraction", &I::genFraction}, 655 {"iachar", &I::genIchar}, 656 {"iand", &I::genIand}, 657 {"ibclr", &I::genIbclr}, 658 {"ibits", &I::genIbits}, 659 {"ibset", &I::genIbset}, 660 {"ichar", &I::genIchar}, 661 {"ieor", &I::genIeor}, 662 {"ishft", &I::genIshft}, 663 {"ishftc", &I::genIshftc}, 664 {"len", 665 &I::genLen, 666 {{{"string", asInquired}, {"kind", asValue}}}, 667 /*isElemental=*/false}, 668 {"len_trim", &I::genLenTrim}, 669 {"lge", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sge>}, 670 {"lgt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sgt>}, 671 {"lle", &I::genCharacterCompare<mlir::arith::CmpIPredicate::sle>}, 672 {"llt", &I::genCharacterCompare<mlir::arith::CmpIPredicate::slt>}, 673 {"max", &I::genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>}, 674 {"maxloc", 675 &I::genMaxloc, 676 {{{"array", asBox}, 677 {"dim", asValue}, 678 {"mask", asBox, handleDynamicOptional}, 679 {"kind", asValue}, 680 {"back", asValue, handleDynamicOptional}}}, 681 /*isElemental=*/false}, 682 {"maxval", 683 &I::genMaxval, 684 {{{"array", asBox}, 685 {"dim", asValue}, 686 {"mask", asBox, handleDynamicOptional}}}, 687 /*isElemental=*/false}, 688 {"min", &I::genExtremum<Extremum::Min, ExtremumBehavior::MinMaxss>}, 689 {"minloc", 690 &I::genMinloc, 691 {{{"array", asBox}, 692 {"dim", asValue}, 693 {"mask", asBox, handleDynamicOptional}, 694 {"kind", asValue}, 695 {"back", asValue, handleDynamicOptional}}}, 696 /*isElemental=*/false}, 697 {"minval", 698 &I::genMinval, 699 {{{"array", asBox}, 700 {"dim", asValue}, 701 {"mask", asBox, handleDynamicOptional}}}, 702 /*isElemental=*/false}, 703 {"mod", &I::genMod}, 704 {"modulo", &I::genModulo}, 705 {"nint", &I::genNint}, 706 {"not", &I::genNot}, 707 {"null", &I::genNull, {{{"mold", asInquired}}}, /*isElemental=*/false}, 708 {"pack", 709 &I::genPack, 710 {{{"array", asBox}, 711 {"mask", asBox}, 712 {"vector", asBox, handleDynamicOptional}}}, 713 /*isElemental=*/false}, 714 {"product", 715 &I::genProduct, 716 {{{"array", asBox}, 717 {"dim", asValue}, 718 {"mask", asBox, handleDynamicOptional}}}, 719 /*isElemental=*/false}, 720 {"random_init", 721 &I::genRandomInit, 722 {{{"repeatable", asValue}, {"image_distinct", asValue}}}, 723 /*isElemental=*/false}, 724 {"random_number", 725 &I::genRandomNumber, 726 {{{"harvest", asBox}}}, 727 /*isElemental=*/false}, 728 {"random_seed", 729 &I::genRandomSeed, 730 {{{"size", asBox}, {"put", asBox}, {"get", asBox}}}, 731 /*isElemental=*/false}, 732 {"scan", 733 &I::genScan, 734 {{{"string", asAddr}, 735 {"set", asAddr}, 736 {"back", asValue, handleDynamicOptional}, 737 {"kind", asValue}}}, 738 /*isElemental=*/true}, 739 {"set_exponent", &I::genSetExponent}, 740 {"size", 741 &I::genSize, 742 {{{"array", asBox}, 743 {"dim", asAddr, handleDynamicOptional}, 744 {"kind", asValue}}}, 745 /*isElemental=*/false}, 746 {"sum", 747 &I::genSum, 748 {{{"array", asBox}, 749 {"dim", asValue}, 750 {"mask", asBox, handleDynamicOptional}}}, 751 /*isElemental=*/false}, 752 {"system_clock", 753 &I::genSystemClock, 754 {{{"count", asAddr}, {"count_rate", asAddr}, {"count_max", asAddr}}}, 755 /*isElemental=*/false}, 756 {"transfer", 757 &I::genTransfer, 758 {{{"source", asAddr}, {"mold", asAddr}, {"size", asValue}}}, 759 /*isElemental=*/false}, 760 {"ubound", 761 &I::genUbound, 762 {{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}}, 763 /*isElemental=*/false}, 764 {"unpack", 765 &I::genUnpack, 766 {{{"vector", asBox}, {"mask", asBox}, {"field", asBox}}}, 767 /*isElemental=*/false}, 768 {"verify", 769 &I::genVerify, 770 {{{"string", asAddr}, 771 {"set", asAddr}, 772 {"back", asValue, handleDynamicOptional}, 773 {"kind", asValue}}}, 774 /*isElemental=*/true}, 775 }; 776 777 static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) { 778 auto compare = [](const IntrinsicHandler &handler, llvm::StringRef name) { 779 return name.compare(handler.name) > 0; 780 }; 781 auto result = 782 std::lower_bound(std::begin(handlers), std::end(handlers), name, compare); 783 return result != std::end(handlers) && result->name == name ? result 784 : nullptr; 785 } 786 787 /// To make fir output more readable for debug, one can outline all intrinsic 788 /// implementation in wrappers (overrides the IntrinsicHandler::outline flag). 789 static llvm::cl::opt<bool> outlineAllIntrinsics( 790 "outline-intrinsics", 791 llvm::cl::desc( 792 "Lower all intrinsic procedure implementation in their own functions"), 793 llvm::cl::init(false)); 794 795 //===----------------------------------------------------------------------===// 796 // Math runtime description and matching utility 797 //===----------------------------------------------------------------------===// 798 799 /// Command line option to modify math runtime version used to implement 800 /// intrinsics. 801 enum MathRuntimeVersion { fastVersion, llvmOnly }; 802 llvm::cl::opt<MathRuntimeVersion> mathRuntimeVersion( 803 "math-runtime", llvm::cl::desc("Select math runtime version:"), 804 llvm::cl::values( 805 clEnumValN(fastVersion, "fast", "use pgmath fast runtime"), 806 clEnumValN(llvmOnly, "llvm", 807 "only use LLVM intrinsics (may be incomplete)")), 808 llvm::cl::init(fastVersion)); 809 810 struct RuntimeFunction { 811 // llvm::StringRef comparison operator are not constexpr, so use string_view. 812 using Key = std::string_view; 813 // Needed for implicit compare with keys. 814 constexpr operator Key() const { return key; } 815 Key key; // intrinsic name 816 llvm::StringRef symbol; 817 fir::runtime::FuncTypeBuilderFunc typeGenerator; 818 }; 819 820 #define RUNTIME_STATIC_DESCRIPTION(name, func) \ 821 {#name, #func, fir::runtime::RuntimeTableKey<decltype(func)>::getTypeModel()}, 822 static constexpr RuntimeFunction pgmathFast[] = { 823 #define PGMATH_FAST 824 #define PGMATH_USE_ALL_TYPES(name, func) RUNTIME_STATIC_DESCRIPTION(name, func) 825 #include "flang/Evaluate/pgmath.h.inc" 826 }; 827 828 static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) { 829 mlir::Type t = mlir::FloatType::getF32(context); 830 return mlir::FunctionType::get(context, {t}, {t}); 831 } 832 833 static mlir::FunctionType genF64F64FuncType(mlir::MLIRContext *context) { 834 mlir::Type t = mlir::FloatType::getF64(context); 835 return mlir::FunctionType::get(context, {t}, {t}); 836 } 837 838 static mlir::FunctionType genF32F32F32FuncType(mlir::MLIRContext *context) { 839 auto t = mlir::FloatType::getF32(context); 840 return mlir::FunctionType::get(context, {t, t}, {t}); 841 } 842 843 static mlir::FunctionType genF64F64F64FuncType(mlir::MLIRContext *context) { 844 auto t = mlir::FloatType::getF64(context); 845 return mlir::FunctionType::get(context, {t, t}, {t}); 846 } 847 848 template <int Bits> 849 static mlir::FunctionType genIntF64FuncType(mlir::MLIRContext *context) { 850 auto t = mlir::FloatType::getF64(context); 851 auto r = mlir::IntegerType::get(context, Bits); 852 return mlir::FunctionType::get(context, {t}, {r}); 853 } 854 855 template <int Bits> 856 static mlir::FunctionType genIntF32FuncType(mlir::MLIRContext *context) { 857 auto t = mlir::FloatType::getF32(context); 858 auto r = mlir::IntegerType::get(context, Bits); 859 return mlir::FunctionType::get(context, {t}, {r}); 860 } 861 862 // TODO : Fill-up this table with more intrinsic. 863 // Note: These are also defined as operations in LLVM dialect. See if this 864 // can be use and has advantages. 865 static constexpr RuntimeFunction llvmIntrinsics[] = { 866 {"abs", "llvm.fabs.f32", genF32F32FuncType}, 867 {"abs", "llvm.fabs.f64", genF64F64FuncType}, 868 // llvm.floor is used for FLOOR, but returns real. 869 {"floor", "llvm.floor.f32", genF32F32FuncType}, 870 {"floor", "llvm.floor.f64", genF64F64FuncType}, 871 {"nint", "llvm.lround.i64.f64", genIntF64FuncType<64>}, 872 {"nint", "llvm.lround.i64.f32", genIntF32FuncType<64>}, 873 {"nint", "llvm.lround.i32.f64", genIntF64FuncType<32>}, 874 {"nint", "llvm.lround.i32.f32", genIntF32FuncType<32>}, 875 {"pow", "llvm.pow.f32", genF32F32F32FuncType}, 876 {"pow", "llvm.pow.f64", genF64F64F64FuncType}, 877 }; 878 879 // This helper class computes a "distance" between two function types. 880 // The distance measures how many narrowing conversions of actual arguments 881 // and result of "from" must be made in order to use "to" instead of "from". 882 // For instance, the distance between ACOS(REAL(10)) and ACOS(REAL(8)) is 883 // greater than the one between ACOS(REAL(10)) and ACOS(REAL(16)). This means 884 // if no implementation of ACOS(REAL(10)) is available, it is better to use 885 // ACOS(REAL(16)) with casts rather than ACOS(REAL(8)). 886 // Note that this is not a symmetric distance and the order of "from" and "to" 887 // arguments matters, d(foo, bar) may not be the same as d(bar, foo) because it 888 // may be safe to replace foo by bar, but not the opposite. 889 class FunctionDistance { 890 public: 891 FunctionDistance() : infinite{true} {} 892 893 FunctionDistance(mlir::FunctionType from, mlir::FunctionType to) { 894 unsigned nInputs = from.getNumInputs(); 895 unsigned nResults = from.getNumResults(); 896 if (nResults != to.getNumResults() || nInputs != to.getNumInputs()) { 897 infinite = true; 898 } else { 899 for (decltype(nInputs) i = 0; i < nInputs && !infinite; ++i) 900 addArgumentDistance(from.getInput(i), to.getInput(i)); 901 for (decltype(nResults) i = 0; i < nResults && !infinite; ++i) 902 addResultDistance(to.getResult(i), from.getResult(i)); 903 } 904 } 905 906 /// Beware both d1.isSmallerThan(d2) *and* d2.isSmallerThan(d1) may be 907 /// false if both d1 and d2 are infinite. This implies that 908 /// d1.isSmallerThan(d2) is not equivalent to !d2.isSmallerThan(d1) 909 bool isSmallerThan(const FunctionDistance &d) const { 910 return !infinite && 911 (d.infinite || std::lexicographical_compare( 912 conversions.begin(), conversions.end(), 913 d.conversions.begin(), d.conversions.end())); 914 } 915 916 bool isLosingPrecision() const { 917 return conversions[narrowingArg] != 0 || conversions[extendingResult] != 0; 918 } 919 920 bool isInfinite() const { return infinite; } 921 922 private: 923 enum class Conversion { Forbidden, None, Narrow, Extend }; 924 925 void addArgumentDistance(mlir::Type from, mlir::Type to) { 926 switch (conversionBetweenTypes(from, to)) { 927 case Conversion::Forbidden: 928 infinite = true; 929 break; 930 case Conversion::None: 931 break; 932 case Conversion::Narrow: 933 conversions[narrowingArg]++; 934 break; 935 case Conversion::Extend: 936 conversions[nonNarrowingArg]++; 937 break; 938 } 939 } 940 941 void addResultDistance(mlir::Type from, mlir::Type to) { 942 switch (conversionBetweenTypes(from, to)) { 943 case Conversion::Forbidden: 944 infinite = true; 945 break; 946 case Conversion::None: 947 break; 948 case Conversion::Narrow: 949 conversions[nonExtendingResult]++; 950 break; 951 case Conversion::Extend: 952 conversions[extendingResult]++; 953 break; 954 } 955 } 956 957 // Floating point can be mlir::FloatType or fir::real 958 static unsigned getFloatingPointWidth(mlir::Type t) { 959 if (auto f{t.dyn_cast<mlir::FloatType>()}) 960 return f.getWidth(); 961 // FIXME: Get width another way for fir.real/complex 962 // - use fir/KindMapping.h and llvm::Type 963 // - or use evaluate/type.h 964 if (auto r{t.dyn_cast<fir::RealType>()}) 965 return r.getFKind() * 4; 966 if (auto cplx{t.dyn_cast<fir::ComplexType>()}) 967 return cplx.getFKind() * 4; 968 llvm_unreachable("not a floating-point type"); 969 } 970 971 static Conversion conversionBetweenTypes(mlir::Type from, mlir::Type to) { 972 if (from == to) 973 return Conversion::None; 974 975 if (auto fromIntTy{from.dyn_cast<mlir::IntegerType>()}) { 976 if (auto toIntTy{to.dyn_cast<mlir::IntegerType>()}) { 977 return fromIntTy.getWidth() > toIntTy.getWidth() ? Conversion::Narrow 978 : Conversion::Extend; 979 } 980 } 981 982 if (fir::isa_real(from) && fir::isa_real(to)) { 983 return getFloatingPointWidth(from) > getFloatingPointWidth(to) 984 ? Conversion::Narrow 985 : Conversion::Extend; 986 } 987 988 if (auto fromCplxTy{from.dyn_cast<fir::ComplexType>()}) { 989 if (auto toCplxTy{to.dyn_cast<fir::ComplexType>()}) { 990 return getFloatingPointWidth(fromCplxTy) > 991 getFloatingPointWidth(toCplxTy) 992 ? Conversion::Narrow 993 : Conversion::Extend; 994 } 995 } 996 // Notes: 997 // - No conversion between character types, specialization of runtime 998 // functions should be made instead. 999 // - It is not clear there is a use case for automatic conversions 1000 // around Logical and it may damage hidden information in the physical 1001 // storage so do not do it. 1002 return Conversion::Forbidden; 1003 } 1004 1005 // Below are indexes to access data in conversions. 1006 // The order in data does matter for lexicographical_compare 1007 enum { 1008 narrowingArg = 0, // usually bad 1009 extendingResult, // usually bad 1010 nonExtendingResult, // usually ok 1011 nonNarrowingArg, // usually ok 1012 dataSize 1013 }; 1014 1015 std::array<int, dataSize> conversions = {}; 1016 bool infinite = false; // When forbidden conversion or wrong argument number 1017 }; 1018 1019 /// Build mlir::FuncOp from runtime symbol description and add 1020 /// fir.runtime attribute. 1021 static mlir::FuncOp getFuncOp(mlir::Location loc, fir::FirOpBuilder &builder, 1022 const RuntimeFunction &runtime) { 1023 mlir::FuncOp function = builder.addNamedFunction( 1024 loc, runtime.symbol, runtime.typeGenerator(builder.getContext())); 1025 function->setAttr("fir.runtime", builder.getUnitAttr()); 1026 return function; 1027 } 1028 1029 /// Select runtime function that has the smallest distance to the intrinsic 1030 /// function type and that will not imply narrowing arguments or extending the 1031 /// result. 1032 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 1033 mlir::FuncOp searchFunctionInLibrary( 1034 mlir::Location loc, fir::FirOpBuilder &builder, 1035 const Fortran::common::StaticMultimapView<RuntimeFunction> &lib, 1036 llvm::StringRef name, mlir::FunctionType funcType, 1037 const RuntimeFunction **bestNearMatch, 1038 FunctionDistance &bestMatchDistance) { 1039 std::pair<const RuntimeFunction *, const RuntimeFunction *> range = 1040 lib.equal_range(name); 1041 for (auto iter = range.first; iter != range.second && iter; ++iter) { 1042 const RuntimeFunction &impl = *iter; 1043 mlir::FunctionType implType = impl.typeGenerator(builder.getContext()); 1044 if (funcType == implType) 1045 return getFuncOp(loc, builder, impl); // exact match 1046 1047 FunctionDistance distance(funcType, implType); 1048 if (distance.isSmallerThan(bestMatchDistance)) { 1049 *bestNearMatch = &impl; 1050 bestMatchDistance = std::move(distance); 1051 } 1052 } 1053 return {}; 1054 } 1055 1056 /// Search runtime for the best runtime function given an intrinsic name 1057 /// and interface. The interface may not be a perfect match in which case 1058 /// the caller is responsible to insert argument and return value conversions. 1059 /// If nothing is found, the mlir::FuncOp will contain a nullptr. 1060 static mlir::FuncOp getRuntimeFunction(mlir::Location loc, 1061 fir::FirOpBuilder &builder, 1062 llvm::StringRef name, 1063 mlir::FunctionType funcType) { 1064 const RuntimeFunction *bestNearMatch = nullptr; 1065 FunctionDistance bestMatchDistance{}; 1066 mlir::FuncOp match; 1067 using RtMap = Fortran::common::StaticMultimapView<RuntimeFunction>; 1068 static constexpr RtMap pgmathF(pgmathFast); 1069 static_assert(pgmathF.Verify() && "map must be sorted"); 1070 if (mathRuntimeVersion == fastVersion) { 1071 match = searchFunctionInLibrary(loc, builder, pgmathF, name, funcType, 1072 &bestNearMatch, bestMatchDistance); 1073 } else { 1074 assert(mathRuntimeVersion == llvmOnly && "unknown math runtime"); 1075 } 1076 if (match) 1077 return match; 1078 1079 // Go through llvm intrinsics if not exact match in libpgmath or if 1080 // mathRuntimeVersion == llvmOnly 1081 static constexpr RtMap llvmIntr(llvmIntrinsics); 1082 static_assert(llvmIntr.Verify() && "map must be sorted"); 1083 if (mlir::FuncOp exactMatch = 1084 searchFunctionInLibrary(loc, builder, llvmIntr, name, funcType, 1085 &bestNearMatch, bestMatchDistance)) 1086 return exactMatch; 1087 1088 if (bestNearMatch != nullptr) { 1089 if (bestMatchDistance.isLosingPrecision()) { 1090 // Using this runtime version requires narrowing the arguments 1091 // or extending the result. It is not numerically safe. There 1092 // is currently no quad math library that was described in 1093 // lowering and could be used here. Emit an error and continue 1094 // generating the code with the narrowing cast so that the user 1095 // can get a complete list of the problematic intrinsic calls. 1096 std::string message("TODO: no math runtime available for '"); 1097 llvm::raw_string_ostream sstream(message); 1098 if (name == "pow") { 1099 assert(funcType.getNumInputs() == 2 && 1100 "power operator has two arguments"); 1101 sstream << funcType.getInput(0) << " ** " << funcType.getInput(1); 1102 } else { 1103 sstream << name << "("; 1104 if (funcType.getNumInputs() > 0) 1105 sstream << funcType.getInput(0); 1106 for (mlir::Type argType : funcType.getInputs().drop_front()) 1107 sstream << ", " << argType; 1108 sstream << ")"; 1109 } 1110 sstream << "'"; 1111 mlir::emitError(loc, message); 1112 } 1113 return getFuncOp(loc, builder, *bestNearMatch); 1114 } 1115 return {}; 1116 } 1117 1118 /// Helpers to get function type from arguments and result type. 1119 static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType, 1120 llvm::ArrayRef<mlir::Value> arguments, 1121 fir::FirOpBuilder &builder) { 1122 llvm::SmallVector<mlir::Type> argTypes; 1123 for (mlir::Value arg : arguments) 1124 argTypes.push_back(arg.getType()); 1125 llvm::SmallVector<mlir::Type> resTypes; 1126 if (resultType) 1127 resTypes.push_back(*resultType); 1128 return mlir::FunctionType::get(builder.getModule().getContext(), argTypes, 1129 resTypes); 1130 } 1131 1132 /// fir::ExtendedValue to mlir::Value translation layer 1133 1134 fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder, 1135 mlir::Location loc) { 1136 assert(val && "optional unhandled here"); 1137 mlir::Type type = val.getType(); 1138 mlir::Value base = val; 1139 mlir::IndexType indexType = builder.getIndexType(); 1140 llvm::SmallVector<mlir::Value> extents; 1141 1142 fir::factory::CharacterExprHelper charHelper{builder, loc}; 1143 // FIXME: we may want to allow non character scalar here. 1144 if (charHelper.isCharacterScalar(type)) 1145 return charHelper.toExtendedValue(val); 1146 1147 if (auto refType = type.dyn_cast<fir::ReferenceType>()) 1148 type = refType.getEleTy(); 1149 1150 if (auto arrayType = type.dyn_cast<fir::SequenceType>()) { 1151 type = arrayType.getEleTy(); 1152 for (fir::SequenceType::Extent extent : arrayType.getShape()) { 1153 if (extent == fir::SequenceType::getUnknownExtent()) 1154 break; 1155 extents.emplace_back( 1156 builder.createIntegerConstant(loc, indexType, extent)); 1157 } 1158 // Last extent might be missing in case of assumed-size. If more extents 1159 // could not be deduced from type, that's an error (a fir.box should 1160 // have been used in the interface). 1161 if (extents.size() + 1 < arrayType.getShape().size()) 1162 mlir::emitError(loc, "cannot retrieve array extents from type"); 1163 } else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) { 1164 fir::emitFatalError(loc, "not yet implemented: descriptor or derived type"); 1165 } 1166 1167 if (!extents.empty()) 1168 return fir::ArrayBoxValue{base, extents}; 1169 return base; 1170 } 1171 1172 mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder, 1173 mlir::Location loc) { 1174 if (const fir::CharBoxValue *charBox = val.getCharBox()) { 1175 mlir::Value buffer = charBox->getBuffer(); 1176 if (buffer.getType().isa<fir::BoxCharType>()) 1177 return buffer; 1178 return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar( 1179 buffer, charBox->getLen()); 1180 } 1181 1182 // FIXME: need to access other ExtendedValue variants and handle them 1183 // properly. 1184 return fir::getBase(val); 1185 } 1186 1187 //===----------------------------------------------------------------------===// 1188 // IntrinsicLibrary 1189 //===----------------------------------------------------------------------===// 1190 1191 /// Emit a TODO error message for as yet unimplemented intrinsics. 1192 static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) { 1193 TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name)); 1194 } 1195 1196 template <typename GeneratorType> 1197 fir::ExtendedValue IntrinsicLibrary::genElementalCall( 1198 GeneratorType generator, llvm::StringRef name, mlir::Type resultType, 1199 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1200 llvm::SmallVector<mlir::Value> scalarArgs; 1201 for (const fir::ExtendedValue &arg : args) 1202 if (arg.getUnboxed() || arg.getCharBox()) 1203 scalarArgs.emplace_back(fir::getBase(arg)); 1204 else 1205 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1206 return invokeGenerator(generator, resultType, scalarArgs); 1207 } 1208 1209 template <> 1210 fir::ExtendedValue 1211 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>( 1212 ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType, 1213 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1214 for (const fir::ExtendedValue &arg : args) 1215 if (!arg.getUnboxed() && !arg.getCharBox()) 1216 fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1217 if (outline) 1218 return outlineInExtendedWrapper(generator, name, resultType, args); 1219 return std::invoke(generator, *this, resultType, args); 1220 } 1221 1222 template <> 1223 fir::ExtendedValue 1224 IntrinsicLibrary::genElementalCall<IntrinsicLibrary::SubroutineGenerator>( 1225 SubroutineGenerator generator, llvm::StringRef name, mlir::Type resultType, 1226 llvm::ArrayRef<fir::ExtendedValue> args, bool outline) { 1227 for (const fir::ExtendedValue &arg : args) 1228 if (!arg.getUnboxed() && !arg.getCharBox()) 1229 // fir::emitFatalError(loc, "nonscalar intrinsic argument"); 1230 crashOnMissingIntrinsic(loc, name); 1231 if (outline) 1232 return outlineInExtendedWrapper(generator, name, resultType, args); 1233 std::invoke(generator, *this, args); 1234 return mlir::Value(); 1235 } 1236 1237 static fir::ExtendedValue 1238 invokeHandler(IntrinsicLibrary::ElementalGenerator generator, 1239 const IntrinsicHandler &handler, 1240 llvm::Optional<mlir::Type> resultType, 1241 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1242 IntrinsicLibrary &lib) { 1243 assert(resultType && "expect elemental intrinsic to be functions"); 1244 return lib.genElementalCall(generator, handler.name, *resultType, args, 1245 outline); 1246 } 1247 1248 static fir::ExtendedValue 1249 invokeHandler(IntrinsicLibrary::ExtendedGenerator generator, 1250 const IntrinsicHandler &handler, 1251 llvm::Optional<mlir::Type> resultType, 1252 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1253 IntrinsicLibrary &lib) { 1254 assert(resultType && "expect intrinsic function"); 1255 if (handler.isElemental) 1256 return lib.genElementalCall(generator, handler.name, *resultType, args, 1257 outline); 1258 if (outline) 1259 return lib.outlineInExtendedWrapper(generator, handler.name, *resultType, 1260 args); 1261 return std::invoke(generator, lib, *resultType, args); 1262 } 1263 1264 static fir::ExtendedValue 1265 invokeHandler(IntrinsicLibrary::SubroutineGenerator generator, 1266 const IntrinsicHandler &handler, 1267 llvm::Optional<mlir::Type> resultType, 1268 llvm::ArrayRef<fir::ExtendedValue> args, bool outline, 1269 IntrinsicLibrary &lib) { 1270 if (handler.isElemental) 1271 return lib.genElementalCall(generator, handler.name, mlir::Type{}, args, 1272 outline); 1273 if (outline) 1274 return lib.outlineInExtendedWrapper(generator, handler.name, resultType, 1275 args); 1276 std::invoke(generator, lib, args); 1277 return mlir::Value{}; 1278 } 1279 1280 fir::ExtendedValue 1281 IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name, 1282 llvm::Optional<mlir::Type> resultType, 1283 llvm::ArrayRef<fir::ExtendedValue> args) { 1284 if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) { 1285 bool outline = handler->outline || outlineAllIntrinsics; 1286 return std::visit( 1287 [&](auto &generator) -> fir::ExtendedValue { 1288 return invokeHandler(generator, *handler, resultType, args, outline, 1289 *this); 1290 }, 1291 handler->generator); 1292 } 1293 1294 if (!resultType) 1295 // Subroutine should have a handler, they are likely missing for now. 1296 crashOnMissingIntrinsic(loc, name); 1297 1298 // Try the runtime if no special handler was defined for the 1299 // intrinsic being called. Maths runtime only has numerical elemental. 1300 // No optional arguments are expected at this point, the code will 1301 // crash if it gets absent optional. 1302 1303 // FIXME: using toValue to get the type won't work with array arguments. 1304 llvm::SmallVector<mlir::Value> mlirArgs; 1305 for (const fir::ExtendedValue &extendedVal : args) { 1306 mlir::Value val = toValue(extendedVal, builder, loc); 1307 if (!val) 1308 // If an absent optional gets there, most likely its handler has just 1309 // not yet been defined. 1310 crashOnMissingIntrinsic(loc, name); 1311 mlirArgs.emplace_back(val); 1312 } 1313 mlir::FunctionType soughtFuncType = 1314 getFunctionType(*resultType, mlirArgs, builder); 1315 1316 IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator = 1317 getRuntimeCallGenerator(name, soughtFuncType); 1318 return genElementalCall(runtimeCallGenerator, name, *resultType, args, 1319 /* outline */ true); 1320 } 1321 1322 mlir::Value 1323 IntrinsicLibrary::invokeGenerator(ElementalGenerator generator, 1324 mlir::Type resultType, 1325 llvm::ArrayRef<mlir::Value> args) { 1326 return std::invoke(generator, *this, resultType, args); 1327 } 1328 1329 mlir::Value 1330 IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator, 1331 mlir::Type resultType, 1332 llvm::ArrayRef<mlir::Value> args) { 1333 return generator(builder, loc, args); 1334 } 1335 1336 mlir::Value 1337 IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator, 1338 mlir::Type resultType, 1339 llvm::ArrayRef<mlir::Value> args) { 1340 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1341 for (mlir::Value arg : args) 1342 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1343 auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs); 1344 return toValue(extendedResult, builder, loc); 1345 } 1346 1347 mlir::Value 1348 IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator, 1349 llvm::ArrayRef<mlir::Value> args) { 1350 llvm::SmallVector<fir::ExtendedValue> extendedArgs; 1351 for (mlir::Value arg : args) 1352 extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); 1353 std::invoke(generator, *this, extendedArgs); 1354 return {}; 1355 } 1356 1357 template <typename GeneratorType> 1358 mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator, 1359 llvm::StringRef name, 1360 mlir::FunctionType funcType, 1361 bool loadRefArguments) { 1362 std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType); 1363 mlir::FuncOp function = builder.getNamedFunction(wrapperName); 1364 if (!function) { 1365 // First time this wrapper is needed, build it. 1366 function = builder.createFunction(loc, wrapperName, funcType); 1367 function->setAttr("fir.intrinsic", builder.getUnitAttr()); 1368 auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; 1369 auto linkage = 1370 mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage); 1371 function->setAttr("llvm.linkage", linkage); 1372 function.addEntryBlock(); 1373 1374 // Create local context to emit code into the newly created function 1375 // This new function is not linked to a source file location, only 1376 // its calls will be. 1377 auto localBuilder = 1378 std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap()); 1379 localBuilder->setInsertionPointToStart(&function.front()); 1380 // Location of code inside wrapper of the wrapper is independent from 1381 // the location of the intrinsic call. 1382 mlir::Location localLoc = localBuilder->getUnknownLoc(); 1383 llvm::SmallVector<mlir::Value> localArguments; 1384 for (mlir::BlockArgument bArg : function.front().getArguments()) { 1385 auto refType = bArg.getType().dyn_cast<fir::ReferenceType>(); 1386 if (loadRefArguments && refType) { 1387 auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg); 1388 localArguments.push_back(loaded); 1389 } else { 1390 localArguments.push_back(bArg); 1391 } 1392 } 1393 1394 IntrinsicLibrary localLib{*localBuilder, localLoc}; 1395 1396 if constexpr (std::is_same_v<GeneratorType, SubroutineGenerator>) { 1397 localLib.invokeGenerator(generator, localArguments); 1398 localBuilder->create<mlir::func::ReturnOp>(localLoc); 1399 } else { 1400 assert(funcType.getNumResults() == 1 && 1401 "expect one result for intrinsic function wrapper type"); 1402 mlir::Type resultType = funcType.getResult(0); 1403 auto result = 1404 localLib.invokeGenerator(generator, resultType, localArguments); 1405 localBuilder->create<mlir::func::ReturnOp>(localLoc, result); 1406 } 1407 } else { 1408 // Wrapper was already built, ensure it has the sought type 1409 assert(function.getFunctionType() == funcType && 1410 "conflict between intrinsic wrapper types"); 1411 } 1412 return function; 1413 } 1414 1415 /// Helpers to detect absent optional (not yet supported in outlining). 1416 bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) { 1417 for (const fir::ExtendedValue &arg : args) 1418 if (!fir::getBase(arg)) 1419 return true; 1420 return false; 1421 } 1422 1423 template <typename GeneratorType> 1424 fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper( 1425 GeneratorType generator, llvm::StringRef name, 1426 llvm::Optional<mlir::Type> resultType, 1427 llvm::ArrayRef<fir::ExtendedValue> args) { 1428 if (hasAbsentOptional(args)) 1429 TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) + 1430 " with absent optional argument"); 1431 llvm::SmallVector<mlir::Value> mlirArgs; 1432 for (const auto &extendedVal : args) 1433 mlirArgs.emplace_back(toValue(extendedVal, builder, loc)); 1434 mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder); 1435 mlir::FuncOp wrapper = getWrapper(generator, name, funcType); 1436 auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs); 1437 if (resultType) 1438 return toExtendedValue(call.getResult(0), builder, loc); 1439 // Subroutine calls 1440 return mlir::Value{}; 1441 } 1442 1443 IntrinsicLibrary::RuntimeCallGenerator 1444 IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name, 1445 mlir::FunctionType soughtFuncType) { 1446 mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType); 1447 if (!funcOp) { 1448 std::string buffer("not yet implemented: missing intrinsic lowering: "); 1449 llvm::raw_string_ostream sstream(buffer); 1450 sstream << name << "\nrequested type was: " << soughtFuncType << '\n'; 1451 fir::emitFatalError(loc, buffer); 1452 } 1453 1454 mlir::FunctionType actualFuncType = funcOp.getFunctionType(); 1455 assert(actualFuncType.getNumResults() == soughtFuncType.getNumResults() && 1456 actualFuncType.getNumInputs() == soughtFuncType.getNumInputs() && 1457 actualFuncType.getNumResults() == 1 && "Bad intrinsic match"); 1458 1459 return [funcOp, actualFuncType, 1460 soughtFuncType](fir::FirOpBuilder &builder, mlir::Location loc, 1461 llvm::ArrayRef<mlir::Value> args) { 1462 llvm::SmallVector<mlir::Value> convertedArguments; 1463 for (auto [fst, snd] : llvm::zip(actualFuncType.getInputs(), args)) 1464 convertedArguments.push_back(builder.createConvert(loc, fst, snd)); 1465 auto call = builder.create<fir::CallOp>(loc, funcOp, convertedArguments); 1466 mlir::Type soughtType = soughtFuncType.getResult(0); 1467 return builder.createConvert(loc, soughtType, call.getResult(0)); 1468 }; 1469 } 1470 1471 void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) { 1472 assert(stmtCtx); 1473 fir::FirOpBuilder *bldr = &builder; 1474 stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); }); 1475 } 1476 1477 fir::ExtendedValue 1478 IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, 1479 mlir::Type resultType, 1480 llvm::StringRef intrinsicName) { 1481 fir::ExtendedValue res = 1482 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1483 return res.match( 1484 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1485 // Add cleanup code 1486 addCleanUpForTemp(loc, box.getAddr()); 1487 return box; 1488 }, 1489 [&](const fir::BoxValue &box) -> fir::ExtendedValue { 1490 // Add cleanup code 1491 auto addr = 1492 builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); 1493 addCleanUpForTemp(loc, addr); 1494 return box; 1495 }, 1496 [&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue { 1497 // Add cleanup code 1498 addCleanUpForTemp(loc, box.getAddr()); 1499 return box; 1500 }, 1501 [&](const mlir::Value &tempAddr) -> fir::ExtendedValue { 1502 // Add cleanup code 1503 addCleanUpForTemp(loc, tempAddr); 1504 return builder.create<fir::LoadOp>(loc, resultType, tempAddr); 1505 }, 1506 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1507 // Add cleanup code 1508 addCleanUpForTemp(loc, box.getAddr()); 1509 return box; 1510 }, 1511 [&](const auto &) -> fir::ExtendedValue { 1512 fir::emitFatalError(loc, "unexpected result for " + intrinsicName); 1513 }); 1514 } 1515 1516 //===----------------------------------------------------------------------===// 1517 // Code generators for the intrinsic 1518 //===----------------------------------------------------------------------===// 1519 1520 mlir::Value IntrinsicLibrary::genRuntimeCall(llvm::StringRef name, 1521 mlir::Type resultType, 1522 llvm::ArrayRef<mlir::Value> args) { 1523 mlir::FunctionType soughtFuncType = 1524 getFunctionType(resultType, args, builder); 1525 return getRuntimeCallGenerator(name, soughtFuncType)(builder, loc, args); 1526 } 1527 1528 // ABS 1529 mlir::Value IntrinsicLibrary::genAbs(mlir::Type resultType, 1530 llvm::ArrayRef<mlir::Value> args) { 1531 assert(args.size() == 1); 1532 mlir::Value arg = args[0]; 1533 mlir::Type type = arg.getType(); 1534 if (fir::isa_real(type)) { 1535 // Runtime call to fp abs. An alternative would be to use mlir 1536 // math::AbsFOp but it does not support all fir floating point types. 1537 return genRuntimeCall("abs", resultType, args); 1538 } 1539 if (auto intType = type.dyn_cast<mlir::IntegerType>()) { 1540 // At the time of this implementation there is no abs op in mlir. 1541 // So, implement abs here without branching. 1542 mlir::Value shift = 1543 builder.createIntegerConstant(loc, intType, intType.getWidth() - 1); 1544 auto mask = builder.create<mlir::arith::ShRSIOp>(loc, arg, shift); 1545 auto xored = builder.create<mlir::arith::XOrIOp>(loc, arg, mask); 1546 return builder.create<mlir::arith::SubIOp>(loc, xored, mask); 1547 } 1548 if (fir::isa_complex(type)) { 1549 // Use HYPOT to fulfill the no underflow/overflow requirement. 1550 auto parts = fir::factory::Complex{builder, loc}.extractParts(arg); 1551 llvm::SmallVector<mlir::Value> args = {parts.first, parts.second}; 1552 return genRuntimeCall("hypot", resultType, args); 1553 } 1554 llvm_unreachable("unexpected type in ABS argument"); 1555 } 1556 1557 // ADJUSTL & ADJUSTR 1558 template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc, 1559 mlir::Value, mlir::Value)> 1560 fir::ExtendedValue 1561 IntrinsicLibrary::genAdjustRtCall(mlir::Type resultType, 1562 llvm::ArrayRef<fir::ExtendedValue> args) { 1563 assert(args.size() == 1); 1564 mlir::Value string = builder.createBox(loc, args[0]); 1565 // Create a mutable fir.box to be passed to the runtime for the result. 1566 fir::MutableBoxValue resultMutableBox = 1567 fir::factory::createTempMutableBox(builder, loc, resultType); 1568 mlir::Value resultIrBox = 1569 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1570 1571 // Call the runtime -- the runtime will allocate the result. 1572 CallRuntime(builder, loc, resultIrBox, string); 1573 1574 // Read result from mutable fir.box and add it to the list of temps to be 1575 // finalized by the StatementContext. 1576 fir::ExtendedValue res = 1577 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1578 return res.match( 1579 [&](const fir::CharBoxValue &box) -> fir::ExtendedValue { 1580 addCleanUpForTemp(loc, fir::getBase(box)); 1581 return box; 1582 }, 1583 [&](const auto &) -> fir::ExtendedValue { 1584 fir::emitFatalError(loc, "result of ADJUSTL is not a scalar character"); 1585 }); 1586 } 1587 1588 // AIMAG 1589 mlir::Value IntrinsicLibrary::genAimag(mlir::Type resultType, 1590 llvm::ArrayRef<mlir::Value> args) { 1591 assert(args.size() == 1); 1592 return fir::factory::Complex{builder, loc}.extractComplexPart( 1593 args[0], true /* isImagPart */); 1594 } 1595 1596 // ALL 1597 fir::ExtendedValue 1598 IntrinsicLibrary::genAll(mlir::Type resultType, 1599 llvm::ArrayRef<fir::ExtendedValue> args) { 1600 1601 assert(args.size() == 2); 1602 // Handle required mask argument 1603 mlir::Value mask = builder.createBox(loc, args[0]); 1604 1605 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 1606 int rank = maskArry.rank(); 1607 assert(rank >= 1); 1608 1609 // Handle optional dim argument 1610 bool absentDim = isAbsent(args[1]); 1611 mlir::Value dim = 1612 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1613 : fir::getBase(args[1]); 1614 1615 if (rank == 1 || absentDim) 1616 return builder.createConvert(loc, resultType, 1617 fir::runtime::genAll(builder, loc, mask, dim)); 1618 1619 // else use the result descriptor AllDim() intrinsic 1620 1621 // Create mutable fir.box to be passed to the runtime for the result. 1622 1623 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 1624 fir::MutableBoxValue resultMutableBox = 1625 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1626 mlir::Value resultIrBox = 1627 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1628 1629 // Call runtime. The runtime is allocating the result. 1630 fir::runtime::genAllDescriptor(builder, loc, resultIrBox, mask, dim); 1631 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 1632 .match( 1633 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1634 addCleanUpForTemp(loc, box.getAddr()); 1635 return box; 1636 }, 1637 [&](const auto &) -> fir::ExtendedValue { 1638 fir::emitFatalError(loc, "Invalid result for ALL"); 1639 }); 1640 } 1641 1642 // ALLOCATED 1643 fir::ExtendedValue 1644 IntrinsicLibrary::genAllocated(mlir::Type resultType, 1645 llvm::ArrayRef<fir::ExtendedValue> args) { 1646 assert(args.size() == 1); 1647 return args[0].match( 1648 [&](const fir::MutableBoxValue &x) -> fir::ExtendedValue { 1649 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, x); 1650 }, 1651 [&](const auto &) -> fir::ExtendedValue { 1652 fir::emitFatalError(loc, 1653 "allocated arg not lowered to MutableBoxValue"); 1654 }); 1655 } 1656 1657 // ANY 1658 fir::ExtendedValue 1659 IntrinsicLibrary::genAny(mlir::Type resultType, 1660 llvm::ArrayRef<fir::ExtendedValue> args) { 1661 1662 assert(args.size() == 2); 1663 // Handle required mask argument 1664 mlir::Value mask = builder.createBox(loc, args[0]); 1665 1666 fir::BoxValue maskArry = builder.createBox(loc, args[0]); 1667 int rank = maskArry.rank(); 1668 assert(rank >= 1); 1669 1670 // Handle optional dim argument 1671 bool absentDim = isAbsent(args[1]); 1672 mlir::Value dim = 1673 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1674 : fir::getBase(args[1]); 1675 1676 if (rank == 1 || absentDim) 1677 return builder.createConvert(loc, resultType, 1678 fir::runtime::genAny(builder, loc, mask, dim)); 1679 1680 // else use the result descriptor AnyDim() intrinsic 1681 1682 // Create mutable fir.box to be passed to the runtime for the result. 1683 1684 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1); 1685 fir::MutableBoxValue resultMutableBox = 1686 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1687 mlir::Value resultIrBox = 1688 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1689 1690 // Call runtime. The runtime is allocating the result. 1691 fir::runtime::genAnyDescriptor(builder, loc, resultIrBox, mask, dim); 1692 return fir::factory::genMutableBoxRead(builder, loc, resultMutableBox) 1693 .match( 1694 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1695 addCleanUpForTemp(loc, box.getAddr()); 1696 return box; 1697 }, 1698 [&](const auto &) -> fir::ExtendedValue { 1699 fir::emitFatalError(loc, "Invalid result for ANY"); 1700 }); 1701 } 1702 1703 // ASSOCIATED 1704 fir::ExtendedValue 1705 IntrinsicLibrary::genAssociated(mlir::Type resultType, 1706 llvm::ArrayRef<fir::ExtendedValue> args) { 1707 assert(args.size() == 2); 1708 auto *pointer = 1709 args[0].match([&](const fir::MutableBoxValue &x) { return &x; }, 1710 [&](const auto &) -> const fir::MutableBoxValue * { 1711 fir::emitFatalError(loc, "pointer not a MutableBoxValue"); 1712 }); 1713 const fir::ExtendedValue &target = args[1]; 1714 if (isAbsent(target)) 1715 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, *pointer); 1716 1717 mlir::Value targetBox = builder.createBox(loc, target); 1718 if (fir::valueHasFirAttribute(fir::getBase(target), 1719 fir::getOptionalAttrName())) { 1720 // Subtle: contrary to other intrinsic optional arguments, disassociated 1721 // POINTER and unallocated ALLOCATABLE actual argument are not considered 1722 // absent here. This is because ASSOCIATED has special requirements for 1723 // TARGET actual arguments that are POINTERs. There is no precise 1724 // requirements for ALLOCATABLEs, but all existing Fortran compilers treat 1725 // them similarly to POINTERs. That is: unallocated TARGETs cause ASSOCIATED 1726 // to rerun false. The runtime deals with the disassociated/unallocated 1727 // case. Simply ensures that TARGET that are OPTIONAL get conditionally 1728 // emboxed here to convey the optional aspect to the runtime. 1729 auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), 1730 fir::getBase(target)); 1731 auto absentBox = builder.create<fir::AbsentOp>(loc, targetBox.getType()); 1732 targetBox = builder.create<mlir::arith::SelectOp>(loc, isPresent, targetBox, 1733 absentBox); 1734 } 1735 mlir::Value pointerBoxRef = 1736 fir::factory::getMutableIRBox(builder, loc, *pointer); 1737 auto pointerBox = builder.create<fir::LoadOp>(loc, pointerBoxRef); 1738 return Fortran::lower::genAssociated(builder, loc, pointerBox, targetBox); 1739 } 1740 1741 // CHAR 1742 fir::ExtendedValue 1743 IntrinsicLibrary::genChar(mlir::Type type, 1744 llvm::ArrayRef<fir::ExtendedValue> args) { 1745 // Optional KIND argument. 1746 assert(args.size() >= 1); 1747 const mlir::Value *arg = args[0].getUnboxed(); 1748 // expect argument to be a scalar integer 1749 if (!arg) 1750 mlir::emitError(loc, "CHAR intrinsic argument not unboxed"); 1751 fir::factory::CharacterExprHelper helper{builder, loc}; 1752 fir::CharacterType::KindTy kind = helper.getCharacterType(type).getFKind(); 1753 mlir::Value cast = helper.createSingletonFromCode(*arg, kind); 1754 mlir::Value len = 1755 builder.createIntegerConstant(loc, builder.getCharacterLengthType(), 1); 1756 return fir::CharBoxValue{cast, len}; 1757 } 1758 1759 // COUNT 1760 fir::ExtendedValue 1761 IntrinsicLibrary::genCount(mlir::Type resultType, 1762 llvm::ArrayRef<fir::ExtendedValue> args) { 1763 assert(args.size() == 3); 1764 1765 // Handle mask argument 1766 fir::BoxValue mask = builder.createBox(loc, args[0]); 1767 unsigned maskRank = mask.rank(); 1768 1769 assert(maskRank > 0); 1770 1771 // Handle optional dim argument 1772 bool absentDim = isAbsent(args[1]); 1773 mlir::Value dim = 1774 absentDim ? builder.createIntegerConstant(loc, builder.getIndexType(), 0) 1775 : fir::getBase(args[1]); 1776 1777 if (absentDim || maskRank == 1) { 1778 // Result is scalar if no dim argument or mask is rank 1. 1779 // So, call specialized Count runtime routine. 1780 return builder.createConvert( 1781 loc, resultType, 1782 fir::runtime::genCount(builder, loc, fir::getBase(mask), dim)); 1783 } 1784 1785 // Call general CountDim runtime routine. 1786 1787 // Handle optional kind argument 1788 bool absentKind = isAbsent(args[2]); 1789 mlir::Value kind = absentKind ? builder.createIntegerConstant( 1790 loc, builder.getIndexType(), 1791 builder.getKindMap().defaultIntegerKind()) 1792 : fir::getBase(args[2]); 1793 1794 // Create mutable fir.box to be passed to the runtime for the result. 1795 mlir::Type type = builder.getVarLenSeqTy(resultType, maskRank - 1); 1796 fir::MutableBoxValue resultMutableBox = 1797 fir::factory::createTempMutableBox(builder, loc, type); 1798 1799 mlir::Value resultIrBox = 1800 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1801 1802 fir::runtime::genCountDim(builder, loc, resultIrBox, fir::getBase(mask), dim, 1803 kind); 1804 1805 // Handle cleanup of allocatable result descriptor and return 1806 fir::ExtendedValue res = 1807 fir::factory::genMutableBoxRead(builder, loc, resultMutableBox); 1808 return res.match( 1809 [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { 1810 // Add cleanup code 1811 addCleanUpForTemp(loc, box.getAddr()); 1812 return box; 1813 }, 1814 [&](const auto &) -> fir::ExtendedValue { 1815 fir::emitFatalError(loc, "unexpected result for COUNT"); 1816 }); 1817 } 1818 1819 // CPU_TIME 1820 void IntrinsicLibrary::genCpuTime(llvm::ArrayRef<fir::ExtendedValue> args) { 1821 assert(args.size() == 1); 1822 const mlir::Value *arg = args[0].getUnboxed(); 1823 assert(arg && "nonscalar cpu_time argument"); 1824 mlir::Value res1 = Fortran::lower::genCpuTime(builder, loc); 1825 mlir::Value res2 = 1826 builder.createConvert(loc, fir::dyn_cast_ptrEleTy(arg->getType()), res1); 1827 builder.create<fir::StoreOp>(loc, res2, *arg); 1828 } 1829 1830 // CSHIFT 1831 fir::ExtendedValue 1832 IntrinsicLibrary::genCshift(mlir::Type resultType, 1833 llvm::ArrayRef<fir::ExtendedValue> args) { 1834 assert(args.size() == 3); 1835 1836 // Handle required ARRAY argument 1837 fir::BoxValue arrayBox = builder.createBox(loc, args[0]); 1838 mlir::Value array = fir::getBase(arrayBox); 1839 unsigned arrayRank = arrayBox.rank(); 1840 1841 // Create mutable fir.box to be passed to the runtime for the result. 1842 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank); 1843 fir::MutableBoxValue resultMutableBox = 1844 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1845 mlir::Value resultIrBox = 1846 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1847 1848 if (arrayRank == 1) { 1849 // Vector case 1850 // Handle required SHIFT argument as a scalar 1851 const mlir::Value *shiftAddr = args[1].getUnboxed(); 1852 assert(shiftAddr && "nonscalar CSHIFT argument"); 1853 auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr); 1854 1855 fir::runtime::genCshiftVector(builder, loc, resultIrBox, array, shift); 1856 } else { 1857 // Non-vector case 1858 // Handle required SHIFT argument as an array 1859 mlir::Value shift = builder.createBox(loc, args[1]); 1860 1861 // Handle optional DIM argument 1862 mlir::Value dim = 1863 isAbsent(args[2]) 1864 ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1865 : fir::getBase(args[2]); 1866 fir::runtime::genCshift(builder, loc, resultIrBox, array, shift, dim); 1867 } 1868 return readAndAddCleanUp(resultMutableBox, resultType, "CSHIFT"); 1869 } 1870 1871 // DATE_AND_TIME 1872 void IntrinsicLibrary::genDateAndTime(llvm::ArrayRef<fir::ExtendedValue> args) { 1873 assert(args.size() == 4 && "date_and_time has 4 args"); 1874 llvm::SmallVector<llvm::Optional<fir::CharBoxValue>> charArgs(3); 1875 for (unsigned i = 0; i < 3; ++i) 1876 if (const fir::CharBoxValue *charBox = args[i].getCharBox()) 1877 charArgs[i] = *charBox; 1878 1879 mlir::Value values = fir::getBase(args[3]); 1880 if (!values) 1881 values = builder.create<fir::AbsentOp>( 1882 loc, fir::BoxType::get(builder.getNoneType())); 1883 1884 Fortran::lower::genDateAndTime(builder, loc, charArgs[0], charArgs[1], 1885 charArgs[2], values); 1886 } 1887 1888 // DIM 1889 mlir::Value IntrinsicLibrary::genDim(mlir::Type resultType, 1890 llvm::ArrayRef<mlir::Value> args) { 1891 assert(args.size() == 2); 1892 if (resultType.isa<mlir::IntegerType>()) { 1893 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 1894 auto diff = builder.create<mlir::arith::SubIOp>(loc, args[0], args[1]); 1895 auto cmp = builder.create<mlir::arith::CmpIOp>( 1896 loc, mlir::arith::CmpIPredicate::sgt, diff, zero); 1897 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 1898 } 1899 assert(fir::isa_real(resultType) && "Only expects real and integer in DIM"); 1900 mlir::Value zero = builder.createRealZeroConstant(loc, resultType); 1901 auto diff = builder.create<mlir::arith::SubFOp>(loc, args[0], args[1]); 1902 auto cmp = builder.create<mlir::arith::CmpFOp>( 1903 loc, mlir::arith::CmpFPredicate::OGT, diff, zero); 1904 return builder.create<mlir::arith::SelectOp>(loc, cmp, diff, zero); 1905 } 1906 1907 // DOT_PRODUCT 1908 fir::ExtendedValue 1909 IntrinsicLibrary::genDotProduct(mlir::Type resultType, 1910 llvm::ArrayRef<fir::ExtendedValue> args) { 1911 return genDotProd(fir::runtime::genDotProduct, resultType, builder, loc, 1912 stmtCtx, args); 1913 } 1914 1915 // EOSHIFT 1916 fir::ExtendedValue 1917 IntrinsicLibrary::genEoshift(mlir::Type resultType, 1918 llvm::ArrayRef<fir::ExtendedValue> args) { 1919 assert(args.size() == 4); 1920 1921 // Handle required ARRAY argument 1922 fir::BoxValue arrayBox = builder.createBox(loc, args[0]); 1923 mlir::Value array = fir::getBase(arrayBox); 1924 unsigned arrayRank = arrayBox.rank(); 1925 1926 // Create mutable fir.box to be passed to the runtime for the result. 1927 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, arrayRank); 1928 fir::MutableBoxValue resultMutableBox = 1929 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 1930 mlir::Value resultIrBox = 1931 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 1932 1933 // Handle optional BOUNDARY argument 1934 mlir::Value boundary = 1935 isAbsent(args[2]) ? builder.create<fir::AbsentOp>( 1936 loc, fir::BoxType::get(builder.getNoneType())) 1937 : builder.createBox(loc, args[2]); 1938 1939 if (arrayRank == 1) { 1940 // Vector case 1941 // Handle required SHIFT argument as a scalar 1942 const mlir::Value *shiftAddr = args[1].getUnboxed(); 1943 assert(shiftAddr && "nonscalar EOSHIFT SHIFT argument"); 1944 auto shift = builder.create<fir::LoadOp>(loc, *shiftAddr); 1945 fir::runtime::genEoshiftVector(builder, loc, resultIrBox, array, shift, 1946 boundary); 1947 } else { 1948 // Non-vector case 1949 // Handle required SHIFT argument as an array 1950 mlir::Value shift = builder.createBox(loc, args[1]); 1951 1952 // Handle optional DIM argument 1953 mlir::Value dim = 1954 isAbsent(args[3]) 1955 ? builder.createIntegerConstant(loc, builder.getIndexType(), 1) 1956 : fir::getBase(args[3]); 1957 fir::runtime::genEoshift(builder, loc, resultIrBox, array, shift, boundary, 1958 dim); 1959 } 1960 return readAndAddCleanUp(resultMutableBox, resultType, 1961 "unexpected result for EOSHIFT"); 1962 } 1963 1964 // EXPONENT 1965 mlir::Value IntrinsicLibrary::genExponent(mlir::Type resultType, 1966 llvm::ArrayRef<mlir::Value> args) { 1967 assert(args.size() == 1); 1968 1969 return builder.createConvert( 1970 loc, resultType, 1971 fir::runtime::genExponent(builder, loc, resultType, 1972 fir::getBase(args[0]))); 1973 } 1974 1975 // FLOOR 1976 mlir::Value IntrinsicLibrary::genFloor(mlir::Type resultType, 1977 llvm::ArrayRef<mlir::Value> args) { 1978 // Optional KIND argument. 1979 assert(args.size() >= 1); 1980 mlir::Value arg = args[0]; 1981 // Use LLVM floor that returns real. 1982 mlir::Value floor = genRuntimeCall("floor", arg.getType(), {arg}); 1983 return builder.createConvert(loc, resultType, floor); 1984 } 1985 1986 // FRACTION 1987 mlir::Value IntrinsicLibrary::genFraction(mlir::Type resultType, 1988 llvm::ArrayRef<mlir::Value> args) { 1989 assert(args.size() == 1); 1990 1991 return builder.createConvert( 1992 loc, resultType, 1993 fir::runtime::genFraction(builder, loc, fir::getBase(args[0]))); 1994 } 1995 1996 // IAND 1997 mlir::Value IntrinsicLibrary::genIand(mlir::Type resultType, 1998 llvm::ArrayRef<mlir::Value> args) { 1999 assert(args.size() == 2); 2000 return builder.create<mlir::arith::AndIOp>(loc, args[0], args[1]); 2001 } 2002 2003 // IBCLR 2004 mlir::Value IntrinsicLibrary::genIbclr(mlir::Type resultType, 2005 llvm::ArrayRef<mlir::Value> args) { 2006 // A conformant IBCLR(I,POS) call satisfies: 2007 // POS >= 0 2008 // POS < BIT_SIZE(I) 2009 // Return: I & (!(1 << POS)) 2010 assert(args.size() == 2); 2011 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2012 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 2013 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2014 auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos); 2015 auto res = builder.create<mlir::arith::XOrIOp>(loc, ones, mask); 2016 return builder.create<mlir::arith::AndIOp>(loc, args[0], res); 2017 } 2018 2019 // IBITS 2020 mlir::Value IntrinsicLibrary::genIbits(mlir::Type resultType, 2021 llvm::ArrayRef<mlir::Value> args) { 2022 // A conformant IBITS(I,POS,LEN) call satisfies: 2023 // POS >= 0 2024 // LEN >= 0 2025 // POS + LEN <= BIT_SIZE(I) 2026 // Return: LEN == 0 ? 0 : (I >> POS) & (-1 >> (BIT_SIZE(I) - LEN)) 2027 // For a conformant call, implementing (I >> POS) with a signed or an 2028 // unsigned shift produces the same result. For a nonconformant call, 2029 // the two choices may produce different results. 2030 assert(args.size() == 3); 2031 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2032 mlir::Value len = builder.createConvert(loc, resultType, args[2]); 2033 mlir::Value bitSize = builder.createIntegerConstant( 2034 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2035 auto shiftCount = builder.create<mlir::arith::SubIOp>(loc, bitSize, len); 2036 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2037 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2038 auto mask = builder.create<mlir::arith::ShRUIOp>(loc, ones, shiftCount); 2039 auto res1 = builder.create<mlir::arith::ShRSIOp>(loc, args[0], pos); 2040 auto res2 = builder.create<mlir::arith::AndIOp>(loc, res1, mask); 2041 auto lenIsZero = builder.create<mlir::arith::CmpIOp>( 2042 loc, mlir::arith::CmpIPredicate::eq, len, zero); 2043 return builder.create<mlir::arith::SelectOp>(loc, lenIsZero, zero, res2); 2044 } 2045 2046 // IBSET 2047 mlir::Value IntrinsicLibrary::genIbset(mlir::Type resultType, 2048 llvm::ArrayRef<mlir::Value> args) { 2049 // A conformant IBSET(I,POS) call satisfies: 2050 // POS >= 0 2051 // POS < BIT_SIZE(I) 2052 // Return: I | (1 << POS) 2053 assert(args.size() == 2); 2054 mlir::Value pos = builder.createConvert(loc, resultType, args[1]); 2055 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 2056 auto mask = builder.create<mlir::arith::ShLIOp>(loc, one, pos); 2057 return builder.create<mlir::arith::OrIOp>(loc, args[0], mask); 2058 } 2059 2060 // ICHAR 2061 fir::ExtendedValue 2062 IntrinsicLibrary::genIchar(mlir::Type resultType, 2063 llvm::ArrayRef<fir::ExtendedValue> args) { 2064 // There can be an optional kind in second argument. 2065 assert(args.size() == 2); 2066 const fir::CharBoxValue *charBox = args[0].getCharBox(); 2067 if (!charBox) 2068 llvm::report_fatal_error("expected character scalar"); 2069 2070 fir::factory::CharacterExprHelper helper{builder, loc}; 2071 mlir::Value buffer = charBox->getBuffer(); 2072 mlir::Type bufferTy = buffer.getType(); 2073 mlir::Value charVal; 2074 if (auto charTy = bufferTy.dyn_cast<fir::CharacterType>()) { 2075 assert(charTy.singleton()); 2076 charVal = buffer; 2077 } else { 2078 // Character is in memory, cast to fir.ref<char> and load. 2079 mlir::Type ty = fir::dyn_cast_ptrEleTy(bufferTy); 2080 if (!ty) 2081 llvm::report_fatal_error("expected memory type"); 2082 // The length of in the character type may be unknown. Casting 2083 // to a singleton ref is required before loading. 2084 fir::CharacterType eleType = helper.getCharacterType(ty); 2085 fir::CharacterType charType = 2086 fir::CharacterType::get(builder.getContext(), eleType.getFKind(), 1); 2087 mlir::Type toTy = builder.getRefType(charType); 2088 mlir::Value cast = builder.createConvert(loc, toTy, buffer); 2089 charVal = builder.create<fir::LoadOp>(loc, cast); 2090 } 2091 LLVM_DEBUG(llvm::dbgs() << "ichar(" << charVal << ")\n"); 2092 auto code = helper.extractCodeFromSingleton(charVal); 2093 return builder.create<mlir::arith::ExtUIOp>(loc, resultType, code); 2094 } 2095 2096 // IEOR 2097 mlir::Value IntrinsicLibrary::genIeor(mlir::Type resultType, 2098 llvm::ArrayRef<mlir::Value> args) { 2099 assert(args.size() == 2); 2100 return builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]); 2101 } 2102 2103 // ISHFT 2104 mlir::Value IntrinsicLibrary::genIshft(mlir::Type resultType, 2105 llvm::ArrayRef<mlir::Value> args) { 2106 // A conformant ISHFT(I,SHIFT) call satisfies: 2107 // abs(SHIFT) <= BIT_SIZE(I) 2108 // Return: abs(SHIFT) >= BIT_SIZE(I) 2109 // ? 0 2110 // : SHIFT < 0 2111 // ? I >> abs(SHIFT) 2112 // : I << abs(SHIFT) 2113 assert(args.size() == 2); 2114 mlir::Value bitSize = builder.createIntegerConstant( 2115 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2116 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2117 mlir::Value shift = builder.createConvert(loc, resultType, args[1]); 2118 mlir::Value absShift = genAbs(resultType, {shift}); 2119 auto left = builder.create<mlir::arith::ShLIOp>(loc, args[0], absShift); 2120 auto right = builder.create<mlir::arith::ShRUIOp>(loc, args[0], absShift); 2121 auto shiftIsLarge = builder.create<mlir::arith::CmpIOp>( 2122 loc, mlir::arith::CmpIPredicate::sge, absShift, bitSize); 2123 auto shiftIsNegative = builder.create<mlir::arith::CmpIOp>( 2124 loc, mlir::arith::CmpIPredicate::slt, shift, zero); 2125 auto sel = 2126 builder.create<mlir::arith::SelectOp>(loc, shiftIsNegative, right, left); 2127 return builder.create<mlir::arith::SelectOp>(loc, shiftIsLarge, zero, sel); 2128 } 2129 2130 // ISHFTC 2131 mlir::Value IntrinsicLibrary::genIshftc(mlir::Type resultType, 2132 llvm::ArrayRef<mlir::Value> args) { 2133 // A conformant ISHFTC(I,SHIFT,SIZE) call satisfies: 2134 // SIZE > 0 2135 // SIZE <= BIT_SIZE(I) 2136 // abs(SHIFT) <= SIZE 2137 // if SHIFT > 0 2138 // leftSize = abs(SHIFT) 2139 // rightSize = SIZE - abs(SHIFT) 2140 // else [if SHIFT < 0] 2141 // leftSize = SIZE - abs(SHIFT) 2142 // rightSize = abs(SHIFT) 2143 // unchanged = SIZE == BIT_SIZE(I) ? 0 : (I >> SIZE) << SIZE 2144 // leftMaskShift = BIT_SIZE(I) - leftSize 2145 // rightMaskShift = BIT_SIZE(I) - rightSize 2146 // left = (I >> rightSize) & (-1 >> leftMaskShift) 2147 // right = (I & (-1 >> rightMaskShift)) << leftSize 2148 // Return: SHIFT == 0 || SIZE == abs(SHIFT) ? I : (unchanged | left | right) 2149 assert(args.size() == 3); 2150 mlir::Value bitSize = builder.createIntegerConstant( 2151 loc, resultType, resultType.cast<mlir::IntegerType>().getWidth()); 2152 mlir::Value I = args[0]; 2153 mlir::Value shift = builder.createConvert(loc, resultType, args[1]); 2154 mlir::Value size = 2155 args[2] ? builder.createConvert(loc, resultType, args[2]) : bitSize; 2156 mlir::Value zero = builder.createIntegerConstant(loc, resultType, 0); 2157 mlir::Value ones = builder.createIntegerConstant(loc, resultType, -1); 2158 mlir::Value absShift = genAbs(resultType, {shift}); 2159 auto elseSize = builder.create<mlir::arith::SubIOp>(loc, size, absShift); 2160 auto shiftIsZero = builder.create<mlir::arith::CmpIOp>( 2161 loc, mlir::arith::CmpIPredicate::eq, shift, zero); 2162 auto shiftEqualsSize = builder.create<mlir::arith::CmpIOp>( 2163 loc, mlir::arith::CmpIPredicate::eq, absShift, size); 2164 auto shiftIsNop = 2165 builder.create<mlir::arith::OrIOp>(loc, shiftIsZero, shiftEqualsSize); 2166 auto shiftIsPositive = builder.create<mlir::arith::CmpIOp>( 2167 loc, mlir::arith::CmpIPredicate::sgt, shift, zero); 2168 auto leftSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive, 2169 absShift, elseSize); 2170 auto rightSize = builder.create<mlir::arith::SelectOp>(loc, shiftIsPositive, 2171 elseSize, absShift); 2172 auto hasUnchanged = builder.create<mlir::arith::CmpIOp>( 2173 loc, mlir::arith::CmpIPredicate::ne, size, bitSize); 2174 auto unchangedTmp1 = builder.create<mlir::arith::ShRUIOp>(loc, I, size); 2175 auto unchangedTmp2 = 2176 builder.create<mlir::arith::ShLIOp>(loc, unchangedTmp1, size); 2177 auto unchanged = builder.create<mlir::arith::SelectOp>(loc, hasUnchanged, 2178 unchangedTmp2, zero); 2179 auto leftMaskShift = 2180 builder.create<mlir::arith::SubIOp>(loc, bitSize, leftSize); 2181 auto leftMask = 2182 builder.create<mlir::arith::ShRUIOp>(loc, ones, leftMaskShift); 2183 auto leftTmp = builder.create<mlir::arith::ShRUIOp>(loc, I, rightSize); 2184 auto left = builder.create<mlir::arith::AndIOp>(loc, leftTmp, leftMask); 2185 auto rightMaskShift = 2186 builder.create<mlir::arith::SubIOp>(loc, bitSize, rightSize); 2187 auto rightMask = 2188 builder.create<mlir::arith::ShRUIOp>(loc, ones, rightMaskShift); 2189 auto rightTmp = builder.create<mlir::arith::AndIOp>(loc, I, rightMask); 2190 auto right = builder.create<mlir::arith::ShLIOp>(loc, rightTmp, leftSize); 2191 auto resTmp = builder.create<mlir::arith::OrIOp>(loc, unchanged, left); 2192 auto res = builder.create<mlir::arith::OrIOp>(loc, resTmp, right); 2193 return builder.create<mlir::arith::SelectOp>(loc, shiftIsNop, I, res); 2194 } 2195 2196 // LEN 2197 // Note that this is only used for an unrestricted intrinsic LEN call. 2198 // Other uses of LEN are rewritten as descriptor inquiries by the front-end. 2199 fir::ExtendedValue 2200 IntrinsicLibrary::genLen(mlir::Type resultType, 2201 llvm::ArrayRef<fir::ExtendedValue> args) { 2202 // Optional KIND argument reflected in result type and otherwise ignored. 2203 assert(args.size() == 1 || args.size() == 2); 2204 mlir::Value len = fir::factory::readCharLen(builder, loc, args[0]); 2205 return builder.createConvert(loc, resultType, len); 2206 } 2207 2208 // LEN_TRIM 2209 fir::ExtendedValue 2210 IntrinsicLibrary::genLenTrim(mlir::Type resultType, 2211 llvm::ArrayRef<fir::ExtendedValue> args) { 2212 // Optional KIND argument reflected in result type and otherwise ignored. 2213 assert(args.size() == 1 || args.size() == 2); 2214 const fir::CharBoxValue *charBox = args[0].getCharBox(); 2215 if (!charBox) 2216 TODO(loc, "character array len_trim"); 2217 auto len = 2218 fir::factory::CharacterExprHelper(builder, loc).createLenTrim(*charBox); 2219 return builder.createConvert(loc, resultType, len); 2220 } 2221 2222 // LGE, LGT, LLE, LLT 2223 template <mlir::arith::CmpIPredicate pred> 2224 fir::ExtendedValue 2225 IntrinsicLibrary::genCharacterCompare(mlir::Type type, 2226 llvm::ArrayRef<fir::ExtendedValue> args) { 2227 assert(args.size() == 2); 2228 return fir::runtime::genCharCompare( 2229 builder, loc, pred, fir::getBase(args[0]), fir::getLen(args[0]), 2230 fir::getBase(args[1]), fir::getLen(args[1])); 2231 } 2232 2233 // Compare two FIR values and return boolean result as i1. 2234 template <Extremum extremum, ExtremumBehavior behavior> 2235 static mlir::Value createExtremumCompare(mlir::Location loc, 2236 fir::FirOpBuilder &builder, 2237 mlir::Value left, mlir::Value right) { 2238 static constexpr mlir::arith::CmpIPredicate integerPredicate = 2239 extremum == Extremum::Max ? mlir::arith::CmpIPredicate::sgt 2240 : mlir::arith::CmpIPredicate::slt; 2241 static constexpr mlir::arith::CmpFPredicate orderedCmp = 2242 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::OGT 2243 : mlir::arith::CmpFPredicate::OLT; 2244 mlir::Type type = left.getType(); 2245 mlir::Value result; 2246 if (fir::isa_real(type)) { 2247 // Note: the signaling/quit aspect of the result required by IEEE 2248 // cannot currently be obtained with LLVM without ad-hoc runtime. 2249 if constexpr (behavior == ExtremumBehavior::IeeeMinMaximumNumber) { 2250 // Return the number if one of the inputs is NaN and the other is 2251 // a number. 2252 auto leftIsResult = 2253 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 2254 auto rightIsNan = builder.create<mlir::arith::CmpFOp>( 2255 loc, mlir::arith::CmpFPredicate::UNE, right, right); 2256 result = 2257 builder.create<mlir::arith::OrIOp>(loc, leftIsResult, rightIsNan); 2258 } else if constexpr (behavior == ExtremumBehavior::IeeeMinMaximum) { 2259 // Always return NaNs if one the input is NaNs 2260 auto leftIsResult = 2261 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 2262 auto leftIsNan = builder.create<mlir::arith::CmpFOp>( 2263 loc, mlir::arith::CmpFPredicate::UNE, left, left); 2264 result = builder.create<mlir::arith::OrIOp>(loc, leftIsResult, leftIsNan); 2265 } else if constexpr (behavior == ExtremumBehavior::MinMaxss) { 2266 // If the left is a NaN, return the right whatever it is. 2267 result = 2268 builder.create<mlir::arith::CmpFOp>(loc, orderedCmp, left, right); 2269 } else if constexpr (behavior == ExtremumBehavior::PgfortranLlvm) { 2270 // If one of the operand is a NaN, return left whatever it is. 2271 static constexpr auto unorderedCmp = 2272 extremum == Extremum::Max ? mlir::arith::CmpFPredicate::UGT 2273 : mlir::arith::CmpFPredicate::ULT; 2274 result = 2275 builder.create<mlir::arith::CmpFOp>(loc, unorderedCmp, left, right); 2276 } else { 2277 // TODO: ieeeMinNum/ieeeMaxNum 2278 static_assert(behavior == ExtremumBehavior::IeeeMinMaxNum, 2279 "ieeeMinNum/ieeeMaxNum behavior not implemented"); 2280 } 2281 } else if (fir::isa_integer(type)) { 2282 result = 2283 builder.create<mlir::arith::CmpIOp>(loc, integerPredicate, left, right); 2284 } else if (fir::isa_char(type)) { 2285 // TODO: ! character min and max is tricky because the result 2286 // length is the length of the longest argument! 2287 // So we may need a temp. 2288 TODO(loc, "CHARACTER min and max"); 2289 } 2290 assert(result && "result must be defined"); 2291 return result; 2292 } 2293 2294 // MAXLOC 2295 fir::ExtendedValue 2296 IntrinsicLibrary::genMaxloc(mlir::Type resultType, 2297 llvm::ArrayRef<fir::ExtendedValue> args) { 2298 return genExtremumloc(fir::runtime::genMaxloc, fir::runtime::genMaxlocDim, 2299 resultType, builder, loc, stmtCtx, 2300 "unexpected result for Maxloc", args); 2301 } 2302 2303 // MAXVAL 2304 fir::ExtendedValue 2305 IntrinsicLibrary::genMaxval(mlir::Type resultType, 2306 llvm::ArrayRef<fir::ExtendedValue> args) { 2307 return genExtremumVal(fir::runtime::genMaxval, fir::runtime::genMaxvalDim, 2308 fir::runtime::genMaxvalChar, resultType, builder, loc, 2309 stmtCtx, "unexpected result for Maxval", args); 2310 } 2311 2312 // MINLOC 2313 fir::ExtendedValue 2314 IntrinsicLibrary::genMinloc(mlir::Type resultType, 2315 llvm::ArrayRef<fir::ExtendedValue> args) { 2316 return genExtremumloc(fir::runtime::genMinloc, fir::runtime::genMinlocDim, 2317 resultType, builder, loc, stmtCtx, 2318 "unexpected result for Minloc", args); 2319 } 2320 2321 // MINVAL 2322 fir::ExtendedValue 2323 IntrinsicLibrary::genMinval(mlir::Type resultType, 2324 llvm::ArrayRef<fir::ExtendedValue> args) { 2325 return genExtremumVal(fir::runtime::genMinval, fir::runtime::genMinvalDim, 2326 fir::runtime::genMinvalChar, resultType, builder, loc, 2327 stmtCtx, "unexpected result for Minval", args); 2328 } 2329 2330 // MIN and MAX 2331 template <Extremum extremum, ExtremumBehavior behavior> 2332 mlir::Value IntrinsicLibrary::genExtremum(mlir::Type, 2333 llvm::ArrayRef<mlir::Value> args) { 2334 assert(args.size() >= 1); 2335 mlir::Value result = args[0]; 2336 for (auto arg : args.drop_front()) { 2337 mlir::Value mask = 2338 createExtremumCompare<extremum, behavior>(loc, builder, result, arg); 2339 result = builder.create<mlir::arith::SelectOp>(loc, mask, result, arg); 2340 } 2341 return result; 2342 } 2343 2344 // MOD 2345 mlir::Value IntrinsicLibrary::genMod(mlir::Type resultType, 2346 llvm::ArrayRef<mlir::Value> args) { 2347 assert(args.size() == 2); 2348 if (resultType.isa<mlir::IntegerType>()) 2349 return builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]); 2350 2351 // Use runtime. Note that mlir::arith::RemFOp implements floating point 2352 // remainder, but it does not work with fir::Real type. 2353 // TODO: consider using mlir::arith::RemFOp when possible, that may help 2354 // folding and optimizations. 2355 return genRuntimeCall("mod", resultType, args); 2356 } 2357 2358 // MODULO 2359 mlir::Value IntrinsicLibrary::genModulo(mlir::Type resultType, 2360 llvm::ArrayRef<mlir::Value> args) { 2361 assert(args.size() == 2); 2362 // No floored modulo op in LLVM/MLIR yet. TODO: add one to MLIR. 2363 // In the meantime, use a simple inlined implementation based on truncated 2364 // modulo (MOD(A, P) implemented by RemIOp, RemFOp). This avoids making manual 2365 // division and multiplication from MODULO formula. 2366 // - If A/P > 0 or MOD(A,P)=0, then INT(A/P) = FLOOR(A/P), and MODULO = MOD. 2367 // - Otherwise, when A/P < 0 and MOD(A,P) !=0, then MODULO(A, P) = 2368 // A-FLOOR(A/P)*P = A-(INT(A/P)-1)*P = A-INT(A/P)*P+P = MOD(A,P)+P 2369 // Note that A/P < 0 if and only if A and P signs are different. 2370 if (resultType.isa<mlir::IntegerType>()) { 2371 auto remainder = 2372 builder.create<mlir::arith::RemSIOp>(loc, args[0], args[1]); 2373 auto argXor = builder.create<mlir::arith::XOrIOp>(loc, args[0], args[1]); 2374 mlir::Value zero = builder.createIntegerConstant(loc, argXor.getType(), 0); 2375 auto argSignDifferent = builder.create<mlir::arith::CmpIOp>( 2376 loc, mlir::arith::CmpIPredicate::slt, argXor, zero); 2377 auto remainderIsNotZero = builder.create<mlir::arith::CmpIOp>( 2378 loc, mlir::arith::CmpIPredicate::ne, remainder, zero); 2379 auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero, 2380 argSignDifferent); 2381 auto remPlusP = 2382 builder.create<mlir::arith::AddIOp>(loc, remainder, args[1]); 2383 return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP, 2384 remainder); 2385 } 2386 // Real case 2387 auto remainder = builder.create<mlir::arith::RemFOp>(loc, args[0], args[1]); 2388 mlir::Value zero = builder.createRealZeroConstant(loc, remainder.getType()); 2389 auto remainderIsNotZero = builder.create<mlir::arith::CmpFOp>( 2390 loc, mlir::arith::CmpFPredicate::UNE, remainder, zero); 2391 auto aLessThanZero = builder.create<mlir::arith::CmpFOp>( 2392 loc, mlir::arith::CmpFPredicate::OLT, args[0], zero); 2393 auto pLessThanZero = builder.create<mlir::arith::CmpFOp>( 2394 loc, mlir::arith::CmpFPredicate::OLT, args[1], zero); 2395 auto argSignDifferent = 2396 builder.create<mlir::arith::XOrIOp>(loc, aLessThanZero, pLessThanZero); 2397 auto mustAddP = builder.create<mlir::arith::AndIOp>(loc, remainderIsNotZero, 2398 argSignDifferent); 2399 auto remPlusP = builder.create<mlir::arith::AddFOp>(loc, remainder, args[1]); 2400 return builder.create<mlir::arith::SelectOp>(loc, mustAddP, remPlusP, 2401 remainder); 2402 } 2403 2404 // NINT 2405 mlir::Value IntrinsicLibrary::genNint(mlir::Type resultType, 2406 llvm::ArrayRef<mlir::Value> args) { 2407 assert(args.size() >= 1); 2408 // Skip optional kind argument to search the runtime; it is already reflected 2409 // in result type. 2410 return genRuntimeCall("nint", resultType, {args[0]}); 2411 } 2412 2413 // NOT 2414 mlir::Value IntrinsicLibrary::genNot(mlir::Type resultType, 2415 llvm::ArrayRef<mlir::Value> args) { 2416 assert(args.size() == 1); 2417 mlir::Value allOnes = builder.createIntegerConstant(loc, resultType, -1); 2418 return builder.create<mlir::arith::XOrIOp>(loc, args[0], allOnes); 2419 } 2420 2421 // NULL 2422 fir::ExtendedValue 2423 IntrinsicLibrary::genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue> args) { 2424 // NULL() without MOLD must be handled in the contexts where it can appear 2425 // (see table 16.5 of Fortran 2018 standard). 2426 assert(args.size() == 1 && isPresent(args[0]) && 2427 "MOLD argument required to lower NULL outside of any context"); 2428 const auto *mold = args[0].getBoxOf<fir::MutableBoxValue>(); 2429 assert(mold && "MOLD must be a pointer or allocatable"); 2430 fir::BoxType boxType = mold->getBoxTy(); 2431 mlir::Value boxStorage = builder.createTemporary(loc, boxType); 2432 mlir::Value box = fir::factory::createUnallocatedBox( 2433 builder, loc, boxType, mold->nonDeferredLenParams()); 2434 builder.create<fir::StoreOp>(loc, box, boxStorage); 2435 return fir::MutableBoxValue(boxStorage, mold->nonDeferredLenParams(), {}); 2436 } 2437 2438 // PACK 2439 fir::ExtendedValue 2440 IntrinsicLibrary::genPack(mlir::Type resultType, 2441 llvm::ArrayRef<fir::ExtendedValue> args) { 2442 [[maybe_unused]] auto numArgs = args.size(); 2443 assert(numArgs == 2 || numArgs == 3); 2444 2445 // Handle required array argument 2446 mlir::Value array = builder.createBox(loc, args[0]); 2447 2448 // Handle required mask argument 2449 mlir::Value mask = builder.createBox(loc, args[1]); 2450 2451 // Handle optional vector argument 2452 mlir::Value vector = isAbsent(args, 2) 2453 ? builder.create<fir::AbsentOp>( 2454 loc, fir::BoxType::get(builder.getI1Type())) 2455 : builder.createBox(loc, args[2]); 2456 2457 // Create mutable fir.box to be passed to the runtime for the result. 2458 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, 1); 2459 fir::MutableBoxValue resultMutableBox = 2460 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2461 mlir::Value resultIrBox = 2462 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2463 2464 fir::runtime::genPack(builder, loc, resultIrBox, array, mask, vector); 2465 2466 return readAndAddCleanUp(resultMutableBox, resultType, 2467 "unexpected result for PACK"); 2468 } 2469 2470 // PRODUCT 2471 fir::ExtendedValue 2472 IntrinsicLibrary::genProduct(mlir::Type resultType, 2473 llvm::ArrayRef<fir::ExtendedValue> args) { 2474 return genProdOrSum(fir::runtime::genProduct, fir::runtime::genProductDim, 2475 resultType, builder, loc, stmtCtx, 2476 "unexpected result for Product", args); 2477 } 2478 2479 // RANDOM_INIT 2480 void IntrinsicLibrary::genRandomInit(llvm::ArrayRef<fir::ExtendedValue> args) { 2481 assert(args.size() == 2); 2482 Fortran::lower::genRandomInit(builder, loc, fir::getBase(args[0]), 2483 fir::getBase(args[1])); 2484 } 2485 2486 // RANDOM_NUMBER 2487 void IntrinsicLibrary::genRandomNumber( 2488 llvm::ArrayRef<fir::ExtendedValue> args) { 2489 assert(args.size() == 1); 2490 Fortran::lower::genRandomNumber(builder, loc, fir::getBase(args[0])); 2491 } 2492 2493 // RANDOM_SEED 2494 void IntrinsicLibrary::genRandomSeed(llvm::ArrayRef<fir::ExtendedValue> args) { 2495 assert(args.size() == 3); 2496 for (int i = 0; i < 3; ++i) 2497 if (isPresent(args[i])) { 2498 Fortran::lower::genRandomSeed(builder, loc, i, fir::getBase(args[i])); 2499 return; 2500 } 2501 Fortran::lower::genRandomSeed(builder, loc, -1, mlir::Value{}); 2502 } 2503 2504 // SCAN 2505 fir::ExtendedValue 2506 IntrinsicLibrary::genScan(mlir::Type resultType, 2507 llvm::ArrayRef<fir::ExtendedValue> args) { 2508 2509 assert(args.size() == 4); 2510 2511 if (isAbsent(args[3])) { 2512 // Kind not specified, so call scan/verify runtime routine that is 2513 // specialized on the kind of characters in string. 2514 2515 // Handle required string base arg 2516 mlir::Value stringBase = fir::getBase(args[0]); 2517 2518 // Handle required set string base arg 2519 mlir::Value setBase = fir::getBase(args[1]); 2520 2521 // Handle kind argument; it is the kind of character in this case 2522 fir::KindTy kind = 2523 fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind( 2524 stringBase.getType()); 2525 2526 // Get string length argument 2527 mlir::Value stringLen = fir::getLen(args[0]); 2528 2529 // Get set string length argument 2530 mlir::Value setLen = fir::getLen(args[1]); 2531 2532 // Handle optional back argument 2533 mlir::Value back = 2534 isAbsent(args[2]) 2535 ? builder.createIntegerConstant(loc, builder.getI1Type(), 0) 2536 : fir::getBase(args[2]); 2537 2538 return builder.createConvert(loc, resultType, 2539 fir::runtime::genScan(builder, loc, kind, 2540 stringBase, stringLen, 2541 setBase, setLen, back)); 2542 } 2543 // else use the runtime descriptor version of scan/verify 2544 2545 // Handle optional argument, back 2546 auto makeRefThenEmbox = [&](mlir::Value b) { 2547 fir::LogicalType logTy = fir::LogicalType::get( 2548 builder.getContext(), builder.getKindMap().defaultLogicalKind()); 2549 mlir::Value temp = builder.createTemporary(loc, logTy); 2550 mlir::Value castb = builder.createConvert(loc, logTy, b); 2551 builder.create<fir::StoreOp>(loc, castb, temp); 2552 return builder.createBox(loc, temp); 2553 }; 2554 mlir::Value back = fir::isUnboxedValue(args[2]) 2555 ? makeRefThenEmbox(*args[2].getUnboxed()) 2556 : builder.create<fir::AbsentOp>( 2557 loc, fir::BoxType::get(builder.getI1Type())); 2558 2559 // Handle required string argument 2560 mlir::Value string = builder.createBox(loc, args[0]); 2561 2562 // Handle required set argument 2563 mlir::Value set = builder.createBox(loc, args[1]); 2564 2565 // Handle kind argument 2566 mlir::Value kind = fir::getBase(args[3]); 2567 2568 // Create result descriptor 2569 fir::MutableBoxValue resultMutableBox = 2570 fir::factory::createTempMutableBox(builder, loc, resultType); 2571 mlir::Value resultIrBox = 2572 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2573 2574 fir::runtime::genScanDescriptor(builder, loc, resultIrBox, string, set, back, 2575 kind); 2576 2577 // Handle cleanup of allocatable result descriptor and return 2578 return readAndAddCleanUp(resultMutableBox, resultType, "SCAN"); 2579 } 2580 2581 // SET_EXPONENT 2582 mlir::Value IntrinsicLibrary::genSetExponent(mlir::Type resultType, 2583 llvm::ArrayRef<mlir::Value> args) { 2584 assert(args.size() == 2); 2585 2586 return builder.createConvert( 2587 loc, resultType, 2588 fir::runtime::genSetExponent(builder, loc, fir::getBase(args[0]), 2589 fir::getBase(args[1]))); 2590 } 2591 2592 // SUM 2593 fir::ExtendedValue 2594 IntrinsicLibrary::genSum(mlir::Type resultType, 2595 llvm::ArrayRef<fir::ExtendedValue> args) { 2596 return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType, 2597 builder, loc, stmtCtx, "unexpected result for Sum", args); 2598 } 2599 2600 // SYSTEM_CLOCK 2601 void IntrinsicLibrary::genSystemClock(llvm::ArrayRef<fir::ExtendedValue> args) { 2602 assert(args.size() == 3); 2603 Fortran::lower::genSystemClock(builder, loc, fir::getBase(args[0]), 2604 fir::getBase(args[1]), fir::getBase(args[2])); 2605 } 2606 2607 // SIZE 2608 fir::ExtendedValue 2609 IntrinsicLibrary::genSize(mlir::Type resultType, 2610 llvm::ArrayRef<fir::ExtendedValue> args) { 2611 // Note that the value of the KIND argument is already reflected in the 2612 // resultType 2613 assert(args.size() == 3); 2614 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 2615 if (boxValue->hasAssumedRank()) 2616 TODO(loc, "SIZE intrinsic with assumed rank argument"); 2617 2618 // Get the ARRAY argument 2619 mlir::Value array = builder.createBox(loc, args[0]); 2620 2621 // The front-end rewrites SIZE without the DIM argument to 2622 // an array of SIZE with DIM in most cases, but it may not be 2623 // possible in some cases like when in SIZE(function_call()). 2624 if (isAbsent(args, 1)) 2625 return builder.createConvert(loc, resultType, 2626 fir::runtime::genSize(builder, loc, array)); 2627 2628 // Get the DIM argument. 2629 mlir::Value dim = fir::getBase(args[1]); 2630 if (!fir::isa_ref_type(dim.getType())) 2631 return builder.createConvert( 2632 loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim)); 2633 2634 mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim); 2635 return builder 2636 .genIfOp(loc, {resultType}, isDynamicallyAbsent, 2637 /*withElseRegion=*/true) 2638 .genThen([&]() { 2639 mlir::Value size = builder.createConvert( 2640 loc, resultType, fir::runtime::genSize(builder, loc, array)); 2641 builder.create<fir::ResultOp>(loc, size); 2642 }) 2643 .genElse([&]() { 2644 mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim); 2645 mlir::Value size = builder.createConvert( 2646 loc, resultType, 2647 fir::runtime::genSizeDim(builder, loc, array, dimValue)); 2648 builder.create<fir::ResultOp>(loc, size); 2649 }) 2650 .getResults()[0]; 2651 } 2652 2653 // TRANSFER 2654 fir::ExtendedValue 2655 IntrinsicLibrary::genTransfer(mlir::Type resultType, 2656 llvm::ArrayRef<fir::ExtendedValue> args) { 2657 2658 assert(args.size() >= 2); // args.size() == 2 when size argument is omitted. 2659 2660 // Handle source argument 2661 mlir::Value source = builder.createBox(loc, args[0]); 2662 2663 // Handle mold argument 2664 mlir::Value mold = builder.createBox(loc, args[1]); 2665 fir::BoxValue moldTmp = mold; 2666 unsigned moldRank = moldTmp.rank(); 2667 2668 bool absentSize = (args.size() == 2); 2669 2670 // Create mutable fir.box to be passed to the runtime for the result. 2671 mlir::Type type = (moldRank == 0 && absentSize) 2672 ? resultType 2673 : builder.getVarLenSeqTy(resultType, 1); 2674 fir::MutableBoxValue resultMutableBox = 2675 fir::factory::createTempMutableBox(builder, loc, type); 2676 2677 if (moldRank == 0 && absentSize) { 2678 // This result is a scalar in this case. 2679 mlir::Value resultIrBox = 2680 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2681 2682 Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold); 2683 } else { 2684 // The result is a rank one array in this case. 2685 mlir::Value resultIrBox = 2686 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2687 2688 if (absentSize) { 2689 Fortran::lower::genTransfer(builder, loc, resultIrBox, source, mold); 2690 } else { 2691 mlir::Value sizeArg = fir::getBase(args[2]); 2692 Fortran::lower::genTransferSize(builder, loc, resultIrBox, source, mold, 2693 sizeArg); 2694 } 2695 } 2696 return readAndAddCleanUp(resultMutableBox, resultType, 2697 "unexpected result for TRANSFER"); 2698 } 2699 2700 // LBOUND 2701 fir::ExtendedValue 2702 IntrinsicLibrary::genLbound(mlir::Type resultType, 2703 llvm::ArrayRef<fir::ExtendedValue> args) { 2704 // Calls to LBOUND that don't have the DIM argument, or for which 2705 // the DIM is a compile time constant, are folded to descriptor inquiries by 2706 // semantics. This function covers the situations where a call to the 2707 // runtime is required. 2708 assert(args.size() == 3); 2709 assert(!isAbsent(args[1])); 2710 if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>()) 2711 if (boxValue->hasAssumedRank()) 2712 TODO(loc, "LBOUND intrinsic with assumed rank argument"); 2713 2714 const fir::ExtendedValue &array = args[0]; 2715 mlir::Value box = array.match( 2716 [&](const fir::BoxValue &boxValue) -> mlir::Value { 2717 // This entity is mapped to a fir.box that may not contain the local 2718 // lower bound information if it is a dummy. Rebox it with the local 2719 // shape information. 2720 mlir::Value localShape = builder.createShape(loc, array); 2721 mlir::Value oldBox = boxValue.getAddr(); 2722 return builder.create<fir::ReboxOp>( 2723 loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{}); 2724 }, 2725 [&](const auto &) -> mlir::Value { 2726 // This a pointer/allocatable, or an entity not yet tracked with a 2727 // fir.box. For pointer/allocatable, createBox will forward the 2728 // descriptor that contains the correct lower bound information. For 2729 // other entities, a new fir.box will be made with the local lower 2730 // bounds. 2731 return builder.createBox(loc, array); 2732 }); 2733 2734 mlir::Value dim = fir::getBase(args[1]); 2735 return builder.createConvert( 2736 loc, resultType, 2737 fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim)); 2738 } 2739 2740 // UBOUND 2741 fir::ExtendedValue 2742 IntrinsicLibrary::genUbound(mlir::Type resultType, 2743 llvm::ArrayRef<fir::ExtendedValue> args) { 2744 assert(args.size() == 3 || args.size() == 2); 2745 if (args.size() == 3) { 2746 // Handle calls to UBOUND with the DIM argument, which return a scalar 2747 mlir::Value extent = fir::getBase(genSize(resultType, args)); 2748 mlir::Value lbound = fir::getBase(genLbound(resultType, args)); 2749 2750 mlir::Value one = builder.createIntegerConstant(loc, resultType, 1); 2751 mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one); 2752 return builder.create<mlir::arith::AddIOp>(loc, ubound, extent); 2753 } else { 2754 // Handle calls to UBOUND without the DIM argument, which return an array 2755 mlir::Value kind = isAbsent(args[1]) 2756 ? builder.createIntegerConstant( 2757 loc, builder.getIndexType(), 2758 builder.getKindMap().defaultIntegerKind()) 2759 : fir::getBase(args[1]); 2760 2761 // Create mutable fir.box to be passed to the runtime for the result. 2762 mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1); 2763 fir::MutableBoxValue resultMutableBox = 2764 fir::factory::createTempMutableBox(builder, loc, type); 2765 mlir::Value resultIrBox = 2766 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2767 2768 fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]), 2769 kind); 2770 2771 return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND"); 2772 } 2773 return mlir::Value(); 2774 } 2775 2776 // UNPACK 2777 fir::ExtendedValue 2778 IntrinsicLibrary::genUnpack(mlir::Type resultType, 2779 llvm::ArrayRef<fir::ExtendedValue> args) { 2780 assert(args.size() == 3); 2781 2782 // Handle required vector argument 2783 mlir::Value vector = builder.createBox(loc, args[0]); 2784 2785 // Handle required mask argument 2786 fir::BoxValue maskBox = builder.createBox(loc, args[1]); 2787 mlir::Value mask = fir::getBase(maskBox); 2788 unsigned maskRank = maskBox.rank(); 2789 2790 // Handle required field argument 2791 mlir::Value field = builder.createBox(loc, args[2]); 2792 2793 // Create mutable fir.box to be passed to the runtime for the result. 2794 mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, maskRank); 2795 fir::MutableBoxValue resultMutableBox = 2796 fir::factory::createTempMutableBox(builder, loc, resultArrayType); 2797 mlir::Value resultIrBox = 2798 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2799 2800 fir::runtime::genUnpack(builder, loc, resultIrBox, vector, mask, field); 2801 2802 return readAndAddCleanUp(resultMutableBox, resultType, 2803 "unexpected result for UNPACK"); 2804 } 2805 2806 // VERIFY 2807 fir::ExtendedValue 2808 IntrinsicLibrary::genVerify(mlir::Type resultType, 2809 llvm::ArrayRef<fir::ExtendedValue> args) { 2810 2811 assert(args.size() == 4); 2812 2813 if (isAbsent(args[3])) { 2814 // Kind not specified, so call scan/verify runtime routine that is 2815 // specialized on the kind of characters in string. 2816 2817 // Handle required string base arg 2818 mlir::Value stringBase = fir::getBase(args[0]); 2819 2820 // Handle required set string base arg 2821 mlir::Value setBase = fir::getBase(args[1]); 2822 2823 // Handle kind argument; it is the kind of character in this case 2824 fir::KindTy kind = 2825 fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind( 2826 stringBase.getType()); 2827 2828 // Get string length argument 2829 mlir::Value stringLen = fir::getLen(args[0]); 2830 2831 // Get set string length argument 2832 mlir::Value setLen = fir::getLen(args[1]); 2833 2834 // Handle optional back argument 2835 mlir::Value back = 2836 isAbsent(args[2]) 2837 ? builder.createIntegerConstant(loc, builder.getI1Type(), 0) 2838 : fir::getBase(args[2]); 2839 2840 return builder.createConvert( 2841 loc, resultType, 2842 fir::runtime::genVerify(builder, loc, kind, stringBase, stringLen, 2843 setBase, setLen, back)); 2844 } 2845 // else use the runtime descriptor version of scan/verify 2846 2847 // Handle optional argument, back 2848 auto makeRefThenEmbox = [&](mlir::Value b) { 2849 fir::LogicalType logTy = fir::LogicalType::get( 2850 builder.getContext(), builder.getKindMap().defaultLogicalKind()); 2851 mlir::Value temp = builder.createTemporary(loc, logTy); 2852 mlir::Value castb = builder.createConvert(loc, logTy, b); 2853 builder.create<fir::StoreOp>(loc, castb, temp); 2854 return builder.createBox(loc, temp); 2855 }; 2856 mlir::Value back = fir::isUnboxedValue(args[2]) 2857 ? makeRefThenEmbox(*args[2].getUnboxed()) 2858 : builder.create<fir::AbsentOp>( 2859 loc, fir::BoxType::get(builder.getI1Type())); 2860 2861 // Handle required string argument 2862 mlir::Value string = builder.createBox(loc, args[0]); 2863 2864 // Handle required set argument 2865 mlir::Value set = builder.createBox(loc, args[1]); 2866 2867 // Handle kind argument 2868 mlir::Value kind = fir::getBase(args[3]); 2869 2870 // Create result descriptor 2871 fir::MutableBoxValue resultMutableBox = 2872 fir::factory::createTempMutableBox(builder, loc, resultType); 2873 mlir::Value resultIrBox = 2874 fir::factory::getMutableIRBox(builder, loc, resultMutableBox); 2875 2876 fir::runtime::genVerifyDescriptor(builder, loc, resultIrBox, string, set, 2877 back, kind); 2878 2879 // Handle cleanup of allocatable result descriptor and return 2880 return readAndAddCleanUp(resultMutableBox, resultType, "VERIFY"); 2881 } 2882 2883 //===----------------------------------------------------------------------===// 2884 // Argument lowering rules interface 2885 //===----------------------------------------------------------------------===// 2886 2887 const Fortran::lower::IntrinsicArgumentLoweringRules * 2888 Fortran::lower::getIntrinsicArgumentLowering(llvm::StringRef intrinsicName) { 2889 if (const IntrinsicHandler *handler = findIntrinsicHandler(intrinsicName)) 2890 if (!handler->argLoweringRules.hasDefaultRules()) 2891 return &handler->argLoweringRules; 2892 return nullptr; 2893 } 2894 2895 /// Return how argument \p argName should be lowered given the rules for the 2896 /// intrinsic function. 2897 Fortran::lower::ArgLoweringRule Fortran::lower::lowerIntrinsicArgumentAs( 2898 mlir::Location loc, const IntrinsicArgumentLoweringRules &rules, 2899 llvm::StringRef argName) { 2900 for (const IntrinsicDummyArgument &arg : rules.args) { 2901 if (arg.name && arg.name == argName) 2902 return {arg.lowerAs, arg.handleDynamicOptional}; 2903 } 2904 fir::emitFatalError( 2905 loc, "internal: unknown intrinsic argument name in lowering '" + argName + 2906 "'"); 2907 } 2908 2909 //===----------------------------------------------------------------------===// 2910 // Public intrinsic call helpers 2911 //===----------------------------------------------------------------------===// 2912 2913 fir::ExtendedValue 2914 Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, 2915 llvm::StringRef name, 2916 llvm::Optional<mlir::Type> resultType, 2917 llvm::ArrayRef<fir::ExtendedValue> args, 2918 Fortran::lower::StatementContext &stmtCtx) { 2919 return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall( 2920 name, resultType, args); 2921 } 2922 2923 mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder, 2924 mlir::Location loc, 2925 llvm::ArrayRef<mlir::Value> args) { 2926 assert(args.size() > 0 && "max requires at least one argument"); 2927 return IntrinsicLibrary{builder, loc} 2928 .genExtremum<Extremum::Max, ExtremumBehavior::MinMaxss>(args[0].getType(), 2929 args); 2930 } 2931 2932 mlir::Value Fortran::lower::genPow(fir::FirOpBuilder &builder, 2933 mlir::Location loc, mlir::Type type, 2934 mlir::Value x, mlir::Value y) { 2935 return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); 2936 } 2937