Skip to content

Commit 859d4a1

Browse files
clementvalschweitzpgijeanPerier
committed
[flang] Lower more cases of assignments on allocatable variables
This patch enables the lowering of various allocatable assignements for character type and numeric types. This patch is part of the upstreaming effort from fir-dev branch. Depends on D120819 Reviewed By: PeteSteinfeld, schweitz Differential Revision: https://reviews.llvm.org/D120820 Co-authored-by: Eric Schweitz <[email protected]> Co-authored-by: Jean Perier <[email protected]>
1 parent 9f37775 commit 859d4a1

File tree

9 files changed

+1191
-6
lines changed

9 files changed

+1191
-6
lines changed

flang/include/flang/Lower/ConvertExpr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ inline mlir::NamedAttribute getAdaptToByRefAttr(fir::FirOpBuilder &builder) {
149149
builder.getUnitAttr()};
150150
}
151151

152+
/// Generate max(\p value, 0) where \p value is a scalar integer.
153+
mlir::Value genMaxWithZero(fir::FirOpBuilder &builder, mlir::Location loc,
154+
mlir::Value value);
155+
152156
} // namespace Fortran::lower
153157

154158
#endif // FORTRAN_LOWER_CONVERTEXPR_H

flang/include/flang/Lower/IntrinsicCall.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ fir::ExtendedValue getAbsentIntrinsicArgument();
8484
// of intrinsic call lowering.
8585
//===----------------------------------------------------------------------===//
8686

87+
/// Generate maximum. There must be at least one argument and all arguments
88+
/// must have the same type.
89+
mlir::Value genMax(fir::FirOpBuilder &, mlir::Location,
90+
llvm::ArrayRef<mlir::Value> args);
91+
8792
/// Generate power function x**y with the given expected
8893
/// result type.
8994
mlir::Value genPow(fir::FirOpBuilder &, mlir::Location, mlir::Type resultType,

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,32 @@ fir::ExtendedValue arraySectionElementToExtendedValue(
488488
fir::FirOpBuilder &builder, mlir::Location loc,
489489
const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice);
490490

491+
/// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalars. The
492+
/// assignment follows Fortran intrinsic assignment semantic (10.2.1.3).
493+
void genScalarAssignment(fir::FirOpBuilder &builder, mlir::Location loc,
494+
const fir::ExtendedValue &lhs,
495+
const fir::ExtendedValue &rhs);
496+
/// Assign \p rhs to \p lhs. Both \p rhs and \p lhs must be scalar derived
497+
/// types. The assignment follows Fortran intrinsic assignment semantic for
498+
/// derived types (10.2.1.3 point 13).
499+
void genRecordAssignment(fir::FirOpBuilder &builder, mlir::Location loc,
500+
const fir::ExtendedValue &lhs,
501+
const fir::ExtendedValue &rhs);
502+
503+
/// Generate the, possibly dynamic, LEN of a CHARACTER. \p arrLoad determines
504+
/// the base array. After applying \p path, the result must be a reference to a
505+
/// `!fir.char` type object. \p substring must have 0, 1, or 2 members. The
506+
/// first member is the starting offset. The second is the ending offset.
507+
mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc,
508+
fir::ArrayLoadOp arrLoad,
509+
llvm::ArrayRef<mlir::Value> path,
510+
llvm::ArrayRef<mlir::Value> substring);
511+
mlir::Value genLenOfCharacter(fir::FirOpBuilder &builder, mlir::Location loc,
512+
fir::SequenceType seqTy, mlir::Value memref,
513+
llvm::ArrayRef<mlir::Value> typeParams,
514+
llvm::ArrayRef<mlir::Value> path,
515+
llvm::ArrayRef<mlir::Value> substring);
516+
491517
} // namespace fir::factory
492518

493519
#endif // FORTRAN_OPTIMIZER_BUILDER_FIRBUILDER_H

flang/include/flang/Optimizer/Builder/Factory.h

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,117 @@ constexpr llvm::StringRef attrFortranArrayOffsets() {
3131
return "Fortran.offsets";
3232
}
3333

