Skip to content

Commit 8c77c01

Browse files
committed
[flang] Initial support of allocate statement with source
Support allocate statement with source in runtime version. The source expression is evaluated only once for each allocate statement. When the source expression has shape-spec, uses it for bounds. Otherwise, get the bounds from the source expression. Get the length if the source expression has deferred length parameter. Reviewed By: clementval, jeanPerier Differential Revision: https://reviews.llvm.org/D137812
1 parent 6f9ff1b commit 8c77c01

File tree

8 files changed

+919
-62
lines changed

8 files changed

+919
-62
lines changed

flang/include/flang/Runtime/assign.h

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// External and internal APIs for data assignment (both intrinsic assignment
10-
// and TBP defined generic ASSIGNMENT(=)). Should be called by lowering
11-
// for any assignments possibly needing special handling. Intrinsic assignment
12-
// to non-allocatable variables whose types are intrinsic need not come through
13-
// here (though they may do so). Assignments to allocatables, and assignments
14-
// whose types may be polymorphic or are monomorphic and of derived types with
15-
// finalization, allocatable components, or components with type-bound defined
16-
// assignments, in the original type or the types of its non-pointer components
17-
// (recursively) must arrive here.
9+
// External APIs for data assignment (both intrinsic assignment and TBP defined
10+
// generic ASSIGNMENT(=)). Should be called by lowering for any assignments
11+
// possibly needing special handling. Intrinsic assignment to non-allocatable
12+
// variables whose types are intrinsic need not come through here (though they
13+
// may do so). Assignments to allocatables, and assignments whose types may be
14+
// polymorphic or are monomorphic and of derived types with finalization,
15+
// allocatable components, or components with type-bound defined assignments, in
16+
// the original type or the types of its non-pointer components (recursively)
17+
// must arrive here.
1818
//
1919
// Non-type-bound generic INTERFACE ASSIGNMENT(=) is resolved in semantics and
2020
// need not be handled here in the runtime; ditto for type conversions on
@@ -27,14 +27,6 @@
2727

