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.dispatch` operation. A virtual call to a method in a dispatch
564 /// table.
565 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
566   using FIROpConversion::FIROpConversion;
567 
568   mlir::LogicalResult
569   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
570                   mlir::ConversionPatternRewriter &rewriter) const override {
571     return rewriter.notifyMatchFailure(
572         dispatch, "fir.dispatch codegen is not implemented yet");
573   }
574 };
575 
576 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
577 /// derived type.
578 struct DispatchTableOpConversion
579     : public FIROpConversion<fir::DispatchTableOp> {
580   using FIROpConversion::FIROpConversion;
581 
582   mlir::LogicalResult
583   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
584                   mlir::ConversionPatternRewriter &rewriter) const override {
585     return rewriter.notifyMatchFailure(
586         dispTab, "fir.dispatch_table codegen is not implemented yet");
587   }
588 };
589 
590 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
591 /// method-name to a function.
592 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
593   using FIROpConversion::FIROpConversion;
594 
595   mlir::LogicalResult
596   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
597                   mlir::ConversionPatternRewriter &rewriter) const override {
598     return rewriter.notifyMatchFailure(
599         dtEnt, "fir.dt_entry codegen is not implemented yet");
600   }
601 };
602 
603 /// Lower `fir.has_value` operation to `llvm.return` operation.
604 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
605   using FIROpConversion::FIROpConversion;
606 
607   mlir::LogicalResult
608   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
609                   mlir::ConversionPatternRewriter &rewriter) const override {
610     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
611     return success();
612   }
613 };
614 
615 /// Lower `fir.global` operation to `llvm.global` operation.
616 /// `fir.insert_on_range` operations are replaced with constant dense attribute
617 /// if they are applied on the full range.
618 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
619   using FIROpConversion::FIROpConversion;
620 
621   mlir::LogicalResult
622   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
623                   mlir::ConversionPatternRewriter &rewriter) const override {
624     auto tyAttr = convertType(global.getType());
625     if (global.getType().isa<fir::BoxType>())
626       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
627     auto loc = global.getLoc();
628     mlir::Attribute initAttr{};
629     if (global.initVal())
630       initAttr = global.initVal().getValue();
631     auto linkage = convertLinkage(global.linkName());
632     auto isConst = global.constant().hasValue();
633     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
634         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
635     auto &gr = g.getInitializerRegion();
636     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
637     if (!gr.empty()) {
638       // Replace insert_on_range with a constant dense attribute if the
639       // initialization is on the full range.
640       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
641       for (auto insertOp : insertOnRangeOps) {
642         if (isFullRange(insertOp.coor(), insertOp.getType())) {
643           auto seqTyAttr = convertType(insertOp.getType());
644           auto *op = insertOp.val().getDefiningOp();
645           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
646           if (!constant) {
647             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
648             if (!convertOp)
649               continue;
650             constant = cast<mlir::arith::ConstantOp>(
651                 convertOp.value().getDefiningOp());
652           }
653           mlir::Type vecType = mlir::VectorType::get(
654               insertOp.getType().getShape(), constant.getType());
655           auto denseAttr = mlir::DenseElementsAttr::get(
656               vecType.cast<ShapedType>(), constant.value());
657           rewriter.setInsertionPointAfter(insertOp);
658           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
659               insertOp, seqTyAttr, denseAttr);
660         }
661       }
662     }
663     rewriter.eraseOp(global);
664     return success();
665   }
666 
667   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
668     auto extents = seqTy.getShape();
669     if (indexes.size() / 2 != extents.size())
670       return false;
671     for (unsigned i = 0; i < indexes.size(); i += 2) {
672       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
673         return false;
674       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
675         return false;
676     }
677     return true;
678   }
679 
680   // TODO: String comparaison should be avoided. Replace linkName with an
681   // enumeration.
682   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
683     if (optLinkage.hasValue()) {
684       auto name = optLinkage.getValue();
685       if (name == "internal")
686         return mlir::LLVM::Linkage::Internal;
687       if (name == "linkonce")
688         return mlir::LLVM::Linkage::Linkonce;
689       if (name == "common")
690         return mlir::LLVM::Linkage::Common;
691       if (name == "weak")
692         return mlir::LLVM::Linkage::Weak;
693     }
694     return mlir::LLVM::Linkage::External;
695   }
696 };
697 
698 template <typename OP>
699 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
700                            typename OP::Adaptor adaptor,
701                            mlir::ConversionPatternRewriter &rewriter) {
702   unsigned conds = select.getNumConditions();
703   auto cases = select.getCases().getValue();
704   mlir::Value selector = adaptor.selector();
705   auto loc = select.getLoc();
706   assert(conds > 0 && "select must have cases");
707 
708   llvm::SmallVector<mlir::Block *> destinations;
709   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
710   mlir::Block *defaultDestination;
711   mlir::ValueRange defaultOperands;
712   llvm::SmallVector<int32_t> caseValues;
713 
714   for (unsigned t = 0; t != conds; ++t) {
715     mlir::Block *dest = select.getSuccessor(t);
716     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
717     const mlir::Attribute &attr = cases[t];
718     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
719       destinations.push_back(dest);
720       destinationsOperands.push_back(destOps.hasValue() ? *destOps
721                                                         : ValueRange());
722       caseValues.push_back(intAttr.getInt());
723       continue;
724     }
725     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
726     assert((t + 1 == conds) && "unit must be last");
727     defaultDestination = dest;
728     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
729   }
730 
731   // LLVM::SwitchOp takes a i32 type for the selector.
732   if (select.getSelector().getType() != rewriter.getI32Type())
733     selector =
734         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
735 
736   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
737       select, selector,
738       /*defaultDestination=*/defaultDestination,
739       /*defaultOperands=*/defaultOperands,
740       /*caseValues=*/caseValues,
741       /*caseDestinations=*/destinations,
742       /*caseOperands=*/destinationsOperands,
743       /*branchWeights=*/ArrayRef<int32_t>());
744 }
745 
746 /// conversion of fir::SelectOp to an if-then-else ladder
747 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
748   using FIROpConversion::FIROpConversion;
749 
750   mlir::LogicalResult
751   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
752                   mlir::ConversionPatternRewriter &rewriter) const override {
753     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
754     return success();
755   }
756 };
757 
758 /// `fir.load` --> `llvm.load`
759 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
760   using FIROpConversion::FIROpConversion;
761 
762   mlir::LogicalResult
763   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
764                   mlir::ConversionPatternRewriter &rewriter) const override {
765     // fir.box is a special case because it is considered as an ssa values in
766     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
767     // and fir.box end up being the same llvm types and loading a
768     // fir.ref<fir.box> is actually a no op in LLVM.
769     if (load.getType().isa<fir::BoxType>()) {
770       rewriter.replaceOp(load, adaptor.getOperands()[0]);
771     } else {
772       mlir::Type ty = convertType(load.getType());
773       ArrayRef<NamedAttribute> at = load->getAttrs();
774       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
775           load, ty, adaptor.getOperands(), at);
776     }
777     return success();
778   }
779 };
780 
781 /// conversion of fir::SelectRankOp to an if-then-else ladder
782 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
783   using FIROpConversion::FIROpConversion;
784 
785   mlir::LogicalResult
786   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
787                   mlir::ConversionPatternRewriter &rewriter) const override {
788     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
789     return success();
790   }
791 };
792 
793 /// `fir.store` --> `llvm.store`
794 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
795   using FIROpConversion::FIROpConversion;
796 
797   mlir::LogicalResult
798   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
799                   mlir::ConversionPatternRewriter &rewriter) const override {
800     if (store.value().getType().isa<fir::BoxType>()) {
801       // fir.box value is actually in memory, load it first before storing it.
802       mlir::Location loc = store.getLoc();
803       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
804       auto val = rewriter.create<mlir::LLVM::LoadOp>(
805           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
806           adaptor.getOperands()[0]);
807       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
808           store, val, adaptor.getOperands()[1]);
809     } else {
810       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
811           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
812     }
813     return success();
814   }
815 };
816 
817 /// convert to LLVM IR dialect `undef`
818 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
819   using FIROpConversion::FIROpConversion;
820 
821   mlir::LogicalResult
822   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
823                   mlir::ConversionPatternRewriter &rewriter) const override {
824     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
825         undef, convertType(undef.getType()));
826     return success();
827   }
828 };
829 
830 /// `fir.unreachable` --> `llvm.unreachable`
831 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
832   using FIROpConversion::FIROpConversion;
833 
834   mlir::LogicalResult
835   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
836                   mlir::ConversionPatternRewriter &rewriter) const override {
837     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
838     return success();
839   }
840 };
841 
842 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
843   using FIROpConversion::FIROpConversion;
844 
845   mlir::LogicalResult
846   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
847                   mlir::ConversionPatternRewriter &rewriter) const override {
848     auto ty = convertType(zero.getType());
849     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
850       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
851     } else if (ty.isa<mlir::IntegerType>()) {
852       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
853           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
854     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
855       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
856           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
857     } else {
858       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
859       return rewriter.notifyMatchFailure(
860           zero,
861           "conversion of fir.zero with aggregate type not implemented yet");
862     }
863     return success();
864   }
865 };
866 
867 // Code shared between insert_value and extract_value Ops.
868 struct ValueOpCommon {
869   // Translate the arguments pertaining to any multidimensional array to
870   // row-major order for LLVM-IR.
871   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
872                          mlir::Type ty) {
873     assert(ty && "type is null");
874     const auto end = attrs.size();
875     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
876       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
877         const auto dim = getDimension(seq);
878         if (dim > 1) {
879           auto ub = std::min(i + dim, end);
880           std::reverse(attrs.begin() + i, attrs.begin() + ub);
881           i += dim - 1;
882         }
883         ty = getArrayElementType(seq);
884       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
885         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
886       } else {
887         llvm_unreachable("index into invalid type");
888       }
889     }
890   }
891 
892   static llvm::SmallVector<mlir::Attribute>
893   collectIndices(mlir::ConversionPatternRewriter &rewriter,
894                  mlir::ArrayAttr arrAttr) {
895     llvm::SmallVector<mlir::Attribute> attrs;
896     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
897       if (i->isa<mlir::IntegerAttr>()) {
898         attrs.push_back(*i);
899       } else {
900         auto fieldName = i->cast<mlir::StringAttr>().getValue();
901         ++i;
902         auto ty = i->cast<mlir::TypeAttr>().getValue();
903         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
904         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
905       }
906     }
907     return attrs;
908   }
909 
910 private:
911   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
912     unsigned result = 1;
913     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
914          eleTy;
915          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
916       ++result;
917     return result;
918   }
919 
920   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
921     auto eleTy = ty.getElementType();
922     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
923       eleTy = arrTy.getElementType();
924     return eleTy;
925   }
926 };
927 
928 /// Extract a subobject value from an ssa-value of aggregate type
929 struct ExtractValueOpConversion
930     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
931       public ValueOpCommon {
932   using FIROpAndTypeConversion::FIROpAndTypeConversion;
933 
934   mlir::LogicalResult
935   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
936             mlir::ConversionPatternRewriter &rewriter) const override {
937     auto attrs = collectIndices(rewriter, extractVal.coor());
938     toRowMajor(attrs, adaptor.getOperands()[0].getType());
939     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
940     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
941         extractVal, ty, adaptor.getOperands()[0], position);
942     return success();
943   }
944 };
945 
946 /// InsertValue is the generalized instruction for the composition of new
947 /// aggregate type values.
948 struct InsertValueOpConversion
949     : public FIROpAndTypeConversion<fir::InsertValueOp>,
950       public ValueOpCommon {
951   using FIROpAndTypeConversion::FIROpAndTypeConversion;
952 
953   mlir::LogicalResult
954   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
955             mlir::ConversionPatternRewriter &rewriter) const override {
956     auto attrs = collectIndices(rewriter, insertVal.coor());
957     toRowMajor(attrs, adaptor.getOperands()[0].getType());
958     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
959     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
960         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
961         position);
962     return success();
963   }
964 };
965 
966 /// InsertOnRange inserts a value into a sequence over a range of offsets.
967 struct InsertOnRangeOpConversion
968     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
969   using FIROpAndTypeConversion::FIROpAndTypeConversion;
970 
971   // Increments an array of subscripts in a row major fasion.
972   void incrementSubscripts(const SmallVector<uint64_t> &dims,
973                            SmallVector<uint64_t> &subscripts) const {
974     for (size_t i = dims.size(); i > 0; --i) {
975       if (++subscripts[i - 1] < dims[i - 1]) {
976         return;
977       }
978       subscripts[i - 1] = 0;
979     }
980   }
981 
982   mlir::LogicalResult
983   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
984             mlir::ConversionPatternRewriter &rewriter) const override {
985 
986     llvm::SmallVector<uint64_t> dims;
987     auto type = adaptor.getOperands()[0].getType();
988 
989     // Iteratively extract the array dimensions from the type.
990     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
991       dims.push_back(t.getNumElements());
992       type = t.getElementType();
993     }
994 
995     SmallVector<uint64_t> lBounds;
996     SmallVector<uint64_t> uBounds;
997 
998     // Extract integer value from the attribute
999     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1000         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1001           return a.cast<IntegerAttr>().getInt();
1002         }));
1003 
1004     // Unzip the upper and lower bound and convert to a row major format.
1005     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1006       uBounds.push_back(*i++);
1007       lBounds.push_back(*i);
1008     }
1009 
1010     auto &subscripts = lBounds;
1011     auto loc = range.getLoc();
1012     mlir::Value lastOp = adaptor.getOperands()[0];
1013     mlir::Value insertVal = adaptor.getOperands()[1];
1014 
1015     auto i64Ty = rewriter.getI64Type();
1016     while (subscripts != uBounds) {
1017       // Convert uint64_t's to Attribute's.
1018       SmallVector<mlir::Attribute> subscriptAttrs;
1019       for (const auto &subscript : subscripts)
1020         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1021       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1022           loc, ty, lastOp, insertVal,
1023           ArrayAttr::get(range.getContext(), subscriptAttrs));
1024 
1025       incrementSubscripts(dims, subscripts);
1026     }
1027 
1028     // Convert uint64_t's to Attribute's.
1029     SmallVector<mlir::Attribute> subscriptAttrs;
1030     for (const auto &subscript : subscripts)
1031       subscriptAttrs.push_back(
1032           IntegerAttr::get(rewriter.getI64Type(), subscript));
1033     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1034 
1035     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1036         range, ty, lastOp, insertVal,
1037         ArrayAttr::get(range.getContext(), arrayRef));
1038 
1039     return success();
1040   }
1041 };
1042 
1043 //
1044 // Primitive operations on Complex types
1045 //
1046 
1047 /// Generate inline code for complex addition/subtraction
1048 template <typename LLVMOP, typename OPTY>
1049 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1050                                      mlir::ConversionPatternRewriter &rewriter,
1051                                      fir::LLVMTypeConverter &lowering) {
1052   mlir::Value a = opnds[0];
1053   mlir::Value b = opnds[1];
1054   auto loc = sumop.getLoc();
1055   auto ctx = sumop.getContext();
1056   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1057   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1058   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1059   mlir::Type ty = lowering.convertType(sumop.getType());
1060   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1061   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1062   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1063   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1064   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1065   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1066   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1067   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1068   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1069 }
1070 
1071 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1072   using FIROpConversion::FIROpConversion;
1073 
1074   mlir::LogicalResult
1075   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1076                   mlir::ConversionPatternRewriter &rewriter) const override {
1077     // given: (x + iy) + (x' + iy')
1078     // result: (x + x') + i(y + y')
1079     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1080                                             rewriter, lowerTy());
1081     rewriter.replaceOp(addc, r.getResult());
1082     return success();
1083   }
1084 };
1085 
1086 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1087   using FIROpConversion::FIROpConversion;
1088 
1089   mlir::LogicalResult
1090   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1091                   mlir::ConversionPatternRewriter &rewriter) const override {
1092     // given: (x + iy) - (x' + iy')
1093     // result: (x - x') + i(y - y')
1094     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1095                                             rewriter, lowerTy());
1096     rewriter.replaceOp(subc, r.getResult());
1097     return success();
1098   }
1099 };
1100 
1101 /// Inlined complex multiply
1102 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1103   using FIROpConversion::FIROpConversion;
1104 
1105   mlir::LogicalResult
1106   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1107                   mlir::ConversionPatternRewriter &rewriter) const override {
1108     // TODO: Can we use a call to __muldc3 ?
1109     // given: (x + iy) * (x' + iy')
1110     // result: (xx'-yy')+i(xy'+yx')
1111     mlir::Value a = adaptor.getOperands()[0];
1112     mlir::Value b = adaptor.getOperands()[1];
1113     auto loc = mulc.getLoc();
1114     auto *ctx = mulc.getContext();
1115     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1116     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1117     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1118     mlir::Type ty = convertType(mulc.getType());
1119     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1120     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1121     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1122     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1123     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1124     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1125     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1126     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1127     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1128     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1129     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1130     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1131     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1132     rewriter.replaceOp(mulc, r0.getResult());
1133     return success();
1134   }
1135 };
1136 
1137 /// Inlined complex division
1138 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1139   using FIROpConversion::FIROpConversion;
1140 
1141   mlir::LogicalResult
1142   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1143                   mlir::ConversionPatternRewriter &rewriter) const override {
1144     // TODO: Can we use a call to __divdc3 instead?
1145     // Just generate inline code for now.
1146     // given: (x + iy) / (x' + iy')
1147     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1148     mlir::Value a = adaptor.getOperands()[0];
1149     mlir::Value b = adaptor.getOperands()[1];
1150     auto loc = divc.getLoc();
1151     auto *ctx = divc.getContext();
1152     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1153     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1154     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1155     mlir::Type ty = convertType(divc.getType());
1156     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1157     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1158     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1159     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1160     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1161     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1162     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1163     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1164     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1165     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1166     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1167     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1168     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1169     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1170     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1171     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1172     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1173     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1174     rewriter.replaceOp(divc, r0.getResult());
1175     return success();
1176   }
1177 };
1178 
1179 /// Inlined complex negation
1180 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1181   using FIROpConversion::FIROpConversion;
1182 
1183   mlir::LogicalResult
1184   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1185                   mlir::ConversionPatternRewriter &rewriter) const override {
1186     // given: -(x + iy)
1187     // result: -x - iy
1188     auto *ctxt = neg.getContext();
1189     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1190     auto ty = convertType(neg.getType());
1191     auto loc = neg.getLoc();
1192     mlir::Value o0 = adaptor.getOperands()[0];
1193     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1194     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1195     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1196     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1197     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1198     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1199     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1200     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1201     return success();
1202   }
1203 };
1204 
1205 } // namespace
1206 
1207 namespace {
1208 /// Convert FIR dialect to LLVM dialect
1209 ///
1210 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1211 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1212 ///
1213 /// This pass is not complete yet. We are upstreaming it in small patches.
1214 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1215 public:
1216   mlir::ModuleOp getModule() { return getOperation(); }
1217 
1218   void runOnOperation() override final {
1219     auto mod = getModule();
1220     if (!forcedTargetTriple.empty()) {
1221       fir::setTargetTriple(mod, forcedTargetTriple);
1222     }
1223 
1224     auto *context = getModule().getContext();
1225     fir::LLVMTypeConverter typeConverter{getModule()};
1226     mlir::OwningRewritePatternList pattern(context);
1227     pattern.insert<
1228         AddcOpConversion, AddrOfOpConversion, AllocaOpConversion,
1229         BoxAddrOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
1230         BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
1231         BoxRankOpConversion, CallOpConversion, ConvertOpConversion,
1232         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
1233         DivcOpConversion, ExtractValueOpConversion, HasValueOpConversion,
1234         GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion,
1235         LoadOpConversion, NegcOpConversion, MulcOpConversion,
1236         SelectOpConversion, SelectRankOpConversion, StoreOpConversion,
1237         SubcOpConversion, UndefOpConversion, UnreachableOpConversion,
1238         ZeroOpConversion>(typeConverter);
1239     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1240     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1241                                                             pattern);
1242     mlir::ConversionTarget target{*context};
1243     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1244 
1245     // required NOPs for applying a full conversion
1246     target.addLegalOp<mlir::ModuleOp>();
1247 
1248     // apply the patterns
1249     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1250                                                std::move(pattern)))) {
1251       signalPassFailure();
1252     }
1253   }
1254 };
1255 } // namespace
1256 
1257 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1258   return std::make_unique<FIRToLLVMLowering>();
1259 }
1260