Skip to content

Commit d1aa9ba

Browse files
authored
[flang] lower select rank (#93967)
Lower select rank according to [assumed-rank lowering design doc](https://github.com/llvm/llvm-project/blob/main/flang/docs/AssumedRank.md). The construct is lowered using fir.box_rank and fir.select_case operation and, for the non pointer/allocatable case, a fir.is_assumed_size + conditional branch before the select_case to deal with the assumed-size case. The way the CFG logic is generated, apart from the extra conditional branch for assumed-size, is similar to what is done for SELECT CASE lowering, hence the sharing of the construct level visitor. For the CFG parts. The main difference is that we need to keep track of the selector to cook it and map it inside the cases (hence the new members of the ConstructContext). The only TODOs left are to deal with the RANK(*) case for polymorphic entities and PDTs. I will do the polymorphic case in a distinct patch, this patch has enough content. Fortran::evaluate::IsSimplyContiguous change is needed to avoid generating copy-in/copy-out runtime calls when passing the RANK(*) associating entity to some implicit interface.
1 parent 4cd115c commit d1aa9ba

File tree

5 files changed

+1045
-17
lines changed

5 files changed

+1045
-17
lines changed

flang/include/flang/Lower/ConvertExprToHLFIR.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,11 @@ convertExprToHLFIR(mlir::Location loc, Fortran::lower::AbstractConverter &,
4141
const Fortran::lower::SomeExpr &, Fortran::lower::SymMap &,
4242
Fortran::lower::StatementContext &);
4343

44-
inline fir::ExtendedValue
45-
translateToExtendedValue(mlir::Location loc, fir::FirOpBuilder &builder,
46-
hlfir::Entity entity,
47-
Fortran::lower::StatementContext &context) {
44+
inline fir::ExtendedValue translateToExtendedValue(
45+
mlir::Location loc, fir::FirOpBuilder &builder, hlfir::Entity entity,
46+
Fortran::lower::StatementContext &context, bool contiguityHint = false) {
4847
auto [exv, exvCleanup] =
49-
hlfir::translateToExtendedValue(loc, builder, entity);
48+
hlfir::translateToExtendedValue(loc, builder, entity, contiguityHint);
5049
if (exvCleanup)
5150
context.attachCleanup(*exvCleanup);
5251
return exv;

flang/lib/Evaluate/check-expression.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -801,8 +801,14 @@ class IsContiguousHelper
801801
// simple contiguity to allow their use in contexts like
802802
// data targets in pointer assignments with remapping.
803803
return true;
804-
} else if (ultimate.has<semantics::AssocEntityDetails>()) {
805-
return Base::operator()(ultimate); // use expr
804+
} else if (const auto *details{
805+
ultimate.detailsIf<semantics::AssocEntityDetails>()}) {
806+
// RANK(*) associating entity is contiguous.
807+
if (details->IsAssumedSize()) {
808+
return true;
809+
} else {
810+
return Base::operator()(ultimate); // use expr
811+
}
806812
} else if (semantics::IsPointer(ultimate) ||
807813
semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) {
808814
return std::nullopt;

flang/lib/Lower/Bridge.cpp

Lines changed: 198 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ struct ConstructContext {
141141

142142
Fortran::lower::pft::Evaluation &eval; // construct eval
143143
Fortran::lower::StatementContext &stmtCtx; // construct exit code
144+
std::optional<hlfir::Entity> selector; // construct selector, if any.
145+
bool pushedScope = false; // was a scoped pushed for this construct?
144146
};
145147

146148
/// Helper class to generate the runtime type info global data and the
@@ -1468,6 +1470,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
14681470
void popActiveConstruct() {
14691471
assert(!activeConstructStack.empty() && "invalid active construct stack");
14701472
activeConstructStack.back().eval.activeConstruct = false;
1473+
if (activeConstructStack.back().pushedScope)
1474+
localSymbols.popScope();
14711475
activeConstructStack.pop_back();
14721476
}
14731477

@@ -2181,7 +2185,7 @@ class FirConverter : public Fortran::lower::AbstractConverter {
21812185
}
21822186
}
21832187

2184-
void genFIR(const Fortran::parser::CaseConstruct &) {
2188+
void genCaseOrRankConstruct() {
21852189
Fortran::lower::pft::Evaluation &eval = getEval();
21862190
Fortran::lower::StatementContext stmtCtx;
21872191
pushActiveConstruct(eval, stmtCtx);
@@ -2203,6 +2207,9 @@ class FirConverter : public Fortran::lower::AbstractConverter {
22032207
}
22042208
popActiveConstruct();
22052209
}
2210+
void genFIR(const Fortran::parser::CaseConstruct &) {
2211+
genCaseOrRankConstruct();
2212+
}
22062213

22072214
template <typename A>
22082215
void genNestedStatement(const Fortran::parser::Statement<A> &stmt) {
@@ -3032,13 +3039,198 @@ class FirConverter : public Fortran::lower::AbstractConverter {
30323039

30333040
void genFIR(const Fortran::parser::SelectRankConstruct &selectRankConstruct) {
30343041
setCurrentPositionAt(selectRankConstruct);
3035-
TODO(toLocation(), "coarray: SelectRankConstruct");
3042+
genCaseOrRankConstruct();
30363043
}
3037-
void genFIR(const Fortran::parser::SelectRankStmt &) {
3038-
TODO(toLocation(), "coarray: SelectRankStmt");
3044+
3045+
void genFIR(const Fortran::parser::SelectRankStmt &selectRankStmt) {
3046+
// Generate a fir.select_case with the selector rank. The RANK(*) case,
3047+
// if any, is handles with a conditional branch before the fir.select_case.
3048+
mlir::Type rankType = builder->getIntegerType(8);
3049+
mlir::MLIRContext *context = builder->getContext();
3050+
mlir::Location loc = toLocation();
3051+
// Build block list for fir.select_case, and identify RANK(*) block, if any.
3052+
// Default block must be placed last in the fir.select_case block list.
3053+
mlir::Block *rankStarBlock = nullptr;
3054+
Fortran::lower::pft::Evaluation &eval = getEval();
3055+
mlir::Block *defaultBlock = eval.parentConstruct->constructExit->block;
3056+
llvm::SmallVector<mlir::Attribute> attrList;
3057+
llvm::SmallVector<mlir::Value> valueList;
3058+
llvm::SmallVector<mlir::Block *> blockList;
3059+
for (Fortran::lower::pft::Evaluation *e = eval.controlSuccessor; e;
3060+
e = e->controlSuccessor) {
3061+
if (const auto *rankCaseStmt =
3062+
e->getIf<Fortran::parser::SelectRankCaseStmt>()) {
3063+
const auto &rank = std::get<Fortran::parser::SelectRankCaseStmt::Rank>(
3064+
rankCaseStmt->t);
3065+
assert(e->block && "missing SelectRankCaseStmt block");
3066+
std::visit(
3067+
Fortran::common::visitors{
3068+
[&](const Fortran::parser::ScalarIntConstantExpr &rankExpr) {
3069+
blockList.emplace_back(e->block);
3070+
attrList.emplace_back(fir::PointIntervalAttr::get(context));
3071+
std::optional<std::int64_t> rankCst =
3072+
Fortran::evaluate::ToInt64(
3073+
Fortran::semantics::GetExpr(rankExpr));
3074+
assert(rankCst.has_value() &&
3075+
"rank expr must be constant integer");
3076+
valueList.emplace_back(
3077+
builder->createIntegerConstant(loc, rankType, *rankCst));
3078+
},
3079+
[&](const Fortran::parser::Star &) {
3080+
rankStarBlock = e->block;
3081+
},
3082+
[&](const Fortran::parser::Default &) {
3083+
defaultBlock = e->block;
3084+
}},
3085+
rank.u);
3086+
}
3087+
}
3088+
attrList.push_back(mlir::UnitAttr::get(context));
3089+
blockList.push_back(defaultBlock);
3090+
3091+
// Lower selector.
3092+
assert(!activeConstructStack.empty() && "must be inside construct");
3093+
assert(!activeConstructStack.back().selector &&
3094+
"selector should not yet be set");
3095+
Fortran::lower::StatementContext &stmtCtx =
3096+
activeConstructStack.back().stmtCtx;
3097+
const Fortran::lower::SomeExpr *selectorExpr =
3098+
std::visit([](const auto &x) { return Fortran::semantics::GetExpr(x); },
3099+
std::get<Fortran::parser::Selector>(selectRankStmt.t).u);
3100+
assert(selectorExpr && "failed to retrieve selector expr");
3101+
hlfir::Entity selector = Fortran::lower::convertExprToHLFIR(
3102+
loc, *this, *selectorExpr, localSymbols, stmtCtx);
3103+
activeConstructStack.back().selector = selector;
3104+
3105+
// Deal with assumed-size first. They must fall into RANK(*) if present, or
3106+
// the default case (F'2023 11.1.10.2.). The selector cannot be an
3107+
// assumed-size if it is allocatable or pointer, so the check is skipped.
3108+
if (!Fortran::evaluate::IsAllocatableOrPointerObject(*selectorExpr)) {
3109+
mlir::Value isAssumedSize = builder->create<fir::IsAssumedSizeOp>(
3110+
loc, builder->getI1Type(), selector);
3111+
// Create new block to hold the fir.select_case for the non assumed-size
3112+
// cases.
3113+
mlir::Block *selectCaseBlock = insertBlock(blockList[0]);
3114+
mlir::Block *assumedSizeBlock =
3115+
rankStarBlock ? rankStarBlock : defaultBlock;
3116+
builder->create<mlir::cf::CondBranchOp>(loc, isAssumedSize,
3117+
assumedSizeBlock, std::nullopt,
3118+
selectCaseBlock, std::nullopt);
3119+
startBlock(selectCaseBlock);
3120+
}
3121+
// Create fir.select_case for the other rank cases.
3122+
mlir::Value rank = builder->create<fir::BoxRankOp>(loc, rankType, selector);
3123+
stmtCtx.finalizeAndReset();
3124+
builder->create<fir::SelectCaseOp>(loc, rank, attrList, valueList,
3125+
blockList);
3126+
}
3127+
3128+
// Get associating entity symbol inside case statement scope.
3129+
static const Fortran::semantics::Symbol &
3130+
getAssociatingEntitySymbol(const Fortran::semantics::Scope &scope) {
3131+
const Fortran::semantics::Symbol *assocSym = nullptr;
3132+
for (const auto &sym : scope.GetSymbols()) {
3133+
if (sym->has<Fortran::semantics::AssocEntityDetails>()) {
3134+
assert(!assocSym &&
3135+
"expect only one associating entity symbol in this scope");
3136+
assocSym = &*sym;
3137+
}
3138+
}
3139+
assert(assocSym && "should contain associating entity symbol");
3140+
return *assocSym;
30393141
}
3040-
void genFIR(const Fortran::parser::SelectRankCaseStmt &) {
3041-
TODO(toLocation(), "coarray: SelectRankCaseStmt");
3142+
3143+
void genFIR(const Fortran::parser::SelectRankCaseStmt &stmt) {
3144+
assert(!activeConstructStack.empty() &&
3145+
"must be inside select rank construct");
3146+
// Pop previous associating entity mapping, if any, and push scope for new
3147+
// mapping.
3148+
if (activeConstructStack.back().pushedScope)
3149+
localSymbols.popScope();
3150+
localSymbols.pushScope();
3151+
activeConstructStack.back().pushedScope = true;
3152+
const Fortran::semantics::Symbol &assocEntitySymbol =
3153+
getAssociatingEntitySymbol(
3154+
bridge.getSemanticsContext().FindScope(getEval().position));
3155+
const auto &details =
3156+
assocEntitySymbol.get<Fortran::semantics::AssocEntityDetails>();
3157+
assert(!activeConstructStack.empty() &&
3158+
activeConstructStack.back().selector.has_value() &&
3159+
"selector must have been created");
3160+
// Get lowered value for the selector.
3161+
hlfir::Entity selector = *activeConstructStack.back().selector;
3162+
assert(selector.isVariable() && "assumed-rank selector are variables");
3163+
// Cook selector mlir::Value according to rank case and map it to
3164+
// associating entity symbol.
3165+
Fortran::lower::StatementContext stmtCtx;
3166+
mlir::Location loc = toLocation();
3167+
if (details.IsAssumedRank()) {
3168+
fir::ExtendedValue selectorExv = Fortran::lower::translateToExtendedValue(
3169+
loc, *builder, selector, stmtCtx);
3170+
addSymbol(assocEntitySymbol, selectorExv);
3171+
} else if (details.IsAssumedSize()) {
3172+
// Create rank-1 assumed-size from descriptor. Assumed-size are contiguous
3173+
// so a new entity can be built from scratch using the base address, type
3174+
// parameters and dynamic type. The selector cannot be a
3175+
// POINTER/ALLOCATBLE as per F'2023 C1160.
3176+
fir::ExtendedValue newExv;
3177+
llvm::SmallVector assumeSizeExtents{
3178+
builder->createMinusOneInteger(loc, builder->getIndexType())};
3179+
mlir::Value baseAddr =
3180+
hlfir::genVariableRawAddress(loc, *builder, selector);
3181+
mlir::Type eleType =
3182+
fir::unwrapSequenceType(fir::unwrapRefType(baseAddr.getType()));
3183+
mlir::Type rank1Type =
3184+
fir::ReferenceType::get(builder->getVarLenSeqTy(eleType, 1));
3185+
baseAddr = builder->createConvert(loc, rank1Type, baseAddr);
3186+
if (selector.isCharacter()) {
3187+
mlir::Value len = hlfir::genCharLength(loc, *builder, selector);
3188+
newExv = fir::CharArrayBoxValue{baseAddr, len, assumeSizeExtents};
3189+
} else if (selector.isDerivedWithLengthParameters()) {
3190+
TODO(loc, "RANK(*) with parameterized derived type selector");
3191+
} else if (selector.isPolymorphic()) {
3192+
TODO(loc, "RANK(*) with polymorphic selector");
3193+
} else {
3194+
// Simple intrinsic or derived type.
3195+
newExv = fir::ArrayBoxValue{baseAddr, assumeSizeExtents};
3196+
}
3197+
addSymbol(assocEntitySymbol, newExv);
3198+
} else {
3199+
int rank = details.rank().value();
3200+
auto boxTy =
3201+
mlir::cast<fir::BaseBoxType>(fir::unwrapRefType(selector.getType()));
3202+
mlir::Type newBoxType = boxTy.getBoxTypeWithNewShape(rank);
3203+
if (fir::isa_ref_type(selector.getType()))
3204+
newBoxType = fir::ReferenceType::get(newBoxType);
3205+
// Give rank info to value via cast, and get rid of the box if not needed
3206+
// (simple scalars, contiguous arrays... This is done by
3207+
// translateVariableToExtendedValue).
3208+
hlfir::Entity rankedBox{
3209+
builder->createConvert(loc, newBoxType, selector)};
3210+
bool isSimplyContiguous = Fortran::evaluate::IsSimplyContiguous(
3211+
assocEntitySymbol, getFoldingContext());
3212+
fir::ExtendedValue newExv = Fortran::lower::translateToExtendedValue(
3213+
loc, *builder, rankedBox, stmtCtx, isSimplyContiguous);
3214+
3215+
// Non deferred length parameters of character allocatable/pointer
3216+
// MutableBoxValue should be properly set before binding it to a symbol in
3217+
// order to get correct assignment semantics.
3218+
if (const fir::MutableBoxValue *mutableBox =
3219+
newExv.getBoxOf<fir::MutableBoxValue>()) {
3220+
if (selector.isCharacter()) {
3221+
auto dynamicType =
3222+
Fortran::evaluate::DynamicType::From(assocEntitySymbol);
3223+
if (!dynamicType.value().HasDeferredTypeParameter()) {
3224+
llvm::SmallVector<mlir::Value> lengthParams;
3225+
hlfir::genLengthParameters(loc, *builder, selector, lengthParams);
3226+
newExv = fir::MutableBoxValue{rankedBox, lengthParams,
3227+
mutableBox->getMutableProperties()};
3228+
}
3229+
}
3230+
}
3231+
addSymbol(assocEntitySymbol, newExv);
3232+
}
3233+
// Statements inside rank case are lowered by SelectRankConstruct visit.
30423234
}
30433235

30443236
void genFIR(const Fortran::parser::SelectTypeConstruct &selectTypeConstruct) {

flang/lib/Optimizer/Builder/HLFIRTools.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ genLboundsAndExtentsFromBox(mlir::Location loc, fir::FirOpBuilder &builder,
115115
static llvm::SmallVector<mlir::Value>
116116
getNonDefaultLowerBounds(mlir::Location loc, fir::FirOpBuilder &builder,
117117
hlfir::Entity entity) {
118+
assert(!entity.isAssumedRank() &&
119+
"cannot compute assumed rank bounds statically");
118120
if (!entity.mayHaveNonDefaultLowerBounds())
119121
return {};
120122
if (auto varIface = entity.getIfVariableInterface()) {
@@ -889,11 +891,14 @@ static fir::ExtendedValue translateVariableToExtendedValue(
889891
fir::MutableProperties{});
890892

891893
if (mlir::isa<fir::BaseBoxType>(base.getType())) {
892-
bool contiguous = variable.isSimplyContiguous() || contiguousHint;
894+
const bool contiguous = variable.isSimplyContiguous() || contiguousHint;
895+
const bool isAssumedRank = variable.isAssumedRank();
893896
if (!contiguous || variable.isPolymorphic() ||
894-
variable.isDerivedWithLengthParameters() || variable.isOptional()) {
895-
llvm::SmallVector<mlir::Value> nonDefaultLbounds =
896-
getNonDefaultLowerBounds(loc, builder, variable);
897+
variable.isDerivedWithLengthParameters() || variable.isOptional() ||
898+
isAssumedRank) {
899+
llvm::SmallVector<mlir::Value> nonDefaultLbounds;
900+
if (!isAssumedRank)
901+
nonDefaultLbounds = getNonDefaultLowerBounds(loc, builder, variable);
897902
return fir::BoxValue(base, nonDefaultLbounds,
898903
getExplicitTypeParams(variable));
899904
}

0 commit comments

Comments
 (0)