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