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   }
943 };
944 
945 /// conversion of fir::SelectRankOp to an if-then-else ladder
946 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
947   using FIROpConversion::FIROpConversion;
948 
949   mlir::LogicalResult
950   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
951                   mlir::ConversionPatternRewriter &rewriter) const override {
952     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
953     return success();
954   }
955 };
956 
957 /// `fir.store` --> `llvm.store`
958 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
959   using FIROpConversion::FIROpConversion;
960 
961   mlir::LogicalResult
962   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
963                   mlir::ConversionPatternRewriter &rewriter) const override {
964     if (store.value().getType().isa<fir::BoxType>()) {
965       // fir.box value is actually in memory, load it first before storing it.
966       mlir::Location loc = store.getLoc();
967       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
968       auto val = rewriter.create<mlir::LLVM::LoadOp>(
969           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
970           adaptor.getOperands()[0]);
971       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
972           store, val, adaptor.getOperands()[1]);
973     } else {
974       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
975           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
976     }
977     return success();
978   }
979 };
980 
981 /// convert to LLVM IR dialect `undef`
982 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
983   using FIROpConversion::FIROpConversion;
984 
985   mlir::LogicalResult
986   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
987                   mlir::ConversionPatternRewriter &rewriter) const override {
988     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
989         undef, convertType(undef.getType()));
990     return success();
991   }
992 };
993 
994 /// `fir.unreachable` --> `llvm.unreachable`
995 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
996   using FIROpConversion::FIROpConversion;
997 
998   mlir::LogicalResult
999   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1000                   mlir::ConversionPatternRewriter &rewriter) const override {
1001     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1002     return success();
1003   }
1004 };
1005 
1006 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1007   using FIROpConversion::FIROpConversion;
1008 
1009   mlir::LogicalResult
1010   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1011                   mlir::ConversionPatternRewriter &rewriter) const override {
1012     auto ty = convertType(zero.getType());
1013     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1014       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1015     } else if (ty.isa<mlir::IntegerType>()) {
1016       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1017           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1018     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1019       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1020           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1021     } else {
1022       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1023       return rewriter.notifyMatchFailure(
1024           zero,
1025           "conversion of fir.zero with aggregate type not implemented yet");
1026     }
1027     return success();
1028   }
1029 };
1030 
1031 // Code shared between insert_value and extract_value Ops.
1032 struct ValueOpCommon {
1033   // Translate the arguments pertaining to any multidimensional array to
1034   // row-major order for LLVM-IR.
1035   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1036                          mlir::Type ty) {
1037     assert(ty && "type is null");
1038     const auto end = attrs.size();
1039     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1040       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1041         const auto dim = getDimension(seq);
1042         if (dim > 1) {
1043           auto ub = std::min(i + dim, end);
1044           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1045           i += dim - 1;
1046         }
1047         ty = getArrayElementType(seq);
1048       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1049         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1050       } else {
1051         llvm_unreachable("index into invalid type");
1052       }
1053     }
1054   }
1055 
1056   static llvm::SmallVector<mlir::Attribute>
1057   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1058                  mlir::ArrayAttr arrAttr) {
1059     llvm::SmallVector<mlir::Attribute> attrs;
1060     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1061       if (i->isa<mlir::IntegerAttr>()) {
1062         attrs.push_back(*i);
1063       } else {
1064         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1065         ++i;
1066         auto ty = i->cast<mlir::TypeAttr>().getValue();
1067         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1068         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1069       }
1070     }
1071     return attrs;
1072   }
1073 
1074 private:
1075   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1076     unsigned result = 1;
1077     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1078          eleTy;
1079          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1080       ++result;
1081     return result;
1082   }
1083 
1084   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1085     auto eleTy = ty.getElementType();
1086     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1087       eleTy = arrTy.getElementType();
1088     return eleTy;
1089   }
1090 };
1091 
1092 /// Extract a subobject value from an ssa-value of aggregate type
1093 struct ExtractValueOpConversion
1094     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1095       public ValueOpCommon {
1096   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1097 
1098   mlir::LogicalResult
1099   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1100             mlir::ConversionPatternRewriter &rewriter) const override {
1101     auto attrs = collectIndices(rewriter, extractVal.coor());
1102     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1103     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1104     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1105         extractVal, ty, adaptor.getOperands()[0], position);
1106     return success();
1107   }
1108 };
1109 
1110 /// InsertValue is the generalized instruction for the composition of new
1111 /// aggregate type values.
1112 struct InsertValueOpConversion
1113     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1114       public ValueOpCommon {
1115   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1116 
1117   mlir::LogicalResult
1118   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1119             mlir::ConversionPatternRewriter &rewriter) const override {
1120     auto attrs = collectIndices(rewriter, insertVal.coor());
1121     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1122     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1123     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1124         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1125         position);
1126     return success();
1127   }
1128 };
1129 
1130 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1131 struct InsertOnRangeOpConversion
1132     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1133   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1134 
1135   // Increments an array of subscripts in a row major fasion.
1136   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1137                            SmallVector<uint64_t> &subscripts) const {
1138     for (size_t i = dims.size(); i > 0; --i) {
1139       if (++subscripts[i - 1] < dims[i - 1]) {
1140         return;
1141       }
1142       subscripts[i - 1] = 0;
1143     }
1144   }
1145 
1146   mlir::LogicalResult
1147   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1148             mlir::ConversionPatternRewriter &rewriter) const override {
1149 
1150     llvm::SmallVector<uint64_t> dims;
1151     auto type = adaptor.getOperands()[0].getType();
1152 
1153     // Iteratively extract the array dimensions from the type.
1154     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1155       dims.push_back(t.getNumElements());
1156       type = t.getElementType();
1157     }
1158 
1159     SmallVector<uint64_t> lBounds;
1160     SmallVector<uint64_t> uBounds;
1161 
1162     // Extract integer value from the attribute
1163     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1164         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1165           return a.cast<IntegerAttr>().getInt();
1166         }));
1167 
1168     // Unzip the upper and lower bound and convert to a row major format.
1169     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1170       uBounds.push_back(*i++);
1171       lBounds.push_back(*i);
1172     }
1173 
1174     auto &subscripts = lBounds;
1175     auto loc = range.getLoc();
1176     mlir::Value lastOp = adaptor.getOperands()[0];
1177     mlir::Value insertVal = adaptor.getOperands()[1];
1178 
1179     auto i64Ty = rewriter.getI64Type();
1180     while (subscripts != uBounds) {
1181       // Convert uint64_t's to Attribute's.
1182       SmallVector<mlir::Attribute> subscriptAttrs;
1183       for (const auto &subscript : subscripts)
1184         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1185       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1186           loc, ty, lastOp, insertVal,
1187           ArrayAttr::get(range.getContext(), subscriptAttrs));
1188 
1189       incrementSubscripts(dims, subscripts);
1190     }
1191 
1192     // Convert uint64_t's to Attribute's.
1193     SmallVector<mlir::Attribute> subscriptAttrs;
1194     for (const auto &subscript : subscripts)
1195       subscriptAttrs.push_back(
1196           IntegerAttr::get(rewriter.getI64Type(), subscript));
1197     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1198 
1199     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1200         range, ty, lastOp, insertVal,
1201         ArrayAttr::get(range.getContext(), arrayRef));
1202 
1203     return success();
1204   }
1205 };
1206 
1207 //
1208 // Primitive operations on Complex types
1209 //
1210 
1211 /// Generate inline code for complex addition/subtraction
1212 template <typename LLVMOP, typename OPTY>
1213 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1214                                      mlir::ConversionPatternRewriter &rewriter,
1215                                      fir::LLVMTypeConverter &lowering) {
1216   mlir::Value a = opnds[0];
1217   mlir::Value b = opnds[1];
1218   auto loc = sumop.getLoc();
1219   auto ctx = sumop.getContext();
1220   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1221   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1222   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1223   mlir::Type ty = lowering.convertType(sumop.getType());
1224   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1225   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1226   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1227   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1228   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1229   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1230   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1231   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1232   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1233 }
1234 
1235 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1236   using FIROpConversion::FIROpConversion;
1237 
1238   mlir::LogicalResult
1239   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1240                   mlir::ConversionPatternRewriter &rewriter) const override {
1241     // given: (x + iy) + (x' + iy')
1242     // result: (x + x') + i(y + y')
1243     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1244                                             rewriter, lowerTy());
1245     rewriter.replaceOp(addc, r.getResult());
1246     return success();
1247   }
1248 };
1249 
1250 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1251   using FIROpConversion::FIROpConversion;
1252 
1253   mlir::LogicalResult
1254   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1255                   mlir::ConversionPatternRewriter &rewriter) const override {
1256     // given: (x + iy) - (x' + iy')
1257     // result: (x - x') + i(y - y')
1258     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1259                                             rewriter, lowerTy());
1260     rewriter.replaceOp(subc, r.getResult());
1261     return success();
1262   }
1263 };
1264 
1265 /// Inlined complex multiply
1266 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1267   using FIROpConversion::FIROpConversion;
1268 
1269   mlir::LogicalResult
1270   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1271                   mlir::ConversionPatternRewriter &rewriter) const override {
1272     // TODO: Can we use a call to __muldc3 ?
1273     // given: (x + iy) * (x' + iy')
1274     // result: (xx'-yy')+i(xy'+yx')
1275     mlir::Value a = adaptor.getOperands()[0];
1276     mlir::Value b = adaptor.getOperands()[1];
1277     auto loc = mulc.getLoc();
1278     auto *ctx = mulc.getContext();
1279     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1280     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1281     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1282     mlir::Type ty = convertType(mulc.getType());
1283     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1284     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1285     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1286     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1287     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1288     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1289     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1290     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1291     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1292     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1293     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1294     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1295     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1296     rewriter.replaceOp(mulc, r0.getResult());
1297     return success();
1298   }
1299 };
1300 
1301 /// Inlined complex division
1302 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1303   using FIROpConversion::FIROpConversion;
1304 
1305   mlir::LogicalResult
1306   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1307                   mlir::ConversionPatternRewriter &rewriter) const override {
1308     // TODO: Can we use a call to __divdc3 instead?
1309     // Just generate inline code for now.
1310     // given: (x + iy) / (x' + iy')
1311     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1312     mlir::Value a = adaptor.getOperands()[0];
1313     mlir::Value b = adaptor.getOperands()[1];
1314     auto loc = divc.getLoc();
1315     auto *ctx = divc.getContext();
1316     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1317     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1318     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1319     mlir::Type ty = convertType(divc.getType());
1320     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1321     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1322     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1323     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1324     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1325     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1326     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1327     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1328     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1329     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1330     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1331     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1332     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1333     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1334     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1335     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1336     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1337     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1338     rewriter.replaceOp(divc, r0.getResult());
1339     return success();
1340   }
1341 };
1342 
1343 /// Inlined complex negation
1344 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1345   using FIROpConversion::FIROpConversion;
1346 
1347   mlir::LogicalResult
1348   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1349                   mlir::ConversionPatternRewriter &rewriter) const override {
1350     // given: -(x + iy)
1351     // result: -x - iy
1352     auto *ctxt = neg.getContext();
1353     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1354     auto ty = convertType(neg.getType());
1355     auto loc = neg.getLoc();
1356     mlir::Value o0 = adaptor.getOperands()[0];
1357     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1358     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1359     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1360     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1361     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1362     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1363     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1364     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1365     return success();
1366   }
1367 };
1368 
1369 /// `fir.is_present` -->
1370 /// ```
1371 ///  %0 = llvm.mlir.constant(0 : i64)
1372 ///  %1 = llvm.ptrtoint %0
1373 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1374 /// ```
1375 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1376   using FIROpConversion::FIROpConversion;
1377 
1378   mlir::LogicalResult
1379   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1380                   mlir::ConversionPatternRewriter &rewriter) const override {
1381     mlir::Type idxTy = lowerTy().indexType();
1382     mlir::Location loc = isPresent.getLoc();
1383     auto ptr = adaptor.getOperands()[0];
1384 
1385     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1386       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1387       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1388 
1389       mlir::Type ty = structTy.getBody()[0];
1390       mlir::MLIRContext *ctx = isPresent.getContext();
1391       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1392       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1393     }
1394     mlir::LLVM::ConstantOp c0 =
1395         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1396     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1397     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1398         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1399 
1400     return success();
1401   }
1402 };
1403 
1404 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1405 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1406 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1407 /// element is the length of the character buffer (`#n`).
1408 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1409   using FIROpConversion::FIROpConversion;
1410 
1411   mlir::LogicalResult
1412   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1413                   mlir::ConversionPatternRewriter &rewriter) const override {
1414     mlir::ValueRange operands = adaptor.getOperands();
1415     MLIRContext *ctx = emboxChar.getContext();
1416 
1417     mlir::Value charBuffer = operands[0];
1418     mlir::Value charBufferLen = operands[1];
1419 
1420     mlir::Location loc = emboxChar.getLoc();
1421     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1422     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1423 
1424     mlir::Type lenTy =
1425         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1426     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1427 
1428     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1429     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1430     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1431         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1432     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1433         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1434 
1435     return success();
1436   }
1437 };
1438 } // namespace
1439 
1440 namespace {
1441 /// Convert FIR dialect to LLVM dialect
1442 ///
1443 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
1444 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
1445 ///
1446 /// This pass is not complete yet. We are upstreaming it in small patches.
1447 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
1448 public:
1449   mlir::ModuleOp getModule() { return getOperation(); }
1450 
1451   void runOnOperation() override final {
1452     auto mod = getModule();
1453     if (!forcedTargetTriple.empty()) {
1454       fir::setTargetTriple(mod, forcedTargetTriple);
1455     }
1456 
1457     auto *context = getModule().getContext();
1458     fir::LLVMTypeConverter typeConverter{getModule()};
1459     mlir::OwningRewritePatternList pattern(context);
1460     pattern.insert<
1461         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
1462         AllocaOpConversion, BoxAddrOpConversion, BoxDimsOpConversion,
1463         BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion,
1464         BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion,
1465         ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion,
1466         DTEntryOpConversion, DivcOpConversion, EmboxCharOpConversion,
1467         ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion,
1468         InsertOnRangeOpConversion, InsertValueOpConversion,
1469         IsPresentOpConversion, LoadOpConversion, NegcOpConversion,
1470         MulcOpConversion, SelectCaseOpConversion, SelectOpConversion,
1471         SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion,
1472         SubcOpConversion, UndefOpConversion, UnreachableOpConversion,
1473         ZeroOpConversion>(typeConverter);
1474     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
1475     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
1476                                                             pattern);
1477     mlir::ConversionTarget target{*context};
1478     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
1479 
1480     // required NOPs for applying a full conversion
1481     target.addLegalOp<mlir::ModuleOp>();
1482 
1483     // apply the patterns
1484     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
1485                                                std::move(pattern)))) {
1486       signalPassFailure();
1487     }
1488   }
1489 };
1490 } // namespace
1491 
1492 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
1493   return std::make_unique<FIRToLLVMLowering>();
1494 }
1495