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