mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-28 16:11:29 +00:00
[flang] Lower general forall statement
This patch lowers general forall statements. The forall are lowered to nested loops. This patch is part of the upstreaming effort from fir-dev branch. Depends on D121385 Reviewed By: PeteSteinfeld, schweitz Differential Revision: https://reviews.llvm.org/D121386 Co-authored-by: V Donaldson <vdonaldson@nvidia.com> Co-authored-by: Jean Perier <jperier@nvidia.com> Co-authored-by: Eric Schweitz <eschweitz@nvidia.com>
This commit is contained in:
parent
f39a971d82
commit
88ae0d61c3
@ -24,15 +24,22 @@ class Location;
|
|||||||
|
|
||||||
namespace fir {
|
namespace fir {
|
||||||
class MutableBoxValue;
|
class MutableBoxValue;
|
||||||
} // namespace fir
|
}
|
||||||
|
|
||||||
namespace Fortran::parser {
|
namespace Fortran::parser {
|
||||||
struct AllocateStmt;
|
struct AllocateStmt;
|
||||||
struct DeallocateStmt;
|
struct DeallocateStmt;
|
||||||
} // namespace Fortran::parser
|
} // namespace Fortran::parser
|
||||||
|
|
||||||
|
namespace Fortran::evaluate {
|
||||||
|
template <typename T>
|
||||||
|
class Expr;
|
||||||
|
struct SomeType;
|
||||||
|
} // namespace Fortran::evaluate
|
||||||
|
|
||||||
namespace Fortran::lower {
|
namespace Fortran::lower {
|
||||||
class AbstractConverter;
|
class AbstractConverter;
|
||||||
|
class StatementContext;
|
||||||
|
|
||||||
namespace pft {
|
namespace pft {
|
||||||
struct Variable;
|
struct Variable;
|
||||||
@ -48,13 +55,23 @@ void genDeallocateStmt(Fortran::lower::AbstractConverter &,
|
|||||||
|
|
||||||
/// Create a MutableBoxValue for an allocatable or pointer entity.
|
/// Create a MutableBoxValue for an allocatable or pointer entity.
|
||||||
/// If the variables is a local variable that is not a dummy, it will be
|
/// If the variables is a local variable that is not a dummy, it will be
|
||||||
/// initialized to unallocated/disassociated status.
|
/// initialized to unallocated/diassociated status.
|
||||||
fir::MutableBoxValue createMutableBox(Fortran::lower::AbstractConverter &,
|
fir::MutableBoxValue createMutableBox(Fortran::lower::AbstractConverter &,
|
||||||
mlir::Location,
|
mlir::Location,
|
||||||
const Fortran::lower::pft::Variable &var,
|
const Fortran::lower::pft::Variable &var,
|
||||||
mlir::Value boxAddr,
|
mlir::Value boxAddr,
|
||||||
mlir::ValueRange nonDeferredParams);
|
mlir::ValueRange nonDeferredParams);
|
||||||
|
|
||||||
|
/// Update a MutableBoxValue to describe the entity designated by the expression
|
||||||
|
/// \p source. This version takes care of \p source lowering.
|
||||||
|
/// If \lbounds is not empty, it is used to defined the MutableBoxValue
|
||||||
|
/// lower bounds, otherwise, the lower bounds from \p source are used.
|
||||||
|
void associateMutableBox(
|
||||||
|
Fortran::lower::AbstractConverter &, mlir::Location,
|
||||||
|
const fir::MutableBoxValue &,
|
||||||
|
const Fortran::evaluate::Expr<Fortran::evaluate::SomeType> &source,
|
||||||
|
mlir::ValueRange lbounds, Fortran::lower::StatementContext &);
|
||||||
|
|
||||||
} // namespace Fortran::lower
|
} // namespace Fortran::lower
|
||||||
|
|
||||||
#endif // FORTRAN_LOWER_ALLOCATABLE_H
|
#endif // FORTRAN_LOWER_ALLOCATABLE_H
|
||||||
|
@ -100,7 +100,10 @@ fir::ExtendedValue createSomeArrayBox(AbstractConverter &converter,
|
|||||||
/// The returned value is null otherwise.
|
/// The returned value is null otherwise.
|
||||||
mlir::Value createSubroutineCall(AbstractConverter &converter,
|
mlir::Value createSubroutineCall(AbstractConverter &converter,
|
||||||
const evaluate::ProcedureRef &call,
|
const evaluate::ProcedureRef &call,
|
||||||
SymMap &symMap, StatementContext &stmtCtx);
|
ExplicitIterSpace &explicitIterSpace,
|
||||||
|
ImplicitIterSpace &implicitIterSpace,
|
||||||
|
SymMap &symMap, StatementContext &stmtCtx,
|
||||||
|
bool isUserDefAssignment);
|
||||||
|
|
||||||
/// Create the address of the box.
|
/// Create the address of the box.
|
||||||
/// \p expr must be the designator of an allocatable/pointer entity.
|
/// \p expr must be the designator of an allocatable/pointer entity.
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace fir {
|
namespace fir {
|
||||||
|
class FirOpBuilder;
|
||||||
|
|
||||||
class CharBoxValue;
|
class CharBoxValue;
|
||||||
class ArrayBoxValue;
|
class ArrayBoxValue;
|
||||||
class CharArrayBoxValue;
|
class CharArrayBoxValue;
|
||||||
@ -402,6 +404,12 @@ bool isArray(const ExtendedValue &exv);
|
|||||||
/// Get the type parameters for `exv`.
|
/// Get the type parameters for `exv`.
|
||||||
llvm::SmallVector<mlir::Value> getTypeParams(const ExtendedValue &exv);
|
llvm::SmallVector<mlir::Value> getTypeParams(const ExtendedValue &exv);
|
||||||
|
|
||||||
|
/// Get exactly one extent for any array-like extended value, \p exv. If \p exv
|
||||||
|
/// is not an array or has rank less then \p dim, the result will be a nullptr.
|
||||||
|
mlir::Value getExtentAtDimension(const ExtendedValue &exv,
|
||||||
|
FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
unsigned dim);
|
||||||
|
|
||||||
/// An extended value is a box of values pertaining to a discrete entity. It is
|
/// An extended value is a box of values pertaining to a discrete entity. It is
|
||||||
/// used in lowering to track all the runtime values related to an entity. For
|
/// used in lowering to track all the runtime values related to an entity. For
|
||||||
/// example, an entity may have an address in memory that contains its value(s)
|
/// example, an entity may have an address in memory that contains its value(s)
|
||||||
|
46
flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
Normal file
46
flang/include/flang/Optimizer/Builder/Runtime/Inquiry.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
//===-- Inquiry.h - generate inquiry runtime API calls ----------*- 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_OPTIMIZER_BUILDER_RUNTIME_INQUIRY_H
|
||||||
|
#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_INQUIRY_H
|
||||||
|
|
||||||
|
namespace mlir {
|
||||||
|
class Value;
|
||||||
|
class Location;
|
||||||
|
} // namespace mlir
|
||||||
|
|
||||||
|
namespace fir {
|
||||||
|
class FirOpBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace fir::runtime {
|
||||||
|
|
||||||
|
/// Generate call to general `LboundDim` runtime routine. Calls to LBOUND
|
||||||
|
/// without a DIM argument get transformed into descriptor inquiries so they're
|
||||||
|
/// not handled in the runtime.
|
||||||
|
mlir::Value genLboundDim(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
mlir::Value array, mlir::Value dim);
|
||||||
|
|
||||||
|
/// Generate call to general `Ubound` runtime routine. Calls to UBOUND
|
||||||
|
/// with a DIM argument get transformed into an expression equivalent to
|
||||||
|
/// SIZE() + LBOUND() - 1, so they don't have an intrinsic in the runtime.
|
||||||
|
void genUbound(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
mlir::Value resultBox, mlir::Value array, mlir::Value kind);
|
||||||
|
|
||||||
|
/// Generate call to `Size` runtime routine. This routine is a specialized
|
||||||
|
/// version when the DIM argument is not specified by the user.
|
||||||
|
mlir::Value genSize(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
mlir::Value array);
|
||||||
|
|
||||||
|
/// Generate call to general `SizeDim` runtime routine. This version is for
|
||||||
|
/// when the user specifies a DIM argument.
|
||||||
|
mlir::Value genSizeDim(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
mlir::Value array, mlir::Value dim);
|
||||||
|
|
||||||
|
} // namespace fir::runtime
|
||||||
|
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_INQUIRY_H
|
@ -666,3 +666,33 @@ fir::MutableBoxValue Fortran::lower::createMutableBox(
|
|||||||
fir::factory::disassociateMutableBox(builder, loc, box);
|
fir::factory::disassociateMutableBox(builder, loc, box);
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// MutableBoxValue reading interface implementation
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
static bool
|
||||||
|
isArraySectionWithoutVectorSubscript(const Fortran::lower::SomeExpr &expr) {
|
||||||
|
return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
|
||||||
|
!Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) &&
|
||||||
|
!Fortran::evaluate::HasVectorSubscript(expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fortran::lower::associateMutableBox(
|
||||||
|
Fortran::lower::AbstractConverter &converter, mlir::Location loc,
|
||||||
|
const fir::MutableBoxValue &box, const Fortran::lower::SomeExpr &source,
|
||||||
|
mlir::ValueRange lbounds, Fortran::lower::StatementContext &stmtCtx) {
|
||||||
|
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
|
||||||
|
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(source)) {
|
||||||
|
fir::factory::disassociateMutableBox(builder, loc, box);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The right hand side must not be evaluated in a temp.
|
||||||
|
// Array sections can be described by fir.box without making a temp.
|
||||||
|
// Otherwise, do not generate a fir.box to avoid having to later use a
|
||||||
|
// fir.rebox to implement the pointer association.
|
||||||
|
fir::ExtendedValue rhs = isArraySectionWithoutVectorSubscript(source)
|
||||||
|
? converter.genExprBox(source, stmtCtx, loc)
|
||||||
|
: converter.genExprAddr(source, stmtCtx);
|
||||||
|
fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds);
|
||||||
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "flang/Optimizer/Builder/BoxValue.h"
|
#include "flang/Optimizer/Builder/BoxValue.h"
|
||||||
#include "flang/Optimizer/Builder/Character.h"
|
#include "flang/Optimizer/Builder/Character.h"
|
||||||
#include "flang/Optimizer/Builder/MutableBox.h"
|
#include "flang/Optimizer/Builder/MutableBox.h"
|
||||||
|
#include "flang/Optimizer/Builder/Runtime/Ragged.h"
|
||||||
#include "flang/Optimizer/Dialect/FIRAttr.h"
|
#include "flang/Optimizer/Dialect/FIRAttr.h"
|
||||||
#include "flang/Optimizer/Support/FIRContext.h"
|
#include "flang/Optimizer/Support/FIRContext.h"
|
||||||
#include "flang/Optimizer/Support/InternalNames.h"
|
#include "flang/Optimizer/Support/InternalNames.h"
|
||||||
@ -849,9 +850,14 @@ private:
|
|||||||
return sym && Fortran::semantics::IsAllocatable(*sym);
|
return sym && Fortran::semantics::IsAllocatable(*sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Shared for both assignments and pointer assignments.
|
||||||
void genAssignment(const Fortran::evaluate::Assignment &assign) {
|
void genAssignment(const Fortran::evaluate::Assignment &assign) {
|
||||||
Fortran::lower::StatementContext stmtCtx;
|
Fortran::lower::StatementContext stmtCtx;
|
||||||
mlir::Location loc = toLocation();
|
mlir::Location loc = toLocation();
|
||||||
|
if (explicitIterationSpace()) {
|
||||||
|
Fortran::lower::createArrayLoads(*this, explicitIterSpace, localSymbols);
|
||||||
|
explicitIterSpace.genLoopNest();
|
||||||
|
}
|
||||||
std::visit(
|
std::visit(
|
||||||
Fortran::common::visitors{
|
Fortran::common::visitors{
|
||||||
// [1] Plain old assignment.
|
// [1] Plain old assignment.
|
||||||
@ -875,7 +881,7 @@ private:
|
|||||||
// on a pointer returns the target address and not the address of
|
// on a pointer returns the target address and not the address of
|
||||||
// the pointer variable.
|
// the pointer variable.
|
||||||
|
|
||||||
if (assign.lhs.Rank() > 0) {
|
if (assign.lhs.Rank() > 0 || explicitIterationSpace()) {
|
||||||
// Array assignment
|
// Array assignment
|
||||||
// See Fortran 2018 10.2.1.3 p5, p6, and p7
|
// See Fortran 2018 10.2.1.3 p5, p6, and p7
|
||||||
genArrayAssignment(assign, stmtCtx);
|
genArrayAssignment(assign, stmtCtx);
|
||||||
@ -934,7 +940,9 @@ private:
|
|||||||
fir::factory::CharacterExprHelper{*builder, loc}.createAssign(
|
fir::factory::CharacterExprHelper{*builder, loc}.createAssign(
|
||||||
lhs, rhs);
|
lhs, rhs);
|
||||||
} else if (isDerivedCategory(lhsType->category())) {
|
} else if (isDerivedCategory(lhsType->category())) {
|
||||||
TODO(toLocation(), "Derived type assignment");
|
// Fortran 2018 10.2.1.3 p13 and p14
|
||||||
|
// Recursively gen an assignment on each element pair.
|
||||||
|
fir::factory::genRecordAssignment(*builder, loc, lhs, rhs);
|
||||||
} else {
|
} else {
|
||||||
llvm_unreachable("unknown category");
|
llvm_unreachable("unknown category");
|
||||||
}
|
}
|
||||||
@ -948,36 +956,132 @@ private:
|
|||||||
// [2] User defined assignment. If the context is a scalar
|
// [2] User defined assignment. If the context is a scalar
|
||||||
// expression then call the procedure.
|
// expression then call the procedure.
|
||||||
[&](const Fortran::evaluate::ProcedureRef &procRef) {
|
[&](const Fortran::evaluate::ProcedureRef &procRef) {
|
||||||
TODO(toLocation(), "User defined assignment");
|
Fortran::lower::StatementContext &ctx =
|
||||||
|
explicitIterationSpace() ? explicitIterSpace.stmtContext()
|
||||||
|
: stmtCtx;
|
||||||
|
Fortran::lower::createSubroutineCall(
|
||||||
|
*this, procRef, explicitIterSpace, implicitIterSpace,
|
||||||
|
localSymbols, ctx, /*isUserDefAssignment=*/true);
|
||||||
},
|
},
|
||||||
|
|
||||||
// [3] Pointer assignment with possibly empty bounds-spec. R1035: a
|
// [3] Pointer assignment with possibly empty bounds-spec. R1035: a
|
||||||
// bounds-spec is a lower bound value.
|
// bounds-spec is a lower bound value.
|
||||||
[&](const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) {
|
[&](const Fortran::evaluate::Assignment::BoundsSpec &lbExprs) {
|
||||||
TODO(toLocation(),
|
if (IsProcedure(assign.rhs))
|
||||||
"Pointer assignment with possibly empty bounds-spec");
|
TODO(loc, "procedure pointer assignment");
|
||||||
|
std::optional<Fortran::evaluate::DynamicType> lhsType =
|
||||||
|
assign.lhs.GetType();
|
||||||
|
std::optional<Fortran::evaluate::DynamicType> rhsType =
|
||||||
|
assign.rhs.GetType();
|
||||||
|
// Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3.
|
||||||
|
if ((lhsType && lhsType->IsPolymorphic()) ||
|
||||||
|
(rhsType && rhsType->IsPolymorphic()))
|
||||||
|
TODO(loc, "pointer assignment involving polymorphic entity");
|
||||||
|
|
||||||
|
// FIXME: in the explicit space context, we want to use
|
||||||
|
// ScalarArrayExprLowering here.
|
||||||
|
fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs);
|
||||||
|
llvm::SmallVector<mlir::Value> lbounds;
|
||||||
|
for (const Fortran::evaluate::ExtentExpr &lbExpr : lbExprs)
|
||||||
|
lbounds.push_back(
|
||||||
|
fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx)));
|
||||||
|
Fortran::lower::associateMutableBox(*this, loc, lhs, assign.rhs,
|
||||||
|
lbounds, stmtCtx);
|
||||||
|
if (explicitIterationSpace()) {
|
||||||
|
mlir::ValueRange inners = explicitIterSpace.getInnerArgs();
|
||||||
|
if (!inners.empty()) {
|
||||||
|
// TODO: should force a copy-in/copy-out here.
|
||||||
|
// e.g., obj%ptr(i+1) => obj%ptr(i)
|
||||||
|
builder->create<fir::ResultOp>(loc, inners);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// [4] Pointer assignment with bounds-remapping. R1036: a
|
// [4] Pointer assignment with bounds-remapping. R1036: a
|
||||||
// bounds-remapping is a pair, lower bound and upper bound.
|
// bounds-remapping is a pair, lower bound and upper bound.
|
||||||
[&](const Fortran::evaluate::Assignment::BoundsRemapping
|
[&](const Fortran::evaluate::Assignment::BoundsRemapping
|
||||||
&boundExprs) {
|
&boundExprs) {
|
||||||
TODO(toLocation(), "Pointer assignment with bounds-remapping");
|
std::optional<Fortran::evaluate::DynamicType> lhsType =
|
||||||
|
assign.lhs.GetType();
|
||||||
|
std::optional<Fortran::evaluate::DynamicType> rhsType =
|
||||||
|
assign.rhs.GetType();
|
||||||
|
// Polymorphic lhs/rhs may need more care. See F2018 10.2.2.3.
|
||||||
|
if ((lhsType && lhsType->IsPolymorphic()) ||
|
||||||
|
(rhsType && rhsType->IsPolymorphic()))
|
||||||
|
TODO(loc, "pointer assignment involving polymorphic entity");
|
||||||
|
|
||||||
|
// FIXME: in the explicit space context, we want to use
|
||||||
|
// ScalarArrayExprLowering here.
|
||||||
|
fir::MutableBoxValue lhs = genExprMutableBox(loc, assign.lhs);
|
||||||
|
if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
|
||||||
|
assign.rhs)) {
|
||||||
|
fir::factory::disassociateMutableBox(*builder, loc, lhs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
llvm::SmallVector<mlir::Value> lbounds;
|
||||||
|
llvm::SmallVector<mlir::Value> ubounds;
|
||||||
|
for (const std::pair<Fortran::evaluate::ExtentExpr,
|
||||||
|
Fortran::evaluate::ExtentExpr> &pair :
|
||||||
|
boundExprs) {
|
||||||
|
const Fortran::evaluate::ExtentExpr &lbExpr = pair.first;
|
||||||
|
const Fortran::evaluate::ExtentExpr &ubExpr = pair.second;
|
||||||
|
lbounds.push_back(
|
||||||
|
fir::getBase(genExprValue(toEvExpr(lbExpr), stmtCtx)));
|
||||||
|
ubounds.push_back(
|
||||||
|
fir::getBase(genExprValue(toEvExpr(ubExpr), stmtCtx)));
|
||||||
|
}
|
||||||
|
// Do not generate a temp in case rhs is an array section.
|
||||||
|
fir::ExtendedValue rhs =
|
||||||
|
isArraySectionWithoutVectorSubscript(assign.rhs)
|
||||||
|
? Fortran::lower::createSomeArrayBox(
|
||||||
|
*this, assign.rhs, localSymbols, stmtCtx)
|
||||||
|
: genExprAddr(assign.rhs, stmtCtx);
|
||||||
|
fir::factory::associateMutableBoxWithRemap(*builder, loc, lhs,
|
||||||
|
rhs, lbounds, ubounds);
|
||||||
|
if (explicitIterationSpace()) {
|
||||||
|
mlir::ValueRange inners = explicitIterSpace.getInnerArgs();
|
||||||
|
if (!inners.empty()) {
|
||||||
|
// TODO: should force a copy-in/copy-out here.
|
||||||
|
// e.g., obj%ptr(i+1) => obj%ptr(i)
|
||||||
|
builder->create<fir::ResultOp>(loc, inners);
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
assign.u);
|
assign.u);
|
||||||
|
if (explicitIterationSpace())
|
||||||
|
Fortran::lower::createArrayMergeStores(*this, explicitIterSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lowering of CALL statement
|
/// Lowering of CALL statement
|
||||||
void genFIR(const Fortran::parser::CallStmt &stmt) {
|
void genFIR(const Fortran::parser::CallStmt &stmt) {
|
||||||
Fortran::lower::StatementContext stmtCtx;
|
Fortran::lower::StatementContext stmtCtx;
|
||||||
|
Fortran::lower::pft::Evaluation &eval = getEval();
|
||||||
setCurrentPosition(stmt.v.source);
|
setCurrentPosition(stmt.v.source);
|
||||||
assert(stmt.typedCall && "Call was not analyzed");
|
assert(stmt.typedCall && "Call was not analyzed");
|
||||||
// Call statement lowering shares code with function call lowering.
|
// Call statement lowering shares code with function call lowering.
|
||||||
mlir::Value res = Fortran::lower::createSubroutineCall(
|
mlir::Value res = Fortran::lower::createSubroutineCall(
|
||||||
*this, *stmt.typedCall, localSymbols, stmtCtx);
|
*this, *stmt.typedCall, explicitIterSpace, implicitIterSpace,
|
||||||
|
localSymbols, stmtCtx, /*isUserDefAssignment=*/false);
|
||||||
if (!res)
|
if (!res)
|
||||||
return; // "Normal" subroutine call.
|
return; // "Normal" subroutine call.
|
||||||
|
// Call with alternate return specifiers.
|
||||||
|
// The call returns an index that selects an alternate return branch target.
|
||||||
|
llvm::SmallVector<int64_t> indexList;
|
||||||
|
llvm::SmallVector<mlir::Block *> blockList;
|
||||||
|
int64_t index = 0;
|
||||||
|
for (const Fortran::parser::ActualArgSpec &arg :
|
||||||
|
std::get<std::list<Fortran::parser::ActualArgSpec>>(stmt.v.t)) {
|
||||||
|
const auto &actual = std::get<Fortran::parser::ActualArg>(arg.t);
|
||||||
|
if (const auto *altReturn =
|
||||||
|
std::get_if<Fortran::parser::AltReturnSpec>(&actual.u)) {
|
||||||
|
indexList.push_back(++index);
|
||||||
|
blockList.push_back(blockOfLabel(eval, altReturn->v));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
blockList.push_back(eval.nonNopSuccessor().block); // default = fallthrough
|
||||||
|
stmtCtx.finalize();
|
||||||
|
builder->create<fir::SelectOp>(toLocation(), res, indexList, blockList);
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::ComputedGotoStmt &stmt) {
|
void genFIR(const Fortran::parser::ComputedGotoStmt &stmt) {
|
||||||
@ -1171,28 +1275,199 @@ private:
|
|||||||
genFIR(stmt.statement);
|
genFIR(stmt.statement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Force the binding of an explicit symbol. This is used to bind and re-bind
|
||||||
|
/// a concurrent control symbol to its value.
|
||||||
|
void forceControlVariableBinding(const Fortran::semantics::Symbol *sym,
|
||||||
|
mlir::Value inducVar) {
|
||||||
|
mlir::Location loc = toLocation();
|
||||||
|
assert(sym && "There must be a symbol to bind");
|
||||||
|
mlir::Type toTy = genType(*sym);
|
||||||
|
// FIXME: this should be a "per iteration" temporary.
|
||||||
|
mlir::Value tmp = builder->createTemporary(
|
||||||
|
loc, toTy, toStringRef(sym->name()),
|
||||||
|
llvm::ArrayRef<mlir::NamedAttribute>{
|
||||||
|
Fortran::lower::getAdaptToByRefAttr(*builder)});
|
||||||
|
mlir::Value cast = builder->createConvert(loc, toTy, inducVar);
|
||||||
|
builder->create<fir::StoreOp>(loc, cast, tmp);
|
||||||
|
localSymbols.addSymbol(*sym, tmp, /*force=*/true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Process a concurrent header for a FORALL. (Concurrent headers for DO
|
||||||
|
/// CONCURRENT loops are lowered elsewhere.)
|
||||||
void genFIR(const Fortran::parser::ConcurrentHeader &header) {
|
void genFIR(const Fortran::parser::ConcurrentHeader &header) {
|
||||||
TODO(toLocation(), "ConcurrentHeader lowering");
|
llvm::SmallVector<mlir::Value> lows;
|
||||||
|
llvm::SmallVector<mlir::Value> highs;
|
||||||
|
llvm::SmallVector<mlir::Value> steps;
|
||||||
|
if (explicitIterSpace.isOutermostForall()) {
|
||||||
|
// For the outermost forall, we evaluate the bounds expressions once.
|
||||||
|
// Contrastingly, if this forall is nested, the bounds expressions are
|
||||||
|
// assumed to be pure, possibly dependent on outer concurrent control
|
||||||
|
// variables, possibly variant with respect to arguments, and will be
|
||||||
|
// re-evaluated.
|
||||||
|
mlir::Location loc = toLocation();
|
||||||
|
mlir::Type idxTy = builder->getIndexType();
|
||||||
|
Fortran::lower::StatementContext &stmtCtx =
|
||||||
|
explicitIterSpace.stmtContext();
|
||||||
|
auto lowerExpr = [&](auto &e) {
|
||||||
|
return fir::getBase(genExprValue(e, stmtCtx));
|
||||||
|
};
|
||||||
|
for (const Fortran::parser::ConcurrentControl &ctrl :
|
||||||
|
std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) {
|
||||||
|
const Fortran::lower::SomeExpr *lo =
|
||||||
|
Fortran::semantics::GetExpr(std::get<1>(ctrl.t));
|
||||||
|
const Fortran::lower::SomeExpr *hi =
|
||||||
|
Fortran::semantics::GetExpr(std::get<2>(ctrl.t));
|
||||||
|
auto &optStep =
|
||||||
|
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(ctrl.t);
|
||||||
|
lows.push_back(builder->createConvert(loc, idxTy, lowerExpr(*lo)));
|
||||||
|
highs.push_back(builder->createConvert(loc, idxTy, lowerExpr(*hi)));
|
||||||
|
steps.push_back(
|
||||||
|
optStep.has_value()
|
||||||
|
? builder->createConvert(
|
||||||
|
loc, idxTy,
|
||||||
|
lowerExpr(*Fortran::semantics::GetExpr(*optStep)))
|
||||||
|
: builder->createIntegerConstant(loc, idxTy, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto lambda = [&, lows, highs, steps]() {
|
||||||
|
// Create our iteration space from the header spec.
|
||||||
|
mlir::Location loc = toLocation();
|
||||||
|
mlir::Type idxTy = builder->getIndexType();
|
||||||
|
llvm::SmallVector<fir::DoLoopOp> loops;
|
||||||
|
Fortran::lower::StatementContext &stmtCtx =
|
||||||
|
explicitIterSpace.stmtContext();
|
||||||
|
auto lowerExpr = [&](auto &e) {
|
||||||
|
return fir::getBase(genExprValue(e, stmtCtx));
|
||||||
|
};
|
||||||
|
const bool outermost = !lows.empty();
|
||||||
|
std::size_t headerIndex = 0;
|
||||||
|
for (const Fortran::parser::ConcurrentControl &ctrl :
|
||||||
|
std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) {
|
||||||
|
const Fortran::semantics::Symbol *ctrlVar =
|
||||||
|
std::get<Fortran::parser::Name>(ctrl.t).symbol;
|
||||||
|
mlir::Value lb;
|
||||||
|
mlir::Value ub;
|
||||||
|
mlir::Value by;
|
||||||
|
if (outermost) {
|
||||||
|
assert(headerIndex < lows.size());
|
||||||
|
if (headerIndex == 0)
|
||||||
|
explicitIterSpace.resetInnerArgs();
|
||||||
|
lb = lows[headerIndex];
|
||||||
|
ub = highs[headerIndex];
|
||||||
|
by = steps[headerIndex++];
|
||||||
|
} else {
|
||||||
|
const Fortran::lower::SomeExpr *lo =
|
||||||
|
Fortran::semantics::GetExpr(std::get<1>(ctrl.t));
|
||||||
|
const Fortran::lower::SomeExpr *hi =
|
||||||
|
Fortran::semantics::GetExpr(std::get<2>(ctrl.t));
|
||||||
|
auto &optStep =
|
||||||
|
std::get<std::optional<Fortran::parser::ScalarIntExpr>>(ctrl.t);
|
||||||
|
lb = builder->createConvert(loc, idxTy, lowerExpr(*lo));
|
||||||
|
ub = builder->createConvert(loc, idxTy, lowerExpr(*hi));
|
||||||
|
by = optStep.has_value()
|
||||||
|
? builder->createConvert(
|
||||||
|
loc, idxTy,
|
||||||
|
lowerExpr(*Fortran::semantics::GetExpr(*optStep)))
|
||||||
|
: builder->createIntegerConstant(loc, idxTy, 1);
|
||||||
|
}
|
||||||
|
auto lp = builder->create<fir::DoLoopOp>(
|
||||||
|
loc, lb, ub, by, /*unordered=*/true,
|
||||||
|
/*finalCount=*/false, explicitIterSpace.getInnerArgs());
|
||||||
|
if (!loops.empty() || !outermost)
|
||||||
|
builder->create<fir::ResultOp>(loc, lp.getResults());
|
||||||
|
explicitIterSpace.setInnerArgs(lp.getRegionIterArgs());
|
||||||
|
builder->setInsertionPointToStart(lp.getBody());
|
||||||
|
forceControlVariableBinding(ctrlVar, lp.getInductionVar());
|
||||||
|
loops.push_back(lp);
|
||||||
|
}
|
||||||
|
if (outermost)
|
||||||
|
explicitIterSpace.setOuterLoop(loops[0]);
|
||||||
|
explicitIterSpace.appendLoops(loops);
|
||||||
|
if (const auto &mask =
|
||||||
|
std::get<std::optional<Fortran::parser::ScalarLogicalExpr>>(
|
||||||
|
header.t);
|
||||||
|
mask.has_value()) {
|
||||||
|
mlir::Type i1Ty = builder->getI1Type();
|
||||||
|
fir::ExtendedValue maskExv =
|
||||||
|
genExprValue(*Fortran::semantics::GetExpr(mask.value()), stmtCtx);
|
||||||
|
mlir::Value cond =
|
||||||
|
builder->createConvert(loc, i1Ty, fir::getBase(maskExv));
|
||||||
|
auto ifOp = builder->create<fir::IfOp>(
|
||||||
|
loc, explicitIterSpace.innerArgTypes(), cond,
|
||||||
|
/*withElseRegion=*/true);
|
||||||
|
builder->create<fir::ResultOp>(loc, ifOp.getResults());
|
||||||
|
builder->setInsertionPointToStart(&ifOp.getElseRegion().front());
|
||||||
|
builder->create<fir::ResultOp>(loc, explicitIterSpace.getInnerArgs());
|
||||||
|
builder->setInsertionPointToStart(&ifOp.getThenRegion().front());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Push the lambda to gen the loop nest context.
|
||||||
|
explicitIterSpace.pushLoopNest(lambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::ForallAssignmentStmt &stmt) {
|
void genFIR(const Fortran::parser::ForallAssignmentStmt &stmt) {
|
||||||
TODO(toLocation(), "ForallAssignmentStmt lowering");
|
std::visit([&](const auto &x) { genFIR(x); }, stmt.u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::EndForallStmt &) {
|
void genFIR(const Fortran::parser::EndForallStmt &) {
|
||||||
TODO(toLocation(), "EndForallStmt lowering");
|
cleanupExplicitSpace();
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::ForallStmt &) {
|
template <typename A>
|
||||||
TODO(toLocation(), "ForallStmt lowering");
|
void prepareExplicitSpace(const A &forall) {
|
||||||
|
if (!explicitIterSpace.isActive())
|
||||||
|
analyzeExplicitSpace(forall);
|
||||||
|
localSymbols.pushScope();
|
||||||
|
explicitIterSpace.enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::ForallConstruct &) {
|
/// Cleanup all the FORALL context information when we exit.
|
||||||
TODO(toLocation(), "ForallConstruct lowering");
|
void cleanupExplicitSpace() {
|
||||||
|
explicitIterSpace.leave();
|
||||||
|
localSymbols.popScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::ForallConstructStmt &) {
|
/// Generate FIR for a FORALL statement.
|
||||||
TODO(toLocation(), "ForallConstructStmt lowering");
|
void genFIR(const Fortran::parser::ForallStmt &stmt) {
|
||||||
|
prepareExplicitSpace(stmt);
|
||||||
|
genFIR(std::get<
|
||||||
|
Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>(
|
||||||
|
stmt.t)
|
||||||
|
.value());
|
||||||
|
genFIR(std::get<Fortran::parser::UnlabeledStatement<
|
||||||
|
Fortran::parser::ForallAssignmentStmt>>(stmt.t)
|
||||||
|
.statement);
|
||||||
|
cleanupExplicitSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate FIR for a FORALL construct.
|
||||||
|
void genFIR(const Fortran::parser::ForallConstruct &forall) {
|
||||||
|
prepareExplicitSpace(forall);
|
||||||
|
genNestedStatement(
|
||||||
|
std::get<
|
||||||
|
Fortran::parser::Statement<Fortran::parser::ForallConstructStmt>>(
|
||||||
|
forall.t));
|
||||||
|
for (const Fortran::parser::ForallBodyConstruct &s :
|
||||||
|
std::get<std::list<Fortran::parser::ForallBodyConstruct>>(forall.t)) {
|
||||||
|
std::visit(
|
||||||
|
Fortran::common::visitors{
|
||||||
|
[&](const Fortran::parser::WhereConstruct &b) { genFIR(b); },
|
||||||
|
[&](const Fortran::common::Indirection<
|
||||||
|
Fortran::parser::ForallConstruct> &b) { genFIR(b.value()); },
|
||||||
|
[&](const auto &b) { genNestedStatement(b); }},
|
||||||
|
s.u);
|
||||||
|
}
|
||||||
|
genNestedStatement(
|
||||||
|
std::get<Fortran::parser::Statement<Fortran::parser::EndForallStmt>>(
|
||||||
|
forall.t));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lower the concurrent header specification.
|
||||||
|
void genFIR(const Fortran::parser::ForallConstructStmt &stmt) {
|
||||||
|
genFIR(std::get<
|
||||||
|
Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>(
|
||||||
|
stmt.t)
|
||||||
|
.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
void genFIR(const Fortran::parser::CompilerDirective &) {
|
void genFIR(const Fortran::parser::CompilerDirective &) {
|
||||||
@ -1750,6 +2025,208 @@ private:
|
|||||||
eval.visit([&](const auto &stmt) { genFIR(stmt); });
|
eval.visit([&](const auto &stmt) { genFIR(stmt); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
// Analysis on a nested explicit iteration space.
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::ConcurrentHeader &header) {
|
||||||
|
explicitIterSpace.pushLevel();
|
||||||
|
for (const Fortran::parser::ConcurrentControl &ctrl :
|
||||||
|
std::get<std::list<Fortran::parser::ConcurrentControl>>(header.t)) {
|
||||||
|
const Fortran::semantics::Symbol *ctrlVar =
|
||||||
|
std::get<Fortran::parser::Name>(ctrl.t).symbol;
|
||||||
|
explicitIterSpace.addSymbol(ctrlVar);
|
||||||
|
}
|
||||||
|
if (const auto &mask =
|
||||||
|
std::get<std::optional<Fortran::parser::ScalarLogicalExpr>>(
|
||||||
|
header.t);
|
||||||
|
mask.has_value())
|
||||||
|
analyzeExplicitSpace(*Fortran::semantics::GetExpr(*mask));
|
||||||
|
}
|
||||||
|
template <bool LHS = false, typename A>
|
||||||
|
void analyzeExplicitSpace(const Fortran::evaluate::Expr<A> &e) {
|
||||||
|
explicitIterSpace.exprBase(&e, LHS);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::evaluate::Assignment *assign) {
|
||||||
|
auto analyzeAssign = [&](const Fortran::lower::SomeExpr &lhs,
|
||||||
|
const Fortran::lower::SomeExpr &rhs) {
|
||||||
|
analyzeExplicitSpace</*LHS=*/true>(lhs);
|
||||||
|
analyzeExplicitSpace(rhs);
|
||||||
|
};
|
||||||
|
std::visit(
|
||||||
|
Fortran::common::visitors{
|
||||||
|
[&](const Fortran::evaluate::ProcedureRef &procRef) {
|
||||||
|
// Ensure the procRef expressions are the one being visited.
|
||||||
|
assert(procRef.arguments().size() == 2);
|
||||||
|
const Fortran::lower::SomeExpr *lhs =
|
||||||
|
procRef.arguments()[0].value().UnwrapExpr();
|
||||||
|
const Fortran::lower::SomeExpr *rhs =
|
||||||
|
procRef.arguments()[1].value().UnwrapExpr();
|
||||||
|
assert(lhs && rhs &&
|
||||||
|
"user defined assignment arguments must be expressions");
|
||||||
|
analyzeAssign(*lhs, *rhs);
|
||||||
|
},
|
||||||
|
[&](const auto &) { analyzeAssign(assign->lhs, assign->rhs); }},
|
||||||
|
assign->u);
|
||||||
|
explicitIterSpace.endAssign();
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::ForallAssignmentStmt &stmt) {
|
||||||
|
std::visit([&](const auto &s) { analyzeExplicitSpace(s); }, stmt.u);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::AssignmentStmt &s) {
|
||||||
|
analyzeExplicitSpace(s.typedAssignment->v.operator->());
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::PointerAssignmentStmt &s) {
|
||||||
|
analyzeExplicitSpace(s.typedAssignment->v.operator->());
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::WhereConstruct &c) {
|
||||||
|
analyzeExplicitSpace(
|
||||||
|
std::get<
|
||||||
|
Fortran::parser::Statement<Fortran::parser::WhereConstructStmt>>(
|
||||||
|
c.t)
|
||||||
|
.statement);
|
||||||
|
for (const Fortran::parser::WhereBodyConstruct &body :
|
||||||
|
std::get<std::list<Fortran::parser::WhereBodyConstruct>>(c.t))
|
||||||
|
analyzeExplicitSpace(body);
|
||||||
|
for (const Fortran::parser::WhereConstruct::MaskedElsewhere &e :
|
||||||
|
std::get<std::list<Fortran::parser::WhereConstruct::MaskedElsewhere>>(
|
||||||
|
c.t))
|
||||||
|
analyzeExplicitSpace(e);
|
||||||
|
if (const auto &e =
|
||||||
|
std::get<std::optional<Fortran::parser::WhereConstruct::Elsewhere>>(
|
||||||
|
c.t);
|
||||||
|
e.has_value())
|
||||||
|
analyzeExplicitSpace(e.operator->());
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::WhereConstructStmt &ws) {
|
||||||
|
const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr(
|
||||||
|
std::get<Fortran::parser::LogicalExpr>(ws.t));
|
||||||
|
addMaskVariable(exp);
|
||||||
|
analyzeExplicitSpace(*exp);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(
|
||||||
|
const Fortran::parser::WhereConstruct::MaskedElsewhere &ew) {
|
||||||
|
analyzeExplicitSpace(
|
||||||
|
std::get<
|
||||||
|
Fortran::parser::Statement<Fortran::parser::MaskedElsewhereStmt>>(
|
||||||
|
ew.t)
|
||||||
|
.statement);
|
||||||
|
for (const Fortran::parser::WhereBodyConstruct &e :
|
||||||
|
std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew.t))
|
||||||
|
analyzeExplicitSpace(e);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::WhereBodyConstruct &body) {
|
||||||
|
std::visit(Fortran::common::visitors{
|
||||||
|
[&](const Fortran::common::Indirection<
|
||||||
|
Fortran::parser::WhereConstruct> &wc) {
|
||||||
|
analyzeExplicitSpace(wc.value());
|
||||||
|
},
|
||||||
|
[&](const auto &s) { analyzeExplicitSpace(s.statement); }},
|
||||||
|
body.u);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::MaskedElsewhereStmt &stmt) {
|
||||||
|
const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr(
|
||||||
|
std::get<Fortran::parser::LogicalExpr>(stmt.t));
|
||||||
|
addMaskVariable(exp);
|
||||||
|
analyzeExplicitSpace(*exp);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
analyzeExplicitSpace(const Fortran::parser::WhereConstruct::Elsewhere *ew) {
|
||||||
|
for (const Fortran::parser::WhereBodyConstruct &e :
|
||||||
|
std::get<std::list<Fortran::parser::WhereBodyConstruct>>(ew->t))
|
||||||
|
analyzeExplicitSpace(e);
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::WhereStmt &stmt) {
|
||||||
|
const Fortran::lower::SomeExpr *exp = Fortran::semantics::GetExpr(
|
||||||
|
std::get<Fortran::parser::LogicalExpr>(stmt.t));
|
||||||
|
addMaskVariable(exp);
|
||||||
|
analyzeExplicitSpace(*exp);
|
||||||
|
const std::optional<Fortran::evaluate::Assignment> &assign =
|
||||||
|
std::get<Fortran::parser::AssignmentStmt>(stmt.t).typedAssignment->v;
|
||||||
|
assert(assign.has_value() && "WHERE has no statement");
|
||||||
|
analyzeExplicitSpace(assign.operator->());
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::ForallStmt &forall) {
|
||||||
|
analyzeExplicitSpace(
|
||||||
|
std::get<
|
||||||
|
Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>(
|
||||||
|
forall.t)
|
||||||
|
.value());
|
||||||
|
analyzeExplicitSpace(std::get<Fortran::parser::UnlabeledStatement<
|
||||||
|
Fortran::parser::ForallAssignmentStmt>>(forall.t)
|
||||||
|
.statement);
|
||||||
|
analyzeExplicitSpacePop();
|
||||||
|
}
|
||||||
|
void
|
||||||
|
analyzeExplicitSpace(const Fortran::parser::ForallConstructStmt &forall) {
|
||||||
|
analyzeExplicitSpace(
|
||||||
|
std::get<
|
||||||
|
Fortran::common::Indirection<Fortran::parser::ConcurrentHeader>>(
|
||||||
|
forall.t)
|
||||||
|
.value());
|
||||||
|
}
|
||||||
|
void analyzeExplicitSpace(const Fortran::parser::ForallConstruct &forall) {
|
||||||
|
analyzeExplicitSpace(
|
||||||
|
std::get<
|
||||||
|
Fortran::parser::Statement<Fortran::parser::ForallConstructStmt>>(
|
||||||
|
forall.t)
|
||||||
|
.statement);
|
||||||
|
for (const Fortran::parser::ForallBodyConstruct &s :
|
||||||
|
std::get<std::list<Fortran::parser::ForallBodyConstruct>>(forall.t)) {
|
||||||
|
std::visit(Fortran::common::visitors{
|
||||||
|
[&](const Fortran::common::Indirection<
|
||||||
|
Fortran::parser::ForallConstruct> &b) {
|
||||||
|
analyzeExplicitSpace(b.value());
|
||||||
|
},
|
||||||
|
[&](const Fortran::parser::WhereConstruct &w) {
|
||||||
|
analyzeExplicitSpace(w);
|
||||||
|
},
|
||||||
|
[&](const auto &b) { analyzeExplicitSpace(b.statement); }},
|
||||||
|
s.u);
|
||||||
|
}
|
||||||
|
analyzeExplicitSpacePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void analyzeExplicitSpacePop() { explicitIterSpace.popLevel(); }
|
||||||
|
|
||||||
|
void addMaskVariable(Fortran::lower::FrontEndExpr exp) {
|
||||||
|
// Note: use i8 to store bool values. This avoids round-down behavior found
|
||||||
|
// with sequences of i1. That is, an array of i1 will be truncated in size
|
||||||
|
// and be too small. For example, a buffer of type fir.array<7xi1> will have
|
||||||
|
// 0 size.
|
||||||
|
mlir::Type i64Ty = builder->getIntegerType(64);
|
||||||
|
mlir::TupleType ty = fir::factory::getRaggedArrayHeaderType(*builder);
|
||||||
|
mlir::Type buffTy = ty.getType(1);
|
||||||
|
mlir::Type shTy = ty.getType(2);
|
||||||
|
mlir::Location loc = toLocation();
|
||||||
|
mlir::Value hdr = builder->createTemporary(loc, ty);
|
||||||
|
// FIXME: Is there a way to create a `zeroinitializer` in LLVM-IR dialect?
|
||||||
|
// For now, explicitly set lazy ragged header to all zeros.
|
||||||
|
// auto nilTup = builder->createNullConstant(loc, ty);
|
||||||
|
// builder->create<fir::StoreOp>(loc, nilTup, hdr);
|
||||||
|
mlir::Type i32Ty = builder->getIntegerType(32);
|
||||||
|
mlir::Value zero = builder->createIntegerConstant(loc, i32Ty, 0);
|
||||||
|
mlir::Value zero64 = builder->createIntegerConstant(loc, i64Ty, 0);
|
||||||
|
mlir::Value flags = builder->create<fir::CoordinateOp>(
|
||||||
|
loc, builder->getRefType(i64Ty), hdr, zero);
|
||||||
|
builder->create<fir::StoreOp>(loc, zero64, flags);
|
||||||
|
mlir::Value one = builder->createIntegerConstant(loc, i32Ty, 1);
|
||||||
|
mlir::Value nullPtr1 = builder->createNullConstant(loc, buffTy);
|
||||||
|
mlir::Value var = builder->create<fir::CoordinateOp>(
|
||||||
|
loc, builder->getRefType(buffTy), hdr, one);
|
||||||
|
builder->create<fir::StoreOp>(loc, nullPtr1, var);
|
||||||
|
mlir::Value two = builder->createIntegerConstant(loc, i32Ty, 2);
|
||||||
|
mlir::Value nullPtr2 = builder->createNullConstant(loc, shTy);
|
||||||
|
mlir::Value shape = builder->create<fir::CoordinateOp>(
|
||||||
|
loc, builder->getRefType(shTy), hdr, two);
|
||||||
|
builder->create<fir::StoreOp>(loc, nullPtr2, shape);
|
||||||
|
implicitIterSpace.addMaskVariable(exp, var, shape, hdr);
|
||||||
|
explicitIterSpace.outermostContext().attachCleanup(
|
||||||
|
[builder = this->builder, hdr, loc]() {
|
||||||
|
fir::runtime::genRaggedArrayDeallocate(loc, *builder, hdr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
|
|
||||||
Fortran::lower::LoweringBridge &bridge;
|
Fortran::lower::LoweringBridge &bridge;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,6 +23,7 @@
|
|||||||
#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/Inquiry.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/Builder/Runtime/Reduction.h"
|
||||||
#include "flang/Optimizer/Support/FatalError.h"
|
#include "flang/Optimizer/Support/FatalError.h"
|
||||||
@ -98,6 +99,9 @@ fir::ExtendedValue Fortran::lower::getAbsentIntrinsicArgument() {
|
|||||||
static bool isAbsent(const fir::ExtendedValue &exv) {
|
static bool isAbsent(const fir::ExtendedValue &exv) {
|
||||||
return !fir::getBase(exv);
|
return !fir::getBase(exv);
|
||||||
}
|
}
|
||||||
|
static bool isAbsent(llvm::ArrayRef<fir::ExtendedValue> args, size_t argIndex) {
|
||||||
|
return args.size() <= argIndex || isAbsent(args[argIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
/// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
|
/// Process calls to Maxval, Minval, Product, Sum intrinsic functions that
|
||||||
/// take a DIM argument.
|
/// take a DIM argument.
|
||||||
@ -233,10 +237,13 @@ 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 genLbound(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
|
||||||
|
fir::ExtendedValue genSize(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
|
||||||
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
|
fir::ExtendedValue genSum(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
|
||||||
|
fir::ExtendedValue genUbound(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.
|
||||||
/// arith::AndIOp.
|
|
||||||
using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
|
using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs);
|
||||||
using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
|
using ExtendedGenerator = decltype(&IntrinsicLibrary::genSum);
|
||||||
using Generator = std::variant<ElementalGenerator, ExtendedGenerator>;
|
using Generator = std::variant<ElementalGenerator, ExtendedGenerator>;
|
||||||
@ -268,6 +275,13 @@ struct IntrinsicLibrary {
|
|||||||
mlir::Type resultType,
|
mlir::Type resultType,
|
||||||
llvm::ArrayRef<mlir::Value> args);
|
llvm::ArrayRef<mlir::Value> args);
|
||||||
|
|
||||||
|
/// Add clean-up for \p temp to the current statement context;
|
||||||
|
void addCleanUpForTemp(mlir::Location loc, mlir::Value temp);
|
||||||
|
/// Helper function for generating code clean-up for result descriptors
|
||||||
|
fir::ExtendedValue readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
|
||||||
|
mlir::Type resultType,
|
||||||
|
llvm::StringRef errMsg);
|
||||||
|
|
||||||
fir::FirOpBuilder &builder;
|
fir::FirOpBuilder &builder;
|
||||||
mlir::Location loc;
|
mlir::Location loc;
|
||||||
Fortran::lower::StatementContext *stmtCtx;
|
Fortran::lower::StatementContext *stmtCtx;
|
||||||
@ -320,6 +334,10 @@ static constexpr IntrinsicHandler handlers[]{
|
|||||||
{"dim", asValue},
|
{"dim", asValue},
|
||||||
{"mask", asBox, handleDynamicOptional}}},
|
{"mask", asBox, handleDynamicOptional}}},
|
||||||
/*isElemental=*/false},
|
/*isElemental=*/false},
|
||||||
|
{"ubound",
|
||||||
|
&I::genUbound,
|
||||||
|
{{{"array", asBox}, {"dim", asValue}, {"kind", asValue}}},
|
||||||
|
/*isElemental=*/false},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
|
static const IntrinsicHandler *findIntrinsicHandler(llvm::StringRef name) {
|
||||||
@ -940,6 +958,52 @@ IntrinsicLibrary::getRuntimeCallGenerator(llvm::StringRef name,
|
|||||||
return builder.createConvert(loc, soughtType, call.getResult(0));
|
return builder.createConvert(loc, soughtType, call.getResult(0));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IntrinsicLibrary::addCleanUpForTemp(mlir::Location loc, mlir::Value temp) {
|
||||||
|
assert(stmtCtx);
|
||||||
|
fir::FirOpBuilder *bldr = &builder;
|
||||||
|
stmtCtx->attachCleanup([=]() { bldr->create<fir::FreeMemOp>(loc, temp); });
|
||||||
|
}
|
||||||
|
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::readAndAddCleanUp(fir::MutableBoxValue resultMutableBox,
|
||||||
|
mlir::Type resultType,
|
||||||
|
llvm::StringRef intrinsicName) {
|
||||||
|
fir::ExtendedValue res =
|
||||||
|
fir::factory::genMutableBoxRead(builder, loc, resultMutableBox);
|
||||||
|
return res.match(
|
||||||
|
[&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
addCleanUpForTemp(loc, box.getAddr());
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const fir::BoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
auto addr =
|
||||||
|
builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
|
||||||
|
addCleanUpForTemp(loc, addr);
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const fir::CharArrayBoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
addCleanUpForTemp(loc, box.getAddr());
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const mlir::Value &tempAddr) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
addCleanUpForTemp(loc, tempAddr);
|
||||||
|
return builder.create<fir::LoadOp>(loc, resultType, tempAddr);
|
||||||
|
},
|
||||||
|
[&](const fir::CharBoxValue &box) -> fir::ExtendedValue {
|
||||||
|
// Add cleanup code
|
||||||
|
addCleanUpForTemp(loc, box.getAddr());
|
||||||
|
return box;
|
||||||
|
},
|
||||||
|
[&](const auto &) -> fir::ExtendedValue {
|
||||||
|
fir::emitFatalError(loc, "unexpected result for " + intrinsicName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Code generators for the intrinsic
|
// Code generators for the intrinsic
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
@ -1071,6 +1135,128 @@ IntrinsicLibrary::genSum(mlir::Type resultType,
|
|||||||
builder, loc, stmtCtx, "unexpected result for Sum", args);
|
builder, loc, stmtCtx, "unexpected result for Sum", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SIZE
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::genSize(mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
// Note that the value of the KIND argument is already reflected in the
|
||||||
|
// resultType
|
||||||
|
assert(args.size() == 3);
|
||||||
|
if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
|
||||||
|
if (boxValue->hasAssumedRank())
|
||||||
|
TODO(loc, "SIZE intrinsic with assumed rank argument");
|
||||||
|
|
||||||
|
// Get the ARRAY argument
|
||||||
|
mlir::Value array = builder.createBox(loc, args[0]);
|
||||||
|
|
||||||
|
// The front-end rewrites SIZE without the DIM argument to
|
||||||
|
// an array of SIZE with DIM in most cases, but it may not be
|
||||||
|
// possible in some cases like when in SIZE(function_call()).
|
||||||
|
if (isAbsent(args, 1))
|
||||||
|
return builder.createConvert(loc, resultType,
|
||||||
|
fir::runtime::genSize(builder, loc, array));
|
||||||
|
|
||||||
|
// Get the DIM argument.
|
||||||
|
mlir::Value dim = fir::getBase(args[1]);
|
||||||
|
if (!fir::isa_ref_type(dim.getType()))
|
||||||
|
return builder.createConvert(
|
||||||
|
loc, resultType, fir::runtime::genSizeDim(builder, loc, array, dim));
|
||||||
|
|
||||||
|
mlir::Value isDynamicallyAbsent = builder.genIsNull(loc, dim);
|
||||||
|
return builder
|
||||||
|
.genIfOp(loc, {resultType}, isDynamicallyAbsent,
|
||||||
|
/*withElseRegion=*/true)
|
||||||
|
.genThen([&]() {
|
||||||
|
mlir::Value size = builder.createConvert(
|
||||||
|
loc, resultType, fir::runtime::genSize(builder, loc, array));
|
||||||
|
builder.create<fir::ResultOp>(loc, size);
|
||||||
|
})
|
||||||
|
.genElse([&]() {
|
||||||
|
mlir::Value dimValue = builder.create<fir::LoadOp>(loc, dim);
|
||||||
|
mlir::Value size = builder.createConvert(
|
||||||
|
loc, resultType,
|
||||||
|
fir::runtime::genSizeDim(builder, loc, array, dimValue));
|
||||||
|
builder.create<fir::ResultOp>(loc, size);
|
||||||
|
})
|
||||||
|
.getResults()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// LBOUND
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::genLbound(mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
// Calls to LBOUND that don't have the DIM argument, or for which
|
||||||
|
// the DIM is a compile time constant, are folded to descriptor inquiries by
|
||||||
|
// semantics. This function covers the situations where a call to the
|
||||||
|
// runtime is required.
|
||||||
|
assert(args.size() == 3);
|
||||||
|
assert(!isAbsent(args[1]));
|
||||||
|
if (const auto *boxValue = args[0].getBoxOf<fir::BoxValue>())
|
||||||
|
if (boxValue->hasAssumedRank())
|
||||||
|
TODO(loc, "LBOUND intrinsic with assumed rank argument");
|
||||||
|
|
||||||
|
const fir::ExtendedValue &array = args[0];
|
||||||
|
mlir::Value box = array.match(
|
||||||
|
[&](const fir::BoxValue &boxValue) -> mlir::Value {
|
||||||
|
// This entity is mapped to a fir.box that may not contain the local
|
||||||
|
// lower bound information if it is a dummy. Rebox it with the local
|
||||||
|
// shape information.
|
||||||
|
mlir::Value localShape = builder.createShape(loc, array);
|
||||||
|
mlir::Value oldBox = boxValue.getAddr();
|
||||||
|
return builder.create<fir::ReboxOp>(
|
||||||
|
loc, oldBox.getType(), oldBox, localShape, /*slice=*/mlir::Value{});
|
||||||
|
},
|
||||||
|
[&](const auto &) -> mlir::Value {
|
||||||
|
// This a pointer/allocatable, or an entity not yet tracked with a
|
||||||
|
// fir.box. For pointer/allocatable, createBox will forward the
|
||||||
|
// descriptor that contains the correct lower bound information. For
|
||||||
|
// other entities, a new fir.box will be made with the local lower
|
||||||
|
// bounds.
|
||||||
|
return builder.createBox(loc, array);
|
||||||
|
});
|
||||||
|
|
||||||
|
mlir::Value dim = fir::getBase(args[1]);
|
||||||
|
return builder.createConvert(
|
||||||
|
loc, resultType,
|
||||||
|
fir::runtime::genLboundDim(builder, loc, fir::getBase(box), dim));
|
||||||
|
}
|
||||||
|
|
||||||
|
// UBOUND
|
||||||
|
fir::ExtendedValue
|
||||||
|
IntrinsicLibrary::genUbound(mlir::Type resultType,
|
||||||
|
llvm::ArrayRef<fir::ExtendedValue> args) {
|
||||||
|
assert(args.size() == 3 || args.size() == 2);
|
||||||
|
if (args.size() == 3) {
|
||||||
|
// Handle calls to UBOUND with the DIM argument, which return a scalar
|
||||||
|
mlir::Value extent = fir::getBase(genSize(resultType, args));
|
||||||
|
mlir::Value lbound = fir::getBase(genLbound(resultType, args));
|
||||||
|
|
||||||
|
mlir::Value one = builder.createIntegerConstant(loc, resultType, 1);
|
||||||
|
mlir::Value ubound = builder.create<mlir::arith::SubIOp>(loc, lbound, one);
|
||||||
|
return builder.create<mlir::arith::AddIOp>(loc, ubound, extent);
|
||||||
|
} else {
|
||||||
|
// Handle calls to UBOUND without the DIM argument, which return an array
|
||||||
|
mlir::Value kind = isAbsent(args[1])
|
||||||
|
? builder.createIntegerConstant(
|
||||||
|
loc, builder.getIndexType(),
|
||||||
|
builder.getKindMap().defaultIntegerKind())
|
||||||
|
: fir::getBase(args[1]);
|
||||||
|
|
||||||
|
// Create mutable fir.box to be passed to the runtime for the result.
|
||||||
|
mlir::Type type = builder.getVarLenSeqTy(resultType, /*rank=*/1);
|
||||||
|
fir::MutableBoxValue resultMutableBox =
|
||||||
|
fir::factory::createTempMutableBox(builder, loc, type);
|
||||||
|
mlir::Value resultIrBox =
|
||||||
|
fir::factory::getMutableIRBox(builder, loc, resultMutableBox);
|
||||||
|
|
||||||
|
fir::runtime::genUbound(builder, loc, resultIrBox, fir::getBase(args[0]),
|
||||||
|
kind);
|
||||||
|
|
||||||
|
return readAndAddCleanUp(resultMutableBox, resultType, "UBOUND");
|
||||||
|
}
|
||||||
|
return mlir::Value();
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Argument lowering rules interface
|
// Argument lowering rules interface
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "flang/Optimizer/Builder/BoxValue.h"
|
#include "flang/Optimizer/Builder/BoxValue.h"
|
||||||
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||||
#include "mlir/IR/BuiltinTypes.h"
|
#include "mlir/IR/BuiltinTypes.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
|
|
||||||
@ -224,3 +225,14 @@ bool fir::BoxValue::verify() const {
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get exactly one extent for any array-like extended value, \p exv. If \p exv
|
||||||
|
/// is not an array or has rank less then \p dim, the result will be a nullptr.
|
||||||
|
mlir::Value fir::getExtentAtDimension(const fir::ExtendedValue &exv,
|
||||||
|
fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc, unsigned dim) {
|
||||||
|
auto extents = fir::factory::getExtents(builder, loc, exv);
|
||||||
|
if (dim < extents.size())
|
||||||
|
return extents[dim];
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ add_flang_library(FIRBuilder
|
|||||||
Runtime/Character.cpp
|
Runtime/Character.cpp
|
||||||
Runtime/Command.cpp
|
Runtime/Command.cpp
|
||||||
Runtime/Derived.cpp
|
Runtime/Derived.cpp
|
||||||
|
Runtime/Inquiry.cpp
|
||||||
Runtime/Numeric.cpp
|
Runtime/Numeric.cpp
|
||||||
Runtime/Ragged.cpp
|
Runtime/Ragged.cpp
|
||||||
Runtime/Reduction.cpp
|
Runtime/Reduction.cpp
|
||||||
|
77
flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
Normal file
77
flang/lib/Optimizer/Builder/Runtime/Inquiry.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//===-- Inquiry.h - generate inquiry runtime API calls ----------*- 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
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "flang/Optimizer/Builder/Runtime/Inquiry.h"
|
||||||
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
||||||
|
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
||||||
|
#include "flang/Runtime/inquiry.h"
|
||||||
|
|
||||||
|
using namespace Fortran::runtime;
|
||||||
|
|
||||||
|
/// Generate call to `Lbound` runtime routine when the DIM argument is present.
|
||||||
|
mlir::Value fir::runtime::genLboundDim(fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc, mlir::Value array,
|
||||||
|
mlir::Value dim) {
|
||||||
|
mlir::FuncOp lboundFunc =
|
||||||
|
fir::runtime::getRuntimeFunc<mkRTKey(LboundDim)>(loc, builder);
|
||||||
|
auto fTy = lboundFunc.getType();
|
||||||
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
||||||
|
auto sourceLine =
|
||||||
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
|
||||||
|
auto args = fir::runtime::createArguments(builder, loc, fTy, array, dim,
|
||||||
|
sourceFile, sourceLine);
|
||||||
|
return builder.create<fir::CallOp>(loc, lboundFunc, args).getResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate call to `Ubound` runtime routine. Calls to UBOUND with a DIM
|
||||||
|
/// argument get transformed into an expression equivalent to
|
||||||
|
/// SIZE() + LBOUND() - 1, so they don't have an intrinsic in the runtime.
|
||||||
|
void fir::runtime::genUbound(fir::FirOpBuilder &builder, mlir::Location loc,
|
||||||
|
mlir::Value resultBox, mlir::Value array,
|
||||||
|
mlir::Value kind) {
|
||||||
|
mlir::FuncOp uboundFunc =
|
||||||
|
fir::runtime::getRuntimeFunc<mkRTKey(Ubound)>(loc, builder);
|
||||||
|
auto fTy = uboundFunc.getType();
|
||||||
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
||||||
|
auto sourceLine =
|
||||||
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(2));
|
||||||
|
auto args = fir::runtime::createArguments(builder, loc, fTy, resultBox, array,
|
||||||
|
kind, sourceFile, sourceLine);
|
||||||
|
builder.create<fir::CallOp>(loc, uboundFunc, args).getResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate call to `Size` runtime routine. This routine is a version when
|
||||||
|
/// the DIM argument is present.
|
||||||
|
mlir::Value fir::runtime::genSizeDim(fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc, mlir::Value array,
|
||||||
|
mlir::Value dim) {
|
||||||
|
mlir::FuncOp sizeFunc =
|
||||||
|
fir::runtime::getRuntimeFunc<mkRTKey(SizeDim)>(loc, builder);
|
||||||
|
auto fTy = sizeFunc.getType();
|
||||||
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
||||||
|
auto sourceLine =
|
||||||
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
|
||||||
|
auto args = fir::runtime::createArguments(builder, loc, fTy, array, dim,
|
||||||
|
sourceFile, sourceLine);
|
||||||
|
return builder.create<fir::CallOp>(loc, sizeFunc, args).getResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate call to `Size` runtime routine. This routine is a version when
|
||||||
|
/// the DIM argument is absent.
|
||||||
|
mlir::Value fir::runtime::genSize(fir::FirOpBuilder &builder,
|
||||||
|
mlir::Location loc, mlir::Value array) {
|
||||||
|
mlir::FuncOp sizeFunc =
|
||||||
|
fir::runtime::getRuntimeFunc<mkRTKey(Size)>(loc, builder);
|
||||||
|
auto fTy = sizeFunc.getType();
|
||||||
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
||||||
|
auto sourceLine =
|
||||||
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(2));
|
||||||
|
auto args = fir::runtime::createArguments(builder, loc, fTy, array,
|
||||||
|
sourceFile, sourceLine);
|
||||||
|
return builder.create<fir::CallOp>(loc, sizeFunc, args).getResult(0);
|
||||||
|
}
|
98
flang/test/Lower/forall/forall-construct.f90
Normal file
98
flang/test/Lower/forall/forall-construct.f90
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
! Test forall lowering
|
||||||
|
|
||||||
|
! RUN: bbc -emit-fir %s -o - | FileCheck %s
|
||||||
|
|
||||||
|
!*** Test a FORALL construct
|
||||||
|
subroutine test_forall_construct(a,b)
|
||||||
|
integer :: i, j
|
||||||
|
real :: a(:,:), b(:,:)
|
||||||
|
forall (i=1:ubound(a,1), j=1:ubound(a,2), b(j,i) > 0.0)
|
||||||
|
a(i,j) = b(j,i) / 3.14
|
||||||
|
end forall
|
||||||
|
end subroutine test_forall_construct
|
||||||
|
|
||||||
|
! CHECK-LABEL: func @_QPtest_forall_construct(
|
||||||
|
! CHECK-SAME: %[[VAL_0:.*]]: !fir.box<!fir.array<?x?xf32>>{{.*}}, %[[VAL_1:.*]]: !fir.box<!fir.array<?x?xf32>>{{.*}}) {
|
||||||
|
! CHECK: %[[VAL_2:.*]] = fir.alloca i32 {adapt.valuebyref, bindc_name = "j"}
|
||||||
|
! CHECK: %[[VAL_3:.*]] = fir.alloca i32 {adapt.valuebyref, bindc_name = "i"}
|
||||||
|
! CHECK: %[[VAL_4:.*]] = arith.constant 1 : i32
|
||||||
|
! CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_4]] : (i32) -> index
|
||||||
|
! CHECK: %[[VAL_6:.*]] = arith.constant 0 : index
|
||||||
|
! CHECK: %[[VAL_7:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_6]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
|
||||||
|
! CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_7]]#1 : (index) -> i64
|
||||||
|
! CHECK: %[[VAL_9:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_10:.*]] = fir.convert %[[VAL_9]] : (index) -> i64
|
||||||
|
! CHECK: %[[VAL_11:.*]] = arith.addi %[[VAL_8]], %[[VAL_10]] : i64
|
||||||
|
! CHECK: %[[VAL_12:.*]] = arith.constant 1 : i64
|
||||||
|
! CHECK: %[[VAL_13:.*]] = arith.subi %[[VAL_11]], %[[VAL_12]] : i64
|
||||||
|
! CHECK: %[[VAL_14:.*]] = fir.convert %[[VAL_13]] : (i64) -> i32
|
||||||
|
! CHECK: %[[VAL_15:.*]] = fir.convert %[[VAL_14]] : (i32) -> index
|
||||||
|
! CHECK: %[[VAL_16:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_17:.*]] = arith.constant 1 : i32
|
||||||
|
! CHECK: %[[VAL_18:.*]] = fir.convert %[[VAL_17]] : (i32) -> index
|
||||||
|
! CHECK: %[[VAL_19:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_20:.*]]:3 = fir.box_dims %[[VAL_0]], %[[VAL_19]] : (!fir.box<!fir.array<?x?xf32>>, index) -> (index, index, index)
|
||||||
|
! CHECK: %[[VAL_21:.*]] = fir.convert %[[VAL_20]]#1 : (index) -> i64
|
||||||
|
! CHECK: %[[VAL_22:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_23:.*]] = fir.convert %[[VAL_22]] : (index) -> i64
|
||||||
|
! CHECK: %[[VAL_24:.*]] = arith.addi %[[VAL_21]], %[[VAL_23]] : i64
|
||||||
|
! CHECK: %[[VAL_25:.*]] = arith.constant 1 : i64
|
||||||
|
! CHECK: %[[VAL_26:.*]] = arith.subi %[[VAL_24]], %[[VAL_25]] : i64
|
||||||
|
! CHECK: %[[VAL_27:.*]] = fir.convert %[[VAL_26]] : (i64) -> i32
|
||||||
|
! CHECK: %[[VAL_28:.*]] = fir.convert %[[VAL_27]] : (i32) -> index
|
||||||
|
! CHECK: %[[VAL_29:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_30:.*]] = fir.array_load %[[VAL_0]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.array<?x?xf32>
|
||||||
|
! CHECK: %[[VAL_31:.*]] = fir.array_load %[[VAL_1]] : (!fir.box<!fir.array<?x?xf32>>) -> !fir.array<?x?xf32>
|
||||||
|
! CHECK: %[[VAL_32:.*]] = fir.do_loop %[[VAL_33:.*]] = %[[VAL_5]] to %[[VAL_15]] step %[[VAL_16]] unordered iter_args(%[[VAL_34:.*]] = %[[VAL_30]]) -> (!fir.array<?x?xf32>) {
|
||||||
|
! CHECK: %[[VAL_35:.*]] = fir.convert %[[VAL_33]] : (index) -> i32
|
||||||
|
! CHECK: fir.store %[[VAL_35]] to %[[VAL_3]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_36:.*]] = fir.do_loop %[[VAL_37:.*]] = %[[VAL_18]] to %[[VAL_28]] step %[[VAL_29]] unordered iter_args(%[[VAL_38:.*]] = %[[VAL_34]]) -> (!fir.array<?x?xf32>) {
|
||||||
|
! CHECK: %[[VAL_39:.*]] = fir.convert %[[VAL_37]] : (index) -> i32
|
||||||
|
! CHECK: fir.store %[[VAL_39]] to %[[VAL_2]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_40:.*]] = fir.load %[[VAL_2]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_41:.*]] = fir.convert %[[VAL_40]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_42:.*]] = arith.constant 1 : i64
|
||||||
|
! CHECK: %[[VAL_43:.*]] = arith.subi %[[VAL_41]], %[[VAL_42]] : i64
|
||||||
|
! CHECK: %[[VAL_44:.*]] = fir.load %[[VAL_3]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_45:.*]] = fir.convert %[[VAL_44]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_46:.*]] = arith.constant 1 : i64
|
||||||
|
! CHECK: %[[VAL_47:.*]] = arith.subi %[[VAL_45]], %[[VAL_46]] : i64
|
||||||
|
! CHECK: %[[VAL_48:.*]] = fir.coordinate_of %[[VAL_1]], %[[VAL_43]], %[[VAL_47]] : (!fir.box<!fir.array<?x?xf32>>, i64, i64) -> !fir.ref<f32>
|
||||||
|
! CHECK: %[[VAL_49:.*]] = fir.load %[[VAL_48]] : !fir.ref<f32>
|
||||||
|
! CHECK: %[[VAL_50:.*]] = arith.constant 0.000000e+00 : f32
|
||||||
|
! CHECK: %[[VAL_51:.*]] = arith.cmpf ogt, %[[VAL_49]], %[[VAL_50]] : f32
|
||||||
|
! CHECK: %[[VAL_52:.*]] = fir.if %[[VAL_51]] -> (!fir.array<?x?xf32>) {
|
||||||
|
! CHECK: %[[VAL_53:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_54:.*]] = fir.load %[[VAL_2]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_55:.*]] = fir.convert %[[VAL_54]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_56:.*]] = fir.convert %[[VAL_55]] : (i64) -> index
|
||||||
|
! CHECK: %[[VAL_57:.*]] = arith.subi %[[VAL_56]], %[[VAL_53]] : index
|
||||||
|
! CHECK: %[[VAL_58:.*]] = fir.load %[[VAL_3]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_59:.*]] = fir.convert %[[VAL_58]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_60:.*]] = fir.convert %[[VAL_59]] : (i64) -> index
|
||||||
|
! CHECK: %[[VAL_61:.*]] = arith.subi %[[VAL_60]], %[[VAL_53]] : index
|
||||||
|
! CHECK: %[[VAL_62:.*]] = arith.constant 3.140000e+00 : f32
|
||||||
|
! CHECK: %[[VAL_63:.*]] = fir.array_fetch %[[VAL_31]], %[[VAL_57]], %[[VAL_61]] : (!fir.array<?x?xf32>, index, index) -> f32
|
||||||
|
! CHECK: %[[VAL_64:.*]] = arith.divf %[[VAL_63]], %[[VAL_62]] : f32
|
||||||
|
! CHECK: %[[VAL_65:.*]] = arith.constant 1 : index
|
||||||
|
! CHECK: %[[VAL_66:.*]] = fir.load %[[VAL_3]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_67:.*]] = fir.convert %[[VAL_66]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_68:.*]] = fir.convert %[[VAL_67]] : (i64) -> index
|
||||||
|
! CHECK: %[[VAL_69:.*]] = arith.subi %[[VAL_68]], %[[VAL_65]] : index
|
||||||
|
! CHECK: %[[VAL_70:.*]] = fir.load %[[VAL_2]] : !fir.ref<i32>
|
||||||
|
! CHECK: %[[VAL_71:.*]] = fir.convert %[[VAL_70]] : (i32) -> i64
|
||||||
|
! CHECK: %[[VAL_72:.*]] = fir.convert %[[VAL_71]] : (i64) -> index
|
||||||
|
! CHECK: %[[VAL_73:.*]] = arith.subi %[[VAL_72]], %[[VAL_65]] : index
|
||||||
|
! CHECK: %[[VAL_74:.*]] = fir.array_update %[[VAL_38]], %[[VAL_64]], %[[VAL_69]], %[[VAL_73]] : (!fir.array<?x?xf32>, f32, index, index) -> !fir.array<?x?xf32>
|
||||||
|
! CHECK: fir.result %[[VAL_74]] : !fir.array<?x?xf32>
|
||||||
|
! CHECK: } else {
|
||||||
|
! CHECK: fir.result %[[VAL_38]] : !fir.array<?x?xf32>
|
||||||
|
! CHECK: }
|
||||||
|
! CHECK: fir.result %[[VAL_75:.*]] : !fir.array<?x?xf32>
|
||||||
|
! CHECK: }
|
||||||
|
! CHECK: fir.result %[[VAL_76:.*]] : !fir.array<?x?xf32>
|
||||||
|
! CHECK: }
|
||||||
|
! CHECK: fir.array_merge_store %[[VAL_30]], %[[VAL_77:.*]] to %[[VAL_0]] : !fir.array<?x?xf32>, !fir.array<?x?xf32>, !fir.box<!fir.array<?x?xf32>>
|
||||||
|
! CHECK: return
|
||||||
|
! CHECK: }
|
||||||
|
|
Loading…
Reference in New Issue
Block a user