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.box_tdesc` to the sequence of operations to extract the type
536 /// descriptor from the box.
537 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
538   using FIROpConversion::FIROpConversion;
539 
540   mlir::LogicalResult
541   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
542                   mlir::ConversionPatternRewriter &rewriter) const override {
543     mlir::Value box = adaptor.getOperands()[0];
544     auto loc = boxtypedesc.getLoc();
545     mlir::Type typeTy =
546         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
547     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
548     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
549     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
550                                                         result);
551     return success();
552   }
553 };
554 
555 // `fir.call` -> `llvm.call`
556 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
557   using FIROpConversion::FIROpConversion;
558 
559   mlir::LogicalResult
560   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
561                   mlir::ConversionPatternRewriter &rewriter) const override {
562     SmallVector<mlir::Type> resultTys;
563     for (auto r : call.getResults())
564       resultTys.push_back(convertType(r.getType()));
565     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
566         call, resultTys, adaptor.getOperands(), call->getAttrs());
567     return success();
568   }
569 };
570 
571 static mlir::Type getComplexEleTy(mlir::Type complex) {
572   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
573     return cc.getElementType();
574   return complex.cast<fir::ComplexType>().getElementType();
575 }
576 
577 /// Compare complex values
578 ///
579 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
580 ///
581 /// For completeness, all other comparison are done on the real component only.
582 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
583   using FIROpConversion::FIROpConversion;
584 
585   mlir::LogicalResult
586   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
587                   mlir::ConversionPatternRewriter &rewriter) const override {
588     mlir::ValueRange operands = adaptor.getOperands();
589     mlir::MLIRContext *ctxt = cmp.getContext();
590     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
591     mlir::Type resTy = convertType(cmp.getType());
592     mlir::Location loc = cmp.getLoc();
593     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
594     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
595                                        loc, eleTy, operands[0], pos0),
596                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
597                                        loc, eleTy, operands[1], pos0)};
598     auto rcp =
599         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
600     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
601     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
602                                        loc, eleTy, operands[0], pos1),
603                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
604                                        loc, eleTy, operands[1], pos1)};
605     auto icp =
606         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
607     SmallVector<mlir::Value, 2> cp{rcp, icp};
608     switch (cmp.getPredicate()) {
609     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
610       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
611       break;
612     case mlir::arith::CmpFPredicate::UNE: // .NE.
613       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
614       break;
615     default:
616       rewriter.replaceOp(cmp, rcp.getResult());
617       break;
618     }
619     return success();
620   }
621 };
622 
623 /// Lower complex constants
624 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
625   using FIROpConversion::FIROpConversion;
626 
627   mlir::LogicalResult
628   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
629                   mlir::ConversionPatternRewriter &rewriter) const override {
630     mlir::Location loc = conc.getLoc();
631     mlir::MLIRContext *ctx = conc.getContext();
632     mlir::Type ty = convertType(conc.getType());
633     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
634     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
635     auto realPart =
636         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
637     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
638     auto imPart =
639         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
640     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
641     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
642     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
643     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
644         loc, ty, undef, realPart, realIndex);
645     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
646                                                            imPart, imIndex);
647     return success();
648   }
649 
650   inline APFloat getValue(mlir::Attribute attr) const {
651     return attr.cast<fir::RealAttr>().getValue();
652   }
653 };
654 
655 /// convert value of from-type to value of to-type
656 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
657   using FIROpConversion::FIROpConversion;
658 
659   static bool isFloatingPointTy(mlir::Type ty) {
660     return ty.isa<mlir::FloatType>();
661   }
662 
663   mlir::LogicalResult
664   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
665                   mlir::ConversionPatternRewriter &rewriter) const override {
666     auto fromTy = convertType(convert.value().getType());
667     auto toTy = convertType(convert.res().getType());
668     mlir::Value op0 = adaptor.getOperands()[0];
669     if (fromTy == toTy) {
670       rewriter.replaceOp(convert, op0);
671       return success();
672     }
673     auto loc = convert.getLoc();
674     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
675                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
676       if (fromBits == toBits) {
677         // TODO: Converting between two floating-point representations with the
678         // same bitwidth is not allowed for now.
679         mlir::emitError(loc,
680                         "cannot implicitly convert between two floating-point "
681                         "representations of the same bitwidth");
682         return {};
683       }
684       if (fromBits > toBits)
685         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
686       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
687     };
688     // Complex to complex conversion.
689     if (fir::isa_complex(convert.value().getType()) &&
690         fir::isa_complex(convert.res().getType())) {
691       // Special case: handle the conversion of a complex such that both the
692       // real and imaginary parts are converted together.
693       auto zero = mlir::ArrayAttr::get(convert.getContext(),
694                                        rewriter.getI32IntegerAttr(0));
695       auto one = mlir::ArrayAttr::get(convert.getContext(),
696                                       rewriter.getI32IntegerAttr(1));
697       auto ty = convertType(getComplexEleTy(convert.value().getType()));
698       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
699       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
700       auto nt = convertType(getComplexEleTy(convert.res().getType()));
701       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
702       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
703       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
704       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
705       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
706       auto i1 =
707           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
708       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
709                                                              ic, one);
710       return mlir::success();
711     }
712     // Floating point to floating point conversion.
713     if (isFloatingPointTy(fromTy)) {
714       if (isFloatingPointTy(toTy)) {
715         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
716         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
717         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
718         rewriter.replaceOp(convert, v);
719         return mlir::success();
720       }
721       if (toTy.isa<mlir::IntegerType>()) {
722         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
723         return mlir::success();
724       }
725     } else if (fromTy.isa<mlir::IntegerType>()) {
726       // Integer to integer conversion.
727       if (toTy.isa<mlir::IntegerType>()) {
728         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
729         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
730         assert(fromBits != toBits);
731         if (fromBits > toBits) {
732           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
733           return mlir::success();
734         }
735         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
736         return mlir::success();
737       }
738       // Integer to floating point conversion.
739       if (isFloatingPointTy(toTy)) {
740         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
741         return mlir::success();
742       }
743       // Integer to pointer conversion.
744       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
745         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
746         return mlir::success();
747       }
748     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
749       // Pointer to integer conversion.
750       if (toTy.isa<mlir::IntegerType>()) {
751         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
752         return mlir::success();
753       }
754       // Pointer to pointer conversion.
755       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
756         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
757         return mlir::success();
758       }
759     }
760     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
761   }
762 };
763 
764 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
765 /// table.
766 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
767   using FIROpConversion::FIROpConversion;
768 
769   mlir::LogicalResult
770   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
771                   mlir::ConversionPatternRewriter &rewriter) const override {
772     return rewriter.notifyMatchFailure(
773         dispatch, "fir.dispatch codegen is not implemented yet");
774   }
775 };
776 
777 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
778 /// derived type.
779 struct DispatchTableOpConversion
780     : public FIROpConversion<fir::DispatchTableOp> {
781   using FIROpConversion::FIROpConversion;
782 
783   mlir::LogicalResult
784   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
785                   mlir::ConversionPatternRewriter &rewriter) const override {
786     return rewriter.notifyMatchFailure(
787         dispTab, "fir.dispatch_table codegen is not implemented yet");
788   }
789 };
790 
791 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
792 /// method-name to a function.
793 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
794   using FIROpConversion::FIROpConversion;
795 
796   mlir::LogicalResult
797   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
798                   mlir::ConversionPatternRewriter &rewriter) const override {
799     return rewriter.notifyMatchFailure(
800         dtEnt, "fir.dt_entry codegen is not implemented yet");
801   }
802 };
803 
804 /// Lower `fir.global_len` operation.
805 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
806   using FIROpConversion::FIROpConversion;
807 
808   mlir::LogicalResult
809   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
810                   mlir::ConversionPatternRewriter &rewriter) const override {
811     return rewriter.notifyMatchFailure(
812         globalLen, "fir.global_len codegen is not implemented yet");
813   }
814 };
815 
816 /// Lower `fir.gentypedesc` to a global constant.
817 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
818   using FIROpConversion::FIROpConversion;
819 
820   mlir::LogicalResult
821   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
822                   mlir::ConversionPatternRewriter &rewriter) const override {
823     return rewriter.notifyMatchFailure(
824         gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet");
825   }
826 };
827 
828 /// Lower `fir.has_value` operation to `llvm.return` operation.
829 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
830   using FIROpConversion::FIROpConversion;
831 
832   mlir::LogicalResult
833   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
834                   mlir::ConversionPatternRewriter &rewriter) const override {
835     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
836     return success();
837   }
838 };
839 
840 /// Lower `fir.global` operation to `llvm.global` operation.
841 /// `fir.insert_on_range` operations are replaced with constant dense attribute
842 /// if they are applied on the full range.
843 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
844   using FIROpConversion::FIROpConversion;
845 
846   mlir::LogicalResult
847   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
848                   mlir::ConversionPatternRewriter &rewriter) const override {
849     auto tyAttr = convertType(global.getType());
850     if (global.getType().isa<fir::BoxType>())
851       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
852     auto loc = global.getLoc();
853     mlir::Attribute initAttr{};
854     if (global.initVal())
855       initAttr = global.initVal().getValue();
856     auto linkage = convertLinkage(global.linkName());
857     auto isConst = global.constant().hasValue();
858     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
859         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
860     auto &gr = g.getInitializerRegion();
861     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
862     if (!gr.empty()) {
863       // Replace insert_on_range with a constant dense attribute if the
864       // initialization is on the full range.
865       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
866       for (auto insertOp : insertOnRangeOps) {
867         if (isFullRange(insertOp.coor(), insertOp.getType())) {
868           auto seqTyAttr = convertType(insertOp.getType());
869           auto *op = insertOp.val().getDefiningOp();
870           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
871           if (!constant) {
872             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
873             if (!convertOp)
874               continue;
875             constant = cast<mlir::arith::ConstantOp>(
876                 convertOp.value().getDefiningOp());
877           }
878           mlir::Type vecType = mlir::VectorType::get(
879               insertOp.getType().getShape(), constant.getType());
880           auto denseAttr = mlir::DenseElementsAttr::get(
881               vecType.cast<ShapedType>(), constant.value());
882           rewriter.setInsertionPointAfter(insertOp);
883           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
884               insertOp, seqTyAttr, denseAttr);
885         }
886       }
887     }
888     rewriter.eraseOp(global);
889     return success();
890   }
891 
892   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
893     auto extents = seqTy.getShape();
894     if (indexes.size() / 2 != extents.size())
895       return false;
896     for (unsigned i = 0; i < indexes.size(); i += 2) {
897       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
898         return false;
899       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
900         return false;
901     }
902     return true;
903   }
904 
905   // TODO: String comparaison should be avoided. Replace linkName with an
906   // enumeration.
907   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
908     if (optLinkage.hasValue()) {
909       auto name = optLinkage.getValue();
910       if (name == "internal")
911         return mlir::LLVM::Linkage::Internal;
912       if (name == "linkonce")
913         return mlir::LLVM::Linkage::Linkonce;
914       if (name == "common")
915         return mlir::LLVM::Linkage::Common;
916       if (name == "weak")
917         return mlir::LLVM::Linkage::Weak;
918     }
919     return mlir::LLVM::Linkage::External;
920   }
921 };
922 
923 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
924                  Optional<mlir::ValueRange> destOps,
925                  mlir::ConversionPatternRewriter &rewriter,
926                  mlir::Block *newBlock) {
927   if (destOps.hasValue())
928     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
929                                           newBlock, mlir::ValueRange());
930   else
931     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
932 }
933 
934 template <typename A, typename B>
935 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
936              mlir::ConversionPatternRewriter &rewriter) {
937   if (destOps.hasValue())
938     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
939                                                   dest);
940   else
941     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
942 }
943 
944 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
945                        Optional<mlir::ValueRange> destOps,
946                        mlir::ConversionPatternRewriter &rewriter) {
947   auto *thisBlock = rewriter.getInsertionBlock();
948   auto *newBlock = createBlock(rewriter, dest);
949   rewriter.setInsertionPointToEnd(thisBlock);
950   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
951   rewriter.setInsertionPointToEnd(newBlock);
952 }
953 
954 /// Conversion of `fir.select_case`
955 ///
956 /// The `fir.select_case` operation is converted to a if-then-else ladder.
957 /// Depending on the case condition type, one or several comparison and
958 /// conditional branching can be generated.
959 ///
960 /// A a point value case such as `case(4)`, a lower bound case such as
961 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
962 /// simple comparison between the selector value and the constant value in the
963 /// case. The block associated with the case condition is then executed if
964 /// the comparison succeed otherwise it branch to the next block with the
965 /// comparison for the the next case conditon.
966 ///
967 /// A closed interval case condition such as `case(7:10)` is converted with a
968 /// first comparison and conditional branching for the lower bound. If
969 /// successful, it branch to a second block with the comparison for the
970 /// upper bound in the same case condition.
971 ///
972 /// TODO: lowering of CHARACTER type cases is not handled yet.
973 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
974   using FIROpConversion::FIROpConversion;
975 
976   mlir::LogicalResult
977   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
978                   mlir::ConversionPatternRewriter &rewriter) const override {
979     unsigned conds = caseOp.getNumConditions();
980     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
981     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
982     LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType();
983     if (ty.isa<fir::CharacterType>())
984       return rewriter.notifyMatchFailure(caseOp,
985                                          "conversion of fir.select_case with "
986                                          "character type not implemented yet");
987     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
988     auto loc = caseOp.getLoc();
989     for (unsigned t = 0; t != conds; ++t) {
990       mlir::Block *dest = caseOp.getSuccessor(t);
991       llvm::Optional<mlir::ValueRange> destOps =
992           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
993       llvm::Optional<mlir::ValueRange> cmpOps =
994           *caseOp.getCompareOperands(adaptor.getOperands(), t);
995       mlir::Value caseArg = *(cmpOps.getValue().begin());
996       mlir::Attribute attr = cases[t];
997       if (attr.isa<fir::PointIntervalAttr>()) {
998         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
999             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
1000         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1001         continue;
1002       }
1003       if (attr.isa<fir::LowerBoundAttr>()) {
1004         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1005             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1006         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1007         continue;
1008       }
1009       if (attr.isa<fir::UpperBoundAttr>()) {
1010         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1011             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
1012         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
1013         continue;
1014       }
1015       if (attr.isa<fir::ClosedIntervalAttr>()) {
1016         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1017             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
1018         auto *thisBlock = rewriter.getInsertionBlock();
1019         auto *newBlock1 = createBlock(rewriter, dest);
1020         auto *newBlock2 = createBlock(rewriter, dest);
1021         rewriter.setInsertionPointToEnd(thisBlock);
1022         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
1023         rewriter.setInsertionPointToEnd(newBlock1);
1024         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
1025         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
1026             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
1027         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
1028         rewriter.setInsertionPointToEnd(newBlock2);
1029         continue;
1030       }
1031       assert(attr.isa<mlir::UnitAttr>());
1032       assert((t + 1 == conds) && "unit must be last");
1033       genBrOp(caseOp, dest, destOps, rewriter);
1034     }
1035     return success();
1036   }
1037 };
1038 
1039 template <typename OP>
1040 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
1041                            typename OP::Adaptor adaptor,
1042                            mlir::ConversionPatternRewriter &rewriter) {
1043   unsigned conds = select.getNumConditions();
1044   auto cases = select.getCases().getValue();
1045   mlir::Value selector = adaptor.selector();
1046   auto loc = select.getLoc();
1047   assert(conds > 0 && "select must have cases");
1048 
1049   llvm::SmallVector<mlir::Block *> destinations;
1050   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
1051   mlir::Block *defaultDestination;
1052   mlir::ValueRange defaultOperands;
1053   llvm::SmallVector<int32_t> caseValues;
1054 
1055   for (unsigned t = 0; t != conds; ++t) {
1056     mlir::Block *dest = select.getSuccessor(t);
1057     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
1058     const mlir::Attribute &attr = cases[t];
1059     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
1060       destinations.push_back(dest);
1061       destinationsOperands.push_back(destOps.hasValue() ? *destOps
1062                                                         : ValueRange());
1063       caseValues.push_back(intAttr.getInt());
1064       continue;
1065     }
1066     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
1067     assert((t + 1 == conds) && "unit must be last");
1068     defaultDestination = dest;
1069     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
1070   }
1071 
1072   // LLVM::SwitchOp takes a i32 type for the selector.
1073   if (select.getSelector().getType() != rewriter.getI32Type())
1074     selector =
1075         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
1076 
1077   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
1078       select, selector,
1079       /*defaultDestination=*/defaultDestination,
1080       /*defaultOperands=*/defaultOperands,
1081       /*caseValues=*/caseValues,
1082       /*caseDestinations=*/destinations,
1083       /*caseOperands=*/destinationsOperands,
1084       /*branchWeights=*/ArrayRef<int32_t>());
1085 }
1086 
1087 /// conversion of fir::SelectOp to an if-then-else ladder
1088 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
1089   using FIROpConversion::FIROpConversion;
1090 
1091   mlir::LogicalResult
1092   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
1093                   mlir::ConversionPatternRewriter &rewriter) const override {
1094     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
1095     return success();
1096   }
1097 };
1098 
1099 /// `fir.load` --> `llvm.load`
1100 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1101   using FIROpConversion::FIROpConversion;
1102 
1103   mlir::LogicalResult
1104   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1105                   mlir::ConversionPatternRewriter &rewriter) const override {
1106     // fir.box is a special case because it is considered as an ssa values in
1107     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1108     // and fir.box end up being the same llvm types and loading a
1109     // fir.ref<fir.box> is actually a no op in LLVM.
1110     if (load.getType().isa<fir::BoxType>()) {
1111       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1112     } else {
1113       mlir::Type ty = convertType(load.getType());
1114       ArrayRef<NamedAttribute> at = load->getAttrs();
1115       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1116           load, ty, adaptor.getOperands(), at);
1117     }
1118     return success();
1119   }
1120 };
1121 
1122 /// Lower `fir.select_type` to LLVM IR dialect.
1123 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
1124   using FIROpConversion::FIROpConversion;
1125 
1126   mlir::LogicalResult
1127   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
1128                   mlir::ConversionPatternRewriter &rewriter) const override {
1129     return rewriter.notifyMatchFailure(
1130         select, "fir.select_type codegen is not implemented yet");
1131   }
1132 };
1133 
1134 /// conversion of fir::SelectRankOp to an if-then-else ladder
1135 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
1136   using FIROpConversion::FIROpConversion;
1137 
1138   mlir::LogicalResult
1139   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
1140                   mlir::ConversionPatternRewriter &rewriter) const override {
1141     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
1142     return success();
1143   }
1144 };
1145 
1146 /// `fir.store` --> `llvm.store`
1147 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1148   using FIROpConversion::FIROpConversion;
1149 
1150   mlir::LogicalResult
1151   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1152                   mlir::ConversionPatternRewriter &rewriter) const override {
1153     if (store.value().getType().isa<fir::BoxType>()) {
1154       // fir.box value is actually in memory, load it first before storing it.
1155       mlir::Location loc = store.getLoc();
1156       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1157       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1158           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1159           adaptor.getOperands()[0]);
1160       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1161           store, val, adaptor.getOperands()[1]);
1162     } else {
1163       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1164           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1165     }
1166     return success();
1167   }
1168 };
1169 
1170 /// convert to LLVM IR dialect `undef`
1171 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1172   using FIROpConversion::FIROpConversion;
1173 
1174   mlir::LogicalResult
1175   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1176                   mlir::ConversionPatternRewriter &rewriter) const override {
1177     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1178         undef, convertType(undef.getType()));
1179     return success();
1180   }
1181 };
1182 
1183 /// `fir.unreachable` --> `llvm.unreachable`
1184 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
1185   using FIROpConversion::FIROpConversion;
1186 
1187   mlir::LogicalResult
1188   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
1189                   mlir::ConversionPatternRewriter &rewriter) const override {
1190     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
1191     return success();
1192   }
1193 };
1194 
1195 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1196   using FIROpConversion::FIROpConversion;
1197 
1198   mlir::LogicalResult
1199   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1200                   mlir::ConversionPatternRewriter &rewriter) const override {
1201     auto ty = convertType(zero.getType());
1202     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1203       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1204     } else if (ty.isa<mlir::IntegerType>()) {
1205       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1206           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1207     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1208       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1209           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1210     } else {
1211       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
1212       return rewriter.notifyMatchFailure(
1213           zero,
1214           "conversion of fir.zero with aggregate type not implemented yet");
1215     }
1216     return success();
1217   }
1218 };
1219 
1220 /// Common base class for embox to descriptor conversion.
1221 template <typename OP>
1222 struct EmboxCommonConversion : public FIROpConversion<OP> {
1223   using FIROpConversion<OP>::FIROpConversion;
1224 
1225   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1226   // The order to find the LLVMFuncOp is as follows:
1227   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1228   // 2. The first ancestor that is a LLVMFuncOp.
1229   mlir::LLVM::LLVMFuncOp
1230   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1231     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1232     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1233                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1234                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1235   }
1236 
1237   // Generate an alloca of size 1 and type \p toTy.
1238   mlir::LLVM::AllocaOp
1239   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1240                     mlir::ConversionPatternRewriter &rewriter) const {
1241     auto thisPt = rewriter.saveInsertionPoint();
1242     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1243     rewriter.setInsertionPointToStart(&func.front());
1244     auto size = this->genI32Constant(loc, rewriter, 1);
1245     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1246     rewriter.restoreInsertionPoint(thisPt);
1247     return al;
1248   }
1249 
1250   static int getCFIAttr(fir::BoxType boxTy) {
1251     auto eleTy = boxTy.getEleTy();
1252     if (eleTy.isa<fir::PointerType>())
1253       return CFI_attribute_pointer;
1254     if (eleTy.isa<fir::HeapType>())
1255       return CFI_attribute_allocatable;
1256     return CFI_attribute_other;
1257   }
1258 
1259   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1260     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1261         .template dyn_cast<fir::RecordType>();
1262   }
1263   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1264     auto recTy = unwrapIfDerived(boxTy);
1265     return recTy && recTy.getNumLenParams() > 0;
1266   }
1267   static bool isDerivedType(fir::BoxType boxTy) {
1268     return unwrapIfDerived(boxTy) != nullptr;
1269   }
1270 
1271   // Get the element size and CFI type code of the boxed value.
1272   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1273       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1274       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1275     auto doInteger =
1276         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1277       int typeCode = fir::integerBitsToTypeCode(width);
1278       return {this->genConstantOffset(loc, rewriter, width / 8),
1279               this->genConstantOffset(loc, rewriter, typeCode)};
1280     };
1281     auto doLogical =
1282         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1283       int typeCode = fir::logicalBitsToTypeCode(width);
1284       return {this->genConstantOffset(loc, rewriter, width / 8),
1285               this->genConstantOffset(loc, rewriter, typeCode)};
1286     };
1287     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1288       int typeCode = fir::realBitsToTypeCode(width);
1289       return {this->genConstantOffset(loc, rewriter, width / 8),
1290               this->genConstantOffset(loc, rewriter, typeCode)};
1291     };
1292     auto doComplex =
1293         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1294       auto typeCode = fir::complexBitsToTypeCode(width);
1295       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1296               this->genConstantOffset(loc, rewriter, typeCode)};
1297     };
1298     auto doCharacter =
1299         [&](unsigned width,
1300             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1301       auto typeCode = fir::characterBitsToTypeCode(width);
1302       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1303       if (width == 8)
1304         return {len, typeCodeVal};
1305       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1306       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1307       auto size =
1308           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1309       return {size, typeCodeVal};
1310     };
1311     auto getKindMap = [&]() -> fir::KindMapping & {
1312       return this->lowerTy().getKindMap();
1313     };
1314     // Pointer-like types.
1315     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1316       boxEleTy = eleTy;
1317     // Integer types.
1318     if (fir::isa_integer(boxEleTy)) {
1319       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1320         return doInteger(ty.getWidth());
1321       auto ty = boxEleTy.cast<fir::IntegerType>();
1322       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1323     }
1324     // Floating point types.
1325     if (fir::isa_real(boxEleTy)) {
1326       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1327         return doFloat(ty.getWidth());
1328       auto ty = boxEleTy.cast<fir::RealType>();
1329       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1330     }
1331     // Complex types.
1332     if (fir::isa_complex(boxEleTy)) {
1333       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1334         return doComplex(
1335             ty.getElementType().cast<mlir::FloatType>().getWidth());
1336       auto ty = boxEleTy.cast<fir::ComplexType>();
1337       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1338     }
1339     // Character types.
1340     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1341       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1342       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1343         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1344         return doCharacter(charWidth, len);
1345       }
1346       assert(!lenParams.empty());
1347       return doCharacter(charWidth, lenParams.back());
1348     }
1349     // Logical type.
1350     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1351       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1352     // Array types.
1353     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1354       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1355     // Derived-type types.
1356     if (boxEleTy.isa<fir::RecordType>()) {
1357       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1358           this->lowerTy().convertType(boxEleTy));
1359       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1360       auto one =
1361           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1362       auto gep = rewriter.create<mlir::LLVM::GEPOp>(
1363           loc, ptrTy, mlir::ValueRange{nullPtr, one});
1364       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1365           loc, this->lowerTy().indexType(), gep);
1366       return {eleSize,
1367               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1368     }
1369     // Reference type.
1370     if (fir::isa_ref_type(boxEleTy)) {
1371       // FIXME: use the target pointer size rather than sizeof(void*)
1372       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1373               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1374     }
1375     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1376   }
1377 
1378   /// Basic pattern to write a field in the descriptor
1379   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1380                           mlir::Location loc, mlir::Value dest,
1381                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1382                           bool bitcast = false) const {
1383     auto boxTy = dest.getType();
1384     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1385     if (bitcast)
1386       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1387     else
1388       value = this->integerCast(loc, rewriter, fldTy, value);
1389     SmallVector<mlir::Attribute, 2> attrs;
1390     for (auto i : fldIndexes)
1391       attrs.push_back(rewriter.getI32IntegerAttr(i));
1392     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1393     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1394                                                       indexesAttr);
1395   }
1396 
1397   inline mlir::Value
1398   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1399                     mlir::Location loc, mlir::Value dest,
1400                     mlir::Value base) const {
1401     return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true);
1402   }
1403 
1404   /// Get the address of the type descriptor global variable that was created by
1405   /// lowering for derived type \p recType.
1406   template <typename BOX>
1407   mlir::Value
1408   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1409                     mlir::Location loc, fir::RecordType recType) const {
1410     std::string name = recType.getLoweredName();
1411     auto module = box->template getParentOfType<mlir::ModuleOp>();
1412     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1413       auto ty = mlir::LLVM::LLVMPointerType::get(
1414           this->lowerTy().convertType(global.getType()));
1415       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1416                                                       global.sym_name());
1417     }
1418     if (auto global =
1419             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1420       // The global may have already been translated to LLVM.
1421       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1422       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1423                                                       global.sym_name());
1424     }
1425     // The global does not exist in the current translation unit, but may be
1426     // defined elsewhere (e.g., type defined in a module).
1427     // For now, create a extern_weak symbol (will become nullptr if unresolved)
1428     // to support generating code without the front-end generated symbols.
1429     // These could be made available_externally to require the symbols to be
1430     // defined elsewhere and to cause link-time failure otherwise.
1431     auto i8Ty = rewriter.getIntegerType(8);
1432     mlir::OpBuilder modBuilder(module.getBodyRegion());
1433     // TODO: The symbol should be lowered to constant in lowering, they are read
1434     // only.
1435     modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
1436                                             mlir::LLVM::Linkage::ExternWeak,
1437                                             name, mlir::Attribute{});
1438     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1439     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1440   }
1441 
1442   template <typename BOX>
1443   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1444   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1445                        unsigned rank, mlir::ValueRange lenParams) const {
1446     auto loc = box.getLoc();
1447     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1448     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1449     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1450     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1451     mlir::Value descriptor =
1452         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1453 
1454     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1455     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1456       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1457         typeparams.push_back(box.substr()[1]);
1458     }
1459 
1460     // Write each of the fields with the appropriate values
1461     auto [eleSize, cfiTy] =
1462         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1463     descriptor =
1464         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1465     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1466                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1467     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1468                              this->genI32Constant(loc, rewriter, rank));
1469     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1470     descriptor =
1471         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1472                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1473     const bool hasAddendum = isDerivedType(boxTy);
1474     descriptor =
1475         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1476                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1477 
1478     if (hasAddendum) {
1479       auto isArray =
1480           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1481       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1482       auto typeDesc =
1483           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1484       descriptor =
1485           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1486                       /*bitCast=*/true);
1487     }
1488 
1489     return {boxTy, descriptor, eleSize};
1490   }
1491 
1492   /// If the embox is not in a globalOp body, allocate storage for the box;
1493   /// store the value inside and return the generated alloca. Return the input
1494   /// value otherwise.
1495   mlir::Value
1496   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1497                                mlir::Location loc, mlir::Value boxValue) const {
1498     auto *thisBlock = rewriter.getInsertionBlock();
1499     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1500       return boxValue;
1501     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1502     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1503     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1504     return alloca;
1505   }
1506 };
1507 
1508 /// Create a generic box on a memory reference. This conversions lowers the
1509 /// abstract box to the appropriate, initialized descriptor.
1510 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1511   using EmboxCommonConversion::EmboxCommonConversion;
1512 
1513   mlir::LogicalResult
1514   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1515                   mlir::ConversionPatternRewriter &rewriter) const override {
1516     assert(!embox.getShape() && "There should be no dims on this embox op");
1517     auto [boxTy, dest, eleSize] =
1518         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1519                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1520     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1521                              adaptor.getOperands()[0]);
1522     if (isDerivedTypeWithLenParams(boxTy))
1523       return rewriter.notifyMatchFailure(
1524           embox, "fir.embox codegen of derived with length parameters not "
1525                  "implemented yet");
1526     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1527     rewriter.replaceOp(embox, result);
1528     return success();
1529   }
1530 };
1531 
1532 // Code shared between insert_value and extract_value Ops.
1533 struct ValueOpCommon {
1534   // Translate the arguments pertaining to any multidimensional array to
1535   // row-major order for LLVM-IR.
1536   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1537                          mlir::Type ty) {
1538     assert(ty && "type is null");
1539     const auto end = attrs.size();
1540     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1541       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1542         const auto dim = getDimension(seq);
1543         if (dim > 1) {
1544           auto ub = std::min(i + dim, end);
1545           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1546           i += dim - 1;
1547         }
1548         ty = getArrayElementType(seq);
1549       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1550         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1551       } else {
1552         llvm_unreachable("index into invalid type");
1553       }
1554     }
1555   }
1556 
1557   static llvm::SmallVector<mlir::Attribute>
1558   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1559                  mlir::ArrayAttr arrAttr) {
1560     llvm::SmallVector<mlir::Attribute> attrs;
1561     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1562       if (i->isa<mlir::IntegerAttr>()) {
1563         attrs.push_back(*i);
1564       } else {
1565         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1566         ++i;
1567         auto ty = i->cast<mlir::TypeAttr>().getValue();
1568         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1569         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1570       }
1571     }
1572     return attrs;
1573   }
1574 
1575 private:
1576   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1577     unsigned result = 1;
1578     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1579          eleTy;
1580          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1581       ++result;
1582     return result;
1583   }
1584 
1585   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1586     auto eleTy = ty.getElementType();
1587     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1588       eleTy = arrTy.getElementType();
1589     return eleTy;
1590   }
1591 };
1592 
1593 /// Extract a subobject value from an ssa-value of aggregate type
1594 struct ExtractValueOpConversion
1595     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1596       public ValueOpCommon {
1597   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1598 
1599   mlir::LogicalResult
1600   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1601             mlir::ConversionPatternRewriter &rewriter) const override {
1602     auto attrs = collectIndices(rewriter, extractVal.coor());
1603     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1604     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1605     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1606         extractVal, ty, adaptor.getOperands()[0], position);
1607     return success();
1608   }
1609 };
1610 
1611 /// InsertValue is the generalized instruction for the composition of new
1612 /// aggregate type values.
1613 struct InsertValueOpConversion
1614     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1615       public ValueOpCommon {
1616   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1617 
1618   mlir::LogicalResult
1619   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1620             mlir::ConversionPatternRewriter &rewriter) const override {
1621     auto attrs = collectIndices(rewriter, insertVal.coor());
1622     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1623     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1624     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1625         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1626         position);
1627     return success();
1628   }
1629 };
1630 
1631 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1632 struct InsertOnRangeOpConversion
1633     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1634   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1635 
1636   // Increments an array of subscripts in a row major fasion.
1637   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1638                            SmallVector<uint64_t> &subscripts) const {
1639     for (size_t i = dims.size(); i > 0; --i) {
1640       if (++subscripts[i - 1] < dims[i - 1]) {
1641         return;
1642       }
1643       subscripts[i - 1] = 0;
1644     }
1645   }
1646 
1647   mlir::LogicalResult
1648   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1649             mlir::ConversionPatternRewriter &rewriter) const override {
1650 
1651     llvm::SmallVector<uint64_t> dims;
1652     auto type = adaptor.getOperands()[0].getType();
1653 
1654     // Iteratively extract the array dimensions from the type.
1655     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1656       dims.push_back(t.getNumElements());
1657       type = t.getElementType();
1658     }
1659 
1660     SmallVector<uint64_t> lBounds;
1661     SmallVector<uint64_t> uBounds;
1662 
1663     // Extract integer value from the attribute
1664     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
1665         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
1666           return a.cast<IntegerAttr>().getInt();
1667         }));
1668 
1669     // Unzip the upper and lower bound and convert to a row major format.
1670     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
1671       uBounds.push_back(*i++);
1672       lBounds.push_back(*i);
1673     }
1674 
1675     auto &subscripts = lBounds;
1676     auto loc = range.getLoc();
1677     mlir::Value lastOp = adaptor.getOperands()[0];
1678     mlir::Value insertVal = adaptor.getOperands()[1];
1679 
1680     auto i64Ty = rewriter.getI64Type();
1681     while (subscripts != uBounds) {
1682       // Convert uint64_t's to Attribute's.
1683       SmallVector<mlir::Attribute> subscriptAttrs;
1684       for (const auto &subscript : subscripts)
1685         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
1686       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1687           loc, ty, lastOp, insertVal,
1688           ArrayAttr::get(range.getContext(), subscriptAttrs));
1689 
1690       incrementSubscripts(dims, subscripts);
1691     }
1692 
1693     // Convert uint64_t's to Attribute's.
1694     SmallVector<mlir::Attribute> subscriptAttrs;
1695     for (const auto &subscript : subscripts)
1696       subscriptAttrs.push_back(
1697           IntegerAttr::get(rewriter.getI64Type(), subscript));
1698     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
1699 
1700     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1701         range, ty, lastOp, insertVal,
1702         ArrayAttr::get(range.getContext(), arrayRef));
1703 
1704     return success();
1705   }
1706 };
1707 
1708 //
1709 // Primitive operations on Complex types
1710 //
1711 
1712 /// Generate inline code for complex addition/subtraction
1713 template <typename LLVMOP, typename OPTY>
1714 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
1715                                      mlir::ConversionPatternRewriter &rewriter,
1716                                      fir::LLVMTypeConverter &lowering) {
1717   mlir::Value a = opnds[0];
1718   mlir::Value b = opnds[1];
1719   auto loc = sumop.getLoc();
1720   auto ctx = sumop.getContext();
1721   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1722   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1723   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
1724   mlir::Type ty = lowering.convertType(sumop.getType());
1725   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1726   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1727   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1728   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1729   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
1730   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
1731   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1732   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
1733   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
1734 }
1735 
1736 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
1737   using FIROpConversion::FIROpConversion;
1738 
1739   mlir::LogicalResult
1740   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
1741                   mlir::ConversionPatternRewriter &rewriter) const override {
1742     // given: (x + iy) + (x' + iy')
1743     // result: (x + x') + i(y + y')
1744     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
1745                                             rewriter, lowerTy());
1746     rewriter.replaceOp(addc, r.getResult());
1747     return success();
1748   }
1749 };
1750 
1751 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
1752   using FIROpConversion::FIROpConversion;
1753 
1754   mlir::LogicalResult
1755   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
1756                   mlir::ConversionPatternRewriter &rewriter) const override {
1757     // given: (x + iy) - (x' + iy')
1758     // result: (x - x') + i(y - y')
1759     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
1760                                             rewriter, lowerTy());
1761     rewriter.replaceOp(subc, r.getResult());
1762     return success();
1763   }
1764 };
1765 
1766 /// Inlined complex multiply
1767 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
1768   using FIROpConversion::FIROpConversion;
1769 
1770   mlir::LogicalResult
1771   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
1772                   mlir::ConversionPatternRewriter &rewriter) const override {
1773     // TODO: Can we use a call to __muldc3 ?
1774     // given: (x + iy) * (x' + iy')
1775     // result: (xx'-yy')+i(xy'+yx')
1776     mlir::Value a = adaptor.getOperands()[0];
1777     mlir::Value b = adaptor.getOperands()[1];
1778     auto loc = mulc.getLoc();
1779     auto *ctx = mulc.getContext();
1780     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1781     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1782     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
1783     mlir::Type ty = convertType(mulc.getType());
1784     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1785     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1786     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1787     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1788     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1789     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1790     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1791     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
1792     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1793     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
1794     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1795     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1796     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1797     rewriter.replaceOp(mulc, r0.getResult());
1798     return success();
1799   }
1800 };
1801 
1802 /// Inlined complex division
1803 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
1804   using FIROpConversion::FIROpConversion;
1805 
1806   mlir::LogicalResult
1807   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
1808                   mlir::ConversionPatternRewriter &rewriter) const override {
1809     // TODO: Can we use a call to __divdc3 instead?
1810     // Just generate inline code for now.
1811     // given: (x + iy) / (x' + iy')
1812     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
1813     mlir::Value a = adaptor.getOperands()[0];
1814     mlir::Value b = adaptor.getOperands()[1];
1815     auto loc = divc.getLoc();
1816     auto *ctx = divc.getContext();
1817     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1818     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1819     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
1820     mlir::Type ty = convertType(divc.getType());
1821     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
1822     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
1823     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
1824     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
1825     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
1826     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
1827     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
1828     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
1829     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
1830     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
1831     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
1832     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
1833     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
1834     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
1835     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
1836     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
1837     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
1838     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
1839     rewriter.replaceOp(divc, r0.getResult());
1840     return success();
1841   }
1842 };
1843 
1844 /// Inlined complex negation
1845 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
1846   using FIROpConversion::FIROpConversion;
1847 
1848   mlir::LogicalResult
1849   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
1850                   mlir::ConversionPatternRewriter &rewriter) const override {
1851     // given: -(x + iy)
1852     // result: -x - iy
1853     auto *ctxt = neg.getContext();
1854     auto eleTy = convertType(getComplexEleTy(neg.getType()));
1855     auto ty = convertType(neg.getType());
1856     auto loc = neg.getLoc();
1857     mlir::Value o0 = adaptor.getOperands()[0];
1858     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
1859     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
1860     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
1861     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
1862     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
1863     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
1864     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
1865     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
1866     return success();
1867   }
1868 };
1869 
1870 /// Conversion pattern for operation that must be dead. The information in these
1871 /// operations is used by other operation. At this point they should not have
1872 /// anymore uses.
1873 /// These operations are normally dead after the pre-codegen pass.
1874 template <typename FromOp>
1875 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
1876   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
1877       : FIROpConversion<FromOp>(lowering) {}
1878   using OpAdaptor = typename FromOp::Adaptor;
1879 
1880   mlir::LogicalResult
1881   matchAndRewrite(FromOp op, OpAdaptor adaptor,
1882                   mlir::ConversionPatternRewriter &rewriter) const final {
1883     if (!op->getUses().empty())
1884       return rewriter.notifyMatchFailure(op, "op must be dead");
1885     rewriter.eraseOp(op);
1886     return success();
1887   }
1888 };
1889 
1890 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
1891   using MustBeDeadConversion::MustBeDeadConversion;
1892 };
1893 
1894 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
1895   using MustBeDeadConversion::MustBeDeadConversion;
1896 };
1897 
1898 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
1899   using MustBeDeadConversion::MustBeDeadConversion;
1900 };
1901 
1902 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
1903   using MustBeDeadConversion::MustBeDeadConversion;
1904 };
1905 
1906 /// `fir.is_present` -->
1907 /// ```
1908 ///  %0 = llvm.mlir.constant(0 : i64)
1909 ///  %1 = llvm.ptrtoint %0
1910 ///  %2 = llvm.icmp "ne" %1, %0 : i64
1911 /// ```
1912 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
1913   using FIROpConversion::FIROpConversion;
1914 
1915   mlir::LogicalResult
1916   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
1917                   mlir::ConversionPatternRewriter &rewriter) const override {
1918     mlir::Type idxTy = lowerTy().indexType();
1919     mlir::Location loc = isPresent.getLoc();
1920     auto ptr = adaptor.getOperands()[0];
1921 
1922     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
1923       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
1924       assert(!structTy.isOpaque() && !structTy.getBody().empty());
1925 
1926       mlir::Type ty = structTy.getBody()[0];
1927       mlir::MLIRContext *ctx = isPresent.getContext();
1928       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1929       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
1930     }
1931     mlir::LLVM::ConstantOp c0 =
1932         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
1933     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
1934     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1935         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
1936 
1937     return success();
1938   }
1939 };
1940 
1941 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
1942 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
1943 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
1944 /// element is the length of the character buffer (`#n`).
1945 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
1946   using FIROpConversion::FIROpConversion;
1947 
1948   mlir::LogicalResult
1949   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
1950                   mlir::ConversionPatternRewriter &rewriter) const override {
1951     mlir::ValueRange operands = adaptor.getOperands();
1952     MLIRContext *ctx = emboxChar.getContext();
1953 
1954     mlir::Value charBuffer = operands[0];
1955     mlir::Value charBufferLen = operands[1];
1956 
1957     mlir::Location loc = emboxChar.getLoc();
1958     mlir::Type llvmStructTy = convertType(emboxChar.getType());
1959     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
1960 
1961     mlir::Type lenTy =
1962         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
1963     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
1964 
1965     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
1966     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
1967     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
1968         loc, llvmStructTy, llvmStruct, charBuffer, c0);
1969     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1970         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
1971 
1972     return success();
1973   }
1974 };
1975 
1976 /// Construct an `llvm.extractvalue` instruction. It will return value at
1977 /// element \p x from  \p tuple.
1978 mlir::LLVM::ExtractValueOp
1979 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
1980                          mlir::ConversionPatternRewriter &rewriter,
1981                          mlir::MLIRContext *ctx, int x) {
1982   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
1983   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
1984   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
1985 }
1986 
1987 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
1988 /// boxchar.
1989 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
1990   using FIROpConversion::FIROpConversion;
1991 
1992   mlir::LogicalResult
1993   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
1994                   mlir::ConversionPatternRewriter &rewriter) const override {
1995     mlir::Value boxChar = adaptor.getOperands()[0];
1996     mlir::Location loc = boxChar.getLoc();
1997     mlir::MLIRContext *ctx = boxChar.getContext();
1998     mlir::Type returnValTy = boxCharLen.getResult().getType();
1999 
2000     constexpr int boxcharLenIdx = 1;
2001     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
2002         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
2003     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
2004     rewriter.replaceOp(boxCharLen, lenAfterCast);
2005 
2006     return success();
2007   }
2008 };
2009 
2010 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2011 /// the character buffer and one for the buffer length.
2012 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2013   using FIROpConversion::FIROpConversion;
2014 
2015   mlir::LogicalResult
2016   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2017                   mlir::ConversionPatternRewriter &rewriter) const override {
2018     MLIRContext *ctx = unboxchar.getContext();
2019 
2020     mlir::Type lenTy = convertType(unboxchar.getType(1));
2021     mlir::Value tuple = adaptor.getOperands()[0];
2022     mlir::Type tupleTy = tuple.getType();
2023 
2024     mlir::Location loc = unboxchar.getLoc();
2025     mlir::Value ptrToBuffer =
2026         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2027 
2028     mlir::LLVM::ExtractValueOp len =
2029         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2030     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2031 
2032     rewriter.replaceOp(unboxchar,
2033                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2034     return success();
2035   }
2036 };
2037 
2038 } // namespace
2039 
2040 namespace {
2041 /// Convert FIR dialect to LLVM dialect
2042 ///
2043 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
2044 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
2045 ///
2046 /// This pass is not complete yet. We are upstreaming it in small patches.
2047 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
2048 public:
2049   mlir::ModuleOp getModule() { return getOperation(); }
2050 
2051   void runOnOperation() override final {
2052     auto mod = getModule();
2053     if (!forcedTargetTriple.empty()) {
2054       fir::setTargetTriple(mod, forcedTargetTriple);
2055     }
2056 
2057     auto *context = getModule().getContext();
2058     fir::LLVMTypeConverter typeConverter{getModule()};
2059     mlir::OwningRewritePatternList pattern(context);
2060     pattern.insert<
2061         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
2062         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
2063         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
2064         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion,
2065         BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion,
2066         ConstcOpConversion, ConvertOpConversion, DispatchOpConversion,
2067         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
2068         EmboxOpConversion, EmboxCharOpConversion, ExtractValueOpConversion,
2069         HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion,
2070         GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion,
2071         IsPresentOpConversion, LoadOpConversion, NegcOpConversion,
2072         MulcOpConversion, SelectCaseOpConversion, SelectOpConversion,
2073         SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion,
2074         ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion,
2075         StoreOpConversion, StringLitOpConversion, SubcOpConversion,
2076         UnboxCharOpConversion, UndefOpConversion, UnreachableOpConversion,
2077         ZeroOpConversion>(typeConverter);
2078     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
2079     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
2080                                                             pattern);
2081     mlir::ConversionTarget target{*context};
2082     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2083 
2084     // required NOPs for applying a full conversion
2085     target.addLegalOp<mlir::ModuleOp>();
2086 
2087     // apply the patterns
2088     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
2089                                                std::move(pattern)))) {
2090       signalPassFailure();
2091     }
2092   }
2093 };
2094 } // namespace
2095 
2096 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
2097   return std::make_unique<FIRToLLVMLowering>();
2098 }
2099