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.len_param_index
831 struct LenParamIndexOpConversion
832     : public FIROpConversion<fir::LenParamIndexOp> {
833   using FIROpConversion::FIROpConversion;
834 
835   // FIXME: this should be specialized by the runtime target
836   mlir::LogicalResult
837   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
838                   mlir::ConversionPatternRewriter &rewriter) const override {
839     return rewriter.notifyMatchFailure(
840         lenp, "fir.len_param_index codegen is not implemented yet");
841   }
842 };
843 
844 /// Lower `fir.gentypedesc` to a global constant.
845 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
846   using FIROpConversion::FIROpConversion;
847 
848   mlir::LogicalResult
849   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
850                   mlir::ConversionPatternRewriter &rewriter) const override {
851     return rewriter.notifyMatchFailure(
852         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
853   }
854 };
855 
856 /// Convert `fir.end`
857 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
858   using FIROpConversion::FIROpConversion;
859 
860   mlir::LogicalResult
861   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
862                   mlir::ConversionPatternRewriter &rewriter) const override {
863     return rewriter.notifyMatchFailure(
864         firEnd, "fir.end codegen is not implemented yet");
865   }
866 };
867 
868 /// Lower `fir.has_value` operation to `llvm.return` operation.
869 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
870   using FIROpConversion::FIROpConversion;
871 
872   mlir::LogicalResult
873   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
874                   mlir::ConversionPatternRewriter &rewriter) const override {
875     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
876     return success();
877   }
878 };
879 
880 /// Lower `fir.global` operation to `llvm.global` operation.
881 /// `fir.insert_on_range` operations are replaced with constant dense attribute
882 /// if they are applied on the full range.
883 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
884   using FIROpConversion::FIROpConversion;
885 
886   mlir::LogicalResult
887   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
888                   mlir::ConversionPatternRewriter &rewriter) const override {
889     auto tyAttr = convertType(global.getType());
890     if (global.getType().isa<fir::BoxType>())
891       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
892     auto loc = global.getLoc();
893     mlir::Attribute initAttr{};
894     if (global.initVal())
895       initAttr = global.initVal().getValue();
896     auto linkage = convertLinkage(global.linkName());
897     auto isConst = global.constant().hasValue();
898     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
899         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
900     auto &gr = g.getInitializerRegion();
901     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
902     if (!gr.empty()) {
903       // Replace insert_on_range with a constant dense attribute if the
904       // initialization is on the full range.
905       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
906       for (auto insertOp : insertOnRangeOps) {
907         if (isFullRange(insertOp.coor(), insertOp.getType())) {
908           auto seqTyAttr = convertType(insertOp.getType());
909           auto *op = insertOp.val().getDefiningOp();
910           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
911           if (!constant) {
912             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
913             if (!convertOp)
914               continue;
915             constant = cast<mlir::arith::ConstantOp>(
916                 convertOp.value().getDefiningOp());
917           }
918           mlir::Type vecType = mlir::VectorType::get(
919               insertOp.getType().getShape(), constant.getType());
920           auto denseAttr = mlir::DenseElementsAttr::get(
921               vecType.cast<ShapedType>(), constant.value());
922           rewriter.setInsertionPointAfter(insertOp);
923           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
924               insertOp, seqTyAttr, denseAttr);
925         }
926       }
927     }
928     rewriter.eraseOp(global);
929     return success();
930   }
931 
932   bool isFullRange(mlir::DenseIntElementsAttr indexes,
933                    fir::SequenceType seqTy) const {
934     auto extents = seqTy.getShape();
935     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
936       return false;
937     auto cur_index = indexes.value_begin<int64_t>();
938     for (unsigned i = 0; i < indexes.size(); i += 2) {
939       if (*(cur_index++) != 0)
940         return false;
941       if (*(cur_index++) != extents[i / 2] - 1)
942         return false;
943     }
944     return true;
945   }
946 
947   // TODO: String comparaison should be avoided. Replace linkName with an
948   // enumeration.
949   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
950     if (optLinkage.hasValue()) {
951       auto name = optLinkage.getValue();
952       if (name == "internal")
953         return mlir::LLVM::Linkage::Internal;
954       if (name == "linkonce")
955         return mlir::LLVM::Linkage::Linkonce;
956       if (name == "common")
957         return mlir::LLVM::Linkage::Common;
958       if (name == "weak")
959         return mlir::LLVM::Linkage::Weak;
960     }
961     return mlir::LLVM::Linkage::External;
962   }
963 };
964 
965 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
966                  Optional<mlir::ValueRange> destOps,
967                  mlir::ConversionPatternRewriter &rewriter,
968                  mlir::Block *newBlock) {
969   if (destOps.hasValue())
970     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
971                                           newBlock, mlir::ValueRange());
972   else
973     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
974 }
975 
976 template <typename A, typename B>
977 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
978              mlir::ConversionPatternRewriter &rewriter) {
979   if (destOps.hasValue())
980     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
981                                                   dest);
982   else
983     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
984 }
985 
986 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
987                        Optional<mlir::ValueRange> destOps,
988                        mlir::ConversionPatternRewriter &rewriter) {
989   auto *thisBlock = rewriter.getInsertionBlock();
990   auto *newBlock = createBlock(rewriter, dest);
991   rewriter.setInsertionPointToEnd(thisBlock);
992   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
993   rewriter.setInsertionPointToEnd(newBlock);
994 }
995 
996 /// Conversion of `fir.select_case`
997 ///
998 /// The `fir.select_case` operation is converted to a if-then-else ladder.
999 /// Depending on the case condition type, one or several comparison and
1000 /// conditional branching can be generated.
1001 ///
1002 /// A a point value case such as `case(4)`, a lower bound case such as
1003 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
1004 /// simple comparison between the selector value and the constant value in the
1005 /// case. The block associated with the case condition is then executed if
1006 /// the comparison succeed otherwise it branch to the next block with the
1007 /// comparison for the the next case conditon.
1008 ///
1009 /// A closed interval case condition such as `case(7:10)` is converted with a
1010 /// first comparison and conditional branching for the lower bound. If
1011 /// successful, it branch to a second block with the comparison for the
1012 /// upper bound in the same case condition.
1013 ///
1014 /// TODO: lowering of CHARACTER type cases is not handled yet.
1015 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
1016   using FIROpConversion::FIROpConversion;
1017 
1018   mlir::LogicalResult
1019   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
1020                   mlir::ConversionPatternRewriter &rewriter) const override {
1021     unsigned conds = caseOp.getNumConditions();
1022     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
1023     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
1024     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
1025     if (ty.isa<fir::CharacterType>())
1026       return rewriter.notifyMatchFailure(caseOp,
1027                                          "conversion of fir.select_case with "
1028                                          "character type not implemented yet");
1029     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
1030     auto loc = caseOp.getLoc();
1031     for (unsigned t = 0; t != conds; ++t) {
1032       mlir::Block *dest = caseOp.getSuccessor(t);
1033       llvm::Optional<mlir::ValueRange> destOps =
1034           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
1035       llvm::Optional<mlir::ValueRange> cmpOps =
1036           *caseOp.getCompareOperands(adaptor.getOperands(), t);
1037       mlir::Value caseArg = *(cmpOps.getValue().begin());
1038       mlir::Attribute attr = cases[t];
1039       if (attr.isa<fir::PointIntervalAttr>()) {
1040         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1041             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
1042         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1043         continue;
1044       }
1045       if (attr.isa<fir::LowerBoundAttr>()) {
1046         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1047             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1048         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1049         continue;
1050       }
1051       if (attr.isa<fir::UpperBoundAttr>()) {
1052         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1053             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
1054         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1055         continue;
1056       }
1057       if (attr.isa<fir::ClosedIntervalAttr>()) {
1058         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1059             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1060         auto *thisBlock = rewriter.getInsertionBlock();
1061         auto *newBlock1 = createBlock(rewriter, dest);
1062         auto *newBlock2 = createBlock(rewriter, dest);
1063         rewriter.setInsertionPointToEnd(thisBlock);
1064         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
1065         rewriter.setInsertionPointToEnd(newBlock1);
1066         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
1067         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
1068             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
1069         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
1070         rewriter.setInsertionPointToEnd(newBlock2);
1071         continue;
1072       }
1073       assert(attr.isa<mlir::UnitAttr>());
1074       assert((t + 1 == conds) && "unit must be last");
1075       genBrOp(caseOp, dest, destOps, rewriter);
1076     }
1077     return success();
1078   }
1079 };
1080 
1081 template <typename OP>
1082 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
1083                            typename OP::Adaptor adaptor,
1084                            mlir::ConversionPatternRewriter &rewriter) {
1085   unsigned conds = select.getNumConditions();
1086   auto cases = select.getCases().getValue();
1087   mlir::Value selector = adaptor.selector();
1088   auto loc = select.getLoc();
1089   assert(conds > 0 && "select must have cases");
1090 
1091   llvm::SmallVector<mlir::Block *> destinations;
1092   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
1093   mlir::Block *defaultDestination;
1094   mlir::ValueRange defaultOperands;
1095   llvm::SmallVector<int32_t> caseValues;
1096 
1097   for (unsigned t = 0; t != conds; ++t) {
1098     mlir::Block *dest = select.getSuccessor(t);
1099     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
1100     const mlir::Attribute &attr = cases[t];
1101     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
1102       destinations.push_back(dest);
1103       destinationsOperands.push_back(destOps.hasValue() ? *destOps
1104                                                         : ValueRange());
1105       caseValues.push_back(intAttr.getInt());
1106       continue;
1107     }
1108     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1109     assert((t + 1 == conds) && "unit must be last");
1110     defaultDestination = dest;
1111     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1112   }
1113 
1114   // LLVM::SwitchOp takes a i32 type for the selector.
1115   if (select.getSelector().getType() != rewriter.getI32Type())
1116     selector =
1117         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1118 
1119   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1120       select, selector,
1121       /*defaultDestination=*/defaultDestination,
1122       /*defaultOperands=*/defaultOperands,
1123       /*caseValues=*/caseValues,
1124       /*caseDestinations=*/destinations,
1125       /*caseOperands=*/destinationsOperands,
1126       /*branchWeights=*/ArrayRef<int32_t>());
1127 }
1128 
1129 /// conversion of fir::SelectOp to an if-then-else ladder
1130 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1131   using FIROpConversion::FIROpConversion;
1132 
1133   mlir::LogicalResult
1134   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1135                   mlir::ConversionPatternRewriter &rewriter) const override {
1136     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1137     return success();
1138   }
1139 };
1140 
1141 /// `fir.load` --> `llvm.load`
1142 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1143   using FIROpConversion::FIROpConversion;
1144 
1145   mlir::LogicalResult
1146   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1147                   mlir::ConversionPatternRewriter &rewriter) const override {
1148     // fir.box is a special case because it is considered as an ssa values in
1149     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1150     // and fir.box end up being the same llvm types and loading a
1151     // fir.ref<fir.box> is actually a no op in LLVM.
1152     if (load.getType().isa<fir::BoxType>()) {
1153       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1154     } else {
1155       mlir::Type ty = convertType(load.getType());
1156       ArrayRef<NamedAttribute> at = load->getAttrs();
1157       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1158           load, ty, adaptor.getOperands(), at);
1159     }
1160     return success();
1161   }
1162 };
1163 
1164 /// Lower `fir.no_reassoc` to LLVM IR dialect.
1165 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
1166 /// math flags?
1167 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
1168   using FIROpConversion::FIROpConversion;
1169 
1170   mlir::LogicalResult
1171   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
1172                   mlir::ConversionPatternRewriter &rewriter) const override {
1173     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
1174     return success();
1175   }
1176 };
1177 
1178 /// Lower `fir.select_type` to LLVM IR dialect.
1179 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1180   using FIROpConversion::FIROpConversion;
1181 
1182   mlir::LogicalResult
1183   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1184                   mlir::ConversionPatternRewriter &rewriter) const override {
1185     return rewriter.notifyMatchFailure(
1186         select, "fir.select_type codegen is not implemented yet");
1187   }
1188 };
1189 
1190 /// conversion of fir::SelectRankOp to an if-then-else ladder
1191 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1192   using FIROpConversion::FIROpConversion;
1193 
1194   mlir::LogicalResult
1195   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1196                   mlir::ConversionPatternRewriter &rewriter) const override {
1197     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1198     return success();
1199   }
1200 };
1201 
1202 /// `fir.store` --> `llvm.store`
1203 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1204   using FIROpConversion::FIROpConversion;
1205 
1206   mlir::LogicalResult
1207   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1208                   mlir::ConversionPatternRewriter &rewriter) const override {
1209     if (store.value().getType().isa<fir::BoxType>()) {
1210       // fir.box value is actually in memory, load it first before storing it.
1211       mlir::Location loc = store.getLoc();
1212       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1213       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1214           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1215           adaptor.getOperands()[0]);
1216       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1217           store, val, adaptor.getOperands()[1]);
1218     } else {
1219       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1220           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1221     }
1222     return success();
1223   }
1224 };
1225 
1226 /// convert to LLVM IR dialect `undef`
1227 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1228   using FIROpConversion::FIROpConversion;
1229 
1230   mlir::LogicalResult
1231   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1232                   mlir::ConversionPatternRewriter &rewriter) const override {
1233     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1234         undef, convertType(undef.getType()));
1235     return success();
1236   }
1237 };
1238 
1239 /// `fir.unreachable` --> `llvm.unreachable`
1240 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1241   using FIROpConversion::FIROpConversion;
1242 
1243   mlir::LogicalResult
1244   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1245                   mlir::ConversionPatternRewriter &rewriter) const override {
1246     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1247     return success();
1248   }
1249 };
1250 
1251 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1252   using FIROpConversion::FIROpConversion;
1253 
1254   mlir::LogicalResult
1255   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1256                   mlir::ConversionPatternRewriter &rewriter) const override {
1257     auto ty = convertType(zero.getType());
1258     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1259       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1260     } else if (ty.isa<mlir::IntegerType>()) {
1261       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1262           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1263     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1264       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1265           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1266     } else {
1267       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1268       return rewriter.notifyMatchFailure(
1269           zero,
1270           "conversion of fir.zero with aggregate type not implemented yet");
1271     }
1272     return success();
1273   }
1274 };
1275 
1276 /// Common base class for embox to descriptor conversion.
1277 template <typename OP>
1278 struct EmboxCommonConversion : public FIROpConversion<OP> {
1279   using FIROpConversion<OP>::FIROpConversion;
1280 
1281   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1282   // The order to find the LLVMFuncOp is as follows:
1283   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1284   // 2. The first ancestor that is a LLVMFuncOp.
1285   mlir::LLVM::LLVMFuncOp
1286   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1287     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1288     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1289                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1290                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1291   }
1292 
1293   // Generate an alloca of size 1 and type \p toTy.
1294   mlir::LLVM::AllocaOp
1295   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1296                     mlir::ConversionPatternRewriter &rewriter) const {
1297     auto thisPt = rewriter.saveInsertionPoint();
1298     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1299     rewriter.setInsertionPointToStart(&func.front());
1300     auto size = this->genI32Constant(loc, rewriter, 1);
1301     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1302     rewriter.restoreInsertionPoint(thisPt);
1303     return al;
1304   }
1305 
1306   static int getCFIAttr(fir::BoxType boxTy) {
1307     auto eleTy = boxTy.getEleTy();
1308     if (eleTy.isa<fir::PointerType>())
1309       return CFI_attribute_pointer;
1310     if (eleTy.isa<fir::HeapType>())
1311       return CFI_attribute_allocatable;
1312     return CFI_attribute_other;
1313   }
1314 
1315   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1316     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1317         .template dyn_cast<fir::RecordType>();
1318   }
1319   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1320     auto recTy = unwrapIfDerived(boxTy);
1321     return recTy && recTy.getNumLenParams() > 0;
1322   }
1323   static bool isDerivedType(fir::BoxType boxTy) {
1324     return unwrapIfDerived(boxTy) != nullptr;
1325   }
1326 
1327   // Get the element size and CFI type code of the boxed value.
1328   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1329       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1330       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1331     auto doInteger =
1332         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1333       int typeCode = fir::integerBitsToTypeCode(width);
1334       return {this->genConstantOffset(loc, rewriter, width / 8),
1335               this->genConstantOffset(loc, rewriter, typeCode)};
1336     };
1337     auto doLogical =
1338         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1339       int typeCode = fir::logicalBitsToTypeCode(width);
1340       return {this->genConstantOffset(loc, rewriter, width / 8),
1341               this->genConstantOffset(loc, rewriter, typeCode)};
1342     };
1343     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1344       int typeCode = fir::realBitsToTypeCode(width);
1345       return {this->genConstantOffset(loc, rewriter, width / 8),
1346               this->genConstantOffset(loc, rewriter, typeCode)};
1347     };
1348     auto doComplex =
1349         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1350       auto typeCode = fir::complexBitsToTypeCode(width);
1351       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1352               this->genConstantOffset(loc, rewriter, typeCode)};
1353     };
1354     auto doCharacter =
1355         [&](unsigned width,
1356             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1357       auto typeCode = fir::characterBitsToTypeCode(width);
1358       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1359       if (width == 8)
1360         return {len, typeCodeVal};
1361       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1362       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1363       auto size =
1364           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1365       return {size, typeCodeVal};
1366     };
1367     auto getKindMap = [&]() -> fir::KindMapping & {
1368       return this->lowerTy().getKindMap();
1369     };
1370     // Pointer-like types.
1371     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1372       boxEleTy = eleTy;
1373     // Integer types.
1374     if (fir::isa_integer(boxEleTy)) {
1375       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1376         return doInteger(ty.getWidth());
1377       auto ty = boxEleTy.cast<fir::IntegerType>();
1378       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1379     }
1380     // Floating point types.
1381     if (fir::isa_real(boxEleTy)) {
1382       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1383         return doFloat(ty.getWidth());
1384       auto ty = boxEleTy.cast<fir::RealType>();
1385       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1386     }
1387     // Complex types.
1388     if (fir::isa_complex(boxEleTy)) {
1389       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1390         return doComplex(
1391             ty.getElementType().cast<mlir::FloatType>().getWidth());
1392       auto ty = boxEleTy.cast<fir::ComplexType>();
1393       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1394     }
1395     // Character types.
1396     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1397       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1398       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1399         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1400         return doCharacter(charWidth, len);
1401       }
1402       assert(!lenParams.empty());
1403       return doCharacter(charWidth, lenParams.back());
1404     }
1405     // Logical type.
1406     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1407       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1408     // Array types.
1409     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1410       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1411     // Derived-type types.
1412     if (boxEleTy.isa<fir::RecordType>()) {
1413       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1414           this->lowerTy().convertType(boxEleTy));
1415       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1416       auto one =
1417           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1418       auto gep = rewriter.create<mlir::LLVM::GEPOp>(
1419           loc, ptrTy, mlir::ValueRange{nullPtr, one});
1420       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1421           loc, this->lowerTy().indexType(), gep);
1422       return {eleSize,
1423               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1424     }
1425     // Reference type.
1426     if (fir::isa_ref_type(boxEleTy)) {
1427       // FIXME: use the target pointer size rather than sizeof(void*)
1428       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1429               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1430     }
1431     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1432   }
1433 
1434   /// Basic pattern to write a field in the descriptor
1435   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1436                           mlir::Location loc, mlir::Value dest,
1437                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1438                           bool bitcast = false) const {
1439     auto boxTy = dest.getType();
1440     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1441     if (bitcast)
1442       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1443     else
1444       value = this->integerCast(loc, rewriter, fldTy, value);
1445     SmallVector<mlir::Attribute, 2> attrs;
1446     for (auto i : fldIndexes)
1447       attrs.push_back(rewriter.getI32IntegerAttr(i));
1448     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1449     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1450                                                       indexesAttr);
1451   }
1452 
1453   inline mlir::Value
1454   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1455                     mlir::Location loc, mlir::Value dest,
1456                     mlir::Value base) const {
1457     return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true);
1458   }
1459 
1460   /// Get the address of the type descriptor global variable that was created by
1461   /// lowering for derived type \p recType.
1462   template <typename BOX>
1463   mlir::Value
1464   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1465                     mlir::Location loc, fir::RecordType recType) const {
1466     std::string name = recType.getLoweredName();
1467     auto module = box->template getParentOfType<mlir::ModuleOp>();
1468     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1469       auto ty = mlir::LLVM::LLVMPointerType::get(
1470           this->lowerTy().convertType(global.getType()));
1471       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1472                                                       global.sym_name());
1473     }
1474     if (auto global =
1475             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1476       // The global may have already been translated to LLVM.
1477       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1478       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1479                                                       global.sym_name());
1480     }
1481     // The global does not exist in the current translation unit, but may be
1482     // defined elsewhere (e.g., type defined in a module).
1483     // For now, create a extern_weak symbol (will become nullptr if unresolved)
1484     // to support generating code without the front-end generated symbols.
1485     // These could be made available_externally to require the symbols to be
1486     // defined elsewhere and to cause link-time failure otherwise.
1487     auto i8Ty = rewriter.getIntegerType(8);
1488     mlir::OpBuilder modBuilder(module.getBodyRegion());
1489     // TODO: The symbol should be lowered to constant in lowering, they are read
1490     // only.
1491     modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
1492                                             mlir::LLVM::Linkage::ExternWeak,
1493                                             name, mlir::Attribute{});
1494     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1495     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1496   }
1497 
1498   template <typename BOX>
1499   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1500   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1501                        unsigned rank, mlir::ValueRange lenParams) const {
1502     auto loc = box.getLoc();
1503     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1504     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1505     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1506     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1507     mlir::Value descriptor =
1508         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1509 
1510     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1511     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1512       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1513         typeparams.push_back(box.substr()[1]);
1514     }
1515 
1516     // Write each of the fields with the appropriate values
1517     auto [eleSize, cfiTy] =
1518         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1519     descriptor =
1520         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1521     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1522                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1523     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1524                              this->genI32Constant(loc, rewriter, rank));
1525     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1526     descriptor =
1527         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1528                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1529     const bool hasAddendum = isDerivedType(boxTy);
1530     descriptor =
1531         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1532                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1533 
1534     if (hasAddendum) {
1535       auto isArray =
1536           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1537       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1538       auto typeDesc =
1539           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1540       descriptor =
1541           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1542                       /*bitCast=*/true);
1543     }
1544 
1545     return {boxTy, descriptor, eleSize};
1546   }
1547 
1548   /// If the embox is not in a globalOp body, allocate storage for the box;
1549   /// store the value inside and return the generated alloca. Return the input
1550   /// value otherwise.
1551   mlir::Value
1552   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1553                                mlir::Location loc, mlir::Value boxValue) const {
1554     auto *thisBlock = rewriter.getInsertionBlock();
1555     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1556       return boxValue;
1557     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1558     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1559     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1560     return alloca;
1561   }
1562 };
1563 
1564 /// Create a generic box on a memory reference. This conversions lowers the
1565 /// abstract box to the appropriate, initialized descriptor.
1566 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1567   using EmboxCommonConversion::EmboxCommonConversion;
1568 
1569   mlir::LogicalResult
1570   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1571                   mlir::ConversionPatternRewriter &rewriter) const override {
1572     assert(!embox.getShape() && "There should be no dims on this embox op");
1573     auto [boxTy, dest, eleSize] =
1574         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1575                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1576     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1577                              adaptor.getOperands()[0]);
1578     if (isDerivedTypeWithLenParams(boxTy))
1579       return rewriter.notifyMatchFailure(
1580           embox, "fir.embox codegen of derived with length parameters not "
1581                  "implemented yet");
1582     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1583     rewriter.replaceOp(embox, result);
1584     return success();
1585   }
1586 };
1587 
1588 /// Lower `fir.emboxproc` operation. Creates a procedure box.
1589 /// TODO: Part of supporting Fortran 2003 procedure pointers.
1590 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1591   using FIROpConversion::FIROpConversion;
1592 
1593   mlir::LogicalResult
1594   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1595                   mlir::ConversionPatternRewriter &rewriter) const override {
1596     return rewriter.notifyMatchFailure(
1597         emboxproc, "fir.emboxproc codegen is not implemented yet");
1598   }
1599 };
1600 
1601 
1602 // Code shared between insert_value and extract_value Ops.
1603 struct ValueOpCommon {
1604   // Translate the arguments pertaining to any multidimensional array to
1605   // row-major order for LLVM-IR.
1606   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1607                          mlir::Type ty) {
1608     assert(ty && "type is null");
1609     const auto end = attrs.size();
1610     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1611       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1612         const auto dim = getDimension(seq);
1613         if (dim > 1) {
1614           auto ub = std::min(i + dim, end);
1615           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1616           i += dim - 1;
1617         }
1618         ty = getArrayElementType(seq);
1619       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1620         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1621       } else {
1622         llvm_unreachable("index into invalid type");
1623       }
1624     }
1625   }
1626 
1627   static llvm::SmallVector<mlir::Attribute>
1628   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1629                  mlir::ArrayAttr arrAttr) {
1630     llvm::SmallVector<mlir::Attribute> attrs;
1631     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1632       if (i->isa<mlir::IntegerAttr>()) {
1633         attrs.push_back(*i);
1634       } else {
1635         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1636         ++i;
1637         auto ty = i->cast<mlir::TypeAttr>().getValue();
1638         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1639         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1640       }
1641     }
1642     return attrs;
1643   }
1644 
1645 private:
1646   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1647     unsigned result = 1;
1648     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1649          eleTy;
1650          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1651       ++result;
1652     return result;
1653   }
1654 
1655   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1656     auto eleTy = ty.getElementType();
1657     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1658       eleTy = arrTy.getElementType();
1659     return eleTy;
1660   }
1661 };
1662 
1663 /// Extract a subobject value from an ssa-value of aggregate type
1664 struct ExtractValueOpConversion
1665     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1666       public ValueOpCommon {
1667   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1668 
1669   mlir::LogicalResult
1670   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1671             mlir::ConversionPatternRewriter &rewriter) const override {
1672     auto attrs = collectIndices(rewriter, extractVal.coor());
1673     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1674     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1675     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1676         extractVal, ty, adaptor.getOperands()[0], position);
1677     return success();
1678   }
1679 };
1680 
1681 /// InsertValue is the generalized instruction for the composition of new
1682 /// aggregate type values.
1683 struct InsertValueOpConversion
1684     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1685       public ValueOpCommon {
1686   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1687 
1688   mlir::LogicalResult
1689   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1690             mlir::ConversionPatternRewriter &rewriter) const override {
1691     auto attrs = collectIndices(rewriter, insertVal.coor());
1692     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1693     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1694     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1695         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1696         position);
1697     return success();
1698   }
1699 };
1700 
1701 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1702 struct InsertOnRangeOpConversion
1703     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1704   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1705 
1706   // Increments an array of subscripts in a row major fasion.
1707   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1708                            SmallVector<uint64_t> &subscripts) const {
1709     for (size_t i = dims.size(); i > 0; --i) {
1710       if (++subscripts[i - 1] < dims[i - 1]) {
1711         return;
1712       }
1713       subscripts[i - 1] = 0;
1714     }
1715   }
1716 
1717   mlir::LogicalResult
1718   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1719             mlir::ConversionPatternRewriter &rewriter) const override {
1720 
1721     llvm::SmallVector<uint64_t> dims;
1722     auto type = adaptor.getOperands()[0].getType();
1723 
1724     // Iteratively extract the array dimensions from the type.
1725     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1726       dims.push_back(t.getNumElements());
1727       type = t.getElementType();
1728     }
1729 
1730     SmallVector<uint64_t> lBounds;
1731     SmallVector<uint64_t> uBounds;
1732 
1733     // Unzip the upper and lower bound and convert to a row major format.
1734     mlir::DenseIntElementsAttr coor = range.coor();
1735     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
1736     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
1737       uBounds.push_back(*i++);
1738       lBounds.push_back(*i);
1739     }
1740 
1741     auto &subscripts = lBounds;
1742     auto loc = range.getLoc();
1743     mlir::Value lastOp = adaptor.getOperands()[0];
1744     mlir::Value insertVal = adaptor.getOperands()[1];
1745 
1746     auto i64Ty = rewriter.getI64Type();
1747     while (subscripts != uBounds) {
1748       // Convert uint64_t's to Attribute's.
1749       SmallVector<mlir::Attribute> subscriptAttrs;
1750       for (const auto &subscript : subscripts)
1751         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1752       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1753           loc, ty, lastOp, insertVal,
1754           ArrayAttr::get(range.getContext(), subscriptAttrs));
1755 
1756       incrementSubscripts(dims, subscripts);
1757     }
1758 
1759     // Convert uint64_t's to Attribute's.
1760     SmallVector<mlir::Attribute> subscriptAttrs;
1761     for (const auto &subscript : subscripts)
1762       subscriptAttrs.push_back(
1763           IntegerAttr::get(rewriter.getI64Type(), subscript));
1764     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1765 
1766     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1767         range, ty, lastOp, insertVal,
1768         ArrayAttr::get(range.getContext(), arrayRef));
1769 
1770     return success();
1771   }
1772 };
1773 
1774 //
1775 // Primitive operations on Complex types
1776 //
1777 
1778 /// Generate inline code for complex addition/subtraction
1779 template <typename LLVMOP, typename OPTY>
1780 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1781                                      mlir::ConversionPatternRewriter &rewriter,
1782                                      fir::LLVMTypeConverter &lowering) {
1783   mlir::Value a = opnds[0];
1784   mlir::Value b = opnds[1];
1785   auto loc = sumop.getLoc();
1786   auto ctx = sumop.getContext();
1787   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1788   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1789   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1790   mlir::Type ty = lowering.convertType(sumop.getType());
1791   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1792   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1793   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1794   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1795   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1796   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1797   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1798   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1799   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1800 }
1801 
1802 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1803   using FIROpConversion::FIROpConversion;
1804 
1805   mlir::LogicalResult
1806   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1807                   mlir::ConversionPatternRewriter &rewriter) const override {
1808     // given: (x + iy) + (x' + iy')
1809     // result: (x + x') + i(y + y')
1810     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1811                                             rewriter, lowerTy());
1812     rewriter.replaceOp(addc, r.getResult());
1813     return success();
1814   }
1815 };
1816 
1817 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1818   using FIROpConversion::FIROpConversion;
1819 
1820   mlir::LogicalResult
1821   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1822                   mlir::ConversionPatternRewriter &rewriter) const override {
1823     // given: (x + iy) - (x' + iy')
1824     // result: (x - x') + i(y - y')
1825     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1826                                             rewriter, lowerTy());
1827     rewriter.replaceOp(subc, r.getResult());
1828     return success();
1829   }
1830 };
1831 
1832 /// Inlined complex multiply
1833 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1834   using FIROpConversion::FIROpConversion;
1835 
1836   mlir::LogicalResult
1837   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1838                   mlir::ConversionPatternRewriter &rewriter) const override {
1839     // TODO: Can we use a call to __muldc3 ?
1840     // given: (x + iy) * (x' + iy')
1841     // result: (xx'-yy')+i(xy'+yx')
1842     mlir::Value a = adaptor.getOperands()[0];
1843     mlir::Value b = adaptor.getOperands()[1];
1844     auto loc = mulc.getLoc();
1845     auto *ctx = mulc.getContext();
1846     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1847     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1848     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1849     mlir::Type ty = convertType(mulc.getType());
1850     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1851     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1852     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1853     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1854     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1855     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1856     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1857     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1858     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1859     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1860     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1861     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1862     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1863     rewriter.replaceOp(mulc, r0.getResult());
1864     return success();
1865   }
1866 };
1867 
1868 /// Inlined complex division
1869 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1870   using FIROpConversion::FIROpConversion;
1871 
1872   mlir::LogicalResult
1873   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1874                   mlir::ConversionPatternRewriter &rewriter) const override {
1875     // TODO: Can we use a call to __divdc3 instead?
1876     // Just generate inline code for now.
1877     // given: (x + iy) / (x' + iy')
1878     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1879     mlir::Value a = adaptor.getOperands()[0];
1880     mlir::Value b = adaptor.getOperands()[1];
1881     auto loc = divc.getLoc();
1882     auto *ctx = divc.getContext();
1883     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1884     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1885     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1886     mlir::Type ty = convertType(divc.getType());
1887     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1888     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1889     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1890     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1891     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1892     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1893     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1894     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1895     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1896     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1897     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1898     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1899     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1900     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1901     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1902     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1903     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1904     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1905     rewriter.replaceOp(divc, r0.getResult());
1906     return success();
1907   }
1908 };
1909 
1910 /// Inlined complex negation
1911 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1912   using FIROpConversion::FIROpConversion;
1913 
1914   mlir::LogicalResult
1915   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1916                   mlir::ConversionPatternRewriter &rewriter) const override {
1917     // given: -(x + iy)
1918     // result: -x - iy
1919     auto *ctxt = neg.getContext();
1920     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1921     auto ty = convertType(neg.getType());
1922     auto loc = neg.getLoc();
1923     mlir::Value o0 = adaptor.getOperands()[0];
1924     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1925     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1926     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1927     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1928     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1929     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1930     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1931     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1932     return success();
1933   }
1934 };
1935 
1936 /// Conversion pattern for operation that must be dead. The information in these
1937 /// operations is used by other operation. At this point they should not have
1938 /// anymore uses.
1939 /// These operations are normally dead after the pre-codegen pass.
1940 template <typename FromOp>
1941 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
1942   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
1943       : FIROpConversion<FromOp>(lowering) {}
1944   using OpAdaptor = typename FromOp::Adaptor;
1945 
1946   mlir::LogicalResult
1947   matchAndRewrite(FromOp op, OpAdaptor adaptor,
1948                   mlir::ConversionPatternRewriter &rewriter) const final {
1949     if (!op->getUses().empty())
1950       return rewriter.notifyMatchFailure(op, "op must be dead");
1951     rewriter.eraseOp(op);
1952     return success();
1953   }
1954 };
1955 
1956 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
1957   using MustBeDeadConversion::MustBeDeadConversion;
1958 };
1959 
1960 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
1961   using MustBeDeadConversion::MustBeDeadConversion;
1962 };
1963 
1964 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
1965   using MustBeDeadConversion::MustBeDeadConversion;
1966 };
1967 
1968 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
1969   using MustBeDeadConversion::MustBeDeadConversion;
1970 };
1971 
1972 /// `fir.is_present` -->
1973 /// ```
1974 ///  %0 = llvm.mlir.constant(0 : i64)
1975 ///  %1 = llvm.ptrtoint %0
1976 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1977 /// ```
1978 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1979   using FIROpConversion::FIROpConversion;
1980 
1981   mlir::LogicalResult
1982   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1983                   mlir::ConversionPatternRewriter &rewriter) const override {
1984     mlir::Type idxTy = lowerTy().indexType();
1985     mlir::Location loc = isPresent.getLoc();
1986     auto ptr = adaptor.getOperands()[0];
1987 
1988     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1989       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1990       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1991 
1992       mlir::Type ty = structTy.getBody()[0];
1993       mlir::MLIRContext *ctx = isPresent.getContext();
1994       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1995       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1996     }
1997     mlir::LLVM::ConstantOp c0 =
1998         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1999     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
2000     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2001         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
2002 
2003     return success();
2004   }
2005 };
2006 
2007 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
2008 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
2009 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
2010 /// element is the length of the character buffer (`#n`).
2011 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
2012   using FIROpConversion::FIROpConversion;
2013 
2014   mlir::LogicalResult
2015   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
2016                   mlir::ConversionPatternRewriter &rewriter) const override {
2017     mlir::ValueRange operands = adaptor.getOperands();
2018     MLIRContext *ctx = emboxChar.getContext();
2019 
2020     mlir::Value charBuffer = operands[0];
2021     mlir::Value charBufferLen = operands[1];
2022 
2023     mlir::Location loc = emboxChar.getLoc();
2024     mlir::Type llvmStructTy = convertType(emboxChar.getType());
2025     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
2026 
2027     mlir::Type lenTy =
2028         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
2029     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
2030 
2031     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2032     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2033     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2034         loc, llvmStructTy, llvmStruct, charBuffer, c0);
2035     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2036         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
2037 
2038     return success();
2039   }
2040 };
2041 
2042 /// Construct an `llvm.extractvalue` instruction. It will return value at
2043 /// element \p x from  \p tuple.
2044 mlir::LLVM::ExtractValueOp
2045 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
2046                          mlir::ConversionPatternRewriter &rewriter,
2047                          mlir::MLIRContext *ctx, int x) {
2048   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
2049   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
2050   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
2051 }
2052 
2053 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
2054 /// boxchar.
2055 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
2056   using FIROpConversion::FIROpConversion;
2057 
2058   mlir::LogicalResult
2059   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
2060                   mlir::ConversionPatternRewriter &rewriter) const override {
2061     mlir::Value boxChar = adaptor.getOperands()[0];
2062     mlir::Location loc = boxChar.getLoc();
2063     mlir::MLIRContext *ctx = boxChar.getContext();
2064     mlir::Type returnValTy = boxCharLen.getResult().getType();
2065 
2066     constexpr int boxcharLenIdx = 1;
2067     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
2068         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
2069     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
2070     rewriter.replaceOp(boxCharLen, lenAfterCast);
2071 
2072     return success();
2073   }
2074 };
2075 
2076 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2077 /// the character buffer and one for the buffer length.
2078 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2079   using FIROpConversion::FIROpConversion;
2080 
2081   mlir::LogicalResult
2082   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2083                   mlir::ConversionPatternRewriter &rewriter) const override {
2084     MLIRContext *ctx = unboxchar.getContext();
2085 
2086     mlir::Type lenTy = convertType(unboxchar.getType(1));
2087     mlir::Value tuple = adaptor.getOperands()[0];
2088     mlir::Type tupleTy = tuple.getType();
2089 
2090     mlir::Location loc = unboxchar.getLoc();
2091     mlir::Value ptrToBuffer =
2092         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2093 
2094     mlir::LLVM::ExtractValueOp len =
2095         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2096     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2097 
2098     rewriter.replaceOp(unboxchar,
2099                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2100     return success();
2101   }
2102 };
2103 
2104 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2105 /// components.
2106 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2107 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2108   using FIROpConversion::FIROpConversion;
2109 
2110   mlir::LogicalResult
2111   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2112                   mlir::ConversionPatternRewriter &rewriter) const override {
2113     return rewriter.notifyMatchFailure(
2114         unboxproc, "fir.unboxproc codegen is not implemented yet");
2115   }
2116 };
2117 
2118 /// Convert `fir.field_index`. The conversion depends on whether the size of
2119 /// the record is static or dynamic.
2120 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2121   using FIROpConversion::FIROpConversion;
2122 
2123   // NB: most field references should be resolved by this point
2124   mlir::LogicalResult
2125   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2126                   mlir::ConversionPatternRewriter &rewriter) const override {
2127     auto recTy = field.on_type().cast<fir::RecordType>();
2128     unsigned index = recTy.getFieldIndex(field.field_id());
2129 
2130     if (!fir::hasDynamicSize(recTy)) {
2131       // Derived type has compile-time constant layout. Return index of the
2132       // component type in the parent type (to be used in GEP).
2133       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2134                                     field.getLoc(), rewriter, index)});
2135       return success();
2136     }
2137 
2138     // Derived type has compile-time constant layout. Call the compiler
2139     // generated function to determine the byte offset of the field at runtime.
2140     // This returns a non-constant.
2141     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2142         field.getContext(), getOffsetMethodName(recTy, field.field_id()));
2143     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2144     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2145         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2146     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2147         field, lowerTy().offsetType(), adaptor.getOperands(),
2148         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2149     return success();
2150   }
2151 
2152   // Re-Construct the name of the compiler generated method that calculates the
2153   // offset
2154   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2155                                                 llvm::StringRef field) {
2156     return recTy.getName().str() + "P." + field.str() + ".offset";
2157   }
2158 };
2159 
2160 } // namespace
2161 
2162 namespace {
2163 /// Convert FIR dialect to LLVM dialect
2164 ///
2165 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
2166 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
2167 ///
2168 /// This pass is not complete yet. We are upstreaming it in small patches.
2169 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
2170 public:
2171   mlir::ModuleOp getModule() { return getOperation(); }
2172 
2173   void runOnOperation() override final {
2174     auto mod = getModule();
2175     if (!forcedTargetTriple.empty()) {
2176       fir::setTargetTriple(mod, forcedTargetTriple);
2177     }
2178 
2179     auto *context = getModule().getContext();
2180     fir::LLVMTypeConverter typeConverter{getModule()};
2181     mlir::OwningRewritePatternList pattern(context);
2182     pattern.insert<
2183         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
2184         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
2185         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
2186         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion,
2187         BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion,
2188         CmpcOpConversion, ConstcOpConversion, ConvertOpConversion,
2189         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
2190         DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
2191         EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
2192         FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
2193         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
2194         InsertValueOpConversion, IsPresentOpConversion,
2195         LenParamIndexOpConversion, LoadOpConversion, NegcOpConversion,
2196         NoReassocOpConversion, MulcOpConversion, SelectCaseOpConversion,
2197         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
2198         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
2199         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
2200         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
2201         UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
2202         typeConverter);
2203     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
2204     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
2205                                                             pattern);
2206     mlir::ConversionTarget target{*context};
2207     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2208 
2209     // required NOPs for applying a full conversion
2210     target.addLegalOp<mlir::ModuleOp>();
2211 
2212     // apply the patterns
2213     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
2214                                                std::move(pattern)))) {
2215       signalPassFailure();
2216     }
2217   }
2218 };
2219 } // namespace
2220 
2221 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
2222   return std::make_unique<FIRToLLVMLowering>();
2223 }
2224