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 "CGOps.h"
15 #include "PassDetail.h"
16 #include "flang/ISO_Fortran_binding.h"
17 #include "flang/Optimizer/Dialect/FIRAttr.h"
18 #include "flang/Optimizer/Dialect/FIROps.h"
19 #include "flang/Optimizer/Support/InternalNames.h"
20 #include "flang/Optimizer/Support/TypeCode.h"
21 #include "flang/Semantics/runtime-type-info.h"
22 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
23 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
24 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
25 #include "mlir/Conversion/LLVMCommon/Pattern.h"
26 #include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
27 #include "mlir/IR/BuiltinTypes.h"
28 #include "mlir/IR/Matchers.h"
29 #include "mlir/Pass/Pass.h"
30 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
31 #include "llvm/ADT/ArrayRef.h"
32 
33 #define DEBUG_TYPE "flang-codegen"
34 
35 using namespace mlir;
36 
37 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
38 #include "TypeConverter.h"
39 
40 // TODO: This should really be recovered from the specified target.
41 static constexpr unsigned defaultAlign = 8;
42 
43 /// `fir.box` attribute values as defined for CFI_attribute_t in
44 /// flang/ISO_Fortran_binding.h.
45 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
46 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
47 
48 static inline mlir::Type getVoidPtrType(mlir::MLIRContext *context) {
49   return mlir::LLVM::LLVMPointerType::get(mlir::IntegerType::get(context, 8));
50 }
51 
52 static mlir::LLVM::ConstantOp
53 genConstantIndex(mlir::Location loc, mlir::Type ity,
54                  mlir::ConversionPatternRewriter &rewriter,
55                  std::int64_t offset) {
56   auto cattr = rewriter.getI64IntegerAttr(offset);
57   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
58 }
59 
60 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
61                           mlir::Block *insertBefore) {
62   assert(insertBefore && "expected valid insertion block");
63   return rewriter.createBlock(insertBefore->getParent(),
64                               mlir::Region::iterator(insertBefore));
65 }
66 
67 namespace {
68 /// FIR conversion pattern template
69 template <typename FromOp>
70 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
71 public:
72   explicit FIROpConversion(fir::LLVMTypeConverter &lowering,
73                            const fir::FIRToLLVMPassOptions &options)
74       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering), options(options) {}
75 
76 protected:
77   mlir::Type convertType(mlir::Type ty) const {
78     return lowerTy().convertType(ty);
79   }
80   mlir::Type voidPtrTy() const { return getVoidPtrType(); }
81 
82   mlir::Type getVoidPtrType() const {
83     return mlir::LLVM::LLVMPointerType::get(
84         mlir::IntegerType::get(&lowerTy().getContext(), 8));
85   }
86 
87   mlir::LLVM::ConstantOp
88   genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
89                  int value) const {
90     mlir::Type i32Ty = rewriter.getI32Type();
91     mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
92     return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
93   }
94 
95   mlir::LLVM::ConstantOp
96   genConstantOffset(mlir::Location loc,
97                     mlir::ConversionPatternRewriter &rewriter,
98                     int offset) const {
99     mlir::Type ity = lowerTy().offsetType();
100     mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
101     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
102   }
103 
104   /// Perform an extension or truncation as needed on an integer value. Lowering
105   /// to the specific target may involve some sign-extending or truncation of
106   /// values, particularly to fit them from abstract box types to the
107   /// appropriate reified structures.
108   mlir::Value integerCast(mlir::Location loc,
109                           mlir::ConversionPatternRewriter &rewriter,
110                           mlir::Type ty, mlir::Value val) const {
111     auto valTy = val.getType();
112     // If the value was not yet lowered, lower its type so that it can
113     // be used in getPrimitiveTypeSizeInBits.
114     if (!valTy.isa<mlir::IntegerType>())
115       valTy = convertType(valTy);
116     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
117     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
118     if (toSize < fromSize)
119       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
120     if (toSize > fromSize)
121       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
122     return val;
123   }
124 
125   /// Construct code sequence to extract the specifc value from a `fir.box`.
126   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
127                               mlir::Type resultTy,
128                               mlir::ConversionPatternRewriter &rewriter,
129                               unsigned boxValue) const {
130     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
131     mlir::LLVM::ConstantOp cValuePos =
132         genConstantOffset(loc, rewriter, boxValue);
133     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
134     auto p = rewriter.create<mlir::LLVM::GEPOp>(
135         loc, pty, box, mlir::ValueRange{c0, cValuePos});
136     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
137   }
138 
139   /// Method to construct code sequence to get the triple for dimension `dim`
140   /// from a box.
141   SmallVector<mlir::Value, 3>
142   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
143                  mlir::Value box, mlir::Value dim,
144                  mlir::ConversionPatternRewriter &rewriter) const {
145     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
146     mlir::LLVM::ConstantOp cDims =
147         genConstantOffset(loc, rewriter, kDimsPosInBox);
148     mlir::LLVM::LoadOp l0 =
149         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
150     mlir::LLVM::LoadOp l1 =
151         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
152     mlir::LLVM::LoadOp l2 =
153         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
154     return {l0.getResult(), l1.getResult(), l2.getResult()};
155   }
156 
157   mlir::LLVM::LoadOp
158   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
159                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
160                  mlir::Type ty,
161                  mlir::ConversionPatternRewriter &rewriter) const {
162     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
163     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
164     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
165     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
166   }
167 
168   mlir::Value
169   loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim,
170                     mlir::ConversionPatternRewriter &rewriter) const {
171     auto idxTy = lowerTy().indexType();
172     auto c0 = genConstantOffset(loc, rewriter, 0);
173     auto cDims = genConstantOffset(loc, rewriter, kDimsPosInBox);
174     auto dimValue = genConstantIndex(loc, idxTy, rewriter, dim);
175     return loadFromOffset(loc, box, c0, cDims, dimValue, kDimStridePos, idxTy,
176                           rewriter);
177   }
178 
179   /// Read base address from a fir.box. Returned address has type ty.
180   mlir::Value
181   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
182                       mlir::ConversionPatternRewriter &rewriter) const {
183     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
184     mlir::LLVM::ConstantOp cAddr =
185         genConstantOffset(loc, rewriter, kAddrPosInBox);
186     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
187     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
188     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
189   }
190 
191   mlir::Value
192   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
193                          mlir::ConversionPatternRewriter &rewriter) const {
194     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
195     mlir::LLVM::ConstantOp cElemLen =
196         genConstantOffset(loc, rewriter, kElemLenPosInBox);
197     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
198     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
199     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
200   }
201 
202   // Get the element type given an LLVM type that is of the form
203   // [llvm.ptr](array|struct|vector)+ and the provided indexes.
204   static mlir::Type getBoxEleTy(mlir::Type type,
205                                 llvm::ArrayRef<unsigned> indexes) {
206     if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
207       type = t.getElementType();
208     for (auto i : indexes) {
209       if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
210         assert(!t.isOpaque() && i < t.getBody().size());
211         type = t.getBody()[i];
212       } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
213         type = t.getElementType();
214       } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
215         type = t.getElementType();
216       } else {
217         fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
218                             "request for invalid box element type");
219       }
220     }
221     return type;
222   }
223 
224   // Return LLVM type of the base address given the LLVM type
225   // of the related descriptor (lowered fir.box type).
226   static mlir::Type getBaseAddrTypeFromBox(mlir::Type type) {
227     return getBoxEleTy(type, {kAddrPosInBox});
228   }
229 
230   // Load the attribute from the \p box and perform a check against \p maskValue
231   // The final comparison is implemented as `(attribute & maskValue) != 0`.
232   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
233                                    mlir::ConversionPatternRewriter &rewriter,
234                                    unsigned maskValue) const {
235     mlir::Type attrTy = rewriter.getI32Type();
236     mlir::Value attribute =
237         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
238     mlir::LLVM::ConstantOp attrMask =
239         genConstantOffset(loc, rewriter, maskValue);
240     auto maskRes =
241         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
242     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
243     return rewriter.create<mlir::LLVM::ICmpOp>(
244         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
245   }
246 
247   template <typename... ARGS>
248   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
249                            mlir::ConversionPatternRewriter &rewriter,
250                            mlir::Value base, ARGS... args) const {
251     SmallVector<mlir::Value> cv{args...};
252     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
253   }
254 
255   fir::LLVMTypeConverter &lowerTy() const {
256     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
257   }
258 
259   const fir::FIRToLLVMPassOptions &options;
260 };
261 
262 /// FIR conversion pattern template
263 template <typename FromOp>
264 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
265 public:
266   using FIROpConversion<FromOp>::FIROpConversion;
267   using OpAdaptor = typename FromOp::Adaptor;
268 
269   mlir::LogicalResult
270   matchAndRewrite(FromOp op, OpAdaptor adaptor,
271                   mlir::ConversionPatternRewriter &rewriter) const final {
272     mlir::Type ty = this->convertType(op.getType());
273     return doRewrite(op, ty, adaptor, rewriter);
274   }
275 
276   virtual mlir::LogicalResult
277   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
278             mlir::ConversionPatternRewriter &rewriter) const = 0;
279 };
280 
281 // Lower `fir.address_of` operation to `llvm.address_of` operation.
282 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
283   using FIROpConversion::FIROpConversion;
284 
285   mlir::LogicalResult
286   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
287                   mlir::ConversionPatternRewriter &rewriter) const override {
288     auto ty = convertType(addr.getType());
289     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
290         addr, ty, addr.getSymbol().getRootReference().getValue());
291     return success();
292   }
293 };
294 } // namespace
295 
296 /// Lookup the function to compute the memory size of this parametric derived
297 /// type. The size of the object may depend on the LEN type parameters of the
298 /// derived type.
299 static mlir::LLVM::LLVMFuncOp
300 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
301                           mlir::ConversionPatternRewriter &rewriter) {
302   auto module = op->getParentOfType<mlir::ModuleOp>();
303   std::string name = recTy.getName().str() + "P.mem.size";
304   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
305 }
306 
307 namespace {
308 /// convert to LLVM IR dialect `alloca`
309 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
310   using FIROpConversion::FIROpConversion;
311 
312   mlir::LogicalResult
313   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
314                   mlir::ConversionPatternRewriter &rewriter) const override {
315     mlir::ValueRange operands = adaptor.getOperands();
316     auto loc = alloc.getLoc();
317     mlir::Type ity = lowerTy().indexType();
318     unsigned i = 0;
319     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
320     mlir::Type ty = convertType(alloc.getType());
321     mlir::Type resultTy = ty;
322     if (alloc.hasLenParams()) {
323       unsigned end = alloc.numLenParams();
324       llvm::SmallVector<mlir::Value> lenParams;
325       for (; i < end; ++i)
326         lenParams.push_back(operands[i]);
327       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
328       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
329         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
330             chrTy.getContext(), chrTy.getFKind());
331         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
332         assert(end == 1);
333         size = integerCast(loc, rewriter, ity, lenParams[0]);
334       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
335         mlir::LLVM::LLVMFuncOp memSizeFn =
336             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
337         if (!memSizeFn)
338           emitError(loc, "did not find allocation function");
339         mlir::NamedAttribute attr = rewriter.getNamedAttr(
340             "callee", mlir::SymbolRefAttr::get(memSizeFn));
341         auto call = rewriter.create<mlir::LLVM::CallOp>(
342             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
343         size = call.getResult(0);
344         ty = mlir::LLVM::LLVMPointerType::get(
345             mlir::IntegerType::get(alloc.getContext(), 8));
346       } else {
347         return emitError(loc, "unexpected type ")
348                << scalarType << " with type parameters";
349       }
350     }
351     if (alloc.hasShapeOperands()) {
352       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
353       // Scale the size by constant factors encoded in the array type.
354       // We only do this for arrays that don't have a constant interior, since
355       // those are the only ones that get decayed to a pointer to the element
356       // type.
357       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
358         if (!seqTy.hasConstantInterior()) {
359           fir::SequenceType::Extent constSize = 1;
360           for (auto extent : seqTy.getShape())
361             if (extent != fir::SequenceType::getUnknownExtent())
362               constSize *= extent;
363           mlir::Value constVal{
364               genConstantIndex(loc, ity, rewriter, constSize).getResult()};
365           size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
366         }
367       }
368       unsigned end = operands.size();
369       for (; i < end; ++i)
370         size = rewriter.create<mlir::LLVM::MulOp>(
371             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
372     }
373     if (ty == resultTy) {
374       // Do not emit the bitcast if ty and resultTy are the same.
375       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
376                                                         alloc->getAttrs());
377     } else {
378       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
379                                                       alloc->getAttrs());
380       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
381     }
382     return success();
383   }
384 };
385 } // namespace
386 
387 /// Construct an `llvm.extractvalue` instruction. It will return value at
388 /// element \p x from  \p tuple.
389 static mlir::LLVM::ExtractValueOp
390 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
391                          mlir::ConversionPatternRewriter &rewriter,
392                          mlir::MLIRContext *ctx, int x) {
393   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
394   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
395   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
396 }
397 
398 namespace {
399 /// Lower `fir.box_addr` to the sequence of operations to extract the first
400 /// element of the box.
401 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
402   using FIROpConversion::FIROpConversion;
403 
404   mlir::LogicalResult
405   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
406                   mlir::ConversionPatternRewriter &rewriter) const override {
407     mlir::Value a = adaptor.getOperands()[0];
408     auto loc = boxaddr.getLoc();
409     mlir::Type ty = convertType(boxaddr.getType());
410     if (auto argty = boxaddr.getVal().getType().dyn_cast<fir::BoxType>()) {
411       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
412     } else {
413       auto c0attr = rewriter.getI32IntegerAttr(0);
414       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
415       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
416                                                               c0);
417     }
418     return success();
419   }
420 };
421 
422 /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
423 /// boxchar.
424 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
425   using FIROpConversion::FIROpConversion;
426 
427   mlir::LogicalResult
428   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
429                   mlir::ConversionPatternRewriter &rewriter) const override {
430     mlir::Value boxChar = adaptor.getOperands()[0];
431     mlir::Location loc = boxChar.getLoc();
432     mlir::MLIRContext *ctx = boxChar.getContext();
433     mlir::Type returnValTy = boxCharLen.getResult().getType();
434 
435     constexpr int boxcharLenIdx = 1;
436     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
437         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
438     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
439     rewriter.replaceOp(boxCharLen, lenAfterCast);
440 
441     return success();
442   }
443 };
444 
445 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
446 /// dimension infomartion from the boxed value.
447 /// Result in a triple set of GEPs and loads.
448 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
449   using FIROpConversion::FIROpConversion;
450 
451   mlir::LogicalResult
452   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
453                   mlir::ConversionPatternRewriter &rewriter) const override {
454     SmallVector<mlir::Type, 3> resultTypes = {
455         convertType(boxdims.getResult(0).getType()),
456         convertType(boxdims.getResult(1).getType()),
457         convertType(boxdims.getResult(2).getType()),
458     };
459     auto results =
460         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
461                        adaptor.getOperands()[1], rewriter);
462     rewriter.replaceOp(boxdims, results);
463     return success();
464   }
465 };
466 
467 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
468 /// an element in the boxed value.
469 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
470   using FIROpConversion::FIROpConversion;
471 
472   mlir::LogicalResult
473   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
474                   mlir::ConversionPatternRewriter &rewriter) const override {
475     mlir::Value a = adaptor.getOperands()[0];
476     auto loc = boxelesz.getLoc();
477     auto ty = convertType(boxelesz.getType());
478     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
479     rewriter.replaceOp(boxelesz, elemSize);
480     return success();
481   }
482 };
483 
484 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
485 /// boxed value was from an ALLOCATABLE entity.
486 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
487   using FIROpConversion::FIROpConversion;
488 
489   mlir::LogicalResult
490   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
491                   mlir::ConversionPatternRewriter &rewriter) const override {
492     mlir::Value box = adaptor.getOperands()[0];
493     auto loc = boxisalloc.getLoc();
494     mlir::Value check =
495         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
496     rewriter.replaceOp(boxisalloc, check);
497     return success();
498   }
499 };
500 
501 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
502 /// boxed is an array.
503 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
504   using FIROpConversion::FIROpConversion;
505 
506   mlir::LogicalResult
507   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
508                   mlir::ConversionPatternRewriter &rewriter) const override {
509     mlir::Value a = adaptor.getOperands()[0];
510     auto loc = boxisarray.getLoc();
511     auto rank =
512         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
513     auto c0 = genConstantOffset(loc, rewriter, 0);
514     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
515         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
516     return success();
517   }
518 };
519 
520 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
521 /// boxed value was from a POINTER entity.
522 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
523   using FIROpConversion::FIROpConversion;
524 
525   mlir::LogicalResult
526   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
527                   mlir::ConversionPatternRewriter &rewriter) const override {
528     mlir::Value box = adaptor.getOperands()[0];
529     auto loc = boxisptr.getLoc();
530     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
531     rewriter.replaceOp(boxisptr, check);
532     return success();
533   }
534 };
535 
536 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
537 /// the box.
538 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
539   using FIROpConversion::FIROpConversion;
540 
541   mlir::LogicalResult
542   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
543                   mlir::ConversionPatternRewriter &rewriter) const override {
544     mlir::Value a = adaptor.getOperands()[0];
545     auto loc = boxrank.getLoc();
546     mlir::Type ty = convertType(boxrank.getType());
547     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
548     rewriter.replaceOp(boxrank, result);
549     return success();
550   }
551 };
552 
553 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
554 /// boxproc.
555 /// TODO: Part of supporting Fortran 2003 procedure pointers.
556 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> {
557   using FIROpConversion::FIROpConversion;
558 
559   mlir::LogicalResult
560   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
561                   mlir::ConversionPatternRewriter &rewriter) const override {
562     TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
563     return failure();
564   }
565 };
566 
567 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
568 /// descriptor from the box.
569 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
570   using FIROpConversion::FIROpConversion;
571 
572   mlir::LogicalResult
573   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
574                   mlir::ConversionPatternRewriter &rewriter) const override {
575     mlir::Value box = adaptor.getOperands()[0];
576     auto loc = boxtypedesc.getLoc();
577     mlir::Type typeTy =
578         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
579     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
580     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
581     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
582                                                         result);
583     return success();
584   }
585 };
586 
587 /// Lower `fir.string_lit` to LLVM IR dialect operation.
588 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
589   using FIROpConversion::FIROpConversion;
590 
591   mlir::LogicalResult
592   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
593                   mlir::ConversionPatternRewriter &rewriter) const override {
594     auto ty = convertType(constop.getType());
595     auto attr = constop.getValue();
596     if (attr.isa<mlir::StringAttr>()) {
597       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
598       return success();
599     }
600 
601     auto charTy = constop.getType().cast<fir::CharacterType>();
602     unsigned bits = lowerTy().characterBitsize(charTy);
603     mlir::Type intTy = rewriter.getIntegerType(bits);
604     mlir::Location loc = constop.getLoc();
605     mlir::Value cst = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
606     if (auto arr = attr.dyn_cast<mlir::DenseElementsAttr>()) {
607       cst = rewriter.create<mlir::LLVM::ConstantOp>(loc, ty, arr);
608     } else if (auto arr = attr.dyn_cast<mlir::ArrayAttr>()) {
609       for (auto a : llvm::enumerate(arr.getValue())) {
610         // convert each character to a precise bitsize
611         auto elemAttr = mlir::IntegerAttr::get(
612             intTy,
613             a.value().cast<mlir::IntegerAttr>().getValue().zextOrTrunc(bits));
614         auto elemCst =
615             rewriter.create<mlir::LLVM::ConstantOp>(loc, intTy, elemAttr);
616         auto index = mlir::ArrayAttr::get(
617             constop.getContext(), rewriter.getI32IntegerAttr(a.index()));
618         cst = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, cst, elemCst,
619                                                          index);
620       }
621     } else {
622       return failure();
623     }
624     rewriter.replaceOp(constop, cst);
625     return success();
626   }
627 };
628 
629 // `fir.call` -> `llvm.call`
630 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
631   using FIROpConversion::FIROpConversion;
632 
633   mlir::LogicalResult
634   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
635                   mlir::ConversionPatternRewriter &rewriter) const override {
636     SmallVector<mlir::Type> resultTys;
637     for (auto r : call.getResults())
638       resultTys.push_back(convertType(r.getType()));
639     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
640         call, resultTys, adaptor.getOperands(), call->getAttrs());
641     return success();
642   }
643 };
644 } // namespace
645 
646 static mlir::Type getComplexEleTy(mlir::Type complex) {
647   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
648     return cc.getElementType();
649   return complex.cast<fir::ComplexType>().getElementType();
650 }
651 
652 namespace {
653 /// Compare complex values
654 ///
655 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
656 ///
657 /// For completeness, all other comparison are done on the real component only.
658 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
659   using FIROpConversion::FIROpConversion;
660 
661   mlir::LogicalResult
662   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
663                   mlir::ConversionPatternRewriter &rewriter) const override {
664     mlir::ValueRange operands = adaptor.getOperands();
665     mlir::MLIRContext *ctxt = cmp.getContext();
666     mlir::Type eleTy = convertType(getComplexEleTy(cmp.getLhs().getType()));
667     mlir::Type resTy = convertType(cmp.getType());
668     mlir::Location loc = cmp.getLoc();
669     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
670     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
671                                        loc, eleTy, operands[0], pos0),
672                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
673                                        loc, eleTy, operands[1], pos0)};
674     auto rcp =
675         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
676     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
677     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
678                                        loc, eleTy, operands[0], pos1),
679                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
680                                        loc, eleTy, operands[1], pos1)};
681     auto icp =
682         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
683     SmallVector<mlir::Value, 2> cp{rcp, icp};
684     switch (cmp.getPredicate()) {
685     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
686       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
687       break;
688     case mlir::arith::CmpFPredicate::UNE: // .NE.
689       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
690       break;
691     default:
692       rewriter.replaceOp(cmp, rcp.getResult());
693       break;
694     }
695     return success();
696   }
697 };
698 
699 /// Lower complex constants
700 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
701   using FIROpConversion::FIROpConversion;
702 
703   mlir::LogicalResult
704   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
705                   mlir::ConversionPatternRewriter &rewriter) const override {
706     mlir::Location loc = conc.getLoc();
707     mlir::MLIRContext *ctx = conc.getContext();
708     mlir::Type ty = convertType(conc.getType());
709     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
710     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
711     auto realPart =
712         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
713     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
714     auto imPart =
715         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
716     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
717     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
718     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
719     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
720         loc, ty, undef, realPart, realIndex);
721     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
722                                                            imPart, imIndex);
723     return success();
724   }
725 
726   inline APFloat getValue(mlir::Attribute attr) const {
727     return attr.cast<fir::RealAttr>().getValue();
728   }
729 };
730 
731 /// convert value of from-type to value of to-type
732 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
733   using FIROpConversion::FIROpConversion;
734 
735   static bool isFloatingPointTy(mlir::Type ty) {
736     return ty.isa<mlir::FloatType>();
737   }
738 
739   mlir::LogicalResult
740   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
741                   mlir::ConversionPatternRewriter &rewriter) const override {
742     auto fromFirTy = convert.getValue().getType();
743     auto toFirTy = convert.getRes().getType();
744     auto fromTy = convertType(fromFirTy);
745     auto toTy = convertType(toFirTy);
746     mlir::Value op0 = adaptor.getOperands()[0];
747     if (fromTy == toTy) {
748       rewriter.replaceOp(convert, op0);
749       return success();
750     }
751     auto loc = convert.getLoc();
752     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
753                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
754       if (fromBits == toBits) {
755         // TODO: Converting between two floating-point representations with the
756         // same bitwidth is not allowed for now.
757         mlir::emitError(loc,
758                         "cannot implicitly convert between two floating-point "
759                         "representations of the same bitwidth");
760         return {};
761       }
762       if (fromBits > toBits)
763         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
764       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
765     };
766     // Complex to complex conversion.
767     if (fir::isa_complex(fromFirTy) && fir::isa_complex(toFirTy)) {
768       // Special case: handle the conversion of a complex such that both the
769       // real and imaginary parts are converted together.
770       auto zero = mlir::ArrayAttr::get(convert.getContext(),
771                                        rewriter.getI32IntegerAttr(0));
772       auto one = mlir::ArrayAttr::get(convert.getContext(),
773                                       rewriter.getI32IntegerAttr(1));
774       auto ty = convertType(getComplexEleTy(convert.getValue().getType()));
775       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
776       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
777       auto nt = convertType(getComplexEleTy(convert.getRes().getType()));
778       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
779       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
780       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
781       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
782       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
783       auto i1 =
784           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
785       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
786                                                              ic, one);
787       return mlir::success();
788     }
789 
790     // Follow UNIX F77 convention for logicals:
791     // 1. underlying integer is not zero => logical is .TRUE.
792     // 2. logical is .TRUE. => set underlying integer to 1.
793     auto i1Type = mlir::IntegerType::get(convert.getContext(), 1);
794     if (fromFirTy.isa<fir::LogicalType>() && toFirTy == i1Type) {
795       mlir::Value zero = genConstantIndex(loc, fromTy, rewriter, 0);
796       rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
797           convert, mlir::LLVM::ICmpPredicate::ne, op0, zero);
798       return mlir::success();
799     }
800     if (fromFirTy == i1Type && toFirTy.isa<fir::LogicalType>()) {
801       rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0);
802       return mlir::success();
803     }
804 
805     // Floating point to floating point conversion.
806     if (isFloatingPointTy(fromTy)) {
807       if (isFloatingPointTy(toTy)) {
808         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
809         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
810         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
811         rewriter.replaceOp(convert, v);
812         return mlir::success();
813       }
814       if (toTy.isa<mlir::IntegerType>()) {
815         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
816         return mlir::success();
817       }
818     } else if (fromTy.isa<mlir::IntegerType>()) {
819       // Integer to integer conversion.
820       if (toTy.isa<mlir::IntegerType>()) {
821         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
822         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
823         assert(fromBits != toBits);
824         if (fromBits > toBits) {
825           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
826           return mlir::success();
827         }
828         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
829         return mlir::success();
830       }
831       // Integer to floating point conversion.
832       if (isFloatingPointTy(toTy)) {
833         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
834         return mlir::success();
835       }
836       // Integer to pointer conversion.
837       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
838         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
839         return mlir::success();
840       }
841     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
842       // Pointer to integer conversion.
843       if (toTy.isa<mlir::IntegerType>()) {
844         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
845         return mlir::success();
846       }
847       // Pointer to pointer conversion.
848       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
849         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
850         return mlir::success();
851       }
852     }
853     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
854   }
855 };
856 
857 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
858 /// table.
859 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
860   using FIROpConversion::FIROpConversion;
861 
862   mlir::LogicalResult
863   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
864                   mlir::ConversionPatternRewriter &rewriter) const override {
865     TODO(dispatch.getLoc(), "fir.dispatch codegen");
866     return failure();
867   }
868 };
869 
870 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
871 /// derived type.
872 struct DispatchTableOpConversion
873     : public FIROpConversion<fir::DispatchTableOp> {
874   using FIROpConversion::FIROpConversion;
875 
876   mlir::LogicalResult
877   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
878                   mlir::ConversionPatternRewriter &rewriter) const override {
879     TODO(dispTab.getLoc(), "fir.dispatch_table codegen");
880     return failure();
881   }
882 };
883 
884 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
885 /// method-name to a function.
886 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
887   using FIROpConversion::FIROpConversion;
888 
889   mlir::LogicalResult
890   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
891                   mlir::ConversionPatternRewriter &rewriter) const override {
892     TODO(dtEnt.getLoc(), "fir.dt_entry codegen");
893     return failure();
894   }
895 };
896 
897 /// Lower `fir.global_len` operation.
898 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
899   using FIROpConversion::FIROpConversion;
900 
901   mlir::LogicalResult
902   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
903                   mlir::ConversionPatternRewriter &rewriter) const override {
904     TODO(globalLen.getLoc(), "fir.global_len codegen");
905     return failure();
906   }
907 };
908 
909 /// Lower fir.len_param_index
910 struct LenParamIndexOpConversion
911     : public FIROpConversion<fir::LenParamIndexOp> {
912   using FIROpConversion::FIROpConversion;
913 
914   // FIXME: this should be specialized by the runtime target
915   mlir::LogicalResult
916   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
917                   mlir::ConversionPatternRewriter &rewriter) const override {
918     TODO(lenp.getLoc(), "fir.len_param_index codegen");
919   }
920 };
921 
922 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
923 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
924 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
925 /// element is the length of the character buffer (`#n`).
926 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
927   using FIROpConversion::FIROpConversion;
928 
929   mlir::LogicalResult
930   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
931                   mlir::ConversionPatternRewriter &rewriter) const override {
932     mlir::ValueRange operands = adaptor.getOperands();
933     MLIRContext *ctx = emboxChar.getContext();
934 
935     mlir::Value charBuffer = operands[0];
936     mlir::Value charBufferLen = operands[1];
937 
938     mlir::Location loc = emboxChar.getLoc();
939     mlir::Type llvmStructTy = convertType(emboxChar.getType());
940     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
941 
942     mlir::Type lenTy =
943         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
944     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
945 
946     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
947     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
948     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
949         loc, llvmStructTy, llvmStruct, charBuffer, c0);
950     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
951         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
952 
953     return success();
954   }
955 };
956 } // namespace
957 
958 /// Return the LLVMFuncOp corresponding to the standard malloc call.
959 static mlir::LLVM::LLVMFuncOp
960 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) {
961   auto module = op->getParentOfType<mlir::ModuleOp>();
962   if (mlir::LLVM::LLVMFuncOp mallocFunc =
963           module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("malloc"))
964     return mallocFunc;
965   mlir::OpBuilder moduleBuilder(
966       op->getParentOfType<mlir::ModuleOp>().getBodyRegion());
967   auto indexType = mlir::IntegerType::get(op.getContext(), 64);
968   return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
969       rewriter.getUnknownLoc(), "malloc",
970       mlir::LLVM::LLVMFunctionType::get(getVoidPtrType(op.getContext()),
971                                         indexType,
972                                         /*isVarArg=*/false));
973 }
974 
975 /// Helper function for generating the LLVM IR that computes the size
976 /// in bytes for a derived type.
977 static mlir::Value
978 computeDerivedTypeSize(mlir::Location loc, mlir::Type ptrTy, mlir::Type idxTy,
979                        mlir::ConversionPatternRewriter &rewriter) {
980   auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
981   mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
982   llvm::SmallVector<mlir::Value> args{one};
983   auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr, args);
984   return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
985 }
986 
987 namespace {
988 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc`
989 struct AllocMemOpConversion : public FIROpConversion<fir::AllocMemOp> {
990   using FIROpConversion::FIROpConversion;
991 
992   mlir::LogicalResult
993   matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor,
994                   mlir::ConversionPatternRewriter &rewriter) const override {
995     auto heapTy = heap.getType();
996     auto ty = convertType(heapTy);
997     mlir::LLVM::LLVMFuncOp mallocFunc = getMalloc(heap, rewriter);
998     mlir::Location loc = heap.getLoc();
999     auto ity = lowerTy().indexType();
1000     auto dataTy = fir::unwrapRefType(heapTy);
1001     if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy)))
1002       TODO(loc, "fir.allocmem codegen of derived type with length parameters");
1003     mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, ty);
1004     // !fir.array<NxMx!fir.char<K,?>> sets `size` to the width of !fir.char<K>.
1005     // So multiply the constant dimensions here.
1006     if (fir::hasDynamicSize(dataTy))
1007       if (auto seqTy = dataTy.dyn_cast<fir::SequenceType>())
1008         if (fir::characterWithDynamicLen(seqTy.getEleTy())) {
1009           fir::SequenceType::Extent arrSize = 1;
1010           for (auto d : seqTy.getShape())
1011             if (d != fir::SequenceType::getUnknownExtent())
1012               arrSize *= d;
1013           size = rewriter.create<mlir::LLVM::MulOp>(
1014               loc, ity, size, genConstantIndex(loc, ity, rewriter, arrSize));
1015         }
1016     for (mlir::Value opnd : adaptor.getOperands())
1017       size = rewriter.create<mlir::LLVM::MulOp>(
1018           loc, ity, size, integerCast(loc, rewriter, ity, opnd));
1019     heap->setAttr("callee", mlir::SymbolRefAttr::get(mallocFunc));
1020     auto malloc = rewriter.create<mlir::LLVM::CallOp>(
1021         loc, ::getVoidPtrType(heap.getContext()), size, heap->getAttrs());
1022     rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(heap, ty,
1023                                                        malloc.getResult(0));
1024     return success();
1025   }
1026 
1027   // Compute the (allocation) size of the allocmem type in bytes.
1028   mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy,
1029                                  mlir::ConversionPatternRewriter &rewriter,
1030                                  mlir::Type llTy) const {
1031     // Use the primitive size, if available.
1032     auto ptrTy = llTy.dyn_cast<mlir::LLVM::LLVMPointerType>();
1033     if (auto size =
1034             mlir::LLVM::getPrimitiveTypeSizeInBits(ptrTy.getElementType()))
1035       return genConstantIndex(loc, idxTy, rewriter, size / 8);
1036 
1037     // Otherwise, generate the GEP trick in LLVM IR to compute the size.
1038     return computeDerivedTypeSize(loc, ptrTy, idxTy, rewriter);
1039   }
1040 };
1041 } // namespace
1042 
1043 /// Return the LLVMFuncOp corresponding to the standard free call.
1044 static mlir::LLVM::LLVMFuncOp
1045 getFree(fir::FreeMemOp op, mlir::ConversionPatternRewriter &rewriter) {
1046   auto module = op->getParentOfType<mlir::ModuleOp>();
1047   if (mlir::LLVM::LLVMFuncOp freeFunc =
1048           module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("free"))
1049     return freeFunc;
1050   mlir::OpBuilder moduleBuilder(module.getBodyRegion());
1051   auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext());
1052   return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
1053       rewriter.getUnknownLoc(), "free",
1054       mlir::LLVM::LLVMFunctionType::get(voidType,
1055                                         getVoidPtrType(op.getContext()),
1056                                         /*isVarArg=*/false));
1057 }
1058 
1059 namespace {
1060 /// Lower a `fir.freemem` instruction into `llvm.call @free`
1061 struct FreeMemOpConversion : public FIROpConversion<fir::FreeMemOp> {
1062   using FIROpConversion::FIROpConversion;
1063 
1064   mlir::LogicalResult
1065   matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor,
1066                   mlir::ConversionPatternRewriter &rewriter) const override {
1067     mlir::LLVM::LLVMFuncOp freeFunc = getFree(freemem, rewriter);
1068     mlir::Location loc = freemem.getLoc();
1069     auto bitcast = rewriter.create<mlir::LLVM::BitcastOp>(
1070         freemem.getLoc(), voidPtrTy(), adaptor.getOperands()[0]);
1071     freemem->setAttr("callee", mlir::SymbolRefAttr::get(freeFunc));
1072     rewriter.create<mlir::LLVM::CallOp>(
1073         loc, mlir::TypeRange{}, mlir::ValueRange{bitcast}, freemem->getAttrs());
1074     rewriter.eraseOp(freemem);
1075     return success();
1076   }
1077 };
1078 } // namespace
1079 
1080 namespace {} // namespace
1081 
1082 /// Common base class for embox to descriptor conversion.
1083 template <typename OP>
1084 struct EmboxCommonConversion : public FIROpConversion<OP> {
1085   using FIROpConversion<OP>::FIROpConversion;
1086 
1087   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1088   // The order to find the LLVMFuncOp is as follows:
1089   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1090   // 2. The first ancestor that is a LLVMFuncOp.
1091   mlir::LLVM::LLVMFuncOp
1092   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1093     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1094     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1095                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1096                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1097   }
1098 
1099   // Generate an alloca of size 1 and type \p toTy.
1100   mlir::LLVM::AllocaOp
1101   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1102                     mlir::ConversionPatternRewriter &rewriter) const {
1103     auto thisPt = rewriter.saveInsertionPoint();
1104     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1105     rewriter.setInsertionPointToStart(&func.front());
1106     auto size = this->genI32Constant(loc, rewriter, 1);
1107     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1108     rewriter.restoreInsertionPoint(thisPt);
1109     return al;
1110   }
1111 
1112   static int getCFIAttr(fir::BoxType boxTy) {
1113     auto eleTy = boxTy.getEleTy();
1114     if (eleTy.isa<fir::PointerType>())
1115       return CFI_attribute_pointer;
1116     if (eleTy.isa<fir::HeapType>())
1117       return CFI_attribute_allocatable;
1118     return CFI_attribute_other;
1119   }
1120 
1121   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1122     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1123         .template dyn_cast<fir::RecordType>();
1124   }
1125   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1126     auto recTy = unwrapIfDerived(boxTy);
1127     return recTy && recTy.getNumLenParams() > 0;
1128   }
1129   static bool isDerivedType(fir::BoxType boxTy) {
1130     return unwrapIfDerived(boxTy) != nullptr;
1131   }
1132 
1133   // Get the element size and CFI type code of the boxed value.
1134   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1135       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1136       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1137     auto doInteger =
1138         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1139       int typeCode = fir::integerBitsToTypeCode(width);
1140       return {this->genConstantOffset(loc, rewriter, width / 8),
1141               this->genConstantOffset(loc, rewriter, typeCode)};
1142     };
1143     auto doLogical =
1144         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1145       int typeCode = fir::logicalBitsToTypeCode(width);
1146       return {this->genConstantOffset(loc, rewriter, width / 8),
1147               this->genConstantOffset(loc, rewriter, typeCode)};
1148     };
1149     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1150       int typeCode = fir::realBitsToTypeCode(width);
1151       return {this->genConstantOffset(loc, rewriter, width / 8),
1152               this->genConstantOffset(loc, rewriter, typeCode)};
1153     };
1154     auto doComplex =
1155         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1156       auto typeCode = fir::complexBitsToTypeCode(width);
1157       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1158               this->genConstantOffset(loc, rewriter, typeCode)};
1159     };
1160     auto doCharacter =
1161         [&](unsigned width,
1162             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1163       auto typeCode = fir::characterBitsToTypeCode(width);
1164       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1165       if (width == 8)
1166         return {len, typeCodeVal};
1167       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1168       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1169       auto size =
1170           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1171       return {size, typeCodeVal};
1172     };
1173     auto getKindMap = [&]() -> fir::KindMapping & {
1174       return this->lowerTy().getKindMap();
1175     };
1176     // Pointer-like types.
1177     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1178       boxEleTy = eleTy;
1179     // Integer types.
1180     if (fir::isa_integer(boxEleTy)) {
1181       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1182         return doInteger(ty.getWidth());
1183       auto ty = boxEleTy.cast<fir::IntegerType>();
1184       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1185     }
1186     // Floating point types.
1187     if (fir::isa_real(boxEleTy)) {
1188       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1189         return doFloat(ty.getWidth());
1190       auto ty = boxEleTy.cast<fir::RealType>();
1191       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1192     }
1193     // Complex types.
1194     if (fir::isa_complex(boxEleTy)) {
1195       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1196         return doComplex(
1197             ty.getElementType().cast<mlir::FloatType>().getWidth());
1198       auto ty = boxEleTy.cast<fir::ComplexType>();
1199       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1200     }
1201     // Character types.
1202     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1203       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1204       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1205         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1206         return doCharacter(charWidth, len);
1207       }
1208       assert(!lenParams.empty());
1209       return doCharacter(charWidth, lenParams.back());
1210     }
1211     // Logical type.
1212     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1213       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1214     // Array types.
1215     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1216       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1217     // Derived-type types.
1218     if (boxEleTy.isa<fir::RecordType>()) {
1219       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1220           this->lowerTy().convertType(boxEleTy));
1221       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1222       auto one =
1223           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1224       auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr,
1225                                                     mlir::ValueRange{one});
1226       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1227           loc, this->lowerTy().indexType(), gep);
1228       return {eleSize,
1229               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1230     }
1231     // Reference type.
1232     if (fir::isa_ref_type(boxEleTy)) {
1233       // FIXME: use the target pointer size rather than sizeof(void*)
1234       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1235               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1236     }
1237     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1238   }
1239 
1240   /// Basic pattern to write a field in the descriptor
1241   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1242                           mlir::Location loc, mlir::Value dest,
1243                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1244                           bool bitcast = false) const {
1245     auto boxTy = dest.getType();
1246     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1247     if (bitcast)
1248       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1249     else
1250       value = this->integerCast(loc, rewriter, fldTy, value);
1251     SmallVector<mlir::Attribute, 2> attrs;
1252     for (auto i : fldIndexes)
1253       attrs.push_back(rewriter.getI32IntegerAttr(i));
1254     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1255     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1256                                                       indexesAttr);
1257   }
1258 
1259   inline mlir::Value
1260   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1261                     mlir::Location loc, mlir::Value dest,
1262                     mlir::Value base) const {
1263     return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
1264                        /*bitCast=*/true);
1265   }
1266 
1267   inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
1268                                       mlir::Location loc, mlir::Value dest,
1269                                       unsigned dim, mlir::Value lb) const {
1270     return insertField(rewriter, loc, dest,
1271                        {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
1272   }
1273 
1274   inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
1275                                   mlir::Location loc, mlir::Value dest,
1276                                   unsigned dim, mlir::Value extent) const {
1277     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
1278                        extent);
1279   }
1280 
1281   inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
1282                                   mlir::Location loc, mlir::Value dest,
1283                                   unsigned dim, mlir::Value stride) const {
1284     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
1285                        stride);
1286   }
1287 
1288   /// Get the address of the type descriptor global variable that was created by
1289   /// lowering for derived type \p recType.
1290   template <typename BOX>
1291   mlir::Value
1292   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1293                     mlir::Location loc, fir::RecordType recType) const {
1294     std::string name =
1295         fir::NameUniquer::getTypeDescriptorName(recType.getName());
1296     auto module = box->template getParentOfType<mlir::ModuleOp>();
1297     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1298       auto ty = mlir::LLVM::LLVMPointerType::get(
1299           this->lowerTy().convertType(global.getType()));
1300       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1301                                                       global.getSymName());
1302     }
1303     if (auto global =
1304             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1305       // The global may have already been translated to LLVM.
1306       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1307       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1308                                                       global.getSymName());
1309     }
1310     // Type info derived types do not have type descriptors since they are the
1311     // types defining type descriptors.
1312     if (!this->options.ignoreMissingTypeDescriptors &&
1313         !fir::NameUniquer::belongsToModule(
1314             name, Fortran::semantics::typeInfoBuiltinModule))
1315       fir::emitFatalError(
1316           loc, "runtime derived type info descriptor was not generated");
1317     return rewriter.create<mlir::LLVM::NullOp>(
1318         loc, ::getVoidPtrType(box.getContext()));
1319   }
1320 
1321   template <typename BOX>
1322   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1323   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1324                        unsigned rank, mlir::ValueRange lenParams) const {
1325     auto loc = box.getLoc();
1326     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1327     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1328     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1329     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1330     mlir::Value descriptor =
1331         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1332 
1333     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1334     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1335       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1336         typeparams.push_back(box.substr()[1]);
1337     }
1338 
1339     // Write each of the fields with the appropriate values
1340     auto [eleSize, cfiTy] =
1341         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1342     descriptor =
1343         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1344     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1345                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1346     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1347                              this->genI32Constant(loc, rewriter, rank));
1348     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1349     descriptor =
1350         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1351                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1352     const bool hasAddendum = isDerivedType(boxTy);
1353     descriptor =
1354         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1355                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1356 
1357     if (hasAddendum) {
1358       auto isArray =
1359           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1360       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1361       auto typeDesc =
1362           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1363       descriptor =
1364           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1365                       /*bitCast=*/true);
1366     }
1367 
1368     return {boxTy, descriptor, eleSize};
1369   }
1370 
1371   /// Compute the base address of a substring given the base address of a scalar
1372   /// string and the zero based string lower bound.
1373   mlir::Value shiftSubstringBase(mlir::ConversionPatternRewriter &rewriter,
1374                                  mlir::Location loc, mlir::Value base,
1375                                  mlir::Value lowerBound) const {
1376     llvm::SmallVector<mlir::Value> gepOperands;
1377     auto baseType =
1378         base.getType().cast<mlir::LLVM::LLVMPointerType>().getElementType();
1379     if (baseType.isa<mlir::LLVM::LLVMArrayType>()) {
1380       auto idxTy = this->lowerTy().indexType();
1381       mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1382       gepOperands.push_back(zero);
1383     }
1384     gepOperands.push_back(lowerBound);
1385     return this->genGEP(loc, base.getType(), rewriter, base, gepOperands);
1386   }
1387 
1388   /// If the embox is not in a globalOp body, allocate storage for the box;
1389   /// store the value inside and return the generated alloca. Return the input
1390   /// value otherwise.
1391   mlir::Value
1392   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1393                                mlir::Location loc, mlir::Value boxValue) const {
1394     auto *thisBlock = rewriter.getInsertionBlock();
1395     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1396       return boxValue;
1397     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1398     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1399     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1400     return alloca;
1401   }
1402 };
1403 
1404 /// Compute the extent of a triplet slice (lb:ub:step).
1405 static mlir::Value
1406 computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
1407                      mlir::Location loc, mlir::Value lb, mlir::Value ub,
1408                      mlir::Value step, mlir::Value zero, mlir::Type type) {
1409   mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
1410   extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
1411   extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
1412   // If the resulting extent is negative (`ub-lb` and `step` have different
1413   // signs), zero must be returned instead.
1414   auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1415       loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
1416   return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
1417 }
1418 
1419 /// Create a generic box on a memory reference. This conversions lowers the
1420 /// abstract box to the appropriate, initialized descriptor.
1421 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1422   using EmboxCommonConversion::EmboxCommonConversion;
1423 
1424   mlir::LogicalResult
1425   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1426                   mlir::ConversionPatternRewriter &rewriter) const override {
1427     assert(!embox.getShape() && "There should be no dims on this embox op");
1428     auto [boxTy, dest, eleSize] =
1429         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1430                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1431     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1432                              adaptor.getOperands()[0]);
1433     if (isDerivedTypeWithLenParams(boxTy)) {
1434       TODO(embox.getLoc(),
1435            "fir.embox codegen of derived with length parameters");
1436       return failure();
1437     }
1438     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1439     rewriter.replaceOp(embox, result);
1440     return success();
1441   }
1442 };
1443 
1444 /// Create a generic box on a memory reference.
1445 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
1446   using EmboxCommonConversion::EmboxCommonConversion;
1447 
1448   mlir::LogicalResult
1449   matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
1450                   mlir::ConversionPatternRewriter &rewriter) const override {
1451     auto [boxTy, dest, eleSize] = consDescriptorPrefix(
1452         xbox, rewriter, xbox.getOutRank(),
1453         adaptor.getOperands().drop_front(xbox.lenParamOffset()));
1454     // Generate the triples in the dims field of the descriptor
1455     mlir::ValueRange operands = adaptor.getOperands();
1456     auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
1457     mlir::Value base = operands[0];
1458     assert(!xbox.shape().empty() && "must have a shape");
1459     unsigned shapeOffset = xbox.shapeOffset();
1460     bool hasShift = !xbox.shift().empty();
1461     unsigned shiftOffset = xbox.shiftOffset();
1462     bool hasSlice = !xbox.slice().empty();
1463     unsigned sliceOffset = xbox.sliceOffset();
1464     mlir::Location loc = xbox.getLoc();
1465     mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
1466     mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
1467     mlir::Value prevDim = integerCast(loc, rewriter, i64Ty, eleSize);
1468     mlir::Value prevPtrOff = one;
1469     mlir::Type eleTy = boxTy.getEleTy();
1470     const unsigned rank = xbox.getRank();
1471     llvm::SmallVector<mlir::Value> gepArgs;
1472     unsigned constRows = 0;
1473     mlir::Value ptrOffset = zero;
1474     if (auto memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType()))
1475       if (auto seqTy = memEleTy.dyn_cast<fir::SequenceType>()) {
1476         mlir::Type seqEleTy = seqTy.getEleTy();
1477         // Adjust the element scaling factor if the element is a dependent type.
1478         if (fir::hasDynamicSize(seqEleTy)) {
1479           if (fir::isa_char(seqEleTy)) {
1480             assert(xbox.lenParams().size() == 1);
1481             prevPtrOff = integerCast(loc, rewriter, i64Ty,
1482                                      operands[xbox.lenParamOffset()]);
1483           } else if (seqEleTy.isa<fir::RecordType>()) {
1484             TODO(loc, "generate call to calculate size of PDT");
1485           } else {
1486             return rewriter.notifyMatchFailure(xbox, "unexpected dynamic type");
1487           }
1488         } else {
1489           constRows = seqTy.getConstantRows();
1490         }
1491       }
1492 
1493     bool hasSubcomp = !xbox.subcomponent().empty();
1494     mlir::Value stepExpr;
1495     if (hasSubcomp) {
1496       // We have a subcomponent. The step value needs to be the number of
1497       // bytes per element (which is a derived type).
1498       mlir::Type ty0 = base.getType();
1499       [[maybe_unused]] auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
1500       assert(ptrTy && "expected pointer type");
1501       mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType());
1502       assert(memEleTy && "expected fir pointer type");
1503       auto seqTy = memEleTy.dyn_cast<fir::SequenceType>();
1504       assert(seqTy && "expected sequence type");
1505       mlir::Type seqEleTy = seqTy.getEleTy();
1506       auto eleTy = mlir::LLVM::LLVMPointerType::get(convertType(seqEleTy));
1507       stepExpr = computeDerivedTypeSize(loc, eleTy, i64Ty, rewriter);
1508     }
1509 
1510     // Process the array subspace arguments (shape, shift, etc.), if any,
1511     // translating everything to values in the descriptor wherever the entity
1512     // has a dynamic array dimension.
1513     for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
1514       mlir::Value extent = operands[shapeOffset];
1515       mlir::Value outerExtent = extent;
1516       bool skipNext = false;
1517       if (hasSlice) {
1518         mlir::Value off = operands[sliceOffset];
1519         mlir::Value adj = one;
1520         if (hasShift)
1521           adj = operands[shiftOffset];
1522         auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
1523         if (constRows > 0) {
1524           gepArgs.push_back(ao);
1525           --constRows;
1526         } else {
1527           auto dimOff =
1528               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
1529           ptrOffset =
1530               rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
1531         }
1532         if (mlir::isa_and_nonnull<fir::UndefOp>(
1533                 xbox.slice()[3 * di + 1].getDefiningOp())) {
1534           // This dimension contains a scalar expression in the array slice op.
1535           // The dimension is loop invariant, will be dropped, and will not
1536           // appear in the descriptor.
1537           skipNext = true;
1538         }
1539       }
1540       if (!skipNext) {
1541         if (hasSlice)
1542           extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
1543                                         operands[sliceOffset + 1],
1544                                         operands[sliceOffset + 2], zero, i64Ty);
1545         // store lower bound (normally 0) for BIND(C) interoperability.
1546         mlir::Value lb = zero;
1547         const bool isaPointerOrAllocatable =
1548             eleTy.isa<fir::PointerType>() || eleTy.isa<fir::HeapType>();
1549         // Lower bound is defaults to 1 for POINTER, ALLOCATABLE, and
1550         // denormalized descriptors.
1551         if (isaPointerOrAllocatable || !normalizedLowerBound(xbox)) {
1552           lb = one;
1553           // If there is a shifted origin and this is not a normalized
1554           // descriptor then use the value from the shift op as the lower bound.
1555           if (hasShift) {
1556             lb = operands[shiftOffset];
1557             auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
1558                 loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
1559             lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one,
1560                                                        lb);
1561           }
1562         }
1563         dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
1564 
1565         dest = insertExtent(rewriter, loc, dest, descIdx, extent);
1566 
1567         // store step (scaled by shaped extent)
1568 
1569         mlir::Value step = hasSubcomp ? stepExpr : prevDim;
1570         if (hasSlice)
1571           step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step,
1572                                                     operands[sliceOffset + 2]);
1573         dest = insertStride(rewriter, loc, dest, descIdx, step);
1574         ++descIdx;
1575       }
1576 
1577       // compute the stride and offset for the next natural dimension
1578       prevDim =
1579           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevDim, outerExtent);
1580       if (constRows == 0)
1581         prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
1582                                                         outerExtent);
1583 
1584       // increment iterators
1585       ++shapeOffset;
1586       if (hasShift)
1587         ++shiftOffset;
1588       if (hasSlice)
1589         sliceOffset += 3;
1590     }
1591     if (hasSlice || hasSubcomp || !xbox.substr().empty()) {
1592       llvm::SmallVector<mlir::Value> args = {ptrOffset};
1593       args.append(gepArgs.rbegin(), gepArgs.rend());
1594       if (hasSubcomp) {
1595         // For each field in the path add the offset to base via the args list.
1596         // In the most general case, some offsets must be computed since
1597         // they are not be known until runtime.
1598         if (fir::hasDynamicSize(fir::unwrapSequenceType(
1599                 fir::unwrapPassByRefType(xbox.memref().getType()))))
1600           TODO(loc, "fir.embox codegen dynamic size component in derived type");
1601         args.append(operands.begin() + xbox.subcomponentOffset(),
1602                     operands.begin() + xbox.subcomponentOffset() +
1603                         xbox.subcomponent().size());
1604       }
1605       base =
1606           rewriter.create<mlir::LLVM::GEPOp>(loc, base.getType(), base, args);
1607       if (!xbox.substr().empty())
1608         base = shiftSubstringBase(rewriter, loc, base,
1609                                   operands[xbox.substrOffset()]);
1610     }
1611     dest = insertBaseAddress(rewriter, loc, dest, base);
1612     if (isDerivedTypeWithLenParams(boxTy))
1613       TODO(loc, "fir.embox codegen of derived with length parameters");
1614 
1615     mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest);
1616     rewriter.replaceOp(xbox, result);
1617     return success();
1618   }
1619 
1620   /// Return true if `xbox` has a normalized lower bounds attribute. A box value
1621   /// that is neither a POINTER nor an ALLOCATABLE should be normalized to a
1622   /// zero origin lower bound for interoperability with BIND(C).
1623   inline static bool normalizedLowerBound(fir::cg::XEmboxOp xbox) {
1624     return xbox->hasAttr(fir::getNormalizedLowerBoundAttrName());
1625   }
1626 };
1627 
1628 /// Create a new box given a box reference.
1629 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
1630   using EmboxCommonConversion::EmboxCommonConversion;
1631 
1632   mlir::LogicalResult
1633   matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
1634                   mlir::ConversionPatternRewriter &rewriter) const override {
1635     mlir::Location loc = rebox.getLoc();
1636     mlir::Type idxTy = lowerTy().indexType();
1637     mlir::Value loweredBox = adaptor.getOperands()[0];
1638     mlir::ValueRange operands = adaptor.getOperands();
1639 
1640     // Create new descriptor and fill its non-shape related data.
1641     llvm::SmallVector<mlir::Value, 2> lenParams;
1642     mlir::Type inputEleTy = getInputEleTy(rebox);
1643     if (auto charTy = inputEleTy.dyn_cast<fir::CharacterType>()) {
1644       mlir::Value len =
1645           loadElementSizeFromBox(loc, idxTy, loweredBox, rewriter);
1646       if (charTy.getFKind() != 1) {
1647         mlir::Value width =
1648             genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
1649         len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width);
1650       }
1651       lenParams.emplace_back(len);
1652     } else if (auto recTy = inputEleTy.dyn_cast<fir::RecordType>()) {
1653       if (recTy.getNumLenParams() != 0)
1654         TODO(loc, "reboxing descriptor of derived type with length parameters");
1655     }
1656     auto [boxTy, dest, eleSize] =
1657         consDescriptorPrefix(rebox, rewriter, rebox.getOutRank(), lenParams);
1658 
1659     // Read input extents, strides, and base address
1660     llvm::SmallVector<mlir::Value> inputExtents;
1661     llvm::SmallVector<mlir::Value> inputStrides;
1662     const unsigned inputRank = rebox.getRank();
1663     for (unsigned i = 0; i < inputRank; ++i) {
1664       mlir::Value dim = genConstantIndex(loc, idxTy, rewriter, i);
1665       SmallVector<mlir::Value, 3> dimInfo =
1666           getDimsFromBox(loc, {idxTy, idxTy, idxTy}, loweredBox, dim, rewriter);
1667       inputExtents.emplace_back(dimInfo[1]);
1668       inputStrides.emplace_back(dimInfo[2]);
1669     }
1670 
1671     mlir::Type baseTy = getBaseAddrTypeFromBox(loweredBox.getType());
1672     mlir::Value baseAddr =
1673         loadBaseAddrFromBox(loc, baseTy, loweredBox, rewriter);
1674 
1675     if (!rebox.slice().empty() || !rebox.subcomponent().empty())
1676       return sliceBox(rebox, dest, baseAddr, inputExtents, inputStrides,
1677                       operands, rewriter);
1678     return reshapeBox(rebox, dest, baseAddr, inputExtents, inputStrides,
1679                       operands, rewriter);
1680   }
1681 
1682 private:
1683   /// Write resulting shape and base address in descriptor, and replace rebox
1684   /// op.
1685   mlir::LogicalResult
1686   finalizeRebox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
1687                 mlir::ValueRange lbounds, mlir::ValueRange extents,
1688                 mlir::ValueRange strides,
1689                 mlir::ConversionPatternRewriter &rewriter) const {
1690     mlir::Location loc = rebox.getLoc();
1691     mlir::Value zero =
1692         genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
1693     mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1);
1694     for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) {
1695       mlir::Value extent = std::get<0>(iter.value());
1696       unsigned dim = iter.index();
1697       mlir::Value lb = one;
1698       if (!lbounds.empty()) {
1699         lb = lbounds[dim];
1700         auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
1701             loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
1702         lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one, lb);
1703       };
1704       dest = insertLowerBound(rewriter, loc, dest, dim, lb);
1705       dest = insertExtent(rewriter, loc, dest, dim, extent);
1706       dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value()));
1707     }
1708     dest = insertBaseAddress(rewriter, loc, dest, base);
1709     mlir::Value result =
1710         placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), dest);
1711     rewriter.replaceOp(rebox, result);
1712     return success();
1713   }
1714 
1715   // Apply slice given the base address, extents and strides of the input box.
1716   mlir::LogicalResult
1717   sliceBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
1718            mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
1719            mlir::ValueRange operands,
1720            mlir::ConversionPatternRewriter &rewriter) const {
1721     mlir::Location loc = rebox.getLoc();
1722     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
1723     mlir::Type idxTy = lowerTy().indexType();
1724     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1725     // Apply subcomponent and substring shift on base address.
1726     if (!rebox.subcomponent().empty() || !rebox.substr().empty()) {
1727       // Cast to inputEleTy* so that a GEP can be used.
1728       mlir::Type inputEleTy = getInputEleTy(rebox);
1729       auto llvmElePtrTy =
1730           mlir::LLVM::LLVMPointerType::get(convertType(inputEleTy));
1731       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, llvmElePtrTy, base);
1732 
1733       if (!rebox.subcomponent().empty()) {
1734         llvm::SmallVector<mlir::Value> gepOperands = {zero};
1735         for (unsigned i = 0; i < rebox.subcomponent().size(); ++i)
1736           gepOperands.push_back(operands[rebox.subcomponentOffset() + i]);
1737         base = genGEP(loc, llvmElePtrTy, rewriter, base, gepOperands);
1738       }
1739       if (!rebox.substr().empty())
1740         base = shiftSubstringBase(rewriter, loc, base,
1741                                   operands[rebox.substrOffset()]);
1742     }
1743 
1744     if (rebox.slice().empty())
1745       // The array section is of the form array[%component][substring], keep
1746       // the input array extents and strides.
1747       return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
1748                            inputExtents, inputStrides, rewriter);
1749 
1750     // Strides from the fir.box are in bytes.
1751     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
1752 
1753     // The slice is of the form array(i:j:k)[%component]. Compute new extents
1754     // and strides.
1755     llvm::SmallVector<mlir::Value> slicedExtents;
1756     llvm::SmallVector<mlir::Value> slicedStrides;
1757     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
1758     const bool sliceHasOrigins = !rebox.shift().empty();
1759     unsigned sliceOps = rebox.sliceOffset();
1760     unsigned shiftOps = rebox.shiftOffset();
1761     auto strideOps = inputStrides.begin();
1762     const unsigned inputRank = inputStrides.size();
1763     for (unsigned i = 0; i < inputRank;
1764          ++i, ++strideOps, ++shiftOps, sliceOps += 3) {
1765       mlir::Value sliceLb =
1766           integerCast(loc, rewriter, idxTy, operands[sliceOps]);
1767       mlir::Value inputStride = *strideOps; // already idxTy
1768       // Apply origin shift: base += (lb-shift)*input_stride
1769       mlir::Value sliceOrigin =
1770           sliceHasOrigins
1771               ? integerCast(loc, rewriter, idxTy, operands[shiftOps])
1772               : one;
1773       mlir::Value diff =
1774           rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin);
1775       mlir::Value offset =
1776           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride);
1777       base = genGEP(loc, voidPtrTy, rewriter, base, offset);
1778       // Apply upper bound and step if this is a triplet. Otherwise, the
1779       // dimension is dropped and no extents/strides are computed.
1780       mlir::Value upper = operands[sliceOps + 1];
1781       const bool isTripletSlice =
1782           !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp());
1783       if (isTripletSlice) {
1784         mlir::Value step =
1785             integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]);
1786         // extent = ub-lb+step/step
1787         mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper);
1788         mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb,
1789                                                   sliceUb, step, zero, idxTy);
1790         slicedExtents.emplace_back(extent);
1791         // stride = step*input_stride
1792         mlir::Value stride =
1793             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride);
1794         slicedStrides.emplace_back(stride);
1795       }
1796     }
1797     return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
1798                          slicedExtents, slicedStrides, rewriter);
1799   }
1800 
1801   /// Apply a new shape to the data described by a box given the base address,
1802   /// extents and strides of the box.
1803   mlir::LogicalResult
1804   reshapeBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
1805              mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
1806              mlir::ValueRange operands,
1807              mlir::ConversionPatternRewriter &rewriter) const {
1808     mlir::ValueRange reboxShifts{operands.begin() + rebox.shiftOffset(),
1809                                  operands.begin() + rebox.shiftOffset() +
1810                                      rebox.shift().size()};
1811     if (rebox.shape().empty()) {
1812       // Only setting new lower bounds.
1813       return finalizeRebox(rebox, dest, base, reboxShifts, inputExtents,
1814                            inputStrides, rewriter);
1815     }
1816 
1817     mlir::Location loc = rebox.getLoc();
1818     // Strides from the fir.box are in bytes.
1819     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
1820     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
1821 
1822     llvm::SmallVector<mlir::Value> newStrides;
1823     llvm::SmallVector<mlir::Value> newExtents;
1824     mlir::Type idxTy = lowerTy().indexType();
1825     // First stride from input box is kept. The rest is assumed contiguous
1826     // (it is not possible to reshape otherwise). If the input is scalar,
1827     // which may be OK if all new extents are ones, the stride does not
1828     // matter, use one.
1829     mlir::Value stride = inputStrides.empty()
1830                              ? genConstantIndex(loc, idxTy, rewriter, 1)
1831                              : inputStrides[0];
1832     for (unsigned i = 0; i < rebox.shape().size(); ++i) {
1833       mlir::Value rawExtent = operands[rebox.shapeOffset() + i];
1834       mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent);
1835       newExtents.emplace_back(extent);
1836       newStrides.emplace_back(stride);
1837       // nextStride = extent * stride;
1838       stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
1839     }
1840     return finalizeRebox(rebox, dest, base, reboxShifts, newExtents, newStrides,
1841                          rewriter);
1842   }
1843 
1844   /// Return scalar element type of the input box.
1845   static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) {
1846     auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.box().getType());
1847     if (auto seqTy = ty.dyn_cast<fir::SequenceType>())
1848       return seqTy.getEleTy();
1849     return ty;
1850   }
1851 };
1852 
1853 /// Lower `fir.emboxproc` operation. Creates a procedure box.
1854 /// TODO: Part of supporting Fortran 2003 procedure pointers.
1855 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1856   using FIROpConversion::FIROpConversion;
1857 
1858   mlir::LogicalResult
1859   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1860                   mlir::ConversionPatternRewriter &rewriter) const override {
1861     TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
1862     return failure();
1863   }
1864 };
1865 
1866 // Code shared between insert_value and extract_value Ops.
1867 struct ValueOpCommon {
1868   // Translate the arguments pertaining to any multidimensional array to
1869   // row-major order for LLVM-IR.
1870   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
1871                          mlir::Type ty) {
1872     assert(ty && "type is null");
1873     const auto end = attrs.size();
1874     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
1875       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1876         const auto dim = getDimension(seq);
1877         if (dim > 1) {
1878           auto ub = std::min(i + dim, end);
1879           std::reverse(attrs.begin() + i, attrs.begin() + ub);
1880           i += dim - 1;
1881         }
1882         ty = getArrayElementType(seq);
1883       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
1884         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
1885       } else {
1886         llvm_unreachable("index into invalid type");
1887       }
1888     }
1889   }
1890 
1891   static llvm::SmallVector<mlir::Attribute>
1892   collectIndices(mlir::ConversionPatternRewriter &rewriter,
1893                  mlir::ArrayAttr arrAttr) {
1894     llvm::SmallVector<mlir::Attribute> attrs;
1895     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
1896       if (i->isa<mlir::IntegerAttr>()) {
1897         attrs.push_back(*i);
1898       } else {
1899         auto fieldName = i->cast<mlir::StringAttr>().getValue();
1900         ++i;
1901         auto ty = i->cast<mlir::TypeAttr>().getValue();
1902         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
1903         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
1904       }
1905     }
1906     return attrs;
1907   }
1908 
1909 private:
1910   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1911     unsigned result = 1;
1912     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
1913          eleTy;
1914          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
1915       ++result;
1916     return result;
1917   }
1918 
1919   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
1920     auto eleTy = ty.getElementType();
1921     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
1922       eleTy = arrTy.getElementType();
1923     return eleTy;
1924   }
1925 };
1926 
1927 namespace {
1928 /// Extract a subobject value from an ssa-value of aggregate type
1929 struct ExtractValueOpConversion
1930     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
1931       public ValueOpCommon {
1932   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1933 
1934   mlir::LogicalResult
1935   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
1936             mlir::ConversionPatternRewriter &rewriter) const override {
1937     auto attrs = collectIndices(rewriter, extractVal.getCoor());
1938     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1939     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
1940     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
1941         extractVal, ty, adaptor.getOperands()[0], position);
1942     return success();
1943   }
1944 };
1945 
1946 /// InsertValue is the generalized instruction for the composition of new
1947 /// aggregate type values.
1948 struct InsertValueOpConversion
1949     : public FIROpAndTypeConversion<fir::InsertValueOp>,
1950       public ValueOpCommon {
1951   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1952 
1953   mlir::LogicalResult
1954   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
1955             mlir::ConversionPatternRewriter &rewriter) const override {
1956     auto attrs = collectIndices(rewriter, insertVal.getCoor());
1957     toRowMajor(attrs, adaptor.getOperands()[0].getType());
1958     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
1959     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
1960         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
1961         position);
1962     return success();
1963   }
1964 };
1965 
1966 /// InsertOnRange inserts a value into a sequence over a range of offsets.
1967 struct InsertOnRangeOpConversion
1968     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
1969   using FIROpAndTypeConversion::FIROpAndTypeConversion;
1970 
1971   // Increments an array of subscripts in a row major fasion.
1972   void incrementSubscripts(const SmallVector<uint64_t> &dims,
1973                            SmallVector<uint64_t> &subscripts) const {
1974     for (size_t i = dims.size(); i > 0; --i) {
1975       if (++subscripts[i - 1] < dims[i - 1]) {
1976         return;
1977       }
1978       subscripts[i - 1] = 0;
1979     }
1980   }
1981 
1982   mlir::LogicalResult
1983   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
1984             mlir::ConversionPatternRewriter &rewriter) const override {
1985 
1986     llvm::SmallVector<uint64_t> dims;
1987     auto type = adaptor.getOperands()[0].getType();
1988 
1989     // Iteratively extract the array dimensions from the type.
1990     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
1991       dims.push_back(t.getNumElements());
1992       type = t.getElementType();
1993     }
1994 
1995     SmallVector<uint64_t> lBounds;
1996     SmallVector<uint64_t> uBounds;
1997 
1998     // Unzip the upper and lower bound and convert to a row major format.
1999     mlir::DenseIntElementsAttr coor = range.getCoor();
2000     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
2001     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
2002       uBounds.push_back(*i++);
2003       lBounds.push_back(*i);
2004     }
2005 
2006     auto &subscripts = lBounds;
2007     auto loc = range.getLoc();
2008     mlir::Value lastOp = adaptor.getOperands()[0];
2009     mlir::Value insertVal = adaptor.getOperands()[1];
2010 
2011     auto i64Ty = rewriter.getI64Type();
2012     while (subscripts != uBounds) {
2013       // Convert uint64_t's to Attribute's.
2014       SmallVector<mlir::Attribute> subscriptAttrs;
2015       for (const auto &subscript : subscripts)
2016         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
2017       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2018           loc, ty, lastOp, insertVal,
2019           ArrayAttr::get(range.getContext(), subscriptAttrs));
2020 
2021       incrementSubscripts(dims, subscripts);
2022     }
2023 
2024     // Convert uint64_t's to Attribute's.
2025     SmallVector<mlir::Attribute> subscriptAttrs;
2026     for (const auto &subscript : subscripts)
2027       subscriptAttrs.push_back(
2028           IntegerAttr::get(rewriter.getI64Type(), subscript));
2029     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
2030 
2031     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2032         range, ty, lastOp, insertVal,
2033         ArrayAttr::get(range.getContext(), arrayRef));
2034 
2035     return success();
2036   }
2037 };
2038 } // namespace
2039 
2040 namespace {
2041 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced,
2042 /// shifted etc. array.
2043 /// (See the static restriction on coordinate_of.) array_coor determines the
2044 /// coordinate (location) of a specific element.
2045 struct XArrayCoorOpConversion
2046     : public FIROpAndTypeConversion<fir::cg::XArrayCoorOp> {
2047   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2048 
2049   mlir::LogicalResult
2050   doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type ty, OpAdaptor adaptor,
2051             mlir::ConversionPatternRewriter &rewriter) const override {
2052     auto loc = coor.getLoc();
2053     mlir::ValueRange operands = adaptor.getOperands();
2054     unsigned rank = coor.getRank();
2055     assert(coor.indices().size() == rank);
2056     assert(coor.shape().empty() || coor.shape().size() == rank);
2057     assert(coor.shift().empty() || coor.shift().size() == rank);
2058     assert(coor.slice().empty() || coor.slice().size() == 3 * rank);
2059     mlir::Type idxTy = lowerTy().indexType();
2060     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2061     mlir::Value prevExt = one;
2062     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
2063     mlir::Value offset = zero;
2064     const bool isShifted = !coor.shift().empty();
2065     const bool isSliced = !coor.slice().empty();
2066     const bool baseIsBoxed = coor.memref().getType().isa<fir::BoxType>();
2067 
2068     auto indexOps = coor.indices().begin();
2069     auto shapeOps = coor.shape().begin();
2070     auto shiftOps = coor.shift().begin();
2071     auto sliceOps = coor.slice().begin();
2072     // For each dimension of the array, generate the offset calculation.
2073     for (unsigned i = 0; i < rank;
2074          ++i, ++indexOps, ++shapeOps, ++shiftOps, sliceOps += 3) {
2075       mlir::Value index =
2076           integerCast(loc, rewriter, idxTy, operands[coor.indicesOffset() + i]);
2077       mlir::Value lb = isShifted ? integerCast(loc, rewriter, idxTy,
2078                                                operands[coor.shiftOffset() + i])
2079                                  : one;
2080       mlir::Value step = one;
2081       bool normalSlice = isSliced;
2082       // Compute zero based index in dimension i of the element, applying
2083       // potential triplets and lower bounds.
2084       if (isSliced) {
2085         mlir::Value ub = *(sliceOps + 1);
2086         normalSlice = !mlir::isa_and_nonnull<fir::UndefOp>(ub.getDefiningOp());
2087         if (normalSlice)
2088           step = integerCast(loc, rewriter, idxTy, *(sliceOps + 2));
2089       }
2090       auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb);
2091       mlir::Value diff =
2092           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step);
2093       if (normalSlice) {
2094         mlir::Value sliceLb =
2095             integerCast(loc, rewriter, idxTy, operands[coor.sliceOffset() + i]);
2096         auto adj = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb);
2097         diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj);
2098       }
2099       // Update the offset given the stride and the zero based index `diff`
2100       // that was just computed.
2101       if (baseIsBoxed) {
2102         // Use stride in bytes from the descriptor.
2103         mlir::Value stride =
2104             loadStrideFromBox(loc, adaptor.getOperands()[0], i, rewriter);
2105         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride);
2106         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
2107       } else {
2108         // Use stride computed at last iteration.
2109         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt);
2110         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
2111         // Compute next stride assuming contiguity of the base array
2112         // (in element number).
2113         auto nextExt =
2114             integerCast(loc, rewriter, idxTy, operands[coor.shapeOffset() + i]);
2115         prevExt =
2116             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt, nextExt);
2117       }
2118     }
2119 
2120     // Add computed offset to the base address.
2121     if (baseIsBoxed) {
2122       // Working with byte offsets. The base address is read from the fir.box.
2123       // and need to be casted to i8* to do the pointer arithmetic.
2124       mlir::Type baseTy =
2125           getBaseAddrTypeFromBox(adaptor.getOperands()[0].getType());
2126       mlir::Value base =
2127           loadBaseAddrFromBox(loc, baseTy, adaptor.getOperands()[0], rewriter);
2128       mlir::Type voidPtrTy = getVoidPtrType();
2129       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
2130       llvm::SmallVector<mlir::Value> args{offset};
2131       auto addr =
2132           rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy, base, args);
2133       if (coor.subcomponent().empty()) {
2134         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, baseTy, addr);
2135         return success();
2136       }
2137       auto casted = rewriter.create<mlir::LLVM::BitcastOp>(loc, baseTy, addr);
2138       args.clear();
2139       args.push_back(zero);
2140       if (!coor.lenParams().empty()) {
2141         // If type parameters are present, then we don't want to use a GEPOp
2142         // as below, as the LLVM struct type cannot be statically defined.
2143         TODO(loc, "derived type with type parameters");
2144       }
2145       // TODO: array offset subcomponents must be converted to LLVM's
2146       // row-major layout here.
2147       for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
2148         args.push_back(operands[i]);
2149       rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, baseTy, casted,
2150                                                      args);
2151       return success();
2152     }
2153 
2154     // The array was not boxed, so it must be contiguous. offset is therefore an
2155     // element offset and the base type is kept in the GEP unless the element
2156     // type size is itself dynamic.
2157     mlir::Value base;
2158     if (coor.subcomponent().empty()) {
2159       // No subcomponent.
2160       if (!coor.lenParams().empty()) {
2161         // Type parameters. Adjust element size explicitly.
2162         auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType());
2163         assert(eleTy && "result must be a reference-like type");
2164         if (fir::characterWithDynamicLen(eleTy)) {
2165           assert(coor.lenParams().size() == 1);
2166           auto bitsInChar = lowerTy().getKindMap().getCharacterBitsize(
2167               eleTy.cast<fir::CharacterType>().getFKind());
2168           auto scaling = genConstantIndex(loc, idxTy, rewriter, bitsInChar / 8);
2169           auto scaledBySize =
2170               rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset, scaling);
2171           auto length =
2172               integerCast(loc, rewriter, idxTy,
2173                           adaptor.getOperands()[coor.lenParamsOffset()]);
2174           offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, scaledBySize,
2175                                                       length);
2176         } else {
2177           TODO(loc, "compute size of derived type with type parameters");
2178         }
2179       }
2180       // Cast the base address to a pointer to T.
2181       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, ty,
2182                                                     adaptor.getOperands()[0]);
2183     } else {
2184       // Operand #0 must have a pointer type. For subcomponent slicing, we
2185       // want to cast away the array type and have a plain struct type.
2186       mlir::Type ty0 = adaptor.getOperands()[0].getType();
2187       auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
2188       assert(ptrTy && "expected pointer type");
2189       mlir::Type eleTy = ptrTy.getElementType();
2190       while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
2191         eleTy = arrTy.getElementType();
2192       auto newTy = mlir::LLVM::LLVMPointerType::get(eleTy);
2193       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, newTy,
2194                                                     adaptor.getOperands()[0]);
2195     }
2196     SmallVector<mlir::Value> args = {offset};
2197     for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
2198       args.push_back(operands[i]);
2199     rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, ty, base, args);
2200     return success();
2201   }
2202 };
2203 } // namespace
2204 
2205 /// Convert to (memory) reference to a reference to a subobject.
2206 /// The coordinate_of op is a Swiss army knife operation that can be used on
2207 /// (memory) references to records, arrays, complex, etc. as well as boxes.
2208 /// With unboxed arrays, there is the restriction that the array have a static
2209 /// shape in all but the last column.
2210 struct CoordinateOpConversion
2211     : public FIROpAndTypeConversion<fir::CoordinateOp> {
2212   using FIROpAndTypeConversion::FIROpAndTypeConversion;
2213 
2214   mlir::LogicalResult
2215   doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor,
2216             mlir::ConversionPatternRewriter &rewriter) const override {
2217     mlir::ValueRange operands = adaptor.getOperands();
2218 
2219     mlir::Location loc = coor.getLoc();
2220     mlir::Value base = operands[0];
2221     mlir::Type baseObjectTy = coor.getBaseType();
2222     mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2223     assert(objectTy && "fir.coordinate_of expects a reference type");
2224 
2225     // Complex type - basically, extract the real or imaginary part
2226     if (fir::isa_complex(objectTy)) {
2227       mlir::LLVM::ConstantOp c0 =
2228           genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
2229       llvm::SmallVector<mlir::Value> offs = {c0, operands[1]};
2230       mlir::Value gep = genGEP(loc, ty, rewriter, base, offs);
2231       rewriter.replaceOp(coor, gep);
2232       return success();
2233     }
2234 
2235     // Boxed type - get the base pointer from the box
2236     if (baseObjectTy.dyn_cast<fir::BoxType>())
2237       return doRewriteBox(coor, ty, operands, loc, rewriter);
2238 
2239     // Reference, pointer or a heap type
2240     if (baseObjectTy.isa<fir::ReferenceType, fir::PointerType, fir::HeapType>())
2241       return doRewriteRefOrPtr(coor, ty, operands, loc, rewriter);
2242 
2243     return rewriter.notifyMatchFailure(
2244         coor, "fir.coordinate_of base operand has unsupported type");
2245   }
2246 
2247   static unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) {
2248     return fir::hasDynamicSize(ty)
2249                ? op.getDefiningOp()
2250                      ->getAttrOfType<mlir::IntegerAttr>("field")
2251                      .getInt()
2252                : getIntValue(op);
2253   }
2254 
2255   static int64_t getIntValue(mlir::Value val) {
2256     assert(val && val.dyn_cast<mlir::OpResult>() && "must not be null value");
2257     mlir::Operation *defop = val.getDefiningOp();
2258 
2259     if (auto constOp = dyn_cast<mlir::arith::ConstantIntOp>(defop))
2260       return constOp.value();
2261     if (auto llConstOp = dyn_cast<mlir::LLVM::ConstantOp>(defop))
2262       if (auto attr = llConstOp.getValue().dyn_cast<mlir::IntegerAttr>())
2263         return attr.getValue().getSExtValue();
2264     fir::emitFatalError(val.getLoc(), "must be a constant");
2265   }
2266 
2267   static bool hasSubDimensions(mlir::Type type) {
2268     return type.isa<fir::SequenceType, fir::RecordType, mlir::TupleType>();
2269   }
2270 
2271   /// Check whether this form of `!fir.coordinate_of` is supported. These
2272   /// additional checks are required, because we are not yet able to convert
2273   /// all valid forms of `!fir.coordinate_of`.
2274   /// TODO: Either implement the unsupported cases or extend the verifier
2275   /// in FIROps.cpp instead.
2276   static bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) {
2277     const std::size_t numOfCoors = coors.size();
2278     std::size_t i = 0;
2279     bool subEle = false;
2280     bool ptrEle = false;
2281     for (; i < numOfCoors; ++i) {
2282       mlir::Value nxtOpnd = coors[i];
2283       if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
2284         subEle = true;
2285         i += arrTy.getDimension() - 1;
2286         type = arrTy.getEleTy();
2287       } else if (auto recTy = type.dyn_cast<fir::RecordType>()) {
2288         subEle = true;
2289         type = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2290       } else if (auto tupTy = type.dyn_cast<mlir::TupleType>()) {
2291         subEle = true;
2292         type = tupTy.getType(getIntValue(nxtOpnd));
2293       } else {
2294         ptrEle = true;
2295       }
2296     }
2297     if (ptrEle)
2298       return (!subEle) && (numOfCoors == 1);
2299     return subEle && (i >= numOfCoors);
2300   }
2301 
2302   /// Walk the abstract memory layout and determine if the path traverses any
2303   /// array types with unknown shape. Return true iff all the array types have a
2304   /// constant shape along the path.
2305   static bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) {
2306     for (std::size_t i = 0, sz = coors.size(); i < sz; ++i) {
2307       mlir::Value nxtOpnd = coors[i];
2308       if (auto arrTy = type.dyn_cast<fir::SequenceType>()) {
2309         if (fir::sequenceWithNonConstantShape(arrTy))
2310           return false;
2311         i += arrTy.getDimension() - 1;
2312         type = arrTy.getEleTy();
2313       } else if (auto strTy = type.dyn_cast<fir::RecordType>()) {
2314         type = strTy.getType(getFieldNumber(strTy, nxtOpnd));
2315       } else if (auto strTy = type.dyn_cast<mlir::TupleType>()) {
2316         type = strTy.getType(getIntValue(nxtOpnd));
2317       } else {
2318         return true;
2319       }
2320     }
2321     return true;
2322   }
2323 
2324 private:
2325   mlir::LogicalResult
2326   doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, mlir::ValueRange operands,
2327                mlir::Location loc,
2328                mlir::ConversionPatternRewriter &rewriter) const {
2329     mlir::Type boxObjTy = coor.getBaseType();
2330     assert(boxObjTy.dyn_cast<fir::BoxType>() && "This is not a `fir.box`");
2331 
2332     mlir::Value boxBaseAddr = operands[0];
2333 
2334     // 1. SPECIAL CASE (uses `fir.len_param_index`):
2335     //   %box = ... : !fir.box<!fir.type<derived{len1:i32}>>
2336     //   %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}>
2337     //   %addr = coordinate_of %box, %lenp
2338     if (coor.getNumOperands() == 2) {
2339       mlir::Operation *coordinateDef =
2340           (*coor.getCoor().begin()).getDefiningOp();
2341       if (isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef))
2342         TODO(loc,
2343              "fir.coordinate_of - fir.len_param_index is not supported yet");
2344     }
2345 
2346     // 2. GENERAL CASE:
2347     // 2.1. (`fir.array`)
2348     //   %box = ... : !fix.box<!fir.array<?xU>>
2349     //   %idx = ... : index
2350     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<U>
2351     // 2.2 (`fir.derived`)
2352     //   %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>>
2353     //   %idx = ... : i32
2354     //   %resultAddr = coordinate_of %box, %idx : !fir.ref<i32>
2355     // 2.3 (`fir.derived` inside `fir.array`)
2356     //   %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32,
2357     //   field_2:f32}>>> %idx1 = ... : index %idx2 = ... : i32 %resultAddr =
2358     //   coordinate_of %box, %idx1, %idx2 : !fir.ref<f32>
2359     // 2.4. TODO: Either document or disable any other case that the following
2360     //  implementation might convert.
2361     mlir::LLVM::ConstantOp c0 =
2362         genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
2363     mlir::Value resultAddr =
2364         loadBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()),
2365                             boxBaseAddr, rewriter);
2366     // Component Type
2367     auto cpnTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy);
2368     mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext());
2369 
2370     for (unsigned i = 1, last = operands.size(); i < last; ++i) {
2371       if (auto arrTy = cpnTy.dyn_cast<fir::SequenceType>()) {
2372         if (i != 1)
2373           TODO(loc, "fir.array nested inside other array and/or derived type");
2374         // Applies byte strides from the box. Ignore lower bound from box
2375         // since fir.coordinate_of indexes are zero based. Lowering takes care
2376         // of lower bound aspects. This both accounts for dynamically sized
2377         // types and non contiguous arrays.
2378         auto idxTy = lowerTy().indexType();
2379         mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0);
2380         for (unsigned index = i, lastIndex = i + arrTy.getDimension();
2381              index < lastIndex; ++index) {
2382           mlir::Value stride =
2383               loadStrideFromBox(loc, operands[0], index - i, rewriter);
2384           auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy,
2385                                                        operands[index], stride);
2386           off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off);
2387         }
2388         auto voidPtrBase =
2389             rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, resultAddr);
2390         SmallVector<mlir::Value> args{off};
2391         resultAddr = rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy,
2392                                                         voidPtrBase, args);
2393         i += arrTy.getDimension() - 1;
2394         cpnTy = arrTy.getEleTy();
2395       } else if (auto recTy = cpnTy.dyn_cast<fir::RecordType>()) {
2396         auto recRefTy =
2397             mlir::LLVM::LLVMPointerType::get(lowerTy().convertType(recTy));
2398         mlir::Value nxtOpnd = operands[i];
2399         auto memObj =
2400             rewriter.create<mlir::LLVM::BitcastOp>(loc, recRefTy, resultAddr);
2401         llvm::SmallVector<mlir::Value> args = {c0, nxtOpnd};
2402         cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2403         auto llvmCurrentObjTy = lowerTy().convertType(cpnTy);
2404         auto gep = rewriter.create<mlir::LLVM::GEPOp>(
2405             loc, mlir::LLVM::LLVMPointerType::get(llvmCurrentObjTy), memObj,
2406             args);
2407         resultAddr =
2408             rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, gep);
2409       } else {
2410         fir::emitFatalError(loc, "unexpected type in coordinate_of");
2411       }
2412     }
2413 
2414     rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, ty, resultAddr);
2415     return success();
2416   }
2417 
2418   mlir::LogicalResult
2419   doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type ty,
2420                     mlir::ValueRange operands, mlir::Location loc,
2421                     mlir::ConversionPatternRewriter &rewriter) const {
2422     mlir::Type baseObjectTy = coor.getBaseType();
2423 
2424     // Component Type
2425     mlir::Type cpnTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2426     bool hasSubdimension = hasSubDimensions(cpnTy);
2427     bool columnIsDeferred = !hasSubdimension;
2428 
2429     if (!supportedCoordinate(cpnTy, operands.drop_front(1)))
2430       TODO(loc, "unsupported combination of coordinate operands");
2431 
2432     const bool hasKnownShape =
2433         arraysHaveKnownShape(cpnTy, operands.drop_front(1));
2434 
2435     // If only the column is `?`, then we can simply place the column value in
2436     // the 0-th GEP position.
2437     if (auto arrTy = cpnTy.dyn_cast<fir::SequenceType>()) {
2438       if (!hasKnownShape) {
2439         const unsigned sz = arrTy.getDimension();
2440         if (arraysHaveKnownShape(arrTy.getEleTy(),
2441                                  operands.drop_front(1 + sz))) {
2442           fir::SequenceType::ShapeRef shape = arrTy.getShape();
2443           bool allConst = true;
2444           for (unsigned i = 0; i < sz - 1; ++i) {
2445             if (shape[i] < 0) {
2446               allConst = false;
2447               break;
2448             }
2449           }
2450           if (allConst)
2451             columnIsDeferred = true;
2452         }
2453       }
2454     }
2455 
2456     if (fir::hasDynamicSize(fir::unwrapSequenceType(cpnTy)))
2457       return mlir::emitError(
2458           loc, "fir.coordinate_of with a dynamic element size is unsupported");
2459 
2460     if (hasKnownShape || columnIsDeferred) {
2461       SmallVector<mlir::Value> offs;
2462       if (hasKnownShape && hasSubdimension) {
2463         mlir::LLVM::ConstantOp c0 =
2464             genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
2465         offs.push_back(c0);
2466       }
2467       Optional<int> dims;
2468       SmallVector<mlir::Value> arrIdx;
2469       for (std::size_t i = 1, sz = operands.size(); i < sz; ++i) {
2470         mlir::Value nxtOpnd = operands[i];
2471 
2472         if (!cpnTy)
2473           return mlir::emitError(loc, "invalid coordinate/check failed");
2474 
2475         // check if the i-th coordinate relates to an array
2476         if (dims.hasValue()) {
2477           arrIdx.push_back(nxtOpnd);
2478           int dimsLeft = *dims;
2479           if (dimsLeft > 1) {
2480             dims = dimsLeft - 1;
2481             continue;
2482           }
2483           cpnTy = cpnTy.cast<fir::SequenceType>().getEleTy();
2484           // append array range in reverse (FIR arrays are column-major)
2485           offs.append(arrIdx.rbegin(), arrIdx.rend());
2486           arrIdx.clear();
2487           dims.reset();
2488           continue;
2489         }
2490         if (auto arrTy = cpnTy.dyn_cast<fir::SequenceType>()) {
2491           int d = arrTy.getDimension() - 1;
2492           if (d > 0) {
2493             dims = d;
2494             arrIdx.push_back(nxtOpnd);
2495             continue;
2496           }
2497           cpnTy = cpnTy.cast<fir::SequenceType>().getEleTy();
2498           offs.push_back(nxtOpnd);
2499           continue;
2500         }
2501 
2502         // check if the i-th coordinate relates to a field
2503         if (auto recTy = cpnTy.dyn_cast<fir::RecordType>())
2504           cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2505         else if (auto tupTy = cpnTy.dyn_cast<mlir::TupleType>())
2506           cpnTy = tupTy.getType(getIntValue(nxtOpnd));
2507         else
2508           cpnTy = nullptr;
2509 
2510         offs.push_back(nxtOpnd);
2511       }
2512       if (dims.hasValue())
2513         offs.append(arrIdx.rbegin(), arrIdx.rend());
2514       mlir::Value base = operands[0];
2515       mlir::Value retval = genGEP(loc, ty, rewriter, base, offs);
2516       rewriter.replaceOp(coor, retval);
2517       return success();
2518     }
2519 
2520     return mlir::emitError(
2521         loc, "fir.coordinate_of base operand has unsupported type");
2522   }
2523 };
2524 
2525 /// Convert `fir.field_index`. The conversion depends on whether the size of
2526 /// the record is static or dynamic.
2527 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2528   using FIROpConversion::FIROpConversion;
2529 
2530   // NB: most field references should be resolved by this point
2531   mlir::LogicalResult
2532   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2533                   mlir::ConversionPatternRewriter &rewriter) const override {
2534     auto recTy = field.getOnType().cast<fir::RecordType>();
2535     unsigned index = recTy.getFieldIndex(field.getFieldId());
2536 
2537     if (!fir::hasDynamicSize(recTy)) {
2538       // Derived type has compile-time constant layout. Return index of the
2539       // component type in the parent type (to be used in GEP).
2540       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2541                                     field.getLoc(), rewriter, index)});
2542       return success();
2543     }
2544 
2545     // Derived type has compile-time constant layout. Call the compiler
2546     // generated function to determine the byte offset of the field at runtime.
2547     // This returns a non-constant.
2548     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2549         field.getContext(), getOffsetMethodName(recTy, field.getFieldId()));
2550     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2551     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2552         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2553     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2554         field, lowerTy().offsetType(), adaptor.getOperands(),
2555         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2556     return success();
2557   }
2558 
2559   // Re-Construct the name of the compiler generated method that calculates the
2560   // offset
2561   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2562                                                 llvm::StringRef field) {
2563     return recTy.getName().str() + "P." + field.str() + ".offset";
2564   }
2565 };
2566 
2567 /// Convert `fir.end`
2568 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
2569   using FIROpConversion::FIROpConversion;
2570 
2571   mlir::LogicalResult
2572   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
2573                   mlir::ConversionPatternRewriter &rewriter) const override {
2574     TODO(firEnd.getLoc(), "fir.end codegen");
2575     return failure();
2576   }
2577 };
2578 
2579 /// Lower `fir.gentypedesc` to a global constant.
2580 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
2581   using FIROpConversion::FIROpConversion;
2582 
2583   mlir::LogicalResult
2584   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
2585                   mlir::ConversionPatternRewriter &rewriter) const override {
2586     TODO(gentypedesc.getLoc(), "fir.gentypedesc codegen");
2587     return failure();
2588   }
2589 };
2590 
2591 /// Lower `fir.has_value` operation to `llvm.return` operation.
2592 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
2593   using FIROpConversion::FIROpConversion;
2594 
2595   mlir::LogicalResult
2596   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
2597                   mlir::ConversionPatternRewriter &rewriter) const override {
2598     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
2599     return success();
2600   }
2601 };
2602 
2603 /// Lower `fir.global` operation to `llvm.global` operation.
2604 /// `fir.insert_on_range` operations are replaced with constant dense attribute
2605 /// if they are applied on the full range.
2606 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
2607   using FIROpConversion::FIROpConversion;
2608 
2609   mlir::LogicalResult
2610   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
2611                   mlir::ConversionPatternRewriter &rewriter) const override {
2612     auto tyAttr = convertType(global.getType());
2613     if (global.getType().isa<fir::BoxType>())
2614       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
2615     auto loc = global.getLoc();
2616     mlir::Attribute initAttr{};
2617     if (global.getInitVal())
2618       initAttr = global.getInitVal().getValue();
2619     auto linkage = convertLinkage(global.getLinkName());
2620     auto isConst = global.getConstant().hasValue();
2621     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
2622         loc, tyAttr, isConst, linkage, global.getSymName(), initAttr);
2623     auto &gr = g.getInitializerRegion();
2624     rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end());
2625     if (!gr.empty()) {
2626       // Replace insert_on_range with a constant dense attribute if the
2627       // initialization is on the full range.
2628       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
2629       for (auto insertOp : insertOnRangeOps) {
2630         if (isFullRange(insertOp.getCoor(), insertOp.getType())) {
2631           auto seqTyAttr = convertType(insertOp.getType());
2632           auto *op = insertOp.getVal().getDefiningOp();
2633           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
2634           if (!constant) {
2635             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
2636             if (!convertOp)
2637               continue;
2638             constant = cast<mlir::arith::ConstantOp>(
2639                 convertOp.getValue().getDefiningOp());
2640           }
2641           mlir::Type vecType = mlir::VectorType::get(
2642               insertOp.getType().getShape(), constant.getType());
2643           auto denseAttr = mlir::DenseElementsAttr::get(
2644               vecType.cast<ShapedType>(), constant.getValue());
2645           rewriter.setInsertionPointAfter(insertOp);
2646           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
2647               insertOp, seqTyAttr, denseAttr);
2648         }
2649       }
2650     }
2651     rewriter.eraseOp(global);
2652     return success();
2653   }
2654 
2655   bool isFullRange(mlir::DenseIntElementsAttr indexes,
2656                    fir::SequenceType seqTy) const {
2657     auto extents = seqTy.getShape();
2658     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
2659       return false;
2660     auto cur_index = indexes.value_begin<int64_t>();
2661     for (unsigned i = 0; i < indexes.size(); i += 2) {
2662       if (*(cur_index++) != 0)
2663         return false;
2664       if (*(cur_index++) != extents[i / 2] - 1)
2665         return false;
2666     }
2667     return true;
2668   }
2669 
2670   // TODO: String comparaison should be avoided. Replace linkName with an
2671   // enumeration.
2672   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
2673     if (optLinkage.hasValue()) {
2674       auto name = optLinkage.getValue();
2675       if (name == "internal")
2676         return mlir::LLVM::Linkage::Internal;
2677       if (name == "linkonce")
2678         return mlir::LLVM::Linkage::Linkonce;
2679       if (name == "linkonce_odr")
2680         return mlir::LLVM::Linkage::LinkonceODR;
2681       if (name == "common")
2682         return mlir::LLVM::Linkage::Common;
2683       if (name == "weak")
2684         return mlir::LLVM::Linkage::Weak;
2685     }
2686     return mlir::LLVM::Linkage::External;
2687   }
2688 };
2689 
2690 /// `fir.load` --> `llvm.load`
2691 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
2692   using FIROpConversion::FIROpConversion;
2693 
2694   mlir::LogicalResult
2695   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
2696                   mlir::ConversionPatternRewriter &rewriter) const override {
2697     // fir.box is a special case because it is considered as an ssa values in
2698     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
2699     // and fir.box end up being the same llvm types and loading a
2700     // fir.ref<fir.box> is actually a no op in LLVM.
2701     if (load.getType().isa<fir::BoxType>()) {
2702       rewriter.replaceOp(load, adaptor.getOperands()[0]);
2703     } else {
2704       mlir::Type ty = convertType(load.getType());
2705       ArrayRef<NamedAttribute> at = load->getAttrs();
2706       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
2707           load, ty, adaptor.getOperands(), at);
2708     }
2709     return success();
2710   }
2711 };
2712 
2713 /// Lower `fir.no_reassoc` to LLVM IR dialect.
2714 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
2715 /// math flags?
2716 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
2717   using FIROpConversion::FIROpConversion;
2718 
2719   mlir::LogicalResult
2720   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
2721                   mlir::ConversionPatternRewriter &rewriter) const override {
2722     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
2723     return success();
2724   }
2725 };
2726 
2727 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
2728                         Optional<mlir::ValueRange> destOps,
2729                         mlir::ConversionPatternRewriter &rewriter,
2730                         mlir::Block *newBlock) {
2731   if (destOps.hasValue())
2732     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
2733                                           newBlock, mlir::ValueRange());
2734   else
2735     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
2736 }
2737 
2738 template <typename A, typename B>
2739 static void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
2740                     mlir::ConversionPatternRewriter &rewriter) {
2741   if (destOps.hasValue())
2742     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
2743                                                   dest);
2744   else
2745     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
2746 }
2747 
2748 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
2749                               mlir::Block *dest,
2750                               Optional<mlir::ValueRange> destOps,
2751                               mlir::ConversionPatternRewriter &rewriter) {
2752   auto *thisBlock = rewriter.getInsertionBlock();
2753   auto *newBlock = createBlock(rewriter, dest);
2754   rewriter.setInsertionPointToEnd(thisBlock);
2755   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
2756   rewriter.setInsertionPointToEnd(newBlock);
2757 }
2758 
2759 /// Conversion of `fir.select_case`
2760 ///
2761 /// The `fir.select_case` operation is converted to a if-then-else ladder.
2762 /// Depending on the case condition type, one or several comparison and
2763 /// conditional branching can be generated.
2764 ///
2765 /// A a point value case such as `case(4)`, a lower bound case such as
2766 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
2767 /// simple comparison between the selector value and the constant value in the
2768 /// case. The block associated with the case condition is then executed if
2769 /// the comparison succeed otherwise it branch to the next block with the
2770 /// comparison for the the next case conditon.
2771 ///
2772 /// A closed interval case condition such as `case(7:10)` is converted with a
2773 /// first comparison and conditional branching for the lower bound. If
2774 /// successful, it branch to a second block with the comparison for the
2775 /// upper bound in the same case condition.
2776 ///
2777 /// TODO: lowering of CHARACTER type cases is not handled yet.
2778 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
2779   using FIROpConversion::FIROpConversion;
2780 
2781   mlir::LogicalResult
2782   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
2783                   mlir::ConversionPatternRewriter &rewriter) const override {
2784     unsigned conds = caseOp.getNumConditions();
2785     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
2786     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
2787     auto ty = caseOp.getSelector().getType();
2788     if (ty.isa<fir::CharacterType>()) {
2789       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
2790       return failure();
2791     }
2792     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
2793     auto loc = caseOp.getLoc();
2794     for (unsigned t = 0; t != conds; ++t) {
2795       mlir::Block *dest = caseOp.getSuccessor(t);
2796       llvm::Optional<mlir::ValueRange> destOps =
2797           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
2798       llvm::Optional<mlir::ValueRange> cmpOps =
2799           *caseOp.getCompareOperands(adaptor.getOperands(), t);
2800       mlir::Value caseArg = *(cmpOps.getValue().begin());
2801       mlir::Attribute attr = cases[t];
2802       if (attr.isa<fir::PointIntervalAttr>()) {
2803         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2804             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
2805         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2806         continue;
2807       }
2808       if (attr.isa<fir::LowerBoundAttr>()) {
2809         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2810             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
2811         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2812         continue;
2813       }
2814       if (attr.isa<fir::UpperBoundAttr>()) {
2815         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2816             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
2817         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
2818         continue;
2819       }
2820       if (attr.isa<fir::ClosedIntervalAttr>()) {
2821         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
2822             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
2823         auto *thisBlock = rewriter.getInsertionBlock();
2824         auto *newBlock1 = createBlock(rewriter, dest);
2825         auto *newBlock2 = createBlock(rewriter, dest);
2826         rewriter.setInsertionPointToEnd(thisBlock);
2827         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
2828         rewriter.setInsertionPointToEnd(newBlock1);
2829         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
2830         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
2831             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
2832         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
2833         rewriter.setInsertionPointToEnd(newBlock2);
2834         continue;
2835       }
2836       assert(attr.isa<mlir::UnitAttr>());
2837       assert((t + 1 == conds) && "unit must be last");
2838       genBrOp(caseOp, dest, destOps, rewriter);
2839     }
2840     return success();
2841   }
2842 };
2843 
2844 template <typename OP>
2845 static void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
2846                                   typename OP::Adaptor adaptor,
2847                                   mlir::ConversionPatternRewriter &rewriter) {
2848   unsigned conds = select.getNumConditions();
2849   auto cases = select.getCases().getValue();
2850   mlir::Value selector = adaptor.getSelector();
2851   auto loc = select.getLoc();
2852   assert(conds > 0 && "select must have cases");
2853 
2854   llvm::SmallVector<mlir::Block *> destinations;
2855   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
2856   mlir::Block *defaultDestination;
2857   mlir::ValueRange defaultOperands;
2858   llvm::SmallVector<int32_t> caseValues;
2859 
2860   for (unsigned t = 0; t != conds; ++t) {
2861     mlir::Block *dest = select.getSuccessor(t);
2862     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
2863     const mlir::Attribute &attr = cases[t];
2864     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
2865       destinations.push_back(dest);
2866       destinationsOperands.push_back(destOps.hasValue() ? *destOps
2867                                                         : ValueRange());
2868       caseValues.push_back(intAttr.getInt());
2869       continue;
2870     }
2871     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
2872     assert((t + 1 == conds) && "unit must be last");
2873     defaultDestination = dest;
2874     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
2875   }
2876 
2877   // LLVM::SwitchOp takes a i32 type for the selector.
2878   if (select.getSelector().getType() != rewriter.getI32Type())
2879     selector =
2880         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
2881 
2882   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
2883       select, selector,
2884       /*defaultDestination=*/defaultDestination,
2885       /*defaultOperands=*/defaultOperands,
2886       /*caseValues=*/caseValues,
2887       /*caseDestinations=*/destinations,
2888       /*caseOperands=*/destinationsOperands,
2889       /*branchWeights=*/ArrayRef<int32_t>());
2890 }
2891 
2892 /// conversion of fir::SelectOp to an if-then-else ladder
2893 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
2894   using FIROpConversion::FIROpConversion;
2895 
2896   mlir::LogicalResult
2897   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
2898                   mlir::ConversionPatternRewriter &rewriter) const override {
2899     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
2900     return success();
2901   }
2902 };
2903 
2904 /// conversion of fir::SelectRankOp to an if-then-else ladder
2905 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
2906   using FIROpConversion::FIROpConversion;
2907 
2908   mlir::LogicalResult
2909   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
2910                   mlir::ConversionPatternRewriter &rewriter) const override {
2911     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
2912     return success();
2913   }
2914 };
2915 
2916 /// Lower `fir.select_type` to LLVM IR dialect.
2917 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
2918   using FIROpConversion::FIROpConversion;
2919 
2920   mlir::LogicalResult
2921   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
2922                   mlir::ConversionPatternRewriter &rewriter) const override {
2923     mlir::emitError(select.getLoc(),
2924                     "fir.select_type should have already been converted");
2925     return failure();
2926   }
2927 };
2928 
2929 /// `fir.store` --> `llvm.store`
2930 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
2931   using FIROpConversion::FIROpConversion;
2932 
2933   mlir::LogicalResult
2934   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
2935                   mlir::ConversionPatternRewriter &rewriter) const override {
2936     if (store.getValue().getType().isa<fir::BoxType>()) {
2937       // fir.box value is actually in memory, load it first before storing it.
2938       mlir::Location loc = store.getLoc();
2939       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
2940       auto val = rewriter.create<mlir::LLVM::LoadOp>(
2941           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
2942           adaptor.getOperands()[0]);
2943       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
2944           store, val, adaptor.getOperands()[1]);
2945     } else {
2946       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
2947           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
2948     }
2949     return success();
2950   }
2951 };
2952 
2953 namespace {
2954 
2955 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
2956 /// the character buffer and one for the buffer length.
2957 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
2958   using FIROpConversion::FIROpConversion;
2959 
2960   mlir::LogicalResult
2961   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
2962                   mlir::ConversionPatternRewriter &rewriter) const override {
2963     MLIRContext *ctx = unboxchar.getContext();
2964 
2965     mlir::Type lenTy = convertType(unboxchar.getType(1));
2966     mlir::Value tuple = adaptor.getOperands()[0];
2967     mlir::Type tupleTy = tuple.getType();
2968 
2969     mlir::Location loc = unboxchar.getLoc();
2970     mlir::Value ptrToBuffer =
2971         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
2972 
2973     mlir::LLVM::ExtractValueOp len =
2974         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
2975     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
2976 
2977     rewriter.replaceOp(unboxchar,
2978                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
2979     return success();
2980   }
2981 };
2982 
2983 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2984 /// components.
2985 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2986 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2987   using FIROpConversion::FIROpConversion;
2988 
2989   mlir::LogicalResult
2990   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2991                   mlir::ConversionPatternRewriter &rewriter) const override {
2992     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
2993     return failure();
2994   }
2995 };
2996 
2997 /// convert to LLVM IR dialect `undef`
2998 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
2999   using FIROpConversion::FIROpConversion;
3000 
3001   mlir::LogicalResult
3002   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
3003                   mlir::ConversionPatternRewriter &rewriter) const override {
3004     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
3005         undef, convertType(undef.getType()));
3006     return success();
3007   }
3008 };
3009 
3010 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
3011   using FIROpConversion::FIROpConversion;
3012 
3013   mlir::LogicalResult
3014   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
3015                   mlir::ConversionPatternRewriter &rewriter) const override {
3016     mlir::Type ty = convertType(zero.getType());
3017     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
3018       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
3019     } else if (ty.isa<mlir::IntegerType>()) {
3020       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
3021           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
3022     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
3023       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
3024           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
3025     } else {
3026       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
3027       return rewriter.notifyMatchFailure(
3028           zero,
3029           "conversion of fir.zero with aggregate type not implemented yet");
3030     }
3031     return success();
3032   }
3033 };
3034 
3035 /// `fir.unreachable` --> `llvm.unreachable`
3036 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
3037   using FIROpConversion::FIROpConversion;
3038 
3039   mlir::LogicalResult
3040   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
3041                   mlir::ConversionPatternRewriter &rewriter) const override {
3042     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
3043     return success();
3044   }
3045 };
3046 
3047 /// `fir.is_present` -->
3048 /// ```
3049 ///  %0 = llvm.mlir.constant(0 : i64)
3050 ///  %1 = llvm.ptrtoint %0
3051 ///  %2 = llvm.icmp "ne" %1, %0 : i64
3052 /// ```
3053 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
3054   using FIROpConversion::FIROpConversion;
3055 
3056   mlir::LogicalResult
3057   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
3058                   mlir::ConversionPatternRewriter &rewriter) const override {
3059     mlir::Type idxTy = lowerTy().indexType();
3060     mlir::Location loc = isPresent.getLoc();
3061     auto ptr = adaptor.getOperands()[0];
3062 
3063     if (isPresent.getVal().getType().isa<fir::BoxCharType>()) {
3064       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
3065       assert(!structTy.isOpaque() && !structTy.getBody().empty());
3066 
3067       mlir::Type ty = structTy.getBody()[0];
3068       mlir::MLIRContext *ctx = isPresent.getContext();
3069       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3070       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
3071     }
3072     mlir::LLVM::ConstantOp c0 =
3073         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
3074     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
3075     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3076         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
3077 
3078     return success();
3079   }
3080 };
3081 
3082 /// Create value signaling an absent optional argument in a call, e.g.
3083 /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
3084 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
3085   using FIROpConversion::FIROpConversion;
3086 
3087   mlir::LogicalResult
3088   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
3089                   mlir::ConversionPatternRewriter &rewriter) const override {
3090     mlir::Type ty = convertType(absent.getType());
3091     mlir::Location loc = absent.getLoc();
3092 
3093     if (absent.getType().isa<fir::BoxCharType>()) {
3094       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
3095       assert(!structTy.isOpaque() && !structTy.getBody().empty());
3096       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3097       auto nullField =
3098           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
3099       mlir::MLIRContext *ctx = absent.getContext();
3100       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3101       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
3102           absent, ty, undefStruct, nullField, c0);
3103     } else {
3104       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
3105     }
3106     return success();
3107   }
3108 };
3109 
3110 //
3111 // Primitive operations on Complex types
3112 //
3113 
3114 /// Generate inline code for complex addition/subtraction
3115 template <typename LLVMOP, typename OPTY>
3116 static mlir::LLVM::InsertValueOp
3117 complexSum(OPTY sumop, mlir::ValueRange opnds,
3118            mlir::ConversionPatternRewriter &rewriter,
3119            fir::LLVMTypeConverter &lowering) {
3120   mlir::Value a = opnds[0];
3121   mlir::Value b = opnds[1];
3122   auto loc = sumop.getLoc();
3123   auto ctx = sumop.getContext();
3124   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3125   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3126   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
3127   mlir::Type ty = lowering.convertType(sumop.getType());
3128   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3129   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3130   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3131   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3132   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
3133   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
3134   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3135   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
3136   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
3137 }
3138 } // namespace
3139 
3140 namespace {
3141 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
3142   using FIROpConversion::FIROpConversion;
3143 
3144   mlir::LogicalResult
3145   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
3146                   mlir::ConversionPatternRewriter &rewriter) const override {
3147     // given: (x + iy) + (x' + iy')
3148     // result: (x + x') + i(y + y')
3149     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
3150                                             rewriter, lowerTy());
3151     rewriter.replaceOp(addc, r.getResult());
3152     return success();
3153   }
3154 };
3155 
3156 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
3157   using FIROpConversion::FIROpConversion;
3158 
3159   mlir::LogicalResult
3160   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
3161                   mlir::ConversionPatternRewriter &rewriter) const override {
3162     // given: (x + iy) - (x' + iy')
3163     // result: (x - x') + i(y - y')
3164     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
3165                                             rewriter, lowerTy());
3166     rewriter.replaceOp(subc, r.getResult());
3167     return success();
3168   }
3169 };
3170 
3171 /// Inlined complex multiply
3172 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
3173   using FIROpConversion::FIROpConversion;
3174 
3175   mlir::LogicalResult
3176   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
3177                   mlir::ConversionPatternRewriter &rewriter) const override {
3178     // TODO: Can we use a call to __muldc3 ?
3179     // given: (x + iy) * (x' + iy')
3180     // result: (xx'-yy')+i(xy'+yx')
3181     mlir::Value a = adaptor.getOperands()[0];
3182     mlir::Value b = adaptor.getOperands()[1];
3183     auto loc = mulc.getLoc();
3184     auto *ctx = mulc.getContext();
3185     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3186     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3187     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
3188     mlir::Type ty = convertType(mulc.getType());
3189     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3190     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3191     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3192     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3193     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
3194     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
3195     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
3196     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
3197     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
3198     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
3199     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3200     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
3201     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
3202     rewriter.replaceOp(mulc, r0.getResult());
3203     return success();
3204   }
3205 };
3206 
3207 /// Inlined complex division
3208 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
3209   using FIROpConversion::FIROpConversion;
3210 
3211   mlir::LogicalResult
3212   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
3213                   mlir::ConversionPatternRewriter &rewriter) const override {
3214     // TODO: Can we use a call to __divdc3 instead?
3215     // Just generate inline code for now.
3216     // given: (x + iy) / (x' + iy')
3217     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
3218     mlir::Value a = adaptor.getOperands()[0];
3219     mlir::Value b = adaptor.getOperands()[1];
3220     auto loc = divc.getLoc();
3221     auto *ctx = divc.getContext();
3222     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
3223     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
3224     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
3225     mlir::Type ty = convertType(divc.getType());
3226     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
3227     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
3228     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
3229     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
3230     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
3231     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
3232     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
3233     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
3234     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
3235     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
3236     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
3237     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
3238     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
3239     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
3240     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
3241     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3242     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
3243     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
3244     rewriter.replaceOp(divc, r0.getResult());
3245     return success();
3246   }
3247 };
3248 
3249 /// Inlined complex negation
3250 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
3251   using FIROpConversion::FIROpConversion;
3252 
3253   mlir::LogicalResult
3254   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
3255                   mlir::ConversionPatternRewriter &rewriter) const override {
3256     // given: -(x + iy)
3257     // result: -x - iy
3258     auto *ctxt = neg.getContext();
3259     auto eleTy = convertType(getComplexEleTy(neg.getType()));
3260     auto ty = convertType(neg.getType());
3261     auto loc = neg.getLoc();
3262     mlir::Value o0 = adaptor.getOperands()[0];
3263     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
3264     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
3265     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
3266     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
3267     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
3268     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
3269     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
3270     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
3271     return success();
3272   }
3273 };
3274 
3275 /// Conversion pattern for operation that must be dead. The information in these
3276 /// operations is used by other operation. At this point they should not have
3277 /// anymore uses.
3278 /// These operations are normally dead after the pre-codegen pass.
3279 template <typename FromOp>
3280 struct MustBeDeadConversion : public FIROpConversion<FromOp> {
3281   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering,
3282                                 const fir::FIRToLLVMPassOptions &options)
3283       : FIROpConversion<FromOp>(lowering, options) {}
3284   using OpAdaptor = typename FromOp::Adaptor;
3285 
3286   mlir::LogicalResult
3287   matchAndRewrite(FromOp op, OpAdaptor adaptor,
3288                   mlir::ConversionPatternRewriter &rewriter) const final {
3289     if (!op->getUses().empty())
3290       return rewriter.notifyMatchFailure(op, "op must be dead");
3291     rewriter.eraseOp(op);
3292     return success();
3293   }
3294 };
3295 
3296 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
3297   using MustBeDeadConversion::MustBeDeadConversion;
3298 };
3299 
3300 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
3301   using MustBeDeadConversion::MustBeDeadConversion;
3302 };
3303 
3304 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
3305   using MustBeDeadConversion::MustBeDeadConversion;
3306 };
3307 
3308 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
3309   using MustBeDeadConversion::MustBeDeadConversion;
3310 };
3311 
3312 } // namespace
3313 
3314 namespace {
3315 /// Convert FIR dialect to LLVM dialect
3316 ///
3317 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
3318 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
3319 ///
3320 /// This pass is not complete yet. We are upstreaming it in small patches.
3321 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
3322 public:
3323   FIRToLLVMLowering() = default;
3324   FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {}
3325   mlir::ModuleOp getModule() { return getOperation(); }
3326 
3327   void runOnOperation() override final {
3328     auto mod = getModule();
3329     if (!forcedTargetTriple.empty()) {
3330       fir::setTargetTriple(mod, forcedTargetTriple);
3331     }
3332 
3333     auto *context = getModule().getContext();
3334     fir::LLVMTypeConverter typeConverter{getModule()};
3335     mlir::RewritePatternSet pattern(context);
3336     pattern.insert<
3337         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
3338         AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
3339         BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
3340         BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
3341         BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion,
3342         CallOpConversion, CmpcOpConversion, ConstcOpConversion,
3343         ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion,
3344         DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion,
3345         EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
3346         ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
3347         FreeMemOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion,
3348         GlobalOpConversion, HasValueOpConversion, InsertOnRangeOpConversion,
3349         InsertValueOpConversion, IsPresentOpConversion,
3350         LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
3351         NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
3352         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
3353         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
3354         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
3355         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
3356         UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
3357         XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter,
3358                                                                   options);
3359     mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern);
3360     mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern);
3361     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
3362                                                             pattern);
3363     mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter,
3364                                                           pattern);
3365     mlir::ConversionTarget target{*context};
3366     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3367     // The OpenMP dialect is legal for Operations without regions, for those
3368     // which contains regions it is legal if the region contains only the
3369     // LLVM dialect.
3370     target.addDynamicallyLegalOp<mlir::omp::ParallelOp, mlir::omp::WsLoopOp,
3371                                  mlir::omp::MasterOp>([&](Operation *op) {
3372       return typeConverter.isLegal(&op->getRegion(0));
3373     });
3374     target.addLegalDialect<mlir::omp::OpenMPDialect>();
3375 
3376     // required NOPs for applying a full conversion
3377     target.addLegalOp<mlir::ModuleOp>();
3378 
3379     // apply the patterns
3380     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
3381                                                std::move(pattern)))) {
3382       signalPassFailure();
3383     }
3384   }
3385 
3386 private:
3387   fir::FIRToLLVMPassOptions options;
3388 };
3389 
3390 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
3391 struct LLVMIRLoweringPass
3392     : public mlir::PassWrapper<LLVMIRLoweringPass,
3393                                mlir::OperationPass<mlir::ModuleOp>> {
3394   MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LLVMIRLoweringPass)
3395 
3396   using Printer = fir::LLVMIRLoweringPrinter;
3397   LLVMIRLoweringPass(raw_ostream &output, Printer p)
3398       : output{output}, printer{p} {}
3399 
3400   mlir::ModuleOp getModule() { return getOperation(); }
3401 
3402   void runOnOperation() override final {
3403     auto *ctx = getModule().getContext();
3404     auto optName = getModule().getName();
3405     llvm::LLVMContext llvmCtx;
3406     if (auto llvmModule = mlir::translateModuleToLLVMIR(
3407             getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
3408       printer(*llvmModule, output);
3409       return;
3410     }
3411 
3412     mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
3413     signalPassFailure();
3414   }
3415 
3416 private:
3417   raw_ostream &output;
3418   Printer printer;
3419 };
3420 
3421 } // namespace
3422 
3423 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
3424   return std::make_unique<FIRToLLVMLowering>();
3425 }
3426 
3427 std::unique_ptr<mlir::Pass>
3428 fir::createFIRToLLVMPass(FIRToLLVMPassOptions options) {
3429   return std::make_unique<FIRToLLVMLowering>(options);
3430 }
3431 
3432 std::unique_ptr<mlir::Pass>
3433 fir::createLLVMDialectToLLVMPass(raw_ostream &output,
3434                                  fir::LLVMIRLoweringPrinter printer) {
3435   return std::make_unique<LLVMIRLoweringPass>(output, printer);
3436 }
3437