[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:
Valentin Clement 2022-03-10 19:43:11 +01:00
parent f39a971d82
commit 88ae0d61c3
No known key found for this signature in database
GPG Key ID: 086D54783C928776
12 changed files with 1929 additions and 25 deletions

View File

@ -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

View File

@ -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.

View File

@ -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)

View 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

View File

@ -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);
}

View File

@ -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

View File

@ -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
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -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 {};
}

View File

@ -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

View 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);
}

View 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: }