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