2828
namespace Fortran::runtime {
2929
class Descriptor;
30-
class Terminator;
31-
32-
// Assigns one object to another via intrinsic assignment (F'2018 10.2.1.3) or
33-
// type-bound (only!) defined assignment (10.2.1.4), as appropriate. Performs
34-
// finalization, scalar expansion, & allocatable (re)allocation as needed.
35-
// Does not perform intrinsic assignment implicit type conversion. Both
36-
// descriptors must be initialized. Recurses as needed to handle components.
37-
void Assign(Descriptor &, const Descriptor &, Terminator &);
3830

3931
extern "C" {
4032
// API for lowering assignment

flang/lib/Lower/Allocatable.cpp

Lines changed: 113 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,29 @@ static mlir::Value genRuntimeAllocate(fir::FirOpBuilder &builder,
183183
return builder.create<fir::CallOp>(loc, callee, operands).getResult(0);
184184
}
185185

186+
/// Generate a sequence of runtime calls to allocate memory and assign with the
187+
/// \p source.
188+
static mlir::Value genRuntimeAllocateSource(fir::FirOpBuilder &builder,
189+
mlir::Location loc,
190+
const fir::MutableBoxValue &box,
191+
fir::ExtendedValue source,
192+
ErrorManager &errorManager) {
193+
mlir::func::FuncOp callee =
194+
box.isPointer()
195+
? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocateSource)>(
196+
loc, builder)
197+
: fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocateSource)>(
198+
loc, builder);
199+
llvm::SmallVector<mlir::Value> args{
200+
box.getAddr(), fir::getBase(source),
201+
errorManager.hasStat, errorManager.errMsgAddr,
202+
errorManager.sourceFile, errorManager.sourceLine};
203+
llvm::SmallVector<mlir::Value> operands;
204+
for (auto [fst, snd] : llvm::zip(args, callee.getFunctionType().getInputs()))
205+
operands.emplace_back(builder.createConvert(loc, snd, fst));
206+
return builder.create<fir::CallOp>(loc, callee, operands).getResult(0);
207+
}
208+
186209
/// Generate a runtime call to deallocate memory.
187210
static mlir::Value genRuntimeDeallocate(fir::FirOpBuilder &builder,
188211
mlir::Location loc,
@@ -255,8 +278,11 @@ class AllocateStmtHelper {
255278
visitAllocateOptions();
256279
lowerAllocateLengthParameters();
257280
errorManager.init(converter, loc, statExpr, errMsgExpr);
258-
if (sourceExpr || moldExpr)
259-
TODO(loc, "lower MOLD/SOURCE expr in allocate");
281+
Fortran::lower::StatementContext stmtCtx;
282+
if (sourceExpr)
283+
sourceExv = converter.genExprBox(loc, *sourceExpr, stmtCtx);
284+
if (moldExpr)
285+
TODO(loc, "lower MOLD expr in allocate");
260286
mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint();
261287
for (const auto &allocation :
262288
std::get<std::list<Fortran::parser::Allocation>>(stmt.t))
@@ -393,45 +419,13 @@ class AllocateStmtHelper {
393419
}
394420
// Generate a sequence of runtime calls.
395421
errorManager.genStatCheck(builder, loc);
396-
if (box.isPointer()) {
397-
// For pointers, the descriptor may still be uninitialized (see Fortran
398-
// 2018 19.5.2.2). The allocation runtime needs to be given a descriptor
399-
// with initialized rank, types and attributes. Initialize the descriptor
400-
// here to ensure these constraints are fulfilled.
401-
mlir::Value nullPointer = fir::factory::createUnallocatedBox(
402-
builder, loc, box.getBoxTy(), box.nonDeferredLenParams());
403-
builder.create<fir::StoreOp>(loc, nullPointer, box.getAddr());
404-
} else {
405-
assert(box.isAllocatable() && "must be an allocatable");
406-
// For allocatables, sync the MutableBoxValue and descriptor before the
407-
// calls in case it is tracked locally by a set of variables.
408-
fir::factory::getMutableIRBox(builder, loc, box);
409-
}
422+
genAllocateObjectInit(box);
410423
if (alloc.hasCoarraySpec())
411424
TODO(loc, "coarray allocation");
412425
if (alloc.type.IsPolymorphic())
413426
genSetType(alloc, box, loc);
414427
genSetDeferredLengthParameters(alloc, box);
415-
// Set bounds for arrays
416-
mlir::Type idxTy = builder.getIndexType();
417-
mlir::Type i32Ty = builder.getIntegerType(32);
418-
Fortran::lower::StatementContext stmtCtx;
419-
for (const auto &iter : llvm::enumerate(alloc.getShapeSpecs())) {
420-
mlir::Value lb;
421-
const auto &bounds = iter.value().t;
422-
if (const std::optional<Fortran::parser::BoundExpr> &lbExpr =
423-
std::get<0>(bounds))
424-
lb = fir::getBase(converter.genExprValue(
425-
loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx));
426-
else
427-
lb = builder.createIntegerConstant(loc, idxTy, 1);
428-
mlir::Value ub = fir::getBase(converter.genExprValue(
429-
loc, Fortran::semantics::GetExpr(std::get<1>(bounds)), stmtCtx));
430-
mlir::Value dimIndex =
431-
builder.createIntegerConstant(loc, i32Ty, iter.index());
432-
// Runtime call
433-
genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub);
434-
}
428+
genAllocateObjectBounds(alloc, box);
435429
mlir::Value stat = genRuntimeAllocate(builder, loc, box, errorManager);
436430
fir::factory::syncMutableBoxFromIRBox(builder, loc, box);
437431
errorManager.assignStat(builder, loc, stat);
@@ -478,8 +472,87 @@ class AllocateStmtHelper {
478472
TODO(loc, "derived type length parameters in allocate");
479473
}
480474

481-
void genSourceAllocation(const Allocation &, const fir::MutableBoxValue &) {
482-
TODO(loc, "SOURCE allocation");
475+
void genAllocateObjectInit(const fir::MutableBoxValue &box) {
476+
if (box.isPointer()) {
477+
// For pointers, the descriptor may still be uninitialized (see Fortran
478+
// 2018 19.5.2.2). The allocation runtime needs to be given a descriptor
479+
// with initialized rank, types and attributes. Initialize the descriptor
480+
// here to ensure these constraints are fulfilled.
481+
mlir::Value nullPointer = fir::factory::createUnallocatedBox(
482+
builder, loc, box.getBoxTy(), box.nonDeferredLenParams());
483+
builder.create<fir::StoreOp>(loc, nullPointer, box.getAddr());
484+
} else {
485+
assert(box.isAllocatable() && "must be an allocatable");
486+
// For allocatables, sync the MutableBoxValue and descriptor before the
487+
// calls in case it is tracked locally by a set of variables.
488+
fir::factory::getMutableIRBox(builder, loc, box);
489+
}
490+
}
491+
492+
void genAllocateObjectBounds(const Allocation &alloc,
493+
const fir::MutableBoxValue &box) {
494+
// Set bounds for arrays
495+
mlir::Type idxTy = builder.getIndexType();
496+
mlir::Type i32Ty = builder.getIntegerType(32);
497+
Fortran::lower::StatementContext stmtCtx;
498+
for (const auto &iter : llvm::enumerate(alloc.getShapeSpecs())) {
499+
mlir::Value lb;
500+
const auto &bounds = iter.value().t;
501+
if (const std::optional<Fortran::parser::BoundExpr> &lbExpr =
502+
std::get<0>(bounds))
503+
lb = fir::getBase(converter.genExprValue(
504+
loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx));
505+
else
506+
lb = builder.createIntegerConstant(loc, idxTy, 1);
507+
mlir::Value ub = fir::getBase(converter.genExprValue(
508+
loc, Fortran::semantics::GetExpr(std::get<1>(bounds)), stmtCtx));
509+
mlir::Value dimIndex =
510+
builder.createIntegerConstant(loc, i32Ty, iter.index());
511+
// Runtime call
512+
genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub);
513+
}
514+
if (sourceExpr && sourceExpr->Rank() > 0 &&
515+
alloc.getShapeSpecs().size() == 0) {
516+
// If the alloc object does not have shape list, get the bounds from the
517+
// source expression.
518+
mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
519+
const auto *sourceBox = sourceExv.getBoxOf<fir::BoxValue>();
520+
assert(sourceBox && "source expression should be lowered to one box");
521+
for (int i = 0; i < sourceExpr->Rank(); ++i) {
522+
auto dimVal = builder.createIntegerConstant(loc, idxTy, i);
523+
auto dimInfo = builder.create<fir::BoxDimsOp>(
524+
loc, idxTy, idxTy, idxTy, sourceBox->getAddr(), dimVal);
525+
mlir::Value lb =
526+
fir::factory::readLowerBound(builder, loc, sourceExv, i, one);
527+
mlir::Value extent = dimInfo.getResult(1);
528+
mlir::Value ub = builder.create<mlir::arith::SubIOp>(
529+
loc, builder.create<mlir::arith::AddIOp>(loc, extent, lb), one);
530+
mlir::Value dimIndex = builder.createIntegerConstant(loc, i32Ty, i);
531+
genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub);
532+
}
533+
}
534+
}
535+
536+
void genSourceAllocation(const Allocation &alloc,
537+
const fir::MutableBoxValue &box) {
538+
// Generate a sequence of runtime calls.
539+
errorManager.genStatCheck(builder, loc);
540+
genAllocateObjectInit(box);
541+
if (alloc.hasCoarraySpec())
542+
TODO(loc, "coarray allocation");
543+
if (alloc.type.IsPolymorphic())
544+
TODO(loc, "polymorphic allocation with SOURCE specifier");
545+
// Set length of the allocate object if it has. Otherwise, get the length
546+
// from source for the deferred length parameter.
547+
if (lenParams.empty() && box.isCharacter() &&
548+
!box.hasNonDeferredLenParams())
549+
lenParams.push_back(fir::factory::readCharLen(builder, loc, sourceExv));
550+
genSetDeferredLengthParameters(alloc, box);
551+
genAllocateObjectBounds(alloc, box);
552+
mlir::Value stat =
553+
genRuntimeAllocateSource(builder, loc, box, sourceExv, errorManager);
554+
fir::factory::syncMutableBoxFromIRBox(builder, loc, box);
555+
errorManager.assignStat(builder, loc, stat);
483556
}
484557
void genMoldAllocation(const Allocation &, const fir::MutableBoxValue &) {
485558
TODO(loc, "MOLD allocation");
@@ -576,6 +649,8 @@ class AllocateStmtHelper {
576649
// value of the length parameters that were specified inside.
577650
llvm::SmallVector<mlir::Value> lenParams;
578651
ErrorManager errorManager;
652+
// 9.7.1.2(7) The source-expr is evaluated exactly once for each AllocateStmt.
653+
fir::ExtendedValue sourceExv;
579654

580655
mlir::Location loc;
581656
};

flang/runtime/allocatable.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "flang/Runtime/allocatable.h"
10+
#include "assign.h"
1011
#include "derived.h"
1112
#include "stat.h"
1213
#include "terminator.h"
1314
#include "type-info.h"
14-
#include "flang/Runtime/assign.h"
1515

1616
namespace Fortran::runtime {
1717
extern "C" {
@@ -88,6 +88,22 @@ int RTNAME(AllocatableAllocate)(Descriptor &descriptor, bool hasStat,
8888
return stat;
8989
}
9090

91+
int RTNAME(AllocatableAllocateSource)(Descriptor &alloc,
92+
const Descriptor &source, bool hasStat, const Descriptor *errMsg,
93+
const char *sourceFile, int sourceLine) {
94+
if (alloc.Elements() == 0) {
95+
return StatOk;
96+
}
97+
int stat{RTNAME(AllocatableAllocate)(
98+
alloc, hasStat, errMsg, sourceFile, sourceLine)};
99+
if (stat == StatOk) {
100+
Terminator terminator{sourceFile, sourceLine};
101+
// 9.7.1.2(7)
102+
Assign(alloc, source, terminator, /*skipRealloc=*/true);
103+
}
104+
return stat;
105+
}
106+
91107
int RTNAME(AllocatableDeallocate)(Descriptor &descriptor, bool hasStat,
92108
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
93109
Terminator terminator{sourceFile, sourceLine};
@@ -125,6 +141,6 @@ void RTNAME(AllocatableDeallocateNoFinal)(
125141
}
126142
}
127143

128-
// TODO: AllocatableCheckLengthParameter, AllocatableAllocateSource
144+
// TODO: AllocatableCheckLengthParameter
129145
}
130146
} // namespace Fortran::runtime

flang/runtime/assign.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "flang/Runtime/assign.h"
10+
#include "assign.h"
1011
#include "derived.h"
1112
#include "stat.h"
1213
#include "terminator.h"
@@ -59,7 +60,8 @@ static void DoElementalDefinedAssignment(const Descriptor &to,
5960
}
6061
}
6162