34+
/// Generate a character copy with optimized forms.
35+
///
36+
/// If the lengths are constant and equal, use load/store rather than a loop.
37+
/// Otherwise, if the lengths are constant and the input is longer than the
38+
/// output, generate a loop to move a truncated portion of the source to the
39+
/// destination. Finally, if the lengths are runtime values or the destination
40+
/// is longer than the source, move the entire source character and pad the
41+
/// destination with spaces as needed.
42+
template <typename B>
43+
void genCharacterCopy(mlir::Value src, mlir::Value srcLen, mlir::Value dst,
44+
mlir::Value dstLen, B &builder, mlir::Location loc) {
45+
auto srcTy =
46+
fir::dyn_cast_ptrEleTy(src.getType()).template cast<fir::CharacterType>();
47+
auto dstTy =
48+
fir::dyn_cast_ptrEleTy(dst.getType()).template cast<fir::CharacterType>();
49+
if (!srcLen && !dstLen && srcTy.getFKind() == dstTy.getFKind() &&
50+
srcTy.getLen() == dstTy.getLen()) {
51+
// same size, so just use load and store
52+
auto load = builder.template create<fir::LoadOp>(loc, src);
53+
builder.template create<fir::StoreOp>(loc, load, dst);
54+
return;
55+
}
56+
auto zero = builder.template create<mlir::arith::ConstantIndexOp>(loc, 0);
57+
auto one = builder.template create<mlir::arith::ConstantIndexOp>(loc, 1);
58+
auto toArrayTy = [&](fir::CharacterType ty) {
59+
return fir::ReferenceType::get(fir::SequenceType::get(
60+
fir::SequenceType::ShapeRef{fir::SequenceType::getUnknownExtent()},
61+
fir::CharacterType::getSingleton(ty.getContext(), ty.getFKind())));
62+
};
63+
auto toEleTy = [&](fir::ReferenceType ty) {
64+
auto seqTy = ty.getEleTy().cast<fir::SequenceType>();
65+
return seqTy.getEleTy().cast<fir::CharacterType>();
66+
};
67+
auto toCoorTy = [&](fir::ReferenceType ty) {
68+
return fir::ReferenceType::get(toEleTy(ty));
69+
};
70+
if (!srcLen && !dstLen && srcTy.getLen() >= dstTy.getLen()) {
71+
auto upper = builder.template create<mlir::arith::ConstantIndexOp>(
72+
loc, dstTy.getLen() - 1);
73+
auto loop = builder.template create<fir::DoLoopOp>(loc, zero, upper, one);
74+
auto insPt = builder.saveInsertionPoint();
75+
builder.setInsertionPointToStart(loop.getBody());
76+
auto csrcTy = toArrayTy(srcTy);
77+
auto csrc = builder.template create<fir::ConvertOp>(loc, csrcTy, src);
78+
auto in = builder.template create<fir::CoordinateOp>(
79+
loc, toCoorTy(csrcTy), csrc, loop.getInductionVar());
80+
auto load = builder.template create<fir::LoadOp>(loc, in);
81+
auto cdstTy = toArrayTy(dstTy);
82+
auto cdst = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
83+
auto out = builder.template create<fir::CoordinateOp>(
84+
loc, toCoorTy(cdstTy), cdst, loop.getInductionVar());
85+
mlir::Value cast =
86+
srcTy.getFKind() == dstTy.getFKind()
87+
? load.getResult()
88+
: builder
89+
.template create<fir::ConvertOp>(loc, toEleTy(cdstTy), load)
90+
.getResult();
91+
builder.template create<fir::StoreOp>(loc, cast, out);
92+
builder.restoreInsertionPoint(insPt);
93+
return;
94+
}
95+
auto minusOne = [&](mlir::Value v) -> mlir::Value {
96+
return builder.template create<mlir::arith::SubIOp>(
97+
loc, builder.template create<fir::ConvertOp>(loc, one.getType(), v),
98+
one);
99+
};
100+
mlir::Value len = dstLen ? minusOne(dstLen)
101+
: builder
102+
.template create<mlir::arith::ConstantIndexOp>(
103+
loc, dstTy.getLen() - 1)
104+
.getResult();
105+
auto loop = builder.template create<fir::DoLoopOp>(loc, zero, len, one);
106+
auto insPt = builder.saveInsertionPoint();
107+
builder.setInsertionPointToStart(loop.getBody());
108+
mlir::Value slen =
109+
srcLen
110+
? builder.template create<fir::ConvertOp>(loc, one.getType(), srcLen)
111+
.getResult()
112+
: builder
113+
.template create<mlir::arith::ConstantIndexOp>(loc,
114+
srcTy.getLen())
115+
.getResult();
116+
auto cond = builder.template create<mlir::arith::CmpIOp>(
117+
loc, mlir::arith::CmpIPredicate::slt, loop.getInductionVar(), slen);
118+
auto ifOp = builder.template create<fir::IfOp>(loc, cond, /*withElse=*/true);
119+
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
120+
auto csrcTy = toArrayTy(srcTy);
121+
auto csrc = builder.template create<fir::ConvertOp>(loc, csrcTy, src);
122+
auto in = builder.template create<fir::CoordinateOp>(
123+
loc, toCoorTy(csrcTy), csrc, loop.getInductionVar());
124+
auto load = builder.template create<fir::LoadOp>(loc, in);
125+
auto cdstTy = toArrayTy(dstTy);
126+
auto cdst = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
127+
auto out = builder.template create<fir::CoordinateOp>(
128+
loc, toCoorTy(cdstTy), cdst, loop.getInductionVar());
129+
mlir::Value cast =
130+
srcTy.getFKind() == dstTy.getFKind()
131+
? load.getResult()
132+
: builder.template create<fir::ConvertOp>(loc, toEleTy(cdstTy), load)
133+
.getResult();
134+
builder.template create<fir::StoreOp>(loc, cast, out);
135+
builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
136+
auto space = builder.template create<fir::StringLitOp>(
137+
loc, toEleTy(cdstTy), llvm::ArrayRef<char>{' '});
138+
auto cdst2 = builder.template create<fir::ConvertOp>(loc, cdstTy, dst);
139+
auto out2 = builder.template create<fir::CoordinateOp>(
140+
loc, toCoorTy(cdstTy), cdst2, loop.getInductionVar());
141+
builder.template create<fir::StoreOp>(loc, space, out2);
142+
builder.restoreInsertionPoint(insPt);
143+
}
144+
34145
/// Get extents from fir.shape/fir.shape_shift op. Empty result if
35146
/// \p shapeVal is empty or is a fir.shift.
36147
inline std::vector<mlir::Value> getExtents(mlir::Value shapeVal) {

flang/lib/Lower/ConvertExpr.cpp

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,59 @@ convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
17531753
return fir::ArrayBoxValue(val, extents);
17541754
}
17551755

