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