62-
void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator) {
63+
void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator,
64+
bool skipRealloc) {
6365
DescriptorAddendum *toAddendum{to.Addendum()};
6466
const typeInfo::DerivedType *toDerived{
6567
toAddendum ? toAddendum->derivedType() : nullptr};
@@ -69,7 +71,7 @@ void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator) {
6971
bool wasJustAllocated{false};
7072
if (to.IsAllocatable()) {
7173
std::size_t lenParms{fromDerived ? fromDerived->LenParameters() : 0};
72-
if (to.IsAllocated()) {
74+
if (to.IsAllocated() && !skipRealloc) {
7375
// Top-level assignments to allocatable variables (*not* components)
7476
// may first deallocate existing content if there's about to be a
7577
// change in type or shape; see F'2018 10.2.1.3(3).
@@ -196,7 +198,7 @@ void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator) {
196198
comp.CreatePointerDescriptor(toCompDesc, to, terminator, toAt);
197199
comp.CreatePointerDescriptor(
198200
fromCompDesc, from, terminator, fromAt);
199-
Assign(toCompDesc, fromCompDesc, terminator);
201+
Assign(toCompDesc, fromCompDesc, terminator, /*skipRealloc=*/false);
200202
}
201203
} else { // Component has intrinsic type; simply copy raw bytes
202204
std::size_t componentByteSize{comp.SizeInBytes(to)};
@@ -241,7 +243,7 @@ void Assign(Descriptor &to, const Descriptor &from, Terminator &terminator) {
241243
continue; // F'2018 10.2.1.3(13)(2)
242244
}
243245
}
244-
Assign(*toDesc, *fromDesc, terminator);
246+
Assign(*toDesc, *fromDesc, terminator, /*skipRealloc=*/false);
245247
}
246248
break;
247249
}