1756+
//===----------------------------------------------------------------------===//
1757+
//
1758+
// Lowering of scalar expressions in an explicit iteration space context.
1759+
//
1760+
//===----------------------------------------------------------------------===//
1761+
1762+
// Shared code for creating a copy of a derived type element. This function is
1763+
// called from a continuation.
1764+
inline static fir::ArrayAmendOp
1765+
createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
1766+
fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
1767+
const fir::ExtendedValue &elementExv, mlir::Type eleTy,
1768+
mlir::Value innerArg) {
1769+
if (destLoad.getTypeparams().empty()) {
1770+
fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
1771+
} else {
1772+
auto boxTy = fir::BoxType::get(eleTy);
1773+
auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
1774+
mlir::Value{}, mlir::Value{},
1775+
destLoad.getTypeparams());
1776+
auto fromBox = builder.create<fir::EmboxOp>(
1777+
loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
1778+
destLoad.getTypeparams());
1779+
fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
1780+
fir::BoxValue(fromBox));
1781+
}
1782+
return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
1783+
destAcc);
1784+
}
1785+
1786+
inline static fir::ArrayAmendOp
1787+
createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
1788+
fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
1789+
const fir::ExtendedValue &srcExv, mlir::Value innerArg,
1790+
llvm::ArrayRef<mlir::Value> bounds) {
1791+
fir::CharBoxValue dstChar(dstOp, dstLen);
1792+
fir::factory::CharacterExprHelper helper{builder, loc};
1793+
if (!bounds.empty()) {
1794+
dstChar = helper.createSubstring(dstChar, bounds);
1795+
fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
1796+
dstChar.getAddr(), dstChar.getLen(), builder,
1797+
loc);
1798+
// Update the LEN to the substring's LEN.
1799+
dstLen = dstChar.getLen();
1800+
}
1801+
// For a CHARACTER, we generate the element assignment loops inline.
1802+
helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
1803+
// Mark this array element as amended.
1804+
mlir::Type ty = innerArg.getType();
1805+
auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
1806+
return amend;
1807+
}
1808+
17561809
//===----------------------------------------------------------------------===//
17571810
//
17581811
// Lowering of array expressions.
@@ -2435,8 +2488,37 @@ class ArrayExprLowering {
24352488
TODO(getLoc(), "genarr Component");
24362489
}
24372490

