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