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