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