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/TypeCode.h"
20 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
21 #include "mlir/Conversion/LLVMCommon/Pattern.h"
22 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
23 #include "mlir/IR/BuiltinTypes.h"
24 #include "mlir/IR/Matchers.h"
25 #include "mlir/Pass/Pass.h"
26 #include "llvm/ADT/ArrayRef.h"
27 
28 #define DEBUG_TYPE "flang-codegen"
29 
30 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
31 #include "TypeConverter.h"
32 
33 // TODO: This should really be recovered from the specified target.
34 static constexpr unsigned defaultAlign = 8;
35 
36 /// `fir.box` attribute values as defined for CFI_attribute_t in
37 /// flang/ISO_Fortran_binding.h.
38 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
39 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
40 
41 static mlir::LLVM::ConstantOp
42 genConstantIndex(mlir::Location loc, mlir::Type ity,
43                  mlir::ConversionPatternRewriter &rewriter,
44                  std::int64_t offset) {
45   auto cattr = rewriter.getI64IntegerAttr(offset);
46   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
47 }
48 
49 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
50                           mlir::Block *insertBefore) {
51   assert(insertBefore && "expected valid insertion block");
52   return rewriter.createBlock(insertBefore->getParent(),
53                               mlir::Region::iterator(insertBefore));
54 }
55 
56 namespace {
57 /// FIR conversion pattern template
58 template <typename FromOp>
59 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
60 public:
61   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
62       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
63 
64 protected:
65   mlir::Type convertType(mlir::Type ty) const {
66     return lowerTy().convertType(ty);
67   }
68 
69   mlir::LLVM::ConstantOp
70   genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
71                  int value) const {
72     mlir::Type i32Ty = rewriter.getI32Type();
73     mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
74     return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
75   }
76 
77   mlir::LLVM::ConstantOp
78   genConstantOffset(mlir::Location loc,
79                     mlir::ConversionPatternRewriter &rewriter,
80                     int offset) const {
81     mlir::Type ity = lowerTy().offsetType();
82     mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
83     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
84   }
85 
86   /// Construct code sequence to extract the specifc value from a `fir.box`.
87   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
88                               mlir::Type resultTy,
89                               mlir::ConversionPatternRewriter &rewriter,
90                               unsigned boxValue) const {
91     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
92     mlir::LLVM::ConstantOp cValuePos =
93         genConstantOffset(loc, rewriter, boxValue);
94     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
95     auto p = rewriter.create<mlir::LLVM::GEPOp>(
96         loc, pty, mlir::ValueRange{box, c0, cValuePos});
97     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
98   }
99 
100   /// Method to construct code sequence to get the triple for dimension `dim`
101   /// from a box.
102   SmallVector<mlir::Value, 3>
103   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
104                  mlir::Value box, mlir::Value dim,
105                  mlir::ConversionPatternRewriter &rewriter) const {
106     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
107     mlir::LLVM::ConstantOp cDims =
108         genConstantOffset(loc, rewriter, kDimsPosInBox);
109     mlir::LLVM::LoadOp l0 =
110         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
111     mlir::LLVM::LoadOp l1 =
112         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
113     mlir::LLVM::LoadOp l2 =
114         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
115     return {l0.getResult(), l1.getResult(), l2.getResult()};
116   }
117 
118   mlir::LLVM::LoadOp
119   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
120                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
121                  mlir::Type ty,
122                  mlir::ConversionPatternRewriter &rewriter) const {
123     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
124     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
125     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
126     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
127   }
128 
129   /// Read base address from a fir.box. Returned address has type ty.
130   mlir::Value
131   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
132                       mlir::ConversionPatternRewriter &rewriter) const {
133     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
134     mlir::LLVM::ConstantOp cAddr =
135         genConstantOffset(loc, rewriter, kAddrPosInBox);
136     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
137     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
138     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
139   }
140 
141   mlir::Value
142   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
143                          mlir::ConversionPatternRewriter &rewriter) const {
144     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
145     mlir::LLVM::ConstantOp cElemLen =
146         genConstantOffset(loc, rewriter, kElemLenPosInBox);
147     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
148     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
149     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
150   }
151 
152   // Load the attribute from the \p box and perform a check against \p maskValue
153   // The final comparison is implemented as `(attribute & maskValue) != 0`.
154   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
155                                    mlir::ConversionPatternRewriter &rewriter,
156                                    unsigned maskValue) const {
157     mlir::Type attrTy = rewriter.getI32Type();
158     mlir::Value attribute =
159         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
160     mlir::LLVM::ConstantOp attrMask =
161         genConstantOffset(loc, rewriter, maskValue);
162     auto maskRes =
163         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
164     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
165     return rewriter.create<mlir::LLVM::ICmpOp>(
166         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
167   }
168 
169   // Get the element type given an LLVM type that is of the form
170   // [llvm.ptr](array|struct|vector)+ and the provided indexes.
171   static mlir::Type getBoxEleTy(mlir::Type type,
172                                 llvm::ArrayRef<unsigned> indexes) {
173     if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
174       type = t.getElementType();
175     for (auto i : indexes) {
176       if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
177         assert(!t.isOpaque() && i < t.getBody().size());
178         type = t.getBody()[i];
179       } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
180         type = t.getElementType();
181       } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
182         type = t.getElementType();
183       } else {
184         fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
185                             "request for invalid box element type");
186       }
187     }
188     return type;
189   }
190 
191   template <typename... ARGS>
192   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
193                            mlir::ConversionPatternRewriter &rewriter,
194                            mlir::Value base, ARGS... args) const {
195     SmallVector<mlir::Value> cv{args...};
196     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
197   }
198 
199   /// Perform an extension or truncation as needed on an integer value. Lowering
200   /// to the specific target may involve some sign-extending or truncation of
201   /// values, particularly to fit them from abstract box types to the
202   /// appropriate reified structures.
203   mlir::Value integerCast(mlir::Location loc,
204                           mlir::ConversionPatternRewriter &rewriter,
205                           mlir::Type ty, mlir::Value val) const {
206     auto valTy = val.getType();
207     // If the value was not yet lowered, lower its type so that it can
208     // be used in getPrimitiveTypeSizeInBits.
209     if (!valTy.isa<mlir::IntegerType>())
210       valTy = convertType(valTy);
211     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
212     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
213     if (toSize < fromSize)
214       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
215     if (toSize > fromSize)
216       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
217     return val;
218   }
219 
220   fir::LLVMTypeConverter &lowerTy() const {
221     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
222   }
223 };
224 
225 /// FIR conversion pattern template
226 template <typename FromOp>
227 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
228 public:
229   using FIROpConversion<FromOp>::FIROpConversion;
230   using OpAdaptor = typename FromOp::Adaptor;
231 
232   mlir::LogicalResult
233   matchAndRewrite(FromOp op, OpAdaptor adaptor,
234                   mlir::ConversionPatternRewriter &rewriter) const final {
235     mlir::Type ty = this->convertType(op.getType());
236     return doRewrite(op, ty, adaptor, rewriter);
237   }
238 
239   virtual mlir::LogicalResult
240   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
241             mlir::ConversionPatternRewriter &rewriter) const = 0;
242 };
243 
244 /// Create value signaling an absent optional argument in a call, e.g.
245 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
246 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
247   using FIROpConversion::FIROpConversion;
248 
249   mlir::LogicalResult
250   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
251                   mlir::ConversionPatternRewriter &rewriter) const override {
252     mlir::Type ty = convertType(absent.getType());
253     mlir::Location loc = absent.getLoc();
254 
255     if (absent.getType().isa<fir::BoxCharType>()) {
256       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
257       assert(!structTy.isOpaque() && !structTy.getBody().empty());
258       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
259       auto nullField =
260           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
261       mlir::MLIRContext *ctx = absent.getContext();
262       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
263       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
264           absent, ty, undefStruct, nullField, c0);
265     } else {
266       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
267     }
268     return success();
269   }
270 };
271 
272 // Lower `fir.address_of` operation to `llvm.address_of` operation.
273 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
274   using FIROpConversion::FIROpConversion;
275 
276   mlir::LogicalResult
277   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
278                   mlir::ConversionPatternRewriter &rewriter) const override {
279     auto ty = convertType(addr.getType());
280     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
281         addr, ty, addr.symbol().getRootReference().getValue());
282     return success();
283   }
284 };
285 } // namespace
286 
287 /// Lookup the function to compute the memory size of this parametric derived
288 /// type. The size of the object may depend on the LEN type parameters of the
289 /// derived type.
290 static mlir::LLVM::LLVMFuncOp
291 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
292                           mlir::ConversionPatternRewriter &rewriter) {
293   auto module = op->getParentOfType<mlir::ModuleOp>();
294   std::string name = recTy.getName().str() + "P.mem.size";
295   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
296 }
297 
298 namespace {
299 /// convert to LLVM IR dialect `alloca`
300 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
301   using FIROpConversion::FIROpConversion;
302 
303   mlir::LogicalResult
304   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
305                   mlir::ConversionPatternRewriter &rewriter) const override {
306     mlir::ValueRange operands = adaptor.getOperands();
307     auto loc = alloc.getLoc();
308     mlir::Type ity = lowerTy().indexType();
309     unsigned i = 0;
310     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
311     mlir::Type ty = convertType(alloc.getType());
312     mlir::Type resultTy = ty;
313     if (alloc.hasLenParams()) {
314       unsigned end = alloc.numLenParams();
315       llvm::SmallVector<mlir::Value> lenParams;
316       for (; i < end; ++i)
317         lenParams.push_back(operands[i]);
318       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
319       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
320         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
321             chrTy.getContext(), chrTy.getFKind());
322         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
323         assert(end == 1);
324         size = integerCast(loc, rewriter, ity, lenParams[0]);
325       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
326         mlir::LLVM::LLVMFuncOp memSizeFn =
327             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
328         if (!memSizeFn)
329           emitError(loc, "did not find allocation function");
330         mlir::NamedAttribute attr = rewriter.getNamedAttr(
331             "callee", mlir::SymbolRefAttr::get(memSizeFn));
332         auto call = rewriter.create<mlir::LLVM::CallOp>(
333             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
334         size = call.getResult(0);
335         ty = mlir::LLVM::LLVMPointerType::get(
336             mlir::IntegerType::get(alloc.getContext(), 8));
337       } else {
338         return emitError(loc, "unexpected type ")
339                << scalarType << " with type parameters";
340       }
341     }
342     if (alloc.hasShapeOperands()) {
343       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
344       // Scale the size by constant factors encoded in the array type.
345       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
346         fir::SequenceType::Extent constSize = 1;
347         for (auto extent : seqTy.getShape())
348           if (extent != fir::SequenceType::getUnknownExtent())
349             constSize *= extent;
350         mlir::Value constVal{
351             genConstantIndex(loc, ity, rewriter, constSize).getResult()};
352         size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
353       }
354       unsigned end = operands.size();
355       for (; i < end; ++i)
356         size = rewriter.create<mlir::LLVM::MulOp>(
357             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
358     }
359     if (ty == resultTy) {
360       // Do not emit the bitcast if ty and resultTy are the same.
361       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
362                                                         alloc->getAttrs());
363     } else {
364       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
365                                                       alloc->getAttrs());
366       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
367     }
368     return success();
369   }
370 };
371 
372 /// Lower `fir.box_addr` to the sequence of operations to extract the first
373 /// element of the box.
374 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
375   using FIROpConversion::FIROpConversion;
376 
377   mlir::LogicalResult
378   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
379                   mlir::ConversionPatternRewriter &rewriter) const override {
380     mlir::Value a = adaptor.getOperands()[0];
381     auto loc = boxaddr.getLoc();
382     mlir::Type ty = convertType(boxaddr.getType());
383     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
384       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
385     } else {
386       auto c0attr = rewriter.getI32IntegerAttr(0);
387       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
388       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
389                                                               c0);
390     }
391     return success();
392   }
393 };
394 
395 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
396 /// dimension infomartion from the boxed value.
397 /// Result in a triple set of GEPs and loads.
398 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
399   using FIROpConversion::FIROpConversion;
400 
401   mlir::LogicalResult
402   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
403                   mlir::ConversionPatternRewriter &rewriter) const override {
404     SmallVector<mlir::Type, 3> resultTypes = {
405         convertType(boxdims.getResult(0).getType()),
406         convertType(boxdims.getResult(1).getType()),
407         convertType(boxdims.getResult(2).getType()),
408     };
409     auto results =
410         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
411                        adaptor.getOperands()[1], rewriter);
412     rewriter.replaceOp(boxdims, results);
413     return success();
414   }
415 };
416 
417 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
418 /// an element in the boxed value.
419 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
420   using FIROpConversion::FIROpConversion;
421 
422   mlir::LogicalResult
423   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
424                   mlir::ConversionPatternRewriter &rewriter) const override {
425     mlir::Value a = adaptor.getOperands()[0];
426     auto loc = boxelesz.getLoc();
427     auto ty = convertType(boxelesz.getType());
428     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
429     rewriter.replaceOp(boxelesz, elemSize);
430     return success();
431   }
432 };
433 
434 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
435 /// boxed value was from an ALLOCATABLE entity.
436 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
437   using FIROpConversion::FIROpConversion;
438 
439   mlir::LogicalResult
440   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
441                   mlir::ConversionPatternRewriter &rewriter) const override {
442     mlir::Value box = adaptor.getOperands()[0];
443     auto loc = boxisalloc.getLoc();
444     mlir::Value check =
445         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
446     rewriter.replaceOp(boxisalloc, check);
447     return success();
448   }
449 };
450 
451 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
452 /// boxed is an array.
453 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
454   using FIROpConversion::FIROpConversion;
455 
456   mlir::LogicalResult
457   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
458                   mlir::ConversionPatternRewriter &rewriter) const override {
459     mlir::Value a = adaptor.getOperands()[0];
460     auto loc = boxisarray.getLoc();
461     auto rank =
462         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
463     auto c0 = genConstantOffset(loc, rewriter, 0);
464     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
465         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
466     return success();
467   }
468 };
469 
470 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
471 /// boxed value was from a POINTER entity.
472 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
473   using FIROpConversion::FIROpConversion;
474 
475   mlir::LogicalResult
476   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
477                   mlir::ConversionPatternRewriter &rewriter) const override {
478     mlir::Value box = adaptor.getOperands()[0];
479     auto loc = boxisptr.getLoc();
480     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
481     rewriter.replaceOp(boxisptr, check);
482     return success();
483   }
484 };
485 
486 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
487 /// the box.
488 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
489   using FIROpConversion::FIROpConversion;
490 
491   mlir::LogicalResult
492   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
493                   mlir::ConversionPatternRewriter &rewriter) const override {
494     mlir::Value a = adaptor.getOperands()[0];
495     auto loc = boxrank.getLoc();
496     mlir::Type ty = convertType(boxrank.getType());
497     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
498     rewriter.replaceOp(boxrank, result);
499     return success();
500   }
501 };
502 
503 /// Lower `fir.string_lit` to LLVM IR dialect operation.
504 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
505   using FIROpConversion::FIROpConversion;
506 
507   mlir::LogicalResult
508   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
509                   mlir::ConversionPatternRewriter &rewriter) const override {
510     auto ty = convertType(constop.getType());
511     auto attr = constop.getValue();
512     if (attr.isa<mlir::StringAttr>()) {
513       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
514       return success();
515     }
516 
517     auto arr = attr.cast<mlir::ArrayAttr>();
518     auto charTy = constop.getType().cast<fir::CharacterType>();
519     unsigned bits = lowerTy().characterBitsize(charTy);
520     mlir::Type intTy = rewriter.getIntegerType(bits);
521     auto attrs = llvm::map_range(
522         arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute {
523           return mlir::IntegerAttr::get(
524               intTy,
525               attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits));
526         });
527     mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy);
528     auto denseAttr = mlir::DenseElementsAttr::get(
529         vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs));
530     rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty,
531                                                          denseAttr);
532     return success();
533   }
534 };
535 
536 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
537 /// boxproc.
538 /// TODO: Part of supporting Fortran 2003 procedure pointers.
539 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> {
540   using FIROpConversion::FIROpConversion;
541 
542   mlir::LogicalResult
543   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
544                   mlir::ConversionPatternRewriter &rewriter) const override {
545     TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
546     return failure();
547   }
548 };
549 
550 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
551 /// descriptor from the box.
552 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
553   using FIROpConversion::FIROpConversion;
554 
555   mlir::LogicalResult
556   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
557                   mlir::ConversionPatternRewriter &rewriter) const override {
558     mlir::Value box = adaptor.getOperands()[0];
559     auto loc = boxtypedesc.getLoc();
560     mlir::Type typeTy =
561         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
562     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
563     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
564     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
565                                                         result);
566     return success();
567   }
568 };
569 
570 // `fir.call` -> `llvm.call`
571 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
572   using FIROpConversion::FIROpConversion;
573 
574   mlir::LogicalResult
575   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
576                   mlir::ConversionPatternRewriter &rewriter) const override {
577     SmallVector<mlir::Type> resultTys;
578     for (auto r : call.getResults())
579       resultTys.push_back(convertType(r.getType()));
580     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
581         call, resultTys, adaptor.getOperands(), call->getAttrs());
582     return success();
583   }
584 };
585 
586 static mlir::Type getComplexEleTy(mlir::Type complex) {
587   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
588     return cc.getElementType();
589   return complex.cast<fir::ComplexType>().getElementType();
590 }
591 
592 /// Compare complex values
593 ///
594 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
595 ///
596 /// For completeness, all other comparison are done on the real component only.
597 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
598   using FIROpConversion::FIROpConversion;
599 
600   mlir::LogicalResult
601   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
602                   mlir::ConversionPatternRewriter &rewriter) const override {
603     mlir::ValueRange operands = adaptor.getOperands();
604     mlir::MLIRContext *ctxt = cmp.getContext();
605     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
606     mlir::Type resTy = convertType(cmp.getType());
607     mlir::Location loc = cmp.getLoc();
608     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
609     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
610                                        loc, eleTy, operands[0], pos0),
611                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
612                                        loc, eleTy, operands[1], pos0)};
613     auto rcp =
614         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
615     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
616     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
617                                        loc, eleTy, operands[0], pos1),
618                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
619                                        loc, eleTy, operands[1], pos1)};
620     auto icp =
621         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
622     SmallVector<mlir::Value, 2> cp{rcp, icp};
623     switch (cmp.getPredicate()) {
624     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
625       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
626       break;
627     case mlir::arith::CmpFPredicate::UNE: // .NE.
628       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
629       break;
630     default:
631       rewriter.replaceOp(cmp, rcp.getResult());
632       break;
633     }
634     return success();
635   }
636 };
637 
638 /// Lower complex constants
639 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
640   using FIROpConversion::FIROpConversion;
641 
642   mlir::LogicalResult
643   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
644                   mlir::ConversionPatternRewriter &rewriter) const override {
645     mlir::Location loc = conc.getLoc();
646     mlir::MLIRContext *ctx = conc.getContext();
647     mlir::Type ty = convertType(conc.getType());
648     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
649     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
650     auto realPart =
651         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
652     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
653     auto imPart =
654         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
655     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
656     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
657     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
658     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
659         loc, ty, undef, realPart, realIndex);
660     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
661                                                            imPart, imIndex);
662     return success();
663   }
664 
665   inline APFloat getValue(mlir::Attribute attr) const {
666     return attr.cast<fir::RealAttr>().getValue();
667   }
668 };
669 
670 /// convert value of from-type to value of to-type
671 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
672   using FIROpConversion::FIROpConversion;
673 
674   static bool isFloatingPointTy(mlir::Type ty) {
675     return ty.isa<mlir::FloatType>();
676   }
677 
678   mlir::LogicalResult
679   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
680                   mlir::ConversionPatternRewriter &rewriter) const override {
681     auto fromTy = convertType(convert.value().getType());
682     auto toTy = convertType(convert.res().getType());
683     mlir::Value op0 = adaptor.getOperands()[0];
684     if (fromTy == toTy) {
685       rewriter.replaceOp(convert, op0);
686       return success();
687     }
688     auto loc = convert.getLoc();
689     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
690                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
691       if (fromBits == toBits) {
692         // TODO: Converting between two floating-point representations with the
693         // same bitwidth is not allowed for now.
694         mlir::emitError(loc,
695                         "cannot implicitly convert between two floating-point "
696                         "representations of the same bitwidth");
697         return {};
698       }
699       if (fromBits > toBits)
700         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
701       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
702     };
703     // Complex to complex conversion.
704     if (fir::isa_complex(convert.value().getType()) &&
705         fir::isa_complex(convert.res().getType())) {
706       // Special case: handle the conversion of a complex such that both the
707       // real and imaginary parts are converted together.
708       auto zero = mlir::ArrayAttr::get(convert.getContext(),
709                                        rewriter.getI32IntegerAttr(0));
710       auto one = mlir::ArrayAttr::get(convert.getContext(),
711                                       rewriter.getI32IntegerAttr(1));
712       auto ty = convertType(getComplexEleTy(convert.value().getType()));
713       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
714       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
715       auto nt = convertType(getComplexEleTy(convert.res().getType()));
716       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
717       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
718       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
719       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
720       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
721       auto i1 =
722           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
723       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
724                                                              ic, one);
725       return mlir::success();
726     }
727     // Floating point to floating point conversion.
728     if (isFloatingPointTy(fromTy)) {
729       if (isFloatingPointTy(toTy)) {
730         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
731         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
732         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
733         rewriter.replaceOp(convert, v);
734         return mlir::success();
735       }
736       if (toTy.isa<mlir::IntegerType>()) {
737         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
738         return mlir::success();
739       }
740     } else if (fromTy.isa<mlir::IntegerType>()) {
741       // Integer to integer conversion.
742       if (toTy.isa<mlir::IntegerType>()) {
743         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
744         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
745         assert(fromBits != toBits);
746         if (fromBits > toBits) {
747           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
748           return mlir::success();
749         }
750         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
751         return mlir::success();
752       }
753       // Integer to floating point conversion.
754       if (isFloatingPointTy(toTy)) {
755         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
756         return mlir::success();
757       }
758       // Integer to pointer conversion.
759       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
760         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
761         return mlir::success();
762       }
763     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
764       // Pointer to integer conversion.
765       if (toTy.isa<mlir::IntegerType>()) {
766         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
767         return mlir::success();
768       }
769       // Pointer to pointer conversion.
770       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
771         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
772         return mlir::success();
773       }
774     }
775     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
776   }
777 };
778 
779 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
780 /// table.
781 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
782   using FIROpConversion::FIROpConversion;
783 
784   mlir::LogicalResult
785   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
786                   mlir::ConversionPatternRewriter &rewriter) const override {
787     TODO(dispatch.getLoc(), "fir.dispatch codegen");
788     return failure();
789   }
790 };
791 
792 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
793 /// derived type.
794 struct DispatchTableOpConversion
795     : public FIROpConversion<fir::DispatchTableOp> {
796   using FIROpConversion::FIROpConversion;
797 
798   mlir::LogicalResult
799   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
800                   mlir::ConversionPatternRewriter &rewriter) const override {
801     TODO(dispTab.getLoc(), "fir.dispatch_table codegen");
802     return failure();
803   }
804 };
805 
806 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
807 /// method-name to a function.
808 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
809   using FIROpConversion::FIROpConversion;
810 
811   mlir::LogicalResult
812   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
813                   mlir::ConversionPatternRewriter &rewriter) const override {
814     TODO(dtEnt.getLoc(), "fir.dt_entry codegen");
815     return failure();
816   }
817 };
818 
819 /// Lower `fir.global_len` operation.
820 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
821   using FIROpConversion::FIROpConversion;
822 
823   mlir::LogicalResult
824   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
825                   mlir::ConversionPatternRewriter &rewriter) const override {
826     TODO(globalLen.getLoc(), "fir.global_len codegen");
827     return failure();
828   }
829 };
830 
831 /// Lower fir.len_param_index
832 struct LenParamIndexOpConversion
833     : public FIROpConversion<fir::LenParamIndexOp> {
834   using FIROpConversion::FIROpConversion;
835 
836   // FIXME: this should be specialized by the runtime target
837   mlir::LogicalResult
838   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
839                   mlir::ConversionPatternRewriter &rewriter) const override {
840     TODO(lenp.getLoc(), "fir.len_param_index codegen");
841   }
842 };
843 
844 /// Lower `fir.gentypedesc` to a global constant.
845 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
846   using FIROpConversion::FIROpConversion;
847 
848   mlir::LogicalResult
849   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
850                   mlir::ConversionPatternRewriter &rewriter) const override {
851     TODO(gentypedesc.getLoc(), "fir.gentypedesc codegen");
852     return failure();
853   }
854 };
855 
856 /// Convert `fir.end`
857 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
858   using FIROpConversion::FIROpConversion;
859 
860   mlir::LogicalResult
861   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
862                   mlir::ConversionPatternRewriter &rewriter) const override {
863     TODO(firEnd.getLoc(), "fir.end codegen");
864     return failure();
865   }
866 };
867 
868 /// Lower `fir.has_value` operation to `llvm.return` operation.
869 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
870   using FIROpConversion::FIROpConversion;
871 
872   mlir::LogicalResult
873   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
874                   mlir::ConversionPatternRewriter &rewriter) const override {
875     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
876     return success();
877   }
878 };
879 
880 /// Lower `fir.global` operation to `llvm.global` operation.
881 /// `fir.insert_on_range` operations are replaced with constant dense attribute
882 /// if they are applied on the full range.
883 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
884   using FIROpConversion::FIROpConversion;
885 
886   mlir::LogicalResult
887   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
888                   mlir::ConversionPatternRewriter &rewriter) const override {
889     auto tyAttr = convertType(global.getType());
890     if (global.getType().isa<fir::BoxType>())
891       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
892     auto loc = global.getLoc();
893     mlir::Attribute initAttr{};
894     if (global.initVal())
895       initAttr = global.initVal().getValue();
896     auto linkage = convertLinkage(global.linkName());
897     auto isConst = global.constant().hasValue();
898     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
899         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
900     auto &gr = g.getInitializerRegion();
901     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
902     if (!gr.empty()) {
903       // Replace insert_on_range with a constant dense attribute if the
904       // initialization is on the full range.
905       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
906       for (auto insertOp : insertOnRangeOps) {
907         if (isFullRange(insertOp.coor(), insertOp.getType())) {
908           auto seqTyAttr = convertType(insertOp.getType());
909           auto *op = insertOp.val().getDefiningOp();
910           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
911           if (!constant) {
912             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
913             if (!convertOp)
914               continue;
915             constant = cast<mlir::arith::ConstantOp>(
916                 convertOp.value().getDefiningOp());
917           }
918           mlir::Type vecType = mlir::VectorType::get(
919               insertOp.getType().getShape(), constant.getType());
920           auto denseAttr = mlir::DenseElementsAttr::get(
921               vecType.cast<ShapedType>(), constant.value());
922           rewriter.setInsertionPointAfter(insertOp);
923           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
924               insertOp, seqTyAttr, denseAttr);
925         }
926       }
927     }
928     rewriter.eraseOp(global);
929     return success();
930   }
931 
932   bool isFullRange(mlir::DenseIntElementsAttr indexes,
933                    fir::SequenceType seqTy) const {
934     auto extents = seqTy.getShape();
935     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
936       return false;
937     auto cur_index = indexes.value_begin<int64_t>();
938     for (unsigned i = 0; i < indexes.size(); i += 2) {
939       if (*(cur_index++) != 0)
940         return false;
941       if (*(cur_index++) != extents[i / 2] - 1)
942         return false;
943     }
944     return true;
945   }
946 
947   // TODO: String comparaison should be avoided. Replace linkName with an
948   // enumeration.
949   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
950     if (optLinkage.hasValue()) {
951       auto name = optLinkage.getValue();
952       if (name == "internal")
953         return mlir::LLVM::Linkage::Internal;
954       if (name == "linkonce")
955         return mlir::LLVM::Linkage::Linkonce;
956       if (name == "common")
957         return mlir::LLVM::Linkage::Common;
958       if (name == "weak")
959         return mlir::LLVM::Linkage::Weak;
960     }
961     return mlir::LLVM::Linkage::External;
962   }
963 };
964 
965 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
966                  Optional<mlir::ValueRange> destOps,
967                  mlir::ConversionPatternRewriter &rewriter,
968                  mlir::Block *newBlock) {
969   if (destOps.hasValue())
970     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
971                                           newBlock, mlir::ValueRange());
972   else
973     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
974 }
975 
976 template <typename A, typename B>
977 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
978              mlir::ConversionPatternRewriter &rewriter) {
979   if (destOps.hasValue())
980     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
981                                                   dest);
982   else
983     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
984 }
985 
986 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
987                        Optional<mlir::ValueRange> destOps,
988                        mlir::ConversionPatternRewriter &rewriter) {
989   auto *thisBlock = rewriter.getInsertionBlock();
990   auto *newBlock = createBlock(rewriter, dest);
991   rewriter.setInsertionPointToEnd(thisBlock);
992   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
993   rewriter.setInsertionPointToEnd(newBlock);
994 }
995 
996 /// Conversion of `fir.select_case`
997 ///
998 /// The `fir.select_case` operation is converted to a if-then-else ladder.
999 /// Depending on the case condition type, one or several comparison and
1000 /// conditional branching can be generated.
1001 ///
1002 /// A a point value case such as `case(4)`, a lower bound case such as
1003 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
1004 /// simple comparison between the selector value and the constant value in the
1005 /// case. The block associated with the case condition is then executed if
1006 /// the comparison succeed otherwise it branch to the next block with the
1007 /// comparison for the the next case conditon.
1008 ///
1009 /// A closed interval case condition such as `case(7:10)` is converted with a
1010 /// first comparison and conditional branching for the lower bound. If
1011 /// successful, it branch to a second block with the comparison for the
1012 /// upper bound in the same case condition.
1013 ///
1014 /// TODO: lowering of CHARACTER type cases is not handled yet.
1015 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
1016   using FIROpConversion::FIROpConversion;
1017 
1018   mlir::LogicalResult
1019   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
1020                   mlir::ConversionPatternRewriter &rewriter) const override {
1021     unsigned conds = caseOp.getNumConditions();
1022     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
1023     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
1024     auto ty = caseOp.getSelector().getType();
1025     if (ty.isa<fir::CharacterType>()) {
1026       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
1027       return failure();
1028     }
1029     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
1030     auto loc = caseOp.getLoc();
1031     for (unsigned t = 0; t != conds; ++t) {
1032       mlir::Block *dest = caseOp.getSuccessor(t);
1033       llvm::Optional<mlir::ValueRange> destOps =
1034           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
1035       llvm::Optional<mlir::ValueRange> cmpOps =
1036           *caseOp.getCompareOperands(adaptor.getOperands(), t);
1037       mlir::Value caseArg = *(cmpOps.getValue().begin());
1038       mlir::Attribute attr = cases[t];
1039       if (attr.isa<fir::PointIntervalAttr>()) {
1040         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1041             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
1042         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1043         continue;
1044       }
1045       if (attr.isa<fir::LowerBoundAttr>()) {
1046         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1047             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1048         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1049         continue;
1050       }
1051       if (attr.isa<fir::UpperBoundAttr>()) {
1052         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1053             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
1054         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1055         continue;
1056       }
1057       if (attr.isa<fir::ClosedIntervalAttr>()) {
1058         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1059             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1060         auto *thisBlock = rewriter.getInsertionBlock();
1061         auto *newBlock1 = createBlock(rewriter, dest);
1062         auto *newBlock2 = createBlock(rewriter, dest);
1063         rewriter.setInsertionPointToEnd(thisBlock);
1064         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
1065         rewriter.setInsertionPointToEnd(newBlock1);
1066         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
1067         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
1068             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
1069         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
1070         rewriter.setInsertionPointToEnd(newBlock2);
1071         continue;
1072       }
1073       assert(attr.isa<mlir::UnitAttr>());
1074       assert((t + 1 == conds) && "unit must be last");
1075       genBrOp(caseOp, dest, destOps, rewriter);
1076     }
1077     return success();
1078   }
1079 };
1080 
1081 template <typename OP>
1082 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
1083                            typename OP::Adaptor adaptor,
1084                            mlir::ConversionPatternRewriter &rewriter) {
1085   unsigned conds = select.getNumConditions();
1086   auto cases = select.getCases().getValue();
1087   mlir::Value selector = adaptor.selector();
1088   auto loc = select.getLoc();
1089   assert(conds > 0 && "select must have cases");
1090 
1091   llvm::SmallVector<mlir::Block *> destinations;
1092   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
1093   mlir::Block *defaultDestination;
1094   mlir::ValueRange defaultOperands;
1095   llvm::SmallVector<int32_t> caseValues;
1096 
1097   for (unsigned t = 0; t != conds; ++t) {
1098     mlir::Block *dest = select.getSuccessor(t);
1099     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
1100     const mlir::Attribute &attr = cases[t];
1101     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
1102       destinations.push_back(dest);
1103       destinationsOperands.push_back(destOps.hasValue() ? *destOps
1104                                                         : ValueRange());
1105       caseValues.push_back(intAttr.getInt());
1106       continue;
1107     }
1108     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1109     assert((t + 1 == conds) && "unit must be last");
1110     defaultDestination = dest;
1111     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1112   }
1113 
1114   // LLVM::SwitchOp takes a i32 type for the selector.
1115   if (select.getSelector().getType() != rewriter.getI32Type())
1116     selector =
1117         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1118 
1119   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1120       select, selector,
1121       /*defaultDestination=*/defaultDestination,
1122       /*defaultOperands=*/defaultOperands,
1123       /*caseValues=*/caseValues,
1124       /*caseDestinations=*/destinations,
1125       /*caseOperands=*/destinationsOperands,
1126       /*branchWeights=*/ArrayRef<int32_t>());
1127 }
1128 
1129 /// conversion of fir::SelectOp to an if-then-else ladder
1130 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1131   using FIROpConversion::FIROpConversion;
1132 
1133   mlir::LogicalResult
1134   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1135                   mlir::ConversionPatternRewriter &rewriter) const override {
1136     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1137     return success();
1138   }
1139 };
1140 
1141 /// `fir.load` --> `llvm.load`
1142 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1143   using FIROpConversion::FIROpConversion;
1144 
1145   mlir::LogicalResult
1146   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1147                   mlir::ConversionPatternRewriter &rewriter) const override {
1148     // fir.box is a special case because it is considered as an ssa values in
1149     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1150     // and fir.box end up being the same llvm types and loading a
1151     // fir.ref<fir.box> is actually a no op in LLVM.
1152     if (load.getType().isa<fir::BoxType>()) {
1153       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1154     } else {
1155       mlir::Type ty = convertType(load.getType());
1156       ArrayRef<NamedAttribute> at = load->getAttrs();
1157       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1158           load, ty, adaptor.getOperands(), at);
1159     }
1160     return success();
1161   }
1162 };
1163 
1164 /// Lower `fir.no_reassoc` to LLVM IR dialect.
1165 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
1166 /// math flags?
1167 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
1168   using FIROpConversion::FIROpConversion;
1169 
1170   mlir::LogicalResult
1171   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
1172                   mlir::ConversionPatternRewriter &rewriter) const override {
1173     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
1174     return success();
1175   }
1176 };
1177 
1178 /// Lower `fir.select_type` to LLVM IR dialect.
1179 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1180   using FIROpConversion::FIROpConversion;
1181 
1182   mlir::LogicalResult
1183   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1184                   mlir::ConversionPatternRewriter &rewriter) const override {
1185     mlir::emitError(select.getLoc(),
1186                     "fir.select_type should have already been converted");
1187     return failure();
1188   }
1189 };
1190 
1191 /// conversion of fir::SelectRankOp to an if-then-else ladder
1192 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1193   using FIROpConversion::FIROpConversion;
1194 
1195   mlir::LogicalResult
1196   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1197                   mlir::ConversionPatternRewriter &rewriter) const override {
1198     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1199     return success();
1200   }
1201 };
1202 
1203 /// `fir.store` --> `llvm.store`
1204 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1205   using FIROpConversion::FIROpConversion;
1206 
1207   mlir::LogicalResult
1208   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1209                   mlir::ConversionPatternRewriter &rewriter) const override {
1210     if (store.value().getType().isa<fir::BoxType>()) {
1211       // fir.box value is actually in memory, load it first before storing it.
1212       mlir::Location loc = store.getLoc();
1213       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1214       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1215           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1216           adaptor.getOperands()[0]);
1217       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1218           store, val, adaptor.getOperands()[1]);
1219     } else {
1220       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1221           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1222     }
1223     return success();
1224   }
1225 };
1226 
1227 /// convert to LLVM IR dialect `undef`
1228 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1229   using FIROpConversion::FIROpConversion;
1230 
1231   mlir::LogicalResult
1232   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1233                   mlir::ConversionPatternRewriter &rewriter) const override {
1234     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1235         undef, convertType(undef.getType()));
1236     return success();
1237   }
1238 };
1239 
1240 /// `fir.unreachable` --> `llvm.unreachable`
1241 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1242   using FIROpConversion::FIROpConversion;
1243 
1244   mlir::LogicalResult
1245   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1246                   mlir::ConversionPatternRewriter &rewriter) const override {
1247     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1248     return success();
1249   }
1250 };
1251 
1252 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1253   using FIROpConversion::FIROpConversion;
1254 
1255   mlir::LogicalResult
1256   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1257                   mlir::ConversionPatternRewriter &rewriter) const override {
1258     mlir::Type ty = convertType(zero.getType());
1259     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1260       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1261     } else if (ty.isa<mlir::IntegerType>()) {
1262       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1263           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1264     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1265       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1266           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1267     } else {
1268       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1269       return rewriter.notifyMatchFailure(
1270           zero,
1271           "conversion of fir.zero with aggregate type not implemented yet");
1272     }
1273     return success();
1274   }
1275 };
1276 
1277 /// Common base class for embox to descriptor conversion.
1278 template <typename OP>
1279 struct EmboxCommonConversion : public FIROpConversion<OP> {
1280   using FIROpConversion<OP>::FIROpConversion;
1281 
1282   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1283   // The order to find the LLVMFuncOp is as follows:
1284   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1285   // 2. The first ancestor that is a LLVMFuncOp.
1286   mlir::LLVM::LLVMFuncOp
1287   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1288     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1289     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1290                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1291                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1292   }
1293 
1294   // Generate an alloca of size 1 and type \p toTy.
1295   mlir::LLVM::AllocaOp
1296   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1297                     mlir::ConversionPatternRewriter &rewriter) const {
1298     auto thisPt = rewriter.saveInsertionPoint();
1299     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1300     rewriter.setInsertionPointToStart(&func.front());
1301     auto size = this->genI32Constant(loc, rewriter, 1);
1302     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1303     rewriter.restoreInsertionPoint(thisPt);
1304     return al;
1305   }
1306 
1307   static int getCFIAttr(fir::BoxType boxTy) {
1308     auto eleTy = boxTy.getEleTy();
1309     if (eleTy.isa<fir::PointerType>())
1310       return CFI_attribute_pointer;
1311     if (eleTy.isa<fir::HeapType>())
1312       return CFI_attribute_allocatable;
1313     return CFI_attribute_other;
1314   }
1315 
1316   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1317     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1318         .template dyn_cast<fir::RecordType>();
1319   }
1320   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1321     auto recTy = unwrapIfDerived(boxTy);
1322     return recTy && recTy.getNumLenParams() > 0;
1323   }
1324   static bool isDerivedType(fir::BoxType boxTy) {
1325     return unwrapIfDerived(boxTy) != nullptr;
1326   }
1327 
1328   // Get the element size and CFI type code of the boxed value.
1329   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1330       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1331       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1332     auto doInteger =
1333         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1334       int typeCode = fir::integerBitsToTypeCode(width);
1335       return {this->genConstantOffset(loc, rewriter, width / 8),
1336               this->genConstantOffset(loc, rewriter, typeCode)};
1337     };
1338     auto doLogical =
1339         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1340       int typeCode = fir::logicalBitsToTypeCode(width);
1341       return {this->genConstantOffset(loc, rewriter, width / 8),
1342               this->genConstantOffset(loc, rewriter, typeCode)};
1343     };
1344     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1345       int typeCode = fir::realBitsToTypeCode(width);
1346       return {this->genConstantOffset(loc, rewriter, width / 8),
1347               this->genConstantOffset(loc, rewriter, typeCode)};
1348     };
1349     auto doComplex =
1350         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1351       auto typeCode = fir::complexBitsToTypeCode(width);
1352       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1353               this->genConstantOffset(loc, rewriter, typeCode)};
1354     };
1355     auto doCharacter =
1356         [&](unsigned width,
1357             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1358       auto typeCode = fir::characterBitsToTypeCode(width);
1359       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1360       if (width == 8)
1361         return {len, typeCodeVal};
1362       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1363       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1364       auto size =
1365           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1366       return {size, typeCodeVal};
1367     };
1368     auto getKindMap = [&]() -> fir::KindMapping & {
1369       return this->lowerTy().getKindMap();
1370     };
1371     // Pointer-like types.
1372     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1373       boxEleTy = eleTy;
1374     // Integer types.
1375     if (fir::isa_integer(boxEleTy)) {
1376       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1377         return doInteger(ty.getWidth());
1378       auto ty = boxEleTy.cast<fir::IntegerType>();
1379       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1380     }
1381     // Floating point types.
1382     if (fir::isa_real(boxEleTy)) {
1383       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1384         return doFloat(ty.getWidth());
1385       auto ty = boxEleTy.cast<fir::RealType>();
1386       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1387     }
1388     // Complex types.
1389     if (fir::isa_complex(boxEleTy)) {
1390       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1391         return doComplex(
1392             ty.getElementType().cast<mlir::FloatType>().getWidth());
1393       auto ty = boxEleTy.cast<fir::ComplexType>();
1394       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1395     }
1396     // Character types.
1397     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1398       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1399       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1400         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1401         return doCharacter(charWidth, len);
1402       }
1403       assert(!lenParams.empty());
1404       return doCharacter(charWidth, lenParams.back());
1405     }
1406     // Logical type.
1407     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1408       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1409     // Array types.
1410     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1411       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1412     // Derived-type types.
1413     if (boxEleTy.isa<fir::RecordType>()) {
1414       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1415           this->lowerTy().convertType(boxEleTy));
1416       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1417       auto one =
1418           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1419       auto gep = rewriter.create<mlir::LLVM::GEPOp>(
1420           loc, ptrTy, mlir::ValueRange{nullPtr, one});
1421       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1422           loc, this->lowerTy().indexType(), gep);
1423       return {eleSize,
1424               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1425     }
1426     // Reference type.
1427     if (fir::isa_ref_type(boxEleTy)) {
1428       // FIXME: use the target pointer size rather than sizeof(void*)
1429       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1430               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1431     }
1432     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1433   }
1434 
1435   /// Basic pattern to write a field in the descriptor
1436   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1437                           mlir::Location loc, mlir::Value dest,
1438                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1439                           bool bitcast = false) const {
1440     auto boxTy = dest.getType();
1441     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1442     if (bitcast)
1443       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1444     else
1445       value = this->integerCast(loc, rewriter, fldTy, value);
1446     SmallVector<mlir::Attribute, 2> attrs;
1447     for (auto i : fldIndexes)
1448       attrs.push_back(rewriter.getI32IntegerAttr(i));
1449     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1450     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1451                                                       indexesAttr);
1452   }
1453 
1454   inline mlir::Value
1455   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1456                     mlir::Location loc, mlir::Value dest,
1457                     mlir::Value base) const {
1458     return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
1459                        /*bitCast=*/true);
1460   }
1461 
1462   inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
1463                                       mlir::Location loc, mlir::Value dest,
1464                                       unsigned dim, mlir::Value lb) const {
1465     return insertField(rewriter, loc, dest,
1466                        {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
1467   }
1468 
1469   inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
1470                                   mlir::Location loc, mlir::Value dest,
1471                                   unsigned dim, mlir::Value extent) const {
1472     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
1473                        extent);
1474   }
1475 
1476   inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
1477                                   mlir::Location loc, mlir::Value dest,
1478                                   unsigned dim, mlir::Value stride) const {
1479     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
1480                        stride);
1481   }
1482 
1483   /// Get the address of the type descriptor global variable that was created by
1484   /// lowering for derived type \p recType.
1485   template <typename BOX>
1486   mlir::Value
1487   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1488                     mlir::Location loc, fir::RecordType recType) const {
1489     std::string name = recType.getLoweredName();
1490     auto module = box->template getParentOfType<mlir::ModuleOp>();
1491     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1492       auto ty = mlir::LLVM::LLVMPointerType::get(
1493           this->lowerTy().convertType(global.getType()));
1494       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1495                                                       global.sym_name());
1496     }
1497     if (auto global =
1498             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1499       // The global may have already been translated to LLVM.
1500       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1501       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1502                                                       global.sym_name());
1503     }
1504     // The global does not exist in the current translation unit, but may be
1505     // defined elsewhere (e.g., type defined in a module).
1506     // For now, create a extern_weak symbol (will become nullptr if unresolved)
1507     // to support generating code without the front-end generated symbols.
1508     // These could be made available_externally to require the symbols to be
1509     // defined elsewhere and to cause link-time failure otherwise.
1510     auto i8Ty = rewriter.getIntegerType(8);
1511     mlir::OpBuilder modBuilder(module.getBodyRegion());
1512     // TODO: The symbol should be lowered to constant in lowering, they are read
1513     // only.
1514     modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
1515                                             mlir::LLVM::Linkage::ExternWeak,
1516                                             name, mlir::Attribute{});
1517     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1518     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1519   }
1520 
1521   template <typename BOX>
1522   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1523   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1524                        unsigned rank, mlir::ValueRange lenParams) const {
1525     auto loc = box.getLoc();
1526     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1527     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1528     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1529     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1530     mlir::Value descriptor =
1531         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1532 
1533     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1534     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1535       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1536         typeparams.push_back(box.substr()[1]);
1537     }
1538 
1539     // Write each of the fields with the appropriate values
1540     auto [eleSize, cfiTy] =
1541         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1542     descriptor =
1543         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1544     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1545                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1546     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1547                              this->genI32Constant(loc, rewriter, rank));
1548     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1549     descriptor =
1550         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1551                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1552     const bool hasAddendum = isDerivedType(boxTy);
1553     descriptor =
1554         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1555                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1556 
1557     if (hasAddendum) {
1558       auto isArray =
1559           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1560       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1561       auto typeDesc =
1562           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1563       descriptor =
1564           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1565                       /*bitCast=*/true);
1566     }
1567 
1568     return {boxTy, descriptor, eleSize};
1569   }
1570 
1571   /// Compute the base address of a substring given the base address of a scalar
1572   /// string and the zero based string lower bound.
1573   mlir::Value shiftSubstringBase(mlir::ConversionPatternRewriter &rewriter,
1574                                  mlir::Location loc, mlir::Value base,
1575                                  mlir::Value lowerBound) const {
1576     llvm::SmallVector<mlir::Value> gepOperands;
1577     auto baseType =
1578         base.getType().cast<mlir::LLVM::LLVMPointerType>().getElementType();
1579     if (baseType.isa<mlir::LLVM::LLVMArrayType>()) {
1580       auto idxTy = this->lowerTy().indexType();
1581       mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1582       gepOperands.push_back(zero);
1583     }
1584     gepOperands.push_back(lowerBound);
1585     return this->genGEP(loc, base.getType(), rewriter, base, gepOperands);
1586   }
1587 
1588   /// If the embox is not in a globalOp body, allocate storage for the box;
1589   /// store the value inside and return the generated alloca. Return the input
1590   /// value otherwise.
1591   mlir::Value
1592   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1593                                mlir::Location loc, mlir::Value boxValue) const {
1594     auto *thisBlock = rewriter.getInsertionBlock();
1595     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1596       return boxValue;
1597     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1598     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1599     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1600     return alloca;
1601   }
1602 };
1603 
1604 /// Compute the extent of a triplet slice (lb:ub:step).
1605 static mlir::Value
1606 computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
1607                      mlir::Location loc, mlir::Value lb, mlir::Value ub,
1608                      mlir::Value step, mlir::Value zero, mlir::Type type) {
1609   mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
1610   extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
1611   extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
1612   // If the resulting extent is negative (`ub-lb` and `step` have different
1613   // signs), zero must be returned instead.
1614   auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1615       loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
1616   return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
1617 }
1618 
1619 /// Helper function for generating the LLVM IR that computes the size
1620 /// in bytes for a derived type.
1621 static mlir::Value
1622 computeDerivedTypeSize(mlir::Location loc, mlir::Type ptrTy, mlir::Type idxTy,
1623                        mlir::ConversionPatternRewriter &rewriter) {
1624   auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1625   mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
1626   llvm::SmallVector<mlir::Value> args{nullPtr, one};
1627   auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, args);
1628   return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
1629 }
1630 
1631 /// Create a generic box on a memory reference. This conversions lowers the
1632 /// abstract box to the appropriate, initialized descriptor.
1633 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1634   using EmboxCommonConversion::EmboxCommonConversion;
1635 
1636   mlir::LogicalResult
1637   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1638                   mlir::ConversionPatternRewriter &rewriter) const override {
1639     assert(!embox.getShape() && "There should be no dims on this embox op");
1640     auto [boxTy, dest, eleSize] =
1641         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1642                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1643     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1644                              adaptor.getOperands()[0]);
1645     if (isDerivedTypeWithLenParams(boxTy)) {
1646       TODO(embox.getLoc(),
1647            "fir.embox codegen of derived with length parameters");
1648       return failure();
1649     }
1650     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1651     rewriter.replaceOp(embox, result);
1652     return success();
1653   }
1654 };
1655 
1656 /// Lower `fir.emboxproc` operation. Creates a procedure box.
1657 /// TODO: Part of supporting Fortran 2003 procedure pointers.
1658 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1659   using FIROpConversion::FIROpConversion;
1660 
1661   mlir::LogicalResult
1662   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1663                   mlir::ConversionPatternRewriter &rewriter) const override {
1664     TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
1665     return failure();
1666   }
1667 };
1668 
1669 /// Create a generic box on a memory reference.
1670 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
1671   using EmboxCommonConversion::EmboxCommonConversion;
1672 
1673   mlir::LogicalResult
1674   matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
1675                   mlir::ConversionPatternRewriter &rewriter) const override {
1676     auto [boxTy, dest, eleSize] = consDescriptorPrefix(
1677         xbox, rewriter, xbox.getOutRank(),
1678         adaptor.getOperands().drop_front(xbox.lenParamOffset()));
1679     // Generate the triples in the dims field of the descriptor
1680     mlir::ValueRange operands = adaptor.getOperands();
1681     auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
1682     mlir::Value base = operands[0];
1683     assert(!xbox.shape().empty() && "must have a shape");
1684     unsigned shapeOffset = xbox.shapeOffset();
1685     bool hasShift = !xbox.shift().empty();
1686     unsigned shiftOffset = xbox.shiftOffset();
1687     bool hasSlice = !xbox.slice().empty();
1688     unsigned sliceOffset = xbox.sliceOffset();
1689     mlir::Location loc = xbox.getLoc();
1690     mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
1691     mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
1692     mlir::Value prevDim = integerCast(loc, rewriter, i64Ty, eleSize);
1693     mlir::Value prevPtrOff = one;
1694     mlir::Type eleTy = boxTy.getEleTy();
1695     const unsigned rank = xbox.getRank();
1696     llvm::SmallVector<mlir::Value> gepArgs;
1697     unsigned constRows = 0;
1698     mlir::Value ptrOffset = zero;
1699     if (auto memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType()))
1700       if (auto seqTy = memEleTy.dyn_cast<fir::SequenceType>()) {
1701         mlir::Type seqEleTy = seqTy.getEleTy();
1702         // Adjust the element scaling factor if the element is a dependent type.
1703         if (fir::hasDynamicSize(seqEleTy)) {
1704           if (fir::isa_char(seqEleTy)) {
1705             assert(xbox.lenParams().size() == 1);
1706             prevPtrOff = integerCast(loc, rewriter, i64Ty,
1707                                      operands[xbox.lenParamOffset()]);
1708           } else if (seqEleTy.isa<fir::RecordType>()) {
1709             TODO(loc, "generate call to calculate size of PDT");
1710           } else {
1711             return rewriter.notifyMatchFailure(xbox, "unexpected dynamic type");
1712           }
1713         } else {
1714           constRows = seqTy.getConstantRows();
1715         }
1716       }
1717 
1718     bool hasSubcomp = !xbox.subcomponent().empty();
1719     mlir::Value stepExpr;
1720     if (hasSubcomp) {
1721       // We have a subcomponent. The step value needs to be the number of
1722       // bytes per element (which is a derived type).
1723       mlir::Type ty0 = base.getType();
1724       [[maybe_unused]] auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
1725       assert(ptrTy && "expected pointer type");
1726       mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType());
1727       assert(memEleTy && "expected fir pointer type");
1728       auto seqTy = memEleTy.dyn_cast<fir::SequenceType>();
1729       assert(seqTy && "expected sequence type");
1730       mlir::Type seqEleTy = seqTy.getEleTy();
1731       auto eleTy = mlir::LLVM::LLVMPointerType::get(convertType(seqEleTy));
1732       stepExpr = computeDerivedTypeSize(loc, eleTy, i64Ty, rewriter);
1733     }
1734 
1735     // Process the array subspace arguments (shape, shift, etc.), if any,
1736     // translating everything to values in the descriptor wherever the entity
1737     // has a dynamic array dimension.
1738     for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
1739       mlir::Value extent = operands[shapeOffset];
1740       mlir::Value outerExtent = extent;
1741       bool skipNext = false;
1742       if (hasSlice) {
1743         mlir::Value off = operands[sliceOffset];
1744         mlir::Value adj = one;
1745         if (hasShift)
1746           adj = operands[shiftOffset];
1747         auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
1748         if (constRows > 0) {
1749           gepArgs.push_back(ao);
1750           --constRows;
1751         } else {
1752           auto dimOff =
1753               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
1754           ptrOffset =
1755               rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
1756         }
1757         if (mlir::isa_and_nonnull<fir::UndefOp>(
1758                 xbox.slice()[3 * di + 1].getDefiningOp())) {
1759           // This dimension contains a scalar expression in the array slice op.
1760           // The dimension is loop invariant, will be dropped, and will not
1761           // appear in the descriptor.
1762           skipNext = true;
1763         }
1764       }
1765       if (!skipNext) {
1766         // store lower bound (normally 0)
1767         mlir::Value lb = zero;
1768         if (eleTy.isa<fir::PointerType>() || eleTy.isa<fir::HeapType>()) {
1769           lb = one;
1770           if (hasShift)
1771             lb = operands[shiftOffset];
1772         }
1773         dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
1774 
1775         // store extent
1776         if (hasSlice)
1777           extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
1778                                         operands[sliceOffset + 1],
1779                                         operands[sliceOffset + 2], zero, i64Ty);
1780         dest = insertExtent(rewriter, loc, dest, descIdx, extent);
1781 
1782         // store step (scaled by shaped extent)
1783 
1784         mlir::Value step = hasSubcomp ? stepExpr : prevDim;
1785         if (hasSlice)
1786           step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step,
1787                                                     operands[sliceOffset + 2]);
1788         dest = insertStride(rewriter, loc, dest, descIdx, step);
1789         ++descIdx;
1790       }
1791 
1792       // compute the stride and offset for the next natural dimension
1793       prevDim =
1794           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevDim, outerExtent);
1795       if (constRows == 0)
1796         prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
1797                                                         outerExtent);
1798 
1799       // increment iterators
1800       ++shapeOffset;
1801       if (hasShift)
1802         ++shiftOffset;
1803       if (hasSlice)
1804         sliceOffset += 3;
1805     }
1806     if (hasSlice || hasSubcomp || !xbox.substr().empty()) {
1807       llvm::SmallVector<mlir::Value> args = {base, ptrOffset};
1808       args.append(gepArgs.rbegin(), gepArgs.rend());
1809       if (hasSubcomp) {
1810         // For each field in the path add the offset to base via the args list.
1811         // In the most general case, some offsets must be computed since
1812         // they are not be known until runtime.
1813         if (fir::hasDynamicSize(fir::unwrapSequenceType(
1814                 fir::unwrapPassByRefType(xbox.memref().getType()))))
1815           TODO(loc, "fir.embox codegen dynamic size component in derived type");
1816         args.append(operands.begin() + xbox.subcomponentOffset(),
1817                     operands.begin() + xbox.subcomponentOffset() +
1818                         xbox.subcomponent().size());
1819       }
1820       base = rewriter.create<mlir::LLVM::GEPOp>(loc, base.getType(), args);
1821       if (!xbox.substr().empty())
1822         base = shiftSubstringBase(rewriter, loc, base,
1823                                   operands[xbox.substrOffset()]);
1824     }
1825     dest = insertBaseAddress(rewriter, loc, dest, base);
1826     if (isDerivedTypeWithLenParams(boxTy))
1827       TODO(loc, "fir.embox codegen of derived with length parameters");
1828 
1829     mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest);
1830     rewriter.replaceOp(xbox, result);
1831     return success();
1832   }
1833 };
1834 
1835 // Code shared between insert_value and extract_value Ops.
1836 struct ValueOpCommon {
1837   // Translate the arguments pertaining to any multidimensional array to
1838   // row-major order for LLVM-IR.
1839   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1840                          mlir::Type ty) {
1841     assert(ty && "type is null");
1842     const auto end = attrs.size();
1843     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1844       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1845         const auto dim = getDimension(seq);
1846         if (dim > 1) {
1847           auto ub = std::min(i + dim, end);
1848           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1849           i += dim - 1;
1850         }
1851         ty = getArrayElementType(seq);
1852       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1853         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1854       } else {
1855         llvm_unreachable("index into invalid type");
1856       }
1857     }
1858   }
1859 
1860   static llvm::SmallVector<mlir::Attribute>
1861   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1862                  mlir::ArrayAttr arrAttr) {
1863     llvm::SmallVector<mlir::Attribute> attrs;
1864     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1865       if (i->isa<mlir::IntegerAttr>()) {
1866         attrs.push_back(*i);
1867       } else {
1868         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1869         ++i;
1870         auto ty = i->cast<mlir::TypeAttr>().getValue();
1871         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1872         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1873       }
1874     }
1875     return attrs;
1876   }
1877 
1878 private:
1879   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1880     unsigned result = 1;
1881     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1882          eleTy;
1883          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1884       ++result;
1885     return result;
1886   }
1887 
1888   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1889     auto eleTy = ty.getElementType();
1890     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1891       eleTy = arrTy.getElementType();
1892     return eleTy;
1893   }
1894 };
1895 
1896 /// Extract a subobject value from an ssa-value of aggregate type
1897 struct ExtractValueOpConversion
1898     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1899       public ValueOpCommon {
1900   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1901 
1902   mlir::LogicalResult
1903   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1904             mlir::ConversionPatternRewriter &rewriter) const override {
1905     auto attrs = collectIndices(rewriter, extractVal.coor());
1906     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1907     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1908     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1909         extractVal, ty, adaptor.getOperands()[0], position);
1910     return success();
1911   }
1912 };
1913 
1914 /// InsertValue is the generalized instruction for the composition of new
1915 /// aggregate type values.
1916 struct InsertValueOpConversion
1917     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1918       public ValueOpCommon {
1919   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1920 
1921   mlir::LogicalResult
1922   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1923             mlir::ConversionPatternRewriter &rewriter) const override {
1924     auto attrs = collectIndices(rewriter, insertVal.coor());
1925     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1926     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1927     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1928         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1929         position);
1930     return success();
1931   }
1932 };
1933 
1934 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1935 struct InsertOnRangeOpConversion
1936     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1937   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1938 
1939   // Increments an array of subscripts in a row major fasion.
1940   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1941                            SmallVector<uint64_t> &subscripts) const {
1942     for (size_t i = dims.size(); i > 0; --i) {
1943       if (++subscripts[i - 1] < dims[i - 1]) {
1944         return;
1945       }
1946       subscripts[i - 1] = 0;
1947     }
1948   }
1949 
1950   mlir::LogicalResult
1951   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1952             mlir::ConversionPatternRewriter &rewriter) const override {
1953 
1954     llvm::SmallVector<uint64_t> dims;
1955     auto type = adaptor.getOperands()[0].getType();
1956 
1957     // Iteratively extract the array dimensions from the type.
1958     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1959       dims.push_back(t.getNumElements());
1960       type = t.getElementType();
1961     }
1962 
1963     SmallVector<uint64_t> lBounds;
1964     SmallVector<uint64_t> uBounds;
1965 
1966     // Unzip the upper and lower bound and convert to a row major format.
1967     mlir::DenseIntElementsAttr coor = range.coor();
1968     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
1969     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
1970       uBounds.push_back(*i++);
1971       lBounds.push_back(*i);
1972     }
1973 
1974     auto &subscripts = lBounds;
1975     auto loc = range.getLoc();
1976     mlir::Value lastOp = adaptor.getOperands()[0];
1977     mlir::Value insertVal = adaptor.getOperands()[1];
1978 
1979     auto i64Ty = rewriter.getI64Type();
1980     while (subscripts != uBounds) {
1981       // Convert uint64_t's to Attribute's.
1982       SmallVector<mlir::Attribute> subscriptAttrs;
1983       for (const auto &subscript : subscripts)
1984         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1985       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1986           loc, ty, lastOp, insertVal,
1987           ArrayAttr::get(range.getContext(), subscriptAttrs));
1988 
1989       incrementSubscripts(dims, subscripts);
1990     }
1991 
1992     // Convert uint64_t's to Attribute's.
1993     SmallVector<mlir::Attribute> subscriptAttrs;
1994     for (const auto &subscript : subscripts)
1995       subscriptAttrs.push_back(
1996           IntegerAttr::get(rewriter.getI64Type(), subscript));
1997     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1998 
1999     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2000         range, ty, lastOp, insertVal,
2001         ArrayAttr::get(range.getContext(), arrayRef));
2002 
2003     return success();
2004   }
2005 };
2006 
2007 //
2008 // Primitive operations on Complex types
2009 //
2010 
2011 /// Generate inline code for complex addition/subtraction
2012 template <typename LLVMOP, typename OPTY>
2013 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
2014                                      mlir::ConversionPatternRewriter &rewriter,
2015                                      fir::LLVMTypeConverter &lowering) {
2016   mlir::Value a = opnds[0];
2017   mlir::Value b = opnds[1];
2018   auto loc = sumop.getLoc();
2019   auto ctx = sumop.getContext();
2020   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2021   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2022   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
2023   mlir::Type ty = lowering.convertType(sumop.getType());
2024   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2025   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2026   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2027   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2028   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
2029   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
2030   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2031   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
2032   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
2033 }
2034 
2035 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
2036   using FIROpConversion::FIROpConversion;
2037 
2038   mlir::LogicalResult
2039   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
2040                   mlir::ConversionPatternRewriter &rewriter) const override {
2041     // given: (x + iy) + (x' + iy')
2042     // result: (x + x') + i(y + y')
2043     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
2044                                             rewriter, lowerTy());
2045     rewriter.replaceOp(addc, r.getResult());
2046     return success();
2047   }
2048 };
2049 
2050 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
2051   using FIROpConversion::FIROpConversion;
2052 
2053   mlir::LogicalResult
2054   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
2055                   mlir::ConversionPatternRewriter &rewriter) const override {
2056     // given: (x + iy) - (x' + iy')
2057     // result: (x - x') + i(y - y')
2058     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
2059                                             rewriter, lowerTy());
2060     rewriter.replaceOp(subc, r.getResult());
2061     return success();
2062   }
2063 };
2064 
2065 /// Inlined complex multiply
2066 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
2067   using FIROpConversion::FIROpConversion;
2068 
2069   mlir::LogicalResult
2070   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
2071                   mlir::ConversionPatternRewriter &rewriter) const override {
2072     // TODO: Can we use a call to __muldc3 ?
2073     // given: (x + iy) * (x' + iy')
2074     // result: (xx'-yy')+i(xy'+yx')
2075     mlir::Value a = adaptor.getOperands()[0];
2076     mlir::Value b = adaptor.getOperands()[1];
2077     auto loc = mulc.getLoc();
2078     auto *ctx = mulc.getContext();
2079     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2080     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2081     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
2082     mlir::Type ty = convertType(mulc.getType());
2083     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2084     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2085     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2086     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2087     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
2088     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
2089     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
2090     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
2091     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
2092     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
2093     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2094     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
2095     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
2096     rewriter.replaceOp(mulc, r0.getResult());
2097     return success();
2098   }
2099 };
2100 
2101 /// Inlined complex division
2102 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
2103   using FIROpConversion::FIROpConversion;
2104 
2105   mlir::LogicalResult
2106   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
2107                   mlir::ConversionPatternRewriter &rewriter) const override {
2108     // TODO: Can we use a call to __divdc3 instead?
2109     // Just generate inline code for now.
2110     // given: (x + iy) / (x' + iy')
2111     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
2112     mlir::Value a = adaptor.getOperands()[0];
2113     mlir::Value b = adaptor.getOperands()[1];
2114     auto loc = divc.getLoc();
2115     auto *ctx = divc.getContext();
2116     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2117     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2118     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
2119     mlir::Type ty = convertType(divc.getType());
2120     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
2121     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
2122     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
2123     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
2124     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
2125     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
2126     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
2127     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
2128     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
2129     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
2130     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
2131     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
2132     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
2133     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
2134     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
2135     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
2136     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
2137     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
2138     rewriter.replaceOp(divc, r0.getResult());
2139     return success();
2140   }
2141 };
2142 
2143 /// Inlined complex negation
2144 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
2145   using FIROpConversion::FIROpConversion;
2146 
2147   mlir::LogicalResult
2148   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
2149                   mlir::ConversionPatternRewriter &rewriter) const override {
2150     // given: -(x + iy)
2151     // result: -x - iy
2152     auto *ctxt = neg.getContext();
2153     auto eleTy = convertType(getComplexEleTy(neg.getType()));
2154     auto ty = convertType(neg.getType());
2155     auto loc = neg.getLoc();
2156     mlir::Value o0 = adaptor.getOperands()[0];
2157     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
2158     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
2159     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
2160     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
2161     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
2162     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
2163     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
2164     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
2165     return success();
2166   }
2167 };
2168 
2169 /// Conversion pattern for operation that must be dead. The information in these
2170 /// operations is used by other operation. At this point they should not have
2171 /// anymore uses.
2172 /// These operations are normally dead after the pre-codegen pass.
2173 template <typename FromOp>
2174 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
2175   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
2176       : FIROpConversion<FromOp>(lowering) {}
2177   using OpAdaptor = typename FromOp::Adaptor;
2178 
2179   mlir::LogicalResult
2180   matchAndRewrite(FromOp op, OpAdaptor adaptor,
2181                   mlir::ConversionPatternRewriter &rewriter) const final {
2182     if (!op->getUses().empty())
2183       return rewriter.notifyMatchFailure(op, "op must be dead");
2184     rewriter.eraseOp(op);
2185     return success();
2186   }
2187 };
2188 
2189 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
2190   using MustBeDeadConversion::MustBeDeadConversion;
2191 };
2192 
2193 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
2194   using MustBeDeadConversion::MustBeDeadConversion;
2195 };
2196 
2197 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
2198   using MustBeDeadConversion::MustBeDeadConversion;
2199 };
2200 
2201 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
2202   using MustBeDeadConversion::MustBeDeadConversion;
2203 };
2204 
2205 /// `fir.is_present` -->
2206 /// ```
2207 ///  %0 = llvm.mlir.constant(0 : i64)
2208 ///  %1 = llvm.ptrtoint %0
2209 ///  %2 = llvm.icmp "ne" %1, %0 : i64
2210 /// ```
2211 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
2212   using FIROpConversion::FIROpConversion;
2213 
2214   mlir::LogicalResult
2215   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
2216                   mlir::ConversionPatternRewriter &rewriter) const override {
2217     mlir::Type idxTy = lowerTy().indexType();
2218     mlir::Location loc = isPresent.getLoc();
2219     auto ptr = adaptor.getOperands()[0];
2220 
2221     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
2222       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
2223       assert(!structTy.isOpaque() && !structTy.getBody().empty());
2224 
2225       mlir::Type ty = structTy.getBody()[0];
2226       mlir::MLIRContext *ctx = isPresent.getContext();
2227       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2228       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
2229     }
2230     mlir::LLVM::ConstantOp c0 =
2231         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
2232     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
2233     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2234         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
2235 
2236     return success();
2237   }
2238 };
2239 
2240 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
2241 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
2242 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
2243 /// element is the length of the character buffer (`#n`).
2244 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
2245   using FIROpConversion::FIROpConversion;
2246 
2247   mlir::LogicalResult
2248   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
2249                   mlir::ConversionPatternRewriter &rewriter) const override {
2250     mlir::ValueRange operands = adaptor.getOperands();
2251     MLIRContext *ctx = emboxChar.getContext();
2252 
2253     mlir::Value charBuffer = operands[0];
2254     mlir::Value charBufferLen = operands[1];
2255 
2256     mlir::Location loc = emboxChar.getLoc();
2257     mlir::Type llvmStructTy = convertType(emboxChar.getType());
2258     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
2259 
2260     mlir::Type lenTy =
2261         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
2262     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
2263 
2264     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2265     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2266     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2267         loc, llvmStructTy, llvmStruct, charBuffer, c0);
2268     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2269         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
2270 
2271     return success();
2272   }
2273 };
2274 
2275 /// Construct an `llvm.extractvalue` instruction. It will return value at
2276 /// element \p x from  \p tuple.
2277 mlir::LLVM::ExtractValueOp
2278 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
2279                          mlir::ConversionPatternRewriter &rewriter,
2280                          mlir::MLIRContext *ctx, int x) {
2281   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
2282   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
2283   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
2284 }
2285 
2286 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
2287 /// boxchar.
2288 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
2289   using FIROpConversion::FIROpConversion;
2290 
2291   mlir::LogicalResult
2292   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
2293                   mlir::ConversionPatternRewriter &rewriter) const override {
2294     mlir::Value boxChar = adaptor.getOperands()[0];
2295     mlir::Location loc = boxChar.getLoc();
2296     mlir::MLIRContext *ctx = boxChar.getContext();
2297     mlir::Type returnValTy = boxCharLen.getResult().getType();
2298 
2299     constexpr int boxcharLenIdx = 1;
2300     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
2301         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
2302     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
2303     rewriter.replaceOp(boxCharLen, lenAfterCast);
2304 
2305     return success();
2306   }
2307 };
2308 
2309 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2310 /// the character buffer and one for the buffer length.
2311 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2312   using FIROpConversion::FIROpConversion;
2313 
2314   mlir::LogicalResult
2315   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2316                   mlir::ConversionPatternRewriter &rewriter) const override {
2317     MLIRContext *ctx = unboxchar.getContext();
2318 
2319     mlir::Type lenTy = convertType(unboxchar.getType(1));
2320     mlir::Value tuple = adaptor.getOperands()[0];
2321     mlir::Type tupleTy = tuple.getType();
2322 
2323     mlir::Location loc = unboxchar.getLoc();
2324     mlir::Value ptrToBuffer =
2325         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2326 
2327     mlir::LLVM::ExtractValueOp len =
2328         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2329     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2330 
2331     rewriter.replaceOp(unboxchar,
2332                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2333     return success();
2334   }
2335 };
2336 
2337 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2338 /// components.
2339 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2340 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2341   using FIROpConversion::FIROpConversion;
2342 
2343   mlir::LogicalResult
2344   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2345                   mlir::ConversionPatternRewriter &rewriter) const override {
2346     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
2347     return failure();
2348   }
2349 };
2350 
2351 /// Convert `fir.field_index`. The conversion depends on whether the size of
2352 /// the record is static or dynamic.
2353 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2354   using FIROpConversion::FIROpConversion;
2355 
2356   // NB: most field references should be resolved by this point
2357   mlir::LogicalResult
2358   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2359                   mlir::ConversionPatternRewriter &rewriter) const override {
2360     auto recTy = field.on_type().cast<fir::RecordType>();
2361     unsigned index = recTy.getFieldIndex(field.field_id());
2362 
2363     if (!fir::hasDynamicSize(recTy)) {
2364       // Derived type has compile-time constant layout. Return index of the
2365       // component type in the parent type (to be used in GEP).
2366       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2367                                     field.getLoc(), rewriter, index)});
2368       return success();
2369     }
2370 
2371     // Derived type has compile-time constant layout. Call the compiler
2372     // generated function to determine the byte offset of the field at runtime.
2373     // This returns a non-constant.
2374     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2375         field.getContext(), getOffsetMethodName(recTy, field.field_id()));
2376     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2377     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2378         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2379     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2380         field, lowerTy().offsetType(), adaptor.getOperands(),
2381         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2382     return success();
2383   }
2384 
2385   // Re-Construct the name of the compiler generated method that calculates the
2386   // offset
2387   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2388                                                 llvm::StringRef field) {
2389     return recTy.getName().str() + "P." + field.str() + ".offset";
2390   }
2391 };
2392 
2393 } // namespace
2394 
2395 namespace {
2396 /// Convert FIR dialect to LLVM dialect
2397 ///
2398 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
2399 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
2400 ///
2401 /// This pass is not complete yet. We are upstreaming it in small patches.
2402 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
2403 public:
2404   mlir::ModuleOp getModule() { return getOperation(); }
2405 
2406   void runOnOperation() override final {
2407     auto mod = getModule();
2408     if (!forcedTargetTriple.empty()) {
2409       fir::setTargetTriple(mod, forcedTargetTriple);
2410     }
2411 
2412     auto *context = getModule().getContext();
2413     fir::LLVMTypeConverter typeConverter{getModule()};
2414     mlir::OwningRewritePatternList pattern(context);
2415     pattern.insert<
2416         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
2417         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
2418         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
2419         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion,
2420         BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion,
2421         CmpcOpConversion, ConstcOpConversion, ConvertOpConversion,
2422         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
2423         DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
2424         EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
2425         FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
2426         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
2427         InsertValueOpConversion, IsPresentOpConversion,
2428         LenParamIndexOpConversion, LoadOpConversion, NegcOpConversion,
2429         NoReassocOpConversion, MulcOpConversion, SelectCaseOpConversion,
2430         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
2431         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
2432         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
2433         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
2434         UndefOpConversion, UnreachableOpConversion, XEmboxOpConversion,
2435         ZeroOpConversion>(typeConverter);
2436     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
2437     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
2438                                                             pattern);
2439     mlir::ConversionTarget target{*context};
2440     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2441 
2442     // required NOPs for applying a full conversion
2443     target.addLegalOp<mlir::ModuleOp>();
2444 
2445     // apply the patterns
2446     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
2447                                                std::move(pattern)))) {
2448       signalPassFailure();
2449     }
2450   }
2451 };
2452 } // namespace
2453 
2454 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
2455   return std::make_unique<FIRToLLVMLowering>();
2456 }
2457