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