flang/runtime/assign.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//===-- runtime/assign.h-----------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// Internal APIs for data assignment (both intrinsic assignment and TBP defined
10+
// generic ASSIGNMENT(=)).
11+
12+
#ifndef FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_
13+
#define FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_
14+
15+
namespace Fortran::runtime {
16+
class Descriptor;
17+
class Terminator;
18+
19+
// Assigns one object to another via intrinsic assignment (F'2018 10.2.1.3) or
20+
// type-bound (only!) defined assignment (10.2.1.4), as appropriate. Performs
21+
// finalization, scalar expansion, & allocatable (re)allocation as needed.
22+
// Does not perform intrinsic assignment implicit type conversion. Both
23+
// descriptors must be initialized. Recurses as needed to handle components.
24+
// Do not perform allocatable reallocation if \p skipRealloc is true, which is
25+
// used for allocate statement with source specifier.
26+
void Assign(
27+
Descriptor &, const Descriptor &, Terminator &, bool skipRealloc = false);
28+
29+
} // namespace Fortran::runtime
30+
#endif // FORTRAN_RUNTIME_ASSIGN_INTERNAL_H_

flang/runtime/pointer.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "flang/Runtime/pointer.h"
10+
#include "assign.h"
1011
#include "derived.h"
1112
#include "stat.h"
1213
#include "terminator.h"
@@ -132,6 +133,22 @@ int RTNAME(PointerAllocate)(Descriptor &pointer, bool hasStat,
132133
return stat;
133134
}
134135

