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 &copyOutPairs,
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 &copyOutPair) {
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 &copyOutPairs, 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 &copyOutPair : 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