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.gentypedesc` to a global constant.
698 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
699   using FIROpConversion::FIROpConversion;
700 
701   mlir::LogicalResult
702   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
703                   mlir::ConversionPatternRewriter &rewriter) const override {
704     return rewriter.notifyMatchFailure(
705         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
706   }
707 };
708 
709 /// Lower `fir.has_value` operation to `llvm.return` operation.
710 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
711   using FIROpConversion::FIROpConversion;
712 
713   mlir::LogicalResult
714   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
715                   mlir::ConversionPatternRewriter &rewriter) const override {
716     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
717     return success();
718   }
719 };
720 
721 /// Lower `fir.global` operation to `llvm.global` operation.
722 /// `fir.insert_on_range` operations are replaced with constant dense attribute
723 /// if they are applied on the full range.
724 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
725   using FIROpConversion::FIROpConversion;
726 
727   mlir::LogicalResult
728   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
729                   mlir::ConversionPatternRewriter &rewriter) const override {
730     auto tyAttr = convertType(global.getType());
731     if (global.getType().isa<fir::BoxType>())
732       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
733     auto loc = global.getLoc();
734     mlir::Attribute initAttr{};
735     if (global.initVal())
736       initAttr = global.initVal().getValue();
737     auto linkage = convertLinkage(global.linkName());
738     auto isConst = global.constant().hasValue();
739     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
740         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
741     auto &gr = g.getInitializerRegion();
742     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
743     if (!gr.empty()) {
744       // Replace insert_on_range with a constant dense attribute if the
745       // initialization is on the full range.
746       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
747       for (auto insertOp : insertOnRangeOps) {
748         if (isFullRange(insertOp.coor(), insertOp.getType())) {
749           auto seqTyAttr = convertType(insertOp.getType());
750           auto *op = insertOp.val().getDefiningOp();
751           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
752           if (!constant) {
753             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
754             if (!convertOp)
755               continue;
756             constant = cast<mlir::arith::ConstantOp>(
757                 convertOp.value().getDefiningOp());
758           }
759           mlir::Type vecType = mlir::VectorType::get(
760               insertOp.getType().getShape(), constant.getType());
761           auto denseAttr = mlir::DenseElementsAttr::get(
762               vecType.cast<ShapedType>(), constant.value());
763           rewriter.setInsertionPointAfter(insertOp);
764           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
765               insertOp, seqTyAttr, denseAttr);
766         }
767       }
768     }
769     rewriter.eraseOp(global);
770     return success();
771   }
772 
773   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
774     auto extents = seqTy.getShape();
775     if (indexes.size() / 2 != extents.size())
776       return false;
777     for (unsigned i = 0; i < indexes.size(); i += 2) {
778       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
779         return false;
780       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
781         return false;
782     }
783     return true;
784   }
785 
786   // TODO: String comparaison should be avoided. Replace linkName with an
787   // enumeration.
788   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
789     if (optLinkage.hasValue()) {
790       auto name = optLinkage.getValue();
791       if (name == "internal")
792         return mlir::LLVM::Linkage::Internal;
793       if (name == "linkonce")
794         return mlir::LLVM::Linkage::Linkonce;
795       if (name == "common")
796         return mlir::LLVM::Linkage::Common;
797       if (name == "weak")
798         return mlir::LLVM::Linkage::Weak;
799     }
800     return mlir::LLVM::Linkage::External;
801   }
802 };
803 
804 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
805                  Optional<mlir::ValueRange> destOps,
806                  mlir::ConversionPatternRewriter &rewriter,
807                  mlir::Block *newBlock) {
808   if (destOps.hasValue())
809     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
810                                           newBlock, mlir::ValueRange());
811   else
812     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
813 }
814 
815 template <typename A, typename B>
816 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
817              mlir::ConversionPatternRewriter &rewriter) {
818   if (destOps.hasValue())
819     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
820                                                   dest);
821   else
822     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
823 }
824 
825 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
826                        Optional<mlir::ValueRange> destOps,
827                        mlir::ConversionPatternRewriter &rewriter) {
828   auto *thisBlock = rewriter.getInsertionBlock();
829   auto *newBlock = createBlock(rewriter, dest);
830   rewriter.setInsertionPointToEnd(thisBlock);
831   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
832   rewriter.setInsertionPointToEnd(newBlock);
833 }
834 
835 /// Conversion of `fir.select_case`
836 ///
837 /// The `fir.select_case` operation is converted to a if-then-else ladder.
838 /// Depending on the case condition type, one or several comparison and
839 /// conditional branching can be generated.
840 ///
841 /// A a point value case such as `case(4)`, a lower bound case such as
842 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
843 /// simple comparison between the selector value and the constant value in the
844 /// case. The block associated with the case condition is then executed if
845 /// the comparison succeed otherwise it branch to the next block with the
846 /// comparison for the the next case conditon.
847 ///
848 /// A closed interval case condition such as `case(7:10)` is converted with a
849 /// first comparison and conditional branching for the lower bound. If
850 /// successful, it branch to a second block with the comparison for the
851 /// upper bound in the same case condition.
852 ///
853 /// TODO: lowering of CHARACTER type cases is not handled yet.
854 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
855   using FIROpConversion::FIROpConversion;
856 
857   mlir::LogicalResult
858   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
859                   mlir::ConversionPatternRewriter &rewriter) const override {
860     unsigned conds = caseOp.getNumConditions();
861     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
862     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
863     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
864     if (ty.isa<fir::CharacterType>())
865       return rewriter.notifyMatchFailure(caseOp,
866                                          "conversion of fir.select_case with "
867                                          "character type not implemented yet");
868     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
869     auto loc = caseOp.getLoc();
870     for (unsigned t = 0; t != conds; ++t) {
871       mlir::Block *dest = caseOp.getSuccessor(t);
872       llvm::Optional<mlir::ValueRange> destOps =
873           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
874       llvm::Optional<mlir::ValueRange> cmpOps =
875           *caseOp.getCompareOperands(adaptor.getOperands(), t);
876       mlir::Value caseArg = *(cmpOps.getValue().begin());
877       mlir::Attribute attr = cases[t];
878       if (attr.isa<fir::PointIntervalAttr>()) {
879         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
880             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
881         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
882         continue;
883       }
884       if (attr.isa<fir::LowerBoundAttr>()) {
885         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
886             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
887         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
888         continue;
889       }
890       if (attr.isa<fir::UpperBoundAttr>()) {
891         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
892             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
893         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
894         continue;
895       }
896       if (attr.isa<fir::ClosedIntervalAttr>()) {
897         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
898             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
899         auto *thisBlock = rewriter.getInsertionBlock();
900         auto *newBlock1 = createBlock(rewriter, dest);
901         auto *newBlock2 = createBlock(rewriter, dest);
902         rewriter.setInsertionPointToEnd(thisBlock);
903         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
904         rewriter.setInsertionPointToEnd(newBlock1);
905         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
906         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
907             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
908         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
909         rewriter.setInsertionPointToEnd(newBlock2);
910         continue;
911       }
912       assert(attr.isa<mlir::UnitAttr>());
913       assert((t + 1 == conds) && "unit must be last");
914       genBrOp(caseOp, dest, destOps, rewriter);
915     }
916     return success();
917   }
918 };
919 
920 template <typename OP>
921 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
922                            typename OP::Adaptor adaptor,
923                            mlir::ConversionPatternRewriter &rewriter) {
924   unsigned conds = select.getNumConditions();
925   auto cases = select.getCases().getValue();
926   mlir::Value selector = adaptor.selector();
927   auto loc = select.getLoc();
928   assert(conds > 0 && "select must have cases");
929 
930   llvm::SmallVector<mlir::Block *> destinations;
931   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
932   mlir::Block *defaultDestination;
933   mlir::ValueRange defaultOperands;
934   llvm::SmallVector<int32_t> caseValues;
935 
936   for (unsigned t = 0; t != conds; ++t) {
937     mlir::Block *dest = select.getSuccessor(t);
938     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
939     const mlir::Attribute &attr = cases[t];
940     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
941       destinations.push_back(dest);
942       destinationsOperands.push_back(destOps.hasValue() ? *destOps
943                                                         : ValueRange());
944       caseValues.push_back(intAttr.getInt());
945       continue;
946     }
947     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
948     assert((t + 1 == conds) && "unit must be last");
949     defaultDestination = dest;
950     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
951   }
952 
953   // LLVM::SwitchOp takes a i32 type for the selector.
954   if (select.getSelector().getType() != rewriter.getI32Type())
955     selector =
956         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
957 
958   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
959       select, selector,
960       /*defaultDestination=*/defaultDestination,
961       /*defaultOperands=*/defaultOperands,
962       /*caseValues=*/caseValues,
963       /*caseDestinations=*/destinations,
964       /*caseOperands=*/destinationsOperands,
965       /*branchWeights=*/ArrayRef<int32_t>());
966 }
967 
968 /// conversion of fir::SelectOp to an if-then-else ladder
969 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
970   using FIROpConversion::FIROpConversion;
971 
972   mlir::LogicalResult
973   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
974                   mlir::ConversionPatternRewriter &rewriter) const override {
975     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
976     return success();
977   }
978 };
979 
980 /// `fir.load` --> `llvm.load`
981 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
982   using FIROpConversion::FIROpConversion;
983 
984   mlir::LogicalResult
985   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
986                   mlir::ConversionPatternRewriter &rewriter) const override {
987     // fir.box is a special case because it is considered as an ssa values in
988     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
989     // and fir.box end up being the same llvm types and loading a
990     // fir.ref<fir.box> is actually a no op in LLVM.
991     if (load.getType().isa<fir::BoxType>()) {
992       rewriter.replaceOp(load, adaptor.getOperands()[0]);
993     } else {
994       mlir::Type ty = convertType(load.getType());
995       ArrayRef<NamedAttribute> at = load->getAttrs();
996       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
997           load, ty, adaptor.getOperands(), at);
998     }
999     return success();
1000   }
1001 };
1002 
1003 /// Lower `fir.select_type` to LLVM IR dialect.
1004 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1005   using FIROpConversion::FIROpConversion;
1006 
1007   mlir::LogicalResult
1008   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1009                   mlir::ConversionPatternRewriter &rewriter) const override {
1010     return rewriter.notifyMatchFailure(
1011         select, "fir.select_type codegen is not implemented yet");
1012   }
1013 };
1014 
1015 /// conversion of fir::SelectRankOp to an if-then-else ladder
1016 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1017   using FIROpConversion::FIROpConversion;
1018 
1019   mlir::LogicalResult
1020   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1021                   mlir::ConversionPatternRewriter &rewriter) const override {
1022     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1023     return success();
1024   }
1025 };
1026 
1027 /// `fir.store` --> `llvm.store`
1028 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1029   using FIROpConversion::FIROpConversion;
1030 
1031   mlir::LogicalResult
1032   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1033                   mlir::ConversionPatternRewriter &rewriter) const override {
1034     if (store.value().getType().isa<fir::BoxType>()) {
1035       // fir.box value is actually in memory, load it first before storing it.
1036       mlir::Location loc = store.getLoc();
1037       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1038       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1039           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1040           adaptor.getOperands()[0]);
1041       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1042           store, val, adaptor.getOperands()[1]);
1043     } else {
1044       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1045           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1046     }
1047     return success();
1048   }
1049 };
1050 
1051 /// convert to LLVM IR dialect `undef`
1052 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1053   using FIROpConversion::FIROpConversion;
1054 
1055   mlir::LogicalResult
1056   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1057                   mlir::ConversionPatternRewriter &rewriter) const override {
1058     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1059         undef, convertType(undef.getType()));
1060     return success();
1061   }
1062 };
1063 
1064 /// `fir.unreachable` --> `llvm.unreachable`
1065 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1066   using FIROpConversion::FIROpConversion;
1067 
1068   mlir::LogicalResult
1069   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1070                   mlir::ConversionPatternRewriter &rewriter) const override {
1071     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1072     return success();
1073   }
1074 };
1075 
1076 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1077   using FIROpConversion::FIROpConversion;
1078 
1079   mlir::LogicalResult
1080   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1081                   mlir::ConversionPatternRewriter &rewriter) const override {
1082     auto ty = convertType(zero.getType());
1083     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1084       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1085     } else if (ty.isa<mlir::IntegerType>()) {
1086       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1087           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1088     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1089       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1090           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1091     } else {
1092       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1093       return rewriter.notifyMatchFailure(
1094           zero,
1095           "conversion of fir.zero with aggregate type not implemented yet");
1096     }
1097     return success();
1098   }
1099 };
1100 
1101 // Code shared between insert_value and extract_value Ops.
1102 struct ValueOpCommon {
1103   // Translate the arguments pertaining to any multidimensional array to
1104   // row-major order for LLVM-IR.
1105   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1106                          mlir::Type ty) {
1107     assert(ty && "type is null");
1108     const auto end = attrs.size();
1109     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1110       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1111         const auto dim = getDimension(seq);
1112         if (dim > 1) {
1113           auto ub = std::min(i + dim, end);
1114           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1115           i += dim - 1;
1116         }
1117         ty = getArrayElementType(seq);
1118       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1119         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1120       } else {
1121         llvm_unreachable("index into invalid type");
1122       }
1123     }
1124   }
1125 
1126   static llvm::SmallVector<mlir::Attribute>
1127   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1128                  mlir::ArrayAttr arrAttr) {
1129     llvm::SmallVector<mlir::Attribute> attrs;
1130     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1131       if (i->isa<mlir::IntegerAttr>()) {
1132         attrs.push_back(*i);
1133       } else {
1134         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1135         ++i;
1136         auto ty = i->cast<mlir::TypeAttr>().getValue();
1137         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1138         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1139       }
1140     }
1141     return attrs;
1142   }
1143 
1144 private:
1145   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1146     unsigned result = 1;
1147     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1148          eleTy;
1149          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1150       ++result;
1151     return result;
1152   }
1153 
1154   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1155     auto eleTy = ty.getElementType();
1156     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1157       eleTy = arrTy.getElementType();
1158     return eleTy;
1159   }
1160 };
1161 
1162 /// Extract a subobject value from an ssa-value of aggregate type
1163 struct ExtractValueOpConversion
1164     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1165       public ValueOpCommon {
1166   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1167 
1168   mlir::LogicalResult
1169   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1170             mlir::ConversionPatternRewriter &rewriter) const override {
1171     auto attrs = collectIndices(rewriter, extractVal.coor());
1172     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1173     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1174     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1175         extractVal, ty, adaptor.getOperands()[0], position);
1176     return success();
1177   }
1178 };
1179 
1180 /// InsertValue is the generalized instruction for the composition of new
1181 /// aggregate type values.
1182 struct InsertValueOpConversion
1183     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1184       public ValueOpCommon {
1185   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1186 
1187   mlir::LogicalResult
1188   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1189             mlir::ConversionPatternRewriter &rewriter) const override {
1190     auto attrs = collectIndices(rewriter, insertVal.coor());
1191     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1192     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1193     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1194         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1195         position);
1196     return success();
1197   }
1198 };
1199 
1200 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1201 struct InsertOnRangeOpConversion
1202     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1203   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1204 
1205   // Increments an array of subscripts in a row major fasion.
1206   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1207                            SmallVector<uint64_t> &subscripts) const {
1208     for (size_t i = dims.size(); i > 0; --i) {
1209       if (++subscripts[i - 1] < dims[i - 1]) {
1210         return;
1211       }
1212       subscripts[i - 1] = 0;
1213     }
1214   }
1215 
1216   mlir::LogicalResult
1217   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1218             mlir::ConversionPatternRewriter &rewriter) const override {
1219 
1220     llvm::SmallVector<uint64_t> dims;
1221     auto type = adaptor.getOperands()[0].getType();
1222 
1223     // Iteratively extract the array dimensions from the type.
1224     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1225       dims.push_back(t.getNumElements());
1226       type = t.getElementType();
1227     }
1228 
1229     SmallVector<uint64_t> lBounds;
1230     SmallVector<uint64_t> uBounds;
1231 
1232     // Extract integer value from the attribute
1233     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1234         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1235           return a.cast<IntegerAttr>().getInt();
1236         }));
1237 
1238     // Unzip the upper and lower bound and convert to a row major format.
1239     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1240       uBounds.push_back(*i++);
1241       lBounds.push_back(*i);
1242     }
1243 
1244     auto &subscripts = lBounds;
1245     auto loc = range.getLoc();
1246     mlir::Value lastOp = adaptor.getOperands()[0];
1247     mlir::Value insertVal = adaptor.getOperands()[1];
1248 
1249     auto i64Ty = rewriter.getI64Type();
1250     while (subscripts != uBounds) {
1251       // Convert uint64_t's to Attribute's.
1252       SmallVector<mlir::Attribute> subscriptAttrs;
1253       for (const auto &subscript : subscripts)
1254         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1255       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1256           loc, ty, lastOp, insertVal,
1257           ArrayAttr::get(range.getContext(), subscriptAttrs));
1258 
1259       incrementSubscripts(dims, subscripts);
1260     }
1261 
1262     // Convert uint64_t's to Attribute's.
1263     SmallVector<mlir::Attribute> subscriptAttrs;
1264     for (const auto &subscript : subscripts)
1265       subscriptAttrs.push_back(
1266           IntegerAttr::get(rewriter.getI64Type(), subscript));
1267     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1268 
1269     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1270         range, ty, lastOp, insertVal,
1271         ArrayAttr::get(range.getContext(), arrayRef));
1272 
1273     return success();
1274   }
1275 };
1276 
1277 //
1278 // Primitive operations on Complex types
1279 //
1280 
1281 /// Generate inline code for complex addition/subtraction
1282 template <typename LLVMOP, typename OPTY>
1283 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1284                                      mlir::ConversionPatternRewriter &rewriter,
1285                                      fir::LLVMTypeConverter &lowering) {
1286   mlir::Value a = opnds[0];
1287   mlir::Value b = opnds[1];
1288   auto loc = sumop.getLoc();
1289   auto ctx = sumop.getContext();
1290   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1291   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1292   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1293   mlir::Type ty = lowering.convertType(sumop.getType());
1294   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1295   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1296   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1297   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1298   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1299   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1300   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1301   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1302   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1303 }
1304 
1305 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1306   using FIROpConversion::FIROpConversion;
1307 
1308   mlir::LogicalResult
1309   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1310                   mlir::ConversionPatternRewriter &rewriter) const override {
1311     // given: (x + iy) + (x' + iy')
1312     // result: (x + x') + i(y + y')
1313     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1314                                             rewriter, lowerTy());
1315     rewriter.replaceOp(addc, r.getResult());
1316     return success();
1317   }
1318 };
1319 
1320 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1321   using FIROpConversion::FIROpConversion;
1322 
1323   mlir::LogicalResult
1324   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1325                   mlir::ConversionPatternRewriter &rewriter) const override {
1326     // given: (x + iy) - (x' + iy')
1327     // result: (x - x') + i(y - y')
1328     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1329                                             rewriter, lowerTy());
1330     rewriter.replaceOp(subc, r.getResult());
1331     return success();
1332   }
1333 };
1334 
1335 /// Inlined complex multiply
1336 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1337   using FIROpConversion::FIROpConversion;
1338 
1339   mlir::LogicalResult
1340   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1341                   mlir::ConversionPatternRewriter &rewriter) const override {
1342     // TODO: Can we use a call to __muldc3 ?
1343     // given: (x + iy) * (x' + iy')
1344     // result: (xx'-yy')+i(xy'+yx')
1345     mlir::Value a = adaptor.getOperands()[0];
1346     mlir::Value b = adaptor.getOperands()[1];
1347     auto loc = mulc.getLoc();
1348     auto *ctx = mulc.getContext();
1349     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1350     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1351     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1352     mlir::Type ty = convertType(mulc.getType());
1353     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1354     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1355     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1356     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1357     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1358     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1359     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1360     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1361     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1362     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1363     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1364     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1365     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1366     rewriter.replaceOp(mulc, r0.getResult());
1367     return success();
1368   }
1369 };
1370 
1371 /// Inlined complex division
1372 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1373   using FIROpConversion::FIROpConversion;
1374 
1375   mlir::LogicalResult
1376   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1377                   mlir::ConversionPatternRewriter &rewriter) const override {
1378     // TODO: Can we use a call to __divdc3 instead?
1379     // Just generate inline code for now.
1380     // given: (x + iy) / (x' + iy')
1381     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1382     mlir::Value a = adaptor.getOperands()[0];
1383     mlir::Value b = adaptor.getOperands()[1];
1384     auto loc = divc.getLoc();
1385     auto *ctx = divc.getContext();
1386     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1387     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1388     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1389     mlir::Type ty = convertType(divc.getType());
1390     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1391     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1392     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1393     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1394     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1395     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1396     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1397     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1398     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1399     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1400     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1401     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1402     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1403     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1404     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1405     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1406     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1407     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1408     rewriter.replaceOp(divc, r0.getResult());
1409     return success();
1410   }
1411 };
1412 
1413 /// Inlined complex negation
1414 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1415   using FIROpConversion::FIROpConversion;
1416 
1417   mlir::LogicalResult
1418   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1419                   mlir::ConversionPatternRewriter &rewriter) const override {
1420     // given: -(x + iy)
1421     // result: -x - iy
1422     auto *ctxt = neg.getContext();
1423     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1424     auto ty = convertType(neg.getType());
1425     auto loc = neg.getLoc();
1426     mlir::Value o0 = adaptor.getOperands()[0];
1427     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1428     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1429     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1430     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1431     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1432     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1433     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1434     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1435     return success();
1436   }
1437 };
1438 
1439 /// `fir.is_present` -->
1440 /// ```
1441 ///  %0 = llvm.mlir.constant(0 : i64)
1442 ///  %1 = llvm.ptrtoint %0
1443 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1444 /// ```
1445 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1446   using FIROpConversion::FIROpConversion;
1447 
1448   mlir::LogicalResult
1449   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1450                   mlir::ConversionPatternRewriter &rewriter) const override {
1451     mlir::Type idxTy = lowerTy().indexType();
1452     mlir::Location loc = isPresent.getLoc();
1453     auto ptr = adaptor.getOperands()[0];
1454 
1455     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1456       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1457       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1458 
1459       mlir::Type ty = structTy.getBody()[0];
1460       mlir::MLIRContext *ctx = isPresent.getContext();
1461       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1462       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1463     }
1464     mlir::LLVM::ConstantOp c0 =
1465         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1466     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1467     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1468         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1469 
1470     return success();
1471   }
1472 };
1473 
1474 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1475 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1476 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1477 /// element is the length of the character buffer (`#n`).
1478 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1479   using FIROpConversion::FIROpConversion;
1480 
1481   mlir::LogicalResult
1482   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1483                   mlir::ConversionPatternRewriter &rewriter) const override {
1484     mlir::ValueRange operands = adaptor.getOperands();
1485     MLIRContext *ctx = emboxChar.getContext();
1486 
1487     mlir::Value charBuffer = operands[0];
1488     mlir::Value charBufferLen = operands[1];
1489 
1490     mlir::Location loc = emboxChar.getLoc();
1491     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1492     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1493 
1494     mlir::Type lenTy =
1495         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1496     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1497 
1498     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1499     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1500     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1501         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1502     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1503         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1504 
1505     return success();
1506   }
1507 };
1508 
1509 /// Construct an `llvm.extractvalue` instruction. It will return value at
1510 /// element \p x from  \p tuple.
1511 mlir::LLVM::ExtractValueOp
1512 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
1513                          mlir::ConversionPatternRewriter &rewriter,
1514                          mlir::MLIRContext *ctx, int x) {
1515   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
1516   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
1517   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
1518 }
1519 
1520 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
1521 /// boxchar.
1522 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
1523   using FIROpConversion::FIROpConversion;
1524 
1525   mlir::LogicalResult
1526   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
1527                   mlir::ConversionPatternRewriter &rewriter) const override {
1528     mlir::Value boxChar = adaptor.getOperands()[0];
1529     mlir::Location loc = boxChar.getLoc();
1530     mlir::MLIRContext *ctx = boxChar.getContext();
1531     mlir::Type returnValTy = boxCharLen.getResult().getType();
1532 
1533     constexpr int boxcharLenIdx = 1;
1534     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
1535         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
1536     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
1537     rewriter.replaceOp(boxCharLen, lenAfterCast);
1538 
1539     return success();
1540   }
1541 };
1542 
1543 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
1544 /// the character buffer and one for the buffer length.
1545 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
1546   using FIROpConversion::FIROpConversion;
1547 
1548   mlir::LogicalResult
1549   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
1550                   mlir::ConversionPatternRewriter &rewriter) const override {
1551     MLIRContext *ctx = unboxchar.getContext();
1552 
1553     mlir::Type lenTy = convertType(unboxchar.getType(1));
1554     mlir::Value tuple = adaptor.getOperands()[0];
1555     mlir::Type tupleTy = tuple.getType();
1556 
1557     mlir::Location loc = unboxchar.getLoc();
1558     mlir::Value ptrToBuffer =
1559         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
1560 
1561     mlir::LLVM::ExtractValueOp len =
1562         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
1563     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
1564 
1565     rewriter.replaceOp(unboxchar,
1566                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
1567     return success();
1568   }
1569 };
1570 
1571 } // namespace
1572 
1573 namespace {
1574 /// Convert FIR dialect to LLVM dialect
1575 ///
1576 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1577 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1578 ///
1579 /// This pass is not complete yet. We are upstreaming it in small patches.
1580 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1581 public:
1582   mlir::ModuleOp getModule() { return getOperation(); }
1583 
1584   void runOnOperation() override final {
1585     auto mod = getModule();
1586     if (!forcedTargetTriple.empty()) {
1587       fir::setTargetTriple(mod, forcedTargetTriple);
1588     }
1589 
1590     auto *context = getModule().getContext();
1591     fir::LLVMTypeConverter typeConverter{getModule()};
1592     mlir::OwningRewritePatternList pattern(context);
1593     pattern.insert<
1594         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1595         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, BoxDimsOpConversion,
1596         BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
1597         BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion,
1598         CmpcOpConversion, ConvertOpConversion, DispatchOpConversion,
1599         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
1600         EmboxCharOpConversion, ExtractValueOpConversion, HasValueOpConversion,
1601         GenTypeDescOpConversion, GlobalLenOpConversion, GlobalOpConversion,
1602         InsertOnRangeOpConversion, InsertValueOpConversion,
1603         IsPresentOpConversion, LoadOpConversion, NegcOpConversion,
1604         MulcOpConversion, SelectCaseOpConversion, SelectOpConversion,
1605         SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion,
1606         SubcOpConversion, UnboxCharOpConversion, UndefOpConversion,
1607         UnreachableOpConversion, ZeroOpConversion>(typeConverter);
1608     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1609     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1610                                                             pattern);
1611     mlir::ConversionTarget target{*context};
1612     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1613 
1614     // required NOPs for applying a full conversion
1615     target.addLegalOp<mlir::ModuleOp>();
1616 
1617     // apply the patterns
1618     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1619                                                std::move(pattern)))) {
1620       signalPassFailure();
1621     }
1622   }
1623 };
1624 } // namespace
1625 
1626 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1627   return std::make_unique<FIRToLLVMLowering>();
1628 }
1629