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 // `fir.call` -> `llvm.call`
469 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
470   using FIROpConversion::FIROpConversion;
471 
472   mlir::LogicalResult
473   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
474                   mlir::ConversionPatternRewriter &rewriter) const override {
475     SmallVector<mlir::Type> resultTys;
476     for (auto r : call.getResults())
477       resultTys.push_back(convertType(r.getType()));
478     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
479         call, resultTys, adaptor.getOperands(), call->getAttrs());
480     return success();
481   }
482 };
483 
484 static mlir::Type getComplexEleTy(mlir::Type complex) {
485   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
486     return cc.getElementType();
487   return complex.cast<fir::ComplexType>().getElementType();
488 }
489 
490 /// convert value of from-type to value of to-type
491 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
492   using FIROpConversion::FIROpConversion;
493 
494   static bool isFloatingPointTy(mlir::Type ty) {
495     return ty.isa<mlir::FloatType>();
496   }
497 
498   mlir::LogicalResult
499   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
500                   mlir::ConversionPatternRewriter &rewriter) const override {
501     auto fromTy = convertType(convert.value().getType());
502     auto toTy = convertType(convert.res().getType());
503     mlir::Value op0 = adaptor.getOperands()[0];
504     if (fromTy == toTy) {
505       rewriter.replaceOp(convert, op0);
506       return success();
507     }
508     auto loc = convert.getLoc();
509     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
510                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
511       if (fromBits == toBits) {
512         // TODO: Converting between two floating-point representations with the
513         // same bitwidth is not allowed for now.
514         mlir::emitError(loc,
515                         "cannot implicitly convert between two floating-point "
516                         "representations of the same bitwidth");
517         return {};
518       }
519       if (fromBits > toBits)
520         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
521       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
522     };
523     // Complex to complex conversion.
524     if (fir::isa_complex(convert.value().getType()) &&
525         fir::isa_complex(convert.res().getType())) {
526       // Special case: handle the conversion of a complex such that both the
527       // real and imaginary parts are converted together.
528       auto zero = mlir::ArrayAttr::get(convert.getContext(),
529                                        rewriter.getI32IntegerAttr(0));
530       auto one = mlir::ArrayAttr::get(convert.getContext(),
531                                       rewriter.getI32IntegerAttr(1));
532       auto ty = convertType(getComplexEleTy(convert.value().getType()));
533       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
534       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
535       auto nt = convertType(getComplexEleTy(convert.res().getType()));
536       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
537       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
538       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
539       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
540       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
541       auto i1 =
542           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
543       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
544                                                              ic, one);
545       return mlir::success();
546     }
547     // Floating point to floating point conversion.
548     if (isFloatingPointTy(fromTy)) {
549       if (isFloatingPointTy(toTy)) {
550         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
551         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
552         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
553         rewriter.replaceOp(convert, v);
554         return mlir::success();
555       }
556       if (toTy.isa<mlir::IntegerType>()) {
557         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
558         return mlir::success();
559       }
560     } else if (fromTy.isa<mlir::IntegerType>()) {
561       // Integer to integer conversion.
562       if (toTy.isa<mlir::IntegerType>()) {
563         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
564         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
565         assert(fromBits != toBits);
566         if (fromBits > toBits) {
567           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
568           return mlir::success();
569         }
570         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
571         return mlir::success();
572       }
573       // Integer to floating point conversion.
574       if (isFloatingPointTy(toTy)) {
575         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
576         return mlir::success();
577       }
578       // Integer to pointer conversion.
579       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
580         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
581         return mlir::success();
582       }
583     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
584       // Pointer to integer conversion.
585       if (toTy.isa<mlir::IntegerType>()) {
586         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
587         return mlir::success();
588       }
589       // Pointer to pointer conversion.
590       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
591         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
592         return mlir::success();
593       }
594     }
595     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
596   }
597 };
598 
599 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
600 /// table.
601 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
602   using FIROpConversion::FIROpConversion;
603 
604   mlir::LogicalResult
605   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
606                   mlir::ConversionPatternRewriter &rewriter) const override {
607     return rewriter.notifyMatchFailure(
608         dispatch, "fir.dispatch codegen is not implemented yet");
609   }
610 };
611 
612 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
613 /// derived type.
614 struct DispatchTableOpConversion
615     : public FIROpConversion<fir::DispatchTableOp> {
616   using FIROpConversion::FIROpConversion;
617 
618   mlir::LogicalResult
619   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
620                   mlir::ConversionPatternRewriter &rewriter) const override {
621     return rewriter.notifyMatchFailure(
622         dispTab, "fir.dispatch_table codegen is not implemented yet");
623   }
624 };
625 
626 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
627 /// method-name to a function.
628 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
629   using FIROpConversion::FIROpConversion;
630 
631   mlir::LogicalResult
632   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
633                   mlir::ConversionPatternRewriter &rewriter) const override {
634     return rewriter.notifyMatchFailure(
635         dtEnt, "fir.dt_entry codegen is not implemented yet");
636   }
637 };
638 
639 /// Lower `fir.has_value` operation to `llvm.return` operation.
640 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
641   using FIROpConversion::FIROpConversion;
642 
643   mlir::LogicalResult
644   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
645                   mlir::ConversionPatternRewriter &rewriter) const override {
646     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
647     return success();
648   }
649 };
650 
651 /// Lower `fir.global` operation to `llvm.global` operation.
652 /// `fir.insert_on_range` operations are replaced with constant dense attribute
653 /// if they are applied on the full range.
654 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
655   using FIROpConversion::FIROpConversion;
656 
657   mlir::LogicalResult
658   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
659                   mlir::ConversionPatternRewriter &rewriter) const override {
660     auto tyAttr = convertType(global.getType());
661     if (global.getType().isa<fir::BoxType>())
662       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
663     auto loc = global.getLoc();
664     mlir::Attribute initAttr{};
665     if (global.initVal())
666       initAttr = global.initVal().getValue();
667     auto linkage = convertLinkage(global.linkName());
668     auto isConst = global.constant().hasValue();
669     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
670         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
671     auto &gr = g.getInitializerRegion();
672     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
673     if (!gr.empty()) {
674       // Replace insert_on_range with a constant dense attribute if the
675       // initialization is on the full range.
676       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
677       for (auto insertOp : insertOnRangeOps) {
678         if (isFullRange(insertOp.coor(), insertOp.getType())) {
679           auto seqTyAttr = convertType(insertOp.getType());
680           auto *op = insertOp.val().getDefiningOp();
681           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
682           if (!constant) {
683             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
684             if (!convertOp)
685               continue;
686             constant = cast<mlir::arith::ConstantOp>(
687                 convertOp.value().getDefiningOp());
688           }
689           mlir::Type vecType = mlir::VectorType::get(
690               insertOp.getType().getShape(), constant.getType());
691           auto denseAttr = mlir::DenseElementsAttr::get(
692               vecType.cast<ShapedType>(), constant.value());
693           rewriter.setInsertionPointAfter(insertOp);
694           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
695               insertOp, seqTyAttr, denseAttr);
696         }
697       }
698     }
699     rewriter.eraseOp(global);
700     return success();
701   }
702 
703   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
704     auto extents = seqTy.getShape();
705     if (indexes.size() / 2 != extents.size())
706       return false;
707     for (unsigned i = 0; i < indexes.size(); i += 2) {
708       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
709         return false;
710       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
711         return false;
712     }
713     return true;
714   }
715 
716   // TODO: String comparaison should be avoided. Replace linkName with an
717   // enumeration.
718   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
719     if (optLinkage.hasValue()) {
720       auto name = optLinkage.getValue();
721       if (name == "internal")
722         return mlir::LLVM::Linkage::Internal;
723       if (name == "linkonce")
724         return mlir::LLVM::Linkage::Linkonce;
725       if (name == "common")
726         return mlir::LLVM::Linkage::Common;
727       if (name == "weak")
728         return mlir::LLVM::Linkage::Weak;
729     }
730     return mlir::LLVM::Linkage::External;
731   }
732 };
733 
734 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
735                  Optional<mlir::ValueRange> destOps,
736                  mlir::ConversionPatternRewriter &rewriter,
737                  mlir::Block *newBlock) {
738   if (destOps.hasValue())
739     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
740                                           newBlock, mlir::ValueRange());
741   else
742     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
743 }
744 
745 template <typename A, typename B>
746 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
747              mlir::ConversionPatternRewriter &rewriter) {
748   if (destOps.hasValue())
749     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
750                                                   dest);
751   else
752     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
753 }
754 
755 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
756                        Optional<mlir::ValueRange> destOps,
757                        mlir::ConversionPatternRewriter &rewriter) {
758   auto *thisBlock = rewriter.getInsertionBlock();
759   auto *newBlock = createBlock(rewriter, dest);
760   rewriter.setInsertionPointToEnd(thisBlock);
761   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
762   rewriter.setInsertionPointToEnd(newBlock);
763 }
764 
765 /// Conversion of `fir.select_case`
766 ///
767 /// The `fir.select_case` operation is converted to a if-then-else ladder.
768 /// Depending on the case condition type, one or several comparison and
769 /// conditional branching can be generated.
770 ///
771 /// A a point value case such as `case(4)`, a lower bound case such as
772 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
773 /// simple comparison between the selector value and the constant value in the
774 /// case. The block associated with the case condition is then executed if
775 /// the comparison succeed otherwise it branch to the next block with the
776 /// comparison for the the next case conditon.
777 ///
778 /// A closed interval case condition such as `case(7:10)` is converted with a
779 /// first comparison and conditional branching for the lower bound. If
780 /// successful, it branch to a second block with the comparison for the
781 /// upper bound in the same case condition.
782 ///
783 /// TODO: lowering of CHARACTER type cases is not handled yet.
784 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
785   using FIROpConversion::FIROpConversion;
786 
787   mlir::LogicalResult
788   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
789                   mlir::ConversionPatternRewriter &rewriter) const override {
790     unsigned conds = caseOp.getNumConditions();
791     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
792     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
793     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
794     if (ty.isa<fir::CharacterType>())
795       return rewriter.notifyMatchFailure(caseOp,
796                                          "conversion of fir.select_case with "
797                                          "character type not implemented yet");
798     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
799     auto loc = caseOp.getLoc();
800     for (unsigned t = 0; t != conds; ++t) {
801       mlir::Block *dest = caseOp.getSuccessor(t);
802       llvm::Optional<mlir::ValueRange> destOps =
803           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
804       llvm::Optional<mlir::ValueRange> cmpOps =
805           *caseOp.getCompareOperands(adaptor.getOperands(), t);
806       mlir::Value caseArg = *(cmpOps.getValue().begin());
807       mlir::Attribute attr = cases[t];
808       if (attr.isa<fir::PointIntervalAttr>()) {
809         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
810             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
811         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
812         continue;
813       }
814       if (attr.isa<fir::LowerBoundAttr>()) {
815         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
816             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
817         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
818         continue;
819       }
820       if (attr.isa<fir::UpperBoundAttr>()) {
821         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
822             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
823         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
824         continue;
825       }
826       if (attr.isa<fir::ClosedIntervalAttr>()) {
827         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
828             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
829         auto *thisBlock = rewriter.getInsertionBlock();
830         auto *newBlock1 = createBlock(rewriter, dest);
831         auto *newBlock2 = createBlock(rewriter, dest);
832         rewriter.setInsertionPointToEnd(thisBlock);
833         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
834         rewriter.setInsertionPointToEnd(newBlock1);
835         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
836         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
837             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
838         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
839         rewriter.setInsertionPointToEnd(newBlock2);
840         continue;
841       }
842       assert(attr.isa<mlir::UnitAttr>());
843       assert((t + 1 == conds) && "unit must be last");
844       genBrOp(caseOp, dest, destOps, rewriter);
845     }
846     return success();
847   }
848 };
849 
850 template <typename OP>
851 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
852                            typename OP::Adaptor adaptor,
853                            mlir::ConversionPatternRewriter &rewriter) {
854   unsigned conds = select.getNumConditions();
855   auto cases = select.getCases().getValue();
856   mlir::Value selector = adaptor.selector();
857   auto loc = select.getLoc();
858   assert(conds > 0 && "select must have cases");
859 
860   llvm::SmallVector<mlir::Block *> destinations;
861   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
862   mlir::Block *defaultDestination;
863   mlir::ValueRange defaultOperands;
864   llvm::SmallVector<int32_t> caseValues;
865 
866   for (unsigned t = 0; t != conds; ++t) {
867     mlir::Block *dest = select.getSuccessor(t);
868     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
869     const mlir::Attribute &attr = cases[t];
870     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
871       destinations.push_back(dest);
872       destinationsOperands.push_back(destOps.hasValue() ? *destOps
873                                                         : ValueRange());
874       caseValues.push_back(intAttr.getInt());
875       continue;
876     }
877     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
878     assert((t + 1 == conds) && "unit must be last");
879     defaultDestination = dest;
880     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
881   }
882 
883   // LLVM::SwitchOp takes a i32 type for the selector.
884   if (select.getSelector().getType() != rewriter.getI32Type())
885     selector =
886         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
887 
888   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
889       select, selector,
890       /*defaultDestination=*/defaultDestination,
891       /*defaultOperands=*/defaultOperands,
892       /*caseValues=*/caseValues,
893       /*caseDestinations=*/destinations,
894       /*caseOperands=*/destinationsOperands,
895       /*branchWeights=*/ArrayRef<int32_t>());
896 }
897 
898 /// conversion of fir::SelectOp to an if-then-else ladder
899 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
900   using FIROpConversion::FIROpConversion;
901 
902   mlir::LogicalResult
903   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
904                   mlir::ConversionPatternRewriter &rewriter) const override {
905     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
906     return success();
907   }
908 };
909 
910 /// `fir.load` --> `llvm.load`
911 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
912   using FIROpConversion::FIROpConversion;
913 
914   mlir::LogicalResult
915   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
916                   mlir::ConversionPatternRewriter &rewriter) const override {
917     // fir.box is a special case because it is considered as an ssa values in
918     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
919     // and fir.box end up being the same llvm types and loading a
920     // fir.ref<fir.box> is actually a no op in LLVM.
921     if (load.getType().isa<fir::BoxType>()) {
922       rewriter.replaceOp(load, adaptor.getOperands()[0]);
923     } else {
924       mlir::Type ty = convertType(load.getType());
925       ArrayRef<NamedAttribute> at = load->getAttrs();
926       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
927           load, ty, adaptor.getOperands(), at);
928     }
929     return success();
930   }
931 };
932 
933 /// conversion of fir::SelectRankOp to an if-then-else ladder
934 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
935   using FIROpConversion::FIROpConversion;
936 
937   mlir::LogicalResult
938   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
939                   mlir::ConversionPatternRewriter &rewriter) const override {
940     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
941     return success();
942   }
943 };
944 
945 /// `fir.store` --> `llvm.store`
946 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
947   using FIROpConversion::FIROpConversion;
948 
949   mlir::LogicalResult
950   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
951                   mlir::ConversionPatternRewriter &rewriter) const override {
952     if (store.value().getType().isa<fir::BoxType>()) {
953       // fir.box value is actually in memory, load it first before storing it.
954       mlir::Location loc = store.getLoc();
955       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
956       auto val = rewriter.create<mlir::LLVM::LoadOp>(
957           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
958           adaptor.getOperands()[0]);
959       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
960           store, val, adaptor.getOperands()[1]);
961     } else {
962       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
963           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
964     }
965     return success();
966   }
967 };
968 
969 /// convert to LLVM IR dialect `undef`
970 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
971   using FIROpConversion::FIROpConversion;
972 
973   mlir::LogicalResult
974   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
975                   mlir::ConversionPatternRewriter &rewriter) const override {
976     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
977         undef, convertType(undef.getType()));
978     return success();
979   }
980 };
981 
982 /// `fir.unreachable` --> `llvm.unreachable`
983 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
984   using FIROpConversion::FIROpConversion;
985 
986   mlir::LogicalResult
987   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
988                   mlir::ConversionPatternRewriter &rewriter) const override {
989     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
990     return success();
991   }
992 };
993 
994 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
995   using FIROpConversion::FIROpConversion;
996 
997   mlir::LogicalResult
998   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
999                   mlir::ConversionPatternRewriter &rewriter) const override {
1000     auto ty = convertType(zero.getType());
1001     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1002       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1003     } else if (ty.isa<mlir::IntegerType>()) {
1004       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1005           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1006     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1007       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1008           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1009     } else {
1010       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1011       return rewriter.notifyMatchFailure(
1012           zero,
1013           "conversion of fir.zero with aggregate type not implemented yet");
1014     }
1015     return success();
1016   }
1017 };
1018 
1019 // Code shared between insert_value and extract_value Ops.
1020 struct ValueOpCommon {
1021   // Translate the arguments pertaining to any multidimensional array to
1022   // row-major order for LLVM-IR.
1023   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1024                          mlir::Type ty) {
1025     assert(ty && "type is null");
1026     const auto end = attrs.size();
1027     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1028       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1029         const auto dim = getDimension(seq);
1030         if (dim > 1) {
1031           auto ub = std::min(i + dim, end);
1032           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1033           i += dim - 1;
1034         }
1035         ty = getArrayElementType(seq);
1036       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1037         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1038       } else {
1039         llvm_unreachable("index into invalid type");
1040       }
1041     }
1042   }
1043 
1044   static llvm::SmallVector<mlir::Attribute>
1045   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1046                  mlir::ArrayAttr arrAttr) {
1047     llvm::SmallVector<mlir::Attribute> attrs;
1048     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1049       if (i->isa<mlir::IntegerAttr>()) {
1050         attrs.push_back(*i);
1051       } else {
1052         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1053         ++i;
1054         auto ty = i->cast<mlir::TypeAttr>().getValue();
1055         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1056         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1057       }
1058     }
1059     return attrs;
1060   }
1061 
1062 private:
1063   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1064     unsigned result = 1;
1065     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1066          eleTy;
1067          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1068       ++result;
1069     return result;
1070   }
1071 
1072   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1073     auto eleTy = ty.getElementType();
1074     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1075       eleTy = arrTy.getElementType();
1076     return eleTy;
1077   }
1078 };
1079 
1080 /// Extract a subobject value from an ssa-value of aggregate type
1081 struct ExtractValueOpConversion
1082     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1083       public ValueOpCommon {
1084   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1085 
1086   mlir::LogicalResult
1087   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1088             mlir::ConversionPatternRewriter &rewriter) const override {
1089     auto attrs = collectIndices(rewriter, extractVal.coor());
1090     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1091     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1092     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1093         extractVal, ty, adaptor.getOperands()[0], position);
1094     return success();
1095   }
1096 };
1097 
1098 /// InsertValue is the generalized instruction for the composition of new
1099 /// aggregate type values.
1100 struct InsertValueOpConversion
1101     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1102       public ValueOpCommon {
1103   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1104 
1105   mlir::LogicalResult
1106   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1107             mlir::ConversionPatternRewriter &rewriter) const override {
1108     auto attrs = collectIndices(rewriter, insertVal.coor());
1109     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1110     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1111     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1112         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1113         position);
1114     return success();
1115   }
1116 };
1117 
1118 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1119 struct InsertOnRangeOpConversion
1120     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1121   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1122 
1123   // Increments an array of subscripts in a row major fasion.
1124   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1125                            SmallVector<uint64_t> &subscripts) const {
1126     for (size_t i = dims.size(); i > 0; --i) {
1127       if (++subscripts[i - 1] < dims[i - 1]) {
1128         return;
1129       }
1130       subscripts[i - 1] = 0;
1131     }
1132   }
1133 
1134   mlir::LogicalResult
1135   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1136             mlir::ConversionPatternRewriter &rewriter) const override {
1137 
1138     llvm::SmallVector<uint64_t> dims;
1139     auto type = adaptor.getOperands()[0].getType();
1140 
1141     // Iteratively extract the array dimensions from the type.
1142     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1143       dims.push_back(t.getNumElements());
1144       type = t.getElementType();
1145     }
1146 
1147     SmallVector<uint64_t> lBounds;
1148     SmallVector<uint64_t> uBounds;
1149 
1150     // Extract integer value from the attribute
1151     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1152         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1153           return a.cast<IntegerAttr>().getInt();
1154         }));
1155 
1156     // Unzip the upper and lower bound and convert to a row major format.
1157     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1158       uBounds.push_back(*i++);
1159       lBounds.push_back(*i);
1160     }
1161 
1162     auto &subscripts = lBounds;
1163     auto loc = range.getLoc();
1164     mlir::Value lastOp = adaptor.getOperands()[0];
1165     mlir::Value insertVal = adaptor.getOperands()[1];
1166 
1167     auto i64Ty = rewriter.getI64Type();
1168     while (subscripts != uBounds) {
1169       // Convert uint64_t's to Attribute's.
1170       SmallVector<mlir::Attribute> subscriptAttrs;
1171       for (const auto &subscript : subscripts)
1172         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1173       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1174           loc, ty, lastOp, insertVal,
1175           ArrayAttr::get(range.getContext(), subscriptAttrs));
1176 
1177       incrementSubscripts(dims, subscripts);
1178     }
1179 
1180     // Convert uint64_t's to Attribute's.
1181     SmallVector<mlir::Attribute> subscriptAttrs;
1182     for (const auto &subscript : subscripts)
1183       subscriptAttrs.push_back(
1184           IntegerAttr::get(rewriter.getI64Type(), subscript));
1185     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1186 
1187     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1188         range, ty, lastOp, insertVal,
1189         ArrayAttr::get(range.getContext(), arrayRef));
1190 
1191     return success();
1192   }
1193 };
1194 
1195 //
1196 // Primitive operations on Complex types
1197 //
1198 
1199 /// Generate inline code for complex addition/subtraction
1200 template <typename LLVMOP, typename OPTY>
1201 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1202                                      mlir::ConversionPatternRewriter &rewriter,
1203                                      fir::LLVMTypeConverter &lowering) {
1204   mlir::Value a = opnds[0];
1205   mlir::Value b = opnds[1];
1206   auto loc = sumop.getLoc();
1207   auto ctx = sumop.getContext();
1208   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1209   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1210   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1211   mlir::Type ty = lowering.convertType(sumop.getType());
1212   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1213   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1214   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1215   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1216   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1217   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1218   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1219   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1220   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1221 }
1222 
1223 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1224   using FIROpConversion::FIROpConversion;
1225 
1226   mlir::LogicalResult
1227   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1228                   mlir::ConversionPatternRewriter &rewriter) const override {
1229     // given: (x + iy) + (x' + iy')
1230     // result: (x + x') + i(y + y')
1231     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1232                                             rewriter, lowerTy());
1233     rewriter.replaceOp(addc, r.getResult());
1234     return success();
1235   }
1236 };
1237 
1238 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1239   using FIROpConversion::FIROpConversion;
1240 
1241   mlir::LogicalResult
1242   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1243                   mlir::ConversionPatternRewriter &rewriter) const override {
1244     // given: (x + iy) - (x' + iy')
1245     // result: (x - x') + i(y - y')
1246     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1247                                             rewriter, lowerTy());
1248     rewriter.replaceOp(subc, r.getResult());
1249     return success();
1250   }
1251 };
1252 
1253 /// Inlined complex multiply
1254 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1255   using FIROpConversion::FIROpConversion;
1256 
1257   mlir::LogicalResult
1258   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1259                   mlir::ConversionPatternRewriter &rewriter) const override {
1260     // TODO: Can we use a call to __muldc3 ?
1261     // given: (x + iy) * (x' + iy')
1262     // result: (xx'-yy')+i(xy'+yx')
1263     mlir::Value a = adaptor.getOperands()[0];
1264     mlir::Value b = adaptor.getOperands()[1];
1265     auto loc = mulc.getLoc();
1266     auto *ctx = mulc.getContext();
1267     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1268     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1269     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1270     mlir::Type ty = convertType(mulc.getType());
1271     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1272     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1273     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1274     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1275     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1276     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1277     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1278     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1279     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1280     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1281     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1282     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1283     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1284     rewriter.replaceOp(mulc, r0.getResult());
1285     return success();
1286   }
1287 };
1288 
1289 /// Inlined complex division
1290 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1291   using FIROpConversion::FIROpConversion;
1292 
1293   mlir::LogicalResult
1294   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1295                   mlir::ConversionPatternRewriter &rewriter) const override {
1296     // TODO: Can we use a call to __divdc3 instead?
1297     // Just generate inline code for now.
1298     // given: (x + iy) / (x' + iy')
1299     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1300     mlir::Value a = adaptor.getOperands()[0];
1301     mlir::Value b = adaptor.getOperands()[1];
1302     auto loc = divc.getLoc();
1303     auto *ctx = divc.getContext();
1304     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1305     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1306     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1307     mlir::Type ty = convertType(divc.getType());
1308     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1309     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1310     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1311     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1312     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1313     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1314     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1315     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1316     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1317     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1318     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1319     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1320     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1321     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1322     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1323     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1324     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1325     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1326     rewriter.replaceOp(divc, r0.getResult());
1327     return success();
1328   }
1329 };
1330 
1331 /// Inlined complex negation
1332 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1333   using FIROpConversion::FIROpConversion;
1334 
1335   mlir::LogicalResult
1336   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1337                   mlir::ConversionPatternRewriter &rewriter) const override {
1338     // given: -(x + iy)
1339     // result: -x - iy
1340     auto *ctxt = neg.getContext();
1341     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1342     auto ty = convertType(neg.getType());
1343     auto loc = neg.getLoc();
1344     mlir::Value o0 = adaptor.getOperands()[0];
1345     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1346     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1347     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1348     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1349     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1350     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1351     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1352     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1353     return success();
1354   }
1355 };
1356 
1357 /// `fir.is_present` -->
1358 /// ```
1359 ///  %0 = llvm.mlir.constant(0 : i64)
1360 ///  %1 = llvm.ptrtoint %0
1361 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1362 /// ```
1363 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1364   using FIROpConversion::FIROpConversion;
1365 
1366   mlir::LogicalResult
1367   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1368                   mlir::ConversionPatternRewriter &rewriter) const override {
1369     mlir::Type idxTy = lowerTy().indexType();
1370     mlir::Location loc = isPresent.getLoc();
1371     auto ptr = adaptor.getOperands()[0];
1372 
1373     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1374       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1375       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1376 
1377       mlir::Type ty = structTy.getBody()[0];
1378       mlir::MLIRContext *ctx = isPresent.getContext();
1379       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1380       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1381     }
1382     mlir::LLVM::ConstantOp c0 =
1383         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1384     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1385     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1386         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1387 
1388     return success();
1389   }
1390 };
1391 } // namespace
1392 
1393 namespace {
1394 /// Convert FIR dialect to LLVM dialect
1395 ///
1396 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1397 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1398 ///
1399 /// This pass is not complete yet. We are upstreaming it in small patches.
1400 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1401 public:
1402   mlir::ModuleOp getModule() { return getOperation(); }
1403 
1404   void runOnOperation() override final {
1405     auto mod = getModule();
1406     if (!forcedTargetTriple.empty()) {
1407       fir::setTargetTriple(mod, forcedTargetTriple);
1408     }
1409 
1410     auto *context = getModule().getContext();
1411     fir::LLVMTypeConverter typeConverter{getModule()};
1412     mlir::OwningRewritePatternList pattern(context);
1413     pattern.insert<
1414         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1415         AllocaOpConversion, BoxAddrOpConversion, BoxDimsOpConversion,
1416         BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
1417         BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion,
1418         ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion,
1419         DTEntryOpConversion, DivcOpConversion, ExtractValueOpConversion,
1420         HasValueOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
1421         InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion,
1422         NegcOpConversion, MulcOpConversion, SelectCaseOpConversion,
1423         SelectOpConversion, SelectRankOpConversion, StoreOpConversion,
1424         SubcOpConversion, UndefOpConversion, UnreachableOpConversion,
1425         ZeroOpConversion>(typeConverter);
1426     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1427     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1428                                                             pattern);
1429     mlir::ConversionTarget target{*context};
1430     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1431 
1432     // required NOPs for applying a full conversion
1433     target.addLegalOp<mlir::ModuleOp>();
1434 
1435     // apply the patterns
1436     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1437                                                std::move(pattern)))) {
1438       signalPassFailure();
1439     }
1440   }
1441 };
1442 } // namespace
1443 
1444 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1445   return std::make_unique<FIRToLLVMLowering>();
1446 }
1447