diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h --- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h +++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h @@ -10,6 +10,8 @@ #define FORTRAN_LOWER_INTRINSICCALL_H #include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/Runtime/RTBuilder.h" +#include "flang/Runtime/iostat.h" #include namespace fir { @@ -27,6 +29,49 @@ std::optional resultType, llvm::ArrayRef args); +/// Enums used to templatize and share lowering of MIN and MAX. +enum class Extremum { Min, Max }; + +// There are different ways to deal with NaNs in MIN and MAX. +// Known existing behaviors are listed below and can be selected for +// f18 MIN/MAX implementation. +enum class ExtremumBehavior { + // Note: the Signaling/quiet aspect of NaNs in the behaviors below are + // not described because there is no way to control/observe such aspect in + // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this + // aspect that are therefore currently not enforced. In the descriptions + // below, NaNs can be signaling or quite. Returned NaNs may be signaling + // if one of the input NaN was signaling but it cannot be guaranteed either. + // Existing compilers using an IEEE behavior (gfortran) also do not fulfill + // signaling/quiet requirements. + IeeeMinMaximumNumber, + // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): + // If one of the argument is and number and the other is NaN, return the + // number. If both arguements are NaN, return NaN. + // Compilers: gfortran. + IeeeMinMaximum, + // IEEE minimum/maximum behavior (754-2019, section 9.6): + // If one of the argument is NaN, return NaN. + MinMaxss, + // x86 minss/maxss behavior: + // If the second argument is a number and the other is NaN, return the number. + // In all other cases where at least one operand is NaN, return NaN. + // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. + PgfortranLlvm, + // "Opposite of" x86 minss/maxss behavior: + // If the first argument is a number and the other is NaN, return the + // number. + // In all other cases where at least one operand is NaN, return NaN. + // Compilers: xlf (only for MIN), and pgfortran (with llvm). + IeeeMinMaxNum + // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): + // TODO: Not implemented. + // It is the only behavior where the signaling/quiet aspect of a NaN argument + // impacts if the result should be NaN or the argument that is a number. + // LLVM/MLIR do not provide ways to observe this aspect, so it is not + // possible to implement it without some target dependent runtime. +}; + /// Enum specifying how intrinsic argument evaluate::Expr should be /// lowered to fir::ExtendedValue to be passed to genIntrinsicCall. enum class LowerIntrinsicArgAs { @@ -63,6 +108,361 @@ /// an intrinsic. struct IntrinsicArgumentLoweringRules; +// TODO error handling -> return a code or directly emit messages ? +struct IntrinsicLibrary { + + // Constructors. + explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc) + : builder{builder}, loc{loc} {} + IntrinsicLibrary() = delete; + IntrinsicLibrary(const IntrinsicLibrary &) = delete; + + /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg + /// and expected result type \p resultType. Return the result and a boolean + /// that, if true, indicates that the result must be freed after use. + std::pair + genIntrinsicCall(llvm::StringRef name, std::optional resultType, + llvm::ArrayRef arg); + + /// Search a runtime function that is associated to the generic intrinsic name + /// and whose signature matches the intrinsic arguments and result types. + /// If no such runtime function is found but a runtime function associated + /// with the Fortran generic exists and has the same number of arguments, + /// conversions will be inserted before and/or after the call. This is to + /// mainly to allow 16 bits float support even-though little or no math + /// runtime is currently available for it. + mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, + llvm::ArrayRef); + + using RuntimeCallGenerator = std::function)>; + RuntimeCallGenerator + getRuntimeCallGenerator(llvm::StringRef name, + mlir::FunctionType soughtFuncType); + + void genAbort(llvm::ArrayRef); + /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in + /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation + /// if the argument is an integer, into llvm intrinsics if the argument is + /// real and to the `hypot` math routine if the argument is of complex type. + mlir::Value genAbs(mlir::Type, llvm::ArrayRef); + template + fir::ExtendedValue genAdjustRtCall(mlir::Type, + llvm::ArrayRef); + mlir::Value genAimag(mlir::Type, llvm::ArrayRef); + mlir::Value genAint(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genAllocated(mlir::Type, + llvm::ArrayRef); + mlir::Value genAnint(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef); + mlir::Value genAtand(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue + genCommandArgumentCount(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genAssociated(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genBesselJn(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genBesselYn(mlir::Type, + llvm::ArrayRef); + /// Lower a bitwise comparison intrinsic using the given comparator. + template + mlir::Value genBitwiseCompare(mlir::Type resultType, + llvm::ArrayRef args); + + mlir::Value genBtest(mlir::Type, llvm::ArrayRef); + mlir::Value genCeiling(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef); + template + fir::ExtendedValue genCharacterCompare(mlir::Type, + llvm::ArrayRef); + mlir::Value genCmplx(mlir::Type, llvm::ArrayRef); + mlir::Value genConjg(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef); + void genCpuTime(llvm::ArrayRef); + fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genCAssociatedCFunPtr(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genCAssociatedCPtr(mlir::Type, + llvm::ArrayRef); + void genCFPointer(llvm::ArrayRef); + fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genCLoc(mlir::Type, llvm::ArrayRef); + void genDateAndTime(llvm::ArrayRef); + mlir::Value genDim(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genDotProduct(mlir::Type, + llvm::ArrayRef); + mlir::Value genDprod(mlir::Type, llvm::ArrayRef); + mlir::Value genDshiftl(mlir::Type, llvm::ArrayRef); + mlir::Value genDshiftr(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef); + void genExit(llvm::ArrayRef); + mlir::Value genExponent(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genExtendsTypeOf(mlir::Type, + llvm::ArrayRef); + template + mlir::Value genExtremum(mlir::Type, llvm::ArrayRef); + mlir::Value genFloor(mlir::Type, llvm::ArrayRef); + mlir::Value genFraction(mlir::Type resultType, + mlir::ArrayRef args); + void genGetCommand(mlir::ArrayRef args); + void genGetCommandArgument(mlir::ArrayRef args); + void genGetEnvironmentVariable(llvm::ArrayRef); + fir::ExtendedValue genIall(mlir::Type, llvm::ArrayRef); + /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments + /// in the llvm::ArrayRef. + mlir::Value genIand(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genIany(mlir::Type, llvm::ArrayRef); + mlir::Value genIbclr(mlir::Type, llvm::ArrayRef); + mlir::Value genIbits(mlir::Type, llvm::ArrayRef); + mlir::Value genIbset(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genFindloc(mlir::Type, llvm::ArrayRef); + mlir::Value genIeeeIsFinite(mlir::Type, llvm::ArrayRef); + mlir::Value genIeeeIsNormal(mlir::Type, llvm::ArrayRef); + template + fir::ExtendedValue genIeeeTypeCompare(mlir::Type, + llvm::ArrayRef); + mlir::Value genIeor(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef); + mlir::Value genIor(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genIparity(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genIsContiguous(mlir::Type, + llvm::ArrayRef); + template + mlir::Value genIsIostatValue(mlir::Type, llvm::ArrayRef); + mlir::Value genIsNan(mlir::Type, llvm::ArrayRef); + mlir::Value genIsFPClass(mlir::Type, llvm::ArrayRef, + int fpclass); + mlir::Value genIshft(mlir::Type, llvm::ArrayRef); + mlir::Value genIshftc(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef); + mlir::Value genLeadz(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genLoc(mlir::Type, llvm::ArrayRef); + template + mlir::Value genMask(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMatmulTranspose(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMerge(mlir::Type, llvm::ArrayRef); + mlir::Value genMergeBits(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef); + mlir::Value genMod(mlir::Type, llvm::ArrayRef); + mlir::Value genModulo(mlir::Type, llvm::ArrayRef); + void genMoveAlloc(llvm::ArrayRef); + void genMvbits(llvm::ArrayRef); + mlir::Value genNearest(mlir::Type, llvm::ArrayRef); + mlir::Value genNint(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genNorm2(mlir::Type, llvm::ArrayRef); + mlir::Value genNot(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genParity(mlir::Type, llvm::ArrayRef); + mlir::Value genPopcnt(mlir::Type, llvm::ArrayRef); + mlir::Value genPoppar(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genPresent(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef); + void genRandomInit(llvm::ArrayRef); + void genRandomNumber(llvm::ArrayRef); + void genRandomSeed(llvm::ArrayRef); + fir::ExtendedValue genReduce(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef); + mlir::Value genRRSpacing(mlir::Type resultType, + llvm::ArrayRef args); + fir::ExtendedValue genSameTypeAs(mlir::Type, + llvm::ArrayRef); + mlir::Value genScale(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef); + mlir::Value genSelectedIntKind(mlir::Type, llvm::ArrayRef); + mlir::Value genSelectedRealKind(mlir::Type, llvm::ArrayRef); + mlir::Value genSetExponent(mlir::Type resultType, + llvm::ArrayRef args); + template + mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef); + mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef); + mlir::Value genSign(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef); + mlir::Value genSpacing(mlir::Type resultType, + llvm::ArrayRef args); + fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genStorageSize(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef); + void genSystemClock(llvm::ArrayRef); + mlir::Value genTrailz(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genTransfer(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genTranspose(mlir::Type, + llvm::ArrayRef); + fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef); + fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef); + /// Implement all conversion functions like DBLE, the first argument is + /// the value to convert. There may be an additional KIND arguments that + /// is ignored because this is already reflected in the result type. + mlir::Value genConversion(mlir::Type, llvm::ArrayRef); + + // PPC intrinsic handlers. + template + void genMtfsf(llvm::ArrayRef); + + /// In the template helper below: + /// - "FN func" is a callback to generate the related intrinsic runtime call. + /// - "FD funcDim" is a callback to generate the "dim" runtime call. + /// - "FC funcChar" is a callback to generate the character runtime call. + /// Helper for MinLoc/MaxLoc. + template + fir::ExtendedValue genExtremumloc(FN func, FD funcDim, llvm::StringRef errMsg, + mlir::Type, + llvm::ArrayRef); + template + /// Helper for MinVal/MaxVal. + fir::ExtendedValue genExtremumVal(FN func, FD funcDim, FC funcChar, + llvm::StringRef errMsg, + mlir::Type resultType, + llvm::ArrayRef args); + /// Process calls to Product, Sum, IAll, IAny, IParity intrinsic functions + template + fir::ExtendedValue genReduction(FN func, FD funcDim, llvm::StringRef errMsg, + mlir::Type resultType, + llvm::ArrayRef args); + + /// Define the different FIR generators that can be mapped to intrinsic to + /// generate the related code. + using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); + using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim); + using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime); + using Generator = + std::variant; + + /// All generators can be outlined. This will build a function named + /// "fir."+ + "." + and generate the + /// intrinsic implementation inside instead of at the intrinsic call sites. + /// This can be used to keep the FIR more readable. Only one function will + /// be generated for all the similar calls in a program. + /// If the Generator is nullptr, the wrapper uses genRuntimeCall. + template + mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name, + mlir::Type resultType, + llvm::ArrayRef args); + template + fir::ExtendedValue + outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, + std::optional resultType, + llvm::ArrayRef args); + + template + mlir::func::FuncOp getWrapper(GeneratorType, llvm::StringRef name, + mlir::FunctionType, + bool loadRefArguments = false); + + /// Generate calls to ElementalGenerator, handling the elemental aspects + template + fir::ExtendedValue + genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args, bool outline); + + /// Helper to invoke code generator for the intrinsics given arguments. + mlir::Value invokeGenerator(ElementalGenerator generator, + mlir::Type resultType, + llvm::ArrayRef args); + mlir::Value invokeGenerator(RuntimeCallGenerator generator, + mlir::Type resultType, + llvm::ArrayRef args); + mlir::Value invokeGenerator(ExtendedGenerator generator, + mlir::Type resultType, + llvm::ArrayRef args); + mlir::Value invokeGenerator(SubroutineGenerator generator, + llvm::ArrayRef args); + + /// Get pointer to unrestricted intrinsic. Generate the related unrestricted + /// intrinsic if it is not defined yet. + mlir::SymbolRefAttr + getUnrestrictedIntrinsicSymbolRefAttr(llvm::StringRef name, + mlir::FunctionType signature); + + /// Helper function for generating code clean-up for result descriptors + fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, + mlir::Type resultType, + llvm::StringRef errMsg); + + void setResultMustBeFreed() { resultMustBeFreed = true; } + + fir::FirOpBuilder &builder; + mlir::Location loc; + bool resultMustBeFreed = false; +}; + +struct IntrinsicDummyArgument { + const char *name = nullptr; + fir::LowerIntrinsicArgAs lowerAs = fir::LowerIntrinsicArgAs::Value; + bool handleDynamicOptional = false; +}; + +/// This is shared by intrinsics and intrinsic module procedures. +struct IntrinsicArgumentLoweringRules { + /// There is no more than 7 non repeated arguments in Fortran intrinsics. + IntrinsicDummyArgument args[7]; + constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } +}; + +/// Structure describing what needs to be done to lower intrinsic or intrinsic +/// module procedure "name". +struct IntrinsicHandler { + const char *name; + IntrinsicLibrary::Generator generator; + // The following may be omitted in the table below. + fir::IntrinsicArgumentLoweringRules argLoweringRules = {}; + bool isElemental = true; + /// Code heavy intrinsic can be outlined to make FIR + /// more readable. + bool outline = false; +}; + +struct RuntimeFunction { + // llvm::StringRef comparison operator are not constexpr, so use string_view. + using Key = std::string_view; + // Needed for implicit compare with keys. + constexpr operator Key() const { return key; } + Key key; // intrinsic name + + // Name of a runtime function that implements the operation. + llvm::StringRef symbol; + fir::runtime::FuncTypeBuilderFunc typeGenerator; +}; + +/// Callback type for generating lowering for a math operation. +using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location, + llvm::StringRef, mlir::FunctionType, + llvm::ArrayRef); + +struct MathOperation { + // llvm::StringRef comparison operator are not constexpr, so use string_view. + using Key = std::string_view; + // Needed for implicit compare with keys. + constexpr operator Key() const { return key; } + // Intrinsic name. + Key key; + + // Name of a runtime function that implements the operation. + llvm::StringRef runtimeFunc; + fir::runtime::FuncTypeBuilderFunc typeGenerator; + + // A callback to generate FIR for the intrinsic defined by 'key'. + // A callback may generate either dedicated MLIR operation(s) or + // a function call to a runtime function with name defined by + // 'runtimeFunc'. + MathGeneratorTy funcGenerator; +}; + /// Return argument lowering rules for an intrinsic. /// Returns a nullptr if all the intrinsic arguments should be lowered by value. const IntrinsicArgumentLoweringRules * diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -61,52 +61,9 @@ /// a call is generated for it. LLVM intrinsics are handled as a math /// runtime library here. -/// Enums used to templatize and share lowering of MIN and MAX. -enum class Extremum { Min, Max }; - -// There are different ways to deal with NaNs in MIN and MAX. -// Known existing behaviors are listed below and can be selected for -// f18 MIN/MAX implementation. -enum class ExtremumBehavior { - // Note: the Signaling/quiet aspect of NaNs in the behaviors below are - // not described because there is no way to control/observe such aspect in - // MLIR/LLVM yet. The IEEE behaviors come with requirements regarding this - // aspect that are therefore currently not enforced. In the descriptions - // below, NaNs can be signaling or quite. Returned NaNs may be signaling - // if one of the input NaN was signaling but it cannot be guaranteed either. - // Existing compilers using an IEEE behavior (gfortran) also do not fulfill - // signaling/quiet requirements. - IeeeMinMaximumNumber, - // IEEE minimumNumber/maximumNumber behavior (754-2019, section 9.6): - // If one of the argument is and number and the other is NaN, return the - // number. If both arguements are NaN, return NaN. - // Compilers: gfortran. - IeeeMinMaximum, - // IEEE minimum/maximum behavior (754-2019, section 9.6): - // If one of the argument is NaN, return NaN. - MinMaxss, - // x86 minss/maxss behavior: - // If the second argument is a number and the other is NaN, return the number. - // In all other cases where at least one operand is NaN, return NaN. - // Compilers: xlf (only for MAX), ifort, pgfortran -nollvm, and nagfor. - PgfortranLlvm, - // "Opposite of" x86 minss/maxss behavior: - // If the first argument is a number and the other is NaN, return the - // number. - // In all other cases where at least one operand is NaN, return NaN. - // Compilers: xlf (only for MIN), and pgfortran (with llvm). - IeeeMinMaxNum - // IEEE minNum/maxNum behavior (754-2008, section 5.3.1): - // TODO: Not implemented. - // It is the only behavior where the signaling/quiet aspect of a NaN argument - // impacts if the result should be NaN or the argument that is a number. - // LLVM/MLIR do not provide ways to observe this aspect, so it is not - // possible to implement it without some target dependent runtime. -}; +namespace fir { -fir::ExtendedValue fir::getAbsentIntrinsicArgument() { - return fir::UnboxedValue{}; -} +fir::ExtendedValue getAbsentIntrinsicArgument() { return fir::UnboxedValue{}; } /// Test if an ExtendedValue is absent. This is used to test if an intrinsic /// argument are absent at compile time. @@ -131,326 +88,6 @@ return !isStaticallyAbsent(exv); } -// TODO error handling -> return a code or directly emit messages ? -struct IntrinsicLibrary { - - // Constructors. - explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc) - : builder{builder}, loc{loc} {} - IntrinsicLibrary() = delete; - IntrinsicLibrary(const IntrinsicLibrary &) = delete; - - /// Generate FIR for call to Fortran intrinsic \p name with arguments \p arg - /// and expected result type \p resultType. Return the result and a boolean - /// that, if true, indicates that the result must be freed after use. - std::pair - genIntrinsicCall(llvm::StringRef name, std::optional resultType, - llvm::ArrayRef arg); - - /// Search a runtime function that is associated to the generic intrinsic name - /// and whose signature matches the intrinsic arguments and result types. - /// If no such runtime function is found but a runtime function associated - /// with the Fortran generic exists and has the same number of arguments, - /// conversions will be inserted before and/or after the call. This is to - /// mainly to allow 16 bits float support even-though little or no math - /// runtime is currently available for it. - mlir::Value genRuntimeCall(llvm::StringRef name, mlir::Type, - llvm::ArrayRef); - - using RuntimeCallGenerator = std::function)>; - RuntimeCallGenerator - getRuntimeCallGenerator(llvm::StringRef name, - mlir::FunctionType soughtFuncType); - - void genAbort(llvm::ArrayRef); - - /// Lowering for the ABS intrinsic. The ABS intrinsic expects one argument in - /// the llvm::ArrayRef. The ABS intrinsic is lowered into MLIR/FIR operation - /// if the argument is an integer, into llvm intrinsics if the argument is - /// real and to the `hypot` math routine if the argument is of complex type. - mlir::Value genAbs(mlir::Type, llvm::ArrayRef); - template - fir::ExtendedValue genAdjustRtCall(mlir::Type, - llvm::ArrayRef); - mlir::Value genAimag(mlir::Type, llvm::ArrayRef); - mlir::Value genAint(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genAllocated(mlir::Type, - llvm::ArrayRef); - mlir::Value genAnint(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef); - mlir::Value genAtand(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue - genCommandArgumentCount(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genAssociated(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genBesselJn(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genBesselYn(mlir::Type, - llvm::ArrayRef); - /// Lower a bitwise comparison intrinsic using the given comparator. - template - mlir::Value genBitwiseCompare(mlir::Type resultType, - llvm::ArrayRef args); - - mlir::Value genBtest(mlir::Type, llvm::ArrayRef); - mlir::Value genCeiling(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef); - template - fir::ExtendedValue genCharacterCompare(mlir::Type, - llvm::ArrayRef); - mlir::Value genCmplx(mlir::Type, llvm::ArrayRef); - mlir::Value genConjg(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef); - void genCpuTime(llvm::ArrayRef); - fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genCAssociatedCFunPtr(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genCAssociatedCPtr(mlir::Type, - llvm::ArrayRef); - void genCFPointer(llvm::ArrayRef); - fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genCLoc(mlir::Type, llvm::ArrayRef); - void genDateAndTime(llvm::ArrayRef); - mlir::Value genDim(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genDotProduct(mlir::Type, - llvm::ArrayRef); - mlir::Value genDprod(mlir::Type, llvm::ArrayRef); - mlir::Value genDshiftl(mlir::Type, llvm::ArrayRef); - mlir::Value genDshiftr(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef); - void genExit(llvm::ArrayRef); - mlir::Value genExponent(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genExtendsTypeOf(mlir::Type, - llvm::ArrayRef); - template - mlir::Value genExtremum(mlir::Type, llvm::ArrayRef); - mlir::Value genFloor(mlir::Type, llvm::ArrayRef); - mlir::Value genFraction(mlir::Type resultType, - mlir::ArrayRef args); - void genGetCommand(mlir::ArrayRef args); - void genGetCommandArgument(mlir::ArrayRef args); - void genGetEnvironmentVariable(llvm::ArrayRef); - fir::ExtendedValue genIall(mlir::Type, llvm::ArrayRef); - /// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments - /// in the llvm::ArrayRef. - mlir::Value genIand(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genIany(mlir::Type, llvm::ArrayRef); - mlir::Value genIbclr(mlir::Type, llvm::ArrayRef); - mlir::Value genIbits(mlir::Type, llvm::ArrayRef); - mlir::Value genIbset(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genFindloc(mlir::Type, llvm::ArrayRef); - mlir::Value genIeeeIsFinite(mlir::Type, llvm::ArrayRef); - mlir::Value genIeeeIsNormal(mlir::Type, llvm::ArrayRef); - template - fir::ExtendedValue genIeeeTypeCompare(mlir::Type, - llvm::ArrayRef); - mlir::Value genIeor(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef); - mlir::Value genIor(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genIparity(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genIsContiguous(mlir::Type, - llvm::ArrayRef); - template - mlir::Value genIsIostatValue(mlir::Type, llvm::ArrayRef); - mlir::Value genIsNan(mlir::Type, llvm::ArrayRef); - mlir::Value genIsFPClass(mlir::Type, llvm::ArrayRef, - int fpclass); - mlir::Value genIshft(mlir::Type, llvm::ArrayRef); - mlir::Value genIshftc(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef); - mlir::Value genLeadz(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genLoc(mlir::Type, llvm::ArrayRef); - template - mlir::Value genMask(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMatmulTranspose(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMerge(mlir::Type, llvm::ArrayRef); - mlir::Value genMergeBits(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef); - mlir::Value genMod(mlir::Type, llvm::ArrayRef); - mlir::Value genModulo(mlir::Type, llvm::ArrayRef); - void genMoveAlloc(llvm::ArrayRef); - void genMvbits(llvm::ArrayRef); - mlir::Value genNearest(mlir::Type, llvm::ArrayRef); - mlir::Value genNint(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genNorm2(mlir::Type, llvm::ArrayRef); - mlir::Value genNot(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genParity(mlir::Type, llvm::ArrayRef); - mlir::Value genPopcnt(mlir::Type, llvm::ArrayRef); - mlir::Value genPoppar(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genPresent(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef); - void genRandomInit(llvm::ArrayRef); - void genRandomNumber(llvm::ArrayRef); - void genRandomSeed(llvm::ArrayRef); - fir::ExtendedValue genReduce(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef); - mlir::Value genRRSpacing(mlir::Type resultType, - llvm::ArrayRef args); - fir::ExtendedValue genSameTypeAs(mlir::Type, - llvm::ArrayRef); - mlir::Value genScale(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef); - mlir::Value genSelectedIntKind(mlir::Type, llvm::ArrayRef); - mlir::Value genSelectedRealKind(mlir::Type, llvm::ArrayRef); - mlir::Value genSetExponent(mlir::Type resultType, - llvm::ArrayRef args); - template - mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef); - mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef); - mlir::Value genSign(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef); - mlir::Value genSpacing(mlir::Type resultType, - llvm::ArrayRef args); - fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genStorageSize(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef); - void genSystemClock(llvm::ArrayRef); - mlir::Value genTrailz(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genTransfer(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genTranspose(mlir::Type, - llvm::ArrayRef); - fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef); - fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef); - /// Implement all conversion functions like DBLE, the first argument is - /// the value to convert. There may be an additional KIND arguments that - /// is ignored because this is already reflected in the result type. - mlir::Value genConversion(mlir::Type, llvm::ArrayRef); - - // PPC intrinsic handlers. - template - void genMtfsf(llvm::ArrayRef); - - /// In the template helper below: - /// - "FN func" is a callback to generate the related intrinsic runtime call. - /// - "FD funcDim" is a callback to generate the "dim" runtime call. - /// - "FC funcChar" is a callback to generate the character runtime call. - /// Helper for MinLoc/MaxLoc. - template - fir::ExtendedValue genExtremumloc(FN func, FD funcDim, llvm::StringRef errMsg, - mlir::Type, - llvm::ArrayRef); - template - /// Helper for MinVal/MaxVal. - fir::ExtendedValue genExtremumVal(FN func, FD funcDim, FC funcChar, - llvm::StringRef errMsg, - mlir::Type resultType, - llvm::ArrayRef args); - /// Process calls to Product, Sum, IAll, IAny, IParity intrinsic functions - template - fir::ExtendedValue genReduction(FN func, FD funcDim, llvm::StringRef errMsg, - mlir::Type resultType, - llvm::ArrayRef args); - - /// Define the different FIR generators that can be mapped to intrinsic to - /// generate the related code. - using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); - using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim); - using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime); - using Generator = - std::variant; - - /// All generators can be outlined. This will build a function named - /// "fir."+ + "." + and generate the - /// intrinsic implementation inside instead of at the intrinsic call sites. - /// This can be used to keep the FIR more readable. Only one function will - /// be generated for all the similar calls in a program. - /// If the Generator is nullptr, the wrapper uses genRuntimeCall. - template - mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name, - mlir::Type resultType, - llvm::ArrayRef args); - template - fir::ExtendedValue - outlineInExtendedWrapper(GeneratorType, llvm::StringRef name, - std::optional resultType, - llvm::ArrayRef args); - - template - mlir::func::FuncOp getWrapper(GeneratorType, llvm::StringRef name, - mlir::FunctionType, - bool loadRefArguments = false); - - /// Generate calls to ElementalGenerator, handling the elemental aspects - template - fir::ExtendedValue - genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType, - llvm::ArrayRef args, bool outline); - - /// Helper to invoke code generator for the intrinsics given arguments. - mlir::Value invokeGenerator(ElementalGenerator generator, - mlir::Type resultType, - llvm::ArrayRef args); - mlir::Value invokeGenerator(RuntimeCallGenerator generator, - mlir::Type resultType, - llvm::ArrayRef args); - mlir::Value invokeGenerator(ExtendedGenerator generator, - mlir::Type resultType, - llvm::ArrayRef args); - mlir::Value invokeGenerator(SubroutineGenerator generator, - llvm::ArrayRef args); - - /// Get pointer to unrestricted intrinsic. Generate the related unrestricted - /// intrinsic if it is not defined yet. - mlir::SymbolRefAttr - getUnrestrictedIntrinsicSymbolRefAttr(llvm::StringRef name, - mlir::FunctionType signature); - - /// Helper function for generating code clean-up for result descriptors - fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox, - mlir::Type resultType, - llvm::StringRef errMsg); - - void setResultMustBeFreed() { resultMustBeFreed = true; } - - fir::FirOpBuilder &builder; - mlir::Location loc; - bool resultMustBeFreed = false; -}; - -struct IntrinsicDummyArgument { - const char *name = nullptr; - fir::LowerIntrinsicArgAs lowerAs = fir::LowerIntrinsicArgAs::Value; - bool handleDynamicOptional = false; -}; - -/// This is shared by intrinsics and intrinsic module procedures. -struct fir::IntrinsicArgumentLoweringRules { - /// There is no more than 7 non repeated arguments in Fortran intrinsics. - IntrinsicDummyArgument args[7]; - constexpr bool hasDefaultRules() const { return args[0].name == nullptr; } -}; - -/// Structure describing what needs to be done to lower intrinsic or intrinsic -/// module procedure "name". -struct IntrinsicHandler { - const char *name; - IntrinsicLibrary::Generator generator; - // The following may be omitted in the table below. - fir::IntrinsicArgumentLoweringRules argLoweringRules = {}; - bool isElemental = true; - /// Code heavy intrinsic can be outlined to make FIR - /// more readable. - bool outline = false; -}; - constexpr auto asValue = fir::LowerIntrinsicArgAs::Value; constexpr auto asAddr = fir::LowerIntrinsicArgAs::Addr; constexpr auto asBox = fir::LowerIntrinsicArgAs::Box; @@ -943,18 +580,6 @@ "dialect to lower complex operations"), llvm::cl::init(false)); -struct RuntimeFunction { - // llvm::StringRef comparison operator are not constexpr, so use string_view. - using Key = std::string_view; - // Needed for implicit compare with keys. - constexpr operator Key() const { return key; } - Key key; // intrinsic name - - // Name of a runtime function that implements the operation. - llvm::StringRef symbol; - fir::runtime::FuncTypeBuilderFunc typeGenerator; -}; - static mlir::FunctionType genF32F32FuncType(mlir::MLIRContext *context) { mlir::Type t = mlir::FloatType::getF32(context); return mlir::FunctionType::get(context, {t}, {t}); @@ -1101,30 +726,6 @@ return mlir::FunctionType::get(context, {ctype, itype}, {ctype}); } -/// Callback type for generating lowering for a math operation. -using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location, - llvm::StringRef, mlir::FunctionType, - llvm::ArrayRef); - -struct MathOperation { - // llvm::StringRef comparison operator are not constexpr, so use string_view. - using Key = std::string_view; - // Needed for implicit compare with keys. - constexpr operator Key() const { return key; } - // Intrinsic name. - Key key; - - // Name of a runtime function that implements the operation. - llvm::StringRef runtimeFunc; - fir::runtime::FuncTypeBuilderFunc typeGenerator; - - // A callback to generate FIR for the intrinsic defined by 'key'. - // A callback may generate either dedicated MLIR operation(s) or - // a function call to a runtime function with name defined by - // 'runtimeFunc'. - MathGeneratorTy funcGenerator; -}; - static mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef libFuncName, mlir::FunctionType libFuncType, @@ -5683,8 +5284,8 @@ // procedure. //===----------------------------------------------------------------------===// -const fir::IntrinsicArgumentLoweringRules * -fir::getIntrinsicArgumentLowering(llvm::StringRef specificName) { +const IntrinsicArgumentLoweringRules * +getIntrinsicArgumentLowering(llvm::StringRef specificName) { llvm::StringRef name = genericName(specificName); if (const IntrinsicHandler *handler = findIntrinsicHandler(name)) if (!handler->argLoweringRules.hasDefaultRules()) @@ -5695,8 +5296,8 @@ /// Return how argument \p argName should be lowered given the rules for the /// intrinsic function. fir::ArgLoweringRule -fir::lowerIntrinsicArgumentAs(const IntrinsicArgumentLoweringRules &rules, - unsigned position) { +lowerIntrinsicArgumentAs(const IntrinsicArgumentLoweringRules &rules, + unsigned position) { assert(position < sizeof(rules.args) / (sizeof(decltype(*rules.args))) && "invalid argument"); return {rules.args[position].lowerAs, @@ -5708,37 +5309,36 @@ //===----------------------------------------------------------------------===// std::pair -fir::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, - llvm::StringRef name, - std::optional resultType, - llvm::ArrayRef args) { +genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::StringRef name, std::optional resultType, + llvm::ArrayRef args) { return IntrinsicLibrary{builder, loc}.genIntrinsicCall(name, resultType, args); } -mlir::Value fir::genMax(fir::FirOpBuilder &builder, mlir::Location loc, - llvm::ArrayRef args) { +mlir::Value genMax(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::ArrayRef args) { assert(args.size() > 0 && "max requires at least one argument"); return IntrinsicLibrary{builder, loc} .genExtremum(args[0].getType(), args); } -mlir::Value fir::genMin(fir::FirOpBuilder &builder, mlir::Location loc, - llvm::ArrayRef args) { +mlir::Value genMin(fir::FirOpBuilder &builder, mlir::Location loc, + llvm::ArrayRef args) { assert(args.size() > 0 && "min requires at least one argument"); return IntrinsicLibrary{builder, loc} .genExtremum(args[0].getType(), args); } -mlir::Value fir::genDivC(fir::FirOpBuilder &builder, mlir::Location loc, - mlir::Type type, mlir::Value x, mlir::Value y) { +mlir::Value genDivC(fir::FirOpBuilder &builder, mlir::Location loc, + mlir::Type type, mlir::Value x, mlir::Value y) { return IntrinsicLibrary{builder, loc}.genRuntimeCall("divc", type, {x, y}); } -mlir::Value fir::genPow(fir::FirOpBuilder &builder, mlir::Location loc, - mlir::Type type, mlir::Value x, mlir::Value y) { +mlir::Value genPow(fir::FirOpBuilder &builder, mlir::Location loc, + mlir::Type type, mlir::Value x, mlir::Value y) { // TODO: since there is no libm version of pow with integer exponent, // we have to provide an alternative implementation for // "precise/strict" FP mode. @@ -5749,9 +5349,11 @@ return IntrinsicLibrary{builder, loc}.genRuntimeCall("pow", type, {x, y}); } -mlir::SymbolRefAttr fir::getUnrestrictedIntrinsicSymbolRefAttr( - fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name, - mlir::FunctionType signature) { +mlir::SymbolRefAttr +getUnrestrictedIntrinsicSymbolRefAttr(fir::FirOpBuilder &builder, + mlir::Location loc, llvm::StringRef name, + mlir::FunctionType signature) { return IntrinsicLibrary{builder, loc}.getUnrestrictedIntrinsicSymbolRefAttr( name, signature); } +} // namespace fir