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