Skip to content

Commit 84c4c44

Browse files
jeanPerierchencha3
authored andcommitted
[flang] Lower sequence associated argument passed by descriptor (llvm#85696)
The current lowering did not handle sequence associated argument passed by descriptor. This case is special because sequence association implies that the actual and dummy argument need to to agree in rank and shape. Usually, arguments that can be sequence associated are passed by raw address, and the shape mistmatch is transparent. But there are three cases of explicit and assumed-size arrays passed by descriptors: - polymorphic arguments - BIND(C) assumed-length arguments (F'2023 18.3.7 (5)). - length parametrized derived types (TBD) The callee side is expecting a descriptor containing the dummy rank and shape. This was not the case. This patch fix that by evaluating the dummy shape on the caller side using the interface (that has to be available when arguments are passed by descriptors).
1 parent 2147d2b commit 84c4c44

File tree

7 files changed

+642
-49
lines changed

7 files changed

+642
-49
lines changed

flang/include/flang/Evaluate/characteristics.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,14 @@ class TypeAndShape {
177177
int corank() const { return corank_; }
178178

179179
int Rank() const { return GetRank(shape_); }
180+
181+
// Can sequence association apply to this argument?
182+
bool CanBeSequenceAssociated() const {
183+
constexpr Attrs notAssumedOrExplicitShape{
184+
~Attrs{Attr::AssumedSize, Attr::Coarray}};
185+
return Rank() > 0 && (attrs() & notAssumedOrExplicitShape).none();
186+
}
187+
180188
bool IsCompatibleWith(parser::ContextualMessages &, const TypeAndShape &that,
181189
const char *thisIs = "pointer", const char *thatIs = "target",
182190
bool omitShapeConformanceCheck = false,

flang/include/flang/Lower/CallInterface.h

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ class CallInterface {
174174
/// May the dummy argument require INTENT(OUT) finalization
175175
/// on entry to the invoked procedure? Provides conservative answer.
176176
bool mayRequireIntentoutFinalization() const;
177+
/// Is the dummy argument an explicit-shape or assumed-size array that
178+
/// must be passed by descriptor? Sequence association imply the actual
179+
/// argument shape/rank may differ with the dummy shape/rank (see F'2023
180+
/// section 15.5.2.12), so care is needed when creating the descriptor
181+
/// for the dummy argument.
182+
bool isSequenceAssociatedDescriptor() const;
177183
/// How entity is passed by.
178184
PassEntityBy passBy;
179185
/// What is the entity (SymbolRef for callee/ActualArgument* for caller)
@@ -273,8 +279,6 @@ class CallerInterface : public CallInterface<CallerInterface> {
273279
actualInputs.resize(getNumFIRArguments());
274280
}
275281

276-
using ExprVisitor = std::function<void(evaluate::Expr<evaluate::SomeType>)>;
277-
278282
/// CRTP callbacks
279283
bool hasAlternateReturns() const;
280284
std::string getMangledName() const;
@@ -312,12 +316,21 @@ class CallerInterface : public CallInterface<CallerInterface> {
312316
/// procedure.
313317
const Fortran::semantics::Symbol *getProcedureSymbol() const;
314318

319+
/// Return the dummy argument symbol if this is a call to a user
320+
/// defined procedure with explicit interface. Returns nullptr if there
321+
/// is no user defined explicit interface.
322+
const Fortran::semantics::Symbol *
323+
getDummySymbol(const PassedEntity &entity) const;
324+
315325
/// Helpers to place the lowered arguments at the right place once they
316326
/// have been lowered.
317327
void placeInput(const PassedEntity &passedEntity, mlir::Value arg);
318328
void placeAddressAndLengthInput(const PassedEntity &passedEntity,
319329
mlir::Value addr, mlir::Value len);
320330

331+
/// Get lowered FIR argument given the Fortran argument.
332+
mlir::Value getInput(const PassedEntity &passedEntity);
333+
321334
/// If this is a call to a procedure pointer or dummy, returns the related
322335
/// procedure designator. Nullptr otherwise.
323336
const Fortran::evaluate::ProcedureDesignator *getIfIndirectCall() const;
@@ -333,13 +346,27 @@ class CallerInterface : public CallInterface<CallerInterface> {
333346
/// the result specification expressions (extents and lengths) ? If needed,
334347
/// this mapping must be done after argument lowering, and before the call
335348
/// itself.
336-
bool mustMapInterfaceSymbols() const;
349+
bool mustMapInterfaceSymbolsForResult() const;
350+
/// Must the caller map function interface symbols in order to evaluate
351+
/// the specification expressions of a given dummy argument?
352+
bool mustMapInterfaceSymbolsForDummyArgument(const PassedEntity &) const;
353+
354+
/// Visitor for specification expression. Boolean indicate the specification
355+
/// expression is for the last extent of an assumed size array.
356+
using ExprVisitor =
357+
std::function<void(evaluate::Expr<evaluate::SomeType>, bool)>;
337358

338359
/// Walk the result non-deferred extent specification expressions.
339-
void walkResultExtents(ExprVisitor) const;
360+
void walkResultExtents(const ExprVisitor &) const;
340361

341362
/// Walk the result non-deferred length specification expressions.
342-
void walkResultLengths(ExprVisitor) const;
363+
void walkResultLengths(const ExprVisitor &) const;
364+
/// Walk non-deferred extent specification expressions of a dummy argument.
365+
void walkDummyArgumentExtents(const PassedEntity &,
366+
const ExprVisitor &) const;
367+
/// Walk non-deferred length specification expressions of a dummy argument.
368+
void walkDummyArgumentLengths(const PassedEntity &,
369+
const ExprVisitor &) const;
343370

344371
/// Get the mlir::Value that is passed as argument \p sym of the function
345372
/// being called. The arguments must have been placed before calling this
@@ -355,6 +382,9 @@ class CallerInterface : public CallInterface<CallerInterface> {
355382
/// returns the storage type.
356383
mlir::Type getResultStorageType() const;
357384

385+
/// Return FIR type of argument.
386+
mlir::Type getDummyArgumentType(const PassedEntity &) const;
387+
358388
// Copy of base implementation.
359389
static constexpr bool hasHostAssociated() { return false; }
360390
mlir::Type getHostAssociatedTy() const {

flang/include/flang/Lower/ConvertVariable.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,16 @@ void mapSymbolAttributes(AbstractConverter &, const semantics::SymbolRef &,
9393
/// Instantiate the variables that appear in the specification expressions
9494
/// of the result of a function call. The instantiated variables are added
9595
/// to \p symMap.
96-
void mapCallInterfaceSymbols(AbstractConverter &,
97-
const Fortran::lower::CallerInterface &caller,
98-
SymMap &symMap);
96+
void mapCallInterfaceSymbolsForResult(
97+
AbstractConverter &, const Fortran::lower::CallerInterface &caller,
98+
SymMap &symMap);
99+
100+
/// Instantiate the variables that appear in the specification expressions
101+
/// of a dummy argument of a procedure call. The instantiated variables are
102+
/// added to \p symMap.
103+
void mapCallInterfaceSymbolsForDummyArgument(
104+
AbstractConverter &, const Fortran::lower::CallerInterface &caller,
105+
SymMap &symMap, const Fortran::semantics::Symbol &dummySymbol);
99106

100107
// TODO: consider saving the initial expression symbol dependence analysis in
101108
// in the PFT variable and dealing with the dependent symbols instantiation in

flang/lib/Lower/CallInterface.cpp

Lines changed: 110 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -310,21 +310,22 @@ bool Fortran::lower::CallerInterface::verifyActualInputs() const {
310310
return true;
311311
}
312312

313-
void Fortran::lower::CallerInterface::walkResultLengths(
314-
ExprVisitor visitor) const {
315-
assert(characteristic && "characteristic was not computed");
316-
const Fortran::evaluate::characteristics::FunctionResult &result =
317-
characteristic->functionResult.value();
318-
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
319-
result.GetTypeAndShape();
320-
assert(typeAndShape && "no result type");
321-
Fortran::evaluate::DynamicType dynamicType = typeAndShape->type();
322-
// Visit result length specification expressions that are explicit.
313+
mlir::Value
314+
Fortran::lower::CallerInterface::getInput(const PassedEntity &passedEntity) {
315+
return actualInputs[passedEntity.firArgument];
316+
}
317+
318+
static void walkLengths(
319+
const Fortran::evaluate::characteristics::TypeAndShape &typeAndShape,
320+
const Fortran::lower::CallerInterface::ExprVisitor &visitor,
321+
Fortran::lower::AbstractConverter &converter) {
322+
Fortran::evaluate::DynamicType dynamicType = typeAndShape.type();
323+
// Visit length specification expressions that are explicit.
323324
if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
324325
if (std::optional<Fortran::evaluate::ExtentExpr> length =
325326
dynamicType.GetCharLength())
326-
visitor(toEvExpr(*length));
327-
} else if (dynamicType.category() == common::TypeCategory::Derived &&
327+
visitor(toEvExpr(*length), /*assumedSize=*/false);
328+
} else if (dynamicType.category() == Fortran::common::TypeCategory::Derived &&
328329
!dynamicType.IsUnlimitedPolymorphic()) {
329330
const Fortran::semantics::DerivedTypeSpec &derivedTypeSpec =
330331
dynamicType.GetDerivedTypeSpec();
@@ -334,32 +335,61 @@ void Fortran::lower::CallerInterface::walkResultLengths(
334335
}
335336
}
336337

338+
void Fortran::lower::CallerInterface::walkResultLengths(
339+
const ExprVisitor &visitor) const {
340+
assert(characteristic && "characteristic was not computed");
341+
const Fortran::evaluate::characteristics::FunctionResult &result =
342+
characteristic->functionResult.value();
343+
const Fortran::evaluate::characteristics::TypeAndShape *typeAndShape =
344+
result.GetTypeAndShape();
345+
assert(typeAndShape && "no result type");
346+
return walkLengths(*typeAndShape, visitor, converter);
347+
}
348+
349+
void Fortran::lower::CallerInterface::walkDummyArgumentLengths(
350+
const PassedEntity &passedEntity, const ExprVisitor &visitor) const {
351+
if (!passedEntity.characteristics)
352+
return;
353+
if (const auto *dummy =
354+
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
355+
&passedEntity.characteristics->u))
356+
walkLengths(dummy->type, visitor, converter);
357+
}
358+
337359
// Compute extent expr from shapeSpec of an explicit shape.
338-
// TODO: Allow evaluate shape analysis to work in a mode where it disregards
339-
// the non-constant aspects when building the shape to avoid having this here.
340360
static Fortran::evaluate::ExtentExpr
341361
getExtentExpr(const Fortran::semantics::ShapeSpec &shapeSpec) {
362+
if (shapeSpec.ubound().isStar())
363+
// F'2023 18.5.3 point 5.
364+
return Fortran::evaluate::ExtentExpr{-1};
342365
const auto &ubound = shapeSpec.ubound().GetExplicit();
343366
const auto &lbound = shapeSpec.lbound().GetExplicit();
344367
assert(lbound && ubound && "shape must be explicit");
345368
return Fortran::common::Clone(*ubound) - Fortran::common::Clone(*lbound) +
346369
Fortran::evaluate::ExtentExpr{1};
347370
}
348371

372+
static void
373+
walkExtents(const Fortran::semantics::Symbol &symbol,
374+
const Fortran::lower::CallerInterface::ExprVisitor &visitor) {
375+
if (const auto *objectDetails =
376+
symbol.detailsIf<Fortran::semantics::ObjectEntityDetails>())
377+
if (objectDetails->shape().IsExplicitShape() ||
378+
Fortran::semantics::IsAssumedSizeArray(symbol))
379+
for (const Fortran::semantics::ShapeSpec &shapeSpec :
380+
objectDetails->shape())
381+
visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)),
382+
/*assumedSize=*/shapeSpec.ubound().isStar());
383+
}
384+
349385
void Fortran::lower::CallerInterface::walkResultExtents(
350-
ExprVisitor visitor) const {
386+
const ExprVisitor &visitor) const {
351387
// Walk directly the result symbol shape (the characteristic shape may contain
352388
// descriptor inquiries to it that would fail to lower on the caller side).
353389
const Fortran::semantics::SubprogramDetails *interfaceDetails =
354390
getInterfaceDetails();
355391
if (interfaceDetails) {
356-
const Fortran::semantics::Symbol &result = interfaceDetails->result();
357-
if (const auto *objectDetails =
358-
result.detailsIf<Fortran::semantics::ObjectEntityDetails>())
359-
if (objectDetails->shape().IsExplicitShape())
360-
for (const Fortran::semantics::ShapeSpec &shapeSpec :
361-
objectDetails->shape())
362-
visitor(Fortran::evaluate::AsGenericExpr(getExtentExpr(shapeSpec)));
392+
walkExtents(interfaceDetails->result(), visitor);
363393
} else {
364394
if (procRef.Rank() != 0)
365395
fir::emitFatalError(
@@ -368,22 +398,44 @@ void Fortran::lower::CallerInterface::walkResultExtents(
368398
}
369399
}
370400

371-
bool Fortran::lower::CallerInterface::mustMapInterfaceSymbols() const {
401+
void Fortran::lower::CallerInterface::walkDummyArgumentExtents(
402+
const PassedEntity &passedEntity, const ExprVisitor &visitor) const {
403+
const Fortran::semantics::SubprogramDetails *interfaceDetails =
404+
getInterfaceDetails();
405+
if (!interfaceDetails)
406+
return;
407+
const Fortran::semantics::Symbol *dummy = getDummySymbol(passedEntity);
408+
assert(dummy && "dummy symbol was not set");
409+
walkExtents(*dummy, visitor);
410+
}
411+
412+
bool Fortran::lower::CallerInterface::mustMapInterfaceSymbolsForResult() const {
372413
assert(characteristic && "characteristic was not computed");
373414
const std::optional<Fortran::evaluate::characteristics::FunctionResult>
374415
&result = characteristic->functionResult;
375416
if (!result || result->CanBeReturnedViaImplicitInterface() ||
376417
!getInterfaceDetails() || result->IsProcedurePointer())
377418
return false;
378419
bool allResultSpecExprConstant = true;
379-
auto visitor = [&](const Fortran::lower::SomeExpr &e) {
420+
auto visitor = [&](const Fortran::lower::SomeExpr &e, bool) {
380421
allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
381422
};
382423
walkResultLengths(visitor);
383424
walkResultExtents(visitor);
384425
return !allResultSpecExprConstant;
385426
}
386427

428+
bool Fortran::lower::CallerInterface::mustMapInterfaceSymbolsForDummyArgument(
429+
const PassedEntity &arg) const {
430+
bool allResultSpecExprConstant = true;
431+
auto visitor = [&](const Fortran::lower::SomeExpr &e, bool) {
432+
allResultSpecExprConstant &= Fortran::evaluate::IsConstantExpr(e);
433+
};
434+
walkDummyArgumentLengths(arg, visitor);
435+
walkDummyArgumentExtents(arg, visitor);
436+
return !allResultSpecExprConstant;
437+
}
438+
387439
mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
388440
const semantics::Symbol &sym) const {
389441
mlir::Location loc = converter.getCurrentLocation();
@@ -401,13 +453,36 @@ mlir::Value Fortran::lower::CallerInterface::getArgumentValue(
401453
return actualInputs[mlirArgIndex];
402454
}
403455

456+
const Fortran::semantics::Symbol *
457+
Fortran::lower::CallerInterface::getDummySymbol(
458+
const PassedEntity &passedEntity) const {
459+
const Fortran::semantics::SubprogramDetails *ifaceDetails =
460+
getInterfaceDetails();
461+
if (!ifaceDetails)
462+
return nullptr;
463+
std::size_t argPosition = 0;
464+
for (const auto &arg : getPassedArguments()) {
465+
if (&arg == &passedEntity)
466+
break;
467+
++argPosition;
468+
}
469+
if (argPosition >= ifaceDetails->dummyArgs().size())
470+
return nullptr;
471+
return ifaceDetails->dummyArgs()[argPosition];
472+
}
473+
404474
mlir::Type Fortran::lower::CallerInterface::getResultStorageType() const {
405475
if (passedResult)
406476
return fir::dyn_cast_ptrEleTy(inputs[passedResult->firArgument].type);
407477
assert(saveResult && !outputs.empty());
408478
return outputs[0].type;
409479
}
410480

481+
mlir::Type Fortran::lower::CallerInterface::getDummyArgumentType(
482+
const PassedEntity &passedEntity) const {
483+
return inputs[passedEntity.firArgument].type;
484+
}
485+
411486
const Fortran::semantics::Symbol &
412487
Fortran::lower::CallerInterface::getResultSymbol() const {
413488
mlir::Location loc = converter.getCurrentLocation();
@@ -1387,6 +1462,17 @@ bool Fortran::lower::CallInterface<
13871462
return Fortran::semantics::IsFinalizable(*derived);
13881463
}
13891464

1465+
template <typename T>
1466+
bool Fortran::lower::CallInterface<
1467+
T>::PassedEntity::isSequenceAssociatedDescriptor() const {
1468+
if (!characteristics || passBy != PassEntityBy::Box)
1469+
return false;
1470+
const auto *dummy =
1471+
std::get_if<Fortran::evaluate::characteristics::DummyDataObject>(
1472+
&characteristics->u);
1473+
return dummy && dummy->type.CanBeSequenceAssociated();
1474+
}
1475+
13901476
template <typename T>
13911477
void Fortran::lower::CallInterface<T>::determineInterface(
13921478
bool isImplicit,

0 commit comments

Comments
 (0)