xref: /llvm-project/flang/lib/Optimizer/CodeGen/CodeGen.cpp (revision 0b80491cd5e7dcb6d5432727d0a2c746a9a9438e)
1 //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
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/Optimizer/CodeGen/CodeGen.h"
14 
15 #include "flang/Optimizer/CodeGen/CGOps.h"
16 #include "flang/Optimizer/CodeGen/CodeGenOpenMP.h"
17 #include "flang/Optimizer/CodeGen/FIROpPatterns.h"
18 #include "flang/Optimizer/CodeGen/TypeConverter.h"
19 #include "flang/Optimizer/Dialect/FIRAttr.h"
20 #include "flang/Optimizer/Dialect/FIROps.h"
21 #include "flang/Optimizer/Dialect/FIRType.h"
22 #include "flang/Optimizer/Support/DataLayout.h"
23 #include "flang/Optimizer/Support/InternalNames.h"
24 #include "flang/Optimizer/Support/TypeCode.h"
25 #include "flang/Optimizer/Support/Utils.h"
26 #include "flang/Runtime/CUDA/descriptor.h"
27 #include "flang/Runtime/CUDA/memory.h"
28 #include "flang/Runtime/allocator-registry-consts.h"
29 #include "flang/Runtime/descriptor-consts.h"
30 #include "flang/Semantics/runtime-type-info.h"
31 #include "mlir/Conversion/ArithCommon/AttrToLLVMConverter.h"
32 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
33 #include "mlir/Conversion/ComplexToLLVM/ComplexToLLVM.h"
34 #include "mlir/Conversion/ComplexToStandard/ComplexToStandard.h"
35 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
36 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
37 #include "mlir/Conversion/LLVMCommon/Pattern.h"
38 #include "mlir/Conversion/MathToFuncs/MathToFuncs.h"
39 #include "mlir/Conversion/MathToLLVM/MathToLLVM.h"
40 #include "mlir/Conversion/MathToLibm/MathToLibm.h"
41 #include "mlir/Conversion/MathToROCDL/MathToROCDL.h"
42 #include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
43 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h"
44 #include "mlir/Dialect/Arith/IR/Arith.h"
45 #include "mlir/Dialect/DLTI/DLTI.h"
46 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
47 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
48 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
49 #include "mlir/Dialect/LLVMIR/Transforms/AddComdats.h"
50 #include "mlir/Dialect/OpenACC/OpenACC.h"
51 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
52 #include "mlir/IR/BuiltinTypes.h"
53 #include "mlir/IR/Matchers.h"
54 #include "mlir/Pass/Pass.h"
55 #include "mlir/Pass/PassManager.h"
56 #include "mlir/Target/LLVMIR/Import.h"
57 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
58 #include "llvm/ADT/ArrayRef.h"
59 #include "llvm/ADT/TypeSwitch.h"
60 
61 namespace fir {
62 #define GEN_PASS_DEF_FIRTOLLVMLOWERING
63 #include "flang/Optimizer/CodeGen/CGPasses.h.inc"
64 } // namespace fir
65 
66 #define DEBUG_TYPE "flang-codegen"
67 
68 // TODO: This should really be recovered from the specified target.
69 static constexpr unsigned defaultAlign = 8;
70 
71 /// `fir.box` attribute values as defined for CFI_attribute_t in
72 /// flang/ISO_Fortran_binding.h.
73 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
74 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
75 
76 static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,
77                                         unsigned addressSpace = 0) {
78   return mlir::LLVM::LLVMPointerType::get(context, addressSpace);
79 }
80 
81 static inline mlir::Type getI8Type(mlir::MLIRContext *context) {
82   return mlir::IntegerType::get(context, 8);
83 }
84 
85 static mlir::LLVM::ConstantOp
86 genConstantIndex(mlir::Location loc, mlir::Type ity,
87                  mlir::ConversionPatternRewriter &rewriter,
88                  std::int64_t offset) {
89   auto cattr = rewriter.getI64IntegerAttr(offset);
90   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
91 }
92 
93 static mlir::Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
94                                 mlir::Block *insertBefore) {
95   assert(insertBefore && "expected valid insertion block");
96   return rewriter.createBlock(insertBefore->getParent(),
97                               mlir::Region::iterator(insertBefore));
98 }
99 
100 /// Extract constant from a value that must be the result of one of the
101 /// ConstantOp operations.
102 static int64_t getConstantIntValue(mlir::Value val) {
103   if (auto constVal = fir::getIntIfConstant(val))
104     return *constVal;
105   fir::emitFatalError(val.getLoc(), "must be a constant");
106 }
107 
108 static unsigned getTypeDescFieldId(mlir::Type ty) {
109   auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty));
110   return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
111 }
112 static unsigned getLenParamFieldId(mlir::Type ty) {
113   return getTypeDescFieldId(ty) + 1;
114 }
115 
116 static llvm::SmallVector<mlir::NamedAttribute>
117 addLLVMOpBundleAttrs(mlir::ConversionPatternRewriter &rewriter,
118                      llvm::ArrayRef<mlir::NamedAttribute> attrs,
119                      int32_t numCallOperands) {
120   llvm::SmallVector<mlir::NamedAttribute> newAttrs;
121   newAttrs.reserve(attrs.size() + 2);
122 
123   for (mlir::NamedAttribute attr : attrs) {
124     if (attr.getName() != "operandSegmentSizes")
125       newAttrs.push_back(attr);
126   }
127 
128   newAttrs.push_back(rewriter.getNamedAttr(
129       "operandSegmentSizes",
130       rewriter.getDenseI32ArrayAttr({numCallOperands, 0})));
131   newAttrs.push_back(rewriter.getNamedAttr("op_bundle_sizes",
132                                            rewriter.getDenseI32ArrayAttr({})));
133   return newAttrs;
134 }
135 
136 namespace {
137 /// Lower `fir.address_of` operation to `llvm.address_of` operation.
138 struct AddrOfOpConversion : public fir::FIROpConversion<fir::AddrOfOp> {
139   using FIROpConversion::FIROpConversion;
140 
141   llvm::LogicalResult
142   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
143                   mlir::ConversionPatternRewriter &rewriter) const override {
144     auto ty = convertType(addr.getType());
145     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
146         addr, ty, addr.getSymbol().getRootReference().getValue());
147     return mlir::success();
148   }
149 };
150 } // namespace
151 
152 /// Lookup the function to compute the memory size of this parametric derived
153 /// type. The size of the object may depend on the LEN type parameters of the
154 /// derived type.
155 static mlir::LLVM::LLVMFuncOp
156 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
157                           mlir::ConversionPatternRewriter &rewriter) {
158   auto module = op->getParentOfType<mlir::ModuleOp>();
159   std::string name = recTy.getName().str() + "P.mem.size";
160   if (auto memSizeFunc = module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name))
161     return memSizeFunc;
162   TODO(op.getLoc(), "did not find allocation function");
163 }
164 
165 // Compute the alloc scale size (constant factors encoded in the array type).
166 // We do this for arrays without a constant interior or arrays of character with
167 // dynamic length arrays, since those are the only ones that get decayed to a
168 // pointer to the element type.
169 template <typename OP>
170 static mlir::Value
171 genAllocationScaleSize(OP op, mlir::Type ity,
172                        mlir::ConversionPatternRewriter &rewriter) {
173   mlir::Location loc = op.getLoc();
174   mlir::Type dataTy = op.getInType();
175   auto seqTy = mlir::dyn_cast<fir::SequenceType>(dataTy);
176   fir::SequenceType::Extent constSize = 1;
177   if (seqTy) {
178     int constRows = seqTy.getConstantRows();
179     const fir::SequenceType::ShapeRef &shape = seqTy.getShape();
180     if (constRows != static_cast<int>(shape.size())) {
181       for (auto extent : shape) {
182         if (constRows-- > 0)
183           continue;
184         if (extent != fir::SequenceType::getUnknownExtent())
185           constSize *= extent;
186       }
187     }
188   }
189 
190   if (constSize != 1) {
191     mlir::Value constVal{
192         genConstantIndex(loc, ity, rewriter, constSize).getResult()};
193     return constVal;
194   }
195   return nullptr;
196 }
197 
198 namespace {
199 struct DeclareOpConversion : public fir::FIROpConversion<fir::cg::XDeclareOp> {
200 public:
201   using FIROpConversion::FIROpConversion;
202   llvm::LogicalResult
203   matchAndRewrite(fir::cg::XDeclareOp declareOp, OpAdaptor adaptor,
204                   mlir::ConversionPatternRewriter &rewriter) const override {
205     auto memRef = adaptor.getOperands()[0];
206     if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(declareOp.getLoc())) {
207       if (auto varAttr =
208               mlir::dyn_cast_or_null<mlir::LLVM::DILocalVariableAttr>(
209                   fusedLoc.getMetadata())) {
210         rewriter.create<mlir::LLVM::DbgDeclareOp>(memRef.getLoc(), memRef,
211                                                   varAttr, nullptr);
212       }
213     }
214     rewriter.replaceOp(declareOp, memRef);
215     return mlir::success();
216   }
217 };
218 } // namespace
219 
220 namespace {
221 /// convert to LLVM IR dialect `alloca`
222 struct AllocaOpConversion : public fir::FIROpConversion<fir::AllocaOp> {
223   using FIROpConversion::FIROpConversion;
224 
225   llvm::LogicalResult
226   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
227                   mlir::ConversionPatternRewriter &rewriter) const override {
228     mlir::ValueRange operands = adaptor.getOperands();
229     auto loc = alloc.getLoc();
230     mlir::Type ity = lowerTy().indexType();
231     unsigned i = 0;
232     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
233     mlir::Type firObjType = fir::unwrapRefType(alloc.getType());
234     mlir::Type llvmObjectType = convertObjectType(firObjType);
235     if (alloc.hasLenParams()) {
236       unsigned end = alloc.numLenParams();
237       llvm::SmallVector<mlir::Value> lenParams;
238       for (; i < end; ++i)
239         lenParams.push_back(operands[i]);
240       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
241       if (auto chrTy = mlir::dyn_cast<fir::CharacterType>(scalarType)) {
242         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
243             chrTy.getContext(), chrTy.getFKind());
244         llvmObjectType = convertType(rawCharTy);
245         assert(end == 1);
246         size = integerCast(loc, rewriter, ity, lenParams[0], /*fold=*/true);
247       } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(scalarType)) {
248         mlir::LLVM::LLVMFuncOp memSizeFn =
249             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
250         if (!memSizeFn)
251           emitError(loc, "did not find allocation function");
252         mlir::NamedAttribute attr = rewriter.getNamedAttr(
253             "callee", mlir::SymbolRefAttr::get(memSizeFn));
254         auto call = rewriter.create<mlir::LLVM::CallOp>(
255             loc, ity, lenParams,
256             addLLVMOpBundleAttrs(rewriter, {attr}, lenParams.size()));
257         size = call.getResult();
258         llvmObjectType = ::getI8Type(alloc.getContext());
259       } else {
260         return emitError(loc, "unexpected type ")
261                << scalarType << " with type parameters";
262       }
263     }
264     if (auto scaleSize = genAllocationScaleSize(alloc, ity, rewriter))
265       size =
266           rewriter.createOrFold<mlir::LLVM::MulOp>(loc, ity, size, scaleSize);
267     if (alloc.hasShapeOperands()) {
268       unsigned end = operands.size();
269       for (; i < end; ++i)
270         size = rewriter.createOrFold<mlir::LLVM::MulOp>(
271             loc, ity, size,
272             integerCast(loc, rewriter, ity, operands[i], /*fold=*/true));
273     }
274 
275     unsigned allocaAs = getAllocaAddressSpace(rewriter);
276     unsigned programAs = getProgramAddressSpace(rewriter);
277 
278     if (mlir::isa<mlir::LLVM::ConstantOp>(size.getDefiningOp())) {
279       // Set the Block in which the llvm alloca should be inserted.
280       mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
281       mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent();
282       mlir::Block *insertBlock =
283           getBlockForAllocaInsert(parentOp, parentRegion);
284 
285       // The old size might have had multiple users, some at a broader scope
286       // than we can safely outline the alloca to. As it is only an
287       // llvm.constant operation, it is faster to clone it than to calculate the
288       // dominance to see if it really should be moved.
289       mlir::Operation *clonedSize = rewriter.clone(*size.getDefiningOp());
290       size = clonedSize->getResult(0);
291       clonedSize->moveBefore(&insertBlock->front());
292       rewriter.setInsertionPointAfter(size.getDefiningOp());
293     }
294 
295     // NOTE: we used to pass alloc->getAttrs() in the builder for non opaque
296     // pointers! Only propagate pinned and bindc_name to help debugging, but
297     // this should have no functional purpose (and passing the operand segment
298     // attribute like before is certainly bad).
299     auto llvmAlloc = rewriter.create<mlir::LLVM::AllocaOp>(
300         loc, ::getLlvmPtrType(alloc.getContext(), allocaAs), llvmObjectType,
301         size);
302     if (alloc.getPinned())
303       llvmAlloc->setDiscardableAttr(alloc.getPinnedAttrName(),
304                                     alloc.getPinnedAttr());
305     if (alloc.getBindcName())
306       llvmAlloc->setDiscardableAttr(alloc.getBindcNameAttrName(),
307                                     alloc.getBindcNameAttr());
308     if (allocaAs == programAs) {
309       rewriter.replaceOp(alloc, llvmAlloc);
310     } else {
311       // if our allocation address space, is not the same as the program address
312       // space, then we must emit a cast to the program address space before
313       // use. An example case would be on AMDGPU, where the allocation address
314       // space is the numeric value 5 (private), and the program address space
315       // is 0 (generic).
316       rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(
317           alloc, ::getLlvmPtrType(alloc.getContext(), programAs), llvmAlloc);
318     }
319     return mlir::success();
320   }
321 };
322 } // namespace
323 
324 namespace {
325 /// Lower `fir.box_addr` to the sequence of operations to extract the first
326 /// element of the box.
327 struct BoxAddrOpConversion : public fir::FIROpConversion<fir::BoxAddrOp> {
328   using FIROpConversion::FIROpConversion;
329 
330   llvm::LogicalResult
331   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
332                   mlir::ConversionPatternRewriter &rewriter) const override {
333     mlir::Value a = adaptor.getOperands()[0];
334     auto loc = boxaddr.getLoc();
335     if (auto argty =
336             mlir::dyn_cast<fir::BaseBoxType>(boxaddr.getVal().getType())) {
337       TypePair boxTyPair = getBoxTypePair(argty);
338       rewriter.replaceOp(boxaddr,
339                          getBaseAddrFromBox(loc, boxTyPair, a, rewriter));
340     } else {
341       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, a, 0);
342     }
343     return mlir::success();
344   }
345 };
346 
347 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
348 /// boxchar.
349 struct BoxCharLenOpConversion : public fir::FIROpConversion<fir::BoxCharLenOp> {
350   using FIROpConversion::FIROpConversion;
351 
352   llvm::LogicalResult
353   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
354                   mlir::ConversionPatternRewriter &rewriter) const override {
355     mlir::Value boxChar = adaptor.getOperands()[0];
356     mlir::Location loc = boxChar.getLoc();
357     mlir::Type returnValTy = boxCharLen.getResult().getType();
358 
359     constexpr int boxcharLenIdx = 1;
360     auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, boxChar,
361                                                            boxcharLenIdx);
362     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
363     rewriter.replaceOp(boxCharLen, lenAfterCast);
364 
365     return mlir::success();
366   }
367 };
368 
369 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
370 /// dimension information from the boxed value.
371 /// Result in a triple set of GEPs and loads.
372 struct BoxDimsOpConversion : public fir::FIROpConversion<fir::BoxDimsOp> {
373   using FIROpConversion::FIROpConversion;
374 
375   llvm::LogicalResult
376   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
377                   mlir::ConversionPatternRewriter &rewriter) const override {
378     llvm::SmallVector<mlir::Type, 3> resultTypes = {
379         convertType(boxdims.getResult(0).getType()),
380         convertType(boxdims.getResult(1).getType()),
381         convertType(boxdims.getResult(2).getType()),
382     };
383     TypePair boxTyPair = getBoxTypePair(boxdims.getVal().getType());
384     auto results = getDimsFromBox(boxdims.getLoc(), resultTypes, boxTyPair,
385                                   adaptor.getOperands()[0],
386                                   adaptor.getOperands()[1], rewriter);
387     rewriter.replaceOp(boxdims, results);
388     return mlir::success();
389   }
390 };
391 
392 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
393 /// an element in the boxed value.
394 struct BoxEleSizeOpConversion : public fir::FIROpConversion<fir::BoxEleSizeOp> {
395   using FIROpConversion::FIROpConversion;
396 
397   llvm::LogicalResult
398   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
399                   mlir::ConversionPatternRewriter &rewriter) const override {
400     mlir::Value box = adaptor.getOperands()[0];
401     auto loc = boxelesz.getLoc();
402     auto ty = convertType(boxelesz.getType());
403     TypePair boxTyPair = getBoxTypePair(boxelesz.getVal().getType());
404     auto elemSize = getElementSizeFromBox(loc, ty, boxTyPair, box, rewriter);
405     rewriter.replaceOp(boxelesz, elemSize);
406     return mlir::success();
407   }
408 };
409 
410 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
411 /// boxed value was from an ALLOCATABLE entity.
412 struct BoxIsAllocOpConversion : public fir::FIROpConversion<fir::BoxIsAllocOp> {
413   using FIROpConversion::FIROpConversion;
414 
415   llvm::LogicalResult
416   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
417                   mlir::ConversionPatternRewriter &rewriter) const override {
418     mlir::Value box = adaptor.getOperands()[0];
419     auto loc = boxisalloc.getLoc();
420     TypePair boxTyPair = getBoxTypePair(boxisalloc.getVal().getType());
421     mlir::Value check =
422         genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrAllocatable);
423     rewriter.replaceOp(boxisalloc, check);
424     return mlir::success();
425   }
426 };
427 
428 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
429 /// boxed is an array.
430 struct BoxIsArrayOpConversion : public fir::FIROpConversion<fir::BoxIsArrayOp> {
431   using FIROpConversion::FIROpConversion;
432 
433   llvm::LogicalResult
434   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
435                   mlir::ConversionPatternRewriter &rewriter) const override {
436     mlir::Value a = adaptor.getOperands()[0];
437     auto loc = boxisarray.getLoc();
438     TypePair boxTyPair = getBoxTypePair(boxisarray.getVal().getType());
439     mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter);
440     mlir::Value c0 = genConstantIndex(loc, rank.getType(), rewriter, 0);
441     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
442         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
443     return mlir::success();
444   }
445 };
446 
447 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
448 /// boxed value was from a POINTER entity.
449 struct BoxIsPtrOpConversion : public fir::FIROpConversion<fir::BoxIsPtrOp> {
450   using FIROpConversion::FIROpConversion;
451 
452   llvm::LogicalResult
453   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
454                   mlir::ConversionPatternRewriter &rewriter) const override {
455     mlir::Value box = adaptor.getOperands()[0];
456     auto loc = boxisptr.getLoc();
457     TypePair boxTyPair = getBoxTypePair(boxisptr.getVal().getType());
458     mlir::Value check =
459         genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrPointer);
460     rewriter.replaceOp(boxisptr, check);
461     return mlir::success();
462   }
463 };
464 
465 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
466 /// the box.
467 struct BoxRankOpConversion : public fir::FIROpConversion<fir::BoxRankOp> {
468   using FIROpConversion::FIROpConversion;
469 
470   llvm::LogicalResult
471   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
472                   mlir::ConversionPatternRewriter &rewriter) const override {
473     mlir::Value a = adaptor.getOperands()[0];
474     auto loc = boxrank.getLoc();
475     mlir::Type ty = convertType(boxrank.getType());
476     TypePair boxTyPair =
477         getBoxTypePair(fir::unwrapRefType(boxrank.getBox().getType()));
478     mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter);
479     mlir::Value result = integerCast(loc, rewriter, ty, rank);
480     rewriter.replaceOp(boxrank, result);
481     return mlir::success();
482   }
483 };
484 
485 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
486 /// boxproc.
487 /// TODO: Part of supporting Fortran 2003 procedure pointers.
488 struct BoxProcHostOpConversion
489     : public fir::FIROpConversion<fir::BoxProcHostOp> {
490   using FIROpConversion::FIROpConversion;
491 
492   llvm::LogicalResult
493   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
494                   mlir::ConversionPatternRewriter &rewriter) const override {
495     TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
496     return mlir::failure();
497   }
498 };
499 
500 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
501 /// descriptor from the box.
502 struct BoxTypeDescOpConversion
503     : public fir::FIROpConversion<fir::BoxTypeDescOp> {
504   using FIROpConversion::FIROpConversion;
505 
506   llvm::LogicalResult
507   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
508                   mlir::ConversionPatternRewriter &rewriter) const override {
509     mlir::Value box = adaptor.getOperands()[0];
510     TypePair boxTyPair = getBoxTypePair(boxtypedesc.getBox().getType());
511     auto typeDescAddr =
512         loadTypeDescAddress(boxtypedesc.getLoc(), boxTyPair, box, rewriter);
513     rewriter.replaceOp(boxtypedesc, typeDescAddr);
514     return mlir::success();
515   }
516 };
517 
518 /// Lower `fir.box_typecode` to a sequence of operations to extract the type
519 /// code in the boxed value.
520 struct BoxTypeCodeOpConversion
521     : public fir::FIROpConversion<fir::BoxTypeCodeOp> {
522   using FIROpConversion::FIROpConversion;
523 
524   llvm::LogicalResult
525   matchAndRewrite(fir::BoxTypeCodeOp op, OpAdaptor adaptor,
526                   mlir::ConversionPatternRewriter &rewriter) const override {
527     mlir::Value box = adaptor.getOperands()[0];
528     auto loc = box.getLoc();
529     auto ty = convertType(op.getType());
530     TypePair boxTyPair = getBoxTypePair(op.getBox().getType());
531     auto typeCode =
532         getValueFromBox(loc, boxTyPair, box, ty, rewriter, kTypePosInBox);
533     rewriter.replaceOp(op, typeCode);
534     return mlir::success();
535   }
536 };
537 
538 /// Lower `fir.string_lit` to LLVM IR dialect operation.
539 struct StringLitOpConversion : public fir::FIROpConversion<fir::StringLitOp> {
540   using FIROpConversion::FIROpConversion;
541 
542   llvm::LogicalResult
543   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
544                   mlir::ConversionPatternRewriter &rewriter) const override {
545     auto ty = convertType(constop.getType());
546     auto attr = constop.getValue();
547     if (mlir::isa<mlir::StringAttr>(attr)) {
548       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
549       return mlir::success();
550     }
551 
552     auto charTy = mlir::cast<fir::CharacterType>(constop.getType());
553     unsigned bits = lowerTy().characterBitsize(charTy);
554     mlir::Type intTy = rewriter.getIntegerType(bits);
555     mlir::Location loc = constop.getLoc();
556     mlir::Value cst = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
557     if (auto arr = mlir::dyn_cast<mlir::DenseElementsAttr>(attr)) {
558       cst = rewriter.create<mlir::LLVM::ConstantOp>(loc, ty, arr);
559     } else if (auto arr = mlir::dyn_cast<mlir::ArrayAttr>(attr)) {
560       for (auto a : llvm::enumerate(arr.getValue())) {
561         // convert each character to a precise bitsize
562         auto elemAttr = mlir::IntegerAttr::get(
563             intTy,
564             mlir::cast<mlir::IntegerAttr>(a.value()).getValue().zextOrTrunc(
565                 bits));
566         auto elemCst =
567             rewriter.create<mlir::LLVM::ConstantOp>(loc, intTy, elemAttr);
568         cst = rewriter.create<mlir::LLVM::InsertValueOp>(loc, cst, elemCst,
569                                                          a.index());
570       }
571     } else {
572       return mlir::failure();
573     }
574     rewriter.replaceOp(constop, cst);
575     return mlir::success();
576   }
577 };
578 
579 /// `fir.call` -> `llvm.call`
580 struct CallOpConversion : public fir::FIROpConversion<fir::CallOp> {
581   using FIROpConversion::FIROpConversion;
582 
583   llvm::LogicalResult
584   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
585                   mlir::ConversionPatternRewriter &rewriter) const override {
586     llvm::SmallVector<mlir::Type> resultTys;
587     for (auto r : call.getResults())
588       resultTys.push_back(convertType(r.getType()));
589     // Convert arith::FastMathFlagsAttr to LLVM::FastMathFlagsAttr.
590     mlir::arith::AttrConvertFastMathToLLVM<fir::CallOp, mlir::LLVM::CallOp>
591         attrConvert(call);
592     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
593         call, resultTys, adaptor.getOperands(),
594         addLLVMOpBundleAttrs(rewriter, attrConvert.getAttrs(),
595                              adaptor.getOperands().size()));
596     return mlir::success();
597   }
598 };
599 } // namespace
600 
601 static mlir::Type getComplexEleTy(mlir::Type complex) {
602   return mlir::cast<mlir::ComplexType>(complex).getElementType();
603 }
604 
605 namespace {
606 /// Compare complex values
607 ///
608 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
609 ///
610 /// For completeness, all other comparison are done on the real component only.
611 struct CmpcOpConversion : public fir::FIROpConversion<fir::CmpcOp> {
612   using FIROpConversion::FIROpConversion;
613 
614   llvm::LogicalResult
615   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
616                   mlir::ConversionPatternRewriter &rewriter) const override {
617     mlir::ValueRange operands = adaptor.getOperands();
618     mlir::Type resTy = convertType(cmp.getType());
619     mlir::Location loc = cmp.getLoc();
620     mlir::LLVM::FastmathFlags fmf =
621         mlir::arith::convertArithFastMathFlagsToLLVM(cmp.getFastmath());
622     mlir::LLVM::FCmpPredicate pred =
623         static_cast<mlir::LLVM::FCmpPredicate>(cmp.getPredicate());
624     auto rcp = rewriter.create<mlir::LLVM::FCmpOp>(
625         loc, resTy, pred,
626         rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 0),
627         rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 0), fmf);
628     auto icp = rewriter.create<mlir::LLVM::FCmpOp>(
629         loc, resTy, pred,
630         rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 1),
631         rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 1), fmf);
632     llvm::SmallVector<mlir::Value, 2> cp = {rcp, icp};
633     switch (cmp.getPredicate()) {
634     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
635       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
636       break;
637     case mlir::arith::CmpFPredicate::UNE: // .NE.
638       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
639       break;
640     default:
641       rewriter.replaceOp(cmp, rcp.getResult());
642       break;
643     }
644     return mlir::success();
645   }
646 };
647 
648 /// convert value of from-type to value of to-type
649 struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
650   using FIROpConversion::FIROpConversion;
651 
652   static bool isFloatingPointTy(mlir::Type ty) {
653     return mlir::isa<mlir::FloatType>(ty);
654   }
655 
656   llvm::LogicalResult
657   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
658                   mlir::ConversionPatternRewriter &rewriter) const override {
659     auto fromFirTy = convert.getValue().getType();
660     auto toFirTy = convert.getRes().getType();
661     auto fromTy = convertType(fromFirTy);
662     auto toTy = convertType(toFirTy);
663     mlir::Value op0 = adaptor.getOperands()[0];
664 
665     if (fromFirTy == toFirTy) {
666       rewriter.replaceOp(convert, op0);
667       return mlir::success();
668     }
669 
670     auto loc = convert.getLoc();
671     auto i1Type = mlir::IntegerType::get(convert.getContext(), 1);
672 
673     if (mlir::isa<fir::RecordType>(toFirTy)) {
674       // Convert to compatible BIND(C) record type.
675       // Double check that the record types are compatible (it should have
676       // already been checked by the verifier).
677       assert(mlir::cast<fir::RecordType>(fromFirTy).getTypeList() ==
678                  mlir::cast<fir::RecordType>(toFirTy).getTypeList() &&
679              "incompatible record types");
680 
681       auto toStTy = mlir::cast<mlir::LLVM::LLVMStructType>(toTy);
682       mlir::Value val = rewriter.create<mlir::LLVM::UndefOp>(loc, toStTy);
683       auto indexTypeMap = toStTy.getSubelementIndexMap();
684       assert(indexTypeMap.has_value() && "invalid record type");
685 
686       for (auto [attr, type] : indexTypeMap.value()) {
687         int64_t index = mlir::cast<mlir::IntegerAttr>(attr).getInt();
688         auto extVal =
689             rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, index);
690         val =
691             rewriter.create<mlir::LLVM::InsertValueOp>(loc, val, extVal, index);
692       }
693 
694       rewriter.replaceOp(convert, val);
695       return mlir::success();
696     }
697 
698     if (mlir::isa<fir::LogicalType>(fromFirTy) ||
699         mlir::isa<fir::LogicalType>(toFirTy)) {
700       // By specification fir::LogicalType value may be any number,
701       // where non-zero value represents .true. and zero value represents
702       // .false.
703       //
704       // integer<->logical conversion requires value normalization.
705       // Conversion from wide logical to narrow logical must set the result
706       // to non-zero iff the input is non-zero - the easiest way to implement
707       // it is to compare the input agains zero and set the result to
708       // the canonical 0/1.
709       // Conversion from narrow logical to wide logical may be implemented
710       // as a zero or sign extension of the input, but it may use value
711       // normalization as well.
712       if (!mlir::isa<mlir::IntegerType>(fromTy) ||
713           !mlir::isa<mlir::IntegerType>(toTy))
714         return mlir::emitError(loc)
715                << "unsupported types for logical conversion: " << fromTy
716                << " -> " << toTy;
717 
718       // Do folding for constant inputs.
719       if (auto constVal = fir::getIntIfConstant(op0)) {
720         mlir::Value normVal =
721             genConstantIndex(loc, toTy, rewriter, *constVal ? 1 : 0);
722         rewriter.replaceOp(convert, normVal);
723         return mlir::success();
724       }
725 
726       // If the input is i1, then we can just zero extend it, and
727       // the result will be normalized.
728       if (fromTy == i1Type) {
729         rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0);
730         return mlir::success();
731       }
732 
733       // Compare the input with zero.
734       mlir::Value zero = genConstantIndex(loc, fromTy, rewriter, 0);
735       auto isTrue = rewriter.create<mlir::LLVM::ICmpOp>(
736           loc, mlir::LLVM::ICmpPredicate::ne, op0, zero);
737 
738       // Zero extend the i1 isTrue result to the required type (unless it is i1
739       // itself).
740       if (toTy != i1Type)
741         rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, isTrue);
742       else
743         rewriter.replaceOp(convert, isTrue.getResult());
744 
745       return mlir::success();
746     }
747 
748     if (fromTy == toTy) {
749       rewriter.replaceOp(convert, op0);
750       return mlir::success();
751     }
752     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
753                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
754       if (fromBits == toBits) {
755         // TODO: Converting between two floating-point representations with the
756         // same bitwidth is not allowed for now.
757         mlir::emitError(loc,
758                         "cannot implicitly convert between two floating-point "
759                         "representations of the same bitwidth");
760         return {};
761       }
762       if (fromBits > toBits)
763         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
764       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
765     };
766     // Complex to complex conversion.
767     if (fir::isa_complex(fromFirTy) && fir::isa_complex(toFirTy)) {
768       // Special case: handle the conversion of a complex such that both the
769       // real and imaginary parts are converted together.
770       auto ty = convertType(getComplexEleTy(convert.getValue().getType()));
771       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 0);
772       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 1);
773       auto nt = convertType(getComplexEleTy(convert.getRes().getType()));
774       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
775       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
776       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
777       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
778       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
779       auto i1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, un, rc, 0);
780       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, i1, ic,
781                                                              1);
782       return mlir::success();
783     }
784 
785     // Floating point to floating point conversion.
786     if (isFloatingPointTy(fromTy)) {
787       if (isFloatingPointTy(toTy)) {
788         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
789         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
790         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
791         rewriter.replaceOp(convert, v);
792         return mlir::success();
793       }
794       if (mlir::isa<mlir::IntegerType>(toTy)) {
795         if (toTy.isUnsignedInteger())
796           rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(convert, toTy, op0);
797         else
798           rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
799         return mlir::success();
800       }
801     } else if (mlir::isa<mlir::IntegerType>(fromTy)) {
802       // Integer to integer conversion.
803       if (mlir::isa<mlir::IntegerType>(toTy)) {
804         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
805         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
806         assert(fromBits != toBits);
807         if (fromBits > toBits) {
808           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
809           return mlir::success();
810         }
811         if (fromFirTy == i1Type || fromFirTy.isUnsignedInteger()) {
812           rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0);
813           return mlir::success();
814         }
815         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
816         return mlir::success();
817       }
818       // Integer to floating point conversion.
819       if (isFloatingPointTy(toTy)) {
820         if (fromTy.isUnsignedInteger())
821           rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(convert, toTy, op0);
822         else
823           rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
824         return mlir::success();
825       }
826       // Integer to pointer conversion.
827       if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) {
828         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
829         return mlir::success();
830       }
831     } else if (mlir::isa<mlir::LLVM::LLVMPointerType>(fromTy)) {
832       // Pointer to integer conversion.
833       if (mlir::isa<mlir::IntegerType>(toTy)) {
834         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
835         return mlir::success();
836       }
837       // Pointer to pointer conversion.
838       if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) {
839         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
840         return mlir::success();
841       }
842     }
843     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
844   }
845 };
846 
847 /// `fir.type_info` operation has no specific CodeGen. The operation is
848 /// only used to carry information during FIR to FIR passes. It may be used
849 /// in the future to generate the runtime type info data structures instead
850 /// of generating them in lowering.
851 struct TypeInfoOpConversion : public fir::FIROpConversion<fir::TypeInfoOp> {
852   using FIROpConversion::FIROpConversion;
853 
854   llvm::LogicalResult
855   matchAndRewrite(fir::TypeInfoOp op, OpAdaptor,
856                   mlir::ConversionPatternRewriter &rewriter) const override {
857     rewriter.eraseOp(op);
858     return mlir::success();
859   }
860 };
861 
862 /// `fir.dt_entry` operation has no specific CodeGen. The operation is only used
863 /// to carry information during FIR to FIR passes.
864 struct DTEntryOpConversion : public fir::FIROpConversion<fir::DTEntryOp> {
865   using FIROpConversion::FIROpConversion;
866 
867   llvm::LogicalResult
868   matchAndRewrite(fir::DTEntryOp op, OpAdaptor,
869                   mlir::ConversionPatternRewriter &rewriter) const override {
870     rewriter.eraseOp(op);
871     return mlir::success();
872   }
873 };
874 
875 /// Lower `fir.global_len` operation.
876 struct GlobalLenOpConversion : public fir::FIROpConversion<fir::GlobalLenOp> {
877   using FIROpConversion::FIROpConversion;
878 
879   llvm::LogicalResult
880   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
881                   mlir::ConversionPatternRewriter &rewriter) const override {
882     TODO(globalLen.getLoc(), "fir.global_len codegen");
883     return mlir::failure();
884   }
885 };
886 
887 /// Lower fir.len_param_index
888 struct LenParamIndexOpConversion
889     : public fir::FIROpConversion<fir::LenParamIndexOp> {
890   using FIROpConversion::FIROpConversion;
891 
892   // FIXME: this should be specialized by the runtime target
893   llvm::LogicalResult
894   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
895                   mlir::ConversionPatternRewriter &rewriter) const override {
896     TODO(lenp.getLoc(), "fir.len_param_index codegen");
897   }
898 };
899 
900 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
901 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
902 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
903 /// element is the length of the character buffer (`#n`).
904 struct EmboxCharOpConversion : public fir::FIROpConversion<fir::EmboxCharOp> {
905   using FIROpConversion::FIROpConversion;
906 
907   llvm::LogicalResult
908   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
909                   mlir::ConversionPatternRewriter &rewriter) const override {
910     mlir::ValueRange operands = adaptor.getOperands();
911 
912     mlir::Value charBuffer = operands[0];
913     mlir::Value charBufferLen = operands[1];
914 
915     mlir::Location loc = emboxChar.getLoc();
916     mlir::Type llvmStructTy = convertType(emboxChar.getType());
917     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
918 
919     mlir::Type lenTy =
920         mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[1];
921     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
922 
923     mlir::Type addrTy =
924         mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[0];
925     if (addrTy != charBuffer.getType())
926       charBuffer =
927           rewriter.create<mlir::LLVM::BitcastOp>(loc, addrTy, charBuffer);
928 
929     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
930         loc, llvmStruct, charBuffer, 0);
931     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
932         emboxChar, insertBufferOp, lenAfterCast, 1);
933 
934     return mlir::success();
935   }
936 };
937 } // namespace
938 
939 template <typename ModuleOp>
940 static mlir::SymbolRefAttr
941 getMallocInModule(ModuleOp mod, fir::AllocMemOp op,
942                   mlir::ConversionPatternRewriter &rewriter) {
943   static constexpr char mallocName[] = "malloc";
944   if (auto mallocFunc =
945           mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(mallocName))
946     return mlir::SymbolRefAttr::get(mallocFunc);
947   if (auto userMalloc =
948           mod.template lookupSymbol<mlir::func::FuncOp>(mallocName))
949     return mlir::SymbolRefAttr::get(userMalloc);
950 
951   mlir::OpBuilder moduleBuilder(mod.getBodyRegion());
952   auto indexType = mlir::IntegerType::get(op.getContext(), 64);
953   auto mallocDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
954       op.getLoc(), mallocName,
955       mlir::LLVM::LLVMFunctionType::get(getLlvmPtrType(op.getContext()),
956                                         indexType,
957                                         /*isVarArg=*/false));
958   return mlir::SymbolRefAttr::get(mallocDecl);
959 }
960 
961 /// Return the LLVMFuncOp corresponding to the standard malloc call.
962 static mlir::SymbolRefAttr
963 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) {
964   if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>())
965     return getMallocInModule(mod, op, rewriter);
966   auto mod = op->getParentOfType<mlir::ModuleOp>();
967   return getMallocInModule(mod, op, rewriter);
968 }
969 
970 /// Helper function for generating the LLVM IR that computes the distance
971 /// in bytes between adjacent elements pointed to by a pointer
972 /// of type \p ptrTy. The result is returned as a value of \p idxTy integer
973 /// type.
974 static mlir::Value
975 computeElementDistance(mlir::Location loc, mlir::Type llvmObjectType,
976                        mlir::Type idxTy,
977                        mlir::ConversionPatternRewriter &rewriter) {
978   // Note that we cannot use something like
979   // mlir::LLVM::getPrimitiveTypeSizeInBits() for the element type here. For
980   // example, it returns 10 bytes for mlir::Float80Type for targets where it
981   // occupies 16 bytes. Proper solution is probably to use
982   // mlir::DataLayout::getTypeABIAlignment(), but DataLayout is not being set
983   // yet (see llvm-project#57230). For the time being use the '(intptr_t)((type
984   // *)0 + 1)' trick for all types. The generated instructions are optimized
985   // into constant by the first pass of InstCombine, so it should not be a
986   // performance issue.
987   auto llvmPtrTy = ::getLlvmPtrType(llvmObjectType.getContext());
988   auto nullPtr = rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
989   auto gep = rewriter.create<mlir::LLVM::GEPOp>(
990       loc, llvmPtrTy, llvmObjectType, nullPtr,
991       llvm::ArrayRef<mlir::LLVM::GEPArg>{1});
992   return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
993 }
994 
995 /// Return value of the stride in bytes between adjacent elements
996 /// of LLVM type \p llTy. The result is returned as a value of
997 /// \p idxTy integer type.
998 static mlir::Value
999 genTypeStrideInBytes(mlir::Location loc, mlir::Type idxTy,
1000                      mlir::ConversionPatternRewriter &rewriter,
1001                      mlir::Type llTy) {
1002   // Create a pointer type and use computeElementDistance().
1003   return computeElementDistance(loc, llTy, idxTy, rewriter);
1004 }
1005 
1006 namespace {
1007 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc`
1008 struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
1009   using FIROpConversion::FIROpConversion;
1010 
1011   llvm::LogicalResult
1012   matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor,
1013                   mlir::ConversionPatternRewriter &rewriter) const override {
1014     mlir::Type heapTy = heap.getType();
1015     mlir::Location loc = heap.getLoc();
1016     auto ity = lowerTy().indexType();
1017     mlir::Type dataTy = fir::unwrapRefType(heapTy);
1018     mlir::Type llvmObjectTy = convertObjectType(dataTy);
1019     if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy)))
1020       TODO(loc, "fir.allocmem codegen of derived type with length parameters");
1021     mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, llvmObjectTy);
1022     if (auto scaleSize = genAllocationScaleSize(heap, ity, rewriter))
1023       size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, scaleSize);
1024     for (mlir::Value opnd : adaptor.getOperands())
1025       size = rewriter.create<mlir::LLVM::MulOp>(
1026           loc, ity, size, integerCast(loc, rewriter, ity, opnd));
1027     heap->setAttr("callee", getMalloc(heap, rewriter));
1028     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
1029         heap, ::getLlvmPtrType(heap.getContext()), size,
1030         addLLVMOpBundleAttrs(rewriter, heap->getAttrs(), 1));
1031     return mlir::success();
1032   }
1033 
1034   /// Compute the allocation size in bytes of the element type of
1035   /// \p llTy pointer type. The result is returned as a value of \p idxTy
1036   /// integer type.
1037   mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy,
1038                                  mlir::ConversionPatternRewriter &rewriter,
1039                                  mlir::Type llTy) const {
1040     return computeElementDistance(loc, llTy, idxTy, rewriter);
1041   }
1042 };
1043 } // namespace
1044 
1045 /// Return the LLVMFuncOp corresponding to the standard free call.
1046 template <typename ModuleOp>
1047 static mlir::SymbolRefAttr
1048 getFreeInModule(ModuleOp mod, fir::FreeMemOp op,
1049                 mlir::ConversionPatternRewriter &rewriter) {
1050   static constexpr char freeName[] = "free";
1051   // Check if free already defined in the module.
1052   if (auto freeFunc =
1053           mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(freeName))
1054     return mlir::SymbolRefAttr::get(freeFunc);
1055   if (auto freeDefinedByUser =
1056           mod.template lookupSymbol<mlir::func::FuncOp>(freeName))
1057     return mlir::SymbolRefAttr::get(freeDefinedByUser);
1058   // Create llvm declaration for free.
1059   mlir::OpBuilder moduleBuilder(mod.getBodyRegion());
1060   auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext());
1061   auto freeDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
1062       rewriter.getUnknownLoc(), freeName,
1063       mlir::LLVM::LLVMFunctionType::get(voidType,
1064                                         getLlvmPtrType(op.getContext()),
1065                                         /*isVarArg=*/false));
1066   return mlir::SymbolRefAttr::get(freeDecl);
1067 }
1068 
1069 static mlir::SymbolRefAttr getFree(fir::FreeMemOp op,
1070                                    mlir::ConversionPatternRewriter &rewriter) {
1071   if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>())
1072     return getFreeInModule(mod, op, rewriter);
1073   auto mod = op->getParentOfType<mlir::ModuleOp>();
1074   return getFreeInModule(mod, op, rewriter);
1075 }
1076 
1077 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1078   unsigned result = 1;
1079   for (auto eleTy =
1080            mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty.getElementType());
1081        eleTy; eleTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(
1082                   eleTy.getElementType()))
1083     ++result;
1084   return result;
1085 }
1086 
1087 namespace {
1088 /// Lower a `fir.freemem` instruction into `llvm.call @free`
1089 struct FreeMemOpConversion : public fir::FIROpConversion<fir::FreeMemOp> {
1090   using FIROpConversion::FIROpConversion;
1091 
1092   llvm::LogicalResult
1093   matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor,
1094                   mlir::ConversionPatternRewriter &rewriter) const override {
1095     mlir::Location loc = freemem.getLoc();
1096     freemem->setAttr("callee", getFree(freemem, rewriter));
1097     rewriter.create<mlir::LLVM::CallOp>(
1098         loc, mlir::TypeRange{}, mlir::ValueRange{adaptor.getHeapref()},
1099         addLLVMOpBundleAttrs(rewriter, freemem->getAttrs(), 1));
1100     rewriter.eraseOp(freemem);
1101     return mlir::success();
1102   }
1103 };
1104 } // namespace
1105 
1106 // Convert subcomponent array indices from column-major to row-major ordering.
1107 static llvm::SmallVector<mlir::Value>
1108 convertSubcomponentIndices(mlir::Location loc, mlir::Type eleTy,
1109                            mlir::ValueRange indices,
1110                            mlir::Type *retTy = nullptr) {
1111   llvm::SmallVector<mlir::Value> result;
1112   llvm::SmallVector<mlir::Value> arrayIndices;
1113 
1114   auto appendArrayIndices = [&] {
1115     if (arrayIndices.empty())
1116       return;
1117     std::reverse(arrayIndices.begin(), arrayIndices.end());
1118     result.append(arrayIndices.begin(), arrayIndices.end());
1119     arrayIndices.clear();
1120   };
1121 
1122   for (mlir::Value index : indices) {
1123     // Component indices can be field index to select a component, or array
1124     // index, to select an element in an array component.
1125     if (auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(eleTy)) {
1126       std::int64_t cstIndex = getConstantIntValue(index);
1127       assert(cstIndex < (int64_t)structTy.getBody().size() &&
1128              "out-of-bounds struct field index");
1129       eleTy = structTy.getBody()[cstIndex];
1130       appendArrayIndices();
1131       result.push_back(index);
1132     } else if (auto arrayTy =
1133                    mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)) {
1134       eleTy = arrayTy.getElementType();
1135       arrayIndices.push_back(index);
1136     } else
1137       fir::emitFatalError(loc, "Unexpected subcomponent type");
1138   }
1139   appendArrayIndices();
1140   if (retTy)
1141     *retTy = eleTy;
1142   return result;
1143 }
1144 
1145 static mlir::Value genSourceFile(mlir::Location loc, mlir::ModuleOp mod,
1146                                  mlir::ConversionPatternRewriter &rewriter) {
1147   auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
1148   if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) {
1149     auto fn = flc.getFilename().str() + '\0';
1150     std::string globalName = fir::factory::uniqueCGIdent("cl", fn);
1151 
1152     if (auto g = mod.lookupSymbol<fir::GlobalOp>(globalName)) {
1153       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, g.getName());
1154     } else if (auto g = mod.lookupSymbol<mlir::LLVM::GlobalOp>(globalName)) {
1155       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, g.getName());
1156     }
1157 
1158     auto crtInsPt = rewriter.saveInsertionPoint();
1159     rewriter.setInsertionPoint(mod.getBody(), mod.getBody()->end());
1160     auto arrayTy = mlir::LLVM::LLVMArrayType::get(
1161         mlir::IntegerType::get(rewriter.getContext(), 8), fn.size());
1162     mlir::LLVM::GlobalOp globalOp = rewriter.create<mlir::LLVM::GlobalOp>(
1163         loc, arrayTy, /*constant=*/true, mlir::LLVM::Linkage::Linkonce,
1164         globalName, mlir::Attribute());
1165 
1166     mlir::Region &region = globalOp.getInitializerRegion();
1167     mlir::Block *block = rewriter.createBlock(&region);
1168     rewriter.setInsertionPoint(block, block->begin());
1169     mlir::Value constValue = rewriter.create<mlir::LLVM::ConstantOp>(
1170         loc, arrayTy, rewriter.getStringAttr(fn));
1171     rewriter.create<mlir::LLVM::ReturnOp>(loc, constValue);
1172     rewriter.restoreInsertionPoint(crtInsPt);
1173     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy,
1174                                                     globalOp.getName());
1175   }
1176   return rewriter.create<mlir::LLVM::ZeroOp>(loc, ptrTy);
1177 }
1178 
1179 static mlir::Value genSourceLine(mlir::Location loc,
1180                                  mlir::ConversionPatternRewriter &rewriter) {
1181   if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc))
1182     return rewriter.create<mlir::LLVM::ConstantOp>(loc, rewriter.getI32Type(),
1183                                                    flc.getLine());
1184   return rewriter.create<mlir::LLVM::ConstantOp>(loc, rewriter.getI32Type(), 0);
1185 }
1186 
1187 static mlir::Value
1188 genCUFAllocDescriptor(mlir::Location loc,
1189                       mlir::ConversionPatternRewriter &rewriter,
1190                       mlir::ModuleOp mod, fir::BaseBoxType boxTy,
1191                       const fir::LLVMTypeConverter &typeConverter) {
1192   std::optional<mlir::DataLayout> dl =
1193       fir::support::getOrSetDataLayout(mod, /*allowDefaultLayout=*/true);
1194   if (!dl)
1195     mlir::emitError(mod.getLoc(),
1196                     "module operation must carry a data layout attribute "
1197                     "to generate llvm IR from FIR");
1198 
1199   mlir::Value sourceFile = genSourceFile(loc, mod, rewriter);
1200   mlir::Value sourceLine = genSourceLine(loc, rewriter);
1201 
1202   mlir::MLIRContext *ctx = mod.getContext();
1203 
1204   mlir::LLVM::LLVMPointerType llvmPointerType =
1205       mlir::LLVM::LLVMPointerType::get(ctx);
1206   mlir::Type llvmInt32Type = mlir::IntegerType::get(ctx, 32);
1207   mlir::Type llvmIntPtrType =
1208       mlir::IntegerType::get(ctx, typeConverter.getPointerBitwidth(0));
1209   auto fctTy = mlir::LLVM::LLVMFunctionType::get(
1210       llvmPointerType, {llvmIntPtrType, llvmPointerType, llvmInt32Type});
1211 
1212   auto llvmFunc = mod.lookupSymbol<mlir::LLVM::LLVMFuncOp>(
1213       RTNAME_STRING(CUFAllocDescriptor));
1214   auto funcFunc =
1215       mod.lookupSymbol<mlir::func::FuncOp>(RTNAME_STRING(CUFAllocDescriptor));
1216   if (!llvmFunc && !funcFunc)
1217     mlir::OpBuilder::atBlockEnd(mod.getBody())
1218         .create<mlir::LLVM::LLVMFuncOp>(loc, RTNAME_STRING(CUFAllocDescriptor),
1219                                         fctTy);
1220 
1221   mlir::Type structTy = typeConverter.convertBoxTypeAsStruct(boxTy);
1222   std::size_t boxSize = dl->getTypeSizeInBits(structTy) / 8;
1223   mlir::Value sizeInBytes =
1224       genConstantIndex(loc, llvmIntPtrType, rewriter, boxSize);
1225   llvm::SmallVector args = {sizeInBytes, sourceFile, sourceLine};
1226   return rewriter
1227       .create<mlir::LLVM::CallOp>(loc, fctTy, RTNAME_STRING(CUFAllocDescriptor),
1228                                   args)
1229       .getResult();
1230 }
1231 
1232 /// Common base class for embox to descriptor conversion.
1233 template <typename OP>
1234 struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
1235   using fir::FIROpConversion<OP>::FIROpConversion;
1236   using TypePair = typename fir::FIROpConversion<OP>::TypePair;
1237 
1238   static int getCFIAttr(fir::BaseBoxType boxTy) {
1239     auto eleTy = boxTy.getEleTy();
1240     if (mlir::isa<fir::PointerType>(eleTy))
1241       return CFI_attribute_pointer;
1242     if (mlir::isa<fir::HeapType>(eleTy))
1243       return CFI_attribute_allocatable;
1244     return CFI_attribute_other;
1245   }
1246 
1247   mlir::Value getCharacterByteSize(mlir::Location loc,
1248                                    mlir::ConversionPatternRewriter &rewriter,
1249                                    fir::CharacterType charTy,
1250                                    mlir::ValueRange lenParams) const {
1251     auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
1252     mlir::Value size =
1253         genTypeStrideInBytes(loc, i64Ty, rewriter, this->convertType(charTy));
1254     if (charTy.hasConstantLen())
1255       return size; // Length accounted for in the genTypeStrideInBytes GEP.
1256     // Otherwise,  multiply the single character size by the length.
1257     assert(!lenParams.empty());
1258     auto len64 = fir::FIROpConversion<OP>::integerCast(loc, rewriter, i64Ty,
1259                                                        lenParams.back());
1260     return rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, size, len64);
1261   }
1262 
1263   // Get the element size and CFI type code of the boxed value.
1264   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1265       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1266       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1267     auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
1268     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1269       boxEleTy = eleTy;
1270     if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(boxEleTy))
1271       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1272     if (mlir::isa<mlir::NoneType>(
1273             boxEleTy)) // unlimited polymorphic or assumed type
1274       return {rewriter.create<mlir::LLVM::ConstantOp>(loc, i64Ty, 0),
1275               this->genConstantOffset(loc, rewriter, CFI_type_other)};
1276     mlir::Value typeCodeVal = this->genConstantOffset(
1277         loc, rewriter,
1278         fir::getTypeCode(boxEleTy, this->lowerTy().getKindMap()));
1279     if (fir::isa_integer(boxEleTy) ||
1280         mlir::dyn_cast<fir::LogicalType>(boxEleTy) || fir::isa_real(boxEleTy) ||
1281         fir::isa_complex(boxEleTy))
1282       return {genTypeStrideInBytes(loc, i64Ty, rewriter,
1283                                    this->convertType(boxEleTy)),
1284               typeCodeVal};
1285     if (auto charTy = mlir::dyn_cast<fir::CharacterType>(boxEleTy))
1286       return {getCharacterByteSize(loc, rewriter, charTy, lenParams),
1287               typeCodeVal};
1288     if (fir::isa_ref_type(boxEleTy)) {
1289       auto ptrTy = ::getLlvmPtrType(rewriter.getContext());
1290       return {genTypeStrideInBytes(loc, i64Ty, rewriter, ptrTy), typeCodeVal};
1291     }
1292     if (mlir::isa<fir::RecordType>(boxEleTy))
1293       return {genTypeStrideInBytes(loc, i64Ty, rewriter,
1294                                    this->convertType(boxEleTy)),
1295               typeCodeVal};
1296     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1297   }
1298 
1299   /// Basic pattern to write a field in the descriptor
1300   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1301                           mlir::Location loc, mlir::Value dest,
1302                           llvm::ArrayRef<std::int64_t> fldIndexes,
1303                           mlir::Value value, bool bitcast = false) const {
1304     auto boxTy = dest.getType();
1305     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1306     if (!bitcast)
1307       value = this->integerCast(loc, rewriter, fldTy, value);
1308     // bitcast are no-ops with LLVM opaque pointers.
1309     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, dest, value,
1310                                                       fldIndexes);
1311   }
1312 
1313   inline mlir::Value
1314   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1315                     mlir::Location loc, mlir::Value dest,
1316                     mlir::Value base) const {
1317     return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
1318                        /*bitCast=*/true);
1319   }
1320 
1321   inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
1322                                       mlir::Location loc, mlir::Value dest,
1323                                       unsigned dim, mlir::Value lb) const {
1324     return insertField(rewriter, loc, dest,
1325                        {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
1326   }
1327 
1328   inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
1329                                   mlir::Location loc, mlir::Value dest,
1330                                   unsigned dim, mlir::Value extent) const {
1331     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
1332                        extent);
1333   }
1334 
1335   inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
1336                                   mlir::Location loc, mlir::Value dest,
1337                                   unsigned dim, mlir::Value stride) const {
1338     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
1339                        stride);
1340   }
1341 
1342   /// Get the address of the type descriptor global variable that was created by
1343   /// lowering for derived type \p recType.
1344   template <typename ModOpTy>
1345   mlir::Value
1346   getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter,
1347                     mlir::Location loc, fir::RecordType recType) const {
1348     std::string name =
1349         this->options.typeDescriptorsRenamedForAssembly
1350             ? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
1351             : fir::NameUniquer::getTypeDescriptorName(recType.getName());
1352     mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
1353     if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) {
1354       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1355                                                       global.getSymName());
1356     }
1357     if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1358       // The global may have already been translated to LLVM.
1359       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1360                                                       global.getSymName());
1361     }
1362     // Type info derived types do not have type descriptors since they are the
1363     // types defining type descriptors.
1364     if (!this->options.ignoreMissingTypeDescriptors &&
1365         !fir::NameUniquer::belongsToModule(
1366             name, Fortran::semantics::typeInfoBuiltinModule))
1367       fir::emitFatalError(
1368           loc, "runtime derived type info descriptor was not generated");
1369     return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
1370   }
1371 
1372   template <typename ModOpTy>
1373   mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod,
1374                                  fir::BaseBoxType boxTy, mlir::Type inputType,
1375                                  mlir::ConversionPatternRewriter &rewriter,
1376                                  unsigned rank, mlir::Value eleSize,
1377                                  mlir::Value cfiTy, mlir::Value typeDesc,
1378                                  int allocatorIdx = kDefaultAllocator,
1379                                  mlir::Value extraField = {}) const {
1380     auto llvmBoxTy = this->lowerTy().convertBoxTypeAsStruct(boxTy, rank);
1381     bool isUnlimitedPolymorphic = fir::isUnlimitedPolymorphicType(boxTy);
1382     bool useInputType = fir::isPolymorphicType(boxTy) || isUnlimitedPolymorphic;
1383     mlir::Value descriptor =
1384         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1385     descriptor =
1386         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1387     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1388                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1389     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1390                              this->genI32Constant(loc, rewriter, rank));
1391     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1392     descriptor =
1393         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1394                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1395 
1396     const bool hasAddendum = fir::boxHasAddendum(boxTy);
1397 
1398     if (extraField) {
1399       // Make sure to set the addendum presence flag according to the
1400       // destination box.
1401       if (hasAddendum) {
1402         auto maskAttr = mlir::IntegerAttr::get(
1403             rewriter.getIntegerType(8, /*isSigned=*/false),
1404             llvm::APInt(8, (uint64_t)_CFI_ADDENDUM_FLAG, /*isSigned=*/false));
1405         mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>(
1406             loc, rewriter.getI8Type(), maskAttr);
1407         extraField = rewriter.create<mlir::LLVM::OrOp>(loc, extraField, mask);
1408       } else {
1409         auto maskAttr = mlir::IntegerAttr::get(
1410             rewriter.getIntegerType(8, /*isSigned=*/false),
1411             llvm::APInt(8, (uint64_t)~_CFI_ADDENDUM_FLAG, /*isSigned=*/true));
1412         mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>(
1413             loc, rewriter.getI8Type(), maskAttr);
1414         extraField = rewriter.create<mlir::LLVM::AndOp>(loc, extraField, mask);
1415       }
1416       // Extra field value is provided so just use it.
1417       descriptor =
1418           insertField(rewriter, loc, descriptor, {kExtraPosInBox}, extraField);
1419     } else {
1420       // Compute the value of the extra field based on allocator_idx and
1421       // addendum present.
1422       unsigned extra = allocatorIdx << _CFI_ALLOCATOR_IDX_SHIFT;
1423       if (hasAddendum)
1424         extra |= _CFI_ADDENDUM_FLAG;
1425       descriptor = insertField(rewriter, loc, descriptor, {kExtraPosInBox},
1426                                this->genI32Constant(loc, rewriter, extra));
1427     }
1428 
1429     if (hasAddendum) {
1430       unsigned typeDescFieldId = getTypeDescFieldId(boxTy);
1431       if (!typeDesc) {
1432         if (useInputType) {
1433           mlir::Type innerType = fir::unwrapInnerType(inputType);
1434           if (innerType && mlir::isa<fir::RecordType>(innerType)) {
1435             auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
1436             typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy);
1437           } else {
1438             // Unlimited polymorphic type descriptor with no record type. Set
1439             // type descriptor address to a clean state.
1440             typeDesc = rewriter.create<mlir::LLVM::ZeroOp>(
1441                 loc, ::getLlvmPtrType(mod.getContext()));
1442           }
1443         } else {
1444           typeDesc = getTypeDescriptor(mod, rewriter, loc,
1445                                        fir::unwrapIfDerived(boxTy));
1446         }
1447       }
1448       if (typeDesc)
1449         descriptor =
1450             insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1451                         /*bitCast=*/true);
1452       // Always initialize the length parameter field to zero to avoid issues
1453       // with uninitialized values in Fortran code trying to compare physical
1454       // representation of derived types with pointer/allocatable components.
1455       // This has been seen in hashing algorithms using TRANSFER.
1456       mlir::Value zero =
1457           genConstantIndex(loc, rewriter.getI64Type(), rewriter, 0);
1458       descriptor = insertField(rewriter, loc, descriptor,
1459                                {getLenParamFieldId(boxTy), 0}, zero);
1460     }
1461     return descriptor;
1462   }
1463 
1464   // Template used for fir::EmboxOp and fir::cg::XEmboxOp
1465   template <typename BOX>
1466   std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value>
1467   consDescriptorPrefix(BOX box, mlir::Type inputType,
1468                        mlir::ConversionPatternRewriter &rewriter, unsigned rank,
1469                        [[maybe_unused]] mlir::ValueRange substrParams,
1470                        mlir::ValueRange lenParams, mlir::Value sourceBox = {},
1471                        mlir::Type sourceBoxType = {}) const {
1472     auto loc = box.getLoc();
1473     auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType());
1474     bool useInputType = fir::isPolymorphicType(boxTy) &&
1475                         !fir::isUnlimitedPolymorphicType(inputType);
1476     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1477     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1478       if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1479         typeparams.push_back(substrParams[1]);
1480     }
1481 
1482     int allocatorIdx = 0;
1483     if constexpr (std::is_same_v<BOX, fir::EmboxOp> ||
1484                   std::is_same_v<BOX, fir::cg::XEmboxOp>) {
1485       if (box.getAllocatorIdx())
1486         allocatorIdx = *box.getAllocatorIdx();
1487     }
1488 
1489     // Write each of the fields with the appropriate values.
1490     // When emboxing an element to a polymorphic descriptor, use the
1491     // input type since the destination descriptor type has not the exact
1492     // information.
1493     auto [eleSize, cfiTy] = getSizeAndTypeCode(
1494         loc, rewriter, useInputType ? inputType : boxTy.getEleTy(), typeparams);
1495 
1496     mlir::Value typeDesc;
1497     mlir::Value extraField;
1498     // When emboxing to a polymorphic box, get the type descriptor, type code
1499     // and element size from the source box if any.
1500     if (fir::isPolymorphicType(boxTy) && sourceBox) {
1501       TypePair sourceBoxTyPair = this->getBoxTypePair(sourceBoxType);
1502       typeDesc =
1503           this->loadTypeDescAddress(loc, sourceBoxTyPair, sourceBox, rewriter);
1504       mlir::Type idxTy = this->lowerTy().indexType();
1505       eleSize = this->getElementSizeFromBox(loc, idxTy, sourceBoxTyPair,
1506                                             sourceBox, rewriter);
1507       cfiTy = this->getValueFromBox(loc, sourceBoxTyPair, sourceBox,
1508                                     cfiTy.getType(), rewriter, kTypePosInBox);
1509       extraField =
1510           this->getExtraFromBox(loc, sourceBoxTyPair, sourceBox, rewriter);
1511     }
1512 
1513     mlir::Value descriptor;
1514     if (auto gpuMod = box->template getParentOfType<mlir::gpu::GPUModuleOp>())
1515       descriptor = populateDescriptor(loc, gpuMod, boxTy, inputType, rewriter,
1516                                       rank, eleSize, cfiTy, typeDesc,
1517                                       allocatorIdx, extraField);
1518     else if (auto mod = box->template getParentOfType<mlir::ModuleOp>())
1519       descriptor = populateDescriptor(loc, mod, boxTy, inputType, rewriter,
1520                                       rank, eleSize, cfiTy, typeDesc,
1521                                       allocatorIdx, extraField);
1522 
1523     return {boxTy, descriptor, eleSize};
1524   }
1525 
1526   std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value>
1527   consDescriptorPrefix(fir::cg::XReboxOp box, mlir::Value loweredBox,
1528                        mlir::ConversionPatternRewriter &rewriter, unsigned rank,
1529                        mlir::ValueRange substrParams,
1530                        mlir::ValueRange lenParams,
1531                        mlir::Value typeDesc = {}) const {
1532     auto loc = box.getLoc();
1533     auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType());
1534     auto inputBoxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getBox().getType());
1535     auto inputBoxTyPair = this->getBoxTypePair(inputBoxTy);
1536     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1537     if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1538       typeparams.push_back(substrParams[1]);
1539 
1540     auto [eleSize, cfiTy] =
1541         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1542 
1543     // Reboxing to a polymorphic entity. eleSize and type code need to
1544     // be retrieved from the initial box and propagated to the new box.
1545     // If the initial box has an addendum, the type desc must be propagated as
1546     // well.
1547     if (fir::isPolymorphicType(boxTy)) {
1548       mlir::Type idxTy = this->lowerTy().indexType();
1549       eleSize = this->getElementSizeFromBox(loc, idxTy, inputBoxTyPair,
1550                                             loweredBox, rewriter);
1551       cfiTy = this->getValueFromBox(loc, inputBoxTyPair, loweredBox,
1552                                     cfiTy.getType(), rewriter, kTypePosInBox);
1553       // TODO: For initial box that are unlimited polymorphic entities, this
1554       // code must be made conditional because unlimited polymorphic entities
1555       // with intrinsic type spec does not have addendum.
1556       if (fir::boxHasAddendum(inputBoxTy))
1557         typeDesc = this->loadTypeDescAddress(loc, inputBoxTyPair, loweredBox,
1558                                              rewriter);
1559     }
1560 
1561     mlir::Value extraField =
1562         this->getExtraFromBox(loc, inputBoxTyPair, loweredBox, rewriter);
1563 
1564     mlir::Value descriptor;
1565     if (auto gpuMod = box->template getParentOfType<mlir::gpu::GPUModuleOp>())
1566       descriptor =
1567           populateDescriptor(loc, gpuMod, boxTy, box.getBox().getType(),
1568                              rewriter, rank, eleSize, cfiTy, typeDesc,
1569                              /*allocatorIdx=*/kDefaultAllocator, extraField);
1570     else if (auto mod = box->template getParentOfType<mlir::ModuleOp>())
1571       descriptor =
1572           populateDescriptor(loc, mod, boxTy, box.getBox().getType(), rewriter,
1573                              rank, eleSize, cfiTy, typeDesc,
1574                              /*allocatorIdx=*/kDefaultAllocator, extraField);
1575 
1576     return {boxTy, descriptor, eleSize};
1577   }
1578 
1579   // Compute the base address of a fir.box given the indices from the slice.
1580   // The indices from the "outer" dimensions (every dimension after the first
1581   // one (included) that is not a compile time constant) must have been
1582   // multiplied with the related extents and added together into \p outerOffset.
1583   mlir::Value
1584   genBoxOffsetGep(mlir::ConversionPatternRewriter &rewriter, mlir::Location loc,
1585                   mlir::Value base, mlir::Type llvmBaseObjectType,
1586                   mlir::Value outerOffset, mlir::ValueRange cstInteriorIndices,
1587                   mlir::ValueRange componentIndices,
1588                   std::optional<mlir::Value> substringOffset) const {
1589     llvm::SmallVector<mlir::LLVM::GEPArg> gepArgs{outerOffset};
1590     mlir::Type resultTy = llvmBaseObjectType;
1591     // Fortran is column major, llvm GEP is row major: reverse the indices here.
1592     for (mlir::Value interiorIndex : llvm::reverse(cstInteriorIndices)) {
1593       auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy);
1594       if (!arrayTy)
1595         fir::emitFatalError(
1596             loc,
1597             "corrupted GEP generated being generated in fir.embox/fir.rebox");
1598       resultTy = arrayTy.getElementType();
1599       gepArgs.push_back(interiorIndex);
1600     }
1601     llvm::SmallVector<mlir::Value> gepIndices =
1602         convertSubcomponentIndices(loc, resultTy, componentIndices, &resultTy);
1603     gepArgs.append(gepIndices.begin(), gepIndices.end());
1604     if (substringOffset) {
1605       if (auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy)) {
1606         gepArgs.push_back(*substringOffset);
1607         resultTy = arrayTy.getElementType();
1608       } else {
1609         // If the CHARACTER length is dynamic, the whole base type should have
1610         // degenerated to an llvm.ptr<i[width]>, and there should not be any
1611         // cstInteriorIndices/componentIndices. The substring offset can be
1612         // added to the outterOffset since it applies on the same LLVM type.
1613         if (gepArgs.size() != 1)
1614           fir::emitFatalError(loc,
1615                               "corrupted substring GEP in fir.embox/fir.rebox");
1616         mlir::Type outterOffsetTy =
1617             llvm::cast<mlir::Value>(gepArgs[0]).getType();
1618         mlir::Value cast =
1619             this->integerCast(loc, rewriter, outterOffsetTy, *substringOffset);
1620 
1621         gepArgs[0] = rewriter.create<mlir::LLVM::AddOp>(
1622             loc, outterOffsetTy, llvm::cast<mlir::Value>(gepArgs[0]), cast);
1623       }
1624     }
1625     mlir::Type llvmPtrTy = ::getLlvmPtrType(resultTy.getContext());
1626     return rewriter.create<mlir::LLVM::GEPOp>(
1627         loc, llvmPtrTy, llvmBaseObjectType, base, gepArgs);
1628   }
1629 
1630   template <typename BOX>
1631   void
1632   getSubcomponentIndices(BOX xbox, mlir::Value memref,
1633                          mlir::ValueRange operands,
1634                          mlir::SmallVectorImpl<mlir::Value> &indices) const {
1635     // For each field in the path add the offset to base via the args list.
1636     // In the most general case, some offsets must be computed since
1637     // they are not be known until runtime.
1638     if (fir::hasDynamicSize(fir::unwrapSequenceType(
1639             fir::unwrapPassByRefType(memref.getType()))))
1640       TODO(xbox.getLoc(),
1641            "fir.embox codegen dynamic size component in derived type");
1642     indices.append(operands.begin() + xbox.getSubcomponentOperandIndex(),
1643                    operands.begin() + xbox.getSubcomponentOperandIndex() +
1644                        xbox.getSubcomponent().size());
1645   }
1646 
1647   static bool isInGlobalOp(mlir::ConversionPatternRewriter &rewriter) {
1648     auto *thisBlock = rewriter.getInsertionBlock();
1649     return thisBlock &&
1650            mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp());
1651   }
1652 
1653   /// If the embox is not in a globalOp body, allocate storage for the box;
1654   /// store the value inside and return the generated alloca. Return the input
1655   /// value otherwise.
1656   mlir::Value
1657   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1658                                mlir::Location loc, mlir::Type boxTy,
1659                                mlir::Value boxValue,
1660                                bool needDeviceAllocation = false) const {
1661     if (isInGlobalOp(rewriter))
1662       return boxValue;
1663     mlir::Type llvmBoxTy = boxValue.getType();
1664     mlir::Value storage;
1665     if (needDeviceAllocation) {
1666       auto mod = boxValue.getDefiningOp()->getParentOfType<mlir::ModuleOp>();
1667       auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(boxTy);
1668       storage =
1669           genCUFAllocDescriptor(loc, rewriter, mod, baseBoxTy, this->lowerTy());
1670     } else {
1671       storage = this->genAllocaAndAddrCastWithType(loc, llvmBoxTy, defaultAlign,
1672                                                    rewriter);
1673     }
1674     auto storeOp = rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, storage);
1675     this->attachTBAATag(storeOp, boxTy, boxTy, nullptr);
1676     return storage;
1677   }
1678 
1679   /// Compute the extent of a triplet slice (lb:ub:step).
1680   mlir::Value computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
1681                                    mlir::Location loc, mlir::Value lb,
1682                                    mlir::Value ub, mlir::Value step,
1683                                    mlir::Value zero, mlir::Type type) const {
1684     lb = this->integerCast(loc, rewriter, type, lb);
1685     ub = this->integerCast(loc, rewriter, type, ub);
1686     step = this->integerCast(loc, rewriter, type, step);
1687     zero = this->integerCast(loc, rewriter, type, zero);
1688     mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
1689     extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
1690     extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
1691     // If the resulting extent is negative (`ub-lb` and `step` have different
1692     // signs), zero must be returned instead.
1693     auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1694         loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
1695     return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
1696   }
1697 };
1698 
1699 /// Create a generic box on a memory reference. This conversions lowers the
1700 /// abstract box to the appropriate, initialized descriptor.
1701 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1702   using EmboxCommonConversion::EmboxCommonConversion;
1703 
1704   llvm::LogicalResult
1705   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1706                   mlir::ConversionPatternRewriter &rewriter) const override {
1707     mlir::ValueRange operands = adaptor.getOperands();
1708     mlir::Value sourceBox;
1709     mlir::Type sourceBoxType;
1710     if (embox.getSourceBox()) {
1711       sourceBox = operands[embox.getSourceBoxOperandIndex()];
1712       sourceBoxType = embox.getSourceBox().getType();
1713     }
1714     assert(!embox.getShape() && "There should be no dims on this embox op");
1715     auto [boxTy, dest, eleSize] = consDescriptorPrefix(
1716         embox, fir::unwrapRefType(embox.getMemref().getType()), rewriter,
1717         /*rank=*/0, /*substrParams=*/mlir::ValueRange{},
1718         adaptor.getTypeparams(), sourceBox, sourceBoxType);
1719     dest = insertBaseAddress(rewriter, embox.getLoc(), dest, operands[0]);
1720     if (fir::isDerivedTypeWithLenParams(boxTy)) {
1721       TODO(embox.getLoc(),
1722            "fir.embox codegen of derived with length parameters");
1723       return mlir::failure();
1724     }
1725     auto result =
1726         placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), boxTy, dest);
1727     rewriter.replaceOp(embox, result);
1728     return mlir::success();
1729   }
1730 };
1731 
1732 static bool isDeviceAllocation(mlir::Value val, mlir::Value adaptorVal) {
1733   if (auto loadOp = mlir::dyn_cast_or_null<fir::LoadOp>(val.getDefiningOp()))
1734     return isDeviceAllocation(loadOp.getMemref(), {});
1735   if (auto boxAddrOp =
1736           mlir::dyn_cast_or_null<fir::BoxAddrOp>(val.getDefiningOp()))
1737     return isDeviceAllocation(boxAddrOp.getVal(), {});
1738   if (auto convertOp =
1739           mlir::dyn_cast_or_null<fir::ConvertOp>(val.getDefiningOp()))
1740     return isDeviceAllocation(convertOp.getValue(), {});
1741   if (!val.getDefiningOp() && adaptorVal) {
1742     if (auto blockArg = llvm::cast<mlir::BlockArgument>(adaptorVal)) {
1743       if (blockArg.getOwner() && blockArg.getOwner()->getParentOp() &&
1744           blockArg.getOwner()->isEntryBlock()) {
1745         if (auto func = mlir::dyn_cast_or_null<mlir::FunctionOpInterface>(
1746                 *blockArg.getOwner()->getParentOp())) {
1747           auto argAttrs = func.getArgAttrs(blockArg.getArgNumber());
1748           for (auto attr : argAttrs) {
1749             if (attr.getName().getValue().ends_with(cuf::getDataAttrName())) {
1750               auto dataAttr =
1751                   mlir::dyn_cast<cuf::DataAttributeAttr>(attr.getValue());
1752               if (dataAttr.getValue() != cuf::DataAttribute::Pinned &&
1753                   dataAttr.getValue() != cuf::DataAttribute::Unified)
1754                 return true;
1755             }
1756           }
1757         }
1758       }
1759     }
1760   }
1761   if (auto callOp = mlir::dyn_cast_or_null<fir::CallOp>(val.getDefiningOp()))
1762     if (callOp.getCallee() &&
1763         (callOp.getCallee().value().getRootReference().getValue().starts_with(
1764              RTNAME_STRING(CUFMemAlloc)) ||
1765          callOp.getCallee().value().getRootReference().getValue().starts_with(
1766              RTNAME_STRING(CUFAllocDescriptor))))
1767       return true;
1768   return false;
1769 }
1770 
1771 /// Create a generic box on a memory reference.
1772 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
1773   using EmboxCommonConversion::EmboxCommonConversion;
1774 
1775   llvm::LogicalResult
1776   matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
1777                   mlir::ConversionPatternRewriter &rewriter) const override {
1778     mlir::ValueRange operands = adaptor.getOperands();
1779     mlir::Value sourceBox;
1780     mlir::Type sourceBoxType;
1781     if (xbox.getSourceBox()) {
1782       sourceBox = operands[xbox.getSourceBoxOperandIndex()];
1783       sourceBoxType = xbox.getSourceBox().getType();
1784     }
1785     auto [boxTy, dest, resultEleSize] = consDescriptorPrefix(
1786         xbox, fir::unwrapRefType(xbox.getMemref().getType()), rewriter,
1787         xbox.getOutRank(), adaptor.getSubstr(), adaptor.getLenParams(),
1788         sourceBox, sourceBoxType);
1789     // Generate the triples in the dims field of the descriptor
1790     auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
1791     assert(!xbox.getShape().empty() && "must have a shape");
1792     unsigned shapeOffset = xbox.getShapeOperandIndex();
1793     bool hasShift = !xbox.getShift().empty();
1794     unsigned shiftOffset = xbox.getShiftOperandIndex();
1795     bool hasSlice = !xbox.getSlice().empty();
1796     unsigned sliceOffset = xbox.getSliceOperandIndex();
1797     mlir::Location loc = xbox.getLoc();
1798     mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
1799     mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
1800     mlir::Value prevPtrOff = one;
1801     mlir::Type eleTy = boxTy.getEleTy();
1802     const unsigned rank = xbox.getRank();
1803     llvm::SmallVector<mlir::Value> cstInteriorIndices;
1804     unsigned constRows = 0;
1805     mlir::Value ptrOffset = zero;
1806     mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.getMemref().getType());
1807     assert(mlir::isa<fir::SequenceType>(memEleTy));
1808     auto seqTy = mlir::cast<fir::SequenceType>(memEleTy);
1809     mlir::Type seqEleTy = seqTy.getEleTy();
1810     // Adjust the element scaling factor if the element is a dependent type.
1811     if (fir::hasDynamicSize(seqEleTy)) {
1812       if (auto charTy = mlir::dyn_cast<fir::CharacterType>(seqEleTy)) {
1813         // The GEP pointer type decays to llvm.ptr<i[width]>.
1814         // The scaling factor is the runtime value of the length.
1815         assert(!adaptor.getLenParams().empty());
1816         prevPtrOff = FIROpConversion::integerCast(
1817             loc, rewriter, i64Ty, adaptor.getLenParams().back());
1818       } else if (mlir::isa<fir::RecordType>(seqEleTy)) {
1819         // prevPtrOff = ;
1820         TODO(loc, "generate call to calculate size of PDT");
1821       } else {
1822         fir::emitFatalError(loc, "unexpected dynamic type");
1823       }
1824     } else {
1825       constRows = seqTy.getConstantRows();
1826     }
1827 
1828     const auto hasSubcomp = !xbox.getSubcomponent().empty();
1829     const bool hasSubstr = !xbox.getSubstr().empty();
1830     // Initial element stride that will be use to compute the step in
1831     // each dimension. Initially, this is the size of the input element.
1832     // Note that when there are no components/substring, the resultEleSize
1833     // that was previously computed matches the input element size.
1834     mlir::Value prevDimByteStride = resultEleSize;
1835     if (hasSubcomp) {
1836       // We have a subcomponent. The step value needs to be the number of
1837       // bytes per element (which is a derived type).
1838       prevDimByteStride =
1839           genTypeStrideInBytes(loc, i64Ty, rewriter, convertType(seqEleTy));
1840     } else if (hasSubstr) {
1841       // We have a substring. The step value needs to be the number of bytes
1842       // per CHARACTER element.
1843       auto charTy = mlir::cast<fir::CharacterType>(seqEleTy);
1844       if (fir::hasDynamicSize(charTy)) {
1845         prevDimByteStride =
1846             getCharacterByteSize(loc, rewriter, charTy, adaptor.getLenParams());
1847       } else {
1848         prevDimByteStride = genConstantIndex(
1849             loc, i64Ty, rewriter,
1850             charTy.getLen() * lowerTy().characterBitsize(charTy) / 8);
1851       }
1852     }
1853 
1854     // Process the array subspace arguments (shape, shift, etc.), if any,
1855     // translating everything to values in the descriptor wherever the entity
1856     // has a dynamic array dimension.
1857     for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
1858       mlir::Value extent =
1859           integerCast(loc, rewriter, i64Ty, operands[shapeOffset]);
1860       mlir::Value outerExtent = extent;
1861       bool skipNext = false;
1862       if (hasSlice) {
1863         mlir::Value off =
1864             integerCast(loc, rewriter, i64Ty, operands[sliceOffset]);
1865         mlir::Value adj = one;
1866         if (hasShift)
1867           adj = integerCast(loc, rewriter, i64Ty, operands[shiftOffset]);
1868         auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
1869         if (constRows > 0) {
1870           cstInteriorIndices.push_back(ao);
1871         } else {
1872           auto dimOff =
1873               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
1874           ptrOffset =
1875               rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
1876         }
1877         if (mlir::isa_and_nonnull<fir::UndefOp>(
1878                 xbox.getSlice()[3 * di + 1].getDefiningOp())) {
1879           // This dimension contains a scalar expression in the array slice op.
1880           // The dimension is loop invariant, will be dropped, and will not
1881           // appear in the descriptor.
1882           skipNext = true;
1883         }
1884       }
1885       if (!skipNext) {
1886         // store extent
1887         if (hasSlice)
1888           extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
1889                                         operands[sliceOffset + 1],
1890                                         operands[sliceOffset + 2], zero, i64Ty);
1891         // Lower bound is normalized to 0 for BIND(C) interoperability.
1892         mlir::Value lb = zero;
1893         const bool isaPointerOrAllocatable =
1894             mlir::isa<fir::PointerType, fir::HeapType>(eleTy);
1895         // Lower bound is defaults to 1 for POINTER, ALLOCATABLE, and
1896         // denormalized descriptors.
1897         if (isaPointerOrAllocatable || !normalizedLowerBound(xbox))
1898           lb = one;
1899         // If there is a shifted origin, and no fir.slice, and this is not
1900         // a normalized descriptor then use the value from the shift op as
1901         // the lower bound.
1902         if (hasShift && !(hasSlice || hasSubcomp || hasSubstr) &&
1903             (isaPointerOrAllocatable || !normalizedLowerBound(xbox))) {
1904           lb = integerCast(loc, rewriter, i64Ty, operands[shiftOffset]);
1905           auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
1906               loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
1907           lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one,
1908                                                      lb);
1909         }
1910         dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
1911 
1912         dest = insertExtent(rewriter, loc, dest, descIdx, extent);
1913 
1914         // store step (scaled by shaped extent)
1915         mlir::Value step = prevDimByteStride;
1916         if (hasSlice) {
1917           mlir::Value sliceStep =
1918               integerCast(loc, rewriter, i64Ty, operands[sliceOffset + 2]);
1919           step =
1920               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step, sliceStep);
1921         }
1922         dest = insertStride(rewriter, loc, dest, descIdx, step);
1923         ++descIdx;
1924       }
1925 
1926       // compute the stride and offset for the next natural dimension
1927       prevDimByteStride = rewriter.create<mlir::LLVM::MulOp>(
1928           loc, i64Ty, prevDimByteStride, outerExtent);
1929       if (constRows == 0)
1930         prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
1931                                                         outerExtent);
1932       else
1933         --constRows;
1934 
1935       // increment iterators
1936       ++shapeOffset;
1937       if (hasShift)
1938         ++shiftOffset;
1939       if (hasSlice)
1940         sliceOffset += 3;
1941     }
1942     mlir::Value base = adaptor.getMemref();
1943     if (hasSlice || hasSubcomp || hasSubstr) {
1944       // Shift the base address.
1945       llvm::SmallVector<mlir::Value> fieldIndices;
1946       std::optional<mlir::Value> substringOffset;
1947       if (hasSubcomp)
1948         getSubcomponentIndices(xbox, xbox.getMemref(), operands, fieldIndices);
1949       if (hasSubstr)
1950         substringOffset = operands[xbox.getSubstrOperandIndex()];
1951       mlir::Type llvmBaseType =
1952           convertType(fir::unwrapRefType(xbox.getMemref().getType()));
1953       base = genBoxOffsetGep(rewriter, loc, base, llvmBaseType, ptrOffset,
1954                              cstInteriorIndices, fieldIndices, substringOffset);
1955     }
1956     dest = insertBaseAddress(rewriter, loc, dest, base);
1957     if (fir::isDerivedTypeWithLenParams(boxTy))
1958       TODO(loc, "fir.embox codegen of derived with length parameters");
1959     mlir::Value result = placeInMemoryIfNotGlobalInit(
1960         rewriter, loc, boxTy, dest,
1961         isDeviceAllocation(xbox.getMemref(), adaptor.getMemref()));
1962     rewriter.replaceOp(xbox, result);
1963     return mlir::success();
1964   }
1965 
1966   /// Return true if `xbox` has a normalized lower bounds attribute. A box value
1967   /// that is neither a POINTER nor an ALLOCATABLE should be normalized to a
1968   /// zero origin lower bound for interoperability with BIND(C).
1969   inline static bool normalizedLowerBound(fir::cg::XEmboxOp xbox) {
1970     return xbox->hasAttr(fir::getNormalizedLowerBoundAttrName());
1971   }
1972 };
1973 
1974 /// Create a new box given a box reference.
1975 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
1976   using EmboxCommonConversion::EmboxCommonConversion;
1977 
1978   llvm::LogicalResult
1979   matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
1980                   mlir::ConversionPatternRewriter &rewriter) const override {
1981     mlir::Location loc = rebox.getLoc();
1982     mlir::Type idxTy = lowerTy().indexType();
1983     mlir::Value loweredBox = adaptor.getOperands()[0];
1984     mlir::ValueRange operands = adaptor.getOperands();
1985 
1986     // Inside a fir.global, the input box was produced as an llvm.struct<>
1987     // because objects cannot be handled in memory inside a fir.global body that
1988     // must be constant foldable. However, the type translation are not
1989     // contextual, so the fir.box<T> type of the operation that produced the
1990     // fir.box was translated to an llvm.ptr<llvm.struct<>> and the MLIR pass
1991     // manager inserted a builtin.unrealized_conversion_cast that was inserted
1992     // and needs to be removed here.
1993     if (isInGlobalOp(rewriter))
1994       if (auto unrealizedCast =
1995               loweredBox.getDefiningOp<mlir::UnrealizedConversionCastOp>())
1996         loweredBox = unrealizedCast.getInputs()[0];
1997 
1998     TypePair inputBoxTyPair = getBoxTypePair(rebox.getBox().getType());
1999 
2000     // Create new descriptor and fill its non-shape related data.
2001     llvm::SmallVector<mlir::Value, 2> lenParams;
2002     mlir::Type inputEleTy = getInputEleTy(rebox);
2003     if (auto charTy = mlir::dyn_cast<fir::CharacterType>(inputEleTy)) {
2004       if (charTy.hasConstantLen()) {
2005         mlir::Value len =
2006             genConstantIndex(loc, idxTy, rewriter, charTy.getLen());
2007         lenParams.emplace_back(len);
2008       } else {
2009         mlir::Value len = getElementSizeFromBox(loc, idxTy, inputBoxTyPair,
2010                                                 loweredBox, rewriter);
2011         if (charTy.getFKind() != 1) {
2012           assert(!isInGlobalOp(rewriter) &&
2013                  "character target in global op must have constant length");
2014           mlir::Value width =
2015               genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
2016           len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width);
2017         }
2018         lenParams.emplace_back(len);
2019       }
2020     } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(inputEleTy)) {
2021       if (recTy.getNumLenParams() != 0)
2022         TODO(loc, "reboxing descriptor of derived type with length parameters");
2023     }
2024 
2025     // Rebox on polymorphic entities needs to carry over the dynamic type.
2026     mlir::Value typeDescAddr;
2027     if (mlir::isa<fir::ClassType>(inputBoxTyPair.fir) &&
2028         mlir::isa<fir::ClassType>(rebox.getType()))
2029       typeDescAddr =
2030           loadTypeDescAddress(loc, inputBoxTyPair, loweredBox, rewriter);
2031 
2032     auto [boxTy, dest, eleSize] =
2033         consDescriptorPrefix(rebox, loweredBox, rewriter, rebox.getOutRank(),
2034                              adaptor.getSubstr(), lenParams, typeDescAddr);
2035 
2036     // Read input extents, strides, and base address
2037     llvm::SmallVector<mlir::Value> inputExtents;
2038     llvm::SmallVector<mlir::Value> inputStrides;
2039     const unsigned inputRank = rebox.getRank();
2040     for (unsigned dim = 0; dim < inputRank; ++dim) {
2041       llvm::SmallVector<mlir::Value, 3> dimInfo =
2042           getDimsFromBox(loc, {idxTy, idxTy, idxTy}, inputBoxTyPair, loweredBox,
2043                          dim, rewriter);
2044       inputExtents.emplace_back(dimInfo[1]);
2045       inputStrides.emplace_back(dimInfo[2]);
2046     }
2047 
2048     mlir::Value baseAddr =
2049         getBaseAddrFromBox(loc, inputBoxTyPair, loweredBox, rewriter);
2050 
2051     if (!rebox.getSlice().empty() || !rebox.getSubcomponent().empty())
2052       return sliceBox(rebox, adaptor, boxTy, dest, baseAddr, inputExtents,
2053                       inputStrides, operands, rewriter);
2054     return reshapeBox(rebox, adaptor, boxTy, dest, baseAddr, inputExtents,
2055                       inputStrides, operands, rewriter);
2056   }
2057 
2058 private:
2059   /// Write resulting shape and base address in descriptor, and replace rebox
2060   /// op.
2061   llvm::LogicalResult
2062   finalizeRebox(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
2063                 mlir::Type destBoxTy, mlir::Value dest, mlir::Value base,
2064                 mlir::ValueRange lbounds, mlir::ValueRange extents,
2065                 mlir::ValueRange strides,
2066                 mlir::ConversionPatternRewriter &rewriter) const {
2067     mlir::Location loc = rebox.getLoc();
2068     mlir::Value zero =
2069         genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
2070     mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1);
2071     for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) {
2072       mlir::Value extent = std::get<0>(iter.value());
2073       unsigned dim = iter.index();
2074       mlir::Value lb = one;
2075       if (!lbounds.empty()) {
2076         lb = lbounds[dim];
2077         auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
2078             loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
2079         lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one, lb);
2080       };
2081       dest = insertLowerBound(rewriter, loc, dest, dim, lb);
2082       dest = insertExtent(rewriter, loc, dest, dim, extent);
2083       dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value()));
2084     }
2085     dest = insertBaseAddress(rewriter, loc, dest, base);
2086     mlir::Value result = placeInMemoryIfNotGlobalInit(
2087         rewriter, rebox.getLoc(), destBoxTy, dest,
2088         isDeviceAllocation(rebox.getBox(), adaptor.getBox()));
2089     rewriter.replaceOp(rebox, result);
2090     return mlir::success();
2091   }
2092 
2093   // Apply slice given the base address, extents and strides of the input box.
2094   llvm::LogicalResult
2095   sliceBox(fir::cg::XReboxOp rebox, OpAdaptor adaptor, mlir::Type destBoxTy,
2096            mlir::Value dest, mlir::Value base, mlir::ValueRange inputExtents,
2097            mlir::ValueRange inputStrides, mlir::ValueRange operands,
2098            mlir::ConversionPatternRewriter &rewriter) const {
2099     mlir::Location loc = rebox.getLoc();
2100     mlir::Type byteTy = ::getI8Type(rebox.getContext());
2101     mlir::Type idxTy = lowerTy().indexType();
2102     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
2103     // Apply subcomponent and substring shift on base address.
2104     if (!rebox.getSubcomponent().empty() || !rebox.getSubstr().empty()) {
2105       // Cast to inputEleTy* so that a GEP can be used.
2106       mlir::Type inputEleTy = getInputEleTy(rebox);
2107       mlir::Type llvmBaseObjectType = convertType(inputEleTy);
2108       llvm::SmallVector<mlir::Value> fieldIndices;
2109       std::optional<mlir::Value> substringOffset;
2110       if (!rebox.getSubcomponent().empty())
2111         getSubcomponentIndices(rebox, rebox.getBox(), operands, fieldIndices);
2112       if (!rebox.getSubstr().empty())
2113         substringOffset = operands[rebox.getSubstrOperandIndex()];
2114       base = genBoxOffsetGep(rewriter, loc, base, llvmBaseObjectType, zero,
2115                              /*cstInteriorIndices=*/std::nullopt, fieldIndices,
2116                              substringOffset);
2117     }
2118 
2119     if (rebox.getSlice().empty())
2120       // The array section is of the form array[%component][substring], keep
2121       // the input array extents and strides.
2122       return finalizeRebox(rebox, adaptor, destBoxTy, dest, base,
2123                            /*lbounds*/ std::nullopt, inputExtents, inputStrides,
2124                            rewriter);
2125 
2126     // The slice is of the form array(i:j:k)[%component]. Compute new extents
2127     // and strides.
2128     llvm::SmallVector<mlir::Value> slicedExtents;
2129     llvm::SmallVector<mlir::Value> slicedStrides;
2130     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2131     const bool sliceHasOrigins = !rebox.getShift().empty();
2132     unsigned sliceOps = rebox.getSliceOperandIndex();
2133     unsigned shiftOps = rebox.getShiftOperandIndex();
2134     auto strideOps = inputStrides.begin();
2135     const unsigned inputRank = inputStrides.size();
2136     for (unsigned i = 0; i < inputRank;
2137          ++i, ++strideOps, ++shiftOps, sliceOps += 3) {
2138       mlir::Value sliceLb =
2139           integerCast(loc, rewriter, idxTy, operands[sliceOps]);
2140       mlir::Value inputStride = *strideOps; // already idxTy
2141       // Apply origin shift: base += (lb-shift)*input_stride
2142       mlir::Value sliceOrigin =
2143           sliceHasOrigins
2144               ? integerCast(loc, rewriter, idxTy, operands[shiftOps])
2145               : one;
2146       mlir::Value diff =
2147           rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin);
2148       mlir::Value offset =
2149           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride);
2150       // Strides from the fir.box are in bytes.
2151       base = genGEP(loc, byteTy, rewriter, base, offset);
2152       // Apply upper bound and step if this is a triplet. Otherwise, the
2153       // dimension is dropped and no extents/strides are computed.
2154       mlir::Value upper = operands[sliceOps + 1];
2155       const bool isTripletSlice =
2156           !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp());
2157       if (isTripletSlice) {
2158         mlir::Value step =
2159             integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]);
2160         // extent = ub-lb+step/step
2161         mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper);
2162         mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb,
2163                                                   sliceUb, step, zero, idxTy);
2164         slicedExtents.emplace_back(extent);
2165         // stride = step*input_stride
2166         mlir::Value stride =
2167             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride);
2168         slicedStrides.emplace_back(stride);
2169       }
2170     }
2171     return finalizeRebox(rebox, adaptor, destBoxTy, dest, base,
2172                          /*lbounds*/ std::nullopt, slicedExtents, slicedStrides,
2173                          rewriter);
2174   }
2175 
2176   /// Apply a new shape to the data described by a box given the base address,
2177   /// extents and strides of the box.
2178   llvm::LogicalResult
2179   reshapeBox(fir::cg::XReboxOp rebox, OpAdaptor adaptor, mlir::Type destBoxTy,
2180              mlir::Value dest, mlir::Value base, mlir::ValueRange inputExtents,
2181              mlir::ValueRange inputStrides, mlir::ValueRange operands,
2182              mlir::ConversionPatternRewriter &rewriter) const {
2183     mlir::ValueRange reboxShifts{
2184         operands.begin() + rebox.getShiftOperandIndex(),
2185         operands.begin() + rebox.getShiftOperandIndex() +
2186             rebox.getShift().size()};
2187     if (rebox.getShape().empty()) {
2188       // Only setting new lower bounds.
2189       return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, reboxShifts,
2190                            inputExtents, inputStrides, rewriter);
2191     }
2192 
2193     mlir::Location loc = rebox.getLoc();
2194 
2195     llvm::SmallVector<mlir::Value> newStrides;
2196     llvm::SmallVector<mlir::Value> newExtents;
2197     mlir::Type idxTy = lowerTy().indexType();
2198     // First stride from input box is kept. The rest is assumed contiguous
2199     // (it is not possible to reshape otherwise). If the input is scalar,
2200     // which may be OK if all new extents are ones, the stride does not
2201     // matter, use one.
2202     mlir::Value stride = inputStrides.empty()
2203                              ? genConstantIndex(loc, idxTy, rewriter, 1)
2204                              : inputStrides[0];
2205     for (unsigned i = 0; i < rebox.getShape().size(); ++i) {
2206       mlir::Value rawExtent = operands[rebox.getShapeOperandIndex() + i];
2207       mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent);
2208       newExtents.emplace_back(extent);
2209       newStrides.emplace_back(stride);
2210       // nextStride = extent * stride;
2211       stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
2212     }
2213     return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, reboxShifts,
2214                          newExtents, newStrides, rewriter);
2215   }
2216 
2217   /// Return scalar element type of the input box.
2218   static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) {
2219     auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.getBox().getType());
2220     if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty))
2221       return seqTy.getEleTy();
2222     return ty;
2223   }
2224 };
2225 
2226 /// Lower `fir.emboxproc` operation. Creates a procedure box.
2227 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2228 struct EmboxProcOpConversion : public fir::FIROpConversion<fir::EmboxProcOp> {
2229   using FIROpConversion::FIROpConversion;
2230 
2231   llvm::LogicalResult
2232   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
2233                   mlir::ConversionPatternRewriter &rewriter) const override {
2234     TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
2235     return mlir::failure();
2236   }
2237 };
2238 
2239 // Code shared between insert_value and extract_value Ops.
2240 struct ValueOpCommon {
2241   // Translate the arguments pertaining to any multidimensional array to
2242   // row-major order for LLVM-IR.
2243   static void toRowMajor(llvm::SmallVectorImpl<int64_t> &indices,
2244                          mlir::Type ty) {
2245     assert(ty && "type is null");
2246     const auto end = indices.size();
2247     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
2248       if (auto seq = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty)) {
2249         const auto dim = getDimension(seq);
2250         if (dim > 1) {
2251           auto ub = std::min(i + dim, end);
2252           std::reverse(indices.begin() + i, indices.begin() + ub);
2253           i += dim - 1;
2254         }
2255         ty = getArrayElementType(seq);
2256       } else if (auto st = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(ty)) {
2257         ty = st.getBody()[indices[i]];
2258       } else {
2259         llvm_unreachable("index into invalid type");
2260       }
2261     }
2262   }
2263 
2264   static llvm::SmallVector<int64_t>
2265   collectIndices(mlir::ConversionPatternRewriter &rewriter,
2266                  mlir::ArrayAttr arrAttr) {
2267     llvm::SmallVector<int64_t> indices;
2268     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
2269       if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(*i)) {
2270         indices.push_back(intAttr.getInt());
2271       } else {
2272         auto fieldName = mlir::cast<mlir::StringAttr>(*i).getValue();
2273         ++i;
2274         auto ty = mlir::cast<mlir::TypeAttr>(*i).getValue();
2275         auto index = mlir::cast<fir::RecordType>(ty).getFieldIndex(fieldName);
2276         indices.push_back(index);
2277       }
2278     }
2279     return indices;
2280   }
2281 
2282 private:
2283   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
2284     auto eleTy = ty.getElementType();
2285     while (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy))
2286       eleTy = arrTy.getElementType();
2287     return eleTy;
2288   }
2289 };
2290 
2291 namespace {
2292 /// Extract a subobject value from an ssa-value of aggregate type
2293 struct ExtractValueOpConversion
2294     : public fir::FIROpAndTypeConversion<fir::ExtractValueOp>,
2295       public ValueOpCommon {
2296   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2297 
2298   llvm::LogicalResult
2299   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
2300             mlir::ConversionPatternRewriter &rewriter) const override {
2301     mlir::ValueRange operands = adaptor.getOperands();
2302     auto indices = collectIndices(rewriter, extractVal.getCoor());
2303     toRowMajor(indices, operands[0].getType());
2304     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
2305         extractVal, operands[0], indices);
2306     return mlir::success();
2307   }
2308 };
2309 
2310 /// InsertValue is the generalized instruction for the composition of new
2311 /// aggregate type values.
2312 struct InsertValueOpConversion
2313     : public mlir::OpConversionPattern<fir::InsertValueOp>,
2314       public ValueOpCommon {
2315   using OpConversionPattern::OpConversionPattern;
2316 
2317   llvm::LogicalResult
2318   matchAndRewrite(fir::InsertValueOp insertVal, OpAdaptor adaptor,
2319                   mlir::ConversionPatternRewriter &rewriter) const override {
2320     mlir::ValueRange operands = adaptor.getOperands();
2321     auto indices = collectIndices(rewriter, insertVal.getCoor());
2322     toRowMajor(indices, operands[0].getType());
2323     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2324         insertVal, operands[0], operands[1], indices);
2325     return mlir::success();
2326   }
2327 };
2328 
2329 /// InsertOnRange inserts a value into a sequence over a range of offsets.
2330 struct InsertOnRangeOpConversion
2331     : public fir::FIROpAndTypeConversion<fir::InsertOnRangeOp> {
2332   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2333 
2334   // Increments an array of subscripts in a row major fasion.
2335   void incrementSubscripts(llvm::ArrayRef<int64_t> dims,
2336                            llvm::SmallVectorImpl<int64_t> &subscripts) const {
2337     for (size_t i = dims.size(); i > 0; --i) {
2338       if (++subscripts[i - 1] < dims[i - 1]) {
2339         return;
2340       }
2341       subscripts[i - 1] = 0;
2342     }
2343   }
2344 
2345   llvm::LogicalResult
2346   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
2347             mlir::ConversionPatternRewriter &rewriter) const override {
2348 
2349     llvm::SmallVector<std::int64_t> dims;
2350     auto type = adaptor.getOperands()[0].getType();
2351 
2352     // Iteratively extract the array dimensions from the type.
2353     while (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) {
2354       dims.push_back(t.getNumElements());
2355       type = t.getElementType();
2356     }
2357 
2358     llvm::SmallVector<std::int64_t> lBounds;
2359     llvm::SmallVector<std::int64_t> uBounds;
2360 
2361     // Unzip the upper and lower bound and convert to a row major format.
2362     mlir::DenseIntElementsAttr coor = range.getCoor();
2363     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
2364     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
2365       uBounds.push_back(*i++);
2366       lBounds.push_back(*i);
2367     }
2368 
2369     auto &subscripts = lBounds;
2370     auto loc = range.getLoc();
2371     mlir::Value lastOp = adaptor.getOperands()[0];
2372     mlir::Value insertVal = adaptor.getOperands()[1];
2373 
2374     while (subscripts != uBounds) {
2375       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2376           loc, lastOp, insertVal, subscripts);
2377 
2378       incrementSubscripts(dims, subscripts);
2379     }
2380 
2381     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2382         range, lastOp, insertVal, subscripts);
2383 
2384     return mlir::success();
2385   }
2386 };
2387 } // namespace
2388 
2389 namespace {
2390 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced,
2391 /// shifted etc. array.
2392 /// (See the static restriction on coordinate_of.) array_coor determines the
2393 /// coordinate (location) of a specific element.
2394 struct XArrayCoorOpConversion
2395     : public fir::FIROpAndTypeConversion<fir::cg::XArrayCoorOp> {
2396   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2397 
2398   llvm::LogicalResult
2399   doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type llvmPtrTy, OpAdaptor adaptor,
2400             mlir::ConversionPatternRewriter &rewriter) const override {
2401     auto loc = coor.getLoc();
2402     mlir::ValueRange operands = adaptor.getOperands();
2403     unsigned rank = coor.getRank();
2404     assert(coor.getIndices().size() == rank);
2405     assert(coor.getShape().empty() || coor.getShape().size() == rank);
2406     assert(coor.getShift().empty() || coor.getShift().size() == rank);
2407     assert(coor.getSlice().empty() || coor.getSlice().size() == 3 * rank);
2408     mlir::Type idxTy = lowerTy().indexType();
2409     unsigned indexOffset = coor.getIndicesOperandIndex();
2410     unsigned shapeOffset = coor.getShapeOperandIndex();
2411     unsigned shiftOffset = coor.getShiftOperandIndex();
2412     unsigned sliceOffset = coor.getSliceOperandIndex();
2413     auto sliceOps = coor.getSlice().begin();
2414     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2415     mlir::Value prevExt = one;
2416     mlir::Value offset = genConstantIndex(loc, idxTy, rewriter, 0);
2417     const bool isShifted = !coor.getShift().empty();
2418     const bool isSliced = !coor.getSlice().empty();
2419     const bool baseIsBoxed =
2420         mlir::isa<fir::BaseBoxType>(coor.getMemref().getType());
2421     TypePair baseBoxTyPair =
2422         baseIsBoxed ? getBoxTypePair(coor.getMemref().getType()) : TypePair{};
2423     mlir::LLVM::IntegerOverflowFlags nsw =
2424         mlir::LLVM::IntegerOverflowFlags::nsw;
2425 
2426     // For each dimension of the array, generate the offset calculation.
2427     for (unsigned i = 0; i < rank; ++i, ++indexOffset, ++shapeOffset,
2428                   ++shiftOffset, sliceOffset += 3, sliceOps += 3) {
2429       mlir::Value index =
2430           integerCast(loc, rewriter, idxTy, operands[indexOffset]);
2431       mlir::Value lb =
2432           isShifted ? integerCast(loc, rewriter, idxTy, operands[shiftOffset])
2433                     : one;
2434       mlir::Value step = one;
2435       bool normalSlice = isSliced;
2436       // Compute zero based index in dimension i of the element, applying
2437       // potential triplets and lower bounds.
2438       if (isSliced) {
2439         mlir::Value originalUb = *(sliceOps + 1);
2440         normalSlice =
2441             !mlir::isa_and_nonnull<fir::UndefOp>(originalUb.getDefiningOp());
2442         if (normalSlice)
2443           step = integerCast(loc, rewriter, idxTy, operands[sliceOffset + 2]);
2444       }
2445       auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb, nsw);
2446       mlir::Value diff =
2447           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step, nsw);
2448       if (normalSlice) {
2449         mlir::Value sliceLb =
2450             integerCast(loc, rewriter, idxTy, operands[sliceOffset]);
2451         auto adj =
2452             rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb, nsw);
2453         diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj, nsw);
2454       }
2455       // Update the offset given the stride and the zero based index `diff`
2456       // that was just computed.
2457       if (baseIsBoxed) {
2458         // Use stride in bytes from the descriptor.
2459         mlir::Value stride =
2460             getStrideFromBox(loc, baseBoxTyPair, operands[0], i, rewriter);
2461         auto sc =
2462             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride, nsw);
2463         offset =
2464             rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw);
2465       } else {
2466         // Use stride computed at last iteration.
2467         auto sc =
2468             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt, nsw);
2469         offset =
2470             rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw);
2471         // Compute next stride assuming contiguity of the base array
2472         // (in element number).
2473         auto nextExt = integerCast(loc, rewriter, idxTy, operands[shapeOffset]);
2474         prevExt = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt,
2475                                                      nextExt, nsw);
2476       }
2477     }
2478 
2479     // Add computed offset to the base address.
2480     if (baseIsBoxed) {
2481       // Working with byte offsets. The base address is read from the fir.box.
2482       // and used in i8* GEP to do the pointer arithmetic.
2483       mlir::Type byteTy = ::getI8Type(coor.getContext());
2484       mlir::Value base =
2485           getBaseAddrFromBox(loc, baseBoxTyPair, operands[0], rewriter);
2486       llvm::SmallVector<mlir::LLVM::GEPArg> args{offset};
2487       auto addr = rewriter.create<mlir::LLVM::GEPOp>(loc, llvmPtrTy, byteTy,
2488                                                      base, args);
2489       if (coor.getSubcomponent().empty()) {
2490         rewriter.replaceOp(coor, addr);
2491         return mlir::success();
2492       }
2493       // Cast the element address from void* to the derived type so that the
2494       // derived type members can be addresses via a GEP using the index of
2495       // components.
2496       mlir::Type elementType =
2497           getLlvmObjectTypeFromBoxType(coor.getMemref().getType());
2498       while (auto arrayTy =
2499                  mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(elementType))
2500         elementType = arrayTy.getElementType();
2501       args.clear();
2502       args.push_back(0);
2503       if (!coor.getLenParams().empty()) {
2504         // If type parameters are present, then we don't want to use a GEPOp
2505         // as below, as the LLVM struct type cannot be statically defined.
2506         TODO(loc, "derived type with type parameters");
2507       }
2508       llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices(
2509           loc, elementType,
2510           operands.slice(coor.getSubcomponentOperandIndex(),
2511                          coor.getSubcomponent().size()));
2512       args.append(indices.begin(), indices.end());
2513       rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, llvmPtrTy,
2514                                                      elementType, addr, args);
2515       return mlir::success();
2516     }
2517 
2518     // The array was not boxed, so it must be contiguous. offset is therefore an
2519     // element offset and the base type is kept in the GEP unless the element
2520     // type size is itself dynamic.
2521     mlir::Type objectTy = fir::unwrapRefType(coor.getMemref().getType());
2522     mlir::Type eleType = fir::unwrapSequenceType(objectTy);
2523     mlir::Type gepObjectType = convertType(eleType);
2524     llvm::SmallVector<mlir::LLVM::GEPArg> args;
2525     if (coor.getSubcomponent().empty()) {
2526       // No subcomponent.
2527       if (!coor.getLenParams().empty()) {
2528         // Type parameters. Adjust element size explicitly.
2529         auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType());
2530         assert(eleTy && "result must be a reference-like type");
2531         if (fir::characterWithDynamicLen(eleTy)) {
2532           assert(coor.getLenParams().size() == 1);
2533           auto length = integerCast(loc, rewriter, idxTy,
2534                                     operands[coor.getLenParamsOperandIndex()]);
2535           offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset,
2536                                                       length, nsw);
2537         } else {
2538           TODO(loc, "compute size of derived type with type parameters");
2539         }
2540       }
2541       args.push_back(offset);
2542     } else {
2543       // There are subcomponents.
2544       args.push_back(offset);
2545       llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices(
2546           loc, gepObjectType,
2547           operands.slice(coor.getSubcomponentOperandIndex(),
2548                          coor.getSubcomponent().size()));
2549       args.append(indices.begin(), indices.end());
2550     }
2551     rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
2552         coor, llvmPtrTy, gepObjectType, adaptor.getMemref(), args);
2553     return mlir::success();
2554   }
2555 };
2556 } // namespace
2557 
2558 /// Convert to (memory) reference to a reference to a subobject.
2559 /// The coordinate_of op is a Swiss army knife operation that can be used on
2560 /// (memory) references to records, arrays, complex, etc. as well as boxes.
2561 /// With unboxed arrays, there is the restriction that the array have a static
2562 /// shape in all but the last column.
2563 struct CoordinateOpConversion
2564     : public fir::FIROpAndTypeConversion<fir::CoordinateOp> {
2565   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2566 
2567   llvm::LogicalResult
2568   doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor,
2569             mlir::ConversionPatternRewriter &rewriter) const override {
2570     mlir::ValueRange operands = adaptor.getOperands();
2571 
2572     mlir::Location loc = coor.getLoc();
2573     mlir::Value base = operands[0];
2574     mlir::Type baseObjectTy = coor.getBaseType();
2575     mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2576     assert(objectTy && "fir.coordinate_of expects a reference type");
2577     mlir::Type llvmObjectTy = convertType(objectTy);
2578 
2579     // Complex type - basically, extract the real or imaginary part
2580     // FIXME: double check why this is done before the fir.box case below.
2581     if (fir::isa_complex(objectTy)) {
2582       mlir::Value gep =
2583           genGEP(loc, llvmObjectTy, rewriter, base, 0, operands[1]);
2584       rewriter.replaceOp(coor, gep);
2585       return mlir::success();
2586     }
2587 
2588     // Boxed type - get the base pointer from the box
2589     if (mlir::dyn_cast<fir::BaseBoxType>(baseObjectTy))
2590       return doRewriteBox(coor, operands, loc, rewriter);
2591 
2592     // Reference, pointer or a heap type
2593     if (mlir::isa<fir::ReferenceType, fir::PointerType, fir::HeapType>(
2594             baseObjectTy))
2595       return doRewriteRefOrPtr(coor, llvmObjectTy, operands, loc, rewriter);
2596 
2597     return rewriter.notifyMatchFailure(
2598         coor, "fir.coordinate_of base operand has unsupported type");
2599   }
2600 
2601   static unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) {
2602     return fir::hasDynamicSize(ty)
2603                ? op.getDefiningOp()
2604                      ->getAttrOfType<mlir::IntegerAttr>("field")
2605                      .getInt()
2606                : getConstantIntValue(op);
2607   }
2608 
2609   static bool hasSubDimensions(mlir::Type type) {
2610     return mlir::isa<fir::SequenceType, fir::RecordType, mlir::TupleType>(type);
2611   }
2612 
2613   /// Check whether this form of `!fir.coordinate_of` is supported. These
2614   /// additional checks are required, because we are not yet able to convert
2615   /// all valid forms of `!fir.coordinate_of`.
2616   /// TODO: Either implement the unsupported cases or extend the verifier
2617   /// in FIROps.cpp instead.
2618   static bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) {
2619     const std::size_t numOfCoors = coors.size();
2620     std::size_t i = 0;
2621     bool subEle = false;
2622     bool ptrEle = false;
2623     for (; i < numOfCoors; ++i) {
2624       mlir::Value nxtOpnd = coors[i];
2625       if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) {
2626         subEle = true;
2627         i += arrTy.getDimension() - 1;
2628         type = arrTy.getEleTy();
2629       } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(type)) {
2630         subEle = true;
2631         type = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2632       } else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(type)) {
2633         subEle = true;
2634         type = tupTy.getType(getConstantIntValue(nxtOpnd));
2635       } else {
2636         ptrEle = true;
2637       }
2638     }
2639     if (ptrEle)
2640       return (!subEle) && (numOfCoors == 1);
2641     return subEle && (i >= numOfCoors);
2642   }
2643 
2644   /// Walk the abstract memory layout and determine if the path traverses any
2645   /// array types with unknown shape. Return true iff all the array types have a
2646   /// constant shape along the path.
2647   static bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) {
2648     for (std::size_t i = 0, sz = coors.size(); i < sz; ++i) {
2649       mlir::Value nxtOpnd = coors[i];
2650       if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) {
2651         if (fir::sequenceWithNonConstantShape(arrTy))
2652           return false;
2653         i += arrTy.getDimension() - 1;
2654         type = arrTy.getEleTy();
2655       } else if (auto strTy = mlir::dyn_cast<fir::RecordType>(type)) {
2656         type = strTy.getType(getFieldNumber(strTy, nxtOpnd));
2657       } else if (auto strTy = mlir::dyn_cast<mlir::TupleType>(type)) {
2658         type = strTy.getType(getConstantIntValue(nxtOpnd));
2659       } else {
2660         return true;
2661       }
2662     }
2663     return true;
2664   }
2665 
2666 private:
2667   llvm::LogicalResult
2668   doRewriteBox(fir::CoordinateOp coor, mlir::ValueRange operands,
2669                mlir::Location loc,
2670                mlir::ConversionPatternRewriter &rewriter) const {
2671     mlir::Type boxObjTy = coor.getBaseType();
2672     assert(mlir::dyn_cast<fir::BaseBoxType>(boxObjTy) &&
2673            "This is not a `fir.box`");
2674     TypePair boxTyPair = getBoxTypePair(boxObjTy);
2675 
2676     mlir::Value boxBaseAddr = operands[0];
2677 
2678     // 1. SPECIAL CASE (uses `fir.len_param_index`):
2679     //   %box = ... : !fir.box<!fir.type<derived{len1:i32}>>
2680     //   %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}>
2681     //   %addr = coordinate_of %box, %lenp
2682     if (coor.getNumOperands() == 2) {
2683       mlir::Operation *coordinateDef =
2684           (*coor.getCoor().begin()).getDefiningOp();
2685       if (mlir::isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef))
2686         TODO(loc,
2687              "fir.coordinate_of - fir.len_param_index is not supported yet");
2688     }
2689 
2690     // 2. GENERAL CASE:
2691     // 2.1. (`fir.array`)
2692     //   %box = ... : !fix.box<!fir.array<?xU>>
2693     //   %idx = ... : index
2694     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<U>
2695     // 2.2 (`fir.derived`)
2696     //   %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>>
2697     //   %idx = ... : i32
2698     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<i32>
2699     // 2.3 (`fir.derived` inside `fir.array`)
2700     //   %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32,
2701     //   field_2:f32}>>> %idx1 = ... : index %idx2 = ... : i32 %resultAddr =
2702     //   coordinate_of %box, %idx1, %idx2 : !fir.ref<f32>
2703     // 2.4. TODO: Either document or disable any other case that the following
2704     //  implementation might convert.
2705     mlir::Value resultAddr =
2706         getBaseAddrFromBox(loc, boxTyPair, boxBaseAddr, rewriter);
2707     // Component Type
2708     auto cpnTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy);
2709     mlir::Type llvmPtrTy = ::getLlvmPtrType(coor.getContext());
2710     mlir::Type byteTy = ::getI8Type(coor.getContext());
2711     mlir::LLVM::IntegerOverflowFlags nsw =
2712         mlir::LLVM::IntegerOverflowFlags::nsw;
2713 
2714     for (unsigned i = 1, last = operands.size(); i < last; ++i) {
2715       if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2716         if (i != 1)
2717           TODO(loc, "fir.array nested inside other array and/or derived type");
2718         // Applies byte strides from the box. Ignore lower bound from box
2719         // since fir.coordinate_of indexes are zero based. Lowering takes care
2720         // of lower bound aspects. This both accounts for dynamically sized
2721         // types and non contiguous arrays.
2722         auto idxTy = lowerTy().indexType();
2723         mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0);
2724         for (unsigned index = i, lastIndex = i + arrTy.getDimension();
2725              index < lastIndex; ++index) {
2726           mlir::Value stride = getStrideFromBox(loc, boxTyPair, operands[0],
2727                                                 index - i, rewriter);
2728           auto sc = rewriter.create<mlir::LLVM::MulOp>(
2729               loc, idxTy, operands[index], stride, nsw);
2730           off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off, nsw);
2731         }
2732         resultAddr = rewriter.create<mlir::LLVM::GEPOp>(
2733             loc, llvmPtrTy, byteTy, resultAddr,
2734             llvm::ArrayRef<mlir::LLVM::GEPArg>{off});
2735         i += arrTy.getDimension() - 1;
2736         cpnTy = arrTy.getEleTy();
2737       } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy)) {
2738         mlir::Value nxtOpnd = operands[i];
2739         cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2740         auto llvmRecTy = lowerTy().convertType(recTy);
2741         resultAddr = rewriter.create<mlir::LLVM::GEPOp>(
2742             loc, llvmPtrTy, llvmRecTy, resultAddr,
2743             llvm::ArrayRef<mlir::LLVM::GEPArg>{0, nxtOpnd});
2744       } else {
2745         fir::emitFatalError(loc, "unexpected type in coordinate_of");
2746       }
2747     }
2748 
2749     rewriter.replaceOp(coor, resultAddr);
2750     return mlir::success();
2751   }
2752 
2753   llvm::LogicalResult
2754   doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type llvmObjectTy,
2755                     mlir::ValueRange operands, mlir::Location loc,
2756                     mlir::ConversionPatternRewriter &rewriter) const {
2757     mlir::Type baseObjectTy = coor.getBaseType();
2758 
2759     // Component Type
2760     mlir::Type cpnTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2761     bool hasSubdimension = hasSubDimensions(cpnTy);
2762     bool columnIsDeferred = !hasSubdimension;
2763 
2764     if (!supportedCoordinate(cpnTy, operands.drop_front(1)))
2765       TODO(loc, "unsupported combination of coordinate operands");
2766 
2767     const bool hasKnownShape =
2768         arraysHaveKnownShape(cpnTy, operands.drop_front(1));
2769 
2770     // If only the column is `?`, then we can simply place the column value in
2771     // the 0-th GEP position.
2772     if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2773       if (!hasKnownShape) {
2774         const unsigned sz = arrTy.getDimension();
2775         if (arraysHaveKnownShape(arrTy.getEleTy(),
2776                                  operands.drop_front(1 + sz))) {
2777           fir::SequenceType::ShapeRef shape = arrTy.getShape();
2778           bool allConst = true;
2779           for (unsigned i = 0; i < sz - 1; ++i) {
2780             if (shape[i] < 0) {
2781               allConst = false;
2782               break;
2783             }
2784           }
2785           if (allConst)
2786             columnIsDeferred = true;
2787         }
2788       }
2789     }
2790 
2791     if (fir::hasDynamicSize(fir::unwrapSequenceType(cpnTy)))
2792       return mlir::emitError(
2793           loc, "fir.coordinate_of with a dynamic element size is unsupported");
2794 
2795     if (hasKnownShape || columnIsDeferred) {
2796       llvm::SmallVector<mlir::LLVM::GEPArg> offs;
2797       if (hasKnownShape && hasSubdimension) {
2798         offs.push_back(0);
2799       }
2800       std::optional<int> dims;
2801       llvm::SmallVector<mlir::Value> arrIdx;
2802       for (std::size_t i = 1, sz = operands.size(); i < sz; ++i) {
2803         mlir::Value nxtOpnd = operands[i];
2804 
2805         if (!cpnTy)
2806           return mlir::emitError(loc, "invalid coordinate/check failed");
2807 
2808         // check if the i-th coordinate relates to an array
2809         if (dims) {
2810           arrIdx.push_back(nxtOpnd);
2811           int dimsLeft = *dims;
2812           if (dimsLeft > 1) {
2813             dims = dimsLeft - 1;
2814             continue;
2815           }
2816           cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType();
2817           // append array range in reverse (FIR arrays are column-major)
2818           offs.append(arrIdx.rbegin(), arrIdx.rend());
2819           arrIdx.clear();
2820           dims.reset();
2821           continue;
2822         }
2823         if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2824           int d = arrTy.getDimension() - 1;
2825           if (d > 0) {
2826             dims = d;
2827             arrIdx.push_back(nxtOpnd);
2828             continue;
2829           }
2830           cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType();
2831           offs.push_back(nxtOpnd);
2832           continue;
2833         }
2834 
2835         // check if the i-th coordinate relates to a field
2836         if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy))
2837           cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2838         else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(cpnTy))
2839           cpnTy = tupTy.getType(getConstantIntValue(nxtOpnd));
2840         else
2841           cpnTy = nullptr;
2842 
2843         offs.push_back(nxtOpnd);
2844       }
2845       if (dims)
2846         offs.append(arrIdx.rbegin(), arrIdx.rend());
2847       mlir::Value base = operands[0];
2848       mlir::Value retval = genGEP(loc, llvmObjectTy, rewriter, base, offs);
2849       rewriter.replaceOp(coor, retval);
2850       return mlir::success();
2851     }
2852 
2853     return mlir::emitError(
2854         loc, "fir.coordinate_of base operand has unsupported type");
2855   }
2856 };
2857 
2858 /// Convert `fir.field_index`. The conversion depends on whether the size of
2859 /// the record is static or dynamic.
2860 struct FieldIndexOpConversion : public fir::FIROpConversion<fir::FieldIndexOp> {
2861   using FIROpConversion::FIROpConversion;
2862 
2863   // NB: most field references should be resolved by this point
2864   llvm::LogicalResult
2865   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2866                   mlir::ConversionPatternRewriter &rewriter) const override {
2867     auto recTy = mlir::cast<fir::RecordType>(field.getOnType());
2868     unsigned index = recTy.getFieldIndex(field.getFieldId());
2869 
2870     if (!fir::hasDynamicSize(recTy)) {
2871       // Derived type has compile-time constant layout. Return index of the
2872       // component type in the parent type (to be used in GEP).
2873       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2874                                     field.getLoc(), rewriter, index)});
2875       return mlir::success();
2876     }
2877 
2878     // Derived type has compile-time constant layout. Call the compiler
2879     // generated function to determine the byte offset of the field at runtime.
2880     // This returns a non-constant.
2881     mlir::FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2882         field.getContext(), getOffsetMethodName(recTy, field.getFieldId()));
2883     mlir::NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2884     mlir::NamedAttribute fieldAttr = rewriter.getNamedAttr(
2885         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2886     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2887         field, lowerTy().offsetType(), adaptor.getOperands(),
2888         addLLVMOpBundleAttrs(rewriter, {callAttr, fieldAttr},
2889                              adaptor.getOperands().size()));
2890     return mlir::success();
2891   }
2892 
2893   // Re-Construct the name of the compiler generated method that calculates the
2894   // offset
2895   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2896                                                 llvm::StringRef field) {
2897     return recTy.getName().str() + "P." + field.str() + ".offset";
2898   }
2899 };
2900 
2901 /// Convert `fir.end`
2902 struct FirEndOpConversion : public fir::FIROpConversion<fir::FirEndOp> {
2903   using FIROpConversion::FIROpConversion;
2904 
2905   llvm::LogicalResult
2906   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
2907                   mlir::ConversionPatternRewriter &rewriter) const override {
2908     TODO(firEnd.getLoc(), "fir.end codegen");
2909     return mlir::failure();
2910   }
2911 };
2912 
2913 /// Lower `fir.type_desc` to a global addr.
2914 struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> {
2915   using FIROpConversion::FIROpConversion;
2916 
2917   llvm::LogicalResult
2918   matchAndRewrite(fir::TypeDescOp typeDescOp, OpAdaptor adaptor,
2919                   mlir::ConversionPatternRewriter &rewriter) const override {
2920     mlir::Type inTy = typeDescOp.getInType();
2921     assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
2922     auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
2923     auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
2924     std::string typeDescName =
2925         this->options.typeDescriptorsRenamedForAssembly
2926             ? fir::NameUniquer::getTypeDescriptorAssemblyName(
2927                   recordType.getName())
2928             : fir::NameUniquer::getTypeDescriptorName(recordType.getName());
2929     auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext());
2930     if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) {
2931       rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
2932           typeDescOp, llvmPtrTy, global.getSymName());
2933       return mlir::success();
2934     } else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
2935       rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
2936           typeDescOp, llvmPtrTy, global.getSymName());
2937       return mlir::success();
2938     }
2939     return mlir::failure();
2940   }
2941 };
2942 
2943 /// Lower `fir.has_value` operation to `llvm.return` operation.
2944 struct HasValueOpConversion
2945     : public mlir::OpConversionPattern<fir::HasValueOp> {
2946   using OpConversionPattern::OpConversionPattern;
2947 
2948   llvm::LogicalResult
2949   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
2950                   mlir::ConversionPatternRewriter &rewriter) const override {
2951     rewriter.replaceOpWithNewOp<mlir::LLVM::ReturnOp>(op,
2952                                                       adaptor.getOperands());
2953     return mlir::success();
2954   }
2955 };
2956 
2957 #ifndef NDEBUG
2958 // Check if attr's type is compatible with ty.
2959 //
2960 // This is done by comparing attr's element type, converted to LLVM type,
2961 // with ty's element type.
2962 //
2963 // Only integer and floating point (including complex) attributes are
2964 // supported. Also, attr is expected to have a TensorType and ty is expected
2965 // to be of LLVMArrayType. If any of the previous conditions is false, then
2966 // the specified attr and ty are not supported by this function and are
2967 // assumed to be compatible.
2968 static inline bool attributeTypeIsCompatible(mlir::MLIRContext *ctx,
2969                                              mlir::Attribute attr,
2970                                              mlir::Type ty) {
2971   // Get attr's LLVM element type.
2972   if (!attr)
2973     return true;
2974   auto intOrFpEleAttr = mlir::dyn_cast<mlir::DenseIntOrFPElementsAttr>(attr);
2975   if (!intOrFpEleAttr)
2976     return true;
2977   auto tensorTy = mlir::dyn_cast<mlir::TensorType>(intOrFpEleAttr.getType());
2978   if (!tensorTy)
2979     return true;
2980   mlir::Type attrEleTy =
2981       mlir::LLVMTypeConverter(ctx).convertType(tensorTy.getElementType());
2982 
2983   // Get ty's element type.
2984   auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty);
2985   if (!arrTy)
2986     return true;
2987   mlir::Type eleTy = arrTy.getElementType();
2988   while ((arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)))
2989     eleTy = arrTy.getElementType();
2990 
2991   return attrEleTy == eleTy;
2992 }
2993 #endif
2994 
2995 /// Lower `fir.global` operation to `llvm.global` operation.
2996 /// `fir.insert_on_range` operations are replaced with constant dense attribute
2997 /// if they are applied on the full range.
2998 struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
2999   using FIROpConversion::FIROpConversion;
3000 
3001   llvm::LogicalResult
3002   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
3003                   mlir::ConversionPatternRewriter &rewriter) const override {
3004 
3005     llvm::SmallVector<mlir::Attribute> dbgExprs;
3006 
3007     if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(global.getLoc())) {
3008       if (auto gvExprAttr = mlir::dyn_cast_if_present<mlir::ArrayAttr>(
3009               fusedLoc.getMetadata())) {
3010         for (auto attr : gvExprAttr.getAsRange<mlir::Attribute>())
3011           if (auto dbgAttr =
3012                   mlir::dyn_cast<mlir::LLVM::DIGlobalVariableExpressionAttr>(
3013                       attr))
3014             dbgExprs.push_back(dbgAttr);
3015       }
3016     }
3017 
3018     auto tyAttr = convertType(global.getType());
3019     if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType()))
3020       tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType);
3021     auto loc = global.getLoc();
3022     mlir::Attribute initAttr = global.getInitVal().value_or(mlir::Attribute());
3023     assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
3024     auto linkage = convertLinkage(global.getLinkName());
3025     auto isConst = global.getConstant().has_value();
3026     mlir::SymbolRefAttr comdat;
3027     llvm::ArrayRef<mlir::NamedAttribute> attrs;
3028     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
3029         loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0,
3030         false, false, comdat, attrs, dbgExprs);
3031 
3032     if (global.getAlignment() && *global.getAlignment() > 0)
3033       g.setAlignment(*global.getAlignment());
3034 
3035     auto module = global->getParentOfType<mlir::ModuleOp>();
3036     auto gpuMod = global->getParentOfType<mlir::gpu::GPUModuleOp>();
3037     // Add comdat if necessary
3038     if (fir::getTargetTriple(module).supportsCOMDAT() &&
3039         (linkage == mlir::LLVM::Linkage::Linkonce ||
3040          linkage == mlir::LLVM::Linkage::LinkonceODR) &&
3041         !gpuMod) {
3042       addComdat(g, rewriter, module);
3043     }
3044 
3045     // Apply all non-Fir::GlobalOp attributes to the LLVM::GlobalOp, preserving
3046     // them; whilst taking care not to apply attributes that are lowered in
3047     // other ways.
3048     llvm::SmallDenseSet<llvm::StringRef> elidedAttrsSet(
3049         global.getAttributeNames().begin(), global.getAttributeNames().end());
3050     for (auto &attr : global->getAttrs())
3051       if (!elidedAttrsSet.contains(attr.getName().strref()))
3052         g->setAttr(attr.getName(), attr.getValue());
3053 
3054     auto &gr = g.getInitializerRegion();
3055     rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end());
3056     if (!gr.empty()) {
3057       // Replace insert_on_range with a constant dense attribute if the
3058       // initialization is on the full range.
3059       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
3060       for (auto insertOp : insertOnRangeOps) {
3061         if (isFullRange(insertOp.getCoor(), insertOp.getType())) {
3062           auto seqTyAttr = convertType(insertOp.getType());
3063           auto *op = insertOp.getVal().getDefiningOp();
3064           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
3065           if (!constant) {
3066             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
3067             if (!convertOp)
3068               continue;
3069             constant = mlir::cast<mlir::arith::ConstantOp>(
3070                 convertOp.getValue().getDefiningOp());
3071           }
3072           mlir::Type vecType = mlir::VectorType::get(
3073               insertOp.getType().getShape(), constant.getType());
3074           auto denseAttr = mlir::DenseElementsAttr::get(
3075               mlir::cast<mlir::ShapedType>(vecType), constant.getValue());
3076           rewriter.setInsertionPointAfter(insertOp);
3077           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
3078               insertOp, seqTyAttr, denseAttr);
3079         }
3080       }
3081     }
3082     rewriter.eraseOp(global);
3083     return mlir::success();
3084   }
3085 
3086   bool isFullRange(mlir::DenseIntElementsAttr indexes,
3087                    fir::SequenceType seqTy) const {
3088     auto extents = seqTy.getShape();
3089     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
3090       return false;
3091     auto cur_index = indexes.value_begin<int64_t>();
3092     for (unsigned i = 0; i < indexes.size(); i += 2) {
3093       if (*(cur_index++) != 0)
3094         return false;
3095       if (*(cur_index++) != extents[i / 2] - 1)
3096         return false;
3097     }
3098     return true;
3099   }
3100 
3101   // TODO: String comparaison should be avoided. Replace linkName with an
3102   // enumeration.
3103   mlir::LLVM::Linkage
3104   convertLinkage(std::optional<llvm::StringRef> optLinkage) const {
3105     if (optLinkage) {
3106       auto name = *optLinkage;
3107       if (name == "internal")
3108         return mlir::LLVM::Linkage::Internal;
3109       if (name == "linkonce")
3110         return mlir::LLVM::Linkage::Linkonce;
3111       if (name == "linkonce_odr")
3112         return mlir::LLVM::Linkage::LinkonceODR;
3113       if (name == "common")
3114         return mlir::LLVM::Linkage::Common;
3115       if (name == "weak")
3116         return mlir::LLVM::Linkage::Weak;
3117     }
3118     return mlir::LLVM::Linkage::External;
3119   }
3120 
3121 private:
3122   static void addComdat(mlir::LLVM::GlobalOp &global,
3123                         mlir::ConversionPatternRewriter &rewriter,
3124                         mlir::ModuleOp module) {
3125     const char *comdatName = "__llvm_comdat";
3126     mlir::LLVM::ComdatOp comdatOp =
3127         module.lookupSymbol<mlir::LLVM::ComdatOp>(comdatName);
3128     if (!comdatOp) {
3129       comdatOp =
3130           rewriter.create<mlir::LLVM::ComdatOp>(module.getLoc(), comdatName);
3131     }
3132     if (auto select = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(
3133             global.getSymName()))
3134       return;
3135     mlir::OpBuilder::InsertionGuard guard(rewriter);
3136     rewriter.setInsertionPointToEnd(&comdatOp.getBody().back());
3137     auto selectorOp = rewriter.create<mlir::LLVM::ComdatSelectorOp>(
3138         comdatOp.getLoc(), global.getSymName(),
3139         mlir::LLVM::comdat::Comdat::Any);
3140     global.setComdatAttr(mlir::SymbolRefAttr::get(
3141         rewriter.getContext(), comdatName,
3142         mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr())));
3143   }
3144 };
3145 
3146 /// `fir.load` --> `llvm.load`
3147 struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
3148   using FIROpConversion::FIROpConversion;
3149 
3150   llvm::LogicalResult
3151   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
3152                   mlir::ConversionPatternRewriter &rewriter) const override {
3153 
3154     mlir::Type llvmLoadTy = convertObjectType(load.getType());
3155     if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(load.getType())) {
3156       // fir.box is a special case because it is considered an ssa value in
3157       // fir, but it is lowered as a pointer to a descriptor. So
3158       // fir.ref<fir.box> and fir.box end up being the same llvm types and
3159       // loading a fir.ref<fir.box> is implemented as taking a snapshot of the
3160       // descriptor value into a new descriptor temp.
3161       auto inputBoxStorage = adaptor.getOperands()[0];
3162       mlir::Value newBoxStorage;
3163       mlir::Location loc = load.getLoc();
3164       if (auto callOp = mlir::dyn_cast_or_null<mlir::LLVM::CallOp>(
3165               inputBoxStorage.getDefiningOp())) {
3166         if (callOp.getCallee() &&
3167             (*callOp.getCallee())
3168                 .starts_with(RTNAME_STRING(CUFAllocDescriptor))) {
3169           // CUDA Fortran local descriptor are allocated in managed memory. So
3170           // new storage must be allocated the same way.
3171           auto mod = load->getParentOfType<mlir::ModuleOp>();
3172           newBoxStorage =
3173               genCUFAllocDescriptor(loc, rewriter, mod, boxTy, lowerTy());
3174         }
3175       }
3176       if (!newBoxStorage)
3177         newBoxStorage = genAllocaAndAddrCastWithType(loc, llvmLoadTy,
3178                                                      defaultAlign, rewriter);
3179 
3180       TypePair boxTypePair{boxTy, llvmLoadTy};
3181       mlir::Value boxSize =
3182           computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter);
3183       auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>(
3184           loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false);
3185 
3186       if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
3187         memcpy.setTBAATags(*optionalTag);
3188       else
3189         attachTBAATag(memcpy, boxTy, boxTy, nullptr);
3190       rewriter.replaceOp(load, newBoxStorage);
3191     } else {
3192       auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(
3193           load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs());
3194       if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
3195         loadOp.setTBAATags(*optionalTag);
3196       else
3197         attachTBAATag(loadOp, load.getType(), load.getType(), nullptr);
3198       rewriter.replaceOp(load, loadOp.getResult());
3199     }
3200     return mlir::success();
3201   }
3202 };
3203 
3204 /// Lower `fir.no_reassoc` to LLVM IR dialect.
3205 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
3206 /// math flags?
3207 struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> {
3208   using FIROpConversion::FIROpConversion;
3209 
3210   llvm::LogicalResult
3211   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
3212                   mlir::ConversionPatternRewriter &rewriter) const override {
3213     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
3214     return mlir::success();
3215   }
3216 };
3217 
3218 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
3219                         std::optional<mlir::ValueRange> destOps,
3220                         mlir::ConversionPatternRewriter &rewriter,
3221                         mlir::Block *newBlock) {
3222   if (destOps)
3223     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, *destOps, newBlock,
3224                                           mlir::ValueRange());
3225   else
3226     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
3227 }
3228 
3229 template <typename A, typename B>
3230 static void genBrOp(A caseOp, mlir::Block *dest, std::optional<B> destOps,
3231                     mlir::ConversionPatternRewriter &rewriter) {
3232   if (destOps)
3233     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, *destOps, dest);
3234   else
3235     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, std::nullopt, dest);
3236 }
3237 
3238 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
3239                               mlir::Block *dest,
3240                               std::optional<mlir::ValueRange> destOps,
3241                               mlir::ConversionPatternRewriter &rewriter) {
3242   auto *thisBlock = rewriter.getInsertionBlock();
3243   auto *newBlock = createBlock(rewriter, dest);
3244   rewriter.setInsertionPointToEnd(thisBlock);
3245   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
3246   rewriter.setInsertionPointToEnd(newBlock);
3247 }
3248 
3249 /// Conversion of `fir.select_case`
3250 ///
3251 /// The `fir.select_case` operation is converted to a if-then-else ladder.
3252 /// Depending on the case condition type, one or several comparison and
3253 /// conditional branching can be generated.
3254 ///
3255 /// A point value case such as `case(4)`, a lower bound case such as
3256 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
3257 /// simple comparison between the selector value and the constant value in the
3258 /// case. The block associated with the case condition is then executed if
3259 /// the comparison succeed otherwise it branch to the next block with the
3260 /// comparison for the next case conditon.
3261 ///
3262 /// A closed interval case condition such as `case(7:10)` is converted with a
3263 /// first comparison and conditional branching for the lower bound. If
3264 /// successful, it branch to a second block with the comparison for the
3265 /// upper bound in the same case condition.
3266 ///
3267 /// TODO: lowering of CHARACTER type cases is not handled yet.
3268 struct SelectCaseOpConversion : public fir::FIROpConversion<fir::SelectCaseOp> {
3269   using FIROpConversion::FIROpConversion;
3270 
3271   llvm::LogicalResult
3272   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
3273                   mlir::ConversionPatternRewriter &rewriter) const override {
3274     unsigned conds = caseOp.getNumConditions();
3275     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
3276     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
3277     auto ty = caseOp.getSelector().getType();
3278     if (mlir::isa<fir::CharacterType>(ty)) {
3279       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
3280       return mlir::failure();
3281     }
3282     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
3283     auto loc = caseOp.getLoc();
3284     for (unsigned t = 0; t != conds; ++t) {
3285       mlir::Block *dest = caseOp.getSuccessor(t);
3286       std::optional<mlir::ValueRange> destOps =
3287           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
3288       std::optional<mlir::ValueRange> cmpOps =
3289           *caseOp.getCompareOperands(adaptor.getOperands(), t);
3290       mlir::Attribute attr = cases[t];
3291       assert(mlir::isa<mlir::UnitAttr>(attr) || cmpOps.has_value());
3292       if (mlir::isa<fir::PointIntervalAttr>(attr)) {
3293         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3294             loc, mlir::LLVM::ICmpPredicate::eq, selector, cmpOps->front());
3295         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3296         continue;
3297       }
3298       if (mlir::isa<fir::LowerBoundAttr>(attr)) {
3299         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3300             loc, mlir::LLVM::ICmpPredicate::sle, cmpOps->front(), selector);
3301         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3302         continue;
3303       }
3304       if (mlir::isa<fir::UpperBoundAttr>(attr)) {
3305         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3306             loc, mlir::LLVM::ICmpPredicate::sle, selector, cmpOps->front());
3307         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3308         continue;
3309       }
3310       if (mlir::isa<fir::ClosedIntervalAttr>(attr)) {
3311         mlir::Value caseArg0 = *cmpOps->begin();
3312         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
3313             loc, mlir::LLVM::ICmpPredicate::sle, caseArg0, selector);
3314         auto *thisBlock = rewriter.getInsertionBlock();
3315         auto *newBlock1 = createBlock(rewriter, dest);
3316         auto *newBlock2 = createBlock(rewriter, dest);
3317         rewriter.setInsertionPointToEnd(thisBlock);
3318         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp0, newBlock1, newBlock2);
3319         rewriter.setInsertionPointToEnd(newBlock1);
3320         mlir::Value caseArg1 = *(cmpOps->begin() + 1);
3321         auto cmp1 = rewriter.create<mlir::LLVM::ICmpOp>(
3322             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg1);
3323         genCondBrOp(loc, cmp1, dest, destOps, rewriter, newBlock2);
3324         rewriter.setInsertionPointToEnd(newBlock2);
3325         continue;
3326       }
3327       assert(mlir::isa<mlir::UnitAttr>(attr));
3328       assert((t + 1 == conds) && "unit must be last");
3329       genBrOp(caseOp, dest, destOps, rewriter);
3330     }
3331     return mlir::success();
3332   }
3333 };
3334 
3335 /// Helper function for converting select ops. This function converts the
3336 /// signature of the given block. If the new block signature is different from
3337 /// `expectedTypes`, returns "failure".
3338 static llvm::FailureOr<mlir::Block *>
3339 getConvertedBlock(mlir::ConversionPatternRewriter &rewriter,
3340                   const mlir::TypeConverter *converter,
3341                   mlir::Operation *branchOp, mlir::Block *block,
3342                   mlir::TypeRange expectedTypes) {
3343   assert(converter && "expected non-null type converter");
3344   assert(!block->isEntryBlock() && "entry blocks have no predecessors");
3345 
3346   // There is nothing to do if the types already match.
3347   if (block->getArgumentTypes() == expectedTypes)
3348     return block;
3349 
3350   // Compute the new block argument types and convert the block.
3351   std::optional<mlir::TypeConverter::SignatureConversion> conversion =
3352       converter->convertBlockSignature(block);
3353   if (!conversion)
3354     return rewriter.notifyMatchFailure(branchOp,
3355                                        "could not compute block signature");
3356   if (expectedTypes != conversion->getConvertedTypes())
3357     return rewriter.notifyMatchFailure(
3358         branchOp,
3359         "mismatch between adaptor operand types and computed block signature");
3360   return rewriter.applySignatureConversion(block, *conversion, converter);
3361 }
3362 
3363 template <typename OP>
3364 static llvm::LogicalResult
3365 selectMatchAndRewrite(const fir::LLVMTypeConverter &lowering, OP select,
3366                       typename OP::Adaptor adaptor,
3367                       mlir::ConversionPatternRewriter &rewriter,
3368                       const mlir::TypeConverter *converter) {
3369   unsigned conds = select.getNumConditions();
3370   auto cases = select.getCases().getValue();
3371   mlir::Value selector = adaptor.getSelector();
3372   auto loc = select.getLoc();
3373   assert(conds > 0 && "select must have cases");
3374 
3375   llvm::SmallVector<mlir::Block *> destinations;
3376   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
3377   mlir::Block *defaultDestination;
3378   mlir::ValueRange defaultOperands;
3379   llvm::SmallVector<int32_t> caseValues;
3380 
3381   for (unsigned t = 0; t != conds; ++t) {
3382     mlir::Block *dest = select.getSuccessor(t);
3383     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
3384     const mlir::Attribute &attr = cases[t];
3385     if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attr)) {
3386       destinationsOperands.push_back(destOps ? *destOps : mlir::ValueRange{});
3387       auto convertedBlock =
3388           getConvertedBlock(rewriter, converter, select, dest,
3389                             mlir::TypeRange(destinationsOperands.back()));
3390       if (mlir::failed(convertedBlock))
3391         return mlir::failure();
3392       destinations.push_back(*convertedBlock);
3393       caseValues.push_back(intAttr.getInt());
3394       continue;
3395     }
3396     assert(mlir::dyn_cast_or_null<mlir::UnitAttr>(attr));
3397     assert((t + 1 == conds) && "unit must be last");
3398     defaultOperands = destOps ? *destOps : mlir::ValueRange{};
3399     auto convertedBlock = getConvertedBlock(rewriter, converter, select, dest,
3400                                             mlir::TypeRange(defaultOperands));
3401     if (mlir::failed(convertedBlock))
3402       return mlir::failure();
3403     defaultDestination = *convertedBlock;
3404   }
3405 
3406   // LLVM::SwitchOp takes a i32 type for the selector.
3407   if (select.getSelector().getType() != rewriter.getI32Type())
3408     selector = rewriter.create<mlir::LLVM::TruncOp>(loc, rewriter.getI32Type(),
3409                                                     selector);
3410 
3411   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
3412       select, selector,
3413       /*defaultDestination=*/defaultDestination,
3414       /*defaultOperands=*/defaultOperands,
3415       /*caseValues=*/caseValues,
3416       /*caseDestinations=*/destinations,
3417       /*caseOperands=*/destinationsOperands,
3418       /*branchWeights=*/llvm::ArrayRef<std::int32_t>());
3419   return mlir::success();
3420 }
3421 
3422 /// conversion of fir::SelectOp to an if-then-else ladder
3423 struct SelectOpConversion : public fir::FIROpConversion<fir::SelectOp> {
3424   using FIROpConversion::FIROpConversion;
3425 
3426   llvm::LogicalResult
3427   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
3428                   mlir::ConversionPatternRewriter &rewriter) const override {
3429     return selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor,
3430                                                 rewriter, getTypeConverter());
3431   }
3432 };
3433 
3434 /// conversion of fir::SelectRankOp to an if-then-else ladder
3435 struct SelectRankOpConversion : public fir::FIROpConversion<fir::SelectRankOp> {
3436   using FIROpConversion::FIROpConversion;
3437 
3438   llvm::LogicalResult
3439   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
3440                   mlir::ConversionPatternRewriter &rewriter) const override {
3441     return selectMatchAndRewrite<fir::SelectRankOp>(
3442         lowerTy(), op, adaptor, rewriter, getTypeConverter());
3443   }
3444 };
3445 
3446 /// Lower `fir.select_type` to LLVM IR dialect.
3447 struct SelectTypeOpConversion : public fir::FIROpConversion<fir::SelectTypeOp> {
3448   using FIROpConversion::FIROpConversion;
3449 
3450   llvm::LogicalResult
3451   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
3452                   mlir::ConversionPatternRewriter &rewriter) const override {
3453     mlir::emitError(select.getLoc(),
3454                     "fir.select_type should have already been converted");
3455     return mlir::failure();
3456   }
3457 };
3458 
3459 /// `fir.store` --> `llvm.store`
3460 struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
3461   using FIROpConversion::FIROpConversion;
3462 
3463   llvm::LogicalResult
3464   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
3465                   mlir::ConversionPatternRewriter &rewriter) const override {
3466     mlir::Location loc = store.getLoc();
3467     mlir::Type storeTy = store.getValue().getType();
3468     mlir::Value llvmValue = adaptor.getValue();
3469     mlir::Value llvmMemref = adaptor.getMemref();
3470     mlir::LLVM::AliasAnalysisOpInterface newOp;
3471     if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) {
3472       mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
3473       // Always use memcpy because LLVM is not as effective at optimizing
3474       // aggregate loads/stores as it is optimizing memcpy.
3475       TypePair boxTypePair{boxTy, llvmBoxTy};
3476       mlir::Value boxSize =
3477           computeBoxSize(loc, boxTypePair, llvmValue, rewriter);
3478       newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
3479           loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false);
3480     } else {
3481       newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref);
3482     }
3483     if (std::optional<mlir::ArrayAttr> optionalTag = store.getTbaa())
3484       newOp.setTBAATags(*optionalTag);
3485     else
3486       attachTBAATag(newOp, storeTy, storeTy, nullptr);
3487     rewriter.eraseOp(store);
3488     return mlir::success();
3489   }
3490 };
3491 
3492 namespace {
3493 
3494 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
3495 /// the character buffer and one for the buffer length.
3496 struct UnboxCharOpConversion : public fir::FIROpConversion<fir::UnboxCharOp> {
3497   using FIROpConversion::FIROpConversion;
3498 
3499   llvm::LogicalResult
3500   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
3501                   mlir::ConversionPatternRewriter &rewriter) const override {
3502     mlir::Type lenTy = convertType(unboxchar.getType(1));
3503     mlir::Value tuple = adaptor.getOperands()[0];
3504 
3505     mlir::Location loc = unboxchar.getLoc();
3506     mlir::Value ptrToBuffer =
3507         rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 0);
3508 
3509     auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 1);
3510     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
3511 
3512     rewriter.replaceOp(unboxchar,
3513                        llvm::ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
3514     return mlir::success();
3515   }
3516 };
3517 
3518 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
3519 /// components.
3520 /// TODO: Part of supporting Fortran 2003 procedure pointers.
3521 struct UnboxProcOpConversion : public fir::FIROpConversion<fir::UnboxProcOp> {
3522   using FIROpConversion::FIROpConversion;
3523 
3524   llvm::LogicalResult
3525   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
3526                   mlir::ConversionPatternRewriter &rewriter) const override {
3527     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
3528     return mlir::failure();
3529   }
3530 };
3531 
3532 /// convert to LLVM IR dialect `undef`
3533 struct UndefOpConversion : public fir::FIROpConversion<fir::UndefOp> {
3534   using FIROpConversion::FIROpConversion;
3535 
3536   llvm::LogicalResult
3537   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
3538                   mlir::ConversionPatternRewriter &rewriter) const override {
3539     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
3540         undef, convertType(undef.getType()));
3541     return mlir::success();
3542   }
3543 };
3544 
3545 struct ZeroOpConversion : public fir::FIROpConversion<fir::ZeroOp> {
3546   using FIROpConversion::FIROpConversion;
3547 
3548   llvm::LogicalResult
3549   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
3550                   mlir::ConversionPatternRewriter &rewriter) const override {
3551     mlir::Type ty = convertType(zero.getType());
3552     rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(zero, ty);
3553     return mlir::success();
3554   }
3555 };
3556 
3557 /// `fir.unreachable` --> `llvm.unreachable`
3558 struct UnreachableOpConversion
3559     : public fir::FIROpConversion<fir::UnreachableOp> {
3560   using FIROpConversion::FIROpConversion;
3561 
3562   llvm::LogicalResult
3563   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
3564                   mlir::ConversionPatternRewriter &rewriter) const override {
3565     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
3566     return mlir::success();
3567   }
3568 };
3569 
3570 /// `fir.is_present` -->
3571 /// ```
3572 ///  %0 = llvm.mlir.constant(0 : i64)
3573 ///  %1 = llvm.ptrtoint %0
3574 ///  %2 = llvm.icmp "ne" %1, %0 : i64
3575 /// ```
3576 struct IsPresentOpConversion : public fir::FIROpConversion<fir::IsPresentOp> {
3577   using FIROpConversion::FIROpConversion;
3578 
3579   llvm::LogicalResult
3580   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
3581                   mlir::ConversionPatternRewriter &rewriter) const override {
3582     mlir::Type idxTy = lowerTy().indexType();
3583     mlir::Location loc = isPresent.getLoc();
3584     auto ptr = adaptor.getOperands()[0];
3585 
3586     if (mlir::isa<fir::BoxCharType>(isPresent.getVal().getType())) {
3587       [[maybe_unused]] auto structTy =
3588           mlir::cast<mlir::LLVM::LLVMStructType>(ptr.getType());
3589       assert(!structTy.isOpaque() && !structTy.getBody().empty());
3590 
3591       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ptr, 0);
3592     }
3593     mlir::LLVM::ConstantOp c0 =
3594         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
3595     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
3596     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3597         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
3598 
3599     return mlir::success();
3600   }
3601 };
3602 
3603 /// Create value signaling an absent optional argument in a call, e.g.
3604 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.zero : !llvm.ptr<i64>`
3605 struct AbsentOpConversion : public fir::FIROpConversion<fir::AbsentOp> {
3606   using FIROpConversion::FIROpConversion;
3607 
3608   llvm::LogicalResult
3609   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
3610                   mlir::ConversionPatternRewriter &rewriter) const override {
3611     mlir::Type ty = convertType(absent.getType());
3612     rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(absent, ty);
3613     return mlir::success();
3614   }
3615 };
3616 
3617 //
3618 // Primitive operations on Complex types
3619 //
3620 
3621 template <typename OPTY>
3622 static inline mlir::LLVM::FastmathFlagsAttr getLLVMFMFAttr(OPTY op) {
3623   return mlir::LLVM::FastmathFlagsAttr::get(
3624       op.getContext(),
3625       mlir::arith::convertArithFastMathFlagsToLLVM(op.getFastmath()));
3626 }
3627 
3628 /// Generate inline code for complex addition/subtraction
3629 template <typename LLVMOP, typename OPTY>
3630 static mlir::LLVM::InsertValueOp
3631 complexSum(OPTY sumop, mlir::ValueRange opnds,
3632            mlir::ConversionPatternRewriter &rewriter,
3633            const fir::LLVMTypeConverter &lowering) {
3634   mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(sumop);
3635   mlir::Value a = opnds[0];
3636   mlir::Value b = opnds[1];
3637   auto loc = sumop.getLoc();
3638   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
3639   mlir::Type ty = lowering.convertType(sumop.getType());
3640   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3641   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3642   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3643   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3644   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1, fmf);
3645   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1, fmf);
3646   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3647   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r0, rx, 0);
3648   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ry, 1);
3649 }
3650 } // namespace
3651 
3652 namespace {
3653 struct AddcOpConversion : public fir::FIROpConversion<fir::AddcOp> {
3654   using FIROpConversion::FIROpConversion;
3655 
3656   llvm::LogicalResult
3657   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
3658                   mlir::ConversionPatternRewriter &rewriter) const override {
3659     // given: (x + iy) + (x' + iy')
3660     // result: (x + x') + i(y + y')
3661     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
3662                                             rewriter, lowerTy());
3663     rewriter.replaceOp(addc, r.getResult());
3664     return mlir::success();
3665   }
3666 };
3667 
3668 struct SubcOpConversion : public fir::FIROpConversion<fir::SubcOp> {
3669   using FIROpConversion::FIROpConversion;
3670 
3671   llvm::LogicalResult
3672   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
3673                   mlir::ConversionPatternRewriter &rewriter) const override {
3674     // given: (x + iy) - (x' + iy')
3675     // result: (x - x') + i(y - y')
3676     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
3677                                             rewriter, lowerTy());
3678     rewriter.replaceOp(subc, r.getResult());
3679     return mlir::success();
3680   }
3681 };
3682 
3683 /// Inlined complex multiply
3684 struct MulcOpConversion : public fir::FIROpConversion<fir::MulcOp> {
3685   using FIROpConversion::FIROpConversion;
3686 
3687   llvm::LogicalResult
3688   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
3689                   mlir::ConversionPatternRewriter &rewriter) const override {
3690     // TODO: Can we use a call to __muldc3 ?
3691     // given: (x + iy) * (x' + iy')
3692     // result: (xx'-yy')+i(xy'+yx')
3693     mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(mulc);
3694     mlir::Value a = adaptor.getOperands()[0];
3695     mlir::Value b = adaptor.getOperands()[1];
3696     auto loc = mulc.getLoc();
3697     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
3698     mlir::Type ty = convertType(mulc.getType());
3699     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3700     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3701     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3702     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3703     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf);
3704     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf);
3705     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf);
3706     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx, fmf);
3707     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf);
3708     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy, fmf);
3709     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3710     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0);
3711     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1);
3712     rewriter.replaceOp(mulc, r0.getResult());
3713     return mlir::success();
3714   }
3715 };
3716 
3717 /// Inlined complex division
3718 struct DivcOpConversion : public fir::FIROpConversion<fir::DivcOp> {
3719   using FIROpConversion::FIROpConversion;
3720 
3721   llvm::LogicalResult
3722   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
3723                   mlir::ConversionPatternRewriter &rewriter) const override {
3724     // TODO: Can we use a call to __divdc3 instead?
3725     // Just generate inline code for now.
3726     // given: (x + iy) / (x' + iy')
3727     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
3728     mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(divc);
3729     mlir::Value a = adaptor.getOperands()[0];
3730     mlir::Value b = adaptor.getOperands()[1];
3731     auto loc = divc.getLoc();
3732     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
3733     mlir::Type ty = convertType(divc.getType());
3734     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3735     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3736     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3737     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3738     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf);
3739     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1, fmf);
3740     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf);
3741     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf);
3742     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf);
3743     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1, fmf);
3744     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1, fmf);
3745     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy, fmf);
3746     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy, fmf);
3747     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d, fmf);
3748     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d, fmf);
3749     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3750     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0);
3751     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1);
3752     rewriter.replaceOp(divc, r0.getResult());
3753     return mlir::success();
3754   }
3755 };
3756 
3757 /// Inlined complex negation
3758 struct NegcOpConversion : public fir::FIROpConversion<fir::NegcOp> {
3759   using FIROpConversion::FIROpConversion;
3760 
3761   llvm::LogicalResult
3762   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
3763                   mlir::ConversionPatternRewriter &rewriter) const override {
3764     // given: -(x + iy)
3765     // result: -x - iy
3766     auto eleTy = convertType(getComplexEleTy(neg.getType()));
3767     auto loc = neg.getLoc();
3768     mlir::Value o0 = adaptor.getOperands()[0];
3769     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 0);
3770     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 1);
3771     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
3772     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
3773     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, o0, nrp, 0);
3774     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, r, nip, 1);
3775     return mlir::success();
3776   }
3777 };
3778 
3779 struct BoxOffsetOpConversion : public fir::FIROpConversion<fir::BoxOffsetOp> {
3780   using FIROpConversion::FIROpConversion;
3781 
3782   llvm::LogicalResult
3783   matchAndRewrite(fir::BoxOffsetOp boxOffset, OpAdaptor adaptor,
3784                   mlir::ConversionPatternRewriter &rewriter) const override {
3785 
3786     mlir::Type pty = ::getLlvmPtrType(boxOffset.getContext());
3787     mlir::Type boxType = fir::unwrapRefType(boxOffset.getBoxRef().getType());
3788     mlir::Type llvmBoxTy =
3789         lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(boxType));
3790     int fieldId = boxOffset.getField() == fir::BoxFieldAttr::derived_type
3791                       ? getTypeDescFieldId(boxType)
3792                       : kAddrPosInBox;
3793     rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
3794         boxOffset, pty, llvmBoxTy, adaptor.getBoxRef(),
3795         llvm::ArrayRef<mlir::LLVM::GEPArg>{0, fieldId});
3796     return mlir::success();
3797   }
3798 };
3799 
3800 /// Conversion pattern for operation that must be dead. The information in these
3801 /// operations is used by other operation. At this point they should not have
3802 /// anymore uses.
3803 /// These operations are normally dead after the pre-codegen pass.
3804 template <typename FromOp>
3805 struct MustBeDeadConversion : public fir::FIROpConversion<FromOp> {
3806   explicit MustBeDeadConversion(const fir::LLVMTypeConverter &lowering,
3807                                 const fir::FIRToLLVMPassOptions &options)
3808       : fir::FIROpConversion<FromOp>(lowering, options) {}
3809   using OpAdaptor = typename FromOp::Adaptor;
3810 
3811   llvm::LogicalResult
3812   matchAndRewrite(FromOp op, OpAdaptor adaptor,
3813                   mlir::ConversionPatternRewriter &rewriter) const final {
3814     if (!op->getUses().empty())
3815       return rewriter.notifyMatchFailure(op, "op must be dead");
3816     rewriter.eraseOp(op);
3817     return mlir::success();
3818   }
3819 };
3820 
3821 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
3822   using MustBeDeadConversion::MustBeDeadConversion;
3823 };
3824 
3825 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
3826   using MustBeDeadConversion::MustBeDeadConversion;
3827 };
3828 
3829 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
3830   using MustBeDeadConversion::MustBeDeadConversion;
3831 };
3832 
3833 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
3834   using MustBeDeadConversion::MustBeDeadConversion;
3835 };
3836 
3837 } // namespace
3838 
3839 namespace {
3840 class RenameMSVCLibmCallees
3841     : public mlir::OpRewritePattern<mlir::LLVM::CallOp> {
3842 public:
3843   using OpRewritePattern::OpRewritePattern;
3844 
3845   llvm::LogicalResult
3846   matchAndRewrite(mlir::LLVM::CallOp op,
3847                   mlir::PatternRewriter &rewriter) const override {
3848     rewriter.startOpModification(op);
3849     auto callee = op.getCallee();
3850     if (callee)
3851       if (*callee == "hypotf")
3852         op.setCalleeAttr(mlir::SymbolRefAttr::get(op.getContext(), "_hypotf"));
3853 
3854     rewriter.finalizeOpModification(op);
3855     return mlir::success();
3856   }
3857 };
3858 
3859 class RenameMSVCLibmFuncs
3860     : public mlir::OpRewritePattern<mlir::LLVM::LLVMFuncOp> {
3861 public:
3862   using OpRewritePattern::OpRewritePattern;
3863 
3864   llvm::LogicalResult
3865   matchAndRewrite(mlir::LLVM::LLVMFuncOp op,
3866                   mlir::PatternRewriter &rewriter) const override {
3867     rewriter.startOpModification(op);
3868     if (op.getSymName() == "hypotf")
3869       op.setSymNameAttr(rewriter.getStringAttr("_hypotf"));
3870     rewriter.finalizeOpModification(op);
3871     return mlir::success();
3872   }
3873 };
3874 } // namespace
3875 
3876 namespace {
3877 /// Convert FIR dialect to LLVM dialect
3878 ///
3879 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
3880 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
3881 class FIRToLLVMLowering
3882     : public fir::impl::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
3883 public:
3884   FIRToLLVMLowering() = default;
3885   FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {}
3886   mlir::ModuleOp getModule() { return getOperation(); }
3887 
3888   void runOnOperation() override final {
3889     auto mod = getModule();
3890     if (!forcedTargetTriple.empty())
3891       fir::setTargetTriple(mod, forcedTargetTriple);
3892 
3893     if (!forcedDataLayout.empty()) {
3894       llvm::DataLayout dl(forcedDataLayout);
3895       fir::support::setMLIRDataLayout(mod, dl);
3896     }
3897 
3898     if (!forcedTargetCPU.empty())
3899       fir::setTargetCPU(mod, forcedTargetCPU);
3900 
3901     if (!forcedTuneCPU.empty())
3902       fir::setTuneCPU(mod, forcedTuneCPU);
3903 
3904     if (!forcedTargetFeatures.empty())
3905       fir::setTargetFeatures(mod, forcedTargetFeatures);
3906 
3907     if (typeDescriptorsRenamedForAssembly)
3908       options.typeDescriptorsRenamedForAssembly =
3909           typeDescriptorsRenamedForAssembly;
3910 
3911     // Run dynamic pass pipeline for converting Math dialect
3912     // operations into other dialects (llvm, func, etc.).
3913     // Some conversions of Math operations cannot be done
3914     // by just using conversion patterns. This is true for
3915     // conversions that affect the ModuleOp, e.g. create new
3916     // function operations in it. We have to run such conversions
3917     // as passes here.
3918     mlir::OpPassManager mathConvertionPM("builtin.module");
3919 
3920     bool isAMDGCN = fir::getTargetTriple(mod).isAMDGCN();
3921     // If compiling for AMD target some math operations must be lowered to AMD
3922     // GPU library calls, the rest can be converted to LLVM intrinsics, which
3923     // is handled in the mathToLLVM conversion. The lowering to libm calls is
3924     // not needed since all math operations are handled this way.
3925     if (isAMDGCN)
3926       mathConvertionPM.addPass(mlir::createConvertMathToROCDL());
3927 
3928     // Convert math::FPowI operations to inline implementation
3929     // only if the exponent's width is greater than 32, otherwise,
3930     // it will be lowered to LLVM intrinsic operation by a later conversion.
3931     mlir::ConvertMathToFuncsOptions mathToFuncsOptions{};
3932     mathToFuncsOptions.minWidthOfFPowIExponent = 33;
3933     mathConvertionPM.addPass(
3934         mlir::createConvertMathToFuncs(mathToFuncsOptions));
3935     mathConvertionPM.addPass(mlir::createConvertComplexToStandardPass());
3936     // Convert Math dialect operations into LLVM dialect operations.
3937     // There is no way to prefer MathToLLVM patterns over MathToLibm
3938     // patterns (applied below), so we have to run MathToLLVM conversion here.
3939     mathConvertionPM.addNestedPass<mlir::func::FuncOp>(
3940         mlir::createConvertMathToLLVMPass());
3941     if (mlir::failed(runPipeline(mathConvertionPM, mod)))
3942       return signalPassFailure();
3943 
3944     std::optional<mlir::DataLayout> dl =
3945         fir::support::getOrSetDataLayout(mod, /*allowDefaultLayout=*/true);
3946     if (!dl) {
3947       mlir::emitError(mod.getLoc(),
3948                       "module operation must carry a data layout attribute "
3949                       "to generate llvm IR from FIR");
3950       signalPassFailure();
3951       return;
3952     }
3953 
3954     auto *context = getModule().getContext();
3955     fir::LLVMTypeConverter typeConverter{getModule(),
3956                                          options.applyTBAA || applyTBAA,
3957                                          options.forceUnifiedTBAATree, *dl};
3958     mlir::RewritePatternSet pattern(context);
3959     fir::populateFIRToLLVMConversionPatterns(typeConverter, pattern, options);
3960     mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern);
3961     mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern);
3962     mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, pattern);
3963     mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter,
3964                                                           pattern);
3965     mlir::cf::populateAssertToLLVMConversionPattern(typeConverter, pattern);
3966     // Math operations that have not been converted yet must be converted
3967     // to Libm.
3968     if (!isAMDGCN)
3969       mlir::populateMathToLibmConversionPatterns(pattern);
3970     mlir::populateComplexToLLVMConversionPatterns(typeConverter, pattern);
3971     mlir::populateVectorToLLVMConversionPatterns(typeConverter, pattern);
3972 
3973     // Flang specific overloads for OpenMP operations, to allow for special
3974     // handling of things like Box types.
3975     fir::populateOpenMPFIRToLLVMConversionPatterns(typeConverter, pattern);
3976 
3977     mlir::ConversionTarget target{*context};
3978     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3979     // The OpenMP dialect is legal for Operations without regions, for those
3980     // which contains regions it is legal if the region contains only the
3981     // LLVM dialect. Add OpenMP dialect as a legal dialect for conversion and
3982     // legalize conversion of OpenMP operations without regions.
3983     mlir::configureOpenMPToLLVMConversionLegality(target, typeConverter);
3984     target.addLegalDialect<mlir::omp::OpenMPDialect>();
3985     target.addLegalDialect<mlir::acc::OpenACCDialect>();
3986     target.addLegalDialect<mlir::gpu::GPUDialect>();
3987 
3988     // required NOPs for applying a full conversion
3989     target.addLegalOp<mlir::ModuleOp>();
3990 
3991     // If we're on Windows, we might need to rename some libm calls.
3992     bool isMSVC = fir::getTargetTriple(mod).isOSMSVCRT();
3993     if (isMSVC) {
3994       pattern.insert<RenameMSVCLibmCallees, RenameMSVCLibmFuncs>(context);
3995 
3996       target.addDynamicallyLegalOp<mlir::LLVM::CallOp>(
3997           [](mlir::LLVM::CallOp op) {
3998             auto callee = op.getCallee();
3999             if (!callee)
4000               return true;
4001             return *callee != "hypotf";
4002           });
4003       target.addDynamicallyLegalOp<mlir::LLVM::LLVMFuncOp>(
4004           [](mlir::LLVM::LLVMFuncOp op) {
4005             return op.getSymName() != "hypotf";
4006           });
4007     }
4008 
4009     // apply the patterns
4010     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
4011                                                std::move(pattern)))) {
4012       signalPassFailure();
4013     }
4014 
4015     // Run pass to add comdats to functions that have weak linkage on relevant
4016     // platforms
4017     if (fir::getTargetTriple(mod).supportsCOMDAT()) {
4018       mlir::OpPassManager comdatPM("builtin.module");
4019       comdatPM.addPass(mlir::LLVM::createLLVMAddComdats());
4020       if (mlir::failed(runPipeline(comdatPM, mod)))
4021         return signalPassFailure();
4022     }
4023   }
4024 
4025 private:
4026   fir::FIRToLLVMPassOptions options;
4027 };
4028 
4029 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
4030 struct LLVMIRLoweringPass
4031     : public mlir::PassWrapper<LLVMIRLoweringPass,
4032                                mlir::OperationPass<mlir::ModuleOp>> {
4033   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LLVMIRLoweringPass)
4034 
4035   LLVMIRLoweringPass(llvm::raw_ostream &output, fir::LLVMIRLoweringPrinter p)
4036       : output{output}, printer{p} {}
4037 
4038   mlir::ModuleOp getModule() { return getOperation(); }
4039 
4040   void runOnOperation() override final {
4041     auto *ctx = getModule().getContext();
4042     auto optName = getModule().getName();
4043     llvm::LLVMContext llvmCtx;
4044     if (auto llvmModule = mlir::translateModuleToLLVMIR(
4045             getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
4046       printer(*llvmModule, output);
4047       return;
4048     }
4049 
4050     mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
4051     signalPassFailure();
4052   }
4053 
4054 private:
4055   llvm::raw_ostream &output;
4056   fir::LLVMIRLoweringPrinter printer;
4057 };
4058 
4059 } // namespace
4060 
4061 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
4062   return std::make_unique<FIRToLLVMLowering>();
4063 }
4064 
4065 std::unique_ptr<mlir::Pass>
4066 fir::createFIRToLLVMPass(fir::FIRToLLVMPassOptions options) {
4067   return std::make_unique<FIRToLLVMLowering>(options);
4068 }
4069 
4070 std::unique_ptr<mlir::Pass>
4071 fir::createLLVMDialectToLLVMPass(llvm::raw_ostream &output,
4072                                  fir::LLVMIRLoweringPrinter printer) {
4073   return std::make_unique<LLVMIRLoweringPass>(output, printer);
4074 }
4075 
4076 void fir::populateFIRToLLVMConversionPatterns(
4077     const fir::LLVMTypeConverter &converter, mlir::RewritePatternSet &patterns,
4078     fir::FIRToLLVMPassOptions &options) {
4079   patterns.insert<
4080       AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
4081       AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
4082       BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
4083       BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
4084       BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
4085       BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
4086       CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
4087       DTEntryOpConversion, DeclareOpConversion, DivcOpConversion,
4088       EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
4089       ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
4090       FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
4091       InsertOnRangeOpConversion, IsPresentOpConversion,
4092       LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
4093       NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
4094       SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
4095       ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
4096       SliceOpConversion, StoreOpConversion, StringLitOpConversion,
4097       SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
4098       UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
4099       UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
4100       XReboxOpConversion, ZeroOpConversion>(converter, options);
4101 
4102   // Patterns that are populated without a type converter do not trigger
4103   // target materializations for the operands of the root op.
4104   patterns.insert<HasValueOpConversion, InsertValueOpConversion>(
4105       patterns.getContext());
4106 }
4107