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