forked from OSchip/llvm-project
[flang] Lower sum intrinsic
This patch enables the lowering of the `sum` intrinsic. It adds also infrastructure to deal with optional arguments in intrinsics and implied loops. This patch is part of the upstreaming effort from fir-dev branch. Reviewed By: PeteSteinfeld Differential Revision: https://reviews.llvm.org/D121221 Co-authored-by: Eric Schweitz <eschweitz@nvidia.com> Co-authored-by: Jean Perier <jperier@nvidia.com> Co-authored-by: mleair <leairmark@gmail.com>
This commit is contained in:
parent
9bb8c80bea
commit
b3eb0e113e
|
@ -76,6 +76,9 @@ public:
|
||||||
/// Get the mlir instance of a symbol.
|
/// Get the mlir instance of a symbol.
|
||||||
virtual mlir::Value getSymbolAddress(SymbolRef sym) = 0;
|
virtual mlir::Value getSymbolAddress(SymbolRef sym) = 0;
|
||||||
|
|
||||||
|
/// Get the binding of an implied do variable by name.
|
||||||
|
virtual mlir::Value impliedDoBinding(llvm::StringRef name) = 0;
|
||||||
|
|
||||||
/// Get the label set associated with a symbol.
|
/// Get the label set associated with a symbol.
|
||||||
virtual bool lookupLabelSet(SymbolRef sym, pft::LabelSet &labelSet) = 0;
|
virtual bool lookupLabelSet(SymbolRef sym, pft::LabelSet &labelSet) = 0;
|
||||||
|
|
||||||
|
|
|
@ -140,6 +140,13 @@ void createAllocatableArrayAssignment(AbstractConverter &converter,
|
||||||
SymMap &symMap,
|
SymMap &symMap,
|
||||||
StatementContext &stmtCtx);
|
StatementContext &stmtCtx);
|
||||||
|
|
||||||
|
/// Lower an array expression with "parallel" semantics. Such a rhs expression
|
||||||
|
/// is fully evaluated prior to being assigned back to a temporary array.
|
||||||
|
fir::ExtendedValue createSomeArrayTempValue(AbstractConverter &converter,
|
||||||
|
const SomeExpr &expr,
|
||||||
|
SymMap &symMap,
|
||||||
|
StatementContext &stmtCtx);
|
||||||
|
|
||||||
// Attribute for an alloca that is a trivial adaptor for converting a value to
|
// Attribute for an alloca that is a trivial adaptor for converting a value to
|
||||||
// pass-by-ref semantics for a VALUE parameter. The optimizer may be able to
|
// pass-by-ref semantics for a VALUE parameter. The optimizer may be able to
|
||||||
// eliminate these.
|
// eliminate these.
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
//===-- Lower/CustomIntrinsicCall.h -----------------------------*- 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
///
|
||||||
|
/// Custom intrinsic lowering for the few intrinsic that have optional
|
||||||
|
/// arguments that prevents them to be handled in a more generic way in
|
||||||
|
/// IntrinsicCall.cpp.
|
||||||
|
/// The core principle is that this interface provides the intrinsic arguments
|
||||||
|
/// via callbacks to generate fir::ExtendedValue (instead of a list of
|
||||||
|
/// precomputed fir::ExtendedValue as done in the default intrinsic call
|
||||||
|
/// lowering). This gives more flexibility to only generate references to
|
||||||
|
/// dynamically optional arguments (pointers, allocatables, OPTIONAL dummies) in
|
||||||
|
/// a safe way.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifndef FORTRAN_LOWER_CUSTOMINTRINSICCALL_H
|
||||||
|
#define FORTRAN_LOWER_CUSTOMINTRINSICCALL_H
|
||||||
|
|
||||||
|
#include "flang/Lower/AbstractConverter.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Fortran {
|
||||||
|
|
||||||
|
namespace evaluate {
|
||||||
|
class ProcedureRef;
|
||||||
|
struct SpecificIntrinsic;
|
||||||
|
} // namespace evaluate
|
||||||
|
|
||||||
|
namespace lower {
|
||||||
|
|
||||||
|
/// Does the call \p procRef to \p intrinsic need to be handle via this custom
|
||||||
|
/// framework due to optional arguments. Otherwise, the tools from
|
||||||
|
/// IntrinsicCall.cpp should be used directly.
|
||||||
|
bool intrinsicRequiresCustomOptionalHandling(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
AbstractConverter &converter);
|
||||||
|
|
||||||
|
/// Type of callback to be provided to prepare the arguments fetching from an
|
||||||
|
/// actual argument expression.
|
||||||
|
using OperandPrepare = std::function<void(const Fortran::lower::SomeExpr &)>;
|
||||||
|
|
||||||
|
/// Type of the callback to inquire about an argument presence, once the call
|
||||||
|
/// preparation was done. An absent optional means the argument is statically
|
||||||
|
/// present. An mlir::Value means the presence must be checked at runtime, and
|
||||||
|
/// that the value contains the "is present" boolean value.
|
||||||
|
using OperandPresent = std::function<llvm::Optional<mlir::Value>(std::size_t)>;
|
||||||
|
|
||||||
|
/// Type of the callback to generate an argument reference after the call
|
||||||
|
/// preparation was done. For optional arguments, the utility guarantees
|
||||||
|
/// these callbacks will only be called in regions where the presence was
|
||||||
|
/// verified. This means the getter callback can dereference the argument
|
||||||
|
/// without any special care.
|
||||||
|
/// For elemental intrinsics, the getter must provide the current iteration
|
||||||
|
/// element value.
|
||||||
|
using OperandGetter = std::function<fir::ExtendedValue(std::size_t)>;
|
||||||
|
|
||||||
|
/// Given a callback \p prepareOptionalArgument to prepare optional
|
||||||
|
/// arguments and a callback \p prepareOtherArgument to prepare non-optional
|
||||||
|
/// arguments prepare the intrinsic arguments calls.
|
||||||
|
/// It is up to the caller to decide what argument preparation means,
|
||||||
|
/// the only contract is that it should later allow the caller to provide
|
||||||
|
/// callbacks to generate argument reference given an argument index without
|
||||||
|
/// any further knowledge of the argument. The function simply visits
|
||||||
|
/// the actual arguments, deciding which ones are dynamically optional,
|
||||||
|
/// and calling the callbacks accordingly in argument order.
|
||||||
|
void prepareCustomIntrinsicArgument(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
llvm::Optional<mlir::Type> retTy,
|
||||||
|
const OperandPrepare &prepareOptionalArgument,
|
||||||
|
const OperandPrepare &prepareOtherArgument, AbstractConverter &converter);
|
||||||
|
|
||||||
|
/// Given a callback \p getOperand to generate a reference to the i-th argument,
|
||||||
|
/// and a callback \p isPresentCheck to test if an argument is present, this
|
||||||
|
/// function lowers the intrinsic calls to \p name whose argument were
|
||||||
|
/// previously prepared with prepareCustomIntrinsicArgument. The elemental
|
||||||
|
/// aspects must be taken into account by the caller (i.e, the function should
|
||||||
|
/// be called during the loop nest generation for elemental intrinsics. It will
|
||||||
|
/// not generate any implicit loop nest on its own).
|
||||||
|
fir::ExtendedValue
|
||||||
|
lowerCustomIntrinsic(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
llvm::StringRef name, llvm::Optional<mlir::Type> retTy,
|
||||||
|
const OperandPresent &isPresentCheck,
|
||||||
|
const OperandGetter &getOperand, std::size_t numOperands,
|
||||||
|
Fortran::lower::StatementContext &stmtCtx);
|
||||||
|
} // namespace lower
|
||||||
|
} // namespace Fortran
|
||||||
|
|
||||||
|
#endif // FORTRAN_LOWER_CUSTOMINTRINSICCALL_H
|
|
@ -18,6 +18,8 @@ class ExtendedValue;
|
||||||
|
|
||||||
namespace Fortran::lower {
|
namespace Fortran::lower {
|
||||||
|
|
||||||
|
class StatementContext;
|
||||||
|
|
||||||
// TODO: Error handling interface ?
|
// TODO: Error handling interface ?
|
||||||
// TODO: Implementation is incomplete. Many intrinsics to tbd.
|
// TODO: Implementation is incomplete. Many intrinsics to tbd.
|
||||||
|
|
||||||
|
@ -27,7 +29,8 @@ namespace Fortran::lower {
|
||||||
fir::ExtendedValue genIntrinsicCall(fir::FirOpBuilder &, mlir::Location,
|
fir::ExtendedValue genIntrinsicCall(fir::FirOpBuilder &, mlir::Location,
|
||||||
llvm::StringRef name,
|
llvm::StringRef name,
|
||||||
llvm::Optional<mlir::Type> resultType,
|
llvm::Optional<mlir::Type> resultType,
|
||||||
llvm::ArrayRef<fir::ExtendedValue> args);
|
llvm::ArrayRef<fir::ExtendedValue> args,
|
||||||
|
StatementContext &);
|
||||||
|
|
||||||
/// Enum specifying how intrinsic argument evaluate::Expr should be
|
/// Enum specifying how intrinsic argument evaluate::Expr should be
|
||||||
/// lowered to fir::ExtendedValue to be passed to genIntrinsicCall.
|
/// lowered to fir::ExtendedValue to be passed to genIntrinsicCall.
|
||||||
|
|
|
@ -420,6 +420,18 @@ llvm::SmallVector<mlir::Value> getExtents(fir::FirOpBuilder &builder,
|
||||||
fir::ExtendedValue readBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
|
fir::ExtendedValue readBoxValue(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
const fir::BoxValue &box);
|
const fir::BoxValue &box);
|
||||||
|
|
||||||
|
/// Get non default (not all ones) lower bounds of \p exv. Returns empty
|
||||||
|
/// vector if the lower bounds are all ones.
|
||||||
|
llvm::SmallVector<mlir::Value>
|
||||||
|
getNonDefaultLowerBounds(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
const fir::ExtendedValue &exv);
|
||||||
|
|
||||||
|
/// Return length parameters associated to \p exv that are not deferred (that
|
||||||
|
/// are available without having to read any fir.box values).
|
||||||
|
/// Empty if \p exv has no length parameters or if they are all deferred.
|
||||||
|
llvm::SmallVector<mlir::Value>
|
||||||
|
getNonDeferredLengthParams(const fir::ExtendedValue &exv);
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// String literal helper helpers
|
// String literal helper helpers
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -127,6 +127,13 @@ inline bool isa_complex(mlir::Type t) {
|
||||||
/// Is `t` a CHARACTER type? Does not check the length.
|
/// Is `t` a CHARACTER type? Does not check the length.
|
||||||
inline bool isa_char(mlir::Type t) { return t.isa<fir::CharacterType>(); }
|
inline bool isa_char(mlir::Type t) { return t.isa<fir::CharacterType>(); }
|
||||||
|
|
||||||
|
/// Is `t` a trivial intrinsic type? CHARACTER is <em>excluded</em> because it
|
||||||
|
/// is a dependent type.
|
||||||
|
inline bool isa_trivial(mlir::Type t) {
|
||||||
|
return isa_integer(t) || isa_real(t) || isa_complex(t) ||
|
||||||
|
t.isa<fir::LogicalType>();
|
||||||
|
}
|
||||||
|
|
||||||
/// Is `t` a CHARACTER type with a LEN other than 1?
|
/// Is `t` a CHARACTER type with a LEN other than 1?
|
||||||
inline bool isa_char_string(mlir::Type t) {
|
inline bool isa_char_string(mlir::Type t) {
|
||||||
if (auto ct = t.dyn_cast_or_null<fir::CharacterType>())
|
if (auto ct = t.dyn_cast_or_null<fir::CharacterType>())
|
||||||
|
@ -184,6 +191,12 @@ inline bool singleIndirectionLevel(mlir::Type ty) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// Return true iff `ty` is the type of an ALLOCATABLE entity or value.
|
||||||
|
bool isAllocatableType(mlir::Type ty);
|
||||||
|
|
||||||
|
/// Return true iff `ty` is a RecordType with members that are allocatable.
|
||||||
|
bool isRecordWithAllocatableMember(mlir::Type ty);
|
||||||
|
|
||||||
/// Return true iff `ty` is a RecordType with type parameters.
|
/// Return true iff `ty` is a RecordType with type parameters.
|
||||||
inline bool isRecordWithTypeParameters(mlir::Type ty) {
|
inline bool isRecordWithTypeParameters(mlir::Type ty) {
|
||||||
if (auto recTy = ty.dyn_cast_or_null<fir::RecordType>())
|
if (auto recTy = ty.dyn_cast_or_null<fir::RecordType>())
|
||||||
|
|
|
@ -177,6 +177,13 @@ public:
|
||||||
return lookupSymbol(sym).getAddr();
|
return lookupSymbol(sym).getAddr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mlir::Value impliedDoBinding(llvm::StringRef name) override final {
|
||||||
|
mlir::Value val = localSymbols.lookupImpliedDo(name);
|
||||||
|
if (!val)
|
||||||
|
fir::emitFatalError(toLocation(), "ac-do-variable has no binding");
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
bool lookupLabelSet(Fortran::lower::SymbolRef sym,
|
bool lookupLabelSet(Fortran::lower::SymbolRef sym,
|
||||||
Fortran::lower::pft::LabelSet &labelSet) override final {
|
Fortran::lower::pft::LabelSet &labelSet) override final {
|
||||||
Fortran::lower::pft::FunctionLikeUnit &owningProc =
|
Fortran::lower::pft::FunctionLikeUnit &owningProc =
|
||||||
|
@ -818,6 +825,13 @@ private:
|
||||||
return cond;
|
return cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isArraySectionWithoutVectorSubscript(const Fortran::lower::SomeExpr &expr) {
|
||||||
|
return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
|
||||||
|
!Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) &&
|
||||||
|
!Fortran::evaluate::HasVectorSubscript(expr);
|
||||||
|
}
|
||||||
|
|
||||||
[[maybe_unused]] static bool
|
[[maybe_unused]] static bool
|
||||||
isFuncResultDesignator(const Fortran::lower::SomeExpr &expr) {
|
isFuncResultDesignator(const Fortran::lower::SomeExpr &expr) {
|
||||||
const Fortran::semantics::Symbol *sym =
|
const Fortran::semantics::Symbol *sym =
|
||||||
|
@ -1086,6 +1100,15 @@ private:
|
||||||
TODO(toLocation(), "SelectCaseStmt lowering");
|
TODO(toLocation(), "SelectCaseStmt lowering");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fir::ExtendedValue
|
||||||
|
genAssociateSelector(const Fortran::lower::SomeExpr &selector,
|
||||||
|
Fortran::lower::StatementContext &stmtCtx) {
|
||||||
|
return isArraySectionWithoutVectorSubscript(selector)
|
||||||
|
? Fortran::lower::createSomeArrayBox(*this, selector,
|
||||||
|
localSymbols, stmtCtx)
|
||||||
|
: genExprAddr(selector, stmtCtx);
|
||||||
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::AssociateConstruct &) {
|
void genFIR(const Fortran::parser::AssociateConstruct &) {
|
||||||
TODO(toLocation(), "AssociateConstruct lowering");
|
TODO(toLocation(), "AssociateConstruct lowering");
|
||||||
}
|
}
|
||||||
|
@ -1457,10 +1480,6 @@ private:
|
||||||
TODO(toLocation(), "EndDoStmt lowering");
|
TODO(toLocation(), "EndDoStmt lowering");
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::EndIfStmt &) {
|
|
||||||
TODO(toLocation(), "EndIfStmt lowering");
|
|
||||||
}
|
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {
|
void genFIR(const Fortran::parser::EndMpSubprogramStmt &) {
|
||||||
TODO(toLocation(), "EndMpSubprogramStmt lowering");
|
TODO(toLocation(), "EndMpSubprogramStmt lowering");
|
||||||
}
|
}
|
||||||
|
@ -1472,6 +1491,7 @@ private:
|
||||||
// Nop statements - No code, or code is generated at the construct level.
|
// Nop statements - No code, or code is generated at the construct level.
|
||||||
void genFIR(const Fortran::parser::ContinueStmt &) {} // nop
|
void genFIR(const Fortran::parser::ContinueStmt &) {} // nop
|
||||||
void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop
|
void genFIR(const Fortran::parser::EndFunctionStmt &) {} // nop
|
||||||
|
void genFIR(const Fortran::parser::EndIfStmt &) {} // nop
|
||||||
void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop
|
void genFIR(const Fortran::parser::EndSubroutineStmt &) {} // nop
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::EntryStmt &) {
|
void genFIR(const Fortran::parser::EntryStmt &) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ add_flang_library(FortranLower
|
||||||
ConvertType.cpp
|
ConvertType.cpp
|
||||||
ConvertVariable.cpp
|
ConvertVariable.cpp
|
||||||
ComponentPath.cpp
|
ComponentPath.cpp
|
||||||
|
CustomIntrinsicCall.cpp
|
||||||
DumpEvaluateExpr.cpp
|
DumpEvaluateExpr.cpp
|
||||||
HostAssociations.cpp
|
HostAssociations.cpp
|
||||||
IntrinsicCall.cpp
|
IntrinsicCall.cpp
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,255 @@
|
||||||
|
//===-- CustomIntrinsicCall.cpp -------------------------------------------===//
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "flang/Lower/CustomIntrinsicCall.h"
|
||||||
|
#include "flang/Evaluate/expression.h"
|
||||||
|
#include "flang/Evaluate/fold.h"
|
||||||
|
#include "flang/Evaluate/tools.h"
|
||||||
|
#include "flang/Lower/IntrinsicCall.h"
|
||||||
|
#include "flang/Lower/Todo.h"
|
||||||
|
|
||||||
|
/// Is this a call to MIN or MAX intrinsic with arguments that may be absent at
|
||||||
|
/// runtime? This is a special case because MIN and MAX can have any number of
|
||||||
|
/// arguments.
|
||||||
|
static bool isMinOrMaxWithDynamicallyOptionalArg(
|
||||||
|
llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
Fortran::evaluate::FoldingContext &foldingContex) {
|
||||||
|
if (name != "min" && name != "max")
|
||||||
|
return false;
|
||||||
|
const auto &args = procRef.arguments();
|
||||||
|
std::size_t argSize = args.size();
|
||||||
|
if (argSize <= 2)
|
||||||
|
return false;
|
||||||
|
for (std::size_t i = 2; i < argSize; ++i) {
|
||||||
|
if (auto *expr =
|
||||||
|
Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(args[i]))
|
||||||
|
if (Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this a call to ISHFTC intrinsic with a SIZE argument that may be absent
|
||||||
|
/// at runtime? This is a special case because the SIZE value to be applied
|
||||||
|
/// when absent is not zero.
|
||||||
|
static bool isIshftcWithDynamicallyOptionalArg(
|
||||||
|
llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
Fortran::evaluate::FoldingContext &foldingContex) {
|
||||||
|
if (name != "ishftc" || procRef.arguments().size() < 3)
|
||||||
|
return false;
|
||||||
|
auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(
|
||||||
|
procRef.arguments()[2]);
|
||||||
|
return expr &&
|
||||||
|
Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is this a call to SYSTEM_CLOCK or RANDOM_SEED intrinsic with arguments that
|
||||||
|
/// may be absent at runtime? This are special cases because that aspect cannot
|
||||||
|
/// be delegated to the runtime via a null fir.box or address given the current
|
||||||
|
/// runtime entry point.
|
||||||
|
static bool isSystemClockOrRandomSeedWithOptionalArg(
|
||||||
|
llvm::StringRef name, const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
Fortran::evaluate::FoldingContext &foldingContex) {
|
||||||
|
if (name != "system_clock" && name != "random_seed")
|
||||||
|
return false;
|
||||||
|
for (const auto &arg : procRef.arguments()) {
|
||||||
|
auto *expr = Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg);
|
||||||
|
if (expr &&
|
||||||
|
Fortran::evaluate::MayBePassedAsAbsentOptional(*expr, foldingContex))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Fortran::lower::intrinsicRequiresCustomOptionalHandling(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
AbstractConverter &converter) {
|
||||||
|
llvm::StringRef name = intrinsic.name;
|
||||||
|
Fortran::evaluate::FoldingContext &fldCtx = converter.getFoldingContext();
|
||||||
|
return isMinOrMaxWithDynamicallyOptionalArg(name, procRef, fldCtx) ||
|
||||||
|
isIshftcWithDynamicallyOptionalArg(name, procRef, fldCtx) ||
|
||||||
|
isSystemClockOrRandomSeedWithOptionalArg(name, procRef, fldCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepareMinOrMaxArguments(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
llvm::Optional<mlir::Type> retTy,
|
||||||
|
const Fortran::lower::OperandPrepare &prepareOptionalArgument,
|
||||||
|
const Fortran::lower::OperandPrepare &prepareOtherArgument,
|
||||||
|
Fortran::lower::AbstractConverter &converter) {
|
||||||
|
assert(retTy && "MIN and MAX must have a return type");
|
||||||
|
mlir::Type resultType = retTy.getValue();
|
||||||
|
mlir::Location loc = converter.getCurrentLocation();
|
||||||
|
if (fir::isa_char(resultType))
|
||||||
|
TODO(loc,
|
||||||
|
"CHARACTER MIN and MAX lowering with dynamically optional arguments");
|
||||||
|
for (auto arg : llvm::enumerate(procRef.arguments())) {
|
||||||
|
const auto *expr =
|
||||||
|
Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
|
||||||
|
if (!expr)
|
||||||
|
continue;
|
||||||
|
if (arg.index() <= 1 || !Fortran::evaluate::MayBePassedAsAbsentOptional(
|
||||||
|
*expr, converter.getFoldingContext())) {
|
||||||
|
// Non optional arguments.
|
||||||
|
prepareOtherArgument(*expr);
|
||||||
|
} else {
|
||||||
|
// Dynamically optional arguments.
|
||||||
|
// Subtle: even for scalar the if-then-else will be generated in the loop
|
||||||
|
// nest because the then part will require the current extremum value that
|
||||||
|
// may depend on previous array element argument and cannot be outlined.
|
||||||
|
prepareOptionalArgument(*expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fir::ExtendedValue
|
||||||
|
lowerMinOrMax(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
llvm::StringRef name, llvm::Optional<mlir::Type> retTy,
|
||||||
|
const Fortran::lower::OperandPresent &isPresentCheck,
|
||||||
|
const Fortran::lower::OperandGetter &getOperand,
|
||||||
|
std::size_t numOperands,
|
||||||
|
Fortran::lower::StatementContext &stmtCtx) {
|
||||||
|
assert(numOperands >= 2 && !isPresentCheck(0) && !isPresentCheck(1) &&
|
||||||
|
"min/max must have at least two non-optional args");
|
||||||
|
assert(retTy && "MIN and MAX must have a return type");
|
||||||
|
mlir::Type resultType = retTy.getValue();
|
||||||
|
llvm::SmallVector<fir::ExtendedValue> args;
|
||||||
|
args.push_back(getOperand(0));
|
||||||
|
args.push_back(getOperand(1));
|
||||||
|
mlir::Value extremum = fir::getBase(Fortran::lower::genIntrinsicCall(
|
||||||
|
builder, loc, name, resultType, args, stmtCtx));
|
||||||
|
|
||||||
|
for (std::size_t opIndex = 2; opIndex < numOperands; ++opIndex) {
|
||||||
|
if (llvm::Optional<mlir::Value> isPresentRuntimeCheck =
|
||||||
|
isPresentCheck(opIndex)) {
|
||||||
|
// Argument is dynamically optional.
|
||||||
|
extremum =
|
||||||
|
builder
|
||||||
|
.genIfOp(loc, {resultType}, isPresentRuntimeCheck.getValue(),
|
||||||
|
/*withElseRegion=*/true)
|
||||||
|
.genThen([&]() {
|
||||||
|
llvm::SmallVector<fir::ExtendedValue> args;
|
||||||
|
args.emplace_back(extremum);
|
||||||
|
args.emplace_back(getOperand(opIndex));
|
||||||
|
fir::ExtendedValue newExtremum =
|
||||||
|
Fortran::lower::genIntrinsicCall(builder, loc, name,
|
||||||
|
resultType, args, stmtCtx);
|
||||||
|
builder.create<fir::ResultOp>(loc, fir::getBase(newExtremum));
|
||||||
|
})
|
||||||
|
.genElse([&]() { builder.create<fir::ResultOp>(loc, extremum); })
|
||||||
|
.getResults()[0];
|
||||||
|
} else {
|
||||||
|
// Argument is know to be present at compile time.
|
||||||
|
llvm::SmallVector<fir::ExtendedValue> args;
|
||||||
|
args.emplace_back(extremum);
|
||||||
|
args.emplace_back(getOperand(opIndex));
|
||||||
|
extremum = fir::getBase(Fortran::lower::genIntrinsicCall(
|
||||||
|
builder, loc, name, resultType, args, stmtCtx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extremum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepareIshftcArguments(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
llvm::Optional<mlir::Type> retTy,
|
||||||
|
const Fortran::lower::OperandPrepare &prepareOptionalArgument,
|
||||||
|
const Fortran::lower::OperandPrepare &prepareOtherArgument,
|
||||||
|
Fortran::lower::AbstractConverter &converter) {
|
||||||
|
for (auto arg : llvm::enumerate(procRef.arguments())) {
|
||||||
|
const auto *expr =
|
||||||
|
Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
|
||||||
|
assert(expr && "expected all ISHFTC argument to be textually present here");
|
||||||
|
if (arg.index() == 2) {
|
||||||
|
assert(Fortran::evaluate::MayBePassedAsAbsentOptional(
|
||||||
|
*expr, converter.getFoldingContext()) &&
|
||||||
|
"expected ISHFTC SIZE arg to be dynamically optional");
|
||||||
|
prepareOptionalArgument(*expr);
|
||||||
|
} else {
|
||||||
|
// Non optional arguments.
|
||||||
|
prepareOtherArgument(*expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static fir::ExtendedValue
|
||||||
|
lowerIshftc(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
llvm::StringRef name, llvm::Optional<mlir::Type> retTy,
|
||||||
|
const Fortran::lower::OperandPresent &isPresentCheck,
|
||||||
|
const Fortran::lower::OperandGetter &getOperand,
|
||||||
|
std::size_t numOperands,
|
||||||
|
Fortran::lower::StatementContext &stmtCtx) {
|
||||||
|
assert(numOperands == 3 && !isPresentCheck(0) && !isPresentCheck(1) &&
|
||||||
|
isPresentCheck(2) &&
|
||||||
|
"only ISHFTC SIZE arg is expected to be dynamically optional here");
|
||||||
|
assert(retTy && "ISFHTC must have a return type");
|
||||||
|
mlir::Type resultType = retTy.getValue();
|
||||||
|
llvm::SmallVector<fir::ExtendedValue> args;
|
||||||
|
args.push_back(getOperand(0));
|
||||||
|
args.push_back(getOperand(1));
|
||||||
|
args.push_back(builder
|
||||||
|
.genIfOp(loc, {resultType}, isPresentCheck(2).getValue(),
|
||||||
|
/*withElseRegion=*/true)
|
||||||
|
.genThen([&]() {
|
||||||
|
fir::ExtendedValue sizeExv = getOperand(2);
|
||||||
|
mlir::Value size = builder.createConvert(
|
||||||
|
loc, resultType, fir::getBase(sizeExv));
|
||||||
|
builder.create<fir::ResultOp>(loc, size);
|
||||||
|
})
|
||||||
|
.genElse([&]() {
|
||||||
|
mlir::Value bitSize = builder.createIntegerConstant(
|
||||||
|
loc, resultType,
|
||||||
|
resultType.cast<mlir::IntegerType>().getWidth());
|
||||||
|
builder.create<fir::ResultOp>(loc, bitSize);
|
||||||
|
})
|
||||||
|
.getResults()[0]);
|
||||||
|
return Fortran::lower::genIntrinsicCall(builder, loc, name, resultType, args,
|
||||||
|
stmtCtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fortran::lower::prepareCustomIntrinsicArgument(
|
||||||
|
const Fortran::evaluate::ProcedureRef &procRef,
|
||||||
|
const Fortran::evaluate::SpecificIntrinsic &intrinsic,
|
||||||
|
llvm::Optional<mlir::Type> retTy,
|
||||||
|
const OperandPrepare &prepareOptionalArgument,
|
||||||
|
const OperandPrepare &prepareOtherArgument, AbstractConverter &converter) {
|
||||||
|
llvm::StringRef name = intrinsic.name;
|
||||||
|
if (name == "min" || name == "max")
|
||||||
|
return prepareMinOrMaxArguments(procRef, intrinsic, retTy,
|
||||||
|
prepareOptionalArgument,
|
||||||
|
prepareOtherArgument, converter);
|
||||||
|
if (name == "ishftc")
|
||||||
|
return prepareIshftcArguments(procRef, intrinsic, retTy,
|
||||||
|
prepareOptionalArgument, prepareOtherArgument,
|
||||||
|
converter);
|
||||||
|
TODO(converter.getCurrentLocation(),
|
||||||
|
"unhandled dynamically optional arguments in SYSTEM_CLOCK or "
|
||||||
|
"RANDOM_SEED");
|
||||||
|
}
|
||||||
|
|
||||||
|
fir::ExtendedValue Fortran::lower::lowerCustomIntrinsic(
|
||||||
|
fir::FirOpBuilder &builder, mlir::Location loc, llvm::StringRef name,
|
||||||
|
llvm::Optional<mlir::Type> retTy, const OperandPresent &isPresentCheck,
|
||||||
|
const OperandGetter &getOperand, std::size_t numOperands,
|
||||||
|
Fortran::lower::StatementContext &stmtCtx) {
|
||||||
|
if (name == "min" || name == "max")
|
||||||
|
return lowerMinOrMax(builder, loc, name, retTy, isPresentCheck, getOperand,
|
||||||
|
numOperands, stmtCtx);
|
||||||
|
if (name == "ishftc")
|
||||||
|
return lowerIshftc(builder, loc, name, retTy, isPresentCheck, getOperand,
|
||||||
|
numOperands, stmtCtx);
|
||||||
|
TODO(loc, "unhandled dynamically optional arguments in SYSTEM_CLOCK or "
|
||||||
|
"RANDOM_SEED");
|
||||||
|
}
|
|
@ -15,14 +15,18 @@
|
||||||
|
|
||||||
#include "flang/Lower/IntrinsicCall.h"
|
#include "flang/Lower/IntrinsicCall.h"
|
||||||
#include "flang/Common/static-multimap-view.h"
|
#include "flang/Common/static-multimap-view.h"
|
||||||
|
#include "flang/Lower/Mangler.h"
|
||||||
#include "flang/Lower/StatementContext.h"
|
#include "flang/Lower/StatementContext.h"
|
||||||
#include "flang/Lower/SymbolMap.h"
|
#include "flang/Lower/SymbolMap.h"
|
||||||
#include "flang/Lower/Todo.h"
|
#include "flang/Lower/Todo.h"
|
||||||
|
#include "flang/Optimizer/Builder/Character.h"
|
||||||
#include "flang/Optimizer/Builder/Complex.h"
|
#include "flang/Optimizer/Builder/Complex.h"
|
||||||
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||||
#include "flang/Optimizer/Builder/MutableBox.h"
|
#include "flang/Optimizer/Builder/MutableBox.h"
|
||||||
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
||||||
|
#include "flang/Optimizer/Builder/Runtime/Reduction.h"
|
||||||
#include "flang/Optimizer/Support/FatalError.h"
|
#include "flang/Optimizer/Support/FatalError.h"
|
||||||
|
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
|
|
||||||
#define DEBUG_TYPE "flang-lower-intrinsic"
|
#define DEBUG_TYPE "flang-lower-intrinsic"
|
||||||
|
@ -90,12 +94,110 @@ fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
|
||||||
return fir::UnboxedValue{};
|
return fir::UnboxedValue{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test if an ExtendedValue is absent.
|
||||||
|
static bool isAbsent(const fir::ExtendedValue &exv) {
|
||||||
|
return !fir::getBase(exv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
|
||||||
|
/// take a DIM argument.
|
||||||
|
template <typename FD>
|
||||||
|
static fir::ExtendedValue
|
||||||
|
genFuncDim(FD funcDim, mlir::Type resultType, fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc, Fortran::lower::StatementContext *stmtCtx,
|
||||||
|
llvm::StringRef errMsg, mlir::Value array, fir::ExtendedValue dimArg,
|
||||||
|
mlir::Value mask, int rank) {
|
||||||
|
|
||||||
|
// Create mutable fir.box to be passed to the runtime for the result.
|
||||||
|
mlir::Type resultArrayType = builder.getVarLenSeqTy(resultType, rank - 1);
|
||||||
|
fir::MutableBoxValue resultMutableBox =
|
||||||
|
fir::factory::createTempMutableBox(builder, loc, resultArrayType);
|
||||||
|
mlir::Value resultIrBox =
|
||||||
|
fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
|
||||||
|
|
||||||
|
mlir::Value dim =
|
||||||
|
isAbsent(dimArg)
|
||||||
|
? builder.createIntegerConstant(loc, builder.getIndexType(), 0)
|
||||||
|
: fir::getBase(dimArg);
|
||||||
|
funcDim(builder, loc, resultIrBox, array, dim, mask);
|
||||||
|
|
||||||
|
fir::ExtendedValue res =
|
||||||
|
fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
|
||||||
|
return res.match(
|
||||||
|
[&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
assert(stmtCtx);
|
||||||
|
fir::FirOpBuilder *bldr = &builder;
|
||||||
|
mlir::Value temp = box.getAddr();
|
||||||
|
stmtCtx->attachCleanup(
|
||||||
|
[=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
assert(stmtCtx);
|
||||||
|
fir::FirOpBuilder *bldr = &builder;
|
||||||
|
mlir::Value temp = box.getAddr();
|
||||||
|
stmtCtx->attachCleanup(
|
||||||
|
[=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const auto &) -> fir::ExtendedValue {
|
||||||
|
fir::emitFatalError(loc, errMsg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process calls to Product, Sum intrinsic functions
|
||||||
|
template <typename FN, typename FD>
|
||||||
|
static fir::ExtendedValue
|
||||||
|
genProdOrSum(FN func, FD funcDim, mlir::Type resultType,
|
||||||
|
fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
Fortran::lower::StatementContext *stmtCtx, llvm::StringRef errMsg,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
|
||||||
|
assert(args.size() == 3);
|
||||||
|
|
||||||
|
// Handle required array argument
|
||||||
|
fir::BoxValue arryTmp = builder.createBox(loc, args[0]);
|
||||||
|
mlir::Value array = fir::getBase(arryTmp);
|
||||||
|
int rank = arryTmp.rank();
|
||||||
|
assert(rank >= 1);
|
||||||
|
|
||||||
|
// Handle optional mask argument
|
||||||
|
auto mask = isAbsent(args[2])
|
||||||
|
? builder.create<fir::AbsentOp>(
|
||||||
|
loc, fir::BoxType::get(builder.getI1Type()))
|
||||||
|
: builder.createBox(loc, args[2]);
|
||||||
|
|
||||||
|
bool absentDim = isAbsent(args[1]);
|
||||||
|
|
||||||
|
// We call the type specific versions because the result is scalar
|
||||||
|
// in the case below.
|
||||||
|
if (absentDim || rank == 1) {
|
||||||
|
mlir::Type ty = array.getType();
|
||||||
|
mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
|
||||||
|
auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
|
||||||
|
if (fir::isa_complex(eleTy)) {
|
||||||
|
mlir::Value result = builder.createTemporary(loc, eleTy);
|
||||||
|
func(builder, loc, array, mask, result);
|
||||||
|
return builder.create<fir::LoadOp>(loc, result);
|
||||||
|
}
|
||||||
|
auto resultBox = builder.create<fir::AbsentOp>(
|
||||||
|
loc, fir::BoxType::get(builder.getI1Type()));
|
||||||
|
return func(builder, loc, array, mask, resultBox);
|
||||||
|
}
|
||||||
|
// Handle Product/Sum cases that have an array result.
|
||||||
|
return genFuncDim(funcDim, resultType, builder, loc, stmtCtx, errMsg, array,
|
||||||
|
args[1], mask, rank);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO error handling -> return a code or directly emit messages ?
|
// TODO error handling -> return a code or directly emit messages ?
|
||||||
struct IntrinsicLibrary {
|
struct IntrinsicLibrary {
|
||||||
|
|
||||||
// Constructors.
|
// Constructors.
|
||||||
explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc)
|
explicit IntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
: builder{builder}, loc{loc} {}
|
Fortran::lower::StatementContext *stmtCtx = nullptr)
|
||||||
|
: builder{builder}, loc{loc}, stmtCtx{stmtCtx} {}
|
||||||
IntrinsicLibrary() = delete;
|
IntrinsicLibrary() = delete;
|
||||||
IntrinsicLibrary(const IntrinsicLibrary &) = delete;
|
IntrinsicLibrary(const IntrinsicLibrary &) = delete;
|
||||||
|
|
||||||
|
@ -131,11 +233,23 @@ struct IntrinsicLibrary {
|
||||||
/// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
|
/// Lowering for the IAND intrinsic. The IAND intrinsic expects two arguments
|
||||||
/// in the llvm::ArrayRef.
|
/// in the llvm::ArrayRef.
|
||||||
mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
|
mlir::Value genIand(mlir::Type, llvm::ArrayRef<mlir::Value>);
|
||||||
|
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
|
||||||
/// Define the different FIR generators that can be mapped to intrinsic to
|
/// Define the different FIR generators that can be mapped to intrinsic to
|
||||||
/// generate the related code. The intrinsic is lowered into an MLIR
|
/// generate the related code. The intrinsic is lowered into an MLIR
|
||||||
/// arith::AndIOp.
|
/// arith::AndIOp.
|
||||||
using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
|
using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
|
||||||
using Generator = std::variant<ElementalGenerator>;
|
using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
|
||||||
|
using Generator = std::variant<ElementalGenerator, ExtendedGenerator>;
|
||||||
|
|
||||||
|
template <typename GeneratorType>
|
||||||
|
fir::ExtendedValue
|
||||||
|
outlineInExtendedWrapper(GeneratorType, llvm::StringRef name,
|
||||||
|
llvm::Optional<mlir::Type> resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args);
|
||||||
|
|
||||||
|
template <typename GeneratorType>
|
||||||
|
mlir::FuncOp getWrapper(GeneratorType, llvm::StringRef name,
|
||||||
|
mlir::FunctionType, bool loadRefArguments = false);
|
||||||
|
|
||||||
/// Generate calls to ElementalGenerator, handling the elemental aspects
|
/// Generate calls to ElementalGenerator, handling the elemental aspects
|
||||||
template <typename GeneratorType>
|
template <typename GeneratorType>
|
||||||
|
@ -150,8 +264,13 @@ struct IntrinsicLibrary {
|
||||||
mlir::Value invokeGenerator(RuntimeCallGenerator generator,
|
mlir::Value invokeGenerator(RuntimeCallGenerator generator,
|
||||||
mlir::Type resultType,
|
mlir::Type resultType,
|
||||||
llvm::ArrayRef<mlir::Value> args);
|
llvm::ArrayRef<mlir::Value> args);
|
||||||
|
mlir::Value invokeGenerator(ExtendedGenerator generator,
|
||||||
|
mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<mlir::Value> args);
|
||||||
|
|
||||||
fir::FirOpBuilder &builder;
|
fir::FirOpBuilder &builder;
|
||||||
mlir::Location loc;
|
mlir::Location loc;
|
||||||
|
Fortran::lower::StatementContext *stmtCtx;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IntrinsicDummyArgument {
|
struct IntrinsicDummyArgument {
|
||||||
|
@ -171,11 +290,20 @@ struct Fortran::lower::IntrinsicArgumentLoweringRules {
|
||||||
struct IntrinsicHandler {
|
struct IntrinsicHandler {
|
||||||
const char *name;
|
const char *name;
|
||||||
IntrinsicLibrary::Generator generator;
|
IntrinsicLibrary::Generator generator;
|
||||||
|
// The following may be omitted in the table below.
|
||||||
Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
|
Fortran::lower::IntrinsicArgumentLoweringRules argLoweringRules = {};
|
||||||
|
bool isElemental = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr auto asValue = Fortran::lower::LowerIntrinsicArgAs::Value;
|
||||||
|
constexpr auto asBox = Fortran::lower::LowerIntrinsicArgAs::Box;
|
||||||
using I = IntrinsicLibrary;
|
using I = IntrinsicLibrary;
|
||||||
|
|
||||||
|
/// Flag to indicate that an intrinsic argument has to be handled as
|
||||||
|
/// being dynamically optional (e.g. special handling when actual
|
||||||
|
/// argument is an optional variable in the current scope).
|
||||||
|
static constexpr bool handleDynamicOptional = true;
|
||||||
|
|
||||||
/// Table that drives the fir generation depending on the intrinsic.
|
/// Table that drives the fir generation depending on the intrinsic.
|
||||||
/// one to one mapping with Fortran arguments. If no mapping is
|
/// one to one mapping with Fortran arguments. If no mapping is
|
||||||
/// defined here for a generic intrinsic, genRuntimeCall will be called
|
/// defined here for a generic intrinsic, genRuntimeCall will be called
|
||||||
|
@ -186,6 +314,12 @@ using I = IntrinsicLibrary;
|
||||||
static constexpr IntrinsicHandler handlers[]{
|
static constexpr IntrinsicHandler handlers[]{
|
||||||
{"abs", &I::genAbs},
|
{"abs", &I::genAbs},
|
||||||
{"iand", &I::genIand},
|
{"iand", &I::genIand},
|
||||||
|
{"sum",
|
||||||
|
&I::genSum,
|
||||||
|
{{{"array", asBox},
|
||||||
|
{"dim", asValue},
|
||||||
|
{"mask", asBox, handleDynamicOptional}}},
|
||||||
|
/*isElemental=*/false},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
|
static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
|
||||||
|
@ -513,10 +647,71 @@ static mlir::FunctionType getFunctionType(llvm::Optional<mlir::Type> resultType,
|
||||||
return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
|
return mlir::FunctionType::get(builder.getModule().getContext(), argTypes,
|
||||||
resTypes);
|
resTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// fir::ExtendedValue to mlir::Value translation layer
|
||||||
|
|
||||||
|
fir::ExtendedValue toExtendedValue(mlir::Value val, fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc) {
|
||||||
|
assert(val && "optional unhandled here");
|
||||||
|
mlir::Type type = val.getType();
|
||||||
|
mlir::Value base = val;
|
||||||
|
mlir::IndexType indexType = builder.getIndexType();
|
||||||
|
llvm::SmallVector<mlir::Value> extents;
|
||||||
|
|
||||||
|
fir::factory::CharacterExprHelper charHelper{builder, loc};
|
||||||
|
// FIXME: we may want to allow non character scalar here.
|
||||||
|
if (charHelper.isCharacterScalar(type))
|
||||||
|
return charHelper.toExtendedValue(val);
|
||||||
|
|
||||||
|
if (auto refType = type.dyn_cast<fir::ReferenceType>())
|
||||||
|
type = refType.getEleTy();
|
||||||
|
|
||||||
|
if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
|
||||||
|
type = arrayType.getEleTy();
|
||||||
|
for (fir::SequenceType::Extent extent : arrayType.getShape()) {
|
||||||
|
if (extent == fir::SequenceType::getUnknownExtent())
|
||||||
|
break;
|
||||||
|
extents.emplace_back(
|
||||||
|
builder.createIntegerConstant(loc, indexType, extent));
|
||||||
|
}
|
||||||
|
// Last extent might be missing in case of assumed-size. If more extents
|
||||||
|
// could not be deduced from type, that's an error (a fir.box should
|
||||||
|
// have been used in the interface).
|
||||||
|
if (extents.size() + 1 < arrayType.getShape().size())
|
||||||
|
mlir::emitError(loc, "cannot retrieve array extents from type");
|
||||||
|
} else if (type.isa<fir::BoxType>() || type.isa<fir::RecordType>()) {
|
||||||
|
fir::emitFatalError(loc, "not yet implemented: descriptor or derived type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extents.empty())
|
||||||
|
return fir::ArrayBoxValue{base, extents};
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
mlir::Value toValue(const fir::ExtendedValue &val, fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc) {
|
||||||
|
if (const fir::CharBoxValue *charBox = val.getCharBox()) {
|
||||||
|
mlir::Value buffer = charBox->getBuffer();
|
||||||
|
if (buffer.getType().isa<fir::BoxCharType>())
|
||||||
|
return buffer;
|
||||||
|
return fir::factory::CharacterExprHelper{builder, loc}.createEmboxChar(
|
||||||
|
buffer, charBox->getLen());
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: need to access other ExtendedValue variants and handle them
|
||||||
|
// properly.
|
||||||
|
return fir::getBase(val);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// IntrinsicLibrary
|
// IntrinsicLibrary
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// Emit a TODO error message for as yet unimplemented intrinsics.
|
||||||
|
static void crashOnMissingIntrinsic(mlir::Location loc, llvm::StringRef name) {
|
||||||
|
TODO(loc, "missing intrinsic lowering: " + llvm::Twine(name));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename GeneratorType>
|
template <typename GeneratorType>
|
||||||
fir::ExtendedValue IntrinsicLibrary::genElementalCall(
|
fir::ExtendedValue IntrinsicLibrary::genElementalCall(
|
||||||
GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
|
GeneratorType generator, llvm::StringRef name, mlir::Type resultType,
|
||||||
|
@ -530,6 +725,19 @@ fir::ExtendedValue IntrinsicLibrary::genElementalCall(
|
||||||
return invokeGenerator(generator, resultType, scalarArgs);
|
return invokeGenerator(generator, resultType, scalarArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::genElementalCall<IntrinsicLibrary::ExtendedGenerator>(
|
||||||
|
ExtendedGenerator generator, llvm::StringRef name, mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args, bool outline) {
|
||||||
|
for (const fir::ExtendedValue &arg : args)
|
||||||
|
if (!arg.getUnboxed() && !arg.getCharBox())
|
||||||
|
fir::emitFatalError(loc, "nonscalar intrinsic argument");
|
||||||
|
if (outline)
|
||||||
|
return outlineInExtendedWrapper(generator, name, resultType, args);
|
||||||
|
return std::invoke(generator, *this, resultType, args);
|
||||||
|
}
|
||||||
|
|
||||||
static fir::ExtendedValue
|
static fir::ExtendedValue
|
||||||
invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
|
invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
|
||||||
const IntrinsicHandler &handler,
|
const IntrinsicHandler &handler,
|
||||||
|
@ -541,6 +749,22 @@ invokeHandler(IntrinsicLibrary::ElementalGenerator generator,
|
||||||
outline);
|
outline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fir::ExtendedValue
|
||||||
|
invokeHandler(IntrinsicLibrary::ExtendedGenerator generator,
|
||||||
|
const IntrinsicHandler &handler,
|
||||||
|
llvm::Optional<mlir::Type> resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args, bool outline,
|
||||||
|
IntrinsicLibrary &lib) {
|
||||||
|
assert(resultType && "expect intrinsic function");
|
||||||
|
if (handler.isElemental)
|
||||||
|
return lib.genElementalCall(generator, handler.name, *resultType, args,
|
||||||
|
outline);
|
||||||
|
if (outline)
|
||||||
|
return lib.outlineInExtendedWrapper(generator, handler.name, *resultType,
|
||||||
|
args);
|
||||||
|
return std::invoke(generator, lib, *resultType, args);
|
||||||
|
}
|
||||||
|
|
||||||
fir::ExtendedValue
|
fir::ExtendedValue
|
||||||
IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
|
IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
|
||||||
llvm::Optional<mlir::Type> resultType,
|
llvm::Optional<mlir::Type> resultType,
|
||||||
|
@ -555,8 +779,32 @@ IntrinsicLibrary::genIntrinsicCall(llvm::StringRef name,
|
||||||
handler->generator);
|
handler->generator);
|
||||||
}
|
}
|
||||||
|
|
||||||
TODO(loc, "genIntrinsicCall runtime");
|
if (!resultType)
|
||||||
return {};
|
// Subroutine should have a handler, they are likely missing for now.
|
||||||
|
crashOnMissingIntrinsic(loc, name);
|
||||||
|
|
||||||
|
// Try the runtime if no special handler was defined for the
|
||||||
|
// intrinsic being called. Maths runtime only has numerical elemental.
|
||||||
|
// No optional arguments are expected at this point, the code will
|
||||||
|
// crash if it gets absent optional.
|
||||||
|
|
||||||
|
// FIXME: using toValue to get the type won't work with array arguments.
|
||||||
|
llvm::SmallVector<mlir::Value> mlirArgs;
|
||||||
|
for (const fir::ExtendedValue &extendedVal : args) {
|
||||||
|
mlir::Value val = toValue(extendedVal, builder, loc);
|
||||||
|
if (!val)
|
||||||
|
// If an absent optional gets there, most likely its handler has just
|
||||||
|
// not yet been defined.
|
||||||
|
crashOnMissingIntrinsic(loc, name);
|
||||||
|
mlirArgs.emplace_back(val);
|
||||||
|
}
|
||||||
|
mlir::FunctionType soughtFuncType =
|
||||||
|
getFunctionType(*resultType, mlirArgs, builder);
|
||||||
|
|
||||||
|
IntrinsicLibrary::RuntimeCallGenerator runtimeCallGenerator =
|
||||||
|
getRuntimeCallGenerator(name, soughtFuncType);
|
||||||
|
return genElementalCall(runtimeCallGenerator, name, *resultType, args,
|
||||||
|
/* outline */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
mlir::Value
|
mlir::Value
|
||||||
|
@ -572,15 +820,108 @@ IntrinsicLibrary::invokeGenerator(RuntimeCallGenerator generator,
|
||||||
llvm::ArrayRef<mlir::Value> args) {
|
llvm::ArrayRef<mlir::Value> args) {
|
||||||
return generator(builder, loc, args);
|
return generator(builder, loc, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mlir::Value
|
||||||
|
IntrinsicLibrary::invokeGenerator(ExtendedGenerator generator,
|
||||||
|
mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<mlir::Value> args) {
|
||||||
|
llvm::SmallVector<fir::ExtendedValue> extendedArgs;
|
||||||
|
for (mlir::Value arg : args)
|
||||||
|
extendedArgs.emplace_back(toExtendedValue(arg, builder, loc));
|
||||||
|
auto extendedResult = std::invoke(generator, *this, resultType, extendedArgs);
|
||||||
|
return toValue(extendedResult, builder, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename GeneratorType>
|
||||||
|
mlir::FuncOp IntrinsicLibrary::getWrapper(GeneratorType generator,
|
||||||
|
llvm::StringRef name,
|
||||||
|
mlir::FunctionType funcType,
|
||||||
|
bool loadRefArguments) {
|
||||||
|
std::string wrapperName = fir::mangleIntrinsicProcedure(name, funcType);
|
||||||
|
mlir::FuncOp function = builder.getNamedFunction(wrapperName);
|
||||||
|
if (!function) {
|
||||||
|
// First time this wrapper is needed, build it.
|
||||||
|
function = builder.createFunction(loc, wrapperName, funcType);
|
||||||
|
function->setAttr("fir.intrinsic", builder.getUnitAttr());
|
||||||
|
auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
|
||||||
|
auto linkage =
|
||||||
|
mlir::LLVM::LinkageAttr::get(builder.getContext(), internalLinkage);
|
||||||
|
function->setAttr("llvm.linkage", linkage);
|
||||||
|
function.addEntryBlock();
|
||||||
|
|
||||||
|
// Create local context to emit code into the newly created function
|
||||||
|
// This new function is not linked to a source file location, only
|
||||||
|
// its calls will be.
|
||||||
|
auto localBuilder =
|
||||||
|
std::make_unique<fir::FirOpBuilder>(function, builder.getKindMap());
|
||||||
|
localBuilder->setInsertionPointToStart(&function.front());
|
||||||
|
// Location of code inside wrapper of the wrapper is independent from
|
||||||
|
// the location of the intrinsic call.
|
||||||
|
mlir::Location localLoc = localBuilder->getUnknownLoc();
|
||||||
|
llvm::SmallVector<mlir::Value> localArguments;
|
||||||
|
for (mlir::BlockArgument bArg : function.front().getArguments()) {
|
||||||
|
auto refType = bArg.getType().dyn_cast<fir::ReferenceType>();
|
||||||
|
if (loadRefArguments && refType) {
|
||||||
|
auto loaded = localBuilder->create<fir::LoadOp>(localLoc, bArg);
|
||||||
|
localArguments.push_back(loaded);
|
||||||
|
} else {
|
||||||
|
localArguments.push_back(bArg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IntrinsicLibrary localLib{*localBuilder, localLoc};
|
||||||
|
|
||||||
|
assert(funcType.getNumResults() == 1 &&
|
||||||
|
"expect one result for intrinsic function wrapper type");
|
||||||
|
mlir::Type resultType = funcType.getResult(0);
|
||||||
|
auto result =
|
||||||
|
localLib.invokeGenerator(generator, resultType, localArguments);
|
||||||
|
localBuilder->create<mlir::func::ReturnOp>(localLoc, result);
|
||||||
|
} else {
|
||||||
|
// Wrapper was already built, ensure it has the sought type
|
||||||
|
assert(function.getType() == funcType &&
|
||||||
|
"conflict between intrinsic wrapper types");
|
||||||
|
}
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helpers to detect absent optional (not yet supported in outlining).
|
||||||
|
bool static hasAbsentOptional(llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
for (const fir::ExtendedValue &arg : args)
|
||||||
|
if (!fir::getBase(arg))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename GeneratorType>
|
||||||
|
fir::ExtendedValue IntrinsicLibrary::outlineInExtendedWrapper(
|
||||||
|
GeneratorType generator, llvm::StringRef name,
|
||||||
|
llvm::Optional<mlir::Type> resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
if (hasAbsentOptional(args))
|
||||||
|
TODO(loc, "cannot outline call to intrinsic " + llvm::Twine(name) +
|
||||||
|
" with absent optional argument");
|
||||||
|
llvm::SmallVector<mlir::Value> mlirArgs;
|
||||||
|
for (const auto &extendedVal : args)
|
||||||
|
mlirArgs.emplace_back(toValue(extendedVal, builder, loc));
|
||||||
|
mlir::FunctionType funcType = getFunctionType(resultType, mlirArgs, builder);
|
||||||
|
mlir::FuncOp wrapper = getWrapper(generator, name, funcType);
|
||||||
|
auto call = builder.create<fir::CallOp>(loc, wrapper, mlirArgs);
|
||||||
|
if (resultType)
|
||||||
|
return toExtendedValue(call.getResult(0), builder, loc);
|
||||||
|
// Subroutine calls
|
||||||
|
return mlir::Value{};
|
||||||
|
}
|
||||||
|
|
||||||
IntrinsicLibrary::RuntimeCallGenerator
|
IntrinsicLibrary::RuntimeCallGenerator
|
||||||
IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
|
IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
|
||||||
mlir::FunctionType soughtFuncType) {
|
mlir::FunctionType soughtFuncType) {
|
||||||
mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType);
|
mlir::FuncOp funcOp = getRuntimeFunction(loc, builder, name, soughtFuncType);
|
||||||
if (!funcOp) {
|
if (!funcOp) {
|
||||||
mlir::emitError(loc,
|
std::string buffer("not yet implemented: missing intrinsic lowering: ");
|
||||||
"TODO: missing intrinsic lowering: " + llvm::Twine(name));
|
llvm::raw_string_ostream sstream(buffer);
|
||||||
llvm::errs() << "requested type was: " << soughtFuncType << "\n";
|
sstream << name << "\nrequested type was: " << soughtFuncType << '\n';
|
||||||
exit(1);
|
fir::emitFatalError(loc, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
mlir::FunctionType actualFuncType = funcOp.getType();
|
mlir::FunctionType actualFuncType = funcOp.getType();
|
||||||
|
@ -722,6 +1063,14 @@ mlir::Value IntrinsicLibrary::genExtremum(mlir::Type,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SUM
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::genSum(mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
return genProdOrSum(fir::runtime::genSum, fir::runtime::genSumDim, resultType,
|
||||||
|
builder, loc, stmtCtx, "unexpected result for Sum", args);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Argument lowering rules interface
|
// Argument lowering rules interface
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -756,9 +1105,10 @@ fir::ExtendedValue
|
||||||
Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
|
Fortran::lower::genIntrinsicCall(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
llvm::StringRef name,
|
llvm::StringRef name,
|
||||||
llvm::Optional<mlir::Type> resultType,
|
llvm::Optional<mlir::Type> resultType,
|
||||||
llvm::ArrayRef<fir::ExtendedValue> args) {
|
llvm::ArrayRef<fir::ExtendedValue> args,
|
||||||
return IntrinsicLibrary{builder, loc}.genIntrinsicCall(name, resultType,
|
Fortran::lower::StatementContext &stmtCtx) {
|
||||||
args);
|
return IntrinsicLibrary{builder, loc, &stmtCtx}.genIntrinsicCall(
|
||||||
|
name, resultType, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
|
mlir::Value Fortran::lower::genMax(fir::FirOpBuilder &builder,
|
||||||
|
|
|
@ -661,6 +661,46 @@ fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder,
|
||||||
box.getLBounds());
|
box.getLBounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::SmallVector<mlir::Value>
|
||||||
|
fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc,
|
||||||
|
const fir::ExtendedValue &exv) {
|
||||||
|
return exv.match(
|
||||||
|
[&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> {
|
||||||
|
return {array.getLBounds().begin(), array.getLBounds().end()};
|
||||||
|
},
|
||||||
|
[&](const fir::CharArrayBoxValue &array)
|
||||||
|
-> llvm::SmallVector<mlir::Value> {
|
||||||
|
return {array.getLBounds().begin(), array.getLBounds().end()};
|
||||||
|
},
|
||||||
|
[&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
|
||||||
|
return {box.getLBounds().begin(), box.getLBounds().end()};
|
||||||
|
},
|
||||||
|
[&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
|
||||||
|
auto load = fir::factory::genMutableBoxRead(builder, loc, box);
|
||||||
|
return fir::factory::getNonDefaultLowerBounds(builder, loc, load);
|
||||||
|
},
|
||||||
|
[&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::SmallVector<mlir::Value>
|
||||||
|
fir::factory::getNonDeferredLengthParams(const fir::ExtendedValue &exv) {
|
||||||
|
return exv.match(
|
||||||
|
[&](const fir::CharArrayBoxValue &character)
|
||||||
|
-> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
|
||||||
|
[&](const fir::CharBoxValue &character)
|
||||||
|
-> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
|
||||||
|
[&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
|
||||||
|
return {box.nonDeferredLenParams().begin(),
|
||||||
|
box.nonDeferredLenParams().end()};
|
||||||
|
},
|
||||||
|
[&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
|
||||||
|
return {box.getExplicitParameters().begin(),
|
||||||
|
box.getExplicitParameters().end()};
|
||||||
|
},
|
||||||
|
[&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
|
||||||
|
}
|
||||||
|
|
||||||
std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
|
std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
|
||||||
llvm::StringRef name) {
|
llvm::StringRef name) {
|
||||||
// For "long" identifiers use a hash value
|
// For "long" identifiers use a hash value
|
||||||
|
|
|
@ -246,6 +246,27 @@ bool hasDynamicSize(mlir::Type t) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isAllocatableType(mlir::Type ty) {
|
||||||
|
if (auto refTy = fir::dyn_cast_ptrEleTy(ty))
|
||||||
|
ty = refTy;
|
||||||
|
if (auto boxTy = ty.dyn_cast<fir::BoxType>())
|
||||||
|
return boxTy.getEleTy().isa<fir::HeapType>();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRecordWithAllocatableMember(mlir::Type ty) {
|
||||||
|
if (auto recTy = ty.dyn_cast<fir::RecordType>())
|
||||||
|
for (auto [field, memTy] : recTy.getTypeList()) {
|
||||||
|
if (fir::isAllocatableType(memTy))
|
||||||
|
return true;
|
||||||
|
// A record type cannot recursively include itself as a direct member.
|
||||||
|
// There must be an intervening `ptr` type, so recursion is safe here.
|
||||||
|
if (memTy.isa<fir::RecordType>() && isRecordWithAllocatableMember(memTy))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace fir
|
} // namespace fir
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
! RUN: bbc -emit-fir %s -o - | FileCheck %s
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test(
|
||||||
|
! CHECK-SAME: %[[arg0:.*]]: !fir.box<!fir.array<?xi32>>{{.*}}) -> i32 {
|
||||||
|
integer function sum_test(a)
|
||||||
|
integer :: a(:)
|
||||||
|
! CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index
|
||||||
|
! CHECK-DAG: %[[a1:.*]] = fir.absent !fir.box<i1>
|
||||||
|
! CHECK-DAG: %[[a3:.*]] = fir.convert %[[arg0]] : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
|
||||||
|
! CHECK-DAG: %[[a5:.*]] = fir.convert %[[c0]] : (index) -> i32
|
||||||
|
! CHECK-DAG: %[[a6:.*]] = fir.convert %[[a1]] : (!fir.box<i1>) -> !fir.box<none>
|
||||||
|
sum_test = sum(a)
|
||||||
|
! CHECK: %{{.*}} = fir.call @_FortranASumInteger4(%[[a3]], %{{.*}}, %{{.*}}, %[[a5]], %[[a6]]) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i32
|
||||||
|
end function
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test2(
|
||||||
|
! CHECK-SAME: %[[arg0:.*]]: !fir.box<!fir.array<?x?xi32>>{{.*}}, %[[arg1:.*]]: !fir.box<!fir.array<?xi32>>{{.*}}) {
|
||||||
|
subroutine sum_test2(a,r)
|
||||||
|
integer :: a(:,:)
|
||||||
|
integer :: r(:)
|
||||||
|
! CHECK-DAG: %[[c2_i32:.*]] = arith.constant 2 : i32
|
||||||
|
! CHECK-DAG: %[[a0:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?xi32>>>
|
||||||
|
! CHECK-DAG: %[[a1:.*]] = fir.absent !fir.box<i1>
|
||||||
|
! CHECK-DAG: %[[a6:.*]] = fir.convert %[[a0]] : (!fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>) -> !fir.ref<!fir.box<none>>
|
||||||
|
! CHECK-DAG: %[[a7:.*]] = fir.convert %[[arg0]] : (!fir.box<!fir.array<?x?xi32>>) -> !fir.box<none>
|
||||||
|
! CHECK-DAG: %[[a9:.*]] = fir.convert %[[a1]] : (!fir.box<i1>) -> !fir.box<none>
|
||||||
|
r = sum(a,dim=2)
|
||||||
|
! CHECK: %{{.*}} = fir.call @_FortranASumDim(%[[a6]], %[[a7]], %[[c2_i32]], %{{.*}}, %{{.*}}, %[[a9]]) : (!fir.ref<!fir.box<none>>, !fir.box<none>, i32, !fir.ref<i8>, i32, !fir.box<none>) -> none
|
||||||
|
! CHECK-DAG: %[[a11:.*]] = fir.load %[[a0]] : !fir.ref<!fir.box<!fir.heap<!fir.array<?xi32>>>>
|
||||||
|
! CHECK-DAG: %[[a13:.*]] = fir.box_addr %[[a11]] : (!fir.box<!fir.heap<!fir.array<?xi32>>>) -> !fir.heap<!fir.array<?xi32>>
|
||||||
|
! CHECK-DAG: fir.freemem %[[a13]]
|
||||||
|
end subroutine
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test3(
|
||||||
|
! CHECK-SAME: %[[arg0:.*]]: !fir.box<!fir.array<?x!fir.complex<4>>>{{.*}}) -> !fir.complex<4> {
|
||||||
|
complex function sum_test3(a)
|
||||||
|
complex :: a(:)
|
||||||
|
! CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index
|
||||||
|
! CHECK-DAG: %[[a0:.*]] = fir.alloca !fir.complex<4>
|
||||||
|
! CHECK-DAG: %[[a3:.*]] = fir.absent !fir.box<i1>
|
||||||
|
! CHECK-DAG: %[[a5:.*]] = fir.convert %[[a0]] : (!fir.ref<!fir.complex<4>>) -> !fir.ref<complex<f32>>
|
||||||
|
! CHECK-DAG: %[[a6:.*]] = fir.convert %[[arg0]] : (!fir.box<!fir.array<?x!fir.complex<4>>>) -> !fir.box<none>
|
||||||
|
! CHECK-DAG: %[[a8:.*]] = fir.convert %[[c0]] : (index) -> i32
|
||||||
|
! CHECK-DAG: %[[a9:.*]] = fir.convert %[[a3]] : (!fir.box<i1>) -> !fir.box<none>
|
||||||
|
sum_test3 = sum(a)
|
||||||
|
! CHECK: %{{.*}} = fir.call @_FortranACppSumComplex4(%[[a5]], %[[a6]], %{{.*}}, %{{.*}}, %[[a8]], %[[a9]]) : (!fir.ref<complex<f32>>, !fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> none
|
||||||
|
end function
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test4(
|
||||||
|
! CHECK-SAME: %[[arg0:.*]]: !fir.box<!fir.array<?x!fir.complex<10>>>{{.*}}) -> !fir.complex<10> {
|
||||||
|
complex(10) function sum_test4(x)
|
||||||
|
complex(10):: x(:)
|
||||||
|
! CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index
|
||||||
|
! CHECK-DAG: %[[a0:.*]] = fir.alloca !fir.complex<10>
|
||||||
|
sum_test4 = sum(x)
|
||||||
|
! CHECK-DAG: %[[a2:.*]] = fir.absent !fir.box<i1>
|
||||||
|
! CHECK-DAG: %[[a4:.*]] = fir.convert %[[a0]] : (!fir.ref<!fir.complex<10>>) -> !fir.ref<complex<f80>>
|
||||||
|
! CHECK-DAG: %[[a5:.*]] = fir.convert %[[arg0]] : (!fir.box<!fir.array<?x!fir.complex<10>>>) -> !fir.box<none>
|
||||||
|
! CHECK-DAG: %[[a7:.*]] = fir.convert %[[c0]] : (index) -> i32
|
||||||
|
! CHECK-DAG: %[[a8:.*]] = fir.convert %[[a2]] : (!fir.box<i1>) -> !fir.box<none>
|
||||||
|
! CHECK: fir.call @_FortranACppSumComplex10(%[[a4]], %[[a5]], %{{.*}}, %{{.*}}, %[[a7]], %8) : (!fir.ref<complex<f80>>, !fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> ()
|
||||||
|
end
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test_optional(
|
||||||
|
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>>
|
||||||
|
integer function sum_test_optional(mask, x)
|
||||||
|
integer :: x(:)
|
||||||
|
logical, optional :: mask(:)
|
||||||
|
sum_test_optional = sum(x, mask=mask)
|
||||||
|
! CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_0]] : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.box<none>
|
||||||
|
! CHECK: fir.call @_FortranASumInteger4(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_9]]) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i32
|
||||||
|
end function
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test_optional_2(
|
||||||
|
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>>
|
||||||
|
integer function sum_test_optional_2(mask, x)
|
||||||
|
integer :: x(:)
|
||||||
|
logical, pointer :: mask(:)
|
||||||
|
sum_test_optional = sum(x, mask=mask)
|
||||||
|
! CHECK: %[[VAL_4:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>>
|
||||||
|
! CHECK: %[[VAL_5:.*]] = fir.box_addr %[[VAL_4]] : (!fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>) -> !fir.ptr<!fir.array<?x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_6:.*]] = fir.convert %[[VAL_5]] : (!fir.ptr<!fir.array<?x!fir.logical<4>>>) -> i64
|
||||||
|
! CHECK: %[[VAL_7:.*]] = arith.constant 0 : i64
|
||||||
|
! CHECK: %[[VAL_8:.*]] = arith.cmpi ne, %[[VAL_6]], %[[VAL_7]] : i64
|
||||||
|
! CHECK: %[[VAL_9:.*]] = fir.load %[[VAL_0]] : !fir.ref<!fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>>
|
||||||
|
! CHECK: %[[VAL_10:.*]] = fir.absent !fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>
|
||||||
|
! CHECK: %[[VAL_11:.*]] = arith.select %[[VAL_8]], %[[VAL_9]], %[[VAL_10]] : !fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>
|
||||||
|
! CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_11]] : (!fir.box<!fir.ptr<!fir.array<?x!fir.logical<4>>>>) -> !fir.box<none>
|
||||||
|
! CHECK: fir.call @_FortranASumInteger4(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_18]]) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i32
|
||||||
|
end function
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test_optional_3(
|
||||||
|
! CHECK-SAME: %[[VAL_0:.*]]: !fir.ref<!fir.array<10x!fir.logical<4>>>
|
||||||
|
integer function sum_test_optional_3(mask, x)
|
||||||
|
integer :: x(:)
|
||||||
|
logical, optional :: mask(10)
|
||||||
|
sum_test_optional = sum(x, mask=mask)
|
||||||
|
! CHECK: %[[VAL_2:.*]] = arith.constant 10 : index
|
||||||
|
! CHECK: %[[VAL_5:.*]] = fir.is_present %[[VAL_0]] : (!fir.ref<!fir.array<10x!fir.logical<4>>>) -> i1
|
||||||
|
! CHECK: %[[VAL_6:.*]] = fir.shape %[[VAL_2]] : (index) -> !fir.shape<1>
|
||||||
|
! CHECK: %[[VAL_7:.*]] = fir.embox %[[VAL_0]](%[[VAL_6]]) : (!fir.ref<!fir.array<10x!fir.logical<4>>>, !fir.shape<1>) -> !fir.box<!fir.array<10x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_8:.*]] = fir.absent !fir.box<!fir.array<10x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_9:.*]] = arith.select %[[VAL_5]], %[[VAL_7]], %[[VAL_8]] : !fir.box<!fir.array<10x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_9]] : (!fir.box<!fir.array<10x!fir.logical<4>>>) -> !fir.box<none>
|
||||||
|
! CHECK: fir.call @_FortranASumInteger4(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_18]]) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i32
|
||||||
|
end function
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPsum_test_optional_4(
|
||||||
|
integer function sum_test_optional_4(x, use_mask)
|
||||||
|
! Test that local allocatable tracked in local variables
|
||||||
|
! are dealt as optional argument correctly.
|
||||||
|
integer :: x(:)
|
||||||
|
logical :: use_mask
|
||||||
|
logical, allocatable :: mask(:)
|
||||||
|
if (use_mask) then
|
||||||
|
allocate(mask(size(x, 1)))
|
||||||
|
call set_mask(mask)
|
||||||
|
! CHECK: fir.call @_QPset_mask
|
||||||
|
end if
|
||||||
|
sum_test_optional = sum(x, mask=mask)
|
||||||
|
! CHECK: %[[VAL_20:.*]] = fir.load %[[VAL_3:.*]] : !fir.ref<!fir.heap<!fir.array<?x!fir.logical<4>>>>
|
||||||
|
! CHECK: %[[VAL_21:.*]] = fir.convert %[[VAL_20]] : (!fir.heap<!fir.array<?x!fir.logical<4>>>) -> i64
|
||||||
|
! CHECK: %[[VAL_22:.*]] = arith.constant 0 : i64
|
||||||
|
! CHECK: %[[VAL_23:.*]] = arith.cmpi ne, %[[VAL_21]], %[[VAL_22]] : i64
|
||||||
|
! CHECK: %[[VAL_24:.*]] = fir.load %[[VAL_4:.*]] : !fir.ref<index>
|
||||||
|
! CHECK: %[[VAL_25:.*]] = fir.load %[[VAL_5:.*]] : !fir.ref<index>
|
||||||
|
! CHECK: %[[VAL_26:.*]] = fir.load %[[VAL_3]] : !fir.ref<!fir.heap<!fir.array<?x!fir.logical<4>>>>
|
||||||
|
! CHECK: %[[VAL_27:.*]] = fir.shape_shift %[[VAL_24]], %[[VAL_25]] : (index, index) -> !fir.shapeshift<1>
|
||||||
|
! CHECK: %[[VAL_28:.*]] = fir.embox %[[VAL_26]](%[[VAL_27]]) : (!fir.heap<!fir.array<?x!fir.logical<4>>>, !fir.shapeshift<1>) -> !fir.box<!fir.array<?x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_29:.*]] = fir.absent !fir.box<!fir.array<?x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_30:.*]] = arith.select %[[VAL_23]], %[[VAL_28]], %[[VAL_29]] : !fir.box<!fir.array<?x!fir.logical<4>>>
|
||||||
|
! CHECK: %[[VAL_37:.*]] = fir.convert %[[VAL_30]] : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.box<none>
|
||||||
|
! CHECK: fir.call @_FortranASumInteger4(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[VAL_37]]) : (!fir.box<none>, !fir.ref<i8>, i32, i32, !fir.box<none>) -> i32
|
||||||
|
end function
|
|
@ -166,3 +166,4 @@ TEST(TimeIntrinsics, DateAndTime) {
|
||||||
EXPECT_LE(minutes, 59);
|
EXPECT_LE(minutes, 59);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue