xref: /llvm-project/flang/lib/Optimizer/Builder/FIRBuilder.cpp (revision bac95752748a46f3c2e9ebeda67e7df2ea642e07)
1 //===-- FIRBuilder.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 #include "flang/Optimizer/Builder/FIRBuilder.h"
10 #include "flang/Optimizer/Builder/BoxValue.h"
11 #include "flang/Optimizer/Builder/Character.h"
12 #include "flang/Optimizer/Builder/Complex.h"
13 #include "flang/Optimizer/Builder/MutableBox.h"
14 #include "flang/Optimizer/Builder/Runtime/Assign.h"
15 #include "flang/Optimizer/Builder/Runtime/Derived.h"
16 #include "flang/Optimizer/Builder/Todo.h"
17 #include "flang/Optimizer/Dialect/CUF/CUFOps.h"
18 #include "flang/Optimizer/Dialect/FIRAttr.h"
19 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
20 #include "flang/Optimizer/Dialect/FIRType.h"
21 #include "flang/Optimizer/Support/DataLayout.h"
22 #include "flang/Optimizer/Support/FatalError.h"
23 #include "flang/Optimizer/Support/InternalNames.h"
24 #include "flang/Optimizer/Support/Utils.h"
25 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
26 #include "mlir/Dialect/OpenACC/OpenACC.h"
27 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
28 #include "llvm/ADT/ArrayRef.h"
29 #include "llvm/ADT/StringExtras.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/ErrorHandling.h"
32 #include "llvm/Support/MD5.h"
33 #include <optional>
34 
35 static llvm::cl::opt<std::size_t>
36     nameLengthHashSize("length-to-hash-string-literal",
37                        llvm::cl::desc("string literals that exceed this length"
38                                       " will use a hash value as their symbol "
39                                       "name"),
40                        llvm::cl::init(32));
41 
42 mlir::func::FuncOp
43 fir::FirOpBuilder::createFunction(mlir::Location loc, mlir::ModuleOp module,
44                                   llvm::StringRef name, mlir::FunctionType ty,
45                                   mlir::SymbolTable *symbolTable) {
46   return fir::createFuncOp(loc, module, name, ty, /*attrs*/ {}, symbolTable);
47 }
48 
49 mlir::func::FuncOp
50 fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
51                                     const mlir::SymbolTable *symbolTable,
52                                     llvm::StringRef name) {
53   if (symbolTable)
54     if (auto func = symbolTable->lookup<mlir::func::FuncOp>(name)) {
55 #ifdef EXPENSIVE_CHECKS
56       assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(name) &&
57              "symbolTable and module out of sync");
58 #endif
59       return func;
60     }
61   return modOp.lookupSymbol<mlir::func::FuncOp>(name);
62 }
63 
64 mlir::func::FuncOp
65 fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp,
66                                     const mlir::SymbolTable *symbolTable,
67                                     mlir::SymbolRefAttr symbol) {
68   if (symbolTable)
69     if (auto func = symbolTable->lookup<mlir::func::FuncOp>(
70             symbol.getLeafReference())) {
71 #ifdef EXPENSIVE_CHECKS
72       assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(symbol) &&
73              "symbolTable and module out of sync");
74 #endif
75       return func;
76     }
77   return modOp.lookupSymbol<mlir::func::FuncOp>(symbol);
78 }
79 
80 fir::GlobalOp
81 fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp,
82                                   const mlir::SymbolTable *symbolTable,
83                                   llvm::StringRef name) {
84   if (symbolTable)
85     if (auto global = symbolTable->lookup<fir::GlobalOp>(name)) {
86 #ifdef EXPENSIVE_CHECKS
87       assert(global == modOp.lookupSymbol<fir::GlobalOp>(name) &&
88              "symbolTable and module out of sync");
89 #endif
90       return global;
91     }
92   return modOp.lookupSymbol<fir::GlobalOp>(name);
93 }
94 
95 mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy) {
96   assert(!mlir::isa<fir::ReferenceType>(eleTy) && "cannot be a reference type");
97   return fir::ReferenceType::get(eleTy);
98 }
99 
100 mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) {
101   fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent());
102   return fir::SequenceType::get(shape, eleTy);
103 }
104 
105 mlir::Type fir::FirOpBuilder::getRealType(int kind) {
106   switch (kindMap.getRealTypeID(kind)) {
107   case llvm::Type::TypeID::HalfTyID:
108     return mlir::Float16Type::get(getContext());
109   case llvm::Type::TypeID::BFloatTyID:
110     return mlir::BFloat16Type::get(getContext());
111   case llvm::Type::TypeID::FloatTyID:
112     return mlir::Float32Type::get(getContext());
113   case llvm::Type::TypeID::DoubleTyID:
114     return mlir::Float64Type::get(getContext());
115   case llvm::Type::TypeID::X86_FP80TyID:
116     return mlir::Float80Type::get(getContext());
117   case llvm::Type::TypeID::FP128TyID:
118     return mlir::Float128Type::get(getContext());
119   default:
120     fir::emitFatalError(mlir::UnknownLoc::get(getContext()),
121                         "unsupported type !fir.real<kind>");
122   }
123 }
124 
125 mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc,
126                                                   mlir::Type ptrType) {
127   auto ty = ptrType ? ptrType : getRefType(getNoneType());
128   return create<fir::ZeroOp>(loc, ty);
129 }
130 
131 mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc,
132                                                      mlir::Type ty,
133                                                      std::int64_t cst) {
134   assert((cst >= 0 || mlir::isa<mlir::IndexType>(ty) ||
135           mlir::cast<mlir::IntegerType>(ty).getWidth() <= 64) &&
136          "must use APint");
137   return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, cst));
138 }
139 
140 mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc,
141                                                     mlir::Type ty) {
142   if (mlir::isa<mlir::IndexType>(ty))
143     return createIntegerConstant(loc, ty, -1);
144   llvm::APInt allOnes =
145       llvm::APInt::getAllOnes(mlir::cast<mlir::IntegerType>(ty).getWidth());
146   return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, allOnes));
147 }
148 
149 mlir::Value
150 fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy,
151                                       llvm::APFloat::integerPart val) {
152   auto apf = [&]() -> llvm::APFloat {
153     if (fltTy.isF16())
154       return llvm::APFloat(llvm::APFloat::IEEEhalf(), val);
155     if (fltTy.isBF16())
156       return llvm::APFloat(llvm::APFloat::BFloat(), val);
157     if (fltTy.isF32())
158       return llvm::APFloat(llvm::APFloat::IEEEsingle(), val);
159     if (fltTy.isF64())
160       return llvm::APFloat(llvm::APFloat::IEEEdouble(), val);
161     if (fltTy.isF80())
162       return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val);
163     if (fltTy.isF128())
164       return llvm::APFloat(llvm::APFloat::IEEEquad(), val);
165     llvm_unreachable("unhandled MLIR floating-point type");
166   };
167   return createRealConstant(loc, fltTy, apf());
168 }
169 
170 mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc,
171                                                   mlir::Type fltTy,
172                                                   const llvm::APFloat &value) {
173   if (mlir::isa<mlir::FloatType>(fltTy)) {
174     auto attr = getFloatAttr(fltTy, value);
175     return create<mlir::arith::ConstantOp>(loc, fltTy, attr);
176   }
177   llvm_unreachable("should use builtin floating-point type");
178 }
179 
180 llvm::SmallVector<mlir::Value>
181 fir::factory::elideExtentsAlreadyInType(mlir::Type type,
182                                         mlir::ValueRange shape) {
183   auto arrTy = mlir::dyn_cast<fir::SequenceType>(type);
184   if (shape.empty() || !arrTy)
185     return {};
186   // elide the constant dimensions before construction
187   assert(shape.size() == arrTy.getDimension());
188   llvm::SmallVector<mlir::Value> dynamicShape;
189   auto typeShape = arrTy.getShape();
190   for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i)
191     if (typeShape[i] == fir::SequenceType::getUnknownExtent())
192       dynamicShape.push_back(shape[i]);
193   return dynamicShape;
194 }
195 
196 llvm::SmallVector<mlir::Value>
197 fir::factory::elideLengthsAlreadyInType(mlir::Type type,
198                                         mlir::ValueRange lenParams) {
199   if (lenParams.empty())
200     return {};
201   if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type))
202     type = arrTy.getEleTy();
203   if (fir::hasDynamicSize(type))
204     return lenParams;
205   return {};
206 }
207 
208 /// Allocate a local variable.
209 /// A local variable ought to have a name in the source code.
210 mlir::Value fir::FirOpBuilder::allocateLocal(
211     mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
212     llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape,
213     llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
214   // Convert the shape extents to `index`, as needed.
215   llvm::SmallVector<mlir::Value> indices;
216   llvm::SmallVector<mlir::Value> elidedShape =
217       fir::factory::elideExtentsAlreadyInType(ty, shape);
218   llvm::SmallVector<mlir::Value> elidedLenParams =
219       fir::factory::elideLengthsAlreadyInType(ty, lenParams);
220   auto idxTy = getIndexType();
221   for (mlir::Value sh : elidedShape)
222     indices.push_back(createConvert(loc, idxTy, sh));
223   // Add a target attribute, if needed.
224   llvm::SmallVector<mlir::NamedAttribute> attrs;
225   if (asTarget)
226     attrs.emplace_back(
227         mlir::StringAttr::get(getContext(), fir::getTargetAttrName()),
228         getUnitAttr());
229   // Create the local variable.
230   if (name.empty()) {
231     if (uniqName.empty())
232       return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices,
233                                    attrs);
234     return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams,
235                                  indices, attrs);
236   }
237   return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams,
238                                indices, attrs);
239 }
240 
241 mlir::Value fir::FirOpBuilder::allocateLocal(
242     mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName,
243     llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape,
244     llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) {
245   return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape,
246                        lenParams, asTarget);
247 }
248 
249 /// Get the block for adding Allocas.
250 mlir::Block *fir::FirOpBuilder::getAllocaBlock() {
251   if (auto accComputeRegionIface =
252           getRegion().getParentOfType<mlir::acc::ComputeRegionOpInterface>()) {
253     return accComputeRegionIface.getAllocaBlock();
254   }
255 
256   if (auto ompOutlineableIface =
257           getRegion()
258               .getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>()) {
259     return ompOutlineableIface.getAllocaBlock();
260   }
261 
262   if (auto recipeIface =
263           getRegion().getParentOfType<mlir::accomp::RecipeInterface>()) {
264     return recipeIface.getAllocaBlock(getRegion());
265   }
266 
267   return getEntryBlock();
268 }
269 
270 static mlir::ArrayAttr makeI64ArrayAttr(llvm::ArrayRef<int64_t> values,
271                                         mlir::MLIRContext *context) {
272   llvm::SmallVector<mlir::Attribute, 4> attrs;
273   attrs.reserve(values.size());
274   for (auto &v : values)
275     attrs.push_back(mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64),
276                                            mlir::APInt(64, v)));
277   return mlir::ArrayAttr::get(context, attrs);
278 }
279 
280 mlir::ArrayAttr fir::FirOpBuilder::create2DI64ArrayAttr(
281     llvm::SmallVectorImpl<llvm::SmallVector<int64_t>> &intData) {
282   llvm::SmallVector<mlir::Attribute> arrayAttr;
283   arrayAttr.reserve(intData.size());
284   mlir::MLIRContext *context = getContext();
285   for (auto &v : intData)
286     arrayAttr.push_back(makeI64ArrayAttr(v, context));
287   return mlir::ArrayAttr::get(context, arrayAttr);
288 }
289 
290 mlir::Value fir::FirOpBuilder::createTemporaryAlloc(
291     mlir::Location loc, mlir::Type type, llvm::StringRef name,
292     mlir::ValueRange lenParams, mlir::ValueRange shape,
293     llvm::ArrayRef<mlir::NamedAttribute> attrs,
294     std::optional<Fortran::common::CUDADataAttr> cudaAttr) {
295   assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference");
296   // If the alloca is inside an OpenMP Op which will be outlined then pin
297   // the alloca here.
298   const bool pinned =
299       getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>();
300   if (cudaAttr) {
301     cuf::DataAttributeAttr attr = cuf::getDataAttribute(getContext(), cudaAttr);
302     return create<cuf::AllocOp>(loc, type, /*unique_name=*/llvm::StringRef{},
303                                 name, attr, lenParams, shape, attrs);
304   } else {
305     return create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{},
306                                  name, pinned, lenParams, shape, attrs);
307   }
308 }
309 
310 /// Create a temporary variable on the stack. Anonymous temporaries have no
311 /// `name` value. Temporaries do not require a uniqued name.
312 mlir::Value fir::FirOpBuilder::createTemporary(
313     mlir::Location loc, mlir::Type type, llvm::StringRef name,
314     mlir::ValueRange shape, mlir::ValueRange lenParams,
315     llvm::ArrayRef<mlir::NamedAttribute> attrs,
316     std::optional<Fortran::common::CUDADataAttr> cudaAttr) {
317   llvm::SmallVector<mlir::Value> dynamicShape =
318       fir::factory::elideExtentsAlreadyInType(type, shape);
319   llvm::SmallVector<mlir::Value> dynamicLength =
320       fir::factory::elideLengthsAlreadyInType(type, lenParams);
321   InsertPoint insPt;
322   const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty();
323   if (hoistAlloc) {
324     insPt = saveInsertionPoint();
325     setInsertionPointToStart(getAllocaBlock());
326   }
327 
328   mlir::Value ae = createTemporaryAlloc(loc, type, name, dynamicLength,
329                                         dynamicShape, attrs, cudaAttr);
330 
331   if (hoistAlloc)
332     restoreInsertionPoint(insPt);
333   return ae;
334 }
335 
336 mlir::Value fir::FirOpBuilder::createHeapTemporary(
337     mlir::Location loc, mlir::Type type, llvm::StringRef name,
338     mlir::ValueRange shape, mlir::ValueRange lenParams,
339     llvm::ArrayRef<mlir::NamedAttribute> attrs) {
340   llvm::SmallVector<mlir::Value> dynamicShape =
341       fir::factory::elideExtentsAlreadyInType(type, shape);
342   llvm::SmallVector<mlir::Value> dynamicLength =
343       fir::factory::elideLengthsAlreadyInType(type, lenParams);
344 
345   assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference");
346   return create<fir::AllocMemOp>(loc, type, /*unique_name=*/llvm::StringRef{},
347                                  name, dynamicLength, dynamicShape, attrs);
348 }
349 
350 mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) {
351   mlir::Type voidPtr = mlir::LLVM::LLVMPointerType::get(
352       getContext(), fir::factory::getAllocaAddressSpace(&getDataLayout()));
353   return create<mlir::LLVM::StackSaveOp>(loc, voidPtr);
354 }
355 
356 void fir::FirOpBuilder::genStackRestore(mlir::Location loc,
357                                         mlir::Value stackPointer) {
358   create<mlir::LLVM::StackRestoreOp>(loc, stackPointer);
359 }
360 
361 /// Create a global variable in the (read-only) data section. A global variable
362 /// must have a unique name to identify and reference it.
363 fir::GlobalOp fir::FirOpBuilder::createGlobal(
364     mlir::Location loc, mlir::Type type, llvm::StringRef name,
365     mlir::StringAttr linkage, mlir::Attribute value, bool isConst,
366     bool isTarget, cuf::DataAttributeAttr dataAttr) {
367   if (auto global = getNamedGlobal(name))
368     return global;
369   auto module = getModule();
370   auto insertPt = saveInsertionPoint();
371   setInsertionPoint(module.getBody(), module.getBody()->end());
372   llvm::SmallVector<mlir::NamedAttribute> attrs;
373   if (dataAttr) {
374     auto globalOpName = mlir::OperationName(fir::GlobalOp::getOperationName(),
375                                             module.getContext());
376     attrs.push_back(mlir::NamedAttribute(
377         fir::GlobalOp::getDataAttrAttrName(globalOpName), dataAttr));
378   }
379   auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type, value,
380                                     linkage, attrs);
381   restoreInsertionPoint(insertPt);
382   if (symbolTable)
383     symbolTable->insert(glob);
384   return glob;
385 }
386 
387 fir::GlobalOp fir::FirOpBuilder::createGlobal(
388     mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst,
389     bool isTarget, std::function<void(FirOpBuilder &)> bodyBuilder,
390     mlir::StringAttr linkage, cuf::DataAttributeAttr dataAttr) {
391   if (auto global = getNamedGlobal(name))
392     return global;
393   auto module = getModule();
394   auto insertPt = saveInsertionPoint();
395   setInsertionPoint(module.getBody(), module.getBody()->end());
396   auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type,
397                                     mlir::Attribute{}, linkage);
398   auto &region = glob.getRegion();
399   region.push_back(new mlir::Block);
400   auto &block = glob.getRegion().back();
401   setInsertionPointToStart(&block);
402   bodyBuilder(*this);
403   restoreInsertionPoint(insertPt);
404   if (symbolTable)
405     symbolTable->insert(glob);
406   return glob;
407 }
408 
409 std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint>
410 fir::FirOpBuilder::createTypeInfoOp(mlir::Location loc,
411                                     fir::RecordType recordType,
412                                     fir::RecordType parentType) {
413   mlir::ModuleOp module = getModule();
414   if (fir::TypeInfoOp typeInfo =
415           fir::lookupTypeInfoOp(recordType.getName(), module, symbolTable))
416     return {typeInfo, InsertPoint{}};
417   InsertPoint insertPoint = saveInsertionPoint();
418   setInsertionPoint(module.getBody(), module.getBody()->end());
419   auto typeInfo = create<fir::TypeInfoOp>(loc, recordType, parentType);
420   if (symbolTable)
421     symbolTable->insert(typeInfo);
422   return {typeInfo, insertPoint};
423 }
424 
425 mlir::Value fir::FirOpBuilder::convertWithSemantics(
426     mlir::Location loc, mlir::Type toTy, mlir::Value val,
427     bool allowCharacterConversion, bool allowRebox) {
428   assert(toTy && "store location must be typed");
429   auto fromTy = val.getType();
430   if (fromTy == toTy)
431     return val;
432   fir::factory::Complex helper{*this, loc};
433   if ((fir::isa_real(fromTy) || fir::isa_integer(fromTy)) &&
434       fir::isa_complex(toTy)) {
435     // imaginary part is zero
436     auto eleTy = helper.getComplexPartType(toTy);
437     auto cast = createConvert(loc, eleTy, val);
438     auto imag = createRealZeroConstant(loc, eleTy);
439     return helper.createComplex(toTy, cast, imag);
440   }
441   if (fir::isa_complex(fromTy) &&
442       (fir::isa_integer(toTy) || fir::isa_real(toTy))) {
443     // drop the imaginary part
444     auto rp = helper.extractComplexPart(val, /*isImagPart=*/false);
445     return createConvert(loc, toTy, rp);
446   }
447   if (allowCharacterConversion) {
448     if (mlir::isa<fir::BoxCharType>(fromTy)) {
449       // Extract the address of the character string and pass it
450       fir::factory::CharacterExprHelper charHelper{*this, loc};
451       std::pair<mlir::Value, mlir::Value> unboxchar =
452           charHelper.createUnboxChar(val);
453       return createConvert(loc, toTy, unboxchar.first);
454     }
455     if (auto boxType = mlir::dyn_cast<fir::BoxCharType>(toTy)) {
456       // Extract the address of the actual argument and create a boxed
457       // character value with an undefined length
458       // TODO: We should really calculate the total size of the actual
459       // argument in characters and use it as the length of the string
460       auto refType = getRefType(boxType.getEleTy());
461       mlir::Value charBase = createConvert(loc, refType, val);
462       // Do not use fir.undef since llvm optimizer is too harsh when it
463       // sees such values (may just delete code).
464       mlir::Value unknownLen = createIntegerConstant(loc, getIndexType(), 0);
465       fir::factory::CharacterExprHelper charHelper{*this, loc};
466       return charHelper.createEmboxChar(charBase, unknownLen);
467     }
468   }
469   if (fir::isa_ref_type(toTy) && fir::isa_box_type(fromTy)) {
470     // Call is expecting a raw data pointer, not a box. Get the data pointer out
471     // of the box and pass that.
472     assert((fir::unwrapRefType(toTy) ==
473                 fir::unwrapRefType(fir::unwrapPassByRefType(fromTy)) &&
474             "element types expected to match"));
475     return create<fir::BoxAddrOp>(loc, toTy, val);
476   }
477   if (fir::isa_ref_type(fromTy) && mlir::isa<fir::BoxProcType>(toTy)) {
478     // Call is expecting a boxed procedure, not a reference to other data type.
479     // Convert the reference to a procedure and embox it.
480     mlir::Type procTy = mlir::cast<fir::BoxProcType>(toTy).getEleTy();
481     mlir::Value proc = createConvert(loc, procTy, val);
482     return create<fir::EmboxProcOp>(loc, toTy, proc);
483   }
484 
485   // Legacy: remove when removing non HLFIR lowering path.
486   if (allowRebox)
487     if (((fir::isPolymorphicType(fromTy) &&
488           (fir::isAllocatableType(fromTy) || fir::isPointerType(fromTy)) &&
489           fir::isPolymorphicType(toTy)) ||
490          (fir::isPolymorphicType(fromTy) && mlir::isa<fir::BoxType>(toTy))) &&
491         !(fir::isUnlimitedPolymorphicType(fromTy) && fir::isAssumedType(toTy)))
492       return create<fir::ReboxOp>(loc, toTy, val, mlir::Value{},
493                                   /*slice=*/mlir::Value{});
494 
495   return createConvert(loc, toTy, val);
496 }
497 
498 mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder,
499                                         mlir::Location loc, mlir::Type toTy,
500                                         mlir::Value val) {
501   if (val.getType() != toTy) {
502     assert((!fir::isa_derived(toTy) ||
503             mlir::cast<fir::RecordType>(val.getType()).getTypeList() ==
504                 mlir::cast<fir::RecordType>(toTy).getTypeList()) &&
505            "incompatible record types");
506     return builder.create<fir::ConvertOp>(loc, toTy, val);
507   }
508   return val;
509 }
510 
511 mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc,
512                                              mlir::Type toTy, mlir::Value val) {
513   return fir::factory::createConvert(*this, loc, toTy, val);
514 }
515 
516 void fir::FirOpBuilder::createStoreWithConvert(mlir::Location loc,
517                                                mlir::Value val,
518                                                mlir::Value addr) {
519   mlir::Value cast =
520       createConvert(loc, fir::unwrapRefType(addr.getType()), val);
521   create<fir::StoreOp>(loc, cast, addr);
522 }
523 
524 mlir::Value fir::FirOpBuilder::loadIfRef(mlir::Location loc, mlir::Value val) {
525   if (fir::isa_ref_type(val.getType()))
526     return create<fir::LoadOp>(loc, val);
527   return val;
528 }
529 
530 fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc,
531                                                       llvm::StringRef data) {
532   auto type = fir::CharacterType::get(getContext(), 1, data.size());
533   auto strAttr = mlir::StringAttr::get(getContext(), data);
534   auto valTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::value());
535   mlir::NamedAttribute dataAttr(valTag, strAttr);
536   auto sizeTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::size());
537   mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size()));
538   llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr};
539   return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type},
540                                   std::nullopt, attrs);
541 }
542 
543 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
544                                         llvm::ArrayRef<mlir::Value> exts) {
545   return create<fir::ShapeOp>(loc, exts);
546 }
547 
548 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
549                                         llvm::ArrayRef<mlir::Value> shift,
550                                         llvm::ArrayRef<mlir::Value> exts) {
551   auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size());
552   llvm::SmallVector<mlir::Value> shapeArgs;
553   auto idxTy = getIndexType();
554   for (auto [lbnd, ext] : llvm::zip(shift, exts)) {
555     auto lb = createConvert(loc, idxTy, lbnd);
556     shapeArgs.push_back(lb);
557     shapeArgs.push_back(ext);
558   }
559   return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs);
560 }
561 
562 mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc,
563                                         const fir::AbstractArrayBox &arr) {
564   if (arr.lboundsAllOne())
565     return genShape(loc, arr.getExtents());
566   return genShape(loc, arr.getLBounds(), arr.getExtents());
567 }
568 
569 mlir::Value fir::FirOpBuilder::genShift(mlir::Location loc,
570                                         llvm::ArrayRef<mlir::Value> shift) {
571   auto shiftType = fir::ShiftType::get(getContext(), shift.size());
572   return create<fir::ShiftOp>(loc, shiftType, shift);
573 }
574 
575 mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc,
576                                            const fir::ExtendedValue &exv) {
577   return exv.match(
578       [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); },
579       [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); },
580       [&](const fir::BoxValue &box) -> mlir::Value {
581         if (!box.getLBounds().empty()) {
582           auto shiftType =
583               fir::ShiftType::get(getContext(), box.getLBounds().size());
584           return create<fir::ShiftOp>(loc, shiftType, box.getLBounds());
585         }
586         return {};
587       },
588       [&](const fir::MutableBoxValue &) -> mlir::Value {
589         // MutableBoxValue must be read into another category to work with them
590         // outside of allocation/assignment contexts.
591         fir::emitFatalError(loc, "createShape on MutableBoxValue");
592       },
593       [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
594 }
595 
596 mlir::Value fir::FirOpBuilder::createSlice(mlir::Location loc,
597                                            const fir::ExtendedValue &exv,
598                                            mlir::ValueRange triples,
599                                            mlir::ValueRange path) {
600   if (triples.empty()) {
601     // If there is no slicing by triple notation, then take the whole array.
602     auto fullShape = [&](const llvm::ArrayRef<mlir::Value> lbounds,
603                          llvm::ArrayRef<mlir::Value> extents) -> mlir::Value {
604       llvm::SmallVector<mlir::Value> trips;
605       auto idxTy = getIndexType();
606       auto one = createIntegerConstant(loc, idxTy, 1);
607       if (lbounds.empty()) {
608         for (auto v : extents) {
609           trips.push_back(one);
610           trips.push_back(v);
611           trips.push_back(one);
612         }
613         return create<fir::SliceOp>(loc, trips, path);
614       }
615       for (auto [lbnd, extent] : llvm::zip(lbounds, extents)) {
616         auto lb = createConvert(loc, idxTy, lbnd);
617         auto ext = createConvert(loc, idxTy, extent);
618         auto shift = create<mlir::arith::SubIOp>(loc, lb, one);
619         auto ub = create<mlir::arith::AddIOp>(loc, ext, shift);
620         trips.push_back(lb);
621         trips.push_back(ub);
622         trips.push_back(one);
623       }
624       return create<fir::SliceOp>(loc, trips, path);
625     };
626     return exv.match(
627         [&](const fir::ArrayBoxValue &box) {
628           return fullShape(box.getLBounds(), box.getExtents());
629         },
630         [&](const fir::CharArrayBoxValue &box) {
631           return fullShape(box.getLBounds(), box.getExtents());
632         },
633         [&](const fir::BoxValue &box) {
634           auto extents = fir::factory::readExtents(*this, loc, box);
635           return fullShape(box.getLBounds(), extents);
636         },
637         [&](const fir::MutableBoxValue &) -> mlir::Value {
638           // MutableBoxValue must be read into another category to work with
639           // them outside of allocation/assignment contexts.
640           fir::emitFatalError(loc, "createSlice on MutableBoxValue");
641         },
642         [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array"); });
643   }
644   return create<fir::SliceOp>(loc, triples, path);
645 }
646 
647 mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc,
648                                          const fir::ExtendedValue &exv,
649                                          bool isPolymorphic,
650                                          bool isAssumedType) {
651   mlir::Value itemAddr = fir::getBase(exv);
652   if (mlir::isa<fir::BaseBoxType>(itemAddr.getType()))
653     return itemAddr;
654   auto elementType = fir::dyn_cast_ptrEleTy(itemAddr.getType());
655   if (!elementType) {
656     mlir::emitError(loc, "internal: expected a memory reference type ")
657         << itemAddr.getType();
658     llvm_unreachable("not a memory reference type");
659   }
660   mlir::Type boxTy;
661   mlir::Value tdesc;
662   // Avoid to wrap a box/class with box/class.
663   if (mlir::isa<fir::BaseBoxType>(elementType)) {
664     boxTy = elementType;
665   } else {
666     boxTy = fir::BoxType::get(elementType);
667     if (isPolymorphic) {
668       elementType = fir::updateTypeForUnlimitedPolymorphic(elementType);
669       if (isAssumedType)
670         boxTy = fir::BoxType::get(elementType);
671       else
672         boxTy = fir::ClassType::get(elementType);
673     }
674   }
675 
676   return exv.match(
677       [&](const fir::ArrayBoxValue &box) -> mlir::Value {
678         mlir::Value empty;
679         mlir::ValueRange emptyRange;
680         mlir::Value s = createShape(loc, exv);
681         return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, /*slice=*/empty,
682                                     /*typeparams=*/emptyRange,
683                                     isPolymorphic ? box.getSourceBox() : tdesc);
684       },
685       [&](const fir::CharArrayBoxValue &box) -> mlir::Value {
686         mlir::Value s = createShape(loc, exv);
687         if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv))
688           return create<fir::EmboxOp>(loc, boxTy, itemAddr, s);
689 
690         mlir::Value emptySlice;
691         llvm::SmallVector<mlir::Value> lenParams{box.getLen()};
692         return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, emptySlice,
693                                     lenParams);
694       },
695       [&](const fir::CharBoxValue &box) -> mlir::Value {
696         if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv))
697           return create<fir::EmboxOp>(loc, boxTy, itemAddr);
698         mlir::Value emptyShape, emptySlice;
699         llvm::SmallVector<mlir::Value> lenParams{box.getLen()};
700         return create<fir::EmboxOp>(loc, boxTy, itemAddr, emptyShape,
701                                     emptySlice, lenParams);
702       },
703       [&](const fir::MutableBoxValue &x) -> mlir::Value {
704         return create<fir::LoadOp>(
705             loc, fir::factory::getMutableIRBox(*this, loc, x));
706       },
707       [&](const fir::PolymorphicValue &p) -> mlir::Value {
708         mlir::Value empty;
709         mlir::ValueRange emptyRange;
710         return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty,
711                                     emptyRange,
712                                     isPolymorphic ? p.getSourceBox() : tdesc);
713       },
714       [&](const auto &) -> mlir::Value {
715         mlir::Value empty;
716         mlir::ValueRange emptyRange;
717         return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty,
718                                     emptyRange, tdesc);
719       });
720 }
721 
722 mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, mlir::Type boxType,
723                                          mlir::Value addr, mlir::Value shape,
724                                          mlir::Value slice,
725                                          llvm::ArrayRef<mlir::Value> lengths,
726                                          mlir::Value tdesc) {
727   mlir::Type valueOrSequenceType = fir::unwrapPassByRefType(boxType);
728   return create<fir::EmboxOp>(
729       loc, boxType, addr, shape, slice,
730       fir::factory::elideLengthsAlreadyInType(valueOrSequenceType, lengths),
731       tdesc);
732 }
733 
734 void fir::FirOpBuilder::dumpFunc() { getFunction().dump(); }
735 
736 static mlir::Value
737 genNullPointerComparison(fir::FirOpBuilder &builder, mlir::Location loc,
738                          mlir::Value addr,
739                          mlir::arith::CmpIPredicate condition) {
740   auto intPtrTy = builder.getIntPtrType();
741   auto ptrToInt = builder.createConvert(loc, intPtrTy, addr);
742   auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0);
743   return builder.create<mlir::arith::CmpIOp>(loc, condition, ptrToInt, c0);
744 }
745 
746 mlir::Value fir::FirOpBuilder::genIsNotNullAddr(mlir::Location loc,
747                                                 mlir::Value addr) {
748   return genNullPointerComparison(*this, loc, addr,
749                                   mlir::arith::CmpIPredicate::ne);
750 }
751 
752 mlir::Value fir::FirOpBuilder::genIsNullAddr(mlir::Location loc,
753                                              mlir::Value addr) {
754   return genNullPointerComparison(*this, loc, addr,
755                                   mlir::arith::CmpIPredicate::eq);
756 }
757 
758 mlir::Value fir::FirOpBuilder::genExtentFromTriplet(mlir::Location loc,
759                                                     mlir::Value lb,
760                                                     mlir::Value ub,
761                                                     mlir::Value step,
762                                                     mlir::Type type) {
763   auto zero = createIntegerConstant(loc, type, 0);
764   lb = createConvert(loc, type, lb);
765   ub = createConvert(loc, type, ub);
766   step = createConvert(loc, type, step);
767   auto diff = create<mlir::arith::SubIOp>(loc, ub, lb);
768   auto add = create<mlir::arith::AddIOp>(loc, diff, step);
769   auto div = create<mlir::arith::DivSIOp>(loc, add, step);
770   auto cmp = create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::sgt,
771                                          div, zero);
772   return create<mlir::arith::SelectOp>(loc, cmp, div, zero);
773 }
774 
775 mlir::Value fir::FirOpBuilder::genAbsentOp(mlir::Location loc,
776                                            mlir::Type argTy) {
777   if (!fir::isCharacterProcedureTuple(argTy))
778     return create<fir::AbsentOp>(loc, argTy);
779 
780   auto boxProc =
781       create<fir::AbsentOp>(loc, mlir::cast<mlir::TupleType>(argTy).getType(0));
782   mlir::Value charLen = create<fir::UndefOp>(loc, getCharacterLengthType());
783   return fir::factory::createCharacterProcedureTuple(*this, loc, argTy, boxProc,
784                                                      charLen);
785 }
786 
787 void fir::FirOpBuilder::setCommonAttributes(mlir::Operation *op) const {
788   auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op);
789   if (fmi) {
790     // TODO: use fmi.setFastMathFlagsAttr() after D137114 is merged.
791     //       For now set the attribute by the name.
792     llvm::StringRef arithFMFAttrName = fmi.getFastMathAttrName();
793     if (fastMathFlags != mlir::arith::FastMathFlags::none)
794       op->setAttr(arithFMFAttrName, mlir::arith::FastMathFlagsAttr::get(
795                                         op->getContext(), fastMathFlags));
796   }
797   auto iofi =
798       mlir::dyn_cast<mlir::arith::ArithIntegerOverflowFlagsInterface>(*op);
799   if (iofi) {
800     llvm::StringRef arithIOFAttrName = iofi.getIntegerOverflowAttrName();
801     if (integerOverflowFlags != mlir::arith::IntegerOverflowFlags::none)
802       op->setAttr(arithIOFAttrName,
803                   mlir::arith::IntegerOverflowFlagsAttr::get(
804                       op->getContext(), integerOverflowFlags));
805   }
806 }
807 
808 void fir::FirOpBuilder::setFastMathFlags(
809     Fortran::common::MathOptionsBase options) {
810   mlir::arith::FastMathFlags arithFMF{};
811   if (options.getFPContractEnabled()) {
812     arithFMF = arithFMF | mlir::arith::FastMathFlags::contract;
813   }
814   if (options.getNoHonorInfs()) {
815     arithFMF = arithFMF | mlir::arith::FastMathFlags::ninf;
816   }
817   if (options.getNoHonorNaNs()) {
818     arithFMF = arithFMF | mlir::arith::FastMathFlags::nnan;
819   }
820   if (options.getApproxFunc()) {
821     arithFMF = arithFMF | mlir::arith::FastMathFlags::afn;
822   }
823   if (options.getNoSignedZeros()) {
824     arithFMF = arithFMF | mlir::arith::FastMathFlags::nsz;
825   }
826   if (options.getAssociativeMath()) {
827     arithFMF = arithFMF | mlir::arith::FastMathFlags::reassoc;
828   }
829   if (options.getReciprocalMath()) {
830     arithFMF = arithFMF | mlir::arith::FastMathFlags::arcp;
831   }
832   setFastMathFlags(arithFMF);
833 }
834 
835 // Construction of an mlir::DataLayout is expensive so only do it on demand and
836 // memoise it in the builder instance
837 mlir::DataLayout &fir::FirOpBuilder::getDataLayout() {
838   if (dataLayout)
839     return *dataLayout;
840   dataLayout = std::make_unique<mlir::DataLayout>(getModule());
841   return *dataLayout;
842 }
843 
844 //===--------------------------------------------------------------------===//
845 // ExtendedValue inquiry helper implementation
846 //===--------------------------------------------------------------------===//
847 
848 mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder,
849                                       mlir::Location loc,
850                                       const fir::ExtendedValue &box) {
851   return box.match(
852       [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); },
853       [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
854         return x.getLen();
855       },
856       [&](const fir::BoxValue &x) -> mlir::Value {
857         assert(x.isCharacter());
858         if (!x.getExplicitParameters().empty())
859           return x.getExplicitParameters()[0];
860         return fir::factory::CharacterExprHelper{builder, loc}
861             .readLengthFromBox(x.getAddr());
862       },
863       [&](const fir::MutableBoxValue &x) -> mlir::Value {
864         return readCharLen(builder, loc,
865                            fir::factory::genMutableBoxRead(builder, loc, x));
866       },
867       [&](const auto &) -> mlir::Value {
868         fir::emitFatalError(
869             loc, "Character length inquiry on a non-character entity");
870       });
871 }
872 
873 mlir::Value fir::factory::readExtent(fir::FirOpBuilder &builder,
874                                      mlir::Location loc,
875                                      const fir::ExtendedValue &box,
876                                      unsigned dim) {
877   assert(box.rank() > dim);
878   return box.match(
879       [&](const fir::ArrayBoxValue &x) -> mlir::Value {
880         return x.getExtents()[dim];
881       },
882       [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
883         return x.getExtents()[dim];
884       },
885       [&](const fir::BoxValue &x) -> mlir::Value {
886         if (!x.getExplicitExtents().empty())
887           return x.getExplicitExtents()[dim];
888         auto idxTy = builder.getIndexType();
889         auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
890         return builder
891             .create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, x.getAddr(),
892                                     dimVal)
893             .getResult(1);
894       },
895       [&](const fir::MutableBoxValue &x) -> mlir::Value {
896         return readExtent(builder, loc,
897                           fir::factory::genMutableBoxRead(builder, loc, x),
898                           dim);
899       },
900       [&](const auto &) -> mlir::Value {
901         fir::emitFatalError(loc, "extent inquiry on scalar");
902       });
903 }
904 
905 mlir::Value fir::factory::readLowerBound(fir::FirOpBuilder &builder,
906                                          mlir::Location loc,
907                                          const fir::ExtendedValue &box,
908                                          unsigned dim,
909                                          mlir::Value defaultValue) {
910   assert(box.rank() > dim);
911   auto lb = box.match(
912       [&](const fir::ArrayBoxValue &x) -> mlir::Value {
913         return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
914       },
915       [&](const fir::CharArrayBoxValue &x) -> mlir::Value {
916         return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
917       },
918       [&](const fir::BoxValue &x) -> mlir::Value {
919         return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim];
920       },
921       [&](const fir::MutableBoxValue &x) -> mlir::Value {
922         return readLowerBound(builder, loc,
923                               fir::factory::genMutableBoxRead(builder, loc, x),
924                               dim, defaultValue);
925       },
926       [&](const auto &) -> mlir::Value {
927         fir::emitFatalError(loc, "lower bound inquiry on scalar");
928       });
929   if (lb)
930     return lb;
931   return defaultValue;
932 }
933 
934 llvm::SmallVector<mlir::Value>
935 fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc,
936                           const fir::BoxValue &box) {
937   llvm::SmallVector<mlir::Value> result;
938   auto explicitExtents = box.getExplicitExtents();
939   if (!explicitExtents.empty()) {
940     result.append(explicitExtents.begin(), explicitExtents.end());
941     return result;
942   }
943   auto rank = box.rank();
944   auto idxTy = builder.getIndexType();
945   for (decltype(rank) dim = 0; dim < rank; ++dim) {
946     auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
947     auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy,
948                                                   box.getAddr(), dimVal);
949     result.emplace_back(dimInfo.getResult(1));
950   }
951   return result;
952 }
953 
954 llvm::SmallVector<mlir::Value>
955 fir::factory::getExtents(mlir::Location loc, fir::FirOpBuilder &builder,
956                          const fir::ExtendedValue &box) {
957   return box.match(
958       [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
959         return {x.getExtents().begin(), x.getExtents().end()};
960       },
961       [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> {
962         return {x.getExtents().begin(), x.getExtents().end()};
963       },
964       [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> {
965         return fir::factory::readExtents(builder, loc, x);
966       },
967       [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> {
968         auto load = fir::factory::genMutableBoxRead(builder, loc, x);
969         return fir::factory::getExtents(loc, builder, load);
970       },
971       [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
972 }
973 
974 fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder,
975                                               mlir::Location loc,
976                                               const fir::BoxValue &box) {
977   assert(!box.hasAssumedRank() &&
978          "cannot read unlimited polymorphic or assumed rank fir.box");
979   auto addr =
980       builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr());
981   if (box.isCharacter()) {
982     auto len = fir::factory::readCharLen(builder, loc, box);
983     if (box.rank() == 0)
984       return fir::CharBoxValue(addr, len);
985     return fir::CharArrayBoxValue(addr, len,
986                                   fir::factory::readExtents(builder, loc, box),
987                                   box.getLBounds());
988   }
989   if (box.isDerivedWithLenParameters())
990     TODO(loc, "read fir.box with length parameters");
991   mlir::Value sourceBox;
992   if (box.isPolymorphic())
993     sourceBox = box.getAddr();
994   if (box.isPolymorphic() && box.rank() == 0)
995     return fir::PolymorphicValue(addr, sourceBox);
996   if (box.rank() == 0)
997     return addr;
998   return fir::ArrayBoxValue(addr, fir::factory::readExtents(builder, loc, box),
999                             box.getLBounds(), sourceBox);
1000 }
1001 
1002 llvm::SmallVector<mlir::Value>
1003 fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder,
1004                                        mlir::Location loc,
1005                                        const fir::ExtendedValue &exv) {
1006   return exv.match(
1007       [&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> {
1008         return {array.getLBounds().begin(), array.getLBounds().end()};
1009       },
1010       [&](const fir::CharArrayBoxValue &array)
1011           -> llvm::SmallVector<mlir::Value> {
1012         return {array.getLBounds().begin(), array.getLBounds().end()};
1013       },
1014       [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
1015         return {box.getLBounds().begin(), box.getLBounds().end()};
1016       },
1017       [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
1018         auto load = fir::factory::genMutableBoxRead(builder, loc, box);
1019         return fir::factory::getNonDefaultLowerBounds(builder, loc, load);
1020       },
1021       [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
1022 }
1023 
1024 llvm::SmallVector<mlir::Value>
1025 fir::factory::getNonDeferredLenParams(const fir::ExtendedValue &exv) {
1026   return exv.match(
1027       [&](const fir::CharArrayBoxValue &character)
1028           -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
1029       [&](const fir::CharBoxValue &character)
1030           -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; },
1031       [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> {
1032         return {box.nonDeferredLenParams().begin(),
1033                 box.nonDeferredLenParams().end()};
1034       },
1035       [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> {
1036         return {box.getExplicitParameters().begin(),
1037                 box.getExplicitParameters().end()};
1038       },
1039       [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; });
1040 }
1041 
1042 // If valTy is a box type, then we need to extract the type parameters from
1043 // the box value.
1044 static llvm::SmallVector<mlir::Value> getFromBox(mlir::Location loc,
1045                                                  fir::FirOpBuilder &builder,
1046                                                  mlir::Type valTy,
1047                                                  mlir::Value boxVal) {
1048   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(valTy)) {
1049     auto eleTy = fir::unwrapAllRefAndSeqType(boxTy.getEleTy());
1050     if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy)) {
1051       if (recTy.getNumLenParams() > 0) {
1052         // Walk each type parameter in the record and get the value.
1053         TODO(loc, "generate code to get LEN type parameters");
1054       }
1055     } else if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) {
1056       if (charTy.hasDynamicLen()) {
1057         auto idxTy = builder.getIndexType();
1058         auto eleSz = builder.create<fir::BoxEleSizeOp>(loc, idxTy, boxVal);
1059         auto kindBytes =
1060             builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8;
1061         mlir::Value charSz =
1062             builder.createIntegerConstant(loc, idxTy, kindBytes);
1063         mlir::Value len =
1064             builder.create<mlir::arith::DivSIOp>(loc, eleSz, charSz);
1065         return {len};
1066       }
1067     }
1068   }
1069   return {};
1070 }
1071 
1072 // fir::getTypeParams() will get the type parameters from the extended value.
1073 // When the extended value is a BoxValue or MutableBoxValue, it may be necessary
1074 // to generate code, so this factory function handles those cases.
1075 // TODO: fix the inverted type tests, etc.
1076 llvm::SmallVector<mlir::Value>
1077 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder,
1078                             const fir::ExtendedValue &exv) {
1079   auto handleBoxed = [&](const auto &box) -> llvm::SmallVector<mlir::Value> {
1080     if (box.isCharacter())
1081       return {fir::factory::readCharLen(builder, loc, exv)};
1082     if (box.isDerivedWithLenParameters()) {
1083       // This should generate code to read the type parameters from the box.
1084       // This requires some consideration however as MutableBoxValues need to be
1085       // in a sane state to be provide the correct values.
1086       TODO(loc, "derived type with type parameters");
1087     }
1088     return {};
1089   };
1090   // Intentionally reuse the original code path to get type parameters for the
1091   // cases that were supported rather than introduce a new path.
1092   return exv.match(
1093       [&](const fir::BoxValue &box) { return handleBoxed(box); },
1094       [&](const fir::MutableBoxValue &box) { return handleBoxed(box); },
1095       [&](const auto &) { return fir::getTypeParams(exv); });
1096 }
1097 
1098 llvm::SmallVector<mlir::Value>
1099 fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder,
1100                             fir::ArrayLoadOp load) {
1101   mlir::Type memTy = load.getMemref().getType();
1102   if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(memTy))
1103     return getFromBox(loc, builder, boxTy, load.getMemref());
1104   return load.getTypeparams();
1105 }
1106 
1107 std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix,
1108                                         llvm::StringRef name) {
1109   // For "long" identifiers use a hash value
1110   if (name.size() > nameLengthHashSize) {
1111     llvm::MD5 hash;
1112     hash.update(name);
1113     llvm::MD5::MD5Result result;
1114     hash.final(result);
1115     llvm::SmallString<32> str;
1116     llvm::MD5::stringifyResult(result, str);
1117     std::string hashName = prefix.str();
1118     hashName.append("X").append(str.c_str());
1119     return fir::NameUniquer::doGenerated(hashName);
1120   }
1121   // "Short" identifiers use a reversible hex string
1122   std::string nm = prefix.str();
1123   return fir::NameUniquer::doGenerated(
1124       nm.append("X").append(llvm::toHex(name)));
1125 }
1126 
1127 mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder,
1128                                              mlir::Location loc) {
1129   if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) {
1130     // must be encoded as asciiz, C string
1131     auto fn = flc.getFilename().str() + '\0';
1132     return fir::getBase(createStringLiteral(builder, loc, fn));
1133   }
1134   return builder.createNullConstant(loc);
1135 }
1136 
1137 mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder,
1138                                            mlir::Location loc,
1139                                            mlir::Type type) {
1140   if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
1141     return builder.createIntegerConstant(loc, type, flc.getLine());
1142   return builder.createIntegerConstant(loc, type, 0);
1143 }
1144 
1145 fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder,
1146                                                      mlir::Location loc,
1147                                                      llvm::StringRef str) {
1148   std::string globalName = fir::factory::uniqueCGIdent("cl", str);
1149   auto type = fir::CharacterType::get(builder.getContext(), 1, str.size());
1150   auto global = builder.getNamedGlobal(globalName);
1151   if (!global)
1152     global = builder.createGlobalConstant(
1153         loc, type, globalName,
1154         [&](fir::FirOpBuilder &builder) {
1155           auto stringLitOp = builder.createStringLitOp(loc, str);
1156           builder.create<fir::HasValueOp>(loc, stringLitOp);
1157         },
1158         builder.createLinkOnceLinkage());
1159   auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(),
1160                                             global.getSymbol());
1161   auto len = builder.createIntegerConstant(
1162       loc, builder.getCharacterLengthType(), str.size());
1163   return fir::CharBoxValue{addr, len};
1164 }
1165 
1166 llvm::SmallVector<mlir::Value>
1167 fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc,
1168                             fir::SequenceType seqTy) {
1169   llvm::SmallVector<mlir::Value> extents;
1170   auto idxTy = builder.getIndexType();
1171   for (auto ext : seqTy.getShape())
1172     extents.emplace_back(
1173         ext == fir::SequenceType::getUnknownExtent()
1174             ? builder.create<fir::UndefOp>(loc, idxTy).getResult()
1175             : builder.createIntegerConstant(loc, idxTy, ext));
1176   return extents;
1177 }
1178 
1179 // FIXME: This needs some work. To correctly determine the extended value of a
1180 // component, one needs the base object, its type, and its type parameters. (An
1181 // alternative would be to provide an already computed address of the final
1182 // component rather than the base object's address, the point being the result
1183 // will require the address of the final component to create the extended
1184 // value.) One further needs the full path of components being applied. One
1185 // needs to apply type-based expressions to type parameters along this said
1186 // path. (See applyPathToType for a type-only derivation.) Finally, one needs to
1187 // compose the extended value of the terminal component, including all of its
1188 // parameters: array lower bounds expressions, extents, type parameters, etc.
1189 // Any of these properties may be deferred until runtime in Fortran. This
1190 // operation may therefore generate a sizeable block of IR, including calls to
1191 // type-based helper functions, so caching the result of this operation in the
1192 // client would be advised as well.
1193 fir::ExtendedValue fir::factory::componentToExtendedValue(
1194     fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value component) {
1195   auto fieldTy = component.getType();
1196   if (auto ty = fir::dyn_cast_ptrEleTy(fieldTy))
1197     fieldTy = ty;
1198   if (mlir::isa<fir::BaseBoxType>(fieldTy)) {
1199     llvm::SmallVector<mlir::Value> nonDeferredTypeParams;
1200     auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy));
1201     if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) {
1202       auto lenTy = builder.getCharacterLengthType();
1203       if (charTy.hasConstantLen())
1204         nonDeferredTypeParams.emplace_back(
1205             builder.createIntegerConstant(loc, lenTy, charTy.getLen()));
1206       // TODO: Starting, F2003, the dynamic character length might be dependent
1207       // on a PDT length parameter. There is no way to make a difference with
1208       // deferred length here yet.
1209     }
1210     if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy))
1211       if (recTy.getNumLenParams() > 0)
1212         TODO(loc, "allocatable and pointer components non deferred length "
1213                   "parameters");
1214 
1215     return fir::MutableBoxValue(component, nonDeferredTypeParams,
1216                                 /*mutableProperties=*/{});
1217   }
1218   llvm::SmallVector<mlir::Value> extents;
1219   if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(fieldTy)) {
1220     fieldTy = seqTy.getEleTy();
1221     auto idxTy = builder.getIndexType();
1222     for (auto extent : seqTy.getShape()) {
1223       if (extent == fir::SequenceType::getUnknownExtent())
1224         TODO(loc, "array component shape depending on length parameters");
1225       extents.emplace_back(builder.createIntegerConstant(loc, idxTy, extent));
1226     }
1227   }
1228   if (auto charTy = mlir::dyn_cast<fir::CharacterType>(fieldTy)) {
1229     auto cstLen = charTy.getLen();
1230     if (cstLen == fir::CharacterType::unknownLen())
1231       TODO(loc, "get character component length from length type parameters");
1232     auto len = builder.createIntegerConstant(
1233         loc, builder.getCharacterLengthType(), cstLen);
1234     if (!extents.empty())
1235       return fir::CharArrayBoxValue{component, len, extents};
1236     return fir::CharBoxValue{component, len};
1237   }
1238   if (auto recordTy = mlir::dyn_cast<fir::RecordType>(fieldTy))
1239     if (recordTy.getNumLenParams() != 0)
1240       TODO(loc,
1241            "lower component ref that is a derived type with length parameter");
1242   if (!extents.empty())
1243     return fir::ArrayBoxValue{component, extents};
1244   return component;
1245 }
1246 
1247 fir::ExtendedValue fir::factory::arrayElementToExtendedValue(
1248     fir::FirOpBuilder &builder, mlir::Location loc,
1249     const fir::ExtendedValue &array, mlir::Value element) {
1250   return array.match(
1251       [&](const fir::CharBoxValue &cb) -> fir::ExtendedValue {
1252         return cb.clone(element);
1253       },
1254       [&](const fir::CharArrayBoxValue &bv) -> fir::ExtendedValue {
1255         return bv.cloneElement(element);
1256       },
1257       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
1258         if (box.isCharacter()) {
1259           auto len = fir::factory::readCharLen(builder, loc, box);
1260           return fir::CharBoxValue{element, len};
1261         }
1262         if (box.isDerivedWithLenParameters())
1263           TODO(loc, "get length parameters from derived type BoxValue");
1264         if (box.isPolymorphic()) {
1265           return fir::PolymorphicValue(element, fir::getBase(box));
1266         }
1267         return element;
1268       },
1269       [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue {
1270         if (box.getSourceBox())
1271           return fir::PolymorphicValue(element, box.getSourceBox());
1272         return element;
1273       },
1274       [&](const auto &) -> fir::ExtendedValue { return element; });
1275 }
1276 
1277 fir::ExtendedValue fir::factory::arraySectionElementToExtendedValue(
1278     fir::FirOpBuilder &builder, mlir::Location loc,
1279     const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice) {
1280   if (!slice)
1281     return arrayElementToExtendedValue(builder, loc, array, element);
1282   auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(slice.getDefiningOp());
1283   assert(sliceOp && "slice must be a sliceOp");
1284   if (sliceOp.getFields().empty())
1285     return arrayElementToExtendedValue(builder, loc, array, element);
1286   // For F95, using componentToExtendedValue will work, but when PDTs are
1287   // lowered. It will be required to go down the slice to propagate the length
1288   // parameters.
1289   return fir::factory::componentToExtendedValue(builder, loc, element);
1290 }
1291 
1292 void fir::factory::genScalarAssignment(fir::FirOpBuilder &builder,
1293                                        mlir::Location loc,
1294                                        const fir::ExtendedValue &lhs,
1295                                        const fir::ExtendedValue &rhs,
1296                                        bool needFinalization,
1297                                        bool isTemporaryLHS) {
1298   assert(lhs.rank() == 0 && rhs.rank() == 0 && "must be scalars");
1299   auto type = fir::unwrapSequenceType(
1300       fir::unwrapPassByRefType(fir::getBase(lhs).getType()));
1301   if (mlir::isa<fir::CharacterType>(type)) {
1302     const fir::CharBoxValue *toChar = lhs.getCharBox();
1303     const fir::CharBoxValue *fromChar = rhs.getCharBox();
1304     assert(toChar && fromChar);
1305     fir::factory::CharacterExprHelper helper{builder, loc};
1306     helper.createAssign(fir::ExtendedValue{*toChar},
1307                         fir::ExtendedValue{*fromChar});
1308   } else if (mlir::isa<fir::RecordType>(type)) {
1309     fir::factory::genRecordAssignment(builder, loc, lhs, rhs, needFinalization,
1310                                       isTemporaryLHS);
1311   } else {
1312     assert(!fir::hasDynamicSize(type));
1313     auto rhsVal = fir::getBase(rhs);
1314     if (fir::isa_ref_type(rhsVal.getType()))
1315       rhsVal = builder.create<fir::LoadOp>(loc, rhsVal);
1316     mlir::Value lhsAddr = fir::getBase(lhs);
1317     rhsVal = builder.createConvert(loc, fir::unwrapRefType(lhsAddr.getType()),
1318                                    rhsVal);
1319     builder.create<fir::StoreOp>(loc, rhsVal, lhsAddr);
1320   }
1321 }
1322 
1323 static void genComponentByComponentAssignment(fir::FirOpBuilder &builder,
1324                                               mlir::Location loc,
1325                                               const fir::ExtendedValue &lhs,
1326                                               const fir::ExtendedValue &rhs,
1327                                               bool isTemporaryLHS) {
1328   auto lbaseType = fir::unwrapPassByRefType(fir::getBase(lhs).getType());
1329   auto lhsType = mlir::dyn_cast<fir::RecordType>(lbaseType);
1330   assert(lhsType && "lhs must be a scalar record type");
1331   auto rbaseType = fir::unwrapPassByRefType(fir::getBase(rhs).getType());
1332   auto rhsType = mlir::dyn_cast<fir::RecordType>(rbaseType);
1333   assert(rhsType && "rhs must be a scalar record type");
1334   auto fieldIndexType = fir::FieldType::get(lhsType.getContext());
1335   for (auto [lhsPair, rhsPair] :
1336        llvm::zip(lhsType.getTypeList(), rhsType.getTypeList())) {
1337     auto &[lFieldName, lFieldTy] = lhsPair;
1338     auto &[rFieldName, rFieldTy] = rhsPair;
1339     assert(!fir::hasDynamicSize(lFieldTy) && !fir::hasDynamicSize(rFieldTy));
1340     mlir::Value rField = builder.create<fir::FieldIndexOp>(
1341         loc, fieldIndexType, rFieldName, rhsType, fir::getTypeParams(rhs));
1342     auto rFieldRefType = builder.getRefType(rFieldTy);
1343     mlir::Value fromCoor = builder.create<fir::CoordinateOp>(
1344         loc, rFieldRefType, fir::getBase(rhs), rField);
1345     mlir::Value field = builder.create<fir::FieldIndexOp>(
1346         loc, fieldIndexType, lFieldName, lhsType, fir::getTypeParams(lhs));
1347     auto fieldRefType = builder.getRefType(lFieldTy);
1348     mlir::Value toCoor = builder.create<fir::CoordinateOp>(
1349         loc, fieldRefType, fir::getBase(lhs), field);
1350     std::optional<fir::DoLoopOp> outerLoop;
1351     if (auto sequenceType = mlir::dyn_cast<fir::SequenceType>(lFieldTy)) {
1352       // Create loops to assign array components elements by elements.
1353       // Note that, since these are components, they either do not overlap,
1354       // or are the same and exactly overlap. They also have compile time
1355       // constant shapes.
1356       mlir::Type idxTy = builder.getIndexType();
1357       llvm::SmallVector<mlir::Value> indices;
1358       mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0);
1359       mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1);
1360       for (auto extent : llvm::reverse(sequenceType.getShape())) {
1361         // TODO: add zero size test !
1362         mlir::Value ub = builder.createIntegerConstant(loc, idxTy, extent - 1);
1363         auto loop = builder.create<fir::DoLoopOp>(loc, zero, ub, one);
1364         if (!outerLoop)
1365           outerLoop = loop;
1366         indices.push_back(loop.getInductionVar());
1367         builder.setInsertionPointToStart(loop.getBody());
1368       }
1369       // Set indices in column-major order.
1370       std::reverse(indices.begin(), indices.end());
1371       auto elementRefType = builder.getRefType(sequenceType.getEleTy());
1372       toCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, toCoor,
1373                                                  indices);
1374       fromCoor = builder.create<fir::CoordinateOp>(loc, elementRefType,
1375                                                    fromCoor, indices);
1376     }
1377     if (auto fieldEleTy = fir::unwrapSequenceType(lFieldTy);
1378         mlir::isa<fir::BaseBoxType>(fieldEleTy)) {
1379       assert(mlir::isa<fir::PointerType>(
1380                  mlir::cast<fir::BaseBoxType>(fieldEleTy).getEleTy()) &&
1381              "allocatable members require deep copy");
1382       auto fromPointerValue = builder.create<fir::LoadOp>(loc, fromCoor);
1383       auto castTo = builder.createConvert(loc, fieldEleTy, fromPointerValue);
1384       builder.create<fir::StoreOp>(loc, castTo, toCoor);
1385     } else {
1386       auto from =
1387           fir::factory::componentToExtendedValue(builder, loc, fromCoor);
1388       auto to = fir::factory::componentToExtendedValue(builder, loc, toCoor);
1389       // If LHS finalization is needed it is expected to be done
1390       // for the parent record, so that component-by-component
1391       // assignments may avoid finalization calls.
1392       fir::factory::genScalarAssignment(builder, loc, to, from,
1393                                         /*needFinalization=*/false,
1394                                         isTemporaryLHS);
1395     }
1396     if (outerLoop)
1397       builder.setInsertionPointAfter(*outerLoop);
1398   }
1399 }
1400 
1401 /// Can the assignment of this record type be implement with a simple memory
1402 /// copy (it requires no deep copy or user defined assignment of components )?
1403 static bool recordTypeCanBeMemCopied(fir::RecordType recordType) {
1404   // c_devptr type is a special case. It has a nested c_ptr field but we know it
1405   // can be copied directly.
1406   if (fir::isa_builtin_c_devptr_type(recordType))
1407     return true;
1408   if (fir::hasDynamicSize(recordType))
1409     return false;
1410   for (auto [_, fieldType] : recordType.getTypeList()) {
1411     // Derived type component may have user assignment (so far, we cannot tell
1412     // in FIR, so assume it is always the case, TODO: get the actual info).
1413     if (mlir::isa<fir::RecordType>(fir::unwrapSequenceType(fieldType)) &&
1414         !fir::isa_builtin_c_devptr_type(fir::unwrapSequenceType(fieldType)))
1415       return false;
1416     // Allocatable components need deep copy.
1417     if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(fieldType))
1418       if (mlir::isa<fir::HeapType>(boxType.getEleTy()))
1419         return false;
1420   }
1421   // Constant size components without user defined assignment and pointers can
1422   // be memcopied.
1423   return true;
1424 }
1425 
1426 static bool mayHaveFinalizer(fir::RecordType recordType,
1427                              fir::FirOpBuilder &builder) {
1428   if (auto typeInfo = builder.getModule().lookupSymbol<fir::TypeInfoOp>(
1429           recordType.getName()))
1430     return !typeInfo.getNoFinal();
1431   // No info, be pessimistic.
1432   return true;
1433 }
1434 
1435 void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder,
1436                                        mlir::Location loc,
1437                                        const fir::ExtendedValue &lhs,
1438                                        const fir::ExtendedValue &rhs,
1439                                        bool needFinalization,
1440                                        bool isTemporaryLHS) {
1441   assert(lhs.rank() == 0 && rhs.rank() == 0 && "assume scalar assignment");
1442   auto baseTy = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(lhs).getType());
1443   assert(baseTy && "must be a memory type");
1444   // Box operands may be polymorphic, it is not entirely clear from 10.2.1.3
1445   // if the assignment is performed on the dynamic of declared type. Use the
1446   // runtime assuming it is performed on the dynamic type.
1447   bool hasBoxOperands =
1448       mlir::isa<fir::BaseBoxType>(fir::getBase(lhs).getType()) ||
1449       mlir::isa<fir::BaseBoxType>(fir::getBase(rhs).getType());
1450   auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy);
1451   assert(recTy && "must be a record type");
1452   if ((needFinalization && mayHaveFinalizer(recTy, builder)) ||
1453       hasBoxOperands || !recordTypeCanBeMemCopied(recTy)) {
1454     auto to = fir::getBase(builder.createBox(loc, lhs));
1455     auto from = fir::getBase(builder.createBox(loc, rhs));
1456     // The runtime entry point may modify the LHS descriptor if it is
1457     // an allocatable. Allocatable assignment is handle elsewhere in lowering,
1458     // so just create a fir.ref<fir.box<>> from the fir.box to comply with the
1459     // runtime interface, but assume the fir.box is unchanged.
1460     // TODO: does this holds true with polymorphic entities ?
1461     auto toMutableBox = builder.createTemporary(loc, to.getType());
1462     builder.create<fir::StoreOp>(loc, to, toMutableBox);
1463     if (isTemporaryLHS)
1464       fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from);
1465     else
1466       fir::runtime::genAssign(builder, loc, toMutableBox, from);
1467     return;
1468   }
1469 
1470   // Otherwise, the derived type has compile time constant size and for which
1471   // the component by component assignment can be replaced by a memory copy.
1472   // Since we do not know the size of the derived type in lowering, do a
1473   // component by component assignment. Note that a single fir.load/fir.store
1474   // could be used on "small" record types, but as the type size grows, this
1475   // leads to issues in LLVM (long compile times, long IR files, and even
1476   // asserts at some point). Since there is no good size boundary, just always
1477   // use component by component assignment here.
1478   genComponentByComponentAssignment(builder, loc, lhs, rhs, isTemporaryLHS);
1479 }
1480 
1481 mlir::TupleType
1482 fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) {
1483   mlir::IntegerType i64Ty = builder.getIntegerType(64);
1484   auto arrTy = fir::SequenceType::get(builder.getIntegerType(8), 1);
1485   auto buffTy = fir::HeapType::get(arrTy);
1486   auto extTy = fir::SequenceType::get(i64Ty, 1);
1487   auto shTy = fir::HeapType::get(extTy);
1488   return mlir::TupleType::get(builder.getContext(), {i64Ty, buffTy, shTy});
1489 }
1490 
1491 mlir::Value fir::factory::genLenOfCharacter(
1492     fir::FirOpBuilder &builder, mlir::Location loc, fir::ArrayLoadOp arrLoad,
1493     llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
1494   llvm::SmallVector<mlir::Value> typeParams(arrLoad.getTypeparams());
1495   return genLenOfCharacter(builder, loc,
1496                            mlir::cast<fir::SequenceType>(arrLoad.getType()),
1497                            arrLoad.getMemref(), typeParams, path, substring);
1498 }
1499 
1500 mlir::Value fir::factory::genLenOfCharacter(
1501     fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy,
1502     mlir::Value memref, llvm::ArrayRef<mlir::Value> typeParams,
1503     llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) {
1504   auto idxTy = builder.getIndexType();
1505   auto zero = builder.createIntegerConstant(loc, idxTy, 0);
1506   auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) {
1507     auto diff = builder.create<mlir::arith::SubIOp>(loc, upper, lower);
1508     auto one = builder.createIntegerConstant(loc, idxTy, 1);
1509     auto size = builder.create<mlir::arith::AddIOp>(loc, diff, one);
1510     auto cmp = builder.create<mlir::arith::CmpIOp>(
1511         loc, mlir::arith::CmpIPredicate::sgt, size, zero);
1512     return builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero);
1513   };
1514   if (substring.size() == 2) {
1515     auto upper = builder.createConvert(loc, idxTy, substring.back());
1516     auto lower = builder.createConvert(loc, idxTy, substring.front());
1517     return saturatedDiff(lower, upper);
1518   }
1519   auto lower = zero;
1520   if (substring.size() == 1)
1521     lower = builder.createConvert(loc, idxTy, substring.front());
1522   auto eleTy = fir::applyPathToType(seqTy, path);
1523   if (!fir::hasDynamicSize(eleTy)) {
1524     if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) {
1525       // Use LEN from the type.
1526       return builder.createIntegerConstant(loc, idxTy, charTy.getLen());
1527     }
1528     // Do we need to support !fir.array<!fir.char<k,n>>?
1529     fir::emitFatalError(loc,
1530                         "application of path did not result in a !fir.char");
1531   }
1532   if (fir::isa_box_type(memref.getType())) {
1533     if (mlir::isa<fir::BoxCharType>(memref.getType()))
1534       return builder.create<fir::BoxCharLenOp>(loc, idxTy, memref);
1535     if (mlir::isa<fir::BoxType>(memref.getType()))
1536       return CharacterExprHelper(builder, loc).readLengthFromBox(memref);
1537     fir::emitFatalError(loc, "memref has wrong type");
1538   }
1539   if (typeParams.empty()) {
1540     fir::emitFatalError(loc, "array_load must have typeparams");
1541   }
1542   if (fir::isa_char(seqTy.getEleTy())) {
1543     assert(typeParams.size() == 1 && "too many typeparams");
1544     return typeParams.front();
1545   }
1546   TODO(loc, "LEN of character must be computed at runtime");
1547 }
1548 
1549 mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder,
1550                                           mlir::Location loc, mlir::Type type) {
1551   mlir::Type i1 = builder.getIntegerType(1);
1552   if (mlir::isa<fir::LogicalType>(type) || type == i1)
1553     return builder.createConvert(loc, type, builder.createBool(loc, false));
1554   if (fir::isa_integer(type))
1555     return builder.createIntegerConstant(loc, type, 0);
1556   if (fir::isa_real(type))
1557     return builder.createRealZeroConstant(loc, type);
1558   if (fir::isa_complex(type)) {
1559     fir::factory::Complex complexHelper(builder, loc);
1560     mlir::Type partType = complexHelper.getComplexPartType(type);
1561     mlir::Value zeroPart = builder.createRealZeroConstant(loc, partType);
1562     return complexHelper.createComplex(type, zeroPart, zeroPart);
1563   }
1564   fir::emitFatalError(loc, "internal: trying to generate zero value of non "
1565                            "numeric or logical type");
1566 }
1567 
1568 std::optional<std::int64_t>
1569 fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub,
1570                                    mlir::Value stride) {
1571   std::function<std::optional<std::int64_t>(mlir::Value)> getConstantValue =
1572       [&](mlir::Value value) -> std::optional<std::int64_t> {
1573     if (auto valInt = fir::getIntIfConstant(value))
1574       return *valInt;
1575     auto *definingOp = value.getDefiningOp();
1576     if (mlir::isa_and_nonnull<fir::ConvertOp>(definingOp)) {
1577       auto valOp = mlir::dyn_cast<fir::ConvertOp>(definingOp);
1578       return getConstantValue(valOp.getValue());
1579     }
1580     return {};
1581   };
1582   if (auto lbInt = getConstantValue(lb)) {
1583     if (auto ubInt = getConstantValue(ub)) {
1584       if (auto strideInt = getConstantValue(stride)) {
1585         if (*strideInt != 0) {
1586           std::int64_t extent = 1 + (*ubInt - *lbInt) / *strideInt;
1587           if (extent > 0)
1588             return extent;
1589         }
1590       }
1591     }
1592   }
1593   return {};
1594 }
1595 
1596 mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder,
1597                                          mlir::Location loc,
1598                                          mlir::Value value) {
1599   mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0);
1600   if (mlir::Operation *definingOp = value.getDefiningOp())
1601     if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp))
1602       if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(cst.getValue()))
1603         return intAttr.getInt() > 0 ? value : zero;
1604   mlir::Value valueIsGreater = builder.create<mlir::arith::CmpIOp>(
1605       loc, mlir::arith::CmpIPredicate::sgt, value, zero);
1606   return builder.create<mlir::arith::SelectOp>(loc, valueIsGreater, value,
1607                                                zero);
1608 }
1609 
1610 static std::pair<mlir::Value, mlir::Type>
1611 genCPtrOrCFunptrFieldIndex(fir::FirOpBuilder &builder, mlir::Location loc,
1612                            mlir::Type cptrTy) {
1613   auto recTy = mlir::cast<fir::RecordType>(cptrTy);
1614   assert(recTy.getTypeList().size() == 1);
1615   auto addrFieldName = recTy.getTypeList()[0].first;
1616   mlir::Type addrFieldTy = recTy.getTypeList()[0].second;
1617   auto fieldIndexType = fir::FieldType::get(cptrTy.getContext());
1618   mlir::Value addrFieldIndex = builder.create<fir::FieldIndexOp>(
1619       loc, fieldIndexType, addrFieldName, recTy,
1620       /*typeParams=*/mlir::ValueRange{});
1621   return {addrFieldIndex, addrFieldTy};
1622 }
1623 
1624 mlir::Value fir::factory::genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder,
1625                                                mlir::Location loc,
1626                                                mlir::Value cPtr,
1627                                                mlir::Type ty) {
1628   auto [addrFieldIndex, addrFieldTy] =
1629       genCPtrOrCFunptrFieldIndex(builder, loc, ty);
1630   return builder.create<fir::CoordinateOp>(loc, builder.getRefType(addrFieldTy),
1631                                            cPtr, addrFieldIndex);
1632 }
1633 
1634 mlir::Value fir::factory::genCDevPtrAddr(fir::FirOpBuilder &builder,
1635                                          mlir::Location loc,
1636                                          mlir::Value cDevPtr, mlir::Type ty) {
1637   auto recTy = mlir::cast<fir::RecordType>(ty);
1638   assert(recTy.getTypeList().size() == 1);
1639   auto cptrFieldName = recTy.getTypeList()[0].first;
1640   mlir::Type cptrFieldTy = recTy.getTypeList()[0].second;
1641   auto fieldIndexType = fir::FieldType::get(ty.getContext());
1642   mlir::Value cptrFieldIndex = builder.create<fir::FieldIndexOp>(
1643       loc, fieldIndexType, cptrFieldName, recTy,
1644       /*typeParams=*/mlir::ValueRange{});
1645   auto cptrCoord = builder.create<fir::CoordinateOp>(
1646       loc, builder.getRefType(cptrFieldTy), cDevPtr, cptrFieldIndex);
1647   auto [addrFieldIndex, addrFieldTy] =
1648       genCPtrOrCFunptrFieldIndex(builder, loc, cptrFieldTy);
1649   return builder.create<fir::CoordinateOp>(loc, builder.getRefType(addrFieldTy),
1650                                            cptrCoord, addrFieldIndex);
1651 }
1652 
1653 mlir::Value fir::factory::genCPtrOrCFunptrValue(fir::FirOpBuilder &builder,
1654                                                 mlir::Location loc,
1655                                                 mlir::Value cPtr) {
1656   mlir::Type cPtrTy = fir::unwrapRefType(cPtr.getType());
1657   if (fir::isa_builtin_cdevptr_type(cPtrTy)) {
1658     // Unwrap c_ptr from c_devptr.
1659     auto [addrFieldIndex, addrFieldTy] =
1660         genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy);
1661     mlir::Value cPtrCoor;
1662     if (fir::isa_ref_type(cPtr.getType())) {
1663       cPtrCoor = builder.create<fir::CoordinateOp>(
1664           loc, builder.getRefType(addrFieldTy), cPtr, addrFieldIndex);
1665     } else {
1666       auto arrayAttr = builder.getArrayAttr(
1667           {builder.getIntegerAttr(builder.getIndexType(), 0)});
1668       cPtrCoor = builder.create<fir::ExtractValueOp>(loc, addrFieldTy, cPtr,
1669                                                      arrayAttr);
1670     }
1671     return genCPtrOrCFunptrValue(builder, loc, cPtrCoor);
1672   }
1673 
1674   if (fir::isa_ref_type(cPtr.getType())) {
1675     mlir::Value cPtrAddr =
1676         fir::factory::genCPtrOrCFunptrAddr(builder, loc, cPtr, cPtrTy);
1677     return builder.create<fir::LoadOp>(loc, cPtrAddr);
1678   }
1679   auto [addrFieldIndex, addrFieldTy] =
1680       genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy);
1681   auto arrayAttr =
1682       builder.getArrayAttr({builder.getIntegerAttr(builder.getIndexType(), 0)});
1683   return builder.create<fir::ExtractValueOp>(loc, addrFieldTy, cPtr, arrayAttr);
1684 }
1685 
1686 fir::BoxValue fir::factory::createBoxValue(fir::FirOpBuilder &builder,
1687                                            mlir::Location loc,
1688                                            const fir::ExtendedValue &exv) {
1689   if (auto *boxValue = exv.getBoxOf<fir::BoxValue>())
1690     return *boxValue;
1691   mlir::Value box = builder.createBox(loc, exv);
1692   llvm::SmallVector<mlir::Value> lbounds;
1693   llvm::SmallVector<mlir::Value> explicitTypeParams;
1694   exv.match(
1695       [&](const fir::ArrayBoxValue &box) {
1696         lbounds.append(box.getLBounds().begin(), box.getLBounds().end());
1697       },
1698       [&](const fir::CharArrayBoxValue &box) {
1699         lbounds.append(box.getLBounds().begin(), box.getLBounds().end());
1700         explicitTypeParams.emplace_back(box.getLen());
1701       },
1702       [&](const fir::CharBoxValue &box) {
1703         explicitTypeParams.emplace_back(box.getLen());
1704       },
1705       [&](const fir::MutableBoxValue &x) {
1706         if (x.rank() > 0) {
1707           // The resulting box lbounds must be coming from the mutable box.
1708           fir::ExtendedValue boxVal =
1709               fir::factory::genMutableBoxRead(builder, loc, x);
1710           // Make sure we do not recurse infinitely.
1711           if (boxVal.getBoxOf<fir::MutableBoxValue>())
1712             fir::emitFatalError(loc, "mutable box read cannot be mutable box");
1713           fir::BoxValue box =
1714               fir::factory::createBoxValue(builder, loc, boxVal);
1715           lbounds.append(box.getLBounds().begin(), box.getLBounds().end());
1716         }
1717         explicitTypeParams.append(x.nonDeferredLenParams().begin(),
1718                                   x.nonDeferredLenParams().end());
1719       },
1720       [](const auto &) {});
1721   return fir::BoxValue(box, lbounds, explicitTypeParams);
1722 }
1723 
1724 mlir::Value fir::factory::createNullBoxProc(fir::FirOpBuilder &builder,
1725                                             mlir::Location loc,
1726                                             mlir::Type boxType) {
1727   auto boxTy{mlir::dyn_cast<fir::BoxProcType>(boxType)};
1728   if (!boxTy)
1729     fir::emitFatalError(loc, "Procedure pointer must be of BoxProcType");
1730   auto boxEleTy{fir::unwrapRefType(boxTy.getEleTy())};
1731   mlir::Value initVal{builder.create<fir::ZeroOp>(loc, boxEleTy)};
1732   return builder.create<fir::EmboxProcOp>(loc, boxTy, initVal);
1733 }
1734 
1735 void fir::factory::setInternalLinkage(mlir::func::FuncOp func) {
1736   auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal;
1737   auto linkage =
1738       mlir::LLVM::LinkageAttr::get(func->getContext(), internalLinkage);
1739   func->setAttr("llvm.linkage", linkage);
1740 }
1741 
1742 uint64_t fir::factory::getAllocaAddressSpace(mlir::DataLayout *dataLayout) {
1743   if (dataLayout)
1744     if (mlir::Attribute addrSpace = dataLayout->getAllocaMemorySpace())
1745       return mlir::cast<mlir::IntegerAttr>(addrSpace).getUInt();
1746   return 0;
1747 }
1748 
1749 llvm::SmallVector<mlir::Value>
1750 fir::factory::deduceOptimalExtents(mlir::ValueRange extents1,
1751                                    mlir::ValueRange extents2) {
1752   llvm::SmallVector<mlir::Value> extents;
1753   extents.reserve(extents1.size());
1754   for (auto [extent1, extent2] : llvm::zip(extents1, extents2)) {
1755     if (!fir::getIntIfConstant(extent1) && fir::getIntIfConstant(extent2))
1756       extents.push_back(extent2);
1757     else
1758       extents.push_back(extent1);
1759   }
1760   return extents;
1761 }
1762 
1763 llvm::SmallVector<mlir::Value> fir::factory::updateRuntimeExtentsForEmptyArrays(
1764     fir::FirOpBuilder &builder, mlir::Location loc, mlir::ValueRange extents) {
1765   if (extents.size() <= 1)
1766     return extents;
1767 
1768   mlir::Type i1Type = builder.getI1Type();
1769   mlir::Value isEmpty = createZeroValue(builder, loc, i1Type);
1770 
1771   llvm::SmallVector<mlir::Value, Fortran::common::maxRank> zeroes;
1772   for (mlir::Value extent : extents) {
1773     mlir::Type type = extent.getType();
1774     mlir::Value zero = createZeroValue(builder, loc, type);
1775     zeroes.push_back(zero);
1776     mlir::Value isZero = builder.create<mlir::arith::CmpIOp>(
1777         loc, mlir::arith::CmpIPredicate::eq, extent, zero);
1778     isEmpty = builder.create<mlir::arith::OrIOp>(loc, isEmpty, isZero);
1779   }
1780 
1781   llvm::SmallVector<mlir::Value, Fortran::common::maxRank> newExtents;
1782   for (auto [zero, extent] : llvm::zip_equal(zeroes, extents)) {
1783     newExtents.push_back(
1784         builder.create<mlir::arith::SelectOp>(loc, isEmpty, zero, extent));
1785   }
1786   return newExtents;
1787 }
1788