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