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