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