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/Optimizer/Dialect/FIROps.h"
16 #include "flang/Optimizer/Dialect/FIRType.h"
17 #include "flang/Optimizer/Support/FIRContext.h"
18 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
19 #include "mlir/Conversion/LLVMCommon/Pattern.h"
20 #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
21 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/Matchers.h"
24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/ArrayRef.h"
26 
27 #define DEBUG_TYPE "flang-codegen"
28 
29 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
30 #include "TypeConverter.h"
31 
32 namespace {
33 /// FIR conversion pattern template
34 template <typename FromOp>
35 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
36 public:
37   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
38       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
39 
40 protected:
41   mlir::Type convertType(mlir::Type ty) const {
42     return lowerTy().convertType(ty);
43   }
44 
45   mlir::LLVM::ConstantOp
46   genConstantOffset(mlir::Location loc,
47                     mlir::ConversionPatternRewriter &rewriter,
48                     int offset) const {
49     auto ity = lowerTy().offsetType();
50     auto cattr = rewriter.getI32IntegerAttr(offset);
51     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
52   }
53 
54   /// Construct code sequence to get the rank from a box.
55   mlir::Value getRankFromBox(mlir::Location loc, mlir::Value box,
56                              mlir::Type resultTy,
57                              mlir::ConversionPatternRewriter &rewriter) const {
58     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
59     mlir::LLVM::ConstantOp cRank =
60         genConstantOffset(loc, rewriter, kRankPosInBox);
61     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
62     auto p = rewriter.create<mlir::LLVM::GEPOp>(
63         loc, pty, mlir::ValueRange{box, c0, cRank});
64     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
65   }
66 
67   /// Method to construct code sequence to get the triple for dimension `dim`
68   /// from a box.
69   SmallVector<mlir::Value, 3>
70   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
71                  mlir::Value box, mlir::Value dim,
72                  mlir::ConversionPatternRewriter &rewriter) const {
73     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
74     mlir::LLVM::ConstantOp cDims =
75         genConstantOffset(loc, rewriter, kDimsPosInBox);
76     mlir::LLVM::LoadOp l0 =
77         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
78     mlir::LLVM::LoadOp l1 =
79         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
80     mlir::LLVM::LoadOp l2 =
81         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
82     return {l0.getResult(), l1.getResult(), l2.getResult()};
83   }
84 
85   mlir::LLVM::LoadOp
86   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
87                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
88                  mlir::Type ty,
89                  mlir::ConversionPatternRewriter &rewriter) const {
90     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
91     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
92     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
93     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
94   }
95 
96   /// Read base address from a fir.box. Returned address has type ty.
97   mlir::Value
98   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
99                       mlir::ConversionPatternRewriter &rewriter) const {
100     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
101     mlir::LLVM::ConstantOp cAddr =
102         genConstantOffset(loc, rewriter, kAddrPosInBox);
103     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
104     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
105     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
106   }
107 
108   mlir::Value
109   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
110                          mlir::ConversionPatternRewriter &rewriter) const {
111     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
112     mlir::LLVM::ConstantOp cElemLen =
113         genConstantOffset(loc, rewriter, kElemLenPosInBox);
114     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
115     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
116     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
117   }
118 
119   template <typename... ARGS>
120   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
121                            mlir::ConversionPatternRewriter &rewriter,
122                            mlir::Value base, ARGS... args) const {
123     SmallVector<mlir::Value> cv{args...};
124     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
125   }
126 
127   fir::LLVMTypeConverter &lowerTy() const {
128     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
129   }
130 };
131 
132 /// FIR conversion pattern template
133 template <typename FromOp>
134 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
135 public:
136   using FIROpConversion<FromOp>::FIROpConversion;
137   using OpAdaptor = typename FromOp::Adaptor;
138 
139   mlir::LogicalResult
140   matchAndRewrite(FromOp op, OpAdaptor adaptor,
141                   mlir::ConversionPatternRewriter &rewriter) const final {
142     mlir::Type ty = this->convertType(op.getType());
143     return doRewrite(op, ty, adaptor, rewriter);
144   }
145 
146   virtual mlir::LogicalResult
147   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
148             mlir::ConversionPatternRewriter &rewriter) const = 0;
149 };
150 
151 // Lower `fir.address_of` operation to `llvm.address_of` operation.
152 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
153   using FIROpConversion::FIROpConversion;
154 
155   mlir::LogicalResult
156   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
157                   mlir::ConversionPatternRewriter &rewriter) const override {
158     auto ty = convertType(addr.getType());
159     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
160         addr, ty, addr.symbol().getRootReference().getValue());
161     return success();
162   }
163 };
164 
165 /// Lower `fir.box_addr` to the sequence of operations to extract the first
166 /// element of the box.
167 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
168   using FIROpConversion::FIROpConversion;
169 
170   mlir::LogicalResult
171   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
172                   mlir::ConversionPatternRewriter &rewriter) const override {
173     mlir::Value a = adaptor.getOperands()[0];
174     auto loc = boxaddr.getLoc();
175     mlir::Type ty = convertType(boxaddr.getType());
176     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
177       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
178     } else {
179       auto c0attr = rewriter.getI32IntegerAttr(0);
180       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
181       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
182                                                               c0);
183     }
184     return success();
185   }
186 };
187 
188 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
189 /// dimension infomartion from the boxed value.
190 /// Result in a triple set of GEPs and loads.
191 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
192   using FIROpConversion::FIROpConversion;
193 
194   mlir::LogicalResult
195   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
196                   mlir::ConversionPatternRewriter &rewriter) const override {
197     SmallVector<mlir::Type, 3> resultTypes = {
198         convertType(boxdims.getResult(0).getType()),
199         convertType(boxdims.getResult(1).getType()),
200         convertType(boxdims.getResult(2).getType()),
201     };
202     auto results =
203         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
204                        adaptor.getOperands()[1], rewriter);
205     rewriter.replaceOp(boxdims, results);
206     return success();
207   }
208 };
209 
210 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
211 /// an element in the boxed value.
212 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
213   using FIROpConversion::FIROpConversion;
214 
215   mlir::LogicalResult
216   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
217                   mlir::ConversionPatternRewriter &rewriter) const override {
218     mlir::Value a = adaptor.getOperands()[0];
219     auto loc = boxelesz.getLoc();
220     auto ty = convertType(boxelesz.getType());
221     rewriter.replaceOp(boxelesz, loadElementSizeFromBox(loc, ty, a, rewriter));
222     return success();
223   }
224 };
225 
226 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
227 /// the box.
228 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
229   using FIROpConversion::FIROpConversion;
230 
231   mlir::LogicalResult
232   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
233                   mlir::ConversionPatternRewriter &rewriter) const override {
234     mlir::Value a = adaptor.getOperands()[0];
235     auto loc = boxrank.getLoc();
236     mlir::Type ty = convertType(boxrank.getType());
237     auto result = getRankFromBox(loc, a, ty, rewriter);
238     rewriter.replaceOp(boxrank, result);
239     return success();
240   }
241 };
242 
243 // `fir.call` -> `llvm.call`
244 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
245   using FIROpConversion::FIROpConversion;
246 
247   mlir::LogicalResult
248   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
249                   mlir::ConversionPatternRewriter &rewriter) const override {
250     SmallVector<mlir::Type> resultTys;
251     for (auto r : call.getResults())
252       resultTys.push_back(convertType(r.getType()));
253     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
254         call, resultTys, adaptor.getOperands(), call->getAttrs());
255     return success();
256   }
257 };
258 
259 static mlir::Type getComplexEleTy(mlir::Type complex) {
260   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
261     return cc.getElementType();
262   return complex.cast<fir::ComplexType>().getElementType();
263 }
264 
265 /// convert value of from-type to value of to-type
266 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
267   using FIROpConversion::FIROpConversion;
268 
269   static bool isFloatingPointTy(mlir::Type ty) {
270     return ty.isa<mlir::FloatType>();
271   }
272 
273   mlir::LogicalResult
274   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
275                   mlir::ConversionPatternRewriter &rewriter) const override {
276     auto fromTy = convertType(convert.value().getType());
277     auto toTy = convertType(convert.res().getType());
278     mlir::Value op0 = adaptor.getOperands()[0];
279     if (fromTy == toTy) {
280       rewriter.replaceOp(convert, op0);
281       return success();
282     }
283     auto loc = convert.getLoc();
284     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
285                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
286       if (fromBits == toBits) {
287         // TODO: Converting between two floating-point representations with the
288         // same bitwidth is not allowed for now.
289         mlir::emitError(loc,
290                         "cannot implicitly convert between two floating-point "
291                         "representations of the same bitwidth");
292         return {};
293       }
294       if (fromBits > toBits)
295         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
296       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
297     };
298     // Complex to complex conversion.
299     if (fir::isa_complex(convert.value().getType()) &&
300         fir::isa_complex(convert.res().getType())) {
301       // Special case: handle the conversion of a complex such that both the
302       // real and imaginary parts are converted together.
303       auto zero = mlir::ArrayAttr::get(convert.getContext(),
304                                        rewriter.getI32IntegerAttr(0));
305       auto one = mlir::ArrayAttr::get(convert.getContext(),
306                                       rewriter.getI32IntegerAttr(1));
307       auto ty = convertType(getComplexEleTy(convert.value().getType()));
308       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
309       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
310       auto nt = convertType(getComplexEleTy(convert.res().getType()));
311       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
312       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
313       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
314       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
315       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
316       auto i1 =
317           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
318       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
319                                                              ic, one);
320       return mlir::success();
321     }
322     // Floating point to floating point conversion.
323     if (isFloatingPointTy(fromTy)) {
324       if (isFloatingPointTy(toTy)) {
325         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
326         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
327         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
328         rewriter.replaceOp(convert, v);
329         return mlir::success();
330       }
331       if (toTy.isa<mlir::IntegerType>()) {
332         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
333         return mlir::success();
334       }
335     } else if (fromTy.isa<mlir::IntegerType>()) {
336       // Integer to integer conversion.
337       if (toTy.isa<mlir::IntegerType>()) {
338         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
339         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
340         assert(fromBits != toBits);
341         if (fromBits > toBits) {
342           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
343           return mlir::success();
344         }
345         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
346         return mlir::success();
347       }
348       // Integer to floating point conversion.
349       if (isFloatingPointTy(toTy)) {
350         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
351         return mlir::success();
352       }
353       // Integer to pointer conversion.
354       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
355         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
356         return mlir::success();
357       }
358     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
359       // Pointer to integer conversion.
360       if (toTy.isa<mlir::IntegerType>()) {
361         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
362         return mlir::success();
363       }
364       // Pointer to pointer conversion.
365       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
366         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
367         return mlir::success();
368       }
369     }
370     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
371   }
372 };
373 
374 /// Lower `fir.has_value` operation to `llvm.return` operation.
375 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
376   using FIROpConversion::FIROpConversion;
377 
378   mlir::LogicalResult
379   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
380                   mlir::ConversionPatternRewriter &rewriter) const override {
381     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
382     return success();
383   }
384 };
385 
386 /// Lower `fir.global` operation to `llvm.global` operation.
387 /// `fir.insert_on_range` operations are replaced with constant dense attribute
388 /// if they are applied on the full range.
389 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
390   using FIROpConversion::FIROpConversion;
391 
392   mlir::LogicalResult
393   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
394                   mlir::ConversionPatternRewriter &rewriter) const override {
395     auto tyAttr = convertType(global.getType());
396     if (global.getType().isa<fir::BoxType>())
397       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
398     auto loc = global.getLoc();
399     mlir::Attribute initAttr{};
400     if (global.initVal())
401       initAttr = global.initVal().getValue();
402     auto linkage = convertLinkage(global.linkName());
403     auto isConst = global.constant().hasValue();
404     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
405         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
406     auto &gr = g.getInitializerRegion();
407     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
408     if (!gr.empty()) {
409       // Replace insert_on_range with a constant dense attribute if the
410       // initialization is on the full range.
411       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
412       for (auto insertOp : insertOnRangeOps) {
413         if (isFullRange(insertOp.coor(), insertOp.getType())) {
414           auto seqTyAttr = convertType(insertOp.getType());
415           auto *op = insertOp.val().getDefiningOp();
416           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
417           if (!constant) {
418             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
419             if (!convertOp)
420               continue;
421             constant = cast<mlir::arith::ConstantOp>(
422                 convertOp.value().getDefiningOp());
423           }
424           mlir::Type vecType = mlir::VectorType::get(
425               insertOp.getType().getShape(), constant.getType());
426           auto denseAttr = mlir::DenseElementsAttr::get(
427               vecType.cast<ShapedType>(), constant.value());
428           rewriter.setInsertionPointAfter(insertOp);
429           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
430               insertOp, seqTyAttr, denseAttr);
431         }
432       }
433     }
434     rewriter.eraseOp(global);
435     return success();
436   }
437 
438   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
439     auto extents = seqTy.getShape();
440     if (indexes.size() / 2 != extents.size())
441       return false;
442     for (unsigned i = 0; i < indexes.size(); i += 2) {
443       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
444         return false;
445       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
446         return false;
447     }
448     return true;
449   }
450 
451   // TODO: String comparaison should be avoided. Replace linkName with an
452   // enumeration.
453   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
454     if (optLinkage.hasValue()) {
455       auto name = optLinkage.getValue();
456       if (name == "internal")
457         return mlir::LLVM::Linkage::Internal;
458       if (name == "linkonce")
459         return mlir::LLVM::Linkage::Linkonce;
460       if (name == "common")
461         return mlir::LLVM::Linkage::Common;
462       if (name == "weak")
463         return mlir::LLVM::Linkage::Weak;
464     }
465     return mlir::LLVM::Linkage::External;
466   }
467 };
468 
469 template <typename OP>
470 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
471                            typename OP::Adaptor adaptor,
472                            mlir::ConversionPatternRewriter &rewriter) {
473   unsigned conds = select.getNumConditions();
474   auto cases = select.getCases().getValue();
475   mlir::Value selector = adaptor.selector();
476   auto loc = select.getLoc();
477   assert(conds > 0 && "select must have cases");
478 
479   llvm::SmallVector<mlir::Block *> destinations;
480   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
481   mlir::Block *defaultDestination;
482   mlir::ValueRange defaultOperands;
483   llvm::SmallVector<int32_t> caseValues;
484 
485   for (unsigned t = 0; t != conds; ++t) {
486     mlir::Block *dest = select.getSuccessor(t);
487     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
488     const mlir::Attribute &attr = cases[t];
489     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
490       destinations.push_back(dest);
491       destinationsOperands.push_back(destOps.hasValue() ? *destOps
492                                                         : ValueRange());
493       caseValues.push_back(intAttr.getInt());
494       continue;
495     }
496     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
497     assert((t + 1 == conds) && "unit must be last");
498     defaultDestination = dest;
499     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
500   }
501 
502   // LLVM::SwitchOp takes a i32 type for the selector.
503   if (select.getSelector().getType() != rewriter.getI32Type())
504     selector =
505         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
506 
507   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
508       select, selector,
509       /*defaultDestination=*/defaultDestination,
510       /*defaultOperands=*/defaultOperands,
511       /*caseValues=*/caseValues,
512       /*caseDestinations=*/destinations,
513       /*caseOperands=*/destinationsOperands,
514       /*branchWeights=*/ArrayRef<int32_t>());
515 }
516 
517 /// conversion of fir::SelectOp to an if-then-else ladder
518 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
519   using FIROpConversion::FIROpConversion;
520 
521   mlir::LogicalResult
522   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
523                   mlir::ConversionPatternRewriter &rewriter) const override {
524     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
525     return success();
526   }
527 };
528 
529 /// `fir.load` --> `llvm.load`
530 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
531   using FIROpConversion::FIROpConversion;
532 
533   mlir::LogicalResult
534   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
535                   mlir::ConversionPatternRewriter &rewriter) const override {
536     // fir.box is a special case because it is considered as an ssa values in
537     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
538     // and fir.box end up being the same llvm types and loading a
539     // fir.ref<fir.box> is actually a no op in LLVM.
540     if (load.getType().isa<fir::BoxType>()) {
541       rewriter.replaceOp(load, adaptor.getOperands()[0]);
542     } else {
543       mlir::Type ty = convertType(load.getType());
544       ArrayRef<NamedAttribute> at = load->getAttrs();
545       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
546           load, ty, adaptor.getOperands(), at);
547     }
548     return success();
549   }
550 };
551 
552 /// conversion of fir::SelectRankOp to an if-then-else ladder
553 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
554   using FIROpConversion::FIROpConversion;
555 
556   mlir::LogicalResult
557   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
558                   mlir::ConversionPatternRewriter &rewriter) const override {
559     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
560     return success();
561   }
562 };
563 
564 /// `fir.store` --> `llvm.store`
565 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
566   using FIROpConversion::FIROpConversion;
567 
568   mlir::LogicalResult
569   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
570                   mlir::ConversionPatternRewriter &rewriter) const override {
571     if (store.value().getType().isa<fir::BoxType>()) {
572       // fir.box value is actually in memory, load it first before storing it.
573       mlir::Location loc = store.getLoc();
574       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
575       auto val = rewriter.create<mlir::LLVM::LoadOp>(
576           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
577           adaptor.getOperands()[0]);
578       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
579           store, val, adaptor.getOperands()[1]);
580     } else {
581       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
582           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
583     }
584     return success();
585   }
586 };
587 
588 /// convert to LLVM IR dialect `undef`
589 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
590   using FIROpConversion::FIROpConversion;
591 
592   mlir::LogicalResult
593   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
594                   mlir::ConversionPatternRewriter &rewriter) const override {
595     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
596         undef, convertType(undef.getType()));
597     return success();
598   }
599 };
600 
601 /// `fir.unreachable` --> `llvm.unreachable`
602 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
603   using FIROpConversion::FIROpConversion;
604 
605   mlir::LogicalResult
606   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
607                   mlir::ConversionPatternRewriter &rewriter) const override {
608     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
609     return success();
610   }
611 };
612 
613 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
614   using FIROpConversion::FIROpConversion;
615 
616   mlir::LogicalResult
617   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
618                   mlir::ConversionPatternRewriter &rewriter) const override {
619     auto ty = convertType(zero.getType());
620     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
621       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
622     } else if (ty.isa<mlir::IntegerType>()) {
623       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
624           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
625     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
626       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
627           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
628     } else {
629       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
630       return rewriter.notifyMatchFailure(
631           zero,
632           "conversion of fir.zero with aggregate type not implemented yet");
633     }
634     return success();
635   }
636 };
637 
638 // Code shared between insert_value and extract_value Ops.
639 struct ValueOpCommon {
640   // Translate the arguments pertaining to any multidimensional array to
641   // row-major order for LLVM-IR.
642   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
643                          mlir::Type ty) {
644     assert(ty && "type is null");
645     const auto end = attrs.size();
646     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
647       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
648         const auto dim = getDimension(seq);
649         if (dim > 1) {
650           auto ub = std::min(i + dim, end);
651           std::reverse(attrs.begin() + i, attrs.begin() + ub);
652           i += dim - 1;
653         }
654         ty = getArrayElementType(seq);
655       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
656         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
657       } else {
658         llvm_unreachable("index into invalid type");
659       }
660     }
661   }
662 
663   static llvm::SmallVector<mlir::Attribute>
664   collectIndices(mlir::ConversionPatternRewriter &rewriter,
665                  mlir::ArrayAttr arrAttr) {
666     llvm::SmallVector<mlir::Attribute> attrs;
667     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
668       if (i->isa<mlir::IntegerAttr>()) {
669         attrs.push_back(*i);
670       } else {
671         auto fieldName = i->cast<mlir::StringAttr>().getValue();
672         ++i;
673         auto ty = i->cast<mlir::TypeAttr>().getValue();
674         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
675         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
676       }
677     }
678     return attrs;
679   }
680 
681 private:
682   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
683     unsigned result = 1;
684     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
685          eleTy;
686          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
687       ++result;
688     return result;
689   }
690 
691   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
692     auto eleTy = ty.getElementType();
693     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
694       eleTy = arrTy.getElementType();
695     return eleTy;
696   }
697 };
698 
699 /// Extract a subobject value from an ssa-value of aggregate type
700 struct ExtractValueOpConversion
701     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
702       public ValueOpCommon {
703   using FIROpAndTypeConversion::FIROpAndTypeConversion;
704 
705   mlir::LogicalResult
706   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
707             mlir::ConversionPatternRewriter &rewriter) const override {
708     auto attrs = collectIndices(rewriter, extractVal.coor());
709     toRowMajor(attrs, adaptor.getOperands()[0].getType());
710     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
711     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
712         extractVal, ty, adaptor.getOperands()[0], position);
713     return success();
714   }
715 };
716 
717 /// InsertValue is the generalized instruction for the composition of new
718 /// aggregate type values.
719 struct InsertValueOpConversion
720     : public FIROpAndTypeConversion<fir::InsertValueOp>,
721       public ValueOpCommon {
722   using FIROpAndTypeConversion::FIROpAndTypeConversion;
723 
724   mlir::LogicalResult
725   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
726             mlir::ConversionPatternRewriter &rewriter) const override {
727     auto attrs = collectIndices(rewriter, insertVal.coor());
728     toRowMajor(attrs, adaptor.getOperands()[0].getType());
729     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
730     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
731         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
732         position);
733     return success();
734   }
735 };
736 
737 /// InsertOnRange inserts a value into a sequence over a range of offsets.
738 struct InsertOnRangeOpConversion
739     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
740   using FIROpAndTypeConversion::FIROpAndTypeConversion;
741 
742   // Increments an array of subscripts in a row major fasion.
743   void incrementSubscripts(const SmallVector<uint64_t> &dims,
744                            SmallVector<uint64_t> &subscripts) const {
745     for (size_t i = dims.size(); i > 0; --i) {
746       if (++subscripts[i - 1] < dims[i - 1]) {
747         return;
748       }
749       subscripts[i - 1] = 0;
750     }
751   }
752 
753   mlir::LogicalResult
754   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
755             mlir::ConversionPatternRewriter &rewriter) const override {
756 
757     llvm::SmallVector<uint64_t> dims;
758     auto type = adaptor.getOperands()[0].getType();
759 
760     // Iteratively extract the array dimensions from the type.
761     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
762       dims.push_back(t.getNumElements());
763       type = t.getElementType();
764     }
765 
766     SmallVector<uint64_t> lBounds;
767     SmallVector<uint64_t> uBounds;
768 
769     // Extract integer value from the attribute
770     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
771         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
772           return a.cast<IntegerAttr>().getInt();
773         }));
774 
775     // Unzip the upper and lower bound and convert to a row major format.
776     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
777       uBounds.push_back(*i++);
778       lBounds.push_back(*i);
779     }
780 
781     auto &subscripts = lBounds;
782     auto loc = range.getLoc();
783     mlir::Value lastOp = adaptor.getOperands()[0];
784     mlir::Value insertVal = adaptor.getOperands()[1];
785 
786     auto i64Ty = rewriter.getI64Type();
787     while (subscripts != uBounds) {
788       // Convert uint64_t's to Attribute's.
789       SmallVector<mlir::Attribute> subscriptAttrs;
790       for (const auto &subscript : subscripts)
791         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
792       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
793           loc, ty, lastOp, insertVal,
794           ArrayAttr::get(range.getContext(), subscriptAttrs));
795 
796       incrementSubscripts(dims, subscripts);
797     }
798 
799     // Convert uint64_t's to Attribute's.
800     SmallVector<mlir::Attribute> subscriptAttrs;
801     for (const auto &subscript : subscripts)
802       subscriptAttrs.push_back(
803           IntegerAttr::get(rewriter.getI64Type(), subscript));
804     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
805 
806     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
807         range, ty, lastOp, insertVal,
808         ArrayAttr::get(range.getContext(), arrayRef));
809 
810     return success();
811   }
812 };
813 
814 //
815 // Primitive operations on Complex types
816 //
817 
818 /// Generate inline code for complex addition/subtraction
819 template <typename LLVMOP, typename OPTY>
820 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
821                                      mlir::ConversionPatternRewriter &rewriter,
822                                      fir::LLVMTypeConverter &lowering) {
823   mlir::Value a = opnds[0];
824   mlir::Value b = opnds[1];
825   auto loc = sumop.getLoc();
826   auto ctx = sumop.getContext();
827   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
828   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
829   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
830   mlir::Type ty = lowering.convertType(sumop.getType());
831   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
832   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
833   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
834   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
835   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
836   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
837   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
838   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
839   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
840 }
841 
842 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
843   using FIROpConversion::FIROpConversion;
844 
845   mlir::LogicalResult
846   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
847                   mlir::ConversionPatternRewriter &rewriter) const override {
848     // given: (x + iy) + (x' + iy')
849     // result: (x + x') + i(y + y')
850     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
851                                             rewriter, lowerTy());
852     rewriter.replaceOp(addc, r.getResult());
853     return success();
854   }
855 };
856 
857 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
858   using FIROpConversion::FIROpConversion;
859 
860   mlir::LogicalResult
861   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
862                   mlir::ConversionPatternRewriter &rewriter) const override {
863     // given: (x + iy) - (x' + iy')
864     // result: (x - x') + i(y - y')
865     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
866                                             rewriter, lowerTy());
867     rewriter.replaceOp(subc, r.getResult());
868     return success();
869   }
870 };
871 
872 /// Inlined complex multiply
873 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
874   using FIROpConversion::FIROpConversion;
875 
876   mlir::LogicalResult
877   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
878                   mlir::ConversionPatternRewriter &rewriter) const override {
879     // TODO: Can we use a call to __muldc3 ?
880     // given: (x + iy) * (x' + iy')
881     // result: (xx'-yy')+i(xy'+yx')
882     mlir::Value a = adaptor.getOperands()[0];
883     mlir::Value b = adaptor.getOperands()[1];
884     auto loc = mulc.getLoc();
885     auto *ctx = mulc.getContext();
886     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
887     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
888     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
889     mlir::Type ty = convertType(mulc.getType());
890     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
891     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
892     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
893     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
894     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
895     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
896     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
897     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
898     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
899     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
900     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
901     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
902     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
903     rewriter.replaceOp(mulc, r0.getResult());
904     return success();
905   }
906 };
907 
908 /// Inlined complex division
909 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
910   using FIROpConversion::FIROpConversion;
911 
912   mlir::LogicalResult
913   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
914                   mlir::ConversionPatternRewriter &rewriter) const override {
915     // TODO: Can we use a call to __divdc3 instead?
916     // Just generate inline code for now.
917     // given: (x + iy) / (x' + iy')
918     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
919     mlir::Value a = adaptor.getOperands()[0];
920     mlir::Value b = adaptor.getOperands()[1];
921     auto loc = divc.getLoc();
922     auto *ctx = divc.getContext();
923     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
924     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
925     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
926     mlir::Type ty = convertType(divc.getType());
927     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
928     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
929     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
930     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
931     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
932     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
933     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
934     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
935     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
936     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
937     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
938     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
939     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
940     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
941     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
942     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
943     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
944     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
945     rewriter.replaceOp(divc, r0.getResult());
946     return success();
947   }
948 };
949 
950 /// Inlined complex negation
951 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
952   using FIROpConversion::FIROpConversion;
953 
954   mlir::LogicalResult
955   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
956                   mlir::ConversionPatternRewriter &rewriter) const override {
957     // given: -(x + iy)
958     // result: -x - iy
959     auto *ctxt = neg.getContext();
960     auto eleTy = convertType(getComplexEleTy(neg.getType()));
961     auto ty = convertType(neg.getType());
962     auto loc = neg.getLoc();
963     mlir::Value o0 = adaptor.getOperands()[0];
964     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
965     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
966     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
967     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
968     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
969     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
970     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
971     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
972     return success();
973   }
974 };
975 
976 } // namespace
977 
978 namespace {
979 /// Convert FIR dialect to LLVM dialect
980 ///
981 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
982 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
983 ///
984 /// This pass is not complete yet. We are upstreaming it in small patches.
985 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
986 public:
987   mlir::ModuleOp getModule() { return getOperation(); }
988 
989   void runOnOperation() override final {
990     auto mod = getModule();
991     if (!forcedTargetTriple.empty()) {
992       fir::setTargetTriple(mod, forcedTargetTriple);
993     }
994 
995     auto *context = getModule().getContext();
996     fir::LLVMTypeConverter typeConverter{getModule()};
997     mlir::OwningRewritePatternList pattern(context);
998     pattern.insert<
999         AddcOpConversion, AddrOfOpConversion, BoxAddrOpConversion,
1000         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxRankOpConversion,
1001         CallOpConversion, ConvertOpConversion, DivcOpConversion,
1002         ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion,
1003         InsertOnRangeOpConversion, InsertValueOpConversion, LoadOpConversion,
1004         NegcOpConversion, MulcOpConversion, SelectOpConversion,
1005         SelectRankOpConversion, StoreOpConversion, SubcOpConversion,
1006         UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
1007         typeConverter);
1008     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1009     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1010                                                             pattern);
1011     mlir::ConversionTarget target{*context};
1012     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1013 
1014     // required NOPs for applying a full conversion
1015     target.addLegalOp<mlir::ModuleOp>();
1016 
1017     // apply the patterns
1018     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1019                                                std::move(pattern)))) {
1020       signalPassFailure();
1021     }
1022   }
1023 };
1024 } // namespace
1025 
1026 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1027   return std::make_unique<FIRToLLVMLowering>();
1028 }
1029