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