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