136+
int RTNAME(PointerAllocateSource)(Descriptor &pointer, const Descriptor &source,
137+
bool hasStat, const Descriptor *errMsg, const char *sourceFile,
138+
int sourceLine) {
139+
if (pointer.Elements() == 0) {
140+
return StatOk;
141+
}
142+
int stat{RTNAME(PointerAllocate)(
143+
pointer, hasStat, errMsg, sourceFile, sourceLine)};
144+
if (stat == StatOk) {
145+
Terminator terminator{sourceFile, sourceLine};
146+
// 9.7.1.2(7)
147+
Assign(pointer, source, terminator, /*skipRealloc=*/true);
148+
}
149+
return stat;
150+
}
151+
135152
int RTNAME(PointerDeallocate)(Descriptor &pointer, bool hasStat,
136153
const Descriptor *errMsg, const char *sourceFile, int sourceLine) {
137154
Terminator terminator{sourceFile, sourceLine};
@@ -187,7 +204,7 @@ bool RTNAME(PointerIsAssociatedWith)(
187204
return true;
188205
}
189206

190-
// TODO: PointerCheckLengthParameter, PointerAllocateSource
207+
// TODO: PointerCheckLengthParameter
191208

192209
} // extern "C"
193210
} // namespace Fortran::runtime

0 commit comments

Comments
 (0)