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 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
502 /// descriptor from the box.
503 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
504   using FIROpConversion::FIROpConversion;
505 
506   mlir::LogicalResult
507   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
508                   mlir::ConversionPatternRewriter &rewriter) const override {
509     mlir::Value box = adaptor.getOperands()[0];
510     auto loc = boxtypedesc.getLoc();
511     mlir::Type typeTy =
512         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
513     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
514     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
515     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
516                                                         result);
517     return success();
518   }
519 };
520 
521 // `fir.call` -> `llvm.call`
522 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
523   using FIROpConversion::FIROpConversion;
524 
525   mlir::LogicalResult
526   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
527                   mlir::ConversionPatternRewriter &rewriter) const override {
528     SmallVector<mlir::Type> resultTys;
529     for (auto r : call.getResults())
530       resultTys.push_back(convertType(r.getType()));
531     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
532         call, resultTys, adaptor.getOperands(), call->getAttrs());
533     return success();
534   }
535 };
536 
537 static mlir::Type getComplexEleTy(mlir::Type complex) {
538   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
539     return cc.getElementType();
540   return complex.cast<fir::ComplexType>().getElementType();
541 }
542 
543 /// Compare complex values
544 ///
545 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
546 ///
547 /// For completeness, all other comparison are done on the real component only.
548 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
549   using FIROpConversion::FIROpConversion;
550 
551   mlir::LogicalResult
552   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
553                   mlir::ConversionPatternRewriter &rewriter) const override {
554     mlir::ValueRange operands = adaptor.getOperands();
555     mlir::MLIRContext *ctxt = cmp.getContext();
556     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
557     mlir::Type resTy = convertType(cmp.getType());
558     mlir::Location loc = cmp.getLoc();
559     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
560     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
561                                        loc, eleTy, operands[0], pos0),
562                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
563                                        loc, eleTy, operands[1], pos0)};
564     auto rcp =
565         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
566     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
567     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
568                                        loc, eleTy, operands[0], pos1),
569                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
570                                        loc, eleTy, operands[1], pos1)};
571     auto icp =
572         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
573     SmallVector<mlir::Value, 2> cp{rcp, icp};
574     switch (cmp.getPredicate()) {
575     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
576       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
577       break;
578     case mlir::arith::CmpFPredicate::UNE: // .NE.
579       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
580       break;
581     default:
582       rewriter.replaceOp(cmp, rcp.getResult());
583       break;
584     }
585     return success();
586   }
587 };
588 
589 /// convert value of from-type to value of to-type
590 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
591   using FIROpConversion::FIROpConversion;
592 
593   static bool isFloatingPointTy(mlir::Type ty) {
594     return ty.isa<mlir::FloatType>();
595   }
596 
597   mlir::LogicalResult
598   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
599                   mlir::ConversionPatternRewriter &rewriter) const override {
600     auto fromTy = convertType(convert.value().getType());
601     auto toTy = convertType(convert.res().getType());
602     mlir::Value op0 = adaptor.getOperands()[0];
603     if (fromTy == toTy) {
604       rewriter.replaceOp(convert, op0);
605       return success();
606     }
607     auto loc = convert.getLoc();
608     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
609                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
610       if (fromBits == toBits) {
611         // TODO: Converting between two floating-point representations with the
612         // same bitwidth is not allowed for now.
613         mlir::emitError(loc,
614                         "cannot implicitly convert between two floating-point "
615                         "representations of the same bitwidth");
616         return {};
617       }
618       if (fromBits > toBits)
619         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
620       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
621     };
622     // Complex to complex conversion.
623     if (fir::isa_complex(convert.value().getType()) &&
624         fir::isa_complex(convert.res().getType())) {
625       // Special case: handle the conversion of a complex such that both the
626       // real and imaginary parts are converted together.
627       auto zero = mlir::ArrayAttr::get(convert.getContext(),
628                                        rewriter.getI32IntegerAttr(0));
629       auto one = mlir::ArrayAttr::get(convert.getContext(),
630                                       rewriter.getI32IntegerAttr(1));
631       auto ty = convertType(getComplexEleTy(convert.value().getType()));
632       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
633       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
634       auto nt = convertType(getComplexEleTy(convert.res().getType()));
635       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
636       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
637       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
638       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
639       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
640       auto i1 =
641           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
642       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
643                                                              ic, one);
644       return mlir::success();
645     }
646     // Floating point to floating point conversion.
647     if (isFloatingPointTy(fromTy)) {
648       if (isFloatingPointTy(toTy)) {
649         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
650         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
651         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
652         rewriter.replaceOp(convert, v);
653         return mlir::success();
654       }
655       if (toTy.isa<mlir::IntegerType>()) {
656         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
657         return mlir::success();
658       }
659     } else if (fromTy.isa<mlir::IntegerType>()) {
660       // Integer to integer conversion.
661       if (toTy.isa<mlir::IntegerType>()) {
662         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
663         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
664         assert(fromBits != toBits);
665         if (fromBits > toBits) {
666           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
667           return mlir::success();
668         }
669         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
670         return mlir::success();
671       }
672       // Integer to floating point conversion.
673       if (isFloatingPointTy(toTy)) {
674         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
675         return mlir::success();
676       }
677       // Integer to pointer conversion.
678       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
679         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
680         return mlir::success();
681       }
682     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
683       // Pointer to integer conversion.
684       if (toTy.isa<mlir::IntegerType>()) {
685         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
686         return mlir::success();
687       }
688       // Pointer to pointer conversion.
689       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
690         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
691         return mlir::success();
692       }
693     }
694     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
695   }
696 };
697 
698 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
699 /// table.
700 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
701   using FIROpConversion::FIROpConversion;
702 
703   mlir::LogicalResult
704   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
705                   mlir::ConversionPatternRewriter &rewriter) const override {
706     return rewriter.notifyMatchFailure(
707         dispatch, "fir.dispatch codegen is not implemented yet");
708   }
709 };
710 
711 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
712 /// derived type.
713 struct DispatchTableOpConversion
714     : public FIROpConversion<fir::DispatchTableOp> {
715   using FIROpConversion::FIROpConversion;
716 
717   mlir::LogicalResult
718   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
719                   mlir::ConversionPatternRewriter &rewriter) const override {
720     return rewriter.notifyMatchFailure(
721         dispTab, "fir.dispatch_table codegen is not implemented yet");
722   }
723 };
724 
725 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
726 /// method-name to a function.
727 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
728   using FIROpConversion::FIROpConversion;
729 
730   mlir::LogicalResult
731   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
732                   mlir::ConversionPatternRewriter &rewriter) const override {
733     return rewriter.notifyMatchFailure(
734         dtEnt, "fir.dt_entry codegen is not implemented yet");
735   }
736 };
737 
738 /// Lower `fir.global_len` operation.
739 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
740   using FIROpConversion::FIROpConversion;
741 
742   mlir::LogicalResult
743   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
744                   mlir::ConversionPatternRewriter &rewriter) const override {
745     return rewriter.notifyMatchFailure(
746         globalLen, "fir.global_len codegen is not implemented yet");
747   }
748 };
749 
750 /// Lower `fir.gentypedesc` to a global constant.
751 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
752   using FIROpConversion::FIROpConversion;
753 
754   mlir::LogicalResult
755   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
756                   mlir::ConversionPatternRewriter &rewriter) const override {
757     return rewriter.notifyMatchFailure(
758         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
759   }
760 };
761 
762 /// Lower `fir.has_value` operation to `llvm.return` operation.
763 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
764   using FIROpConversion::FIROpConversion;
765 
766   mlir::LogicalResult
767   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
768                   mlir::ConversionPatternRewriter &rewriter) const override {
769     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
770     return success();
771   }
772 };
773 
774 /// Lower `fir.global` operation to `llvm.global` operation.
775 /// `fir.insert_on_range` operations are replaced with constant dense attribute
776 /// if they are applied on the full range.
777 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
778   using FIROpConversion::FIROpConversion;
779 
780   mlir::LogicalResult
781   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
782                   mlir::ConversionPatternRewriter &rewriter) const override {
783     auto tyAttr = convertType(global.getType());
784     if (global.getType().isa<fir::BoxType>())
785       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
786     auto loc = global.getLoc();
787     mlir::Attribute initAttr{};
788     if (global.initVal())
789       initAttr = global.initVal().getValue();
790     auto linkage = convertLinkage(global.linkName());
791     auto isConst = global.constant().hasValue();
792     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
793         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
794     auto &gr = g.getInitializerRegion();
795     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
796     if (!gr.empty()) {
797       // Replace insert_on_range with a constant dense attribute if the
798       // initialization is on the full range.
799       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
800       for (auto insertOp : insertOnRangeOps) {
801         if (isFullRange(insertOp.coor(), insertOp.getType())) {
802           auto seqTyAttr = convertType(insertOp.getType());
803           auto *op = insertOp.val().getDefiningOp();
804           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
805           if (!constant) {
806             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
807             if (!convertOp)
808               continue;
809             constant = cast<mlir::arith::ConstantOp>(
810                 convertOp.value().getDefiningOp());
811           }
812           mlir::Type vecType = mlir::VectorType::get(
813               insertOp.getType().getShape(), constant.getType());
814           auto denseAttr = mlir::DenseElementsAttr::get(
815               vecType.cast<ShapedType>(), constant.value());
816           rewriter.setInsertionPointAfter(insertOp);
817           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
818               insertOp, seqTyAttr, denseAttr);
819         }
820       }
821     }
822     rewriter.eraseOp(global);
823     return success();
824   }
825 
826   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
827     auto extents = seqTy.getShape();
828     if (indexes.size() / 2 != extents.size())
829       return false;
830     for (unsigned i = 0; i < indexes.size(); i += 2) {
831       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
832         return false;
833       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
834         return false;
835     }
836     return true;
837   }
838 
839   // TODO: String comparaison should be avoided. Replace linkName with an
840   // enumeration.
841   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
842     if (optLinkage.hasValue()) {
843       auto name = optLinkage.getValue();
844       if (name == "internal")
845         return mlir::LLVM::Linkage::Internal;
846       if (name == "linkonce")
847         return mlir::LLVM::Linkage::Linkonce;
848       if (name == "common")
849         return mlir::LLVM::Linkage::Common;
850       if (name == "weak")
851         return mlir::LLVM::Linkage::Weak;
852     }
853     return mlir::LLVM::Linkage::External;
854   }
855 };
856 
857 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
858                  Optional<mlir::ValueRange> destOps,
859                  mlir::ConversionPatternRewriter &rewriter,
860                  mlir::Block *newBlock) {
861   if (destOps.hasValue())
862     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
863                                           newBlock, mlir::ValueRange());
864   else
865     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
866 }
867 
868 template <typename A, typename B>
869 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
870              mlir::ConversionPatternRewriter &rewriter) {
871   if (destOps.hasValue())
872     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
873                                                   dest);
874   else
875     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
876 }
877 
878 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
879                        Optional<mlir::ValueRange> destOps,
880                        mlir::ConversionPatternRewriter &rewriter) {
881   auto *thisBlock = rewriter.getInsertionBlock();
882   auto *newBlock = createBlock(rewriter, dest);
883   rewriter.setInsertionPointToEnd(thisBlock);
884   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
885   rewriter.setInsertionPointToEnd(newBlock);
886 }
887 
888 /// Conversion of `fir.select_case`
889 ///
890 /// The `fir.select_case` operation is converted to a if-then-else ladder.
891 /// Depending on the case condition type, one or several comparison and
892 /// conditional branching can be generated.
893 ///
894 /// A a point value case such as `case(4)`, a lower bound case such as
895 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
896 /// simple comparison between the selector value and the constant value in the
897 /// case. The block associated with the case condition is then executed if
898 /// the comparison succeed otherwise it branch to the next block with the
899 /// comparison for the the next case conditon.
900 ///
901 /// A closed interval case condition such as `case(7:10)` is converted with a
902 /// first comparison and conditional branching for the lower bound. If
903 /// successful, it branch to a second block with the comparison for the
904 /// upper bound in the same case condition.
905 ///
906 /// TODO: lowering of CHARACTER type cases is not handled yet.
907 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
908   using FIROpConversion::FIROpConversion;
909 
910   mlir::LogicalResult
911   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
912                   mlir::ConversionPatternRewriter &rewriter) const override {
913     unsigned conds = caseOp.getNumConditions();
914     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
915     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
916     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
917     if (ty.isa<fir::CharacterType>())
918       return rewriter.notifyMatchFailure(caseOp,
919                                          "conversion of fir.select_case with "
920                                          "character type not implemented yet");
921     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
922     auto loc = caseOp.getLoc();
923     for (unsigned t = 0; t != conds; ++t) {
924       mlir::Block *dest = caseOp.getSuccessor(t);
925       llvm::Optional<mlir::ValueRange> destOps =
926           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
927       llvm::Optional<mlir::ValueRange> cmpOps =
928           *caseOp.getCompareOperands(adaptor.getOperands(), t);
929       mlir::Value caseArg = *(cmpOps.getValue().begin());
930       mlir::Attribute attr = cases[t];
931       if (attr.isa<fir::PointIntervalAttr>()) {
932         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
933             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
934         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
935         continue;
936       }
937       if (attr.isa<fir::LowerBoundAttr>()) {
938         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
939             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
940         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
941         continue;
942       }
943       if (attr.isa<fir::UpperBoundAttr>()) {
944         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
945             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
946         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
947         continue;
948       }
949       if (attr.isa<fir::ClosedIntervalAttr>()) {
950         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
951             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
952         auto *thisBlock = rewriter.getInsertionBlock();
953         auto *newBlock1 = createBlock(rewriter, dest);
954         auto *newBlock2 = createBlock(rewriter, dest);
955         rewriter.setInsertionPointToEnd(thisBlock);
956         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
957         rewriter.setInsertionPointToEnd(newBlock1);
958         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
959         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
960             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
961         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
962         rewriter.setInsertionPointToEnd(newBlock2);
963         continue;
964       }
965       assert(attr.isa<mlir::UnitAttr>());
966       assert((t + 1 == conds) && "unit must be last");
967       genBrOp(caseOp, dest, destOps, rewriter);
968     }
969     return success();
970   }
971 };
972 
973 template <typename OP>
974 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
975                            typename OP::Adaptor adaptor,
976                            mlir::ConversionPatternRewriter &rewriter) {
977   unsigned conds = select.getNumConditions();
978   auto cases = select.getCases().getValue();
979   mlir::Value selector = adaptor.selector();
980   auto loc = select.getLoc();
981   assert(conds > 0 && "select must have cases");
982 
983   llvm::SmallVector<mlir::Block *> destinations;
984   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
985   mlir::Block *defaultDestination;
986   mlir::ValueRange defaultOperands;
987   llvm::SmallVector<int32_t> caseValues;
988 
989   for (unsigned t = 0; t != conds; ++t) {
990     mlir::Block *dest = select.getSuccessor(t);
991     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
992     const mlir::Attribute &attr = cases[t];
993     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
994       destinations.push_back(dest);
995       destinationsOperands.push_back(destOps.hasValue() ? *destOps
996                                                         : ValueRange());
997       caseValues.push_back(intAttr.getInt());
998       continue;
999     }
1000     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1001     assert((t + 1 == conds) && "unit must be last");
1002     defaultDestination = dest;
1003     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1004   }
1005 
1006   // LLVM::SwitchOp takes a i32 type for the selector.
1007   if (select.getSelector().getType() != rewriter.getI32Type())
1008     selector =
1009         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1010 
1011   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1012       select, selector,
1013       /*defaultDestination=*/defaultDestination,
1014       /*defaultOperands=*/defaultOperands,
1015       /*caseValues=*/caseValues,
1016       /*caseDestinations=*/destinations,
1017       /*caseOperands=*/destinationsOperands,
1018       /*branchWeights=*/ArrayRef<int32_t>());
1019 }
1020 
1021 /// conversion of fir::SelectOp to an if-then-else ladder
1022 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1023   using FIROpConversion::FIROpConversion;
1024 
1025   mlir::LogicalResult
1026   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1027                   mlir::ConversionPatternRewriter &rewriter) const override {
1028     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1029     return success();
1030   }
1031 };
1032 
1033 /// `fir.load` --> `llvm.load`
1034 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1035   using FIROpConversion::FIROpConversion;
1036 
1037   mlir::LogicalResult
1038   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1039                   mlir::ConversionPatternRewriter &rewriter) const override {
1040     // fir.box is a special case because it is considered as an ssa values in
1041     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1042     // and fir.box end up being the same llvm types and loading a
1043     // fir.ref<fir.box> is actually a no op in LLVM.
1044     if (load.getType().isa<fir::BoxType>()) {
1045       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1046     } else {
1047       mlir::Type ty = convertType(load.getType());
1048       ArrayRef<NamedAttribute> at = load->getAttrs();
1049       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1050           load, ty, adaptor.getOperands(), at);
1051     }
1052     return success();
1053   }
1054 };
1055 
1056 /// Lower `fir.select_type` to LLVM IR dialect.
1057 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1058   using FIROpConversion::FIROpConversion;
1059 
1060   mlir::LogicalResult
1061   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1062                   mlir::ConversionPatternRewriter &rewriter) const override {
1063     return rewriter.notifyMatchFailure(
1064         select, "fir.select_type codegen is not implemented yet");
1065   }
1066 };
1067 
1068 /// conversion of fir::SelectRankOp to an if-then-else ladder
1069 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1070   using FIROpConversion::FIROpConversion;
1071 
1072   mlir::LogicalResult
1073   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1074                   mlir::ConversionPatternRewriter &rewriter) const override {
1075     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1076     return success();
1077   }
1078 };
1079 
1080 /// `fir.store` --> `llvm.store`
1081 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1082   using FIROpConversion::FIROpConversion;
1083 
1084   mlir::LogicalResult
1085   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1086                   mlir::ConversionPatternRewriter &rewriter) const override {
1087     if (store.value().getType().isa<fir::BoxType>()) {
1088       // fir.box value is actually in memory, load it first before storing it.
1089       mlir::Location loc = store.getLoc();
1090       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1091       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1092           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1093           adaptor.getOperands()[0]);
1094       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1095           store, val, adaptor.getOperands()[1]);
1096     } else {
1097       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1098           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1099     }
1100     return success();
1101   }
1102 };
1103 
1104 /// convert to LLVM IR dialect `undef`
1105 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1106   using FIROpConversion::FIROpConversion;
1107 
1108   mlir::LogicalResult
1109   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1110                   mlir::ConversionPatternRewriter &rewriter) const override {
1111     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1112         undef, convertType(undef.getType()));
1113     return success();
1114   }
1115 };
1116 
1117 /// `fir.unreachable` --> `llvm.unreachable`
1118 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1119   using FIROpConversion::FIROpConversion;
1120 
1121   mlir::LogicalResult
1122   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1123                   mlir::ConversionPatternRewriter &rewriter) const override {
1124     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1125     return success();
1126   }
1127 };
1128 
1129 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1130   using FIROpConversion::FIROpConversion;
1131 
1132   mlir::LogicalResult
1133   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1134                   mlir::ConversionPatternRewriter &rewriter) const override {
1135     auto ty = convertType(zero.getType());
1136     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1137       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1138     } else if (ty.isa<mlir::IntegerType>()) {
1139       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1140           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1141     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1142       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1143           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1144     } else {
1145       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1146       return rewriter.notifyMatchFailure(
1147           zero,
1148           "conversion of fir.zero with aggregate type not implemented yet");
1149     }
1150     return success();
1151   }
1152 };
1153 
1154 // Code shared between insert_value and extract_value Ops.
1155 struct ValueOpCommon {
1156   // Translate the arguments pertaining to any multidimensional array to
1157   // row-major order for LLVM-IR.
1158   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1159                          mlir::Type ty) {
1160     assert(ty && "type is null");
1161     const auto end = attrs.size();
1162     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1163       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1164         const auto dim = getDimension(seq);
1165         if (dim > 1) {
1166           auto ub = std::min(i + dim, end);
1167           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1168           i += dim - 1;
1169         }
1170         ty = getArrayElementType(seq);
1171       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1172         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1173       } else {
1174         llvm_unreachable("index into invalid type");
1175       }
1176     }
1177   }
1178 
1179   static llvm::SmallVector<mlir::Attribute>
1180   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1181                  mlir::ArrayAttr arrAttr) {
1182     llvm::SmallVector<mlir::Attribute> attrs;
1183     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1184       if (i->isa<mlir::IntegerAttr>()) {
1185         attrs.push_back(*i);
1186       } else {
1187         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1188         ++i;
1189         auto ty = i->cast<mlir::TypeAttr>().getValue();
1190         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1191         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1192       }
1193     }
1194     return attrs;
1195   }
1196 
1197 private:
1198   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1199     unsigned result = 1;
1200     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1201          eleTy;
1202          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1203       ++result;
1204     return result;
1205   }
1206 
1207   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1208     auto eleTy = ty.getElementType();
1209     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1210       eleTy = arrTy.getElementType();
1211     return eleTy;
1212   }
1213 };
1214 
1215 /// Extract a subobject value from an ssa-value of aggregate type
1216 struct ExtractValueOpConversion
1217     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1218       public ValueOpCommon {
1219   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1220 
1221   mlir::LogicalResult
1222   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1223             mlir::ConversionPatternRewriter &rewriter) const override {
1224     auto attrs = collectIndices(rewriter, extractVal.coor());
1225     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1226     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1227     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1228         extractVal, ty, adaptor.getOperands()[0], position);
1229     return success();
1230   }
1231 };
1232 
1233 /// InsertValue is the generalized instruction for the composition of new
1234 /// aggregate type values.
1235 struct InsertValueOpConversion
1236     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1237       public ValueOpCommon {
1238   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1239 
1240   mlir::LogicalResult
1241   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1242             mlir::ConversionPatternRewriter &rewriter) const override {
1243     auto attrs = collectIndices(rewriter, insertVal.coor());
1244     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1245     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1246     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1247         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1248         position);
1249     return success();
1250   }
1251 };
1252 
1253 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1254 struct InsertOnRangeOpConversion
1255     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1256   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1257 
1258   // Increments an array of subscripts in a row major fasion.
1259   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1260                            SmallVector<uint64_t> &subscripts) const {
1261     for (size_t i = dims.size(); i > 0; --i) {
1262       if (++subscripts[i - 1] < dims[i - 1]) {
1263         return;
1264       }
1265       subscripts[i - 1] = 0;
1266     }
1267   }
1268 
1269   mlir::LogicalResult
1270   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1271             mlir::ConversionPatternRewriter &rewriter) const override {
1272 
1273     llvm::SmallVector<uint64_t> dims;
1274     auto type = adaptor.getOperands()[0].getType();
1275 
1276     // Iteratively extract the array dimensions from the type.
1277     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1278       dims.push_back(t.getNumElements());
1279       type = t.getElementType();
1280     }
1281 
1282     SmallVector<uint64_t> lBounds;
1283     SmallVector<uint64_t> uBounds;
1284 
1285     // Extract integer value from the attribute
1286     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1287         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1288           return a.cast<IntegerAttr>().getInt();
1289         }));
1290 
1291     // Unzip the upper and lower bound and convert to a row major format.
1292     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1293       uBounds.push_back(*i++);
1294       lBounds.push_back(*i);
1295     }
1296 
1297     auto &subscripts = lBounds;
1298     auto loc = range.getLoc();
1299     mlir::Value lastOp = adaptor.getOperands()[0];
1300     mlir::Value insertVal = adaptor.getOperands()[1];
1301 
1302     auto i64Ty = rewriter.getI64Type();
1303     while (subscripts != uBounds) {
1304       // Convert uint64_t's to Attribute's.
1305       SmallVector<mlir::Attribute> subscriptAttrs;
1306       for (const auto &subscript : subscripts)
1307         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1308       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1309           loc, ty, lastOp, insertVal,
1310           ArrayAttr::get(range.getContext(), subscriptAttrs));
1311 
1312       incrementSubscripts(dims, subscripts);
1313     }
1314 
1315     // Convert uint64_t's to Attribute's.
1316     SmallVector<mlir::Attribute> subscriptAttrs;
1317     for (const auto &subscript : subscripts)
1318       subscriptAttrs.push_back(
1319           IntegerAttr::get(rewriter.getI64Type(), subscript));
1320     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1321 
1322     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1323         range, ty, lastOp, insertVal,
1324         ArrayAttr::get(range.getContext(), arrayRef));
1325 
1326     return success();
1327   }
1328 };
1329 
1330 //
1331 // Primitive operations on Complex types
1332 //
1333 
1334 /// Generate inline code for complex addition/subtraction
1335 template <typename LLVMOP, typename OPTY>
1336 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1337                                      mlir::ConversionPatternRewriter &rewriter,
1338                                      fir::LLVMTypeConverter &lowering) {
1339   mlir::Value a = opnds[0];
1340   mlir::Value b = opnds[1];
1341   auto loc = sumop.getLoc();
1342   auto ctx = sumop.getContext();
1343   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1344   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1345   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1346   mlir::Type ty = lowering.convertType(sumop.getType());
1347   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1348   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1349   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1350   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1351   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1352   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1353   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1354   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1355   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1356 }
1357 
1358 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1359   using FIROpConversion::FIROpConversion;
1360 
1361   mlir::LogicalResult
1362   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1363                   mlir::ConversionPatternRewriter &rewriter) const override {
1364     // given: (x + iy) + (x' + iy')
1365     // result: (x + x') + i(y + y')
1366     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1367                                             rewriter, lowerTy());
1368     rewriter.replaceOp(addc, r.getResult());
1369     return success();
1370   }
1371 };
1372 
1373 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1374   using FIROpConversion::FIROpConversion;
1375 
1376   mlir::LogicalResult
1377   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1378                   mlir::ConversionPatternRewriter &rewriter) const override {
1379     // given: (x + iy) - (x' + iy')
1380     // result: (x - x') + i(y - y')
1381     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1382                                             rewriter, lowerTy());
1383     rewriter.replaceOp(subc, r.getResult());
1384     return success();
1385   }
1386 };
1387 
1388 /// Inlined complex multiply
1389 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1390   using FIROpConversion::FIROpConversion;
1391 
1392   mlir::LogicalResult
1393   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1394                   mlir::ConversionPatternRewriter &rewriter) const override {
1395     // TODO: Can we use a call to __muldc3 ?
1396     // given: (x + iy) * (x' + iy')
1397     // result: (xx'-yy')+i(xy'+yx')
1398     mlir::Value a = adaptor.getOperands()[0];
1399     mlir::Value b = adaptor.getOperands()[1];
1400     auto loc = mulc.getLoc();
1401     auto *ctx = mulc.getContext();
1402     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1403     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1404     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1405     mlir::Type ty = convertType(mulc.getType());
1406     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1407     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1408     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1409     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1410     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1411     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1412     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1413     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1414     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1415     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1416     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1417     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1418     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1419     rewriter.replaceOp(mulc, r0.getResult());
1420     return success();
1421   }
1422 };
1423 
1424 /// Inlined complex division
1425 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1426   using FIROpConversion::FIROpConversion;
1427 
1428   mlir::LogicalResult
1429   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1430                   mlir::ConversionPatternRewriter &rewriter) const override {
1431     // TODO: Can we use a call to __divdc3 instead?
1432     // Just generate inline code for now.
1433     // given: (x + iy) / (x' + iy')
1434     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1435     mlir::Value a = adaptor.getOperands()[0];
1436     mlir::Value b = adaptor.getOperands()[1];
1437     auto loc = divc.getLoc();
1438     auto *ctx = divc.getContext();
1439     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1440     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1441     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1442     mlir::Type ty = convertType(divc.getType());
1443     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1444     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1445     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1446     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1447     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1448     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1449     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1450     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1451     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1452     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1453     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1454     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1455     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1456     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1457     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1458     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1459     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1460     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1461     rewriter.replaceOp(divc, r0.getResult());
1462     return success();
1463   }
1464 };
1465 
1466 /// Inlined complex negation
1467 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1468   using FIROpConversion::FIROpConversion;
1469 
1470   mlir::LogicalResult
1471   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1472                   mlir::ConversionPatternRewriter &rewriter) const override {
1473     // given: -(x + iy)
1474     // result: -x - iy
1475     auto *ctxt = neg.getContext();
1476     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1477     auto ty = convertType(neg.getType());
1478     auto loc = neg.getLoc();
1479     mlir::Value o0 = adaptor.getOperands()[0];
1480     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1481     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1482     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1483     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1484     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1485     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1486     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1487     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1488     return success();
1489   }
1490 };
1491 
1492 /// Conversion pattern for operation that must be dead. The information in these
1493 /// operations is used by other operation. At this point they should not have
1494 /// anymore uses.
1495 /// These operations are normally dead after the pre-codegen pass.
1496 template <typename FromOp>
1497 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
1498   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
1499       : FIROpConversion<FromOp>(lowering) {}
1500   using OpAdaptor = typename FromOp::Adaptor;
1501 
1502   mlir::LogicalResult
1503   matchAndRewrite(FromOp op, OpAdaptor adaptor,
1504                   mlir::ConversionPatternRewriter &rewriter) const final {
1505     if (!op->getUses().empty())
1506       return rewriter.notifyMatchFailure(op, "op must be dead");
1507     rewriter.eraseOp(op);
1508     return success();
1509   }
1510 };
1511 
1512 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
1513   using MustBeDeadConversion::MustBeDeadConversion;
1514 };
1515 
1516 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
1517   using MustBeDeadConversion::MustBeDeadConversion;
1518 };
1519 
1520 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
1521   using MustBeDeadConversion::MustBeDeadConversion;
1522 };
1523 
1524 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
1525   using MustBeDeadConversion::MustBeDeadConversion;
1526 };
1527 
1528 /// `fir.is_present` -->
1529 /// ```
1530 ///  %0 = llvm.mlir.constant(0 : i64)
1531 ///  %1 = llvm.ptrtoint %0
1532 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1533 /// ```
1534 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1535   using FIROpConversion::FIROpConversion;
1536 
1537   mlir::LogicalResult
1538   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1539                   mlir::ConversionPatternRewriter &rewriter) const override {
1540     mlir::Type idxTy = lowerTy().indexType();
1541     mlir::Location loc = isPresent.getLoc();
1542     auto ptr = adaptor.getOperands()[0];
1543 
1544     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1545       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1546       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1547 
1548       mlir::Type ty = structTy.getBody()[0];
1549       mlir::MLIRContext *ctx = isPresent.getContext();
1550       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1551       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1552     }
1553     mlir::LLVM::ConstantOp c0 =
1554         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1555     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1556     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1557         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1558 
1559     return success();
1560   }
1561 };
1562 
1563 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1564 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1565 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1566 /// element is the length of the character buffer (`#n`).
1567 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1568   using FIROpConversion::FIROpConversion;
1569 
1570   mlir::LogicalResult
1571   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1572                   mlir::ConversionPatternRewriter &rewriter) const override {
1573     mlir::ValueRange operands = adaptor.getOperands();
1574     MLIRContext *ctx = emboxChar.getContext();
1575 
1576     mlir::Value charBuffer = operands[0];
1577     mlir::Value charBufferLen = operands[1];
1578 
1579     mlir::Location loc = emboxChar.getLoc();
1580     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1581     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1582 
1583     mlir::Type lenTy =
1584         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1585     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1586 
1587     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1588     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1589     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1590         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1591     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1592         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1593 
1594     return success();
1595   }
1596 };
1597 
1598 /// Construct an `llvm.extractvalue` instruction. It will return value at
1599 /// element \p x from  \p tuple.
1600 mlir::LLVM::ExtractValueOp
1601 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
1602                          mlir::ConversionPatternRewriter &rewriter,
1603                          mlir::MLIRContext *ctx, int x) {
1604   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
1605   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
1606   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
1607 }
1608 
1609 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
1610 /// boxchar.
1611 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
1612   using FIROpConversion::FIROpConversion;
1613 
1614   mlir::LogicalResult
1615   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
1616                   mlir::ConversionPatternRewriter &rewriter) const override {
1617     mlir::Value boxChar = adaptor.getOperands()[0];
1618     mlir::Location loc = boxChar.getLoc();
1619     mlir::MLIRContext *ctx = boxChar.getContext();
1620     mlir::Type returnValTy = boxCharLen.getResult().getType();
1621 
1622     constexpr int boxcharLenIdx = 1;
1623     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
1624         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
1625     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
1626     rewriter.replaceOp(boxCharLen, lenAfterCast);
1627 
1628     return success();
1629   }
1630 };
1631 
1632 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
1633 /// the character buffer and one for the buffer length.
1634 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
1635   using FIROpConversion::FIROpConversion;
1636 
1637   mlir::LogicalResult
1638   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
1639                   mlir::ConversionPatternRewriter &rewriter) const override {
1640     MLIRContext *ctx = unboxchar.getContext();
1641 
1642     mlir::Type lenTy = convertType(unboxchar.getType(1));
1643     mlir::Value tuple = adaptor.getOperands()[0];
1644     mlir::Type tupleTy = tuple.getType();
1645 
1646     mlir::Location loc = unboxchar.getLoc();
1647     mlir::Value ptrToBuffer =
1648         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
1649 
1650     mlir::LLVM::ExtractValueOp len =
1651         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
1652     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
1653 
1654     rewriter.replaceOp(unboxchar,
1655                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
1656     return success();
1657   }
1658 };
1659 
1660 } // namespace
1661 
1662 namespace {
1663 /// Convert FIR dialect to LLVM dialect
1664 ///
1665 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1666 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1667 ///
1668 /// This pass is not complete yet. We are upstreaming it in small patches.
1669 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1670 public:
1671   mlir::ModuleOp getModule() { return getOperation(); }
1672 
1673   void runOnOperation() override final {
1674     auto mod = getModule();
1675     if (!forcedTargetTriple.empty()) {
1676       fir::setTargetTriple(mod, forcedTargetTriple);
1677     }
1678 
1679     auto *context = getModule().getContext();
1680     fir::LLVMTypeConverter typeConverter{getModule()};
1681     mlir::OwningRewritePatternList pattern(context);
1682     pattern.insert<
1683         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1684         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
1685         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
1686         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion,
1687         BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion,
1688         ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion,
1689         DTEntryOpConversion, DivcOpConversion, EmboxCharOpConversion,
1690         ExtractValueOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
1691         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
1692         InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion,
1693         NegcOpConversion, MulcOpConversion, SelectCaseOpConversion,
1694         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
1695         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
1696         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
1697         SubcOpConversion, UnboxCharOpConversion, UndefOpConversion,
1698         UnreachableOpConversion, ZeroOpConversion>(typeConverter);
1699     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1700     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1701                                                             pattern);
1702     mlir::ConversionTarget target{*context};
1703     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1704 
1705     // required NOPs for applying a full conversion
1706     target.addLegalOp<mlir::ModuleOp>();
1707 
1708     // apply the patterns
1709     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1710                                                std::move(pattern)))) {
1711       signalPassFailure();
1712     }
1713   }
1714 };
1715 } // namespace
1716 
1717 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1718   return std::make_unique<FIRToLLVMLowering>();
1719 }
1720