2491+
/// Array reference with subscripts. If this has rank > 0, this is a form
2492+
/// of an array section (slice).
2493+
///
2494+
/// There are two "slicing" primitives that may be applied on a dimension by
2495+
/// dimension basis: (1) triple notation and (2) vector addressing. Since
2496+
/// dimensions can be selectively sliced, some dimensions may contain
2497+
/// regular scalar expressions and those dimensions do not participate in
2498+
/// the array expression evaluation.
24382499
CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
2439-
TODO(getLoc(), "genar ArrayRef");
2500+
if (explicitSpaceIsActive()) {
2501+
TODO(getLoc(), "genarr ArrayRef explicitSpace");
2502+
} else {
2503+
if (Fortran::lower::isRankedArrayAccess(x)) {
2504+
components.reversePath.push_back(&x);
2505+
return genImplicitArrayAccess(x.base(), components);
2506+
}
2507+
}
2508+
bool atEnd = pathIsEmpty(components);
2509+
components.reversePath.push_back(&x);
2510+
auto result = genarr(x.base(), components);
2511+
if (components.applied)
2512+
return result;
2513+
mlir::Location loc = getLoc();
2514+
if (atEnd) {
2515+
if (x.Rank() == 0)
2516+
return genAsScalar(x);
2517+
fir::emitFatalError(loc, "expected scalar");
2518+
}
2519+
return [=](IterSpace) -> ExtValue {
2520+
fir::emitFatalError(loc, "reached arrayref with path");
2521+
};
24402522
}
24412523

24422524
CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
@@ -2454,6 +2536,10 @@ class ArrayExprLowering {
24542536
x.u);
24552537
}
24562538

2539+
bool pathIsEmpty(const ComponentPath &components) {
2540+
return components.reversePath.empty();
2541+
}
2542+
24572543
CC genarr(const Fortran::evaluate::ComplexPart &x,
24582544
ComponentPath &components) {
24592545
TODO(getLoc(), "genarr ComplexPart");
@@ -2666,7 +2752,30 @@ class ArrayExprLowering {
26662752
mlir::Type arrTy = innerArg.getType();
26672753
mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
26682754
if (isAdjustedArrayElementType(eleTy)) {
2669-
TODO(loc, "isAdjustedArrayElementType");
2755+
// The elemental update is in the memref domain. Under this semantics,
2756+
// we must always copy the computed new element from its location in
2757+
// memory into the destination array.
2758+
mlir::Type resRefTy = builder.getRefType(eleTy);
2759+
// Get a reference to the array element to be amended.
2760+
auto arrayOp = builder.create<fir::ArrayAccessOp>(
2761+
loc, resRefTy, innerArg, iterSpace.iterVec(),
2762+
destination.getTypeparams());
2763+
if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
2764+
llvm::SmallVector<mlir::Value> substringBounds;
2765+
populateBounds(substringBounds, substring);
2766+
mlir::Value dstLen = fir::factory::genLenOfCharacter(
2767+
builder, loc, destination, iterSpace.iterVec(), substringBounds);
2768+
fir::ArrayAmendOp amend = createCharArrayAmend(
2769+
loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
2770+
return abstractArrayExtValue(amend, dstLen);
2771+
}
2772+
if (fir::isa_derived(eleTy)) {
2773+
fir::ArrayAmendOp amend = createDerivedArrayAmend(
2774+
loc, destination, builder, arrayOp, exv, eleTy, innerArg);
2775+
return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
2776+
}
2777+
assert(eleTy.isa<fir::SequenceType>() && "must be an array");
2778+
TODO(loc, "array (as element) assignment");
26702779
}
26712780
// By value semantics. The element is being assigned by value.
26722781
mlir::Value ele = builder.createConvert(loc, eleTy, fir::getBase(exv));
@@ -2987,3 +3096,15 @@ void Fortran::lower::createAllocatableArrayAssignment(
29873096
ArrayExprLowering::lowerAllocatableArrayAssignment(
29883097
converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
29893098
}
3099+
3100+
mlir::Value Fortran::lower::genMaxWithZero(fir::FirOpBuilder &builder,
3101+
mlir::Location loc,
3102+
mlir::Value value) {
3103+
mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
3104+
if (mlir::Operation *definingOp = value.getDefiningOp())
3105+
if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
3106+
if (auto intAttr = cst.getValue().dyn_cast<mlir::IntegerAttr>())
3107+
return intAttr.getInt() < 0 ? zero : value;
3108+
return Fortran::lower::genMax(builder, loc,
3109+
llvm::SmallVector<mlir::Value>{value, zero});
3110+
}

0 commit comments

Comments
 (0)