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