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