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