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 "flang/Optimizer/Support/TypeCode.h"
19 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
20 #include "mlir/Conversion/LLVMCommon/Pattern.h"
21 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/Matchers.h"
24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/ArrayRef.h"
26 
27 #define DEBUG_TYPE "flang-codegen"
28 
29 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
30 #include "TypeConverter.h"
31 
32 // TODO: This should really be recovered from the specified target.
33 static constexpr unsigned defaultAlign = 8;
34 
35 /// `fir.box` attribute values as defined for CFI_attribute_t in
36 /// flang/ISO_Fortran_binding.h.
37 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
38 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
39 
40 static mlir::LLVM::ConstantOp
41 genConstantIndex(mlir::Location loc, mlir::Type ity,
42                  mlir::ConversionPatternRewriter &rewriter,
43                  std::int64_t offset) {
44   auto cattr = rewriter.getI64IntegerAttr(offset);
45   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
46 }
47 
48 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
49                           mlir::Block *insertBefore) {
50   assert(insertBefore && "expected valid insertion block");
51   return rewriter.createBlock(insertBefore->getParent(),
52                               mlir::Region::iterator(insertBefore));
53 }
54 
55 namespace {
56 /// FIR conversion pattern template
57 template <typename FromOp>
58 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
59 public:
60   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
61       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
62 
63 protected:
64   mlir::Type convertType(mlir::Type ty) const {
65     return lowerTy().convertType(ty);
66   }
67 
68   mlir::LLVM::ConstantOp
69   genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
70                  int value) const {
71     mlir::Type i32Ty = rewriter.getI32Type();
72     mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
73     return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
74   }
75 
76   mlir::LLVM::ConstantOp
77   genConstantOffset(mlir::Location loc,
78                     mlir::ConversionPatternRewriter &rewriter,
79                     int offset) const {
80     mlir::Type ity = lowerTy().offsetType();
81     mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
82     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
83   }
84 
85   /// Construct code sequence to extract the specifc value from a `fir.box`.
86   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
87                               mlir::Type resultTy,
88                               mlir::ConversionPatternRewriter &rewriter,
89                               unsigned boxValue) const {
90     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
91     mlir::LLVM::ConstantOp cValuePos =
92         genConstantOffset(loc, rewriter, boxValue);
93     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
94     auto p = rewriter.create<mlir::LLVM::GEPOp>(
95         loc, pty, mlir::ValueRange{box, c0, cValuePos});
96     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
97   }
98 
99   /// Method to construct code sequence to get the triple for dimension `dim`
100   /// from a box.
101   SmallVector<mlir::Value, 3>
102   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
103                  mlir::Value box, mlir::Value dim,
104                  mlir::ConversionPatternRewriter &rewriter) const {
105     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
106     mlir::LLVM::ConstantOp cDims =
107         genConstantOffset(loc, rewriter, kDimsPosInBox);
108     mlir::LLVM::LoadOp l0 =
109         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
110     mlir::LLVM::LoadOp l1 =
111         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
112     mlir::LLVM::LoadOp l2 =
113         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
114     return {l0.getResult(), l1.getResult(), l2.getResult()};
115   }
116 
117   mlir::LLVM::LoadOp
118   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
119                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
120                  mlir::Type ty,
121                  mlir::ConversionPatternRewriter &rewriter) const {
122     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
123     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
124     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
125     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
126   }
127 
128   /// Read base address from a fir.box. Returned address has type ty.
129   mlir::Value
130   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
131                       mlir::ConversionPatternRewriter &rewriter) const {
132     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
133     mlir::LLVM::ConstantOp cAddr =
134         genConstantOffset(loc, rewriter, kAddrPosInBox);
135     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
136     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
137     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
138   }
139 
140   mlir::Value
141   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
142                          mlir::ConversionPatternRewriter &rewriter) const {
143     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
144     mlir::LLVM::ConstantOp cElemLen =
145         genConstantOffset(loc, rewriter, kElemLenPosInBox);
146     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
147     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
148     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
149   }
150 
151   // Load the attribute from the \p box and perform a check against \p maskValue
152   // The final comparison is implemented as `(attribute & maskValue) != 0`.
153   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
154                                    mlir::ConversionPatternRewriter &rewriter,
155                                    unsigned maskValue) const {
156     mlir::Type attrTy = rewriter.getI32Type();
157     mlir::Value attribute =
158         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
159     mlir::LLVM::ConstantOp attrMask =
160         genConstantOffset(loc, rewriter, maskValue);
161     auto maskRes =
162         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
163     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
164     return rewriter.create<mlir::LLVM::ICmpOp>(
165         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
166   }
167 
168   // Get the element type given an LLVM type that is of the form
169   // [llvm.ptr](array|struct|vector)+ and the provided indexes.
170   static mlir::Type getBoxEleTy(mlir::Type type,
171                                 llvm::ArrayRef<unsigned> indexes) {
172     if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
173       type = t.getElementType();
174     for (auto i : indexes) {
175       if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
176         assert(!t.isOpaque() && i < t.getBody().size());
177         type = t.getBody()[i];
178       } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
179         type = t.getElementType();
180       } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
181         type = t.getElementType();
182       } else {
183         fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
184                             "request for invalid box element type");
185       }
186     }
187     return type;
188   }
189 
190   template <typename... ARGS>
191   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
192                            mlir::ConversionPatternRewriter &rewriter,
193                            mlir::Value base, ARGS... args) const {
194     SmallVector<mlir::Value> cv{args...};
195     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
196   }
197 
198   /// Perform an extension or truncation as needed on an integer value. Lowering
199   /// to the specific target may involve some sign-extending or truncation of
200   /// values, particularly to fit them from abstract box types to the
201   /// appropriate reified structures.
202   mlir::Value integerCast(mlir::Location loc,
203                           mlir::ConversionPatternRewriter &rewriter,
204                           mlir::Type ty, mlir::Value val) const {
205     auto valTy = val.getType();
206     // If the value was not yet lowered, lower its type so that it can
207     // be used in getPrimitiveTypeSizeInBits.
208     if (!valTy.isa<mlir::IntegerType>())
209       valTy = convertType(valTy);
210     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
211     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
212     if (toSize < fromSize)
213       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
214     if (toSize > fromSize)
215       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
216     return val;
217   }
218 
219   fir::LLVMTypeConverter &lowerTy() const {
220     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
221   }
222 };
223 
224 /// FIR conversion pattern template
225 template <typename FromOp>
226 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
227 public:
228   using FIROpConversion<FromOp>::FIROpConversion;
229   using OpAdaptor = typename FromOp::Adaptor;
230 
231   mlir::LogicalResult
232   matchAndRewrite(FromOp op, OpAdaptor adaptor,
233                   mlir::ConversionPatternRewriter &rewriter) const final {
234     mlir::Type ty = this->convertType(op.getType());
235     return doRewrite(op, ty, adaptor, rewriter);
236   }
237 
238   virtual mlir::LogicalResult
239   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
240             mlir::ConversionPatternRewriter &rewriter) const = 0;
241 };
242 
243 /// Create value signaling an absent optional argument in a call, e.g.
244 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
245 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
246   using FIROpConversion::FIROpConversion;
247 
248   mlir::LogicalResult
249   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
250                   mlir::ConversionPatternRewriter &rewriter) const override {
251     mlir::Type ty = convertType(absent.getType());
252     mlir::Location loc = absent.getLoc();
253 
254     if (absent.getType().isa<fir::BoxCharType>()) {
255       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
256       assert(!structTy.isOpaque() && !structTy.getBody().empty());
257       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
258       auto nullField =
259           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
260       mlir::MLIRContext *ctx = absent.getContext();
261       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
262       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
263           absent, ty, undefStruct, nullField, c0);
264     } else {
265       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
266     }
267     return success();
268   }
269 };
270 
271 // Lower `fir.address_of` operation to `llvm.address_of` operation.
272 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
273   using FIROpConversion::FIROpConversion;
274 
275   mlir::LogicalResult
276   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
277                   mlir::ConversionPatternRewriter &rewriter) const override {
278     auto ty = convertType(addr.getType());
279     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
280         addr, ty, addr.symbol().getRootReference().getValue());
281     return success();
282   }
283 };
284 } // namespace
285 
286 /// Lookup the function to compute the memory size of this parametric derived
287 /// type. The size of the object may depend on the LEN type parameters of the
288 /// derived type.
289 static mlir::LLVM::LLVMFuncOp
290 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
291                           mlir::ConversionPatternRewriter &rewriter) {
292   auto module = op->getParentOfType<mlir::ModuleOp>();
293   std::string name = recTy.getName().str() + "P.mem.size";
294   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
295 }
296 
297 namespace {
298 /// convert to LLVM IR dialect `alloca`
299 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
300   using FIROpConversion::FIROpConversion;
301 
302   mlir::LogicalResult
303   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
304                   mlir::ConversionPatternRewriter &rewriter) const override {
305     mlir::ValueRange operands = adaptor.getOperands();
306     auto loc = alloc.getLoc();
307     mlir::Type ity = lowerTy().indexType();
308     unsigned i = 0;
309     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
310     mlir::Type ty = convertType(alloc.getType());
311     mlir::Type resultTy = ty;
312     if (alloc.hasLenParams()) {
313       unsigned end = alloc.numLenParams();
314       llvm::SmallVector<mlir::Value> lenParams;
315       for (; i < end; ++i)
316         lenParams.push_back(operands[i]);
317       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
318       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
319         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
320             chrTy.getContext(), chrTy.getFKind());
321         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
322         assert(end == 1);
323         size = integerCast(loc, rewriter, ity, lenParams[0]);
324       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
325         mlir::LLVM::LLVMFuncOp memSizeFn =
326             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
327         if (!memSizeFn)
328           emitError(loc, "did not find allocation function");
329         mlir::NamedAttribute attr = rewriter.getNamedAttr(
330             "callee", mlir::SymbolRefAttr::get(memSizeFn));
331         auto call = rewriter.create<mlir::LLVM::CallOp>(
332             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
333         size = call.getResult(0);
334         ty = mlir::LLVM::LLVMPointerType::get(
335             mlir::IntegerType::get(alloc.getContext(), 8));
336       } else {
337         return emitError(loc, "unexpected type ")
338                << scalarType << " with type parameters";
339       }
340     }
341     if (alloc.hasShapeOperands()) {
342       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
343       // Scale the size by constant factors encoded in the array type.
344       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
345         fir::SequenceType::Extent constSize = 1;
346         for (auto extent : seqTy.getShape())
347           if (extent != fir::SequenceType::getUnknownExtent())
348             constSize *= extent;
349         mlir::Value constVal{
350             genConstantIndex(loc, ity, rewriter, constSize).getResult()};
351         size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
352       }
353       unsigned end = operands.size();
354       for (; i < end; ++i)
355         size = rewriter.create<mlir::LLVM::MulOp>(
356             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
357     }
358     if (ty == resultTy) {
359       // Do not emit the bitcast if ty and resultTy are the same.
360       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
361                                                         alloc->getAttrs());
362     } else {
363       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
364                                                       alloc->getAttrs());
365       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
366     }
367     return success();
368   }
369 };
370 
371 /// Lower `fir.box_addr` to the sequence of operations to extract the first
372 /// element of the box.
373 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
374   using FIROpConversion::FIROpConversion;
375 
376   mlir::LogicalResult
377   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
378                   mlir::ConversionPatternRewriter &rewriter) const override {
379     mlir::Value a = adaptor.getOperands()[0];
380     auto loc = boxaddr.getLoc();
381     mlir::Type ty = convertType(boxaddr.getType());
382     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
383       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
384     } else {
385       auto c0attr = rewriter.getI32IntegerAttr(0);
386       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
387       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
388                                                               c0);
389     }
390     return success();
391   }
392 };
393 
394 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
395 /// dimension infomartion from the boxed value.
396 /// Result in a triple set of GEPs and loads.
397 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
398   using FIROpConversion::FIROpConversion;
399 
400   mlir::LogicalResult
401   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
402                   mlir::ConversionPatternRewriter &rewriter) const override {
403     SmallVector<mlir::Type, 3> resultTypes = {
404         convertType(boxdims.getResult(0).getType()),
405         convertType(boxdims.getResult(1).getType()),
406         convertType(boxdims.getResult(2).getType()),
407     };
408     auto results =
409         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
410                        adaptor.getOperands()[1], rewriter);
411     rewriter.replaceOp(boxdims, results);
412     return success();
413   }
414 };
415 
416 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
417 /// an element in the boxed value.
418 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
419   using FIROpConversion::FIROpConversion;
420 
421   mlir::LogicalResult
422   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
423                   mlir::ConversionPatternRewriter &rewriter) const override {
424     mlir::Value a = adaptor.getOperands()[0];
425     auto loc = boxelesz.getLoc();
426     auto ty = convertType(boxelesz.getType());
427     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
428     rewriter.replaceOp(boxelesz, elemSize);
429     return success();
430   }
431 };
432 
433 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
434 /// boxed value was from an ALLOCATABLE entity.
435 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
436   using FIROpConversion::FIROpConversion;
437 
438   mlir::LogicalResult
439   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
440                   mlir::ConversionPatternRewriter &rewriter) const override {
441     mlir::Value box = adaptor.getOperands()[0];
442     auto loc = boxisalloc.getLoc();
443     mlir::Value check =
444         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
445     rewriter.replaceOp(boxisalloc, check);
446     return success();
447   }
448 };
449 
450 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
451 /// boxed is an array.
452 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
453   using FIROpConversion::FIROpConversion;
454 
455   mlir::LogicalResult
456   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
457                   mlir::ConversionPatternRewriter &rewriter) const override {
458     mlir::Value a = adaptor.getOperands()[0];
459     auto loc = boxisarray.getLoc();
460     auto rank =
461         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
462     auto c0 = genConstantOffset(loc, rewriter, 0);
463     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
464         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
465     return success();
466   }
467 };
468 
469 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
470 /// boxed value was from a POINTER entity.
471 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
472   using FIROpConversion::FIROpConversion;
473 
474   mlir::LogicalResult
475   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
476                   mlir::ConversionPatternRewriter &rewriter) const override {
477     mlir::Value box = adaptor.getOperands()[0];
478     auto loc = boxisptr.getLoc();
479     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
480     rewriter.replaceOp(boxisptr, check);
481     return success();
482   }
483 };
484 
485 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
486 /// the box.
487 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
488   using FIROpConversion::FIROpConversion;
489 
490   mlir::LogicalResult
491   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
492                   mlir::ConversionPatternRewriter &rewriter) const override {
493     mlir::Value a = adaptor.getOperands()[0];
494     auto loc = boxrank.getLoc();
495     mlir::Type ty = convertType(boxrank.getType());
496     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
497     rewriter.replaceOp(boxrank, result);
498     return success();
499   }
500 };
501 
502 /// Lower `fir.string_lit` to LLVM IR dialect operation.
503 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
504   using FIROpConversion::FIROpConversion;
505 
506   mlir::LogicalResult
507   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
508                   mlir::ConversionPatternRewriter &rewriter) const override {
509     auto ty = convertType(constop.getType());
510     auto attr = constop.getValue();
511     if (attr.isa<mlir::StringAttr>()) {
512       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
513       return success();
514     }
515 
516     auto arr = attr.cast<mlir::ArrayAttr>();
517     auto charTy = constop.getType().cast<fir::CharacterType>();
518     unsigned bits = lowerTy().characterBitsize(charTy);
519     mlir::Type intTy = rewriter.getIntegerType(bits);
520     auto attrs = llvm::map_range(
521         arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute {
522           return mlir::IntegerAttr::get(
523               intTy,
524               attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits));
525         });
526     mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy);
527     auto denseAttr = mlir::DenseElementsAttr::get(
528         vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs));
529     rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty,
530                                                          denseAttr);
531     return success();
532   }
533 };
534 
535 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
536 /// boxproc.
537 /// TODO: Part of supporting Fortran 2003 procedure pointers.
538 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> {
539   using FIROpConversion::FIROpConversion;
540 
541   mlir::LogicalResult
542   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
543                   mlir::ConversionPatternRewriter &rewriter) const override {
544     return rewriter.notifyMatchFailure(
545         boxprochost, "fir.boxproc_host codegen is not implemented yet");
546   }
547 };
548 
549 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
550 /// descriptor from the box.
551 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
552   using FIROpConversion::FIROpConversion;
553 
554   mlir::LogicalResult
555   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
556                   mlir::ConversionPatternRewriter &rewriter) const override {
557     mlir::Value box = adaptor.getOperands()[0];
558     auto loc = boxtypedesc.getLoc();
559     mlir::Type typeTy =
560         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
561     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
562     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
563     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
564                                                         result);
565     return success();
566   }
567 };
568 
569 // `fir.call` -> `llvm.call`
570 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
571   using FIROpConversion::FIROpConversion;
572 
573   mlir::LogicalResult
574   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
575                   mlir::ConversionPatternRewriter &rewriter) const override {
576     SmallVector<mlir::Type> resultTys;
577     for (auto r : call.getResults())
578       resultTys.push_back(convertType(r.getType()));
579     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
580         call, resultTys, adaptor.getOperands(), call->getAttrs());
581     return success();
582   }
583 };
584 
585 static mlir::Type getComplexEleTy(mlir::Type complex) {
586   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
587     return cc.getElementType();
588   return complex.cast<fir::ComplexType>().getElementType();
589 }
590 
591 /// Compare complex values
592 ///
593 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
594 ///
595 /// For completeness, all other comparison are done on the real component only.
596 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
597   using FIROpConversion::FIROpConversion;
598 
599   mlir::LogicalResult
600   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
601                   mlir::ConversionPatternRewriter &rewriter) const override {
602     mlir::ValueRange operands = adaptor.getOperands();
603     mlir::MLIRContext *ctxt = cmp.getContext();
604     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
605     mlir::Type resTy = convertType(cmp.getType());
606     mlir::Location loc = cmp.getLoc();
607     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
608     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
609                                        loc, eleTy, operands[0], pos0),
610                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
611                                        loc, eleTy, operands[1], pos0)};
612     auto rcp =
613         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
614     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
615     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
616                                        loc, eleTy, operands[0], pos1),
617                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
618                                        loc, eleTy, operands[1], pos1)};
619     auto icp =
620         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
621     SmallVector<mlir::Value, 2> cp{rcp, icp};
622     switch (cmp.getPredicate()) {
623     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
624       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
625       break;
626     case mlir::arith::CmpFPredicate::UNE: // .NE.
627       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
628       break;
629     default:
630       rewriter.replaceOp(cmp, rcp.getResult());
631       break;
632     }
633     return success();
634   }
635 };
636 
637 /// Lower complex constants
638 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
639   using FIROpConversion::FIROpConversion;
640 
641   mlir::LogicalResult
642   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
643                   mlir::ConversionPatternRewriter &rewriter) const override {
644     mlir::Location loc = conc.getLoc();
645     mlir::MLIRContext *ctx = conc.getContext();
646     mlir::Type ty = convertType(conc.getType());
647     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
648     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
649     auto realPart =
650         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
651     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
652     auto imPart =
653         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
654     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
655     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
656     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
657     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
658         loc, ty, undef, realPart, realIndex);
659     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
660                                                            imPart, imIndex);
661     return success();
662   }
663 
664   inline APFloat getValue(mlir::Attribute attr) const {
665     return attr.cast<fir::RealAttr>().getValue();
666   }
667 };
668 
669 /// convert value of from-type to value of to-type
670 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
671   using FIROpConversion::FIROpConversion;
672 
673   static bool isFloatingPointTy(mlir::Type ty) {
674     return ty.isa<mlir::FloatType>();
675   }
676 
677   mlir::LogicalResult
678   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
679                   mlir::ConversionPatternRewriter &rewriter) const override {
680     auto fromTy = convertType(convert.value().getType());
681     auto toTy = convertType(convert.res().getType());
682     mlir::Value op0 = adaptor.getOperands()[0];
683     if (fromTy == toTy) {
684       rewriter.replaceOp(convert, op0);
685       return success();
686     }
687     auto loc = convert.getLoc();
688     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
689                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
690       if (fromBits == toBits) {
691         // TODO: Converting between two floating-point representations with the
692         // same bitwidth is not allowed for now.
693         mlir::emitError(loc,
694                         "cannot implicitly convert between two floating-point "
695                         "representations of the same bitwidth");
696         return {};
697       }
698       if (fromBits > toBits)
699         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
700       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
701     };
702     // Complex to complex conversion.
703     if (fir::isa_complex(convert.value().getType()) &&
704         fir::isa_complex(convert.res().getType())) {
705       // Special case: handle the conversion of a complex such that both the
706       // real and imaginary parts are converted together.
707       auto zero = mlir::ArrayAttr::get(convert.getContext(),
708                                        rewriter.getI32IntegerAttr(0));
709       auto one = mlir::ArrayAttr::get(convert.getContext(),
710                                       rewriter.getI32IntegerAttr(1));
711       auto ty = convertType(getComplexEleTy(convert.value().getType()));
712       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
713       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
714       auto nt = convertType(getComplexEleTy(convert.res().getType()));
715       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
716       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
717       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
718       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
719       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
720       auto i1 =
721           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
722       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
723                                                              ic, one);
724       return mlir::success();
725     }
726     // Floating point to floating point conversion.
727     if (isFloatingPointTy(fromTy)) {
728       if (isFloatingPointTy(toTy)) {
729         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
730         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
731         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
732         rewriter.replaceOp(convert, v);
733         return mlir::success();
734       }
735       if (toTy.isa<mlir::IntegerType>()) {
736         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
737         return mlir::success();
738       }
739     } else if (fromTy.isa<mlir::IntegerType>()) {
740       // Integer to integer conversion.
741       if (toTy.isa<mlir::IntegerType>()) {
742         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
743         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
744         assert(fromBits != toBits);
745         if (fromBits > toBits) {
746           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
747           return mlir::success();
748         }
749         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
750         return mlir::success();
751       }
752       // Integer to floating point conversion.
753       if (isFloatingPointTy(toTy)) {
754         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
755         return mlir::success();
756       }
757       // Integer to pointer conversion.
758       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
759         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
760         return mlir::success();
761       }
762     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
763       // Pointer to integer conversion.
764       if (toTy.isa<mlir::IntegerType>()) {
765         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
766         return mlir::success();
767       }
768       // Pointer to pointer conversion.
769       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
770         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
771         return mlir::success();
772       }
773     }
774     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
775   }
776 };
777 
778 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
779 /// table.
780 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
781   using FIROpConversion::FIROpConversion;
782 
783   mlir::LogicalResult
784   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
785                   mlir::ConversionPatternRewriter &rewriter) const override {
786     return rewriter.notifyMatchFailure(
787         dispatch, "fir.dispatch codegen is not implemented yet");
788   }
789 };
790 
791 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
792 /// derived type.
793 struct DispatchTableOpConversion
794     : public FIROpConversion<fir::DispatchTableOp> {
795   using FIROpConversion::FIROpConversion;
796 
797   mlir::LogicalResult
798   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
799                   mlir::ConversionPatternRewriter &rewriter) const override {
800     return rewriter.notifyMatchFailure(
801         dispTab, "fir.dispatch_table codegen is not implemented yet");
802   }
803 };
804 
805 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
806 /// method-name to a function.
807 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
808   using FIROpConversion::FIROpConversion;
809 
810   mlir::LogicalResult
811   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
812                   mlir::ConversionPatternRewriter &rewriter) const override {
813     return rewriter.notifyMatchFailure(
814         dtEnt, "fir.dt_entry codegen is not implemented yet");
815   }
816 };
817 
818 /// Lower `fir.global_len` operation.
819 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
820   using FIROpConversion::FIROpConversion;
821 
822   mlir::LogicalResult
823   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
824                   mlir::ConversionPatternRewriter &rewriter) const override {
825     return rewriter.notifyMatchFailure(
826         globalLen, "fir.global_len codegen is not implemented yet");
827   }
828 };
829 
830 /// Lower `fir.gentypedesc` to a global constant.
831 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
832   using FIROpConversion::FIROpConversion;
833 
834   mlir::LogicalResult
835   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
836                   mlir::ConversionPatternRewriter &rewriter) const override {
837     return rewriter.notifyMatchFailure(
838         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
839   }
840 };
841 
842 /// Convert `fir.end`
843 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
844   using FIROpConversion::FIROpConversion;
845 
846   mlir::LogicalResult
847   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
848                   mlir::ConversionPatternRewriter &rewriter) const override {
849     return rewriter.notifyMatchFailure(
850         firEnd, "fir.end codegen is not implemented yet");
851   }
852 };
853 
854 /// Lower `fir.has_value` operation to `llvm.return` operation.
855 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
856   using FIROpConversion::FIROpConversion;
857 
858   mlir::LogicalResult
859   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
860                   mlir::ConversionPatternRewriter &rewriter) const override {
861     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
862     return success();
863   }
864 };
865 
866 /// Lower `fir.global` operation to `llvm.global` operation.
867 /// `fir.insert_on_range` operations are replaced with constant dense attribute
868 /// if they are applied on the full range.
869 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
870   using FIROpConversion::FIROpConversion;
871 
872   mlir::LogicalResult
873   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
874                   mlir::ConversionPatternRewriter &rewriter) const override {
875     auto tyAttr = convertType(global.getType());
876     if (global.getType().isa<fir::BoxType>())
877       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
878     auto loc = global.getLoc();
879     mlir::Attribute initAttr{};
880     if (global.initVal())
881       initAttr = global.initVal().getValue();
882     auto linkage = convertLinkage(global.linkName());
883     auto isConst = global.constant().hasValue();
884     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
885         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
886     auto &gr = g.getInitializerRegion();
887     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
888     if (!gr.empty()) {
889       // Replace insert_on_range with a constant dense attribute if the
890       // initialization is on the full range.
891       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
892       for (auto insertOp : insertOnRangeOps) {
893         if (isFullRange(insertOp.coor(), insertOp.getType())) {
894           auto seqTyAttr = convertType(insertOp.getType());
895           auto *op = insertOp.val().getDefiningOp();
896           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
897           if (!constant) {
898             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
899             if (!convertOp)
900               continue;
901             constant = cast<mlir::arith::ConstantOp>(
902                 convertOp.value().getDefiningOp());
903           }
904           mlir::Type vecType = mlir::VectorType::get(
905               insertOp.getType().getShape(), constant.getType());
906           auto denseAttr = mlir::DenseElementsAttr::get(
907               vecType.cast<ShapedType>(), constant.value());
908           rewriter.setInsertionPointAfter(insertOp);
909           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
910               insertOp, seqTyAttr, denseAttr);
911         }
912       }
913     }
914     rewriter.eraseOp(global);
915     return success();
916   }
917 
918   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
919     auto extents = seqTy.getShape();
920     if (indexes.size() / 2 != extents.size())
921       return false;
922     for (unsigned i = 0; i < indexes.size(); i += 2) {
923       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
924         return false;
925       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
926         return false;
927     }
928     return true;
929   }
930 
931   // TODO: String comparaison should be avoided. Replace linkName with an
932   // enumeration.
933   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
934     if (optLinkage.hasValue()) {
935       auto name = optLinkage.getValue();
936       if (name == "internal")
937         return mlir::LLVM::Linkage::Internal;
938       if (name == "linkonce")
939         return mlir::LLVM::Linkage::Linkonce;
940       if (name == "common")
941         return mlir::LLVM::Linkage::Common;
942       if (name == "weak")
943         return mlir::LLVM::Linkage::Weak;
944     }
945     return mlir::LLVM::Linkage::External;
946   }
947 };
948 
949 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
950                  Optional<mlir::ValueRange> destOps,
951                  mlir::ConversionPatternRewriter &rewriter,
952                  mlir::Block *newBlock) {
953   if (destOps.hasValue())
954     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
955                                           newBlock, mlir::ValueRange());
956   else
957     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
958 }
959 
960 template <typename A, typename B>
961 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
962              mlir::ConversionPatternRewriter &rewriter) {
963   if (destOps.hasValue())
964     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
965                                                   dest);
966   else
967     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
968 }
969 
970 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
971                        Optional<mlir::ValueRange> destOps,
972                        mlir::ConversionPatternRewriter &rewriter) {
973   auto *thisBlock = rewriter.getInsertionBlock();
974   auto *newBlock = createBlock(rewriter, dest);
975   rewriter.setInsertionPointToEnd(thisBlock);
976   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
977   rewriter.setInsertionPointToEnd(newBlock);
978 }
979 
980 /// Conversion of `fir.select_case`
981 ///
982 /// The `fir.select_case` operation is converted to a if-then-else ladder.
983 /// Depending on the case condition type, one or several comparison and
984 /// conditional branching can be generated.
985 ///
986 /// A a point value case such as `case(4)`, a lower bound case such as
987 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
988 /// simple comparison between the selector value and the constant value in the
989 /// case. The block associated with the case condition is then executed if
990 /// the comparison succeed otherwise it branch to the next block with the
991 /// comparison for the the next case conditon.
992 ///
993 /// A closed interval case condition such as `case(7:10)` is converted with a
994 /// first comparison and conditional branching for the lower bound. If
995 /// successful, it branch to a second block with the comparison for the
996 /// upper bound in the same case condition.
997 ///
998 /// TODO: lowering of CHARACTER type cases is not handled yet.
999 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
1000   using FIROpConversion::FIROpConversion;
1001 
1002   mlir::LogicalResult
1003   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
1004                   mlir::ConversionPatternRewriter &rewriter) const override {
1005     unsigned conds = caseOp.getNumConditions();
1006     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
1007     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
1008     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
1009     if (ty.isa<fir::CharacterType>())
1010       return rewriter.notifyMatchFailure(caseOp,
1011                                          "conversion of fir.select_case with "
1012                                          "character type not implemented yet");
1013     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
1014     auto loc = caseOp.getLoc();
1015     for (unsigned t = 0; t != conds; ++t) {
1016       mlir::Block *dest = caseOp.getSuccessor(t);
1017       llvm::Optional<mlir::ValueRange> destOps =
1018           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
1019       llvm::Optional<mlir::ValueRange> cmpOps =
1020           *caseOp.getCompareOperands(adaptor.getOperands(), t);
1021       mlir::Value caseArg = *(cmpOps.getValue().begin());
1022       mlir::Attribute attr = cases[t];
1023       if (attr.isa<fir::PointIntervalAttr>()) {
1024         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1025             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
1026         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1027         continue;
1028       }
1029       if (attr.isa<fir::LowerBoundAttr>()) {
1030         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1031             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1032         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1033         continue;
1034       }
1035       if (attr.isa<fir::UpperBoundAttr>()) {
1036         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1037             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
1038         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1039         continue;
1040       }
1041       if (attr.isa<fir::ClosedIntervalAttr>()) {
1042         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1043             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1044         auto *thisBlock = rewriter.getInsertionBlock();
1045         auto *newBlock1 = createBlock(rewriter, dest);
1046         auto *newBlock2 = createBlock(rewriter, dest);
1047         rewriter.setInsertionPointToEnd(thisBlock);
1048         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
1049         rewriter.setInsertionPointToEnd(newBlock1);
1050         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
1051         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
1052             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
1053         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
1054         rewriter.setInsertionPointToEnd(newBlock2);
1055         continue;
1056       }
1057       assert(attr.isa<mlir::UnitAttr>());
1058       assert((t + 1 == conds) && "unit must be last");
1059       genBrOp(caseOp, dest, destOps, rewriter);
1060     }
1061     return success();
1062   }
1063 };
1064 
1065 template <typename OP>
1066 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
1067                            typename OP::Adaptor adaptor,
1068                            mlir::ConversionPatternRewriter &rewriter) {
1069   unsigned conds = select.getNumConditions();
1070   auto cases = select.getCases().getValue();
1071   mlir::Value selector = adaptor.selector();
1072   auto loc = select.getLoc();
1073   assert(conds > 0 && "select must have cases");
1074 
1075   llvm::SmallVector<mlir::Block *> destinations;
1076   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
1077   mlir::Block *defaultDestination;
1078   mlir::ValueRange defaultOperands;
1079   llvm::SmallVector<int32_t> caseValues;
1080 
1081   for (unsigned t = 0; t != conds; ++t) {
1082     mlir::Block *dest = select.getSuccessor(t);
1083     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
1084     const mlir::Attribute &attr = cases[t];
1085     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
1086       destinations.push_back(dest);
1087       destinationsOperands.push_back(destOps.hasValue() ? *destOps
1088                                                         : ValueRange());
1089       caseValues.push_back(intAttr.getInt());
1090       continue;
1091     }
1092     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1093     assert((t + 1 == conds) && "unit must be last");
1094     defaultDestination = dest;
1095     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1096   }
1097 
1098   // LLVM::SwitchOp takes a i32 type for the selector.
1099   if (select.getSelector().getType() != rewriter.getI32Type())
1100     selector =
1101         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1102 
1103   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1104       select, selector,
1105       /*defaultDestination=*/defaultDestination,
1106       /*defaultOperands=*/defaultOperands,
1107       /*caseValues=*/caseValues,
1108       /*caseDestinations=*/destinations,
1109       /*caseOperands=*/destinationsOperands,
1110       /*branchWeights=*/ArrayRef<int32_t>());
1111 }
1112 
1113 /// conversion of fir::SelectOp to an if-then-else ladder
1114 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1115   using FIROpConversion::FIROpConversion;
1116 
1117   mlir::LogicalResult
1118   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1119                   mlir::ConversionPatternRewriter &rewriter) const override {
1120     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1121     return success();
1122   }
1123 };
1124 
1125 /// `fir.load` --> `llvm.load`
1126 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1127   using FIROpConversion::FIROpConversion;
1128 
1129   mlir::LogicalResult
1130   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1131                   mlir::ConversionPatternRewriter &rewriter) const override {
1132     // fir.box is a special case because it is considered as an ssa values in
1133     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1134     // and fir.box end up being the same llvm types and loading a
1135     // fir.ref<fir.box> is actually a no op in LLVM.
1136     if (load.getType().isa<fir::BoxType>()) {
1137       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1138     } else {
1139       mlir::Type ty = convertType(load.getType());
1140       ArrayRef<NamedAttribute> at = load->getAttrs();
1141       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1142           load, ty, adaptor.getOperands(), at);
1143     }
1144     return success();
1145   }
1146 };
1147 
1148 /// Lower `fir.select_type` to LLVM IR dialect.
1149 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1150   using FIROpConversion::FIROpConversion;
1151 
1152   mlir::LogicalResult
1153   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1154                   mlir::ConversionPatternRewriter &rewriter) const override {
1155     return rewriter.notifyMatchFailure(
1156         select, "fir.select_type codegen is not implemented yet");
1157   }
1158 };
1159 
1160 /// conversion of fir::SelectRankOp to an if-then-else ladder
1161 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1162   using FIROpConversion::FIROpConversion;
1163 
1164   mlir::LogicalResult
1165   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1166                   mlir::ConversionPatternRewriter &rewriter) const override {
1167     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1168     return success();
1169   }
1170 };
1171 
1172 /// `fir.store` --> `llvm.store`
1173 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1174   using FIROpConversion::FIROpConversion;
1175 
1176   mlir::LogicalResult
1177   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1178                   mlir::ConversionPatternRewriter &rewriter) const override {
1179     if (store.value().getType().isa<fir::BoxType>()) {
1180       // fir.box value is actually in memory, load it first before storing it.
1181       mlir::Location loc = store.getLoc();
1182       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1183       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1184           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1185           adaptor.getOperands()[0]);
1186       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1187           store, val, adaptor.getOperands()[1]);
1188     } else {
1189       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1190           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1191     }
1192     return success();
1193   }
1194 };
1195 
1196 /// convert to LLVM IR dialect `undef`
1197 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1198   using FIROpConversion::FIROpConversion;
1199 
1200   mlir::LogicalResult
1201   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1202                   mlir::ConversionPatternRewriter &rewriter) const override {
1203     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1204         undef, convertType(undef.getType()));
1205     return success();
1206   }
1207 };
1208 
1209 /// `fir.unreachable` --> `llvm.unreachable`
1210 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1211   using FIROpConversion::FIROpConversion;
1212 
1213   mlir::LogicalResult
1214   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1215                   mlir::ConversionPatternRewriter &rewriter) const override {
1216     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1217     return success();
1218   }
1219 };
1220 
1221 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1222   using FIROpConversion::FIROpConversion;
1223 
1224   mlir::LogicalResult
1225   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1226                   mlir::ConversionPatternRewriter &rewriter) const override {
1227     auto ty = convertType(zero.getType());
1228     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1229       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1230     } else if (ty.isa<mlir::IntegerType>()) {
1231       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1232           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1233     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1234       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1235           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1236     } else {
1237       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1238       return rewriter.notifyMatchFailure(
1239           zero,
1240           "conversion of fir.zero with aggregate type not implemented yet");
1241     }
1242     return success();
1243   }
1244 };
1245 
1246 /// Common base class for embox to descriptor conversion.
1247 template <typename OP>
1248 struct EmboxCommonConversion : public FIROpConversion<OP> {
1249   using FIROpConversion<OP>::FIROpConversion;
1250 
1251   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1252   // The order to find the LLVMFuncOp is as follows:
1253   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1254   // 2. The first ancestor that is a LLVMFuncOp.
1255   mlir::LLVM::LLVMFuncOp
1256   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1257     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1258     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1259                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1260                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1261   }
1262 
1263   // Generate an alloca of size 1 and type \p toTy.
1264   mlir::LLVM::AllocaOp
1265   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1266                     mlir::ConversionPatternRewriter &rewriter) const {
1267     auto thisPt = rewriter.saveInsertionPoint();
1268     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1269     rewriter.setInsertionPointToStart(&func.front());
1270     auto size = this->genI32Constant(loc, rewriter, 1);
1271     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1272     rewriter.restoreInsertionPoint(thisPt);
1273     return al;
1274   }
1275 
1276   static int getCFIAttr(fir::BoxType boxTy) {
1277     auto eleTy = boxTy.getEleTy();
1278     if (eleTy.isa<fir::PointerType>())
1279       return CFI_attribute_pointer;
1280     if (eleTy.isa<fir::HeapType>())
1281       return CFI_attribute_allocatable;
1282     return CFI_attribute_other;
1283   }
1284 
1285   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1286     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1287         .template dyn_cast<fir::RecordType>();
1288   }
1289   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1290     auto recTy = unwrapIfDerived(boxTy);
1291     return recTy && recTy.getNumLenParams() > 0;
1292   }
1293   static bool isDerivedType(fir::BoxType boxTy) {
1294     return unwrapIfDerived(boxTy) != nullptr;
1295   }
1296 
1297   // Get the element size and CFI type code of the boxed value.
1298   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1299       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1300       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1301     auto doInteger =
1302         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1303       int typeCode = fir::integerBitsToTypeCode(width);
1304       return {this->genConstantOffset(loc, rewriter, width / 8),
1305               this->genConstantOffset(loc, rewriter, typeCode)};
1306     };
1307     auto doLogical =
1308         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1309       int typeCode = fir::logicalBitsToTypeCode(width);
1310       return {this->genConstantOffset(loc, rewriter, width / 8),
1311               this->genConstantOffset(loc, rewriter, typeCode)};
1312     };
1313     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1314       int typeCode = fir::realBitsToTypeCode(width);
1315       return {this->genConstantOffset(loc, rewriter, width / 8),
1316               this->genConstantOffset(loc, rewriter, typeCode)};
1317     };
1318     auto doComplex =
1319         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1320       auto typeCode = fir::complexBitsToTypeCode(width);
1321       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1322               this->genConstantOffset(loc, rewriter, typeCode)};
1323     };
1324     auto doCharacter =
1325         [&](unsigned width,
1326             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1327       auto typeCode = fir::characterBitsToTypeCode(width);
1328       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1329       if (width == 8)
1330         return {len, typeCodeVal};
1331       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1332       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1333       auto size =
1334           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1335       return {size, typeCodeVal};
1336     };
1337     auto getKindMap = [&]() -> fir::KindMapping & {
1338       return this->lowerTy().getKindMap();
1339     };
1340     // Pointer-like types.
1341     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1342       boxEleTy = eleTy;
1343     // Integer types.
1344     if (fir::isa_integer(boxEleTy)) {
1345       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1346         return doInteger(ty.getWidth());
1347       auto ty = boxEleTy.cast<fir::IntegerType>();
1348       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1349     }
1350     // Floating point types.
1351     if (fir::isa_real(boxEleTy)) {
1352       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1353         return doFloat(ty.getWidth());
1354       auto ty = boxEleTy.cast<fir::RealType>();
1355       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1356     }
1357     // Complex types.
1358     if (fir::isa_complex(boxEleTy)) {
1359       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1360         return doComplex(
1361             ty.getElementType().cast<mlir::FloatType>().getWidth());
1362       auto ty = boxEleTy.cast<fir::ComplexType>();
1363       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1364     }
1365     // Character types.
1366     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1367       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1368       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1369         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1370         return doCharacter(charWidth, len);
1371       }
1372       assert(!lenParams.empty());
1373       return doCharacter(charWidth, lenParams.back());
1374     }
1375     // Logical type.
1376     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1377       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1378     // Array types.
1379     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1380       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1381     // Derived-type types.
1382     if (boxEleTy.isa<fir::RecordType>()) {
1383       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1384           this->lowerTy().convertType(boxEleTy));
1385       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1386       auto one =
1387           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1388       auto gep = rewriter.create<mlir::LLVM::GEPOp>(
1389           loc, ptrTy, mlir::ValueRange{nullPtr, one});
1390       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1391           loc, this->lowerTy().indexType(), gep);
1392       return {eleSize,
1393               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1394     }
1395     // Reference type.
1396     if (fir::isa_ref_type(boxEleTy)) {
1397       // FIXME: use the target pointer size rather than sizeof(void*)
1398       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1399               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1400     }
1401     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1402   }
1403 
1404   /// Basic pattern to write a field in the descriptor
1405   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1406                           mlir::Location loc, mlir::Value dest,
1407                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1408                           bool bitcast = false) const {
1409     auto boxTy = dest.getType();
1410     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1411     if (bitcast)
1412       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1413     else
1414       value = this->integerCast(loc, rewriter, fldTy, value);
1415     SmallVector<mlir::Attribute, 2> attrs;
1416     for (auto i : fldIndexes)
1417       attrs.push_back(rewriter.getI32IntegerAttr(i));
1418     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1419     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1420                                                       indexesAttr);
1421   }
1422 
1423   inline mlir::Value
1424   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1425                     mlir::Location loc, mlir::Value dest,
1426                     mlir::Value base) const {
1427     return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true);
1428   }
1429 
1430   /// Get the address of the type descriptor global variable that was created by
1431   /// lowering for derived type \p recType.
1432   template <typename BOX>
1433   mlir::Value
1434   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1435                     mlir::Location loc, fir::RecordType recType) const {
1436     std::string name = recType.getLoweredName();
1437     auto module = box->template getParentOfType<mlir::ModuleOp>();
1438     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1439       auto ty = mlir::LLVM::LLVMPointerType::get(
1440           this->lowerTy().convertType(global.getType()));
1441       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1442                                                       global.sym_name());
1443     }
1444     if (auto global =
1445             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1446       // The global may have already been translated to LLVM.
1447       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1448       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1449                                                       global.sym_name());
1450     }
1451     // The global does not exist in the current translation unit, but may be
1452     // defined elsewhere (e.g., type defined in a module).
1453     // For now, create a extern_weak symbol (will become nullptr if unresolved)
1454     // to support generating code without the front-end generated symbols.
1455     // These could be made available_externally to require the symbols to be
1456     // defined elsewhere and to cause link-time failure otherwise.
1457     auto i8Ty = rewriter.getIntegerType(8);
1458     mlir::OpBuilder modBuilder(module.getBodyRegion());
1459     // TODO: The symbol should be lowered to constant in lowering, they are read
1460     // only.
1461     modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
1462                                             mlir::LLVM::Linkage::ExternWeak,
1463                                             name, mlir::Attribute{});
1464     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1465     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1466   }
1467 
1468   template <typename BOX>
1469   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1470   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1471                        unsigned rank, mlir::ValueRange lenParams) const {
1472     auto loc = box.getLoc();
1473     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1474     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1475     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1476     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1477     mlir::Value descriptor =
1478         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1479 
1480     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1481     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1482       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1483         typeparams.push_back(box.substr()[1]);
1484     }
1485 
1486     // Write each of the fields with the appropriate values
1487     auto [eleSize, cfiTy] =
1488         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1489     descriptor =
1490         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1491     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1492                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1493     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1494                              this->genI32Constant(loc, rewriter, rank));
1495     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1496     descriptor =
1497         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1498                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1499     const bool hasAddendum = isDerivedType(boxTy);
1500     descriptor =
1501         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1502                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1503 
1504     if (hasAddendum) {
1505       auto isArray =
1506           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1507       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1508       auto typeDesc =
1509           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1510       descriptor =
1511           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1512                       /*bitCast=*/true);
1513     }
1514 
1515     return {boxTy, descriptor, eleSize};
1516   }
1517 
1518   /// If the embox is not in a globalOp body, allocate storage for the box;
1519   /// store the value inside and return the generated alloca. Return the input
1520   /// value otherwise.
1521   mlir::Value
1522   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1523                                mlir::Location loc, mlir::Value boxValue) const {
1524     auto *thisBlock = rewriter.getInsertionBlock();
1525     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1526       return boxValue;
1527     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1528     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1529     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1530     return alloca;
1531   }
1532 };
1533 
1534 /// Create a generic box on a memory reference. This conversions lowers the
1535 /// abstract box to the appropriate, initialized descriptor.
1536 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1537   using EmboxCommonConversion::EmboxCommonConversion;
1538 
1539   mlir::LogicalResult
1540   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1541                   mlir::ConversionPatternRewriter &rewriter) const override {
1542     assert(!embox.getShape() && "There should be no dims on this embox op");
1543     auto [boxTy, dest, eleSize] =
1544         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1545                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1546     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1547                              adaptor.getOperands()[0]);
1548     if (isDerivedTypeWithLenParams(boxTy))
1549       return rewriter.notifyMatchFailure(
1550           embox, "fir.embox codegen of derived with length parameters not "
1551                  "implemented yet");
1552     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1553     rewriter.replaceOp(embox, result);
1554     return success();
1555   }
1556 };
1557 
1558 /// Lower `fir.emboxproc` operation. Creates a procedure box.
1559 /// TODO: Part of supporting Fortran 2003 procedure pointers.
1560 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1561   using FIROpConversion::FIROpConversion;
1562 
1563   mlir::LogicalResult
1564   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1565                   mlir::ConversionPatternRewriter &rewriter) const override {
1566     return rewriter.notifyMatchFailure(
1567         emboxproc, "fir.emboxproc codegen is not implemented yet");
1568   }
1569 };
1570 
1571 
1572 // Code shared between insert_value and extract_value Ops.
1573 struct ValueOpCommon {
1574   // Translate the arguments pertaining to any multidimensional array to
1575   // row-major order for LLVM-IR.
1576   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1577                          mlir::Type ty) {
1578     assert(ty && "type is null");
1579     const auto end = attrs.size();
1580     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1581       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1582         const auto dim = getDimension(seq);
1583         if (dim > 1) {
1584           auto ub = std::min(i + dim, end);
1585           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1586           i += dim - 1;
1587         }
1588         ty = getArrayElementType(seq);
1589       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1590         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1591       } else {
1592         llvm_unreachable("index into invalid type");
1593       }
1594     }
1595   }
1596 
1597   static llvm::SmallVector<mlir::Attribute>
1598   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1599                  mlir::ArrayAttr arrAttr) {
1600     llvm::SmallVector<mlir::Attribute> attrs;
1601     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1602       if (i->isa<mlir::IntegerAttr>()) {
1603         attrs.push_back(*i);
1604       } else {
1605         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1606         ++i;
1607         auto ty = i->cast<mlir::TypeAttr>().getValue();
1608         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1609         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1610       }
1611     }
1612     return attrs;
1613   }
1614 
1615 private:
1616   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1617     unsigned result = 1;
1618     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1619          eleTy;
1620          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1621       ++result;
1622     return result;
1623   }
1624 
1625   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1626     auto eleTy = ty.getElementType();
1627     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1628       eleTy = arrTy.getElementType();
1629     return eleTy;
1630   }
1631 };
1632 
1633 /// Extract a subobject value from an ssa-value of aggregate type
1634 struct ExtractValueOpConversion
1635     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1636       public ValueOpCommon {
1637   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1638 
1639   mlir::LogicalResult
1640   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1641             mlir::ConversionPatternRewriter &rewriter) const override {
1642     auto attrs = collectIndices(rewriter, extractVal.coor());
1643     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1644     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1645     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1646         extractVal, ty, adaptor.getOperands()[0], position);
1647     return success();
1648   }
1649 };
1650 
1651 /// InsertValue is the generalized instruction for the composition of new
1652 /// aggregate type values.
1653 struct InsertValueOpConversion
1654     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1655       public ValueOpCommon {
1656   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1657 
1658   mlir::LogicalResult
1659   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1660             mlir::ConversionPatternRewriter &rewriter) const override {
1661     auto attrs = collectIndices(rewriter, insertVal.coor());
1662     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1663     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1664     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1665         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1666         position);
1667     return success();
1668   }
1669 };
1670 
1671 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1672 struct InsertOnRangeOpConversion
1673     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1674   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1675 
1676   // Increments an array of subscripts in a row major fasion.
1677   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1678                            SmallVector<uint64_t> &subscripts) const {
1679     for (size_t i = dims.size(); i > 0; --i) {
1680       if (++subscripts[i - 1] < dims[i - 1]) {
1681         return;
1682       }
1683       subscripts[i - 1] = 0;
1684     }
1685   }
1686 
1687   mlir::LogicalResult
1688   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1689             mlir::ConversionPatternRewriter &rewriter) const override {
1690 
1691     llvm::SmallVector<uint64_t> dims;
1692     auto type = adaptor.getOperands()[0].getType();
1693 
1694     // Iteratively extract the array dimensions from the type.
1695     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1696       dims.push_back(t.getNumElements());
1697       type = t.getElementType();
1698     }
1699 
1700     SmallVector<uint64_t> lBounds;
1701     SmallVector<uint64_t> uBounds;
1702 
1703     // Extract integer value from the attribute
1704     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1705         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1706           return a.cast<IntegerAttr>().getInt();
1707         }));
1708 
1709     // Unzip the upper and lower bound and convert to a row major format.
1710     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1711       uBounds.push_back(*i++);
1712       lBounds.push_back(*i);
1713     }
1714 
1715     auto &subscripts = lBounds;
1716     auto loc = range.getLoc();
1717     mlir::Value lastOp = adaptor.getOperands()[0];
1718     mlir::Value insertVal = adaptor.getOperands()[1];
1719 
1720     auto i64Ty = rewriter.getI64Type();
1721     while (subscripts != uBounds) {
1722       // Convert uint64_t's to Attribute's.
1723       SmallVector<mlir::Attribute> subscriptAttrs;
1724       for (const auto &subscript : subscripts)
1725         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1726       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1727           loc, ty, lastOp, insertVal,
1728           ArrayAttr::get(range.getContext(), subscriptAttrs));
1729 
1730       incrementSubscripts(dims, subscripts);
1731     }
1732 
1733     // Convert uint64_t's to Attribute's.
1734     SmallVector<mlir::Attribute> subscriptAttrs;
1735     for (const auto &subscript : subscripts)
1736       subscriptAttrs.push_back(
1737           IntegerAttr::get(rewriter.getI64Type(), subscript));
1738     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1739 
1740     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1741         range, ty, lastOp, insertVal,
1742         ArrayAttr::get(range.getContext(), arrayRef));
1743 
1744     return success();
1745   }
1746 };
1747 
1748 //
1749 // Primitive operations on Complex types
1750 //
1751 
1752 /// Generate inline code for complex addition/subtraction
1753 template <typename LLVMOP, typename OPTY>
1754 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1755                                      mlir::ConversionPatternRewriter &rewriter,
1756                                      fir::LLVMTypeConverter &lowering) {
1757   mlir::Value a = opnds[0];
1758   mlir::Value b = opnds[1];
1759   auto loc = sumop.getLoc();
1760   auto ctx = sumop.getContext();
1761   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1762   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1763   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1764   mlir::Type ty = lowering.convertType(sumop.getType());
1765   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1766   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1767   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1768   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1769   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1770   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1771   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1772   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1773   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1774 }
1775 
1776 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1777   using FIROpConversion::FIROpConversion;
1778 
1779   mlir::LogicalResult
1780   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1781                   mlir::ConversionPatternRewriter &rewriter) const override {
1782     // given: (x + iy) + (x' + iy')
1783     // result: (x + x') + i(y + y')
1784     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1785                                             rewriter, lowerTy());
1786     rewriter.replaceOp(addc, r.getResult());
1787     return success();
1788   }
1789 };
1790 
1791 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1792   using FIROpConversion::FIROpConversion;
1793 
1794   mlir::LogicalResult
1795   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1796                   mlir::ConversionPatternRewriter &rewriter) const override {
1797     // given: (x + iy) - (x' + iy')
1798     // result: (x - x') + i(y - y')
1799     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1800                                             rewriter, lowerTy());
1801     rewriter.replaceOp(subc, r.getResult());
1802     return success();
1803   }
1804 };
1805 
1806 /// Inlined complex multiply
1807 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1808   using FIROpConversion::FIROpConversion;
1809 
1810   mlir::LogicalResult
1811   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1812                   mlir::ConversionPatternRewriter &rewriter) const override {
1813     // TODO: Can we use a call to __muldc3 ?
1814     // given: (x + iy) * (x' + iy')
1815     // result: (xx'-yy')+i(xy'+yx')
1816     mlir::Value a = adaptor.getOperands()[0];
1817     mlir::Value b = adaptor.getOperands()[1];
1818     auto loc = mulc.getLoc();
1819     auto *ctx = mulc.getContext();
1820     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1821     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1822     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1823     mlir::Type ty = convertType(mulc.getType());
1824     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1825     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1826     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1827     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1828     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1829     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1830     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1831     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1832     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1833     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1834     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1835     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1836     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1837     rewriter.replaceOp(mulc, r0.getResult());
1838     return success();
1839   }
1840 };
1841 
1842 /// Inlined complex division
1843 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1844   using FIROpConversion::FIROpConversion;
1845 
1846   mlir::LogicalResult
1847   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1848                   mlir::ConversionPatternRewriter &rewriter) const override {
1849     // TODO: Can we use a call to __divdc3 instead?
1850     // Just generate inline code for now.
1851     // given: (x + iy) / (x' + iy')
1852     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1853     mlir::Value a = adaptor.getOperands()[0];
1854     mlir::Value b = adaptor.getOperands()[1];
1855     auto loc = divc.getLoc();
1856     auto *ctx = divc.getContext();
1857     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1858     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1859     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1860     mlir::Type ty = convertType(divc.getType());
1861     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1862     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1863     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1864     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1865     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1866     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1867     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1868     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1869     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1870     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1871     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1872     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1873     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1874     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1875     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1876     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1877     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1878     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1879     rewriter.replaceOp(divc, r0.getResult());
1880     return success();
1881   }
1882 };
1883 
1884 /// Inlined complex negation
1885 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1886   using FIROpConversion::FIROpConversion;
1887 
1888   mlir::LogicalResult
1889   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1890                   mlir::ConversionPatternRewriter &rewriter) const override {
1891     // given: -(x + iy)
1892     // result: -x - iy
1893     auto *ctxt = neg.getContext();
1894     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1895     auto ty = convertType(neg.getType());
1896     auto loc = neg.getLoc();
1897     mlir::Value o0 = adaptor.getOperands()[0];
1898     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1899     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1900     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1901     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1902     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1903     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1904     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1905     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1906     return success();
1907   }
1908 };
1909 
1910 /// Conversion pattern for operation that must be dead. The information in these
1911 /// operations is used by other operation. At this point they should not have
1912 /// anymore uses.
1913 /// These operations are normally dead after the pre-codegen pass.
1914 template <typename FromOp>
1915 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
1916   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
1917       : FIROpConversion<FromOp>(lowering) {}
1918   using OpAdaptor = typename FromOp::Adaptor;
1919 
1920   mlir::LogicalResult
1921   matchAndRewrite(FromOp op, OpAdaptor adaptor,
1922                   mlir::ConversionPatternRewriter &rewriter) const final {
1923     if (!op->getUses().empty())
1924       return rewriter.notifyMatchFailure(op, "op must be dead");
1925     rewriter.eraseOp(op);
1926     return success();
1927   }
1928 };
1929 
1930 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
1931   using MustBeDeadConversion::MustBeDeadConversion;
1932 };
1933 
1934 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
1935   using MustBeDeadConversion::MustBeDeadConversion;
1936 };
1937 
1938 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
1939   using MustBeDeadConversion::MustBeDeadConversion;
1940 };
1941 
1942 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
1943   using MustBeDeadConversion::MustBeDeadConversion;
1944 };
1945 
1946 /// `fir.is_present` -->
1947 /// ```
1948 ///  %0 = llvm.mlir.constant(0 : i64)
1949 ///  %1 = llvm.ptrtoint %0
1950 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1951 /// ```
1952 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1953   using FIROpConversion::FIROpConversion;
1954 
1955   mlir::LogicalResult
1956   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1957                   mlir::ConversionPatternRewriter &rewriter) const override {
1958     mlir::Type idxTy = lowerTy().indexType();
1959     mlir::Location loc = isPresent.getLoc();
1960     auto ptr = adaptor.getOperands()[0];
1961 
1962     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1963       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1964       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1965 
1966       mlir::Type ty = structTy.getBody()[0];
1967       mlir::MLIRContext *ctx = isPresent.getContext();
1968       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1969       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1970     }
1971     mlir::LLVM::ConstantOp c0 =
1972         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1973     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1974     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1975         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1976 
1977     return success();
1978   }
1979 };
1980 
1981 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1982 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1983 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1984 /// element is the length of the character buffer (`#n`).
1985 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1986   using FIROpConversion::FIROpConversion;
1987 
1988   mlir::LogicalResult
1989   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1990                   mlir::ConversionPatternRewriter &rewriter) const override {
1991     mlir::ValueRange operands = adaptor.getOperands();
1992     MLIRContext *ctx = emboxChar.getContext();
1993 
1994     mlir::Value charBuffer = operands[0];
1995     mlir::Value charBufferLen = operands[1];
1996 
1997     mlir::Location loc = emboxChar.getLoc();
1998     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1999     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
2000 
2001     mlir::Type lenTy =
2002         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
2003     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
2004 
2005     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2006     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
2007     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2008         loc, llvmStructTy, llvmStruct, charBuffer, c0);
2009     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2010         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
2011 
2012     return success();
2013   }
2014 };
2015 
2016 /// Construct an `llvm.extractvalue` instruction. It will return value at
2017 /// element \p x from  \p tuple.
2018 mlir::LLVM::ExtractValueOp
2019 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
2020                          mlir::ConversionPatternRewriter &rewriter,
2021                          mlir::MLIRContext *ctx, int x) {
2022   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
2023   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
2024   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
2025 }
2026 
2027 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
2028 /// boxchar.
2029 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
2030   using FIROpConversion::FIROpConversion;
2031 
2032   mlir::LogicalResult
2033   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
2034                   mlir::ConversionPatternRewriter &rewriter) const override {
2035     mlir::Value boxChar = adaptor.getOperands()[0];
2036     mlir::Location loc = boxChar.getLoc();
2037     mlir::MLIRContext *ctx = boxChar.getContext();
2038     mlir::Type returnValTy = boxCharLen.getResult().getType();
2039 
2040     constexpr int boxcharLenIdx = 1;
2041     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
2042         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
2043     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
2044     rewriter.replaceOp(boxCharLen, lenAfterCast);
2045 
2046     return success();
2047   }
2048 };
2049 
2050 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2051 /// the character buffer and one for the buffer length.
2052 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2053   using FIROpConversion::FIROpConversion;
2054 
2055   mlir::LogicalResult
2056   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2057                   mlir::ConversionPatternRewriter &rewriter) const override {
2058     MLIRContext *ctx = unboxchar.getContext();
2059 
2060     mlir::Type lenTy = convertType(unboxchar.getType(1));
2061     mlir::Value tuple = adaptor.getOperands()[0];
2062     mlir::Type tupleTy = tuple.getType();
2063 
2064     mlir::Location loc = unboxchar.getLoc();
2065     mlir::Value ptrToBuffer =
2066         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2067 
2068     mlir::LLVM::ExtractValueOp len =
2069         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2070     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2071 
2072     rewriter.replaceOp(unboxchar,
2073                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2074     return success();
2075   }
2076 };
2077 
2078 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2079 /// components.
2080 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2081 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2082   using FIROpConversion::FIROpConversion;
2083 
2084   mlir::LogicalResult
2085   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2086                   mlir::ConversionPatternRewriter &rewriter) const override {
2087     return rewriter.notifyMatchFailure(
2088         unboxproc, "fir.unboxproc codegen is not implemented yet");
2089   }
2090 };
2091 
2092 /// Convert `fir.field_index`. The conversion depends on whether the size of
2093 /// the record is static or dynamic.
2094 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2095   using FIROpConversion::FIROpConversion;
2096 
2097   // NB: most field references should be resolved by this point
2098   mlir::LogicalResult
2099   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2100                   mlir::ConversionPatternRewriter &rewriter) const override {
2101     auto recTy = field.on_type().cast<fir::RecordType>();
2102     unsigned index = recTy.getFieldIndex(field.field_id());
2103 
2104     if (!fir::hasDynamicSize(recTy)) {
2105       // Derived type has compile-time constant layout. Return index of the
2106       // component type in the parent type (to be used in GEP).
2107       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2108                                     field.getLoc(), rewriter, index)});
2109       return success();
2110     }
2111 
2112     // Derived type has compile-time constant layout. Call the compiler
2113     // generated function to determine the byte offset of the field at runtime.
2114     // This returns a non-constant.
2115     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2116         field.getContext(), getOffsetMethodName(recTy, field.field_id()));
2117     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2118     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2119         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2120     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2121         field, lowerTy().offsetType(), adaptor.getOperands(),
2122         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2123     return success();
2124   }
2125 
2126   // Re-Construct the name of the compiler generated method that calculates the
2127   // offset
2128   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2129                                                 llvm::StringRef field) {
2130     return recTy.getName().str() + "P." + field.str() + ".offset";
2131   }
2132 };
2133 
2134 } // namespace
2135 
2136 namespace {
2137 /// Convert FIR dialect to LLVM dialect
2138 ///
2139 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
2140 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
2141 ///
2142 /// This pass is not complete yet. We are upstreaming it in small patches.
2143 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
2144 public:
2145   mlir::ModuleOp getModule() { return getOperation(); }
2146 
2147   void runOnOperation() override final {
2148     auto mod = getModule();
2149     if (!forcedTargetTriple.empty()) {
2150       fir::setTargetTriple(mod, forcedTargetTriple);
2151     }
2152 
2153     auto *context = getModule().getContext();
2154     fir::LLVMTypeConverter typeConverter{getModule()};
2155     mlir::OwningRewritePatternList pattern(context);
2156     pattern.insert<
2157         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
2158         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
2159         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
2160         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion,
2161         BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion,
2162         CmpcOpConversion, ConstcOpConversion, ConvertOpConversion,
2163         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
2164         DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
2165         EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
2166         FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
2167         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
2168         InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion,
2169         NegcOpConversion, MulcOpConversion, SelectCaseOpConversion,
2170         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
2171         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
2172         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
2173         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
2174         UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>(
2175         typeConverter);
2176     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
2177     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
2178                                                             pattern);
2179     mlir::ConversionTarget target{*context};
2180     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2181 
2182     // required NOPs for applying a full conversion
2183     target.addLegalOp<mlir::ModuleOp>();
2184 
2185     // apply the patterns
2186     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
2187                                                std::move(pattern)))) {
2188       signalPassFailure();
2189     }
2190   }
2191 };
2192 } // namespace
2193 
2194 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
2195   return std::make_unique<FIRToLLVMLowering>();
2196 }
2197