1 //===-- ConvertExpr.cpp ---------------------------------------------------===//
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 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "flang/Lower/ConvertExpr.h"
14 #include "flang/Common/default-kinds.h"
15 #include "flang/Common/unwrap.h"
16 #include "flang/Evaluate/fold.h"
17 #include "flang/Evaluate/real.h"
18 #include "flang/Evaluate/traverse.h"
19 #include "flang/Lower/Allocatable.h"
20 #include "flang/Lower/Bridge.h"
21 #include "flang/Lower/BuiltinModules.h"
22 #include "flang/Lower/CallInterface.h"
23 #include "flang/Lower/Coarray.h"
24 #include "flang/Lower/ComponentPath.h"
25 #include "flang/Lower/ConvertType.h"
26 #include "flang/Lower/ConvertVariable.h"
27 #include "flang/Lower/CustomIntrinsicCall.h"
28 #include "flang/Lower/DumpEvaluateExpr.h"
29 #include "flang/Lower/IntrinsicCall.h"
30 #include "flang/Lower/Mangler.h"
31 #include "flang/Lower/Runtime.h"
32 #include "flang/Lower/Support/Utils.h"
33 #include "flang/Optimizer/Builder/Character.h"
34 #include "flang/Optimizer/Builder/Complex.h"
35 #include "flang/Optimizer/Builder/Factory.h"
36 #include "flang/Optimizer/Builder/Runtime/Character.h"
37 #include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
38 #include "flang/Optimizer/Builder/Runtime/Ragged.h"
39 #include "flang/Optimizer/Builder/Todo.h"
40 #include "flang/Optimizer/Dialect/FIRAttr.h"
41 #include "flang/Optimizer/Dialect/FIRDialect.h"
42 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
43 #include "flang/Optimizer/Support/FatalError.h"
44 #include "flang/Semantics/expression.h"
45 #include "flang/Semantics/symbol.h"
46 #include "flang/Semantics/tools.h"
47 #include "flang/Semantics/type.h"
48 #include "mlir/Dialect/Func/IR/FuncOps.h"
49 #include "llvm/ADT/TypeSwitch.h"
50 #include "llvm/Support/CommandLine.h"
51 #include "llvm/Support/Debug.h"
52 #include "llvm/Support/ErrorHandling.h"
53 #include "llvm/Support/raw_ostream.h"
54 #include <algorithm>
55
56 #define DEBUG_TYPE "flang-lower-expr"
57
58 //===----------------------------------------------------------------------===//
59 // The composition and structure of Fortran::evaluate::Expr is defined in
60 // the various header files in include/flang/Evaluate. You are referred
61 // there for more information on these data structures. Generally speaking,
62 // these data structures are a strongly typed family of abstract data types
63 // that, composed as trees, describe the syntax of Fortran expressions.
64 //
65 // This part of the bridge can traverse these tree structures and lower them
66 // to the correct FIR representation in SSA form.
67 //===----------------------------------------------------------------------===//
68
69 static llvm::cl::opt<bool> generateArrayCoordinate(
70 "gen-array-coor",
71 llvm::cl::desc("in lowering create ArrayCoorOp instead of CoordinateOp"),
72 llvm::cl::init(false));
73
74 // The default attempts to balance a modest allocation size with expected user
75 // input to minimize bounds checks and reallocations during dynamic array
76 // construction. Some user codes may have very large array constructors for
77 // which the default can be increased.
78 static llvm::cl::opt<unsigned> clInitialBufferSize(
79 "array-constructor-initial-buffer-size",
80 llvm::cl::desc(
81 "set the incremental array construction buffer size (default=32)"),
82 llvm::cl::init(32u));
83
84 // Lower TRANSPOSE as an "elemental" function that swaps the array
85 // expression's iteration space, so that no runtime call is needed.
86 // This lowering may help get rid of unnecessary creation of temporary
87 // arrays. Note that the runtime TRANSPOSE implementation may be different
88 // from the "inline" FIR, e.g. it may diagnose out-of-memory conditions
89 // during the temporary allocation whereas the inline implementation
90 // relies on AllocMemOp that will silently return null in case
91 // there is not enough memory. So it may be a good idea to set
92 // this option to false for -O0.
93 static llvm::cl::opt<bool> optimizeTranspose(
94 "opt-transpose",
95 llvm::cl::desc("lower transpose without using a runtime call"),
96 llvm::cl::init(true));
97
98 /// The various semantics of a program constituent (or a part thereof) as it may
99 /// appear in an expression.
100 ///
101 /// Given the following Fortran declarations.
102 /// ```fortran
103 /// REAL :: v1, v2, v3
104 /// REAL, POINTER :: vp1
105 /// REAL :: a1(c), a2(c)
106 /// REAL ELEMENTAL FUNCTION f1(arg) ! array -> array
107 /// FUNCTION f2(arg) ! array -> array
108 /// vp1 => v3 ! 1
109 /// v1 = v2 * vp1 ! 2
110 /// a1 = a1 + a2 ! 3
111 /// a1 = f1(a2) ! 4
112 /// a1 = f2(a2) ! 5
113 /// ```
114 ///
115 /// In line 1, `vp1` is a BoxAddr to copy a box value into. The box value is
116 /// constructed from the DataAddr of `v3`.
117 /// In line 2, `v1` is a DataAddr to copy a value into. The value is constructed
118 /// from the DataValue of `v2` and `vp1`. DataValue is implicitly a double
119 /// dereference in the `vp1` case.
120 /// In line 3, `a1` and `a2` on the rhs are RefTransparent. The `a1` on the lhs
121 /// is CopyInCopyOut as `a1` is replaced elementally by the additions.
122 /// In line 4, `a2` can be RefTransparent, ByValueArg, RefOpaque, or BoxAddr if
123 /// `arg` is declared as C-like pass-by-value, VALUE, INTENT(?), or ALLOCATABLE/
124 /// POINTER, respectively. `a1` on the lhs is CopyInCopyOut.
125 /// In line 5, `a2` may be DataAddr or BoxAddr assuming f2 is transformational.
126 /// `a1` on the lhs is again CopyInCopyOut.
127 enum class ConstituentSemantics {
128 // Scalar data reference semantics.
129 //
130 // For these let `v` be the location in memory of a variable with value `x`
131 DataValue, // refers to the value `x`
132 DataAddr, // refers to the address `v`
133 BoxValue, // refers to a box value containing `v`
134 BoxAddr, // refers to the address of a box value containing `v`
135
136 // Array data reference semantics.
137 //
138 // For these let `a` be the location in memory of a sequence of value `[xs]`.
139 // Let `x_i` be the `i`-th value in the sequence `[xs]`.
140
141 // Referentially transparent. Refers to the array's value, `[xs]`.
142 RefTransparent,
143 // Refers to an ephemeral address `tmp` containing value `x_i` (15.5.2.3.p7
144 // note 2). (Passing a copy by reference to simulate pass-by-value.)
145 ByValueArg,
146 // Refers to the merge of array value `[xs]` with another array value `[ys]`.
147 // This merged array value will be written into memory location `a`.
148 CopyInCopyOut,
149 // Similar to CopyInCopyOut but `a` may be a transient projection (rather than
150 // a whole array).
151 ProjectedCopyInCopyOut,
152 // Similar to ProjectedCopyInCopyOut, except the merge value is not assigned
153 // automatically by the framework. Instead, and address for `[xs]` is made
154 // accessible so that custom assignments to `[xs]` can be implemented.
155 CustomCopyInCopyOut,
156 // Referentially opaque. Refers to the address of `x_i`.
157 RefOpaque
158 };
159
160 /// Convert parser's INTEGER relational operators to MLIR. TODO: using
161 /// unordered, but we may want to cons ordered in certain situation.
162 static mlir::arith::CmpIPredicate
translateRelational(Fortran::common::RelationalOperator rop)163 translateRelational(Fortran::common::RelationalOperator rop) {
164 switch (rop) {
165 case Fortran::common::RelationalOperator::LT:
166 return mlir::arith::CmpIPredicate::slt;
167 case Fortran::common::RelationalOperator::LE:
168 return mlir::arith::CmpIPredicate::sle;
169 case Fortran::common::RelationalOperator::EQ:
170 return mlir::arith::CmpIPredicate::eq;
171 case Fortran::common::RelationalOperator::NE:
172 return mlir::arith::CmpIPredicate::ne;
173 case Fortran::common::RelationalOperator::GT:
174 return mlir::arith::CmpIPredicate::sgt;
175 case Fortran::common::RelationalOperator::GE:
176 return mlir::arith::CmpIPredicate::sge;
177 }
178 llvm_unreachable("unhandled INTEGER relational operator");
179 }
180
181 /// Convert parser's REAL relational operators to MLIR.
182 /// The choice of order (O prefix) vs unorder (U prefix) follows Fortran 2018
183 /// requirements in the IEEE context (table 17.1 of F2018). This choice is
184 /// also applied in other contexts because it is easier and in line with
185 /// other Fortran compilers.
186 /// FIXME: The signaling/quiet aspect of the table 17.1 requirement is not
187 /// fully enforced. FIR and LLVM `fcmp` instructions do not give any guarantee
188 /// whether the comparison will signal or not in case of quiet NaN argument.
189 static mlir::arith::CmpFPredicate
translateFloatRelational(Fortran::common::RelationalOperator rop)190 translateFloatRelational(Fortran::common::RelationalOperator rop) {
191 switch (rop) {
192 case Fortran::common::RelationalOperator::LT:
193 return mlir::arith::CmpFPredicate::OLT;
194 case Fortran::common::RelationalOperator::LE:
195 return mlir::arith::CmpFPredicate::OLE;
196 case Fortran::common::RelationalOperator::EQ:
197 return mlir::arith::CmpFPredicate::OEQ;
198 case Fortran::common::RelationalOperator::NE:
199 return mlir::arith::CmpFPredicate::UNE;
200 case Fortran::common::RelationalOperator::GT:
201 return mlir::arith::CmpFPredicate::OGT;
202 case Fortran::common::RelationalOperator::GE:
203 return mlir::arith::CmpFPredicate::OGE;
204 }
205 llvm_unreachable("unhandled REAL relational operator");
206 }
207
genActualIsPresentTest(fir::FirOpBuilder & builder,mlir::Location loc,fir::ExtendedValue actual)208 static mlir::Value genActualIsPresentTest(fir::FirOpBuilder &builder,
209 mlir::Location loc,
210 fir::ExtendedValue actual) {
211 if (const auto *ptrOrAlloc = actual.getBoxOf<fir::MutableBoxValue>())
212 return fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
213 *ptrOrAlloc);
214 // Optional case (not that optional allocatable/pointer cannot be absent
215 // when passed to CMPLX as per 15.5.2.12 point 3 (7) and (8)). It is
216 // therefore possible to catch them in the `then` case above.
217 return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(),
218 fir::getBase(actual));
219 }
220
221 /// Convert the array_load, `load`, to an extended value. If `path` is not
222 /// empty, then traverse through the components designated. The base value is
223 /// `newBase`. This does not accept an array_load with a slice operand.
224 static fir::ExtendedValue
arrayLoadExtValue(fir::FirOpBuilder & builder,mlir::Location loc,fir::ArrayLoadOp load,llvm::ArrayRef<mlir::Value> path,mlir::Value newBase,mlir::Value newLen={})225 arrayLoadExtValue(fir::FirOpBuilder &builder, mlir::Location loc,
226 fir::ArrayLoadOp load, llvm::ArrayRef<mlir::Value> path,
227 mlir::Value newBase, mlir::Value newLen = {}) {
228 // Recover the extended value from the load.
229 if (load.getSlice())
230 fir::emitFatalError(loc, "array_load with slice is not allowed");
231 mlir::Type arrTy = load.getType();
232 if (!path.empty()) {
233 mlir::Type ty = fir::applyPathToType(arrTy, path);
234 if (!ty)
235 fir::emitFatalError(loc, "path does not apply to type");
236 if (!ty.isa<fir::SequenceType>()) {
237 if (fir::isa_char(ty)) {
238 mlir::Value len = newLen;
239 if (!len)
240 len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
241 load.getMemref());
242 if (!len) {
243 assert(load.getTypeparams().size() == 1 &&
244 "length must be in array_load");
245 len = load.getTypeparams()[0];
246 }
247 return fir::CharBoxValue{newBase, len};
248 }
249 return newBase;
250 }
251 arrTy = ty.cast<fir::SequenceType>();
252 }
253
254 auto arrayToExtendedValue =
255 [&](const llvm::SmallVector<mlir::Value> &extents,
__anon67dfc8450102(const llvm::SmallVector<mlir::Value> &extents, const llvm::SmallVector<mlir::Value> &origins) 256 const llvm::SmallVector<mlir::Value> &origins) -> fir::ExtendedValue {
257 mlir::Type eleTy = fir::unwrapSequenceType(arrTy);
258 if (fir::isa_char(eleTy)) {
259 mlir::Value len = newLen;
260 if (!len)
261 len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
262 load.getMemref());
263 if (!len) {
264 assert(load.getTypeparams().size() == 1 &&
265 "length must be in array_load");
266 len = load.getTypeparams()[0];
267 }
268 return fir::CharArrayBoxValue(newBase, len, extents, origins);
269 }
270 return fir::ArrayBoxValue(newBase, extents, origins);
271 };
272 // Use the shape op, if there is one.
273 mlir::Value shapeVal = load.getShape();
274 if (shapeVal) {
275 if (!mlir::isa<fir::ShiftOp>(shapeVal.getDefiningOp())) {
276 auto extents = fir::factory::getExtents(shapeVal);
277 auto origins = fir::factory::getOrigins(shapeVal);
278 return arrayToExtendedValue(extents, origins);
279 }
280 if (!fir::isa_box_type(load.getMemref().getType()))
281 fir::emitFatalError(loc, "shift op is invalid in this context");
282 }
283
284 // If we're dealing with the array_load op (not a subobject) and the load does
285 // not have any type parameters, then read the extents from the original box.
286 // The origin may be either from the box or a shift operation. Create and
287 // return the array extended value.
288 if (path.empty() && load.getTypeparams().empty()) {
289 auto oldBox = load.getMemref();
290 fir::ExtendedValue exv = fir::factory::readBoxValue(builder, loc, oldBox);
291 auto extents = fir::factory::getExtents(loc, builder, exv);
292 auto origins = fir::factory::getNonDefaultLowerBounds(builder, loc, exv);
293 if (shapeVal) {
294 // shapeVal is a ShiftOp and load.memref() is a boxed value.
295 newBase = builder.create<fir::ReboxOp>(loc, oldBox.getType(), oldBox,
296 shapeVal, /*slice=*/mlir::Value{});
297 origins = fir::factory::getOrigins(shapeVal);
298 }
299 return fir::substBase(arrayToExtendedValue(extents, origins), newBase);
300 }
301 TODO(loc, "path to a POINTER, ALLOCATABLE, or other component that requires "
302 "dereferencing; generating the type parameters is a hard "
303 "requirement for correctness.");
304 }
305
306 /// Place \p exv in memory if it is not already a memory reference. If
307 /// \p forceValueType is provided, the value is first casted to the provided
308 /// type before being stored (this is mainly intended for logicals whose value
309 /// may be `i1` but needed to be stored as Fortran logicals).
310 static fir::ExtendedValue
placeScalarValueInMemory(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv,mlir::Type storageType)311 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
312 const fir::ExtendedValue &exv,
313 mlir::Type storageType) {
314 mlir::Value valBase = fir::getBase(exv);
315 if (fir::conformsWithPassByRef(valBase.getType()))
316 return exv;
317
318 assert(!fir::hasDynamicSize(storageType) &&
319 "only expect statically sized scalars to be by value");
320
321 // Since `a` is not itself a valid referent, determine its value and
322 // create a temporary location at the beginning of the function for
323 // referencing.
324 mlir::Value val = builder.createConvert(loc, storageType, valBase);
325 mlir::Value temp = builder.createTemporary(
326 loc, storageType,
327 llvm::ArrayRef<mlir::NamedAttribute>{
328 Fortran::lower::getAdaptToByRefAttr(builder)});
329 builder.create<fir::StoreOp>(loc, val, temp);
330 return fir::substBase(exv, temp);
331 }
332
333 // Copy a copy of scalar \p exv in a new temporary.
334 static fir::ExtendedValue
createInMemoryScalarCopy(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv)335 createInMemoryScalarCopy(fir::FirOpBuilder &builder, mlir::Location loc,
336 const fir::ExtendedValue &exv) {
337 assert(exv.rank() == 0 && "input to scalar memory copy must be a scalar");
338 if (exv.getCharBox() != nullptr)
339 return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(exv);
340 if (fir::isDerivedWithLenParameters(exv))
341 TODO(loc, "copy derived type with length parameters");
342 mlir::Type type = fir::unwrapPassByRefType(fir::getBase(exv).getType());
343 fir::ExtendedValue temp = builder.createTemporary(loc, type);
344 fir::factory::genScalarAssignment(builder, loc, temp, exv);
345 return temp;
346 }
347
348 // An expression with non-zero rank is an array expression.
349 template <typename A>
isArray(const A & x)350 static bool isArray(const A &x) {
351 return x.Rank() != 0;
352 }
353
354 /// Is this a variable wrapped in parentheses?
355 template <typename A>
isParenthesizedVariable(const A &)356 static bool isParenthesizedVariable(const A &) {
357 return false;
358 }
359 template <typename T>
isParenthesizedVariable(const Fortran::evaluate::Expr<T> & expr)360 static bool isParenthesizedVariable(const Fortran::evaluate::Expr<T> &expr) {
361 using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
362 using Parentheses = Fortran::evaluate::Parentheses<T>;
363 if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
364 if (const auto *parentheses = std::get_if<Parentheses>(&expr.u))
365 return Fortran::evaluate::IsVariable(parentheses->left());
366 return false;
367 } else {
368 return std::visit([&](const auto &x) { return isParenthesizedVariable(x); },
369 expr.u);
370 }
371 }
372
373 /// Does \p expr only refer to symbols that are mapped to IR values in \p symMap
374 /// ?
allSymbolsInExprPresentInMap(const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap)375 static bool allSymbolsInExprPresentInMap(const Fortran::lower::SomeExpr &expr,
376 Fortran::lower::SymMap &symMap) {
377 for (const auto &sym : Fortran::evaluate::CollectSymbols(expr))
378 if (!symMap.lookupSymbol(sym))
379 return false;
380 return true;
381 }
382
383 /// Generate a load of a value from an address. Beware that this will lose
384 /// any dynamic type information for polymorphic entities (note that unlimited
385 /// polymorphic cannot be loaded and must not be provided here).
genLoad(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & addr)386 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
387 mlir::Location loc,
388 const fir::ExtendedValue &addr) {
389 return addr.match(
390 [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
391 [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
392 if (fir::unwrapRefType(fir::getBase(v).getType())
393 .isa<fir::RecordType>())
394 return v;
395 return builder.create<fir::LoadOp>(loc, fir::getBase(v));
396 },
397 [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
398 return genLoad(builder, loc,
399 fir::factory::genMutableBoxRead(builder, loc, box));
400 },
401 [&](const fir::BoxValue &box) -> fir::ExtendedValue {
402 if (box.isUnlimitedPolymorphic())
403 fir::emitFatalError(
404 loc, "attempting to load an unlimited polymorphic entity");
405 return genLoad(builder, loc,
406 fir::factory::readBoxValue(builder, loc, box));
407 },
408 [&](const auto &) -> fir::ExtendedValue {
409 fir::emitFatalError(
410 loc, "attempting to load whole array or procedure address");
411 });
412 }
413
414 /// Create an optional dummy argument value from entity \p exv that may be
415 /// absent. This can only be called with numerical or logical scalar \p exv.
416 /// If \p exv is considered absent according to 15.5.2.12 point 1., the returned
417 /// value is zero (or false), otherwise it is the value of \p exv.
genOptionalValue(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv,mlir::Value isPresent)418 static fir::ExtendedValue genOptionalValue(fir::FirOpBuilder &builder,
419 mlir::Location loc,
420 const fir::ExtendedValue &exv,
421 mlir::Value isPresent) {
422 mlir::Type eleType = fir::getBaseTypeOf(exv);
423 assert(exv.rank() == 0 && fir::isa_trivial(eleType) &&
424 "must be a numerical or logical scalar");
425 return builder
426 .genIfOp(loc, {eleType}, isPresent,
427 /*withElseRegion=*/true)
428 .genThen([&]() {
429 mlir::Value val = fir::getBase(genLoad(builder, loc, exv));
430 builder.create<fir::ResultOp>(loc, val);
431 })
432 .genElse([&]() {
433 mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
434 builder.create<fir::ResultOp>(loc, zero);
435 })
436 .getResults()[0];
437 }
438
439 /// Create an optional dummy argument address from entity \p exv that may be
440 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
441 /// returned value is a null pointer, otherwise it is the address of \p exv.
genOptionalAddr(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv,mlir::Value isPresent)442 static fir::ExtendedValue genOptionalAddr(fir::FirOpBuilder &builder,
443 mlir::Location loc,
444 const fir::ExtendedValue &exv,
445 mlir::Value isPresent) {
446 // If it is an exv pointer/allocatable, then it cannot be absent
447 // because it is passed to a non-pointer/non-allocatable.
448 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
449 return fir::factory::genMutableBoxRead(builder, loc, *box);
450 // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL
451 // address and can be passed directly.
452 return exv;
453 }
454
455 /// Create an optional dummy argument address from entity \p exv that may be
456 /// absent. If \p exv is considered absent according to 15.5.2.12 point 1., the
457 /// returned value is an absent fir.box, otherwise it is a fir.box describing \p
458 /// exv.
genOptionalBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv,mlir::Value isPresent)459 static fir::ExtendedValue genOptionalBox(fir::FirOpBuilder &builder,
460 mlir::Location loc,
461 const fir::ExtendedValue &exv,
462 mlir::Value isPresent) {
463 // Non allocatable/pointer optional box -> simply forward
464 if (exv.getBoxOf<fir::BoxValue>())
465 return exv;
466
467 fir::ExtendedValue newExv = exv;
468 // Optional allocatable/pointer -> Cannot be absent, but need to translate
469 // unallocated/diassociated into absent fir.box.
470 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>())
471 newExv = fir::factory::genMutableBoxRead(builder, loc, *box);
472
473 // createBox will not do create any invalid memory dereferences if exv is
474 // absent. The created fir.box will not be usable, but the SelectOp below
475 // ensures it won't be.
476 mlir::Value box = builder.createBox(loc, newExv);
477 mlir::Type boxType = box.getType();
478 auto absent = builder.create<fir::AbsentOp>(loc, boxType);
479 auto boxOrAbsent = builder.create<mlir::arith::SelectOp>(
480 loc, boxType, isPresent, box, absent);
481 return fir::BoxValue(boxOrAbsent);
482 }
483
484 /// Is this a call to an elemental procedure with at least one array argument?
485 static bool
isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef & procRef)486 isElementalProcWithArrayArgs(const Fortran::evaluate::ProcedureRef &procRef) {
487 if (procRef.IsElemental())
488 for (const std::optional<Fortran::evaluate::ActualArgument> &arg :
489 procRef.arguments())
490 if (arg && arg->Rank() != 0)
491 return true;
492 return false;
493 }
494 template <typename T>
isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &)495 static bool isElementalProcWithArrayArgs(const Fortran::evaluate::Expr<T> &) {
496 return false;
497 }
498 template <>
isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr & x)499 bool isElementalProcWithArrayArgs(const Fortran::lower::SomeExpr &x) {
500 if (const auto *procRef = std::get_if<Fortran::evaluate::ProcedureRef>(&x.u))
501 return isElementalProcWithArrayArgs(*procRef);
502 return false;
503 }
504
505 /// Some auxiliary data for processing initialization in ScalarExprLowering
506 /// below. This is currently used for generating dense attributed global
507 /// arrays.
508 struct InitializerData {
InitializerDataInitializerData509 explicit InitializerData(bool getRawVals = false) : genRawVals{getRawVals} {}
510 llvm::SmallVector<mlir::Attribute> rawVals; // initialization raw values
511 mlir::Type rawType; // Type of elements processed for rawVals vector.
512 bool genRawVals; // generate the rawVals vector if set.
513 };
514
515 /// If \p arg is the address of a function with a denoted host-association tuple
516 /// argument, then return the host-associations tuple value of the current
517 /// procedure. Otherwise, return nullptr.
518 static mlir::Value
argumentHostAssocs(Fortran::lower::AbstractConverter & converter,mlir::Value arg)519 argumentHostAssocs(Fortran::lower::AbstractConverter &converter,
520 mlir::Value arg) {
521 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) {
522 auto &builder = converter.getFirOpBuilder();
523 if (auto funcOp = builder.getNamedFunction(addr.getSymbol()))
524 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName()))
525 return converter.hostAssocTupleValue();
526 }
527 return {};
528 }
529
530 /// \p argTy must be a tuple (pair) of boxproc and integral types. Convert the
531 /// \p funcAddr argument to a boxproc value, with the host-association as
532 /// required. Call the factory function to finish creating the tuple value.
533 static mlir::Value
createBoxProcCharTuple(Fortran::lower::AbstractConverter & converter,mlir::Type argTy,mlir::Value funcAddr,mlir::Value charLen)534 createBoxProcCharTuple(Fortran::lower::AbstractConverter &converter,
535 mlir::Type argTy, mlir::Value funcAddr,
536 mlir::Value charLen) {
537 auto boxTy =
538 argTy.cast<mlir::TupleType>().getType(0).cast<fir::BoxProcType>();
539 mlir::Location loc = converter.getCurrentLocation();
540 auto &builder = converter.getFirOpBuilder();
541 auto boxProc = [&]() -> mlir::Value {
542 if (auto host = argumentHostAssocs(converter, funcAddr))
543 return builder.create<fir::EmboxProcOp>(
544 loc, boxTy, llvm::ArrayRef<mlir::Value>{funcAddr, host});
545 return builder.create<fir::EmboxProcOp>(loc, boxTy, funcAddr);
546 }();
547 return fir::factory::createCharacterProcedureTuple(builder, loc, argTy,
548 boxProc, charLen);
549 }
550
551 /// Given an optional fir.box, returns an fir.box that is the original one if
552 /// it is present and it otherwise an unallocated box.
553 /// Absent fir.box are implemented as a null pointer descriptor. Generated
554 /// code may need to unconditionally read a fir.box that can be absent.
555 /// This helper allows creating a fir.box that can be read in all cases
556 /// outside of a fir.if (isPresent) region. However, the usages of the value
557 /// read from such box should still only be done in a fir.if(isPresent).
558 static fir::ExtendedValue
absentBoxToUnallocatedBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::ExtendedValue & exv,mlir::Value isPresent)559 absentBoxToUnallocatedBox(fir::FirOpBuilder &builder, mlir::Location loc,
560 const fir::ExtendedValue &exv,
561 mlir::Value isPresent) {
562 mlir::Value box = fir::getBase(exv);
563 mlir::Type boxType = box.getType();
564 assert(boxType.isa<fir::BoxType>() && "argument must be a fir.box");
565 mlir::Value emptyBox =
566 fir::factory::createUnallocatedBox(builder, loc, boxType, llvm::None);
567 auto safeToReadBox =
568 builder.create<mlir::arith::SelectOp>(loc, isPresent, box, emptyBox);
569 return fir::substBase(exv, safeToReadBox);
570 }
571
572 // Helper to get the ultimate first symbol. This works around the fact that
573 // symbol resolution in the front end doesn't always resolve a symbol to its
574 // ultimate symbol but may leave placeholder indirections for use and host
575 // associations.
576 template <typename A>
getFirstSym(const A & obj)577 const Fortran::semantics::Symbol &getFirstSym(const A &obj) {
578 return obj.GetFirstSymbol().GetUltimate();
579 }
580
581 // Helper to get the ultimate last symbol.
582 template <typename A>
getLastSym(const A & obj)583 const Fortran::semantics::Symbol &getLastSym(const A &obj) {
584 return obj.GetLastSymbol().GetUltimate();
585 }
586
587 static bool
isIntrinsicModuleProcRef(const Fortran::evaluate::ProcedureRef & procRef)588 isIntrinsicModuleProcRef(const Fortran::evaluate::ProcedureRef &procRef) {
589 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
590 if (!symbol)
591 return false;
592 const Fortran::semantics::Symbol *module =
593 symbol->GetUltimate().owner().GetSymbol();
594 return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC) &&
595 module->name().ToString().find("omp_lib") == std::string::npos;
596 }
597
598 // A set of visitors to detect if the given expression
599 // is a TRANSPOSE call that should be lowered without using
600 // runtime TRANSPOSE implementation.
601 template <typename T>
isOptimizableTranspose(const T &)602 static bool isOptimizableTranspose(const T &) {
603 return false;
604 }
605
606 static bool
isOptimizableTranspose(const Fortran::evaluate::ProcedureRef & procRef)607 isOptimizableTranspose(const Fortran::evaluate::ProcedureRef &procRef) {
608 const Fortran::evaluate::SpecificIntrinsic *intrin =
609 procRef.proc().GetSpecificIntrinsic();
610 return optimizeTranspose && intrin && intrin->name == "transpose";
611 }
612
613 template <typename T>
614 static bool
isOptimizableTranspose(const Fortran::evaluate::FunctionRef<T> & funcRef)615 isOptimizableTranspose(const Fortran::evaluate::FunctionRef<T> &funcRef) {
616 return isOptimizableTranspose(
617 static_cast<const Fortran::evaluate::ProcedureRef &>(funcRef));
618 }
619
620 template <typename T>
isOptimizableTranspose(Fortran::evaluate::Expr<T> expr)621 static bool isOptimizableTranspose(Fortran::evaluate::Expr<T> expr) {
622 // If optimizeTranspose is not enabled, return false right away.
623 if (!optimizeTranspose)
624 return false;
625
626 return std::visit([&](const auto &e) { return isOptimizableTranspose(e); },
627 expr.u);
628 }
629
630 namespace {
631
632 /// Lowering of Fortran::evaluate::Expr<T> expressions
633 class ScalarExprLowering {
634 public:
635 using ExtValue = fir::ExtendedValue;
636
ScalarExprLowering(mlir::Location loc,Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,InitializerData * initializer=nullptr)637 explicit ScalarExprLowering(mlir::Location loc,
638 Fortran::lower::AbstractConverter &converter,
639 Fortran::lower::SymMap &symMap,
640 Fortran::lower::StatementContext &stmtCtx,
641 InitializerData *initializer = nullptr)
642 : location{loc}, converter{converter},
643 builder{converter.getFirOpBuilder()}, stmtCtx{stmtCtx}, symMap{symMap},
644 inInitializer{initializer} {}
645
genExtAddr(const Fortran::lower::SomeExpr & expr)646 ExtValue genExtAddr(const Fortran::lower::SomeExpr &expr) {
647 return gen(expr);
648 }
649
650 /// Lower `expr` to be passed as a fir.box argument. Do not create a temp
651 /// for the expr if it is a variable that can be described as a fir.box.
genBoxArg(const Fortran::lower::SomeExpr & expr)652 ExtValue genBoxArg(const Fortran::lower::SomeExpr &expr) {
653 bool saveUseBoxArg = useBoxArg;
654 useBoxArg = true;
655 ExtValue result = gen(expr);
656 useBoxArg = saveUseBoxArg;
657 return result;
658 }
659
genExtValue(const Fortran::lower::SomeExpr & expr)660 ExtValue genExtValue(const Fortran::lower::SomeExpr &expr) {
661 return genval(expr);
662 }
663
664 /// Lower an expression that is a pointer or an allocatable to a
665 /// MutableBoxValue.
666 fir::MutableBoxValue
genMutableBoxValue(const Fortran::lower::SomeExpr & expr)667 genMutableBoxValue(const Fortran::lower::SomeExpr &expr) {
668 // Pointers and allocatables can only be:
669 // - a simple designator "x"
670 // - a component designator "a%b(i,j)%x"
671 // - a function reference "foo()"
672 // - result of NULL() or NULL(MOLD) intrinsic.
673 // NULL() requires some context to be lowered, so it is not handled
674 // here and must be lowered according to the context where it appears.
675 ExtValue exv = std::visit(
676 [&](const auto &x) { return genMutableBoxValueImpl(x); }, expr.u);
677 const fir::MutableBoxValue *mutableBox =
678 exv.getBoxOf<fir::MutableBoxValue>();
679 if (!mutableBox)
680 fir::emitFatalError(getLoc(), "expr was not lowered to MutableBoxValue");
681 return *mutableBox;
682 }
683
684 template <typename T>
genMutableBoxValueImpl(const T &)685 ExtValue genMutableBoxValueImpl(const T &) {
686 // NULL() case should not be handled here.
687 fir::emitFatalError(getLoc(), "NULL() must be lowered in its context");
688 }
689
690 /// A `NULL()` in a position where a mutable box is expected has the same
691 /// semantics as an absent optional box value.
genMutableBoxValueImpl(const Fortran::evaluate::NullPointer &)692 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::NullPointer &) {
693 mlir::Location loc = getLoc();
694 auto nullConst = builder.createNullConstant(loc);
695 auto noneTy = mlir::NoneType::get(builder.getContext());
696 auto polyRefTy = fir::LLVMPointerType::get(noneTy);
697 // MutableBoxValue will dereference the box, so create a bogus temporary for
698 // the `nullptr`. The LLVM optimizer will garbage collect the temp.
699 auto temp =
700 builder.createTemporary(loc, polyRefTy, /*shape=*/mlir::ValueRange{});
701 auto nullPtr = builder.createConvert(loc, polyRefTy, nullConst);
702 builder.create<fir::StoreOp>(loc, nullPtr, temp);
703 auto nullBoxTy = builder.getRefType(fir::BoxType::get(noneTy));
704 return fir::MutableBoxValue(builder.createConvert(loc, nullBoxTy, temp),
705 /*lenParameters=*/mlir::ValueRange{},
706 /*mutableProperties=*/{});
707 }
708
709 template <typename T>
710 ExtValue
genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> & funRef)711 genMutableBoxValueImpl(const Fortran::evaluate::FunctionRef<T> &funRef) {
712 return genRawProcedureRef(funRef, converter.genType(toEvExpr(funRef)));
713 }
714
715 template <typename T>
716 ExtValue
genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> & designator)717 genMutableBoxValueImpl(const Fortran::evaluate::Designator<T> &designator) {
718 return std::visit(
719 Fortran::common::visitors{
720 [&](const Fortran::evaluate::SymbolRef &sym) -> ExtValue {
721 return symMap.lookupSymbol(*sym).toExtendedValue();
722 },
723 [&](const Fortran::evaluate::Component &comp) -> ExtValue {
724 return genComponent(comp);
725 },
726 [&](const auto &) -> ExtValue {
727 fir::emitFatalError(getLoc(),
728 "not an allocatable or pointer designator");
729 }},
730 designator.u);
731 }
732
733 template <typename T>
genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> & expr)734 ExtValue genMutableBoxValueImpl(const Fortran::evaluate::Expr<T> &expr) {
735 return std::visit([&](const auto &x) { return genMutableBoxValueImpl(x); },
736 expr.u);
737 }
738
getLoc()739 mlir::Location getLoc() { return location; }
740
741 template <typename A>
genunbox(const A & expr)742 mlir::Value genunbox(const A &expr) {
743 ExtValue e = genval(expr);
744 if (const fir::UnboxedValue *r = e.getUnboxed())
745 return *r;
746 fir::emitFatalError(getLoc(), "unboxed expression expected");
747 }
748
749 /// Generate an integral constant of `value`
750 template <int KIND>
genIntegerConstant(mlir::MLIRContext * context,std::int64_t value)751 mlir::Value genIntegerConstant(mlir::MLIRContext *context,
752 std::int64_t value) {
753 mlir::Type type =
754 converter.genType(Fortran::common::TypeCategory::Integer, KIND);
755 return builder.createIntegerConstant(getLoc(), type, value);
756 }
757
758 /// Generate a logical/boolean constant of `value`
genBoolConstant(bool value)759 mlir::Value genBoolConstant(bool value) {
760 return builder.createBool(getLoc(), value);
761 }
762
763 /// Generate a real constant with a value `value`.
764 template <int KIND>
genRealConstant(mlir::MLIRContext * context,const llvm::APFloat & value)765 mlir::Value genRealConstant(mlir::MLIRContext *context,
766 const llvm::APFloat &value) {
767 mlir::Type fltTy = Fortran::lower::convertReal(context, KIND);
768 return builder.createRealConstant(getLoc(), fltTy, value);
769 }
770
getSomeKindInteger()771 mlir::Type getSomeKindInteger() { return builder.getIndexType(); }
772
getFunction(llvm::StringRef name,mlir::FunctionType funTy)773 mlir::func::FuncOp getFunction(llvm::StringRef name,
774 mlir::FunctionType funTy) {
775 if (mlir::func::FuncOp func = builder.getNamedFunction(name))
776 return func;
777 return builder.createFunction(getLoc(), name, funTy);
778 }
779
780 template <typename OpTy>
createCompareOp(mlir::arith::CmpIPredicate pred,const ExtValue & left,const ExtValue & right)781 mlir::Value createCompareOp(mlir::arith::CmpIPredicate pred,
782 const ExtValue &left, const ExtValue &right) {
783 if (const fir::UnboxedValue *lhs = left.getUnboxed())
784 if (const fir::UnboxedValue *rhs = right.getUnboxed())
785 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
786 fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
787 }
788 template <typename OpTy, typename A>
createCompareOp(const A & ex,mlir::arith::CmpIPredicate pred)789 mlir::Value createCompareOp(const A &ex, mlir::arith::CmpIPredicate pred) {
790 ExtValue left = genval(ex.left());
791 return createCompareOp<OpTy>(pred, left, genval(ex.right()));
792 }
793
794 template <typename OpTy>
createFltCmpOp(mlir::arith::CmpFPredicate pred,const ExtValue & left,const ExtValue & right)795 mlir::Value createFltCmpOp(mlir::arith::CmpFPredicate pred,
796 const ExtValue &left, const ExtValue &right) {
797 if (const fir::UnboxedValue *lhs = left.getUnboxed())
798 if (const fir::UnboxedValue *rhs = right.getUnboxed())
799 return builder.create<OpTy>(getLoc(), pred, *lhs, *rhs);
800 fir::emitFatalError(getLoc(), "array compare should be handled in genarr");
801 }
802 template <typename OpTy, typename A>
createFltCmpOp(const A & ex,mlir::arith::CmpFPredicate pred)803 mlir::Value createFltCmpOp(const A &ex, mlir::arith::CmpFPredicate pred) {
804 ExtValue left = genval(ex.left());
805 return createFltCmpOp<OpTy>(pred, left, genval(ex.right()));
806 }
807
808 /// Create a call to the runtime to compare two CHARACTER values.
809 /// Precondition: This assumes that the two values have `fir.boxchar` type.
createCharCompare(mlir::arith::CmpIPredicate pred,const ExtValue & left,const ExtValue & right)810 mlir::Value createCharCompare(mlir::arith::CmpIPredicate pred,
811 const ExtValue &left, const ExtValue &right) {
812 return fir::runtime::genCharCompare(builder, getLoc(), pred, left, right);
813 }
814
815 template <typename A>
createCharCompare(const A & ex,mlir::arith::CmpIPredicate pred)816 mlir::Value createCharCompare(const A &ex, mlir::arith::CmpIPredicate pred) {
817 ExtValue left = genval(ex.left());
818 return createCharCompare(pred, left, genval(ex.right()));
819 }
820
821 /// Returns a reference to a symbol or its box/boxChar descriptor if it has
822 /// one.
gen(Fortran::semantics::SymbolRef sym)823 ExtValue gen(Fortran::semantics::SymbolRef sym) {
824 if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
825 return val.match(
826 [&](const Fortran::lower::SymbolBox::PointerOrAllocatable &boxAddr) {
827 return fir::factory::genMutableBoxRead(builder, getLoc(), boxAddr);
828 },
829 [&val](auto &) { return val.toExtendedValue(); });
830 LLVM_DEBUG(llvm::dbgs()
831 << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
832 fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
833 }
834
genLoad(const ExtValue & exv)835 ExtValue genLoad(const ExtValue &exv) {
836 return ::genLoad(builder, getLoc(), exv);
837 }
838
genval(Fortran::semantics::SymbolRef sym)839 ExtValue genval(Fortran::semantics::SymbolRef sym) {
840 mlir::Location loc = getLoc();
841 ExtValue var = gen(sym);
842 if (const fir::UnboxedValue *s = var.getUnboxed())
843 if (fir::isa_ref_type(s->getType())) {
844 // A function with multiple entry points returning different types
845 // tags all result variables with one of the largest types to allow
846 // them to share the same storage. A reference to a result variable
847 // of one of the other types requires conversion to the actual type.
848 fir::UnboxedValue addr = *s;
849 if (Fortran::semantics::IsFunctionResult(sym)) {
850 mlir::Type resultType = converter.genType(*sym);
851 if (addr.getType() != resultType)
852 addr = builder.createConvert(loc, builder.getRefType(resultType),
853 addr);
854 }
855 return genLoad(addr);
856 }
857 return var;
858 }
859
genval(const Fortran::evaluate::BOZLiteralConstant &)860 ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
861 TODO(getLoc(), "BOZ");
862 }
863
864 /// Return indirection to function designated in ProcedureDesignator.
865 /// The type of the function indirection is not guaranteed to match the one
866 /// of the ProcedureDesignator due to Fortran implicit typing rules.
genval(const Fortran::evaluate::ProcedureDesignator & proc)867 ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
868 mlir::Location loc = getLoc();
869 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
870 proc.GetSpecificIntrinsic()) {
871 mlir::FunctionType signature =
872 Fortran::lower::translateSignature(proc, converter);
873 // Intrinsic lowering is based on the generic name, so retrieve it here in
874 // case it is different from the specific name. The type of the specific
875 // intrinsic is retained in the signature.
876 std::string genericName =
877 converter.getFoldingContext().intrinsics().GetGenericIntrinsicName(
878 intrinsic->name);
879 mlir::SymbolRefAttr symbolRefAttr =
880 Fortran::lower::getUnrestrictedIntrinsicSymbolRefAttr(
881 builder, loc, genericName, signature);
882 mlir::Value funcPtr =
883 builder.create<fir::AddrOfOp>(loc, signature, symbolRefAttr);
884 return funcPtr;
885 }
886 const Fortran::semantics::Symbol *symbol = proc.GetSymbol();
887 assert(symbol && "expected symbol in ProcedureDesignator");
888 mlir::Value funcPtr;
889 mlir::Value funcPtrResultLength;
890 if (Fortran::semantics::IsDummy(*symbol)) {
891 Fortran::lower::SymbolBox val = symMap.lookupSymbol(*symbol);
892 assert(val && "Dummy procedure not in symbol map");
893 funcPtr = val.getAddr();
894 if (fir::isCharacterProcedureTuple(funcPtr.getType(),
895 /*acceptRawFunc=*/false))
896 std::tie(funcPtr, funcPtrResultLength) =
897 fir::factory::extractCharacterProcedureTuple(builder, loc, funcPtr);
898 } else {
899 std::string name = converter.mangleName(*symbol);
900 mlir::func::FuncOp func =
901 Fortran::lower::getOrDeclareFunction(name, proc, converter);
902 // Abstract results require later rewrite of the function type.
903 // This currently does not happen inside GloalOps, causing LLVM
904 // IR verification failure. This helper is only here to catch these
905 // cases and emit a TODOs for now.
906 if (inInitializer && fir::hasAbstractResult(func.getFunctionType()))
907 TODO(converter.genLocation(symbol->name()),
908 "static description of non trivial procedure bindings");
909 funcPtr = builder.create<fir::AddrOfOp>(loc, func.getFunctionType(),
910 builder.getSymbolRefAttr(name));
911 }
912 if (Fortran::lower::mustPassLengthWithDummyProcedure(proc, converter)) {
913 // The result length, if available here, must be propagated along the
914 // procedure address so that call sites where the result length is assumed
915 // can retrieve the length.
916 Fortran::evaluate::DynamicType resultType = proc.GetType().value();
917 if (const auto &lengthExpr = resultType.GetCharLength()) {
918 // The length expression may refer to dummy argument symbols that are
919 // meaningless without any actual arguments. Leave the length as
920 // unknown in that case, it be resolved on the call site
921 // with the actual arguments.
922 if (allSymbolsInExprPresentInMap(toEvExpr(*lengthExpr), symMap)) {
923 mlir::Value rawLen = fir::getBase(genval(*lengthExpr));
924 // F2018 7.4.4.2 point 5.
925 funcPtrResultLength =
926 fir::factory::genMaxWithZero(builder, getLoc(), rawLen);
927 }
928 }
929 if (!funcPtrResultLength)
930 funcPtrResultLength = builder.createIntegerConstant(
931 loc, builder.getCharacterLengthType(), -1);
932 return fir::CharBoxValue{funcPtr, funcPtrResultLength};
933 }
934 return funcPtr;
935 }
genval(const Fortran::evaluate::NullPointer &)936 ExtValue genval(const Fortran::evaluate::NullPointer &) {
937 return builder.createNullConstant(getLoc());
938 }
939
940 static bool
isDerivedTypeWithLenParameters(const Fortran::semantics::Symbol & sym)941 isDerivedTypeWithLenParameters(const Fortran::semantics::Symbol &sym) {
942 if (const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType())
943 if (const Fortran::semantics::DerivedTypeSpec *derived =
944 declTy->AsDerived())
945 return Fortran::semantics::CountLenParameters(*derived) > 0;
946 return false;
947 }
948
isBuiltinCPtr(const Fortran::semantics::Symbol & sym)949 static bool isBuiltinCPtr(const Fortran::semantics::Symbol &sym) {
950 if (const Fortran::semantics::DeclTypeSpec *declType = sym.GetType())
951 if (const Fortran::semantics::DerivedTypeSpec *derived =
952 declType->AsDerived())
953 return Fortran::semantics::IsIsoCType(derived);
954 return false;
955 }
956
957 /// Lower structure constructor without a temporary. This can be used in
958 /// fir::GloablOp, and assumes that the structure component is a constant.
genStructComponentInInitializer(const Fortran::evaluate::StructureConstructor & ctor)959 ExtValue genStructComponentInInitializer(
960 const Fortran::evaluate::StructureConstructor &ctor) {
961 mlir::Location loc = getLoc();
962 mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor));
963 auto recTy = ty.cast<fir::RecordType>();
964 auto fieldTy = fir::FieldType::get(ty.getContext());
965 mlir::Value res = builder.create<fir::UndefOp>(loc, recTy);
966
967 for (const auto &[sym, expr] : ctor.values()) {
968 // Parent components need more work because they do not appear in the
969 // fir.rec type.
970 if (sym->test(Fortran::semantics::Symbol::Flag::ParentComp))
971 TODO(loc, "parent component in structure constructor");
972
973 llvm::StringRef name = toStringRef(sym->name());
974 mlir::Type componentTy = recTy.getType(name);
975 // FIXME: type parameters must come from the derived-type-spec
976 auto field = builder.create<fir::FieldIndexOp>(
977 loc, fieldTy, name, ty,
978 /*typeParams=*/mlir::ValueRange{} /*TODO*/);
979
980 if (Fortran::semantics::IsAllocatable(sym))
981 TODO(loc, "allocatable component in structure constructor");
982
983 if (Fortran::semantics::IsPointer(sym)) {
984 mlir::Value initialTarget = Fortran::lower::genInitialDataTarget(
985 converter, loc, componentTy, expr.value());
986 res = builder.create<fir::InsertValueOp>(
987 loc, recTy, res, initialTarget,
988 builder.getArrayAttr(field.getAttributes()));
989 continue;
990 }
991
992 if (isDerivedTypeWithLenParameters(sym))
993 TODO(loc, "component with length parameters in structure constructor");
994
995 if (isBuiltinCPtr(sym)) {
996 // Builtin c_ptr and c_funptr have special handling because initial
997 // value are handled for them as an extension.
998 mlir::Value addr = fir::getBase(Fortran::lower::genExtAddrInInitializer(
999 converter, loc, expr.value()));
1000 if (addr.getType() == componentTy) {
1001 // Do nothing. The Ev::Expr was returned as a value that can be
1002 // inserted directly to the component without an intermediary.
1003 } else {
1004 // The Ev::Expr returned is an initializer that is a pointer (e.g.,
1005 // null) that must be inserted into an intermediate cptr record
1006 // value's address field, which ought to be an intptr_t on the target.
1007 assert((fir::isa_ref_type(addr.getType()) ||
1008 addr.getType().isa<mlir::FunctionType>()) &&
1009 "expect reference type for address field");
1010 assert(fir::isa_derived(componentTy) &&
1011 "expect C_PTR, C_FUNPTR to be a record");
1012 auto cPtrRecTy = componentTy.cast<fir::RecordType>();
1013 llvm::StringRef addrFieldName =
1014 Fortran::lower::builtin::cptrFieldName;
1015 mlir::Type addrFieldTy = cPtrRecTy.getType(addrFieldName);
1016 auto addrField = builder.create<fir::FieldIndexOp>(
1017 loc, fieldTy, addrFieldName, componentTy,
1018 /*typeParams=*/mlir::ValueRange{});
1019 mlir::Value castAddr = builder.createConvert(loc, addrFieldTy, addr);
1020 auto undef = builder.create<fir::UndefOp>(loc, componentTy);
1021 addr = builder.create<fir::InsertValueOp>(
1022 loc, componentTy, undef, castAddr,
1023 builder.getArrayAttr(addrField.getAttributes()));
1024 }
1025 res = builder.create<fir::InsertValueOp>(
1026 loc, recTy, res, addr, builder.getArrayAttr(field.getAttributes()));
1027 continue;
1028 }
1029
1030 mlir::Value val = fir::getBase(genval(expr.value()));
1031 assert(!fir::isa_ref_type(val.getType()) && "expecting a constant value");
1032 mlir::Value castVal = builder.createConvert(loc, componentTy, val);
1033 res = builder.create<fir::InsertValueOp>(
1034 loc, recTy, res, castVal,
1035 builder.getArrayAttr(field.getAttributes()));
1036 }
1037 return res;
1038 }
1039
1040 /// A structure constructor is lowered two ways. In an initializer context,
1041 /// the entire structure must be constant, so the aggregate value is
1042 /// constructed inline. This allows it to be the body of a GlobalOp.
1043 /// Otherwise, the structure constructor is in an expression. In that case, a
1044 /// temporary object is constructed in the stack frame of the procedure.
genval(const Fortran::evaluate::StructureConstructor & ctor)1045 ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
1046 if (inInitializer)
1047 return genStructComponentInInitializer(ctor);
1048 mlir::Location loc = getLoc();
1049 mlir::Type ty = translateSomeExprToFIRType(converter, toEvExpr(ctor));
1050 auto recTy = ty.cast<fir::RecordType>();
1051 auto fieldTy = fir::FieldType::get(ty.getContext());
1052 mlir::Value res = builder.createTemporary(loc, recTy);
1053
1054 for (const auto &value : ctor.values()) {
1055 const Fortran::semantics::Symbol &sym = *value.first;
1056 const Fortran::lower::SomeExpr &expr = value.second.value();
1057 // Parent components need more work because they do not appear in the
1058 // fir.rec type.
1059 if (sym.test(Fortran::semantics::Symbol::Flag::ParentComp))
1060 TODO(loc, "parent component in structure constructor");
1061
1062 if (isDerivedTypeWithLenParameters(sym))
1063 TODO(loc, "component with length parameters in structure constructor");
1064
1065 llvm::StringRef name = toStringRef(sym.name());
1066 // FIXME: type parameters must come from the derived-type-spec
1067 mlir::Value field = builder.create<fir::FieldIndexOp>(
1068 loc, fieldTy, name, ty,
1069 /*typeParams=*/mlir::ValueRange{} /*TODO*/);
1070 mlir::Type coorTy = builder.getRefType(recTy.getType(name));
1071 auto coor = builder.create<fir::CoordinateOp>(loc, coorTy,
1072 fir::getBase(res), field);
1073 ExtValue to = fir::factory::componentToExtendedValue(builder, loc, coor);
1074 to.match(
1075 [&](const fir::UnboxedValue &toPtr) {
1076 ExtValue value = genval(expr);
1077 fir::factory::genScalarAssignment(builder, loc, to, value);
1078 },
1079 [&](const fir::CharBoxValue &) {
1080 ExtValue value = genval(expr);
1081 fir::factory::genScalarAssignment(builder, loc, to, value);
1082 },
1083 [&](const fir::ArrayBoxValue &) {
1084 Fortran::lower::createSomeArrayAssignment(converter, to, expr,
1085 symMap, stmtCtx);
1086 },
1087 [&](const fir::CharArrayBoxValue &) {
1088 Fortran::lower::createSomeArrayAssignment(converter, to, expr,
1089 symMap, stmtCtx);
1090 },
1091 [&](const fir::BoxValue &toBox) {
1092 fir::emitFatalError(loc, "derived type components must not be "
1093 "represented by fir::BoxValue");
1094 },
1095 [&](const fir::MutableBoxValue &toBox) {
1096 if (toBox.isPointer()) {
1097 Fortran::lower::associateMutableBox(
1098 converter, loc, toBox, expr, /*lbounds=*/llvm::None, stmtCtx);
1099 return;
1100 }
1101 // For allocatable components, a deep copy is needed.
1102 TODO(loc, "allocatable components in derived type assignment");
1103 },
1104 [&](const fir::ProcBoxValue &toBox) {
1105 TODO(loc, "procedure pointer component in derived type assignment");
1106 });
1107 }
1108 return res;
1109 }
1110
1111 /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
genval(const Fortran::evaluate::ImpliedDoIndex & var)1112 ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
1113 return converter.impliedDoBinding(toStringRef(var.name));
1114 }
1115
genval(const Fortran::evaluate::DescriptorInquiry & desc)1116 ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
1117 ExtValue exv = desc.base().IsSymbol() ? gen(getLastSym(desc.base()))
1118 : gen(desc.base().GetComponent());
1119 mlir::IndexType idxTy = builder.getIndexType();
1120 mlir::Location loc = getLoc();
1121 auto castResult = [&](mlir::Value v) {
1122 using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
1123 return builder.createConvert(
1124 loc, converter.genType(ResTy::category, ResTy::kind), v);
1125 };
1126 switch (desc.field()) {
1127 case Fortran::evaluate::DescriptorInquiry::Field::Len:
1128 return castResult(fir::factory::readCharLen(builder, loc, exv));
1129 case Fortran::evaluate::DescriptorInquiry::Field::LowerBound:
1130 return castResult(fir::factory::readLowerBound(
1131 builder, loc, exv, desc.dimension(),
1132 builder.createIntegerConstant(loc, idxTy, 1)));
1133 case Fortran::evaluate::DescriptorInquiry::Field::Extent:
1134 return castResult(
1135 fir::factory::readExtent(builder, loc, exv, desc.dimension()));
1136 case Fortran::evaluate::DescriptorInquiry::Field::Rank:
1137 TODO(loc, "rank inquiry on assumed rank");
1138 case Fortran::evaluate::DescriptorInquiry::Field::Stride:
1139 // So far the front end does not generate this inquiry.
1140 TODO(loc, "stride inquiry");
1141 }
1142 llvm_unreachable("unknown descriptor inquiry");
1143 }
1144
genval(const Fortran::evaluate::TypeParamInquiry &)1145 ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
1146 TODO(getLoc(), "type parameter inquiry");
1147 }
1148
extractComplexPart(mlir::Value cplx,bool isImagPart)1149 mlir::Value extractComplexPart(mlir::Value cplx, bool isImagPart) {
1150 return fir::factory::Complex{builder, getLoc()}.extractComplexPart(
1151 cplx, isImagPart);
1152 }
1153
1154 template <int KIND>
genval(const Fortran::evaluate::ComplexComponent<KIND> & part)1155 ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
1156 return extractComplexPart(genunbox(part.left()), part.isImaginaryPart);
1157 }
1158
1159 template <int KIND>
genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,KIND>> & op)1160 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1161 Fortran::common::TypeCategory::Integer, KIND>> &op) {
1162 mlir::Value input = genunbox(op.left());
1163 // Like LLVM, integer negation is the binary op "0 - value"
1164 mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
1165 return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
1166 }
1167 template <int KIND>
genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Real,KIND>> & op)1168 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1169 Fortran::common::TypeCategory::Real, KIND>> &op) {
1170 return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
1171 }
1172 template <int KIND>
genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex,KIND>> & op)1173 ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
1174 Fortran::common::TypeCategory::Complex, KIND>> &op) {
1175 return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
1176 }
1177
1178 template <typename OpTy>
createBinaryOp(const ExtValue & left,const ExtValue & right)1179 mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
1180 assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
1181 mlir::Value lhs = fir::getBase(left);
1182 mlir::Value rhs = fir::getBase(right);
1183 assert(lhs.getType() == rhs.getType() && "types must be the same");
1184 return builder.create<OpTy>(getLoc(), lhs, rhs);
1185 }
1186
1187 template <typename OpTy, typename A>
createBinaryOp(const A & ex)1188 mlir::Value createBinaryOp(const A &ex) {
1189 ExtValue left = genval(ex.left());
1190 return createBinaryOp<OpTy>(left, genval(ex.right()));
1191 }
1192
1193 #undef GENBIN
1194 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \
1195 template <int KIND> \
1196 ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
1197 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
1198 return createBinaryOp<GenBinFirOp>(x); \
1199 }
1200
GENBIN(Add,Integer,mlir::arith::AddIOp)1201 GENBIN(Add, Integer, mlir::arith::AddIOp)
1202 GENBIN(Add, Real, mlir::arith::AddFOp)
1203 GENBIN(Add, Complex, fir::AddcOp)
1204 GENBIN(Subtract, Integer, mlir::arith::SubIOp)
1205 GENBIN(Subtract, Real, mlir::arith::SubFOp)
1206 GENBIN(Subtract, Complex, fir::SubcOp)
1207 GENBIN(Multiply, Integer, mlir::arith::MulIOp)
1208 GENBIN(Multiply, Real, mlir::arith::MulFOp)
1209 GENBIN(Multiply, Complex, fir::MulcOp)
1210 GENBIN(Divide, Integer, mlir::arith::DivSIOp)
1211 GENBIN(Divide, Real, mlir::arith::DivFOp)
1212 GENBIN(Divide, Complex, fir::DivcOp)
1213
1214 template <Fortran::common::TypeCategory TC, int KIND>
1215 ExtValue genval(
1216 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
1217 mlir::Type ty = converter.genType(TC, KIND);
1218 mlir::Value lhs = genunbox(op.left());
1219 mlir::Value rhs = genunbox(op.right());
1220 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
1221 }
1222
1223 template <Fortran::common::TypeCategory TC, int KIND>
genval(const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC,KIND>> & op)1224 ExtValue genval(
1225 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
1226 &op) {
1227 mlir::Type ty = converter.genType(TC, KIND);
1228 mlir::Value lhs = genunbox(op.left());
1229 mlir::Value rhs = genunbox(op.right());
1230 return Fortran::lower::genPow(builder, getLoc(), ty, lhs, rhs);
1231 }
1232
1233 template <int KIND>
genval(const Fortran::evaluate::ComplexConstructor<KIND> & op)1234 ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
1235 mlir::Value realPartValue = genunbox(op.left());
1236 return fir::factory::Complex{builder, getLoc()}.createComplex(
1237 KIND, realPartValue, genunbox(op.right()));
1238 }
1239
1240 template <int KIND>
genval(const Fortran::evaluate::Concat<KIND> & op)1241 ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
1242 ExtValue lhs = genval(op.left());
1243 ExtValue rhs = genval(op.right());
1244 const fir::CharBoxValue *lhsChar = lhs.getCharBox();
1245 const fir::CharBoxValue *rhsChar = rhs.getCharBox();
1246 if (lhsChar && rhsChar)
1247 return fir::factory::CharacterExprHelper{builder, getLoc()}
1248 .createConcatenate(*lhsChar, *rhsChar);
1249 TODO(getLoc(), "character array concatenate");
1250 }
1251
1252 /// MIN and MAX operations
1253 template <Fortran::common::TypeCategory TC, int KIND>
1254 ExtValue
genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC,KIND>> & op)1255 genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
1256 &op) {
1257 mlir::Value lhs = genunbox(op.left());
1258 mlir::Value rhs = genunbox(op.right());
1259 switch (op.ordering) {
1260 case Fortran::evaluate::Ordering::Greater:
1261 return Fortran::lower::genMax(builder, getLoc(),
1262 llvm::ArrayRef<mlir::Value>{lhs, rhs});
1263 case Fortran::evaluate::Ordering::Less:
1264 return Fortran::lower::genMin(builder, getLoc(),
1265 llvm::ArrayRef<mlir::Value>{lhs, rhs});
1266 case Fortran::evaluate::Ordering::Equal:
1267 llvm_unreachable("Equal is not a valid ordering in this context");
1268 }
1269 llvm_unreachable("unknown ordering");
1270 }
1271
1272 // Change the dynamic length information without actually changing the
1273 // underlying character storage.
1274 fir::ExtendedValue
replaceScalarCharacterLength(const fir::ExtendedValue & scalarChar,mlir::Value newLenValue)1275 replaceScalarCharacterLength(const fir::ExtendedValue &scalarChar,
1276 mlir::Value newLenValue) {
1277 mlir::Location loc = getLoc();
1278 const fir::CharBoxValue *charBox = scalarChar.getCharBox();
1279 if (!charBox)
1280 fir::emitFatalError(loc, "expected scalar character");
1281 mlir::Value charAddr = charBox->getAddr();
1282 auto charType =
1283 fir::unwrapPassByRefType(charAddr.getType()).cast<fir::CharacterType>();
1284 if (charType.hasConstantLen()) {
1285 // Erase previous constant length from the base type.
1286 fir::CharacterType::LenType newLen = fir::CharacterType::unknownLen();
1287 mlir::Type newCharTy = fir::CharacterType::get(
1288 builder.getContext(), charType.getFKind(), newLen);
1289 mlir::Type newType = fir::ReferenceType::get(newCharTy);
1290 charAddr = builder.createConvert(loc, newType, charAddr);
1291 return fir::CharBoxValue{charAddr, newLenValue};
1292 }
1293 return fir::CharBoxValue{charAddr, newLenValue};
1294 }
1295
1296 template <int KIND>
genval(const Fortran::evaluate::SetLength<KIND> & x)1297 ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
1298 mlir::Value newLenValue = genunbox(x.right());
1299 fir::ExtendedValue lhs = gen(x.left());
1300 return replaceScalarCharacterLength(lhs, newLenValue);
1301 }
1302
1303 template <int KIND>
genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,KIND>> & op)1304 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1305 Fortran::common::TypeCategory::Integer, KIND>> &op) {
1306 return createCompareOp<mlir::arith::CmpIOp>(op,
1307 translateRelational(op.opr));
1308 }
1309 template <int KIND>
genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Real,KIND>> & op)1310 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1311 Fortran::common::TypeCategory::Real, KIND>> &op) {
1312 return createFltCmpOp<mlir::arith::CmpFOp>(
1313 op, translateFloatRelational(op.opr));
1314 }
1315 template <int KIND>
genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex,KIND>> & op)1316 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1317 Fortran::common::TypeCategory::Complex, KIND>> &op) {
1318 return createFltCmpOp<fir::CmpcOp>(op, translateFloatRelational(op.opr));
1319 }
1320 template <int KIND>
genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Character,KIND>> & op)1321 ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
1322 Fortran::common::TypeCategory::Character, KIND>> &op) {
1323 return createCharCompare(op, translateRelational(op.opr));
1324 }
1325
1326 ExtValue
genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> & op)1327 genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
1328 return std::visit([&](const auto &x) { return genval(x); }, op.u);
1329 }
1330
1331 template <Fortran::common::TypeCategory TC1, int KIND,
1332 Fortran::common::TypeCategory TC2>
1333 ExtValue
genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1,KIND>,TC2> & convert)1334 genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
1335 TC2> &convert) {
1336 mlir::Type ty = converter.genType(TC1, KIND);
1337 auto fromExpr = genval(convert.left());
1338 auto loc = getLoc();
1339 return fromExpr.match(
1340 [&](const fir::CharBoxValue &boxchar) -> ExtValue {
1341 if constexpr (TC1 == Fortran::common::TypeCategory::Character &&
1342 TC2 == TC1) {
1343 // Use char_convert. Each code point is translated from a
1344 // narrower/wider encoding to the target encoding. For example, 'A'
1345 // may be translated from 0x41 : i8 to 0x0041 : i16. The symbol
1346 // for euro (0x20AC : i16) may be translated from a wide character
1347 // to "0xE2 0x82 0xAC" : UTF-8.
1348 mlir::Value bufferSize = boxchar.getLen();
1349 auto kindMap = builder.getKindMap();
1350 mlir::Value boxCharAddr = boxchar.getAddr();
1351 auto fromTy = boxCharAddr.getType();
1352 if (auto charTy = fromTy.dyn_cast<fir::CharacterType>()) {
1353 // boxchar is a value, not a variable. Turn it into a temporary.
1354 // As a value, it ought to have a constant LEN value.
1355 assert(charTy.hasConstantLen() && "must have constant length");
1356 mlir::Value tmp = builder.createTemporary(loc, charTy);
1357 builder.create<fir::StoreOp>(loc, boxCharAddr, tmp);
1358 boxCharAddr = tmp;
1359 }
1360 auto fromBits =
1361 kindMap.getCharacterBitsize(fir::unwrapRefType(fromTy)
1362 .cast<fir::CharacterType>()
1363 .getFKind());
1364 auto toBits = kindMap.getCharacterBitsize(
1365 ty.cast<fir::CharacterType>().getFKind());
1366 if (toBits < fromBits) {
1367 // Scale by relative ratio to give a buffer of the same length.
1368 auto ratio = builder.createIntegerConstant(
1369 loc, bufferSize.getType(), fromBits / toBits);
1370 bufferSize =
1371 builder.create<mlir::arith::MulIOp>(loc, bufferSize, ratio);
1372 }
1373 auto dest = builder.create<fir::AllocaOp>(
1374 loc, ty, mlir::ValueRange{bufferSize});
1375 builder.create<fir::CharConvertOp>(loc, boxCharAddr,
1376 boxchar.getLen(), dest);
1377 return fir::CharBoxValue{dest, boxchar.getLen()};
1378 } else {
1379 fir::emitFatalError(
1380 loc, "unsupported evaluate::Convert between CHARACTER type "
1381 "category and non-CHARACTER category");
1382 }
1383 },
1384 [&](const fir::UnboxedValue &value) -> ExtValue {
1385 return builder.convertWithSemantics(loc, ty, value);
1386 },
1387 [&](auto &) -> ExtValue {
1388 fir::emitFatalError(loc, "unsupported evaluate::Convert");
1389 });
1390 }
1391
1392 template <typename A>
genval(const Fortran::evaluate::Parentheses<A> & op)1393 ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
1394 ExtValue input = genval(op.left());
1395 mlir::Value base = fir::getBase(input);
1396 mlir::Value newBase =
1397 builder.create<fir::NoReassocOp>(getLoc(), base.getType(), base);
1398 return fir::substBase(input, newBase);
1399 }
1400
1401 template <int KIND>
genval(const Fortran::evaluate::Not<KIND> & op)1402 ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
1403 mlir::Value logical = genunbox(op.left());
1404 mlir::Value one = genBoolConstant(true);
1405 mlir::Value val =
1406 builder.createConvert(getLoc(), builder.getI1Type(), logical);
1407 return builder.create<mlir::arith::XOrIOp>(getLoc(), val, one);
1408 }
1409
1410 template <int KIND>
genval(const Fortran::evaluate::LogicalOperation<KIND> & op)1411 ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
1412 mlir::IntegerType i1Type = builder.getI1Type();
1413 mlir::Value slhs = genunbox(op.left());
1414 mlir::Value srhs = genunbox(op.right());
1415 mlir::Value lhs = builder.createConvert(getLoc(), i1Type, slhs);
1416 mlir::Value rhs = builder.createConvert(getLoc(), i1Type, srhs);
1417 switch (op.logicalOperator) {
1418 case Fortran::evaluate::LogicalOperator::And:
1419 return createBinaryOp<mlir::arith::AndIOp>(lhs, rhs);
1420 case Fortran::evaluate::LogicalOperator::Or:
1421 return createBinaryOp<mlir::arith::OrIOp>(lhs, rhs);
1422 case Fortran::evaluate::LogicalOperator::Eqv:
1423 return createCompareOp<mlir::arith::CmpIOp>(
1424 mlir::arith::CmpIPredicate::eq, lhs, rhs);
1425 case Fortran::evaluate::LogicalOperator::Neqv:
1426 return createCompareOp<mlir::arith::CmpIOp>(
1427 mlir::arith::CmpIPredicate::ne, lhs, rhs);
1428 case Fortran::evaluate::LogicalOperator::Not:
1429 // lib/evaluate expression for .NOT. is Fortran::evaluate::Not<KIND>.
1430 llvm_unreachable(".NOT. is not a binary operator");
1431 }
1432 llvm_unreachable("unhandled logical operation");
1433 }
1434
1435 /// Convert a scalar literal constant to IR.
1436 template <Fortran::common::TypeCategory TC, int KIND>
genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC,KIND>> & value)1437 ExtValue genScalarLit(
1438 const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
1439 &value) {
1440 if constexpr (TC == Fortran::common::TypeCategory::Integer) {
1441 return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
1442 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
1443 return genBoolConstant(value.IsTrue());
1444 } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
1445 std::string str = value.DumpHexadecimal();
1446 if constexpr (KIND == 2) {
1447 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEhalf(), str);
1448 return genRealConstant<KIND>(builder.getContext(), floatVal);
1449 } else if constexpr (KIND == 3) {
1450 auto floatVal = consAPFloat(llvm::APFloatBase::BFloat(), str);
1451 return genRealConstant<KIND>(builder.getContext(), floatVal);
1452 } else if constexpr (KIND == 4) {
1453 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEsingle(), str);
1454 return genRealConstant<KIND>(builder.getContext(), floatVal);
1455 } else if constexpr (KIND == 10) {
1456 auto floatVal =
1457 consAPFloat(llvm::APFloatBase::x87DoubleExtended(), str);
1458 return genRealConstant<KIND>(builder.getContext(), floatVal);
1459 } else if constexpr (KIND == 16) {
1460 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEquad(), str);
1461 return genRealConstant<KIND>(builder.getContext(), floatVal);
1462 } else {
1463 // convert everything else to double
1464 auto floatVal = consAPFloat(llvm::APFloatBase::IEEEdouble(), str);
1465 return genRealConstant<KIND>(builder.getContext(), floatVal);
1466 }
1467 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
1468 using TR =
1469 Fortran::evaluate::Type<Fortran::common::TypeCategory::Real, KIND>;
1470 Fortran::evaluate::ComplexConstructor<KIND> ctor(
1471 Fortran::evaluate::Expr<TR>{
1472 Fortran::evaluate::Constant<TR>{value.REAL()}},
1473 Fortran::evaluate::Expr<TR>{
1474 Fortran::evaluate::Constant<TR>{value.AIMAG()}});
1475 return genunbox(ctor);
1476 } else /*constexpr*/ {
1477 llvm_unreachable("unhandled constant");
1478 }
1479 }
1480
1481 /// Convert string, \p s, to an APFloat value. Recognize and handle Inf and
1482 /// NaN strings as well. \p s is assumed to not contain any spaces.
consAPFloat(const llvm::fltSemantics & fsem,llvm::StringRef s)1483 static llvm::APFloat consAPFloat(const llvm::fltSemantics &fsem,
1484 llvm::StringRef s) {
1485 assert(s.find(' ') == llvm::StringRef::npos);
1486 if (s.compare_insensitive("-inf") == 0)
1487 return llvm::APFloat::getInf(fsem, /*negative=*/true);
1488 if (s.compare_insensitive("inf") == 0 || s.compare_insensitive("+inf") == 0)
1489 return llvm::APFloat::getInf(fsem);
1490 // TODO: Add support for quiet and signaling NaNs.
1491 if (s.compare_insensitive("-nan") == 0)
1492 return llvm::APFloat::getNaN(fsem, /*negative=*/true);
1493 if (s.compare_insensitive("nan") == 0 || s.compare_insensitive("+nan") == 0)
1494 return llvm::APFloat::getNaN(fsem);
1495 return {fsem, s};
1496 }
1497
1498 /// Generate a raw literal value and store it in the rawVals vector.
1499 template <Fortran::common::TypeCategory TC, int KIND>
1500 void
genRawLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC,KIND>> & value)1501 genRawLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
1502 &value) {
1503 mlir::Attribute val;
1504 assert(inInitializer != nullptr);
1505 if constexpr (TC == Fortran::common::TypeCategory::Integer) {
1506 inInitializer->rawType = converter.genType(TC, KIND);
1507 val = builder.getIntegerAttr(inInitializer->rawType, value.ToInt64());
1508 } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
1509 inInitializer->rawType =
1510 converter.genType(Fortran::common::TypeCategory::Integer, KIND);
1511 val = builder.getIntegerAttr(inInitializer->rawType, value.IsTrue());
1512 } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
1513 std::string str = value.DumpHexadecimal();
1514 inInitializer->rawType = converter.genType(TC, KIND);
1515 auto floatVal =
1516 consAPFloat(builder.getKindMap().getFloatSemantics(KIND), str);
1517 val = builder.getFloatAttr(inInitializer->rawType, floatVal);
1518 } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
1519 std::string strReal = value.REAL().DumpHexadecimal();
1520 std::string strImg = value.AIMAG().DumpHexadecimal();
1521 inInitializer->rawType = converter.genType(TC, KIND);
1522 auto realVal =
1523 consAPFloat(builder.getKindMap().getFloatSemantics(KIND), strReal);
1524 val = builder.getFloatAttr(inInitializer->rawType, realVal);
1525 inInitializer->rawVals.push_back(val);
1526 auto imgVal =
1527 consAPFloat(builder.getKindMap().getFloatSemantics(KIND), strImg);
1528 val = builder.getFloatAttr(inInitializer->rawType, imgVal);
1529 }
1530 inInitializer->rawVals.push_back(val);
1531 }
1532
1533 /// Convert a scalar literal CHARACTER to IR.
1534 template <int KIND>
1535 ExtValue
genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<Fortran::common::TypeCategory::Character,KIND>> & value,int64_t len)1536 genScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
1537 Fortran::common::TypeCategory::Character, KIND>> &value,
1538 int64_t len) {
1539 using ET = typename std::decay_t<decltype(value)>::value_type;
1540 if constexpr (KIND == 1) {
1541 assert(value.size() == static_cast<std::uint64_t>(len));
1542 // Outline character constant in ro data if it is not in an initializer.
1543 if (!inInitializer)
1544 return fir::factory::createStringLiteral(builder, getLoc(), value);
1545 // When in an initializer context, construct the literal op itself and do
1546 // not construct another constant object in rodata.
1547 fir::StringLitOp stringLit = builder.createStringLitOp(getLoc(), value);
1548 mlir::Value lenp = builder.createIntegerConstant(
1549 getLoc(), builder.getCharacterLengthType(), len);
1550 return fir::CharBoxValue{stringLit.getResult(), lenp};
1551 }
1552 fir::CharacterType type =
1553 fir::CharacterType::get(builder.getContext(), KIND, len);
1554 auto consLit = [&]() -> fir::StringLitOp {
1555 mlir::MLIRContext *context = builder.getContext();
1556 std::int64_t size = static_cast<std::int64_t>(value.size());
1557 mlir::ShapedType shape = mlir::RankedTensorType::get(
1558 llvm::ArrayRef<std::int64_t>{size},
1559 mlir::IntegerType::get(builder.getContext(), sizeof(ET) * 8));
1560 auto denseAttr = mlir::DenseElementsAttr::get(
1561 shape, llvm::ArrayRef<ET>{value.data(), value.size()});
1562 auto denseTag = mlir::StringAttr::get(context, fir::StringLitOp::xlist());
1563 mlir::NamedAttribute dataAttr(denseTag, denseAttr);
1564 auto sizeTag = mlir::StringAttr::get(context, fir::StringLitOp::size());
1565 mlir::NamedAttribute sizeAttr(sizeTag, builder.getI64IntegerAttr(len));
1566 llvm::SmallVector<mlir::NamedAttribute> attrs = {dataAttr, sizeAttr};
1567 return builder.create<fir::StringLitOp>(
1568 getLoc(), llvm::ArrayRef<mlir::Type>{type}, llvm::None, attrs);
1569 };
1570
1571 mlir::Value lenp = builder.createIntegerConstant(
1572 getLoc(), builder.getCharacterLengthType(), len);
1573 // When in an initializer context, construct the literal op itself and do
1574 // not construct another constant object in rodata.
1575 if (inInitializer)
1576 return fir::CharBoxValue{consLit().getResult(), lenp};
1577
1578 // Otherwise, the string is in a plain old expression so "outline" the value
1579 // by hashconsing it to a constant literal object.
1580
1581 auto size =
1582 converter.getKindMap().getCharacterBitsize(KIND) / 8 * value.size();
1583 llvm::StringRef strVal(reinterpret_cast<const char *>(value.c_str()), size);
1584 std::string globalName = fir::factory::uniqueCGIdent("cl", strVal);
1585 fir::GlobalOp global = builder.getNamedGlobal(globalName);
1586 if (!global)
1587 global = builder.createGlobalConstant(
1588 getLoc(), type, globalName,
1589 [&](fir::FirOpBuilder &builder) {
1590 fir::StringLitOp str = consLit();
1591 builder.create<fir::HasValueOp>(getLoc(), str);
1592 },
1593 builder.createLinkOnceLinkage());
1594 auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
1595 global.getSymbol());
1596 return fir::CharBoxValue{addr, lenp};
1597 }
1598
1599 template <Fortran::common::TypeCategory TC, int KIND>
genArrayLit(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC,KIND>> & con)1600 ExtValue genArrayLit(
1601 const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
1602 &con) {
1603 mlir::Location loc = getLoc();
1604 mlir::IndexType idxTy = builder.getIndexType();
1605 Fortran::evaluate::ConstantSubscript size =
1606 Fortran::evaluate::GetSize(con.shape());
1607 fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
1608 mlir::Type eleTy;
1609 if constexpr (TC == Fortran::common::TypeCategory::Character)
1610 eleTy = converter.genType(TC, KIND, {con.LEN()});
1611 else
1612 eleTy = converter.genType(TC, KIND);
1613 auto arrayTy = fir::SequenceType::get(shape, eleTy);
1614 mlir::Value array;
1615 llvm::SmallVector<mlir::Value> lbounds;
1616 llvm::SmallVector<mlir::Value> extents;
1617 if (!inInitializer || !inInitializer->genRawVals) {
1618 array = builder.create<fir::UndefOp>(loc, arrayTy);
1619 for (auto [lb, extent] : llvm::zip(con.lbounds(), shape)) {
1620 lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1));
1621 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
1622 }
1623 }
1624 if (size == 0) {
1625 if constexpr (TC == Fortran::common::TypeCategory::Character) {
1626 mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
1627 return fir::CharArrayBoxValue{array, len, extents, lbounds};
1628 } else {
1629 return fir::ArrayBoxValue{array, extents, lbounds};
1630 }
1631 }
1632 Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
1633 auto createIdx = [&]() {
1634 llvm::SmallVector<mlir::Attribute> idx;
1635 for (size_t i = 0; i < subscripts.size(); ++i)
1636 idx.push_back(
1637 builder.getIntegerAttr(idxTy, subscripts[i] - con.lbounds()[i]));
1638 return idx;
1639 };
1640 if constexpr (TC == Fortran::common::TypeCategory::Character) {
1641 assert(array && "array must not be nullptr");
1642 do {
1643 mlir::Value elementVal =
1644 fir::getBase(genScalarLit<KIND>(con.At(subscripts), con.LEN()));
1645 array = builder.create<fir::InsertValueOp>(
1646 loc, arrayTy, array, elementVal, builder.getArrayAttr(createIdx()));
1647 } while (con.IncrementSubscripts(subscripts));
1648 mlir::Value len = builder.createIntegerConstant(loc, idxTy, con.LEN());
1649 return fir::CharArrayBoxValue{array, len, extents, lbounds};
1650 } else {
1651 llvm::SmallVector<mlir::Attribute> rangeStartIdx;
1652 uint64_t rangeSize = 0;
1653 do {
1654 if (inInitializer && inInitializer->genRawVals) {
1655 genRawLit<TC, KIND>(con.At(subscripts));
1656 continue;
1657 }
1658 auto getElementVal = [&]() {
1659 return builder.createConvert(
1660 loc, eleTy,
1661 fir::getBase(genScalarLit<TC, KIND>(con.At(subscripts))));
1662 };
1663 Fortran::evaluate::ConstantSubscripts nextSubscripts = subscripts;
1664 bool nextIsSame = con.IncrementSubscripts(nextSubscripts) &&
1665 con.At(subscripts) == con.At(nextSubscripts);
1666 if (!rangeSize && !nextIsSame) { // single (non-range) value
1667 array = builder.create<fir::InsertValueOp>(
1668 loc, arrayTy, array, getElementVal(),
1669 builder.getArrayAttr(createIdx()));
1670 } else if (!rangeSize) { // start a range
1671 rangeStartIdx = createIdx();
1672 rangeSize = 1;
1673 } else if (nextIsSame) { // expand a range
1674 ++rangeSize;
1675 } else { // end a range
1676 llvm::SmallVector<int64_t> rangeBounds;
1677 llvm::SmallVector<mlir::Attribute> idx = createIdx();
1678 for (size_t i = 0; i < idx.size(); ++i) {
1679 rangeBounds.push_back(rangeStartIdx[i]
1680 .cast<mlir::IntegerAttr>()
1681 .getValue()
1682 .getSExtValue());
1683 rangeBounds.push_back(
1684 idx[i].cast<mlir::IntegerAttr>().getValue().getSExtValue());
1685 }
1686 array = builder.create<fir::InsertOnRangeOp>(
1687 loc, arrayTy, array, getElementVal(),
1688 builder.getIndexVectorAttr(rangeBounds));
1689 rangeSize = 0;
1690 }
1691 } while (con.IncrementSubscripts(subscripts));
1692 return fir::ArrayBoxValue{array, extents, lbounds};
1693 }
1694 }
1695
genArrayLit(const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> & con)1696 fir::ExtendedValue genArrayLit(
1697 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
1698 mlir::Location loc = getLoc();
1699 mlir::IndexType idxTy = builder.getIndexType();
1700 Fortran::evaluate::ConstantSubscript size =
1701 Fortran::evaluate::GetSize(con.shape());
1702 fir::SequenceType::Shape shape(con.shape().begin(), con.shape().end());
1703 mlir::Type eleTy = converter.genType(con.GetType().GetDerivedTypeSpec());
1704 auto arrayTy = fir::SequenceType::get(shape, eleTy);
1705 mlir::Value array = builder.create<fir::UndefOp>(loc, arrayTy);
1706 llvm::SmallVector<mlir::Value> lbounds;
1707 llvm::SmallVector<mlir::Value> extents;
1708 for (auto [lb, extent] : llvm::zip(con.lbounds(), con.shape())) {
1709 lbounds.push_back(builder.createIntegerConstant(loc, idxTy, lb - 1));
1710 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
1711 }
1712 if (size == 0)
1713 return fir::ArrayBoxValue{array, extents, lbounds};
1714 Fortran::evaluate::ConstantSubscripts subscripts = con.lbounds();
1715 do {
1716 mlir::Value derivedVal = fir::getBase(genval(con.At(subscripts)));
1717 llvm::SmallVector<mlir::Attribute> idx;
1718 for (auto [dim, lb] : llvm::zip(subscripts, con.lbounds()))
1719 idx.push_back(builder.getIntegerAttr(idxTy, dim - lb));
1720 array = builder.create<fir::InsertValueOp>(
1721 loc, arrayTy, array, derivedVal, builder.getArrayAttr(idx));
1722 } while (con.IncrementSubscripts(subscripts));
1723 return fir::ArrayBoxValue{array, extents, lbounds};
1724 }
1725
1726 template <Fortran::common::TypeCategory TC, int KIND>
1727 ExtValue
genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC,KIND>> & con)1728 genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
1729 &con) {
1730 if (con.Rank() > 0)
1731 return genArrayLit(con);
1732 std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
1733 opt = con.GetScalarValue();
1734 assert(opt.has_value() && "constant has no value");
1735 if constexpr (TC == Fortran::common::TypeCategory::Character) {
1736 return genScalarLit<KIND>(opt.value(), con.LEN());
1737 } else {
1738 return genScalarLit<TC, KIND>(opt.value());
1739 }
1740 }
genval(const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> & con)1741 fir::ExtendedValue genval(
1742 const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
1743 if (con.Rank() > 0)
1744 return genArrayLit(con);
1745 if (auto ctor = con.GetScalarValue())
1746 return genval(ctor.value());
1747 fir::emitFatalError(getLoc(),
1748 "constant of derived type has no constructor");
1749 }
1750
1751 template <typename A>
genval(const Fortran::evaluate::ArrayConstructor<A> &)1752 ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
1753 fir::emitFatalError(getLoc(), "array constructor: should not reach here");
1754 }
1755
gen(const Fortran::evaluate::ComplexPart & x)1756 ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
1757 mlir::Location loc = getLoc();
1758 auto idxTy = builder.getI32Type();
1759 ExtValue exv = gen(x.complex());
1760 mlir::Value base = fir::getBase(exv);
1761 fir::factory::Complex helper{builder, loc};
1762 mlir::Type eleTy =
1763 helper.getComplexPartType(fir::dyn_cast_ptrEleTy(base.getType()));
1764 mlir::Value offset = builder.createIntegerConstant(
1765 loc, idxTy,
1766 x.part() == Fortran::evaluate::ComplexPart::Part::RE ? 0 : 1);
1767 mlir::Value result = builder.create<fir::CoordinateOp>(
1768 loc, builder.getRefType(eleTy), base, mlir::ValueRange{offset});
1769 return {result};
1770 }
genval(const Fortran::evaluate::ComplexPart & x)1771 ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
1772 return genLoad(gen(x));
1773 }
1774
1775 /// Reference to a substring.
gen(const Fortran::evaluate::Substring & s)1776 ExtValue gen(const Fortran::evaluate::Substring &s) {
1777 // Get base string
1778 auto baseString = std::visit(
1779 Fortran::common::visitors{
1780 [&](const Fortran::evaluate::DataRef &x) { return gen(x); },
1781 [&](const Fortran::evaluate::StaticDataObject::Pointer &p)
1782 -> ExtValue {
1783 if (std::optional<std::string> str = p->AsString())
1784 return fir::factory::createStringLiteral(builder, getLoc(),
1785 *str);
1786 // TODO: convert StaticDataObject to Constant<T> and use normal
1787 // constant path. Beware that StaticDataObject data() takes into
1788 // account build machine endianness.
1789 TODO(getLoc(),
1790 "StaticDataObject::Pointer substring with kind > 1");
1791 },
1792 },
1793 s.parent());
1794 llvm::SmallVector<mlir::Value> bounds;
1795 mlir::Value lower = genunbox(s.lower());
1796 bounds.push_back(lower);
1797 if (Fortran::evaluate::MaybeExtentExpr upperBound = s.upper()) {
1798 mlir::Value upper = genunbox(*upperBound);
1799 bounds.push_back(upper);
1800 }
1801 fir::factory::CharacterExprHelper charHelper{builder, getLoc()};
1802 return baseString.match(
1803 [&](const fir::CharBoxValue &x) -> ExtValue {
1804 return charHelper.createSubstring(x, bounds);
1805 },
1806 [&](const fir::CharArrayBoxValue &) -> ExtValue {
1807 fir::emitFatalError(
1808 getLoc(),
1809 "array substring should be handled in array expression");
1810 },
1811 [&](const auto &) -> ExtValue {
1812 fir::emitFatalError(getLoc(), "substring base is not a CharBox");
1813 });
1814 }
1815
1816 /// The value of a substring.
genval(const Fortran::evaluate::Substring & ss)1817 ExtValue genval(const Fortran::evaluate::Substring &ss) {
1818 // FIXME: why is the value of a substring being lowered the same as the
1819 // address of a substring?
1820 return gen(ss);
1821 }
1822
genval(const Fortran::evaluate::Subscript & subs)1823 ExtValue genval(const Fortran::evaluate::Subscript &subs) {
1824 if (auto *s = std::get_if<Fortran::evaluate::IndirectSubscriptIntegerExpr>(
1825 &subs.u)) {
1826 if (s->value().Rank() > 0)
1827 fir::emitFatalError(getLoc(), "vector subscript is not scalar");
1828 return {genval(s->value())};
1829 }
1830 fir::emitFatalError(getLoc(), "subscript triple notation is not scalar");
1831 }
genSubscript(const Fortran::evaluate::Subscript & subs)1832 ExtValue genSubscript(const Fortran::evaluate::Subscript &subs) {
1833 return genval(subs);
1834 }
1835
gen(const Fortran::evaluate::DataRef & dref)1836 ExtValue gen(const Fortran::evaluate::DataRef &dref) {
1837 return std::visit([&](const auto &x) { return gen(x); }, dref.u);
1838 }
genval(const Fortran::evaluate::DataRef & dref)1839 ExtValue genval(const Fortran::evaluate::DataRef &dref) {
1840 return std::visit([&](const auto &x) { return genval(x); }, dref.u);
1841 }
1842
1843 // Helper function to turn the Component structure into a list of nested
1844 // components, ordered from largest/leftmost to smallest/rightmost:
1845 // - where only the smallest/rightmost item may be allocatable or a pointer
1846 // (nested allocatable/pointer components require nested coordinate_of ops)
1847 // - that does not contain any parent components
1848 // (the front end places parent components directly in the object)
1849 // Return the object used as the base coordinate for the component chain.
1850 static Fortran::evaluate::DataRef const *
reverseComponents(const Fortran::evaluate::Component & cmpt,std::list<const Fortran::evaluate::Component * > & list)1851 reverseComponents(const Fortran::evaluate::Component &cmpt,
1852 std::list<const Fortran::evaluate::Component *> &list) {
1853 if (!getLastSym(cmpt).test(Fortran::semantics::Symbol::Flag::ParentComp))
1854 list.push_front(&cmpt);
1855 return std::visit(
1856 Fortran::common::visitors{
1857 [&](const Fortran::evaluate::Component &x) {
1858 if (Fortran::semantics::IsAllocatableOrPointer(getLastSym(x)))
1859 return &cmpt.base();
1860 return reverseComponents(x, list);
1861 },
1862 [&](auto &) { return &cmpt.base(); },
1863 },
1864 cmpt.base().u);
1865 }
1866
1867 // Return the coordinate of the component reference
genComponent(const Fortran::evaluate::Component & cmpt)1868 ExtValue genComponent(const Fortran::evaluate::Component &cmpt) {
1869 std::list<const Fortran::evaluate::Component *> list;
1870 const Fortran::evaluate::DataRef *base = reverseComponents(cmpt, list);
1871 llvm::SmallVector<mlir::Value> coorArgs;
1872 ExtValue obj = gen(*base);
1873 mlir::Type ty = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(obj).getType());
1874 mlir::Location loc = getLoc();
1875 auto fldTy = fir::FieldType::get(&converter.getMLIRContext());
1876 // FIXME: need to thread the LEN type parameters here.
1877 for (const Fortran::evaluate::Component *field : list) {
1878 auto recTy = ty.cast<fir::RecordType>();
1879 const Fortran::semantics::Symbol &sym = getLastSym(*field);
1880 llvm::StringRef name = toStringRef(sym.name());
1881 coorArgs.push_back(builder.create<fir::FieldIndexOp>(
1882 loc, fldTy, name, recTy, fir::getTypeParams(obj)));
1883 ty = recTy.getType(name);
1884 }
1885 ty = builder.getRefType(ty);
1886 return fir::factory::componentToExtendedValue(
1887 builder, loc,
1888 builder.create<fir::CoordinateOp>(loc, ty, fir::getBase(obj),
1889 coorArgs));
1890 }
1891
gen(const Fortran::evaluate::Component & cmpt)1892 ExtValue gen(const Fortran::evaluate::Component &cmpt) {
1893 // Components may be pointer or allocatable. In the gen() path, the mutable
1894 // aspect is lost to simplify handling on the client side. To retain the
1895 // mutable aspect, genMutableBoxValue should be used.
1896 return genComponent(cmpt).match(
1897 [&](const fir::MutableBoxValue &mutableBox) {
1898 return fir::factory::genMutableBoxRead(builder, getLoc(), mutableBox);
1899 },
1900 [](auto &box) -> ExtValue { return box; });
1901 }
1902
genval(const Fortran::evaluate::Component & cmpt)1903 ExtValue genval(const Fortran::evaluate::Component &cmpt) {
1904 return genLoad(gen(cmpt));
1905 }
1906
1907 // Determine the result type after removing `dims` dimensions from the array
1908 // type `arrTy`
genSubType(mlir::Type arrTy,unsigned dims)1909 mlir::Type genSubType(mlir::Type arrTy, unsigned dims) {
1910 mlir::Type unwrapTy = fir::dyn_cast_ptrOrBoxEleTy(arrTy);
1911 assert(unwrapTy && "must be a pointer or box type");
1912 auto seqTy = unwrapTy.cast<fir::SequenceType>();
1913 llvm::ArrayRef<int64_t> shape = seqTy.getShape();
1914 assert(shape.size() > 0 && "removing columns for sequence sans shape");
1915 assert(dims <= shape.size() && "removing more columns than exist");
1916 fir::SequenceType::Shape newBnds;
1917 // follow Fortran semantics and remove columns (from right)
1918 std::size_t e = shape.size() - dims;
1919 for (decltype(e) i = 0; i < e; ++i)
1920 newBnds.push_back(shape[i]);
1921 if (!newBnds.empty())
1922 return fir::SequenceType::get(newBnds, seqTy.getEleTy());
1923 return seqTy.getEleTy();
1924 }
1925
1926 // Generate the code for a Bound value.
genval(const Fortran::semantics::Bound & bound)1927 ExtValue genval(const Fortran::semantics::Bound &bound) {
1928 if (bound.isExplicit()) {
1929 Fortran::semantics::MaybeSubscriptIntExpr sub = bound.GetExplicit();
1930 if (sub.has_value())
1931 return genval(*sub);
1932 return genIntegerConstant<8>(builder.getContext(), 1);
1933 }
1934 TODO(getLoc(), "non explicit semantics::Bound implementation");
1935 }
1936
isSlice(const Fortran::evaluate::ArrayRef & aref)1937 static bool isSlice(const Fortran::evaluate::ArrayRef &aref) {
1938 for (const Fortran::evaluate::Subscript &sub : aref.subscript())
1939 if (std::holds_alternative<Fortran::evaluate::Triplet>(sub.u))
1940 return true;
1941 return false;
1942 }
1943
1944 /// Lower an ArrayRef to a fir.coordinate_of given its lowered base.
genCoordinateOp(const ExtValue & array,const Fortran::evaluate::ArrayRef & aref)1945 ExtValue genCoordinateOp(const ExtValue &array,
1946 const Fortran::evaluate::ArrayRef &aref) {
1947 mlir::Location loc = getLoc();
1948 // References to array of rank > 1 with non constant shape that are not
1949 // fir.box must be collapsed into an offset computation in lowering already.
1950 // The same is needed with dynamic length character arrays of all ranks.
1951 mlir::Type baseType =
1952 fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(array).getType());
1953 if ((array.rank() > 1 && fir::hasDynamicSize(baseType)) ||
1954 fir::characterWithDynamicLen(fir::unwrapSequenceType(baseType)))
1955 if (!array.getBoxOf<fir::BoxValue>())
1956 return genOffsetAndCoordinateOp(array, aref);
1957 // Generate a fir.coordinate_of with zero based array indexes.
1958 llvm::SmallVector<mlir::Value> args;
1959 for (const auto &subsc : llvm::enumerate(aref.subscript())) {
1960 ExtValue subVal = genSubscript(subsc.value());
1961 assert(fir::isUnboxedValue(subVal) && "subscript must be simple scalar");
1962 mlir::Value val = fir::getBase(subVal);
1963 mlir::Type ty = val.getType();
1964 mlir::Value lb = getLBound(array, subsc.index(), ty);
1965 args.push_back(builder.create<mlir::arith::SubIOp>(loc, ty, val, lb));
1966 }
1967
1968 mlir::Value base = fir::getBase(array);
1969 auto seqTy =
1970 fir::dyn_cast_ptrOrBoxEleTy(base.getType()).cast<fir::SequenceType>();
1971 assert(args.size() == seqTy.getDimension());
1972 mlir::Type ty = builder.getRefType(seqTy.getEleTy());
1973 auto addr = builder.create<fir::CoordinateOp>(loc, ty, base, args);
1974 return fir::factory::arrayElementToExtendedValue(builder, loc, array, addr);
1975 }
1976
1977 /// Lower an ArrayRef to a fir.coordinate_of using an element offset instead
1978 /// of array indexes.
1979 /// This generates offset computation from the indexes and length parameters,
1980 /// and use the offset to access the element with a fir.coordinate_of. This
1981 /// must only be used if it is not possible to generate a normal
1982 /// fir.coordinate_of using array indexes (i.e. when the shape information is
1983 /// unavailable in the IR).
genOffsetAndCoordinateOp(const ExtValue & array,const Fortran::evaluate::ArrayRef & aref)1984 ExtValue genOffsetAndCoordinateOp(const ExtValue &array,
1985 const Fortran::evaluate::ArrayRef &aref) {
1986 mlir::Location loc = getLoc();
1987 mlir::Value addr = fir::getBase(array);
1988 mlir::Type arrTy = fir::dyn_cast_ptrEleTy(addr.getType());
1989 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
1990 mlir::Type seqTy = builder.getRefType(builder.getVarLenSeqTy(eleTy));
1991 mlir::Type refTy = builder.getRefType(eleTy);
1992 mlir::Value base = builder.createConvert(loc, seqTy, addr);
1993 mlir::IndexType idxTy = builder.getIndexType();
1994 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1995 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1996 auto getLB = [&](const auto &arr, unsigned dim) -> mlir::Value {
1997 return arr.getLBounds().empty() ? one : arr.getLBounds()[dim];
1998 };
1999 auto genFullDim = [&](const auto &arr, mlir::Value delta) -> mlir::Value {
2000 mlir::Value total = zero;
2001 assert(arr.getExtents().size() == aref.subscript().size());
2002 delta = builder.createConvert(loc, idxTy, delta);
2003 unsigned dim = 0;
2004 for (auto [ext, sub] : llvm::zip(arr.getExtents(), aref.subscript())) {
2005 ExtValue subVal = genSubscript(sub);
2006 assert(fir::isUnboxedValue(subVal));
2007 mlir::Value val =
2008 builder.createConvert(loc, idxTy, fir::getBase(subVal));
2009 mlir::Value lb = builder.createConvert(loc, idxTy, getLB(arr, dim));
2010 mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, val, lb);
2011 mlir::Value prod =
2012 builder.create<mlir::arith::MulIOp>(loc, delta, diff);
2013 total = builder.create<mlir::arith::AddIOp>(loc, prod, total);
2014 if (ext)
2015 delta = builder.create<mlir::arith::MulIOp>(loc, delta, ext);
2016 ++dim;
2017 }
2018 mlir::Type origRefTy = refTy;
2019 if (fir::factory::CharacterExprHelper::isCharacterScalar(refTy)) {
2020 fir::CharacterType chTy =
2021 fir::factory::CharacterExprHelper::getCharacterType(refTy);
2022 if (fir::characterWithDynamicLen(chTy)) {
2023 mlir::MLIRContext *ctx = builder.getContext();
2024 fir::KindTy kind =
2025 fir::factory::CharacterExprHelper::getCharacterKind(chTy);
2026 fir::CharacterType singleTy =
2027 fir::CharacterType::getSingleton(ctx, kind);
2028 refTy = builder.getRefType(singleTy);
2029 mlir::Type seqRefTy =
2030 builder.getRefType(builder.getVarLenSeqTy(singleTy));
2031 base = builder.createConvert(loc, seqRefTy, base);
2032 }
2033 }
2034 auto coor = builder.create<fir::CoordinateOp>(
2035 loc, refTy, base, llvm::ArrayRef<mlir::Value>{total});
2036 // Convert to expected, original type after address arithmetic.
2037 return builder.createConvert(loc, origRefTy, coor);
2038 };
2039 return array.match(
2040 [&](const fir::ArrayBoxValue &arr) -> ExtValue {
2041 // FIXME: this check can be removed when slicing is implemented
2042 if (isSlice(aref))
2043 fir::emitFatalError(
2044 getLoc(),
2045 "slice should be handled in array expression context");
2046 return genFullDim(arr, one);
2047 },
2048 [&](const fir::CharArrayBoxValue &arr) -> ExtValue {
2049 mlir::Value delta = arr.getLen();
2050 // If the length is known in the type, fir.coordinate_of will
2051 // already take the length into account.
2052 if (fir::factory::CharacterExprHelper::hasConstantLengthInType(arr))
2053 delta = one;
2054 return fir::CharBoxValue(genFullDim(arr, delta), arr.getLen());
2055 },
2056 [&](const fir::BoxValue &arr) -> ExtValue {
2057 // CoordinateOp for BoxValue is not generated here. The dimensions
2058 // must be kept in the fir.coordinate_op so that potential fir.box
2059 // strides can be applied by codegen.
2060 fir::emitFatalError(
2061 loc, "internal: BoxValue in dim-collapsed fir.coordinate_of");
2062 },
2063 [&](const auto &) -> ExtValue {
2064 fir::emitFatalError(loc, "internal: array processing failed");
2065 });
2066 }
2067
2068 /// Lower an ArrayRef to a fir.array_coor.
genArrayCoorOp(const ExtValue & exv,const Fortran::evaluate::ArrayRef & aref)2069 ExtValue genArrayCoorOp(const ExtValue &exv,
2070 const Fortran::evaluate::ArrayRef &aref) {
2071 mlir::Location loc = getLoc();
2072 mlir::Value addr = fir::getBase(exv);
2073 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType());
2074 mlir::Type eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
2075 mlir::Type refTy = builder.getRefType(eleTy);
2076 mlir::IndexType idxTy = builder.getIndexType();
2077 llvm::SmallVector<mlir::Value> arrayCoorArgs;
2078 // The ArrayRef is expected to be scalar here, arrays are handled in array
2079 // expression lowering. So no vector subscript or triplet is expected here.
2080 for (const auto &sub : aref.subscript()) {
2081 ExtValue subVal = genSubscript(sub);
2082 assert(fir::isUnboxedValue(subVal));
2083 arrayCoorArgs.push_back(
2084 builder.createConvert(loc, idxTy, fir::getBase(subVal)));
2085 }
2086 mlir::Value shape = builder.createShape(loc, exv);
2087 mlir::Value elementAddr = builder.create<fir::ArrayCoorOp>(
2088 loc, refTy, addr, shape, /*slice=*/mlir::Value{}, arrayCoorArgs,
2089 fir::getTypeParams(exv));
2090 return fir::factory::arrayElementToExtendedValue(builder, loc, exv,
2091 elementAddr);
2092 }
2093
2094 /// Return the coordinate of the array reference.
gen(const Fortran::evaluate::ArrayRef & aref)2095 ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
2096 ExtValue base = aref.base().IsSymbol() ? gen(getFirstSym(aref.base()))
2097 : gen(aref.base().GetComponent());
2098 // Check for command-line override to use array_coor op.
2099 if (generateArrayCoordinate)
2100 return genArrayCoorOp(base, aref);
2101 // Otherwise, use coordinate_of op.
2102 return genCoordinateOp(base, aref);
2103 }
2104
2105 /// Return lower bounds of \p box in dimension \p dim. The returned value
2106 /// has type \ty.
getLBound(const ExtValue & box,unsigned dim,mlir::Type ty)2107 mlir::Value getLBound(const ExtValue &box, unsigned dim, mlir::Type ty) {
2108 assert(box.rank() > 0 && "must be an array");
2109 mlir::Location loc = getLoc();
2110 mlir::Value one = builder.createIntegerConstant(loc, ty, 1);
2111 mlir::Value lb = fir::factory::readLowerBound(builder, loc, box, dim, one);
2112 return builder.createConvert(loc, ty, lb);
2113 }
2114
genval(const Fortran::evaluate::ArrayRef & aref)2115 ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
2116 return genLoad(gen(aref));
2117 }
2118
gen(const Fortran::evaluate::CoarrayRef & coref)2119 ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
2120 return Fortran::lower::CoarrayExprHelper{converter, getLoc(), symMap}
2121 .genAddr(coref);
2122 }
2123
genval(const Fortran::evaluate::CoarrayRef & coref)2124 ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
2125 return Fortran::lower::CoarrayExprHelper{converter, getLoc(), symMap}
2126 .genValue(coref);
2127 }
2128
2129 template <typename A>
gen(const Fortran::evaluate::Designator<A> & des)2130 ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
2131 return std::visit([&](const auto &x) { return gen(x); }, des.u);
2132 }
2133 template <typename A>
genval(const Fortran::evaluate::Designator<A> & des)2134 ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
2135 return std::visit([&](const auto &x) { return genval(x); }, des.u);
2136 }
2137
genType(const Fortran::evaluate::DynamicType & dt)2138 mlir::Type genType(const Fortran::evaluate::DynamicType &dt) {
2139 if (dt.category() != Fortran::common::TypeCategory::Derived)
2140 return converter.genType(dt.category(), dt.kind());
2141 return converter.genType(dt.GetDerivedTypeSpec());
2142 }
2143
2144 /// Lower a function reference
2145 template <typename A>
genFunctionRef(const Fortran::evaluate::FunctionRef<A> & funcRef)2146 ExtValue genFunctionRef(const Fortran::evaluate::FunctionRef<A> &funcRef) {
2147 if (!funcRef.GetType().has_value())
2148 fir::emitFatalError(getLoc(), "a function must have a type");
2149 mlir::Type resTy = genType(*funcRef.GetType());
2150 return genProcedureRef(funcRef, {resTy});
2151 }
2152
2153 /// Lower function call `funcRef` and return a reference to the resultant
2154 /// value. This is required for lowering expressions such as `f1(f2(v))`.
2155 template <typename A>
gen(const Fortran::evaluate::FunctionRef<A> & funcRef)2156 ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
2157 ExtValue retVal = genFunctionRef(funcRef);
2158 mlir::Type resultType = converter.genType(toEvExpr(funcRef));
2159 return placeScalarValueInMemory(builder, getLoc(), retVal, resultType);
2160 }
2161
2162 /// Helper to lower intrinsic arguments for inquiry intrinsic.
2163 ExtValue
lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr & expr)2164 lowerIntrinsicArgumentAsInquired(const Fortran::lower::SomeExpr &expr) {
2165 if (Fortran::evaluate::IsAllocatableOrPointerObject(
2166 expr, converter.getFoldingContext()))
2167 return genMutableBoxValue(expr);
2168 /// Do not create temps for array sections whose properties only need to be
2169 /// inquired: create a descriptor that will be inquired.
2170 if (Fortran::evaluate::IsVariable(expr) && isArray(expr) &&
2171 !Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr))
2172 return lowerIntrinsicArgumentAsBox(expr);
2173 return gen(expr);
2174 }
2175
2176 /// Helper to lower intrinsic arguments to a fir::BoxValue.
2177 /// It preserves all the non default lower bounds/non deferred length
2178 /// parameter information.
lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr & expr)2179 ExtValue lowerIntrinsicArgumentAsBox(const Fortran::lower::SomeExpr &expr) {
2180 mlir::Location loc = getLoc();
2181 ExtValue exv = genBoxArg(expr);
2182 mlir::Value box = builder.createBox(loc, exv);
2183 return fir::BoxValue(
2184 box, fir::factory::getNonDefaultLowerBounds(builder, loc, exv),
2185 fir::factory::getNonDeferredLenParams(exv));
2186 }
2187
2188 /// Generate a call to a Fortran intrinsic or intrinsic module procedure.
genIntrinsicRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> resultType,llvm::Optional<const Fortran::evaluate::SpecificIntrinsic> intrinsic=llvm::None)2189 ExtValue genIntrinsicRef(
2190 const Fortran::evaluate::ProcedureRef &procRef,
2191 llvm::Optional<mlir::Type> resultType,
2192 llvm::Optional<const Fortran::evaluate::SpecificIntrinsic> intrinsic =
2193 llvm::None) {
2194 llvm::SmallVector<ExtValue> operands;
2195
2196 std::string name =
2197 intrinsic ? intrinsic->name
2198 : procRef.proc().GetSymbol()->GetUltimate().name().ToString();
2199 mlir::Location loc = getLoc();
2200 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
2201 procRef, *intrinsic, converter)) {
2202 using ExvAndPresence = std::pair<ExtValue, llvm::Optional<mlir::Value>>;
2203 llvm::SmallVector<ExvAndPresence, 4> operands;
2204 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
2205 ExtValue optionalArg = lowerIntrinsicArgumentAsInquired(expr);
2206 mlir::Value isPresent =
2207 genActualIsPresentTest(builder, loc, optionalArg);
2208 operands.emplace_back(optionalArg, isPresent);
2209 };
2210 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
2211 operands.emplace_back(genval(expr), llvm::None);
2212 };
2213 Fortran::lower::prepareCustomIntrinsicArgument(
2214 procRef, *intrinsic, resultType, prepareOptionalArg, prepareOtherArg,
2215 converter);
2216
2217 auto getArgument = [&](std::size_t i) -> ExtValue {
2218 if (fir::conformsWithPassByRef(
2219 fir::getBase(operands[i].first).getType()))
2220 return genLoad(operands[i].first);
2221 return operands[i].first;
2222 };
2223 auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
2224 return operands[i].second;
2225 };
2226 return Fortran::lower::lowerCustomIntrinsic(
2227 builder, loc, name, resultType, isPresent, getArgument,
2228 operands.size(), stmtCtx);
2229 }
2230
2231 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
2232 Fortran::lower::getIntrinsicArgumentLowering(name);
2233 for (const auto &arg : llvm::enumerate(procRef.arguments())) {
2234 auto *expr =
2235 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
2236 if (!expr) {
2237 // Absent optional.
2238 operands.emplace_back(Fortran::lower::getAbsentIntrinsicArgument());
2239 continue;
2240 }
2241 if (!argLowering) {
2242 // No argument lowering instruction, lower by value.
2243 operands.emplace_back(genval(*expr));
2244 continue;
2245 }
2246 // Ad-hoc argument lowering handling.
2247 Fortran::lower::ArgLoweringRule argRules =
2248 Fortran::lower::lowerIntrinsicArgumentAs(*argLowering, arg.index());
2249 if (argRules.handleDynamicOptional &&
2250 Fortran::evaluate::MayBePassedAsAbsentOptional(
2251 *expr, converter.getFoldingContext())) {
2252 ExtValue optional = lowerIntrinsicArgumentAsInquired(*expr);
2253 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optional);
2254 switch (argRules.lowerAs) {
2255 case Fortran::lower::LowerIntrinsicArgAs::Value:
2256 operands.emplace_back(
2257 genOptionalValue(builder, loc, optional, isPresent));
2258 continue;
2259 case Fortran::lower::LowerIntrinsicArgAs::Addr:
2260 operands.emplace_back(
2261 genOptionalAddr(builder, loc, optional, isPresent));
2262 continue;
2263 case Fortran::lower::LowerIntrinsicArgAs::Box:
2264 operands.emplace_back(
2265 genOptionalBox(builder, loc, optional, isPresent));
2266 continue;
2267 case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2268 operands.emplace_back(optional);
2269 continue;
2270 }
2271 llvm_unreachable("bad switch");
2272 }
2273 switch (argRules.lowerAs) {
2274 case Fortran::lower::LowerIntrinsicArgAs::Value:
2275 operands.emplace_back(genval(*expr));
2276 continue;
2277 case Fortran::lower::LowerIntrinsicArgAs::Addr:
2278 operands.emplace_back(gen(*expr));
2279 continue;
2280 case Fortran::lower::LowerIntrinsicArgAs::Box:
2281 operands.emplace_back(lowerIntrinsicArgumentAsBox(*expr));
2282 continue;
2283 case Fortran::lower::LowerIntrinsicArgAs::Inquired:
2284 operands.emplace_back(lowerIntrinsicArgumentAsInquired(*expr));
2285 continue;
2286 }
2287 llvm_unreachable("bad switch");
2288 }
2289 // Let the intrinsic library lower the intrinsic procedure call
2290 return Fortran::lower::genIntrinsicCall(builder, getLoc(), name, resultType,
2291 operands, stmtCtx);
2292 }
2293
2294 /// helper to detect statement functions
2295 static bool
isStatementFunctionCall(const Fortran::evaluate::ProcedureRef & procRef)2296 isStatementFunctionCall(const Fortran::evaluate::ProcedureRef &procRef) {
2297 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol())
2298 if (const auto *details =
2299 symbol->detailsIf<Fortran::semantics::SubprogramDetails>())
2300 return details->stmtFunction().has_value();
2301 return false;
2302 }
2303
2304 /// Generate Statement function calls
genStmtFunctionRef(const Fortran::evaluate::ProcedureRef & procRef)2305 ExtValue genStmtFunctionRef(const Fortran::evaluate::ProcedureRef &procRef) {
2306 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
2307 assert(symbol && "expected symbol in ProcedureRef of statement functions");
2308 const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>();
2309
2310 // Statement functions have their own scope, we just need to associate
2311 // the dummy symbols to argument expressions. They are no
2312 // optional/alternate return arguments. Statement functions cannot be
2313 // recursive (directly or indirectly) so it is safe to add dummy symbols to
2314 // the local map here.
2315 symMap.pushScope();
2316 for (auto [arg, bind] :
2317 llvm::zip(details.dummyArgs(), procRef.arguments())) {
2318 assert(arg && "alternate return in statement function");
2319 assert(bind && "optional argument in statement function");
2320 const auto *expr = bind->UnwrapExpr();
2321 // TODO: assumed type in statement function, that surprisingly seems
2322 // allowed, probably because nobody thought of restricting this usage.
2323 // gfortran/ifort compiles this.
2324 assert(expr && "assumed type used as statement function argument");
2325 // As per Fortran 2018 C1580, statement function arguments can only be
2326 // scalars, so just pass the box with the address. The only care is to
2327 // to use the dummy character explicit length if any instead of the
2328 // actual argument length (that can be bigger).
2329 if (const Fortran::semantics::DeclTypeSpec *type = arg->GetType())
2330 if (type->category() == Fortran::semantics::DeclTypeSpec::Character)
2331 if (const Fortran::semantics::MaybeIntExpr &lenExpr =
2332 type->characterTypeSpec().length().GetExplicit()) {
2333 mlir::Value len = fir::getBase(genval(*lenExpr));
2334 // F2018 7.4.4.2 point 5.
2335 len = fir::factory::genMaxWithZero(builder, getLoc(), len);
2336 symMap.addSymbol(*arg,
2337 replaceScalarCharacterLength(gen(*expr), len));
2338 continue;
2339 }
2340 symMap.addSymbol(*arg, gen(*expr));
2341 }
2342
2343 // Explicitly map statement function host associated symbols to their
2344 // parent scope lowered symbol box.
2345 for (const Fortran::semantics::SymbolRef &sym :
2346 Fortran::evaluate::CollectSymbols(*details.stmtFunction()))
2347 if (const auto *details =
2348 sym->detailsIf<Fortran::semantics::HostAssocDetails>())
2349 if (!symMap.lookupSymbol(*sym))
2350 symMap.addSymbol(*sym, gen(details->symbol()));
2351
2352 ExtValue result = genval(details.stmtFunction().value());
2353 LLVM_DEBUG(llvm::dbgs() << "stmt-function: " << result << '\n');
2354 symMap.popScope();
2355 return result;
2356 }
2357
2358 /// Helper to package a Value and its properties into an ExtendedValue.
toExtendedValue(mlir::Location loc,mlir::Value base,llvm::ArrayRef<mlir::Value> extents,llvm::ArrayRef<mlir::Value> lengths)2359 static ExtValue toExtendedValue(mlir::Location loc, mlir::Value base,
2360 llvm::ArrayRef<mlir::Value> extents,
2361 llvm::ArrayRef<mlir::Value> lengths) {
2362 mlir::Type type = base.getType();
2363 if (type.isa<fir::BoxType>())
2364 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents);
2365 type = fir::unwrapRefType(type);
2366 if (type.isa<fir::BoxType>())
2367 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {});
2368 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
2369 if (seqTy.getDimension() != extents.size())
2370 fir::emitFatalError(loc, "incorrect number of extents for array");
2371 if (seqTy.getEleTy().isa<fir::CharacterType>()) {
2372 if (lengths.empty())
2373 fir::emitFatalError(loc, "missing length for character");
2374 assert(lengths.size() == 1);
2375 return fir::CharArrayBoxValue(base, lengths[0], extents);
2376 }
2377 return fir::ArrayBoxValue(base, extents);
2378 }
2379 if (type.isa<fir::CharacterType>()) {
2380 if (lengths.empty())
2381 fir::emitFatalError(loc, "missing length for character");
2382 assert(lengths.size() == 1);
2383 return fir::CharBoxValue(base, lengths[0]);
2384 }
2385 return base;
2386 }
2387
2388 // Find the argument that corresponds to the host associations.
2389 // Verify some assumptions about how the signature was built here.
2390 [[maybe_unused]] static unsigned
findHostAssocTuplePos(mlir::func::FuncOp fn)2391 findHostAssocTuplePos(mlir::func::FuncOp fn) {
2392 // Scan the argument list from last to first as the host associations are
2393 // appended for now.
2394 for (unsigned i = fn.getNumArguments(); i > 0; --i)
2395 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) {
2396 // Host assoc tuple must be last argument (for now).
2397 assert(i == fn.getNumArguments() && "tuple must be last");
2398 return i - 1;
2399 }
2400 llvm_unreachable("anyFuncArgsHaveAttr failed");
2401 }
2402
2403 /// Create a contiguous temporary array with the same shape,
2404 /// length parameters and type as mold. It is up to the caller to deallocate
2405 /// the temporary.
genArrayTempFromMold(const ExtValue & mold,llvm::StringRef tempName)2406 ExtValue genArrayTempFromMold(const ExtValue &mold,
2407 llvm::StringRef tempName) {
2408 mlir::Type type = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(mold).getType());
2409 assert(type && "expected descriptor or memory type");
2410 mlir::Location loc = getLoc();
2411 llvm::SmallVector<mlir::Value> extents =
2412 fir::factory::getExtents(loc, builder, mold);
2413 llvm::SmallVector<mlir::Value> allocMemTypeParams =
2414 fir::getTypeParams(mold);
2415 mlir::Value charLen;
2416 mlir::Type elementType = fir::unwrapSequenceType(type);
2417 if (auto charType = elementType.dyn_cast<fir::CharacterType>()) {
2418 charLen = allocMemTypeParams.empty()
2419 ? fir::factory::readCharLen(builder, loc, mold)
2420 : allocMemTypeParams[0];
2421 if (charType.hasDynamicLen() && allocMemTypeParams.empty())
2422 allocMemTypeParams.push_back(charLen);
2423 } else if (fir::hasDynamicSize(elementType)) {
2424 TODO(loc, "creating temporary for derived type with length parameters");
2425 }
2426
2427 mlir::Value temp = builder.create<fir::AllocMemOp>(
2428 loc, type, tempName, allocMemTypeParams, extents);
2429 if (fir::unwrapSequenceType(type).isa<fir::CharacterType>())
2430 return fir::CharArrayBoxValue{temp, charLen, extents};
2431 return fir::ArrayBoxValue{temp, extents};
2432 }
2433
2434 /// Copy \p source array into \p dest array. Both arrays must be
2435 /// conforming, but neither array must be contiguous.
genArrayCopy(ExtValue dest,ExtValue source)2436 void genArrayCopy(ExtValue dest, ExtValue source) {
2437 return createSomeArrayAssignment(converter, dest, source, symMap, stmtCtx);
2438 }
2439
2440 /// Lower a non-elemental procedure reference and read allocatable and pointer
2441 /// results into normal values.
genProcedureRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> resultType)2442 ExtValue genProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
2443 llvm::Optional<mlir::Type> resultType) {
2444 ExtValue res = genRawProcedureRef(procRef, resultType);
2445 // In most contexts, pointers and allocatable do not appear as allocatable
2446 // or pointer variable on the caller side (see 8.5.3 note 1 for
2447 // allocatables). The few context where this can happen must call
2448 // genRawProcedureRef directly.
2449 if (const auto *box = res.getBoxOf<fir::MutableBoxValue>())
2450 return fir::factory::genMutableBoxRead(builder, getLoc(), *box);
2451 return res;
2452 }
2453
2454 /// Given a call site for which the arguments were already lowered, generate
2455 /// the call and return the result. This function deals with explicit result
2456 /// allocation and lowering if needed. It also deals with passing the host
2457 /// link to internal procedures.
genCallOpAndResult(Fortran::lower::CallerInterface & caller,mlir::FunctionType callSiteType,llvm::Optional<mlir::Type> resultType)2458 ExtValue genCallOpAndResult(Fortran::lower::CallerInterface &caller,
2459 mlir::FunctionType callSiteType,
2460 llvm::Optional<mlir::Type> resultType) {
2461 mlir::Location loc = getLoc();
2462 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
2463 // Handle cases where caller must allocate the result or a fir.box for it.
2464 bool mustPopSymMap = false;
2465 if (caller.mustMapInterfaceSymbols()) {
2466 symMap.pushScope();
2467 mustPopSymMap = true;
2468 Fortran::lower::mapCallInterfaceSymbols(converter, caller, symMap);
2469 }
2470 // If this is an indirect call, retrieve the function address. Also retrieve
2471 // the result length if this is a character function (note that this length
2472 // will be used only if there is no explicit length in the local interface).
2473 mlir::Value funcPointer;
2474 mlir::Value charFuncPointerLength;
2475 if (const Fortran::semantics::Symbol *sym =
2476 caller.getIfIndirectCallSymbol()) {
2477 funcPointer = symMap.lookupSymbol(*sym).getAddr();
2478 if (!funcPointer)
2479 fir::emitFatalError(loc, "failed to find indirect call symbol address");
2480 if (fir::isCharacterProcedureTuple(funcPointer.getType(),
2481 /*acceptRawFunc=*/false))
2482 std::tie(funcPointer, charFuncPointerLength) =
2483 fir::factory::extractCharacterProcedureTuple(builder, loc,
2484 funcPointer);
2485 }
2486
2487 mlir::IndexType idxTy = builder.getIndexType();
2488 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value {
2489 return builder.createConvert(
2490 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx)));
2491 };
2492 llvm::SmallVector<mlir::Value> resultLengths;
2493 auto allocatedResult = [&]() -> llvm::Optional<ExtValue> {
2494 llvm::SmallVector<mlir::Value> extents;
2495 llvm::SmallVector<mlir::Value> lengths;
2496 if (!caller.callerAllocateResult())
2497 return {};
2498 mlir::Type type = caller.getResultStorageType();
2499 if (type.isa<fir::SequenceType>())
2500 caller.walkResultExtents([&](const Fortran::lower::SomeExpr &e) {
2501 extents.emplace_back(lowerSpecExpr(e));
2502 });
2503 caller.walkResultLengths([&](const Fortran::lower::SomeExpr &e) {
2504 lengths.emplace_back(lowerSpecExpr(e));
2505 });
2506
2507 // Result length parameters should not be provided to box storage
2508 // allocation and save_results, but they are still useful information to
2509 // keep in the ExtendedValue if non-deferred.
2510 if (!type.isa<fir::BoxType>()) {
2511 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) {
2512 // Calling an assumed length function. This is only possible if this
2513 // is a call to a character dummy procedure.
2514 if (!charFuncPointerLength)
2515 fir::emitFatalError(loc, "failed to retrieve character function "
2516 "length while calling it");
2517 lengths.push_back(charFuncPointerLength);
2518 }
2519 resultLengths = lengths;
2520 }
2521
2522 if (!extents.empty() || !lengths.empty()) {
2523 auto *bldr = &converter.getFirOpBuilder();
2524 auto stackSaveFn = fir::factory::getLlvmStackSave(builder);
2525 auto stackSaveSymbol = bldr->getSymbolRefAttr(stackSaveFn.getName());
2526 mlir::Value sp =
2527 bldr->create<fir::CallOp>(
2528 loc, stackSaveFn.getFunctionType().getResults(),
2529 stackSaveSymbol, mlir::ValueRange{})
2530 .getResult(0);
2531 stmtCtx.attachCleanup([bldr, loc, sp]() {
2532 auto stackRestoreFn = fir::factory::getLlvmStackRestore(*bldr);
2533 auto stackRestoreSymbol =
2534 bldr->getSymbolRefAttr(stackRestoreFn.getName());
2535 bldr->create<fir::CallOp>(
2536 loc, stackRestoreFn.getFunctionType().getResults(),
2537 stackRestoreSymbol, mlir::ValueRange{sp});
2538 });
2539 }
2540 mlir::Value temp =
2541 builder.createTemporary(loc, type, ".result", extents, resultLengths);
2542 return toExtendedValue(loc, temp, extents, lengths);
2543 }();
2544
2545 if (mustPopSymMap)
2546 symMap.popScope();
2547
2548 // Place allocated result or prepare the fir.save_result arguments.
2549 mlir::Value arrayResultShape;
2550 if (allocatedResult) {
2551 if (std::optional<Fortran::lower::CallInterface<
2552 Fortran::lower::CallerInterface>::PassedEntity>
2553 resultArg = caller.getPassedResult()) {
2554 if (resultArg->passBy == PassBy::AddressAndLength)
2555 caller.placeAddressAndLengthInput(*resultArg,
2556 fir::getBase(*allocatedResult),
2557 fir::getLen(*allocatedResult));
2558 else if (resultArg->passBy == PassBy::BaseAddress)
2559 caller.placeInput(*resultArg, fir::getBase(*allocatedResult));
2560 else
2561 fir::emitFatalError(
2562 loc, "only expect character scalar result to be passed by ref");
2563 } else {
2564 assert(caller.mustSaveResult());
2565 arrayResultShape = allocatedResult->match(
2566 [&](const fir::CharArrayBoxValue &) {
2567 return builder.createShape(loc, *allocatedResult);
2568 },
2569 [&](const fir::ArrayBoxValue &) {
2570 return builder.createShape(loc, *allocatedResult);
2571 },
2572 [&](const auto &) { return mlir::Value{}; });
2573 }
2574 }
2575
2576 // In older Fortran, procedure argument types are inferred. This may lead
2577 // different view of what the function signature is in different locations.
2578 // Casts are inserted as needed below to accommodate this.
2579
2580 // The mlir::func::FuncOp type prevails, unless it has a different number of
2581 // arguments which can happen in legal program if it was passed as a dummy
2582 // procedure argument earlier with no further type information.
2583 mlir::SymbolRefAttr funcSymbolAttr;
2584 bool addHostAssociations = false;
2585 if (!funcPointer) {
2586 mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType();
2587 mlir::SymbolRefAttr symbolAttr =
2588 builder.getSymbolRefAttr(caller.getMangledName());
2589 if (callSiteType.getNumResults() == funcOpType.getNumResults() &&
2590 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() &&
2591 fir::anyFuncArgsHaveAttr(caller.getFuncOp(),
2592 fir::getHostAssocAttrName())) {
2593 // The number of arguments is off by one, and we're lowering a function
2594 // with host associations. Modify call to include host associations
2595 // argument by appending the value at the end of the operands.
2596 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) ==
2597 converter.hostAssocTupleValue().getType());
2598 addHostAssociations = true;
2599 }
2600 if (!addHostAssociations &&
2601 (callSiteType.getNumResults() != funcOpType.getNumResults() ||
2602 callSiteType.getNumInputs() != funcOpType.getNumInputs())) {
2603 // Deal with argument number mismatch by making a function pointer so
2604 // that function type cast can be inserted. Do not emit a warning here
2605 // because this can happen in legal program if the function is not
2606 // defined here and it was first passed as an argument without any more
2607 // information.
2608 funcPointer =
2609 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
2610 } else if (callSiteType.getResults() != funcOpType.getResults()) {
2611 // Implicit interface result type mismatch are not standard Fortran, but
2612 // some compilers are not complaining about it. The front end is not
2613 // protecting lowering from this currently. Support this with a
2614 // discouraging warning.
2615 LLVM_DEBUG(mlir::emitWarning(
2616 loc, "a return type mismatch is not standard compliant and may "
2617 "lead to undefined behavior."));
2618 // Cast the actual function to the current caller implicit type because
2619 // that is the behavior we would get if we could not see the definition.
2620 funcPointer =
2621 builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr);
2622 } else {
2623 funcSymbolAttr = symbolAttr;
2624 }
2625 }
2626
2627 mlir::FunctionType funcType =
2628 funcPointer ? callSiteType : caller.getFuncOp().getFunctionType();
2629 llvm::SmallVector<mlir::Value> operands;
2630 // First operand of indirect call is the function pointer. Cast it to
2631 // required function type for the call to handle procedures that have a
2632 // compatible interface in Fortran, but that have different signatures in
2633 // FIR.
2634 if (funcPointer) {
2635 operands.push_back(
2636 funcPointer.getType().isa<fir::BoxProcType>()
2637 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer)
2638 : builder.createConvert(loc, funcType, funcPointer));
2639 }
2640
2641 // Deal with potential mismatches in arguments types. Passing an array to a
2642 // scalar argument should for instance be tolerated here.
2643 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface();
2644 for (auto [fst, snd] :
2645 llvm::zip(caller.getInputs(), funcType.getInputs())) {
2646 // When passing arguments to a procedure that can be called by implicit
2647 // interface, allow any character actual arguments to be passed to dummy
2648 // arguments of any type and vice versa.
2649 mlir::Value cast;
2650 auto *context = builder.getContext();
2651 if (snd.isa<fir::BoxProcType>() &&
2652 fst.getType().isa<mlir::FunctionType>()) {
2653 auto funcTy = mlir::FunctionType::get(context, llvm::None, llvm::None);
2654 auto boxProcTy = builder.getBoxProcType(funcTy);
2655 if (mlir::Value host = argumentHostAssocs(converter, fst)) {
2656 cast = builder.create<fir::EmboxProcOp>(
2657 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host});
2658 } else {
2659 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst);
2660 }
2661 } else {
2662 if (fir::isa_derived(snd)) {
2663 // FIXME: This seems like a serious bug elsewhere in lowering. Paper
2664 // over the problem for now.
2665 TODO(loc, "derived type argument passed by value");
2666 }
2667 assert(!fir::isa_derived(snd));
2668 cast = builder.convertWithSemantics(loc, snd, fst,
2669 callingImplicitInterface);
2670 }
2671 operands.push_back(cast);
2672 }
2673
2674 // Add host associations as necessary.
2675 if (addHostAssociations)
2676 operands.push_back(converter.hostAssocTupleValue());
2677
2678 auto call = builder.create<fir::CallOp>(loc, funcType.getResults(),
2679 funcSymbolAttr, operands);
2680
2681 if (caller.mustSaveResult())
2682 builder.create<fir::SaveResultOp>(loc, call.getResult(0),
2683 fir::getBase(allocatedResult.value()),
2684 arrayResultShape, resultLengths);
2685
2686 if (allocatedResult) {
2687 allocatedResult->match(
2688 [&](const fir::MutableBoxValue &box) {
2689 if (box.isAllocatable()) {
2690 // 9.7.3.2 point 4. Finalize allocatables.
2691 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
2692 stmtCtx.attachCleanup([bldr, loc, box]() {
2693 fir::factory::genFinalization(*bldr, loc, box);
2694 });
2695 }
2696 },
2697 [](const auto &) {});
2698 return *allocatedResult;
2699 }
2700
2701 if (!resultType)
2702 return mlir::Value{}; // subroutine call
2703 // For now, Fortran return values are implemented with a single MLIR
2704 // function return value.
2705 assert(call.getNumResults() == 1 &&
2706 "Expected exactly one result in FUNCTION call");
2707 return call.getResult(0);
2708 }
2709
2710 /// Like genExtAddr, but ensure the address returned is a temporary even if \p
2711 /// expr is variable inside parentheses.
genTempExtAddr(const Fortran::lower::SomeExpr & expr)2712 ExtValue genTempExtAddr(const Fortran::lower::SomeExpr &expr) {
2713 // In general, genExtAddr might not create a temp for variable inside
2714 // parentheses to avoid creating array temporary in sub-expressions. It only
2715 // ensures the sub-expression is not re-associated with other parts of the
2716 // expression. In the call semantics, there is a difference between expr and
2717 // variable (see R1524). For expressions, a variable storage must not be
2718 // argument associated since it could be modified inside the call, or the
2719 // variable could also be modified by other means during the call.
2720 if (!isParenthesizedVariable(expr))
2721 return genExtAddr(expr);
2722 if (expr.Rank() > 0)
2723 return asArray(expr);
2724 mlir::Location loc = getLoc();
2725 return genExtValue(expr).match(
2726 [&](const fir::CharBoxValue &boxChar) -> ExtValue {
2727 return fir::factory::CharacterExprHelper{builder, loc}.createTempFrom(
2728 boxChar);
2729 },
2730 [&](const fir::UnboxedValue &v) -> ExtValue {
2731 mlir::Type type = v.getType();
2732 mlir::Value value = v;
2733 if (fir::isa_ref_type(type))
2734 value = builder.create<fir::LoadOp>(loc, value);
2735 mlir::Value temp = builder.createTemporary(loc, value.getType());
2736 builder.create<fir::StoreOp>(loc, value, temp);
2737 return temp;
2738 },
2739 [&](const fir::BoxValue &x) -> ExtValue {
2740 // Derived type scalar that may be polymorphic.
2741 assert(!x.hasRank() && x.isDerived());
2742 if (x.isDerivedWithLenParameters())
2743 fir::emitFatalError(
2744 loc, "making temps for derived type with length parameters");
2745 // TODO: polymorphic aspects should be kept but for now the temp
2746 // created always has the declared type.
2747 mlir::Value var =
2748 fir::getBase(fir::factory::readBoxValue(builder, loc, x));
2749 auto value = builder.create<fir::LoadOp>(loc, var);
2750 mlir::Value temp = builder.createTemporary(loc, value.getType());
2751 builder.create<fir::StoreOp>(loc, value, temp);
2752 return temp;
2753 },
2754 [&](const auto &) -> ExtValue {
2755 fir::emitFatalError(loc, "expr is not a scalar value");
2756 });
2757 }
2758
2759 /// Helper structure to track potential copy-in of non contiguous variable
2760 /// argument into a contiguous temp. It is used to deallocate the temp that
2761 /// may have been created as well as to the copy-out from the temp to the
2762 /// variable after the call.
2763 struct CopyOutPair {
2764 ExtValue var;
2765 ExtValue temp;
2766 // Flag to indicate if the argument may have been modified by the
2767 // callee, in which case it must be copied-out to the variable.
2768 bool argMayBeModifiedByCall;
2769 // Optional boolean value that, if present and false, prevents
2770 // the copy-out and temp deallocation.
2771 llvm::Optional<mlir::Value> restrictCopyAndFreeAtRuntime;
2772 };
2773 using CopyOutPairs = llvm::SmallVector<CopyOutPair, 4>;
2774
2775 /// Helper to read any fir::BoxValue into other fir::ExtendedValue categories
2776 /// not based on fir.box.
2777 /// This will lose any non contiguous stride information and dynamic type and
2778 /// should only be called if \p exv is known to be contiguous or if its base
2779 /// address will be replaced by a contiguous one. If \p exv is not a
2780 /// fir::BoxValue, this is a no-op.
readIfBoxValue(const ExtValue & exv)2781 ExtValue readIfBoxValue(const ExtValue &exv) {
2782 if (const auto *box = exv.getBoxOf<fir::BoxValue>())
2783 return fir::factory::readBoxValue(builder, getLoc(), *box);
2784 return exv;
2785 }
2786
2787 /// Generate a contiguous temp to pass \p actualArg as argument \p arg. The
2788 /// creation of the temp and copy-in can be made conditional at runtime by
2789 /// providing a runtime boolean flag \p restrictCopyAtRuntime (in which case
2790 /// the temp and copy will only be made if the value is true at runtime).
genCopyIn(const ExtValue & actualArg,const Fortran::lower::CallerInterface::PassedEntity & arg,CopyOutPairs & copyOutPairs,llvm::Optional<mlir::Value> restrictCopyAtRuntime,bool byValue)2791 ExtValue genCopyIn(const ExtValue &actualArg,
2792 const Fortran::lower::CallerInterface::PassedEntity &arg,
2793 CopyOutPairs ©OutPairs,
2794 llvm::Optional<mlir::Value> restrictCopyAtRuntime,
2795 bool byValue) {
2796 const bool doCopyOut = !byValue && arg.mayBeModifiedByCall();
2797 llvm::StringRef tempName = byValue ? ".copy" : ".copyinout";
2798 if (!restrictCopyAtRuntime) {
2799 ExtValue temp = genArrayTempFromMold(actualArg, tempName);
2800 if (arg.mayBeReadByCall())
2801 genArrayCopy(temp, actualArg);
2802 copyOutPairs.emplace_back(
2803 CopyOutPair{actualArg, temp, doCopyOut, restrictCopyAtRuntime});
2804 return temp;
2805 }
2806 // Otherwise, need to be careful to only copy-in if allowed at runtime.
2807 mlir::Location loc = getLoc();
2808 auto addrType = fir::HeapType::get(
2809 fir::unwrapPassByRefType(fir::getBase(actualArg).getType()));
2810 mlir::Value addr =
2811 builder
2812 .genIfOp(loc, {addrType}, *restrictCopyAtRuntime,
2813 /*withElseRegion=*/true)
2814 .genThen([&]() {
2815 auto temp = genArrayTempFromMold(actualArg, tempName);
2816 if (arg.mayBeReadByCall())
2817 genArrayCopy(temp, actualArg);
2818 builder.create<fir::ResultOp>(loc, fir::getBase(temp));
2819 })
2820 .genElse([&]() {
2821 auto nullPtr = builder.createNullConstant(loc, addrType);
2822 builder.create<fir::ResultOp>(loc, nullPtr);
2823 })
2824 .getResults()[0];
2825 // Associate the temp address with actualArg lengths and extents.
2826 fir::ExtendedValue temp = fir::substBase(readIfBoxValue(actualArg), addr);
2827 copyOutPairs.emplace_back(
2828 CopyOutPair{actualArg, temp, doCopyOut, restrictCopyAtRuntime});
2829 return temp;
2830 }
2831
2832 /// Generate copy-out if needed and free the temporary for an argument that
2833 /// has been copied-in into a contiguous temp.
genCopyOut(const CopyOutPair & copyOutPair)2834 void genCopyOut(const CopyOutPair ©OutPair) {
2835 mlir::Location loc = getLoc();
2836 if (!copyOutPair.restrictCopyAndFreeAtRuntime) {
2837 if (copyOutPair.argMayBeModifiedByCall)
2838 genArrayCopy(copyOutPair.var, copyOutPair.temp);
2839 builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp));
2840 return;
2841 }
2842 builder.genIfThen(loc, *copyOutPair.restrictCopyAndFreeAtRuntime)
2843 .genThen([&]() {
2844 if (copyOutPair.argMayBeModifiedByCall)
2845 genArrayCopy(copyOutPair.var, copyOutPair.temp);
2846 builder.create<fir::FreeMemOp>(loc, fir::getBase(copyOutPair.temp));
2847 })
2848 .end();
2849 }
2850
2851 /// Lower a designator to a variable that may be absent at runtime into an
2852 /// ExtendedValue where all the properties (base address, shape and length
2853 /// parameters) can be safely read (set to zero if not present). It also
2854 /// returns a boolean mlir::Value telling if the variable is present at
2855 /// runtime.
2856 /// This is useful to later be able to do conditional copy-in/copy-out
2857 /// or to retrieve the base address without having to deal with the case
2858 /// where the actual may be an absent fir.box.
2859 std::pair<ExtValue, mlir::Value>
prepareActualThatMayBeAbsent(const Fortran::lower::SomeExpr & expr)2860 prepareActualThatMayBeAbsent(const Fortran::lower::SomeExpr &expr) {
2861 mlir::Location loc = getLoc();
2862 if (Fortran::evaluate::IsAllocatableOrPointerObject(
2863 expr, converter.getFoldingContext())) {
2864 // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated,
2865 // it is as if the argument was absent. The main care here is to
2866 // not do a copy-in/copy-out because the temp address, even though
2867 // pointing to a null size storage, would not be a nullptr and
2868 // therefore the argument would not be considered absent on the
2869 // callee side. Note: if wholeSymbol is optional, it cannot be
2870 // absent as per 15.5.2.12 point 7. and 8. We rely on this to
2871 // un-conditionally read the allocatable/pointer descriptor here.
2872 fir::MutableBoxValue mutableBox = genMutableBoxValue(expr);
2873 mlir::Value isPresent = fir::factory::genIsAllocatedOrAssociatedTest(
2874 builder, loc, mutableBox);
2875 fir::ExtendedValue actualArg =
2876 fir::factory::genMutableBoxRead(builder, loc, mutableBox);
2877 return {actualArg, isPresent};
2878 }
2879 // Absent descriptor cannot be read. To avoid any issue in
2880 // copy-in/copy-out, and when retrieving the address/length
2881 // create an descriptor pointing to a null address here if the
2882 // fir.box is absent.
2883 ExtValue actualArg = gen(expr);
2884 mlir::Value actualArgBase = fir::getBase(actualArg);
2885 mlir::Value isPresent = builder.create<fir::IsPresentOp>(
2886 loc, builder.getI1Type(), actualArgBase);
2887 if (!actualArgBase.getType().isa<fir::BoxType>())
2888 return {actualArg, isPresent};
2889 ExtValue safeToReadBox =
2890 absentBoxToUnallocatedBox(builder, loc, actualArg, isPresent);
2891 return {safeToReadBox, isPresent};
2892 }
2893
2894 /// Create a temp on the stack for scalar actual arguments that may be absent
2895 /// at runtime, but must be passed via a temp if they are presents.
2896 fir::ExtendedValue
createScalarTempForArgThatMayBeAbsent(ExtValue actualArg,mlir::Value isPresent)2897 createScalarTempForArgThatMayBeAbsent(ExtValue actualArg,
2898 mlir::Value isPresent) {
2899 mlir::Location loc = getLoc();
2900 mlir::Type type = fir::unwrapRefType(fir::getBase(actualArg).getType());
2901 if (fir::isDerivedWithLenParameters(actualArg))
2902 TODO(loc, "parametrized derived type optional scalar argument copy-in");
2903 if (const fir::CharBoxValue *charBox = actualArg.getCharBox()) {
2904 mlir::Value len = charBox->getLen();
2905 mlir::Value zero = builder.createIntegerConstant(loc, len.getType(), 0);
2906 len = builder.create<mlir::arith::SelectOp>(loc, isPresent, len, zero);
2907 mlir::Value temp = builder.createTemporary(
2908 loc, type, /*name=*/{},
2909 /*shape=*/{}, mlir::ValueRange{len},
2910 llvm::ArrayRef<mlir::NamedAttribute>{
2911 Fortran::lower::getAdaptToByRefAttr(builder)});
2912 return fir::CharBoxValue{temp, len};
2913 }
2914 assert((fir::isa_trivial(type) || type.isa<fir::RecordType>()) &&
2915 "must be simple scalar");
2916 return builder.createTemporary(
2917 loc, type,
2918 llvm::ArrayRef<mlir::NamedAttribute>{
2919 Fortran::lower::getAdaptToByRefAttr(builder)});
2920 }
2921
2922 template <typename A>
isCharacterType(const A & exp)2923 bool isCharacterType(const A &exp) {
2924 if (auto type = exp.GetType())
2925 return type->category() == Fortran::common::TypeCategory::Character;
2926 return false;
2927 }
2928
2929 /// Lower an actual argument that must be passed via an address.
2930 /// This generates of the copy-in/copy-out if the actual is not contiguous, or
2931 /// the creation of the temp if the actual is a variable and \p byValue is
2932 /// true. It handles the cases where the actual may be absent, and all of the
2933 /// copying has to be conditional at runtime.
prepareActualToBaseAddressLike(const Fortran::lower::SomeExpr & expr,const Fortran::lower::CallerInterface::PassedEntity & arg,CopyOutPairs & copyOutPairs,bool byValue)2934 ExtValue prepareActualToBaseAddressLike(
2935 const Fortran::lower::SomeExpr &expr,
2936 const Fortran::lower::CallerInterface::PassedEntity &arg,
2937 CopyOutPairs ©OutPairs, bool byValue) {
2938 mlir::Location loc = getLoc();
2939 const bool isArray = expr.Rank() > 0;
2940 const bool actualArgIsVariable = Fortran::evaluate::IsVariable(expr);
2941 // It must be possible to modify VALUE arguments on the callee side, even
2942 // if the actual argument is a literal or named constant. Hence, the
2943 // address of static storage must not be passed in that case, and a copy
2944 // must be made even if this is not a variable.
2945 // Note: isArray should be used here, but genBoxArg already creates copies
2946 // for it, so do not duplicate the copy until genBoxArg behavior is changed.
2947 const bool isStaticConstantByValue =
2948 byValue && Fortran::evaluate::IsActuallyConstant(expr) &&
2949 (isCharacterType(expr));
2950 const bool variableNeedsCopy =
2951 actualArgIsVariable &&
2952 (byValue || (isArray && !Fortran::evaluate::IsSimplyContiguous(
2953 expr, converter.getFoldingContext())));
2954 const bool needsCopy = isStaticConstantByValue || variableNeedsCopy;
2955 auto argAddr = [&]() -> ExtValue {
2956 if (!actualArgIsVariable && !needsCopy)
2957 // Actual argument is not a variable. Make sure a variable address is
2958 // not passed.
2959 return genTempExtAddr(expr);
2960 ExtValue baseAddr;
2961 if (arg.isOptional() && Fortran::evaluate::MayBePassedAsAbsentOptional(
2962 expr, converter.getFoldingContext())) {
2963 auto [actualArgBind, isPresent] = prepareActualThatMayBeAbsent(expr);
2964 const ExtValue &actualArg = actualArgBind;
2965 if (!needsCopy)
2966 return actualArg;
2967
2968 if (isArray)
2969 return genCopyIn(actualArg, arg, copyOutPairs, isPresent, byValue);
2970 // Scalars, create a temp, and use it conditionally at runtime if
2971 // the argument is present.
2972 ExtValue temp =
2973 createScalarTempForArgThatMayBeAbsent(actualArg, isPresent);
2974 mlir::Type tempAddrTy = fir::getBase(temp).getType();
2975 mlir::Value selectAddr =
2976 builder
2977 .genIfOp(loc, {tempAddrTy}, isPresent,
2978 /*withElseRegion=*/true)
2979 .genThen([&]() {
2980 fir::factory::genScalarAssignment(builder, loc, temp,
2981 actualArg);
2982 builder.create<fir::ResultOp>(loc, fir::getBase(temp));
2983 })
2984 .genElse([&]() {
2985 mlir::Value absent =
2986 builder.create<fir::AbsentOp>(loc, tempAddrTy);
2987 builder.create<fir::ResultOp>(loc, absent);
2988 })
2989 .getResults()[0];
2990 return fir::substBase(temp, selectAddr);
2991 }
2992 // Actual cannot be absent, the actual argument can safely be
2993 // copied-in/copied-out without any care if needed.
2994 if (isArray) {
2995 ExtValue box = genBoxArg(expr);
2996 if (needsCopy)
2997 return genCopyIn(box, arg, copyOutPairs,
2998 /*restrictCopyAtRuntime=*/llvm::None, byValue);
2999 // Contiguous: just use the box we created above!
3000 // This gets "unboxed" below, if needed.
3001 return box;
3002 }
3003 // Actual argument is a non-optional, non-pointer, non-allocatable
3004 // scalar.
3005 ExtValue actualArg = genExtAddr(expr);
3006 if (needsCopy)
3007 return createInMemoryScalarCopy(builder, loc, actualArg);
3008 return actualArg;
3009 }();
3010 // Scalar and contiguous expressions may be lowered to a fir.box,
3011 // either to account for potential polymorphism, or because lowering
3012 // did not account for some contiguity hints.
3013 // Here, polymorphism does not matter (an entity of the declared type
3014 // is passed, not one of the dynamic type), and the expr is known to
3015 // be simply contiguous, so it is safe to unbox it and pass the
3016 // address without making a copy.
3017 return readIfBoxValue(argAddr);
3018 }
3019
3020 /// Lower a non-elemental procedure reference.
genRawProcedureRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> resultType)3021 ExtValue genRawProcedureRef(const Fortran::evaluate::ProcedureRef &procRef,
3022 llvm::Optional<mlir::Type> resultType) {
3023 mlir::Location loc = getLoc();
3024 if (isElementalProcWithArrayArgs(procRef))
3025 fir::emitFatalError(loc, "trying to lower elemental procedure with array "
3026 "arguments as normal procedure");
3027
3028 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
3029 procRef.proc().GetSpecificIntrinsic())
3030 return genIntrinsicRef(procRef, resultType, *intrinsic);
3031
3032 if (isIntrinsicModuleProcRef(procRef))
3033 return genIntrinsicRef(procRef, resultType);
3034
3035 if (isStatementFunctionCall(procRef))
3036 return genStmtFunctionRef(procRef);
3037
3038 Fortran::lower::CallerInterface caller(procRef, converter);
3039 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
3040
3041 llvm::SmallVector<fir::MutableBoxValue> mutableModifiedByCall;
3042 // List of <var, temp> where temp must be copied into var after the call.
3043 CopyOutPairs copyOutPairs;
3044
3045 mlir::FunctionType callSiteType = caller.genFunctionType();
3046
3047 // Lower the actual arguments and map the lowered values to the dummy
3048 // arguments.
3049 for (const Fortran::lower::CallInterface<
3050 Fortran::lower::CallerInterface>::PassedEntity &arg :
3051 caller.getPassedArguments()) {
3052 const auto *actual = arg.entity;
3053 mlir::Type argTy = callSiteType.getInput(arg.firArgument);
3054 if (!actual) {
3055 // Optional dummy argument for which there is no actual argument.
3056 caller.placeInput(arg, builder.create<fir::AbsentOp>(loc, argTy));
3057 continue;
3058 }
3059 const auto *expr = actual->UnwrapExpr();
3060 if (!expr)
3061 TODO(loc, "assumed type actual argument");
3062
3063 if (arg.passBy == PassBy::Value) {
3064 ExtValue argVal = genval(*expr);
3065 if (!fir::isUnboxedValue(argVal))
3066 fir::emitFatalError(
3067 loc, "internal error: passing non trivial value by value");
3068 caller.placeInput(arg, fir::getBase(argVal));
3069 continue;
3070 }
3071
3072 if (arg.passBy == PassBy::MutableBox) {
3073 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(
3074 *expr)) {
3075 // If expr is NULL(), the mutableBox created must be a deallocated
3076 // pointer with the dummy argument characteristics (see table 16.5
3077 // in Fortran 2018 standard).
3078 // No length parameters are set for the created box because any non
3079 // deferred type parameters of the dummy will be evaluated on the
3080 // callee side, and it is illegal to use NULL without a MOLD if any
3081 // dummy length parameters are assumed.
3082 mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy);
3083 assert(boxTy && boxTy.isa<fir::BoxType>() &&
3084 "must be a fir.box type");
3085 mlir::Value boxStorage = builder.createTemporary(loc, boxTy);
3086 mlir::Value nullBox = fir::factory::createUnallocatedBox(
3087 builder, loc, boxTy, /*nonDeferredParams=*/{});
3088 builder.create<fir::StoreOp>(loc, nullBox, boxStorage);
3089 caller.placeInput(arg, boxStorage);
3090 continue;
3091 }
3092 if (fir::isPointerType(argTy) &&
3093 !Fortran::evaluate::IsObjectPointer(
3094 *expr, converter.getFoldingContext())) {
3095 // Passing a non POINTER actual argument to a POINTER dummy argument.
3096 // Create a pointer of the dummy argument type and assign the actual
3097 // argument to it.
3098 mlir::Value irBox =
3099 builder.createTemporary(loc, fir::unwrapRefType(argTy));
3100 // Non deferred parameters will be evaluated on the callee side.
3101 fir::MutableBoxValue pointer(irBox,
3102 /*nonDeferredParams=*/mlir::ValueRange{},
3103 /*mutableProperties=*/{});
3104 Fortran::lower::associateMutableBox(converter, loc, pointer, *expr,
3105 /*lbounds=*/llvm::None, stmtCtx);
3106 caller.placeInput(arg, irBox);
3107 continue;
3108 }
3109 // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE.
3110 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
3111 mlir::Value irBox =
3112 fir::factory::getMutableIRBox(builder, loc, mutableBox);
3113 caller.placeInput(arg, irBox);
3114 if (arg.mayBeModifiedByCall())
3115 mutableModifiedByCall.emplace_back(std::move(mutableBox));
3116 continue;
3117 }
3118 if (arg.passBy == PassBy::BaseAddress || arg.passBy == PassBy::BoxChar ||
3119 arg.passBy == PassBy::BaseAddressValueAttribute ||
3120 arg.passBy == PassBy::CharBoxValueAttribute) {
3121 const bool byValue = arg.passBy == PassBy::BaseAddressValueAttribute ||
3122 arg.passBy == PassBy::CharBoxValueAttribute;
3123 ExtValue argAddr =
3124 prepareActualToBaseAddressLike(*expr, arg, copyOutPairs, byValue);
3125 if (arg.passBy == PassBy::BaseAddress ||
3126 arg.passBy == PassBy::BaseAddressValueAttribute) {
3127 caller.placeInput(arg, fir::getBase(argAddr));
3128 } else {
3129 assert(arg.passBy == PassBy::BoxChar ||
3130 arg.passBy == PassBy::CharBoxValueAttribute);
3131 auto helper = fir::factory::CharacterExprHelper{builder, loc};
3132 auto boxChar = argAddr.match(
3133 [&](const fir::CharBoxValue &x) { return helper.createEmbox(x); },
3134 [&](const fir::CharArrayBoxValue &x) {
3135 return helper.createEmbox(x);
3136 },
3137 [&](const auto &x) -> mlir::Value {
3138 // Fortran allows an actual argument of a completely different
3139 // type to be passed to a procedure expecting a CHARACTER in the
3140 // dummy argument position. When this happens, the data pointer
3141 // argument is simply assumed to point to CHARACTER data and the
3142 // LEN argument used is garbage. Simulate this behavior by
3143 // free-casting the base address to be a !fir.char reference and
3144 // setting the LEN argument to undefined. What could go wrong?
3145 auto dataPtr = fir::getBase(x);
3146 assert(!dataPtr.getType().template isa<fir::BoxType>());
3147 return builder.convertWithSemantics(
3148 loc, argTy, dataPtr,
3149 /*allowCharacterConversion=*/true);
3150 });
3151 caller.placeInput(arg, boxChar);
3152 }
3153 } else if (arg.passBy == PassBy::Box) {
3154 // Before lowering to an address, handle the allocatable/pointer actual
3155 // argument to optional fir.box dummy. It is legal to pass
3156 // unallocated/disassociated entity to an optional. In this case, an
3157 // absent fir.box must be created instead of a fir.box with a null value
3158 // (Fortran 2018 15.5.2.12 point 1).
3159 if (arg.isOptional() && Fortran::evaluate::IsAllocatableOrPointerObject(
3160 *expr, converter.getFoldingContext())) {
3161 // Note that passing an absent allocatable to a non-allocatable
3162 // optional dummy argument is illegal (15.5.2.12 point 3 (8)). So
3163 // nothing has to be done to generate an absent argument in this case,
3164 // and it is OK to unconditionally read the mutable box here.
3165 fir::MutableBoxValue mutableBox = genMutableBoxValue(*expr);
3166 mlir::Value isAllocated =
3167 fir::factory::genIsAllocatedOrAssociatedTest(builder, loc,
3168 mutableBox);
3169 auto absent = builder.create<fir::AbsentOp>(loc, argTy);
3170 /// For now, assume it is not OK to pass the allocatable/pointer
3171 /// descriptor to a non pointer/allocatable dummy. That is a strict
3172 /// interpretation of 18.3.6 point 4 that stipulates the descriptor
3173 /// has the dummy attributes in BIND(C) contexts.
3174 mlir::Value box = builder.createBox(
3175 loc, fir::factory::genMutableBoxRead(builder, loc, mutableBox));
3176 // Need the box types to be exactly similar for the selectOp.
3177 mlir::Value convertedBox = builder.createConvert(loc, argTy, box);
3178 caller.placeInput(arg, builder.create<mlir::arith::SelectOp>(
3179 loc, isAllocated, convertedBox, absent));
3180 } else {
3181 // Make sure a variable address is only passed if the expression is
3182 // actually a variable.
3183 mlir::Value box =
3184 Fortran::evaluate::IsVariable(*expr)
3185 ? builder.createBox(loc, genBoxArg(*expr))
3186 : builder.createBox(getLoc(), genTempExtAddr(*expr));
3187 caller.placeInput(arg, box);
3188 }
3189 } else if (arg.passBy == PassBy::AddressAndLength) {
3190 ExtValue argRef = genExtAddr(*expr);
3191 caller.placeAddressAndLengthInput(arg, fir::getBase(argRef),
3192 fir::getLen(argRef));
3193 } else if (arg.passBy == PassBy::CharProcTuple) {
3194 ExtValue argRef = genExtAddr(*expr);
3195 mlir::Value tuple = createBoxProcCharTuple(
3196 converter, argTy, fir::getBase(argRef), fir::getLen(argRef));
3197 caller.placeInput(arg, tuple);
3198 } else {
3199 TODO(loc, "pass by value in non elemental function call");
3200 }
3201 }
3202
3203 ExtValue result = genCallOpAndResult(caller, callSiteType, resultType);
3204
3205 // Sync pointers and allocatables that may have been modified during the
3206 // call.
3207 for (const auto &mutableBox : mutableModifiedByCall)
3208 fir::factory::syncMutableBoxFromIRBox(builder, loc, mutableBox);
3209 // Handle case where result was passed as argument
3210
3211 // Copy-out temps that were created for non contiguous variable arguments if
3212 // needed.
3213 for (const auto ©OutPair : copyOutPairs)
3214 genCopyOut(copyOutPair);
3215
3216 return result;
3217 }
3218
3219 template <typename A>
genval(const Fortran::evaluate::FunctionRef<A> & funcRef)3220 ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
3221 ExtValue result = genFunctionRef(funcRef);
3222 if (result.rank() == 0 && fir::isa_ref_type(fir::getBase(result).getType()))
3223 return genLoad(result);
3224 return result;
3225 }
3226
genval(const Fortran::evaluate::ProcedureRef & procRef)3227 ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
3228 llvm::Optional<mlir::Type> resTy;
3229 if (procRef.hasAlternateReturns())
3230 resTy = builder.getIndexType();
3231 return genProcedureRef(procRef, resTy);
3232 }
3233
3234 template <typename A>
isScalar(const A & x)3235 bool isScalar(const A &x) {
3236 return x.Rank() == 0;
3237 }
3238
3239 /// Helper to detect Transformational function reference.
3240 template <typename T>
isTransformationalRef(const T &)3241 bool isTransformationalRef(const T &) {
3242 return false;
3243 }
3244 template <typename T>
isTransformationalRef(const Fortran::evaluate::FunctionRef<T> & funcRef)3245 bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
3246 return !funcRef.IsElemental() && funcRef.Rank();
3247 }
3248 template <typename T>
isTransformationalRef(Fortran::evaluate::Expr<T> expr)3249 bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
3250 return std::visit([&](const auto &e) { return isTransformationalRef(e); },
3251 expr.u);
3252 }
3253
3254 template <typename A>
asArray(const A & x)3255 ExtValue asArray(const A &x) {
3256 return Fortran::lower::createSomeArrayTempValue(converter, toEvExpr(x),
3257 symMap, stmtCtx);
3258 }
3259
3260 /// Lower an array value as an argument. This argument can be passed as a box
3261 /// value, so it may be possible to avoid making a temporary.
3262 template <typename A>
asArrayArg(const Fortran::evaluate::Expr<A> & x)3263 ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x) {
3264 return std::visit([&](const auto &e) { return asArrayArg(e, x); }, x.u);
3265 }
3266 template <typename A, typename B>
asArrayArg(const Fortran::evaluate::Expr<A> & x,const B & y)3267 ExtValue asArrayArg(const Fortran::evaluate::Expr<A> &x, const B &y) {
3268 return std::visit([&](const auto &e) { return asArrayArg(e, y); }, x.u);
3269 }
3270 template <typename A, typename B>
asArrayArg(const Fortran::evaluate::Designator<A> &,const B & x)3271 ExtValue asArrayArg(const Fortran::evaluate::Designator<A> &, const B &x) {
3272 // Designator is being passed as an argument to a procedure. Lower the
3273 // expression to a boxed value.
3274 auto someExpr = toEvExpr(x);
3275 return Fortran::lower::createBoxValue(getLoc(), converter, someExpr, symMap,
3276 stmtCtx);
3277 }
3278 template <typename A, typename B>
asArrayArg(const A &,const B & x)3279 ExtValue asArrayArg(const A &, const B &x) {
3280 // If the expression to pass as an argument is not a designator, then create
3281 // an array temp.
3282 return asArray(x);
3283 }
3284
3285 template <typename A>
gen(const Fortran::evaluate::Expr<A> & x)3286 ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
3287 // Whole array symbols or components, and results of transformational
3288 // functions already have a storage and the scalar expression lowering path
3289 // is used to not create a new temporary storage.
3290 if (isScalar(x) ||
3291 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
3292 (isTransformationalRef(x) && !isOptimizableTranspose(x)))
3293 return std::visit([&](const auto &e) { return genref(e); }, x.u);
3294 if (useBoxArg)
3295 return asArrayArg(x);
3296 return asArray(x);
3297 }
3298 template <typename A>
genval(const Fortran::evaluate::Expr<A> & x)3299 ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
3300 if (isScalar(x) || Fortran::evaluate::UnwrapWholeSymbolDataRef(x) ||
3301 inInitializer)
3302 return std::visit([&](const auto &e) { return genval(e); }, x.u);
3303 return asArray(x);
3304 }
3305
3306 template <int KIND>
genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<Fortran::common::TypeCategory::Logical,KIND>> & exp)3307 ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
3308 Fortran::common::TypeCategory::Logical, KIND>> &exp) {
3309 return std::visit([&](const auto &e) { return genval(e); }, exp.u);
3310 }
3311
3312 using RefSet =
3313 std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
3314 Fortran::evaluate::DataRef, Fortran::evaluate::Component,
3315 Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
3316 Fortran::semantics::SymbolRef>;
3317 template <typename A>
3318 static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
3319
3320 template <typename A, typename = std::enable_if_t<inRefSet<A>>>
genref(const A & a)3321 ExtValue genref(const A &a) {
3322 return gen(a);
3323 }
3324 template <typename A>
genref(const A & a)3325 ExtValue genref(const A &a) {
3326 if (inInitializer) {
3327 // Initialization expressions can never allocate memory.
3328 return genval(a);
3329 }
3330 mlir::Type storageType = converter.genType(toEvExpr(a));
3331 return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
3332 }
3333
3334 template <typename A, template <typename> typename T,
3335 typename B = std::decay_t<T<A>>,
3336 std::enable_if_t<
3337 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
3338 std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
3339 std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
3340 bool> = true>
genref(const T<A> & x)3341 ExtValue genref(const T<A> &x) {
3342 return gen(x);
3343 }
3344
3345 private:
3346 mlir::Location location;
3347 Fortran::lower::AbstractConverter &converter;
3348 fir::FirOpBuilder &builder;
3349 Fortran::lower::StatementContext &stmtCtx;
3350 Fortran::lower::SymMap &symMap;
3351 InitializerData *inInitializer = nullptr;
3352 bool useBoxArg = false; // expression lowered as argument
3353 };
3354 } // namespace
3355
3356 // Helper for changing the semantics in a given context. Preserves the current
3357 // semantics which is resumed when the "push" goes out of scope.
3358 #define PushSemantics(PushVal) \
3359 [[maybe_unused]] auto pushSemanticsLocalVariable##__LINE__ = \
3360 Fortran::common::ScopedSet(semant, PushVal);
3361
isAdjustedArrayElementType(mlir::Type t)3362 static bool isAdjustedArrayElementType(mlir::Type t) {
3363 return fir::isa_char(t) || fir::isa_derived(t) || t.isa<fir::SequenceType>();
3364 }
elementTypeWasAdjusted(mlir::Type t)3365 static bool elementTypeWasAdjusted(mlir::Type t) {
3366 if (auto ty = t.dyn_cast<fir::ReferenceType>())
3367 return isAdjustedArrayElementType(ty.getEleTy());
3368 return false;
3369 }
adjustedArrayElementType(mlir::Type t)3370 static mlir::Type adjustedArrayElementType(mlir::Type t) {
3371 return isAdjustedArrayElementType(t) ? fir::ReferenceType::get(t) : t;
3372 }
3373
3374 /// Helper to generate calls to scalar user defined assignment procedures.
genScalarUserDefinedAssignmentCall(fir::FirOpBuilder & builder,mlir::Location loc,mlir::func::FuncOp func,const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs)3375 static void genScalarUserDefinedAssignmentCall(fir::FirOpBuilder &builder,
3376 mlir::Location loc,
3377 mlir::func::FuncOp func,
3378 const fir::ExtendedValue &lhs,
3379 const fir::ExtendedValue &rhs) {
3380 auto prepareUserDefinedArg =
3381 [](fir::FirOpBuilder &builder, mlir::Location loc,
3382 const fir::ExtendedValue &value, mlir::Type argType) -> mlir::Value {
3383 if (argType.isa<fir::BoxCharType>()) {
3384 const fir::CharBoxValue *charBox = value.getCharBox();
3385 assert(charBox && "argument type mismatch in elemental user assignment");
3386 return fir::factory::CharacterExprHelper{builder, loc}.createEmbox(
3387 *charBox);
3388 }
3389 if (argType.isa<fir::BoxType>()) {
3390 mlir::Value box = builder.createBox(loc, value);
3391 return builder.createConvert(loc, argType, box);
3392 }
3393 // Simple pass by address.
3394 mlir::Type argBaseType = fir::unwrapRefType(argType);
3395 assert(!fir::hasDynamicSize(argBaseType));
3396 mlir::Value from = fir::getBase(value);
3397 if (argBaseType != fir::unwrapRefType(from.getType())) {
3398 // With logicals, it is possible that from is i1 here.
3399 if (fir::isa_ref_type(from.getType()))
3400 from = builder.create<fir::LoadOp>(loc, from);
3401 from = builder.createConvert(loc, argBaseType, from);
3402 }
3403 if (!fir::isa_ref_type(from.getType())) {
3404 mlir::Value temp = builder.createTemporary(loc, argBaseType);
3405 builder.create<fir::StoreOp>(loc, from, temp);
3406 from = temp;
3407 }
3408 return builder.createConvert(loc, argType, from);
3409 };
3410 assert(func.getNumArguments() == 2);
3411 mlir::Type lhsType = func.getFunctionType().getInput(0);
3412 mlir::Type rhsType = func.getFunctionType().getInput(1);
3413 mlir::Value lhsArg = prepareUserDefinedArg(builder, loc, lhs, lhsType);
3414 mlir::Value rhsArg = prepareUserDefinedArg(builder, loc, rhs, rhsType);
3415 builder.create<fir::CallOp>(loc, func, mlir::ValueRange{lhsArg, rhsArg});
3416 }
3417
3418 /// Convert the result of a fir.array_modify to an ExtendedValue given the
3419 /// related fir.array_load.
arrayModifyToExv(fir::FirOpBuilder & builder,mlir::Location loc,fir::ArrayLoadOp load,mlir::Value elementAddr)3420 static fir::ExtendedValue arrayModifyToExv(fir::FirOpBuilder &builder,
3421 mlir::Location loc,
3422 fir::ArrayLoadOp load,
3423 mlir::Value elementAddr) {
3424 mlir::Type eleTy = fir::unwrapPassByRefType(elementAddr.getType());
3425 if (fir::isa_char(eleTy)) {
3426 auto len = fir::factory::CharacterExprHelper{builder, loc}.getLength(
3427 load.getMemref());
3428 if (!len) {
3429 assert(load.getTypeparams().size() == 1 &&
3430 "length must be in array_load");
3431 len = load.getTypeparams()[0];
3432 }
3433 return fir::CharBoxValue{elementAddr, len};
3434 }
3435 return elementAddr;
3436 }
3437
3438 //===----------------------------------------------------------------------===//
3439 //
3440 // Lowering of scalar expressions in an explicit iteration space context.
3441 //
3442 //===----------------------------------------------------------------------===//
3443
3444 // Shared code for creating a copy of a derived type element. This function is
3445 // called from a continuation.
3446 inline static fir::ArrayAmendOp
createDerivedArrayAmend(mlir::Location loc,fir::ArrayLoadOp destLoad,fir::FirOpBuilder & builder,fir::ArrayAccessOp destAcc,const fir::ExtendedValue & elementExv,mlir::Type eleTy,mlir::Value innerArg)3447 createDerivedArrayAmend(mlir::Location loc, fir::ArrayLoadOp destLoad,
3448 fir::FirOpBuilder &builder, fir::ArrayAccessOp destAcc,
3449 const fir::ExtendedValue &elementExv, mlir::Type eleTy,
3450 mlir::Value innerArg) {
3451 if (destLoad.getTypeparams().empty()) {
3452 fir::factory::genRecordAssignment(builder, loc, destAcc, elementExv);
3453 } else {
3454 auto boxTy = fir::BoxType::get(eleTy);
3455 auto toBox = builder.create<fir::EmboxOp>(loc, boxTy, destAcc.getResult(),
3456 mlir::Value{}, mlir::Value{},
3457 destLoad.getTypeparams());
3458 auto fromBox = builder.create<fir::EmboxOp>(
3459 loc, boxTy, fir::getBase(elementExv), mlir::Value{}, mlir::Value{},
3460 destLoad.getTypeparams());
3461 fir::factory::genRecordAssignment(builder, loc, fir::BoxValue(toBox),
3462 fir::BoxValue(fromBox));
3463 }
3464 return builder.create<fir::ArrayAmendOp>(loc, innerArg.getType(), innerArg,
3465 destAcc);
3466 }
3467
3468 inline static fir::ArrayAmendOp
createCharArrayAmend(mlir::Location loc,fir::FirOpBuilder & builder,fir::ArrayAccessOp dstOp,mlir::Value & dstLen,const fir::ExtendedValue & srcExv,mlir::Value innerArg,llvm::ArrayRef<mlir::Value> bounds)3469 createCharArrayAmend(mlir::Location loc, fir::FirOpBuilder &builder,
3470 fir::ArrayAccessOp dstOp, mlir::Value &dstLen,
3471 const fir::ExtendedValue &srcExv, mlir::Value innerArg,
3472 llvm::ArrayRef<mlir::Value> bounds) {
3473 fir::CharBoxValue dstChar(dstOp, dstLen);
3474 fir::factory::CharacterExprHelper helper{builder, loc};
3475 if (!bounds.empty()) {
3476 dstChar = helper.createSubstring(dstChar, bounds);
3477 fir::factory::genCharacterCopy(fir::getBase(srcExv), fir::getLen(srcExv),
3478 dstChar.getAddr(), dstChar.getLen(), builder,
3479 loc);
3480 // Update the LEN to the substring's LEN.
3481 dstLen = dstChar.getLen();
3482 }
3483 // For a CHARACTER, we generate the element assignment loops inline.
3484 helper.createAssign(fir::ExtendedValue{dstChar}, srcExv);
3485 // Mark this array element as amended.
3486 mlir::Type ty = innerArg.getType();
3487 auto amend = builder.create<fir::ArrayAmendOp>(loc, ty, innerArg, dstOp);
3488 return amend;
3489 }
3490
3491 /// Build an ExtendedValue from a fir.array<?x...?xT> without actually setting
3492 /// the actual extents and lengths. This is only to allow their propagation as
3493 /// ExtendedValue without triggering verifier failures when propagating
3494 /// character/arrays as unboxed values. Only the base of the resulting
3495 /// ExtendedValue should be used, it is undefined to use the length or extents
3496 /// of the extended value returned,
3497 inline static fir::ExtendedValue
convertToArrayBoxValue(mlir::Location loc,fir::FirOpBuilder & builder,mlir::Value val,mlir::Value len)3498 convertToArrayBoxValue(mlir::Location loc, fir::FirOpBuilder &builder,
3499 mlir::Value val, mlir::Value len) {
3500 mlir::Type ty = fir::unwrapRefType(val.getType());
3501 mlir::IndexType idxTy = builder.getIndexType();
3502 auto seqTy = ty.cast<fir::SequenceType>();
3503 auto undef = builder.create<fir::UndefOp>(loc, idxTy);
3504 llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), undef);
3505 if (fir::isa_char(seqTy.getEleTy()))
3506 return fir::CharArrayBoxValue(val, len ? len : undef, extents);
3507 return fir::ArrayBoxValue(val, extents);
3508 }
3509
3510 //===----------------------------------------------------------------------===//
3511 //
3512 // Lowering of array expressions.
3513 //
3514 //===----------------------------------------------------------------------===//
3515
3516 namespace {
3517 class ArrayExprLowering {
3518 using ExtValue = fir::ExtendedValue;
3519
3520 /// Structure to keep track of lowered array operands in the
3521 /// array expression. Useful to later deduce the shape of the
3522 /// array expression.
3523 struct ArrayOperand {
3524 /// Array base (can be a fir.box).
3525 mlir::Value memref;
3526 /// ShapeOp, ShapeShiftOp or ShiftOp
3527 mlir::Value shape;
3528 /// SliceOp
3529 mlir::Value slice;
3530 /// Can this operand be absent ?
3531 bool mayBeAbsent = false;
3532 };
3533
3534 using ImplicitSubscripts = Fortran::lower::details::ImplicitSubscripts;
3535 using PathComponent = Fortran::lower::PathComponent;
3536
3537 /// Active iteration space.
3538 using IterationSpace = Fortran::lower::IterationSpace;
3539 using IterSpace = const Fortran::lower::IterationSpace &;
3540
3541 /// Current continuation. Function that will generate IR for a single
3542 /// iteration of the pending iterative loop structure.
3543 using CC = Fortran::lower::GenerateElementalArrayFunc;
3544
3545 /// Projection continuation. Function that will project one iteration space
3546 /// into another.
3547 using PC = std::function<IterationSpace(IterSpace)>;
3548 using ArrayBaseTy =
3549 std::variant<std::monostate, const Fortran::evaluate::ArrayRef *,
3550 const Fortran::evaluate::DataRef *>;
3551 using ComponentPath = Fortran::lower::ComponentPath;
3552
3553 public:
3554 //===--------------------------------------------------------------------===//
3555 // Regular array assignment
3556 //===--------------------------------------------------------------------===//
3557
3558 /// Entry point for array assignments. Both the left-hand and right-hand sides
3559 /// can either be ExtendedValue or evaluate::Expr.
3560 template <typename TL, typename TR>
lowerArrayAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const TL & lhs,const TR & rhs)3561 static void lowerArrayAssignment(Fortran::lower::AbstractConverter &converter,
3562 Fortran::lower::SymMap &symMap,
3563 Fortran::lower::StatementContext &stmtCtx,
3564 const TL &lhs, const TR &rhs) {
3565 ArrayExprLowering ael(converter, stmtCtx, symMap,
3566 ConstituentSemantics::CopyInCopyOut);
3567 ael.lowerArrayAssignment(lhs, rhs);
3568 }
3569
3570 template <typename TL, typename TR>
lowerArrayAssignment(const TL & lhs,const TR & rhs)3571 void lowerArrayAssignment(const TL &lhs, const TR &rhs) {
3572 mlir::Location loc = getLoc();
3573 /// Here the target subspace is not necessarily contiguous. The ArrayUpdate
3574 /// continuation is implicitly returned in `ccStoreToDest` and the ArrayLoad
3575 /// in `destination`.
3576 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
3577 ccStoreToDest = genarr(lhs);
3578 determineShapeOfDest(lhs);
3579 semant = ConstituentSemantics::RefTransparent;
3580 ExtValue exv = lowerArrayExpression(rhs);
3581 if (explicitSpaceIsActive()) {
3582 explicitSpace->finalizeContext();
3583 builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3584 } else {
3585 builder.create<fir::ArrayMergeStoreOp>(
3586 loc, destination, fir::getBase(exv), destination.getMemref(),
3587 destination.getSlice(), destination.getTypeparams());
3588 }
3589 }
3590
3591 //===--------------------------------------------------------------------===//
3592 // WHERE array assignment, FORALL assignment, and FORALL+WHERE array
3593 // assignment
3594 //===--------------------------------------------------------------------===//
3595
3596 /// Entry point for array assignment when the iteration space is explicitly
3597 /// defined (Fortran's FORALL) with or without masks, and/or the implied
3598 /// iteration space involves masks (Fortran's WHERE). Both contexts (explicit
3599 /// space and implicit space with masks) may be present.
lowerAnyMaskedArrayAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace)3600 static void lowerAnyMaskedArrayAssignment(
3601 Fortran::lower::AbstractConverter &converter,
3602 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3603 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3604 Fortran::lower::ExplicitIterSpace &explicitSpace,
3605 Fortran::lower::ImplicitIterSpace &implicitSpace) {
3606 if (explicitSpace.isActive() && lhs.Rank() == 0) {
3607 // Scalar assignment expression in a FORALL context.
3608 ArrayExprLowering ael(converter, stmtCtx, symMap,
3609 ConstituentSemantics::RefTransparent,
3610 &explicitSpace, &implicitSpace);
3611 ael.lowerScalarAssignment(lhs, rhs);
3612 return;
3613 }
3614 // Array assignment expression in a FORALL and/or WHERE context.
3615 ArrayExprLowering ael(converter, stmtCtx, symMap,
3616 ConstituentSemantics::CopyInCopyOut, &explicitSpace,
3617 &implicitSpace);
3618 ael.lowerArrayAssignment(lhs, rhs);
3619 }
3620
3621 //===--------------------------------------------------------------------===//
3622 // Array assignment to array of pointer box values.
3623 //===--------------------------------------------------------------------===//
3624
3625 /// Entry point for assignment to pointer in an array of pointers.
lowerArrayOfPointerAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace,const llvm::SmallVector<mlir::Value> & lbounds,llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds)3626 static void lowerArrayOfPointerAssignment(
3627 Fortran::lower::AbstractConverter &converter,
3628 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3629 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3630 Fortran::lower::ExplicitIterSpace &explicitSpace,
3631 Fortran::lower::ImplicitIterSpace &implicitSpace,
3632 const llvm::SmallVector<mlir::Value> &lbounds,
3633 llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds) {
3634 ArrayExprLowering ael(converter, stmtCtx, symMap,
3635 ConstituentSemantics::CopyInCopyOut, &explicitSpace,
3636 &implicitSpace);
3637 ael.lowerArrayOfPointerAssignment(lhs, rhs, lbounds, ubounds);
3638 }
3639
3640 /// Scalar pointer assignment in an explicit iteration space.
3641 ///
3642 /// Pointers may be bound to targets in a FORALL context. This is a scalar
3643 /// assignment in the sense there is never an implied iteration space, even if
3644 /// the pointer is to a target with non-zero rank. Since the pointer
3645 /// assignment must appear in a FORALL construct, correctness may require that
3646 /// the array of pointers follow copy-in/copy-out semantics. The pointer
3647 /// assignment may include a bounds-spec (lower bounds), a bounds-remapping
3648 /// (lower and upper bounds), or neither.
lowerArrayOfPointerAssignment(const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,const llvm::SmallVector<mlir::Value> & lbounds,llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds)3649 void lowerArrayOfPointerAssignment(
3650 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3651 const llvm::SmallVector<mlir::Value> &lbounds,
3652 llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds) {
3653 setPointerAssignmentBounds(lbounds, ubounds);
3654 if (rhs.Rank() == 0 ||
3655 (Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs) &&
3656 Fortran::evaluate::IsAllocatableOrPointerObject(
3657 rhs, converter.getFoldingContext()))) {
3658 lowerScalarAssignment(lhs, rhs);
3659 return;
3660 }
3661 TODO(getLoc(),
3662 "auto boxing of a ranked expression on RHS for pointer assignment");
3663 }
3664
3665 //===--------------------------------------------------------------------===//
3666 // Array assignment to allocatable array
3667 //===--------------------------------------------------------------------===//
3668
3669 /// Entry point for assignment to allocatable array.
lowerAllocatableArrayAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace)3670 static void lowerAllocatableArrayAssignment(
3671 Fortran::lower::AbstractConverter &converter,
3672 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3673 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
3674 Fortran::lower::ExplicitIterSpace &explicitSpace,
3675 Fortran::lower::ImplicitIterSpace &implicitSpace) {
3676 ArrayExprLowering ael(converter, stmtCtx, symMap,
3677 ConstituentSemantics::CopyInCopyOut, &explicitSpace,
3678 &implicitSpace);
3679 ael.lowerAllocatableArrayAssignment(lhs, rhs);
3680 }
3681
3682 /// Lower an assignment to allocatable array, where the LHS array
3683 /// is represented with \p lhs extended value produced in different
3684 /// branches created in genReallocIfNeeded(). The RHS lowering
3685 /// is provided via \p rhsCC continuation.
lowerAllocatableArrayAssignment(ExtValue lhs,CC rhsCC)3686 void lowerAllocatableArrayAssignment(ExtValue lhs, CC rhsCC) {
3687 mlir::Location loc = getLoc();
3688 // Check if the initial destShape is null, which means
3689 // it has not been computed from rhs (e.g. rhs is scalar).
3690 bool destShapeIsEmpty = destShape.empty();
3691 // Create ArrayLoad for the mutable box and save it into `destination`.
3692 PushSemantics(ConstituentSemantics::ProjectedCopyInCopyOut);
3693 ccStoreToDest = genarr(lhs);
3694 // destShape is either non-null on entry to this function,
3695 // or has been just set by lhs lowering.
3696 assert(!destShape.empty() && "destShape must have been set.");
3697 // Finish lowering the loop nest.
3698 assert(destination && "destination must have been set");
3699 ExtValue exv = lowerArrayExpression(rhsCC, destination.getType());
3700 if (!explicitSpaceIsActive())
3701 builder.create<fir::ArrayMergeStoreOp>(
3702 loc, destination, fir::getBase(exv), destination.getMemref(),
3703 destination.getSlice(), destination.getTypeparams());
3704 // destShape may originally be null, if rhs did not define a shape.
3705 // In this case the destShape is computed from lhs, and we may have
3706 // multiple different lhs values for different branches created
3707 // in genReallocIfNeeded(). We cannot reuse destShape computed
3708 // in different branches, so we have to reset it,
3709 // so that it is recomputed for the next branch FIR generation.
3710 if (destShapeIsEmpty)
3711 destShape.clear();
3712 }
3713
3714 /// Assignment to allocatable array.
3715 ///
3716 /// The semantics are reverse that of a "regular" array assignment. The rhs
3717 /// defines the iteration space of the computation and the lhs is
3718 /// resized/reallocated to fit if necessary.
lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs)3719 void lowerAllocatableArrayAssignment(const Fortran::lower::SomeExpr &lhs,
3720 const Fortran::lower::SomeExpr &rhs) {
3721 // With assignment to allocatable, we want to lower the rhs first and use
3722 // its shape to determine if we need to reallocate, etc.
3723 mlir::Location loc = getLoc();
3724 // FIXME: If the lhs is in an explicit iteration space, the assignment may
3725 // be to an array of allocatable arrays rather than a single allocatable
3726 // array.
3727 fir::MutableBoxValue mutableBox =
3728 Fortran::lower::createMutableBox(loc, converter, lhs, symMap);
3729 if (rhs.Rank() > 0)
3730 determineShapeOfDest(rhs);
3731 auto rhsCC = [&]() {
3732 PushSemantics(ConstituentSemantics::RefTransparent);
3733 return genarr(rhs);
3734 }();
3735
3736 llvm::SmallVector<mlir::Value> lengthParams;
3737 // Currently no safe way to gather length from rhs (at least for
3738 // character, it cannot be taken from array_loads since it may be
3739 // changed by concatenations).
3740 if ((mutableBox.isCharacter() && !mutableBox.hasNonDeferredLenParams()) ||
3741 mutableBox.isDerivedWithLenParameters())
3742 TODO(loc, "gather rhs LEN parameters in assignment to allocatable");
3743
3744 // The allocatable must take lower bounds from the expr if it is
3745 // reallocated and the right hand side is not a scalar.
3746 const bool takeLboundsIfRealloc = rhs.Rank() > 0;
3747 llvm::SmallVector<mlir::Value> lbounds;
3748 // When the reallocated LHS takes its lower bounds from the RHS,
3749 // they will be non default only if the RHS is a whole array
3750 // variable. Otherwise, lbounds is left empty and default lower bounds
3751 // will be used.
3752 if (takeLboundsIfRealloc &&
3753 Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(rhs)) {
3754 assert(arrayOperands.size() == 1 &&
3755 "lbounds can only come from one array");
3756 auto lbs = fir::factory::getOrigins(arrayOperands[0].shape);
3757 lbounds.append(lbs.begin(), lbs.end());
3758 }
3759 auto assignToStorage = [&](fir::ExtendedValue newLhs) {
3760 // The lambda will be called repeatedly by genReallocIfNeeded().
3761 lowerAllocatableArrayAssignment(newLhs, rhsCC);
3762 };
3763 fir::factory::MutableBoxReallocation realloc =
3764 fir::factory::genReallocIfNeeded(builder, loc, mutableBox, destShape,
3765 lengthParams, assignToStorage);
3766 if (explicitSpaceIsActive()) {
3767 explicitSpace->finalizeContext();
3768 builder.create<fir::ResultOp>(loc, fir::getBase(realloc.newValue));
3769 }
3770 fir::factory::finalizeRealloc(builder, loc, mutableBox, lbounds,
3771 takeLboundsIfRealloc, realloc);
3772 }
3773
3774 /// Entry point for when an array expression appears in a context where the
3775 /// result must be boxed. (BoxValue semantics.)
3776 static ExtValue
lowerBoxedArrayExpression(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & expr)3777 lowerBoxedArrayExpression(Fortran::lower::AbstractConverter &converter,
3778 Fortran::lower::SymMap &symMap,
3779 Fortran::lower::StatementContext &stmtCtx,
3780 const Fortran::lower::SomeExpr &expr) {
3781 ArrayExprLowering ael{converter, stmtCtx, symMap,
3782 ConstituentSemantics::BoxValue};
3783 return ael.lowerBoxedArrayExpr(expr);
3784 }
3785
lowerBoxedArrayExpr(const Fortran::lower::SomeExpr & exp)3786 ExtValue lowerBoxedArrayExpr(const Fortran::lower::SomeExpr &exp) {
3787 PushSemantics(ConstituentSemantics::BoxValue);
3788 return std::visit(
3789 [&](const auto &e) {
3790 auto f = genarr(e);
3791 ExtValue exv = f(IterationSpace{});
3792 if (fir::getBase(exv).getType().template isa<fir::BoxType>())
3793 return exv;
3794 fir::emitFatalError(getLoc(), "array must be emboxed");
3795 },
3796 exp.u);
3797 }
3798
3799 /// Entry point into lowering an expression with rank. This entry point is for
3800 /// lowering a rhs expression, for example. (RefTransparent semantics.)
3801 static ExtValue
lowerNewArrayExpression(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & expr)3802 lowerNewArrayExpression(Fortran::lower::AbstractConverter &converter,
3803 Fortran::lower::SymMap &symMap,
3804 Fortran::lower::StatementContext &stmtCtx,
3805 const Fortran::lower::SomeExpr &expr) {
3806 ArrayExprLowering ael{converter, stmtCtx, symMap};
3807 ael.determineShapeOfDest(expr);
3808 ExtValue loopRes = ael.lowerArrayExpression(expr);
3809 fir::ArrayLoadOp dest = ael.destination;
3810 mlir::Value tempRes = dest.getMemref();
3811 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
3812 mlir::Location loc = converter.getCurrentLocation();
3813 builder.create<fir::ArrayMergeStoreOp>(loc, dest, fir::getBase(loopRes),
3814 tempRes, dest.getSlice(),
3815 dest.getTypeparams());
3816
3817 auto arrTy =
3818 fir::dyn_cast_ptrEleTy(tempRes.getType()).cast<fir::SequenceType>();
3819 if (auto charTy =
3820 arrTy.getEleTy().template dyn_cast<fir::CharacterType>()) {
3821 if (fir::characterWithDynamicLen(charTy))
3822 TODO(loc, "CHARACTER does not have constant LEN");
3823 mlir::Value len = builder.createIntegerConstant(
3824 loc, builder.getCharacterLengthType(), charTy.getLen());
3825 return fir::CharArrayBoxValue(tempRes, len, dest.getExtents());
3826 }
3827 return fir::ArrayBoxValue(tempRes, dest.getExtents());
3828 }
3829
lowerLazyArrayExpression(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & expr,mlir::Value raggedHeader)3830 static void lowerLazyArrayExpression(
3831 Fortran::lower::AbstractConverter &converter,
3832 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
3833 const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader) {
3834 ArrayExprLowering ael(converter, stmtCtx, symMap);
3835 ael.lowerLazyArrayExpression(expr, raggedHeader);
3836 }
3837
3838 /// Lower the expression \p expr into a buffer that is created on demand. The
3839 /// variable containing the pointer to the buffer is \p var and the variable
3840 /// containing the shape of the buffer is \p shapeBuffer.
lowerLazyArrayExpression(const Fortran::lower::SomeExpr & expr,mlir::Value header)3841 void lowerLazyArrayExpression(const Fortran::lower::SomeExpr &expr,
3842 mlir::Value header) {
3843 mlir::Location loc = getLoc();
3844 mlir::TupleType hdrTy = fir::factory::getRaggedArrayHeaderType(builder);
3845 mlir::IntegerType i32Ty = builder.getIntegerType(32);
3846
3847 // Once the loop extents have been computed, which may require being inside
3848 // some explicit loops, lazily allocate the expression on the heap. The
3849 // following continuation creates the buffer as needed.
3850 ccPrelude = [=](llvm::ArrayRef<mlir::Value> shape) {
3851 mlir::IntegerType i64Ty = builder.getIntegerType(64);
3852 mlir::Value byteSize = builder.createIntegerConstant(loc, i64Ty, 1);
3853 fir::runtime::genRaggedArrayAllocate(
3854 loc, builder, header, /*asHeaders=*/false, byteSize, shape);
3855 };
3856
3857 // Create a dummy array_load before the loop. We're storing to a lazy
3858 // temporary, so there will be no conflict and no copy-in. TODO: skip this
3859 // as there isn't any necessity for it.
3860 ccLoadDest = [=](llvm::ArrayRef<mlir::Value> shape) -> fir::ArrayLoadOp {
3861 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3862 auto var = builder.create<fir::CoordinateOp>(
3863 loc, builder.getRefType(hdrTy.getType(1)), header, one);
3864 auto load = builder.create<fir::LoadOp>(loc, var);
3865 mlir::Type eleTy =
3866 fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3867 auto seqTy = fir::SequenceType::get(eleTy, shape.size());
3868 mlir::Value castTo =
3869 builder.createConvert(loc, fir::HeapType::get(seqTy), load);
3870 mlir::Value shapeOp = builder.genShape(loc, shape);
3871 return builder.create<fir::ArrayLoadOp>(
3872 loc, seqTy, castTo, shapeOp, /*slice=*/mlir::Value{}, llvm::None);
3873 };
3874 // Custom lowering of the element store to deal with the extra indirection
3875 // to the lazy allocated buffer.
3876 ccStoreToDest = [=](IterSpace iters) {
3877 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
3878 auto var = builder.create<fir::CoordinateOp>(
3879 loc, builder.getRefType(hdrTy.getType(1)), header, one);
3880 auto load = builder.create<fir::LoadOp>(loc, var);
3881 mlir::Type eleTy =
3882 fir::unwrapSequenceType(fir::unwrapRefType(load.getType()));
3883 auto seqTy = fir::SequenceType::get(eleTy, iters.iterVec().size());
3884 auto toTy = fir::HeapType::get(seqTy);
3885 mlir::Value castTo = builder.createConvert(loc, toTy, load);
3886 mlir::Value shape = builder.genShape(loc, genIterationShape());
3887 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
3888 loc, builder, castTo.getType(), shape, iters.iterVec());
3889 auto eleAddr = builder.create<fir::ArrayCoorOp>(
3890 loc, builder.getRefType(eleTy), castTo, shape,
3891 /*slice=*/mlir::Value{}, indices, destination.getTypeparams());
3892 mlir::Value eleVal =
3893 builder.createConvert(loc, eleTy, iters.getElement());
3894 builder.create<fir::StoreOp>(loc, eleVal, eleAddr);
3895 return iters.innerArgument();
3896 };
3897
3898 // Lower the array expression now. Clean-up any temps that may have
3899 // been generated when lowering `expr` right after the lowered value
3900 // was stored to the ragged array temporary. The local temps will not
3901 // be needed afterwards.
3902 stmtCtx.pushScope();
3903 [[maybe_unused]] ExtValue loopRes = lowerArrayExpression(expr);
3904 stmtCtx.finalizeAndPop();
3905 assert(fir::getBase(loopRes));
3906 }
3907
3908 static void
lowerElementalUserAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace,const Fortran::evaluate::ProcedureRef & procRef)3909 lowerElementalUserAssignment(Fortran::lower::AbstractConverter &converter,
3910 Fortran::lower::SymMap &symMap,
3911 Fortran::lower::StatementContext &stmtCtx,
3912 Fortran::lower::ExplicitIterSpace &explicitSpace,
3913 Fortran::lower::ImplicitIterSpace &implicitSpace,
3914 const Fortran::evaluate::ProcedureRef &procRef) {
3915 ArrayExprLowering ael(converter, stmtCtx, symMap,
3916 ConstituentSemantics::CustomCopyInCopyOut,
3917 &explicitSpace, &implicitSpace);
3918 assert(procRef.arguments().size() == 2);
3919 const auto *lhs = procRef.arguments()[0].value().UnwrapExpr();
3920 const auto *rhs = procRef.arguments()[1].value().UnwrapExpr();
3921 assert(lhs && rhs &&
3922 "user defined assignment arguments must be expressions");
3923 mlir::func::FuncOp func =
3924 Fortran::lower::CallerInterface(procRef, converter).getFuncOp();
3925 ael.lowerElementalUserAssignment(func, *lhs, *rhs);
3926 }
3927
lowerElementalUserAssignment(mlir::func::FuncOp userAssignment,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs)3928 void lowerElementalUserAssignment(mlir::func::FuncOp userAssignment,
3929 const Fortran::lower::SomeExpr &lhs,
3930 const Fortran::lower::SomeExpr &rhs) {
3931 mlir::Location loc = getLoc();
3932 PushSemantics(ConstituentSemantics::CustomCopyInCopyOut);
3933 auto genArrayModify = genarr(lhs);
3934 ccStoreToDest = [=](IterSpace iters) -> ExtValue {
3935 auto modifiedArray = genArrayModify(iters);
3936 auto arrayModify = mlir::dyn_cast_or_null<fir::ArrayModifyOp>(
3937 fir::getBase(modifiedArray).getDefiningOp());
3938 assert(arrayModify && "must be created by ArrayModifyOp");
3939 fir::ExtendedValue lhs =
3940 arrayModifyToExv(builder, loc, destination, arrayModify.getResult(0));
3941 genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, lhs,
3942 iters.elementExv());
3943 return modifiedArray;
3944 };
3945 determineShapeOfDest(lhs);
3946 semant = ConstituentSemantics::RefTransparent;
3947 auto exv = lowerArrayExpression(rhs);
3948 if (explicitSpaceIsActive()) {
3949 explicitSpace->finalizeContext();
3950 builder.create<fir::ResultOp>(loc, fir::getBase(exv));
3951 } else {
3952 builder.create<fir::ArrayMergeStoreOp>(
3953 loc, destination, fir::getBase(exv), destination.getMemref(),
3954 destination.getSlice(), destination.getTypeparams());
3955 }
3956 }
3957
3958 /// Lower an elemental subroutine call with at least one array argument.
3959 /// An elemental subroutine is an exception and does not have copy-in/copy-out
3960 /// semantics. See 15.8.3.
3961 /// Do NOT use this for user defined assignments.
3962 static void
lowerElementalSubroutine(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,const Fortran::lower::SomeExpr & call)3963 lowerElementalSubroutine(Fortran::lower::AbstractConverter &converter,
3964 Fortran::lower::SymMap &symMap,
3965 Fortran::lower::StatementContext &stmtCtx,
3966 const Fortran::lower::SomeExpr &call) {
3967 ArrayExprLowering ael(converter, stmtCtx, symMap,
3968 ConstituentSemantics::RefTransparent);
3969 ael.lowerElementalSubroutine(call);
3970 }
3971
3972 // TODO: See the comment in genarr(const Fortran::lower::Parentheses<T>&).
3973 // This is skipping generation of copy-in/copy-out code for analysis that is
3974 // required when arguments are in parentheses.
lowerElementalSubroutine(const Fortran::lower::SomeExpr & call)3975 void lowerElementalSubroutine(const Fortran::lower::SomeExpr &call) {
3976 auto f = genarr(call);
3977 llvm::SmallVector<mlir::Value> shape = genIterationShape();
3978 auto [iterSpace, insPt] = genImplicitLoops(shape, /*innerArg=*/{});
3979 f(iterSpace);
3980 finalizeElementCtx();
3981 builder.restoreInsertionPoint(insPt);
3982 }
3983
lowerScalarAssignment(const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs)3984 ExtValue lowerScalarAssignment(const Fortran::lower::SomeExpr &lhs,
3985 const Fortran::lower::SomeExpr &rhs) {
3986 PushSemantics(ConstituentSemantics::RefTransparent);
3987 // 1) Lower the rhs expression with array_fetch op(s).
3988 IterationSpace iters;
3989 iters.setElement(genarr(rhs)(iters));
3990 // 2) Lower the lhs expression to an array_update.
3991 semant = ConstituentSemantics::ProjectedCopyInCopyOut;
3992 auto lexv = genarr(lhs)(iters);
3993 // 3) Finalize the inner context.
3994 explicitSpace->finalizeContext();
3995 // 4) Thread the array value updated forward. Note: the lhs might be
3996 // ill-formed (performing scalar assignment in an array context),
3997 // in which case there is no array to thread.
3998 auto loc = getLoc();
3999 auto createResult = [&](auto op) {
4000 mlir::Value oldInnerArg = op.getSequence();
4001 std::size_t offset = explicitSpace->argPosition(oldInnerArg);
4002 explicitSpace->setInnerArg(offset, fir::getBase(lexv));
4003 finalizeElementCtx();
4004 builder.create<fir::ResultOp>(loc, fir::getBase(lexv));
4005 };
4006 if (mlir::Operation *defOp = fir::getBase(lexv).getDefiningOp()) {
4007 llvm::TypeSwitch<mlir::Operation *>(defOp)
4008 .Case([&](fir::ArrayUpdateOp op) { createResult(op); })
4009 .Case([&](fir::ArrayAmendOp op) { createResult(op); })
4010 .Case([&](fir::ArrayModifyOp op) { createResult(op); })
4011 .Default([&](mlir::Operation *) { finalizeElementCtx(); });
4012 } else {
4013 // `lhs` isn't from a `fir.array_load`, so there is no array modifications
4014 // to thread through the iteration space.
4015 finalizeElementCtx();
4016 }
4017 return lexv;
4018 }
4019
lowerScalarUserAssignment(Fortran::lower::AbstractConverter & converter,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx,Fortran::lower::ExplicitIterSpace & explicitIterSpace,mlir::func::FuncOp userAssignmentFunction,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs)4020 static ExtValue lowerScalarUserAssignment(
4021 Fortran::lower::AbstractConverter &converter,
4022 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx,
4023 Fortran::lower::ExplicitIterSpace &explicitIterSpace,
4024 mlir::func::FuncOp userAssignmentFunction,
4025 const Fortran::lower::SomeExpr &lhs,
4026 const Fortran::lower::SomeExpr &rhs) {
4027 Fortran::lower::ImplicitIterSpace implicit;
4028 ArrayExprLowering ael(converter, stmtCtx, symMap,
4029 ConstituentSemantics::RefTransparent,
4030 &explicitIterSpace, &implicit);
4031 return ael.lowerScalarUserAssignment(userAssignmentFunction, lhs, rhs);
4032 }
4033
lowerScalarUserAssignment(mlir::func::FuncOp userAssignment,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs)4034 ExtValue lowerScalarUserAssignment(mlir::func::FuncOp userAssignment,
4035 const Fortran::lower::SomeExpr &lhs,
4036 const Fortran::lower::SomeExpr &rhs) {
4037 mlir::Location loc = getLoc();
4038 if (rhs.Rank() > 0)
4039 TODO(loc, "user-defined elemental assigment from expression with rank");
4040 // 1) Lower the rhs expression with array_fetch op(s).
4041 IterationSpace iters;
4042 iters.setElement(genarr(rhs)(iters));
4043 fir::ExtendedValue elementalExv = iters.elementExv();
4044 // 2) Lower the lhs expression to an array_modify.
4045 semant = ConstituentSemantics::CustomCopyInCopyOut;
4046 auto lexv = genarr(lhs)(iters);
4047 bool isIllFormedLHS = false;
4048 // 3) Insert the call
4049 if (auto modifyOp = mlir::dyn_cast<fir::ArrayModifyOp>(
4050 fir::getBase(lexv).getDefiningOp())) {
4051 mlir::Value oldInnerArg = modifyOp.getSequence();
4052 std::size_t offset = explicitSpace->argPosition(oldInnerArg);
4053 explicitSpace->setInnerArg(offset, fir::getBase(lexv));
4054 fir::ExtendedValue exv =
4055 arrayModifyToExv(builder, loc, explicitSpace->getLhsLoad(0).value(),
4056 modifyOp.getResult(0));
4057 genScalarUserDefinedAssignmentCall(builder, loc, userAssignment, exv,
4058 elementalExv);
4059 } else {
4060 // LHS is ill formed, it is a scalar with no references to FORALL
4061 // subscripts, so there is actually no array assignment here. The user
4062 // code is probably bad, but still insert user assignment call since it
4063 // was not rejected by semantics (a warning was emitted).
4064 isIllFormedLHS = true;
4065 genScalarUserDefinedAssignmentCall(builder, getLoc(), userAssignment,
4066 lexv, elementalExv);
4067 }
4068 // 4) Finalize the inner context.
4069 explicitSpace->finalizeContext();
4070 // 5). Thread the array value updated forward.
4071 if (!isIllFormedLHS) {
4072 finalizeElementCtx();
4073 builder.create<fir::ResultOp>(getLoc(), fir::getBase(lexv));
4074 }
4075 return lexv;
4076 }
4077
4078 private:
determineShapeOfDest(const fir::ExtendedValue & lhs)4079 void determineShapeOfDest(const fir::ExtendedValue &lhs) {
4080 destShape = fir::factory::getExtents(getLoc(), builder, lhs);
4081 }
4082
determineShapeOfDest(const Fortran::lower::SomeExpr & lhs)4083 void determineShapeOfDest(const Fortran::lower::SomeExpr &lhs) {
4084 if (!destShape.empty())
4085 return;
4086 if (explicitSpaceIsActive() && determineShapeWithSlice(lhs))
4087 return;
4088 mlir::Type idxTy = builder.getIndexType();
4089 mlir::Location loc = getLoc();
4090 if (std::optional<Fortran::evaluate::ConstantSubscripts> constantShape =
4091 Fortran::evaluate::GetConstantExtents(converter.getFoldingContext(),
4092 lhs))
4093 for (Fortran::common::ConstantSubscript extent : *constantShape)
4094 destShape.push_back(builder.createIntegerConstant(loc, idxTy, extent));
4095 }
4096
genShapeFromDataRef(const Fortran::semantics::Symbol & x)4097 bool genShapeFromDataRef(const Fortran::semantics::Symbol &x) {
4098 return false;
4099 }
genShapeFromDataRef(const Fortran::evaluate::CoarrayRef &)4100 bool genShapeFromDataRef(const Fortran::evaluate::CoarrayRef &) {
4101 TODO(getLoc(), "coarray ref");
4102 return false;
4103 }
genShapeFromDataRef(const Fortran::evaluate::Component & x)4104 bool genShapeFromDataRef(const Fortran::evaluate::Component &x) {
4105 return x.base().Rank() > 0 ? genShapeFromDataRef(x.base()) : false;
4106 }
genShapeFromDataRef(const Fortran::evaluate::ArrayRef & x)4107 bool genShapeFromDataRef(const Fortran::evaluate::ArrayRef &x) {
4108 if (x.Rank() == 0)
4109 return false;
4110 if (x.base().Rank() > 0)
4111 if (genShapeFromDataRef(x.base()))
4112 return true;
4113 // x has rank and x.base did not produce a shape.
4114 ExtValue exv = x.base().IsSymbol() ? asScalarRef(getFirstSym(x.base()))
4115 : asScalarRef(x.base().GetComponent());
4116 mlir::Location loc = getLoc();
4117 mlir::IndexType idxTy = builder.getIndexType();
4118 llvm::SmallVector<mlir::Value> definedShape =
4119 fir::factory::getExtents(loc, builder, exv);
4120 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4121 for (auto ss : llvm::enumerate(x.subscript())) {
4122 std::visit(Fortran::common::visitors{
4123 [&](const Fortran::evaluate::Triplet &trip) {
4124 // For a subscript of triple notation, we compute the
4125 // range of this dimension of the iteration space.
4126 auto lo = [&]() {
4127 if (auto optLo = trip.lower())
4128 return fir::getBase(asScalar(*optLo));
4129 return getLBound(exv, ss.index(), one);
4130 }();
4131 auto hi = [&]() {
4132 if (auto optHi = trip.upper())
4133 return fir::getBase(asScalar(*optHi));
4134 return getUBound(exv, ss.index(), one);
4135 }();
4136 auto step = builder.createConvert(
4137 loc, idxTy, fir::getBase(asScalar(trip.stride())));
4138 auto extent = builder.genExtentFromTriplet(loc, lo, hi,
4139 step, idxTy);
4140 destShape.push_back(extent);
4141 },
4142 [&](auto) {}},
4143 ss.value().u);
4144 }
4145 return true;
4146 }
genShapeFromDataRef(const Fortran::evaluate::NamedEntity & x)4147 bool genShapeFromDataRef(const Fortran::evaluate::NamedEntity &x) {
4148 if (x.IsSymbol())
4149 return genShapeFromDataRef(getFirstSym(x));
4150 return genShapeFromDataRef(x.GetComponent());
4151 }
genShapeFromDataRef(const Fortran::evaluate::DataRef & x)4152 bool genShapeFromDataRef(const Fortran::evaluate::DataRef &x) {
4153 return std::visit([&](const auto &v) { return genShapeFromDataRef(v); },
4154 x.u);
4155 }
4156
4157 /// When in an explicit space, the ranked component must be evaluated to
4158 /// determine the actual number of iterations when slicing triples are
4159 /// present. Lower these expressions here.
determineShapeWithSlice(const Fortran::lower::SomeExpr & lhs)4160 bool determineShapeWithSlice(const Fortran::lower::SomeExpr &lhs) {
4161 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(
4162 llvm::dbgs() << "determine shape of:\n", lhs));
4163 // FIXME: We may not want to use ExtractDataRef here since it doesn't deal
4164 // with substrings, etc.
4165 std::optional<Fortran::evaluate::DataRef> dref =
4166 Fortran::evaluate::ExtractDataRef(lhs);
4167 return dref.has_value() ? genShapeFromDataRef(*dref) : false;
4168 }
4169
4170 /// CHARACTER and derived type elements are treated as memory references. The
4171 /// numeric types are treated as values.
adjustedArraySubtype(mlir::Type ty,mlir::ValueRange indices)4172 static mlir::Type adjustedArraySubtype(mlir::Type ty,
4173 mlir::ValueRange indices) {
4174 mlir::Type pathTy = fir::applyPathToType(ty, indices);
4175 assert(pathTy && "indices failed to apply to type");
4176 return adjustedArrayElementType(pathTy);
4177 }
4178
4179 /// Lower rhs of an array expression.
lowerArrayExpression(const Fortran::lower::SomeExpr & exp)4180 ExtValue lowerArrayExpression(const Fortran::lower::SomeExpr &exp) {
4181 mlir::Type resTy = converter.genType(exp);
4182 return std::visit(
4183 [&](const auto &e) { return lowerArrayExpression(genarr(e), resTy); },
4184 exp.u);
4185 }
lowerArrayExpression(const ExtValue & exv)4186 ExtValue lowerArrayExpression(const ExtValue &exv) {
4187 assert(!explicitSpace);
4188 mlir::Type resTy = fir::unwrapPassByRefType(fir::getBase(exv).getType());
4189 return lowerArrayExpression(genarr(exv), resTy);
4190 }
4191
populateBounds(llvm::SmallVectorImpl<mlir::Value> & bounds,const Fortran::evaluate::Substring * substring)4192 void populateBounds(llvm::SmallVectorImpl<mlir::Value> &bounds,
4193 const Fortran::evaluate::Substring *substring) {
4194 if (!substring)
4195 return;
4196 bounds.push_back(fir::getBase(asScalar(substring->lower())));
4197 if (auto upper = substring->upper())
4198 bounds.push_back(fir::getBase(asScalar(*upper)));
4199 }
4200
4201 /// Convert the original value, \p origVal, to type \p eleTy. When in a
4202 /// pointer assignment context, generate an appropriate `fir.rebox` for
4203 /// dealing with any bounds parameters on the pointer assignment.
convertElementForUpdate(mlir::Location loc,mlir::Type eleTy,mlir::Value origVal)4204 mlir::Value convertElementForUpdate(mlir::Location loc, mlir::Type eleTy,
4205 mlir::Value origVal) {
4206 if (auto origEleTy = fir::dyn_cast_ptrEleTy(origVal.getType()))
4207 if (origEleTy.isa<fir::BoxType>()) {
4208 // If origVal is a box variable, load it so it is in the value domain.
4209 origVal = builder.create<fir::LoadOp>(loc, origVal);
4210 }
4211 if (origVal.getType().isa<fir::BoxType>() && !eleTy.isa<fir::BoxType>()) {
4212 if (isPointerAssignment())
4213 TODO(loc, "lhs of pointer assignment returned unexpected value");
4214 TODO(loc, "invalid box conversion in elemental computation");
4215 }
4216 if (isPointerAssignment() && eleTy.isa<fir::BoxType>() &&
4217 !origVal.getType().isa<fir::BoxType>()) {
4218 // This is a pointer assignment and the rhs is a raw reference to a TARGET
4219 // in memory. Embox the reference so it can be stored to the boxed
4220 // POINTER variable.
4221 assert(fir::isa_ref_type(origVal.getType()));
4222 if (auto eleTy = fir::dyn_cast_ptrEleTy(origVal.getType());
4223 fir::hasDynamicSize(eleTy))
4224 TODO(loc, "TARGET of pointer assignment with runtime size/shape");
4225 auto memrefTy = fir::boxMemRefType(eleTy.cast<fir::BoxType>());
4226 auto castTo = builder.createConvert(loc, memrefTy, origVal);
4227 origVal = builder.create<fir::EmboxOp>(loc, eleTy, castTo);
4228 }
4229 mlir::Value val = builder.createConvert(loc, eleTy, origVal);
4230 if (isBoundsSpec()) {
4231 auto lbs = lbounds.value();
4232 if (lbs.size() > 0) {
4233 // Rebox the value with user-specified shift.
4234 auto shiftTy = fir::ShiftType::get(eleTy.getContext(), lbs.size());
4235 mlir::Value shiftOp = builder.create<fir::ShiftOp>(loc, shiftTy, lbs);
4236 val = builder.create<fir::ReboxOp>(loc, eleTy, val, shiftOp,
4237 mlir::Value{});
4238 }
4239 } else if (isBoundsRemap()) {
4240 auto lbs = lbounds.value();
4241 if (lbs.size() > 0) {
4242 // Rebox the value with user-specified shift and shape.
4243 auto shapeShiftArgs = flatZip(lbs, ubounds.value());
4244 auto shapeTy = fir::ShapeShiftType::get(eleTy.getContext(), lbs.size());
4245 mlir::Value shapeShift =
4246 builder.create<fir::ShapeShiftOp>(loc, shapeTy, shapeShiftArgs);
4247 val = builder.create<fir::ReboxOp>(loc, eleTy, val, shapeShift,
4248 mlir::Value{});
4249 }
4250 }
4251 return val;
4252 }
4253
4254 /// Default store to destination implementation.
4255 /// This implements the default case, which is to assign the value in
4256 /// `iters.element` into the destination array, `iters.innerArgument`. Handles
4257 /// by value and by reference assignment.
defaultStoreToDestination(const Fortran::evaluate::Substring * substring)4258 CC defaultStoreToDestination(const Fortran::evaluate::Substring *substring) {
4259 return [=](IterSpace iterSpace) -> ExtValue {
4260 mlir::Location loc = getLoc();
4261 mlir::Value innerArg = iterSpace.innerArgument();
4262 fir::ExtendedValue exv = iterSpace.elementExv();
4263 mlir::Type arrTy = innerArg.getType();
4264 mlir::Type eleTy = fir::applyPathToType(arrTy, iterSpace.iterVec());
4265 if (isAdjustedArrayElementType(eleTy)) {
4266 // The elemental update is in the memref domain. Under this semantics,
4267 // we must always copy the computed new element from its location in
4268 // memory into the destination array.
4269 mlir::Type resRefTy = builder.getRefType(eleTy);
4270 // Get a reference to the array element to be amended.
4271 auto arrayOp = builder.create<fir::ArrayAccessOp>(
4272 loc, resRefTy, innerArg, iterSpace.iterVec(),
4273 fir::factory::getTypeParams(loc, builder, destination));
4274 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
4275 llvm::SmallVector<mlir::Value> substringBounds;
4276 populateBounds(substringBounds, substring);
4277 mlir::Value dstLen = fir::factory::genLenOfCharacter(
4278 builder, loc, destination, iterSpace.iterVec(), substringBounds);
4279 fir::ArrayAmendOp amend = createCharArrayAmend(
4280 loc, builder, arrayOp, dstLen, exv, innerArg, substringBounds);
4281 return abstractArrayExtValue(amend, dstLen);
4282 }
4283 if (fir::isa_derived(eleTy)) {
4284 fir::ArrayAmendOp amend = createDerivedArrayAmend(
4285 loc, destination, builder, arrayOp, exv, eleTy, innerArg);
4286 return abstractArrayExtValue(amend /*FIXME: typeparams?*/);
4287 }
4288 assert(eleTy.isa<fir::SequenceType>() && "must be an array");
4289 TODO(loc, "array (as element) assignment");
4290 }
4291 // By value semantics. The element is being assigned by value.
4292 auto ele = convertElementForUpdate(loc, eleTy, fir::getBase(exv));
4293 auto update = builder.create<fir::ArrayUpdateOp>(
4294 loc, arrTy, innerArg, ele, iterSpace.iterVec(),
4295 destination.getTypeparams());
4296 return abstractArrayExtValue(update);
4297 };
4298 }
4299
4300 /// For an elemental array expression.
4301 /// 1. Lower the scalars and array loads.
4302 /// 2. Create the iteration space.
4303 /// 3. Create the element-by-element computation in the loop.
4304 /// 4. Return the resulting array value.
4305 /// If no destination was set in the array context, a temporary of
4306 /// \p resultTy will be created to hold the evaluated expression.
4307 /// Otherwise, \p resultTy is ignored and the expression is evaluated
4308 /// in the destination. \p f is a continuation built from an
4309 /// evaluate::Expr or an ExtendedValue.
lowerArrayExpression(CC f,mlir::Type resultTy)4310 ExtValue lowerArrayExpression(CC f, mlir::Type resultTy) {
4311 mlir::Location loc = getLoc();
4312 auto [iterSpace, insPt] = genIterSpace(resultTy);
4313 auto exv = f(iterSpace);
4314 iterSpace.setElement(std::move(exv));
4315 auto lambda = ccStoreToDest
4316 ? *ccStoreToDest
4317 : defaultStoreToDestination(/*substring=*/nullptr);
4318 mlir::Value updVal = fir::getBase(lambda(iterSpace));
4319 finalizeElementCtx();
4320 builder.create<fir::ResultOp>(loc, updVal);
4321 builder.restoreInsertionPoint(insPt);
4322 return abstractArrayExtValue(iterSpace.outerResult());
4323 }
4324
4325 /// Compute the shape of a slice.
computeSliceShape(mlir::Value slice)4326 llvm::SmallVector<mlir::Value> computeSliceShape(mlir::Value slice) {
4327 llvm::SmallVector<mlir::Value> slicedShape;
4328 auto slOp = mlir::cast<fir::SliceOp>(slice.getDefiningOp());
4329 mlir::Operation::operand_range triples = slOp.getTriples();
4330 mlir::IndexType idxTy = builder.getIndexType();
4331 mlir::Location loc = getLoc();
4332 for (unsigned i = 0, end = triples.size(); i < end; i += 3) {
4333 if (!mlir::isa_and_nonnull<fir::UndefOp>(
4334 triples[i + 1].getDefiningOp())) {
4335 // (..., lb:ub:step, ...) case: extent = max((ub-lb+step)/step, 0)
4336 // See Fortran 2018 9.5.3.3.2 section for more details.
4337 mlir::Value res = builder.genExtentFromTriplet(
4338 loc, triples[i], triples[i + 1], triples[i + 2], idxTy);
4339 slicedShape.emplace_back(res);
4340 } else {
4341 // do nothing. `..., i, ...` case, so dimension is dropped.
4342 }
4343 }
4344 return slicedShape;
4345 }
4346
4347 /// Get the shape from an ArrayOperand. The shape of the array is adjusted if
4348 /// the array was sliced.
getShape(ArrayOperand array)4349 llvm::SmallVector<mlir::Value> getShape(ArrayOperand array) {
4350 if (array.slice)
4351 return computeSliceShape(array.slice);
4352 if (array.memref.getType().isa<fir::BoxType>())
4353 return fir::factory::readExtents(builder, getLoc(),
4354 fir::BoxValue{array.memref});
4355 return fir::factory::getExtents(array.shape);
4356 }
4357
4358 /// Get the shape from an ArrayLoad.
getShape(fir::ArrayLoadOp arrayLoad)4359 llvm::SmallVector<mlir::Value> getShape(fir::ArrayLoadOp arrayLoad) {
4360 return getShape(ArrayOperand{arrayLoad.getMemref(), arrayLoad.getShape(),
4361 arrayLoad.getSlice()});
4362 }
4363
4364 /// Returns the first array operand that may not be absent. If all
4365 /// array operands may be absent, return the first one.
getInducingShapeArrayOperand() const4366 const ArrayOperand &getInducingShapeArrayOperand() const {
4367 assert(!arrayOperands.empty());
4368 for (const ArrayOperand &op : arrayOperands)
4369 if (!op.mayBeAbsent)
4370 return op;
4371 // If all arrays operand appears in optional position, then none of them
4372 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the
4373 // first operands.
4374 // TODO: There is an opportunity to add a runtime check here that
4375 // this array is present as required.
4376 return arrayOperands[0];
4377 }
4378
4379 /// Generate the shape of the iteration space over the array expression. The
4380 /// iteration space may be implicit, explicit, or both. If it is implied it is
4381 /// based on the destination and operand array loads, or an optional
4382 /// Fortran::evaluate::Shape from the front end. If the shape is explicit,
4383 /// this returns any implicit shape component, if it exists.
genIterationShape()4384 llvm::SmallVector<mlir::Value> genIterationShape() {
4385 // Use the precomputed destination shape.
4386 if (!destShape.empty())
4387 return destShape;
4388 // Otherwise, use the destination's shape.
4389 if (destination)
4390 return getShape(destination);
4391 // Otherwise, use the first ArrayLoad operand shape.
4392 if (!arrayOperands.empty())
4393 return getShape(getInducingShapeArrayOperand());
4394 fir::emitFatalError(getLoc(),
4395 "failed to compute the array expression shape");
4396 }
4397
explicitSpaceIsActive() const4398 bool explicitSpaceIsActive() const {
4399 return explicitSpace && explicitSpace->isActive();
4400 }
4401
implicitSpaceHasMasks() const4402 bool implicitSpaceHasMasks() const {
4403 return implicitSpace && !implicitSpace->empty();
4404 }
4405
genMaskAccess(mlir::Value tmp,mlir::Value shape)4406 CC genMaskAccess(mlir::Value tmp, mlir::Value shape) {
4407 mlir::Location loc = getLoc();
4408 return [=, builder = &converter.getFirOpBuilder()](IterSpace iters) {
4409 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(tmp.getType());
4410 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
4411 mlir::Type eleRefTy = builder->getRefType(eleTy);
4412 mlir::IntegerType i1Ty = builder->getI1Type();
4413 // Adjust indices for any shift of the origin of the array.
4414 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
4415 loc, *builder, tmp.getType(), shape, iters.iterVec());
4416 auto addr =
4417 builder->create<fir::ArrayCoorOp>(loc, eleRefTy, tmp, shape,
4418 /*slice=*/mlir::Value{}, indices,
4419 /*typeParams=*/llvm::None);
4420 auto load = builder->create<fir::LoadOp>(loc, addr);
4421 return builder->createConvert(loc, i1Ty, load);
4422 };
4423 }
4424
4425 /// Construct the incremental instantiations of the ragged array structure.
4426 /// Rebind the lazy buffer variable, etc. as we go.
4427 template <bool withAllocation = false>
prepareRaggedArrays(Fortran::lower::FrontEndExpr expr)4428 mlir::Value prepareRaggedArrays(Fortran::lower::FrontEndExpr expr) {
4429 assert(explicitSpaceIsActive());
4430 mlir::Location loc = getLoc();
4431 mlir::TupleType raggedTy = fir::factory::getRaggedArrayHeaderType(builder);
4432 llvm::SmallVector<llvm::SmallVector<fir::DoLoopOp>> loopStack =
4433 explicitSpace->getLoopStack();
4434 const std::size_t depth = loopStack.size();
4435 mlir::IntegerType i64Ty = builder.getIntegerType(64);
4436 [[maybe_unused]] mlir::Value byteSize =
4437 builder.createIntegerConstant(loc, i64Ty, 1);
4438 mlir::Value header = implicitSpace->lookupMaskHeader(expr);
4439 for (std::remove_const_t<decltype(depth)> i = 0; i < depth; ++i) {
4440 auto insPt = builder.saveInsertionPoint();
4441 if (i < depth - 1)
4442 builder.setInsertionPoint(loopStack[i + 1][0]);
4443
4444 // Compute and gather the extents.
4445 llvm::SmallVector<mlir::Value> extents;
4446 for (auto doLoop : loopStack[i])
4447 extents.push_back(builder.genExtentFromTriplet(
4448 loc, doLoop.getLowerBound(), doLoop.getUpperBound(),
4449 doLoop.getStep(), i64Ty));
4450 if constexpr (withAllocation) {
4451 fir::runtime::genRaggedArrayAllocate(
4452 loc, builder, header, /*asHeader=*/true, byteSize, extents);
4453 }
4454
4455 // Compute the dynamic position into the header.
4456 llvm::SmallVector<mlir::Value> offsets;
4457 for (auto doLoop : loopStack[i]) {
4458 auto m = builder.create<mlir::arith::SubIOp>(
4459 loc, doLoop.getInductionVar(), doLoop.getLowerBound());
4460 auto n = builder.create<mlir::arith::DivSIOp>(loc, m, doLoop.getStep());
4461 mlir::Value one = builder.createIntegerConstant(loc, n.getType(), 1);
4462 offsets.push_back(builder.create<mlir::arith::AddIOp>(loc, n, one));
4463 }
4464 mlir::IntegerType i32Ty = builder.getIntegerType(32);
4465 mlir::Value uno = builder.createIntegerConstant(loc, i32Ty, 1);
4466 mlir::Type coorTy = builder.getRefType(raggedTy.getType(1));
4467 auto hdOff = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
4468 auto toTy = fir::SequenceType::get(raggedTy, offsets.size());
4469 mlir::Type toRefTy = builder.getRefType(toTy);
4470 auto ldHdr = builder.create<fir::LoadOp>(loc, hdOff);
4471 mlir::Value hdArr = builder.createConvert(loc, toRefTy, ldHdr);
4472 auto shapeOp = builder.genShape(loc, extents);
4473 header = builder.create<fir::ArrayCoorOp>(
4474 loc, builder.getRefType(raggedTy), hdArr, shapeOp,
4475 /*slice=*/mlir::Value{}, offsets,
4476 /*typeparams=*/mlir::ValueRange{});
4477 auto hdrVar = builder.create<fir::CoordinateOp>(loc, coorTy, header, uno);
4478 auto inVar = builder.create<fir::LoadOp>(loc, hdrVar);
4479 mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
4480 mlir::Type coorTy2 = builder.getRefType(raggedTy.getType(2));
4481 auto hdrSh = builder.create<fir::CoordinateOp>(loc, coorTy2, header, two);
4482 auto shapePtr = builder.create<fir::LoadOp>(loc, hdrSh);
4483 // Replace the binding.
4484 implicitSpace->rebind(expr, genMaskAccess(inVar, shapePtr));
4485 if (i < depth - 1)
4486 builder.restoreInsertionPoint(insPt);
4487 }
4488 return header;
4489 }
4490
4491 /// Lower mask expressions with implied iteration spaces from the variants of
4492 /// WHERE syntax. Since it is legal for mask expressions to have side-effects
4493 /// and modify values that will be used for the lhs, rhs, or both of
4494 /// subsequent assignments, the mask must be evaluated before the assignment
4495 /// is processed.
4496 /// Mask expressions are array expressions too.
genMasks()4497 void genMasks() {
4498 // Lower the mask expressions, if any.
4499 if (implicitSpaceHasMasks()) {
4500 mlir::Location loc = getLoc();
4501 // Mask expressions are array expressions too.
4502 for (const auto *e : implicitSpace->getExprs())
4503 if (e && !implicitSpace->isLowered(e)) {
4504 if (mlir::Value var = implicitSpace->lookupMaskVariable(e)) {
4505 // Allocate the mask buffer lazily.
4506 assert(explicitSpaceIsActive());
4507 mlir::Value header =
4508 prepareRaggedArrays</*withAllocations=*/true>(e);
4509 Fortran::lower::createLazyArrayTempValue(converter, *e, header,
4510 symMap, stmtCtx);
4511 // Close the explicit loops.
4512 builder.create<fir::ResultOp>(loc, explicitSpace->getInnerArgs());
4513 builder.setInsertionPointAfter(explicitSpace->getOuterLoop());
4514 // Open a new copy of the explicit loop nest.
4515 explicitSpace->genLoopNest();
4516 continue;
4517 }
4518 fir::ExtendedValue tmp = Fortran::lower::createSomeArrayTempValue(
4519 converter, *e, symMap, stmtCtx);
4520 mlir::Value shape = builder.createShape(loc, tmp);
4521 implicitSpace->bind(e, genMaskAccess(fir::getBase(tmp), shape));
4522 }
4523
4524 // Set buffer from the header.
4525 for (const auto *e : implicitSpace->getExprs()) {
4526 if (!e)
4527 continue;
4528 if (implicitSpace->lookupMaskVariable(e)) {
4529 // Index into the ragged buffer to retrieve cached results.
4530 const int rank = e->Rank();
4531 assert(destShape.empty() ||
4532 static_cast<std::size_t>(rank) == destShape.size());
4533 mlir::Value header = prepareRaggedArrays(e);
4534 mlir::TupleType raggedTy =
4535 fir::factory::getRaggedArrayHeaderType(builder);
4536 mlir::IntegerType i32Ty = builder.getIntegerType(32);
4537 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1);
4538 auto coor1 = builder.create<fir::CoordinateOp>(
4539 loc, builder.getRefType(raggedTy.getType(1)), header, one);
4540 auto db = builder.create<fir::LoadOp>(loc, coor1);
4541 mlir::Type eleTy =
4542 fir::unwrapSequenceType(fir::unwrapRefType(db.getType()));
4543 mlir::Type buffTy =
4544 builder.getRefType(fir::SequenceType::get(eleTy, rank));
4545 // Address of ragged buffer data.
4546 mlir::Value buff = builder.createConvert(loc, buffTy, db);
4547
4548 mlir::Value two = builder.createIntegerConstant(loc, i32Ty, 2);
4549 auto coor2 = builder.create<fir::CoordinateOp>(
4550 loc, builder.getRefType(raggedTy.getType(2)), header, two);
4551 auto shBuff = builder.create<fir::LoadOp>(loc, coor2);
4552 mlir::IntegerType i64Ty = builder.getIntegerType(64);
4553 mlir::IndexType idxTy = builder.getIndexType();
4554 llvm::SmallVector<mlir::Value> extents;
4555 for (std::remove_const_t<decltype(rank)> i = 0; i < rank; ++i) {
4556 mlir::Value off = builder.createIntegerConstant(loc, i32Ty, i);
4557 auto coor = builder.create<fir::CoordinateOp>(
4558 loc, builder.getRefType(i64Ty), shBuff, off);
4559 auto ldExt = builder.create<fir::LoadOp>(loc, coor);
4560 extents.push_back(builder.createConvert(loc, idxTy, ldExt));
4561 }
4562 if (destShape.empty())
4563 destShape = extents;
4564 // Construct shape of buffer.
4565 mlir::Value shapeOp = builder.genShape(loc, extents);
4566
4567 // Replace binding with the local result.
4568 implicitSpace->rebind(e, genMaskAccess(buff, shapeOp));
4569 }
4570 }
4571 }
4572 }
4573
4574 // FIXME: should take multiple inner arguments.
4575 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
genImplicitLoops(mlir::ValueRange shape,mlir::Value innerArg)4576 genImplicitLoops(mlir::ValueRange shape, mlir::Value innerArg) {
4577 mlir::Location loc = getLoc();
4578 mlir::IndexType idxTy = builder.getIndexType();
4579 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
4580 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
4581 llvm::SmallVector<mlir::Value> loopUppers;
4582
4583 // Convert any implied shape to closed interval form. The fir.do_loop will
4584 // run from 0 to `extent - 1` inclusive.
4585 for (auto extent : shape)
4586 loopUppers.push_back(
4587 builder.create<mlir::arith::SubIOp>(loc, extent, one));
4588
4589 // Iteration space is created with outermost columns, innermost rows
4590 llvm::SmallVector<fir::DoLoopOp> loops;
4591
4592 const std::size_t loopDepth = loopUppers.size();
4593 llvm::SmallVector<mlir::Value> ivars;
4594
4595 for (auto i : llvm::enumerate(llvm::reverse(loopUppers))) {
4596 if (i.index() > 0) {
4597 assert(!loops.empty());
4598 builder.setInsertionPointToStart(loops.back().getBody());
4599 }
4600 fir::DoLoopOp loop;
4601 if (innerArg) {
4602 loop = builder.create<fir::DoLoopOp>(
4603 loc, zero, i.value(), one, isUnordered(),
4604 /*finalCount=*/false, mlir::ValueRange{innerArg});
4605 innerArg = loop.getRegionIterArgs().front();
4606 if (explicitSpaceIsActive())
4607 explicitSpace->setInnerArg(0, innerArg);
4608 } else {
4609 loop = builder.create<fir::DoLoopOp>(loc, zero, i.value(), one,
4610 isUnordered(),
4611 /*finalCount=*/false);
4612 }
4613 ivars.push_back(loop.getInductionVar());
4614 loops.push_back(loop);
4615 }
4616
4617 if (innerArg)
4618 for (std::remove_const_t<decltype(loopDepth)> i = 0; i + 1 < loopDepth;
4619 ++i) {
4620 builder.setInsertionPointToEnd(loops[i].getBody());
4621 builder.create<fir::ResultOp>(loc, loops[i + 1].getResult(0));
4622 }
4623
4624 // Move insertion point to the start of the innermost loop in the nest.
4625 builder.setInsertionPointToStart(loops.back().getBody());
4626 // Set `afterLoopNest` to just after the entire loop nest.
4627 auto currPt = builder.saveInsertionPoint();
4628 builder.setInsertionPointAfter(loops[0]);
4629 auto afterLoopNest = builder.saveInsertionPoint();
4630 builder.restoreInsertionPoint(currPt);
4631
4632 // Put the implicit loop variables in row to column order to match FIR's
4633 // Ops. (The loops were constructed from outermost column to innermost
4634 // row.)
4635 mlir::Value outerRes = loops[0].getResult(0);
4636 return {IterationSpace(innerArg, outerRes, llvm::reverse(ivars)),
4637 afterLoopNest};
4638 }
4639
4640 /// Build the iteration space into which the array expression will be lowered.
4641 /// The resultType is used to create a temporary, if needed.
4642 std::pair<IterationSpace, mlir::OpBuilder::InsertPoint>
genIterSpace(mlir::Type resultType)4643 genIterSpace(mlir::Type resultType) {
4644 mlir::Location loc = getLoc();
4645 llvm::SmallVector<mlir::Value> shape = genIterationShape();
4646 if (!destination) {
4647 // Allocate storage for the result if it is not already provided.
4648 destination = createAndLoadSomeArrayTemp(resultType, shape);
4649 }
4650
4651 // Generate the lazy mask allocation, if one was given.
4652 if (ccPrelude)
4653 (*ccPrelude)(shape);
4654
4655 // Now handle the implicit loops.
4656 mlir::Value inner = explicitSpaceIsActive()
4657 ? explicitSpace->getInnerArgs().front()
4658 : destination.getResult();
4659 auto [iters, afterLoopNest] = genImplicitLoops(shape, inner);
4660 mlir::Value innerArg = iters.innerArgument();
4661
4662 // Generate the mask conditional structure, if there are masks. Unlike the
4663 // explicit masks, which are interleaved, these mask expression appear in
4664 // the innermost loop.
4665 if (implicitSpaceHasMasks()) {
4666 // Recover the cached condition from the mask buffer.
4667 auto genCond = [&](Fortran::lower::FrontEndExpr e, IterSpace iters) {
4668 return implicitSpace->getBoundClosure(e)(iters);
4669 };
4670
4671 // Handle the negated conditions in topological order of the WHERE
4672 // clauses. See 10.2.3.2p4 as to why this control structure is produced.
4673 for (llvm::SmallVector<Fortran::lower::FrontEndExpr> maskExprs :
4674 implicitSpace->getMasks()) {
4675 const std::size_t size = maskExprs.size() - 1;
4676 auto genFalseBlock = [&](const auto *e, auto &&cond) {
4677 auto ifOp = builder.create<fir::IfOp>(
4678 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
4679 /*withElseRegion=*/true);
4680 builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
4681 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
4682 builder.create<fir::ResultOp>(loc, innerArg);
4683 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
4684 };
4685 auto genTrueBlock = [&](const auto *e, auto &&cond) {
4686 auto ifOp = builder.create<fir::IfOp>(
4687 loc, mlir::TypeRange{innerArg.getType()}, fir::getBase(cond),
4688 /*withElseRegion=*/true);
4689 builder.create<fir::ResultOp>(loc, ifOp.getResult(0));
4690 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
4691 builder.create<fir::ResultOp>(loc, innerArg);
4692 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
4693 };
4694 for (std::remove_const_t<decltype(size)> i = 0; i < size; ++i)
4695 if (const auto *e = maskExprs[i])
4696 genFalseBlock(e, genCond(e, iters));
4697
4698 // The last condition is either non-negated or unconditionally negated.
4699 if (const auto *e = maskExprs[size])
4700 genTrueBlock(e, genCond(e, iters));
4701 }
4702 }
4703
4704 // We're ready to lower the body (an assignment statement) for this context
4705 // of loop nests at this point.
4706 return {iters, afterLoopNest};
4707 }
4708
4709 fir::ArrayLoadOp
createAndLoadSomeArrayTemp(mlir::Type type,llvm::ArrayRef<mlir::Value> shape)4710 createAndLoadSomeArrayTemp(mlir::Type type,
4711 llvm::ArrayRef<mlir::Value> shape) {
4712 if (ccLoadDest)
4713 return (*ccLoadDest)(shape);
4714 auto seqTy = type.dyn_cast<fir::SequenceType>();
4715 assert(seqTy && "must be an array");
4716 mlir::Location loc = getLoc();
4717 // TODO: Need to thread the LEN parameters here. For character, they may
4718 // differ from the operands length (e.g concatenation). So the array loads
4719 // type parameters are not enough.
4720 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>())
4721 if (charTy.hasDynamicLen())
4722 TODO(loc, "character array expression temp with dynamic length");
4723 if (auto recTy = seqTy.getEleTy().dyn_cast<fir::RecordType>())
4724 if (recTy.getNumLenParams() > 0)
4725 TODO(loc, "derived type array expression temp with LEN parameters");
4726 if (mlir::Type eleTy = fir::unwrapSequenceType(type);
4727 fir::isRecordWithAllocatableMember(eleTy))
4728 TODO(loc, "creating an array temp where the element type has "
4729 "allocatable members");
4730 mlir::Value temp = seqTy.hasConstantShape()
4731 ? builder.create<fir::AllocMemOp>(loc, type)
4732 : builder.create<fir::AllocMemOp>(
4733 loc, type, ".array.expr", llvm::None, shape);
4734 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
4735 stmtCtx.attachCleanup(
4736 [bldr, loc, temp]() { bldr->create<fir::FreeMemOp>(loc, temp); });
4737 mlir::Value shapeOp = genShapeOp(shape);
4738 return builder.create<fir::ArrayLoadOp>(loc, seqTy, temp, shapeOp,
4739 /*slice=*/mlir::Value{},
4740 llvm::None);
4741 }
4742
genShapeOp(mlir::Location loc,fir::FirOpBuilder & builder,llvm::ArrayRef<mlir::Value> shape)4743 static fir::ShapeOp genShapeOp(mlir::Location loc, fir::FirOpBuilder &builder,
4744 llvm::ArrayRef<mlir::Value> shape) {
4745 mlir::IndexType idxTy = builder.getIndexType();
4746 llvm::SmallVector<mlir::Value> idxShape;
4747 for (auto s : shape)
4748 idxShape.push_back(builder.createConvert(loc, idxTy, s));
4749 auto shapeTy = fir::ShapeType::get(builder.getContext(), idxShape.size());
4750 return builder.create<fir::ShapeOp>(loc, shapeTy, idxShape);
4751 }
4752
genShapeOp(llvm::ArrayRef<mlir::Value> shape)4753 fir::ShapeOp genShapeOp(llvm::ArrayRef<mlir::Value> shape) {
4754 return genShapeOp(getLoc(), builder, shape);
4755 }
4756
4757 //===--------------------------------------------------------------------===//
4758 // Expression traversal and lowering.
4759 //===--------------------------------------------------------------------===//
4760
4761 /// Lower the expression, \p x, in a scalar context.
4762 template <typename A>
asScalar(const A & x)4763 ExtValue asScalar(const A &x) {
4764 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.genval(x);
4765 }
4766
4767 /// Lower the expression, \p x, in a scalar context. If this is an explicit
4768 /// space, the expression may be scalar and refer to an array. We want to
4769 /// raise the array access to array operations in FIR to analyze potential
4770 /// conflicts even when the result is a scalar element.
4771 template <typename A>
asScalarArray(const A & x)4772 ExtValue asScalarArray(const A &x) {
4773 return explicitSpaceIsActive() && !isPointerAssignment()
4774 ? genarr(x)(IterationSpace{})
4775 : asScalar(x);
4776 }
4777
4778 /// Lower the expression in a scalar context to a memory reference.
4779 template <typename A>
asScalarRef(const A & x)4780 ExtValue asScalarRef(const A &x) {
4781 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}.gen(x);
4782 }
4783
4784 /// Lower an expression without dereferencing any indirection that may be
4785 /// a nullptr (because this is an absent optional or unallocated/disassociated
4786 /// descriptor). The returned expression cannot be addressed directly, it is
4787 /// meant to inquire about its status before addressing the related entity.
4788 template <typename A>
asInquired(const A & x)4789 ExtValue asInquired(const A &x) {
4790 return ScalarExprLowering{getLoc(), converter, symMap, stmtCtx}
4791 .lowerIntrinsicArgumentAsInquired(x);
4792 }
4793
4794 /// Some temporaries are allocated on an element-by-element basis during the
4795 /// array expression evaluation. Collect the cleanups here so the resources
4796 /// can be freed before the next loop iteration, avoiding memory leaks. etc.
getElementCtx()4797 Fortran::lower::StatementContext &getElementCtx() {
4798 if (!elementCtx) {
4799 stmtCtx.pushScope();
4800 elementCtx = true;
4801 }
4802 return stmtCtx;
4803 }
4804
4805 /// If there were temporaries created for this element evaluation, finalize
4806 /// and deallocate the resources now. This should be done just prior the the
4807 /// fir::ResultOp at the end of the innermost loop.
finalizeElementCtx()4808 void finalizeElementCtx() {
4809 if (elementCtx) {
4810 stmtCtx.finalizeAndPop();
4811 elementCtx = false;
4812 }
4813 }
4814
4815 /// Lower an elemental function array argument. This ensures array
4816 /// sub-expressions that are not variables and must be passed by address
4817 /// are lowered by value and placed in memory.
4818 template <typename A>
genElementalArgument(const A & x)4819 CC genElementalArgument(const A &x) {
4820 // Ensure the returned element is in memory if this is what was requested.
4821 if ((semant == ConstituentSemantics::RefOpaque ||
4822 semant == ConstituentSemantics::DataAddr ||
4823 semant == ConstituentSemantics::ByValueArg)) {
4824 if (!Fortran::evaluate::IsVariable(x)) {
4825 PushSemantics(ConstituentSemantics::DataValue);
4826 CC cc = genarr(x);
4827 mlir::Location loc = getLoc();
4828 if (isParenthesizedVariable(x)) {
4829 // Parenthesised variables are lowered to a reference to the variable
4830 // storage. When passing it as an argument, a copy must be passed.
4831 return [=](IterSpace iters) -> ExtValue {
4832 return createInMemoryScalarCopy(builder, loc, cc(iters));
4833 };
4834 }
4835 mlir::Type storageType =
4836 fir::unwrapSequenceType(converter.genType(toEvExpr(x)));
4837 return [=](IterSpace iters) -> ExtValue {
4838 return placeScalarValueInMemory(builder, loc, cc(iters), storageType);
4839 };
4840 }
4841 }
4842 return genarr(x);
4843 }
4844
4845 // A reference to a Fortran elemental intrinsic or intrinsic module procedure.
genElementalIntrinsicProcRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> retTy,llvm::Optional<const Fortran::evaluate::SpecificIntrinsic> intrinsic=llvm::None)4846 CC genElementalIntrinsicProcRef(
4847 const Fortran::evaluate::ProcedureRef &procRef,
4848 llvm::Optional<mlir::Type> retTy,
4849 llvm::Optional<const Fortran::evaluate::SpecificIntrinsic> intrinsic =
4850 llvm::None) {
4851
4852 llvm::SmallVector<CC> operands;
4853 std::string name =
4854 intrinsic ? intrinsic->name
4855 : procRef.proc().GetSymbol()->GetUltimate().name().ToString();
4856 const Fortran::lower::IntrinsicArgumentLoweringRules *argLowering =
4857 Fortran::lower::getIntrinsicArgumentLowering(name);
4858 mlir::Location loc = getLoc();
4859 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling(
4860 procRef, *intrinsic, converter)) {
4861 using CcPairT = std::pair<CC, llvm::Optional<mlir::Value>>;
4862 llvm::SmallVector<CcPairT> operands;
4863 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) {
4864 if (expr.Rank() == 0) {
4865 ExtValue optionalArg = this->asInquired(expr);
4866 mlir::Value isPresent =
4867 genActualIsPresentTest(builder, loc, optionalArg);
4868 operands.emplace_back(
4869 [=](IterSpace iters) -> ExtValue {
4870 return genLoad(builder, loc, optionalArg);
4871 },
4872 isPresent);
4873 } else {
4874 auto [cc, isPresent, _] = this->genOptionalArrayFetch(expr);
4875 operands.emplace_back(cc, isPresent);
4876 }
4877 };
4878 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr) {
4879 PushSemantics(ConstituentSemantics::RefTransparent);
4880 operands.emplace_back(genElementalArgument(expr), llvm::None);
4881 };
4882 Fortran::lower::prepareCustomIntrinsicArgument(
4883 procRef, *intrinsic, retTy, prepareOptionalArg, prepareOtherArg,
4884 converter);
4885
4886 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
4887 return [=](IterSpace iters) -> ExtValue {
4888 auto getArgument = [&](std::size_t i) -> ExtValue {
4889 return operands[i].first(iters);
4890 };
4891 auto isPresent = [&](std::size_t i) -> llvm::Optional<mlir::Value> {
4892 return operands[i].second;
4893 };
4894 return Fortran::lower::lowerCustomIntrinsic(
4895 *bldr, loc, name, retTy, isPresent, getArgument, operands.size(),
4896 getElementCtx());
4897 };
4898 }
4899 /// Otherwise, pre-lower arguments and use intrinsic lowering utility.
4900 for (const auto &arg : llvm::enumerate(procRef.arguments())) {
4901 const auto *expr =
4902 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value());
4903 if (!expr) {
4904 // Absent optional.
4905 operands.emplace_back([=](IterSpace) { return mlir::Value{}; });
4906 } else if (!argLowering) {
4907 // No argument lowering instruction, lower by value.
4908 PushSemantics(ConstituentSemantics::RefTransparent);
4909 operands.emplace_back(genElementalArgument(*expr));
4910 } else {
4911 // Ad-hoc argument lowering handling.
4912 Fortran::lower::ArgLoweringRule argRules =
4913 Fortran::lower::lowerIntrinsicArgumentAs(*argLowering, arg.index());
4914 if (argRules.handleDynamicOptional &&
4915 Fortran::evaluate::MayBePassedAsAbsentOptional(
4916 *expr, converter.getFoldingContext())) {
4917 // Currently, there is not elemental intrinsic that requires lowering
4918 // a potentially absent argument to something else than a value (apart
4919 // from character MAX/MIN that are handled elsewhere.)
4920 if (argRules.lowerAs != Fortran::lower::LowerIntrinsicArgAs::Value)
4921 TODO(loc, "non trivial optional elemental intrinsic array "
4922 "argument");
4923 PushSemantics(ConstituentSemantics::RefTransparent);
4924 operands.emplace_back(genarrForwardOptionalArgumentToCall(*expr));
4925 continue;
4926 }
4927 switch (argRules.lowerAs) {
4928 case Fortran::lower::LowerIntrinsicArgAs::Value: {
4929 PushSemantics(ConstituentSemantics::RefTransparent);
4930 operands.emplace_back(genElementalArgument(*expr));
4931 } break;
4932 case Fortran::lower::LowerIntrinsicArgAs::Addr: {
4933 // Note: assume does not have Fortran VALUE attribute semantics.
4934 PushSemantics(ConstituentSemantics::RefOpaque);
4935 operands.emplace_back(genElementalArgument(*expr));
4936 } break;
4937 case Fortran::lower::LowerIntrinsicArgAs::Box: {
4938 PushSemantics(ConstituentSemantics::RefOpaque);
4939 auto lambda = genElementalArgument(*expr);
4940 operands.emplace_back([=](IterSpace iters) {
4941 return builder.createBox(loc, lambda(iters));
4942 });
4943 } break;
4944 case Fortran::lower::LowerIntrinsicArgAs::Inquired:
4945 TODO(loc, "intrinsic function with inquired argument");
4946 break;
4947 }
4948 }
4949 }
4950
4951 // Let the intrinsic library lower the intrinsic procedure call
4952 return [=](IterSpace iters) {
4953 llvm::SmallVector<ExtValue> args;
4954 for (const auto &cc : operands)
4955 args.push_back(cc(iters));
4956 return Fortran::lower::genIntrinsicCall(builder, loc, name, retTy, args,
4957 getElementCtx());
4958 };
4959 }
4960
4961 /// Lower a procedure reference to a user-defined elemental procedure.
genElementalUserDefinedProcRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> retTy)4962 CC genElementalUserDefinedProcRef(
4963 const Fortran::evaluate::ProcedureRef &procRef,
4964 llvm::Optional<mlir::Type> retTy) {
4965 using PassBy = Fortran::lower::CallerInterface::PassEntityBy;
4966
4967 // 10.1.4 p5. Impure elemental procedures must be called in element order.
4968 if (const Fortran::semantics::Symbol *procSym = procRef.proc().GetSymbol())
4969 if (!Fortran::semantics::IsPureProcedure(*procSym))
4970 setUnordered(false);
4971
4972 Fortran::lower::CallerInterface caller(procRef, converter);
4973 llvm::SmallVector<CC> operands;
4974 operands.reserve(caller.getPassedArguments().size());
4975 mlir::Location loc = getLoc();
4976 mlir::FunctionType callSiteType = caller.genFunctionType();
4977 for (const Fortran::lower::CallInterface<
4978 Fortran::lower::CallerInterface>::PassedEntity &arg :
4979 caller.getPassedArguments()) {
4980 // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout)
4981 // arguments must be called in element order.
4982 if (arg.mayBeModifiedByCall())
4983 setUnordered(false);
4984 const auto *actual = arg.entity;
4985 mlir::Type argTy = callSiteType.getInput(arg.firArgument);
4986 if (!actual) {
4987 // Optional dummy argument for which there is no actual argument.
4988 auto absent = builder.create<fir::AbsentOp>(loc, argTy);
4989 operands.emplace_back([=](IterSpace) { return absent; });
4990 continue;
4991 }
4992 const auto *expr = actual->UnwrapExpr();
4993 if (!expr)
4994 TODO(loc, "assumed type actual argument");
4995
4996 LLVM_DEBUG(expr->AsFortran(llvm::dbgs()
4997 << "argument: " << arg.firArgument << " = [")
4998 << "]\n");
4999 if (arg.isOptional() && Fortran::evaluate::MayBePassedAsAbsentOptional(
5000 *expr, converter.getFoldingContext()))
5001 TODO(loc,
5002 "passing dynamically optional argument to elemental procedures");
5003 switch (arg.passBy) {
5004 case PassBy::Value: {
5005 // True pass-by-value semantics.
5006 PushSemantics(ConstituentSemantics::RefTransparent);
5007 operands.emplace_back(genElementalArgument(*expr));
5008 } break;
5009 case PassBy::BaseAddressValueAttribute: {
5010 // VALUE attribute or pass-by-reference to a copy semantics. (byval*)
5011 if (isArray(*expr)) {
5012 PushSemantics(ConstituentSemantics::ByValueArg);
5013 operands.emplace_back(genElementalArgument(*expr));
5014 } else {
5015 // Store scalar value in a temp to fulfill VALUE attribute.
5016 mlir::Value val = fir::getBase(asScalar(*expr));
5017 mlir::Value temp = builder.createTemporary(
5018 loc, val.getType(),
5019 llvm::ArrayRef<mlir::NamedAttribute>{
5020 Fortran::lower::getAdaptToByRefAttr(builder)});
5021 builder.create<fir::StoreOp>(loc, val, temp);
5022 operands.emplace_back(
5023 [=](IterSpace iters) -> ExtValue { return temp; });
5024 }
5025 } break;
5026 case PassBy::BaseAddress: {
5027 if (isArray(*expr)) {
5028 PushSemantics(ConstituentSemantics::RefOpaque);
5029 operands.emplace_back(genElementalArgument(*expr));
5030 } else {
5031 ExtValue exv = asScalarRef(*expr);
5032 operands.emplace_back([=](IterSpace iters) { return exv; });
5033 }
5034 } break;
5035 case PassBy::CharBoxValueAttribute: {
5036 if (isArray(*expr)) {
5037 PushSemantics(ConstituentSemantics::DataValue);
5038 auto lambda = genElementalArgument(*expr);
5039 operands.emplace_back([=](IterSpace iters) {
5040 return fir::factory::CharacterExprHelper{builder, loc}
5041 .createTempFrom(lambda(iters));
5042 });
5043 } else {
5044 fir::factory::CharacterExprHelper helper(builder, loc);
5045 fir::CharBoxValue argVal = helper.createTempFrom(asScalarRef(*expr));
5046 operands.emplace_back(
5047 [=](IterSpace iters) -> ExtValue { return argVal; });
5048 }
5049 } break;
5050 case PassBy::BoxChar: {
5051 PushSemantics(ConstituentSemantics::RefOpaque);
5052 operands.emplace_back(genElementalArgument(*expr));
5053 } break;
5054 case PassBy::AddressAndLength:
5055 // PassBy::AddressAndLength is only used for character results. Results
5056 // are not handled here.
5057 fir::emitFatalError(
5058 loc, "unexpected PassBy::AddressAndLength in elemental call");
5059 break;
5060 case PassBy::CharProcTuple: {
5061 ExtValue argRef = asScalarRef(*expr);
5062 mlir::Value tuple = createBoxProcCharTuple(
5063 converter, argTy, fir::getBase(argRef), fir::getLen(argRef));
5064 operands.emplace_back(
5065 [=](IterSpace iters) -> ExtValue { return tuple; });
5066 } break;
5067 case PassBy::Box:
5068 case PassBy::MutableBox:
5069 // See C15100 and C15101
5070 fir::emitFatalError(loc, "cannot be POINTER, ALLOCATABLE");
5071 }
5072 }
5073
5074 if (caller.getIfIndirectCallSymbol())
5075 fir::emitFatalError(loc, "cannot be indirect call");
5076
5077 // The lambda is mutable so that `caller` copy can be modified inside it.
5078 return
5079 [=, caller = std::move(caller)](IterSpace iters) mutable -> ExtValue {
5080 for (const auto &[cc, argIface] :
5081 llvm::zip(operands, caller.getPassedArguments())) {
5082 auto exv = cc(iters);
5083 auto arg = exv.match(
5084 [&](const fir::CharBoxValue &cb) -> mlir::Value {
5085 return fir::factory::CharacterExprHelper{builder, loc}
5086 .createEmbox(cb);
5087 },
5088 [&](const auto &) { return fir::getBase(exv); });
5089 caller.placeInput(argIface, arg);
5090 }
5091 return ScalarExprLowering{loc, converter, symMap, getElementCtx()}
5092 .genCallOpAndResult(caller, callSiteType, retTy);
5093 };
5094 }
5095
5096 /// Lower TRANSPOSE call without using runtime TRANSPOSE.
5097 /// Return continuation for generating the TRANSPOSE result.
5098 /// The continuation just swaps the iteration space before
5099 /// invoking continuation for the argument.
genTransposeProcRef(const Fortran::evaluate::ProcedureRef & procRef)5100 CC genTransposeProcRef(const Fortran::evaluate::ProcedureRef &procRef) {
5101 assert(procRef.arguments().size() == 1 &&
5102 "TRANSPOSE must have one argument.");
5103 const auto *argExpr = procRef.arguments()[0].value().UnwrapExpr();
5104 assert(argExpr);
5105
5106 llvm::SmallVector<mlir::Value> savedDestShape = destShape;
5107 assert((destShape.empty() || destShape.size() == 2) &&
5108 "TRANSPOSE destination must have rank 2.");
5109
5110 if (!savedDestShape.empty())
5111 std::swap(destShape[0], destShape[1]);
5112
5113 PushSemantics(ConstituentSemantics::RefTransparent);
5114 llvm::SmallVector<CC> operands{genElementalArgument(*argExpr)};
5115
5116 if (!savedDestShape.empty()) {
5117 // If destShape was set before transpose lowering, then
5118 // restore it. Otherwise, ...
5119 destShape = savedDestShape;
5120 } else if (!destShape.empty()) {
5121 // ... if destShape has been set from the argument lowering,
5122 // then reverse it.
5123 assert(destShape.size() == 2 &&
5124 "TRANSPOSE destination must have rank 2.");
5125 std::swap(destShape[0], destShape[1]);
5126 }
5127
5128 return [=](IterSpace iters) {
5129 assert(iters.iterVec().size() == 2 &&
5130 "TRANSPOSE expects 2D iterations space.");
5131 IterationSpace newIters(iters, {iters.iterValue(1), iters.iterValue(0)});
5132 return operands.front()(newIters);
5133 };
5134 }
5135
5136 /// Generate a procedure reference. This code is shared for both functions and
5137 /// subroutines, the difference being reflected by `retTy`.
genProcRef(const Fortran::evaluate::ProcedureRef & procRef,llvm::Optional<mlir::Type> retTy)5138 CC genProcRef(const Fortran::evaluate::ProcedureRef &procRef,
5139 llvm::Optional<mlir::Type> retTy) {
5140 mlir::Location loc = getLoc();
5141
5142 if (isOptimizableTranspose(procRef))
5143 return genTransposeProcRef(procRef);
5144
5145 if (procRef.IsElemental()) {
5146 if (const Fortran::evaluate::SpecificIntrinsic *intrin =
5147 procRef.proc().GetSpecificIntrinsic()) {
5148 // All elemental intrinsic functions are pure and cannot modify their
5149 // arguments. The only elemental subroutine, MVBITS has an Intent(inout)
5150 // argument. So for this last one, loops must be in element order
5151 // according to 15.8.3 p1.
5152 if (!retTy)
5153 setUnordered(false);
5154
5155 // Elemental intrinsic call.
5156 // The intrinsic procedure is called once per element of the array.
5157 return genElementalIntrinsicProcRef(procRef, retTy, *intrin);
5158 }
5159 if (isIntrinsicModuleProcRef(procRef))
5160 return genElementalIntrinsicProcRef(procRef, retTy);
5161 if (ScalarExprLowering::isStatementFunctionCall(procRef))
5162 fir::emitFatalError(loc, "statement function cannot be elemental");
5163
5164 // Elemental call.
5165 // The procedure is called once per element of the array argument(s).
5166 return genElementalUserDefinedProcRef(procRef, retTy);
5167 }
5168
5169 // Transformational call.
5170 // The procedure is called once and produces a value of rank > 0.
5171 if (const Fortran::evaluate::SpecificIntrinsic *intrinsic =
5172 procRef.proc().GetSpecificIntrinsic()) {
5173 if (explicitSpaceIsActive() && procRef.Rank() == 0) {
5174 // Elide any implicit loop iters.
5175 return [=, &procRef](IterSpace) {
5176 return ScalarExprLowering{loc, converter, symMap, stmtCtx}
5177 .genIntrinsicRef(procRef, retTy, *intrinsic);
5178 };
5179 }
5180 return genarr(
5181 ScalarExprLowering{loc, converter, symMap, stmtCtx}.genIntrinsicRef(
5182 procRef, retTy, *intrinsic));
5183 }
5184
5185 const bool isPtrAssn = isPointerAssignment();
5186 if (explicitSpaceIsActive() && procRef.Rank() == 0) {
5187 // Elide any implicit loop iters.
5188 return [=, &procRef](IterSpace) {
5189 ScalarExprLowering sel(loc, converter, symMap, stmtCtx);
5190 return isPtrAssn ? sel.genRawProcedureRef(procRef, retTy)
5191 : sel.genProcedureRef(procRef, retTy);
5192 };
5193 }
5194 // In the default case, the call can be hoisted out of the loop nest. Apply
5195 // the iterations to the result, which may be an array value.
5196 ScalarExprLowering sel(loc, converter, symMap, stmtCtx);
5197 auto exv = isPtrAssn ? sel.genRawProcedureRef(procRef, retTy)
5198 : sel.genProcedureRef(procRef, retTy);
5199 return genarr(exv);
5200 }
5201
genarr(const Fortran::evaluate::ProcedureDesignator &)5202 CC genarr(const Fortran::evaluate::ProcedureDesignator &) {
5203 TODO(getLoc(), "procedure designator");
5204 }
genarr(const Fortran::evaluate::ProcedureRef & x)5205 CC genarr(const Fortran::evaluate::ProcedureRef &x) {
5206 if (x.hasAlternateReturns())
5207 fir::emitFatalError(getLoc(),
5208 "array procedure reference with alt-return");
5209 return genProcRef(x, llvm::None);
5210 }
5211 template <typename A>
genScalarAndForwardValue(const A & x)5212 CC genScalarAndForwardValue(const A &x) {
5213 ExtValue result = asScalar(x);
5214 return [=](IterSpace) { return result; };
5215 }
5216 template <typename A, typename = std::enable_if_t<Fortran::common::HasMember<
5217 A, Fortran::evaluate::TypelessExpression>>>
genarr(const A & x)5218 CC genarr(const A &x) {
5219 return genScalarAndForwardValue(x);
5220 }
5221
5222 template <typename A>
genarr(const Fortran::evaluate::Expr<A> & x)5223 CC genarr(const Fortran::evaluate::Expr<A> &x) {
5224 LLVM_DEBUG(Fortran::lower::DumpEvaluateExpr::dump(llvm::dbgs(), x));
5225 if (isArray(x) || (explicitSpaceIsActive() && isLeftHandSide()) ||
5226 isElementalProcWithArrayArgs(x))
5227 return std::visit([&](const auto &e) { return genarr(e); }, x.u);
5228 if (explicitSpaceIsActive()) {
5229 assert(!isArray(x) && !isLeftHandSide());
5230 auto cc = std::visit([&](const auto &e) { return genarr(e); }, x.u);
5231 auto result = cc(IterationSpace{});
5232 return [=](IterSpace) { return result; };
5233 }
5234 return genScalarAndForwardValue(x);
5235 }
5236
5237 // Converting a value of memory bound type requires creating a temp and
5238 // copying the value.
convertAdjustedType(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type toType,const ExtValue & exv)5239 static ExtValue convertAdjustedType(fir::FirOpBuilder &builder,
5240 mlir::Location loc, mlir::Type toType,
5241 const ExtValue &exv) {
5242 return exv.match(
5243 [&](const fir::CharBoxValue &cb) -> ExtValue {
5244 mlir::Value len = cb.getLen();
5245 auto mem =
5246 builder.create<fir::AllocaOp>(loc, toType, mlir::ValueRange{len});
5247 fir::CharBoxValue result(mem, len);
5248 fir::factory::CharacterExprHelper{builder, loc}.createAssign(
5249 ExtValue{result}, exv);
5250 return result;
5251 },
5252 [&](const auto &) -> ExtValue {
5253 fir::emitFatalError(loc, "convert on adjusted extended value");
5254 });
5255 }
5256 template <Fortran::common::TypeCategory TC1, int KIND,
5257 Fortran::common::TypeCategory TC2>
genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1,KIND>,TC2> & x)5258 CC genarr(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
5259 TC2> &x) {
5260 mlir::Location loc = getLoc();
5261 auto lambda = genarr(x.left());
5262 mlir::Type ty = converter.genType(TC1, KIND);
5263 return [=](IterSpace iters) -> ExtValue {
5264 auto exv = lambda(iters);
5265 mlir::Value val = fir::getBase(exv);
5266 auto valTy = val.getType();
5267 if (elementTypeWasAdjusted(valTy) &&
5268 !(fir::isa_ref_type(valTy) && fir::isa_integer(ty)))
5269 return convertAdjustedType(builder, loc, ty, exv);
5270 return builder.createConvert(loc, ty, val);
5271 };
5272 }
5273
5274 template <int KIND>
genarr(const Fortran::evaluate::ComplexComponent<KIND> & x)5275 CC genarr(const Fortran::evaluate::ComplexComponent<KIND> &x) {
5276 mlir::Location loc = getLoc();
5277 auto lambda = genarr(x.left());
5278 bool isImagPart = x.isImaginaryPart;
5279 return [=](IterSpace iters) -> ExtValue {
5280 mlir::Value lhs = fir::getBase(lambda(iters));
5281 return fir::factory::Complex{builder, loc}.extractComplexPart(lhs,
5282 isImagPart);
5283 };
5284 }
5285
5286 template <typename T>
genarr(const Fortran::evaluate::Parentheses<T> & x)5287 CC genarr(const Fortran::evaluate::Parentheses<T> &x) {
5288 mlir::Location loc = getLoc();
5289 if (isReferentiallyOpaque()) {
5290 // Context is a call argument in, for example, an elemental procedure
5291 // call. TODO: all array arguments should use array_load, array_access,
5292 // array_amend, and INTENT(OUT), INTENT(INOUT) arguments should have
5293 // array_merge_store ops.
5294 TODO(loc, "parentheses on argument in elemental call");
5295 }
5296 auto f = genarr(x.left());
5297 return [=](IterSpace iters) -> ExtValue {
5298 auto val = f(iters);
5299 mlir::Value base = fir::getBase(val);
5300 auto newBase =
5301 builder.create<fir::NoReassocOp>(loc, base.getType(), base);
5302 return fir::substBase(val, newBase);
5303 };
5304 }
5305 template <int KIND>
genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,KIND>> & x)5306 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
5307 Fortran::common::TypeCategory::Integer, KIND>> &x) {
5308 mlir::Location loc = getLoc();
5309 auto f = genarr(x.left());
5310 return [=](IterSpace iters) -> ExtValue {
5311 mlir::Value val = fir::getBase(f(iters));
5312 mlir::Type ty =
5313 converter.genType(Fortran::common::TypeCategory::Integer, KIND);
5314 mlir::Value zero = builder.createIntegerConstant(loc, ty, 0);
5315 return builder.create<mlir::arith::SubIOp>(loc, zero, val);
5316 };
5317 }
5318 template <int KIND>
genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Real,KIND>> & x)5319 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
5320 Fortran::common::TypeCategory::Real, KIND>> &x) {
5321 mlir::Location loc = getLoc();
5322 auto f = genarr(x.left());
5323 return [=](IterSpace iters) -> ExtValue {
5324 return builder.create<mlir::arith::NegFOp>(loc, fir::getBase(f(iters)));
5325 };
5326 }
5327 template <int KIND>
genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex,KIND>> & x)5328 CC genarr(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
5329 Fortran::common::TypeCategory::Complex, KIND>> &x) {
5330 mlir::Location loc = getLoc();
5331 auto f = genarr(x.left());
5332 return [=](IterSpace iters) -> ExtValue {
5333 return builder.create<fir::NegcOp>(loc, fir::getBase(f(iters)));
5334 };
5335 }
5336
5337 //===--------------------------------------------------------------------===//
5338 // Binary elemental ops
5339 //===--------------------------------------------------------------------===//
5340
5341 template <typename OP, typename A>
createBinaryOp(const A & evEx)5342 CC createBinaryOp(const A &evEx) {
5343 mlir::Location loc = getLoc();
5344 auto lambda = genarr(evEx.left());
5345 auto rf = genarr(evEx.right());
5346 return [=](IterSpace iters) -> ExtValue {
5347 mlir::Value left = fir::getBase(lambda(iters));
5348 mlir::Value right = fir::getBase(rf(iters));
5349 return builder.create<OP>(loc, left, right);
5350 };
5351 }
5352
5353 #undef GENBIN
5354 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp) \
5355 template <int KIND> \
5356 CC genarr(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
5357 Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
5358 return createBinaryOp<GenBinFirOp>(x); \
5359 }
5360
GENBIN(Add,Integer,mlir::arith::AddIOp)5361 GENBIN(Add, Integer, mlir::arith::AddIOp)
5362 GENBIN(Add, Real, mlir::arith::AddFOp)
5363 GENBIN(Add, Complex, fir::AddcOp)
5364 GENBIN(Subtract, Integer, mlir::arith::SubIOp)
5365 GENBIN(Subtract, Real, mlir::arith::SubFOp)
5366 GENBIN(Subtract, Complex, fir::SubcOp)
5367 GENBIN(Multiply, Integer, mlir::arith::MulIOp)
5368 GENBIN(Multiply, Real, mlir::arith::MulFOp)
5369 GENBIN(Multiply, Complex, fir::MulcOp)
5370 GENBIN(Divide, Integer, mlir::arith::DivSIOp)
5371 GENBIN(Divide, Real, mlir::arith::DivFOp)
5372 GENBIN(Divide, Complex, fir::DivcOp)
5373
5374 template <Fortran::common::TypeCategory TC, int KIND>
5375 CC genarr(
5376 const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &x) {
5377 mlir::Location loc = getLoc();
5378 mlir::Type ty = converter.genType(TC, KIND);
5379 auto lf = genarr(x.left());
5380 auto rf = genarr(x.right());
5381 return [=](IterSpace iters) -> ExtValue {
5382 mlir::Value lhs = fir::getBase(lf(iters));
5383 mlir::Value rhs = fir::getBase(rf(iters));
5384 return Fortran::lower::genPow(builder, loc, ty, lhs, rhs);
5385 };
5386 }
5387 template <Fortran::common::TypeCategory TC, int KIND>
genarr(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC,KIND>> & x)5388 CC genarr(
5389 const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>> &x) {
5390 mlir::Location loc = getLoc();
5391 auto lf = genarr(x.left());
5392 auto rf = genarr(x.right());
5393 switch (x.ordering) {
5394 case Fortran::evaluate::Ordering::Greater:
5395 return [=](IterSpace iters) -> ExtValue {
5396 mlir::Value lhs = fir::getBase(lf(iters));
5397 mlir::Value rhs = fir::getBase(rf(iters));
5398 return Fortran::lower::genMax(builder, loc,
5399 llvm::ArrayRef<mlir::Value>{lhs, rhs});
5400 };
5401 case Fortran::evaluate::Ordering::Less:
5402 return [=](IterSpace iters) -> ExtValue {
5403 mlir::Value lhs = fir::getBase(lf(iters));
5404 mlir::Value rhs = fir::getBase(rf(iters));
5405 return Fortran::lower::genMin(builder, loc,
5406 llvm::ArrayRef<mlir::Value>{lhs, rhs});
5407 };
5408 case Fortran::evaluate::Ordering::Equal:
5409 llvm_unreachable("Equal is not a valid ordering in this context");
5410 }
5411 llvm_unreachable("unknown ordering");
5412 }
5413 template <Fortran::common::TypeCategory TC, int KIND>
genarr(const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC,KIND>> & x)5414 CC genarr(
5415 const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
5416 &x) {
5417 mlir::Location loc = getLoc();
5418 auto ty = converter.genType(TC, KIND);
5419 auto lf = genarr(x.left());
5420 auto rf = genarr(x.right());
5421 return [=](IterSpace iters) {
5422 mlir::Value lhs = fir::getBase(lf(iters));
5423 mlir::Value rhs = fir::getBase(rf(iters));
5424 return Fortran::lower::genPow(builder, loc, ty, lhs, rhs);
5425 };
5426 }
5427 template <int KIND>
genarr(const Fortran::evaluate::ComplexConstructor<KIND> & x)5428 CC genarr(const Fortran::evaluate::ComplexConstructor<KIND> &x) {
5429 mlir::Location loc = getLoc();
5430 auto lf = genarr(x.left());
5431 auto rf = genarr(x.right());
5432 return [=](IterSpace iters) -> ExtValue {
5433 mlir::Value lhs = fir::getBase(lf(iters));
5434 mlir::Value rhs = fir::getBase(rf(iters));
5435 return fir::factory::Complex{builder, loc}.createComplex(KIND, lhs, rhs);
5436 };
5437 }
5438
5439 /// Fortran's concatenation operator `//`.
5440 template <int KIND>
genarr(const Fortran::evaluate::Concat<KIND> & x)5441 CC genarr(const Fortran::evaluate::Concat<KIND> &x) {
5442 mlir::Location loc = getLoc();
5443 auto lf = genarr(x.left());
5444 auto rf = genarr(x.right());
5445 return [=](IterSpace iters) -> ExtValue {
5446 auto lhs = lf(iters);
5447 auto rhs = rf(iters);
5448 const fir::CharBoxValue *lchr = lhs.getCharBox();
5449 const fir::CharBoxValue *rchr = rhs.getCharBox();
5450 if (lchr && rchr) {
5451 return fir::factory::CharacterExprHelper{builder, loc}
5452 .createConcatenate(*lchr, *rchr);
5453 }
5454 TODO(loc, "concat on unexpected extended values");
5455 return mlir::Value{};
5456 };
5457 }
5458
5459 template <int KIND>
genarr(const Fortran::evaluate::SetLength<KIND> & x)5460 CC genarr(const Fortran::evaluate::SetLength<KIND> &x) {
5461 auto lf = genarr(x.left());
5462 mlir::Value rhs = fir::getBase(asScalar(x.right()));
5463 return [=](IterSpace iters) -> ExtValue {
5464 mlir::Value lhs = fir::getBase(lf(iters));
5465 return fir::CharBoxValue{lhs, rhs};
5466 };
5467 }
5468
5469 template <typename A>
genarr(const Fortran::evaluate::Constant<A> & x)5470 CC genarr(const Fortran::evaluate::Constant<A> &x) {
5471 if (x.Rank() == 0)
5472 return genScalarAndForwardValue(x);
5473 mlir::Location loc = getLoc();
5474 mlir::IndexType idxTy = builder.getIndexType();
5475 mlir::Type arrTy = converter.genType(toEvExpr(x));
5476 std::string globalName = Fortran::lower::mangle::mangleArrayLiteral(x);
5477 fir::GlobalOp global = builder.getNamedGlobal(globalName);
5478 if (!global) {
5479 mlir::Type symTy = arrTy;
5480 mlir::Type eleTy = symTy.cast<fir::SequenceType>().getEleTy();
5481 // If we have a rank-1 array of integer, real, or logical, then we can
5482 // create a global array with the dense attribute.
5483 //
5484 // The mlir tensor type can only handle integer, real, or logical. It
5485 // does not currently support nested structures which is required for
5486 // complex.
5487 //
5488 // Also, we currently handle just rank-1 since tensor type assumes
5489 // row major array ordering. We will need to reorder the dimensions
5490 // in the tensor type to support Fortran's column major array ordering.
5491 // How to create this tensor type is to be determined.
5492 if (x.Rank() == 1 &&
5493 eleTy.isa<fir::LogicalType, mlir::IntegerType, mlir::FloatType>())
5494 global = Fortran::lower::createDenseGlobal(
5495 loc, arrTy, globalName, builder.createInternalLinkage(), true,
5496 toEvExpr(x), converter);
5497 // Note: If call to createDenseGlobal() returns 0, then call
5498 // createGlobalConstant() below.
5499 if (!global)
5500 global = builder.createGlobalConstant(
5501 loc, arrTy, globalName,
5502 [&](fir::FirOpBuilder &builder) {
5503 Fortran::lower::StatementContext stmtCtx(
5504 /*cleanupProhibited=*/true);
5505 fir::ExtendedValue result =
5506 Fortran::lower::createSomeInitializerExpression(
5507 loc, converter, toEvExpr(x), symMap, stmtCtx);
5508 mlir::Value castTo =
5509 builder.createConvert(loc, arrTy, fir::getBase(result));
5510 builder.create<fir::HasValueOp>(loc, castTo);
5511 },
5512 builder.createInternalLinkage());
5513 }
5514 auto addr = builder.create<fir::AddrOfOp>(getLoc(), global.resultType(),
5515 global.getSymbol());
5516 auto seqTy = global.getType().cast<fir::SequenceType>();
5517 llvm::SmallVector<mlir::Value> extents;
5518 for (auto extent : seqTy.getShape())
5519 extents.push_back(builder.createIntegerConstant(loc, idxTy, extent));
5520 if (auto charTy = seqTy.getEleTy().dyn_cast<fir::CharacterType>()) {
5521 mlir::Value len = builder.createIntegerConstant(loc, builder.getI64Type(),
5522 charTy.getLen());
5523 return genarr(fir::CharArrayBoxValue{addr, len, extents});
5524 }
5525 return genarr(fir::ArrayBoxValue{addr, extents});
5526 }
5527
5528 //===--------------------------------------------------------------------===//
5529 // A vector subscript expression may be wrapped with a cast to INTEGER*8.
5530 // Get rid of it here so the vector can be loaded. Add it back when
5531 // generating the elemental evaluation (inside the loop nest).
5532
5533 static Fortran::lower::SomeExpr
ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,8>> & x)5534 ignoreEvConvert(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
5535 Fortran::common::TypeCategory::Integer, 8>> &x) {
5536 return std::visit([&](const auto &v) { return ignoreEvConvert(v); }, x.u);
5537 }
5538 template <Fortran::common::TypeCategory FROM>
ignoreEvConvert(const Fortran::evaluate::Convert<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,8>,FROM> & x)5539 static Fortran::lower::SomeExpr ignoreEvConvert(
5540 const Fortran::evaluate::Convert<
5541 Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer, 8>,
5542 FROM> &x) {
5543 return toEvExpr(x.left());
5544 }
5545 template <typename A>
ignoreEvConvert(const A & x)5546 static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) {
5547 return toEvExpr(x);
5548 }
5549
5550 //===--------------------------------------------------------------------===//
5551 // Get the `Se::Symbol*` for the subscript expression, `x`. This symbol can
5552 // be used to determine the lbound, ubound of the vector.
5553
5554 template <typename A>
5555 static const Fortran::semantics::Symbol *
extractSubscriptSymbol(const Fortran::evaluate::Expr<A> & x)5556 extractSubscriptSymbol(const Fortran::evaluate::Expr<A> &x) {
5557 return std::visit([&](const auto &v) { return extractSubscriptSymbol(v); },
5558 x.u);
5559 }
5560 template <typename A>
5561 static const Fortran::semantics::Symbol *
extractSubscriptSymbol(const Fortran::evaluate::Designator<A> & x)5562 extractSubscriptSymbol(const Fortran::evaluate::Designator<A> &x) {
5563 return Fortran::evaluate::UnwrapWholeSymbolDataRef(x);
5564 }
5565 template <typename A>
extractSubscriptSymbol(const A & x)5566 static const Fortran::semantics::Symbol *extractSubscriptSymbol(const A &x) {
5567 return nullptr;
5568 }
5569
5570 //===--------------------------------------------------------------------===//
5571
5572 /// Get the declared lower bound value of the array `x` in dimension `dim`.
5573 /// The argument `one` must be an ssa-value for the constant 1.
getLBound(const ExtValue & x,unsigned dim,mlir::Value one)5574 mlir::Value getLBound(const ExtValue &x, unsigned dim, mlir::Value one) {
5575 return fir::factory::readLowerBound(builder, getLoc(), x, dim, one);
5576 }
5577
5578 /// Get the declared upper bound value of the array `x` in dimension `dim`.
5579 /// The argument `one` must be an ssa-value for the constant 1.
getUBound(const ExtValue & x,unsigned dim,mlir::Value one)5580 mlir::Value getUBound(const ExtValue &x, unsigned dim, mlir::Value one) {
5581 mlir::Location loc = getLoc();
5582 mlir::Value lb = getLBound(x, dim, one);
5583 mlir::Value extent = fir::factory::readExtent(builder, loc, x, dim);
5584 auto add = builder.create<mlir::arith::AddIOp>(loc, lb, extent);
5585 return builder.create<mlir::arith::SubIOp>(loc, add, one);
5586 }
5587
5588 /// Return the extent of the boxed array `x` in dimesion `dim`.
getExtent(const ExtValue & x,unsigned dim)5589 mlir::Value getExtent(const ExtValue &x, unsigned dim) {
5590 return fir::factory::readExtent(builder, getLoc(), x, dim);
5591 }
5592
5593 template <typename A>
genArrayBase(const A & base)5594 ExtValue genArrayBase(const A &base) {
5595 ScalarExprLowering sel{getLoc(), converter, symMap, stmtCtx};
5596 return base.IsSymbol() ? sel.gen(getFirstSym(base))
5597 : sel.gen(base.GetComponent());
5598 }
5599
5600 template <typename A>
hasEvArrayRef(const A & x)5601 bool hasEvArrayRef(const A &x) {
5602 struct HasEvArrayRefHelper
5603 : public Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper> {
5604 HasEvArrayRefHelper()
5605 : Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>(*this) {}
5606 using Fortran::evaluate::AnyTraverse<HasEvArrayRefHelper>::operator();
5607 bool operator()(const Fortran::evaluate::ArrayRef &) const {
5608 return true;
5609 }
5610 } helper;
5611 return helper(x);
5612 }
5613
genVectorSubscriptArrayFetch(const Fortran::lower::SomeExpr & expr,std::size_t dim)5614 CC genVectorSubscriptArrayFetch(const Fortran::lower::SomeExpr &expr,
5615 std::size_t dim) {
5616 PushSemantics(ConstituentSemantics::RefTransparent);
5617 auto saved = Fortran::common::ScopedSet(explicitSpace, nullptr);
5618 llvm::SmallVector<mlir::Value> savedDestShape = destShape;
5619 destShape.clear();
5620 auto result = genarr(expr);
5621 if (destShape.empty())
5622 TODO(getLoc(), "expected vector to have an extent");
5623 assert(destShape.size() == 1 && "vector has rank > 1");
5624 if (destShape[0] != savedDestShape[dim]) {
5625 // Not the same, so choose the smaller value.
5626 mlir::Location loc = getLoc();
5627 auto cmp = builder.create<mlir::arith::CmpIOp>(
5628 loc, mlir::arith::CmpIPredicate::sgt, destShape[0],
5629 savedDestShape[dim]);
5630 auto sel = builder.create<mlir::arith::SelectOp>(
5631 loc, cmp, savedDestShape[dim], destShape[0]);
5632 savedDestShape[dim] = sel;
5633 destShape = savedDestShape;
5634 }
5635 return result;
5636 }
5637
5638 /// Generate an access by vector subscript using the index in the iteration
5639 /// vector at `dim`.
genAccessByVector(mlir::Location loc,CC genArrFetch,IterSpace iters,std::size_t dim)5640 mlir::Value genAccessByVector(mlir::Location loc, CC genArrFetch,
5641 IterSpace iters, std::size_t dim) {
5642 IterationSpace vecIters(iters,
5643 llvm::ArrayRef<mlir::Value>{iters.iterValue(dim)});
5644 fir::ExtendedValue fetch = genArrFetch(vecIters);
5645 mlir::IndexType idxTy = builder.getIndexType();
5646 return builder.createConvert(loc, idxTy, fir::getBase(fetch));
5647 }
5648
5649 /// When we have an array reference, the expressions specified in each
5650 /// dimension may be slice operations (e.g. `i:j:k`), vectors, or simple
5651 /// (loop-invarianet) scalar expressions. This returns the base entity, the
5652 /// resulting type, and a continuation to adjust the default iteration space.
genSliceIndices(ComponentPath & cmptData,const ExtValue & arrayExv,const Fortran::evaluate::ArrayRef & x,bool atBase)5653 void genSliceIndices(ComponentPath &cmptData, const ExtValue &arrayExv,
5654 const Fortran::evaluate::ArrayRef &x, bool atBase) {
5655 mlir::Location loc = getLoc();
5656 mlir::IndexType idxTy = builder.getIndexType();
5657 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
5658 llvm::SmallVector<mlir::Value> &trips = cmptData.trips;
5659 LLVM_DEBUG(llvm::dbgs() << "array: " << arrayExv << '\n');
5660 auto &pc = cmptData.pc;
5661 const bool useTripsForSlice = !explicitSpaceIsActive();
5662 const bool createDestShape = destShape.empty();
5663 bool useSlice = false;
5664 std::size_t shapeIndex = 0;
5665 for (auto sub : llvm::enumerate(x.subscript())) {
5666 const std::size_t subsIndex = sub.index();
5667 std::visit(
5668 Fortran::common::visitors{
5669 [&](const Fortran::evaluate::Triplet &t) {
5670 mlir::Value lowerBound;
5671 if (auto optLo = t.lower())
5672 lowerBound = fir::getBase(asScalarArray(*optLo));
5673 else
5674 lowerBound = getLBound(arrayExv, subsIndex, one);
5675 lowerBound = builder.createConvert(loc, idxTy, lowerBound);
5676 mlir::Value stride = fir::getBase(asScalarArray(t.stride()));
5677 stride = builder.createConvert(loc, idxTy, stride);
5678 if (useTripsForSlice || createDestShape) {
5679 // Generate a slice operation for the triplet. The first and
5680 // second position of the triplet may be omitted, and the
5681 // declared lbound and/or ubound expression values,
5682 // respectively, should be used instead.
5683 trips.push_back(lowerBound);
5684 mlir::Value upperBound;
5685 if (auto optUp = t.upper())
5686 upperBound = fir::getBase(asScalarArray(*optUp));
5687 else
5688 upperBound = getUBound(arrayExv, subsIndex, one);
5689 upperBound = builder.createConvert(loc, idxTy, upperBound);
5690 trips.push_back(upperBound);
5691 trips.push_back(stride);
5692 if (createDestShape) {
5693 auto extent = builder.genExtentFromTriplet(
5694 loc, lowerBound, upperBound, stride, idxTy);
5695 destShape.push_back(extent);
5696 }
5697 useSlice = true;
5698 }
5699 if (!useTripsForSlice) {
5700 auto currentPC = pc;
5701 pc = [=](IterSpace iters) {
5702 IterationSpace newIters = currentPC(iters);
5703 mlir::Value impliedIter = newIters.iterValue(subsIndex);
5704 // FIXME: must use the lower bound of this component.
5705 auto arrLowerBound =
5706 atBase ? getLBound(arrayExv, subsIndex, one) : one;
5707 auto initial = builder.create<mlir::arith::SubIOp>(
5708 loc, lowerBound, arrLowerBound);
5709 auto prod = builder.create<mlir::arith::MulIOp>(
5710 loc, impliedIter, stride);
5711 auto result =
5712 builder.create<mlir::arith::AddIOp>(loc, initial, prod);
5713 newIters.setIndexValue(subsIndex, result);
5714 return newIters;
5715 };
5716 }
5717 shapeIndex++;
5718 },
5719 [&](const Fortran::evaluate::IndirectSubscriptIntegerExpr &ie) {
5720 const auto &e = ie.value(); // dereference
5721 if (isArray(e)) {
5722 // This is a vector subscript. Use the index values as read
5723 // from a vector to determine the temporary array value.
5724 // Note: 9.5.3.3.3(3) specifies undefined behavior for
5725 // multiple updates to any specific array element through a
5726 // vector subscript with replicated values.
5727 assert(!isBoxValue() &&
5728 "fir.box cannot be created with vector subscripts");
5729 // TODO: Avoid creating a new evaluate::Expr here
5730 auto arrExpr = ignoreEvConvert(e);
5731 if (createDestShape) {
5732 destShape.push_back(fir::factory::getExtentAtDimension(
5733 loc, builder, arrayExv, subsIndex));
5734 }
5735 auto genArrFetch =
5736 genVectorSubscriptArrayFetch(arrExpr, shapeIndex);
5737 auto currentPC = pc;
5738 pc = [=](IterSpace iters) {
5739 IterationSpace newIters = currentPC(iters);
5740 auto val = genAccessByVector(loc, genArrFetch, newIters,
5741 subsIndex);
5742 // Value read from vector subscript array and normalized
5743 // using the base array's lower bound value.
5744 mlir::Value lb = fir::factory::readLowerBound(
5745 builder, loc, arrayExv, subsIndex, one);
5746 auto origin = builder.create<mlir::arith::SubIOp>(
5747 loc, idxTy, val, lb);
5748 newIters.setIndexValue(subsIndex, origin);
5749 return newIters;
5750 };
5751 if (useTripsForSlice) {
5752 LLVM_ATTRIBUTE_UNUSED auto vectorSubscriptShape =
5753 getShape(arrayOperands.back());
5754 auto undef = builder.create<fir::UndefOp>(loc, idxTy);
5755 trips.push_back(undef);
5756 trips.push_back(undef);
5757 trips.push_back(undef);
5758 }
5759 shapeIndex++;
5760 } else {
5761 // This is a regular scalar subscript.
5762 if (useTripsForSlice) {
5763 // A regular scalar index, which does not yield an array
5764 // section. Use a degenerate slice operation
5765 // `(e:undef:undef)` in this dimension as a placeholder.
5766 // This does not necessarily change the rank of the original
5767 // array, so the iteration space must also be extended to
5768 // include this expression in this dimension to adjust to
5769 // the array's declared rank.
5770 mlir::Value v = fir::getBase(asScalarArray(e));
5771 trips.push_back(v);
5772 auto undef = builder.create<fir::UndefOp>(loc, idxTy);
5773 trips.push_back(undef);
5774 trips.push_back(undef);
5775 auto currentPC = pc;
5776 // Cast `e` to index type.
5777 mlir::Value iv = builder.createConvert(loc, idxTy, v);
5778 // Normalize `e` by subtracting the declared lbound.
5779 mlir::Value lb = fir::factory::readLowerBound(
5780 builder, loc, arrayExv, subsIndex, one);
5781 mlir::Value ivAdj =
5782 builder.create<mlir::arith::SubIOp>(loc, idxTy, iv, lb);
5783 // Add lbound adjusted value of `e` to the iteration vector
5784 // (except when creating a box because the iteration vector
5785 // is empty).
5786 if (!isBoxValue())
5787 pc = [=](IterSpace iters) {
5788 IterationSpace newIters = currentPC(iters);
5789 newIters.insertIndexValue(subsIndex, ivAdj);
5790 return newIters;
5791 };
5792 } else {
5793 auto currentPC = pc;
5794 mlir::Value newValue = fir::getBase(asScalarArray(e));
5795 mlir::Value result =
5796 builder.createConvert(loc, idxTy, newValue);
5797 mlir::Value lb = fir::factory::readLowerBound(
5798 builder, loc, arrayExv, subsIndex, one);
5799 result = builder.create<mlir::arith::SubIOp>(loc, idxTy,
5800 result, lb);
5801 pc = [=](IterSpace iters) {
5802 IterationSpace newIters = currentPC(iters);
5803 newIters.insertIndexValue(subsIndex, result);
5804 return newIters;
5805 };
5806 }
5807 }
5808 }},
5809 sub.value().u);
5810 }
5811 if (!useSlice)
5812 trips.clear();
5813 }
5814
unwrapBoxEleTy(mlir::Type ty)5815 static mlir::Type unwrapBoxEleTy(mlir::Type ty) {
5816 if (auto boxTy = ty.dyn_cast<fir::BoxType>())
5817 return fir::unwrapRefType(boxTy.getEleTy());
5818 return ty;
5819 }
5820
getShape(mlir::Type ty)5821 llvm::SmallVector<mlir::Value> getShape(mlir::Type ty) {
5822 llvm::SmallVector<mlir::Value> result;
5823 ty = unwrapBoxEleTy(ty);
5824 mlir::Location loc = getLoc();
5825 mlir::IndexType idxTy = builder.getIndexType();
5826 for (auto extent : ty.cast<fir::SequenceType>().getShape()) {
5827 auto v = extent == fir::SequenceType::getUnknownExtent()
5828 ? builder.create<fir::UndefOp>(loc, idxTy).getResult()
5829 : builder.createIntegerConstant(loc, idxTy, extent);
5830 result.push_back(v);
5831 }
5832 return result;
5833 }
5834
genarr(const Fortran::semantics::SymbolRef & sym,ComponentPath & components)5835 CC genarr(const Fortran::semantics::SymbolRef &sym,
5836 ComponentPath &components) {
5837 return genarr(sym.get(), components);
5838 }
5839
abstractArrayExtValue(mlir::Value val,mlir::Value len={})5840 ExtValue abstractArrayExtValue(mlir::Value val, mlir::Value len = {}) {
5841 return convertToArrayBoxValue(getLoc(), builder, val, len);
5842 }
5843
genarr(const ExtValue & extMemref)5844 CC genarr(const ExtValue &extMemref) {
5845 ComponentPath dummy(/*isImplicit=*/true);
5846 return genarr(extMemref, dummy);
5847 }
5848
5849 // If the slice values are given then use them. Otherwise, generate triples
5850 // that cover the entire shape specified by \p shapeVal.
5851 inline llvm::SmallVector<mlir::Value>
padSlice(llvm::ArrayRef<mlir::Value> triples,mlir::Value shapeVal)5852 padSlice(llvm::ArrayRef<mlir::Value> triples, mlir::Value shapeVal) {
5853 llvm::SmallVector<mlir::Value> result;
5854 mlir::Location loc = getLoc();
5855 if (triples.size()) {
5856 result.assign(triples.begin(), triples.end());
5857 } else {
5858 auto one = builder.createIntegerConstant(loc, builder.getIndexType(), 1);
5859 if (!shapeVal) {
5860 TODO(loc, "shape must be recovered from box");
5861 } else if (auto shapeOp = mlir::dyn_cast_or_null<fir::ShapeOp>(
5862 shapeVal.getDefiningOp())) {
5863 for (auto ext : shapeOp.getExtents()) {
5864 result.push_back(one);
5865 result.push_back(ext);
5866 result.push_back(one);
5867 }
5868 } else if (auto shapeShift = mlir::dyn_cast_or_null<fir::ShapeShiftOp>(
5869 shapeVal.getDefiningOp())) {
5870 for (auto [lb, ext] :
5871 llvm::zip(shapeShift.getOrigins(), shapeShift.getExtents())) {
5872 result.push_back(lb);
5873 result.push_back(ext);
5874 result.push_back(one);
5875 }
5876 } else {
5877 TODO(loc, "shape must be recovered from box");
5878 }
5879 }
5880 return result;
5881 }
5882
5883 /// Base case of generating an array reference,
genarr(const ExtValue & extMemref,ComponentPath & components)5884 CC genarr(const ExtValue &extMemref, ComponentPath &components) {
5885 mlir::Location loc = getLoc();
5886 mlir::Value memref = fir::getBase(extMemref);
5887 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(memref.getType());
5888 assert(arrTy.isa<fir::SequenceType>() && "memory ref must be an array");
5889 mlir::Value shape = builder.createShape(loc, extMemref);
5890 mlir::Value slice;
5891 if (components.isSlice()) {
5892 if (isBoxValue() && components.substring) {
5893 // Append the substring operator to emboxing Op as it will become an
5894 // interior adjustment (add offset, adjust LEN) to the CHARACTER value
5895 // being referenced in the descriptor.
5896 llvm::SmallVector<mlir::Value> substringBounds;
5897 populateBounds(substringBounds, components.substring);
5898 // Convert to (offset, size)
5899 mlir::Type iTy = substringBounds[0].getType();
5900 if (substringBounds.size() != 2) {
5901 fir::CharacterType charTy =
5902 fir::factory::CharacterExprHelper::getCharType(arrTy);
5903 if (charTy.hasConstantLen()) {
5904 mlir::IndexType idxTy = builder.getIndexType();
5905 fir::CharacterType::LenType charLen = charTy.getLen();
5906 mlir::Value lenValue =
5907 builder.createIntegerConstant(loc, idxTy, charLen);
5908 substringBounds.push_back(lenValue);
5909 } else {
5910 llvm::SmallVector<mlir::Value> typeparams =
5911 fir::getTypeParams(extMemref);
5912 substringBounds.push_back(typeparams.back());
5913 }
5914 }
5915 // Convert the lower bound to 0-based substring.
5916 mlir::Value one =
5917 builder.createIntegerConstant(loc, substringBounds[0].getType(), 1);
5918 substringBounds[0] =
5919 builder.create<mlir::arith::SubIOp>(loc, substringBounds[0], one);
5920 // Convert the upper bound to a length.
5921 mlir::Value cast = builder.createConvert(loc, iTy, substringBounds[1]);
5922 mlir::Value zero = builder.createIntegerConstant(loc, iTy, 0);
5923 auto size =
5924 builder.create<mlir::arith::SubIOp>(loc, cast, substringBounds[0]);
5925 auto cmp = builder.create<mlir::arith::CmpIOp>(
5926 loc, mlir::arith::CmpIPredicate::sgt, size, zero);
5927 // size = MAX(upper - (lower - 1), 0)
5928 substringBounds[1] =
5929 builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
5930 slice = builder.create<fir::SliceOp>(
5931 loc, padSlice(components.trips, shape), components.suffixComponents,
5932 substringBounds);
5933 } else {
5934 slice = builder.createSlice(loc, extMemref, components.trips,
5935 components.suffixComponents);
5936 }
5937 if (components.hasComponents()) {
5938 auto seqTy = arrTy.cast<fir::SequenceType>();
5939 mlir::Type eleTy =
5940 fir::applyPathToType(seqTy.getEleTy(), components.suffixComponents);
5941 if (!eleTy)
5942 fir::emitFatalError(loc, "slicing path is ill-formed");
5943 if (auto realTy = eleTy.dyn_cast<fir::RealType>())
5944 eleTy = Fortran::lower::convertReal(realTy.getContext(),
5945 realTy.getFKind());
5946
5947 // create the type of the projected array.
5948 arrTy = fir::SequenceType::get(seqTy.getShape(), eleTy);
5949 LLVM_DEBUG(llvm::dbgs()
5950 << "type of array projection from component slicing: "
5951 << eleTy << ", " << arrTy << '\n');
5952 }
5953 }
5954 arrayOperands.push_back(ArrayOperand{memref, shape, slice});
5955 if (destShape.empty())
5956 destShape = getShape(arrayOperands.back());
5957 if (isBoxValue()) {
5958 // Semantics are a reference to a boxed array.
5959 // This case just requires that an embox operation be created to box the
5960 // value. The value of the box is forwarded in the continuation.
5961 mlir::Type reduceTy = reduceRank(arrTy, slice);
5962 auto boxTy = fir::BoxType::get(reduceTy);
5963 if (components.substring) {
5964 // Adjust char length to substring size.
5965 fir::CharacterType charTy =
5966 fir::factory::CharacterExprHelper::getCharType(reduceTy);
5967 auto seqTy = reduceTy.cast<fir::SequenceType>();
5968 // TODO: Use a constant for fir.char LEN if we can compute it.
5969 boxTy = fir::BoxType::get(
5970 fir::SequenceType::get(fir::CharacterType::getUnknownLen(
5971 builder.getContext(), charTy.getFKind()),
5972 seqTy.getDimension()));
5973 }
5974 mlir::Value embox =
5975 memref.getType().isa<fir::BoxType>()
5976 ? builder.create<fir::ReboxOp>(loc, boxTy, memref, shape, slice)
5977 .getResult()
5978 : builder
5979 .create<fir::EmboxOp>(loc, boxTy, memref, shape, slice,
5980 fir::getTypeParams(extMemref))
5981 .getResult();
5982 return [=](IterSpace) -> ExtValue { return fir::BoxValue(embox); };
5983 }
5984 auto eleTy = arrTy.cast<fir::SequenceType>().getEleTy();
5985 if (isReferentiallyOpaque()) {
5986 // Semantics are an opaque reference to an array.
5987 // This case forwards a continuation that will generate the address
5988 // arithmetic to the array element. This does not have copy-in/copy-out
5989 // semantics. No attempt to copy the array value will be made during the
5990 // interpretation of the Fortran statement.
5991 mlir::Type refEleTy = builder.getRefType(eleTy);
5992 return [=](IterSpace iters) -> ExtValue {
5993 // ArrayCoorOp does not expect zero based indices.
5994 llvm::SmallVector<mlir::Value> indices = fir::factory::originateIndices(
5995 loc, builder, memref.getType(), shape, iters.iterVec());
5996 mlir::Value coor = builder.create<fir::ArrayCoorOp>(
5997 loc, refEleTy, memref, shape, slice, indices,
5998 fir::getTypeParams(extMemref));
5999 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6000 llvm::SmallVector<mlir::Value> substringBounds;
6001 populateBounds(substringBounds, components.substring);
6002 if (!substringBounds.empty()) {
6003 mlir::Value dstLen = fir::factory::genLenOfCharacter(
6004 builder, loc, arrTy.cast<fir::SequenceType>(), memref,
6005 fir::getTypeParams(extMemref), iters.iterVec(),
6006 substringBounds);
6007 fir::CharBoxValue dstChar(coor, dstLen);
6008 return fir::factory::CharacterExprHelper{builder, loc}
6009 .createSubstring(dstChar, substringBounds);
6010 }
6011 }
6012 return fir::factory::arraySectionElementToExtendedValue(
6013 builder, loc, extMemref, coor, slice);
6014 };
6015 }
6016 auto arrLoad = builder.create<fir::ArrayLoadOp>(
6017 loc, arrTy, memref, shape, slice, fir::getTypeParams(extMemref));
6018 mlir::Value arrLd = arrLoad.getResult();
6019 if (isProjectedCopyInCopyOut()) {
6020 // Semantics are projected copy-in copy-out.
6021 // The backing store of the destination of an array expression may be
6022 // partially modified. These updates are recorded in FIR by forwarding a
6023 // continuation that generates an `array_update` Op. The destination is
6024 // always loaded at the beginning of the statement and merged at the
6025 // end.
6026 destination = arrLoad;
6027 auto lambda = ccStoreToDest
6028 ? *ccStoreToDest
6029 : defaultStoreToDestination(components.substring);
6030 return [=](IterSpace iters) -> ExtValue { return lambda(iters); };
6031 }
6032 if (isCustomCopyInCopyOut()) {
6033 // Create an array_modify to get the LHS element address and indicate
6034 // the assignment, the actual assignment must be implemented in
6035 // ccStoreToDest.
6036 destination = arrLoad;
6037 return [=](IterSpace iters) -> ExtValue {
6038 mlir::Value innerArg = iters.innerArgument();
6039 mlir::Type resTy = innerArg.getType();
6040 mlir::Type eleTy = fir::applyPathToType(resTy, iters.iterVec());
6041 mlir::Type refEleTy =
6042 fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
6043 auto arrModify = builder.create<fir::ArrayModifyOp>(
6044 loc, mlir::TypeRange{refEleTy, resTy}, innerArg, iters.iterVec(),
6045 destination.getTypeparams());
6046 return abstractArrayExtValue(arrModify.getResult(1));
6047 };
6048 }
6049 if (isCopyInCopyOut()) {
6050 // Semantics are copy-in copy-out.
6051 // The continuation simply forwards the result of the `array_load` Op,
6052 // which is the value of the array as it was when loaded. All data
6053 // references with rank > 0 in an array expression typically have
6054 // copy-in copy-out semantics.
6055 return [=](IterSpace) -> ExtValue { return arrLd; };
6056 }
6057 llvm::SmallVector<mlir::Value> arrLdTypeParams =
6058 fir::factory::getTypeParams(loc, builder, arrLoad);
6059 if (isValueAttribute()) {
6060 // Semantics are value attribute.
6061 // Here the continuation will `array_fetch` a value from an array and
6062 // then store that value in a temporary. One can thus imitate pass by
6063 // value even when the call is pass by reference.
6064 return [=](IterSpace iters) -> ExtValue {
6065 mlir::Value base;
6066 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
6067 if (isAdjustedArrayElementType(eleTy)) {
6068 mlir::Type eleRefTy = builder.getRefType(eleTy);
6069 base = builder.create<fir::ArrayAccessOp>(
6070 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
6071 } else {
6072 base = builder.create<fir::ArrayFetchOp>(
6073 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
6074 }
6075 mlir::Value temp = builder.createTemporary(
6076 loc, base.getType(),
6077 llvm::ArrayRef<mlir::NamedAttribute>{
6078 Fortran::lower::getAdaptToByRefAttr(builder)});
6079 builder.create<fir::StoreOp>(loc, base, temp);
6080 return fir::factory::arraySectionElementToExtendedValue(
6081 builder, loc, extMemref, temp, slice);
6082 };
6083 }
6084 // In the default case, the array reference forwards an `array_fetch` or
6085 // `array_access` Op in the continuation.
6086 return [=](IterSpace iters) -> ExtValue {
6087 mlir::Type eleTy = fir::applyPathToType(arrTy, iters.iterVec());
6088 if (isAdjustedArrayElementType(eleTy)) {
6089 mlir::Type eleRefTy = builder.getRefType(eleTy);
6090 mlir::Value arrayOp = builder.create<fir::ArrayAccessOp>(
6091 loc, eleRefTy, arrLd, iters.iterVec(), arrLdTypeParams);
6092 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6093 llvm::SmallVector<mlir::Value> substringBounds;
6094 populateBounds(substringBounds, components.substring);
6095 if (!substringBounds.empty()) {
6096 mlir::Value dstLen = fir::factory::genLenOfCharacter(
6097 builder, loc, arrLoad, iters.iterVec(), substringBounds);
6098 fir::CharBoxValue dstChar(arrayOp, dstLen);
6099 return fir::factory::CharacterExprHelper{builder, loc}
6100 .createSubstring(dstChar, substringBounds);
6101 }
6102 }
6103 return fir::factory::arraySectionElementToExtendedValue(
6104 builder, loc, extMemref, arrayOp, slice);
6105 }
6106 auto arrFetch = builder.create<fir::ArrayFetchOp>(
6107 loc, eleTy, arrLd, iters.iterVec(), arrLdTypeParams);
6108 return fir::factory::arraySectionElementToExtendedValue(
6109 builder, loc, extMemref, arrFetch, slice);
6110 };
6111 }
6112
6113 std::tuple<CC, mlir::Value, mlir::Type>
genOptionalArrayFetch(const Fortran::lower::SomeExpr & expr)6114 genOptionalArrayFetch(const Fortran::lower::SomeExpr &expr) {
6115 assert(expr.Rank() > 0 && "expr must be an array");
6116 mlir::Location loc = getLoc();
6117 ExtValue optionalArg = asInquired(expr);
6118 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
6119 // Generate an array load and access to an array that may be an absent
6120 // optional or an unallocated optional.
6121 mlir::Value base = getBase(optionalArg);
6122 const bool hasOptionalAttr =
6123 fir::valueHasFirAttribute(base, fir::getOptionalAttrName());
6124 mlir::Type baseType = fir::unwrapRefType(base.getType());
6125 const bool isBox = baseType.isa<fir::BoxType>();
6126 const bool isAllocOrPtr = Fortran::evaluate::IsAllocatableOrPointerObject(
6127 expr, converter.getFoldingContext());
6128 mlir::Type arrType = fir::unwrapPassByRefType(baseType);
6129 mlir::Type eleType = fir::unwrapSequenceType(arrType);
6130 ExtValue exv = optionalArg;
6131 if (hasOptionalAttr && isBox && !isAllocOrPtr) {
6132 // Elemental argument cannot be allocatable or pointers (C15100).
6133 // Hence, per 15.5.2.12 3 (8) and (9), the provided Allocatable and
6134 // Pointer optional arrays cannot be absent. The only kind of entities
6135 // that can get here are optional assumed shape and polymorphic entities.
6136 exv = absentBoxToUnallocatedBox(builder, loc, exv, isPresent);
6137 }
6138 // All the properties can be read from any fir.box but the read values may
6139 // be undefined and should only be used inside a fir.if (canBeRead) region.
6140 if (const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>())
6141 exv = fir::factory::genMutableBoxRead(builder, loc, *mutableBox);
6142
6143 mlir::Value memref = fir::getBase(exv);
6144 mlir::Value shape = builder.createShape(loc, exv);
6145 mlir::Value noSlice;
6146 auto arrLoad = builder.create<fir::ArrayLoadOp>(
6147 loc, arrType, memref, shape, noSlice, fir::getTypeParams(exv));
6148 mlir::Operation::operand_range arrLdTypeParams = arrLoad.getTypeparams();
6149 mlir::Value arrLd = arrLoad.getResult();
6150 // Mark the load to tell later passes it is unsafe to use this array_load
6151 // shape unconditionally.
6152 arrLoad->setAttr(fir::getOptionalAttrName(), builder.getUnitAttr());
6153
6154 // Place the array as optional on the arrayOperands stack so that its
6155 // shape will only be used as a fallback to induce the implicit loop nest
6156 // (that is if there is no non optional array arguments).
6157 arrayOperands.push_back(
6158 ArrayOperand{memref, shape, noSlice, /*mayBeAbsent=*/true});
6159
6160 // By value semantics.
6161 auto cc = [=](IterSpace iters) -> ExtValue {
6162 auto arrFetch = builder.create<fir::ArrayFetchOp>(
6163 loc, eleType, arrLd, iters.iterVec(), arrLdTypeParams);
6164 return fir::factory::arraySectionElementToExtendedValue(
6165 builder, loc, exv, arrFetch, noSlice);
6166 };
6167 return {cc, isPresent, eleType};
6168 }
6169
6170 /// Generate a continuation to pass \p expr to an OPTIONAL argument of an
6171 /// elemental procedure. This is meant to handle the cases where \p expr might
6172 /// be dynamically absent (i.e. when it is a POINTER, an ALLOCATABLE or an
6173 /// OPTIONAL variable). If p\ expr is guaranteed to be present genarr() can
6174 /// directly be called instead.
genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr & expr)6175 CC genarrForwardOptionalArgumentToCall(const Fortran::lower::SomeExpr &expr) {
6176 mlir::Location loc = getLoc();
6177 // Only by-value numerical and logical so far.
6178 if (semant != ConstituentSemantics::RefTransparent)
6179 TODO(loc, "optional arguments in user defined elemental procedures");
6180
6181 // Handle scalar argument case (the if-then-else is generated outside of the
6182 // implicit loop nest).
6183 if (expr.Rank() == 0) {
6184 ExtValue optionalArg = asInquired(expr);
6185 mlir::Value isPresent = genActualIsPresentTest(builder, loc, optionalArg);
6186 mlir::Value elementValue =
6187 fir::getBase(genOptionalValue(builder, loc, optionalArg, isPresent));
6188 return [=](IterSpace iters) -> ExtValue { return elementValue; };
6189 }
6190
6191 CC cc;
6192 mlir::Value isPresent;
6193 mlir::Type eleType;
6194 std::tie(cc, isPresent, eleType) = genOptionalArrayFetch(expr);
6195 return [=](IterSpace iters) -> ExtValue {
6196 mlir::Value elementValue =
6197 builder
6198 .genIfOp(loc, {eleType}, isPresent,
6199 /*withElseRegion=*/true)
6200 .genThen([&]() {
6201 builder.create<fir::ResultOp>(loc, fir::getBase(cc(iters)));
6202 })
6203 .genElse([&]() {
6204 mlir::Value zero =
6205 fir::factory::createZeroValue(builder, loc, eleType);
6206 builder.create<fir::ResultOp>(loc, zero);
6207 })
6208 .getResults()[0];
6209 return elementValue;
6210 };
6211 }
6212
6213 /// Reduce the rank of a array to be boxed based on the slice's operands.
reduceRank(mlir::Type arrTy,mlir::Value slice)6214 static mlir::Type reduceRank(mlir::Type arrTy, mlir::Value slice) {
6215 if (slice) {
6216 auto slOp = mlir::dyn_cast<fir::SliceOp>(slice.getDefiningOp());
6217 assert(slOp && "expected slice op");
6218 auto seqTy = arrTy.dyn_cast<fir::SequenceType>();
6219 assert(seqTy && "expected array type");
6220 mlir::Operation::operand_range triples = slOp.getTriples();
6221 fir::SequenceType::Shape shape;
6222 // reduce the rank for each invariant dimension
6223 for (unsigned i = 1, end = triples.size(); i < end; i += 3) {
6224 if (auto extent = fir::factory::getExtentFromTriplet(
6225 triples[i - 1], triples[i], triples[i + 1]))
6226 shape.push_back(*extent);
6227 else if (!mlir::isa_and_nonnull<fir::UndefOp>(
6228 triples[i].getDefiningOp()))
6229 shape.push_back(fir::SequenceType::getUnknownExtent());
6230 }
6231 return fir::SequenceType::get(shape, seqTy.getEleTy());
6232 }
6233 // not sliced, so no change in rank
6234 return arrTy;
6235 }
6236
6237 /// Example: <code>array%RE</code>
genarr(const Fortran::evaluate::ComplexPart & x,ComponentPath & components)6238 CC genarr(const Fortran::evaluate::ComplexPart &x,
6239 ComponentPath &components) {
6240 components.reversePath.push_back(&x);
6241 return genarr(x.complex(), components);
6242 }
6243
6244 template <typename A>
genSlicePath(const A & x,ComponentPath & components)6245 CC genSlicePath(const A &x, ComponentPath &components) {
6246 return genarr(x, components);
6247 }
6248
genarr(const Fortran::evaluate::StaticDataObject::Pointer &,ComponentPath & components)6249 CC genarr(const Fortran::evaluate::StaticDataObject::Pointer &,
6250 ComponentPath &components) {
6251 fir::emitFatalError(getLoc(), "substring of static array object");
6252 }
6253
6254 /// Substrings (see 9.4.1)
genarr(const Fortran::evaluate::Substring & x,ComponentPath & components)6255 CC genarr(const Fortran::evaluate::Substring &x, ComponentPath &components) {
6256 components.substring = &x;
6257 return std::visit([&](const auto &v) { return genarr(v, components); },
6258 x.parent());
6259 }
6260
6261 template <typename T>
genarr(const Fortran::evaluate::FunctionRef<T> & funRef)6262 CC genarr(const Fortran::evaluate::FunctionRef<T> &funRef) {
6263 // Note that it's possible that the function being called returns either an
6264 // array or a scalar. In the first case, use the element type of the array.
6265 return genProcRef(
6266 funRef, fir::unwrapSequenceType(converter.genType(toEvExpr(funRef))));
6267 }
6268
6269 //===--------------------------------------------------------------------===//
6270 // Array construction
6271 //===--------------------------------------------------------------------===//
6272
6273 /// Target agnostic computation of the size of an element in the array.
6274 /// Returns the size in bytes with type `index` or a null Value if the element
6275 /// size is not constant.
computeElementSize(const ExtValue & exv,mlir::Type eleTy,mlir::Type resTy)6276 mlir::Value computeElementSize(const ExtValue &exv, mlir::Type eleTy,
6277 mlir::Type resTy) {
6278 mlir::Location loc = getLoc();
6279 mlir::IndexType idxTy = builder.getIndexType();
6280 mlir::Value multiplier = builder.createIntegerConstant(loc, idxTy, 1);
6281 if (fir::hasDynamicSize(eleTy)) {
6282 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6283 // Array of char with dynamic LEN parameter. Downcast to an array
6284 // of singleton char, and scale by the len type parameter from
6285 // `exv`.
6286 exv.match(
6287 [&](const fir::CharBoxValue &cb) { multiplier = cb.getLen(); },
6288 [&](const fir::CharArrayBoxValue &cb) { multiplier = cb.getLen(); },
6289 [&](const fir::BoxValue &box) {
6290 multiplier = fir::factory::CharacterExprHelper(builder, loc)
6291 .readLengthFromBox(box.getAddr());
6292 },
6293 [&](const fir::MutableBoxValue &box) {
6294 multiplier = fir::factory::CharacterExprHelper(builder, loc)
6295 .readLengthFromBox(box.getAddr());
6296 },
6297 [&](const auto &) {
6298 fir::emitFatalError(loc,
6299 "array constructor element has unknown size");
6300 });
6301 fir::CharacterType newEleTy = fir::CharacterType::getSingleton(
6302 eleTy.getContext(), charTy.getFKind());
6303 if (auto seqTy = resTy.dyn_cast<fir::SequenceType>()) {
6304 assert(eleTy == seqTy.getEleTy());
6305 resTy = fir::SequenceType::get(seqTy.getShape(), newEleTy);
6306 }
6307 eleTy = newEleTy;
6308 } else {
6309 TODO(loc, "dynamic sized type");
6310 }
6311 }
6312 mlir::Type eleRefTy = builder.getRefType(eleTy);
6313 mlir::Type resRefTy = builder.getRefType(resTy);
6314 mlir::Value nullPtr = builder.createNullConstant(loc, resRefTy);
6315 auto offset = builder.create<fir::CoordinateOp>(
6316 loc, eleRefTy, nullPtr, mlir::ValueRange{multiplier});
6317 return builder.createConvert(loc, idxTy, offset);
6318 }
6319
6320 /// Get the function signature of the LLVM memcpy intrinsic.
memcpyType()6321 mlir::FunctionType memcpyType() {
6322 return fir::factory::getLlvmMemcpy(builder).getFunctionType();
6323 }
6324
6325 /// Create a call to the LLVM memcpy intrinsic.
createCallMemcpy(llvm::ArrayRef<mlir::Value> args)6326 void createCallMemcpy(llvm::ArrayRef<mlir::Value> args) {
6327 mlir::Location loc = getLoc();
6328 mlir::func::FuncOp memcpyFunc = fir::factory::getLlvmMemcpy(builder);
6329 mlir::SymbolRefAttr funcSymAttr =
6330 builder.getSymbolRefAttr(memcpyFunc.getName());
6331 mlir::FunctionType funcTy = memcpyFunc.getFunctionType();
6332 builder.create<fir::CallOp>(loc, funcTy.getResults(), funcSymAttr, args);
6333 }
6334
6335 // Construct code to check for a buffer overrun and realloc the buffer when
6336 // space is depleted. This is done between each item in the ac-value-list.
growBuffer(mlir::Value mem,mlir::Value needed,mlir::Value bufferSize,mlir::Value buffSize,mlir::Value eleSz)6337 mlir::Value growBuffer(mlir::Value mem, mlir::Value needed,
6338 mlir::Value bufferSize, mlir::Value buffSize,
6339 mlir::Value eleSz) {
6340 mlir::Location loc = getLoc();
6341 mlir::func::FuncOp reallocFunc = fir::factory::getRealloc(builder);
6342 auto cond = builder.create<mlir::arith::CmpIOp>(
6343 loc, mlir::arith::CmpIPredicate::sle, bufferSize, needed);
6344 auto ifOp = builder.create<fir::IfOp>(loc, mem.getType(), cond,
6345 /*withElseRegion=*/true);
6346 auto insPt = builder.saveInsertionPoint();
6347 builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
6348 // Not enough space, resize the buffer.
6349 mlir::IndexType idxTy = builder.getIndexType();
6350 mlir::Value two = builder.createIntegerConstant(loc, idxTy, 2);
6351 auto newSz = builder.create<mlir::arith::MulIOp>(loc, needed, two);
6352 builder.create<fir::StoreOp>(loc, newSz, buffSize);
6353 mlir::Value byteSz = builder.create<mlir::arith::MulIOp>(loc, newSz, eleSz);
6354 mlir::SymbolRefAttr funcSymAttr =
6355 builder.getSymbolRefAttr(reallocFunc.getName());
6356 mlir::FunctionType funcTy = reallocFunc.getFunctionType();
6357 auto newMem = builder.create<fir::CallOp>(
6358 loc, funcTy.getResults(), funcSymAttr,
6359 llvm::ArrayRef<mlir::Value>{
6360 builder.createConvert(loc, funcTy.getInputs()[0], mem),
6361 builder.createConvert(loc, funcTy.getInputs()[1], byteSz)});
6362 mlir::Value castNewMem =
6363 builder.createConvert(loc, mem.getType(), newMem.getResult(0));
6364 builder.create<fir::ResultOp>(loc, castNewMem);
6365 builder.setInsertionPointToStart(&ifOp.getElseRegion().front());
6366 // Otherwise, just forward the buffer.
6367 builder.create<fir::ResultOp>(loc, mem);
6368 builder.restoreInsertionPoint(insPt);
6369 return ifOp.getResult(0);
6370 }
6371
6372 /// Copy the next value (or vector of values) into the array being
6373 /// constructed.
copyNextArrayCtorSection(const ExtValue & exv,mlir::Value buffPos,mlir::Value buffSize,mlir::Value mem,mlir::Value eleSz,mlir::Type eleTy,mlir::Type eleRefTy,mlir::Type resTy)6374 mlir::Value copyNextArrayCtorSection(const ExtValue &exv, mlir::Value buffPos,
6375 mlir::Value buffSize, mlir::Value mem,
6376 mlir::Value eleSz, mlir::Type eleTy,
6377 mlir::Type eleRefTy, mlir::Type resTy) {
6378 mlir::Location loc = getLoc();
6379 auto off = builder.create<fir::LoadOp>(loc, buffPos);
6380 auto limit = builder.create<fir::LoadOp>(loc, buffSize);
6381 mlir::IndexType idxTy = builder.getIndexType();
6382 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
6383
6384 if (fir::isRecordWithAllocatableMember(eleTy))
6385 TODO(loc, "deep copy on allocatable members");
6386
6387 if (!eleSz) {
6388 // Compute the element size at runtime.
6389 assert(fir::hasDynamicSize(eleTy));
6390 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6391 auto charBytes =
6392 builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
6393 mlir::Value bytes =
6394 builder.createIntegerConstant(loc, idxTy, charBytes);
6395 mlir::Value length = fir::getLen(exv);
6396 if (!length)
6397 fir::emitFatalError(loc, "result is not boxed character");
6398 eleSz = builder.create<mlir::arith::MulIOp>(loc, bytes, length);
6399 } else {
6400 TODO(loc, "PDT size");
6401 // Will call the PDT's size function with the type parameters.
6402 }
6403 }
6404
6405 // Compute the coordinate using `fir.coordinate_of`, or, if the type has
6406 // dynamic size, generating the pointer arithmetic.
6407 auto computeCoordinate = [&](mlir::Value buff, mlir::Value off) {
6408 mlir::Type refTy = eleRefTy;
6409 if (fir::hasDynamicSize(eleTy)) {
6410 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
6411 // Scale a simple pointer using dynamic length and offset values.
6412 auto chTy = fir::CharacterType::getSingleton(charTy.getContext(),
6413 charTy.getFKind());
6414 refTy = builder.getRefType(chTy);
6415 mlir::Type toTy = builder.getRefType(builder.getVarLenSeqTy(chTy));
6416 buff = builder.createConvert(loc, toTy, buff);
6417 off = builder.create<mlir::arith::MulIOp>(loc, off, eleSz);
6418 } else {
6419 TODO(loc, "PDT offset");
6420 }
6421 }
6422 auto coor = builder.create<fir::CoordinateOp>(loc, refTy, buff,
6423 mlir::ValueRange{off});
6424 return builder.createConvert(loc, eleRefTy, coor);
6425 };
6426
6427 // Lambda to lower an abstract array box value.
6428 auto doAbstractArray = [&](const auto &v) {
6429 // Compute the array size.
6430 mlir::Value arrSz = one;
6431 for (auto ext : v.getExtents())
6432 arrSz = builder.create<mlir::arith::MulIOp>(loc, arrSz, ext);
6433
6434 // Grow the buffer as needed.
6435 auto endOff = builder.create<mlir::arith::AddIOp>(loc, off, arrSz);
6436 mem = growBuffer(mem, endOff, limit, buffSize, eleSz);
6437
6438 // Copy the elements to the buffer.
6439 mlir::Value byteSz =
6440 builder.create<mlir::arith::MulIOp>(loc, arrSz, eleSz);
6441 auto buff = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
6442 mlir::Value buffi = computeCoordinate(buff, off);
6443 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
6444 builder, loc, memcpyType(), buffi, v.getAddr(), byteSz,
6445 /*volatile=*/builder.createBool(loc, false));
6446 createCallMemcpy(args);
6447
6448 // Save the incremented buffer position.
6449 builder.create<fir::StoreOp>(loc, endOff, buffPos);
6450 };
6451
6452 // Copy a trivial scalar value into the buffer.
6453 auto doTrivialScalar = [&](const ExtValue &v, mlir::Value len = {}) {
6454 // Increment the buffer position.
6455 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
6456
6457 // Grow the buffer as needed.
6458 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
6459
6460 // Store the element in the buffer.
6461 mlir::Value buff =
6462 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
6463 auto buffi = builder.create<fir::CoordinateOp>(loc, eleRefTy, buff,
6464 mlir::ValueRange{off});
6465 fir::factory::genScalarAssignment(
6466 builder, loc,
6467 [&]() -> ExtValue {
6468 if (len)
6469 return fir::CharBoxValue(buffi, len);
6470 return buffi;
6471 }(),
6472 v);
6473 builder.create<fir::StoreOp>(loc, plusOne, buffPos);
6474 };
6475
6476 // Copy the value.
6477 exv.match(
6478 [&](mlir::Value) { doTrivialScalar(exv); },
6479 [&](const fir::CharBoxValue &v) {
6480 auto buffer = v.getBuffer();
6481 if (fir::isa_char(buffer.getType())) {
6482 doTrivialScalar(exv, eleSz);
6483 } else {
6484 // Increment the buffer position.
6485 auto plusOne = builder.create<mlir::arith::AddIOp>(loc, off, one);
6486
6487 // Grow the buffer as needed.
6488 mem = growBuffer(mem, plusOne, limit, buffSize, eleSz);
6489
6490 // Store the element in the buffer.
6491 mlir::Value buff =
6492 builder.createConvert(loc, fir::HeapType::get(resTy), mem);
6493 mlir::Value buffi = computeCoordinate(buff, off);
6494 llvm::SmallVector<mlir::Value> args = fir::runtime::createArguments(
6495 builder, loc, memcpyType(), buffi, v.getAddr(), eleSz,
6496 /*volatile=*/builder.createBool(loc, false));
6497 createCallMemcpy(args);
6498
6499 builder.create<fir::StoreOp>(loc, plusOne, buffPos);
6500 }
6501 },
6502 [&](const fir::ArrayBoxValue &v) { doAbstractArray(v); },
6503 [&](const fir::CharArrayBoxValue &v) { doAbstractArray(v); },
6504 [&](const auto &) {
6505 TODO(loc, "unhandled array constructor expression");
6506 });
6507 return mem;
6508 }
6509
6510 // Lower the expr cases in an ac-value-list.
6511 template <typename A>
6512 std::pair<ExtValue, bool>
genArrayCtorInitializer(const Fortran::evaluate::Expr<A> & x,mlir::Type,mlir::Value,mlir::Value,mlir::Value,Fortran::lower::StatementContext & stmtCtx)6513 genArrayCtorInitializer(const Fortran::evaluate::Expr<A> &x, mlir::Type,
6514 mlir::Value, mlir::Value, mlir::Value,
6515 Fortran::lower::StatementContext &stmtCtx) {
6516 if (isArray(x))
6517 return {lowerNewArrayExpression(converter, symMap, stmtCtx, toEvExpr(x)),
6518 /*needCopy=*/true};
6519 return {asScalar(x), /*needCopy=*/true};
6520 }
6521
6522 // Lower an ac-implied-do in an ac-value-list.
6523 template <typename A>
6524 std::pair<ExtValue, bool>
genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> & x,mlir::Type resTy,mlir::Value mem,mlir::Value buffPos,mlir::Value buffSize,Fortran::lower::StatementContext &)6525 genArrayCtorInitializer(const Fortran::evaluate::ImpliedDo<A> &x,
6526 mlir::Type resTy, mlir::Value mem,
6527 mlir::Value buffPos, mlir::Value buffSize,
6528 Fortran::lower::StatementContext &) {
6529 mlir::Location loc = getLoc();
6530 mlir::IndexType idxTy = builder.getIndexType();
6531 mlir::Value lo =
6532 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.lower())));
6533 mlir::Value up =
6534 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.upper())));
6535 mlir::Value step =
6536 builder.createConvert(loc, idxTy, fir::getBase(asScalar(x.stride())));
6537 auto seqTy = resTy.template cast<fir::SequenceType>();
6538 mlir::Type eleTy = fir::unwrapSequenceType(seqTy);
6539 auto loop =
6540 builder.create<fir::DoLoopOp>(loc, lo, up, step, /*unordered=*/false,
6541 /*finalCount=*/false, mem);
6542 // create a new binding for x.name(), to ac-do-variable, to the iteration
6543 // value.
6544 symMap.pushImpliedDoBinding(toStringRef(x.name()), loop.getInductionVar());
6545 auto insPt = builder.saveInsertionPoint();
6546 builder.setInsertionPointToStart(loop.getBody());
6547 // Thread mem inside the loop via loop argument.
6548 mem = loop.getRegionIterArgs()[0];
6549
6550 mlir::Type eleRefTy = builder.getRefType(eleTy);
6551
6552 // Any temps created in the loop body must be freed inside the loop body.
6553 stmtCtx.pushScope();
6554 llvm::Optional<mlir::Value> charLen;
6555 for (const Fortran::evaluate::ArrayConstructorValue<A> &acv : x.values()) {
6556 auto [exv, copyNeeded] = std::visit(
6557 [&](const auto &v) {
6558 return genArrayCtorInitializer(v, resTy, mem, buffPos, buffSize,
6559 stmtCtx);
6560 },
6561 acv.u);
6562 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
6563 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
6564 eleSz, eleTy, eleRefTy, resTy)
6565 : fir::getBase(exv);
6566 if (fir::isa_char(seqTy.getEleTy()) && !charLen) {
6567 charLen = builder.createTemporary(loc, builder.getI64Type());
6568 mlir::Value castLen =
6569 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
6570 builder.create<fir::StoreOp>(loc, castLen, charLen.value());
6571 }
6572 }
6573 stmtCtx.finalizeAndPop();
6574
6575 builder.create<fir::ResultOp>(loc, mem);
6576 builder.restoreInsertionPoint(insPt);
6577 mem = loop.getResult(0);
6578 symMap.popImpliedDoBinding();
6579 llvm::SmallVector<mlir::Value> extents = {
6580 builder.create<fir::LoadOp>(loc, buffPos).getResult()};
6581
6582 // Convert to extended value.
6583 if (fir::isa_char(seqTy.getEleTy())) {
6584 auto len = builder.create<fir::LoadOp>(loc, charLen.value());
6585 return {fir::CharArrayBoxValue{mem, len, extents}, /*needCopy=*/false};
6586 }
6587 return {fir::ArrayBoxValue{mem, extents}, /*needCopy=*/false};
6588 }
6589
6590 // To simplify the handling and interaction between the various cases, array
6591 // constructors are always lowered to the incremental construction code
6592 // pattern, even if the extent of the array value is constant. After the
6593 // MemToReg pass and constant folding, the optimizer should be able to
6594 // determine that all the buffer overrun tests are false when the
6595 // incremental construction wasn't actually required.
6596 template <typename A>
genarr(const Fortran::evaluate::ArrayConstructor<A> & x)6597 CC genarr(const Fortran::evaluate::ArrayConstructor<A> &x) {
6598 mlir::Location loc = getLoc();
6599 auto evExpr = toEvExpr(x);
6600 mlir::Type resTy = translateSomeExprToFIRType(converter, evExpr);
6601 mlir::IndexType idxTy = builder.getIndexType();
6602 auto seqTy = resTy.template cast<fir::SequenceType>();
6603 mlir::Type eleTy = fir::unwrapSequenceType(resTy);
6604 mlir::Value buffSize = builder.createTemporary(loc, idxTy, ".buff.size");
6605 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
6606 mlir::Value buffPos = builder.createTemporary(loc, idxTy, ".buff.pos");
6607 builder.create<fir::StoreOp>(loc, zero, buffPos);
6608 // Allocate space for the array to be constructed.
6609 mlir::Value mem;
6610 if (fir::hasDynamicSize(resTy)) {
6611 if (fir::hasDynamicSize(eleTy)) {
6612 // The size of each element may depend on a general expression. Defer
6613 // creating the buffer until after the expression is evaluated.
6614 mem = builder.createNullConstant(loc, builder.getRefType(eleTy));
6615 builder.create<fir::StoreOp>(loc, zero, buffSize);
6616 } else {
6617 mlir::Value initBuffSz =
6618 builder.createIntegerConstant(loc, idxTy, clInitialBufferSize);
6619 mem = builder.create<fir::AllocMemOp>(
6620 loc, eleTy, /*typeparams=*/llvm::None, initBuffSz);
6621 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
6622 }
6623 } else {
6624 mem = builder.create<fir::AllocMemOp>(loc, resTy);
6625 int64_t buffSz = 1;
6626 for (auto extent : seqTy.getShape())
6627 buffSz *= extent;
6628 mlir::Value initBuffSz =
6629 builder.createIntegerConstant(loc, idxTy, buffSz);
6630 builder.create<fir::StoreOp>(loc, initBuffSz, buffSize);
6631 }
6632 // Compute size of element
6633 mlir::Type eleRefTy = builder.getRefType(eleTy);
6634
6635 // Populate the buffer with the elements, growing as necessary.
6636 llvm::Optional<mlir::Value> charLen;
6637 for (const auto &expr : x) {
6638 auto [exv, copyNeeded] = std::visit(
6639 [&](const auto &e) {
6640 return genArrayCtorInitializer(e, resTy, mem, buffPos, buffSize,
6641 stmtCtx);
6642 },
6643 expr.u);
6644 mlir::Value eleSz = computeElementSize(exv, eleTy, resTy);
6645 mem = copyNeeded ? copyNextArrayCtorSection(exv, buffPos, buffSize, mem,
6646 eleSz, eleTy, eleRefTy, resTy)
6647 : fir::getBase(exv);
6648 if (fir::isa_char(seqTy.getEleTy()) && !charLen) {
6649 charLen = builder.createTemporary(loc, builder.getI64Type());
6650 mlir::Value castLen =
6651 builder.createConvert(loc, builder.getI64Type(), fir::getLen(exv));
6652 builder.create<fir::StoreOp>(loc, castLen, charLen.value());
6653 }
6654 }
6655 mem = builder.createConvert(loc, fir::HeapType::get(resTy), mem);
6656 llvm::SmallVector<mlir::Value> extents = {
6657 builder.create<fir::LoadOp>(loc, buffPos)};
6658
6659 // Cleanup the temporary.
6660 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder();
6661 stmtCtx.attachCleanup(
6662 [bldr, loc, mem]() { bldr->create<fir::FreeMemOp>(loc, mem); });
6663
6664 // Return the continuation.
6665 if (fir::isa_char(seqTy.getEleTy())) {
6666 if (charLen) {
6667 auto len = builder.create<fir::LoadOp>(loc, *charLen);
6668 return genarr(fir::CharArrayBoxValue{mem, len, extents});
6669 }
6670 return genarr(fir::CharArrayBoxValue{mem, zero, extents});
6671 }
6672 return genarr(fir::ArrayBoxValue{mem, extents});
6673 }
6674
genarr(const Fortran::evaluate::ImpliedDoIndex &)6675 CC genarr(const Fortran::evaluate::ImpliedDoIndex &) {
6676 fir::emitFatalError(getLoc(), "implied do index cannot have rank > 0");
6677 }
genarr(const Fortran::evaluate::TypeParamInquiry & x)6678 CC genarr(const Fortran::evaluate::TypeParamInquiry &x) {
6679 TODO(getLoc(), "array expr type parameter inquiry");
6680 return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
6681 }
genarr(const Fortran::evaluate::DescriptorInquiry & x)6682 CC genarr(const Fortran::evaluate::DescriptorInquiry &x) {
6683 TODO(getLoc(), "array expr descriptor inquiry");
6684 return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
6685 }
genarr(const Fortran::evaluate::StructureConstructor & x)6686 CC genarr(const Fortran::evaluate::StructureConstructor &x) {
6687 TODO(getLoc(), "structure constructor");
6688 return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
6689 }
6690
6691 //===--------------------------------------------------------------------===//
6692 // LOCICAL operators (.NOT., .AND., .EQV., etc.)
6693 //===--------------------------------------------------------------------===//
6694
6695 template <int KIND>
genarr(const Fortran::evaluate::Not<KIND> & x)6696 CC genarr(const Fortran::evaluate::Not<KIND> &x) {
6697 mlir::Location loc = getLoc();
6698 mlir::IntegerType i1Ty = builder.getI1Type();
6699 auto lambda = genarr(x.left());
6700 mlir::Value truth = builder.createBool(loc, true);
6701 return [=](IterSpace iters) -> ExtValue {
6702 mlir::Value logical = fir::getBase(lambda(iters));
6703 mlir::Value val = builder.createConvert(loc, i1Ty, logical);
6704 return builder.create<mlir::arith::XOrIOp>(loc, val, truth);
6705 };
6706 }
6707 template <typename OP, typename A>
createBinaryBoolOp(const A & x)6708 CC createBinaryBoolOp(const A &x) {
6709 mlir::Location loc = getLoc();
6710 mlir::IntegerType i1Ty = builder.getI1Type();
6711 auto lf = genarr(x.left());
6712 auto rf = genarr(x.right());
6713 return [=](IterSpace iters) -> ExtValue {
6714 mlir::Value left = fir::getBase(lf(iters));
6715 mlir::Value right = fir::getBase(rf(iters));
6716 mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
6717 mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
6718 return builder.create<OP>(loc, lhs, rhs);
6719 };
6720 }
6721 template <typename OP, typename A>
createCompareBoolOp(mlir::arith::CmpIPredicate pred,const A & x)6722 CC createCompareBoolOp(mlir::arith::CmpIPredicate pred, const A &x) {
6723 mlir::Location loc = getLoc();
6724 mlir::IntegerType i1Ty = builder.getI1Type();
6725 auto lf = genarr(x.left());
6726 auto rf = genarr(x.right());
6727 return [=](IterSpace iters) -> ExtValue {
6728 mlir::Value left = fir::getBase(lf(iters));
6729 mlir::Value right = fir::getBase(rf(iters));
6730 mlir::Value lhs = builder.createConvert(loc, i1Ty, left);
6731 mlir::Value rhs = builder.createConvert(loc, i1Ty, right);
6732 return builder.create<OP>(loc, pred, lhs, rhs);
6733 };
6734 }
6735 template <int KIND>
genarr(const Fortran::evaluate::LogicalOperation<KIND> & x)6736 CC genarr(const Fortran::evaluate::LogicalOperation<KIND> &x) {
6737 switch (x.logicalOperator) {
6738 case Fortran::evaluate::LogicalOperator::And:
6739 return createBinaryBoolOp<mlir::arith::AndIOp>(x);
6740 case Fortran::evaluate::LogicalOperator::Or:
6741 return createBinaryBoolOp<mlir::arith::OrIOp>(x);
6742 case Fortran::evaluate::LogicalOperator::Eqv:
6743 return createCompareBoolOp<mlir::arith::CmpIOp>(
6744 mlir::arith::CmpIPredicate::eq, x);
6745 case Fortran::evaluate::LogicalOperator::Neqv:
6746 return createCompareBoolOp<mlir::arith::CmpIOp>(
6747 mlir::arith::CmpIPredicate::ne, x);
6748 case Fortran::evaluate::LogicalOperator::Not:
6749 llvm_unreachable(".NOT. handled elsewhere");
6750 }
6751 llvm_unreachable("unhandled case");
6752 }
6753
6754 //===--------------------------------------------------------------------===//
6755 // Relational operators (<, <=, ==, etc.)
6756 //===--------------------------------------------------------------------===//
6757
6758 template <typename OP, typename PRED, typename A>
createCompareOp(PRED pred,const A & x)6759 CC createCompareOp(PRED pred, const A &x) {
6760 mlir::Location loc = getLoc();
6761 auto lf = genarr(x.left());
6762 auto rf = genarr(x.right());
6763 return [=](IterSpace iters) -> ExtValue {
6764 mlir::Value lhs = fir::getBase(lf(iters));
6765 mlir::Value rhs = fir::getBase(rf(iters));
6766 return builder.create<OP>(loc, pred, lhs, rhs);
6767 };
6768 }
6769 template <typename A>
createCompareCharOp(mlir::arith::CmpIPredicate pred,const A & x)6770 CC createCompareCharOp(mlir::arith::CmpIPredicate pred, const A &x) {
6771 mlir::Location loc = getLoc();
6772 auto lf = genarr(x.left());
6773 auto rf = genarr(x.right());
6774 return [=](IterSpace iters) -> ExtValue {
6775 auto lhs = lf(iters);
6776 auto rhs = rf(iters);
6777 return fir::runtime::genCharCompare(builder, loc, pred, lhs, rhs);
6778 };
6779 }
6780 template <int KIND>
genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,KIND>> & x)6781 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
6782 Fortran::common::TypeCategory::Integer, KIND>> &x) {
6783 return createCompareOp<mlir::arith::CmpIOp>(translateRelational(x.opr), x);
6784 }
6785 template <int KIND>
genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Character,KIND>> & x)6786 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
6787 Fortran::common::TypeCategory::Character, KIND>> &x) {
6788 return createCompareCharOp(translateRelational(x.opr), x);
6789 }
6790 template <int KIND>
genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Real,KIND>> & x)6791 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
6792 Fortran::common::TypeCategory::Real, KIND>> &x) {
6793 return createCompareOp<mlir::arith::CmpFOp>(translateFloatRelational(x.opr),
6794 x);
6795 }
6796 template <int KIND>
genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<Fortran::common::TypeCategory::Complex,KIND>> & x)6797 CC genarr(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
6798 Fortran::common::TypeCategory::Complex, KIND>> &x) {
6799 return createCompareOp<fir::CmpcOp>(translateFloatRelational(x.opr), x);
6800 }
genarr(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> & r)6801 CC genarr(
6802 const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &r) {
6803 return std::visit([&](const auto &x) { return genarr(x); }, r.u);
6804 }
6805
6806 template <typename A>
genarr(const Fortran::evaluate::Designator<A> & des)6807 CC genarr(const Fortran::evaluate::Designator<A> &des) {
6808 ComponentPath components(des.Rank() > 0);
6809 return std::visit([&](const auto &x) { return genarr(x, components); },
6810 des.u);
6811 }
6812
6813 /// Is the path component rank > 0?
ranked(const PathComponent & x)6814 static bool ranked(const PathComponent &x) {
6815 return std::visit(Fortran::common::visitors{
6816 [](const ImplicitSubscripts &) { return false; },
6817 [](const auto *v) { return v->Rank() > 0; }},
6818 x);
6819 }
6820
extendComponent(Fortran::lower::ComponentPath & component,mlir::Type coorTy,mlir::ValueRange vals)6821 void extendComponent(Fortran::lower::ComponentPath &component,
6822 mlir::Type coorTy, mlir::ValueRange vals) {
6823 auto *bldr = &converter.getFirOpBuilder();
6824 llvm::SmallVector<mlir::Value> offsets(vals.begin(), vals.end());
6825 auto currentFunc = component.getExtendCoorRef();
6826 auto loc = getLoc();
6827 auto newCoorRef = [bldr, coorTy, offsets, currentFunc,
6828 loc](mlir::Value val) -> mlir::Value {
6829 return bldr->create<fir::CoordinateOp>(loc, bldr->getRefType(coorTy),
6830 currentFunc(val), offsets);
6831 };
6832 component.extendCoorRef = newCoorRef;
6833 }
6834
6835 //===-------------------------------------------------------------------===//
6836 // Array data references in an explicit iteration space.
6837 //
6838 // Use the base array that was loaded before the loop nest.
6839 //===-------------------------------------------------------------------===//
6840
6841 /// Lower the path (`revPath`, in reverse) to be appended to an array_fetch or
6842 /// array_update op. \p ty is the initial type of the array
6843 /// (reference). Returns the type of the element after application of the
6844 /// path in \p components.
6845 ///
6846 /// TODO: This needs to deal with array's with initial bounds other than 1.
6847 /// TODO: Thread type parameters correctly.
lowerPath(const ExtValue & arrayExv,ComponentPath & components)6848 mlir::Type lowerPath(const ExtValue &arrayExv, ComponentPath &components) {
6849 mlir::Location loc = getLoc();
6850 mlir::Type ty = fir::getBase(arrayExv).getType();
6851 auto &revPath = components.reversePath;
6852 ty = fir::unwrapPassByRefType(ty);
6853 bool prefix = true;
6854 bool deref = false;
6855 auto addComponentList = [&](mlir::Type ty, mlir::ValueRange vals) {
6856 if (deref) {
6857 extendComponent(components, ty, vals);
6858 } else if (prefix) {
6859 for (auto v : vals)
6860 components.prefixComponents.push_back(v);
6861 } else {
6862 for (auto v : vals)
6863 components.suffixComponents.push_back(v);
6864 }
6865 };
6866 mlir::IndexType idxTy = builder.getIndexType();
6867 mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
6868 bool atBase = true;
6869 auto saveSemant = semant;
6870 if (isProjectedCopyInCopyOut())
6871 semant = ConstituentSemantics::RefTransparent;
6872 unsigned index = 0;
6873 for (const auto &v : llvm::reverse(revPath)) {
6874 std::visit(
6875 Fortran::common::visitors{
6876 [&](const ImplicitSubscripts &) {
6877 prefix = false;
6878 ty = fir::unwrapSequenceType(ty);
6879 },
6880 [&](const Fortran::evaluate::ComplexPart *x) {
6881 assert(!prefix && "complex part must be at end");
6882 mlir::Value offset = builder.createIntegerConstant(
6883 loc, builder.getI32Type(),
6884 x->part() == Fortran::evaluate::ComplexPart::Part::RE ? 0
6885 : 1);
6886 components.suffixComponents.push_back(offset);
6887 ty = fir::applyPathToType(ty, mlir::ValueRange{offset});
6888 },
6889 [&](const Fortran::evaluate::ArrayRef *x) {
6890 if (Fortran::lower::isRankedArrayAccess(*x)) {
6891 genSliceIndices(components, arrayExv, *x, atBase);
6892 ty = fir::unwrapSeqOrBoxedSeqType(ty);
6893 } else {
6894 // Array access where the expressions are scalar and cannot
6895 // depend upon the implied iteration space.
6896 unsigned ssIndex = 0u;
6897 llvm::SmallVector<mlir::Value> componentsToAdd;
6898 for (const auto &ss : x->subscript()) {
6899 std::visit(
6900 Fortran::common::visitors{
6901 [&](const Fortran::evaluate::
6902 IndirectSubscriptIntegerExpr &ie) {
6903 const auto &e = ie.value();
6904 if (isArray(e))
6905 fir::emitFatalError(
6906 loc,
6907 "multiple components along single path "
6908 "generating array subexpressions");
6909 // Lower scalar index expression, append it to
6910 // subs.
6911 mlir::Value subscriptVal =
6912 fir::getBase(asScalarArray(e));
6913 // arrayExv is the base array. It needs to reflect
6914 // the current array component instead.
6915 // FIXME: must use lower bound of this component,
6916 // not just the constant 1.
6917 mlir::Value lb =
6918 atBase ? fir::factory::readLowerBound(
6919 builder, loc, arrayExv, ssIndex,
6920 one)
6921 : one;
6922 mlir::Value val = builder.createConvert(
6923 loc, idxTy, subscriptVal);
6924 mlir::Value ivAdj =
6925 builder.create<mlir::arith::SubIOp>(
6926 loc, idxTy, val, lb);
6927 componentsToAdd.push_back(
6928 builder.createConvert(loc, idxTy, ivAdj));
6929 },
6930 [&](const auto &) {
6931 fir::emitFatalError(
6932 loc, "multiple components along single path "
6933 "generating array subexpressions");
6934 }},
6935 ss.u);
6936 ssIndex++;
6937 }
6938 ty = fir::unwrapSeqOrBoxedSeqType(ty);
6939 addComponentList(ty, componentsToAdd);
6940 }
6941 },
6942 [&](const Fortran::evaluate::Component *x) {
6943 auto fieldTy = fir::FieldType::get(builder.getContext());
6944 llvm::StringRef name = toStringRef(getLastSym(*x).name());
6945 if (auto recTy = ty.dyn_cast<fir::RecordType>()) {
6946 ty = recTy.getType(name);
6947 auto fld = builder.create<fir::FieldIndexOp>(
6948 loc, fieldTy, name, recTy, fir::getTypeParams(arrayExv));
6949 addComponentList(ty, {fld});
6950 if (index != revPath.size() - 1 || !isPointerAssignment()) {
6951 // Need an intermediate dereference if the boxed value
6952 // appears in the middle of the component path or if it is
6953 // on the right and this is not a pointer assignment.
6954 if (auto boxTy = ty.dyn_cast<fir::BoxType>()) {
6955 auto currentFunc = components.getExtendCoorRef();
6956 auto loc = getLoc();
6957 auto *bldr = &converter.getFirOpBuilder();
6958 auto newCoorRef = [=](mlir::Value val) -> mlir::Value {
6959 return bldr->create<fir::LoadOp>(loc, currentFunc(val));
6960 };
6961 components.extendCoorRef = newCoorRef;
6962 deref = true;
6963 }
6964 }
6965 } else if (auto boxTy = ty.dyn_cast<fir::BoxType>()) {
6966 ty = fir::unwrapRefType(boxTy.getEleTy());
6967 auto recTy = ty.cast<fir::RecordType>();
6968 ty = recTy.getType(name);
6969 auto fld = builder.create<fir::FieldIndexOp>(
6970 loc, fieldTy, name, recTy, fir::getTypeParams(arrayExv));
6971 extendComponent(components, ty, {fld});
6972 } else {
6973 TODO(loc, "other component type");
6974 }
6975 }},
6976 v);
6977 atBase = false;
6978 ++index;
6979 }
6980 semant = saveSemant;
6981 ty = fir::unwrapSequenceType(ty);
6982 components.applied = true;
6983 return ty;
6984 }
6985
genSubstringBounds(ComponentPath & components)6986 llvm::SmallVector<mlir::Value> genSubstringBounds(ComponentPath &components) {
6987 llvm::SmallVector<mlir::Value> result;
6988 if (components.substring)
6989 populateBounds(result, components.substring);
6990 return result;
6991 }
6992
applyPathToArrayLoad(fir::ArrayLoadOp load,ComponentPath & components)6993 CC applyPathToArrayLoad(fir::ArrayLoadOp load, ComponentPath &components) {
6994 mlir::Location loc = getLoc();
6995 auto revPath = components.reversePath;
6996 fir::ExtendedValue arrayExv =
6997 arrayLoadExtValue(builder, loc, load, {}, load);
6998 mlir::Type eleTy = lowerPath(arrayExv, components);
6999 auto currentPC = components.pc;
7000 auto pc = [=, prefix = components.prefixComponents,
7001 suffix = components.suffixComponents](IterSpace iters) {
7002 // Add path prefix and suffix.
7003 return IterationSpace(currentPC(iters), prefix, suffix);
7004 };
7005 components.resetPC();
7006 llvm::SmallVector<mlir::Value> substringBounds =
7007 genSubstringBounds(components);
7008 if (isProjectedCopyInCopyOut()) {
7009 destination = load;
7010 auto lambda = [=, esp = this->explicitSpace](IterSpace iters) mutable {
7011 mlir::Value innerArg = esp->findArgumentOfLoad(load);
7012 if (isAdjustedArrayElementType(eleTy)) {
7013 mlir::Type eleRefTy = builder.getRefType(eleTy);
7014 auto arrayOp = builder.create<fir::ArrayAccessOp>(
7015 loc, eleRefTy, innerArg, iters.iterVec(),
7016 fir::factory::getTypeParams(loc, builder, load));
7017 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
7018 mlir::Value dstLen = fir::factory::genLenOfCharacter(
7019 builder, loc, load, iters.iterVec(), substringBounds);
7020 fir::ArrayAmendOp amend = createCharArrayAmend(
7021 loc, builder, arrayOp, dstLen, iters.elementExv(), innerArg,
7022 substringBounds);
7023 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), amend,
7024 dstLen);
7025 }
7026 if (fir::isa_derived(eleTy)) {
7027 fir::ArrayAmendOp amend =
7028 createDerivedArrayAmend(loc, load, builder, arrayOp,
7029 iters.elementExv(), eleTy, innerArg);
7030 return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
7031 amend);
7032 }
7033 assert(eleTy.isa<fir::SequenceType>());
7034 TODO(loc, "array (as element) assignment");
7035 }
7036 if (components.hasExtendCoorRef()) {
7037 auto eleBoxTy =
7038 fir::applyPathToType(innerArg.getType(), iters.iterVec());
7039 assert(eleBoxTy && eleBoxTy.isa<fir::BoxType>());
7040 auto arrayOp = builder.create<fir::ArrayAccessOp>(
7041 loc, builder.getRefType(eleBoxTy), innerArg, iters.iterVec(),
7042 fir::factory::getTypeParams(loc, builder, load));
7043 mlir::Value addr = components.getExtendCoorRef()(arrayOp);
7044 components.resetExtendCoorRef();
7045 // When the lhs is a boxed value and the context is not a pointer
7046 // assignment, then insert the dereference of the box before any
7047 // conversion and store.
7048 if (!isPointerAssignment()) {
7049 if (auto boxTy = eleTy.dyn_cast<fir::BoxType>()) {
7050 eleTy = fir::boxMemRefType(boxTy);
7051 addr = builder.create<fir::BoxAddrOp>(loc, eleTy, addr);
7052 eleTy = fir::unwrapRefType(eleTy);
7053 }
7054 }
7055 auto ele = convertElementForUpdate(loc, eleTy, iters.getElement());
7056 builder.create<fir::StoreOp>(loc, ele, addr);
7057 auto amend = builder.create<fir::ArrayAmendOp>(
7058 loc, innerArg.getType(), innerArg, arrayOp);
7059 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), amend);
7060 }
7061 auto ele = convertElementForUpdate(loc, eleTy, iters.getElement());
7062 auto update = builder.create<fir::ArrayUpdateOp>(
7063 loc, innerArg.getType(), innerArg, ele, iters.iterVec(),
7064 fir::factory::getTypeParams(loc, builder, load));
7065 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), update);
7066 };
7067 return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
7068 }
7069 if (isCustomCopyInCopyOut()) {
7070 // Create an array_modify to get the LHS element address and indicate
7071 // the assignment, and create the call to the user defined assignment.
7072 destination = load;
7073 auto lambda = [=](IterSpace iters) mutable {
7074 mlir::Value innerArg = explicitSpace->findArgumentOfLoad(load);
7075 mlir::Type refEleTy =
7076 fir::isa_ref_type(eleTy) ? eleTy : builder.getRefType(eleTy);
7077 auto arrModify = builder.create<fir::ArrayModifyOp>(
7078 loc, mlir::TypeRange{refEleTy, innerArg.getType()}, innerArg,
7079 iters.iterVec(), load.getTypeparams());
7080 return arrayLoadExtValue(builder, loc, load, iters.iterVec(),
7081 arrModify.getResult(1));
7082 };
7083 return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
7084 }
7085 auto lambda = [=, semant = this->semant](IterSpace iters) mutable {
7086 if (semant == ConstituentSemantics::RefOpaque ||
7087 isAdjustedArrayElementType(eleTy)) {
7088 mlir::Type resTy = builder.getRefType(eleTy);
7089 // Use array element reference semantics.
7090 auto access = builder.create<fir::ArrayAccessOp>(
7091 loc, resTy, load, iters.iterVec(),
7092 fir::factory::getTypeParams(loc, builder, load));
7093 mlir::Value newBase = access;
7094 if (fir::isa_char(eleTy)) {
7095 mlir::Value dstLen = fir::factory::genLenOfCharacter(
7096 builder, loc, load, iters.iterVec(), substringBounds);
7097 if (!substringBounds.empty()) {
7098 fir::CharBoxValue charDst{access, dstLen};
7099 fir::factory::CharacterExprHelper helper{builder, loc};
7100 charDst = helper.createSubstring(charDst, substringBounds);
7101 newBase = charDst.getAddr();
7102 }
7103 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase,
7104 dstLen);
7105 }
7106 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), newBase);
7107 }
7108 if (components.hasExtendCoorRef()) {
7109 auto eleBoxTy = fir::applyPathToType(load.getType(), iters.iterVec());
7110 assert(eleBoxTy && eleBoxTy.isa<fir::BoxType>());
7111 auto access = builder.create<fir::ArrayAccessOp>(
7112 loc, builder.getRefType(eleBoxTy), load, iters.iterVec(),
7113 fir::factory::getTypeParams(loc, builder, load));
7114 mlir::Value addr = components.getExtendCoorRef()(access);
7115 components.resetExtendCoorRef();
7116 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), addr);
7117 }
7118 if (isPointerAssignment()) {
7119 auto eleTy = fir::applyPathToType(load.getType(), iters.iterVec());
7120 if (!eleTy.isa<fir::BoxType>()) {
7121 // Rhs is a regular expression that will need to be boxed before
7122 // assigning to the boxed variable.
7123 auto typeParams = fir::factory::getTypeParams(loc, builder, load);
7124 auto access = builder.create<fir::ArrayAccessOp>(
7125 loc, builder.getRefType(eleTy), load, iters.iterVec(),
7126 typeParams);
7127 auto addr = components.getExtendCoorRef()(access);
7128 components.resetExtendCoorRef();
7129 auto ptrEleTy = fir::PointerType::get(eleTy);
7130 auto ptrAddr = builder.createConvert(loc, ptrEleTy, addr);
7131 auto boxTy = fir::BoxType::get(ptrEleTy);
7132 // FIXME: The typeparams to the load may be different than those of
7133 // the subobject.
7134 if (components.hasExtendCoorRef())
7135 TODO(loc, "need to adjust typeparameter(s) to reflect the final "
7136 "component");
7137 mlir::Value embox =
7138 builder.create<fir::EmboxOp>(loc, boxTy, ptrAddr,
7139 /*shape=*/mlir::Value{},
7140 /*slice=*/mlir::Value{}, typeParams);
7141 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), embox);
7142 }
7143 }
7144 auto fetch = builder.create<fir::ArrayFetchOp>(
7145 loc, eleTy, load, iters.iterVec(), load.getTypeparams());
7146 return arrayLoadExtValue(builder, loc, load, iters.iterVec(), fetch);
7147 };
7148 return [=](IterSpace iters) mutable { return lambda(pc(iters)); };
7149 }
7150
7151 template <typename A>
genImplicitArrayAccess(const A & x,ComponentPath & components)7152 CC genImplicitArrayAccess(const A &x, ComponentPath &components) {
7153 components.reversePath.push_back(ImplicitSubscripts{});
7154 ExtValue exv = asScalarRef(x);
7155 lowerPath(exv, components);
7156 auto lambda = genarr(exv, components);
7157 return [=](IterSpace iters) { return lambda(components.pc(iters)); };
7158 }
genImplicitArrayAccess(const Fortran::evaluate::NamedEntity & x,ComponentPath & components)7159 CC genImplicitArrayAccess(const Fortran::evaluate::NamedEntity &x,
7160 ComponentPath &components) {
7161 if (x.IsSymbol())
7162 return genImplicitArrayAccess(getFirstSym(x), components);
7163 return genImplicitArrayAccess(x.GetComponent(), components);
7164 }
7165
7166 template <typename A>
genAsScalar(const A & x)7167 CC genAsScalar(const A &x) {
7168 mlir::Location loc = getLoc();
7169 if (isProjectedCopyInCopyOut()) {
7170 return [=, &x, builder = &converter.getFirOpBuilder()](
7171 IterSpace iters) -> ExtValue {
7172 ExtValue exv = asScalarRef(x);
7173 mlir::Value addr = fir::getBase(exv);
7174 mlir::Type eleTy = fir::unwrapRefType(addr.getType());
7175 if (isAdjustedArrayElementType(eleTy)) {
7176 if (fir::isa_char(eleTy)) {
7177 fir::factory::CharacterExprHelper{*builder, loc}.createAssign(
7178 exv, iters.elementExv());
7179 } else if (fir::isa_derived(eleTy)) {
7180 TODO(loc, "assignment of derived type");
7181 } else {
7182 fir::emitFatalError(loc, "array type not expected in scalar");
7183 }
7184 } else {
7185 auto eleVal = convertElementForUpdate(loc, eleTy, iters.getElement());
7186 builder->create<fir::StoreOp>(loc, eleVal, addr);
7187 }
7188 return exv;
7189 };
7190 }
7191 return [=, &x](IterSpace) { return asScalar(x); };
7192 }
7193
tailIsPointerInPointerAssignment(const Fortran::semantics::Symbol & x,ComponentPath & components)7194 bool tailIsPointerInPointerAssignment(const Fortran::semantics::Symbol &x,
7195 ComponentPath &components) {
7196 return isPointerAssignment() && Fortran::semantics::IsPointer(x) &&
7197 !components.hasComponents();
7198 }
tailIsPointerInPointerAssignment(const Fortran::evaluate::Component & x,ComponentPath & components)7199 bool tailIsPointerInPointerAssignment(const Fortran::evaluate::Component &x,
7200 ComponentPath &components) {
7201 return tailIsPointerInPointerAssignment(getLastSym(x), components);
7202 }
7203
genarr(const Fortran::semantics::Symbol & x,ComponentPath & components)7204 CC genarr(const Fortran::semantics::Symbol &x, ComponentPath &components) {
7205 if (explicitSpaceIsActive()) {
7206 if (x.Rank() > 0 && !tailIsPointerInPointerAssignment(x, components))
7207 components.reversePath.push_back(ImplicitSubscripts{});
7208 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x))
7209 return applyPathToArrayLoad(load, components);
7210 } else {
7211 return genImplicitArrayAccess(x, components);
7212 }
7213 if (pathIsEmpty(components))
7214 return components.substring ? genAsScalar(*components.substring)
7215 : genAsScalar(x);
7216 mlir::Location loc = getLoc();
7217 return [=](IterSpace) -> ExtValue {
7218 fir::emitFatalError(loc, "reached symbol with path");
7219 };
7220 }
7221
7222 /// Lower a component path with or without rank.
7223 /// Example: <code>array%baz%qux%waldo</code>
genarr(const Fortran::evaluate::Component & x,ComponentPath & components)7224 CC genarr(const Fortran::evaluate::Component &x, ComponentPath &components) {
7225 if (explicitSpaceIsActive()) {
7226 if (x.base().Rank() == 0 && x.Rank() > 0 &&
7227 !tailIsPointerInPointerAssignment(x, components))
7228 components.reversePath.push_back(ImplicitSubscripts{});
7229 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x))
7230 return applyPathToArrayLoad(load, components);
7231 } else {
7232 if (x.base().Rank() == 0)
7233 return genImplicitArrayAccess(x, components);
7234 }
7235 bool atEnd = pathIsEmpty(components);
7236 if (!getLastSym(x).test(Fortran::semantics::Symbol::Flag::ParentComp))
7237 // Skip parent components; their components are placed directly in the
7238 // object.
7239 components.reversePath.push_back(&x);
7240 auto result = genarr(x.base(), components);
7241 if (components.applied)
7242 return result;
7243 if (atEnd)
7244 return genAsScalar(x);
7245 mlir::Location loc = getLoc();
7246 return [=](IterSpace) -> ExtValue {
7247 fir::emitFatalError(loc, "reached component with path");
7248 };
7249 }
7250
7251 /// Array reference with subscripts. If this has rank > 0, this is a form
7252 /// of an array section (slice).
7253 ///
7254 /// There are two "slicing" primitives that may be applied on a dimension by
7255 /// dimension basis: (1) triple notation and (2) vector addressing. Since
7256 /// dimensions can be selectively sliced, some dimensions may contain
7257 /// regular scalar expressions and those dimensions do not participate in
7258 /// the array expression evaluation.
genarr(const Fortran::evaluate::ArrayRef & x,ComponentPath & components)7259 CC genarr(const Fortran::evaluate::ArrayRef &x, ComponentPath &components) {
7260 if (explicitSpaceIsActive()) {
7261 if (Fortran::lower::isRankedArrayAccess(x))
7262 components.reversePath.push_back(ImplicitSubscripts{});
7263 if (fir::ArrayLoadOp load = explicitSpace->findBinding(&x)) {
7264 components.reversePath.push_back(&x);
7265 return applyPathToArrayLoad(load, components);
7266 }
7267 } else {
7268 if (Fortran::lower::isRankedArrayAccess(x)) {
7269 components.reversePath.push_back(&x);
7270 return genImplicitArrayAccess(x.base(), components);
7271 }
7272 }
7273 bool atEnd = pathIsEmpty(components);
7274 components.reversePath.push_back(&x);
7275 auto result = genarr(x.base(), components);
7276 if (components.applied)
7277 return result;
7278 mlir::Location loc = getLoc();
7279 if (atEnd) {
7280 if (x.Rank() == 0)
7281 return genAsScalar(x);
7282 fir::emitFatalError(loc, "expected scalar");
7283 }
7284 return [=](IterSpace) -> ExtValue {
7285 fir::emitFatalError(loc, "reached arrayref with path");
7286 };
7287 }
7288
genarr(const Fortran::evaluate::CoarrayRef & x,ComponentPath & components)7289 CC genarr(const Fortran::evaluate::CoarrayRef &x, ComponentPath &components) {
7290 TODO(getLoc(), "coarray reference");
7291 }
7292
genarr(const Fortran::evaluate::NamedEntity & x,ComponentPath & components)7293 CC genarr(const Fortran::evaluate::NamedEntity &x,
7294 ComponentPath &components) {
7295 return x.IsSymbol() ? genarr(getFirstSym(x), components)
7296 : genarr(x.GetComponent(), components);
7297 }
7298
genarr(const Fortran::evaluate::DataRef & x,ComponentPath & components)7299 CC genarr(const Fortran::evaluate::DataRef &x, ComponentPath &components) {
7300 return std::visit([&](const auto &v) { return genarr(v, components); },
7301 x.u);
7302 }
7303
pathIsEmpty(const ComponentPath & components)7304 bool pathIsEmpty(const ComponentPath &components) {
7305 return components.reversePath.empty();
7306 }
7307
ArrayExprLowering(Fortran::lower::AbstractConverter & converter,Fortran::lower::StatementContext & stmtCtx,Fortran::lower::SymMap & symMap)7308 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
7309 Fortran::lower::StatementContext &stmtCtx,
7310 Fortran::lower::SymMap &symMap)
7311 : converter{converter}, builder{converter.getFirOpBuilder()},
7312 stmtCtx{stmtCtx}, symMap{symMap} {}
7313
ArrayExprLowering(Fortran::lower::AbstractConverter & converter,Fortran::lower::StatementContext & stmtCtx,Fortran::lower::SymMap & symMap,ConstituentSemantics sem)7314 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
7315 Fortran::lower::StatementContext &stmtCtx,
7316 Fortran::lower::SymMap &symMap,
7317 ConstituentSemantics sem)
7318 : converter{converter}, builder{converter.getFirOpBuilder()},
7319 stmtCtx{stmtCtx}, symMap{symMap}, semant{sem} {}
7320
ArrayExprLowering(Fortran::lower::AbstractConverter & converter,Fortran::lower::StatementContext & stmtCtx,Fortran::lower::SymMap & symMap,ConstituentSemantics sem,Fortran::lower::ExplicitIterSpace * expSpace,Fortran::lower::ImplicitIterSpace * impSpace)7321 explicit ArrayExprLowering(Fortran::lower::AbstractConverter &converter,
7322 Fortran::lower::StatementContext &stmtCtx,
7323 Fortran::lower::SymMap &symMap,
7324 ConstituentSemantics sem,
7325 Fortran::lower::ExplicitIterSpace *expSpace,
7326 Fortran::lower::ImplicitIterSpace *impSpace)
7327 : converter{converter}, builder{converter.getFirOpBuilder()},
7328 stmtCtx{stmtCtx}, symMap{symMap},
7329 explicitSpace((expSpace && expSpace->isActive()) ? expSpace : nullptr),
7330 implicitSpace((impSpace && !impSpace->empty()) ? impSpace : nullptr),
7331 semant{sem} {
7332 // Generate any mask expressions, as necessary. This is the compute step
7333 // that creates the effective masks. See 10.2.3.2 in particular.
7334 genMasks();
7335 }
7336
getLoc()7337 mlir::Location getLoc() { return converter.getCurrentLocation(); }
7338
7339 /// Array appears in a lhs context such that it is assigned after the rhs is
7340 /// fully evaluated.
isCopyInCopyOut()7341 inline bool isCopyInCopyOut() {
7342 return semant == ConstituentSemantics::CopyInCopyOut;
7343 }
7344
7345 /// Array appears in a lhs (or temp) context such that a projected,
7346 /// discontiguous subspace of the array is assigned after the rhs is fully
7347 /// evaluated. That is, the rhs array value is merged into a section of the
7348 /// lhs array.
isProjectedCopyInCopyOut()7349 inline bool isProjectedCopyInCopyOut() {
7350 return semant == ConstituentSemantics::ProjectedCopyInCopyOut;
7351 }
7352
7353 // ???: Do we still need this?
isCustomCopyInCopyOut()7354 inline bool isCustomCopyInCopyOut() {
7355 return semant == ConstituentSemantics::CustomCopyInCopyOut;
7356 }
7357
7358 /// Are we lowering in a left-hand side context?
isLeftHandSide()7359 inline bool isLeftHandSide() {
7360 return isCopyInCopyOut() || isProjectedCopyInCopyOut() ||
7361 isCustomCopyInCopyOut();
7362 }
7363
7364 /// Array appears in a context where it must be boxed.
isBoxValue()7365 inline bool isBoxValue() { return semant == ConstituentSemantics::BoxValue; }
7366
7367 /// Array appears in a context where differences in the memory reference can
7368 /// be observable in the computational results. For example, an array
7369 /// element is passed to an impure procedure.
isReferentiallyOpaque()7370 inline bool isReferentiallyOpaque() {
7371 return semant == ConstituentSemantics::RefOpaque;
7372 }
7373
7374 /// Array appears in a context where it is passed as a VALUE argument.
isValueAttribute()7375 inline bool isValueAttribute() {
7376 return semant == ConstituentSemantics::ByValueArg;
7377 }
7378
7379 /// Can the loops over the expression be unordered?
isUnordered() const7380 inline bool isUnordered() const { return unordered; }
7381
setUnordered(bool b)7382 void setUnordered(bool b) { unordered = b; }
7383
isPointerAssignment() const7384 inline bool isPointerAssignment() const { return lbounds.has_value(); }
7385
isBoundsSpec() const7386 inline bool isBoundsSpec() const {
7387 return isPointerAssignment() && !ubounds.has_value();
7388 }
7389
isBoundsRemap() const7390 inline bool isBoundsRemap() const {
7391 return isPointerAssignment() && ubounds.has_value();
7392 }
7393
setPointerAssignmentBounds(const llvm::SmallVector<mlir::Value> & lbs,llvm::Optional<llvm::SmallVector<mlir::Value>> ubs)7394 void setPointerAssignmentBounds(
7395 const llvm::SmallVector<mlir::Value> &lbs,
7396 llvm::Optional<llvm::SmallVector<mlir::Value>> ubs) {
7397 lbounds = lbs;
7398 ubounds = ubs;
7399 }
7400
7401 Fortran::lower::AbstractConverter &converter;
7402 fir::FirOpBuilder &builder;
7403 Fortran::lower::StatementContext &stmtCtx;
7404 bool elementCtx = false;
7405 Fortran::lower::SymMap &symMap;
7406 /// The continuation to generate code to update the destination.
7407 llvm::Optional<CC> ccStoreToDest;
7408 llvm::Optional<std::function<void(llvm::ArrayRef<mlir::Value>)>> ccPrelude;
7409 llvm::Optional<std::function<fir::ArrayLoadOp(llvm::ArrayRef<mlir::Value>)>>
7410 ccLoadDest;
7411 /// The destination is the loaded array into which the results will be
7412 /// merged.
7413 fir::ArrayLoadOp destination;
7414 /// The shape of the destination.
7415 llvm::SmallVector<mlir::Value> destShape;
7416 /// List of arrays in the expression that have been loaded.
7417 llvm::SmallVector<ArrayOperand> arrayOperands;
7418 /// If there is a user-defined iteration space, explicitShape will hold the
7419 /// information from the front end.
7420 Fortran::lower::ExplicitIterSpace *explicitSpace = nullptr;
7421 Fortran::lower::ImplicitIterSpace *implicitSpace = nullptr;
7422 ConstituentSemantics semant = ConstituentSemantics::RefTransparent;
7423 /// `lbounds`, `ubounds` are used in POINTER value assignments, which may only
7424 /// occur in an explicit iteration space.
7425 llvm::Optional<llvm::SmallVector<mlir::Value>> lbounds;
7426 llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds;
7427 // Can the array expression be evaluated in any order?
7428 // Will be set to false if any of the expression parts prevent this.
7429 bool unordered = true;
7430 };
7431 } // namespace
7432
createSomeExtendedExpression(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7433 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
7434 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7435 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7436 Fortran::lower::StatementContext &stmtCtx) {
7437 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
7438 return ScalarExprLowering{loc, converter, symMap, stmtCtx}.genval(expr);
7439 }
7440
createDenseGlobal(mlir::Location loc,mlir::Type symTy,llvm::StringRef globalName,mlir::StringAttr linkage,bool isConst,const Fortran::lower::SomeExpr & expr,Fortran::lower::AbstractConverter & converter)7441 fir::GlobalOp Fortran::lower::createDenseGlobal(
7442 mlir::Location loc, mlir::Type symTy, llvm::StringRef globalName,
7443 mlir::StringAttr linkage, bool isConst,
7444 const Fortran::lower::SomeExpr &expr,
7445 Fortran::lower::AbstractConverter &converter) {
7446
7447 Fortran::lower::StatementContext stmtCtx(/*prohibited=*/true);
7448 Fortran::lower::SymMap emptyMap;
7449 InitializerData initData(/*genRawVals=*/true);
7450 ScalarExprLowering sel(loc, converter, emptyMap, stmtCtx,
7451 /*initializer=*/&initData);
7452 sel.genval(expr);
7453
7454 size_t sz = initData.rawVals.size();
7455 llvm::ArrayRef<mlir::Attribute> ar = {initData.rawVals.data(), sz};
7456
7457 mlir::RankedTensorType tensorTy;
7458 auto &builder = converter.getFirOpBuilder();
7459 mlir::Type iTy = initData.rawType;
7460 if (!iTy)
7461 return 0; // array extent is probably 0 in this case, so just return 0.
7462 tensorTy = mlir::RankedTensorType::get(sz, iTy);
7463 auto init = mlir::DenseElementsAttr::get(tensorTy, ar);
7464 return builder.createGlobal(loc, symTy, globalName, linkage, init, isConst);
7465 }
7466
createSomeInitializerExpression(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7467 fir::ExtendedValue Fortran::lower::createSomeInitializerExpression(
7468 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7469 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7470 Fortran::lower::StatementContext &stmtCtx) {
7471 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
7472 InitializerData initData; // needed for initializations
7473 return ScalarExprLowering{loc, converter, symMap, stmtCtx,
7474 /*initializer=*/&initData}
7475 .genval(expr);
7476 }
7477
createSomeExtendedAddress(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7478 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
7479 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7480 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7481 Fortran::lower::StatementContext &stmtCtx) {
7482 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
7483 return ScalarExprLowering(loc, converter, symMap, stmtCtx).gen(expr);
7484 }
7485
createInitializerAddress(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7486 fir::ExtendedValue Fortran::lower::createInitializerAddress(
7487 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7488 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7489 Fortran::lower::StatementContext &stmtCtx) {
7490 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
7491 InitializerData init;
7492 return ScalarExprLowering(loc, converter, symMap, stmtCtx, &init).gen(expr);
7493 }
7494
createSomeArrayAssignment(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7495 void Fortran::lower::createSomeArrayAssignment(
7496 Fortran::lower::AbstractConverter &converter,
7497 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
7498 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
7499 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
7500 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
7501 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
7502 }
7503
createSomeArrayAssignment(Fortran::lower::AbstractConverter & converter,const fir::ExtendedValue & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7504 void Fortran::lower::createSomeArrayAssignment(
7505 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
7506 const Fortran::lower::SomeExpr &rhs, Fortran::lower::SymMap &symMap,
7507 Fortran::lower::StatementContext &stmtCtx) {
7508 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
7509 rhs.AsFortran(llvm::dbgs() << "assign expression: ") << '\n';);
7510 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
7511 }
createSomeArrayAssignment(Fortran::lower::AbstractConverter & converter,const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7512 void Fortran::lower::createSomeArrayAssignment(
7513 Fortran::lower::AbstractConverter &converter, const fir::ExtendedValue &lhs,
7514 const fir::ExtendedValue &rhs, Fortran::lower::SymMap &symMap,
7515 Fortran::lower::StatementContext &stmtCtx) {
7516 LLVM_DEBUG(llvm::dbgs() << "onto array: " << lhs << '\n';
7517 llvm::dbgs() << "assign expression: " << rhs << '\n';);
7518 ArrayExprLowering::lowerArrayAssignment(converter, symMap, stmtCtx, lhs, rhs);
7519 }
7520
createAnyMaskedArrayAssignment(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7521 void Fortran::lower::createAnyMaskedArrayAssignment(
7522 Fortran::lower::AbstractConverter &converter,
7523 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
7524 Fortran::lower::ExplicitIterSpace &explicitSpace,
7525 Fortran::lower::ImplicitIterSpace &implicitSpace,
7526 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
7527 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "onto array: ") << '\n';
7528 rhs.AsFortran(llvm::dbgs() << "assign expression: ")
7529 << " given the explicit iteration space:\n"
7530 << explicitSpace << "\n and implied mask conditions:\n"
7531 << implicitSpace << '\n';);
7532 ArrayExprLowering::lowerAnyMaskedArrayAssignment(
7533 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
7534 }
7535
createAllocatableArrayAssignment(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7536 void Fortran::lower::createAllocatableArrayAssignment(
7537 Fortran::lower::AbstractConverter &converter,
7538 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
7539 Fortran::lower::ExplicitIterSpace &explicitSpace,
7540 Fortran::lower::ImplicitIterSpace &implicitSpace,
7541 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
7542 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining array: ") << '\n';
7543 rhs.AsFortran(llvm::dbgs() << "assign expression: ")
7544 << " given the explicit iteration space:\n"
7545 << explicitSpace << "\n and implied mask conditions:\n"
7546 << implicitSpace << '\n';);
7547 ArrayExprLowering::lowerAllocatableArrayAssignment(
7548 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace);
7549 }
7550
createArrayOfPointerAssignment(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & lhs,const Fortran::lower::SomeExpr & rhs,Fortran::lower::ExplicitIterSpace & explicitSpace,Fortran::lower::ImplicitIterSpace & implicitSpace,const llvm::SmallVector<mlir::Value> & lbounds,llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7551 void Fortran::lower::createArrayOfPointerAssignment(
7552 Fortran::lower::AbstractConverter &converter,
7553 const Fortran::lower::SomeExpr &lhs, const Fortran::lower::SomeExpr &rhs,
7554 Fortran::lower::ExplicitIterSpace &explicitSpace,
7555 Fortran::lower::ImplicitIterSpace &implicitSpace,
7556 const llvm::SmallVector<mlir::Value> &lbounds,
7557 llvm::Optional<llvm::SmallVector<mlir::Value>> ubounds,
7558 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
7559 LLVM_DEBUG(lhs.AsFortran(llvm::dbgs() << "defining pointer: ") << '\n';
7560 rhs.AsFortran(llvm::dbgs() << "assign expression: ")
7561 << " given the explicit iteration space:\n"
7562 << explicitSpace << "\n and implied mask conditions:\n"
7563 << implicitSpace << '\n';);
7564 assert(explicitSpace.isActive() && "must be in FORALL construct");
7565 ArrayExprLowering::lowerArrayOfPointerAssignment(
7566 converter, symMap, stmtCtx, lhs, rhs, explicitSpace, implicitSpace,
7567 lbounds, ubounds);
7568 }
7569
createSomeArrayTempValue(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7570 fir::ExtendedValue Fortran::lower::createSomeArrayTempValue(
7571 Fortran::lower::AbstractConverter &converter,
7572 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7573 Fortran::lower::StatementContext &stmtCtx) {
7574 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
7575 return ArrayExprLowering::lowerNewArrayExpression(converter, symMap, stmtCtx,
7576 expr);
7577 }
7578
createLazyArrayTempValue(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,mlir::Value raggedHeader,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7579 void Fortran::lower::createLazyArrayTempValue(
7580 Fortran::lower::AbstractConverter &converter,
7581 const Fortran::lower::SomeExpr &expr, mlir::Value raggedHeader,
7582 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) {
7583 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "array value: ") << '\n');
7584 ArrayExprLowering::lowerLazyArrayExpression(converter, symMap, stmtCtx, expr,
7585 raggedHeader);
7586 }
7587
7588 fir::ExtendedValue
createSomeArrayBox(Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7589 Fortran::lower::createSomeArrayBox(Fortran::lower::AbstractConverter &converter,
7590 const Fortran::lower::SomeExpr &expr,
7591 Fortran::lower::SymMap &symMap,
7592 Fortran::lower::StatementContext &stmtCtx) {
7593 LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "box designator: ") << '\n');
7594 return ArrayExprLowering::lowerBoxedArrayExpression(converter, symMap,
7595 stmtCtx, expr);
7596 }
7597
createMutableBox(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap)7598 fir::MutableBoxValue Fortran::lower::createMutableBox(
7599 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7600 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
7601 // MutableBox lowering StatementContext does not need to be propagated
7602 // to the caller because the result value is a variable, not a temporary
7603 // expression. The StatementContext clean-up can occur before using the
7604 // resulting MutableBoxValue. Variables of all other types are handled in the
7605 // bridge.
7606 Fortran::lower::StatementContext dummyStmtCtx;
7607 return ScalarExprLowering{loc, converter, symMap, dummyStmtCtx}
7608 .genMutableBoxValue(expr);
7609 }
7610
createBoxValue(mlir::Location loc,Fortran::lower::AbstractConverter & converter,const Fortran::lower::SomeExpr & expr,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7611 fir::ExtendedValue Fortran::lower::createBoxValue(
7612 mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7613 const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap,
7614 Fortran::lower::StatementContext &stmtCtx) {
7615 if (expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) &&
7616 !Fortran::evaluate::HasVectorSubscript(expr))
7617 return Fortran::lower::createSomeArrayBox(converter, expr, symMap, stmtCtx);
7618 fir::ExtendedValue addr = Fortran::lower::createSomeExtendedAddress(
7619 loc, converter, expr, symMap, stmtCtx);
7620 return fir::BoxValue(converter.getFirOpBuilder().createBox(loc, addr));
7621 }
7622
createSubroutineCall(AbstractConverter & converter,const evaluate::ProcedureRef & call,ExplicitIterSpace & explicitIterSpace,ImplicitIterSpace & implicitIterSpace,SymMap & symMap,StatementContext & stmtCtx,bool isUserDefAssignment)7623 mlir::Value Fortran::lower::createSubroutineCall(
7624 AbstractConverter &converter, const evaluate::ProcedureRef &call,
7625 ExplicitIterSpace &explicitIterSpace, ImplicitIterSpace &implicitIterSpace,
7626 SymMap &symMap, StatementContext &stmtCtx, bool isUserDefAssignment) {
7627 mlir::Location loc = converter.getCurrentLocation();
7628
7629 if (isUserDefAssignment) {
7630 assert(call.arguments().size() == 2);
7631 const auto *lhs = call.arguments()[0].value().UnwrapExpr();
7632 const auto *rhs = call.arguments()[1].value().UnwrapExpr();
7633 assert(lhs && rhs &&
7634 "user defined assignment arguments must be expressions");
7635 if (call.IsElemental() && lhs->Rank() > 0) {
7636 // Elemental user defined assignment has special requirements to deal with
7637 // LHS/RHS overlaps. See 10.2.1.5 p2.
7638 ArrayExprLowering::lowerElementalUserAssignment(
7639 converter, symMap, stmtCtx, explicitIterSpace, implicitIterSpace,
7640 call);
7641 } else if (explicitIterSpace.isActive() && lhs->Rank() == 0) {
7642 // Scalar defined assignment (elemental or not) in a FORALL context.
7643 mlir::func::FuncOp func =
7644 Fortran::lower::CallerInterface(call, converter).getFuncOp();
7645 ArrayExprLowering::lowerScalarUserAssignment(
7646 converter, symMap, stmtCtx, explicitIterSpace, func, *lhs, *rhs);
7647 } else if (explicitIterSpace.isActive()) {
7648 // TODO: need to array fetch/modify sub-arrays?
7649 TODO(loc, "non elemental user defined array assignment inside FORALL");
7650 } else {
7651 if (!implicitIterSpace.empty())
7652 fir::emitFatalError(
7653 loc,
7654 "C1032: user defined assignment inside WHERE must be elemental");
7655 // Non elemental user defined assignment outside of FORALL and WHERE.
7656 // FIXME: The non elemental user defined assignment case with array
7657 // arguments must be take into account potential overlap. So far the front
7658 // end does not add parentheses around the RHS argument in the call as it
7659 // should according to 15.4.3.4.3 p2.
7660 Fortran::lower::createSomeExtendedExpression(
7661 loc, converter, toEvExpr(call), symMap, stmtCtx);
7662 }
7663 return {};
7664 }
7665
7666 assert(implicitIterSpace.empty() && !explicitIterSpace.isActive() &&
7667 "subroutine calls are not allowed inside WHERE and FORALL");
7668
7669 if (isElementalProcWithArrayArgs(call)) {
7670 ArrayExprLowering::lowerElementalSubroutine(converter, symMap, stmtCtx,
7671 toEvExpr(call));
7672 return {};
7673 }
7674 // Simple subroutine call, with potential alternate return.
7675 auto res = Fortran::lower::createSomeExtendedExpression(
7676 loc, converter, toEvExpr(call), symMap, stmtCtx);
7677 return fir::getBase(res);
7678 }
7679
7680 template <typename A>
genArrayLoad(mlir::Location loc,Fortran::lower::AbstractConverter & converter,fir::FirOpBuilder & builder,const A * x,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7681 fir::ArrayLoadOp genArrayLoad(mlir::Location loc,
7682 Fortran::lower::AbstractConverter &converter,
7683 fir::FirOpBuilder &builder, const A *x,
7684 Fortran::lower::SymMap &symMap,
7685 Fortran::lower::StatementContext &stmtCtx) {
7686 auto exv = ScalarExprLowering{loc, converter, symMap, stmtCtx}.gen(*x);
7687 mlir::Value addr = fir::getBase(exv);
7688 mlir::Value shapeOp = builder.createShape(loc, exv);
7689 mlir::Type arrTy = fir::dyn_cast_ptrOrBoxEleTy(addr.getType());
7690 return builder.create<fir::ArrayLoadOp>(loc, arrTy, addr, shapeOp,
7691 /*slice=*/mlir::Value{},
7692 fir::getTypeParams(exv));
7693 }
7694 template <>
7695 fir::ArrayLoadOp
genArrayLoad(mlir::Location loc,Fortran::lower::AbstractConverter & converter,fir::FirOpBuilder & builder,const Fortran::evaluate::ArrayRef * x,Fortran::lower::SymMap & symMap,Fortran::lower::StatementContext & stmtCtx)7696 genArrayLoad(mlir::Location loc, Fortran::lower::AbstractConverter &converter,
7697 fir::FirOpBuilder &builder, const Fortran::evaluate::ArrayRef *x,
7698 Fortran::lower::SymMap &symMap,
7699 Fortran::lower::StatementContext &stmtCtx) {
7700 if (x->base().IsSymbol())
7701 return genArrayLoad(loc, converter, builder, &getLastSym(x->base()), symMap,
7702 stmtCtx);
7703 return genArrayLoad(loc, converter, builder, &x->base().GetComponent(),
7704 symMap, stmtCtx);
7705 }
7706
createArrayLoads(Fortran::lower::AbstractConverter & converter,Fortran::lower::ExplicitIterSpace & esp,Fortran::lower::SymMap & symMap)7707 void Fortran::lower::createArrayLoads(
7708 Fortran::lower::AbstractConverter &converter,
7709 Fortran::lower::ExplicitIterSpace &esp, Fortran::lower::SymMap &symMap) {
7710 std::size_t counter = esp.getCounter();
7711 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
7712 mlir::Location loc = converter.getCurrentLocation();
7713 Fortran::lower::StatementContext &stmtCtx = esp.stmtContext();
7714 // Gen the fir.array_load ops.
7715 auto genLoad = [&](const auto *x) -> fir::ArrayLoadOp {
7716 return genArrayLoad(loc, converter, builder, x, symMap, stmtCtx);
7717 };
7718 if (esp.lhsBases[counter]) {
7719 auto &base = *esp.lhsBases[counter];
7720 auto load = std::visit(genLoad, base);
7721 esp.initialArgs.push_back(load);
7722 esp.resetInnerArgs();
7723 esp.bindLoad(base, load);
7724 }
7725 for (const auto &base : esp.rhsBases[counter])
7726 esp.bindLoad(base, std::visit(genLoad, base));
7727 }
7728
createArrayMergeStores(Fortran::lower::AbstractConverter & converter,Fortran::lower::ExplicitIterSpace & esp)7729 void Fortran::lower::createArrayMergeStores(
7730 Fortran::lower::AbstractConverter &converter,
7731 Fortran::lower::ExplicitIterSpace &esp) {
7732 fir::FirOpBuilder &builder = converter.getFirOpBuilder();
7733 mlir::Location loc = converter.getCurrentLocation();
7734 builder.setInsertionPointAfter(esp.getOuterLoop());
7735 // Gen the fir.array_merge_store ops for all LHS arrays.
7736 for (auto i : llvm::enumerate(esp.getOuterLoop().getResults()))
7737 if (llvm::Optional<fir::ArrayLoadOp> ldOpt = esp.getLhsLoad(i.index())) {
7738 fir::ArrayLoadOp load = *ldOpt;
7739 builder.create<fir::ArrayMergeStoreOp>(loc, load, i.value(),
7740 load.getMemref(), load.getSlice(),
7741 load.getTypeparams());
7742 }
7743 if (esp.loopCleanup) {
7744 (*esp.loopCleanup)(builder);
7745 esp.loopCleanup = llvm::None;
7746 }
7747 esp.initialArgs.clear();
7748 esp.innerArgs.clear();
7749 esp.outerLoop = llvm::None;
7750 esp.resetBindings();
7751 esp.incrementCounter();
7752 }
7753