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