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 "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
19 #include "mlir/Conversion/LLVMCommon/Pattern.h"
20 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
21 #include "mlir/IR/BuiltinTypes.h"
22 #include "mlir/IR/Matchers.h"
23 #include "mlir/Pass/Pass.h"
24 #include "llvm/ADT/ArrayRef.h"
25 
26 #define DEBUG_TYPE "flang-codegen"
27 
28 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
29 #include "TypeConverter.h"
30 
31 /// `fir.box` attribute values as defined for CFI_attribute_t in
32 /// flang/ISO_Fortran_binding.h.
33 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
34 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
35 
36 static mlir::LLVM::ConstantOp
37 genConstantIndex(mlir::Location loc, mlir::Type ity,
38                  mlir::ConversionPatternRewriter &rewriter,
39                  std::int64_t offset) {
40   auto cattr = rewriter.getI64IntegerAttr(offset);
41   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
42 }
43 
44 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
45                           mlir::Block *insertBefore) {
46   assert(insertBefore && "expected valid insertion block");
47   return rewriter.createBlock(insertBefore->getParent(),
48                               mlir::Region::iterator(insertBefore));
49 }
50 
51 namespace {
52 /// FIR conversion pattern template
53 template <typename FromOp>
54 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
55 public:
56   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
57       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
58 
59 protected:
60   mlir::Type convertType(mlir::Type ty) const {
61     return lowerTy().convertType(ty);
62   }
63 
64   mlir::LLVM::ConstantOp
65   genConstantOffset(mlir::Location loc,
66                     mlir::ConversionPatternRewriter &rewriter,
67                     int offset) const {
68     auto ity = lowerTy().offsetType();
69     auto cattr = rewriter.getI32IntegerAttr(offset);
70     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
71   }
72 
73   /// Construct code sequence to extract the specifc value from a `fir.box`.
74   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
75                               mlir::Type resultTy,
76                               mlir::ConversionPatternRewriter &rewriter,
77                               unsigned boxValue) const {
78     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
79     mlir::LLVM::ConstantOp cValuePos =
80         genConstantOffset(loc, rewriter, boxValue);
81     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
82     auto p = rewriter.create<mlir::LLVM::GEPOp>(
83         loc, pty, mlir::ValueRange{box, c0, cValuePos});
84     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
85   }
86 
87   /// Method to construct code sequence to get the triple for dimension `dim`
88   /// from a box.
89   SmallVector<mlir::Value, 3>
90   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
91                  mlir::Value box, mlir::Value dim,
92                  mlir::ConversionPatternRewriter &rewriter) const {
93     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
94     mlir::LLVM::ConstantOp cDims =
95         genConstantOffset(loc, rewriter, kDimsPosInBox);
96     mlir::LLVM::LoadOp l0 =
97         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
98     mlir::LLVM::LoadOp l1 =
99         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
100     mlir::LLVM::LoadOp l2 =
101         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
102     return {l0.getResult(), l1.getResult(), l2.getResult()};
103   }
104 
105   mlir::LLVM::LoadOp
106   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
107                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
108                  mlir::Type ty,
109                  mlir::ConversionPatternRewriter &rewriter) const {
110     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
111     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
112     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
113     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
114   }
115 
116   /// Read base address from a fir.box. Returned address has type ty.
117   mlir::Value
118   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
119                       mlir::ConversionPatternRewriter &rewriter) const {
120     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
121     mlir::LLVM::ConstantOp cAddr =
122         genConstantOffset(loc, rewriter, kAddrPosInBox);
123     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
124     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
125     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
126   }
127 
128   mlir::Value
129   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
130                          mlir::ConversionPatternRewriter &rewriter) const {
131     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
132     mlir::LLVM::ConstantOp cElemLen =
133         genConstantOffset(loc, rewriter, kElemLenPosInBox);
134     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
135     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
136     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
137   }
138 
139   // Load the attribute from the \p box and perform a check against \p maskValue
140   // The final comparison is implemented as `(attribute & maskValue) != 0`.
141   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
142                                    mlir::ConversionPatternRewriter &rewriter,
143                                    unsigned maskValue) const {
144     mlir::Type attrTy = rewriter.getI32Type();
145     mlir::Value attribute =
146         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
147     mlir::LLVM::ConstantOp attrMask =
148         genConstantOffset(loc, rewriter, maskValue);
149     auto maskRes =
150         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
151     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
152     return rewriter.create<mlir::LLVM::ICmpOp>(
153         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
154   }
155 
156   template <typename... ARGS>
157   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
158                            mlir::ConversionPatternRewriter &rewriter,
159                            mlir::Value base, ARGS... args) const {
160     SmallVector<mlir::Value> cv{args...};
161     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
162   }
163 
164   /// Perform an extension or truncation as needed on an integer value. Lowering
165   /// to the specific target may involve some sign-extending or truncation of
166   /// values, particularly to fit them from abstract box types to the
167   /// appropriate reified structures.
168   mlir::Value integerCast(mlir::Location loc,
169                           mlir::ConversionPatternRewriter &rewriter,
170                           mlir::Type ty, mlir::Value val) const {
171     auto valTy = val.getType();
172     // If the value was not yet lowered, lower its type so that it can
173     // be used in getPrimitiveTypeSizeInBits.
174     if (!valTy.isa<mlir::IntegerType>())
175       valTy = convertType(valTy);
176     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
177     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
178     if (toSize < fromSize)
179       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
180     if (toSize > fromSize)
181       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
182     return val;
183   }
184 
185   fir::LLVMTypeConverter &lowerTy() const {
186     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
187   }
188 };
189 
190 /// FIR conversion pattern template
191 template <typename FromOp>
192 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
193 public:
194   using FIROpConversion<FromOp>::FIROpConversion;
195   using OpAdaptor = typename FromOp::Adaptor;
196 
197   mlir::LogicalResult
198   matchAndRewrite(FromOp op, OpAdaptor adaptor,
199                   mlir::ConversionPatternRewriter &rewriter) const final {
200     mlir::Type ty = this->convertType(op.getType());
201     return doRewrite(op, ty, adaptor, rewriter);
202   }
203 
204   virtual mlir::LogicalResult
205   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
206             mlir::ConversionPatternRewriter &rewriter) const = 0;
207 };
208 
209 /// Create value signaling an absent optional argument in a call, e.g.
210 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
211 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
212   using FIROpConversion::FIROpConversion;
213 
214   mlir::LogicalResult
215   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
216                   mlir::ConversionPatternRewriter &rewriter) const override {
217     mlir::Type ty = convertType(absent.getType());
218     mlir::Location loc = absent.getLoc();
219 
220     if (absent.getType().isa<fir::BoxCharType>()) {
221       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
222       assert(!structTy.isOpaque() && !structTy.getBody().empty());
223       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
224       auto nullField =
225           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
226       mlir::MLIRContext *ctx = absent.getContext();
227       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
228       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
229           absent, ty, undefStruct, nullField, c0);
230     } else {
231       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
232     }
233     return success();
234   }
235 };
236 
237 // Lower `fir.address_of` operation to `llvm.address_of` operation.
238 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
239   using FIROpConversion::FIROpConversion;
240 
241   mlir::LogicalResult
242   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
243                   mlir::ConversionPatternRewriter &rewriter) const override {
244     auto ty = convertType(addr.getType());
245     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
246         addr, ty, addr.symbol().getRootReference().getValue());
247     return success();
248   }
249 };
250 } // namespace
251 
252 /// Lookup the function to compute the memory size of this parametric derived
253 /// type. The size of the object may depend on the LEN type parameters of the
254 /// derived type.
255 static mlir::LLVM::LLVMFuncOp
256 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
257                           mlir::ConversionPatternRewriter &rewriter) {
258   auto module = op->getParentOfType<mlir::ModuleOp>();
259   std::string name = recTy.getName().str() + "P.mem.size";
260   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
261 }
262 
263 namespace {
264 /// convert to LLVM IR dialect `alloca`
265 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
266   using FIROpConversion::FIROpConversion;
267 
268   mlir::LogicalResult
269   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
270                   mlir::ConversionPatternRewriter &rewriter) const override {
271     mlir::ValueRange operands = adaptor.getOperands();
272     auto loc = alloc.getLoc();
273     mlir::Type ity = lowerTy().indexType();
274     unsigned i = 0;
275     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
276     mlir::Type ty = convertType(alloc.getType());
277     mlir::Type resultTy = ty;
278     if (alloc.hasLenParams()) {
279       unsigned end = alloc.numLenParams();
280       llvm::SmallVector<mlir::Value> lenParams;
281       for (; i < end; ++i)
282         lenParams.push_back(operands[i]);
283       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
284       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
285         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
286             chrTy.getContext(), chrTy.getFKind());
287         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
288         assert(end == 1);
289         size = integerCast(loc, rewriter, ity, lenParams[0]);
290       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
291         mlir::LLVM::LLVMFuncOp memSizeFn =
292             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
293         if (!memSizeFn)
294           emitError(loc, "did not find allocation function");
295         mlir::NamedAttribute attr = rewriter.getNamedAttr(
296             "callee", mlir::SymbolRefAttr::get(memSizeFn));
297         auto call = rewriter.create<mlir::LLVM::CallOp>(
298             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
299         size = call.getResult(0);
300         ty = mlir::LLVM::LLVMPointerType::get(
301             mlir::IntegerType::get(alloc.getContext(), 8));
302       } else {
303         return emitError(loc, "unexpected type ")
304                << scalarType << " with type parameters";
305       }
306     }
307     if (alloc.hasShapeOperands()) {
308       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
309       // Scale the size by constant factors encoded in the array type.
310       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
311         fir::SequenceType::Extent constSize = 1;
312         for (auto extent : seqTy.getShape())
313           if (extent != fir::SequenceType::getUnknownExtent())
314             constSize *= extent;
315         mlir::Value constVal{
316             genConstantIndex(loc, ity, rewriter, constSize).getResult()};
317         size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
318       }
319       unsigned end = operands.size();
320       for (; i < end; ++i)
321         size = rewriter.create<mlir::LLVM::MulOp>(
322             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
323     }
324     if (ty == resultTy) {
325       // Do not emit the bitcast if ty and resultTy are the same.
326       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
327                                                         alloc->getAttrs());
328     } else {
329       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
330                                                       alloc->getAttrs());
331       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
332     }
333     return success();
334   }
335 };
336 
337 /// Lower `fir.box_addr` to the sequence of operations to extract the first
338 /// element of the box.
339 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
340   using FIROpConversion::FIROpConversion;
341 
342   mlir::LogicalResult
343   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
344                   mlir::ConversionPatternRewriter &rewriter) const override {
345     mlir::Value a = adaptor.getOperands()[0];
346     auto loc = boxaddr.getLoc();
347     mlir::Type ty = convertType(boxaddr.getType());
348     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
349       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
350     } else {
351       auto c0attr = rewriter.getI32IntegerAttr(0);
352       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
353       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
354                                                               c0);
355     }
356     return success();
357   }
358 };
359 
360 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
361 /// dimension infomartion from the boxed value.
362 /// Result in a triple set of GEPs and loads.
363 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
364   using FIROpConversion::FIROpConversion;
365 
366   mlir::LogicalResult
367   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
368                   mlir::ConversionPatternRewriter &rewriter) const override {
369     SmallVector<mlir::Type, 3> resultTypes = {
370         convertType(boxdims.getResult(0).getType()),
371         convertType(boxdims.getResult(1).getType()),
372         convertType(boxdims.getResult(2).getType()),
373     };
374     auto results =
375         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
376                        adaptor.getOperands()[1], rewriter);
377     rewriter.replaceOp(boxdims, results);
378     return success();
379   }
380 };
381 
382 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
383 /// an element in the boxed value.
384 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
385   using FIROpConversion::FIROpConversion;
386 
387   mlir::LogicalResult
388   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
389                   mlir::ConversionPatternRewriter &rewriter) const override {
390     mlir::Value a = adaptor.getOperands()[0];
391     auto loc = boxelesz.getLoc();
392     auto ty = convertType(boxelesz.getType());
393     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
394     rewriter.replaceOp(boxelesz, elemSize);
395     return success();
396   }
397 };
398 
399 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
400 /// boxed value was from an ALLOCATABLE entity.
401 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
402   using FIROpConversion::FIROpConversion;
403 
404   mlir::LogicalResult
405   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
406                   mlir::ConversionPatternRewriter &rewriter) const override {
407     mlir::Value box = adaptor.getOperands()[0];
408     auto loc = boxisalloc.getLoc();
409     mlir::Value check =
410         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
411     rewriter.replaceOp(boxisalloc, check);
412     return success();
413   }
414 };
415 
416 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
417 /// boxed is an array.
418 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
419   using FIROpConversion::FIROpConversion;
420 
421   mlir::LogicalResult
422   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
423                   mlir::ConversionPatternRewriter &rewriter) const override {
424     mlir::Value a = adaptor.getOperands()[0];
425     auto loc = boxisarray.getLoc();
426     auto rank =
427         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
428     auto c0 = genConstantOffset(loc, rewriter, 0);
429     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
430         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
431     return success();
432   }
433 };
434 
435 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
436 /// boxed value was from a POINTER entity.
437 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
438   using FIROpConversion::FIROpConversion;
439 
440   mlir::LogicalResult
441   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
442                   mlir::ConversionPatternRewriter &rewriter) const override {
443     mlir::Value box = adaptor.getOperands()[0];
444     auto loc = boxisptr.getLoc();
445     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
446     rewriter.replaceOp(boxisptr, check);
447     return success();
448   }
449 };
450 
451 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
452 /// the box.
453 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
454   using FIROpConversion::FIROpConversion;
455 
456   mlir::LogicalResult
457   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
458                   mlir::ConversionPatternRewriter &rewriter) const override {
459     mlir::Value a = adaptor.getOperands()[0];
460     auto loc = boxrank.getLoc();
461     mlir::Type ty = convertType(boxrank.getType());
462     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
463     rewriter.replaceOp(boxrank, result);
464     return success();
465   }
466 };
467 
468 /// Lower `fir.string_lit` to LLVM IR dialect operation.
469 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
470   using FIROpConversion::FIROpConversion;
471 
472   mlir::LogicalResult
473   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
474                   mlir::ConversionPatternRewriter &rewriter) const override {
475     auto ty = convertType(constop.getType());
476     auto attr = constop.getValue();
477     if (attr.isa<mlir::StringAttr>()) {
478       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
479       return success();
480     }
481 
482     auto arr = attr.cast<mlir::ArrayAttr>();
483     auto charTy = constop.getType().cast<fir::CharacterType>();
484     unsigned bits = lowerTy().characterBitsize(charTy);
485     mlir::Type intTy = rewriter.getIntegerType(bits);
486     auto attrs = llvm::map_range(
487         arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute {
488           return mlir::IntegerAttr::get(
489               intTy,
490               attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits));
491         });
492     mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy);
493     auto denseAttr = mlir::DenseElementsAttr::get(
494         vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs));
495     rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty,
496                                                          denseAttr);
497     return success();
498   }
499 };
500 
501 // `fir.call` -> `llvm.call`
502 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
503   using FIROpConversion::FIROpConversion;
504 
505   mlir::LogicalResult
506   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
507                   mlir::ConversionPatternRewriter &rewriter) const override {
508     SmallVector<mlir::Type> resultTys;
509     for (auto r : call.getResults())
510       resultTys.push_back(convertType(r.getType()));
511     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
512         call, resultTys, adaptor.getOperands(), call->getAttrs());
513     return success();
514   }
515 };
516 
517 static mlir::Type getComplexEleTy(mlir::Type complex) {
518   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
519     return cc.getElementType();
520   return complex.cast<fir::ComplexType>().getElementType();
521 }
522 
523 /// Compare complex values
524 ///
525 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
526 ///
527 /// For completeness, all other comparison are done on the real component only.
528 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
529   using FIROpConversion::FIROpConversion;
530 
531   mlir::LogicalResult
532   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
533                   mlir::ConversionPatternRewriter &rewriter) const override {
534     mlir::ValueRange operands = adaptor.getOperands();
535     mlir::MLIRContext *ctxt = cmp.getContext();
536     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
537     mlir::Type resTy = convertType(cmp.getType());
538     mlir::Location loc = cmp.getLoc();
539     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
540     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
541                                        loc, eleTy, operands[0], pos0),
542                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
543                                        loc, eleTy, operands[1], pos0)};
544     auto rcp =
545         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
546     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
547     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
548                                        loc, eleTy, operands[0], pos1),
549                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
550                                        loc, eleTy, operands[1], pos1)};
551     auto icp =
552         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
553     SmallVector<mlir::Value, 2> cp{rcp, icp};
554     switch (cmp.getPredicate()) {
555     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
556       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
557       break;
558     case mlir::arith::CmpFPredicate::UNE: // .NE.
559       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
560       break;
561     default:
562       rewriter.replaceOp(cmp, rcp.getResult());
563       break;
564     }
565     return success();
566   }
567 };
568 
569 /// convert value of from-type to value of to-type
570 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
571   using FIROpConversion::FIROpConversion;
572 
573   static bool isFloatingPointTy(mlir::Type ty) {
574     return ty.isa<mlir::FloatType>();
575   }
576 
577   mlir::LogicalResult
578   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
579                   mlir::ConversionPatternRewriter &rewriter) const override {
580     auto fromTy = convertType(convert.value().getType());
581     auto toTy = convertType(convert.res().getType());
582     mlir::Value op0 = adaptor.getOperands()[0];
583     if (fromTy == toTy) {
584       rewriter.replaceOp(convert, op0);
585       return success();
586     }
587     auto loc = convert.getLoc();
588     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
589                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
590       if (fromBits == toBits) {
591         // TODO: Converting between two floating-point representations with the
592         // same bitwidth is not allowed for now.
593         mlir::emitError(loc,
594                         "cannot implicitly convert between two floating-point "
595                         "representations of the same bitwidth");
596         return {};
597       }
598       if (fromBits > toBits)
599         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
600       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
601     };
602     // Complex to complex conversion.
603     if (fir::isa_complex(convert.value().getType()) &&
604         fir::isa_complex(convert.res().getType())) {
605       // Special case: handle the conversion of a complex such that both the
606       // real and imaginary parts are converted together.
607       auto zero = mlir::ArrayAttr::get(convert.getContext(),
608                                        rewriter.getI32IntegerAttr(0));
609       auto one = mlir::ArrayAttr::get(convert.getContext(),
610                                       rewriter.getI32IntegerAttr(1));
611       auto ty = convertType(getComplexEleTy(convert.value().getType()));
612       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
613       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
614       auto nt = convertType(getComplexEleTy(convert.res().getType()));
615       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
616       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
617       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
618       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
619       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
620       auto i1 =
621           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
622       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
623                                                              ic, one);
624       return mlir::success();
625     }
626     // Floating point to floating point conversion.
627     if (isFloatingPointTy(fromTy)) {
628       if (isFloatingPointTy(toTy)) {
629         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
630         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
631         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
632         rewriter.replaceOp(convert, v);
633         return mlir::success();
634       }
635       if (toTy.isa<mlir::IntegerType>()) {
636         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
637         return mlir::success();
638       }
639     } else if (fromTy.isa<mlir::IntegerType>()) {
640       // Integer to integer conversion.
641       if (toTy.isa<mlir::IntegerType>()) {
642         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
643         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
644         assert(fromBits != toBits);
645         if (fromBits > toBits) {
646           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
647           return mlir::success();
648         }
649         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
650         return mlir::success();
651       }
652       // Integer to floating point conversion.
653       if (isFloatingPointTy(toTy)) {
654         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
655         return mlir::success();
656       }
657       // Integer to pointer conversion.
658       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
659         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
660         return mlir::success();
661       }
662     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
663       // Pointer to integer conversion.
664       if (toTy.isa<mlir::IntegerType>()) {
665         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
666         return mlir::success();
667       }
668       // Pointer to pointer conversion.
669       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
670         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
671         return mlir::success();
672       }
673     }
674     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
675   }
676 };
677 
678 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
679 /// table.
680 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
681   using FIROpConversion::FIROpConversion;
682 
683   mlir::LogicalResult
684   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
685                   mlir::ConversionPatternRewriter &rewriter) const override {
686     return rewriter.notifyMatchFailure(
687         dispatch, "fir.dispatch codegen is not implemented yet");
688   }
689 };
690 
691 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
692 /// derived type.
693 struct DispatchTableOpConversion
694     : public FIROpConversion<fir::DispatchTableOp> {
695   using FIROpConversion::FIROpConversion;
696 
697   mlir::LogicalResult
698   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
699                   mlir::ConversionPatternRewriter &rewriter) const override {
700     return rewriter.notifyMatchFailure(
701         dispTab, "fir.dispatch_table codegen is not implemented yet");
702   }
703 };
704 
705 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
706 /// method-name to a function.
707 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
708   using FIROpConversion::FIROpConversion;
709 
710   mlir::LogicalResult
711   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
712                   mlir::ConversionPatternRewriter &rewriter) const override {
713     return rewriter.notifyMatchFailure(
714         dtEnt, "fir.dt_entry codegen is not implemented yet");
715   }
716 };
717 
718 /// Lower `fir.global_len` operation.
719 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
720   using FIROpConversion::FIROpConversion;
721 
722   mlir::LogicalResult
723   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
724                   mlir::ConversionPatternRewriter &rewriter) const override {
725     return rewriter.notifyMatchFailure(
726         globalLen, "fir.global_len codegen is not implemented yet");
727   }
728 };
729 
730 /// Lower `fir.gentypedesc` to a global constant.
731 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
732   using FIROpConversion::FIROpConversion;
733 
734   mlir::LogicalResult
735   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
736                   mlir::ConversionPatternRewriter &rewriter) const override {
737     return rewriter.notifyMatchFailure(
738         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
739   }
740 };
741 
742 /// Lower `fir.has_value` operation to `llvm.return` operation.
743 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
744   using FIROpConversion::FIROpConversion;
745 
746   mlir::LogicalResult
747   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
748                   mlir::ConversionPatternRewriter &rewriter) const override {
749     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
750     return success();
751   }
752 };
753 
754 /// Lower `fir.global` operation to `llvm.global` operation.
755 /// `fir.insert_on_range` operations are replaced with constant dense attribute
756 /// if they are applied on the full range.
757 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
758   using FIROpConversion::FIROpConversion;
759 
760   mlir::LogicalResult
761   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
762                   mlir::ConversionPatternRewriter &rewriter) const override {
763     auto tyAttr = convertType(global.getType());
764     if (global.getType().isa<fir::BoxType>())
765       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
766     auto loc = global.getLoc();
767     mlir::Attribute initAttr{};
768     if (global.initVal())
769       initAttr = global.initVal().getValue();
770     auto linkage = convertLinkage(global.linkName());
771     auto isConst = global.constant().hasValue();
772     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
773         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
774     auto &gr = g.getInitializerRegion();
775     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
776     if (!gr.empty()) {
777       // Replace insert_on_range with a constant dense attribute if the
778       // initialization is on the full range.
779       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
780       for (auto insertOp : insertOnRangeOps) {
781         if (isFullRange(insertOp.coor(), insertOp.getType())) {
782           auto seqTyAttr = convertType(insertOp.getType());
783           auto *op = insertOp.val().getDefiningOp();
784           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
785           if (!constant) {
786             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
787             if (!convertOp)
788               continue;
789             constant = cast<mlir::arith::ConstantOp>(
790                 convertOp.value().getDefiningOp());
791           }
792           mlir::Type vecType = mlir::VectorType::get(
793               insertOp.getType().getShape(), constant.getType());
794           auto denseAttr = mlir::DenseElementsAttr::get(
795               vecType.cast<ShapedType>(), constant.value());
796           rewriter.setInsertionPointAfter(insertOp);
797           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
798               insertOp, seqTyAttr, denseAttr);
799         }
800       }
801     }
802     rewriter.eraseOp(global);
803     return success();
804   }
805 
806   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
807     auto extents = seqTy.getShape();
808     if (indexes.size() / 2 != extents.size())
809       return false;
810     for (unsigned i = 0; i < indexes.size(); i += 2) {
811       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
812         return false;
813       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
814         return false;
815     }
816     return true;
817   }
818 
819   // TODO: String comparaison should be avoided. Replace linkName with an
820   // enumeration.
821   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
822     if (optLinkage.hasValue()) {
823       auto name = optLinkage.getValue();
824       if (name == "internal")
825         return mlir::LLVM::Linkage::Internal;
826       if (name == "linkonce")
827         return mlir::LLVM::Linkage::Linkonce;
828       if (name == "common")
829         return mlir::LLVM::Linkage::Common;
830       if (name == "weak")
831         return mlir::LLVM::Linkage::Weak;
832     }
833     return mlir::LLVM::Linkage::External;
834   }
835 };
836 
837 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
838                  Optional<mlir::ValueRange> destOps,
839                  mlir::ConversionPatternRewriter &rewriter,
840                  mlir::Block *newBlock) {
841   if (destOps.hasValue())
842     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
843                                           newBlock, mlir::ValueRange());
844   else
845     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
846 }
847 
848 template <typename A, typename B>
849 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
850              mlir::ConversionPatternRewriter &rewriter) {
851   if (destOps.hasValue())
852     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
853                                                   dest);
854   else
855     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
856 }
857 
858 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
859                        Optional<mlir::ValueRange> destOps,
860                        mlir::ConversionPatternRewriter &rewriter) {
861   auto *thisBlock = rewriter.getInsertionBlock();
862   auto *newBlock = createBlock(rewriter, dest);
863   rewriter.setInsertionPointToEnd(thisBlock);
864   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
865   rewriter.setInsertionPointToEnd(newBlock);
866 }
867 
868 /// Conversion of `fir.select_case`
869 ///
870 /// The `fir.select_case` operation is converted to a if-then-else ladder.
871 /// Depending on the case condition type, one or several comparison and
872 /// conditional branching can be generated.
873 ///
874 /// A a point value case such as `case(4)`, a lower bound case such as
875 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
876 /// simple comparison between the selector value and the constant value in the
877 /// case. The block associated with the case condition is then executed if
878 /// the comparison succeed otherwise it branch to the next block with the
879 /// comparison for the the next case conditon.
880 ///
881 /// A closed interval case condition such as `case(7:10)` is converted with a
882 /// first comparison and conditional branching for the lower bound. If
883 /// successful, it branch to a second block with the comparison for the
884 /// upper bound in the same case condition.
885 ///
886 /// TODO: lowering of CHARACTER type cases is not handled yet.
887 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
888   using FIROpConversion::FIROpConversion;
889 
890   mlir::LogicalResult
891   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
892                   mlir::ConversionPatternRewriter &rewriter) const override {
893     unsigned conds = caseOp.getNumConditions();
894     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
895     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
896     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
897     if (ty.isa<fir::CharacterType>())
898       return rewriter.notifyMatchFailure(caseOp,
899                                          "conversion of fir.select_case with "
900                                          "character type not implemented yet");
901     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
902     auto loc = caseOp.getLoc();
903     for (unsigned t = 0; t != conds; ++t) {
904       mlir::Block *dest = caseOp.getSuccessor(t);
905       llvm::Optional<mlir::ValueRange> destOps =
906           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
907       llvm::Optional<mlir::ValueRange> cmpOps =
908           *caseOp.getCompareOperands(adaptor.getOperands(), t);
909       mlir::Value caseArg = *(cmpOps.getValue().begin());
910       mlir::Attribute attr = cases[t];
911       if (attr.isa<fir::PointIntervalAttr>()) {
912         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
913             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
914         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
915         continue;
916       }
917       if (attr.isa<fir::LowerBoundAttr>()) {
918         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
919             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
920         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
921         continue;
922       }
923       if (attr.isa<fir::UpperBoundAttr>()) {
924         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
925             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
926         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
927         continue;
928       }
929       if (attr.isa<fir::ClosedIntervalAttr>()) {
930         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
931             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
932         auto *thisBlock = rewriter.getInsertionBlock();
933         auto *newBlock1 = createBlock(rewriter, dest);
934         auto *newBlock2 = createBlock(rewriter, dest);
935         rewriter.setInsertionPointToEnd(thisBlock);
936         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
937         rewriter.setInsertionPointToEnd(newBlock1);
938         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
939         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
940             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
941         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
942         rewriter.setInsertionPointToEnd(newBlock2);
943         continue;
944       }
945       assert(attr.isa<mlir::UnitAttr>());
946       assert((t + 1 == conds) && "unit must be last");
947       genBrOp(caseOp, dest, destOps, rewriter);
948     }
949     return success();
950   }
951 };
952 
953 template <typename OP>
954 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
955                            typename OP::Adaptor adaptor,
956                            mlir::ConversionPatternRewriter &rewriter) {
957   unsigned conds = select.getNumConditions();
958   auto cases = select.getCases().getValue();
959   mlir::Value selector = adaptor.selector();
960   auto loc = select.getLoc();
961   assert(conds > 0 && "select must have cases");
962 
963   llvm::SmallVector<mlir::Block *> destinations;
964   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
965   mlir::Block *defaultDestination;
966   mlir::ValueRange defaultOperands;
967   llvm::SmallVector<int32_t> caseValues;
968 
969   for (unsigned t = 0; t != conds; ++t) {
970     mlir::Block *dest = select.getSuccessor(t);
971     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
972     const mlir::Attribute &attr = cases[t];
973     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
974       destinations.push_back(dest);
975       destinationsOperands.push_back(destOps.hasValue() ? *destOps
976                                                         : ValueRange());
977       caseValues.push_back(intAttr.getInt());
978       continue;
979     }
980     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
981     assert((t + 1 == conds) && "unit must be last");
982     defaultDestination = dest;
983     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
984   }
985 
986   // LLVM::SwitchOp takes a i32 type for the selector.
987   if (select.getSelector().getType() != rewriter.getI32Type())
988     selector =
989         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
990 
991   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
992       select, selector,
993       /*defaultDestination=*/defaultDestination,
994       /*defaultOperands=*/defaultOperands,
995       /*caseValues=*/caseValues,
996       /*caseDestinations=*/destinations,
997       /*caseOperands=*/destinationsOperands,
998       /*branchWeights=*/ArrayRef<int32_t>());
999 }
1000 
1001 /// conversion of fir::SelectOp to an if-then-else ladder
1002 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1003   using FIROpConversion::FIROpConversion;
1004 
1005   mlir::LogicalResult
1006   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1007                   mlir::ConversionPatternRewriter &rewriter) const override {
1008     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1009     return success();
1010   }
1011 };
1012 
1013 /// `fir.load` --> `llvm.load`
1014 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1015   using FIROpConversion::FIROpConversion;
1016 
1017   mlir::LogicalResult
1018   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1019                   mlir::ConversionPatternRewriter &rewriter) const override {
1020     // fir.box is a special case because it is considered as an ssa values in
1021     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1022     // and fir.box end up being the same llvm types and loading a
1023     // fir.ref<fir.box> is actually a no op in LLVM.
1024     if (load.getType().isa<fir::BoxType>()) {
1025       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1026     } else {
1027       mlir::Type ty = convertType(load.getType());
1028       ArrayRef<NamedAttribute> at = load->getAttrs();
1029       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1030           load, ty, adaptor.getOperands(), at);
1031     }
1032     return success();
1033   }
1034 };
1035 
1036 /// Lower `fir.select_type` to LLVM IR dialect.
1037 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1038   using FIROpConversion::FIROpConversion;
1039 
1040   mlir::LogicalResult
1041   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1042                   mlir::ConversionPatternRewriter &rewriter) const override {
1043     return rewriter.notifyMatchFailure(
1044         select, "fir.select_type codegen is not implemented yet");
1045   }
1046 };
1047 
1048 /// conversion of fir::SelectRankOp to an if-then-else ladder
1049 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1050   using FIROpConversion::FIROpConversion;
1051 
1052   mlir::LogicalResult
1053   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1054                   mlir::ConversionPatternRewriter &rewriter) const override {
1055     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1056     return success();
1057   }
1058 };
1059 
1060 /// `fir.store` --> `llvm.store`
1061 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1062   using FIROpConversion::FIROpConversion;
1063 
1064   mlir::LogicalResult
1065   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1066                   mlir::ConversionPatternRewriter &rewriter) const override {
1067     if (store.value().getType().isa<fir::BoxType>()) {
1068       // fir.box value is actually in memory, load it first before storing it.
1069       mlir::Location loc = store.getLoc();
1070       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1071       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1072           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1073           adaptor.getOperands()[0]);
1074       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1075           store, val, adaptor.getOperands()[1]);
1076     } else {
1077       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1078           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1079     }
1080     return success();
1081   }
1082 };
1083 
1084 /// convert to LLVM IR dialect `undef`
1085 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1086   using FIROpConversion::FIROpConversion;
1087 
1088   mlir::LogicalResult
1089   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1090                   mlir::ConversionPatternRewriter &rewriter) const override {
1091     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1092         undef, convertType(undef.getType()));
1093     return success();
1094   }
1095 };
1096 
1097 /// `fir.unreachable` --> `llvm.unreachable`
1098 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1099   using FIROpConversion::FIROpConversion;
1100 
1101   mlir::LogicalResult
1102   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1103                   mlir::ConversionPatternRewriter &rewriter) const override {
1104     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1105     return success();
1106   }
1107 };
1108 
1109 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1110   using FIROpConversion::FIROpConversion;
1111 
1112   mlir::LogicalResult
1113   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1114                   mlir::ConversionPatternRewriter &rewriter) const override {
1115     auto ty = convertType(zero.getType());
1116     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1117       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1118     } else if (ty.isa<mlir::IntegerType>()) {
1119       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1120           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1121     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1122       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1123           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1124     } else {
1125       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1126       return rewriter.notifyMatchFailure(
1127           zero,
1128           "conversion of fir.zero with aggregate type not implemented yet");
1129     }
1130     return success();
1131   }
1132 };
1133 
1134 // Code shared between insert_value and extract_value Ops.
1135 struct ValueOpCommon {
1136   // Translate the arguments pertaining to any multidimensional array to
1137   // row-major order for LLVM-IR.
1138   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1139                          mlir::Type ty) {
1140     assert(ty && "type is null");
1141     const auto end = attrs.size();
1142     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1143       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1144         const auto dim = getDimension(seq);
1145         if (dim > 1) {
1146           auto ub = std::min(i + dim, end);
1147           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1148           i += dim - 1;
1149         }
1150         ty = getArrayElementType(seq);
1151       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1152         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1153       } else {
1154         llvm_unreachable("index into invalid type");
1155       }
1156     }
1157   }
1158 
1159   static llvm::SmallVector<mlir::Attribute>
1160   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1161                  mlir::ArrayAttr arrAttr) {
1162     llvm::SmallVector<mlir::Attribute> attrs;
1163     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1164       if (i->isa<mlir::IntegerAttr>()) {
1165         attrs.push_back(*i);
1166       } else {
1167         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1168         ++i;
1169         auto ty = i->cast<mlir::TypeAttr>().getValue();
1170         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1171         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1172       }
1173     }
1174     return attrs;
1175   }
1176 
1177 private:
1178   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1179     unsigned result = 1;
1180     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1181          eleTy;
1182          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1183       ++result;
1184     return result;
1185   }
1186 
1187   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1188     auto eleTy = ty.getElementType();
1189     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1190       eleTy = arrTy.getElementType();
1191     return eleTy;
1192   }
1193 };
1194 
1195 /// Extract a subobject value from an ssa-value of aggregate type
1196 struct ExtractValueOpConversion
1197     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1198       public ValueOpCommon {
1199   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1200 
1201   mlir::LogicalResult
1202   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1203             mlir::ConversionPatternRewriter &rewriter) const override {
1204     auto attrs = collectIndices(rewriter, extractVal.coor());
1205     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1206     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1207     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1208         extractVal, ty, adaptor.getOperands()[0], position);
1209     return success();
1210   }
1211 };
1212 
1213 /// InsertValue is the generalized instruction for the composition of new
1214 /// aggregate type values.
1215 struct InsertValueOpConversion
1216     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1217       public ValueOpCommon {
1218   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1219 
1220   mlir::LogicalResult
1221   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1222             mlir::ConversionPatternRewriter &rewriter) const override {
1223     auto attrs = collectIndices(rewriter, insertVal.coor());
1224     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1225     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1226     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1227         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1228         position);
1229     return success();
1230   }
1231 };
1232 
1233 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1234 struct InsertOnRangeOpConversion
1235     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1236   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1237 
1238   // Increments an array of subscripts in a row major fasion.
1239   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1240                            SmallVector<uint64_t> &subscripts) const {
1241     for (size_t i = dims.size(); i > 0; --i) {
1242       if (++subscripts[i - 1] < dims[i - 1]) {
1243         return;
1244       }
1245       subscripts[i - 1] = 0;
1246     }
1247   }
1248 
1249   mlir::LogicalResult
1250   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1251             mlir::ConversionPatternRewriter &rewriter) const override {
1252 
1253     llvm::SmallVector<uint64_t> dims;
1254     auto type = adaptor.getOperands()[0].getType();
1255 
1256     // Iteratively extract the array dimensions from the type.
1257     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1258       dims.push_back(t.getNumElements());
1259       type = t.getElementType();
1260     }
1261 
1262     SmallVector<uint64_t> lBounds;
1263     SmallVector<uint64_t> uBounds;
1264 
1265     // Extract integer value from the attribute
1266     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1267         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1268           return a.cast<IntegerAttr>().getInt();
1269         }));
1270 
1271     // Unzip the upper and lower bound and convert to a row major format.
1272     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1273       uBounds.push_back(*i++);
1274       lBounds.push_back(*i);
1275     }
1276 
1277     auto &subscripts = lBounds;
1278     auto loc = range.getLoc();
1279     mlir::Value lastOp = adaptor.getOperands()[0];
1280     mlir::Value insertVal = adaptor.getOperands()[1];
1281 
1282     auto i64Ty = rewriter.getI64Type();
1283     while (subscripts != uBounds) {
1284       // Convert uint64_t's to Attribute's.
1285       SmallVector<mlir::Attribute> subscriptAttrs;
1286       for (const auto &subscript : subscripts)
1287         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1288       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1289           loc, ty, lastOp, insertVal,
1290           ArrayAttr::get(range.getContext(), subscriptAttrs));
1291 
1292       incrementSubscripts(dims, subscripts);
1293     }
1294 
1295     // Convert uint64_t's to Attribute's.
1296     SmallVector<mlir::Attribute> subscriptAttrs;
1297     for (const auto &subscript : subscripts)
1298       subscriptAttrs.push_back(
1299           IntegerAttr::get(rewriter.getI64Type(), subscript));
1300     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1301 
1302     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1303         range, ty, lastOp, insertVal,
1304         ArrayAttr::get(range.getContext(), arrayRef));
1305 
1306     return success();
1307   }
1308 };
1309 
1310 //
1311 // Primitive operations on Complex types
1312 //
1313 
1314 /// Generate inline code for complex addition/subtraction
1315 template <typename LLVMOP, typename OPTY>
1316 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1317                                      mlir::ConversionPatternRewriter &rewriter,
1318                                      fir::LLVMTypeConverter &lowering) {
1319   mlir::Value a = opnds[0];
1320   mlir::Value b = opnds[1];
1321   auto loc = sumop.getLoc();
1322   auto ctx = sumop.getContext();
1323   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1324   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1325   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1326   mlir::Type ty = lowering.convertType(sumop.getType());
1327   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1328   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1329   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1330   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1331   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1332   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1333   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1334   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1335   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1336 }
1337 
1338 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1339   using FIROpConversion::FIROpConversion;
1340 
1341   mlir::LogicalResult
1342   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1343                   mlir::ConversionPatternRewriter &rewriter) const override {
1344     // given: (x + iy) + (x' + iy')
1345     // result: (x + x') + i(y + y')
1346     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1347                                             rewriter, lowerTy());
1348     rewriter.replaceOp(addc, r.getResult());
1349     return success();
1350   }
1351 };
1352 
1353 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1354   using FIROpConversion::FIROpConversion;
1355 
1356   mlir::LogicalResult
1357   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1358                   mlir::ConversionPatternRewriter &rewriter) const override {
1359     // given: (x + iy) - (x' + iy')
1360     // result: (x - x') + i(y - y')
1361     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1362                                             rewriter, lowerTy());
1363     rewriter.replaceOp(subc, r.getResult());
1364     return success();
1365   }
1366 };
1367 
1368 /// Inlined complex multiply
1369 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1370   using FIROpConversion::FIROpConversion;
1371 
1372   mlir::LogicalResult
1373   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1374                   mlir::ConversionPatternRewriter &rewriter) const override {
1375     // TODO: Can we use a call to __muldc3 ?
1376     // given: (x + iy) * (x' + iy')
1377     // result: (xx'-yy')+i(xy'+yx')
1378     mlir::Value a = adaptor.getOperands()[0];
1379     mlir::Value b = adaptor.getOperands()[1];
1380     auto loc = mulc.getLoc();
1381     auto *ctx = mulc.getContext();
1382     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1383     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1384     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1385     mlir::Type ty = convertType(mulc.getType());
1386     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1387     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1388     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1389     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1390     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1391     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1392     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1393     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1394     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1395     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1396     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1397     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1398     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1399     rewriter.replaceOp(mulc, r0.getResult());
1400     return success();
1401   }
1402 };
1403 
1404 /// Inlined complex division
1405 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1406   using FIROpConversion::FIROpConversion;
1407 
1408   mlir::LogicalResult
1409   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1410                   mlir::ConversionPatternRewriter &rewriter) const override {
1411     // TODO: Can we use a call to __divdc3 instead?
1412     // Just generate inline code for now.
1413     // given: (x + iy) / (x' + iy')
1414     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1415     mlir::Value a = adaptor.getOperands()[0];
1416     mlir::Value b = adaptor.getOperands()[1];
1417     auto loc = divc.getLoc();
1418     auto *ctx = divc.getContext();
1419     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1420     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1421     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1422     mlir::Type ty = convertType(divc.getType());
1423     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1424     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1425     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1426     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1427     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1428     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1429     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1430     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1431     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1432     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1433     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1434     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1435     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1436     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1437     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1438     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1439     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1440     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1441     rewriter.replaceOp(divc, r0.getResult());
1442     return success();
1443   }
1444 };
1445 
1446 /// Inlined complex negation
1447 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1448   using FIROpConversion::FIROpConversion;
1449 
1450   mlir::LogicalResult
1451   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1452                   mlir::ConversionPatternRewriter &rewriter) const override {
1453     // given: -(x + iy)
1454     // result: -x - iy
1455     auto *ctxt = neg.getContext();
1456     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1457     auto ty = convertType(neg.getType());
1458     auto loc = neg.getLoc();
1459     mlir::Value o0 = adaptor.getOperands()[0];
1460     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1461     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1462     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1463     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1464     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1465     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1466     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1467     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1468     return success();
1469   }
1470 };
1471 
1472 /// `fir.is_present` -->
1473 /// ```
1474 ///  %0 = llvm.mlir.constant(0 : i64)
1475 ///  %1 = llvm.ptrtoint %0
1476 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1477 /// ```
1478 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1479   using FIROpConversion::FIROpConversion;
1480 
1481   mlir::LogicalResult
1482   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1483                   mlir::ConversionPatternRewriter &rewriter) const override {
1484     mlir::Type idxTy = lowerTy().indexType();
1485     mlir::Location loc = isPresent.getLoc();
1486     auto ptr = adaptor.getOperands()[0];
1487 
1488     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1489       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1490       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1491 
1492       mlir::Type ty = structTy.getBody()[0];
1493       mlir::MLIRContext *ctx = isPresent.getContext();
1494       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1495       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1496     }
1497     mlir::LLVM::ConstantOp c0 =
1498         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1499     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1500     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1501         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1502 
1503     return success();
1504   }
1505 };
1506 
1507 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1508 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1509 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1510 /// element is the length of the character buffer (`#n`).
1511 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1512   using FIROpConversion::FIROpConversion;
1513 
1514   mlir::LogicalResult
1515   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1516                   mlir::ConversionPatternRewriter &rewriter) const override {
1517     mlir::ValueRange operands = adaptor.getOperands();
1518     MLIRContext *ctx = emboxChar.getContext();
1519 
1520     mlir::Value charBuffer = operands[0];
1521     mlir::Value charBufferLen = operands[1];
1522 
1523     mlir::Location loc = emboxChar.getLoc();
1524     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1525     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1526 
1527     mlir::Type lenTy =
1528         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1529     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1530 
1531     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1532     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1533     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1534         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1535     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1536         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1537 
1538     return success();
1539   }
1540 };
1541 
1542 /// Construct an `llvm.extractvalue` instruction. It will return value at
1543 /// element \p x from  \p tuple.
1544 mlir::LLVM::ExtractValueOp
1545 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
1546                          mlir::ConversionPatternRewriter &rewriter,
1547                          mlir::MLIRContext *ctx, int x) {
1548   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
1549   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
1550   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
1551 }
1552 
1553 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
1554 /// boxchar.
1555 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
1556   using FIROpConversion::FIROpConversion;
1557 
1558   mlir::LogicalResult
1559   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
1560                   mlir::ConversionPatternRewriter &rewriter) const override {
1561     mlir::Value boxChar = adaptor.getOperands()[0];
1562     mlir::Location loc = boxChar.getLoc();
1563     mlir::MLIRContext *ctx = boxChar.getContext();
1564     mlir::Type returnValTy = boxCharLen.getResult().getType();
1565 
1566     constexpr int boxcharLenIdx = 1;
1567     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
1568         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
1569     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
1570     rewriter.replaceOp(boxCharLen, lenAfterCast);
1571 
1572     return success();
1573   }
1574 };
1575 
1576 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
1577 /// the character buffer and one for the buffer length.
1578 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
1579   using FIROpConversion::FIROpConversion;
1580 
1581   mlir::LogicalResult
1582   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
1583                   mlir::ConversionPatternRewriter &rewriter) const override {
1584     MLIRContext *ctx = unboxchar.getContext();
1585 
1586     mlir::Type lenTy = convertType(unboxchar.getType(1));
1587     mlir::Value tuple = adaptor.getOperands()[0];
1588     mlir::Type tupleTy = tuple.getType();
1589 
1590     mlir::Location loc = unboxchar.getLoc();
1591     mlir::Value ptrToBuffer =
1592         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
1593 
1594     mlir::LLVM::ExtractValueOp len =
1595         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
1596     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
1597 
1598     rewriter.replaceOp(unboxchar,
1599                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
1600     return success();
1601   }
1602 };
1603 
1604 } // namespace
1605 
1606 namespace {
1607 /// Convert FIR dialect to LLVM dialect
1608 ///
1609 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1610 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1611 ///
1612 /// This pass is not complete yet. We are upstreaming it in small patches.
1613 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1614 public:
1615   mlir::ModuleOp getModule() { return getOperation(); }
1616 
1617   void runOnOperation() override final {
1618     auto mod = getModule();
1619     if (!forcedTargetTriple.empty()) {
1620       fir::setTargetTriple(mod, forcedTargetTriple);
1621     }
1622 
1623     auto *context = getModule().getContext();
1624     fir::LLVMTypeConverter typeConverter{getModule()};
1625     mlir::OwningRewritePatternList pattern(context);
1626     pattern.insert<
1627         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1628         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
1629         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
1630         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion,
1631         CallOpConversion, CmpcOpConversion, ConvertOpConversion,
1632         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
1633         DivcOpConversion, EmboxCharOpConversion, ExtractValueOpConversion,
1634         HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion,
1635         GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion,
1636         IsPresentOpConversion, LoadOpConversion, NegcOpConversion,
1637         MulcOpConversion, SelectCaseOpConversion, SelectOpConversion,
1638         SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion,
1639         StringLitOpConversion, SubcOpConversion, UnboxCharOpConversion,
1640         UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
1641         typeConverter);
1642     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1643     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1644                                                             pattern);
1645     mlir::ConversionTarget target{*context};
1646     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1647 
1648     // required NOPs for applying a full conversion
1649     target.addLegalOp<mlir::ModuleOp>();
1650 
1651     // apply the patterns
1652     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1653                                                std::move(pattern)))) {
1654       signalPassFailure();
1655     }
1656   }
1657 };
1658 } // namespace
1659 
1660 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1661   return std::make_unique<FIRToLLVMLowering>();
1662 }
1663