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 /// Create value signaling an absent optional argument in a call, e.g.
210 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
211 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
212   using FIROpConversion::FIROpConversion;
213 
214   mlir::LogicalResult
215   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
216                   mlir::ConversionPatternRewriter &rewriter) const override {
217     mlir::Type ty = convertType(absent.getType());
218     mlir::Location loc = absent.getLoc();
219 
220     if (absent.getType().isa<fir::BoxCharType>()) {
221       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
222       assert(!structTy.isOpaque() && !structTy.getBody().empty());
223       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
224       auto nullField =
225           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
226       mlir::MLIRContext *ctx = absent.getContext();
227       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
228       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
229           absent, ty, undefStruct, nullField, c0);
230     } else {
231       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
232     }
233     return success();
234   }
235 };
236 
237 // Lower `fir.address_of` operation to `llvm.address_of` operation.
238 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
239   using FIROpConversion::FIROpConversion;
240 
241   mlir::LogicalResult
242   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
243                   mlir::ConversionPatternRewriter &rewriter) const override {
244     auto ty = convertType(addr.getType());
245     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
246         addr, ty, addr.symbol().getRootReference().getValue());
247     return success();
248   }
249 };
250 } // namespace
251 
252 /// Lookup the function to compute the memory size of this parametric derived
253 /// type. The size of the object may depend on the LEN type parameters of the
254 /// derived type.
255 static mlir::LLVM::LLVMFuncOp
256 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
257                           mlir::ConversionPatternRewriter &rewriter) {
258   auto module = op->getParentOfType<mlir::ModuleOp>();
259   std::string name = recTy.getName().str() + "P.mem.size";
260   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
261 }
262 
263 namespace {
264 /// convert to LLVM IR dialect `alloca`
265 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
266   using FIROpConversion::FIROpConversion;
267 
268   mlir::LogicalResult
269   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
270                   mlir::ConversionPatternRewriter &rewriter) const override {
271     mlir::ValueRange operands = adaptor.getOperands();
272     auto loc = alloc.getLoc();
273     mlir::Type ity = lowerTy().indexType();
274     unsigned i = 0;
275     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
276     mlir::Type ty = convertType(alloc.getType());
277     mlir::Type resultTy = ty;
278     if (alloc.hasLenParams()) {
279       unsigned end = alloc.numLenParams();
280       llvm::SmallVector<mlir::Value> lenParams;
281       for (; i < end; ++i)
282         lenParams.push_back(operands[i]);
283       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
284       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
285         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
286             chrTy.getContext(), chrTy.getFKind());
287         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
288         assert(end == 1);
289         size = integerCast(loc, rewriter, ity, lenParams[0]);
290       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
291         mlir::LLVM::LLVMFuncOp memSizeFn =
292             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
293         if (!memSizeFn)
294           emitError(loc, "did not find allocation function");
295         mlir::NamedAttribute attr = rewriter.getNamedAttr(
296             "callee", mlir::SymbolRefAttr::get(memSizeFn));
297         auto call = rewriter.create<mlir::LLVM::CallOp>(
298             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
299         size = call.getResult(0);
300         ty = mlir::LLVM::LLVMPointerType::get(
301             mlir::IntegerType::get(alloc.getContext(), 8));
302       } else {
303         return emitError(loc, "unexpected type ")
304                << scalarType << " with type parameters";
305       }
306     }
307     if (alloc.hasShapeOperands()) {
308       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
309       // Scale the size by constant factors encoded in the array type.
310       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
311         fir::SequenceType::Extent constSize = 1;
312         for (auto extent : seqTy.getShape())
313           if (extent != fir::SequenceType::getUnknownExtent())
314             constSize *= extent;
315         mlir::Value constVal{
316             genConstantIndex(loc, ity, rewriter, constSize).getResult()};
317         size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
318       }
319       unsigned end = operands.size();
320       for (; i < end; ++i)
321         size = rewriter.create<mlir::LLVM::MulOp>(
322             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
323     }
324     if (ty == resultTy) {
325       // Do not emit the bitcast if ty and resultTy are the same.
326       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
327                                                         alloc->getAttrs());
328     } else {
329       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
330                                                       alloc->getAttrs());
331       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
332     }
333     return success();
334   }
335 };
336 
337 /// Lower `fir.box_addr` to the sequence of operations to extract the first
338 /// element of the box.
339 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
340   using FIROpConversion::FIROpConversion;
341 
342   mlir::LogicalResult
343   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
344                   mlir::ConversionPatternRewriter &rewriter) const override {
345     mlir::Value a = adaptor.getOperands()[0];
346     auto loc = boxaddr.getLoc();
347     mlir::Type ty = convertType(boxaddr.getType());
348     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
349       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
350     } else {
351       auto c0attr = rewriter.getI32IntegerAttr(0);
352       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
353       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
354                                                               c0);
355     }
356     return success();
357   }
358 };
359 
360 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
361 /// dimension infomartion from the boxed value.
362 /// Result in a triple set of GEPs and loads.
363 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
364   using FIROpConversion::FIROpConversion;
365 
366   mlir::LogicalResult
367   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
368                   mlir::ConversionPatternRewriter &rewriter) const override {
369     SmallVector<mlir::Type, 3> resultTypes = {
370         convertType(boxdims.getResult(0).getType()),
371         convertType(boxdims.getResult(1).getType()),
372         convertType(boxdims.getResult(2).getType()),
373     };
374     auto results =
375         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
376                        adaptor.getOperands()[1], rewriter);
377     rewriter.replaceOp(boxdims, results);
378     return success();
379   }
380 };
381 
382 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
383 /// an element in the boxed value.
384 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
385   using FIROpConversion::FIROpConversion;
386 
387   mlir::LogicalResult
388   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
389                   mlir::ConversionPatternRewriter &rewriter) const override {
390     mlir::Value a = adaptor.getOperands()[0];
391     auto loc = boxelesz.getLoc();
392     auto ty = convertType(boxelesz.getType());
393     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
394     rewriter.replaceOp(boxelesz, elemSize);
395     return success();
396   }
397 };
398 
399 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
400 /// boxed value was from an ALLOCATABLE entity.
401 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
402   using FIROpConversion::FIROpConversion;
403 
404   mlir::LogicalResult
405   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
406                   mlir::ConversionPatternRewriter &rewriter) const override {
407     mlir::Value box = adaptor.getOperands()[0];
408     auto loc = boxisalloc.getLoc();
409     mlir::Value check =
410         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
411     rewriter.replaceOp(boxisalloc, check);
412     return success();
413   }
414 };
415 
416 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
417 /// boxed is an array.
418 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
419   using FIROpConversion::FIROpConversion;
420 
421   mlir::LogicalResult
422   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
423                   mlir::ConversionPatternRewriter &rewriter) const override {
424     mlir::Value a = adaptor.getOperands()[0];
425     auto loc = boxisarray.getLoc();
426     auto rank =
427         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
428     auto c0 = genConstantOffset(loc, rewriter, 0);
429     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
430         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
431     return success();
432   }
433 };
434 
435 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
436 /// boxed value was from a POINTER entity.
437 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
438   using FIROpConversion::FIROpConversion;
439 
440   mlir::LogicalResult
441   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
442                   mlir::ConversionPatternRewriter &rewriter) const override {
443     mlir::Value box = adaptor.getOperands()[0];
444     auto loc = boxisptr.getLoc();
445     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
446     rewriter.replaceOp(boxisptr, check);
447     return success();
448   }
449 };
450 
451 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
452 /// the box.
453 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
454   using FIROpConversion::FIROpConversion;
455 
456   mlir::LogicalResult
457   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
458                   mlir::ConversionPatternRewriter &rewriter) const override {
459     mlir::Value a = adaptor.getOperands()[0];
460     auto loc = boxrank.getLoc();
461     mlir::Type ty = convertType(boxrank.getType());
462     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
463     rewriter.replaceOp(boxrank, result);
464     return success();
465   }
466 };
467 
468 // `fir.call` -> `llvm.call`
469 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
470   using FIROpConversion::FIROpConversion;
471 
472   mlir::LogicalResult
473   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
474                   mlir::ConversionPatternRewriter &rewriter) const override {
475     SmallVector<mlir::Type> resultTys;
476     for (auto r : call.getResults())
477       resultTys.push_back(convertType(r.getType()));
478     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
479         call, resultTys, adaptor.getOperands(), call->getAttrs());
480     return success();
481   }
482 };
483 
484 static mlir::Type getComplexEleTy(mlir::Type complex) {
485   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
486     return cc.getElementType();
487   return complex.cast<fir::ComplexType>().getElementType();
488 }
489 
490 /// Compare complex values
491 ///
492 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
493 ///
494 /// For completeness, all other comparison are done on the real component only.
495 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
496   using FIROpConversion::FIROpConversion;
497 
498   mlir::LogicalResult
499   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
500                   mlir::ConversionPatternRewriter &rewriter) const override {
501     mlir::ValueRange operands = adaptor.getOperands();
502     mlir::MLIRContext *ctxt = cmp.getContext();
503     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
504     mlir::Type resTy = convertType(cmp.getType());
505     mlir::Location loc = cmp.getLoc();
506     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
507     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
508                                        loc, eleTy, operands[0], pos0),
509                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
510                                        loc, eleTy, operands[1], pos0)};
511     auto rcp =
512         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
513     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
514     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
515                                        loc, eleTy, operands[0], pos1),
516                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
517                                        loc, eleTy, operands[1], pos1)};
518     auto icp =
519         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
520     SmallVector<mlir::Value, 2> cp{rcp, icp};
521     switch (cmp.getPredicate()) {
522     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
523       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
524       break;
525     case mlir::arith::CmpFPredicate::UNE: // .NE.
526       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
527       break;
528     default:
529       rewriter.replaceOp(cmp, rcp.getResult());
530       break;
531     }
532     return success();
533   }
534 };
535 
536 /// convert value of from-type to value of to-type
537 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
538   using FIROpConversion::FIROpConversion;
539 
540   static bool isFloatingPointTy(mlir::Type ty) {
541     return ty.isa<mlir::FloatType>();
542   }
543 
544   mlir::LogicalResult
545   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
546                   mlir::ConversionPatternRewriter &rewriter) const override {
547     auto fromTy = convertType(convert.value().getType());
548     auto toTy = convertType(convert.res().getType());
549     mlir::Value op0 = adaptor.getOperands()[0];
550     if (fromTy == toTy) {
551       rewriter.replaceOp(convert, op0);
552       return success();
553     }
554     auto loc = convert.getLoc();
555     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
556                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
557       if (fromBits == toBits) {
558         // TODO: Converting between two floating-point representations with the
559         // same bitwidth is not allowed for now.
560         mlir::emitError(loc,
561                         "cannot implicitly convert between two floating-point "
562                         "representations of the same bitwidth");
563         return {};
564       }
565       if (fromBits > toBits)
566         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
567       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
568     };
569     // Complex to complex conversion.
570     if (fir::isa_complex(convert.value().getType()) &&
571         fir::isa_complex(convert.res().getType())) {
572       // Special case: handle the conversion of a complex such that both the
573       // real and imaginary parts are converted together.
574       auto zero = mlir::ArrayAttr::get(convert.getContext(),
575                                        rewriter.getI32IntegerAttr(0));
576       auto one = mlir::ArrayAttr::get(convert.getContext(),
577                                       rewriter.getI32IntegerAttr(1));
578       auto ty = convertType(getComplexEleTy(convert.value().getType()));
579       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
580       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
581       auto nt = convertType(getComplexEleTy(convert.res().getType()));
582       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
583       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
584       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
585       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
586       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
587       auto i1 =
588           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
589       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
590                                                              ic, one);
591       return mlir::success();
592     }
593     // Floating point to floating point conversion.
594     if (isFloatingPointTy(fromTy)) {
595       if (isFloatingPointTy(toTy)) {
596         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
597         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
598         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
599         rewriter.replaceOp(convert, v);
600         return mlir::success();
601       }
602       if (toTy.isa<mlir::IntegerType>()) {
603         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
604         return mlir::success();
605       }
606     } else if (fromTy.isa<mlir::IntegerType>()) {
607       // Integer to integer conversion.
608       if (toTy.isa<mlir::IntegerType>()) {
609         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
610         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
611         assert(fromBits != toBits);
612         if (fromBits > toBits) {
613           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
614           return mlir::success();
615         }
616         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
617         return mlir::success();
618       }
619       // Integer to floating point conversion.
620       if (isFloatingPointTy(toTy)) {
621         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
622         return mlir::success();
623       }
624       // Integer to pointer conversion.
625       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
626         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
627         return mlir::success();
628       }
629     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
630       // Pointer to integer conversion.
631       if (toTy.isa<mlir::IntegerType>()) {
632         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
633         return mlir::success();
634       }
635       // Pointer to pointer conversion.
636       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
637         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
638         return mlir::success();
639       }
640     }
641     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
642   }
643 };
644 
645 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
646 /// table.
647 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
648   using FIROpConversion::FIROpConversion;
649 
650   mlir::LogicalResult
651   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
652                   mlir::ConversionPatternRewriter &rewriter) const override {
653     return rewriter.notifyMatchFailure(
654         dispatch, "fir.dispatch codegen is not implemented yet");
655   }
656 };
657 
658 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
659 /// derived type.
660 struct DispatchTableOpConversion
661     : public FIROpConversion<fir::DispatchTableOp> {
662   using FIROpConversion::FIROpConversion;
663 
664   mlir::LogicalResult
665   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
666                   mlir::ConversionPatternRewriter &rewriter) const override {
667     return rewriter.notifyMatchFailure(
668         dispTab, "fir.dispatch_table codegen is not implemented yet");
669   }
670 };
671 
672 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
673 /// method-name to a function.
674 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
675   using FIROpConversion::FIROpConversion;
676 
677   mlir::LogicalResult
678   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
679                   mlir::ConversionPatternRewriter &rewriter) const override {
680     return rewriter.notifyMatchFailure(
681         dtEnt, "fir.dt_entry codegen is not implemented yet");
682   }
683 };
684 
685 /// Lower `fir.global_len` operation.
686 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
687   using FIROpConversion::FIROpConversion;
688 
689   mlir::LogicalResult
690   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
691                   mlir::ConversionPatternRewriter &rewriter) const override {
692     return rewriter.notifyMatchFailure(
693         globalLen, "fir.global_len codegen is not implemented yet");
694   }
695 };
696 
697 /// Lower `fir.has_value` operation to `llvm.return` operation.
698 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
699   using FIROpConversion::FIROpConversion;
700 
701   mlir::LogicalResult
702   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
703                   mlir::ConversionPatternRewriter &rewriter) const override {
704     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
705     return success();
706   }
707 };
708 
709 /// Lower `fir.global` operation to `llvm.global` operation.
710 /// `fir.insert_on_range` operations are replaced with constant dense attribute
711 /// if they are applied on the full range.
712 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
713   using FIROpConversion::FIROpConversion;
714 
715   mlir::LogicalResult
716   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
717                   mlir::ConversionPatternRewriter &rewriter) const override {
718     auto tyAttr = convertType(global.getType());
719     if (global.getType().isa<fir::BoxType>())
720       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
721     auto loc = global.getLoc();
722     mlir::Attribute initAttr{};
723     if (global.initVal())
724       initAttr = global.initVal().getValue();
725     auto linkage = convertLinkage(global.linkName());
726     auto isConst = global.constant().hasValue();
727     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
728         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
729     auto &gr = g.getInitializerRegion();
730     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
731     if (!gr.empty()) {
732       // Replace insert_on_range with a constant dense attribute if the
733       // initialization is on the full range.
734       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
735       for (auto insertOp : insertOnRangeOps) {
736         if (isFullRange(insertOp.coor(), insertOp.getType())) {
737           auto seqTyAttr = convertType(insertOp.getType());
738           auto *op = insertOp.val().getDefiningOp();
739           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
740           if (!constant) {
741             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
742             if (!convertOp)
743               continue;
744             constant = cast<mlir::arith::ConstantOp>(
745                 convertOp.value().getDefiningOp());
746           }
747           mlir::Type vecType = mlir::VectorType::get(
748               insertOp.getType().getShape(), constant.getType());
749           auto denseAttr = mlir::DenseElementsAttr::get(
750               vecType.cast<ShapedType>(), constant.value());
751           rewriter.setInsertionPointAfter(insertOp);
752           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
753               insertOp, seqTyAttr, denseAttr);
754         }
755       }
756     }
757     rewriter.eraseOp(global);
758     return success();
759   }
760 
761   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
762     auto extents = seqTy.getShape();
763     if (indexes.size() / 2 != extents.size())
764       return false;
765     for (unsigned i = 0; i < indexes.size(); i += 2) {
766       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
767         return false;
768       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
769         return false;
770     }
771     return true;
772   }
773 
774   // TODO: String comparaison should be avoided. Replace linkName with an
775   // enumeration.
776   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
777     if (optLinkage.hasValue()) {
778       auto name = optLinkage.getValue();
779       if (name == "internal")
780         return mlir::LLVM::Linkage::Internal;
781       if (name == "linkonce")
782         return mlir::LLVM::Linkage::Linkonce;
783       if (name == "common")
784         return mlir::LLVM::Linkage::Common;
785       if (name == "weak")
786         return mlir::LLVM::Linkage::Weak;
787     }
788     return mlir::LLVM::Linkage::External;
789   }
790 };
791 
792 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
793                  Optional<mlir::ValueRange> destOps,
794                  mlir::ConversionPatternRewriter &rewriter,
795                  mlir::Block *newBlock) {
796   if (destOps.hasValue())
797     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
798                                           newBlock, mlir::ValueRange());
799   else
800     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
801 }
802 
803 template <typename A, typename B>
804 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
805              mlir::ConversionPatternRewriter &rewriter) {
806   if (destOps.hasValue())
807     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
808                                                   dest);
809   else
810     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
811 }
812 
813 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
814                        Optional<mlir::ValueRange> destOps,
815                        mlir::ConversionPatternRewriter &rewriter) {
816   auto *thisBlock = rewriter.getInsertionBlock();
817   auto *newBlock = createBlock(rewriter, dest);
818   rewriter.setInsertionPointToEnd(thisBlock);
819   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
820   rewriter.setInsertionPointToEnd(newBlock);
821 }
822 
823 /// Conversion of `fir.select_case`
824 ///
825 /// The `fir.select_case` operation is converted to a if-then-else ladder.
826 /// Depending on the case condition type, one or several comparison and
827 /// conditional branching can be generated.
828 ///
829 /// A a point value case such as `case(4)`, a lower bound case such as
830 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
831 /// simple comparison between the selector value and the constant value in the
832 /// case. The block associated with the case condition is then executed if
833 /// the comparison succeed otherwise it branch to the next block with the
834 /// comparison for the the next case conditon.
835 ///
836 /// A closed interval case condition such as `case(7:10)` is converted with a
837 /// first comparison and conditional branching for the lower bound. If
838 /// successful, it branch to a second block with the comparison for the
839 /// upper bound in the same case condition.
840 ///
841 /// TODO: lowering of CHARACTER type cases is not handled yet.
842 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
843   using FIROpConversion::FIROpConversion;
844 
845   mlir::LogicalResult
846   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
847                   mlir::ConversionPatternRewriter &rewriter) const override {
848     unsigned conds = caseOp.getNumConditions();
849     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
850     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
851     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
852     if (ty.isa<fir::CharacterType>())
853       return rewriter.notifyMatchFailure(caseOp,
854                                          "conversion of fir.select_case with "
855                                          "character type not implemented yet");
856     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
857     auto loc = caseOp.getLoc();
858     for (unsigned t = 0; t != conds; ++t) {
859       mlir::Block *dest = caseOp.getSuccessor(t);
860       llvm::Optional<mlir::ValueRange> destOps =
861           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
862       llvm::Optional<mlir::ValueRange> cmpOps =
863           *caseOp.getCompareOperands(adaptor.getOperands(), t);
864       mlir::Value caseArg = *(cmpOps.getValue().begin());
865       mlir::Attribute attr = cases[t];
866       if (attr.isa<fir::PointIntervalAttr>()) {
867         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
868             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
869         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
870         continue;
871       }
872       if (attr.isa<fir::LowerBoundAttr>()) {
873         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
874             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
875         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
876         continue;
877       }
878       if (attr.isa<fir::UpperBoundAttr>()) {
879         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
880             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
881         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
882         continue;
883       }
884       if (attr.isa<fir::ClosedIntervalAttr>()) {
885         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
886             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
887         auto *thisBlock = rewriter.getInsertionBlock();
888         auto *newBlock1 = createBlock(rewriter, dest);
889         auto *newBlock2 = createBlock(rewriter, dest);
890         rewriter.setInsertionPointToEnd(thisBlock);
891         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
892         rewriter.setInsertionPointToEnd(newBlock1);
893         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
894         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
895             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
896         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
897         rewriter.setInsertionPointToEnd(newBlock2);
898         continue;
899       }
900       assert(attr.isa<mlir::UnitAttr>());
901       assert((t + 1 == conds) && "unit must be last");
902       genBrOp(caseOp, dest, destOps, rewriter);
903     }
904     return success();
905   }
906 };
907 
908 template <typename OP>
909 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
910                            typename OP::Adaptor adaptor,
911                            mlir::ConversionPatternRewriter &rewriter) {
912   unsigned conds = select.getNumConditions();
913   auto cases = select.getCases().getValue();
914   mlir::Value selector = adaptor.selector();
915   auto loc = select.getLoc();
916   assert(conds > 0 && "select must have cases");
917 
918   llvm::SmallVector<mlir::Block *> destinations;
919   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
920   mlir::Block *defaultDestination;
921   mlir::ValueRange defaultOperands;
922   llvm::SmallVector<int32_t> caseValues;
923 
924   for (unsigned t = 0; t != conds; ++t) {
925     mlir::Block *dest = select.getSuccessor(t);
926     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
927     const mlir::Attribute &attr = cases[t];
928     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
929       destinations.push_back(dest);
930       destinationsOperands.push_back(destOps.hasValue() ? *destOps
931                                                         : ValueRange());
932       caseValues.push_back(intAttr.getInt());
933       continue;
934     }
935     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
936     assert((t + 1 == conds) && "unit must be last");
937     defaultDestination = dest;
938     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
939   }
940 
941   // LLVM::SwitchOp takes a i32 type for the selector.
942   if (select.getSelector().getType() != rewriter.getI32Type())
943     selector =
944         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
945 
946   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
947       select, selector,
948       /*defaultDestination=*/defaultDestination,
949       /*defaultOperands=*/defaultOperands,
950       /*caseValues=*/caseValues,
951       /*caseDestinations=*/destinations,
952       /*caseOperands=*/destinationsOperands,
953       /*branchWeights=*/ArrayRef<int32_t>());
954 }
955 
956 /// conversion of fir::SelectOp to an if-then-else ladder
957 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
958   using FIROpConversion::FIROpConversion;
959 
960   mlir::LogicalResult
961   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
962                   mlir::ConversionPatternRewriter &rewriter) const override {
963     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
964     return success();
965   }
966 };
967 
968 /// `fir.load` --> `llvm.load`
969 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
970   using FIROpConversion::FIROpConversion;
971 
972   mlir::LogicalResult
973   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
974                   mlir::ConversionPatternRewriter &rewriter) const override {
975     // fir.box is a special case because it is considered as an ssa values in
976     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
977     // and fir.box end up being the same llvm types and loading a
978     // fir.ref<fir.box> is actually a no op in LLVM.
979     if (load.getType().isa<fir::BoxType>()) {
980       rewriter.replaceOp(load, adaptor.getOperands()[0]);
981     } else {
982       mlir::Type ty = convertType(load.getType());
983       ArrayRef<NamedAttribute> at = load->getAttrs();
984       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
985           load, ty, adaptor.getOperands(), at);
986     }
987     return success();
988   }
989 };
990 
991 /// Lower `fir.select_type` to LLVM IR dialect.
992 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
993   using FIROpConversion::FIROpConversion;
994 
995   mlir::LogicalResult
996   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
997                   mlir::ConversionPatternRewriter &rewriter) const override {
998     return rewriter.notifyMatchFailure(
999         select, "fir.select_type codegen is not implemented yet");
1000   }
1001 };
1002 
1003 /// conversion of fir::SelectRankOp to an if-then-else ladder
1004 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1005   using FIROpConversion::FIROpConversion;
1006 
1007   mlir::LogicalResult
1008   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1009                   mlir::ConversionPatternRewriter &rewriter) const override {
1010     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1011     return success();
1012   }
1013 };
1014 
1015 /// `fir.store` --> `llvm.store`
1016 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1017   using FIROpConversion::FIROpConversion;
1018 
1019   mlir::LogicalResult
1020   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1021                   mlir::ConversionPatternRewriter &rewriter) const override {
1022     if (store.value().getType().isa<fir::BoxType>()) {
1023       // fir.box value is actually in memory, load it first before storing it.
1024       mlir::Location loc = store.getLoc();
1025       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1026       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1027           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1028           adaptor.getOperands()[0]);
1029       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1030           store, val, adaptor.getOperands()[1]);
1031     } else {
1032       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1033           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1034     }
1035     return success();
1036   }
1037 };
1038 
1039 /// convert to LLVM IR dialect `undef`
1040 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1041   using FIROpConversion::FIROpConversion;
1042 
1043   mlir::LogicalResult
1044   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1045                   mlir::ConversionPatternRewriter &rewriter) const override {
1046     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1047         undef, convertType(undef.getType()));
1048     return success();
1049   }
1050 };
1051 
1052 /// `fir.unreachable` --> `llvm.unreachable`
1053 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1054   using FIROpConversion::FIROpConversion;
1055 
1056   mlir::LogicalResult
1057   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1058                   mlir::ConversionPatternRewriter &rewriter) const override {
1059     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1060     return success();
1061   }
1062 };
1063 
1064 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1065   using FIROpConversion::FIROpConversion;
1066 
1067   mlir::LogicalResult
1068   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1069                   mlir::ConversionPatternRewriter &rewriter) const override {
1070     auto ty = convertType(zero.getType());
1071     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1072       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1073     } else if (ty.isa<mlir::IntegerType>()) {
1074       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1075           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1076     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1077       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1078           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1079     } else {
1080       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1081       return rewriter.notifyMatchFailure(
1082           zero,
1083           "conversion of fir.zero with aggregate type not implemented yet");
1084     }
1085     return success();
1086   }
1087 };
1088 
1089 // Code shared between insert_value and extract_value Ops.
1090 struct ValueOpCommon {
1091   // Translate the arguments pertaining to any multidimensional array to
1092   // row-major order for LLVM-IR.
1093   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1094                          mlir::Type ty) {
1095     assert(ty && "type is null");
1096     const auto end = attrs.size();
1097     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1098       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1099         const auto dim = getDimension(seq);
1100         if (dim > 1) {
1101           auto ub = std::min(i + dim, end);
1102           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1103           i += dim - 1;
1104         }
1105         ty = getArrayElementType(seq);
1106       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1107         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1108       } else {
1109         llvm_unreachable("index into invalid type");
1110       }
1111     }
1112   }
1113 
1114   static llvm::SmallVector<mlir::Attribute>
1115   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1116                  mlir::ArrayAttr arrAttr) {
1117     llvm::SmallVector<mlir::Attribute> attrs;
1118     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1119       if (i->isa<mlir::IntegerAttr>()) {
1120         attrs.push_back(*i);
1121       } else {
1122         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1123         ++i;
1124         auto ty = i->cast<mlir::TypeAttr>().getValue();
1125         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1126         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1127       }
1128     }
1129     return attrs;
1130   }
1131 
1132 private:
1133   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1134     unsigned result = 1;
1135     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1136          eleTy;
1137          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1138       ++result;
1139     return result;
1140   }
1141 
1142   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1143     auto eleTy = ty.getElementType();
1144     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1145       eleTy = arrTy.getElementType();
1146     return eleTy;
1147   }
1148 };
1149 
1150 /// Extract a subobject value from an ssa-value of aggregate type
1151 struct ExtractValueOpConversion
1152     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1153       public ValueOpCommon {
1154   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1155 
1156   mlir::LogicalResult
1157   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1158             mlir::ConversionPatternRewriter &rewriter) const override {
1159     auto attrs = collectIndices(rewriter, extractVal.coor());
1160     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1161     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1162     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1163         extractVal, ty, adaptor.getOperands()[0], position);
1164     return success();
1165   }
1166 };
1167 
1168 /// InsertValue is the generalized instruction for the composition of new
1169 /// aggregate type values.
1170 struct InsertValueOpConversion
1171     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1172       public ValueOpCommon {
1173   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1174 
1175   mlir::LogicalResult
1176   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1177             mlir::ConversionPatternRewriter &rewriter) const override {
1178     auto attrs = collectIndices(rewriter, insertVal.coor());
1179     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1180     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1181     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1182         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1183         position);
1184     return success();
1185   }
1186 };
1187 
1188 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1189 struct InsertOnRangeOpConversion
1190     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1191   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1192 
1193   // Increments an array of subscripts in a row major fasion.
1194   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1195                            SmallVector<uint64_t> &subscripts) const {
1196     for (size_t i = dims.size(); i > 0; --i) {
1197       if (++subscripts[i - 1] < dims[i - 1]) {
1198         return;
1199       }
1200       subscripts[i - 1] = 0;
1201     }
1202   }
1203 
1204   mlir::LogicalResult
1205   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1206             mlir::ConversionPatternRewriter &rewriter) const override {
1207 
1208     llvm::SmallVector<uint64_t> dims;
1209     auto type = adaptor.getOperands()[0].getType();
1210 
1211     // Iteratively extract the array dimensions from the type.
1212     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1213       dims.push_back(t.getNumElements());
1214       type = t.getElementType();
1215     }
1216 
1217     SmallVector<uint64_t> lBounds;
1218     SmallVector<uint64_t> uBounds;
1219 
1220     // Extract integer value from the attribute
1221     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1222         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1223           return a.cast<IntegerAttr>().getInt();
1224         }));
1225 
1226     // Unzip the upper and lower bound and convert to a row major format.
1227     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1228       uBounds.push_back(*i++);
1229       lBounds.push_back(*i);
1230     }
1231 
1232     auto &subscripts = lBounds;
1233     auto loc = range.getLoc();
1234     mlir::Value lastOp = adaptor.getOperands()[0];
1235     mlir::Value insertVal = adaptor.getOperands()[1];
1236 
1237     auto i64Ty = rewriter.getI64Type();
1238     while (subscripts != uBounds) {
1239       // Convert uint64_t's to Attribute's.
1240       SmallVector<mlir::Attribute> subscriptAttrs;
1241       for (const auto &subscript : subscripts)
1242         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1243       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1244           loc, ty, lastOp, insertVal,
1245           ArrayAttr::get(range.getContext(), subscriptAttrs));
1246 
1247       incrementSubscripts(dims, subscripts);
1248     }
1249 
1250     // Convert uint64_t's to Attribute's.
1251     SmallVector<mlir::Attribute> subscriptAttrs;
1252     for (const auto &subscript : subscripts)
1253       subscriptAttrs.push_back(
1254           IntegerAttr::get(rewriter.getI64Type(), subscript));
1255     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1256 
1257     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1258         range, ty, lastOp, insertVal,
1259         ArrayAttr::get(range.getContext(), arrayRef));
1260 
1261     return success();
1262   }
1263 };
1264 
1265 //
1266 // Primitive operations on Complex types
1267 //
1268 
1269 /// Generate inline code for complex addition/subtraction
1270 template <typename LLVMOP, typename OPTY>
1271 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1272                                      mlir::ConversionPatternRewriter &rewriter,
1273                                      fir::LLVMTypeConverter &lowering) {
1274   mlir::Value a = opnds[0];
1275   mlir::Value b = opnds[1];
1276   auto loc = sumop.getLoc();
1277   auto ctx = sumop.getContext();
1278   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1279   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1280   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1281   mlir::Type ty = lowering.convertType(sumop.getType());
1282   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1283   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1284   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1285   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1286   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1287   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1288   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1289   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1290   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1291 }
1292 
1293 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1294   using FIROpConversion::FIROpConversion;
1295 
1296   mlir::LogicalResult
1297   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1298                   mlir::ConversionPatternRewriter &rewriter) const override {
1299     // given: (x + iy) + (x' + iy')
1300     // result: (x + x') + i(y + y')
1301     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1302                                             rewriter, lowerTy());
1303     rewriter.replaceOp(addc, r.getResult());
1304     return success();
1305   }
1306 };
1307 
1308 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1309   using FIROpConversion::FIROpConversion;
1310 
1311   mlir::LogicalResult
1312   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1313                   mlir::ConversionPatternRewriter &rewriter) const override {
1314     // given: (x + iy) - (x' + iy')
1315     // result: (x - x') + i(y - y')
1316     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1317                                             rewriter, lowerTy());
1318     rewriter.replaceOp(subc, r.getResult());
1319     return success();
1320   }
1321 };
1322 
1323 /// Inlined complex multiply
1324 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1325   using FIROpConversion::FIROpConversion;
1326 
1327   mlir::LogicalResult
1328   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1329                   mlir::ConversionPatternRewriter &rewriter) const override {
1330     // TODO: Can we use a call to __muldc3 ?
1331     // given: (x + iy) * (x' + iy')
1332     // result: (xx'-yy')+i(xy'+yx')
1333     mlir::Value a = adaptor.getOperands()[0];
1334     mlir::Value b = adaptor.getOperands()[1];
1335     auto loc = mulc.getLoc();
1336     auto *ctx = mulc.getContext();
1337     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1338     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1339     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1340     mlir::Type ty = convertType(mulc.getType());
1341     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1342     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1343     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1344     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1345     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1346     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1347     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1348     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1349     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1350     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1351     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1352     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1353     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1354     rewriter.replaceOp(mulc, r0.getResult());
1355     return success();
1356   }
1357 };
1358 
1359 /// Inlined complex division
1360 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1361   using FIROpConversion::FIROpConversion;
1362 
1363   mlir::LogicalResult
1364   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1365                   mlir::ConversionPatternRewriter &rewriter) const override {
1366     // TODO: Can we use a call to __divdc3 instead?
1367     // Just generate inline code for now.
1368     // given: (x + iy) / (x' + iy')
1369     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1370     mlir::Value a = adaptor.getOperands()[0];
1371     mlir::Value b = adaptor.getOperands()[1];
1372     auto loc = divc.getLoc();
1373     auto *ctx = divc.getContext();
1374     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1375     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1376     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1377     mlir::Type ty = convertType(divc.getType());
1378     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1379     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1380     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1381     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1382     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1383     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1384     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1385     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1386     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1387     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1388     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1389     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1390     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1391     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1392     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1393     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1394     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1395     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1396     rewriter.replaceOp(divc, r0.getResult());
1397     return success();
1398   }
1399 };
1400 
1401 /// Inlined complex negation
1402 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1403   using FIROpConversion::FIROpConversion;
1404 
1405   mlir::LogicalResult
1406   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1407                   mlir::ConversionPatternRewriter &rewriter) const override {
1408     // given: -(x + iy)
1409     // result: -x - iy
1410     auto *ctxt = neg.getContext();
1411     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1412     auto ty = convertType(neg.getType());
1413     auto loc = neg.getLoc();
1414     mlir::Value o0 = adaptor.getOperands()[0];
1415     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1416     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1417     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1418     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1419     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1420     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1421     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1422     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1423     return success();
1424   }
1425 };
1426 
1427 /// `fir.is_present` -->
1428 /// ```
1429 ///  %0 = llvm.mlir.constant(0 : i64)
1430 ///  %1 = llvm.ptrtoint %0
1431 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1432 /// ```
1433 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1434   using FIROpConversion::FIROpConversion;
1435 
1436   mlir::LogicalResult
1437   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1438                   mlir::ConversionPatternRewriter &rewriter) const override {
1439     mlir::Type idxTy = lowerTy().indexType();
1440     mlir::Location loc = isPresent.getLoc();
1441     auto ptr = adaptor.getOperands()[0];
1442 
1443     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1444       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1445       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1446 
1447       mlir::Type ty = structTy.getBody()[0];
1448       mlir::MLIRContext *ctx = isPresent.getContext();
1449       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1450       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1451     }
1452     mlir::LLVM::ConstantOp c0 =
1453         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1454     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1455     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1456         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1457 
1458     return success();
1459   }
1460 };
1461 
1462 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1463 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1464 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1465 /// element is the length of the character buffer (`#n`).
1466 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1467   using FIROpConversion::FIROpConversion;
1468 
1469   mlir::LogicalResult
1470   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1471                   mlir::ConversionPatternRewriter &rewriter) const override {
1472     mlir::ValueRange operands = adaptor.getOperands();
1473     MLIRContext *ctx = emboxChar.getContext();
1474 
1475     mlir::Value charBuffer = operands[0];
1476     mlir::Value charBufferLen = operands[1];
1477 
1478     mlir::Location loc = emboxChar.getLoc();
1479     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1480     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1481 
1482     mlir::Type lenTy =
1483         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1484     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1485 
1486     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1487     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1488     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1489         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1490     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1491         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1492 
1493     return success();
1494   }
1495 };
1496 
1497 /// Construct an `llvm.extractvalue` instruction. It will return value at
1498 /// element \p x from  \p tuple.
1499 mlir::LLVM::ExtractValueOp
1500 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
1501                          mlir::ConversionPatternRewriter &rewriter,
1502                          mlir::MLIRContext *ctx, int x) {
1503   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
1504   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
1505   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
1506 }
1507 
1508 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
1509 /// the character buffer and one for the buffer length.
1510 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
1511   using FIROpConversion::FIROpConversion;
1512 
1513   mlir::LogicalResult
1514   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
1515                   mlir::ConversionPatternRewriter &rewriter) const override {
1516     MLIRContext *ctx = unboxchar.getContext();
1517 
1518     mlir::Type lenTy = convertType(unboxchar.getType(1));
1519     mlir::Value tuple = adaptor.getOperands()[0];
1520     mlir::Type tupleTy = tuple.getType();
1521 
1522     mlir::Location loc = unboxchar.getLoc();
1523     mlir::Value ptrToBuffer =
1524         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
1525 
1526     mlir::LLVM::ExtractValueOp len =
1527         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
1528     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
1529 
1530     rewriter.replaceOp(unboxchar,
1531                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
1532     return success();
1533   }
1534 };
1535 
1536 } // namespace
1537 
1538 namespace {
1539 /// Convert FIR dialect to LLVM dialect
1540 ///
1541 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1542 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1543 ///
1544 /// This pass is not complete yet. We are upstreaming it in small patches.
1545 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1546 public:
1547   mlir::ModuleOp getModule() { return getOperation(); }
1548 
1549   void runOnOperation() override final {
1550     auto mod = getModule();
1551     if (!forcedTargetTriple.empty()) {
1552       fir::setTargetTriple(mod, forcedTargetTriple);
1553     }
1554 
1555     auto *context = getModule().getContext();
1556     fir::LLVMTypeConverter typeConverter{getModule()};
1557     mlir::OwningRewritePatternList pattern(context);
1558     pattern.insert<
1559         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1560         AllocaOpConversion, BoxAddrOpConversion, BoxDimsOpConversion,
1561         BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
1562         BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion,
1563         CmpcOpConversion, ConvertOpConversion, DispatchOpConversion,
1564         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
1565         EmboxCharOpConversion, ExtractValueOpConversion, HasValueOpConversion,
1566         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
1567         InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion,
1568         NegcOpConversion, MulcOpConversion, SelectCaseOpConversion,
1569         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
1570         StoreOpConversion, SubcOpConversion, UnboxCharOpConversion,
1571         UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
1572         typeConverter);
1573 
1574     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1575     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1576                                                             pattern);
1577     mlir::ConversionTarget target{*context};
1578     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1579 
1580     // required NOPs for applying a full conversion
1581     target.addLegalOp<mlir::ModuleOp>();
1582 
1583     // apply the patterns
1584     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1585                                                std::move(pattern)))) {
1586       signalPassFailure();
1587     }
1588   }
1589 };
1590 } // namespace
1591 
1592 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1593   return std::make_unique<FIRToLLVMLowering>();
1594 }
1595