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 == "common")
2651         return mlir::LLVM::Linkage::Common;
2652       if (name == "weak")
2653         return mlir::LLVM::Linkage::Weak;
2654     }
2655     return mlir::LLVM::Linkage::External;
2656   }
2657 };
2658 
2659 /// `fir.load` --> `llvm.load`
2660 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
2661   using FIROpConversion::FIROpConversion;
2662 
2663   mlir::LogicalResult
2664   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
2665                   mlir::ConversionPatternRewriter &rewriter) const override {
2666     // fir.box is a special case because it is considered as an ssa values in
2667     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
2668     // and fir.box end up being the same llvm types and loading a
2669     // fir.ref<fir.box> is actually a no op in LLVM.
2670     if (load.getType().isa<fir::BoxType>()) {
2671       rewriter.replaceOp(load, adaptor.getOperands()[0]);
2672     } else {
2673       mlir::Type ty = convertType(load.getType());
2674       ArrayRef<NamedAttribute> at = load->getAttrs();
2675       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
2676           load, ty, adaptor.getOperands(), at);
2677     }
2678     return success();
2679   }
2680 };
2681 
2682 /// Lower `fir.no_reassoc` to LLVM IR dialect.
2683 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
2684 /// math flags?
2685 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
2686   using FIROpConversion::FIROpConversion;
2687 
2688   mlir::LogicalResult
2689   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
2690                   mlir::ConversionPatternRewriter &rewriter) const override {
2691     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
2692     return success();
2693   }
2694 };
2695 
2696 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
2697                         Optional<mlir::ValueRange> destOps,
2698                         mlir::ConversionPatternRewriter &rewriter,
2699                         mlir::Block *newBlock) {
2700   if (destOps.hasValue())
2701     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
2702                                           newBlock, mlir::ValueRange());
2703   else
2704     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
2705 }
2706 
2707 template <typename A, typename B>
2708 static void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
2709                     mlir::ConversionPatternRewriter &rewriter) {
2710   if (destOps.hasValue())
2711     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
2712                                                   dest);
2713   else
2714     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
2715 }
2716 
2717 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
2718                               mlir::Block *dest,
2719                               Optional<mlir::ValueRange> destOps,
2720                               mlir::ConversionPatternRewriter &rewriter) {
2721   auto *thisBlock = rewriter.getInsertionBlock();
2722   auto *newBlock = createBlock(rewriter, dest);
2723   rewriter.setInsertionPointToEnd(thisBlock);
2724   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
2725   rewriter.setInsertionPointToEnd(newBlock);
2726 }
2727 
2728 /// Conversion of `fir.select_case`
2729 ///
2730 /// The `fir.select_case` operation is converted to a if-then-else ladder.
2731 /// Depending on the case condition type, one or several comparison and
2732 /// conditional branching can be generated.
2733 ///
2734 /// A a point value case such as `case(4)`, a lower bound case such as
2735 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
2736 /// simple comparison between the selector value and the constant value in the
2737 /// case. The block associated with the case condition is then executed if
2738 /// the comparison succeed otherwise it branch to the next block with the
2739 /// comparison for the the next case conditon.
2740 ///
2741 /// A closed interval case condition such as `case(7:10)` is converted with a
2742 /// first comparison and conditional branching for the lower bound. If
2743 /// successful, it branch to a second block with the comparison for the
2744 /// upper bound in the same case condition.
2745 ///
2746 /// TODO: lowering of CHARACTER type cases is not handled yet.
2747 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
2748   using FIROpConversion::FIROpConversion;
2749 
2750   mlir::LogicalResult
2751   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
2752                   mlir::ConversionPatternRewriter &rewriter) const override {
2753     unsigned conds = caseOp.getNumConditions();
2754     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
2755     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
2756     auto ty = caseOp.getSelector().getType();
2757     if (ty.isa<fir::CharacterType>()) {
2758       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
2759       return failure();
2760     }
2761     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
2762     auto loc = caseOp.getLoc();
2763     for (unsigned t = 0; t != conds; ++t) {
2764       mlir::Block *dest = caseOp.getSuccessor(t);
2765       llvm::Optional<mlir::ValueRange> destOps =
2766           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
2767       llvm::Optional<mlir::ValueRange> cmpOps =
2768           *caseOp.getCompareOperands(adaptor.getOperands(), t);
2769       mlir::Value caseArg = *(cmpOps.getValue().begin());
2770       mlir::Attribute attr = cases[t];
2771       if (attr.isa<fir::PointIntervalAttr>()) {
2772         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2773             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
2774         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2775         continue;
2776       }
2777       if (attr.isa<fir::LowerBoundAttr>()) {
2778         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2779             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
2780         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2781         continue;
2782       }
2783       if (attr.isa<fir::UpperBoundAttr>()) {
2784         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2785             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
2786         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2787         continue;
2788       }
2789       if (attr.isa<fir::ClosedIntervalAttr>()) {
2790         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2791             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
2792         auto *thisBlock = rewriter.getInsertionBlock();
2793         auto *newBlock1 = createBlock(rewriter, dest);
2794         auto *newBlock2 = createBlock(rewriter, dest);
2795         rewriter.setInsertionPointToEnd(thisBlock);
2796         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
2797         rewriter.setInsertionPointToEnd(newBlock1);
2798         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
2799         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
2800             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
2801         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
2802         rewriter.setInsertionPointToEnd(newBlock2);
2803         continue;
2804       }
2805       assert(attr.isa<mlir::UnitAttr>());
2806       assert((t + 1 == conds) && "unit must be last");
2807       genBrOp(caseOp, dest, destOps, rewriter);
2808     }
2809     return success();
2810   }
2811 };
2812 
2813 template <typename OP>
2814 static void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
2815                                   typename OP::Adaptor adaptor,
2816                                   mlir::ConversionPatternRewriter &rewriter) {
2817   unsigned conds = select.getNumConditions();
2818   auto cases = select.getCases().getValue();
2819   mlir::Value selector = adaptor.getSelector();
2820   auto loc = select.getLoc();
2821   assert(conds > 0 && "select must have cases");
2822 
2823   llvm::SmallVector<mlir::Block *> destinations;
2824   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
2825   mlir::Block *defaultDestination;
2826   mlir::ValueRange defaultOperands;
2827   llvm::SmallVector<int32_t> caseValues;
2828 
2829   for (unsigned t = 0; t != conds; ++t) {
2830     mlir::Block *dest = select.getSuccessor(t);
2831     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
2832     const mlir::Attribute &attr = cases[t];
2833     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
2834       destinations.push_back(dest);
2835       destinationsOperands.push_back(destOps.hasValue() ? *destOps
2836                                                         : ValueRange());
2837       caseValues.push_back(intAttr.getInt());
2838       continue;
2839     }
2840     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
2841     assert((t + 1 == conds) && "unit must be last");
2842     defaultDestination = dest;
2843     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
2844   }
2845 
2846   // LLVM::SwitchOp takes a i32 type for the selector.
2847   if (select.getSelector().getType() != rewriter.getI32Type())
2848     selector =
2849         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
2850 
2851   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
2852       select, selector,
2853       /*defaultDestination=*/defaultDestination,
2854       /*defaultOperands=*/defaultOperands,
2855       /*caseValues=*/caseValues,
2856       /*caseDestinations=*/destinations,
2857       /*caseOperands=*/destinationsOperands,
2858       /*branchWeights=*/ArrayRef<int32_t>());
2859 }
2860 
2861 /// conversion of fir::SelectOp to an if-then-else ladder
2862 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
2863   using FIROpConversion::FIROpConversion;
2864 
2865   mlir::LogicalResult
2866   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
2867                   mlir::ConversionPatternRewriter &rewriter) const override {
2868     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
2869     return success();
2870   }
2871 };
2872 
2873 /// conversion of fir::SelectRankOp to an if-then-else ladder
2874 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
2875   using FIROpConversion::FIROpConversion;
2876 
2877   mlir::LogicalResult
2878   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
2879                   mlir::ConversionPatternRewriter &rewriter) const override {
2880     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
2881     return success();
2882   }
2883 };
2884 
2885 /// Lower `fir.select_type` to LLVM IR dialect.
2886 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
2887   using FIROpConversion::FIROpConversion;
2888 
2889   mlir::LogicalResult
2890   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
2891                   mlir::ConversionPatternRewriter &rewriter) const override {
2892     mlir::emitError(select.getLoc(),
2893                     "fir.select_type should have already been converted");
2894     return failure();
2895   }
2896 };
2897 
2898 /// `fir.store` --> `llvm.store`
2899 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
2900   using FIROpConversion::FIROpConversion;
2901 
2902   mlir::LogicalResult
2903   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
2904                   mlir::ConversionPatternRewriter &rewriter) const override {
2905     if (store.getValue().getType().isa<fir::BoxType>()) {
2906       // fir.box value is actually in memory, load it first before storing it.
2907       mlir::Location loc = store.getLoc();
2908       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
2909       auto val = rewriter.create<mlir::LLVM::LoadOp>(
2910           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
2911           adaptor.getOperands()[0]);
2912       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
2913           store, val, adaptor.getOperands()[1]);
2914     } else {
2915       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
2916           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
2917     }
2918     return success();
2919   }
2920 };
2921 
2922 namespace {
2923 
2924 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2925 /// the character buffer and one for the buffer length.
2926 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2927   using FIROpConversion::FIROpConversion;
2928 
2929   mlir::LogicalResult
2930   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2931                   mlir::ConversionPatternRewriter &rewriter) const override {
2932     MLIRContext *ctx = unboxchar.getContext();
2933 
2934     mlir::Type lenTy = convertType(unboxchar.getType(1));
2935     mlir::Value tuple = adaptor.getOperands()[0];
2936     mlir::Type tupleTy = tuple.getType();
2937 
2938     mlir::Location loc = unboxchar.getLoc();
2939     mlir::Value ptrToBuffer =
2940         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2941 
2942     mlir::LLVM::ExtractValueOp len =
2943         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2944     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2945 
2946     rewriter.replaceOp(unboxchar,
2947                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2948     return success();
2949   }
2950 };
2951 
2952 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2953 /// components.
2954 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2955 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2956   using FIROpConversion::FIROpConversion;
2957 
2958   mlir::LogicalResult
2959   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2960                   mlir::ConversionPatternRewriter &rewriter) const override {
2961     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
2962     return failure();
2963   }
2964 };
2965 
2966 /// convert to LLVM IR dialect `undef`
2967 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
2968   using FIROpConversion::FIROpConversion;
2969 
2970   mlir::LogicalResult
2971   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
2972                   mlir::ConversionPatternRewriter &rewriter) const override {
2973     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
2974         undef, convertType(undef.getType()));
2975     return success();
2976   }
2977 };
2978 
2979 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
2980   using FIROpConversion::FIROpConversion;
2981 
2982   mlir::LogicalResult
2983   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
2984                   mlir::ConversionPatternRewriter &rewriter) const override {
2985     mlir::Type ty = convertType(zero.getType());
2986     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
2987       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
2988     } else if (ty.isa<mlir::IntegerType>()) {
2989       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2990           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
2991     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
2992       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2993           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
2994     } else {
2995       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
2996       return rewriter.notifyMatchFailure(
2997           zero,
2998           "conversion of fir.zero with aggregate type not implemented yet");
2999     }
3000     return success();
3001   }
3002 };
3003 
3004 /// `fir.unreachable` --> `llvm.unreachable`
3005 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
3006   using FIROpConversion::FIROpConversion;
3007 
3008   mlir::LogicalResult
3009   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
3010                   mlir::ConversionPatternRewriter &rewriter) const override {
3011     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
3012     return success();
3013   }
3014 };
3015 
3016 /// `fir.is_present` -->
3017 /// ```
3018 ///  %0 = llvm.mlir.constant(0 : i64)
3019 ///  %1 = llvm.ptrtoint %0
3020 ///  %2 = llvm.icmp "ne" %1, %0 : i64
3021 /// ```
3022 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
3023   using FIROpConversion::FIROpConversion;
3024 
3025   mlir::LogicalResult
3026   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
3027                   mlir::ConversionPatternRewriter &rewriter) const override {
3028     mlir::Type idxTy = lowerTy().indexType();
3029     mlir::Location loc = isPresent.getLoc();
3030     auto ptr = adaptor.getOperands()[0];
3031 
3032     if (isPresent.getVal().getType().isa<fir::BoxCharType>()) {
3033       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
3034       assert(!structTy.isOpaque() && !structTy.getBody().empty());
3035 
3036       mlir::Type ty = structTy.getBody()[0];
3037       mlir::MLIRContext *ctx = isPresent.getContext();
3038       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3039       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
3040     }
3041     mlir::LLVM::ConstantOp c0 =
3042         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
3043     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
3044     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3045         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
3046 
3047     return success();
3048   }
3049 };
3050 
3051 /// Create value signaling an absent optional argument in a call, e.g.
3052 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
3053 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
3054   using FIROpConversion::FIROpConversion;
3055 
3056   mlir::LogicalResult
3057   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
3058                   mlir::ConversionPatternRewriter &rewriter) const override {
3059     mlir::Type ty = convertType(absent.getType());
3060     mlir::Location loc = absent.getLoc();
3061 
3062     if (absent.getType().isa<fir::BoxCharType>()) {
3063       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
3064       assert(!structTy.isOpaque() && !structTy.getBody().empty());
3065       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3066       auto nullField =
3067           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
3068       mlir::MLIRContext *ctx = absent.getContext();
3069       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3070       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
3071           absent, ty, undefStruct, nullField, c0);
3072     } else {
3073       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
3074     }
3075     return success();
3076   }
3077 };
3078 
3079 //
3080 // Primitive operations on Complex types
3081 //
3082 
3083 /// Generate inline code for complex addition/subtraction
3084 template <typename LLVMOP, typename OPTY>
3085 static mlir::LLVM::InsertValueOp
3086 complexSum(OPTY sumop, mlir::ValueRange opnds,
3087            mlir::ConversionPatternRewriter &rewriter,
3088            fir::LLVMTypeConverter &lowering) {
3089   mlir::Value a = opnds[0];
3090   mlir::Value b = opnds[1];
3091   auto loc = sumop.getLoc();
3092   auto ctx = sumop.getContext();
3093   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3094   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3095   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
3096   mlir::Type ty = lowering.convertType(sumop.getType());
3097   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3098   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3099   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3100   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3101   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
3102   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
3103   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3104   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
3105   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
3106 }
3107 } // namespace
3108 
3109 namespace {
3110 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
3111   using FIROpConversion::FIROpConversion;
3112 
3113   mlir::LogicalResult
3114   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
3115                   mlir::ConversionPatternRewriter &rewriter) const override {
3116     // given: (x + iy) + (x' + iy')
3117     // result: (x + x') + i(y + y')
3118     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
3119                                             rewriter, lowerTy());
3120     rewriter.replaceOp(addc, r.getResult());
3121     return success();
3122   }
3123 };
3124 
3125 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
3126   using FIROpConversion::FIROpConversion;
3127 
3128   mlir::LogicalResult
3129   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
3130                   mlir::ConversionPatternRewriter &rewriter) const override {
3131     // given: (x + iy) - (x' + iy')
3132     // result: (x - x') + i(y - y')
3133     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
3134                                             rewriter, lowerTy());
3135     rewriter.replaceOp(subc, r.getResult());
3136     return success();
3137   }
3138 };
3139 
3140 /// Inlined complex multiply
3141 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
3142   using FIROpConversion::FIROpConversion;
3143 
3144   mlir::LogicalResult
3145   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
3146                   mlir::ConversionPatternRewriter &rewriter) const override {
3147     // TODO: Can we use a call to __muldc3 ?
3148     // given: (x + iy) * (x' + iy')
3149     // result: (xx'-yy')+i(xy'+yx')
3150     mlir::Value a = adaptor.getOperands()[0];
3151     mlir::Value b = adaptor.getOperands()[1];
3152     auto loc = mulc.getLoc();
3153     auto *ctx = mulc.getContext();
3154     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3155     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3156     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
3157     mlir::Type ty = convertType(mulc.getType());
3158     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3159     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3160     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3161     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3162     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
3163     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
3164     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
3165     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
3166     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
3167     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
3168     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3169     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
3170     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
3171     rewriter.replaceOp(mulc, r0.getResult());
3172     return success();
3173   }
3174 };
3175 
3176 /// Inlined complex division
3177 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
3178   using FIROpConversion::FIROpConversion;
3179 
3180   mlir::LogicalResult
3181   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
3182                   mlir::ConversionPatternRewriter &rewriter) const override {
3183     // TODO: Can we use a call to __divdc3 instead?
3184     // Just generate inline code for now.
3185     // given: (x + iy) / (x' + iy')
3186     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
3187     mlir::Value a = adaptor.getOperands()[0];
3188     mlir::Value b = adaptor.getOperands()[1];
3189     auto loc = divc.getLoc();
3190     auto *ctx = divc.getContext();
3191     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3192     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3193     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
3194     mlir::Type ty = convertType(divc.getType());
3195     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3196     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3197     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3198     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3199     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
3200     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
3201     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
3202     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
3203     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
3204     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
3205     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
3206     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
3207     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
3208     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
3209     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
3210     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3211     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
3212     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
3213     rewriter.replaceOp(divc, r0.getResult());
3214     return success();
3215   }
3216 };
3217 
3218 /// Inlined complex negation
3219 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
3220   using FIROpConversion::FIROpConversion;
3221 
3222   mlir::LogicalResult
3223   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
3224                   mlir::ConversionPatternRewriter &rewriter) const override {
3225     // given: -(x + iy)
3226     // result: -x - iy
3227     auto *ctxt = neg.getContext();
3228     auto eleTy = convertType(getComplexEleTy(neg.getType()));
3229     auto ty = convertType(neg.getType());
3230     auto loc = neg.getLoc();
3231     mlir::Value o0 = adaptor.getOperands()[0];
3232     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
3233     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
3234     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
3235     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
3236     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
3237     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
3238     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
3239     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
3240     return success();
3241   }
3242 };
3243 
3244 /// Conversion pattern for operation that must be dead. The information in these
3245 /// operations is used by other operation. At this point they should not have
3246 /// anymore uses.
3247 /// These operations are normally dead after the pre-codegen pass.
3248 template <typename FromOp>
3249 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
3250   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering,
3251                                 const fir::FIRToLLVMPassOptions &options)
3252       : FIROpConversion<FromOp>(lowering, options) {}
3253   using OpAdaptor = typename FromOp::Adaptor;
3254 
3255   mlir::LogicalResult
3256   matchAndRewrite(FromOp op, OpAdaptor adaptor,
3257                   mlir::ConversionPatternRewriter &rewriter) const final {
3258     if (!op->getUses().empty())
3259       return rewriter.notifyMatchFailure(op, "op must be dead");
3260     rewriter.eraseOp(op);
3261     return success();
3262   }
3263 };
3264 
3265 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
3266   using MustBeDeadConversion::MustBeDeadConversion;
3267 };
3268 
3269 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
3270   using MustBeDeadConversion::MustBeDeadConversion;
3271 };
3272 
3273 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
3274   using MustBeDeadConversion::MustBeDeadConversion;
3275 };
3276 
3277 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
3278   using MustBeDeadConversion::MustBeDeadConversion;
3279 };
3280 
3281 } // namespace
3282 
3283 namespace {
3284 /// Convert FIR dialect to LLVM dialect
3285 ///
3286 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
3287 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
3288 ///
3289 /// This pass is not complete yet. We are upstreaming it in small patches.
3290 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
3291 public:
3292   FIRToLLVMLowering() = default;
3293   FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {}
3294   mlir::ModuleOp getModule() { return getOperation(); }
3295 
3296   void runOnOperation() override final {
3297     auto mod = getModule();
3298     if (!forcedTargetTriple.empty()) {
3299       fir::setTargetTriple(mod, forcedTargetTriple);
3300     }
3301 
3302     auto *context = getModule().getContext();
3303     fir::LLVMTypeConverter typeConverter{getModule()};
3304     mlir::RewritePatternSet pattern(context);
3305     pattern.insert<
3306         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
3307         AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
3308         BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
3309         BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
3310         BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion,
3311         CallOpConversion, CmpcOpConversion, ConstcOpConversion,
3312         ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion,
3313         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
3314         EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
3315         ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
3316         FreeMemOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion,
3317         GlobalOpConversion, HasValueOpConversion, InsertOnRangeOpConversion,
3318         InsertValueOpConversion, IsPresentOpConversion,
3319         LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
3320         NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
3321         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
3322         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
3323         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
3324         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
3325         UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
3326         XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter,
3327                                                                   options);
3328     mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern);
3329     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
3330                                                             pattern);
3331     mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter,
3332                                                           pattern);
3333     mlir::ConversionTarget target{*context};
3334     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3335 
3336     // required NOPs for applying a full conversion
3337     target.addLegalOp<mlir::ModuleOp>();
3338 
3339     // apply the patterns
3340     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
3341                                                std::move(pattern)))) {
3342       signalPassFailure();
3343     }
3344   }
3345 
3346 private:
3347   fir::FIRToLLVMPassOptions options;
3348 };
3349 
3350 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
3351 struct LLVMIRLoweringPass
3352     : public mlir::PassWrapper<LLVMIRLoweringPass,
3353                                mlir::OperationPass<mlir::ModuleOp>> {
3354   using Printer = fir::LLVMIRLoweringPrinter;
3355   LLVMIRLoweringPass(raw_ostream &output, Printer p)
3356       : output{output}, printer{p} {}
3357 
3358   mlir::ModuleOp getModule() { return getOperation(); }
3359 
3360   void runOnOperation() override final {
3361     auto *ctx = getModule().getContext();
3362     auto optName = getModule().getName();
3363     llvm::LLVMContext llvmCtx;
3364     if (auto llvmModule = mlir::translateModuleToLLVMIR(
3365             getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
3366       printer(*llvmModule, output);
3367       return;
3368     }
3369 
3370     mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
3371     signalPassFailure();
3372   }
3373 
3374 private:
3375   raw_ostream &output;
3376   Printer printer;
3377 };
3378 
3379 } // namespace
3380 
3381 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
3382   return std::make_unique<FIRToLLVMLowering>();
3383 }
3384 
3385 std::unique_ptr<mlir::Pass>
3386 fir::createFIRToLLVMPass(FIRToLLVMPassOptions options) {
3387   return std::make_unique<FIRToLLVMLowering>(options);
3388 }
3389 
3390 std::unique_ptr<mlir::Pass>
3391 fir::createLLVMDialectToLLVMPass(raw_ostream &output,
3392                                  fir::LLVMIRLoweringPrinter printer) {
3393   return std::make_unique<LLVMIRLoweringPass>(output, printer);
3394 }
3395