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 #include "CGOps.h"
15 #include "PassDetail.h"
16 #include "flang/ISO_Fortran_binding.h"
17 #include "flang/Optimizer/Dialect/FIRAttr.h"
18 #include "flang/Optimizer/Dialect/FIROps.h"
19 #include "flang/Optimizer/Support/InternalNames.h"
20 #include "flang/Optimizer/Support/TypeCode.h"
21 #include "flang/Semantics/runtime-type-info.h"
22 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
23 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
24 #include "mlir/Conversion/LLVMCommon/Pattern.h"
25 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
26 #include "mlir/IR/BuiltinTypes.h"
27 #include "mlir/IR/Matchers.h"
28 #include "mlir/Pass/Pass.h"
29 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
30 #include "llvm/ADT/ArrayRef.h"
31 
32 #define DEBUG_TYPE "flang-codegen"
33 
34 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
35 #include "TypeConverter.h"
36 
37 // TODO: This should really be recovered from the specified target.
38 static constexpr unsigned defaultAlign = 8;
39 
40 /// `fir.box` attribute values as defined for CFI_attribute_t in
41 /// flang/ISO_Fortran_binding.h.
42 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
43 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
44 
45 static inline mlir::Type getVoidPtrType(mlir::MLIRContext *context) {
46   return mlir::LLVM::LLVMPointerType::get(mlir::IntegerType::get(context, 8));
47 }
48 
49 static mlir::LLVM::ConstantOp
50 genConstantIndex(mlir::Location loc, mlir::Type ity,
51                  mlir::ConversionPatternRewriter &rewriter,
52                  std::int64_t offset) {
53   auto cattr = rewriter.getI64IntegerAttr(offset);
54   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
55 }
56 
57 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
58                           mlir::Block *insertBefore) {
59   assert(insertBefore && "expected valid insertion block");
60   return rewriter.createBlock(insertBefore->getParent(),
61                               mlir::Region::iterator(insertBefore));
62 }
63 
64 namespace {
65 /// FIR conversion pattern template
66 template <typename FromOp>
67 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
68 public:
69   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
70       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
71 
72 protected:
73   mlir::Type convertType(mlir::Type ty) const {
74     return lowerTy().convertType(ty);
75   }
76   mlir::Type voidPtrTy() const { return getVoidPtrType(); }
77 
78   mlir::Type getVoidPtrType() const {
79     return mlir::LLVM::LLVMPointerType::get(
80         mlir::IntegerType::get(&lowerTy().getContext(), 8));
81   }
82 
83   mlir::LLVM::ConstantOp
84   genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
85                  int value) const {
86     mlir::Type i32Ty = rewriter.getI32Type();
87     mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
88     return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
89   }
90 
91   mlir::LLVM::ConstantOp
92   genConstantOffset(mlir::Location loc,
93                     mlir::ConversionPatternRewriter &rewriter,
94                     int offset) const {
95     mlir::Type ity = lowerTy().offsetType();
96     mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
97     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
98   }
99 
100   /// Construct code sequence to extract the specifc value from a `fir.box`.
101   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
102                               mlir::Type resultTy,
103                               mlir::ConversionPatternRewriter &rewriter,
104                               unsigned boxValue) const {
105     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
106     mlir::LLVM::ConstantOp cValuePos =
107         genConstantOffset(loc, rewriter, boxValue);
108     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
109     auto p = rewriter.create<mlir::LLVM::GEPOp>(
110         loc, pty, box, mlir::ValueRange{c0, cValuePos});
111     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
112   }
113 
114   /// Method to construct code sequence to get the triple for dimension `dim`
115   /// from a box.
116   SmallVector<mlir::Value, 3>
117   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
118                  mlir::Value box, mlir::Value dim,
119                  mlir::ConversionPatternRewriter &rewriter) const {
120     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
121     mlir::LLVM::ConstantOp cDims =
122         genConstantOffset(loc, rewriter, kDimsPosInBox);
123     mlir::LLVM::LoadOp l0 =
124         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
125     mlir::LLVM::LoadOp l1 =
126         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
127     mlir::LLVM::LoadOp l2 =
128         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
129     return {l0.getResult(), l1.getResult(), l2.getResult()};
130   }
131 
132   mlir::LLVM::LoadOp
133   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
134                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
135                  mlir::Type ty,
136                  mlir::ConversionPatternRewriter &rewriter) const {
137     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
138     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
139     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
140     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
141   }
142 
143   mlir::Value
144   loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim,
145                     mlir::ConversionPatternRewriter &rewriter) const {
146     auto idxTy = lowerTy().indexType();
147     auto c0 = genConstantOffset(loc, rewriter, 0);
148     auto cDims = genConstantOffset(loc, rewriter, kDimsPosInBox);
149     auto dimValue = genConstantIndex(loc, idxTy, rewriter, dim);
150     return loadFromOffset(loc, box, c0, cDims, dimValue, kDimStridePos, idxTy,
151                           rewriter);
152   }
153 
154   /// Read base address from a fir.box. Returned address has type ty.
155   mlir::Value
156   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
157                       mlir::ConversionPatternRewriter &rewriter) const {
158     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
159     mlir::LLVM::ConstantOp cAddr =
160         genConstantOffset(loc, rewriter, kAddrPosInBox);
161     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
162     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
163     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
164   }
165 
166   mlir::Value
167   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
168                          mlir::ConversionPatternRewriter &rewriter) const {
169     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
170     mlir::LLVM::ConstantOp cElemLen =
171         genConstantOffset(loc, rewriter, kElemLenPosInBox);
172     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
173     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
174     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
175   }
176 
177   // Load the attribute from the \p box and perform a check against \p maskValue
178   // The final comparison is implemented as `(attribute & maskValue) != 0`.
179   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
180                                    mlir::ConversionPatternRewriter &rewriter,
181                                    unsigned maskValue) const {
182     mlir::Type attrTy = rewriter.getI32Type();
183     mlir::Value attribute =
184         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
185     mlir::LLVM::ConstantOp attrMask =
186         genConstantOffset(loc, rewriter, maskValue);
187     auto maskRes =
188         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
189     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
190     return rewriter.create<mlir::LLVM::ICmpOp>(
191         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
192   }
193 
194   // Get the element type given an LLVM type that is of the form
195   // [llvm.ptr](array|struct|vector)+ and the provided indexes.
196   static mlir::Type getBoxEleTy(mlir::Type type,
197                                 llvm::ArrayRef<unsigned> indexes) {
198     if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
199       type = t.getElementType();
200     for (auto i : indexes) {
201       if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
202         assert(!t.isOpaque() && i < t.getBody().size());
203         type = t.getBody()[i];
204       } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
205         type = t.getElementType();
206       } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
207         type = t.getElementType();
208       } else {
209         fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
210                             "request for invalid box element type");
211       }
212     }
213     return type;
214   }
215 
216   // Return LLVM type of the base address given the LLVM type
217   // of the related descriptor (lowered fir.box type).
218   static mlir::Type getBaseAddrTypeFromBox(mlir::Type type) {
219     return getBoxEleTy(type, {kAddrPosInBox});
220   }
221 
222   template <typename... ARGS>
223   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
224                            mlir::ConversionPatternRewriter &rewriter,
225                            mlir::Value base, ARGS... args) const {
226     SmallVector<mlir::Value> cv{args...};
227     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
228   }
229 
230   /// Perform an extension or truncation as needed on an integer value. Lowering
231   /// to the specific target may involve some sign-extending or truncation of
232   /// values, particularly to fit them from abstract box types to the
233   /// appropriate reified structures.
234   mlir::Value integerCast(mlir::Location loc,
235                           mlir::ConversionPatternRewriter &rewriter,
236                           mlir::Type ty, mlir::Value val) const {
237     auto valTy = val.getType();
238     // If the value was not yet lowered, lower its type so that it can
239     // be used in getPrimitiveTypeSizeInBits.
240     if (!valTy.isa<mlir::IntegerType>())
241       valTy = convertType(valTy);
242     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
243     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
244     if (toSize < fromSize)
245       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
246     if (toSize > fromSize)
247       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
248     return val;
249   }
250 
251   fir::LLVMTypeConverter &lowerTy() const {
252     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
253   }
254 };
255 
256 /// FIR conversion pattern template
257 template <typename FromOp>
258 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
259 public:
260   using FIROpConversion<FromOp>::FIROpConversion;
261   using OpAdaptor = typename FromOp::Adaptor;
262 
263   mlir::LogicalResult
264   matchAndRewrite(FromOp op, OpAdaptor adaptor,
265                   mlir::ConversionPatternRewriter &rewriter) const final {
266     mlir::Type ty = this->convertType(op.getType());
267     return doRewrite(op, ty, adaptor, rewriter);
268   }
269 
270   virtual mlir::LogicalResult
271   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
272             mlir::ConversionPatternRewriter &rewriter) const = 0;
273 };
274 
275 /// Create value signaling an absent optional argument in a call, e.g.
276 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
277 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
278   using FIROpConversion::FIROpConversion;
279 
280   mlir::LogicalResult
281   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
282                   mlir::ConversionPatternRewriter &rewriter) const override {
283     mlir::Type ty = convertType(absent.getType());
284     mlir::Location loc = absent.getLoc();
285 
286     if (absent.getType().isa<fir::BoxCharType>()) {
287       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
288       assert(!structTy.isOpaque() && !structTy.getBody().empty());
289       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
290       auto nullField =
291           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
292       mlir::MLIRContext *ctx = absent.getContext();
293       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
294       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
295           absent, ty, undefStruct, nullField, c0);
296     } else {
297       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
298     }
299     return success();
300   }
301 };
302 
303 // Lower `fir.address_of` operation to `llvm.address_of` operation.
304 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
305   using FIROpConversion::FIROpConversion;
306 
307   mlir::LogicalResult
308   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
309                   mlir::ConversionPatternRewriter &rewriter) const override {
310     auto ty = convertType(addr.getType());
311     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
312         addr, ty, addr.getSymbol().getRootReference().getValue());
313     return success();
314   }
315 };
316 } // namespace
317 
318 /// Lookup the function to compute the memory size of this parametric derived
319 /// type. The size of the object may depend on the LEN type parameters of the
320 /// derived type.
321 static mlir::LLVM::LLVMFuncOp
322 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
323                           mlir::ConversionPatternRewriter &rewriter) {
324   auto module = op->getParentOfType<mlir::ModuleOp>();
325   std::string name = recTy.getName().str() + "P.mem.size";
326   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
327 }
328 
329 namespace {
330 /// convert to LLVM IR dialect `alloca`
331 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
332   using FIROpConversion::FIROpConversion;
333 
334   mlir::LogicalResult
335   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
336                   mlir::ConversionPatternRewriter &rewriter) const override {
337     mlir::ValueRange operands = adaptor.getOperands();
338     auto loc = alloc.getLoc();
339     mlir::Type ity = lowerTy().indexType();
340     unsigned i = 0;
341     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
342     mlir::Type ty = convertType(alloc.getType());
343     mlir::Type resultTy = ty;
344     if (alloc.hasLenParams()) {
345       unsigned end = alloc.numLenParams();
346       llvm::SmallVector<mlir::Value> lenParams;
347       for (; i < end; ++i)
348         lenParams.push_back(operands[i]);
349       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
350       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
351         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
352             chrTy.getContext(), chrTy.getFKind());
353         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
354         assert(end == 1);
355         size = integerCast(loc, rewriter, ity, lenParams[0]);
356       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
357         mlir::LLVM::LLVMFuncOp memSizeFn =
358             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
359         if (!memSizeFn)
360           emitError(loc, "did not find allocation function");
361         mlir::NamedAttribute attr = rewriter.getNamedAttr(
362             "callee", mlir::SymbolRefAttr::get(memSizeFn));
363         auto call = rewriter.create<mlir::LLVM::CallOp>(
364             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
365         size = call.getResult(0);
366         ty = mlir::LLVM::LLVMPointerType::get(
367             mlir::IntegerType::get(alloc.getContext(), 8));
368       } else {
369         return emitError(loc, "unexpected type ")
370                << scalarType << " with type parameters";
371       }
372     }
373     if (alloc.hasShapeOperands()) {
374       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
375       // Scale the size by constant factors encoded in the array type.
376       // We only do this for arrays that don't have a constant interior, since
377       // those are the only ones that get decayed to a pointer to the element
378       // type.
379       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
380         if (!seqTy.hasConstantInterior()) {
381           fir::SequenceType::Extent constSize = 1;
382           for (auto extent : seqTy.getShape())
383             if (extent != fir::SequenceType::getUnknownExtent())
384               constSize *= extent;
385           mlir::Value constVal{
386               genConstantIndex(loc, ity, rewriter, constSize).getResult()};
387           size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
388         }
389       }
390       unsigned end = operands.size();
391       for (; i < end; ++i)
392         size = rewriter.create<mlir::LLVM::MulOp>(
393             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
394     }
395     if (ty == resultTy) {
396       // Do not emit the bitcast if ty and resultTy are the same.
397       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
398                                                         alloc->getAttrs());
399     } else {
400       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
401                                                       alloc->getAttrs());
402       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
403     }
404     return success();
405   }
406 };
407 
408 /// Lower `fir.box_addr` to the sequence of operations to extract the first
409 /// element of the box.
410 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
411   using FIROpConversion::FIROpConversion;
412 
413   mlir::LogicalResult
414   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
415                   mlir::ConversionPatternRewriter &rewriter) const override {
416     mlir::Value a = adaptor.getOperands()[0];
417     auto loc = boxaddr.getLoc();
418     mlir::Type ty = convertType(boxaddr.getType());
419     if (auto argty = boxaddr.getVal().getType().dyn_cast<fir::BoxType>()) {
420       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
421     } else {
422       auto c0attr = rewriter.getI32IntegerAttr(0);
423       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
424       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
425                                                               c0);
426     }
427     return success();
428   }
429 };
430 
431 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
432 /// dimension infomartion from the boxed value.
433 /// Result in a triple set of GEPs and loads.
434 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
435   using FIROpConversion::FIROpConversion;
436 
437   mlir::LogicalResult
438   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
439                   mlir::ConversionPatternRewriter &rewriter) const override {
440     SmallVector<mlir::Type, 3> resultTypes = {
441         convertType(boxdims.getResult(0).getType()),
442         convertType(boxdims.getResult(1).getType()),
443         convertType(boxdims.getResult(2).getType()),
444     };
445     auto results =
446         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
447                        adaptor.getOperands()[1], rewriter);
448     rewriter.replaceOp(boxdims, results);
449     return success();
450   }
451 };
452 
453 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
454 /// an element in the boxed value.
455 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
456   using FIROpConversion::FIROpConversion;
457 
458   mlir::LogicalResult
459   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
460                   mlir::ConversionPatternRewriter &rewriter) const override {
461     mlir::Value a = adaptor.getOperands()[0];
462     auto loc = boxelesz.getLoc();
463     auto ty = convertType(boxelesz.getType());
464     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
465     rewriter.replaceOp(boxelesz, elemSize);
466     return success();
467   }
468 };
469 
470 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
471 /// boxed value was from an ALLOCATABLE entity.
472 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
473   using FIROpConversion::FIROpConversion;
474 
475   mlir::LogicalResult
476   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
477                   mlir::ConversionPatternRewriter &rewriter) const override {
478     mlir::Value box = adaptor.getOperands()[0];
479     auto loc = boxisalloc.getLoc();
480     mlir::Value check =
481         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
482     rewriter.replaceOp(boxisalloc, check);
483     return success();
484   }
485 };
486 
487 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
488 /// boxed is an array.
489 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
490   using FIROpConversion::FIROpConversion;
491 
492   mlir::LogicalResult
493   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
494                   mlir::ConversionPatternRewriter &rewriter) const override {
495     mlir::Value a = adaptor.getOperands()[0];
496     auto loc = boxisarray.getLoc();
497     auto rank =
498         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
499     auto c0 = genConstantOffset(loc, rewriter, 0);
500     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
501         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
502     return success();
503   }
504 };
505 
506 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
507 /// boxed value was from a POINTER entity.
508 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
509   using FIROpConversion::FIROpConversion;
510 
511   mlir::LogicalResult
512   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
513                   mlir::ConversionPatternRewriter &rewriter) const override {
514     mlir::Value box = adaptor.getOperands()[0];
515     auto loc = boxisptr.getLoc();
516     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
517     rewriter.replaceOp(boxisptr, check);
518     return success();
519   }
520 };
521 
522 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
523 /// the box.
524 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
525   using FIROpConversion::FIROpConversion;
526 
527   mlir::LogicalResult
528   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
529                   mlir::ConversionPatternRewriter &rewriter) const override {
530     mlir::Value a = adaptor.getOperands()[0];
531     auto loc = boxrank.getLoc();
532     mlir::Type ty = convertType(boxrank.getType());
533     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
534     rewriter.replaceOp(boxrank, result);
535     return success();
536   }
537 };
538 
539 /// Lower `fir.string_lit` to LLVM IR dialect operation.
540 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
541   using FIROpConversion::FIROpConversion;
542 
543   mlir::LogicalResult
544   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
545                   mlir::ConversionPatternRewriter &rewriter) const override {
546     auto ty = convertType(constop.getType());
547     auto attr = constop.getValue();
548     if (attr.isa<mlir::StringAttr>()) {
549       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
550       return success();
551     }
552 
553     auto arr = attr.cast<mlir::ArrayAttr>();
554     auto charTy = constop.getType().cast<fir::CharacterType>();
555     unsigned bits = lowerTy().characterBitsize(charTy);
556     mlir::Type intTy = rewriter.getIntegerType(bits);
557     auto attrs = llvm::map_range(
558         arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute {
559           return mlir::IntegerAttr::get(
560               intTy,
561               attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits));
562         });
563     mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy);
564     auto denseAttr = mlir::DenseElementsAttr::get(
565         vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs));
566     rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty,
567                                                          denseAttr);
568     return success();
569   }
570 };
571 
572 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
573 /// boxproc.
574 /// TODO: Part of supporting Fortran 2003 procedure pointers.
575 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> {
576   using FIROpConversion::FIROpConversion;
577 
578   mlir::LogicalResult
579   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
580                   mlir::ConversionPatternRewriter &rewriter) const override {
581     TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
582     return failure();
583   }
584 };
585 
586 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
587 /// descriptor from the box.
588 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
589   using FIROpConversion::FIROpConversion;
590 
591   mlir::LogicalResult
592   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
593                   mlir::ConversionPatternRewriter &rewriter) const override {
594     mlir::Value box = adaptor.getOperands()[0];
595     auto loc = boxtypedesc.getLoc();
596     mlir::Type typeTy =
597         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
598     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
599     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
600     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
601                                                         result);
602     return success();
603   }
604 };
605 
606 // `fir.call` -> `llvm.call`
607 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
608   using FIROpConversion::FIROpConversion;
609 
610   mlir::LogicalResult
611   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
612                   mlir::ConversionPatternRewriter &rewriter) const override {
613     SmallVector<mlir::Type> resultTys;
614     for (auto r : call.getResults())
615       resultTys.push_back(convertType(r.getType()));
616     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
617         call, resultTys, adaptor.getOperands(), call->getAttrs());
618     return success();
619   }
620 };
621 } // namespace
622 
623 static mlir::Type getComplexEleTy(mlir::Type complex) {
624   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
625     return cc.getElementType();
626   return complex.cast<fir::ComplexType>().getElementType();
627 }
628 
629 namespace {
630 /// Compare complex values
631 ///
632 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
633 ///
634 /// For completeness, all other comparison are done on the real component only.
635 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
636   using FIROpConversion::FIROpConversion;
637 
638   mlir::LogicalResult
639   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
640                   mlir::ConversionPatternRewriter &rewriter) const override {
641     mlir::ValueRange operands = adaptor.getOperands();
642     mlir::MLIRContext *ctxt = cmp.getContext();
643     mlir::Type eleTy = convertType(getComplexEleTy(cmp.getLhs().getType()));
644     mlir::Type resTy = convertType(cmp.getType());
645     mlir::Location loc = cmp.getLoc();
646     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
647     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
648                                        loc, eleTy, operands[0], pos0),
649                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
650                                        loc, eleTy, operands[1], pos0)};
651     auto rcp =
652         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
653     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
654     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
655                                        loc, eleTy, operands[0], pos1),
656                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
657                                        loc, eleTy, operands[1], pos1)};
658     auto icp =
659         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
660     SmallVector<mlir::Value, 2> cp{rcp, icp};
661     switch (cmp.getPredicate()) {
662     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
663       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
664       break;
665     case mlir::arith::CmpFPredicate::UNE: // .NE.
666       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
667       break;
668     default:
669       rewriter.replaceOp(cmp, rcp.getResult());
670       break;
671     }
672     return success();
673   }
674 };
675 
676 /// Lower complex constants
677 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
678   using FIROpConversion::FIROpConversion;
679 
680   mlir::LogicalResult
681   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
682                   mlir::ConversionPatternRewriter &rewriter) const override {
683     mlir::Location loc = conc.getLoc();
684     mlir::MLIRContext *ctx = conc.getContext();
685     mlir::Type ty = convertType(conc.getType());
686     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
687     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
688     auto realPart =
689         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
690     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
691     auto imPart =
692         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
693     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
694     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
695     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
696     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
697         loc, ty, undef, realPart, realIndex);
698     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
699                                                            imPart, imIndex);
700     return success();
701   }
702 
703   inline APFloat getValue(mlir::Attribute attr) const {
704     return attr.cast<fir::RealAttr>().getValue();
705   }
706 };
707 
708 /// convert value of from-type to value of to-type
709 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
710   using FIROpConversion::FIROpConversion;
711 
712   static bool isFloatingPointTy(mlir::Type ty) {
713     return ty.isa<mlir::FloatType>();
714   }
715 
716   mlir::LogicalResult
717   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
718                   mlir::ConversionPatternRewriter &rewriter) const override {
719     auto fromTy = convertType(convert.getValue().getType());
720     auto toTy = convertType(convert.getRes().getType());
721     mlir::Value op0 = adaptor.getOperands()[0];
722     if (fromTy == toTy) {
723       rewriter.replaceOp(convert, op0);
724       return success();
725     }
726     auto loc = convert.getLoc();
727     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
728                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
729       if (fromBits == toBits) {
730         // TODO: Converting between two floating-point representations with the
731         // same bitwidth is not allowed for now.
732         mlir::emitError(loc,
733                         "cannot implicitly convert between two floating-point "
734                         "representations of the same bitwidth");
735         return {};
736       }
737       if (fromBits > toBits)
738         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
739       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
740     };
741     // Complex to complex conversion.
742     if (fir::isa_complex(convert.getValue().getType()) &&
743         fir::isa_complex(convert.getRes().getType())) {
744       // Special case: handle the conversion of a complex such that both the
745       // real and imaginary parts are converted together.
746       auto zero = mlir::ArrayAttr::get(convert.getContext(),
747                                        rewriter.getI32IntegerAttr(0));
748       auto one = mlir::ArrayAttr::get(convert.getContext(),
749                                       rewriter.getI32IntegerAttr(1));
750       auto ty = convertType(getComplexEleTy(convert.getValue().getType()));
751       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
752       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
753       auto nt = convertType(getComplexEleTy(convert.getRes().getType()));
754       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
755       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
756       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
757       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
758       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
759       auto i1 =
760           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
761       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
762                                                              ic, one);
763       return mlir::success();
764     }
765     // Floating point to floating point conversion.
766     if (isFloatingPointTy(fromTy)) {
767       if (isFloatingPointTy(toTy)) {
768         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
769         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
770         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
771         rewriter.replaceOp(convert, v);
772         return mlir::success();
773       }
774       if (toTy.isa<mlir::IntegerType>()) {
775         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
776         return mlir::success();
777       }
778     } else if (fromTy.isa<mlir::IntegerType>()) {
779       // Integer to integer conversion.
780       if (toTy.isa<mlir::IntegerType>()) {
781         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
782         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
783         assert(fromBits != toBits);
784         if (fromBits > toBits) {
785           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
786           return mlir::success();
787         }
788         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
789         return mlir::success();
790       }
791       // Integer to floating point conversion.
792       if (isFloatingPointTy(toTy)) {
793         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
794         return mlir::success();
795       }
796       // Integer to pointer conversion.
797       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
798         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
799         return mlir::success();
800       }
801     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
802       // Pointer to integer conversion.
803       if (toTy.isa<mlir::IntegerType>()) {
804         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
805         return mlir::success();
806       }
807       // Pointer to pointer conversion.
808       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
809         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
810         return mlir::success();
811       }
812     }
813     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
814   }
815 };
816 
817 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
818 /// table.
819 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
820   using FIROpConversion::FIROpConversion;
821 
822   mlir::LogicalResult
823   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
824                   mlir::ConversionPatternRewriter &rewriter) const override {
825     TODO(dispatch.getLoc(), "fir.dispatch codegen");
826     return failure();
827   }
828 };
829 
830 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
831 /// derived type.
832 struct DispatchTableOpConversion
833     : public FIROpConversion<fir::DispatchTableOp> {
834   using FIROpConversion::FIROpConversion;
835 
836   mlir::LogicalResult
837   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
838                   mlir::ConversionPatternRewriter &rewriter) const override {
839     TODO(dispTab.getLoc(), "fir.dispatch_table codegen");
840     return failure();
841   }
842 };
843 
844 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
845 /// method-name to a function.
846 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
847   using FIROpConversion::FIROpConversion;
848 
849   mlir::LogicalResult
850   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
851                   mlir::ConversionPatternRewriter &rewriter) const override {
852     TODO(dtEnt.getLoc(), "fir.dt_entry codegen");
853     return failure();
854   }
855 };
856 
857 /// Lower `fir.global_len` operation.
858 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
859   using FIROpConversion::FIROpConversion;
860 
861   mlir::LogicalResult
862   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
863                   mlir::ConversionPatternRewriter &rewriter) const override {
864     TODO(globalLen.getLoc(), "fir.global_len codegen");
865     return failure();
866   }
867 };
868 
869 /// Lower fir.len_param_index
870 struct LenParamIndexOpConversion
871     : public FIROpConversion<fir::LenParamIndexOp> {
872   using FIROpConversion::FIROpConversion;
873 
874   // FIXME: this should be specialized by the runtime target
875   mlir::LogicalResult
876   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
877                   mlir::ConversionPatternRewriter &rewriter) const override {
878     TODO(lenp.getLoc(), "fir.len_param_index codegen");
879   }
880 };
881 
882 /// Lower `fir.gentypedesc` to a global constant.
883 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
884   using FIROpConversion::FIROpConversion;
885 
886   mlir::LogicalResult
887   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
888                   mlir::ConversionPatternRewriter &rewriter) const override {
889     TODO(gentypedesc.getLoc(), "fir.gentypedesc codegen");
890     return failure();
891   }
892 };
893 } // namespace
894 
895 /// Return the LLVMFuncOp corresponding to the standard malloc call.
896 static mlir::LLVM::LLVMFuncOp
897 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) {
898   auto module = op->getParentOfType<mlir::ModuleOp>();
899   if (mlir::LLVM::LLVMFuncOp mallocFunc =
900           module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("malloc"))
901     return mallocFunc;
902   mlir::OpBuilder moduleBuilder(
903       op->getParentOfType<mlir::ModuleOp>().getBodyRegion());
904   auto indexType = mlir::IntegerType::get(op.getContext(), 64);
905   return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
906       rewriter.getUnknownLoc(), "malloc",
907       mlir::LLVM::LLVMFunctionType::get(getVoidPtrType(op.getContext()),
908                                         indexType,
909                                         /*isVarArg=*/false));
910 }
911 
912 /// Helper function for generating the LLVM IR that computes the size
913 /// in bytes for a derived type.
914 static mlir::Value
915 computeDerivedTypeSize(mlir::Location loc, mlir::Type ptrTy, mlir::Type idxTy,
916                        mlir::ConversionPatternRewriter &rewriter) {
917   auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
918   mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
919   llvm::SmallVector<mlir::Value> args{one};
920   auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr, args);
921   return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
922 }
923 
924 namespace {
925 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc`
926 struct AllocMemOpConversion : public FIROpConversion<fir::AllocMemOp> {
927   using FIROpConversion::FIROpConversion;
928 
929   mlir::LogicalResult
930   matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor,
931                   mlir::ConversionPatternRewriter &rewriter) const override {
932     auto heapTy = heap.getType();
933     auto ty = convertType(heapTy);
934     mlir::LLVM::LLVMFuncOp mallocFunc = getMalloc(heap, rewriter);
935     mlir::Location loc = heap.getLoc();
936     auto ity = lowerTy().indexType();
937     auto dataTy = fir::unwrapRefType(heapTy);
938     if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy)))
939       TODO(loc, "fir.allocmem codegen of derived type with length parameters");
940     mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, ty);
941     // !fir.array<NxMx!fir.char<K,?>> sets `size` to the width of !fir.char<K>.
942     // So multiply the constant dimensions here.
943     if (fir::hasDynamicSize(dataTy))
944       if (auto seqTy = dataTy.dyn_cast<fir::SequenceType>())
945         if (fir::characterWithDynamicLen(seqTy.getEleTy())) {
946           fir::SequenceType::Extent arrSize = 1;
947           for (auto d : seqTy.getShape())
948             if (d != fir::SequenceType::getUnknownExtent())
949               arrSize *= d;
950           size = rewriter.create<mlir::LLVM::MulOp>(
951               loc, ity, size, genConstantIndex(loc, ity, rewriter, arrSize));
952         }
953     for (mlir::Value opnd : adaptor.getOperands())
954       size = rewriter.create<mlir::LLVM::MulOp>(
955           loc, ity, size, integerCast(loc, rewriter, ity, opnd));
956     heap->setAttr("callee", mlir::SymbolRefAttr::get(mallocFunc));
957     auto malloc = rewriter.create<mlir::LLVM::CallOp>(
958         loc, ::getVoidPtrType(heap.getContext()), size, heap->getAttrs());
959     rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(heap, ty,
960                                                        malloc.getResult(0));
961     return success();
962   }
963 
964   // Compute the (allocation) size of the allocmem type in bytes.
965   mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy,
966                                  mlir::ConversionPatternRewriter &rewriter,
967                                  mlir::Type llTy) const {
968     // Use the primitive size, if available.
969     auto ptrTy = llTy.dyn_cast<mlir::LLVM::LLVMPointerType>();
970     if (auto size =
971             mlir::LLVM::getPrimitiveTypeSizeInBits(ptrTy.getElementType()))
972       return genConstantIndex(loc, idxTy, rewriter, size / 8);
973 
974     // Otherwise, generate the GEP trick in LLVM IR to compute the size.
975     return computeDerivedTypeSize(loc, ptrTy, idxTy, rewriter);
976   }
977 };
978 } // namespace
979 
980 /// Return the LLVMFuncOp corresponding to the standard free call.
981 static mlir::LLVM::LLVMFuncOp
982 getFree(fir::FreeMemOp op, mlir::ConversionPatternRewriter &rewriter) {
983   auto module = op->getParentOfType<mlir::ModuleOp>();
984   if (mlir::LLVM::LLVMFuncOp freeFunc =
985           module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("free"))
986     return freeFunc;
987   mlir::OpBuilder moduleBuilder(module.getBodyRegion());
988   auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext());
989   return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
990       rewriter.getUnknownLoc(), "free",
991       mlir::LLVM::LLVMFunctionType::get(voidType,
992                                         getVoidPtrType(op.getContext()),
993                                         /*isVarArg=*/false));
994 }
995 
996 namespace {
997 /// Lower a `fir.freemem` instruction into `llvm.call @free`
998 struct FreeMemOpConversion : public FIROpConversion<fir::FreeMemOp> {
999   using FIROpConversion::FIROpConversion;
1000 
1001   mlir::LogicalResult
1002   matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor,
1003                   mlir::ConversionPatternRewriter &rewriter) const override {
1004     mlir::LLVM::LLVMFuncOp freeFunc = getFree(freemem, rewriter);
1005     mlir::Location loc = freemem.getLoc();
1006     auto bitcast = rewriter.create<mlir::LLVM::BitcastOp>(
1007         freemem.getLoc(), voidPtrTy(), adaptor.getOperands()[0]);
1008     freemem->setAttr("callee", mlir::SymbolRefAttr::get(freeFunc));
1009     rewriter.create<mlir::LLVM::CallOp>(
1010         loc, mlir::TypeRange{}, mlir::ValueRange{bitcast}, freemem->getAttrs());
1011     rewriter.eraseOp(freemem);
1012     return success();
1013   }
1014 };
1015 
1016 /// Convert `fir.end`
1017 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
1018   using FIROpConversion::FIROpConversion;
1019 
1020   mlir::LogicalResult
1021   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
1022                   mlir::ConversionPatternRewriter &rewriter) const override {
1023     TODO(firEnd.getLoc(), "fir.end codegen");
1024     return failure();
1025   }
1026 };
1027 
1028 /// Lower `fir.has_value` operation to `llvm.return` operation.
1029 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
1030   using FIROpConversion::FIROpConversion;
1031 
1032   mlir::LogicalResult
1033   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
1034                   mlir::ConversionPatternRewriter &rewriter) const override {
1035     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
1036     return success();
1037   }
1038 };
1039 
1040 /// Lower `fir.global` operation to `llvm.global` operation.
1041 /// `fir.insert_on_range` operations are replaced with constant dense attribute
1042 /// if they are applied on the full range.
1043 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
1044   using FIROpConversion::FIROpConversion;
1045 
1046   mlir::LogicalResult
1047   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
1048                   mlir::ConversionPatternRewriter &rewriter) const override {
1049     auto tyAttr = convertType(global.getType());
1050     if (global.getType().isa<fir::BoxType>())
1051       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
1052     auto loc = global.getLoc();
1053     mlir::Attribute initAttr{};
1054     if (global.getInitVal())
1055       initAttr = global.getInitVal().getValue();
1056     auto linkage = convertLinkage(global.getLinkName());
1057     auto isConst = global.getConstant().hasValue();
1058     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
1059         loc, tyAttr, isConst, linkage, global.getSymName(), initAttr);
1060     auto &gr = g.getInitializerRegion();
1061     rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end());
1062     if (!gr.empty()) {
1063       // Replace insert_on_range with a constant dense attribute if the
1064       // initialization is on the full range.
1065       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
1066       for (auto insertOp : insertOnRangeOps) {
1067         if (isFullRange(insertOp.getCoor(), insertOp.getType())) {
1068           auto seqTyAttr = convertType(insertOp.getType());
1069           auto *op = insertOp.getVal().getDefiningOp();
1070           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
1071           if (!constant) {
1072             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
1073             if (!convertOp)
1074               continue;
1075             constant = cast<mlir::arith::ConstantOp>(
1076                 convertOp.getValue().getDefiningOp());
1077           }
1078           mlir::Type vecType = mlir::VectorType::get(
1079               insertOp.getType().getShape(), constant.getType());
1080           auto denseAttr = mlir::DenseElementsAttr::get(
1081               vecType.cast<ShapedType>(), constant.getValue());
1082           rewriter.setInsertionPointAfter(insertOp);
1083           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
1084               insertOp, seqTyAttr, denseAttr);
1085         }
1086       }
1087     }
1088     rewriter.eraseOp(global);
1089     return success();
1090   }
1091 
1092   bool isFullRange(mlir::DenseIntElementsAttr indexes,
1093                    fir::SequenceType seqTy) const {
1094     auto extents = seqTy.getShape();
1095     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
1096       return false;
1097     auto cur_index = indexes.value_begin<int64_t>();
1098     for (unsigned i = 0; i < indexes.size(); i += 2) {
1099       if (*(cur_index++) != 0)
1100         return false;
1101       if (*(cur_index++) != extents[i / 2] - 1)
1102         return false;
1103     }
1104     return true;
1105   }
1106 
1107   // TODO: String comparaison should be avoided. Replace linkName with an
1108   // enumeration.
1109   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
1110     if (optLinkage.hasValue()) {
1111       auto name = optLinkage.getValue();
1112       if (name == "internal")
1113         return mlir::LLVM::Linkage::Internal;
1114       if (name == "linkonce")
1115         return mlir::LLVM::Linkage::Linkonce;
1116       if (name == "common")
1117         return mlir::LLVM::Linkage::Common;
1118       if (name == "weak")
1119         return mlir::LLVM::Linkage::Weak;
1120     }
1121     return mlir::LLVM::Linkage::External;
1122   }
1123 };
1124 } // namespace
1125 
1126 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
1127                         Optional<mlir::ValueRange> destOps,
1128                         mlir::ConversionPatternRewriter &rewriter,
1129                         mlir::Block *newBlock) {
1130   if (destOps.hasValue())
1131     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
1132                                           newBlock, mlir::ValueRange());
1133   else
1134     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
1135 }
1136 
1137 template <typename A, typename B>
1138 static void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
1139                     mlir::ConversionPatternRewriter &rewriter) {
1140   if (destOps.hasValue())
1141     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
1142                                                   dest);
1143   else
1144     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
1145 }
1146 
1147 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
1148                               mlir::Block *dest,
1149                               Optional<mlir::ValueRange> destOps,
1150                               mlir::ConversionPatternRewriter &rewriter) {
1151   auto *thisBlock = rewriter.getInsertionBlock();
1152   auto *newBlock = createBlock(rewriter, dest);
1153   rewriter.setInsertionPointToEnd(thisBlock);
1154   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
1155   rewriter.setInsertionPointToEnd(newBlock);
1156 }
1157 
1158 namespace {
1159 /// Conversion of `fir.select_case`
1160 ///
1161 /// The `fir.select_case` operation is converted to a if-then-else ladder.
1162 /// Depending on the case condition type, one or several comparison and
1163 /// conditional branching can be generated.
1164 ///
1165 /// A a point value case such as `case(4)`, a lower bound case such as
1166 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
1167 /// simple comparison between the selector value and the constant value in the
1168 /// case. The block associated with the case condition is then executed if
1169 /// the comparison succeed otherwise it branch to the next block with the
1170 /// comparison for the the next case conditon.
1171 ///
1172 /// A closed interval case condition such as `case(7:10)` is converted with a
1173 /// first comparison and conditional branching for the lower bound. If
1174 /// successful, it branch to a second block with the comparison for the
1175 /// upper bound in the same case condition.
1176 ///
1177 /// TODO: lowering of CHARACTER type cases is not handled yet.
1178 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
1179   using FIROpConversion::FIROpConversion;
1180 
1181   mlir::LogicalResult
1182   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
1183                   mlir::ConversionPatternRewriter &rewriter) const override {
1184     unsigned conds = caseOp.getNumConditions();
1185     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
1186     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
1187     auto ty = caseOp.getSelector().getType();
1188     if (ty.isa<fir::CharacterType>()) {
1189       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
1190       return failure();
1191     }
1192     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
1193     auto loc = caseOp.getLoc();
1194     for (unsigned t = 0; t != conds; ++t) {
1195       mlir::Block *dest = caseOp.getSuccessor(t);
1196       llvm::Optional<mlir::ValueRange> destOps =
1197           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
1198       llvm::Optional<mlir::ValueRange> cmpOps =
1199           *caseOp.getCompareOperands(adaptor.getOperands(), t);
1200       mlir::Value caseArg = *(cmpOps.getValue().begin());
1201       mlir::Attribute attr = cases[t];
1202       if (attr.isa<fir::PointIntervalAttr>()) {
1203         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1204             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
1205         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1206         continue;
1207       }
1208       if (attr.isa<fir::LowerBoundAttr>()) {
1209         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1210             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1211         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1212         continue;
1213       }
1214       if (attr.isa<fir::UpperBoundAttr>()) {
1215         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1216             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
1217         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1218         continue;
1219       }
1220       if (attr.isa<fir::ClosedIntervalAttr>()) {
1221         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1222             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1223         auto *thisBlock = rewriter.getInsertionBlock();
1224         auto *newBlock1 = createBlock(rewriter, dest);
1225         auto *newBlock2 = createBlock(rewriter, dest);
1226         rewriter.setInsertionPointToEnd(thisBlock);
1227         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
1228         rewriter.setInsertionPointToEnd(newBlock1);
1229         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
1230         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
1231             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
1232         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
1233         rewriter.setInsertionPointToEnd(newBlock2);
1234         continue;
1235       }
1236       assert(attr.isa<mlir::UnitAttr>());
1237       assert((t + 1 == conds) && "unit must be last");
1238       genBrOp(caseOp, dest, destOps, rewriter);
1239     }
1240     return success();
1241   }
1242 };
1243 } // namespace
1244 
1245 template <typename OP>
1246 static void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
1247                                   typename OP::Adaptor adaptor,
1248                                   mlir::ConversionPatternRewriter &rewriter) {
1249   unsigned conds = select.getNumConditions();
1250   auto cases = select.getCases().getValue();
1251   mlir::Value selector = adaptor.getSelector();
1252   auto loc = select.getLoc();
1253   assert(conds > 0 && "select must have cases");
1254 
1255   llvm::SmallVector<mlir::Block *> destinations;
1256   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
1257   mlir::Block *defaultDestination;
1258   mlir::ValueRange defaultOperands;
1259   llvm::SmallVector<int32_t> caseValues;
1260 
1261   for (unsigned t = 0; t != conds; ++t) {
1262     mlir::Block *dest = select.getSuccessor(t);
1263     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
1264     const mlir::Attribute &attr = cases[t];
1265     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
1266       destinations.push_back(dest);
1267       destinationsOperands.push_back(destOps.hasValue() ? *destOps
1268                                                         : ValueRange());
1269       caseValues.push_back(intAttr.getInt());
1270       continue;
1271     }
1272     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1273     assert((t + 1 == conds) && "unit must be last");
1274     defaultDestination = dest;
1275     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1276   }
1277 
1278   // LLVM::SwitchOp takes a i32 type for the selector.
1279   if (select.getSelector().getType() != rewriter.getI32Type())
1280     selector =
1281         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1282 
1283   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1284       select, selector,
1285       /*defaultDestination=*/defaultDestination,
1286       /*defaultOperands=*/defaultOperands,
1287       /*caseValues=*/caseValues,
1288       /*caseDestinations=*/destinations,
1289       /*caseOperands=*/destinationsOperands,
1290       /*branchWeights=*/ArrayRef<int32_t>());
1291 }
1292 
1293 namespace {
1294 /// conversion of fir::SelectOp to an if-then-else ladder
1295 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1296   using FIROpConversion::FIROpConversion;
1297 
1298   mlir::LogicalResult
1299   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1300                   mlir::ConversionPatternRewriter &rewriter) const override {
1301     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1302     return success();
1303   }
1304 };
1305 
1306 /// `fir.load` --> `llvm.load`
1307 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1308   using FIROpConversion::FIROpConversion;
1309 
1310   mlir::LogicalResult
1311   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1312                   mlir::ConversionPatternRewriter &rewriter) const override {
1313     // fir.box is a special case because it is considered as an ssa values in
1314     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1315     // and fir.box end up being the same llvm types and loading a
1316     // fir.ref<fir.box> is actually a no op in LLVM.
1317     if (load.getType().isa<fir::BoxType>()) {
1318       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1319     } else {
1320       mlir::Type ty = convertType(load.getType());
1321       ArrayRef<NamedAttribute> at = load->getAttrs();
1322       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1323           load, ty, adaptor.getOperands(), at);
1324     }
1325     return success();
1326   }
1327 };
1328 
1329 /// Lower `fir.no_reassoc` to LLVM IR dialect.
1330 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
1331 /// math flags?
1332 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
1333   using FIROpConversion::FIROpConversion;
1334 
1335   mlir::LogicalResult
1336   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
1337                   mlir::ConversionPatternRewriter &rewriter) const override {
1338     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
1339     return success();
1340   }
1341 };
1342 
1343 /// Lower `fir.select_type` to LLVM IR dialect.
1344 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1345   using FIROpConversion::FIROpConversion;
1346 
1347   mlir::LogicalResult
1348   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1349                   mlir::ConversionPatternRewriter &rewriter) const override {
1350     mlir::emitError(select.getLoc(),
1351                     "fir.select_type should have already been converted");
1352     return failure();
1353   }
1354 };
1355 
1356 /// conversion of fir::SelectRankOp to an if-then-else ladder
1357 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1358   using FIROpConversion::FIROpConversion;
1359 
1360   mlir::LogicalResult
1361   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1362                   mlir::ConversionPatternRewriter &rewriter) const override {
1363     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1364     return success();
1365   }
1366 };
1367 
1368 /// `fir.store` --> `llvm.store`
1369 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1370   using FIROpConversion::FIROpConversion;
1371 
1372   mlir::LogicalResult
1373   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1374                   mlir::ConversionPatternRewriter &rewriter) const override {
1375     if (store.getValue().getType().isa<fir::BoxType>()) {
1376       // fir.box value is actually in memory, load it first before storing it.
1377       mlir::Location loc = store.getLoc();
1378       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1379       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1380           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1381           adaptor.getOperands()[0]);
1382       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1383           store, val, adaptor.getOperands()[1]);
1384     } else {
1385       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1386           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1387     }
1388     return success();
1389   }
1390 };
1391 
1392 /// convert to LLVM IR dialect `undef`
1393 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1394   using FIROpConversion::FIROpConversion;
1395 
1396   mlir::LogicalResult
1397   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1398                   mlir::ConversionPatternRewriter &rewriter) const override {
1399     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1400         undef, convertType(undef.getType()));
1401     return success();
1402   }
1403 };
1404 
1405 /// `fir.unreachable` --> `llvm.unreachable`
1406 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1407   using FIROpConversion::FIROpConversion;
1408 
1409   mlir::LogicalResult
1410   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1411                   mlir::ConversionPatternRewriter &rewriter) const override {
1412     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1413     return success();
1414   }
1415 };
1416 
1417 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1418   using FIROpConversion::FIROpConversion;
1419 
1420   mlir::LogicalResult
1421   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1422                   mlir::ConversionPatternRewriter &rewriter) const override {
1423     mlir::Type ty = convertType(zero.getType());
1424     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1425       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1426     } else if (ty.isa<mlir::IntegerType>()) {
1427       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1428           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1429     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1430       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1431           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1432     } else {
1433       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1434       return rewriter.notifyMatchFailure(
1435           zero,
1436           "conversion of fir.zero with aggregate type not implemented yet");
1437     }
1438     return success();
1439   }
1440 };
1441 } // namespace
1442 
1443 /// Common base class for embox to descriptor conversion.
1444 template <typename OP>
1445 struct EmboxCommonConversion : public FIROpConversion<OP> {
1446   using FIROpConversion<OP>::FIROpConversion;
1447 
1448   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1449   // The order to find the LLVMFuncOp is as follows:
1450   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1451   // 2. The first ancestor that is a LLVMFuncOp.
1452   mlir::LLVM::LLVMFuncOp
1453   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1454     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1455     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1456                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1457                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1458   }
1459 
1460   // Generate an alloca of size 1 and type \p toTy.
1461   mlir::LLVM::AllocaOp
1462   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1463                     mlir::ConversionPatternRewriter &rewriter) const {
1464     auto thisPt = rewriter.saveInsertionPoint();
1465     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1466     rewriter.setInsertionPointToStart(&func.front());
1467     auto size = this->genI32Constant(loc, rewriter, 1);
1468     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1469     rewriter.restoreInsertionPoint(thisPt);
1470     return al;
1471   }
1472 
1473   static int getCFIAttr(fir::BoxType boxTy) {
1474     auto eleTy = boxTy.getEleTy();
1475     if (eleTy.isa<fir::PointerType>())
1476       return CFI_attribute_pointer;
1477     if (eleTy.isa<fir::HeapType>())
1478       return CFI_attribute_allocatable;
1479     return CFI_attribute_other;
1480   }
1481 
1482   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1483     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1484         .template dyn_cast<fir::RecordType>();
1485   }
1486   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1487     auto recTy = unwrapIfDerived(boxTy);
1488     return recTy && recTy.getNumLenParams() > 0;
1489   }
1490   static bool isDerivedType(fir::BoxType boxTy) {
1491     return unwrapIfDerived(boxTy) != nullptr;
1492   }
1493 
1494   // Get the element size and CFI type code of the boxed value.
1495   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1496       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1497       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1498     auto doInteger =
1499         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1500       int typeCode = fir::integerBitsToTypeCode(width);
1501       return {this->genConstantOffset(loc, rewriter, width / 8),
1502               this->genConstantOffset(loc, rewriter, typeCode)};
1503     };
1504     auto doLogical =
1505         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1506       int typeCode = fir::logicalBitsToTypeCode(width);
1507       return {this->genConstantOffset(loc, rewriter, width / 8),
1508               this->genConstantOffset(loc, rewriter, typeCode)};
1509     };
1510     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1511       int typeCode = fir::realBitsToTypeCode(width);
1512       return {this->genConstantOffset(loc, rewriter, width / 8),
1513               this->genConstantOffset(loc, rewriter, typeCode)};
1514     };
1515     auto doComplex =
1516         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1517       auto typeCode = fir::complexBitsToTypeCode(width);
1518       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1519               this->genConstantOffset(loc, rewriter, typeCode)};
1520     };
1521     auto doCharacter =
1522         [&](unsigned width,
1523             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1524       auto typeCode = fir::characterBitsToTypeCode(width);
1525       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1526       if (width == 8)
1527         return {len, typeCodeVal};
1528       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1529       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1530       auto size =
1531           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1532       return {size, typeCodeVal};
1533     };
1534     auto getKindMap = [&]() -> fir::KindMapping & {
1535       return this->lowerTy().getKindMap();
1536     };
1537     // Pointer-like types.
1538     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1539       boxEleTy = eleTy;
1540     // Integer types.
1541     if (fir::isa_integer(boxEleTy)) {
1542       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1543         return doInteger(ty.getWidth());
1544       auto ty = boxEleTy.cast<fir::IntegerType>();
1545       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1546     }
1547     // Floating point types.
1548     if (fir::isa_real(boxEleTy)) {
1549       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1550         return doFloat(ty.getWidth());
1551       auto ty = boxEleTy.cast<fir::RealType>();
1552       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1553     }
1554     // Complex types.
1555     if (fir::isa_complex(boxEleTy)) {
1556       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1557         return doComplex(
1558             ty.getElementType().cast<mlir::FloatType>().getWidth());
1559       auto ty = boxEleTy.cast<fir::ComplexType>();
1560       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1561     }
1562     // Character types.
1563     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1564       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1565       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1566         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1567         return doCharacter(charWidth, len);
1568       }
1569       assert(!lenParams.empty());
1570       return doCharacter(charWidth, lenParams.back());
1571     }
1572     // Logical type.
1573     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1574       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1575     // Array types.
1576     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1577       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1578     // Derived-type types.
1579     if (boxEleTy.isa<fir::RecordType>()) {
1580       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1581           this->lowerTy().convertType(boxEleTy));
1582       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1583       auto one =
1584           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1585       auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr,
1586                                                     mlir::ValueRange{one});
1587       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1588           loc, this->lowerTy().indexType(), gep);
1589       return {eleSize,
1590               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1591     }
1592     // Reference type.
1593     if (fir::isa_ref_type(boxEleTy)) {
1594       // FIXME: use the target pointer size rather than sizeof(void*)
1595       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1596               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1597     }
1598     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1599   }
1600 
1601   /// Basic pattern to write a field in the descriptor
1602   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1603                           mlir::Location loc, mlir::Value dest,
1604                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1605                           bool bitcast = false) const {
1606     auto boxTy = dest.getType();
1607     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1608     if (bitcast)
1609       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1610     else
1611       value = this->integerCast(loc, rewriter, fldTy, value);
1612     SmallVector<mlir::Attribute, 2> attrs;
1613     for (auto i : fldIndexes)
1614       attrs.push_back(rewriter.getI32IntegerAttr(i));
1615     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1616     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1617                                                       indexesAttr);
1618   }
1619 
1620   inline mlir::Value
1621   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1622                     mlir::Location loc, mlir::Value dest,
1623                     mlir::Value base) const {
1624     return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
1625                        /*bitCast=*/true);
1626   }
1627 
1628   inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
1629                                       mlir::Location loc, mlir::Value dest,
1630                                       unsigned dim, mlir::Value lb) const {
1631     return insertField(rewriter, loc, dest,
1632                        {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
1633   }
1634 
1635   inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
1636                                   mlir::Location loc, mlir::Value dest,
1637                                   unsigned dim, mlir::Value extent) const {
1638     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
1639                        extent);
1640   }
1641 
1642   inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
1643                                   mlir::Location loc, mlir::Value dest,
1644                                   unsigned dim, mlir::Value stride) const {
1645     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
1646                        stride);
1647   }
1648 
1649   /// Get the address of the type descriptor global variable that was created by
1650   /// lowering for derived type \p recType.
1651   template <typename BOX>
1652   mlir::Value
1653   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1654                     mlir::Location loc, fir::RecordType recType) const {
1655     std::string name = recType.translateNameToFrontendMangledName();
1656     auto module = box->template getParentOfType<mlir::ModuleOp>();
1657     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1658       auto ty = mlir::LLVM::LLVMPointerType::get(
1659           this->lowerTy().convertType(global.getType()));
1660       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1661                                                       global.getSymName());
1662     }
1663     if (auto global =
1664             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1665       // The global may have already been translated to LLVM.
1666       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1667       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1668                                                       global.getSymName());
1669     }
1670     if (fir::NameUniquer::belongsToModule(
1671             name, Fortran::semantics::typeInfoBuiltinModule)) {
1672       // Type info derived types do not have type descriptors since they are the
1673       // types defining type descriptors.
1674       return rewriter.create<mlir::LLVM::NullOp>(
1675           loc, ::getVoidPtrType(box.getContext()));
1676     }
1677     // The global does not exist in the current translation unit, but may be
1678     // defined elsewhere (e.g., type defined in a module).
1679     // Create an available_externally global to require the symbols to be
1680     // defined elsewhere and to cause link-time failure otherwise.
1681     auto i8Ty = rewriter.getIntegerType(8);
1682     mlir::OpBuilder modBuilder(module.getBodyRegion());
1683     modBuilder.create<mlir::LLVM::GlobalOp>(
1684         loc, i8Ty, /*isConstant=*/true,
1685         mlir::LLVM::Linkage::AvailableExternally, name, mlir::Attribute{});
1686     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1687     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1688   }
1689 
1690   template <typename BOX>
1691   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1692   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1693                        unsigned rank, mlir::ValueRange lenParams) const {
1694     auto loc = box.getLoc();
1695     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1696     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1697     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1698     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1699     mlir::Value descriptor =
1700         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1701 
1702     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1703     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1704       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1705         typeparams.push_back(box.substr()[1]);
1706     }
1707 
1708     // Write each of the fields with the appropriate values
1709     auto [eleSize, cfiTy] =
1710         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1711     descriptor =
1712         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1713     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1714                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1715     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1716                              this->genI32Constant(loc, rewriter, rank));
1717     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1718     descriptor =
1719         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1720                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1721     const bool hasAddendum = isDerivedType(boxTy);
1722     descriptor =
1723         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1724                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1725 
1726     if (hasAddendum) {
1727       auto isArray =
1728           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1729       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1730       auto typeDesc =
1731           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1732       descriptor =
1733           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1734                       /*bitCast=*/true);
1735     }
1736 
1737     return {boxTy, descriptor, eleSize};
1738   }
1739 
1740   /// Compute the base address of a substring given the base address of a scalar
1741   /// string and the zero based string lower bound.
1742   mlir::Value shiftSubstringBase(mlir::ConversionPatternRewriter &rewriter,
1743                                  mlir::Location loc, mlir::Value base,
1744                                  mlir::Value lowerBound) const {
1745     llvm::SmallVector<mlir::Value> gepOperands;
1746     auto baseType =
1747         base.getType().cast<mlir::LLVM::LLVMPointerType>().getElementType();
1748     if (baseType.isa<mlir::LLVM::LLVMArrayType>()) {
1749       auto idxTy = this->lowerTy().indexType();
1750       mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1751       gepOperands.push_back(zero);
1752     }
1753     gepOperands.push_back(lowerBound);
1754     return this->genGEP(loc, base.getType(), rewriter, base, gepOperands);
1755   }
1756 
1757   /// If the embox is not in a globalOp body, allocate storage for the box;
1758   /// store the value inside and return the generated alloca. Return the input
1759   /// value otherwise.
1760   mlir::Value
1761   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1762                                mlir::Location loc, mlir::Value boxValue) const {
1763     auto *thisBlock = rewriter.getInsertionBlock();
1764     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1765       return boxValue;
1766     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1767     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1768     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1769     return alloca;
1770   }
1771 };
1772 
1773 /// Compute the extent of a triplet slice (lb:ub:step).
1774 static mlir::Value
1775 computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
1776                      mlir::Location loc, mlir::Value lb, mlir::Value ub,
1777                      mlir::Value step, mlir::Value zero, mlir::Type type) {
1778   mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
1779   extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
1780   extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
1781   // If the resulting extent is negative (`ub-lb` and `step` have different
1782   // signs), zero must be returned instead.
1783   auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1784       loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
1785   return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
1786 }
1787 
1788 /// Create a generic box on a memory reference. This conversions lowers the
1789 /// abstract box to the appropriate, initialized descriptor.
1790 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1791   using EmboxCommonConversion::EmboxCommonConversion;
1792 
1793   mlir::LogicalResult
1794   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1795                   mlir::ConversionPatternRewriter &rewriter) const override {
1796     assert(!embox.getShape() && "There should be no dims on this embox op");
1797     auto [boxTy, dest, eleSize] =
1798         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1799                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1800     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1801                              adaptor.getOperands()[0]);
1802     if (isDerivedTypeWithLenParams(boxTy)) {
1803       TODO(embox.getLoc(),
1804            "fir.embox codegen of derived with length parameters");
1805       return failure();
1806     }
1807     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1808     rewriter.replaceOp(embox, result);
1809     return success();
1810   }
1811 };
1812 
1813 /// Lower `fir.emboxproc` operation. Creates a procedure box.
1814 /// TODO: Part of supporting Fortran 2003 procedure pointers.
1815 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1816   using FIROpConversion::FIROpConversion;
1817 
1818   mlir::LogicalResult
1819   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1820                   mlir::ConversionPatternRewriter &rewriter) const override {
1821     TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
1822     return failure();
1823   }
1824 };
1825 
1826 /// Create a generic box on a memory reference.
1827 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
1828   using EmboxCommonConversion::EmboxCommonConversion;
1829 
1830   mlir::LogicalResult
1831   matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
1832                   mlir::ConversionPatternRewriter &rewriter) const override {
1833     auto [boxTy, dest, eleSize] = consDescriptorPrefix(
1834         xbox, rewriter, xbox.getOutRank(),
1835         adaptor.getOperands().drop_front(xbox.lenParamOffset()));
1836     // Generate the triples in the dims field of the descriptor
1837     mlir::ValueRange operands = adaptor.getOperands();
1838     auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
1839     mlir::Value base = operands[0];
1840     assert(!xbox.shape().empty() && "must have a shape");
1841     unsigned shapeOffset = xbox.shapeOffset();
1842     bool hasShift = !xbox.shift().empty();
1843     unsigned shiftOffset = xbox.shiftOffset();
1844     bool hasSlice = !xbox.slice().empty();
1845     unsigned sliceOffset = xbox.sliceOffset();
1846     mlir::Location loc = xbox.getLoc();
1847     mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
1848     mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
1849     mlir::Value prevDim = integerCast(loc, rewriter, i64Ty, eleSize);
1850     mlir::Value prevPtrOff = one;
1851     mlir::Type eleTy = boxTy.getEleTy();
1852     const unsigned rank = xbox.getRank();
1853     llvm::SmallVector<mlir::Value> gepArgs;
1854     unsigned constRows = 0;
1855     mlir::Value ptrOffset = zero;
1856     if (auto memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType()))
1857       if (auto seqTy = memEleTy.dyn_cast<fir::SequenceType>()) {
1858         mlir::Type seqEleTy = seqTy.getEleTy();
1859         // Adjust the element scaling factor if the element is a dependent type.
1860         if (fir::hasDynamicSize(seqEleTy)) {
1861           if (fir::isa_char(seqEleTy)) {
1862             assert(xbox.lenParams().size() == 1);
1863             prevPtrOff = integerCast(loc, rewriter, i64Ty,
1864                                      operands[xbox.lenParamOffset()]);
1865           } else if (seqEleTy.isa<fir::RecordType>()) {
1866             TODO(loc, "generate call to calculate size of PDT");
1867           } else {
1868             return rewriter.notifyMatchFailure(xbox, "unexpected dynamic type");
1869           }
1870         } else {
1871           constRows = seqTy.getConstantRows();
1872         }
1873       }
1874 
1875     bool hasSubcomp = !xbox.subcomponent().empty();
1876     mlir::Value stepExpr;
1877     if (hasSubcomp) {
1878       // We have a subcomponent. The step value needs to be the number of
1879       // bytes per element (which is a derived type).
1880       mlir::Type ty0 = base.getType();
1881       [[maybe_unused]] auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
1882       assert(ptrTy && "expected pointer type");
1883       mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType());
1884       assert(memEleTy && "expected fir pointer type");
1885       auto seqTy = memEleTy.dyn_cast<fir::SequenceType>();
1886       assert(seqTy && "expected sequence type");
1887       mlir::Type seqEleTy = seqTy.getEleTy();
1888       auto eleTy = mlir::LLVM::LLVMPointerType::get(convertType(seqEleTy));
1889       stepExpr = computeDerivedTypeSize(loc, eleTy, i64Ty, rewriter);
1890     }
1891 
1892     // Process the array subspace arguments (shape, shift, etc.), if any,
1893     // translating everything to values in the descriptor wherever the entity
1894     // has a dynamic array dimension.
1895     for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
1896       mlir::Value extent = operands[shapeOffset];
1897       mlir::Value outerExtent = extent;
1898       bool skipNext = false;
1899       if (hasSlice) {
1900         mlir::Value off = operands[sliceOffset];
1901         mlir::Value adj = one;
1902         if (hasShift)
1903           adj = operands[shiftOffset];
1904         auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
1905         if (constRows > 0) {
1906           gepArgs.push_back(ao);
1907           --constRows;
1908         } else {
1909           auto dimOff =
1910               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
1911           ptrOffset =
1912               rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
1913         }
1914         if (mlir::isa_and_nonnull<fir::UndefOp>(
1915                 xbox.slice()[3 * di + 1].getDefiningOp())) {
1916           // This dimension contains a scalar expression in the array slice op.
1917           // The dimension is loop invariant, will be dropped, and will not
1918           // appear in the descriptor.
1919           skipNext = true;
1920         }
1921       }
1922       if (!skipNext) {
1923         // store lower bound (normally 0)
1924         mlir::Value lb = zero;
1925         if (eleTy.isa<fir::PointerType>() || eleTy.isa<fir::HeapType>()) {
1926           lb = one;
1927           if (hasShift)
1928             lb = operands[shiftOffset];
1929         }
1930         dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
1931 
1932         // store extent
1933         if (hasSlice)
1934           extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
1935                                         operands[sliceOffset + 1],
1936                                         operands[sliceOffset + 2], zero, i64Ty);
1937         dest = insertExtent(rewriter, loc, dest, descIdx, extent);
1938 
1939         // store step (scaled by shaped extent)
1940 
1941         mlir::Value step = hasSubcomp ? stepExpr : prevDim;
1942         if (hasSlice)
1943           step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step,
1944                                                     operands[sliceOffset + 2]);
1945         dest = insertStride(rewriter, loc, dest, descIdx, step);
1946         ++descIdx;
1947       }
1948 
1949       // compute the stride and offset for the next natural dimension
1950       prevDim =
1951           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevDim, outerExtent);
1952       if (constRows == 0)
1953         prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
1954                                                         outerExtent);
1955 
1956       // increment iterators
1957       ++shapeOffset;
1958       if (hasShift)
1959         ++shiftOffset;
1960       if (hasSlice)
1961         sliceOffset += 3;
1962     }
1963     if (hasSlice || hasSubcomp || !xbox.substr().empty()) {
1964       llvm::SmallVector<mlir::Value> args = {ptrOffset};
1965       args.append(gepArgs.rbegin(), gepArgs.rend());
1966       if (hasSubcomp) {
1967         // For each field in the path add the offset to base via the args list.
1968         // In the most general case, some offsets must be computed since
1969         // they are not be known until runtime.
1970         if (fir::hasDynamicSize(fir::unwrapSequenceType(
1971                 fir::unwrapPassByRefType(xbox.memref().getType()))))
1972           TODO(loc, "fir.embox codegen dynamic size component in derived type");
1973         args.append(operands.begin() + xbox.subcomponentOffset(),
1974                     operands.begin() + xbox.subcomponentOffset() +
1975                         xbox.subcomponent().size());
1976       }
1977       base =
1978           rewriter.create<mlir::LLVM::GEPOp>(loc, base.getType(), base, args);
1979       if (!xbox.substr().empty())
1980         base = shiftSubstringBase(rewriter, loc, base,
1981                                   operands[xbox.substrOffset()]);
1982     }
1983     dest = insertBaseAddress(rewriter, loc, dest, base);
1984     if (isDerivedTypeWithLenParams(boxTy))
1985       TODO(loc, "fir.embox codegen of derived with length parameters");
1986 
1987     mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest);
1988     rewriter.replaceOp(xbox, result);
1989     return success();
1990   }
1991 };
1992 
1993 /// Create a new box given a box reference.
1994 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
1995   using EmboxCommonConversion::EmboxCommonConversion;
1996 
1997   mlir::LogicalResult
1998   matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
1999                   mlir::ConversionPatternRewriter &rewriter) const override {
2000     mlir::Location loc = rebox.getLoc();
2001     mlir::Type idxTy = lowerTy().indexType();
2002     mlir::Value loweredBox = adaptor.getOperands()[0];
2003     mlir::ValueRange operands = adaptor.getOperands();
2004 
2005     // Create new descriptor and fill its non-shape related data.
2006     llvm::SmallVector<mlir::Value, 2> lenParams;
2007     mlir::Type inputEleTy = getInputEleTy(rebox);
2008     if (auto charTy = inputEleTy.dyn_cast<fir::CharacterType>()) {
2009       mlir::Value len =
2010           loadElementSizeFromBox(loc, idxTy, loweredBox, rewriter);
2011       if (charTy.getFKind() != 1) {
2012         mlir::Value width =
2013             genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
2014         len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width);
2015       }
2016       lenParams.emplace_back(len);
2017     } else if (auto recTy = inputEleTy.dyn_cast<fir::RecordType>()) {
2018       if (recTy.getNumLenParams() != 0)
2019         TODO(loc, "reboxing descriptor of derived type with length parameters");
2020     }
2021     auto [boxTy, dest, eleSize] =
2022         consDescriptorPrefix(rebox, rewriter, rebox.getOutRank(), lenParams);
2023 
2024     // Read input extents, strides, and base address
2025     llvm::SmallVector<mlir::Value> inputExtents;
2026     llvm::SmallVector<mlir::Value> inputStrides;
2027     const unsigned inputRank = rebox.getRank();
2028     for (unsigned i = 0; i < inputRank; ++i) {
2029       mlir::Value dim = genConstantIndex(loc, idxTy, rewriter, i);
2030       SmallVector<mlir::Value, 3> dimInfo =
2031           getDimsFromBox(loc, {idxTy, idxTy, idxTy}, loweredBox, dim, rewriter);
2032       inputExtents.emplace_back(dimInfo[1]);
2033       inputStrides.emplace_back(dimInfo[2]);
2034     }
2035 
2036     mlir::Type baseTy = getBaseAddrTypeFromBox(loweredBox.getType());
2037     mlir::Value baseAddr =
2038         loadBaseAddrFromBox(loc, baseTy, loweredBox, rewriter);
2039 
2040     if (!rebox.slice().empty() || !rebox.subcomponent().empty())
2041       return sliceBox(rebox, dest, baseAddr, inputExtents, inputStrides,
2042                       operands, rewriter);
2043     return reshapeBox(rebox, dest, baseAddr, inputExtents, inputStrides,
2044                       operands, rewriter);
2045   }
2046 
2047 private:
2048   /// Write resulting shape and base address in descriptor, and replace rebox
2049   /// op.
2050   mlir::LogicalResult
2051   finalizeRebox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
2052                 mlir::ValueRange lbounds, mlir::ValueRange extents,
2053                 mlir::ValueRange strides,
2054                 mlir::ConversionPatternRewriter &rewriter) const {
2055     mlir::Location loc = rebox.getLoc();
2056     mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1);
2057     for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) {
2058       unsigned dim = iter.index();
2059       mlir::Value lb = lbounds.empty() ? one : lbounds[dim];
2060       dest = insertLowerBound(rewriter, loc, dest, dim, lb);
2061       dest = insertExtent(rewriter, loc, dest, dim, std::get<0>(iter.value()));
2062       dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value()));
2063     }
2064     dest = insertBaseAddress(rewriter, loc, dest, base);
2065     mlir::Value result =
2066         placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), dest);
2067     rewriter.replaceOp(rebox, result);
2068     return success();
2069   }
2070 
2071   // Apply slice given the base address, extents and strides of the input box.
2072   mlir::LogicalResult
2073   sliceBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
2074            mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
2075            mlir::ValueRange operands,
2076            mlir::ConversionPatternRewriter &rewriter) const {
2077     mlir::Location loc = rebox.getLoc();
2078     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
2079     mlir::Type idxTy = lowerTy().indexType();
2080     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
2081     // Apply subcomponent and substring shift on base address.
2082     if (!rebox.subcomponent().empty() || !rebox.substr().empty()) {
2083       // Cast to inputEleTy* so that a GEP can be used.
2084       mlir::Type inputEleTy = getInputEleTy(rebox);
2085       auto llvmElePtrTy =
2086           mlir::LLVM::LLVMPointerType::get(convertType(inputEleTy));
2087       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, llvmElePtrTy, base);
2088 
2089       if (!rebox.subcomponent().empty()) {
2090         llvm::SmallVector<mlir::Value> gepOperands = {zero};
2091         for (unsigned i = 0; i < rebox.subcomponent().size(); ++i)
2092           gepOperands.push_back(operands[rebox.subcomponentOffset() + i]);
2093         base = genGEP(loc, llvmElePtrTy, rewriter, base, gepOperands);
2094       }
2095       if (!rebox.substr().empty())
2096         base = shiftSubstringBase(rewriter, loc, base,
2097                                   operands[rebox.substrOffset()]);
2098     }
2099 
2100     if (rebox.slice().empty())
2101       // The array section is of the form array[%component][substring], keep
2102       // the input array extents and strides.
2103       return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
2104                            inputExtents, inputStrides, rewriter);
2105 
2106     // Strides from the fir.box are in bytes.
2107     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
2108 
2109     // The slice is of the form array(i:j:k)[%component]. Compute new extents
2110     // and strides.
2111     llvm::SmallVector<mlir::Value> slicedExtents;
2112     llvm::SmallVector<mlir::Value> slicedStrides;
2113     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2114     const bool sliceHasOrigins = !rebox.shift().empty();
2115     unsigned sliceOps = rebox.sliceOffset();
2116     unsigned shiftOps = rebox.shiftOffset();
2117     auto strideOps = inputStrides.begin();
2118     const unsigned inputRank = inputStrides.size();
2119     for (unsigned i = 0; i < inputRank;
2120          ++i, ++strideOps, ++shiftOps, sliceOps += 3) {
2121       mlir::Value sliceLb =
2122           integerCast(loc, rewriter, idxTy, operands[sliceOps]);
2123       mlir::Value inputStride = *strideOps; // already idxTy
2124       // Apply origin shift: base += (lb-shift)*input_stride
2125       mlir::Value sliceOrigin =
2126           sliceHasOrigins
2127               ? integerCast(loc, rewriter, idxTy, operands[shiftOps])
2128               : one;
2129       mlir::Value diff =
2130           rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin);
2131       mlir::Value offset =
2132           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride);
2133       base = genGEP(loc, voidPtrTy, rewriter, base, offset);
2134       // Apply upper bound and step if this is a triplet. Otherwise, the
2135       // dimension is dropped and no extents/strides are computed.
2136       mlir::Value upper = operands[sliceOps + 1];
2137       const bool isTripletSlice =
2138           !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp());
2139       if (isTripletSlice) {
2140         mlir::Value step =
2141             integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]);
2142         // extent = ub-lb+step/step
2143         mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper);
2144         mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb,
2145                                                   sliceUb, step, zero, idxTy);
2146         slicedExtents.emplace_back(extent);
2147         // stride = step*input_stride
2148         mlir::Value stride =
2149             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride);
2150         slicedStrides.emplace_back(stride);
2151       }
2152     }
2153     return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
2154                          slicedExtents, slicedStrides, rewriter);
2155   }
2156 
2157   /// Apply a new shape to the data described by a box given the base address,
2158   /// extents and strides of the box.
2159   mlir::LogicalResult
2160   reshapeBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
2161              mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
2162              mlir::ValueRange operands,
2163              mlir::ConversionPatternRewriter &rewriter) const {
2164     mlir::ValueRange reboxShifts{operands.begin() + rebox.shiftOffset(),
2165                                  operands.begin() + rebox.shiftOffset() +
2166                                      rebox.shift().size()};
2167     if (rebox.shape().empty()) {
2168       // Only setting new lower bounds.
2169       return finalizeRebox(rebox, dest, base, reboxShifts, inputExtents,
2170                            inputStrides, rewriter);
2171     }
2172 
2173     mlir::Location loc = rebox.getLoc();
2174     // Strides from the fir.box are in bytes.
2175     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
2176     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
2177 
2178     llvm::SmallVector<mlir::Value> newStrides;
2179     llvm::SmallVector<mlir::Value> newExtents;
2180     mlir::Type idxTy = lowerTy().indexType();
2181     // First stride from input box is kept. The rest is assumed contiguous
2182     // (it is not possible to reshape otherwise). If the input is scalar,
2183     // which may be OK if all new extents are ones, the stride does not
2184     // matter, use one.
2185     mlir::Value stride = inputStrides.empty()
2186                              ? genConstantIndex(loc, idxTy, rewriter, 1)
2187                              : inputStrides[0];
2188     for (unsigned i = 0; i < rebox.shape().size(); ++i) {
2189       mlir::Value rawExtent = operands[rebox.shapeOffset() + i];
2190       mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent);
2191       newExtents.emplace_back(extent);
2192       newStrides.emplace_back(stride);
2193       // nextStride = extent * stride;
2194       stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
2195     }
2196     return finalizeRebox(rebox, dest, base, reboxShifts, newExtents, newStrides,
2197                          rewriter);
2198   }
2199 
2200   /// Return scalar element type of the input box.
2201   static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) {
2202     auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.box().getType());
2203     if (auto seqTy = ty.dyn_cast<fir::SequenceType>())
2204       return seqTy.getEleTy();
2205     return ty;
2206   }
2207 };
2208 
2209 // Code shared between insert_value and extract_value Ops.
2210 struct ValueOpCommon {
2211   // Translate the arguments pertaining to any multidimensional array to
2212   // row-major order for LLVM-IR.
2213   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
2214                          mlir::Type ty) {
2215     assert(ty && "type is null");
2216     const auto end = attrs.size();
2217     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
2218       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
2219         const auto dim = getDimension(seq);
2220         if (dim > 1) {
2221           auto ub = std::min(i + dim, end);
2222           std::reverse(attrs.begin() + i, attrs.begin() + ub);
2223           i += dim - 1;
2224         }
2225         ty = getArrayElementType(seq);
2226       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
2227         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
2228       } else {
2229         llvm_unreachable("index into invalid type");
2230       }
2231     }
2232   }
2233 
2234   static llvm::SmallVector<mlir::Attribute>
2235   collectIndices(mlir::ConversionPatternRewriter &rewriter,
2236                  mlir::ArrayAttr arrAttr) {
2237     llvm::SmallVector<mlir::Attribute> attrs;
2238     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
2239       if (i->isa<mlir::IntegerAttr>()) {
2240         attrs.push_back(*i);
2241       } else {
2242         auto fieldName = i->cast<mlir::StringAttr>().getValue();
2243         ++i;
2244         auto ty = i->cast<mlir::TypeAttr>().getValue();
2245         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
2246         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
2247       }
2248     }
2249     return attrs;
2250   }
2251 
2252 private:
2253   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
2254     unsigned result = 1;
2255     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
2256          eleTy;
2257          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
2258       ++result;
2259     return result;
2260   }
2261 
2262   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
2263     auto eleTy = ty.getElementType();
2264     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
2265       eleTy = arrTy.getElementType();
2266     return eleTy;
2267   }
2268 };
2269 
2270 namespace {
2271 /// Extract a subobject value from an ssa-value of aggregate type
2272 struct ExtractValueOpConversion
2273     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
2274       public ValueOpCommon {
2275   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2276 
2277   mlir::LogicalResult
2278   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
2279             mlir::ConversionPatternRewriter &rewriter) const override {
2280     auto attrs = collectIndices(rewriter, extractVal.getCoor());
2281     toRowMajor(attrs, adaptor.getOperands()[0].getType());
2282     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
2283     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
2284         extractVal, ty, adaptor.getOperands()[0], position);
2285     return success();
2286   }
2287 };
2288 
2289 /// InsertValue is the generalized instruction for the composition of new
2290 /// aggregate type values.
2291 struct InsertValueOpConversion
2292     : public FIROpAndTypeConversion<fir::InsertValueOp>,
2293       public ValueOpCommon {
2294   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2295 
2296   mlir::LogicalResult
2297   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
2298             mlir::ConversionPatternRewriter &rewriter) const override {
2299     auto attrs = collectIndices(rewriter, insertVal.getCoor());
2300     toRowMajor(attrs, adaptor.getOperands()[0].getType());
2301     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
2302     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2303         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
2304         position);
2305     return success();
2306   }
2307 };
2308 
2309 /// InsertOnRange inserts a value into a sequence over a range of offsets.
2310 struct InsertOnRangeOpConversion
2311     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
2312   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2313 
2314   // Increments an array of subscripts in a row major fasion.
2315   void incrementSubscripts(const SmallVector<uint64_t> &dims,
2316                            SmallVector<uint64_t> &subscripts) const {
2317     for (size_t i = dims.size(); i > 0; --i) {
2318       if (++subscripts[i - 1] < dims[i - 1]) {
2319         return;
2320       }
2321       subscripts[i - 1] = 0;
2322     }
2323   }
2324 
2325   mlir::LogicalResult
2326   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
2327             mlir::ConversionPatternRewriter &rewriter) const override {
2328 
2329     llvm::SmallVector<uint64_t> dims;
2330     auto type = adaptor.getOperands()[0].getType();
2331 
2332     // Iteratively extract the array dimensions from the type.
2333     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
2334       dims.push_back(t.getNumElements());
2335       type = t.getElementType();
2336     }
2337 
2338     SmallVector<uint64_t> lBounds;
2339     SmallVector<uint64_t> uBounds;
2340 
2341     // Unzip the upper and lower bound and convert to a row major format.
2342     mlir::DenseIntElementsAttr coor = range.getCoor();
2343     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
2344     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
2345       uBounds.push_back(*i++);
2346       lBounds.push_back(*i);
2347     }
2348 
2349     auto &subscripts = lBounds;
2350     auto loc = range.getLoc();
2351     mlir::Value lastOp = adaptor.getOperands()[0];
2352     mlir::Value insertVal = adaptor.getOperands()[1];
2353 
2354     auto i64Ty = rewriter.getI64Type();
2355     while (subscripts != uBounds) {
2356       // Convert uint64_t's to Attribute's.
2357       SmallVector<mlir::Attribute> subscriptAttrs;
2358       for (const auto &subscript : subscripts)
2359         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
2360       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2361           loc, ty, lastOp, insertVal,
2362           ArrayAttr::get(range.getContext(), subscriptAttrs));
2363 
2364       incrementSubscripts(dims, subscripts);
2365     }
2366 
2367     // Convert uint64_t's to Attribute's.
2368     SmallVector<mlir::Attribute> subscriptAttrs;
2369     for (const auto &subscript : subscripts)
2370       subscriptAttrs.push_back(
2371           IntegerAttr::get(rewriter.getI64Type(), subscript));
2372     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
2373 
2374     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2375         range, ty, lastOp, insertVal,
2376         ArrayAttr::get(range.getContext(), arrayRef));
2377 
2378     return success();
2379   }
2380 };
2381 } // namespace
2382 
2383 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced,
2384 /// shifted etc. array.
2385 /// (See the static restriction on coordinate_of.) array_coor determines the
2386 /// coordinate (location) of a specific element.
2387 struct XArrayCoorOpConversion
2388     : public FIROpAndTypeConversion<fir::cg::XArrayCoorOp> {
2389   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2390 
2391   mlir::LogicalResult
2392   doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type ty, OpAdaptor adaptor,
2393             mlir::ConversionPatternRewriter &rewriter) const override {
2394     auto loc = coor.getLoc();
2395     mlir::ValueRange operands = adaptor.getOperands();
2396     unsigned rank = coor.getRank();
2397     assert(coor.indices().size() == rank);
2398     assert(coor.shape().empty() || coor.shape().size() == rank);
2399     assert(coor.shift().empty() || coor.shift().size() == rank);
2400     assert(coor.slice().empty() || coor.slice().size() == 3 * rank);
2401     mlir::Type idxTy = lowerTy().indexType();
2402     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2403     mlir::Value prevExt = one;
2404     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
2405     mlir::Value offset = zero;
2406     const bool isShifted = !coor.shift().empty();
2407     const bool isSliced = !coor.slice().empty();
2408     const bool baseIsBoxed = coor.memref().getType().isa<fir::BoxType>();
2409 
2410     auto indexOps = coor.indices().begin();
2411     auto shapeOps = coor.shape().begin();
2412     auto shiftOps = coor.shift().begin();
2413     auto sliceOps = coor.slice().begin();
2414     // For each dimension of the array, generate the offset calculation.
2415     for (unsigned i = 0; i < rank;
2416          ++i, ++indexOps, ++shapeOps, ++shiftOps, sliceOps += 3) {
2417       mlir::Value index =
2418           integerCast(loc, rewriter, idxTy, operands[coor.indicesOffset() + i]);
2419       mlir::Value lb = isShifted ? integerCast(loc, rewriter, idxTy,
2420                                                operands[coor.shiftOffset() + i])
2421                                  : one;
2422       mlir::Value step = one;
2423       bool normalSlice = isSliced;
2424       // Compute zero based index in dimension i of the element, applying
2425       // potential triplets and lower bounds.
2426       if (isSliced) {
2427         mlir::Value ub = *(sliceOps + 1);
2428         normalSlice = !mlir::isa_and_nonnull<fir::UndefOp>(ub.getDefiningOp());
2429         if (normalSlice)
2430           step = integerCast(loc, rewriter, idxTy, *(sliceOps + 2));
2431       }
2432       auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb);
2433       mlir::Value diff =
2434           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step);
2435       if (normalSlice) {
2436         mlir::Value sliceLb =
2437             integerCast(loc, rewriter, idxTy, operands[coor.sliceOffset() + i]);
2438         auto adj = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb);
2439         diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj);
2440       }
2441       // Update the offset given the stride and the zero based index `diff`
2442       // that was just computed.
2443       if (baseIsBoxed) {
2444         // Use stride in bytes from the descriptor.
2445         mlir::Value stride =
2446             loadStrideFromBox(loc, adaptor.getOperands()[0], i, rewriter);
2447         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride);
2448         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
2449       } else {
2450         // Use stride computed at last iteration.
2451         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt);
2452         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
2453         // Compute next stride assuming contiguity of the base array
2454         // (in element number).
2455         auto nextExt =
2456             integerCast(loc, rewriter, idxTy, operands[coor.shapeOffset() + i]);
2457         prevExt =
2458             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt, nextExt);
2459       }
2460     }
2461 
2462     // Add computed offset to the base address.
2463     if (baseIsBoxed) {
2464       // Working with byte offsets. The base address is read from the fir.box.
2465       // and need to be casted to i8* to do the pointer arithmetic.
2466       mlir::Type baseTy =
2467           getBaseAddrTypeFromBox(adaptor.getOperands()[0].getType());
2468       mlir::Value base =
2469           loadBaseAddrFromBox(loc, baseTy, adaptor.getOperands()[0], rewriter);
2470       mlir::Type voidPtrTy = getVoidPtrType();
2471       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
2472       llvm::SmallVector<mlir::Value> args{offset};
2473       auto addr =
2474           rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy, base, args);
2475       if (coor.subcomponent().empty()) {
2476         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, baseTy, addr);
2477         return success();
2478       }
2479       auto casted = rewriter.create<mlir::LLVM::BitcastOp>(loc, baseTy, addr);
2480       args.clear();
2481       args.push_back(zero);
2482       if (!coor.lenParams().empty()) {
2483         // If type parameters are present, then we don't want to use a GEPOp
2484         // as below, as the LLVM struct type cannot be statically defined.
2485         TODO(loc, "derived type with type parameters");
2486       }
2487       // TODO: array offset subcomponents must be converted to LLVM's
2488       // row-major layout here.
2489       for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
2490         args.push_back(operands[i]);
2491       rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, baseTy, casted,
2492                                                      args);
2493       return success();
2494     }
2495 
2496     // The array was not boxed, so it must be contiguous. offset is therefore an
2497     // element offset and the base type is kept in the GEP unless the element
2498     // type size is itself dynamic.
2499     mlir::Value base;
2500     if (coor.subcomponent().empty()) {
2501       // No subcomponent.
2502       if (!coor.lenParams().empty()) {
2503         // Type parameters. Adjust element size explicitly.
2504         auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType());
2505         assert(eleTy && "result must be a reference-like type");
2506         if (fir::characterWithDynamicLen(eleTy)) {
2507           assert(coor.lenParams().size() == 1);
2508           auto bitsInChar = lowerTy().getKindMap().getCharacterBitsize(
2509               eleTy.cast<fir::CharacterType>().getFKind());
2510           auto scaling = genConstantIndex(loc, idxTy, rewriter, bitsInChar / 8);
2511           auto scaledBySize =
2512               rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset, scaling);
2513           auto length =
2514               integerCast(loc, rewriter, idxTy,
2515                           adaptor.getOperands()[coor.lenParamsOffset()]);
2516           offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, scaledBySize,
2517                                                       length);
2518         } else {
2519           TODO(loc, "compute size of derived type with type parameters");
2520         }
2521       }
2522       // Cast the base address to a pointer to T.
2523       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, ty,
2524                                                     adaptor.getOperands()[0]);
2525     } else {
2526       // Operand #0 must have a pointer type. For subcomponent slicing, we
2527       // want to cast away the array type and have a plain struct type.
2528       mlir::Type ty0 = adaptor.getOperands()[0].getType();
2529       auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
2530       assert(ptrTy && "expected pointer type");
2531       mlir::Type eleTy = ptrTy.getElementType();
2532       while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
2533         eleTy = arrTy.getElementType();
2534       auto newTy = mlir::LLVM::LLVMPointerType::get(eleTy);
2535       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, newTy,
2536                                                     adaptor.getOperands()[0]);
2537     }
2538     SmallVector<mlir::Value> args = {offset};
2539     for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
2540       args.push_back(operands[i]);
2541     rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, ty, base, args);
2542     return success();
2543   }
2544 };
2545 
2546 //
2547 // Primitive operations on Complex types
2548 //
2549 
2550 /// Generate inline code for complex addition/subtraction
2551 template <typename LLVMOP, typename OPTY>
2552 static mlir::LLVM::InsertValueOp
2553 complexSum(OPTY sumop, mlir::ValueRange opnds,
2554            mlir::ConversionPatternRewriter &rewriter,
2555            fir::LLVMTypeConverter &lowering) {
2556   mlir::Value a = opnds[0];
2557   mlir::Value b = opnds[1];
2558   auto loc = sumop.getLoc();
2559   auto ctx = sumop.getContext();
2560   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2561   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2562   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
2563   mlir::Type ty = lowering.convertType(sumop.getType());
2564   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2565   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2566   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2567   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2568   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
2569   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
2570   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2571   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
2572   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
2573 }
2574 
2575 namespace {
2576 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
2577   using FIROpConversion::FIROpConversion;
2578 
2579   mlir::LogicalResult
2580   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
2581                   mlir::ConversionPatternRewriter &rewriter) const override {
2582     // given: (x + iy) + (x' + iy')
2583     // result: (x + x') + i(y + y')
2584     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
2585                                             rewriter, lowerTy());
2586     rewriter.replaceOp(addc, r.getResult());
2587     return success();
2588   }
2589 };
2590 
2591 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
2592   using FIROpConversion::FIROpConversion;
2593 
2594   mlir::LogicalResult
2595   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
2596                   mlir::ConversionPatternRewriter &rewriter) const override {
2597     // given: (x + iy) - (x' + iy')
2598     // result: (x - x') + i(y - y')
2599     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
2600                                             rewriter, lowerTy());
2601     rewriter.replaceOp(subc, r.getResult());
2602     return success();
2603   }
2604 };
2605 
2606 /// Inlined complex multiply
2607 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
2608   using FIROpConversion::FIROpConversion;
2609 
2610   mlir::LogicalResult
2611   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
2612                   mlir::ConversionPatternRewriter &rewriter) const override {
2613     // TODO: Can we use a call to __muldc3 ?
2614     // given: (x + iy) * (x' + iy')
2615     // result: (xx'-yy')+i(xy'+yx')
2616     mlir::Value a = adaptor.getOperands()[0];
2617     mlir::Value b = adaptor.getOperands()[1];
2618     auto loc = mulc.getLoc();
2619     auto *ctx = mulc.getContext();
2620     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2621     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2622     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
2623     mlir::Type ty = convertType(mulc.getType());
2624     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2625     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2626     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2627     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2628     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
2629     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
2630     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
2631     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
2632     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
2633     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
2634     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2635     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
2636     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
2637     rewriter.replaceOp(mulc, r0.getResult());
2638     return success();
2639   }
2640 };
2641 
2642 /// Inlined complex division
2643 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
2644   using FIROpConversion::FIROpConversion;
2645 
2646   mlir::LogicalResult
2647   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
2648                   mlir::ConversionPatternRewriter &rewriter) const override {
2649     // TODO: Can we use a call to __divdc3 instead?
2650     // Just generate inline code for now.
2651     // given: (x + iy) / (x' + iy')
2652     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
2653     mlir::Value a = adaptor.getOperands()[0];
2654     mlir::Value b = adaptor.getOperands()[1];
2655     auto loc = divc.getLoc();
2656     auto *ctx = divc.getContext();
2657     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2658     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2659     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
2660     mlir::Type ty = convertType(divc.getType());
2661     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2662     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2663     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2664     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2665     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
2666     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
2667     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
2668     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
2669     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
2670     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
2671     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
2672     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
2673     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
2674     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
2675     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
2676     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2677     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
2678     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
2679     rewriter.replaceOp(divc, r0.getResult());
2680     return success();
2681   }
2682 };
2683 
2684 /// Inlined complex negation
2685 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
2686   using FIROpConversion::FIROpConversion;
2687 
2688   mlir::LogicalResult
2689   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
2690                   mlir::ConversionPatternRewriter &rewriter) const override {
2691     // given: -(x + iy)
2692     // result: -x - iy
2693     auto *ctxt = neg.getContext();
2694     auto eleTy = convertType(getComplexEleTy(neg.getType()));
2695     auto ty = convertType(neg.getType());
2696     auto loc = neg.getLoc();
2697     mlir::Value o0 = adaptor.getOperands()[0];
2698     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
2699     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
2700     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
2701     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
2702     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
2703     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
2704     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
2705     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
2706     return success();
2707   }
2708 };
2709 
2710 /// Conversion pattern for operation that must be dead. The information in these
2711 /// operations is used by other operation. At this point they should not have
2712 /// anymore uses.
2713 /// These operations are normally dead after the pre-codegen pass.
2714 template <typename FromOp>
2715 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
2716   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
2717       : FIROpConversion<FromOp>(lowering) {}
2718   using OpAdaptor = typename FromOp::Adaptor;
2719 
2720   mlir::LogicalResult
2721   matchAndRewrite(FromOp op, OpAdaptor adaptor,
2722                   mlir::ConversionPatternRewriter &rewriter) const final {
2723     if (!op->getUses().empty())
2724       return rewriter.notifyMatchFailure(op, "op must be dead");
2725     rewriter.eraseOp(op);
2726     return success();
2727   }
2728 };
2729 
2730 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
2731   using MustBeDeadConversion::MustBeDeadConversion;
2732 };
2733 
2734 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
2735   using MustBeDeadConversion::MustBeDeadConversion;
2736 };
2737 
2738 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
2739   using MustBeDeadConversion::MustBeDeadConversion;
2740 };
2741 
2742 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
2743   using MustBeDeadConversion::MustBeDeadConversion;
2744 };
2745 
2746 /// `fir.is_present` -->
2747 /// ```
2748 ///  %0 = llvm.mlir.constant(0 : i64)
2749 ///  %1 = llvm.ptrtoint %0
2750 ///  %2 = llvm.icmp "ne" %1, %0 : i64
2751 /// ```
2752 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
2753   using FIROpConversion::FIROpConversion;
2754 
2755   mlir::LogicalResult
2756   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
2757                   mlir::ConversionPatternRewriter &rewriter) const override {
2758     mlir::Type idxTy = lowerTy().indexType();
2759     mlir::Location loc = isPresent.getLoc();
2760     auto ptr = adaptor.getOperands()[0];
2761 
2762     if (isPresent.getVal().getType().isa<fir::BoxCharType>()) {
2763       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
2764       assert(!structTy.isOpaque() && !structTy.getBody().empty());
2765 
2766       mlir::Type ty = structTy.getBody()[0];
2767       mlir::MLIRContext *ctx = isPresent.getContext();
2768       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2769       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
2770     }
2771     mlir::LLVM::ConstantOp c0 =
2772         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
2773     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
2774     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2775         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
2776 
2777     return success();
2778   }
2779 };
2780 
2781 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
2782 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
2783 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
2784 /// element is the length of the character buffer (`#n`).
2785 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
2786   using FIROpConversion::FIROpConversion;
2787 
2788   mlir::LogicalResult
2789   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
2790                   mlir::ConversionPatternRewriter &rewriter) const override {
2791     mlir::ValueRange operands = adaptor.getOperands();
2792     MLIRContext *ctx = emboxChar.getContext();
2793 
2794     mlir::Value charBuffer = operands[0];
2795     mlir::Value charBufferLen = operands[1];
2796 
2797     mlir::Location loc = emboxChar.getLoc();
2798     mlir::Type llvmStructTy = convertType(emboxChar.getType());
2799     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
2800 
2801     mlir::Type lenTy =
2802         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
2803     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
2804 
2805     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2806     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2807     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2808         loc, llvmStructTy, llvmStruct, charBuffer, c0);
2809     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2810         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
2811 
2812     return success();
2813   }
2814 };
2815 } // namespace
2816 
2817 /// Construct an `llvm.extractvalue` instruction. It will return value at
2818 /// element \p x from  \p tuple.
2819 static mlir::LLVM::ExtractValueOp
2820 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
2821                          mlir::ConversionPatternRewriter &rewriter,
2822                          mlir::MLIRContext *ctx, int x) {
2823   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
2824   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
2825   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
2826 }
2827 
2828 namespace {
2829 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
2830 /// boxchar.
2831 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
2832   using FIROpConversion::FIROpConversion;
2833 
2834   mlir::LogicalResult
2835   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
2836                   mlir::ConversionPatternRewriter &rewriter) const override {
2837     mlir::Value boxChar = adaptor.getOperands()[0];
2838     mlir::Location loc = boxChar.getLoc();
2839     mlir::MLIRContext *ctx = boxChar.getContext();
2840     mlir::Type returnValTy = boxCharLen.getResult().getType();
2841 
2842     constexpr int boxcharLenIdx = 1;
2843     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
2844         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
2845     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
2846     rewriter.replaceOp(boxCharLen, lenAfterCast);
2847 
2848     return success();
2849   }
2850 };
2851 
2852 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2853 /// the character buffer and one for the buffer length.
2854 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2855   using FIROpConversion::FIROpConversion;
2856 
2857   mlir::LogicalResult
2858   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2859                   mlir::ConversionPatternRewriter &rewriter) const override {
2860     MLIRContext *ctx = unboxchar.getContext();
2861 
2862     mlir::Type lenTy = convertType(unboxchar.getType(1));
2863     mlir::Value tuple = adaptor.getOperands()[0];
2864     mlir::Type tupleTy = tuple.getType();
2865 
2866     mlir::Location loc = unboxchar.getLoc();
2867     mlir::Value ptrToBuffer =
2868         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2869 
2870     mlir::LLVM::ExtractValueOp len =
2871         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2872     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2873 
2874     rewriter.replaceOp(unboxchar,
2875                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2876     return success();
2877   }
2878 };
2879 
2880 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2881 /// components.
2882 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2883 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2884   using FIROpConversion::FIROpConversion;
2885 
2886   mlir::LogicalResult
2887   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2888                   mlir::ConversionPatternRewriter &rewriter) const override {
2889     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
2890     return failure();
2891   }
2892 };
2893 
2894 /// Convert `fir.field_index`. The conversion depends on whether the size of
2895 /// the record is static or dynamic.
2896 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2897   using FIROpConversion::FIROpConversion;
2898 
2899   // NB: most field references should be resolved by this point
2900   mlir::LogicalResult
2901   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2902                   mlir::ConversionPatternRewriter &rewriter) const override {
2903     auto recTy = field.getOnType().cast<fir::RecordType>();
2904     unsigned index = recTy.getFieldIndex(field.getFieldId());
2905 
2906     if (!fir::hasDynamicSize(recTy)) {
2907       // Derived type has compile-time constant layout. Return index of the
2908       // component type in the parent type (to be used in GEP).
2909       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2910                                     field.getLoc(), rewriter, index)});
2911       return success();
2912     }
2913 
2914     // Derived type has compile-time constant layout. Call the compiler
2915     // generated function to determine the byte offset of the field at runtime.
2916     // This returns a non-constant.
2917     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2918         field.getContext(), getOffsetMethodName(recTy, field.getFieldId()));
2919     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2920     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2921         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2922     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2923         field, lowerTy().offsetType(), adaptor.getOperands(),
2924         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2925     return success();
2926   }
2927 
2928   // Re-Construct the name of the compiler generated method that calculates the
2929   // offset
2930   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2931                                                 llvm::StringRef field) {
2932     return recTy.getName().str() + "P." + field.str() + ".offset";
2933   }
2934 };
2935 
2936 /// Convert to (memory) reference to a reference to a subobject.
2937 /// The coordinate_of op is a Swiss army knife operation that can be used on
2938 /// (memory) references to records, arrays, complex, etc. as well as boxes.
2939 /// With unboxed arrays, there is the restriction that the array have a static
2940 /// shape in all but the last column.
2941 struct CoordinateOpConversion
2942     : public FIROpAndTypeConversion<fir::CoordinateOp> {
2943   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2944 
2945   mlir::LogicalResult
2946   doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor,
2947             mlir::ConversionPatternRewriter &rewriter) const override {
2948     mlir::ValueRange operands = adaptor.getOperands();
2949 
2950     mlir::Location loc = coor.getLoc();
2951     mlir::Value base = operands[0];
2952     mlir::Type baseObjectTy = coor.getBaseType();
2953     mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2954     assert(objectTy && "fir.coordinate_of expects a reference type");
2955 
2956     // Complex type - basically, extract the real or imaginary part
2957     if (fir::isa_complex(objectTy)) {
2958       mlir::LLVM::ConstantOp c0 =
2959           genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
2960       SmallVector<mlir::Value> offs = {c0, operands[1]};
2961       mlir::Value gep = genGEP(loc, ty, rewriter, base, offs);
2962       rewriter.replaceOp(coor, gep);
2963       return success();
2964     }
2965 
2966     // Boxed type - get the base pointer from the box
2967     if (baseObjectTy.dyn_cast<fir::BoxType>())
2968       return doRewriteBox(coor, ty, operands, loc, rewriter);
2969 
2970     // Reference or pointer type
2971     if (baseObjectTy.isa<fir::ReferenceType, fir::PointerType>())
2972       return doRewriteRefOrPtr(coor, ty, operands, loc, rewriter);
2973 
2974     return rewriter.notifyMatchFailure(
2975         coor, "fir.coordinate_of base operand has unsupported type");
2976   }
2977 
2978   unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) const {
2979     return fir::hasDynamicSize(ty)
2980                ? op.getDefiningOp()
2981                      ->getAttrOfType<mlir::IntegerAttr>("field")
2982                      .getInt()
2983                : getIntValue(op);
2984   }
2985 
2986   int64_t getIntValue(mlir::Value val) const {
2987     assert(val && val.dyn_cast<mlir::OpResult>() && "must not be null value");
2988     mlir::Operation *defop = val.getDefiningOp();
2989 
2990     if (auto constOp = dyn_cast<mlir::arith::ConstantIntOp>(defop))
2991       return constOp.value();
2992     if (auto llConstOp = dyn_cast<mlir::LLVM::ConstantOp>(defop))
2993       if (auto attr = llConstOp.getValue().dyn_cast<mlir::IntegerAttr>())
2994         return attr.getValue().getSExtValue();
2995     fir::emitFatalError(val.getLoc(), "must be a constant");
2996   }
2997 
2998   bool hasSubDimensions(mlir::Type type) const {
2999     return type.isa<fir::SequenceType, fir::RecordType, mlir::TupleType>();
3000   }
3001 
3002   /// Check whether this form of `!fir.coordinate_of` is supported. These
3003   /// additional checks are required, because we are not yet able to convert
3004   /// all valid forms of `!fir.coordinate_of`.
3005   /// TODO: Either implement the unsupported cases or extend the verifier
3006   /// in FIROps.cpp instead.
3007   bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) const {
3008     const std::size_t numOfCoors = coors.size();
3009     std::size_t i = 0;
3010     bool subEle = false;
3011     bool ptrEle = false;
3012     for (; i < numOfCoors; ++i) {
3013       mlir::Value nxtOpnd = coors[i];
3014       if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
3015         subEle = true;
3016         i += arrTy.getDimension() - 1;
3017         type = arrTy.getEleTy();
3018       } else if (auto recTy = type.dyn_cast<fir::RecordType>()) {
3019         subEle = true;
3020         type = recTy.getType(getFieldNumber(recTy, nxtOpnd));
3021       } else if (auto tupTy = type.dyn_cast<mlir::TupleType>()) {
3022         subEle = true;
3023         type = tupTy.getType(getIntValue(nxtOpnd));
3024       } else {
3025         ptrEle = true;
3026       }
3027     }
3028     if (ptrEle)
3029       return (!subEle) && (numOfCoors == 1);
3030     return subEle && (i >= numOfCoors);
3031   }
3032 
3033   /// Walk the abstract memory layout and determine if the path traverses any
3034   /// array types with unknown shape. Return true iff all the array types have a
3035   /// constant shape along the path.
3036   bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) const {
3037     const std::size_t sz = coors.size();
3038     std::size_t i = 0;
3039     for (; i < sz; ++i) {
3040       mlir::Value nxtOpnd = coors[i];
3041       if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
3042         if (fir::sequenceWithNonConstantShape(arrTy))
3043           return false;
3044         i += arrTy.getDimension() - 1;
3045         type = arrTy.getEleTy();
3046       } else if (auto strTy = type.dyn_cast<fir::RecordType>()) {
3047         type = strTy.getType(getFieldNumber(strTy, nxtOpnd));
3048       } else if (auto strTy = type.dyn_cast<mlir::TupleType>()) {
3049         type = strTy.getType(getIntValue(nxtOpnd));
3050       } else {
3051         return true;
3052       }
3053     }
3054     return true;
3055   }
3056 
3057 private:
3058   mlir::LogicalResult
3059   doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, mlir::ValueRange operands,
3060                mlir::Location loc,
3061                mlir::ConversionPatternRewriter &rewriter) const {
3062     mlir::Type boxObjTy = coor.getBaseType();
3063     assert(boxObjTy.dyn_cast<fir::BoxType>() && "This is not a `fir.box`");
3064 
3065     mlir::Value boxBaseAddr = operands[0];
3066 
3067     // 1. SPECIAL CASE (uses `fir.len_param_index`):
3068     //   %box = ... : !fir.box<!fir.type<derived{len1:i32}>>
3069     //   %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}>
3070     //   %addr = coordinate_of %box, %lenp
3071     if (coor.getNumOperands() == 2) {
3072       mlir::Operation *coordinateDef =
3073           (*coor.getCoor().begin()).getDefiningOp();
3074       if (isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef)) {
3075         TODO(loc,
3076              "fir.coordinate_of - fir.len_param_index is not supported yet");
3077       }
3078     }
3079 
3080     // 2. GENERAL CASE:
3081     // 2.1. (`fir.array`)
3082     //   %box = ... : !fix.box<!fir.array<?xU>>
3083     //   %idx = ... : index
3084     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<U>
3085     // 2.2 (`fir.derived`)
3086     //   %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>>
3087     //   %idx = ... : i32
3088     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<i32>
3089     // 2.3 (`fir.derived` inside `fir.array`)
3090     //   %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32, field_2:f32}>>>
3091     //   %idx1 = ... : index
3092     //   %idx2 = ... : i32
3093     //   %resultAddr = coordinate_of %box, %idx1, %idx2 : !fir.ref<f32>
3094     // 2.4. TODO: Either document or disable any other case that the following
3095     //  implementation might convert.
3096     mlir::LLVM::ConstantOp c0 =
3097         genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
3098     mlir::Value resultAddr =
3099         loadBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()),
3100                             boxBaseAddr, rewriter);
3101     auto currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy);
3102     mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext());
3103 
3104     for (unsigned i = 1, last = operands.size(); i < last; ++i) {
3105       if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) {
3106         if (i != 1)
3107           TODO(loc, "fir.array nested inside other array and/or derived type");
3108         // Applies byte strides from the box. Ignore lower bound from box
3109         // since fir.coordinate_of indexes are zero based. Lowering takes care
3110         // of lower bound aspects. This both accounts for dynamically sized
3111         // types and non contiguous arrays.
3112         auto idxTy = lowerTy().indexType();
3113         mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0);
3114         for (unsigned index = i, lastIndex = i + arrTy.getDimension();
3115              index < lastIndex; ++index) {
3116           mlir::Value stride =
3117               loadStrideFromBox(loc, operands[0], index - i, rewriter);
3118           auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy,
3119                                                        operands[index], stride);
3120           off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off);
3121         }
3122         auto voidPtrBase =
3123             rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, resultAddr);
3124         SmallVector<mlir::Value> args{off};
3125         resultAddr = rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy,
3126                                                         voidPtrBase, args);
3127         i += arrTy.getDimension() - 1;
3128         currentObjTy = arrTy.getEleTy();
3129       } else if (auto recTy = currentObjTy.dyn_cast<fir::RecordType>()) {
3130         auto recRefTy =
3131             mlir::LLVM::LLVMPointerType::get(lowerTy().convertType(recTy));
3132         mlir::Value nxtOpnd = operands[i];
3133         auto memObj =
3134             rewriter.create<mlir::LLVM::BitcastOp>(loc, recRefTy, resultAddr);
3135         llvm::SmallVector<mlir::Value> args = {c0, nxtOpnd};
3136         currentObjTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
3137         auto llvmCurrentObjTy = lowerTy().convertType(currentObjTy);
3138         auto gep = rewriter.create<mlir::LLVM::GEPOp>(
3139             loc, mlir::LLVM::LLVMPointerType::get(llvmCurrentObjTy), memObj,
3140             args);
3141         resultAddr =
3142             rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, gep);
3143       } else {
3144         fir::emitFatalError(loc, "unexpected type in coordinate_of");
3145       }
3146     }
3147 
3148     rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, ty, resultAddr);
3149     return success();
3150   }
3151 
3152   mlir::LogicalResult
3153   doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type ty,
3154                     mlir::ValueRange operands, mlir::Location loc,
3155                     mlir::ConversionPatternRewriter &rewriter) const {
3156     mlir::Type baseObjectTy = coor.getBaseType();
3157 
3158     mlir::Type currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
3159     bool hasSubdimension = hasSubDimensions(currentObjTy);
3160     bool columnIsDeferred = !hasSubdimension;
3161 
3162     if (!supportedCoordinate(currentObjTy, operands.drop_front(1))) {
3163       TODO(loc, "unsupported combination of coordinate operands");
3164     }
3165 
3166     const bool hasKnownShape =
3167         arraysHaveKnownShape(currentObjTy, operands.drop_front(1));
3168 
3169     // If only the column is `?`, then we can simply place the column value in
3170     // the 0-th GEP position.
3171     if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) {
3172       if (!hasKnownShape) {
3173         const unsigned sz = arrTy.getDimension();
3174         if (arraysHaveKnownShape(arrTy.getEleTy(),
3175                                  operands.drop_front(1 + sz))) {
3176           llvm::ArrayRef<int64_t> shape = arrTy.getShape();
3177           bool allConst = true;
3178           for (unsigned i = 0; i < sz - 1; ++i) {
3179             if (shape[i] < 0) {
3180               allConst = false;
3181               break;
3182             }
3183           }
3184           if (allConst)
3185             columnIsDeferred = true;
3186         }
3187       }
3188     }
3189 
3190     if (fir::hasDynamicSize(fir::unwrapSequenceType(currentObjTy))) {
3191       mlir::emitError(
3192           loc, "fir.coordinate_of with a dynamic element size is unsupported");
3193       return failure();
3194     }
3195 
3196     if (hasKnownShape || columnIsDeferred) {
3197       SmallVector<mlir::Value> offs;
3198       if (hasKnownShape && hasSubdimension) {
3199         mlir::LLVM::ConstantOp c0 =
3200             genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
3201         offs.push_back(c0);
3202       }
3203       const std::size_t sz = operands.size();
3204       Optional<int> dims;
3205       SmallVector<mlir::Value> arrIdx;
3206       for (std::size_t i = 1; i < sz; ++i) {
3207         mlir::Value nxtOpnd = operands[i];
3208 
3209         if (!currentObjTy) {
3210           mlir::emitError(loc, "invalid coordinate/check failed");
3211           return failure();
3212         }
3213 
3214         // check if the i-th coordinate relates to an array
3215         if (dims.hasValue()) {
3216           arrIdx.push_back(nxtOpnd);
3217           int dimsLeft = *dims;
3218           if (dimsLeft > 1) {
3219             dims = dimsLeft - 1;
3220             continue;
3221           }
3222           currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy();
3223           // append array range in reverse (FIR arrays are column-major)
3224           offs.append(arrIdx.rbegin(), arrIdx.rend());
3225           arrIdx.clear();
3226           dims.reset();
3227           continue;
3228         }
3229         if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) {
3230           int d = arrTy.getDimension() - 1;
3231           if (d > 0) {
3232             dims = d;
3233             arrIdx.push_back(nxtOpnd);
3234             continue;
3235           }
3236           currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy();
3237           offs.push_back(nxtOpnd);
3238           continue;
3239         }
3240 
3241         // check if the i-th coordinate relates to a field
3242         if (auto recTy = currentObjTy.dyn_cast<fir::RecordType>())
3243           currentObjTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
3244         else if (auto tupTy = currentObjTy.dyn_cast<mlir::TupleType>())
3245           currentObjTy = tupTy.getType(getIntValue(nxtOpnd));
3246         else
3247           currentObjTy = nullptr;
3248 
3249         offs.push_back(nxtOpnd);
3250       }
3251       if (dims.hasValue())
3252         offs.append(arrIdx.rbegin(), arrIdx.rend());
3253       mlir::Value base = operands[0];
3254       mlir::Value retval = genGEP(loc, ty, rewriter, base, offs);
3255       rewriter.replaceOp(coor, retval);
3256       return success();
3257     }
3258 
3259     mlir::emitError(loc, "fir.coordinate_of base operand has unsupported type");
3260     return failure();
3261   }
3262 };
3263 
3264 } // namespace
3265 
3266 namespace {
3267 /// Convert FIR dialect to LLVM dialect
3268 ///
3269 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
3270 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
3271 ///
3272 /// This pass is not complete yet. We are upstreaming it in small patches.
3273 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
3274 public:
3275   mlir::ModuleOp getModule() { return getOperation(); }
3276 
3277   void runOnOperation() override final {
3278     auto mod = getModule();
3279     if (!forcedTargetTriple.empty()) {
3280       fir::setTargetTriple(mod, forcedTargetTriple);
3281     }
3282 
3283     auto *context = getModule().getContext();
3284     fir::LLVMTypeConverter typeConverter{getModule()};
3285     mlir::RewritePatternSet pattern(context);
3286     pattern.insert<
3287         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
3288         AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
3289         BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
3290         BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
3291         BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion,
3292         CallOpConversion, CmpcOpConversion, ConstcOpConversion,
3293         ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion,
3294         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
3295         EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
3296         ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
3297         FreeMemOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
3298         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
3299         InsertValueOpConversion, IsPresentOpConversion,
3300         LenParamIndexOpConversion, LoadOpConversion, NegcOpConversion,
3301         NoReassocOpConversion, MulcOpConversion, SelectCaseOpConversion,
3302         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
3303         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
3304         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
3305         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
3306         UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
3307         XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(
3308         typeConverter);
3309     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
3310     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
3311                                                             pattern);
3312     mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter,
3313                                                           pattern);
3314     mlir::ConversionTarget target{*context};
3315     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3316 
3317     // required NOPs for applying a full conversion
3318     target.addLegalOp<mlir::ModuleOp>();
3319 
3320     // apply the patterns
3321     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
3322                                                std::move(pattern)))) {
3323       signalPassFailure();
3324     }
3325   }
3326 };
3327 
3328 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
3329 struct LLVMIRLoweringPass
3330     : public mlir::PassWrapper<LLVMIRLoweringPass,
3331                                mlir::OperationPass<mlir::ModuleOp>> {
3332   using Printer = fir::LLVMIRLoweringPrinter;
3333   LLVMIRLoweringPass(raw_ostream &output, Printer p)
3334       : output{output}, printer{p} {}
3335 
3336   mlir::ModuleOp getModule() { return getOperation(); }
3337 
3338   void runOnOperation() override final {
3339     auto *ctx = getModule().getContext();
3340     auto optName = getModule().getName();
3341     llvm::LLVMContext llvmCtx;
3342     if (auto llvmModule = mlir::translateModuleToLLVMIR(
3343             getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
3344       printer(*llvmModule, output);
3345       return;
3346     }
3347 
3348     mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
3349     signalPassFailure();
3350   }
3351 
3352 private:
3353   raw_ostream &output;
3354   Printer printer;
3355 };
3356 
3357 } // namespace
3358 
3359 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
3360   return std::make_unique<FIRToLLVMLowering>();
3361 }
3362 
3363 std::unique_ptr<mlir::Pass>
3364 fir::createLLVMDialectToLLVMPass(raw_ostream &output,
3365                                  fir::LLVMIRLoweringPrinter printer) {
3366   return std::make_unique<LLVMIRLoweringPass>(output, printer);
3367 }
3368