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