//===-- Builder/IntrinsicCall.h -- lowering of intrinsics -------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef FORTRAN_LOWER_INTRINSICCALL_H
#define FORTRAN_LOWER_INTRINSICCALL_H
#include "flang/Lower/AbstractConverter.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/Runtime/Character.h"
#include "flang/Optimizer/Builder/Runtime/Numeric.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Runtime/entry-names.h"
#include "flang/Runtime/iostat.h"
#include "mlir/Dialect/Complex/IR/Complex.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/Math/IR/Math.h"
#include <optional>
namespace fir {
class StatementContext;
struct IntrinsicHandlerEntry;
/// Lower an intrinsic call given the intrinsic \p name, its \p resultType (that
/// must be std::nullopt if and only if this is a subroutine call), and its
/// lowered arguments \p args. The returned pair contains the result value
/// (null mlir::Value for subroutine calls), and a boolean that indicates if
/// this result must be freed after use.
std::pair<fir::ExtendedValue, bool>
genIntrinsicCall(fir::FirOpBuilder &, mlir::Location, llvm::StringRef name,
std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args,
Fortran::lower::AbstractConverter *converter = nullptr);
/// Same as the entry above except that instead of an intrinsic name it takes an
/// IntrinsicHandlerEntry obtained by a previous lookup for a handler to lower
/// this intrinsic (see lookupIntrinsicHandler).
std::pair<fir::ExtendedValue, bool>
genIntrinsicCall(fir::FirOpBuilder &, mlir::Location,
const IntrinsicHandlerEntry &,
std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args,
Fortran::lower::AbstractConverter *converter = nullptr);
/// 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 {
/// Lower argument to a value. Mainly intended for scalar arguments.
Value,
/// Lower argument to an address. Only valid when the argument properties are
/// fully defined (e.g. allocatable is allocated...).
Addr,
/// Lower argument to a box.
Box,
/// Lower argument without assuming that the argument is fully defined.
/// It can be used on unallocated allocatable, disassociated pointer,
/// or absent optional. This is meant for inquiry intrinsic arguments.
Inquired
};
/// Define how a given intrinsic argument must be lowered.
struct ArgLoweringRule {
LowerIntrinsicArgAs lowerAs;
/// Value:
// - Numerical: 0
// - Logical : false
// - Derived/character: not possible. Need custom intrinsic lowering.
// Addr:
// - nullptr
// Box:
// - absent box
// AsInquired:
// - no-op
bool handleDynamicOptional;
};
constexpr auto asValue = fir::LowerIntrinsicArgAs::Value;
constexpr auto asAddr = fir::LowerIntrinsicArgAs::Addr;
constexpr auto asBox = fir::LowerIntrinsicArgAs::Box;
constexpr auto asInquired = fir::LowerIntrinsicArgAs::Inquired;
/// Opaque class defining the argument lowering rules for all the argument of
/// 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,
Fortran::lower::AbstractConverter *converter = nullptr)
: builder{builder}, loc{loc}, converter{converter} {}
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<fir::ExtendedValue, bool>
genIntrinsicCall(llvm::StringRef name, std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> 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<mlir::Value>);
using RuntimeCallGenerator = std::function<mlir::Value(
fir::FirOpBuilder &, mlir::Location, llvm::ArrayRef<mlir::Value>)>;
RuntimeCallGenerator
getRuntimeCallGenerator(llvm::StringRef name,
mlir::FunctionType soughtFuncType);
/// Helper to generate TODOs for module procedures that must be intercepted in
/// lowering and are not yet implemented.
template <const char *intrinsicName>
void genModuleProcTODO(llvm::ArrayRef<fir::ExtendedValue>);
void genAbort(llvm::ArrayRef<fir::ExtendedValue>);
/// 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<mlir::Value>);
mlir::Value genAcosd(mlir::Type, llvm::ArrayRef<mlir::Value>);
template <void (*CallRuntime)(fir::FirOpBuilder &, mlir::Location loc,
mlir::Value, mlir::Value)>
fir::ExtendedValue genAdjustRtCall(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genAimag(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genAint(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genAll(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genAllocated(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genAnint(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genAny(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genAtanpi(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue
genCommandArgumentCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genAsind(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genAssociated(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genAtand(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genBesselJn(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genBesselYn(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpIPredicate pred>
mlir::Value genBitwiseCompare(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
mlir::Value genBtest(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genCeiling(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genChar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpIPredicate pred>
fir::ExtendedValue genCharacterCompare(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genCmplx(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genConjg(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genCount(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
void genCpuTime(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCAssociatedCFunPtr(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCAssociatedCPtr(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genErfcScaled(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
void genCFPointer(llvm::ArrayRef<fir::ExtendedValue>);
void genCFProcPointer(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCFunLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genCLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpIPredicate pred>
fir::ExtendedValue genCPtrCompare(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genCosd(mlir::Type, llvm::ArrayRef<mlir::Value>);
void genDateAndTime(llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genDim(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genDotProduct(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genDprod(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genDshiftl(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genDshiftr(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
void genExit(llvm::ArrayRef<fir::ExtendedValue>);
void genExecuteCommandLine(mlir::ArrayRef<fir::ExtendedValue> args);
fir::ExtendedValue genEtime(std::optional<mlir::Type>,
mlir::ArrayRef<fir::ExtendedValue> args);
mlir::Value genExponent(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genExtendsTypeOf(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
template <Extremum, ExtremumBehavior>
mlir::Value genExtremum(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genFloor(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genFraction(mlir::Type resultType,
mlir::ArrayRef<mlir::Value> args);
fir::ExtendedValue genGetCwd(std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
void genGetCommand(mlir::ArrayRef<fir::ExtendedValue> args);
mlir::Value genGetPID(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
void genGetCommandArgument(mlir::ArrayRef<fir::ExtendedValue> args);
void genGetEnvironmentVariable(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genIall(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genIany(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIbclr(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIbits(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIbset(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genIchar(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genFindloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIeeeClass(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeCopySign(mlir::Type, llvm::ArrayRef<mlir::Value>);
void genIeeeGetFlag(llvm::ArrayRef<fir::ExtendedValue>);
void genIeeeGetHaltingMode(llvm::ArrayRef<fir::ExtendedValue>);
template <bool isGet>
void genIeeeGetOrSetModes(llvm::ArrayRef<fir::ExtendedValue>);
template <bool isGet>
void genIeeeGetOrSetStatus(llvm::ArrayRef<fir::ExtendedValue>);
void genIeeeGetRoundingMode(llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIeeeIsFinite(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeIsNan(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeIsNegative(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeIsNormal(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeLogb(mlir::Type, mlir::ArrayRef<mlir::Value>);
template <bool isMax, bool isNum, bool isMag>
mlir::Value genIeeeMaxMin(mlir::Type, llvm::ArrayRef<mlir::Value>);
template <mlir::arith::CmpFPredicate pred>
mlir::Value genIeeeQuietCompare(mlir::Type resultType,
llvm::ArrayRef<mlir::Value>);
template <bool isFlag>
void genIeeeSetFlagOrHaltingMode(llvm::ArrayRef<fir::ExtendedValue>);
void genIeeeSetRoundingMode(llvm::ArrayRef<fir::ExtendedValue>);
template <mlir::arith::CmpFPredicate pred>
mlir::Value genIeeeSignalingCompare(mlir::Type resultType,
llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeSignbit(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue
genIeeeSupportFlagOrHalting(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIeeeSupportRounding(mlir::Type, llvm::ArrayRef<mlir::Value>);
template <mlir::arith::CmpIPredicate pred>
mlir::Value genIeeeTypeCompare(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeUnordered(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeeeValue(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIeor(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genIndex(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genIor(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genIparity(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genIsContiguous(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
template <Fortran::runtime::io::Iostat value>
mlir::Value genIsIostatValue(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIsFPClass(mlir::Type, llvm::ArrayRef<mlir::Value>,
int fpclass);
mlir::Value genIshft(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genIshftc(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genLeadz(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genLen(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genLenTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genLoc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
template <typename Shift>
mlir::Value genMask(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genMatmul(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genMatmulTranspose(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genMaxloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genMaxval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genMerge(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genMergeBits(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genMinloc(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genMinval(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genMod(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genModulo(mlir::Type, llvm::ArrayRef<mlir::Value>);
void genMoveAlloc(llvm::ArrayRef<fir::ExtendedValue>);
void genMvbits(llvm::ArrayRef<fir::ExtendedValue>);
enum class NearestProc { Nearest, NextAfter, NextDown, NextUp };
template <NearestProc>
mlir::Value genNearest(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genNint(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genNorm2(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genNot(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genNull(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genPack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genParity(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genPopcnt(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genPoppar(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genPresent(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genProduct(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
void genRandomInit(llvm::ArrayRef<fir::ExtendedValue>);
void genRandomNumber(llvm::ArrayRef<fir::ExtendedValue>);
void genRandomSeed(llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genReduce(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genReduceDim(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genRename(std::optional<mlir::Type>,
mlir::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genRepeat(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genReshape(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genRRSpacing(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
fir::ExtendedValue genSameTypeAs(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genScale(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genScan(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSecond(std::optional<mlir::Type>,
mlir::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSelectedCharKind(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genSelectedIntKind(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSelectedLogicalKind(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSelectedRealKind(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSetExponent(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
fir::ExtendedValue genShape(mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue>);
template <typename Shift>
mlir::Value genShift(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
mlir::Value genShiftA(mlir::Type resultType, llvm::ArrayRef<mlir::Value>);
mlir::Value genSign(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genSind(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSizeOf(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genSpacing(mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genStorageSize(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
void genSignalSubroutine(llvm::ArrayRef<fir::ExtendedValue>);
void genSleep(llvm::ArrayRef<fir::ExtendedValue>);
void genSystem(mlir::ArrayRef<fir::ExtendedValue> args);
void genSystemClock(llvm::ArrayRef<fir::ExtendedValue>);
mlir::Value genTand(mlir::Type, llvm::ArrayRef<mlir::Value>);
mlir::Value genTrailz(mlir::Type, llvm::ArrayRef<mlir::Value>);
fir::ExtendedValue genTransfer(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genTranspose(mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genTrim(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genUbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genUnpack(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
fir::ExtendedValue genVerify(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
/// 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<mlir::Value>);
/// 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 <typename FN, typename FD>
fir::ExtendedValue genExtremumloc(FN func, FD funcDim, llvm::StringRef errMsg,
mlir::Type,
llvm::ArrayRef<fir::ExtendedValue>);
template <typename FN, typename FD, typename FC>
/// Helper for MinVal/MaxVal.
fir::ExtendedValue genExtremumVal(FN func, FD funcDim, FC funcChar,
llvm::StringRef errMsg,
mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
/// Process calls to Product, Sum, IAll, IAny, IParity intrinsic functions
template <typename FN, typename FD>
fir::ExtendedValue genReduction(FN func, FD funcDim, llvm::StringRef errMsg,
mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
/// Generate code to raise \p excepts if \p cond is absent,
/// or present and true.
void genRaiseExcept(int excepts, mlir::Value cond = {});
/// Generate a quiet NaN of a given floating point type.
mlir::Value genQNan(mlir::Type resultType);
/// 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);
/// The generator for intrinsic that has both function and subroutine form.
using DualGenerator = decltype(&IntrinsicLibrary::genEtime);
using Generator = std::variant<ElementalGenerator, ExtendedGenerator,
SubroutineGenerator, DualGenerator>;
/// All generators can be outlined. This will build a function named
/// "fir."+ <generic name> + "." + <result type code> 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 <typename GeneratorType>
mlir::Value outlineInWrapper(GeneratorType, llvm::StringRef name,
mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
template <typename GeneratorType>
fir::ExtendedValue
outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
std::optional<mlir::Type> resultType,
llvm::ArrayRef<fir::ExtendedValue> args);
template <typename GeneratorType>
mlir::func::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
mlir::FunctionType,
bool loadRefArguments = false);
/// Generate calls to ElementalGenerator, handling the elemental aspects
template <typename GeneratorType>
fir::ExtendedValue
genElementalCall(GeneratorType, llvm::StringRef name, mlir::Type resultType,
llvm::ArrayRef<fir::ExtendedValue> args, bool outline);
/// Helper to invoke code generator for the intrinsics given arguments.
mlir::Value invokeGenerator(ElementalGenerator generator,
mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
mlir::Value invokeGenerator(RuntimeCallGenerator generator,
mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
mlir::Value invokeGenerator(ExtendedGenerator generator,
mlir::Type resultType,
llvm::ArrayRef<mlir::Value> args);
mlir::Value invokeGenerator(SubroutineGenerator generator,
llvm::ArrayRef<mlir::Value> args);
mlir::Value invokeGenerator(DualGenerator generator,
llvm::ArrayRef<mlir::Value> args);
mlir::Value invokeGenerator(DualGenerator generator, mlir::Type resultType,
llvm::ArrayRef<mlir::Value> 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;
Fortran::lower::AbstractConverter *converter = nullptr;
};
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;
};
struct MathOperation {
// Callback type for generating lowering for a math operation.
using MathGeneratorTy = mlir::Value (*)(fir::FirOpBuilder &, mlir::Location,
const MathOperation &,
mlir::FunctionType,
llvm::ArrayRef<mlir::Value>);
// Overrides fir::runtime::FuncTypeBuilderFunc to add FirOpBuilder argument.
using FuncTypeBuilderFunc = mlir::FunctionType (*)(mlir::MLIRContext *,
fir::FirOpBuilder &);
// 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;
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;
};
// Enum of most supported intrinsic argument or return types.
enum class ParamTypeId {
Void,
Address, // pointer (to an [array of] Integers of some kind)
Integer,
Real,
Complex,
IntegerVector,
UnsignedVector,
RealVector,
};
// Helper function to get length of a 16-byte vector of element type eleTy.
static int getVecLen(mlir::Type eleTy) {
assert((mlir::isa<mlir::IntegerType>(eleTy) ||
mlir::isa<mlir::FloatType>(eleTy)) &&
"unsupported vector element type");
return 16 / (eleTy.getIntOrFloatBitWidth() / 8);
}
template <ParamTypeId t, int k>
struct ParamType {
// Supported kinds can be checked with static asserts at compile time.
static_assert(t != ParamTypeId::Integer || k == 1 || k == 2 || k == 4 ||
k == 8,
"Unsupported integer kind");
static_assert(t != ParamTypeId::Real || k == 4 || k == 8 || k == 10 ||
k == 16,
"Unsupported real kind");
static_assert(t != ParamTypeId::Complex || k == 2 || k == 3 || k == 4 ||
k == 8 || k == 10 || k == 16,
"Unsupported complex kind");
static const ParamTypeId ty = t;
static const int kind = k;
};
// Namespace encapsulating type definitions for parameter types.
namespace Ty {
using Void = ParamType<ParamTypeId::Void, 0>;
template <int k>
using Address = ParamType<ParamTypeId::Address, k>;
template <int k>
using Integer = ParamType<ParamTypeId::Integer, k>;
template <int k>
using Real = ParamType<ParamTypeId::Real, k>;
template <int k>
using Complex = ParamType<ParamTypeId::Complex, k>;
template <int k>
using IntegerVector = ParamType<ParamTypeId::IntegerVector, k>;
template <int k>
using UnsignedVector = ParamType<ParamTypeId::UnsignedVector, k>;
template <int k>
using RealVector = ParamType<ParamTypeId::RealVector, k>;
} // namespace Ty
// Helper function that generates most types that are supported for intrinsic
// arguments and return type. Used by `genFuncType` to generate function
// types for most of the intrinsics.
static inline mlir::Type getTypeHelper(mlir::MLIRContext *context,
fir::FirOpBuilder &builder,
ParamTypeId typeId, int kind) {
mlir::Type r;
unsigned bits{0};
switch (typeId) {
case ParamTypeId::Void:
llvm::report_fatal_error("can not get type of void");
break;
case ParamTypeId::Address:
bits = builder.getKindMap().getIntegerBitsize(kind);
assert(bits != 0 && "failed to convert address kind to integer bitsize");
r = fir::ReferenceType::get(mlir::IntegerType::get(context, bits));
break;
case ParamTypeId::Integer:
case ParamTypeId::IntegerVector:
bits = builder.getKindMap().getIntegerBitsize(kind);
assert(bits != 0 && "failed to convert kind to integer bitsize");
r = mlir::IntegerType::get(context, bits);
break;
case ParamTypeId::UnsignedVector:
bits = builder.getKindMap().getIntegerBitsize(kind);
assert(bits != 0 && "failed to convert kind to unsigned bitsize");
r = mlir::IntegerType::get(context, bits, mlir::IntegerType::Unsigned);
break;
case ParamTypeId::Real:
case ParamTypeId::RealVector:
r = builder.getRealType(kind);
break;
case ParamTypeId::Complex:
r = fir::ComplexType::get(context, kind);
break;
}
switch (typeId) {
case ParamTypeId::Void:
case ParamTypeId::Address:
case ParamTypeId::Integer:
case ParamTypeId::Real:
case ParamTypeId::Complex:
break;
case ParamTypeId::IntegerVector:
case ParamTypeId::UnsignedVector:
case ParamTypeId::RealVector:
// convert to vector type
r = fir::VectorType::get(getVecLen(r), r);
}
return r;
}
// Generic function type generator that supports most of the function types
// used by intrinsics.
template <typename TyR, typename... ArgTys>
static inline mlir::FunctionType genFuncType(mlir::MLIRContext *context,
fir::FirOpBuilder &builder) {
llvm::SmallVector<ParamTypeId> argTys = {ArgTys::ty...};
llvm::SmallVector<int> argKinds = {ArgTys::kind...};
llvm::SmallVector<mlir::Type> argTypes;
for (size_t i = 0; i < argTys.size(); ++i) {
argTypes.push_back(getTypeHelper(context, builder, argTys[i], argKinds[i]));
}
if (TyR::ty == ParamTypeId::Void)
return mlir::FunctionType::get(context, argTypes, std::nullopt);
auto resType = getTypeHelper(context, builder, TyR::ty, TyR::kind);
return mlir::FunctionType::get(context, argTypes, {resType});
}
/// Entry into the tables describing how an intrinsic must be lowered.
struct IntrinsicHandlerEntry {
using RuntimeGeneratorRange =
std::pair<const MathOperation *, const MathOperation *>;
IntrinsicHandlerEntry(const IntrinsicHandler *handler) : entry{handler} {
assert(handler && "handler must not be nullptr");
};
IntrinsicHandlerEntry(RuntimeGeneratorRange rt) : entry{rt} {};
const IntrinsicArgumentLoweringRules *getArgumentLoweringRules() const;
std::variant<const IntrinsicHandler *, RuntimeGeneratorRange> entry;
};
//===----------------------------------------------------------------------===//
// Helper functions for argument handling.
//===----------------------------------------------------------------------===//
static inline mlir::Type getConvertedElementType(mlir::MLIRContext *context,
mlir::Type eleTy) {
if (mlir::isa<mlir::IntegerType>(eleTy) && !eleTy.isSignlessInteger()) {
const auto intTy{mlir::dyn_cast<mlir::IntegerType>(eleTy)};
auto newEleTy{mlir::IntegerType::get(context, intTy.getWidth())};
return newEleTy;
}
return eleTy;
}
static inline llvm::SmallVector<mlir::Value, 4>
getBasesForArgs(llvm::ArrayRef<fir::ExtendedValue> args) {
llvm::SmallVector<mlir::Value, 4> baseVec;
for (auto arg : args)
baseVec.push_back(getBase(arg));
return baseVec;
}
static inline llvm::SmallVector<mlir::Type, 4>
getTypesForArgs(llvm::ArrayRef<mlir::Value> args) {
llvm::SmallVector<mlir::Type, 4> typeVec;
for (auto arg : args)
typeVec.push_back(arg.getType());
return typeVec;
}
mlir::Value genLibCall(fir::FirOpBuilder &builder, mlir::Location loc,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args);
template <typename T>
mlir::Value genMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
const MathOperation &mathOp,
mlir::FunctionType mathLibFuncType,
llvm::ArrayRef<mlir::Value> args);
template <typename T>
mlir::Value genComplexMathOp(fir::FirOpBuilder &builder, mlir::Location loc,
const MathOperation &mathOp,
mlir::FunctionType mathLibFuncType,
llvm::ArrayRef<mlir::Value> args);
mlir::Value genLibSplitComplexArgsCall(fir::FirOpBuilder &builder,
mlir::Location loc,
const MathOperation &mathOp,
mlir::FunctionType libFuncType,
llvm::ArrayRef<mlir::Value> args);
/// Lookup for a handler or runtime call generator to lower intrinsic
/// \p intrinsicName.
std::optional<IntrinsicHandlerEntry>
lookupIntrinsicHandler(fir::FirOpBuilder &, llvm::StringRef intrinsicName,
std::optional<mlir::Type> resultType);
/// Generate a TODO error message for an as yet unimplemented intrinsic.
void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name);
/// Return argument lowering rules for an intrinsic.
/// Returns a nullptr if all the intrinsic arguments should be lowered by value.
const IntrinsicArgumentLoweringRules *
getIntrinsicArgumentLowering(llvm::StringRef intrinsicName);
/// Return how argument \p argName should be lowered given the rules for the
/// intrinsic function. The argument names are the one defined by the standard.
ArgLoweringRule lowerIntrinsicArgumentAs(const IntrinsicArgumentLoweringRules &,
unsigned position);
/// Return place-holder for absent intrinsic arguments.
fir::ExtendedValue getAbsentIntrinsicArgument();
/// Get SymbolRefAttr of runtime (or wrapper function containing inlined
// implementation) of an unrestricted intrinsic (defined by its signature
// and generic name)
mlir::SymbolRefAttr
getUnrestrictedIntrinsicSymbolRefAttr(fir::FirOpBuilder &, mlir::Location,
llvm::StringRef name,
mlir::FunctionType signature);
//===----------------------------------------------------------------------===//
// Direct access to intrinsics that may be used by lowering outside
// of intrinsic call lowering.
//===----------------------------------------------------------------------===//
/// Generate maximum. There must be at least one argument and all arguments
/// must have the same type.
mlir::Value genMax(fir::FirOpBuilder &, mlir::Location,
llvm::ArrayRef<mlir::Value> args);
/// Generate minimum. Same constraints as genMax.
mlir::Value genMin(fir::FirOpBuilder &, mlir::Location,
llvm::ArrayRef<mlir::Value> args);
/// Generate Complex divide with the given expected
/// result type.
mlir::Value genDivC(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Type resultType, mlir::Value x, mlir::Value y);
/// Generate power function x**y with the given expected
/// result type.
mlir::Value genPow(fir::FirOpBuilder &, mlir::Location, mlir::Type resultType,
mlir::Value x, mlir::Value y);
} // namespace fir
#endif // FORTRAN_LOWER_INTRINSICCALL_H