Skip to content

Commit 9683a9c

Browse files
committed
[flang][hlfir] Array constructor lowering [part 3/4]
Lower the cases that require runtime support to deal with the allocation, reallocation, or copy of ac-values to the array constructor storage. Differential Revision: https://reviews.llvm.org/D144513
1 parent 8f5962b commit 9683a9c

File tree

7 files changed

+449
-14
lines changed

7 files changed

+449
-14
lines changed

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,10 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
338338
mlir::Value createBox(mlir::Location loc, const fir::ExtendedValue &exv,
339339
bool isPolymorphic = false);
340340

341+
mlir::Value createBox(mlir::Location loc, mlir::Type boxType,
342+
mlir::Value addr, mlir::Value shape, mlir::Value slice,
343+
llvm::ArrayRef<mlir::Value> lengths, mlir::Value tdesc);
344+
341345
/// Create constant i1 with value 1. if \p b is true or 0. otherwise
342346
mlir::Value createBool(mlir::Location loc, bool b) {
343347
return createIntegerConstant(loc, getIntegerType(1), b ? 1 : 0);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===- ArrayConstructor.h - array constructor runtime API calls -*- 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+
#ifndef FORTRAN_OPTIMIZER_BUILDER_RUNTIME_ARRAYCONSTRUCTOR_H
10+
#define FORTRAN_OPTIMIZER_BUILDER_RUNTIME_ARRAYCONSTRUCTOR_H
11+
12+
namespace mlir {
13+
class Value;
14+
class Location;
15+
} // namespace mlir
16+
17+
namespace fir {
18+
class FirOpBuilder;
19+
}
20+
21+
namespace fir::runtime {
22+
23+
mlir::Value genInitArrayConstructorVector(mlir::Location loc,
24+
fir::FirOpBuilder &builder,
25+
mlir::Value toBox,
26+
mlir::Value useValueLengthParameters);
27+
28+
void genPushArrayConstructorValue(mlir::Location loc,
29+
fir::FirOpBuilder &builder,
30+
mlir::Value arrayConstructorVector,
31+
mlir::Value fromBox);
32+
33+
void genPushArrayConstructorSimpleScalar(mlir::Location loc,
34+
fir::FirOpBuilder &builder,
35+
mlir::Value arrayConstructorVector,
36+
mlir::Value fromAddress);
37+
38+
} // namespace fir::runtime
39+
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_ARRAYCONSTRUCTOR_H

flang/lib/Lower/ConvertArrayConstructor.cpp

Lines changed: 156 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
#include "flang/Lower/StatementContext.h"
1515
#include "flang/Lower/SymbolMap.h"
1616
#include "flang/Optimizer/Builder/HLFIRTools.h"
17+
#include "flang/Optimizer/Builder/Runtime/ArrayConstructor.h"
1718
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
1819
#include "flang/Optimizer/Builder/Todo.h"
1920
#include "flang/Optimizer/HLFIR/HLFIROps.h"
20-
#include "flang/Runtime/array-constructor.h"
2121

2222
// Array constructors are lowered with three different strategies.
2323
// All strategies are not possible with all array constructors.
@@ -287,7 +287,150 @@ class AsElementalStrategy {
287287
hlfir::ElementalOp elementalOp{};
288288
};
289289

290-
// TODO: add and implement RuntimeTempStrategy.
290+
/// Class that implements the "runtime temp strategy" to lower array
291+
/// constructors.
292+
class RuntimeTempStrategy {
293+
/// Name that will be given to the temporary allocation and hlfir.declare in
294+
/// the IR.
295+
static constexpr char tempName[] = ".tmp.arrayctor";
296+
297+
public:
298+
/// Start lowering an array constructor according to the runtime strategy.
299+
/// The temporary is only created if the extents and length parameters are
300+
/// already known. Otherwise, the handling of the allocation (and
301+
/// reallocation) is left up to the runtime.
302+
/// \p extent is the pre-computed extent of the array constructor, if it could
303+
/// be pre-computed. It is std::nullopt otherwise.
304+
/// \p lengths are the pre-computed length parameters of the array
305+
/// constructor, if they could be precomputed. \p missingLengthParameters is
306+
/// set to true if the length parameters could not be precomputed.
307+
RuntimeTempStrategy(mlir::Location loc, fir::FirOpBuilder &builder,
308+
fir::SequenceType declaredType,
309+
std::optional<mlir::Value> extent,
310+
llvm::ArrayRef<mlir::Value> lengths,
311+
bool missingLengthParameters)
312+
: arrayConstructorElementType{declaredType.getEleTy()} {
313+
mlir::Type heapType = fir::HeapType::get(declaredType);
314+
mlir::Type boxType = fir::BoxType::get(heapType);
315+
allocatableTemp = builder.createTemporary(loc, boxType, tempName);
316+
mlir::Value initialBoxValue;
317+
if (extent && !missingLengthParameters) {
318+
llvm::SmallVector<mlir::Value, 1> extents{*extent};
319+
mlir::Value tempStorage = builder.createHeapTemporary(
320+
loc, declaredType, tempName, extents, lengths);
321+
mlir::Value shape = builder.genShape(loc, extents);
322+
declare = builder.create<hlfir::DeclareOp>(
323+
loc, tempStorage, tempName, shape, lengths,
324+
fir::FortranVariableFlagsAttr{});
325+
initialBoxValue =
326+
builder.createBox(loc, boxType, declare->getOriginalBase(), shape,
327+
/*slice=*/mlir::Value{}, lengths, /*tdesc=*/{});
328+
} else {
329+
// The runtime will have to do the initial allocation.
330+
// The declare operation cannot be emitted in this case since the final
331+
// array constructor has not yet been allocated. Instead, the resulting
332+
// temporary variable will be extracted from the allocatable descriptor
333+
// after all the API calls.
334+
// Prepare the initial state of the allocatable descriptor with a
335+
// deallocated status and all the available knowledge about the extent
336+
// and length parameters.
337+
llvm::SmallVector<mlir::Value> emboxLengths(lengths.begin(),
338+
lengths.end());
339+
if (!extent)
340+
extent = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
341+
if (missingLengthParameters) {
342+
if (declaredType.getEleTy().isa<fir::CharacterType>())
343+
emboxLengths.push_back(builder.createIntegerConstant(
344+
loc, builder.getCharacterLengthType(), 0));
345+
else
346+
TODO(loc,
347+
"parametrized derived type array constructor without type-spec");
348+
}
349+
mlir::Value nullAddr = builder.createNullConstant(loc, heapType);
350+
mlir::Value shape = builder.genShape(loc, {*extent});
351+
initialBoxValue = builder.createBox(loc, boxType, nullAddr, shape,
352+
/*slice=*/mlir::Value{}, emboxLengths,
353+
/*tdesc=*/{});
354+
}
355+
builder.create<fir::StoreOp>(loc, initialBoxValue, allocatableTemp);
356+
arrayConstructorVector = fir::runtime::genInitArrayConstructorVector(
357+
loc, builder, allocatableTemp,
358+
builder.createBool(loc, missingLengthParameters));
359+
}
360+
361+
bool useSimplePushRuntime(hlfir::Entity value) {
362+
return value.isScalar() &&
363+
!arrayConstructorElementType.isa<fir::CharacterType>() &&
364+
!fir::isRecordWithAllocatableMember(arrayConstructorElementType) &&
365+
!fir::isRecordWithTypeParameters(arrayConstructorElementType);
366+
}
367+
368+
/// Push a lowered ac-value into the array constructor vector using
369+
/// the runtime API.
370+
void pushValue(mlir::Location loc, fir::FirOpBuilder &builder,
371+
hlfir::Entity value) {
372+
if (useSimplePushRuntime(value)) {
373+
auto [addrExv, cleanUp] = hlfir::convertToAddress(
374+
loc, builder, value, arrayConstructorElementType);
375+
mlir::Value addr = fir::getBase(addrExv);
376+
if (addr.getType().isa<fir::BaseBoxType>())
377+
addr = builder.create<fir::BoxAddrOp>(loc, addr);
378+
fir::runtime::genPushArrayConstructorSimpleScalar(
379+
loc, builder, arrayConstructorVector, addr);
380+
if (cleanUp)
381+
(*cleanUp)();
382+
return;
383+
}
384+
auto [boxExv, cleanUp] =
385+
hlfir::convertToBox(loc, builder, value, arrayConstructorElementType);
386+
fir::runtime::genPushArrayConstructorValue(
387+
loc, builder, arrayConstructorVector, fir::getBase(boxExv));
388+
if (cleanUp)
389+
(*cleanUp)();
390+
}
391+
392+
/// Start a fir.do_loop with the control from an implied-do and return
393+
/// the loop induction variable that is the ac-do-variable value.
394+
mlir::Value startImpliedDo(mlir::Location loc, fir::FirOpBuilder &builder,
395+
mlir::Value lower, mlir::Value upper,
396+
mlir::Value stride) {
397+
auto loop = builder.create<fir::DoLoopOp>(loc, lower, upper, stride,
398+
/*unordered=*/false,
399+
/*finalCount=*/false);
400+
builder.setInsertionPointToStart(loop.getBody());
401+
return loop.getInductionVar();
402+
}
403+
404+
/// Move the temporary to an hlfir.expr value (array constructors are not
405+
/// variables and cannot be further modified).
406+
hlfir::Entity finishArrayCtorLowering(mlir::Location loc,
407+
fir::FirOpBuilder &builder) {
408+
// Temp is created using createHeapTemporary, or allocated on the heap
409+
// by the runtime.
410+
mlir::Value mustFree = builder.createBool(loc, true);
411+
mlir::Value temp;
412+
if (declare)
413+
temp = declare->getBase();
414+
else
415+
temp = hlfir::derefPointersAndAllocatables(
416+
loc, builder, hlfir::Entity{allocatableTemp});
417+
auto hlfirExpr = builder.create<hlfir::AsExprOp>(loc, temp, mustFree);
418+
return hlfir::Entity{hlfirExpr};
419+
}
420+
421+
private:
422+
/// Element type of the array constructor being built.
423+
mlir::Type arrayConstructorElementType;
424+
/// Allocatable descriptor for the storage of the array constructor being
425+
/// built.
426+
mlir::Value allocatableTemp;
427+
/// Structure that allows the runtime API to maintain the status of
428+
/// of the array constructor being built between two API calls.
429+
mlir::Value arrayConstructorVector;
430+
/// DeclareOp for the array constructor storage, if it was possible to
431+
/// allocate it before any API calls.
432+
std::optional<hlfir::DeclareOp> declare;
433+
};
291434

292435
/// Wrapper class that dispatch to the selected array constructor lowering
293436
/// strategy and does nothing else.
@@ -322,7 +465,7 @@ class ArrayCtorLoweringStrategy {
322465

323466
private:
324467
std::variant<InlinedTempStrategy, LooplessInlinedTempStrategy,
325-
AsElementalStrategy>
468+
AsElementalStrategy, RuntimeTempStrategy>
326469
implVariant;
327470
};
328471
} // namespace
@@ -382,9 +525,8 @@ struct LengthAndTypeCollector<Character<Kind>> {
382525
/// Does the array constructor have length parameters that
383526
/// LengthAndTypeCollector::collect could not lower because this requires
384527
/// lowering an ac-value and must be delayed?
385-
static bool
386-
failedToGatherLengthParameters(mlir::Type elementType,
387-
llvm::ArrayRef<mlir::Value> lengths) {
528+
static bool missingLengthParameters(mlir::Type elementType,
529+
llvm::ArrayRef<mlir::Value> lengths) {
388530
return (elementType.isa<fir::CharacterType>() ||
389531
fir::isRecordWithTypeParameters(elementType)) &&
390532
lengths.empty();
@@ -505,8 +647,8 @@ static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy(
505647
// Try to gather the array constructor extent.
506648
mlir::Value extent;
507649
fir::SequenceType::Extent typeExtent = fir::SequenceType::getUnknownExtent();
508-
auto shapeExpr =
509-
Fortran::evaluate::GetShape(converter.getFoldingContext(), arrayCtorExpr);
650+
auto shapeExpr = Fortran::evaluate::GetContextFreeShape(
651+
converter.getFoldingContext(), arrayCtorExpr);
510652
if (shapeExpr && shapeExpr->size() == 1 && (*shapeExpr)[0]) {
511653
const Fortran::evaluate::ExtentExpr &extentExpr = *(*shapeExpr)[0];
512654
if (auto constantExtent = Fortran::evaluate::ToInt64(extentExpr)) {
@@ -531,15 +673,17 @@ static ArrayCtorLoweringStrategy selectArrayCtorLoweringStrategy(
531673
// Run an analysis of the array constructor ac-value.
532674
ArrayCtorAnalysis analysis(converter.getFoldingContext(), arrayCtorExpr);
533675
bool needToEvaluateOneExprToGetLengthParameters =
534-
failedToGatherLengthParameters(elementType, lengths);
676+
missingLengthParameters(elementType, lengths);
677+
auto declaredType = fir::SequenceType::get({typeExtent}, elementType);
535678

536679
// Based on what was gathered and the result of the analysis, select and
537680
// instantiate the right lowering strategy for the array constructor.
538681
if (!extent || needToEvaluateOneExprToGetLengthParameters ||
539682
analysis.anyArrayExpr)
540-
TODO(loc, "Lowering of array constructor requiring the runtime");
541-
542-
auto declaredType = fir::SequenceType::get({typeExtent}, elementType);
683+
return RuntimeTempStrategy(
684+
loc, builder, declaredType,
685+
extent ? std::optional<mlir::Value>(extent) : std::nullopt, lengths,
686+
needToEvaluateOneExprToGetLengthParameters);
543687
// Note: array constructors containing impure ac-value expr are currently not
544688
// rewritten to hlfir.elemental because impure expressions should be evaluated
545689
// in order, and hlfir.elemental currently misses a way to indicate that.
@@ -562,8 +706,6 @@ static void genAcValue(mlir::Location loc,
562706
Fortran::lower::SymMap &symMap,
563707
Fortran::lower::StatementContext &stmtCtx,
564708
ArrayCtorLoweringStrategy &arrayBuilder) {
565-
if (expr.Rank() != 0)
566-
TODO(loc, "array constructor with array ac-value in HLFIR");
567709
// TODO: get rid of the toEvExpr indirection.
568710
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
569711
hlfir::Entity value = Fortran::lower::convertExprToHLFIR(

flang/lib/Optimizer/Builder/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_flang_library(FIRBuilder
1111
LowLevelIntrinsics.cpp
1212
MutableBox.cpp
1313
Runtime/Allocatable.cpp
14+
Runtime/ArrayConstructor.cpp
1415
Runtime/Assign.cpp
1516
Runtime/Character.cpp
1617
Runtime/Command.cpp

flang/lib/Optimizer/Builder/FIRBuilder.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,17 @@ mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
562562
});
563563
}
564564

565+
mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, mlir::Type boxType,
566+
mlir::Value addr, mlir::Value shape,
567+
mlir::Value slice,
568+
llvm::ArrayRef<mlir::Value> lengths,
569+
mlir::Value tdesc) {
570+
mlir::Type valueOrSequenceType = fir::unwrapPassByRefType(boxType);
571+
return create<fir::EmboxOp>(
572+
loc, boxType, addr, shape, slice,
573+
elideLengthsAlreadyInType(valueOrSequenceType, lengths), tdesc);
574+
}
575+
565576
void fir::FirOpBuilder::dumpFunc() { getFunction().dump(); }
566577

567578
static mlir::Value
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//===- ArrayConstructor.cpp - array constructor runtime API calls ---------===//
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+
#include "flang/Optimizer/Builder/Runtime/ArrayConstructor.h"
10+
#include "flang/Optimizer/Builder/FIRBuilder.h"
11+
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
12+
#include "flang/Runtime/array-constructor.h"
13+
14+
using namespace Fortran::runtime;
15+
16+
namespace fir::runtime {
17+
template <>
18+
constexpr TypeBuilderFunc
19+
getModel<Fortran::runtime::ArrayConstructorVector &>() {
20+
return getModel<void *>();
21+
}
22+
} // namespace fir::runtime
23+
24+
mlir::Value fir::runtime::genInitArrayConstructorVector(
25+
mlir::Location loc, fir::FirOpBuilder &builder, mlir::Value toBox,
26+
mlir::Value useValueLengthParameters) {
27+
// Allocate storage for the runtime cookie for the array constructor vector.
28+
// Use the "host" size and alignment, but double them to be safe regardless of
29+
// the target. The "cookieSize" argument is used to validate this wild
30+
// assumption until runtime interfaces are improved.
31+
std::size_t arrayVectorStructBitSize =
32+
2 * sizeof(Fortran::runtime::ArrayConstructorVector) * 8;
33+
std::size_t alignLike = alignof(Fortran::runtime::ArrayConstructorVector) * 8;
34+
fir::SequenceType::Extent numElem =
35+
(arrayVectorStructBitSize + alignLike - 1) / alignLike;
36+
mlir::Type intType = builder.getIntegerType(alignLike);
37+
mlir::Type seqType = fir::SequenceType::get({numElem}, intType);
38+
mlir::Value cookie =
39+
builder.createTemporary(loc, seqType, ".rt.arrayctor.vector");
40+
41+
mlir::func::FuncOp func =
42+
fir::runtime::getRuntimeFunc<mkRTKey(InitArrayConstructorVector)>(
43+
loc, builder);
44+
mlir::FunctionType funcType = func.getFunctionType();
45+
cookie = builder.createConvert(loc, funcType.getInput(0), cookie);
46+
mlir::Value cookieSize = builder.createIntegerConstant(
47+
loc, funcType.getInput(3), numElem * alignLike / 8);
48+
mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc);
49+
mlir::Value sourceLine =
50+
fir::factory::locationToLineNo(builder, loc, funcType.getInput(5));
51+
auto args = fir::runtime::createArguments(builder, loc, funcType, cookie,
52+
toBox, useValueLengthParameters,
53+
cookieSize, sourceFile, sourceLine);
54+
builder.create<fir::CallOp>(loc, func, args);
55+
return cookie;
56+
}
57+
58+
void fir::runtime::genPushArrayConstructorValue(
59+
mlir::Location loc, fir::FirOpBuilder &builder,
60+
mlir::Value arrayConstructorVector, mlir::Value fromBox) {
61+
mlir::func::FuncOp func =
62+
fir::runtime::getRuntimeFunc<mkRTKey(PushArrayConstructorValue)>(loc,
63+
builder);
64+
mlir::FunctionType funcType = func.getFunctionType();
65+
auto args = fir::runtime::createArguments(builder, loc, funcType,
66+
arrayConstructorVector, fromBox);
67+
builder.create<fir::CallOp>(loc, func, args);
68+
}
69+
70+
void fir::runtime::genPushArrayConstructorSimpleScalar(
71+
mlir::Location loc, fir::FirOpBuilder &builder,
72+
mlir::Value arrayConstructorVector, mlir::Value fromAddress) {
73+
mlir::func::FuncOp func =
74+
fir::runtime::getRuntimeFunc<mkRTKey(PushArrayConstructorSimpleScalar)>(
75+
loc, builder);
76+
mlir::FunctionType funcType = func.getFunctionType();
77+
auto args = fir::runtime::createArguments(
78+
builder, loc, funcType, arrayConstructorVector, fromAddress);
79+
builder.create<fir::CallOp>(loc, func, args);
80+
}

0 commit comments

Comments
 (0)