1044d5b5dSValentin Clement //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
2044d5b5dSValentin Clement //
3044d5b5dSValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4044d5b5dSValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5044d5b5dSValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6044d5b5dSValentin Clement //
7044d5b5dSValentin Clement //===----------------------------------------------------------------------===//
8044d5b5dSValentin Clement //
9044d5b5dSValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10044d5b5dSValentin Clement //
11044d5b5dSValentin Clement //===----------------------------------------------------------------------===//
12044d5b5dSValentin Clement 
13044d5b5dSValentin Clement #include "flang/Optimizer/CodeGen/CodeGen.h"
141f551032SValentin Clement #include "CGOps.h"
15044d5b5dSValentin Clement #include "PassDetail.h"
16b6e44ecdSValentin Clement #include "flang/ISO_Fortran_binding.h"
1739f4ef81SValentin Clement #include "flang/Optimizer/Dialect/FIRAttr.h"
18044d5b5dSValentin Clement #include "flang/Optimizer/Dialect/FIROps.h"
19af6ee580SValentin Clement #include "flang/Optimizer/Support/TypeCode.h"
20044d5b5dSValentin Clement #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
21044d5b5dSValentin Clement #include "mlir/Conversion/LLVMCommon/Pattern.h"
22044d5b5dSValentin Clement #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
23044d5b5dSValentin Clement #include "mlir/IR/BuiltinTypes.h"
243ae8e442SValentin Clement #include "mlir/IR/Matchers.h"
25044d5b5dSValentin Clement #include "mlir/Pass/Pass.h"
26044d5b5dSValentin Clement #include "llvm/ADT/ArrayRef.h"
27044d5b5dSValentin Clement 
28044d5b5dSValentin Clement #define DEBUG_TYPE "flang-codegen"
29044d5b5dSValentin Clement 
30044d5b5dSValentin Clement // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
31044d5b5dSValentin Clement #include "TypeConverter.h"
32044d5b5dSValentin Clement 
33af6ee580SValentin Clement // TODO: This should really be recovered from the specified target.
34af6ee580SValentin Clement static constexpr unsigned defaultAlign = 8;
35af6ee580SValentin Clement 
36b6e44ecdSValentin Clement /// `fir.box` attribute values as defined for CFI_attribute_t in
37b6e44ecdSValentin Clement /// flang/ISO_Fortran_binding.h.
38b6e44ecdSValentin Clement static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
39b6e44ecdSValentin Clement static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
40b6e44ecdSValentin Clement 
41*fa517555SKiran Chandramohan static mlir::Type getVoidPtrType(mlir::MLIRContext *context) {
42*fa517555SKiran Chandramohan   return mlir::LLVM::LLVMPointerType::get(mlir::IntegerType::get(context, 8));
43*fa517555SKiran Chandramohan }
44*fa517555SKiran Chandramohan 
451e6d9c06SDiana Picus static mlir::LLVM::ConstantOp
461e6d9c06SDiana Picus genConstantIndex(mlir::Location loc, mlir::Type ity,
471e6d9c06SDiana Picus                  mlir::ConversionPatternRewriter &rewriter,
481e6d9c06SDiana Picus                  std::int64_t offset) {
491e6d9c06SDiana Picus   auto cattr = rewriter.getI64IntegerAttr(offset);
501e6d9c06SDiana Picus   return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
511e6d9c06SDiana Picus }
521e6d9c06SDiana Picus 
5339f4ef81SValentin Clement static Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
5439f4ef81SValentin Clement                           mlir::Block *insertBefore) {
5539f4ef81SValentin Clement   assert(insertBefore && "expected valid insertion block");
5639f4ef81SValentin Clement   return rewriter.createBlock(insertBefore->getParent(),
5739f4ef81SValentin Clement                               mlir::Region::iterator(insertBefore));
5839f4ef81SValentin Clement }
5939f4ef81SValentin Clement 
60044d5b5dSValentin Clement namespace {
61044d5b5dSValentin Clement /// FIR conversion pattern template
62044d5b5dSValentin Clement template <typename FromOp>
63044d5b5dSValentin Clement class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
64044d5b5dSValentin Clement public:
65044d5b5dSValentin Clement   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
66044d5b5dSValentin Clement       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
67044d5b5dSValentin Clement 
68044d5b5dSValentin Clement protected:
69044d5b5dSValentin Clement   mlir::Type convertType(mlir::Type ty) const {
70044d5b5dSValentin Clement     return lowerTy().convertType(ty);
71044d5b5dSValentin Clement   }
72044d5b5dSValentin Clement 
735d27abe6SValentin Clement   mlir::Type getVoidPtrType() const {
745d27abe6SValentin Clement     return mlir::LLVM::LLVMPointerType::get(
755d27abe6SValentin Clement         mlir::IntegerType::get(&lowerTy().getContext(), 8));
765d27abe6SValentin Clement   }
775d27abe6SValentin Clement 
78df3b9810SValentin Clement   mlir::LLVM::ConstantOp
79af6ee580SValentin Clement   genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
80af6ee580SValentin Clement                  int value) const {
81af6ee580SValentin Clement     mlir::Type i32Ty = rewriter.getI32Type();
82af6ee580SValentin Clement     mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value);
83af6ee580SValentin Clement     return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr);
84af6ee580SValentin Clement   }
85af6ee580SValentin Clement 
86af6ee580SValentin Clement   mlir::LLVM::ConstantOp
87df3b9810SValentin Clement   genConstantOffset(mlir::Location loc,
88df3b9810SValentin Clement                     mlir::ConversionPatternRewriter &rewriter,
89df3b9810SValentin Clement                     int offset) const {
90af6ee580SValentin Clement     mlir::Type ity = lowerTy().offsetType();
91af6ee580SValentin Clement     mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset);
92df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
93df3b9810SValentin Clement   }
94df3b9810SValentin Clement 
95b6e44ecdSValentin Clement   /// Construct code sequence to extract the specifc value from a `fir.box`.
96b6e44ecdSValentin Clement   mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box,
97df3b9810SValentin Clement                               mlir::Type resultTy,
98b6e44ecdSValentin Clement                               mlir::ConversionPatternRewriter &rewriter,
99b6e44ecdSValentin Clement                               unsigned boxValue) const {
100df3b9810SValentin Clement     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
101b6e44ecdSValentin Clement     mlir::LLVM::ConstantOp cValuePos =
102b6e44ecdSValentin Clement         genConstantOffset(loc, rewriter, boxValue);
103df3b9810SValentin Clement     auto pty = mlir::LLVM::LLVMPointerType::get(resultTy);
104df3b9810SValentin Clement     auto p = rewriter.create<mlir::LLVM::GEPOp>(
105b6e44ecdSValentin Clement         loc, pty, mlir::ValueRange{box, c0, cValuePos});
106df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p);
107df3b9810SValentin Clement   }
108df3b9810SValentin Clement 
109df3b9810SValentin Clement   /// Method to construct code sequence to get the triple for dimension `dim`
110df3b9810SValentin Clement   /// from a box.
111df3b9810SValentin Clement   SmallVector<mlir::Value, 3>
112df3b9810SValentin Clement   getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys,
113df3b9810SValentin Clement                  mlir::Value box, mlir::Value dim,
114df3b9810SValentin Clement                  mlir::ConversionPatternRewriter &rewriter) const {
115df3b9810SValentin Clement     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
116df3b9810SValentin Clement     mlir::LLVM::ConstantOp cDims =
117df3b9810SValentin Clement         genConstantOffset(loc, rewriter, kDimsPosInBox);
118df3b9810SValentin Clement     mlir::LLVM::LoadOp l0 =
119df3b9810SValentin Clement         loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter);
120df3b9810SValentin Clement     mlir::LLVM::LoadOp l1 =
121df3b9810SValentin Clement         loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter);
122df3b9810SValentin Clement     mlir::LLVM::LoadOp l2 =
123df3b9810SValentin Clement         loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter);
124df3b9810SValentin Clement     return {l0.getResult(), l1.getResult(), l2.getResult()};
125df3b9810SValentin Clement   }
126df3b9810SValentin Clement 
127df3b9810SValentin Clement   mlir::LLVM::LoadOp
128df3b9810SValentin Clement   loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0,
129df3b9810SValentin Clement                  mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off,
130df3b9810SValentin Clement                  mlir::Type ty,
131df3b9810SValentin Clement                  mlir::ConversionPatternRewriter &rewriter) const {
132df3b9810SValentin Clement     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
133df3b9810SValentin Clement     mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off);
134df3b9810SValentin Clement     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c);
135df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
136df3b9810SValentin Clement   }
137df3b9810SValentin Clement 
1385d27abe6SValentin Clement   mlir::Value
1395d27abe6SValentin Clement   loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim,
1405d27abe6SValentin Clement                     mlir::ConversionPatternRewriter &rewriter) const {
1415d27abe6SValentin Clement     auto idxTy = lowerTy().indexType();
1425d27abe6SValentin Clement     auto c0 = genConstantOffset(loc, rewriter, 0);
1435d27abe6SValentin Clement     auto cDims = genConstantOffset(loc, rewriter, kDimsPosInBox);
1445d27abe6SValentin Clement     auto dimValue = genConstantIndex(loc, idxTy, rewriter, dim);
1455d27abe6SValentin Clement     return loadFromOffset(loc, box, c0, cDims, dimValue, kDimStridePos, idxTy,
1465d27abe6SValentin Clement                           rewriter);
1475d27abe6SValentin Clement   }
1485d27abe6SValentin Clement 
149df3b9810SValentin Clement   /// Read base address from a fir.box. Returned address has type ty.
150df3b9810SValentin Clement   mlir::Value
151df3b9810SValentin Clement   loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
152df3b9810SValentin Clement                       mlir::ConversionPatternRewriter &rewriter) const {
153df3b9810SValentin Clement     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
154df3b9810SValentin Clement     mlir::LLVM::ConstantOp cAddr =
155df3b9810SValentin Clement         genConstantOffset(loc, rewriter, kAddrPosInBox);
156df3b9810SValentin Clement     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
157df3b9810SValentin Clement     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr);
158df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
159df3b9810SValentin Clement   }
160df3b9810SValentin Clement 
161df3b9810SValentin Clement   mlir::Value
162df3b9810SValentin Clement   loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box,
163df3b9810SValentin Clement                          mlir::ConversionPatternRewriter &rewriter) const {
164df3b9810SValentin Clement     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
165df3b9810SValentin Clement     mlir::LLVM::ConstantOp cElemLen =
166df3b9810SValentin Clement         genConstantOffset(loc, rewriter, kElemLenPosInBox);
167df3b9810SValentin Clement     auto pty = mlir::LLVM::LLVMPointerType::get(ty);
168df3b9810SValentin Clement     mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen);
169df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p);
170df3b9810SValentin Clement   }
171df3b9810SValentin Clement 
172b6e44ecdSValentin Clement   // Load the attribute from the \p box and perform a check against \p maskValue
173b6e44ecdSValentin Clement   // The final comparison is implemented as `(attribute & maskValue) != 0`.
174b6e44ecdSValentin Clement   mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box,
175b6e44ecdSValentin Clement                                    mlir::ConversionPatternRewriter &rewriter,
176b6e44ecdSValentin Clement                                    unsigned maskValue) const {
177b6e44ecdSValentin Clement     mlir::Type attrTy = rewriter.getI32Type();
178b6e44ecdSValentin Clement     mlir::Value attribute =
179b6e44ecdSValentin Clement         getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox);
180b6e44ecdSValentin Clement     mlir::LLVM::ConstantOp attrMask =
181b6e44ecdSValentin Clement         genConstantOffset(loc, rewriter, maskValue);
182b6e44ecdSValentin Clement     auto maskRes =
183b6e44ecdSValentin Clement         rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask);
184b6e44ecdSValentin Clement     mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0);
185b6e44ecdSValentin Clement     return rewriter.create<mlir::LLVM::ICmpOp>(
186b6e44ecdSValentin Clement         loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0);
187b6e44ecdSValentin Clement   }
188b6e44ecdSValentin Clement 
189af6ee580SValentin Clement   // Get the element type given an LLVM type that is of the form
190af6ee580SValentin Clement   // [llvm.ptr](array|struct|vector)+ and the provided indexes.
191af6ee580SValentin Clement   static mlir::Type getBoxEleTy(mlir::Type type,
192af6ee580SValentin Clement                                 llvm::ArrayRef<unsigned> indexes) {
193af6ee580SValentin Clement     if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
194af6ee580SValentin Clement       type = t.getElementType();
195af6ee580SValentin Clement     for (auto i : indexes) {
196af6ee580SValentin Clement       if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
197af6ee580SValentin Clement         assert(!t.isOpaque() && i < t.getBody().size());
198af6ee580SValentin Clement         type = t.getBody()[i];
199af6ee580SValentin Clement       } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
200af6ee580SValentin Clement         type = t.getElementType();
201af6ee580SValentin Clement       } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
202af6ee580SValentin Clement         type = t.getElementType();
203af6ee580SValentin Clement       } else {
204af6ee580SValentin Clement         fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
205af6ee580SValentin Clement                             "request for invalid box element type");
206af6ee580SValentin Clement       }
207af6ee580SValentin Clement     }
208af6ee580SValentin Clement     return type;
209af6ee580SValentin Clement   }
210af6ee580SValentin Clement 
2115d27abe6SValentin Clement   // Return LLVM type of the base address given the LLVM type
2125d27abe6SValentin Clement   // of the related descriptor (lowered fir.box type).
2135d27abe6SValentin Clement   static mlir::Type getBaseAddrTypeFromBox(mlir::Type type) {
2145d27abe6SValentin Clement     return getBoxEleTy(type, {kAddrPosInBox});
2155d27abe6SValentin Clement   }
2165d27abe6SValentin Clement 
217df3b9810SValentin Clement   template <typename... ARGS>
218df3b9810SValentin Clement   mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
219df3b9810SValentin Clement                            mlir::ConversionPatternRewriter &rewriter,
220df3b9810SValentin Clement                            mlir::Value base, ARGS... args) const {
221df3b9810SValentin Clement     SmallVector<mlir::Value> cv{args...};
222df3b9810SValentin Clement     return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv);
223df3b9810SValentin Clement   }
224df3b9810SValentin Clement 
2251e6d9c06SDiana Picus   /// Perform an extension or truncation as needed on an integer value. Lowering
2261e6d9c06SDiana Picus   /// to the specific target may involve some sign-extending or truncation of
2271e6d9c06SDiana Picus   /// values, particularly to fit them from abstract box types to the
2281e6d9c06SDiana Picus   /// appropriate reified structures.
2291e6d9c06SDiana Picus   mlir::Value integerCast(mlir::Location loc,
2301e6d9c06SDiana Picus                           mlir::ConversionPatternRewriter &rewriter,
2311e6d9c06SDiana Picus                           mlir::Type ty, mlir::Value val) const {
2321e6d9c06SDiana Picus     auto valTy = val.getType();
2331e6d9c06SDiana Picus     // If the value was not yet lowered, lower its type so that it can
2341e6d9c06SDiana Picus     // be used in getPrimitiveTypeSizeInBits.
2351e6d9c06SDiana Picus     if (!valTy.isa<mlir::IntegerType>())
2361e6d9c06SDiana Picus       valTy = convertType(valTy);
2371e6d9c06SDiana Picus     auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
2381e6d9c06SDiana Picus     auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy);
2391e6d9c06SDiana Picus     if (toSize < fromSize)
2401e6d9c06SDiana Picus       return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val);
2411e6d9c06SDiana Picus     if (toSize > fromSize)
2421e6d9c06SDiana Picus       return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val);
2431e6d9c06SDiana Picus     return val;
2441e6d9c06SDiana Picus   }
2451e6d9c06SDiana Picus 
246044d5b5dSValentin Clement   fir::LLVMTypeConverter &lowerTy() const {
247044d5b5dSValentin Clement     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
248044d5b5dSValentin Clement   }
249044d5b5dSValentin Clement };
250044d5b5dSValentin Clement 
2513ae8e442SValentin Clement /// FIR conversion pattern template
2523ae8e442SValentin Clement template <typename FromOp>
2533ae8e442SValentin Clement class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
2543ae8e442SValentin Clement public:
2553ae8e442SValentin Clement   using FIROpConversion<FromOp>::FIROpConversion;
2563ae8e442SValentin Clement   using OpAdaptor = typename FromOp::Adaptor;
2573ae8e442SValentin Clement 
2583ae8e442SValentin Clement   mlir::LogicalResult
2593ae8e442SValentin Clement   matchAndRewrite(FromOp op, OpAdaptor adaptor,
2603ae8e442SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const final {
2613ae8e442SValentin Clement     mlir::Type ty = this->convertType(op.getType());
2623ae8e442SValentin Clement     return doRewrite(op, ty, adaptor, rewriter);
2633ae8e442SValentin Clement   }
2643ae8e442SValentin Clement 
2653ae8e442SValentin Clement   virtual mlir::LogicalResult
2663ae8e442SValentin Clement   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
2673ae8e442SValentin Clement             mlir::ConversionPatternRewriter &rewriter) const = 0;
2683ae8e442SValentin Clement };
2693ae8e442SValentin Clement 
270420ad7ceSAndrzej Warzynski /// Create value signaling an absent optional argument in a call, e.g.
271420ad7ceSAndrzej Warzynski /// `fir.absent !fir.ref<i64>` -->  `llvm.mlir.null : !llvm.ptr<i64>`
272420ad7ceSAndrzej Warzynski struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> {
273420ad7ceSAndrzej Warzynski   using FIROpConversion::FIROpConversion;
274420ad7ceSAndrzej Warzynski 
275420ad7ceSAndrzej Warzynski   mlir::LogicalResult
276420ad7ceSAndrzej Warzynski   matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
277420ad7ceSAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
278420ad7ceSAndrzej Warzynski     mlir::Type ty = convertType(absent.getType());
279420ad7ceSAndrzej Warzynski     mlir::Location loc = absent.getLoc();
280420ad7ceSAndrzej Warzynski 
281420ad7ceSAndrzej Warzynski     if (absent.getType().isa<fir::BoxCharType>()) {
282420ad7ceSAndrzej Warzynski       auto structTy = ty.cast<mlir::LLVM::LLVMStructType>();
283420ad7ceSAndrzej Warzynski       assert(!structTy.isOpaque() && !structTy.getBody().empty());
284420ad7ceSAndrzej Warzynski       auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
285420ad7ceSAndrzej Warzynski       auto nullField =
286420ad7ceSAndrzej Warzynski           rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]);
287420ad7ceSAndrzej Warzynski       mlir::MLIRContext *ctx = absent.getContext();
288420ad7ceSAndrzej Warzynski       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
289420ad7ceSAndrzej Warzynski       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
290420ad7ceSAndrzej Warzynski           absent, ty, undefStruct, nullField, c0);
291420ad7ceSAndrzej Warzynski     } else {
292420ad7ceSAndrzej Warzynski       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty);
293420ad7ceSAndrzej Warzynski     }
294420ad7ceSAndrzej Warzynski     return success();
295420ad7ceSAndrzej Warzynski   }
296420ad7ceSAndrzej Warzynski };
297420ad7ceSAndrzej Warzynski 
2980c4a7a52SValentin Clement // Lower `fir.address_of` operation to `llvm.address_of` operation.
299044d5b5dSValentin Clement struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
300044d5b5dSValentin Clement   using FIROpConversion::FIROpConversion;
301044d5b5dSValentin Clement 
302044d5b5dSValentin Clement   mlir::LogicalResult
303044d5b5dSValentin Clement   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
304044d5b5dSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
305044d5b5dSValentin Clement     auto ty = convertType(addr.getType());
306044d5b5dSValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
307044d5b5dSValentin Clement         addr, ty, addr.symbol().getRootReference().getValue());
308044d5b5dSValentin Clement     return success();
309044d5b5dSValentin Clement   }
310044d5b5dSValentin Clement };
3111e6d9c06SDiana Picus } // namespace
3121e6d9c06SDiana Picus 
3131e6d9c06SDiana Picus /// Lookup the function to compute the memory size of this parametric derived
3141e6d9c06SDiana Picus /// type. The size of the object may depend on the LEN type parameters of the
3151e6d9c06SDiana Picus /// derived type.
3161e6d9c06SDiana Picus static mlir::LLVM::LLVMFuncOp
3171e6d9c06SDiana Picus getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
3181e6d9c06SDiana Picus                           mlir::ConversionPatternRewriter &rewriter) {
3191e6d9c06SDiana Picus   auto module = op->getParentOfType<mlir::ModuleOp>();
3201e6d9c06SDiana Picus   std::string name = recTy.getName().str() + "P.mem.size";
3211e6d9c06SDiana Picus   return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name);
3221e6d9c06SDiana Picus }
3231e6d9c06SDiana Picus 
3241e6d9c06SDiana Picus namespace {
3251e6d9c06SDiana Picus /// convert to LLVM IR dialect `alloca`
3261e6d9c06SDiana Picus struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> {
3271e6d9c06SDiana Picus   using FIROpConversion::FIROpConversion;
3281e6d9c06SDiana Picus 
3291e6d9c06SDiana Picus   mlir::LogicalResult
3301e6d9c06SDiana Picus   matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
3311e6d9c06SDiana Picus                   mlir::ConversionPatternRewriter &rewriter) const override {
3321e6d9c06SDiana Picus     mlir::ValueRange operands = adaptor.getOperands();
3331e6d9c06SDiana Picus     auto loc = alloc.getLoc();
3341e6d9c06SDiana Picus     mlir::Type ity = lowerTy().indexType();
3351e6d9c06SDiana Picus     unsigned i = 0;
3361e6d9c06SDiana Picus     mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
3371e6d9c06SDiana Picus     mlir::Type ty = convertType(alloc.getType());
3381e6d9c06SDiana Picus     mlir::Type resultTy = ty;
3391e6d9c06SDiana Picus     if (alloc.hasLenParams()) {
3401e6d9c06SDiana Picus       unsigned end = alloc.numLenParams();
3411e6d9c06SDiana Picus       llvm::SmallVector<mlir::Value> lenParams;
3421e6d9c06SDiana Picus       for (; i < end; ++i)
3431e6d9c06SDiana Picus         lenParams.push_back(operands[i]);
3441e6d9c06SDiana Picus       mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
3451e6d9c06SDiana Picus       if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) {
3461e6d9c06SDiana Picus         fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
3471e6d9c06SDiana Picus             chrTy.getContext(), chrTy.getFKind());
3481e6d9c06SDiana Picus         ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy));
3491e6d9c06SDiana Picus         assert(end == 1);
3501e6d9c06SDiana Picus         size = integerCast(loc, rewriter, ity, lenParams[0]);
3511e6d9c06SDiana Picus       } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) {
3521e6d9c06SDiana Picus         mlir::LLVM::LLVMFuncOp memSizeFn =
3531e6d9c06SDiana Picus             getDependentTypeMemSizeFn(recTy, alloc, rewriter);
3541e6d9c06SDiana Picus         if (!memSizeFn)
3551e6d9c06SDiana Picus           emitError(loc, "did not find allocation function");
3561e6d9c06SDiana Picus         mlir::NamedAttribute attr = rewriter.getNamedAttr(
3571e6d9c06SDiana Picus             "callee", mlir::SymbolRefAttr::get(memSizeFn));
3581e6d9c06SDiana Picus         auto call = rewriter.create<mlir::LLVM::CallOp>(
3591e6d9c06SDiana Picus             loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr});
3601e6d9c06SDiana Picus         size = call.getResult(0);
3611e6d9c06SDiana Picus         ty = mlir::LLVM::LLVMPointerType::get(
3621e6d9c06SDiana Picus             mlir::IntegerType::get(alloc.getContext(), 8));
3631e6d9c06SDiana Picus       } else {
3641e6d9c06SDiana Picus         return emitError(loc, "unexpected type ")
3651e6d9c06SDiana Picus                << scalarType << " with type parameters";
3661e6d9c06SDiana Picus       }
3671e6d9c06SDiana Picus     }
3681e6d9c06SDiana Picus     if (alloc.hasShapeOperands()) {
3691e6d9c06SDiana Picus       mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType());
3701e6d9c06SDiana Picus       // Scale the size by constant factors encoded in the array type.
3711e6d9c06SDiana Picus       if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) {
3721e6d9c06SDiana Picus         fir::SequenceType::Extent constSize = 1;
3731e6d9c06SDiana Picus         for (auto extent : seqTy.getShape())
3741e6d9c06SDiana Picus           if (extent != fir::SequenceType::getUnknownExtent())
3751e6d9c06SDiana Picus             constSize *= extent;
3761e6d9c06SDiana Picus         mlir::Value constVal{
3771e6d9c06SDiana Picus             genConstantIndex(loc, ity, rewriter, constSize).getResult()};
3781e6d9c06SDiana Picus         size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal);
3791e6d9c06SDiana Picus       }
3801e6d9c06SDiana Picus       unsigned end = operands.size();
3811e6d9c06SDiana Picus       for (; i < end; ++i)
3821e6d9c06SDiana Picus         size = rewriter.create<mlir::LLVM::MulOp>(
3831e6d9c06SDiana Picus             loc, ity, size, integerCast(loc, rewriter, ity, operands[i]));
3841e6d9c06SDiana Picus     }
3851e6d9c06SDiana Picus     if (ty == resultTy) {
3861e6d9c06SDiana Picus       // Do not emit the bitcast if ty and resultTy are the same.
3871e6d9c06SDiana Picus       rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size,
3881e6d9c06SDiana Picus                                                         alloc->getAttrs());
3891e6d9c06SDiana Picus     } else {
3901e6d9c06SDiana Picus       auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size,
3911e6d9c06SDiana Picus                                                       alloc->getAttrs());
3921e6d9c06SDiana Picus       rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al);
3931e6d9c06SDiana Picus     }
3941e6d9c06SDiana Picus     return success();
3951e6d9c06SDiana Picus   }
3961e6d9c06SDiana Picus };
397044d5b5dSValentin Clement 
398df3b9810SValentin Clement /// Lower `fir.box_addr` to the sequence of operations to extract the first
399df3b9810SValentin Clement /// element of the box.
400df3b9810SValentin Clement struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> {
401df3b9810SValentin Clement   using FIROpConversion::FIROpConversion;
402df3b9810SValentin Clement 
403df3b9810SValentin Clement   mlir::LogicalResult
404df3b9810SValentin Clement   matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
405df3b9810SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
406df3b9810SValentin Clement     mlir::Value a = adaptor.getOperands()[0];
407df3b9810SValentin Clement     auto loc = boxaddr.getLoc();
408df3b9810SValentin Clement     mlir::Type ty = convertType(boxaddr.getType());
409df3b9810SValentin Clement     if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) {
410df3b9810SValentin Clement       rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter));
411df3b9810SValentin Clement     } else {
412df3b9810SValentin Clement       auto c0attr = rewriter.getI32IntegerAttr(0);
413df3b9810SValentin Clement       auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr);
414df3b9810SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a,
415df3b9810SValentin Clement                                                               c0);
416df3b9810SValentin Clement     }
417df3b9810SValentin Clement     return success();
418df3b9810SValentin Clement   }
419df3b9810SValentin Clement };
420df3b9810SValentin Clement 
421df3b9810SValentin Clement /// Lower `fir.box_dims` to a sequence of operations to extract the requested
422df3b9810SValentin Clement /// dimension infomartion from the boxed value.
423df3b9810SValentin Clement /// Result in a triple set of GEPs and loads.
424df3b9810SValentin Clement struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> {
425df3b9810SValentin Clement   using FIROpConversion::FIROpConversion;
426df3b9810SValentin Clement 
427df3b9810SValentin Clement   mlir::LogicalResult
428df3b9810SValentin Clement   matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
429df3b9810SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
430df3b9810SValentin Clement     SmallVector<mlir::Type, 3> resultTypes = {
431df3b9810SValentin Clement         convertType(boxdims.getResult(0).getType()),
432df3b9810SValentin Clement         convertType(boxdims.getResult(1).getType()),
433df3b9810SValentin Clement         convertType(boxdims.getResult(2).getType()),
434df3b9810SValentin Clement     };
435df3b9810SValentin Clement     auto results =
436df3b9810SValentin Clement         getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0],
437df3b9810SValentin Clement                        adaptor.getOperands()[1], rewriter);
438df3b9810SValentin Clement     rewriter.replaceOp(boxdims, results);
439df3b9810SValentin Clement     return success();
440df3b9810SValentin Clement   }
441df3b9810SValentin Clement };
442df3b9810SValentin Clement 
443df3b9810SValentin Clement /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
444df3b9810SValentin Clement /// an element in the boxed value.
445df3b9810SValentin Clement struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> {
446df3b9810SValentin Clement   using FIROpConversion::FIROpConversion;
447df3b9810SValentin Clement 
448df3b9810SValentin Clement   mlir::LogicalResult
449df3b9810SValentin Clement   matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
450df3b9810SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
451df3b9810SValentin Clement     mlir::Value a = adaptor.getOperands()[0];
452df3b9810SValentin Clement     auto loc = boxelesz.getLoc();
453df3b9810SValentin Clement     auto ty = convertType(boxelesz.getType());
454b6e44ecdSValentin Clement     auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox);
455b6e44ecdSValentin Clement     rewriter.replaceOp(boxelesz, elemSize);
456b6e44ecdSValentin Clement     return success();
457b6e44ecdSValentin Clement   }
458b6e44ecdSValentin Clement };
459b6e44ecdSValentin Clement 
460b6e44ecdSValentin Clement /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
461b6e44ecdSValentin Clement /// boxed value was from an ALLOCATABLE entity.
462b6e44ecdSValentin Clement struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> {
463b6e44ecdSValentin Clement   using FIROpConversion::FIROpConversion;
464b6e44ecdSValentin Clement 
465b6e44ecdSValentin Clement   mlir::LogicalResult
466b6e44ecdSValentin Clement   matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
467b6e44ecdSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
468b6e44ecdSValentin Clement     mlir::Value box = adaptor.getOperands()[0];
469b6e44ecdSValentin Clement     auto loc = boxisalloc.getLoc();
470b6e44ecdSValentin Clement     mlir::Value check =
471b6e44ecdSValentin Clement         genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable);
472b6e44ecdSValentin Clement     rewriter.replaceOp(boxisalloc, check);
473b6e44ecdSValentin Clement     return success();
474b6e44ecdSValentin Clement   }
475b6e44ecdSValentin Clement };
476b6e44ecdSValentin Clement 
477b6e44ecdSValentin Clement /// Lower `fir.box_isarray` to a sequence of operations to determine if the
478b6e44ecdSValentin Clement /// boxed is an array.
479b6e44ecdSValentin Clement struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> {
480b6e44ecdSValentin Clement   using FIROpConversion::FIROpConversion;
481b6e44ecdSValentin Clement 
482b6e44ecdSValentin Clement   mlir::LogicalResult
483b6e44ecdSValentin Clement   matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
484b6e44ecdSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
485b6e44ecdSValentin Clement     mlir::Value a = adaptor.getOperands()[0];
486b6e44ecdSValentin Clement     auto loc = boxisarray.getLoc();
487b6e44ecdSValentin Clement     auto rank =
488b6e44ecdSValentin Clement         getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox);
489b6e44ecdSValentin Clement     auto c0 = genConstantOffset(loc, rewriter, 0);
490b6e44ecdSValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
491b6e44ecdSValentin Clement         boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
492b6e44ecdSValentin Clement     return success();
493b6e44ecdSValentin Clement   }
494b6e44ecdSValentin Clement };
495b6e44ecdSValentin Clement 
496b6e44ecdSValentin Clement /// Lower `fir.box_isptr` to a sequence of operations to determined if the
497b6e44ecdSValentin Clement /// boxed value was from a POINTER entity.
498b6e44ecdSValentin Clement struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> {
499b6e44ecdSValentin Clement   using FIROpConversion::FIROpConversion;
500b6e44ecdSValentin Clement 
501b6e44ecdSValentin Clement   mlir::LogicalResult
502b6e44ecdSValentin Clement   matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
503b6e44ecdSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
504b6e44ecdSValentin Clement     mlir::Value box = adaptor.getOperands()[0];
505b6e44ecdSValentin Clement     auto loc = boxisptr.getLoc();
506b6e44ecdSValentin Clement     mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer);
507b6e44ecdSValentin Clement     rewriter.replaceOp(boxisptr, check);
508df3b9810SValentin Clement     return success();
509df3b9810SValentin Clement   }
510df3b9810SValentin Clement };
511df3b9810SValentin Clement 
512df3b9810SValentin Clement /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
513df3b9810SValentin Clement /// the box.
514df3b9810SValentin Clement struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> {
515df3b9810SValentin Clement   using FIROpConversion::FIROpConversion;
516df3b9810SValentin Clement 
517df3b9810SValentin Clement   mlir::LogicalResult
518df3b9810SValentin Clement   matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
519df3b9810SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
520df3b9810SValentin Clement     mlir::Value a = adaptor.getOperands()[0];
521df3b9810SValentin Clement     auto loc = boxrank.getLoc();
522df3b9810SValentin Clement     mlir::Type ty = convertType(boxrank.getType());
523b6e44ecdSValentin Clement     auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox);
524df3b9810SValentin Clement     rewriter.replaceOp(boxrank, result);
525df3b9810SValentin Clement     return success();
526df3b9810SValentin Clement   }
527df3b9810SValentin Clement };
528df3b9810SValentin Clement 
5291a2ec667SValentin Clement /// Lower `fir.string_lit` to LLVM IR dialect operation.
5301a2ec667SValentin Clement struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> {
5311a2ec667SValentin Clement   using FIROpConversion::FIROpConversion;
5321a2ec667SValentin Clement 
5331a2ec667SValentin Clement   mlir::LogicalResult
5341a2ec667SValentin Clement   matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
5351a2ec667SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
5361a2ec667SValentin Clement     auto ty = convertType(constop.getType());
5371a2ec667SValentin Clement     auto attr = constop.getValue();
5381a2ec667SValentin Clement     if (attr.isa<mlir::StringAttr>()) {
5391a2ec667SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
5401a2ec667SValentin Clement       return success();
5411a2ec667SValentin Clement     }
5421a2ec667SValentin Clement 
5431a2ec667SValentin Clement     auto arr = attr.cast<mlir::ArrayAttr>();
5441a2ec667SValentin Clement     auto charTy = constop.getType().cast<fir::CharacterType>();
5451a2ec667SValentin Clement     unsigned bits = lowerTy().characterBitsize(charTy);
5461a2ec667SValentin Clement     mlir::Type intTy = rewriter.getIntegerType(bits);
5471a2ec667SValentin Clement     auto attrs = llvm::map_range(
5481a2ec667SValentin Clement         arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute {
5491a2ec667SValentin Clement           return mlir::IntegerAttr::get(
5501a2ec667SValentin Clement               intTy,
5511a2ec667SValentin Clement               attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits));
5521a2ec667SValentin Clement         });
5531a2ec667SValentin Clement     mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy);
5541a2ec667SValentin Clement     auto denseAttr = mlir::DenseElementsAttr::get(
5551a2ec667SValentin Clement         vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs));
5561a2ec667SValentin Clement     rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty,
5571a2ec667SValentin Clement                                                          denseAttr);
5581a2ec667SValentin Clement     return success();
5591a2ec667SValentin Clement   }
5601a2ec667SValentin Clement };
5611a2ec667SValentin Clement 
562cc505c0bSKiran Chandramohan /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
563cc505c0bSKiran Chandramohan /// boxproc.
564cc505c0bSKiran Chandramohan /// TODO: Part of supporting Fortran 2003 procedure pointers.
565cc505c0bSKiran Chandramohan struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> {
566cc505c0bSKiran Chandramohan   using FIROpConversion::FIROpConversion;
567cc505c0bSKiran Chandramohan 
568cc505c0bSKiran Chandramohan   mlir::LogicalResult
569cc505c0bSKiran Chandramohan   matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
570cc505c0bSKiran Chandramohan                   mlir::ConversionPatternRewriter &rewriter) const override {
5717ce8c6fcSKiran Chandramohan     TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
5727ce8c6fcSKiran Chandramohan     return failure();
573cc505c0bSKiran Chandramohan   }
574cc505c0bSKiran Chandramohan };
575cc505c0bSKiran Chandramohan 
576e38ef2ffSValentin Clement /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
577e38ef2ffSValentin Clement /// descriptor from the box.
578e38ef2ffSValentin Clement struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> {
579e38ef2ffSValentin Clement   using FIROpConversion::FIROpConversion;
580e38ef2ffSValentin Clement 
581e38ef2ffSValentin Clement   mlir::LogicalResult
582e38ef2ffSValentin Clement   matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
583e38ef2ffSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
584e38ef2ffSValentin Clement     mlir::Value box = adaptor.getOperands()[0];
585e38ef2ffSValentin Clement     auto loc = boxtypedesc.getLoc();
586e38ef2ffSValentin Clement     mlir::Type typeTy =
587e38ef2ffSValentin Clement         fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext());
588e38ef2ffSValentin Clement     auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox);
589e38ef2ffSValentin Clement     auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy);
590e38ef2ffSValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy,
591e38ef2ffSValentin Clement                                                         result);
592e38ef2ffSValentin Clement     return success();
593e38ef2ffSValentin Clement   }
594e38ef2ffSValentin Clement };
595e38ef2ffSValentin Clement 
596ddd11b9aSAndrzej Warzynski // `fir.call` -> `llvm.call`
597ddd11b9aSAndrzej Warzynski struct CallOpConversion : public FIROpConversion<fir::CallOp> {
598ddd11b9aSAndrzej Warzynski   using FIROpConversion::FIROpConversion;
599ddd11b9aSAndrzej Warzynski 
600ddd11b9aSAndrzej Warzynski   mlir::LogicalResult
601ddd11b9aSAndrzej Warzynski   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
602ddd11b9aSAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
603ddd11b9aSAndrzej Warzynski     SmallVector<mlir::Type> resultTys;
604ddd11b9aSAndrzej Warzynski     for (auto r : call.getResults())
605ddd11b9aSAndrzej Warzynski       resultTys.push_back(convertType(r.getType()));
606ddd11b9aSAndrzej Warzynski     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
607ddd11b9aSAndrzej Warzynski         call, resultTys, adaptor.getOperands(), call->getAttrs());
608ddd11b9aSAndrzej Warzynski     return success();
609ddd11b9aSAndrzej Warzynski   }
610ddd11b9aSAndrzej Warzynski };
611ddd11b9aSAndrzej Warzynski 
612092cee5fSValentin Clement static mlir::Type getComplexEleTy(mlir::Type complex) {
613092cee5fSValentin Clement   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
614092cee5fSValentin Clement     return cc.getElementType();
615092cee5fSValentin Clement   return complex.cast<fir::ComplexType>().getElementType();
616092cee5fSValentin Clement }
617092cee5fSValentin Clement 
618f1dfc027SDiana Picus /// Compare complex values
619f1dfc027SDiana Picus ///
620f1dfc027SDiana Picus /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
621f1dfc027SDiana Picus ///
622f1dfc027SDiana Picus /// For completeness, all other comparison are done on the real component only.
623f1dfc027SDiana Picus struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> {
624f1dfc027SDiana Picus   using FIROpConversion::FIROpConversion;
625f1dfc027SDiana Picus 
626f1dfc027SDiana Picus   mlir::LogicalResult
627f1dfc027SDiana Picus   matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
628f1dfc027SDiana Picus                   mlir::ConversionPatternRewriter &rewriter) const override {
629f1dfc027SDiana Picus     mlir::ValueRange operands = adaptor.getOperands();
630f1dfc027SDiana Picus     mlir::MLIRContext *ctxt = cmp.getContext();
631f1dfc027SDiana Picus     mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType()));
632f1dfc027SDiana Picus     mlir::Type resTy = convertType(cmp.getType());
633f1dfc027SDiana Picus     mlir::Location loc = cmp.getLoc();
634f1dfc027SDiana Picus     auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
635f1dfc027SDiana Picus     SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>(
636f1dfc027SDiana Picus                                        loc, eleTy, operands[0], pos0),
637f1dfc027SDiana Picus                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
638f1dfc027SDiana Picus                                        loc, eleTy, operands[1], pos0)};
639f1dfc027SDiana Picus     auto rcp =
640f1dfc027SDiana Picus         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs());
641f1dfc027SDiana Picus     auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
642f1dfc027SDiana Picus     SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>(
643f1dfc027SDiana Picus                                        loc, eleTy, operands[0], pos1),
644f1dfc027SDiana Picus                                    rewriter.create<mlir::LLVM::ExtractValueOp>(
645f1dfc027SDiana Picus                                        loc, eleTy, operands[1], pos1)};
646f1dfc027SDiana Picus     auto icp =
647f1dfc027SDiana Picus         rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs());
648f1dfc027SDiana Picus     SmallVector<mlir::Value, 2> cp{rcp, icp};
649f1dfc027SDiana Picus     switch (cmp.getPredicate()) {
650f1dfc027SDiana Picus     case mlir::arith::CmpFPredicate::OEQ: // .EQ.
651f1dfc027SDiana Picus       rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
652f1dfc027SDiana Picus       break;
653f1dfc027SDiana Picus     case mlir::arith::CmpFPredicate::UNE: // .NE.
654f1dfc027SDiana Picus       rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
655f1dfc027SDiana Picus       break;
656f1dfc027SDiana Picus     default:
657f1dfc027SDiana Picus       rewriter.replaceOp(cmp, rcp.getResult());
658f1dfc027SDiana Picus       break;
659f1dfc027SDiana Picus     }
660f1dfc027SDiana Picus     return success();
661f1dfc027SDiana Picus   }
662f1dfc027SDiana Picus };
663f1dfc027SDiana Picus 
664e81d73edSDiana Picus /// Lower complex constants
665e81d73edSDiana Picus struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> {
666e81d73edSDiana Picus   using FIROpConversion::FIROpConversion;
667e81d73edSDiana Picus 
668e81d73edSDiana Picus   mlir::LogicalResult
669e81d73edSDiana Picus   matchAndRewrite(fir::ConstcOp conc, OpAdaptor,
670e81d73edSDiana Picus                   mlir::ConversionPatternRewriter &rewriter) const override {
671e81d73edSDiana Picus     mlir::Location loc = conc.getLoc();
672e81d73edSDiana Picus     mlir::MLIRContext *ctx = conc.getContext();
673e81d73edSDiana Picus     mlir::Type ty = convertType(conc.getType());
674e81d73edSDiana Picus     mlir::Type ety = convertType(getComplexEleTy(conc.getType()));
675e81d73edSDiana Picus     auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal()));
676e81d73edSDiana Picus     auto realPart =
677e81d73edSDiana Picus         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr);
678e81d73edSDiana Picus     auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary()));
679e81d73edSDiana Picus     auto imPart =
680e81d73edSDiana Picus         rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr);
681e81d73edSDiana Picus     auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
682e81d73edSDiana Picus     auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
683e81d73edSDiana Picus     auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
684e81d73edSDiana Picus     auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>(
685e81d73edSDiana Picus         loc, ty, undef, realPart, realIndex);
686e81d73edSDiana Picus     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal,
687e81d73edSDiana Picus                                                            imPart, imIndex);
688e81d73edSDiana Picus     return success();
689e81d73edSDiana Picus   }
690e81d73edSDiana Picus 
691e81d73edSDiana Picus   inline APFloat getValue(mlir::Attribute attr) const {
692e81d73edSDiana Picus     return attr.cast<fir::RealAttr>().getValue();
693e81d73edSDiana Picus   }
694e81d73edSDiana Picus };
695e81d73edSDiana Picus 
696092cee5fSValentin Clement /// convert value of from-type to value of to-type
697092cee5fSValentin Clement struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
698092cee5fSValentin Clement   using FIROpConversion::FIROpConversion;
699092cee5fSValentin Clement 
700092cee5fSValentin Clement   static bool isFloatingPointTy(mlir::Type ty) {
701092cee5fSValentin Clement     return ty.isa<mlir::FloatType>();
702092cee5fSValentin Clement   }
703092cee5fSValentin Clement 
704092cee5fSValentin Clement   mlir::LogicalResult
705092cee5fSValentin Clement   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
706092cee5fSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
707092cee5fSValentin Clement     auto fromTy = convertType(convert.value().getType());
708092cee5fSValentin Clement     auto toTy = convertType(convert.res().getType());
709092cee5fSValentin Clement     mlir::Value op0 = adaptor.getOperands()[0];
710092cee5fSValentin Clement     if (fromTy == toTy) {
711092cee5fSValentin Clement       rewriter.replaceOp(convert, op0);
712092cee5fSValentin Clement       return success();
713092cee5fSValentin Clement     }
714092cee5fSValentin Clement     auto loc = convert.getLoc();
715092cee5fSValentin Clement     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
716092cee5fSValentin Clement                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
717092cee5fSValentin Clement       if (fromBits == toBits) {
718092cee5fSValentin Clement         // TODO: Converting between two floating-point representations with the
719092cee5fSValentin Clement         // same bitwidth is not allowed for now.
720092cee5fSValentin Clement         mlir::emitError(loc,
721092cee5fSValentin Clement                         "cannot implicitly convert between two floating-point "
722092cee5fSValentin Clement                         "representations of the same bitwidth");
723092cee5fSValentin Clement         return {};
724092cee5fSValentin Clement       }
725092cee5fSValentin Clement       if (fromBits > toBits)
726092cee5fSValentin Clement         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
727092cee5fSValentin Clement       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
728092cee5fSValentin Clement     };
729092cee5fSValentin Clement     // Complex to complex conversion.
730092cee5fSValentin Clement     if (fir::isa_complex(convert.value().getType()) &&
731092cee5fSValentin Clement         fir::isa_complex(convert.res().getType())) {
732092cee5fSValentin Clement       // Special case: handle the conversion of a complex such that both the
733092cee5fSValentin Clement       // real and imaginary parts are converted together.
734092cee5fSValentin Clement       auto zero = mlir::ArrayAttr::get(convert.getContext(),
735092cee5fSValentin Clement                                        rewriter.getI32IntegerAttr(0));
736092cee5fSValentin Clement       auto one = mlir::ArrayAttr::get(convert.getContext(),
737092cee5fSValentin Clement                                       rewriter.getI32IntegerAttr(1));
738092cee5fSValentin Clement       auto ty = convertType(getComplexEleTy(convert.value().getType()));
739092cee5fSValentin Clement       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
740092cee5fSValentin Clement       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
741092cee5fSValentin Clement       auto nt = convertType(getComplexEleTy(convert.res().getType()));
742092cee5fSValentin Clement       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
743092cee5fSValentin Clement       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
744092cee5fSValentin Clement       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
745092cee5fSValentin Clement       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
746092cee5fSValentin Clement       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
747092cee5fSValentin Clement       auto i1 =
748092cee5fSValentin Clement           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
749092cee5fSValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
750092cee5fSValentin Clement                                                              ic, one);
751092cee5fSValentin Clement       return mlir::success();
752092cee5fSValentin Clement     }
753092cee5fSValentin Clement     // Floating point to floating point conversion.
754092cee5fSValentin Clement     if (isFloatingPointTy(fromTy)) {
755092cee5fSValentin Clement       if (isFloatingPointTy(toTy)) {
756092cee5fSValentin Clement         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
757092cee5fSValentin Clement         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
758092cee5fSValentin Clement         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
759092cee5fSValentin Clement         rewriter.replaceOp(convert, v);
760092cee5fSValentin Clement         return mlir::success();
761092cee5fSValentin Clement       }
762092cee5fSValentin Clement       if (toTy.isa<mlir::IntegerType>()) {
763092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
764092cee5fSValentin Clement         return mlir::success();
765092cee5fSValentin Clement       }
766092cee5fSValentin Clement     } else if (fromTy.isa<mlir::IntegerType>()) {
767092cee5fSValentin Clement       // Integer to integer conversion.
768092cee5fSValentin Clement       if (toTy.isa<mlir::IntegerType>()) {
769092cee5fSValentin Clement         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
770092cee5fSValentin Clement         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
771092cee5fSValentin Clement         assert(fromBits != toBits);
772092cee5fSValentin Clement         if (fromBits > toBits) {
773092cee5fSValentin Clement           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
774092cee5fSValentin Clement           return mlir::success();
775092cee5fSValentin Clement         }
776092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
777092cee5fSValentin Clement         return mlir::success();
778092cee5fSValentin Clement       }
779092cee5fSValentin Clement       // Integer to floating point conversion.
780092cee5fSValentin Clement       if (isFloatingPointTy(toTy)) {
781092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
782092cee5fSValentin Clement         return mlir::success();
783092cee5fSValentin Clement       }
784092cee5fSValentin Clement       // Integer to pointer conversion.
785092cee5fSValentin Clement       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
786092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
787092cee5fSValentin Clement         return mlir::success();
788092cee5fSValentin Clement       }
789092cee5fSValentin Clement     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
790092cee5fSValentin Clement       // Pointer to integer conversion.
791092cee5fSValentin Clement       if (toTy.isa<mlir::IntegerType>()) {
792092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
793092cee5fSValentin Clement         return mlir::success();
794092cee5fSValentin Clement       }
795092cee5fSValentin Clement       // Pointer to pointer conversion.
796092cee5fSValentin Clement       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
797092cee5fSValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
798092cee5fSValentin Clement         return mlir::success();
799092cee5fSValentin Clement       }
800092cee5fSValentin Clement     }
801092cee5fSValentin Clement     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
802092cee5fSValentin Clement   }
803092cee5fSValentin Clement };
804092cee5fSValentin Clement 
8059534e361SValentin Clement /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch
8069534e361SValentin Clement /// table.
8079534e361SValentin Clement struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> {
8089534e361SValentin Clement   using FIROpConversion::FIROpConversion;
8099534e361SValentin Clement 
8109534e361SValentin Clement   mlir::LogicalResult
8119534e361SValentin Clement   matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor,
8129534e361SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
8137ce8c6fcSKiran Chandramohan     TODO(dispatch.getLoc(), "fir.dispatch codegen");
8147ce8c6fcSKiran Chandramohan     return failure();
8159534e361SValentin Clement   }
8169534e361SValentin Clement };
8179534e361SValentin Clement 
8189534e361SValentin Clement /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran
8199534e361SValentin Clement /// derived type.
8209534e361SValentin Clement struct DispatchTableOpConversion
8219534e361SValentin Clement     : public FIROpConversion<fir::DispatchTableOp> {
8229534e361SValentin Clement   using FIROpConversion::FIROpConversion;
8239534e361SValentin Clement 
8249534e361SValentin Clement   mlir::LogicalResult
8259534e361SValentin Clement   matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor,
8269534e361SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
8277ce8c6fcSKiran Chandramohan     TODO(dispTab.getLoc(), "fir.dispatch_table codegen");
8287ce8c6fcSKiran Chandramohan     return failure();
8299534e361SValentin Clement   }
8309534e361SValentin Clement };
8319534e361SValentin Clement 
8329534e361SValentin Clement /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a
8339534e361SValentin Clement /// method-name to a function.
8349534e361SValentin Clement struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> {
8359534e361SValentin Clement   using FIROpConversion::FIROpConversion;
8369534e361SValentin Clement 
8379534e361SValentin Clement   mlir::LogicalResult
8389534e361SValentin Clement   matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor,
8399534e361SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
8407ce8c6fcSKiran Chandramohan     TODO(dtEnt.getLoc(), "fir.dt_entry codegen");
8417ce8c6fcSKiran Chandramohan     return failure();
8429534e361SValentin Clement   }
8439534e361SValentin Clement };
8449534e361SValentin Clement 
845677df8c7SValentin Clement /// Lower `fir.global_len` operation.
846677df8c7SValentin Clement struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> {
847677df8c7SValentin Clement   using FIROpConversion::FIROpConversion;
848677df8c7SValentin Clement 
849677df8c7SValentin Clement   mlir::LogicalResult
850677df8c7SValentin Clement   matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
851677df8c7SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
8527ce8c6fcSKiran Chandramohan     TODO(globalLen.getLoc(), "fir.global_len codegen");
8537ce8c6fcSKiran Chandramohan     return failure();
854677df8c7SValentin Clement   }
855677df8c7SValentin Clement };
856677df8c7SValentin Clement 
857cdc476abSDiana Picus /// Lower fir.len_param_index
858cdc476abSDiana Picus struct LenParamIndexOpConversion
859cdc476abSDiana Picus     : public FIROpConversion<fir::LenParamIndexOp> {
860cdc476abSDiana Picus   using FIROpConversion::FIROpConversion;
861cdc476abSDiana Picus 
862cdc476abSDiana Picus   // FIXME: this should be specialized by the runtime target
863cdc476abSDiana Picus   mlir::LogicalResult
864cdc476abSDiana Picus   matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
865cdc476abSDiana Picus                   mlir::ConversionPatternRewriter &rewriter) const override {
8667ce8c6fcSKiran Chandramohan     TODO(lenp.getLoc(), "fir.len_param_index codegen");
867cdc476abSDiana Picus   }
868cdc476abSDiana Picus };
869cdc476abSDiana Picus 
87031246187SValentin Clement /// Lower `fir.gentypedesc` to a global constant.
87131246187SValentin Clement struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> {
87231246187SValentin Clement   using FIROpConversion::FIROpConversion;
87331246187SValentin Clement 
87431246187SValentin Clement   mlir::LogicalResult
87531246187SValentin Clement   matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor,
87631246187SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
8777ce8c6fcSKiran Chandramohan     TODO(gentypedesc.getLoc(), "fir.gentypedesc codegen");
8787ce8c6fcSKiran Chandramohan     return failure();
87931246187SValentin Clement   }
88031246187SValentin Clement };
88131246187SValentin Clement 
88222d332a0SAndrzej Warzynski /// Convert `fir.end`
88322d332a0SAndrzej Warzynski struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> {
88422d332a0SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
88522d332a0SAndrzej Warzynski 
88622d332a0SAndrzej Warzynski   mlir::LogicalResult
88722d332a0SAndrzej Warzynski   matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
88822d332a0SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
8897ce8c6fcSKiran Chandramohan     TODO(firEnd.getLoc(), "fir.end codegen");
8907ce8c6fcSKiran Chandramohan     return failure();
89122d332a0SAndrzej Warzynski   }
89222d332a0SAndrzej Warzynski };
89322d332a0SAndrzej Warzynski 
8940c4a7a52SValentin Clement /// Lower `fir.has_value` operation to `llvm.return` operation.
895044d5b5dSValentin Clement struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
896044d5b5dSValentin Clement   using FIROpConversion::FIROpConversion;
897044d5b5dSValentin Clement 
898044d5b5dSValentin Clement   mlir::LogicalResult
899044d5b5dSValentin Clement   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
900044d5b5dSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
901044d5b5dSValentin Clement     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
902044d5b5dSValentin Clement     return success();
903044d5b5dSValentin Clement   }
904044d5b5dSValentin Clement };
905044d5b5dSValentin Clement 
9060c4a7a52SValentin Clement /// Lower `fir.global` operation to `llvm.global` operation.
9070c4a7a52SValentin Clement /// `fir.insert_on_range` operations are replaced with constant dense attribute
9080c4a7a52SValentin Clement /// if they are applied on the full range.
909044d5b5dSValentin Clement struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
910044d5b5dSValentin Clement   using FIROpConversion::FIROpConversion;
911044d5b5dSValentin Clement 
912044d5b5dSValentin Clement   mlir::LogicalResult
913044d5b5dSValentin Clement   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
914044d5b5dSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
915044d5b5dSValentin Clement     auto tyAttr = convertType(global.getType());
916044d5b5dSValentin Clement     if (global.getType().isa<fir::BoxType>())
917044d5b5dSValentin Clement       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
918044d5b5dSValentin Clement     auto loc = global.getLoc();
919044d5b5dSValentin Clement     mlir::Attribute initAttr{};
920044d5b5dSValentin Clement     if (global.initVal())
921044d5b5dSValentin Clement       initAttr = global.initVal().getValue();
922044d5b5dSValentin Clement     auto linkage = convertLinkage(global.linkName());
923044d5b5dSValentin Clement     auto isConst = global.constant().hasValue();
924044d5b5dSValentin Clement     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
925044d5b5dSValentin Clement         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
926044d5b5dSValentin Clement     auto &gr = g.getInitializerRegion();
927044d5b5dSValentin Clement     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
928044d5b5dSValentin Clement     if (!gr.empty()) {
929044d5b5dSValentin Clement       // Replace insert_on_range with a constant dense attribute if the
930044d5b5dSValentin Clement       // initialization is on the full range.
931044d5b5dSValentin Clement       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
932044d5b5dSValentin Clement       for (auto insertOp : insertOnRangeOps) {
933044d5b5dSValentin Clement         if (isFullRange(insertOp.coor(), insertOp.getType())) {
934044d5b5dSValentin Clement           auto seqTyAttr = convertType(insertOp.getType());
935044d5b5dSValentin Clement           auto *op = insertOp.val().getDefiningOp();
936044d5b5dSValentin Clement           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
937044d5b5dSValentin Clement           if (!constant) {
938044d5b5dSValentin Clement             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
939044d5b5dSValentin Clement             if (!convertOp)
940044d5b5dSValentin Clement               continue;
941044d5b5dSValentin Clement             constant = cast<mlir::arith::ConstantOp>(
942044d5b5dSValentin Clement                 convertOp.value().getDefiningOp());
943044d5b5dSValentin Clement           }
944044d5b5dSValentin Clement           mlir::Type vecType = mlir::VectorType::get(
945044d5b5dSValentin Clement               insertOp.getType().getShape(), constant.getType());
946044d5b5dSValentin Clement           auto denseAttr = mlir::DenseElementsAttr::get(
947044d5b5dSValentin Clement               vecType.cast<ShapedType>(), constant.value());
948044d5b5dSValentin Clement           rewriter.setInsertionPointAfter(insertOp);
949044d5b5dSValentin Clement           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
950044d5b5dSValentin Clement               insertOp, seqTyAttr, denseAttr);
951044d5b5dSValentin Clement         }
952044d5b5dSValentin Clement       }
953044d5b5dSValentin Clement     }
954044d5b5dSValentin Clement     rewriter.eraseOp(global);
955044d5b5dSValentin Clement     return success();
956044d5b5dSValentin Clement   }
957044d5b5dSValentin Clement 
9588ec0f221SMehdi Amini   bool isFullRange(mlir::DenseIntElementsAttr indexes,
9598ec0f221SMehdi Amini                    fir::SequenceType seqTy) const {
960044d5b5dSValentin Clement     auto extents = seqTy.getShape();
9618ec0f221SMehdi Amini     if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
962044d5b5dSValentin Clement       return false;
9638ec0f221SMehdi Amini     auto cur_index = indexes.value_begin<int64_t>();
964044d5b5dSValentin Clement     for (unsigned i = 0; i < indexes.size(); i += 2) {
9658ec0f221SMehdi Amini       if (*(cur_index++) != 0)
966044d5b5dSValentin Clement         return false;
9678ec0f221SMehdi Amini       if (*(cur_index++) != extents[i / 2] - 1)
968044d5b5dSValentin Clement         return false;
969044d5b5dSValentin Clement     }
970044d5b5dSValentin Clement     return true;
971044d5b5dSValentin Clement   }
972044d5b5dSValentin Clement 
9730c4a7a52SValentin Clement   // TODO: String comparaison should be avoided. Replace linkName with an
9740c4a7a52SValentin Clement   // enumeration.
975044d5b5dSValentin Clement   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
976044d5b5dSValentin Clement     if (optLinkage.hasValue()) {
977044d5b5dSValentin Clement       auto name = optLinkage.getValue();
978044d5b5dSValentin Clement       if (name == "internal")
979044d5b5dSValentin Clement         return mlir::LLVM::Linkage::Internal;
980044d5b5dSValentin Clement       if (name == "linkonce")
981044d5b5dSValentin Clement         return mlir::LLVM::Linkage::Linkonce;
982044d5b5dSValentin Clement       if (name == "common")
983044d5b5dSValentin Clement         return mlir::LLVM::Linkage::Common;
984044d5b5dSValentin Clement       if (name == "weak")
985044d5b5dSValentin Clement         return mlir::LLVM::Linkage::Weak;
986044d5b5dSValentin Clement     }
987044d5b5dSValentin Clement     return mlir::LLVM::Linkage::External;
988044d5b5dSValentin Clement   }
989044d5b5dSValentin Clement };
990044d5b5dSValentin Clement 
99139f4ef81SValentin Clement void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
99239f4ef81SValentin Clement                  Optional<mlir::ValueRange> destOps,
99339f4ef81SValentin Clement                  mlir::ConversionPatternRewriter &rewriter,
99439f4ef81SValentin Clement                  mlir::Block *newBlock) {
99539f4ef81SValentin Clement   if (destOps.hasValue())
99639f4ef81SValentin Clement     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(),
99739f4ef81SValentin Clement                                           newBlock, mlir::ValueRange());
99839f4ef81SValentin Clement   else
99939f4ef81SValentin Clement     rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
100039f4ef81SValentin Clement }
100139f4ef81SValentin Clement 
100239f4ef81SValentin Clement template <typename A, typename B>
100339f4ef81SValentin Clement void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps,
100439f4ef81SValentin Clement              mlir::ConversionPatternRewriter &rewriter) {
100539f4ef81SValentin Clement   if (destOps.hasValue())
100639f4ef81SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(),
100739f4ef81SValentin Clement                                                   dest);
100839f4ef81SValentin Clement   else
100939f4ef81SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest);
101039f4ef81SValentin Clement }
101139f4ef81SValentin Clement 
101239f4ef81SValentin Clement void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
101339f4ef81SValentin Clement                        Optional<mlir::ValueRange> destOps,
101439f4ef81SValentin Clement                        mlir::ConversionPatternRewriter &rewriter) {
101539f4ef81SValentin Clement   auto *thisBlock = rewriter.getInsertionBlock();
101639f4ef81SValentin Clement   auto *newBlock = createBlock(rewriter, dest);
101739f4ef81SValentin Clement   rewriter.setInsertionPointToEnd(thisBlock);
101839f4ef81SValentin Clement   genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
101939f4ef81SValentin Clement   rewriter.setInsertionPointToEnd(newBlock);
102039f4ef81SValentin Clement }
102139f4ef81SValentin Clement 
102239f4ef81SValentin Clement /// Conversion of `fir.select_case`
102339f4ef81SValentin Clement ///
102439f4ef81SValentin Clement /// The `fir.select_case` operation is converted to a if-then-else ladder.
102539f4ef81SValentin Clement /// Depending on the case condition type, one or several comparison and
102639f4ef81SValentin Clement /// conditional branching can be generated.
102739f4ef81SValentin Clement ///
102839f4ef81SValentin Clement /// A a point value case such as `case(4)`, a lower bound case such as
102939f4ef81SValentin Clement /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
103039f4ef81SValentin Clement /// simple comparison between the selector value and the constant value in the
103139f4ef81SValentin Clement /// case. The block associated with the case condition is then executed if
103239f4ef81SValentin Clement /// the comparison succeed otherwise it branch to the next block with the
103339f4ef81SValentin Clement /// comparison for the the next case conditon.
103439f4ef81SValentin Clement ///
103539f4ef81SValentin Clement /// A closed interval case condition such as `case(7:10)` is converted with a
103639f4ef81SValentin Clement /// first comparison and conditional branching for the lower bound. If
103739f4ef81SValentin Clement /// successful, it branch to a second block with the comparison for the
103839f4ef81SValentin Clement /// upper bound in the same case condition.
103939f4ef81SValentin Clement ///
104039f4ef81SValentin Clement /// TODO: lowering of CHARACTER type cases is not handled yet.
104139f4ef81SValentin Clement struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> {
104239f4ef81SValentin Clement   using FIROpConversion::FIROpConversion;
104339f4ef81SValentin Clement 
104439f4ef81SValentin Clement   mlir::LogicalResult
104539f4ef81SValentin Clement   matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
104639f4ef81SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
104739f4ef81SValentin Clement     unsigned conds = caseOp.getNumConditions();
104839f4ef81SValentin Clement     llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
104939f4ef81SValentin Clement     // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
10507ce8c6fcSKiran Chandramohan     auto ty = caseOp.getSelector().getType();
10517ce8c6fcSKiran Chandramohan     if (ty.isa<fir::CharacterType>()) {
10527ce8c6fcSKiran Chandramohan       TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
10537ce8c6fcSKiran Chandramohan       return failure();
10547ce8c6fcSKiran Chandramohan     }
105539f4ef81SValentin Clement     mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
105639f4ef81SValentin Clement     auto loc = caseOp.getLoc();
105739f4ef81SValentin Clement     for (unsigned t = 0; t != conds; ++t) {
105839f4ef81SValentin Clement       mlir::Block *dest = caseOp.getSuccessor(t);
105939f4ef81SValentin Clement       llvm::Optional<mlir::ValueRange> destOps =
106039f4ef81SValentin Clement           caseOp.getSuccessorOperands(adaptor.getOperands(), t);
106139f4ef81SValentin Clement       llvm::Optional<mlir::ValueRange> cmpOps =
106239f4ef81SValentin Clement           *caseOp.getCompareOperands(adaptor.getOperands(), t);
106339f4ef81SValentin Clement       mlir::Value caseArg = *(cmpOps.getValue().begin());
106439f4ef81SValentin Clement       mlir::Attribute attr = cases[t];
106539f4ef81SValentin Clement       if (attr.isa<fir::PointIntervalAttr>()) {
106639f4ef81SValentin Clement         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
106739f4ef81SValentin Clement             loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg);
106839f4ef81SValentin Clement         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
106939f4ef81SValentin Clement         continue;
107039f4ef81SValentin Clement       }
107139f4ef81SValentin Clement       if (attr.isa<fir::LowerBoundAttr>()) {
107239f4ef81SValentin Clement         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
107339f4ef81SValentin Clement             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
107439f4ef81SValentin Clement         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
107539f4ef81SValentin Clement         continue;
107639f4ef81SValentin Clement       }
107739f4ef81SValentin Clement       if (attr.isa<fir::UpperBoundAttr>()) {
107839f4ef81SValentin Clement         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
107939f4ef81SValentin Clement             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg);
108039f4ef81SValentin Clement         genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
108139f4ef81SValentin Clement         continue;
108239f4ef81SValentin Clement       }
108339f4ef81SValentin Clement       if (attr.isa<fir::ClosedIntervalAttr>()) {
108439f4ef81SValentin Clement         auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
108539f4ef81SValentin Clement             loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector);
108639f4ef81SValentin Clement         auto *thisBlock = rewriter.getInsertionBlock();
108739f4ef81SValentin Clement         auto *newBlock1 = createBlock(rewriter, dest);
108839f4ef81SValentin Clement         auto *newBlock2 = createBlock(rewriter, dest);
108939f4ef81SValentin Clement         rewriter.setInsertionPointToEnd(thisBlock);
109039f4ef81SValentin Clement         rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2);
109139f4ef81SValentin Clement         rewriter.setInsertionPointToEnd(newBlock1);
109239f4ef81SValentin Clement         mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1);
109339f4ef81SValentin Clement         auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
109439f4ef81SValentin Clement             loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0);
109539f4ef81SValentin Clement         genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2);
109639f4ef81SValentin Clement         rewriter.setInsertionPointToEnd(newBlock2);
109739f4ef81SValentin Clement         continue;
109839f4ef81SValentin Clement       }
109939f4ef81SValentin Clement       assert(attr.isa<mlir::UnitAttr>());
110039f4ef81SValentin Clement       assert((t + 1 == conds) && "unit must be last");
110139f4ef81SValentin Clement       genBrOp(caseOp, dest, destOps, rewriter);
110239f4ef81SValentin Clement     }
110339f4ef81SValentin Clement     return success();
110439f4ef81SValentin Clement   }
110539f4ef81SValentin Clement };
110639f4ef81SValentin Clement 
11078c239909SValentin Clement template <typename OP>
11088c239909SValentin Clement void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
11098c239909SValentin Clement                            typename OP::Adaptor adaptor,
11108c239909SValentin Clement                            mlir::ConversionPatternRewriter &rewriter) {
11118c239909SValentin Clement   unsigned conds = select.getNumConditions();
11128c239909SValentin Clement   auto cases = select.getCases().getValue();
11138c239909SValentin Clement   mlir::Value selector = adaptor.selector();
11148c239909SValentin Clement   auto loc = select.getLoc();
11158c239909SValentin Clement   assert(conds > 0 && "select must have cases");
11168c239909SValentin Clement 
11178c239909SValentin Clement   llvm::SmallVector<mlir::Block *> destinations;
11188c239909SValentin Clement   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
11198c239909SValentin Clement   mlir::Block *defaultDestination;
11208c239909SValentin Clement   mlir::ValueRange defaultOperands;
11218c239909SValentin Clement   llvm::SmallVector<int32_t> caseValues;
11228c239909SValentin Clement 
11238c239909SValentin Clement   for (unsigned t = 0; t != conds; ++t) {
11248c239909SValentin Clement     mlir::Block *dest = select.getSuccessor(t);
11258c239909SValentin Clement     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
11268c239909SValentin Clement     const mlir::Attribute &attr = cases[t];
11278c239909SValentin Clement     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
11288c239909SValentin Clement       destinations.push_back(dest);
11298c239909SValentin Clement       destinationsOperands.push_back(destOps.hasValue() ? *destOps
11308c239909SValentin Clement                                                         : ValueRange());
11318c239909SValentin Clement       caseValues.push_back(intAttr.getInt());
11328c239909SValentin Clement       continue;
11338c239909SValentin Clement     }
11348c239909SValentin Clement     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
11358c239909SValentin Clement     assert((t + 1 == conds) && "unit must be last");
11368c239909SValentin Clement     defaultDestination = dest;
11378c239909SValentin Clement     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
11388c239909SValentin Clement   }
11398c239909SValentin Clement 
11408c239909SValentin Clement   // LLVM::SwitchOp takes a i32 type for the selector.
11418c239909SValentin Clement   if (select.getSelector().getType() != rewriter.getI32Type())
11428c239909SValentin Clement     selector =
11438c239909SValentin Clement         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
11448c239909SValentin Clement 
11458c239909SValentin Clement   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
11468c239909SValentin Clement       select, selector,
11478c239909SValentin Clement       /*defaultDestination=*/defaultDestination,
11488c239909SValentin Clement       /*defaultOperands=*/defaultOperands,
11498c239909SValentin Clement       /*caseValues=*/caseValues,
11508c239909SValentin Clement       /*caseDestinations=*/destinations,
11518c239909SValentin Clement       /*caseOperands=*/destinationsOperands,
11528c239909SValentin Clement       /*branchWeights=*/ArrayRef<int32_t>());
11538c239909SValentin Clement }
11548c239909SValentin Clement 
11558c239909SValentin Clement /// conversion of fir::SelectOp to an if-then-else ladder
11568c239909SValentin Clement struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
11578c239909SValentin Clement   using FIROpConversion::FIROpConversion;
11588c239909SValentin Clement 
11598c239909SValentin Clement   mlir::LogicalResult
11608c239909SValentin Clement   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
11618c239909SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
11628c239909SValentin Clement     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
11638c239909SValentin Clement     return success();
11648c239909SValentin Clement   }
11658c239909SValentin Clement };
11668c239909SValentin Clement 
1167e3349fa1SAndrzej Warzynski /// `fir.load` --> `llvm.load`
1168e3349fa1SAndrzej Warzynski struct LoadOpConversion : public FIROpConversion<fir::LoadOp> {
1169e3349fa1SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
1170e3349fa1SAndrzej Warzynski 
1171e3349fa1SAndrzej Warzynski   mlir::LogicalResult
1172e3349fa1SAndrzej Warzynski   matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
1173e3349fa1SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
1174e3349fa1SAndrzej Warzynski     // fir.box is a special case because it is considered as an ssa values in
1175e3349fa1SAndrzej Warzynski     // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box>
1176e3349fa1SAndrzej Warzynski     // and fir.box end up being the same llvm types and loading a
1177e3349fa1SAndrzej Warzynski     // fir.ref<fir.box> is actually a no op in LLVM.
1178e3349fa1SAndrzej Warzynski     if (load.getType().isa<fir::BoxType>()) {
1179e3349fa1SAndrzej Warzynski       rewriter.replaceOp(load, adaptor.getOperands()[0]);
1180e3349fa1SAndrzej Warzynski     } else {
1181e3349fa1SAndrzej Warzynski       mlir::Type ty = convertType(load.getType());
1182e3349fa1SAndrzej Warzynski       ArrayRef<NamedAttribute> at = load->getAttrs();
1183e3349fa1SAndrzej Warzynski       rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
1184e3349fa1SAndrzej Warzynski           load, ty, adaptor.getOperands(), at);
1185e3349fa1SAndrzej Warzynski     }
1186e3349fa1SAndrzej Warzynski     return success();
1187e3349fa1SAndrzej Warzynski   }
1188e3349fa1SAndrzej Warzynski };
1189e3349fa1SAndrzej Warzynski 
1190b8207db7SValentin Clement /// Lower `fir.no_reassoc` to LLVM IR dialect.
1191b8207db7SValentin Clement /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
1192b8207db7SValentin Clement /// math flags?
1193b8207db7SValentin Clement struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> {
1194b8207db7SValentin Clement   using FIROpConversion::FIROpConversion;
1195b8207db7SValentin Clement 
1196b8207db7SValentin Clement   mlir::LogicalResult
1197b8207db7SValentin Clement   matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
1198b8207db7SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
1199b8207db7SValentin Clement     rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
1200b8207db7SValentin Clement     return success();
1201b8207db7SValentin Clement   }
1202b8207db7SValentin Clement };
1203b8207db7SValentin Clement 
12042a299e4fSValentin Clement /// Lower `fir.select_type` to LLVM IR dialect.
12052a299e4fSValentin Clement struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> {
12062a299e4fSValentin Clement   using FIROpConversion::FIROpConversion;
12072a299e4fSValentin Clement 
12082a299e4fSValentin Clement   mlir::LogicalResult
12092a299e4fSValentin Clement   matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
12102a299e4fSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
12117ce8c6fcSKiran Chandramohan     mlir::emitError(select.getLoc(),
12127ce8c6fcSKiran Chandramohan                     "fir.select_type should have already been converted");
12137ce8c6fcSKiran Chandramohan     return failure();
12142a299e4fSValentin Clement   }
12152a299e4fSValentin Clement };
12162a299e4fSValentin Clement 
12178c239909SValentin Clement /// conversion of fir::SelectRankOp to an if-then-else ladder
12188c239909SValentin Clement struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
12198c239909SValentin Clement   using FIROpConversion::FIROpConversion;
12208c239909SValentin Clement 
12218c239909SValentin Clement   mlir::LogicalResult
12228c239909SValentin Clement   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
12238c239909SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
12248c239909SValentin Clement     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
12258c239909SValentin Clement     return success();
12268c239909SValentin Clement   }
12278c239909SValentin Clement };
12288c239909SValentin Clement 
1229e3349fa1SAndrzej Warzynski /// `fir.store` --> `llvm.store`
1230e3349fa1SAndrzej Warzynski struct StoreOpConversion : public FIROpConversion<fir::StoreOp> {
1231e3349fa1SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
1232e3349fa1SAndrzej Warzynski 
1233e3349fa1SAndrzej Warzynski   mlir::LogicalResult
1234e3349fa1SAndrzej Warzynski   matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
1235e3349fa1SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
1236e3349fa1SAndrzej Warzynski     if (store.value().getType().isa<fir::BoxType>()) {
1237e3349fa1SAndrzej Warzynski       // fir.box value is actually in memory, load it first before storing it.
1238e3349fa1SAndrzej Warzynski       mlir::Location loc = store.getLoc();
1239e3349fa1SAndrzej Warzynski       mlir::Type boxPtrTy = adaptor.getOperands()[0].getType();
1240e3349fa1SAndrzej Warzynski       auto val = rewriter.create<mlir::LLVM::LoadOp>(
1241e3349fa1SAndrzej Warzynski           loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(),
1242e3349fa1SAndrzej Warzynski           adaptor.getOperands()[0]);
1243e3349fa1SAndrzej Warzynski       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1244e3349fa1SAndrzej Warzynski           store, val, adaptor.getOperands()[1]);
1245e3349fa1SAndrzej Warzynski     } else {
1246e3349fa1SAndrzej Warzynski       rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
1247e3349fa1SAndrzej Warzynski           store, adaptor.getOperands()[0], adaptor.getOperands()[1]);
1248e3349fa1SAndrzej Warzynski     }
1249e3349fa1SAndrzej Warzynski     return success();
1250e3349fa1SAndrzej Warzynski   }
1251e3349fa1SAndrzej Warzynski };
1252e3349fa1SAndrzej Warzynski 
1253e3349fa1SAndrzej Warzynski /// convert to LLVM IR dialect `undef`
1254044d5b5dSValentin Clement struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
1255044d5b5dSValentin Clement   using FIROpConversion::FIROpConversion;
1256044d5b5dSValentin Clement 
1257044d5b5dSValentin Clement   mlir::LogicalResult
1258044d5b5dSValentin Clement   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
1259044d5b5dSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
1260044d5b5dSValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1261044d5b5dSValentin Clement         undef, convertType(undef.getType()));
1262044d5b5dSValentin Clement     return success();
1263044d5b5dSValentin Clement   }
1264044d5b5dSValentin Clement };
1265a7a61359SValentin Clement 
1266e3349fa1SAndrzej Warzynski /// `fir.unreachable` --> `llvm.unreachable`
126732e08248SAndrzej Warzynski struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
126832e08248SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
126932e08248SAndrzej Warzynski 
127032e08248SAndrzej Warzynski   mlir::LogicalResult
127132e08248SAndrzej Warzynski   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
127232e08248SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
127332e08248SAndrzej Warzynski     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
127432e08248SAndrzej Warzynski     return success();
127532e08248SAndrzej Warzynski   }
127632e08248SAndrzej Warzynski };
127732e08248SAndrzej Warzynski 
1278a7a61359SValentin Clement struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
1279a7a61359SValentin Clement   using FIROpConversion::FIROpConversion;
1280a7a61359SValentin Clement 
1281a7a61359SValentin Clement   mlir::LogicalResult
1282a7a61359SValentin Clement   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
1283a7a61359SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
12847ce8c6fcSKiran Chandramohan     mlir::Type ty = convertType(zero.getType());
1285a7a61359SValentin Clement     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
1286a7a61359SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
1287a7a61359SValentin Clement     } else if (ty.isa<mlir::IntegerType>()) {
1288a7a61359SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1289a7a61359SValentin Clement           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
1290a7a61359SValentin Clement     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
1291a7a61359SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
1292a7a61359SValentin Clement           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
1293a7a61359SValentin Clement     } else {
1294a7a61359SValentin Clement       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
129552d813edSValentin Clement       return rewriter.notifyMatchFailure(
129652d813edSValentin Clement           zero,
1297a7a61359SValentin Clement           "conversion of fir.zero with aggregate type not implemented yet");
1298a7a61359SValentin Clement     }
1299a7a61359SValentin Clement     return success();
1300a7a61359SValentin Clement   }
1301a7a61359SValentin Clement };
130232e08248SAndrzej Warzynski 
1303af6ee580SValentin Clement /// Common base class for embox to descriptor conversion.
1304af6ee580SValentin Clement template <typename OP>
1305af6ee580SValentin Clement struct EmboxCommonConversion : public FIROpConversion<OP> {
1306af6ee580SValentin Clement   using FIROpConversion<OP>::FIROpConversion;
1307af6ee580SValentin Clement 
1308af6ee580SValentin Clement   // Find the LLVMFuncOp in whose entry block the alloca should be inserted.
1309af6ee580SValentin Clement   // The order to find the LLVMFuncOp is as follows:
1310af6ee580SValentin Clement   // 1. The parent operation of the current block if it is a LLVMFuncOp.
1311af6ee580SValentin Clement   // 2. The first ancestor that is a LLVMFuncOp.
1312af6ee580SValentin Clement   mlir::LLVM::LLVMFuncOp
1313af6ee580SValentin Clement   getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const {
1314af6ee580SValentin Clement     mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
1315af6ee580SValentin Clement     return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp)
1316af6ee580SValentin Clement                ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
1317af6ee580SValentin Clement                : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
1318af6ee580SValentin Clement   }
1319af6ee580SValentin Clement 
1320af6ee580SValentin Clement   // Generate an alloca of size 1 and type \p toTy.
1321af6ee580SValentin Clement   mlir::LLVM::AllocaOp
1322af6ee580SValentin Clement   genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment,
1323af6ee580SValentin Clement                     mlir::ConversionPatternRewriter &rewriter) const {
1324af6ee580SValentin Clement     auto thisPt = rewriter.saveInsertionPoint();
1325af6ee580SValentin Clement     mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter);
1326af6ee580SValentin Clement     rewriter.setInsertionPointToStart(&func.front());
1327af6ee580SValentin Clement     auto size = this->genI32Constant(loc, rewriter, 1);
1328af6ee580SValentin Clement     auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
1329af6ee580SValentin Clement     rewriter.restoreInsertionPoint(thisPt);
1330af6ee580SValentin Clement     return al;
1331af6ee580SValentin Clement   }
1332af6ee580SValentin Clement 
1333af6ee580SValentin Clement   static int getCFIAttr(fir::BoxType boxTy) {
1334af6ee580SValentin Clement     auto eleTy = boxTy.getEleTy();
1335af6ee580SValentin Clement     if (eleTy.isa<fir::PointerType>())
1336af6ee580SValentin Clement       return CFI_attribute_pointer;
1337af6ee580SValentin Clement     if (eleTy.isa<fir::HeapType>())
1338af6ee580SValentin Clement       return CFI_attribute_allocatable;
1339af6ee580SValentin Clement     return CFI_attribute_other;
1340af6ee580SValentin Clement   }
1341af6ee580SValentin Clement 
1342af6ee580SValentin Clement   static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) {
1343af6ee580SValentin Clement     return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy))
1344af6ee580SValentin Clement         .template dyn_cast<fir::RecordType>();
1345af6ee580SValentin Clement   }
1346af6ee580SValentin Clement   static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) {
1347af6ee580SValentin Clement     auto recTy = unwrapIfDerived(boxTy);
1348af6ee580SValentin Clement     return recTy && recTy.getNumLenParams() > 0;
1349af6ee580SValentin Clement   }
1350af6ee580SValentin Clement   static bool isDerivedType(fir::BoxType boxTy) {
1351af6ee580SValentin Clement     return unwrapIfDerived(boxTy) != nullptr;
1352af6ee580SValentin Clement   }
1353af6ee580SValentin Clement 
1354af6ee580SValentin Clement   // Get the element size and CFI type code of the boxed value.
1355af6ee580SValentin Clement   std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1356af6ee580SValentin Clement       mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1357af6ee580SValentin Clement       mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1358af6ee580SValentin Clement     auto doInteger =
1359af6ee580SValentin Clement         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1360af6ee580SValentin Clement       int typeCode = fir::integerBitsToTypeCode(width);
1361af6ee580SValentin Clement       return {this->genConstantOffset(loc, rewriter, width / 8),
1362af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, typeCode)};
1363af6ee580SValentin Clement     };
1364af6ee580SValentin Clement     auto doLogical =
1365af6ee580SValentin Clement         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1366af6ee580SValentin Clement       int typeCode = fir::logicalBitsToTypeCode(width);
1367af6ee580SValentin Clement       return {this->genConstantOffset(loc, rewriter, width / 8),
1368af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, typeCode)};
1369af6ee580SValentin Clement     };
1370af6ee580SValentin Clement     auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1371af6ee580SValentin Clement       int typeCode = fir::realBitsToTypeCode(width);
1372af6ee580SValentin Clement       return {this->genConstantOffset(loc, rewriter, width / 8),
1373af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, typeCode)};
1374af6ee580SValentin Clement     };
1375af6ee580SValentin Clement     auto doComplex =
1376af6ee580SValentin Clement         [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
1377af6ee580SValentin Clement       auto typeCode = fir::complexBitsToTypeCode(width);
1378af6ee580SValentin Clement       return {this->genConstantOffset(loc, rewriter, width / 8 * 2),
1379af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, typeCode)};
1380af6ee580SValentin Clement     };
1381af6ee580SValentin Clement     auto doCharacter =
1382af6ee580SValentin Clement         [&](unsigned width,
1383af6ee580SValentin Clement             mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> {
1384af6ee580SValentin Clement       auto typeCode = fir::characterBitsToTypeCode(width);
1385af6ee580SValentin Clement       auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode);
1386af6ee580SValentin Clement       if (width == 8)
1387af6ee580SValentin Clement         return {len, typeCodeVal};
1388af6ee580SValentin Clement       auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8);
1389af6ee580SValentin Clement       auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64);
1390af6ee580SValentin Clement       auto size =
1391af6ee580SValentin Clement           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len);
1392af6ee580SValentin Clement       return {size, typeCodeVal};
1393af6ee580SValentin Clement     };
1394af6ee580SValentin Clement     auto getKindMap = [&]() -> fir::KindMapping & {
1395af6ee580SValentin Clement       return this->lowerTy().getKindMap();
1396af6ee580SValentin Clement     };
1397af6ee580SValentin Clement     // Pointer-like types.
1398af6ee580SValentin Clement     if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1399af6ee580SValentin Clement       boxEleTy = eleTy;
1400af6ee580SValentin Clement     // Integer types.
1401af6ee580SValentin Clement     if (fir::isa_integer(boxEleTy)) {
1402af6ee580SValentin Clement       if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>())
1403af6ee580SValentin Clement         return doInteger(ty.getWidth());
1404af6ee580SValentin Clement       auto ty = boxEleTy.cast<fir::IntegerType>();
1405af6ee580SValentin Clement       return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
1406af6ee580SValentin Clement     }
1407af6ee580SValentin Clement     // Floating point types.
1408af6ee580SValentin Clement     if (fir::isa_real(boxEleTy)) {
1409af6ee580SValentin Clement       if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
1410af6ee580SValentin Clement         return doFloat(ty.getWidth());
1411af6ee580SValentin Clement       auto ty = boxEleTy.cast<fir::RealType>();
1412af6ee580SValentin Clement       return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
1413af6ee580SValentin Clement     }
1414af6ee580SValentin Clement     // Complex types.
1415af6ee580SValentin Clement     if (fir::isa_complex(boxEleTy)) {
1416af6ee580SValentin Clement       if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
1417af6ee580SValentin Clement         return doComplex(
1418af6ee580SValentin Clement             ty.getElementType().cast<mlir::FloatType>().getWidth());
1419af6ee580SValentin Clement       auto ty = boxEleTy.cast<fir::ComplexType>();
1420af6ee580SValentin Clement       return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
1421af6ee580SValentin Clement     }
1422af6ee580SValentin Clement     // Character types.
1423af6ee580SValentin Clement     if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
1424af6ee580SValentin Clement       auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind());
1425af6ee580SValentin Clement       if (ty.getLen() != fir::CharacterType::unknownLen()) {
1426af6ee580SValentin Clement         auto len = this->genConstantOffset(loc, rewriter, ty.getLen());
1427af6ee580SValentin Clement         return doCharacter(charWidth, len);
1428af6ee580SValentin Clement       }
1429af6ee580SValentin Clement       assert(!lenParams.empty());
1430af6ee580SValentin Clement       return doCharacter(charWidth, lenParams.back());
1431af6ee580SValentin Clement     }
1432af6ee580SValentin Clement     // Logical type.
1433af6ee580SValentin Clement     if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>())
1434af6ee580SValentin Clement       return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
1435af6ee580SValentin Clement     // Array types.
1436af6ee580SValentin Clement     if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
1437af6ee580SValentin Clement       return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1438af6ee580SValentin Clement     // Derived-type types.
1439af6ee580SValentin Clement     if (boxEleTy.isa<fir::RecordType>()) {
1440af6ee580SValentin Clement       auto ptrTy = mlir::LLVM::LLVMPointerType::get(
1441af6ee580SValentin Clement           this->lowerTy().convertType(boxEleTy));
1442af6ee580SValentin Clement       auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
1443af6ee580SValentin Clement       auto one =
1444af6ee580SValentin Clement           genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
1445af6ee580SValentin Clement       auto gep = rewriter.create<mlir::LLVM::GEPOp>(
1446af6ee580SValentin Clement           loc, ptrTy, mlir::ValueRange{nullPtr, one});
1447af6ee580SValentin Clement       auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
1448af6ee580SValentin Clement           loc, this->lowerTy().indexType(), gep);
1449af6ee580SValentin Clement       return {eleSize,
1450af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())};
1451af6ee580SValentin Clement     }
1452af6ee580SValentin Clement     // Reference type.
1453af6ee580SValentin Clement     if (fir::isa_ref_type(boxEleTy)) {
1454af6ee580SValentin Clement       // FIXME: use the target pointer size rather than sizeof(void*)
1455af6ee580SValentin Clement       return {this->genConstantOffset(loc, rewriter, sizeof(void *)),
1456af6ee580SValentin Clement               this->genConstantOffset(loc, rewriter, CFI_type_cptr)};
1457af6ee580SValentin Clement     }
1458af6ee580SValentin Clement     fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1459af6ee580SValentin Clement   }
1460af6ee580SValentin Clement 
1461af6ee580SValentin Clement   /// Basic pattern to write a field in the descriptor
1462af6ee580SValentin Clement   mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1463af6ee580SValentin Clement                           mlir::Location loc, mlir::Value dest,
1464af6ee580SValentin Clement                           ArrayRef<unsigned> fldIndexes, mlir::Value value,
1465af6ee580SValentin Clement                           bool bitcast = false) const {
1466af6ee580SValentin Clement     auto boxTy = dest.getType();
1467af6ee580SValentin Clement     auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1468af6ee580SValentin Clement     if (bitcast)
1469af6ee580SValentin Clement       value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
1470af6ee580SValentin Clement     else
1471af6ee580SValentin Clement       value = this->integerCast(loc, rewriter, fldTy, value);
1472af6ee580SValentin Clement     SmallVector<mlir::Attribute, 2> attrs;
1473af6ee580SValentin Clement     for (auto i : fldIndexes)
1474af6ee580SValentin Clement       attrs.push_back(rewriter.getI32IntegerAttr(i));
1475af6ee580SValentin Clement     auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
1476af6ee580SValentin Clement     return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value,
1477af6ee580SValentin Clement                                                       indexesAttr);
1478af6ee580SValentin Clement   }
1479af6ee580SValentin Clement 
1480af6ee580SValentin Clement   inline mlir::Value
1481af6ee580SValentin Clement   insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1482af6ee580SValentin Clement                     mlir::Location loc, mlir::Value dest,
1483af6ee580SValentin Clement                     mlir::Value base) const {
14841f551032SValentin Clement     return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
14851f551032SValentin Clement                        /*bitCast=*/true);
14861f551032SValentin Clement   }
14871f551032SValentin Clement 
14881f551032SValentin Clement   inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
14891f551032SValentin Clement                                       mlir::Location loc, mlir::Value dest,
14901f551032SValentin Clement                                       unsigned dim, mlir::Value lb) const {
14911f551032SValentin Clement     return insertField(rewriter, loc, dest,
14921f551032SValentin Clement                        {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
14931f551032SValentin Clement   }
14941f551032SValentin Clement 
14951f551032SValentin Clement   inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
14961f551032SValentin Clement                                   mlir::Location loc, mlir::Value dest,
14971f551032SValentin Clement                                   unsigned dim, mlir::Value extent) const {
14981f551032SValentin Clement     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
14991f551032SValentin Clement                        extent);
15001f551032SValentin Clement   }
15011f551032SValentin Clement 
15021f551032SValentin Clement   inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
15031f551032SValentin Clement                                   mlir::Location loc, mlir::Value dest,
15041f551032SValentin Clement                                   unsigned dim, mlir::Value stride) const {
15051f551032SValentin Clement     return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
15061f551032SValentin Clement                        stride);
1507af6ee580SValentin Clement   }
1508af6ee580SValentin Clement 
1509af6ee580SValentin Clement   /// Get the address of the type descriptor global variable that was created by
1510af6ee580SValentin Clement   /// lowering for derived type \p recType.
1511af6ee580SValentin Clement   template <typename BOX>
1512af6ee580SValentin Clement   mlir::Value
1513af6ee580SValentin Clement   getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter,
1514af6ee580SValentin Clement                     mlir::Location loc, fir::RecordType recType) const {
1515af6ee580SValentin Clement     std::string name = recType.getLoweredName();
1516af6ee580SValentin Clement     auto module = box->template getParentOfType<mlir::ModuleOp>();
1517af6ee580SValentin Clement     if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
1518af6ee580SValentin Clement       auto ty = mlir::LLVM::LLVMPointerType::get(
1519af6ee580SValentin Clement           this->lowerTy().convertType(global.getType()));
1520af6ee580SValentin Clement       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1521af6ee580SValentin Clement                                                       global.sym_name());
1522af6ee580SValentin Clement     }
1523af6ee580SValentin Clement     if (auto global =
1524af6ee580SValentin Clement             module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1525af6ee580SValentin Clement       // The global may have already been translated to LLVM.
1526af6ee580SValentin Clement       auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
1527af6ee580SValentin Clement       return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
1528af6ee580SValentin Clement                                                       global.sym_name());
1529af6ee580SValentin Clement     }
1530af6ee580SValentin Clement     // The global does not exist in the current translation unit, but may be
1531af6ee580SValentin Clement     // defined elsewhere (e.g., type defined in a module).
1532af6ee580SValentin Clement     // For now, create a extern_weak symbol (will become nullptr if unresolved)
1533af6ee580SValentin Clement     // to support generating code without the front-end generated symbols.
1534af6ee580SValentin Clement     // These could be made available_externally to require the symbols to be
1535af6ee580SValentin Clement     // defined elsewhere and to cause link-time failure otherwise.
1536af6ee580SValentin Clement     auto i8Ty = rewriter.getIntegerType(8);
1537af6ee580SValentin Clement     mlir::OpBuilder modBuilder(module.getBodyRegion());
1538af6ee580SValentin Clement     // TODO: The symbol should be lowered to constant in lowering, they are read
1539af6ee580SValentin Clement     // only.
1540af6ee580SValentin Clement     modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
1541af6ee580SValentin Clement                                             mlir::LLVM::Linkage::ExternWeak,
1542af6ee580SValentin Clement                                             name, mlir::Attribute{});
1543af6ee580SValentin Clement     auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
1544af6ee580SValentin Clement     return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
1545af6ee580SValentin Clement   }
1546af6ee580SValentin Clement 
1547af6ee580SValentin Clement   template <typename BOX>
1548af6ee580SValentin Clement   std::tuple<fir::BoxType, mlir::Value, mlir::Value>
1549af6ee580SValentin Clement   consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
1550af6ee580SValentin Clement                        unsigned rank, mlir::ValueRange lenParams) const {
1551af6ee580SValentin Clement     auto loc = box.getLoc();
1552af6ee580SValentin Clement     auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
1553af6ee580SValentin Clement     auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
1554af6ee580SValentin Clement     auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
1555af6ee580SValentin Clement     auto llvmBoxTy = llvmBoxPtrTy.getElementType();
1556af6ee580SValentin Clement     mlir::Value descriptor =
1557af6ee580SValentin Clement         rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1558af6ee580SValentin Clement 
1559af6ee580SValentin Clement     llvm::SmallVector<mlir::Value> typeparams = lenParams;
1560af6ee580SValentin Clement     if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1561af6ee580SValentin Clement       if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1562af6ee580SValentin Clement         typeparams.push_back(box.substr()[1]);
1563af6ee580SValentin Clement     }
1564af6ee580SValentin Clement 
1565af6ee580SValentin Clement     // Write each of the fields with the appropriate values
1566af6ee580SValentin Clement     auto [eleSize, cfiTy] =
1567af6ee580SValentin Clement         getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1568af6ee580SValentin Clement     descriptor =
1569af6ee580SValentin Clement         insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1570af6ee580SValentin Clement     descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1571af6ee580SValentin Clement                              this->genI32Constant(loc, rewriter, CFI_VERSION));
1572af6ee580SValentin Clement     descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1573af6ee580SValentin Clement                              this->genI32Constant(loc, rewriter, rank));
1574af6ee580SValentin Clement     descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1575af6ee580SValentin Clement     descriptor =
1576af6ee580SValentin Clement         insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1577af6ee580SValentin Clement                     this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1578af6ee580SValentin Clement     const bool hasAddendum = isDerivedType(boxTy);
1579af6ee580SValentin Clement     descriptor =
1580af6ee580SValentin Clement         insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox},
1581af6ee580SValentin Clement                     this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0));
1582af6ee580SValentin Clement 
1583af6ee580SValentin Clement     if (hasAddendum) {
1584af6ee580SValentin Clement       auto isArray =
1585af6ee580SValentin Clement           fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>();
1586af6ee580SValentin Clement       unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
1587af6ee580SValentin Clement       auto typeDesc =
1588af6ee580SValentin Clement           getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy));
1589af6ee580SValentin Clement       descriptor =
1590af6ee580SValentin Clement           insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1591af6ee580SValentin Clement                       /*bitCast=*/true);
1592af6ee580SValentin Clement     }
1593af6ee580SValentin Clement 
1594af6ee580SValentin Clement     return {boxTy, descriptor, eleSize};
1595af6ee580SValentin Clement   }
1596af6ee580SValentin Clement 
15971f551032SValentin Clement   /// Compute the base address of a substring given the base address of a scalar
15981f551032SValentin Clement   /// string and the zero based string lower bound.
15991f551032SValentin Clement   mlir::Value shiftSubstringBase(mlir::ConversionPatternRewriter &rewriter,
16001f551032SValentin Clement                                  mlir::Location loc, mlir::Value base,
16011f551032SValentin Clement                                  mlir::Value lowerBound) const {
16021f551032SValentin Clement     llvm::SmallVector<mlir::Value> gepOperands;
16031f551032SValentin Clement     auto baseType =
16041f551032SValentin Clement         base.getType().cast<mlir::LLVM::LLVMPointerType>().getElementType();
16051f551032SValentin Clement     if (baseType.isa<mlir::LLVM::LLVMArrayType>()) {
16061f551032SValentin Clement       auto idxTy = this->lowerTy().indexType();
16071f551032SValentin Clement       mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
16081f551032SValentin Clement       gepOperands.push_back(zero);
16091f551032SValentin Clement     }
16101f551032SValentin Clement     gepOperands.push_back(lowerBound);
16111f551032SValentin Clement     return this->genGEP(loc, base.getType(), rewriter, base, gepOperands);
16121f551032SValentin Clement   }
16131f551032SValentin Clement 
1614af6ee580SValentin Clement   /// If the embox is not in a globalOp body, allocate storage for the box;
1615af6ee580SValentin Clement   /// store the value inside and return the generated alloca. Return the input
1616af6ee580SValentin Clement   /// value otherwise.
1617af6ee580SValentin Clement   mlir::Value
1618af6ee580SValentin Clement   placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1619af6ee580SValentin Clement                                mlir::Location loc, mlir::Value boxValue) const {
1620af6ee580SValentin Clement     auto *thisBlock = rewriter.getInsertionBlock();
1621af6ee580SValentin Clement     if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
1622af6ee580SValentin Clement       return boxValue;
1623af6ee580SValentin Clement     auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
1624af6ee580SValentin Clement     auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
1625af6ee580SValentin Clement     rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1626af6ee580SValentin Clement     return alloca;
1627af6ee580SValentin Clement   }
1628af6ee580SValentin Clement };
1629af6ee580SValentin Clement 
16301f551032SValentin Clement /// Compute the extent of a triplet slice (lb:ub:step).
16311f551032SValentin Clement static mlir::Value
16321f551032SValentin Clement computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
16331f551032SValentin Clement                      mlir::Location loc, mlir::Value lb, mlir::Value ub,
16341f551032SValentin Clement                      mlir::Value step, mlir::Value zero, mlir::Type type) {
16351f551032SValentin Clement   mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
16361f551032SValentin Clement   extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
16371f551032SValentin Clement   extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
16381f551032SValentin Clement   // If the resulting extent is negative (`ub-lb` and `step` have different
16391f551032SValentin Clement   // signs), zero must be returned instead.
16401f551032SValentin Clement   auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
16411f551032SValentin Clement       loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
16421f551032SValentin Clement   return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
16431f551032SValentin Clement }
16441f551032SValentin Clement 
16451f551032SValentin Clement /// Helper function for generating the LLVM IR that computes the size
16461f551032SValentin Clement /// in bytes for a derived type.
16471f551032SValentin Clement static mlir::Value
16481f551032SValentin Clement computeDerivedTypeSize(mlir::Location loc, mlir::Type ptrTy, mlir::Type idxTy,
16491f551032SValentin Clement                        mlir::ConversionPatternRewriter &rewriter) {
16501f551032SValentin Clement   auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
16511f551032SValentin Clement   mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
16521f551032SValentin Clement   llvm::SmallVector<mlir::Value> args{nullPtr, one};
16531f551032SValentin Clement   auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, args);
16541f551032SValentin Clement   return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
16551f551032SValentin Clement }
16561f551032SValentin Clement 
1657af6ee580SValentin Clement /// Create a generic box on a memory reference. This conversions lowers the
1658af6ee580SValentin Clement /// abstract box to the appropriate, initialized descriptor.
1659af6ee580SValentin Clement struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1660af6ee580SValentin Clement   using EmboxCommonConversion::EmboxCommonConversion;
1661af6ee580SValentin Clement 
1662af6ee580SValentin Clement   mlir::LogicalResult
1663af6ee580SValentin Clement   matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1664af6ee580SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
1665af6ee580SValentin Clement     assert(!embox.getShape() && "There should be no dims on this embox op");
1666af6ee580SValentin Clement     auto [boxTy, dest, eleSize] =
1667af6ee580SValentin Clement         consDescriptorPrefix(embox, rewriter, /*rank=*/0,
1668af6ee580SValentin Clement                              /*lenParams=*/adaptor.getOperands().drop_front(1));
1669af6ee580SValentin Clement     dest = insertBaseAddress(rewriter, embox.getLoc(), dest,
1670af6ee580SValentin Clement                              adaptor.getOperands()[0]);
16717ce8c6fcSKiran Chandramohan     if (isDerivedTypeWithLenParams(boxTy)) {
16727ce8c6fcSKiran Chandramohan       TODO(embox.getLoc(),
16737ce8c6fcSKiran Chandramohan            "fir.embox codegen of derived with length parameters");
16747ce8c6fcSKiran Chandramohan       return failure();
16757ce8c6fcSKiran Chandramohan     }
1676af6ee580SValentin Clement     auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest);
1677af6ee580SValentin Clement     rewriter.replaceOp(embox, result);
1678af6ee580SValentin Clement     return success();
1679af6ee580SValentin Clement   }
1680af6ee580SValentin Clement };
1681af6ee580SValentin Clement 
1682cc505c0bSKiran Chandramohan /// Lower `fir.emboxproc` operation. Creates a procedure box.
1683cc505c0bSKiran Chandramohan /// TODO: Part of supporting Fortran 2003 procedure pointers.
1684cc505c0bSKiran Chandramohan struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> {
1685cc505c0bSKiran Chandramohan   using FIROpConversion::FIROpConversion;
1686cc505c0bSKiran Chandramohan 
1687cc505c0bSKiran Chandramohan   mlir::LogicalResult
1688cc505c0bSKiran Chandramohan   matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
1689cc505c0bSKiran Chandramohan                   mlir::ConversionPatternRewriter &rewriter) const override {
16907ce8c6fcSKiran Chandramohan     TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
16917ce8c6fcSKiran Chandramohan     return failure();
1692cc505c0bSKiran Chandramohan   }
1693cc505c0bSKiran Chandramohan };
1694cc505c0bSKiran Chandramohan 
16951f551032SValentin Clement /// Create a generic box on a memory reference.
16961f551032SValentin Clement struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
16971f551032SValentin Clement   using EmboxCommonConversion::EmboxCommonConversion;
16981f551032SValentin Clement 
16991f551032SValentin Clement   mlir::LogicalResult
17001f551032SValentin Clement   matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
17011f551032SValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
17021f551032SValentin Clement     auto [boxTy, dest, eleSize] = consDescriptorPrefix(
17031f551032SValentin Clement         xbox, rewriter, xbox.getOutRank(),
17041f551032SValentin Clement         adaptor.getOperands().drop_front(xbox.lenParamOffset()));
17051f551032SValentin Clement     // Generate the triples in the dims field of the descriptor
17061f551032SValentin Clement     mlir::ValueRange operands = adaptor.getOperands();
17071f551032SValentin Clement     auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
17081f551032SValentin Clement     mlir::Value base = operands[0];
17091f551032SValentin Clement     assert(!xbox.shape().empty() && "must have a shape");
17101f551032SValentin Clement     unsigned shapeOffset = xbox.shapeOffset();
17111f551032SValentin Clement     bool hasShift = !xbox.shift().empty();
17121f551032SValentin Clement     unsigned shiftOffset = xbox.shiftOffset();
17131f551032SValentin Clement     bool hasSlice = !xbox.slice().empty();
17141f551032SValentin Clement     unsigned sliceOffset = xbox.sliceOffset();
17151f551032SValentin Clement     mlir::Location loc = xbox.getLoc();
17161f551032SValentin Clement     mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
17171f551032SValentin Clement     mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
17181f551032SValentin Clement     mlir::Value prevDim = integerCast(loc, rewriter, i64Ty, eleSize);
17191f551032SValentin Clement     mlir::Value prevPtrOff = one;
17201f551032SValentin Clement     mlir::Type eleTy = boxTy.getEleTy();
17211f551032SValentin Clement     const unsigned rank = xbox.getRank();
17221f551032SValentin Clement     llvm::SmallVector<mlir::Value> gepArgs;
17231f551032SValentin Clement     unsigned constRows = 0;
17241f551032SValentin Clement     mlir::Value ptrOffset = zero;
17251f551032SValentin Clement     if (auto memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType()))
17261f551032SValentin Clement       if (auto seqTy = memEleTy.dyn_cast<fir::SequenceType>()) {
17271f551032SValentin Clement         mlir::Type seqEleTy = seqTy.getEleTy();
17281f551032SValentin Clement         // Adjust the element scaling factor if the element is a dependent type.
17291f551032SValentin Clement         if (fir::hasDynamicSize(seqEleTy)) {
17301f551032SValentin Clement           if (fir::isa_char(seqEleTy)) {
17311f551032SValentin Clement             assert(xbox.lenParams().size() == 1);
17321f551032SValentin Clement             prevPtrOff = integerCast(loc, rewriter, i64Ty,
17331f551032SValentin Clement                                      operands[xbox.lenParamOffset()]);
17341f551032SValentin Clement           } else if (seqEleTy.isa<fir::RecordType>()) {
17351f551032SValentin Clement             TODO(loc, "generate call to calculate size of PDT");
17361f551032SValentin Clement           } else {
17371f551032SValentin Clement             return rewriter.notifyMatchFailure(xbox, "unexpected dynamic type");
17381f551032SValentin Clement           }
17391f551032SValentin Clement         } else {
17401f551032SValentin Clement           constRows = seqTy.getConstantRows();
17411f551032SValentin Clement         }
17421f551032SValentin Clement       }
17431f551032SValentin Clement 
17441f551032SValentin Clement     bool hasSubcomp = !xbox.subcomponent().empty();
17451f551032SValentin Clement     mlir::Value stepExpr;
17461f551032SValentin Clement     if (hasSubcomp) {
17471f551032SValentin Clement       // We have a subcomponent. The step value needs to be the number of
17481f551032SValentin Clement       // bytes per element (which is a derived type).
17491f551032SValentin Clement       mlir::Type ty0 = base.getType();
17501f551032SValentin Clement       [[maybe_unused]] auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
17511f551032SValentin Clement       assert(ptrTy && "expected pointer type");
17521f551032SValentin Clement       mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType());
17531f551032SValentin Clement       assert(memEleTy && "expected fir pointer type");
17541f551032SValentin Clement       auto seqTy = memEleTy.dyn_cast<fir::SequenceType>();
17551f551032SValentin Clement       assert(seqTy && "expected sequence type");
17561f551032SValentin Clement       mlir::Type seqEleTy = seqTy.getEleTy();
17571f551032SValentin Clement       auto eleTy = mlir::LLVM::LLVMPointerType::get(convertType(seqEleTy));
17581f551032SValentin Clement       stepExpr = computeDerivedTypeSize(loc, eleTy, i64Ty, rewriter);
17591f551032SValentin Clement     }
17601f551032SValentin Clement 
17611f551032SValentin Clement     // Process the array subspace arguments (shape, shift, etc.), if any,
17621f551032SValentin Clement     // translating everything to values in the descriptor wherever the entity
17631f551032SValentin Clement     // has a dynamic array dimension.
17641f551032SValentin Clement     for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
17651f551032SValentin Clement       mlir::Value extent = operands[shapeOffset];
17661f551032SValentin Clement       mlir::Value outerExtent = extent;
17671f551032SValentin Clement       bool skipNext = false;
17681f551032SValentin Clement       if (hasSlice) {
17691f551032SValentin Clement         mlir::Value off = operands[sliceOffset];
17701f551032SValentin Clement         mlir::Value adj = one;
17711f551032SValentin Clement         if (hasShift)
17721f551032SValentin Clement           adj = operands[shiftOffset];
17731f551032SValentin Clement         auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
17741f551032SValentin Clement         if (constRows > 0) {
17751f551032SValentin Clement           gepArgs.push_back(ao);
17761f551032SValentin Clement           --constRows;
17771f551032SValentin Clement         } else {
17781f551032SValentin Clement           auto dimOff =
17791f551032SValentin Clement               rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
17801f551032SValentin Clement           ptrOffset =
17811f551032SValentin Clement               rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
17821f551032SValentin Clement         }
17831f551032SValentin Clement         if (mlir::isa_and_nonnull<fir::UndefOp>(
17841f551032SValentin Clement                 xbox.slice()[3 * di + 1].getDefiningOp())) {
17851f551032SValentin Clement           // This dimension contains a scalar expression in the array slice op.
17861f551032SValentin Clement           // The dimension is loop invariant, will be dropped, and will not
17871f551032SValentin Clement           // appear in the descriptor.
17881f551032SValentin Clement           skipNext = true;
17891f551032SValentin Clement         }
17901f551032SValentin Clement       }
17911f551032SValentin Clement       if (!skipNext) {
17921f551032SValentin Clement         // store lower bound (normally 0)
17931f551032SValentin Clement         mlir::Value lb = zero;
17941f551032SValentin Clement         if (eleTy.isa<fir::PointerType>() || eleTy.isa<fir::HeapType>()) {
17951f551032SValentin Clement           lb = one;
17961f551032SValentin Clement           if (hasShift)
17971f551032SValentin Clement             lb = operands[shiftOffset];
17981f551032SValentin Clement         }
17991f551032SValentin Clement         dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
18001f551032SValentin Clement 
18011f551032SValentin Clement         // store extent
18021f551032SValentin Clement         if (hasSlice)
18031f551032SValentin Clement           extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
18041f551032SValentin Clement                                         operands[sliceOffset + 1],
18051f551032SValentin Clement                                         operands[sliceOffset + 2], zero, i64Ty);
18061f551032SValentin Clement         dest = insertExtent(rewriter, loc, dest, descIdx, extent);
18071f551032SValentin Clement 
18081f551032SValentin Clement         // store step (scaled by shaped extent)
18091f551032SValentin Clement 
18101f551032SValentin Clement         mlir::Value step = hasSubcomp ? stepExpr : prevDim;
18111f551032SValentin Clement         if (hasSlice)
18121f551032SValentin Clement           step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step,
18131f551032SValentin Clement                                                     operands[sliceOffset + 2]);
18141f551032SValentin Clement         dest = insertStride(rewriter, loc, dest, descIdx, step);
18151f551032SValentin Clement         ++descIdx;
18161f551032SValentin Clement       }
18171f551032SValentin Clement 
18181f551032SValentin Clement       // compute the stride and offset for the next natural dimension
18191f551032SValentin Clement       prevDim =
18201f551032SValentin Clement           rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevDim, outerExtent);
18211f551032SValentin Clement       if (constRows == 0)
18221f551032SValentin Clement         prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
18231f551032SValentin Clement                                                         outerExtent);
18241f551032SValentin Clement 
18251f551032SValentin Clement       // increment iterators
18261f551032SValentin Clement       ++shapeOffset;
18271f551032SValentin Clement       if (hasShift)
18281f551032SValentin Clement         ++shiftOffset;
18291f551032SValentin Clement       if (hasSlice)
18301f551032SValentin Clement         sliceOffset += 3;
18311f551032SValentin Clement     }
18321f551032SValentin Clement     if (hasSlice || hasSubcomp || !xbox.substr().empty()) {
18331f551032SValentin Clement       llvm::SmallVector<mlir::Value> args = {base, ptrOffset};
18341f551032SValentin Clement       args.append(gepArgs.rbegin(), gepArgs.rend());
18351f551032SValentin Clement       if (hasSubcomp) {
18361f551032SValentin Clement         // For each field in the path add the offset to base via the args list.
18371f551032SValentin Clement         // In the most general case, some offsets must be computed since
18381f551032SValentin Clement         // they are not be known until runtime.
18391f551032SValentin Clement         if (fir::hasDynamicSize(fir::unwrapSequenceType(
18401f551032SValentin Clement                 fir::unwrapPassByRefType(xbox.memref().getType()))))
18411f551032SValentin Clement           TODO(loc, "fir.embox codegen dynamic size component in derived type");
18421f551032SValentin Clement         args.append(operands.begin() + xbox.subcomponentOffset(),
18431f551032SValentin Clement                     operands.begin() + xbox.subcomponentOffset() +
18441f551032SValentin Clement                         xbox.subcomponent().size());
18451f551032SValentin Clement       }
18461f551032SValentin Clement       base = rewriter.create<mlir::LLVM::GEPOp>(loc, base.getType(), args);
18471f551032SValentin Clement       if (!xbox.substr().empty())
18481f551032SValentin Clement         base = shiftSubstringBase(rewriter, loc, base,
18491f551032SValentin Clement                                   operands[xbox.substrOffset()]);
18501f551032SValentin Clement     }
18511f551032SValentin Clement     dest = insertBaseAddress(rewriter, loc, dest, base);
18521f551032SValentin Clement     if (isDerivedTypeWithLenParams(boxTy))
18531f551032SValentin Clement       TODO(loc, "fir.embox codegen of derived with length parameters");
18541f551032SValentin Clement 
18551f551032SValentin Clement     mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest);
18561f551032SValentin Clement     rewriter.replaceOp(xbox, result);
18571f551032SValentin Clement     return success();
18581f551032SValentin Clement   }
18591f551032SValentin Clement };
18601f551032SValentin Clement 
1861*fa517555SKiran Chandramohan /// Create a new box given a box reference.
1862*fa517555SKiran Chandramohan struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
1863*fa517555SKiran Chandramohan   using EmboxCommonConversion::EmboxCommonConversion;
1864*fa517555SKiran Chandramohan 
1865*fa517555SKiran Chandramohan   mlir::LogicalResult
1866*fa517555SKiran Chandramohan   matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
1867*fa517555SKiran Chandramohan                   mlir::ConversionPatternRewriter &rewriter) const override {
1868*fa517555SKiran Chandramohan     mlir::Location loc = rebox.getLoc();
1869*fa517555SKiran Chandramohan     mlir::Type idxTy = lowerTy().indexType();
1870*fa517555SKiran Chandramohan     mlir::Value loweredBox = adaptor.getOperands()[0];
1871*fa517555SKiran Chandramohan     mlir::ValueRange operands = adaptor.getOperands();
1872*fa517555SKiran Chandramohan 
1873*fa517555SKiran Chandramohan     // Create new descriptor and fill its non-shape related data.
1874*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value, 2> lenParams;
1875*fa517555SKiran Chandramohan     mlir::Type inputEleTy = getInputEleTy(rebox);
1876*fa517555SKiran Chandramohan     if (auto charTy = inputEleTy.dyn_cast<fir::CharacterType>()) {
1877*fa517555SKiran Chandramohan       mlir::Value len =
1878*fa517555SKiran Chandramohan           loadElementSizeFromBox(loc, idxTy, loweredBox, rewriter);
1879*fa517555SKiran Chandramohan       if (charTy.getFKind() != 1) {
1880*fa517555SKiran Chandramohan         mlir::Value width =
1881*fa517555SKiran Chandramohan             genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
1882*fa517555SKiran Chandramohan         len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width);
1883*fa517555SKiran Chandramohan       }
1884*fa517555SKiran Chandramohan       lenParams.emplace_back(len);
1885*fa517555SKiran Chandramohan     } else if (auto recTy = inputEleTy.dyn_cast<fir::RecordType>()) {
1886*fa517555SKiran Chandramohan       if (recTy.getNumLenParams() != 0)
1887*fa517555SKiran Chandramohan         TODO(loc, "reboxing descriptor of derived type with length parameters");
1888*fa517555SKiran Chandramohan     }
1889*fa517555SKiran Chandramohan     auto [boxTy, dest, eleSize] =
1890*fa517555SKiran Chandramohan         consDescriptorPrefix(rebox, rewriter, rebox.getOutRank(), lenParams);
1891*fa517555SKiran Chandramohan 
1892*fa517555SKiran Chandramohan     // Read input extents, strides, and base address
1893*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> inputExtents;
1894*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> inputStrides;
1895*fa517555SKiran Chandramohan     const unsigned inputRank = rebox.getRank();
1896*fa517555SKiran Chandramohan     for (unsigned i = 0; i < inputRank; ++i) {
1897*fa517555SKiran Chandramohan       mlir::Value dim = genConstantIndex(loc, idxTy, rewriter, i);
1898*fa517555SKiran Chandramohan       SmallVector<mlir::Value, 3> dimInfo =
1899*fa517555SKiran Chandramohan           getDimsFromBox(loc, {idxTy, idxTy, idxTy}, loweredBox, dim, rewriter);
1900*fa517555SKiran Chandramohan       inputExtents.emplace_back(dimInfo[1]);
1901*fa517555SKiran Chandramohan       inputStrides.emplace_back(dimInfo[2]);
1902*fa517555SKiran Chandramohan     }
1903*fa517555SKiran Chandramohan 
1904*fa517555SKiran Chandramohan     mlir::Type baseTy = getBaseAddrTypeFromBox(loweredBox.getType());
1905*fa517555SKiran Chandramohan     mlir::Value baseAddr =
1906*fa517555SKiran Chandramohan         loadBaseAddrFromBox(loc, baseTy, loweredBox, rewriter);
1907*fa517555SKiran Chandramohan 
1908*fa517555SKiran Chandramohan     if (!rebox.slice().empty() || !rebox.subcomponent().empty())
1909*fa517555SKiran Chandramohan       return sliceBox(rebox, dest, baseAddr, inputExtents, inputStrides,
1910*fa517555SKiran Chandramohan                       operands, rewriter);
1911*fa517555SKiran Chandramohan     return reshapeBox(rebox, dest, baseAddr, inputExtents, inputStrides,
1912*fa517555SKiran Chandramohan                       operands, rewriter);
1913*fa517555SKiran Chandramohan   }
1914*fa517555SKiran Chandramohan 
1915*fa517555SKiran Chandramohan private:
1916*fa517555SKiran Chandramohan   /// Write resulting shape and base address in descriptor, and replace rebox
1917*fa517555SKiran Chandramohan   /// op.
1918*fa517555SKiran Chandramohan   mlir::LogicalResult
1919*fa517555SKiran Chandramohan   finalizeRebox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
1920*fa517555SKiran Chandramohan                 mlir::ValueRange lbounds, mlir::ValueRange extents,
1921*fa517555SKiran Chandramohan                 mlir::ValueRange strides,
1922*fa517555SKiran Chandramohan                 mlir::ConversionPatternRewriter &rewriter) const {
1923*fa517555SKiran Chandramohan     mlir::Location loc = rebox.getLoc();
1924*fa517555SKiran Chandramohan     mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1);
1925*fa517555SKiran Chandramohan     for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) {
1926*fa517555SKiran Chandramohan       unsigned dim = iter.index();
1927*fa517555SKiran Chandramohan       mlir::Value lb = lbounds.empty() ? one : lbounds[dim];
1928*fa517555SKiran Chandramohan       dest = insertLowerBound(rewriter, loc, dest, dim, lb);
1929*fa517555SKiran Chandramohan       dest = insertExtent(rewriter, loc, dest, dim, std::get<0>(iter.value()));
1930*fa517555SKiran Chandramohan       dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value()));
1931*fa517555SKiran Chandramohan     }
1932*fa517555SKiran Chandramohan     dest = insertBaseAddress(rewriter, loc, dest, base);
1933*fa517555SKiran Chandramohan     mlir::Value result =
1934*fa517555SKiran Chandramohan         placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), dest);
1935*fa517555SKiran Chandramohan     rewriter.replaceOp(rebox, result);
1936*fa517555SKiran Chandramohan     return success();
1937*fa517555SKiran Chandramohan   }
1938*fa517555SKiran Chandramohan 
1939*fa517555SKiran Chandramohan   // Apply slice given the base address, extents and strides of the input box.
1940*fa517555SKiran Chandramohan   mlir::LogicalResult
1941*fa517555SKiran Chandramohan   sliceBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
1942*fa517555SKiran Chandramohan            mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
1943*fa517555SKiran Chandramohan            mlir::ValueRange operands,
1944*fa517555SKiran Chandramohan            mlir::ConversionPatternRewriter &rewriter) const {
1945*fa517555SKiran Chandramohan     mlir::Location loc = rebox.getLoc();
1946*fa517555SKiran Chandramohan     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
1947*fa517555SKiran Chandramohan     mlir::Type idxTy = lowerTy().indexType();
1948*fa517555SKiran Chandramohan     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1949*fa517555SKiran Chandramohan     // Apply subcomponent and substring shift on base address.
1950*fa517555SKiran Chandramohan     if (!rebox.subcomponent().empty() || !rebox.substr().empty()) {
1951*fa517555SKiran Chandramohan       // Cast to inputEleTy* so that a GEP can be used.
1952*fa517555SKiran Chandramohan       mlir::Type inputEleTy = getInputEleTy(rebox);
1953*fa517555SKiran Chandramohan       auto llvmElePtrTy =
1954*fa517555SKiran Chandramohan           mlir::LLVM::LLVMPointerType::get(convertType(inputEleTy));
1955*fa517555SKiran Chandramohan       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, llvmElePtrTy, base);
1956*fa517555SKiran Chandramohan 
1957*fa517555SKiran Chandramohan       if (!rebox.subcomponent().empty()) {
1958*fa517555SKiran Chandramohan         llvm::SmallVector<mlir::Value> gepOperands = {zero};
1959*fa517555SKiran Chandramohan         for (unsigned i = 0; i < rebox.subcomponent().size(); ++i)
1960*fa517555SKiran Chandramohan           gepOperands.push_back(operands[rebox.subcomponentOffset() + i]);
1961*fa517555SKiran Chandramohan         base = genGEP(loc, llvmElePtrTy, rewriter, base, gepOperands);
1962*fa517555SKiran Chandramohan       }
1963*fa517555SKiran Chandramohan       if (!rebox.substr().empty())
1964*fa517555SKiran Chandramohan         base = shiftSubstringBase(rewriter, loc, base,
1965*fa517555SKiran Chandramohan                                   operands[rebox.substrOffset()]);
1966*fa517555SKiran Chandramohan     }
1967*fa517555SKiran Chandramohan 
1968*fa517555SKiran Chandramohan     if (rebox.slice().empty())
1969*fa517555SKiran Chandramohan       // The array section is of the form array[%component][substring], keep
1970*fa517555SKiran Chandramohan       // the input array extents and strides.
1971*fa517555SKiran Chandramohan       return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
1972*fa517555SKiran Chandramohan                            inputExtents, inputStrides, rewriter);
1973*fa517555SKiran Chandramohan 
1974*fa517555SKiran Chandramohan     // Strides from the fir.box are in bytes.
1975*fa517555SKiran Chandramohan     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
1976*fa517555SKiran Chandramohan 
1977*fa517555SKiran Chandramohan     // The slice is of the form array(i:j:k)[%component]. Compute new extents
1978*fa517555SKiran Chandramohan     // and strides.
1979*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> slicedExtents;
1980*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> slicedStrides;
1981*fa517555SKiran Chandramohan     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
1982*fa517555SKiran Chandramohan     const bool sliceHasOrigins = !rebox.shift().empty();
1983*fa517555SKiran Chandramohan     unsigned sliceOps = rebox.sliceOffset();
1984*fa517555SKiran Chandramohan     unsigned shiftOps = rebox.shiftOffset();
1985*fa517555SKiran Chandramohan     auto strideOps = inputStrides.begin();
1986*fa517555SKiran Chandramohan     const unsigned inputRank = inputStrides.size();
1987*fa517555SKiran Chandramohan     for (unsigned i = 0; i < inputRank;
1988*fa517555SKiran Chandramohan          ++i, ++strideOps, ++shiftOps, sliceOps += 3) {
1989*fa517555SKiran Chandramohan       mlir::Value sliceLb =
1990*fa517555SKiran Chandramohan           integerCast(loc, rewriter, idxTy, operands[sliceOps]);
1991*fa517555SKiran Chandramohan       mlir::Value inputStride = *strideOps; // already idxTy
1992*fa517555SKiran Chandramohan       // Apply origin shift: base += (lb-shift)*input_stride
1993*fa517555SKiran Chandramohan       mlir::Value sliceOrigin =
1994*fa517555SKiran Chandramohan           sliceHasOrigins
1995*fa517555SKiran Chandramohan               ? integerCast(loc, rewriter, idxTy, operands[shiftOps])
1996*fa517555SKiran Chandramohan               : one;
1997*fa517555SKiran Chandramohan       mlir::Value diff =
1998*fa517555SKiran Chandramohan           rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin);
1999*fa517555SKiran Chandramohan       mlir::Value offset =
2000*fa517555SKiran Chandramohan           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride);
2001*fa517555SKiran Chandramohan       base = genGEP(loc, voidPtrTy, rewriter, base, offset);
2002*fa517555SKiran Chandramohan       // Apply upper bound and step if this is a triplet. Otherwise, the
2003*fa517555SKiran Chandramohan       // dimension is dropped and no extents/strides are computed.
2004*fa517555SKiran Chandramohan       mlir::Value upper = operands[sliceOps + 1];
2005*fa517555SKiran Chandramohan       const bool isTripletSlice =
2006*fa517555SKiran Chandramohan           !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp());
2007*fa517555SKiran Chandramohan       if (isTripletSlice) {
2008*fa517555SKiran Chandramohan         mlir::Value step =
2009*fa517555SKiran Chandramohan             integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]);
2010*fa517555SKiran Chandramohan         // extent = ub-lb+step/step
2011*fa517555SKiran Chandramohan         mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper);
2012*fa517555SKiran Chandramohan         mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb,
2013*fa517555SKiran Chandramohan                                                   sliceUb, step, zero, idxTy);
2014*fa517555SKiran Chandramohan         slicedExtents.emplace_back(extent);
2015*fa517555SKiran Chandramohan         // stride = step*input_stride
2016*fa517555SKiran Chandramohan         mlir::Value stride =
2017*fa517555SKiran Chandramohan             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride);
2018*fa517555SKiran Chandramohan         slicedStrides.emplace_back(stride);
2019*fa517555SKiran Chandramohan       }
2020*fa517555SKiran Chandramohan     }
2021*fa517555SKiran Chandramohan     return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None,
2022*fa517555SKiran Chandramohan                          slicedExtents, slicedStrides, rewriter);
2023*fa517555SKiran Chandramohan   }
2024*fa517555SKiran Chandramohan 
2025*fa517555SKiran Chandramohan   /// Apply a new shape to the data described by a box given the base address,
2026*fa517555SKiran Chandramohan   /// extents and strides of the box.
2027*fa517555SKiran Chandramohan   mlir::LogicalResult
2028*fa517555SKiran Chandramohan   reshapeBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base,
2029*fa517555SKiran Chandramohan              mlir::ValueRange inputExtents, mlir::ValueRange inputStrides,
2030*fa517555SKiran Chandramohan              mlir::ValueRange operands,
2031*fa517555SKiran Chandramohan              mlir::ConversionPatternRewriter &rewriter) const {
2032*fa517555SKiran Chandramohan     mlir::ValueRange reboxShifts{operands.begin() + rebox.shiftOffset(),
2033*fa517555SKiran Chandramohan                                  operands.begin() + rebox.shiftOffset() +
2034*fa517555SKiran Chandramohan                                      rebox.shift().size()};
2035*fa517555SKiran Chandramohan     if (rebox.shape().empty()) {
2036*fa517555SKiran Chandramohan       // Only setting new lower bounds.
2037*fa517555SKiran Chandramohan       return finalizeRebox(rebox, dest, base, reboxShifts, inputExtents,
2038*fa517555SKiran Chandramohan                            inputStrides, rewriter);
2039*fa517555SKiran Chandramohan     }
2040*fa517555SKiran Chandramohan 
2041*fa517555SKiran Chandramohan     mlir::Location loc = rebox.getLoc();
2042*fa517555SKiran Chandramohan     // Strides from the fir.box are in bytes.
2043*fa517555SKiran Chandramohan     mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext());
2044*fa517555SKiran Chandramohan     base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
2045*fa517555SKiran Chandramohan 
2046*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> newStrides;
2047*fa517555SKiran Chandramohan     llvm::SmallVector<mlir::Value> newExtents;
2048*fa517555SKiran Chandramohan     mlir::Type idxTy = lowerTy().indexType();
2049*fa517555SKiran Chandramohan     // First stride from input box is kept. The rest is assumed contiguous
2050*fa517555SKiran Chandramohan     // (it is not possible to reshape otherwise). If the input is scalar,
2051*fa517555SKiran Chandramohan     // which may be OK if all new extents are ones, the stride does not
2052*fa517555SKiran Chandramohan     // matter, use one.
2053*fa517555SKiran Chandramohan     mlir::Value stride = inputStrides.empty()
2054*fa517555SKiran Chandramohan                              ? genConstantIndex(loc, idxTy, rewriter, 1)
2055*fa517555SKiran Chandramohan                              : inputStrides[0];
2056*fa517555SKiran Chandramohan     for (unsigned i = 0; i < rebox.shape().size(); ++i) {
2057*fa517555SKiran Chandramohan       mlir::Value rawExtent = operands[rebox.shapeOffset() + i];
2058*fa517555SKiran Chandramohan       mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent);
2059*fa517555SKiran Chandramohan       newExtents.emplace_back(extent);
2060*fa517555SKiran Chandramohan       newStrides.emplace_back(stride);
2061*fa517555SKiran Chandramohan       // nextStride = extent * stride;
2062*fa517555SKiran Chandramohan       stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
2063*fa517555SKiran Chandramohan     }
2064*fa517555SKiran Chandramohan     return finalizeRebox(rebox, dest, base, reboxShifts, newExtents, newStrides,
2065*fa517555SKiran Chandramohan                          rewriter);
2066*fa517555SKiran Chandramohan   }
2067*fa517555SKiran Chandramohan 
2068*fa517555SKiran Chandramohan   /// Return scalar element type of the input box.
2069*fa517555SKiran Chandramohan   static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) {
2070*fa517555SKiran Chandramohan     auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.box().getType());
2071*fa517555SKiran Chandramohan     if (auto seqTy = ty.dyn_cast<fir::SequenceType>())
2072*fa517555SKiran Chandramohan       return seqTy.getEleTy();
2073*fa517555SKiran Chandramohan     return ty;
2074*fa517555SKiran Chandramohan   }
2075*fa517555SKiran Chandramohan };
2076*fa517555SKiran Chandramohan 
207754c56347SValentin Clement // Code shared between insert_value and extract_value Ops.
207854c56347SValentin Clement struct ValueOpCommon {
207954c56347SValentin Clement   // Translate the arguments pertaining to any multidimensional array to
208054c56347SValentin Clement   // row-major order for LLVM-IR.
208154c56347SValentin Clement   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
208254c56347SValentin Clement                          mlir::Type ty) {
208354c56347SValentin Clement     assert(ty && "type is null");
208454c56347SValentin Clement     const auto end = attrs.size();
208554c56347SValentin Clement     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
208654c56347SValentin Clement       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
208754c56347SValentin Clement         const auto dim = getDimension(seq);
208854c56347SValentin Clement         if (dim > 1) {
208954c56347SValentin Clement           auto ub = std::min(i + dim, end);
209054c56347SValentin Clement           std::reverse(attrs.begin() + i, attrs.begin() + ub);
209154c56347SValentin Clement           i += dim - 1;
209254c56347SValentin Clement         }
209354c56347SValentin Clement         ty = getArrayElementType(seq);
209454c56347SValentin Clement       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
209554c56347SValentin Clement         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
209654c56347SValentin Clement       } else {
209754c56347SValentin Clement         llvm_unreachable("index into invalid type");
209854c56347SValentin Clement       }
209954c56347SValentin Clement     }
210054c56347SValentin Clement   }
210154c56347SValentin Clement 
210254c56347SValentin Clement   static llvm::SmallVector<mlir::Attribute>
210354c56347SValentin Clement   collectIndices(mlir::ConversionPatternRewriter &rewriter,
210454c56347SValentin Clement                  mlir::ArrayAttr arrAttr) {
210554c56347SValentin Clement     llvm::SmallVector<mlir::Attribute> attrs;
210654c56347SValentin Clement     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
210754c56347SValentin Clement       if (i->isa<mlir::IntegerAttr>()) {
210854c56347SValentin Clement         attrs.push_back(*i);
210954c56347SValentin Clement       } else {
211054c56347SValentin Clement         auto fieldName = i->cast<mlir::StringAttr>().getValue();
211154c56347SValentin Clement         ++i;
211254c56347SValentin Clement         auto ty = i->cast<mlir::TypeAttr>().getValue();
211354c56347SValentin Clement         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
211454c56347SValentin Clement         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
211554c56347SValentin Clement       }
211654c56347SValentin Clement     }
211754c56347SValentin Clement     return attrs;
211854c56347SValentin Clement   }
211954c56347SValentin Clement 
212054c56347SValentin Clement private:
212154c56347SValentin Clement   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
212254c56347SValentin Clement     unsigned result = 1;
212354c56347SValentin Clement     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
212454c56347SValentin Clement          eleTy;
212554c56347SValentin Clement          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
212654c56347SValentin Clement       ++result;
212754c56347SValentin Clement     return result;
212854c56347SValentin Clement   }
212954c56347SValentin Clement 
213054c56347SValentin Clement   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
213154c56347SValentin Clement     auto eleTy = ty.getElementType();
213254c56347SValentin Clement     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
213354c56347SValentin Clement       eleTy = arrTy.getElementType();
213454c56347SValentin Clement     return eleTy;
213554c56347SValentin Clement   }
213654c56347SValentin Clement };
213754c56347SValentin Clement 
213854c56347SValentin Clement /// Extract a subobject value from an ssa-value of aggregate type
213954c56347SValentin Clement struct ExtractValueOpConversion
214054c56347SValentin Clement     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
214154c56347SValentin Clement       public ValueOpCommon {
214254c56347SValentin Clement   using FIROpAndTypeConversion::FIROpAndTypeConversion;
214354c56347SValentin Clement 
214454c56347SValentin Clement   mlir::LogicalResult
214554c56347SValentin Clement   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
214654c56347SValentin Clement             mlir::ConversionPatternRewriter &rewriter) const override {
214754c56347SValentin Clement     auto attrs = collectIndices(rewriter, extractVal.coor());
214854c56347SValentin Clement     toRowMajor(attrs, adaptor.getOperands()[0].getType());
214954c56347SValentin Clement     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
215054c56347SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
215154c56347SValentin Clement         extractVal, ty, adaptor.getOperands()[0], position);
215254c56347SValentin Clement     return success();
215354c56347SValentin Clement   }
215454c56347SValentin Clement };
215554c56347SValentin Clement 
215654c56347SValentin Clement /// InsertValue is the generalized instruction for the composition of new
215754c56347SValentin Clement /// aggregate type values.
215854c56347SValentin Clement struct InsertValueOpConversion
215954c56347SValentin Clement     : public FIROpAndTypeConversion<fir::InsertValueOp>,
216054c56347SValentin Clement       public ValueOpCommon {
216154c56347SValentin Clement   using FIROpAndTypeConversion::FIROpAndTypeConversion;
216254c56347SValentin Clement 
216354c56347SValentin Clement   mlir::LogicalResult
216454c56347SValentin Clement   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
216554c56347SValentin Clement             mlir::ConversionPatternRewriter &rewriter) const override {
216654c56347SValentin Clement     auto attrs = collectIndices(rewriter, insertVal.coor());
216754c56347SValentin Clement     toRowMajor(attrs, adaptor.getOperands()[0].getType());
216854c56347SValentin Clement     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
216954c56347SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
217054c56347SValentin Clement         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
217154c56347SValentin Clement         position);
217254c56347SValentin Clement     return success();
217354c56347SValentin Clement   }
217454c56347SValentin Clement };
217554c56347SValentin Clement 
21763ae8e442SValentin Clement /// InsertOnRange inserts a value into a sequence over a range of offsets.
21773ae8e442SValentin Clement struct InsertOnRangeOpConversion
21783ae8e442SValentin Clement     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
21793ae8e442SValentin Clement   using FIROpAndTypeConversion::FIROpAndTypeConversion;
21803ae8e442SValentin Clement 
21813ae8e442SValentin Clement   // Increments an array of subscripts in a row major fasion.
21823ae8e442SValentin Clement   void incrementSubscripts(const SmallVector<uint64_t> &dims,
21833ae8e442SValentin Clement                            SmallVector<uint64_t> &subscripts) const {
21843ae8e442SValentin Clement     for (size_t i = dims.size(); i > 0; --i) {
21853ae8e442SValentin Clement       if (++subscripts[i - 1] < dims[i - 1]) {
21863ae8e442SValentin Clement         return;
21873ae8e442SValentin Clement       }
21883ae8e442SValentin Clement       subscripts[i - 1] = 0;
21893ae8e442SValentin Clement     }
21903ae8e442SValentin Clement   }
21913ae8e442SValentin Clement 
21923ae8e442SValentin Clement   mlir::LogicalResult
21933ae8e442SValentin Clement   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
21943ae8e442SValentin Clement             mlir::ConversionPatternRewriter &rewriter) const override {
21953ae8e442SValentin Clement 
21963ae8e442SValentin Clement     llvm::SmallVector<uint64_t> dims;
21973ae8e442SValentin Clement     auto type = adaptor.getOperands()[0].getType();
21983ae8e442SValentin Clement 
21993ae8e442SValentin Clement     // Iteratively extract the array dimensions from the type.
22003ae8e442SValentin Clement     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
22013ae8e442SValentin Clement       dims.push_back(t.getNumElements());
22023ae8e442SValentin Clement       type = t.getElementType();
22033ae8e442SValentin Clement     }
22043ae8e442SValentin Clement 
22053ae8e442SValentin Clement     SmallVector<uint64_t> lBounds;
22063ae8e442SValentin Clement     SmallVector<uint64_t> uBounds;
22073ae8e442SValentin Clement 
22083ae8e442SValentin Clement     // Unzip the upper and lower bound and convert to a row major format.
22098ec0f221SMehdi Amini     mlir::DenseIntElementsAttr coor = range.coor();
22108ec0f221SMehdi Amini     auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
22118ec0f221SMehdi Amini     for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
22123ae8e442SValentin Clement       uBounds.push_back(*i++);
22133ae8e442SValentin Clement       lBounds.push_back(*i);
22143ae8e442SValentin Clement     }
22153ae8e442SValentin Clement 
22163ae8e442SValentin Clement     auto &subscripts = lBounds;
22173ae8e442SValentin Clement     auto loc = range.getLoc();
22183ae8e442SValentin Clement     mlir::Value lastOp = adaptor.getOperands()[0];
22193ae8e442SValentin Clement     mlir::Value insertVal = adaptor.getOperands()[1];
22203ae8e442SValentin Clement 
22213ae8e442SValentin Clement     auto i64Ty = rewriter.getI64Type();
22223ae8e442SValentin Clement     while (subscripts != uBounds) {
22233ae8e442SValentin Clement       // Convert uint64_t's to Attribute's.
22243ae8e442SValentin Clement       SmallVector<mlir::Attribute> subscriptAttrs;
22253ae8e442SValentin Clement       for (const auto &subscript : subscripts)
22263ae8e442SValentin Clement         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
22273ae8e442SValentin Clement       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
22283ae8e442SValentin Clement           loc, ty, lastOp, insertVal,
22293ae8e442SValentin Clement           ArrayAttr::get(range.getContext(), subscriptAttrs));
22303ae8e442SValentin Clement 
22313ae8e442SValentin Clement       incrementSubscripts(dims, subscripts);
22323ae8e442SValentin Clement     }
22333ae8e442SValentin Clement 
22343ae8e442SValentin Clement     // Convert uint64_t's to Attribute's.
22353ae8e442SValentin Clement     SmallVector<mlir::Attribute> subscriptAttrs;
22363ae8e442SValentin Clement     for (const auto &subscript : subscripts)
22373ae8e442SValentin Clement       subscriptAttrs.push_back(
22383ae8e442SValentin Clement           IntegerAttr::get(rewriter.getI64Type(), subscript));
22393ae8e442SValentin Clement     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
22403ae8e442SValentin Clement 
22413ae8e442SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
22423ae8e442SValentin Clement         range, ty, lastOp, insertVal,
22433ae8e442SValentin Clement         ArrayAttr::get(range.getContext(), arrayRef));
22443ae8e442SValentin Clement 
22453ae8e442SValentin Clement     return success();
22463ae8e442SValentin Clement   }
22473ae8e442SValentin Clement };
22487b5132daSValentin Clement 
22495d27abe6SValentin Clement /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced,
22505d27abe6SValentin Clement /// shifted etc. array.
22515d27abe6SValentin Clement /// (See the static restriction on coordinate_of.) array_coor determines the
22525d27abe6SValentin Clement /// coordinate (location) of a specific element.
22535d27abe6SValentin Clement struct XArrayCoorOpConversion
22545d27abe6SValentin Clement     : public FIROpAndTypeConversion<fir::cg::XArrayCoorOp> {
22555d27abe6SValentin Clement   using FIROpAndTypeConversion::FIROpAndTypeConversion;
22565d27abe6SValentin Clement 
22575d27abe6SValentin Clement   mlir::LogicalResult
22585d27abe6SValentin Clement   doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type ty, OpAdaptor adaptor,
22595d27abe6SValentin Clement             mlir::ConversionPatternRewriter &rewriter) const override {
22605d27abe6SValentin Clement     auto loc = coor.getLoc();
22615d27abe6SValentin Clement     mlir::ValueRange operands = adaptor.getOperands();
22625d27abe6SValentin Clement     unsigned rank = coor.getRank();
22635d27abe6SValentin Clement     assert(coor.indices().size() == rank);
22645d27abe6SValentin Clement     assert(coor.shape().empty() || coor.shape().size() == rank);
22655d27abe6SValentin Clement     assert(coor.shift().empty() || coor.shift().size() == rank);
22665d27abe6SValentin Clement     assert(coor.slice().empty() || coor.slice().size() == 3 * rank);
22675d27abe6SValentin Clement     mlir::Type idxTy = lowerTy().indexType();
22685d27abe6SValentin Clement     mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
22695d27abe6SValentin Clement     mlir::Value prevExt = one;
22705d27abe6SValentin Clement     mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
22715d27abe6SValentin Clement     mlir::Value offset = zero;
22725d27abe6SValentin Clement     const bool isShifted = !coor.shift().empty();
22735d27abe6SValentin Clement     const bool isSliced = !coor.slice().empty();
22745d27abe6SValentin Clement     const bool baseIsBoxed = coor.memref().getType().isa<fir::BoxType>();
22755d27abe6SValentin Clement 
22765d27abe6SValentin Clement     auto indexOps = coor.indices().begin();
22775d27abe6SValentin Clement     auto shapeOps = coor.shape().begin();
22785d27abe6SValentin Clement     auto shiftOps = coor.shift().begin();
22795d27abe6SValentin Clement     auto sliceOps = coor.slice().begin();
22805d27abe6SValentin Clement     // For each dimension of the array, generate the offset calculation.
22815d27abe6SValentin Clement     for (unsigned i = 0; i < rank;
22825d27abe6SValentin Clement          ++i, ++indexOps, ++shapeOps, ++shiftOps, sliceOps += 3) {
22835d27abe6SValentin Clement       mlir::Value index =
22845d27abe6SValentin Clement           integerCast(loc, rewriter, idxTy, operands[coor.indicesOffset() + i]);
22855d27abe6SValentin Clement       mlir::Value lb = isShifted ? integerCast(loc, rewriter, idxTy,
22865d27abe6SValentin Clement                                                operands[coor.shiftOffset() + i])
22875d27abe6SValentin Clement                                  : one;
22885d27abe6SValentin Clement       mlir::Value step = one;
22895d27abe6SValentin Clement       bool normalSlice = isSliced;
22905d27abe6SValentin Clement       // Compute zero based index in dimension i of the element, applying
22915d27abe6SValentin Clement       // potential triplets and lower bounds.
22925d27abe6SValentin Clement       if (isSliced) {
22935d27abe6SValentin Clement         mlir::Value ub = *(sliceOps + 1);
22945d27abe6SValentin Clement         normalSlice = !mlir::isa_and_nonnull<fir::UndefOp>(ub.getDefiningOp());
22955d27abe6SValentin Clement         if (normalSlice)
22965d27abe6SValentin Clement           step = integerCast(loc, rewriter, idxTy, *(sliceOps + 2));
22975d27abe6SValentin Clement       }
22985d27abe6SValentin Clement       auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb);
22995d27abe6SValentin Clement       mlir::Value diff =
23005d27abe6SValentin Clement           rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step);
23015d27abe6SValentin Clement       if (normalSlice) {
23025d27abe6SValentin Clement         mlir::Value sliceLb =
23035d27abe6SValentin Clement             integerCast(loc, rewriter, idxTy, operands[coor.sliceOffset() + i]);
23045d27abe6SValentin Clement         auto adj = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb);
23055d27abe6SValentin Clement         diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj);
23065d27abe6SValentin Clement       }
23075d27abe6SValentin Clement       // Update the offset given the stride and the zero based index `diff`
23085d27abe6SValentin Clement       // that was just computed.
23095d27abe6SValentin Clement       if (baseIsBoxed) {
23105d27abe6SValentin Clement         // Use stride in bytes from the descriptor.
23115d27abe6SValentin Clement         mlir::Value stride =
23125d27abe6SValentin Clement             loadStrideFromBox(loc, adaptor.getOperands()[0], i, rewriter);
23135d27abe6SValentin Clement         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride);
23145d27abe6SValentin Clement         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
23155d27abe6SValentin Clement       } else {
23165d27abe6SValentin Clement         // Use stride computed at last iteration.
23175d27abe6SValentin Clement         auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt);
23185d27abe6SValentin Clement         offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset);
23195d27abe6SValentin Clement         // Compute next stride assuming contiguity of the base array
23205d27abe6SValentin Clement         // (in element number).
23215d27abe6SValentin Clement         auto nextExt =
23225d27abe6SValentin Clement             integerCast(loc, rewriter, idxTy, operands[coor.shapeOffset() + i]);
23235d27abe6SValentin Clement         prevExt =
23245d27abe6SValentin Clement             rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt, nextExt);
23255d27abe6SValentin Clement       }
23265d27abe6SValentin Clement     }
23275d27abe6SValentin Clement 
23285d27abe6SValentin Clement     // Add computed offset to the base address.
23295d27abe6SValentin Clement     if (baseIsBoxed) {
23305d27abe6SValentin Clement       // Working with byte offsets. The base address is read from the fir.box.
23315d27abe6SValentin Clement       // and need to be casted to i8* to do the pointer arithmetic.
23325d27abe6SValentin Clement       mlir::Type baseTy =
23335d27abe6SValentin Clement           getBaseAddrTypeFromBox(adaptor.getOperands()[0].getType());
23345d27abe6SValentin Clement       mlir::Value base =
23355d27abe6SValentin Clement           loadBaseAddrFromBox(loc, baseTy, adaptor.getOperands()[0], rewriter);
23365d27abe6SValentin Clement       mlir::Type voidPtrTy = getVoidPtrType();
23375d27abe6SValentin Clement       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base);
23385d27abe6SValentin Clement       llvm::SmallVector<mlir::Value> args{base, offset};
23395d27abe6SValentin Clement       auto addr = rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy, args);
23405d27abe6SValentin Clement       if (coor.subcomponent().empty()) {
23415d27abe6SValentin Clement         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, baseTy, addr);
23425d27abe6SValentin Clement         return success();
23435d27abe6SValentin Clement       }
23445d27abe6SValentin Clement       auto casted = rewriter.create<mlir::LLVM::BitcastOp>(loc, baseTy, addr);
23455d27abe6SValentin Clement       args.clear();
23465d27abe6SValentin Clement       args.push_back(casted);
23475d27abe6SValentin Clement       args.push_back(zero);
23485d27abe6SValentin Clement       if (!coor.lenParams().empty()) {
23495d27abe6SValentin Clement         // If type parameters are present, then we don't want to use a GEPOp
23505d27abe6SValentin Clement         // as below, as the LLVM struct type cannot be statically defined.
23515d27abe6SValentin Clement         TODO(loc, "derived type with type parameters");
23525d27abe6SValentin Clement       }
23535d27abe6SValentin Clement       // TODO: array offset subcomponents must be converted to LLVM's
23545d27abe6SValentin Clement       // row-major layout here.
23555d27abe6SValentin Clement       for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
23565d27abe6SValentin Clement         args.push_back(operands[i]);
23575d27abe6SValentin Clement       rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, baseTy, args);
23585d27abe6SValentin Clement       return success();
23595d27abe6SValentin Clement     }
23605d27abe6SValentin Clement 
23615d27abe6SValentin Clement     // The array was not boxed, so it must be contiguous. offset is therefore an
23625d27abe6SValentin Clement     // element offset and the base type is kept in the GEP unless the element
23635d27abe6SValentin Clement     // type size is itself dynamic.
23645d27abe6SValentin Clement     mlir::Value base;
23655d27abe6SValentin Clement     if (coor.subcomponent().empty()) {
23665d27abe6SValentin Clement       // No subcomponent.
23675d27abe6SValentin Clement       if (!coor.lenParams().empty()) {
23685d27abe6SValentin Clement         // Type parameters. Adjust element size explicitly.
23695d27abe6SValentin Clement         auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType());
23705d27abe6SValentin Clement         assert(eleTy && "result must be a reference-like type");
23715d27abe6SValentin Clement         if (fir::characterWithDynamicLen(eleTy)) {
23725d27abe6SValentin Clement           assert(coor.lenParams().size() == 1);
23735d27abe6SValentin Clement           auto bitsInChar = lowerTy().getKindMap().getCharacterBitsize(
23745d27abe6SValentin Clement               eleTy.cast<fir::CharacterType>().getFKind());
23755d27abe6SValentin Clement           auto scaling = genConstantIndex(loc, idxTy, rewriter, bitsInChar / 8);
23765d27abe6SValentin Clement           auto scaledBySize =
23775d27abe6SValentin Clement               rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset, scaling);
23785d27abe6SValentin Clement           auto length =
23795d27abe6SValentin Clement               integerCast(loc, rewriter, idxTy,
23805d27abe6SValentin Clement                           adaptor.getOperands()[coor.lenParamsOffset()]);
23815d27abe6SValentin Clement           offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, scaledBySize,
23825d27abe6SValentin Clement                                                       length);
23835d27abe6SValentin Clement         } else {
23845d27abe6SValentin Clement           TODO(loc, "compute size of derived type with type parameters");
23855d27abe6SValentin Clement         }
23865d27abe6SValentin Clement       }
23875d27abe6SValentin Clement       // Cast the base address to a pointer to T.
23885d27abe6SValentin Clement       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, ty,
23895d27abe6SValentin Clement                                                     adaptor.getOperands()[0]);
23905d27abe6SValentin Clement     } else {
23915d27abe6SValentin Clement       // Operand #0 must have a pointer type. For subcomponent slicing, we
23925d27abe6SValentin Clement       // want to cast away the array type and have a plain struct type.
23935d27abe6SValentin Clement       mlir::Type ty0 = adaptor.getOperands()[0].getType();
23945d27abe6SValentin Clement       auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>();
23955d27abe6SValentin Clement       assert(ptrTy && "expected pointer type");
23965d27abe6SValentin Clement       mlir::Type eleTy = ptrTy.getElementType();
23975d27abe6SValentin Clement       while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
23985d27abe6SValentin Clement         eleTy = arrTy.getElementType();
23995d27abe6SValentin Clement       auto newTy = mlir::LLVM::LLVMPointerType::get(eleTy);
24005d27abe6SValentin Clement       base = rewriter.create<mlir::LLVM::BitcastOp>(loc, newTy,
24015d27abe6SValentin Clement                                                     adaptor.getOperands()[0]);
24025d27abe6SValentin Clement     }
24035d27abe6SValentin Clement     SmallVector<mlir::Value> args = {base, offset};
24045d27abe6SValentin Clement     for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i)
24055d27abe6SValentin Clement       args.push_back(operands[i]);
24065d27abe6SValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, ty, args);
24075d27abe6SValentin Clement     return success();
24085d27abe6SValentin Clement   }
24095d27abe6SValentin Clement };
24105d27abe6SValentin Clement 
24117b5132daSValentin Clement //
24127b5132daSValentin Clement // Primitive operations on Complex types
24137b5132daSValentin Clement //
24147b5132daSValentin Clement 
24157b5132daSValentin Clement /// Generate inline code for complex addition/subtraction
24167b5132daSValentin Clement template <typename LLVMOP, typename OPTY>
24177b5132daSValentin Clement mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
24187b5132daSValentin Clement                                      mlir::ConversionPatternRewriter &rewriter,
24197b5132daSValentin Clement                                      fir::LLVMTypeConverter &lowering) {
24207b5132daSValentin Clement   mlir::Value a = opnds[0];
24217b5132daSValentin Clement   mlir::Value b = opnds[1];
24227b5132daSValentin Clement   auto loc = sumop.getLoc();
24237b5132daSValentin Clement   auto ctx = sumop.getContext();
24247b5132daSValentin Clement   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
24257b5132daSValentin Clement   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
24267b5132daSValentin Clement   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
24277b5132daSValentin Clement   mlir::Type ty = lowering.convertType(sumop.getType());
24287b5132daSValentin Clement   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
24297b5132daSValentin Clement   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
24307b5132daSValentin Clement   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
24317b5132daSValentin Clement   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
24327b5132daSValentin Clement   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
24337b5132daSValentin Clement   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
24347b5132daSValentin Clement   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
24357b5132daSValentin Clement   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
24367b5132daSValentin Clement   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
24377b5132daSValentin Clement }
24387b5132daSValentin Clement 
24397b5132daSValentin Clement struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
24407b5132daSValentin Clement   using FIROpConversion::FIROpConversion;
24417b5132daSValentin Clement 
24427b5132daSValentin Clement   mlir::LogicalResult
24437b5132daSValentin Clement   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
24447b5132daSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
24457b5132daSValentin Clement     // given: (x + iy) + (x' + iy')
24467b5132daSValentin Clement     // result: (x + x') + i(y + y')
24477b5132daSValentin Clement     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
24487b5132daSValentin Clement                                             rewriter, lowerTy());
24497b5132daSValentin Clement     rewriter.replaceOp(addc, r.getResult());
24507b5132daSValentin Clement     return success();
24517b5132daSValentin Clement   }
24527b5132daSValentin Clement };
24537b5132daSValentin Clement 
24547b5132daSValentin Clement struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
24557b5132daSValentin Clement   using FIROpConversion::FIROpConversion;
24567b5132daSValentin Clement 
24577b5132daSValentin Clement   mlir::LogicalResult
24587b5132daSValentin Clement   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
24597b5132daSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
24607b5132daSValentin Clement     // given: (x + iy) - (x' + iy')
24617b5132daSValentin Clement     // result: (x - x') + i(y - y')
24627b5132daSValentin Clement     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
24637b5132daSValentin Clement                                             rewriter, lowerTy());
24647b5132daSValentin Clement     rewriter.replaceOp(subc, r.getResult());
24657b5132daSValentin Clement     return success();
24667b5132daSValentin Clement   }
24677b5132daSValentin Clement };
24687b5132daSValentin Clement 
24697b5132daSValentin Clement /// Inlined complex multiply
24707b5132daSValentin Clement struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
24717b5132daSValentin Clement   using FIROpConversion::FIROpConversion;
24727b5132daSValentin Clement 
24737b5132daSValentin Clement   mlir::LogicalResult
24747b5132daSValentin Clement   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
24757b5132daSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
24767b5132daSValentin Clement     // TODO: Can we use a call to __muldc3 ?
24777b5132daSValentin Clement     // given: (x + iy) * (x' + iy')
24787b5132daSValentin Clement     // result: (xx'-yy')+i(xy'+yx')
24797b5132daSValentin Clement     mlir::Value a = adaptor.getOperands()[0];
24807b5132daSValentin Clement     mlir::Value b = adaptor.getOperands()[1];
24817b5132daSValentin Clement     auto loc = mulc.getLoc();
24827b5132daSValentin Clement     auto *ctx = mulc.getContext();
24837b5132daSValentin Clement     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
24847b5132daSValentin Clement     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
24857b5132daSValentin Clement     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
24867b5132daSValentin Clement     mlir::Type ty = convertType(mulc.getType());
24877b5132daSValentin Clement     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
24887b5132daSValentin Clement     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
24897b5132daSValentin Clement     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
24907b5132daSValentin Clement     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
24917b5132daSValentin Clement     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
24927b5132daSValentin Clement     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
24937b5132daSValentin Clement     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
24947b5132daSValentin Clement     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
24957b5132daSValentin Clement     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
24967b5132daSValentin Clement     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
24977b5132daSValentin Clement     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
24987b5132daSValentin Clement     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
24997b5132daSValentin Clement     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
25007b5132daSValentin Clement     rewriter.replaceOp(mulc, r0.getResult());
25017b5132daSValentin Clement     return success();
25027b5132daSValentin Clement   }
25037b5132daSValentin Clement };
25047b5132daSValentin Clement 
25057b5132daSValentin Clement /// Inlined complex division
25067b5132daSValentin Clement struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
25077b5132daSValentin Clement   using FIROpConversion::FIROpConversion;
25087b5132daSValentin Clement 
25097b5132daSValentin Clement   mlir::LogicalResult
25107b5132daSValentin Clement   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
25117b5132daSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
25127b5132daSValentin Clement     // TODO: Can we use a call to __divdc3 instead?
25137b5132daSValentin Clement     // Just generate inline code for now.
25147b5132daSValentin Clement     // given: (x + iy) / (x' + iy')
25157b5132daSValentin Clement     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
25167b5132daSValentin Clement     mlir::Value a = adaptor.getOperands()[0];
25177b5132daSValentin Clement     mlir::Value b = adaptor.getOperands()[1];
25187b5132daSValentin Clement     auto loc = divc.getLoc();
25197b5132daSValentin Clement     auto *ctx = divc.getContext();
25207b5132daSValentin Clement     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
25217b5132daSValentin Clement     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
25227b5132daSValentin Clement     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
25237b5132daSValentin Clement     mlir::Type ty = convertType(divc.getType());
25247b5132daSValentin Clement     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
25257b5132daSValentin Clement     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
25267b5132daSValentin Clement     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
25277b5132daSValentin Clement     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
25287b5132daSValentin Clement     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
25297b5132daSValentin Clement     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
25307b5132daSValentin Clement     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
25317b5132daSValentin Clement     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
25327b5132daSValentin Clement     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
25337b5132daSValentin Clement     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
25347b5132daSValentin Clement     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
25357b5132daSValentin Clement     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
25367b5132daSValentin Clement     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
25377b5132daSValentin Clement     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
25387b5132daSValentin Clement     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
25397b5132daSValentin Clement     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
25407b5132daSValentin Clement     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
25417b5132daSValentin Clement     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
25427b5132daSValentin Clement     rewriter.replaceOp(divc, r0.getResult());
25437b5132daSValentin Clement     return success();
25447b5132daSValentin Clement   }
25457b5132daSValentin Clement };
25467b5132daSValentin Clement 
25477b5132daSValentin Clement /// Inlined complex negation
25487b5132daSValentin Clement struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
25497b5132daSValentin Clement   using FIROpConversion::FIROpConversion;
25507b5132daSValentin Clement 
25517b5132daSValentin Clement   mlir::LogicalResult
25527b5132daSValentin Clement   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
25537b5132daSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const override {
25547b5132daSValentin Clement     // given: -(x + iy)
25557b5132daSValentin Clement     // result: -x - iy
25567b5132daSValentin Clement     auto *ctxt = neg.getContext();
25577b5132daSValentin Clement     auto eleTy = convertType(getComplexEleTy(neg.getType()));
25587b5132daSValentin Clement     auto ty = convertType(neg.getType());
25597b5132daSValentin Clement     auto loc = neg.getLoc();
25607b5132daSValentin Clement     mlir::Value o0 = adaptor.getOperands()[0];
25617b5132daSValentin Clement     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
25627b5132daSValentin Clement     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
25637b5132daSValentin Clement     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
25647b5132daSValentin Clement     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
25657b5132daSValentin Clement     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
25667b5132daSValentin Clement     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
25677b5132daSValentin Clement     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
25687b5132daSValentin Clement     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
25697b5132daSValentin Clement     return success();
25707b5132daSValentin Clement   }
25717b5132daSValentin Clement };
25727b5132daSValentin Clement 
25731ed5a90fSValentin Clement /// Conversion pattern for operation that must be dead. The information in these
25741ed5a90fSValentin Clement /// operations is used by other operation. At this point they should not have
25751ed5a90fSValentin Clement /// anymore uses.
25761ed5a90fSValentin Clement /// These operations are normally dead after the pre-codegen pass.
25771ed5a90fSValentin Clement template <typename FromOp>
25781ed5a90fSValentin Clement struct MustBeDeadConversion : public FIROpConversion<FromOp> {
25791ed5a90fSValentin Clement   explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering)
25801ed5a90fSValentin Clement       : FIROpConversion<FromOp>(lowering) {}
25811ed5a90fSValentin Clement   using OpAdaptor = typename FromOp::Adaptor;
25821ed5a90fSValentin Clement 
25831ed5a90fSValentin Clement   mlir::LogicalResult
25841ed5a90fSValentin Clement   matchAndRewrite(FromOp op, OpAdaptor adaptor,
25851ed5a90fSValentin Clement                   mlir::ConversionPatternRewriter &rewriter) const final {
25861ed5a90fSValentin Clement     if (!op->getUses().empty())
25871ed5a90fSValentin Clement       return rewriter.notifyMatchFailure(op, "op must be dead");
25881ed5a90fSValentin Clement     rewriter.eraseOp(op);
25891ed5a90fSValentin Clement     return success();
25901ed5a90fSValentin Clement   }
25911ed5a90fSValentin Clement };
25921ed5a90fSValentin Clement 
25931ed5a90fSValentin Clement struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
25941ed5a90fSValentin Clement   using MustBeDeadConversion::MustBeDeadConversion;
25951ed5a90fSValentin Clement };
25961ed5a90fSValentin Clement 
25971ed5a90fSValentin Clement struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
25981ed5a90fSValentin Clement   using MustBeDeadConversion::MustBeDeadConversion;
25991ed5a90fSValentin Clement };
26001ed5a90fSValentin Clement 
26011ed5a90fSValentin Clement struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
26021ed5a90fSValentin Clement   using MustBeDeadConversion::MustBeDeadConversion;
26031ed5a90fSValentin Clement };
26041ed5a90fSValentin Clement 
26051ed5a90fSValentin Clement struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
26061ed5a90fSValentin Clement   using MustBeDeadConversion::MustBeDeadConversion;
26071ed5a90fSValentin Clement };
26081ed5a90fSValentin Clement 
2609420ad7ceSAndrzej Warzynski /// `fir.is_present` -->
2610420ad7ceSAndrzej Warzynski /// ```
2611420ad7ceSAndrzej Warzynski ///  %0 = llvm.mlir.constant(0 : i64)
2612420ad7ceSAndrzej Warzynski ///  %1 = llvm.ptrtoint %0
2613420ad7ceSAndrzej Warzynski ///  %2 = llvm.icmp "ne" %1, %0 : i64
2614420ad7ceSAndrzej Warzynski /// ```
2615420ad7ceSAndrzej Warzynski struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> {
2616420ad7ceSAndrzej Warzynski   using FIROpConversion::FIROpConversion;
2617420ad7ceSAndrzej Warzynski 
2618420ad7ceSAndrzej Warzynski   mlir::LogicalResult
2619420ad7ceSAndrzej Warzynski   matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
2620420ad7ceSAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
2621420ad7ceSAndrzej Warzynski     mlir::Type idxTy = lowerTy().indexType();
2622420ad7ceSAndrzej Warzynski     mlir::Location loc = isPresent.getLoc();
2623420ad7ceSAndrzej Warzynski     auto ptr = adaptor.getOperands()[0];
2624420ad7ceSAndrzej Warzynski 
2625420ad7ceSAndrzej Warzynski     if (isPresent.val().getType().isa<fir::BoxCharType>()) {
2626420ad7ceSAndrzej Warzynski       auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>();
2627420ad7ceSAndrzej Warzynski       assert(!structTy.isOpaque() && !structTy.getBody().empty());
2628420ad7ceSAndrzej Warzynski 
2629420ad7ceSAndrzej Warzynski       mlir::Type ty = structTy.getBody()[0];
2630420ad7ceSAndrzej Warzynski       mlir::MLIRContext *ctx = isPresent.getContext();
2631420ad7ceSAndrzej Warzynski       auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
2632420ad7ceSAndrzej Warzynski       ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0);
2633420ad7ceSAndrzej Warzynski     }
2634420ad7ceSAndrzej Warzynski     mlir::LLVM::ConstantOp c0 =
2635420ad7ceSAndrzej Warzynski         genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
2636420ad7ceSAndrzej Warzynski     auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
2637420ad7ceSAndrzej Warzynski     rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2638420ad7ceSAndrzej Warzynski         isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
2639420ad7ceSAndrzej Warzynski 
2640420ad7ceSAndrzej Warzynski     return success();
2641420ad7ceSAndrzej Warzynski   }
2642420ad7ceSAndrzej Warzynski };
26431e77b095SAndrzej Warzynski 
26441e77b095SAndrzej Warzynski /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
26451e77b095SAndrzej Warzynski /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
26461e77b095SAndrzej Warzynski /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
26471e77b095SAndrzej Warzynski /// element is the length of the character buffer (`#n`).
26481e77b095SAndrzej Warzynski struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> {
26491e77b095SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
26501e77b095SAndrzej Warzynski 
26511e77b095SAndrzej Warzynski   mlir::LogicalResult
26521e77b095SAndrzej Warzynski   matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
26531e77b095SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
26541e77b095SAndrzej Warzynski     mlir::ValueRange operands = adaptor.getOperands();
26551e77b095SAndrzej Warzynski     MLIRContext *ctx = emboxChar.getContext();
26561e77b095SAndrzej Warzynski 
26571e77b095SAndrzej Warzynski     mlir::Value charBuffer = operands[0];
26581e77b095SAndrzej Warzynski     mlir::Value charBufferLen = operands[1];
26591e77b095SAndrzej Warzynski 
26601e77b095SAndrzej Warzynski     mlir::Location loc = emboxChar.getLoc();
26611e77b095SAndrzej Warzynski     mlir::Type llvmStructTy = convertType(emboxChar.getType());
26621e77b095SAndrzej Warzynski     auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
26631e77b095SAndrzej Warzynski 
26641e77b095SAndrzej Warzynski     mlir::Type lenTy =
26651e77b095SAndrzej Warzynski         llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1];
26661e77b095SAndrzej Warzynski     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
26671e77b095SAndrzej Warzynski 
26681e77b095SAndrzej Warzynski     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
26691e77b095SAndrzej Warzynski     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
26701e77b095SAndrzej Warzynski     auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
26711e77b095SAndrzej Warzynski         loc, llvmStructTy, llvmStruct, charBuffer, c0);
26721e77b095SAndrzej Warzynski     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
26731e77b095SAndrzej Warzynski         emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1);
26741e77b095SAndrzej Warzynski 
26751e77b095SAndrzej Warzynski     return success();
26761e77b095SAndrzej Warzynski   }
26771e77b095SAndrzej Warzynski };
267814867ffcSAndrzej Warzynski 
267914867ffcSAndrzej Warzynski /// Construct an `llvm.extractvalue` instruction. It will return value at
268014867ffcSAndrzej Warzynski /// element \p x from  \p tuple.
268114867ffcSAndrzej Warzynski mlir::LLVM::ExtractValueOp
268214867ffcSAndrzej Warzynski genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty,
268314867ffcSAndrzej Warzynski                          mlir::ConversionPatternRewriter &rewriter,
268414867ffcSAndrzej Warzynski                          mlir::MLIRContext *ctx, int x) {
268514867ffcSAndrzej Warzynski   auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x));
268614867ffcSAndrzej Warzynski   auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x];
268714867ffcSAndrzej Warzynski   return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx);
268814867ffcSAndrzej Warzynski }
268914867ffcSAndrzej Warzynski 
26906c3d7fd4SAndrzej Warzynski /// Convert `!fir.boxchar_len` to  `!llvm.extractvalue` for the 2nd part of the
26916c3d7fd4SAndrzej Warzynski /// boxchar.
26926c3d7fd4SAndrzej Warzynski struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> {
26936c3d7fd4SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
26946c3d7fd4SAndrzej Warzynski 
26956c3d7fd4SAndrzej Warzynski   mlir::LogicalResult
26966c3d7fd4SAndrzej Warzynski   matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
26976c3d7fd4SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
26986c3d7fd4SAndrzej Warzynski     mlir::Value boxChar = adaptor.getOperands()[0];
26996c3d7fd4SAndrzej Warzynski     mlir::Location loc = boxChar.getLoc();
27006c3d7fd4SAndrzej Warzynski     mlir::MLIRContext *ctx = boxChar.getContext();
27016c3d7fd4SAndrzej Warzynski     mlir::Type returnValTy = boxCharLen.getResult().getType();
27026c3d7fd4SAndrzej Warzynski 
27036c3d7fd4SAndrzej Warzynski     constexpr int boxcharLenIdx = 1;
27046c3d7fd4SAndrzej Warzynski     mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex(
27056c3d7fd4SAndrzej Warzynski         loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx);
27066c3d7fd4SAndrzej Warzynski     mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
27076c3d7fd4SAndrzej Warzynski     rewriter.replaceOp(boxCharLen, lenAfterCast);
27086c3d7fd4SAndrzej Warzynski 
27096c3d7fd4SAndrzej Warzynski     return success();
27106c3d7fd4SAndrzej Warzynski   }
27116c3d7fd4SAndrzej Warzynski };
27126c3d7fd4SAndrzej Warzynski 
271314867ffcSAndrzej Warzynski /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
271414867ffcSAndrzej Warzynski /// the character buffer and one for the buffer length.
271514867ffcSAndrzej Warzynski struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> {
271614867ffcSAndrzej Warzynski   using FIROpConversion::FIROpConversion;
271714867ffcSAndrzej Warzynski 
271814867ffcSAndrzej Warzynski   mlir::LogicalResult
271914867ffcSAndrzej Warzynski   matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
272014867ffcSAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
272114867ffcSAndrzej Warzynski     MLIRContext *ctx = unboxchar.getContext();
272214867ffcSAndrzej Warzynski 
272314867ffcSAndrzej Warzynski     mlir::Type lenTy = convertType(unboxchar.getType(1));
272414867ffcSAndrzej Warzynski     mlir::Value tuple = adaptor.getOperands()[0];
272514867ffcSAndrzej Warzynski     mlir::Type tupleTy = tuple.getType();
272614867ffcSAndrzej Warzynski 
272714867ffcSAndrzej Warzynski     mlir::Location loc = unboxchar.getLoc();
272814867ffcSAndrzej Warzynski     mlir::Value ptrToBuffer =
272914867ffcSAndrzej Warzynski         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0);
273014867ffcSAndrzej Warzynski 
273114867ffcSAndrzej Warzynski     mlir::LLVM::ExtractValueOp len =
273214867ffcSAndrzej Warzynski         genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1);
273314867ffcSAndrzej Warzynski     mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
273414867ffcSAndrzej Warzynski 
273514867ffcSAndrzej Warzynski     rewriter.replaceOp(unboxchar,
273614867ffcSAndrzej Warzynski                        ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
273714867ffcSAndrzej Warzynski     return success();
273814867ffcSAndrzej Warzynski   }
273914867ffcSAndrzej Warzynski };
274014867ffcSAndrzej Warzynski 
2741cc505c0bSKiran Chandramohan /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
2742cc505c0bSKiran Chandramohan /// components.
2743cc505c0bSKiran Chandramohan /// TODO: Part of supporting Fortran 2003 procedure pointers.
2744cc505c0bSKiran Chandramohan struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> {
2745cc505c0bSKiran Chandramohan   using FIROpConversion::FIROpConversion;
2746cc505c0bSKiran Chandramohan 
2747cc505c0bSKiran Chandramohan   mlir::LogicalResult
2748cc505c0bSKiran Chandramohan   matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
2749cc505c0bSKiran Chandramohan                   mlir::ConversionPatternRewriter &rewriter) const override {
27507ce8c6fcSKiran Chandramohan     TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
27517ce8c6fcSKiran Chandramohan     return failure();
2752cc505c0bSKiran Chandramohan   }
2753cc505c0bSKiran Chandramohan };
2754cc505c0bSKiran Chandramohan 
2755e6c66ef5SAndrzej Warzynski /// Convert `fir.field_index`. The conversion depends on whether the size of
2756e6c66ef5SAndrzej Warzynski /// the record is static or dynamic.
2757e6c66ef5SAndrzej Warzynski struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> {
2758e6c66ef5SAndrzej Warzynski   using FIROpConversion::FIROpConversion;
2759e6c66ef5SAndrzej Warzynski 
2760e6c66ef5SAndrzej Warzynski   // NB: most field references should be resolved by this point
2761e6c66ef5SAndrzej Warzynski   mlir::LogicalResult
2762e6c66ef5SAndrzej Warzynski   matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2763e6c66ef5SAndrzej Warzynski                   mlir::ConversionPatternRewriter &rewriter) const override {
2764e6c66ef5SAndrzej Warzynski     auto recTy = field.on_type().cast<fir::RecordType>();
2765e6c66ef5SAndrzej Warzynski     unsigned index = recTy.getFieldIndex(field.field_id());
2766e6c66ef5SAndrzej Warzynski 
2767e6c66ef5SAndrzej Warzynski     if (!fir::hasDynamicSize(recTy)) {
2768e6c66ef5SAndrzej Warzynski       // Derived type has compile-time constant layout. Return index of the
2769e6c66ef5SAndrzej Warzynski       // component type in the parent type (to be used in GEP).
2770e6c66ef5SAndrzej Warzynski       rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2771e6c66ef5SAndrzej Warzynski                                     field.getLoc(), rewriter, index)});
2772e6c66ef5SAndrzej Warzynski       return success();
2773e6c66ef5SAndrzej Warzynski     }
2774e6c66ef5SAndrzej Warzynski 
2775e6c66ef5SAndrzej Warzynski     // Derived type has compile-time constant layout. Call the compiler
2776e6c66ef5SAndrzej Warzynski     // generated function to determine the byte offset of the field at runtime.
2777e6c66ef5SAndrzej Warzynski     // This returns a non-constant.
2778e6c66ef5SAndrzej Warzynski     FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2779e6c66ef5SAndrzej Warzynski         field.getContext(), getOffsetMethodName(recTy, field.field_id()));
2780e6c66ef5SAndrzej Warzynski     NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2781e6c66ef5SAndrzej Warzynski     NamedAttribute fieldAttr = rewriter.getNamedAttr(
2782e6c66ef5SAndrzej Warzynski         "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2783e6c66ef5SAndrzej Warzynski     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2784e6c66ef5SAndrzej Warzynski         field, lowerTy().offsetType(), adaptor.getOperands(),
2785e6c66ef5SAndrzej Warzynski         llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr});
2786e6c66ef5SAndrzej Warzynski     return success();
2787e6c66ef5SAndrzej Warzynski   }
2788e6c66ef5SAndrzej Warzynski 
2789e6c66ef5SAndrzej Warzynski   // Re-Construct the name of the compiler generated method that calculates the
2790e6c66ef5SAndrzej Warzynski   // offset
2791e6c66ef5SAndrzej Warzynski   inline static std::string getOffsetMethodName(fir::RecordType recTy,
2792e6c66ef5SAndrzej Warzynski                                                 llvm::StringRef field) {
2793e6c66ef5SAndrzej Warzynski     return recTy.getName().str() + "P." + field.str() + ".offset";
2794e6c66ef5SAndrzej Warzynski   }
2795e6c66ef5SAndrzej Warzynski };
2796e6c66ef5SAndrzej Warzynski 
2797044d5b5dSValentin Clement } // namespace
2798044d5b5dSValentin Clement 
2799044d5b5dSValentin Clement namespace {
2800044d5b5dSValentin Clement /// Convert FIR dialect to LLVM dialect
2801044d5b5dSValentin Clement ///
2802044d5b5dSValentin Clement /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
2803044d5b5dSValentin Clement /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
2804044d5b5dSValentin Clement ///
2805044d5b5dSValentin Clement /// This pass is not complete yet. We are upstreaming it in small patches.
2806044d5b5dSValentin Clement class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
2807044d5b5dSValentin Clement public:
2808044d5b5dSValentin Clement   mlir::ModuleOp getModule() { return getOperation(); }
2809044d5b5dSValentin Clement 
2810044d5b5dSValentin Clement   void runOnOperation() override final {
28117b5132daSValentin Clement     auto mod = getModule();
28127b5132daSValentin Clement     if (!forcedTargetTriple.empty()) {
28137b5132daSValentin Clement       fir::setTargetTriple(mod, forcedTargetTriple);
28147b5132daSValentin Clement     }
28157b5132daSValentin Clement 
2816044d5b5dSValentin Clement     auto *context = getModule().getContext();
2817044d5b5dSValentin Clement     fir::LLVMTypeConverter typeConverter{getModule()};
2818044d5b5dSValentin Clement     mlir::OwningRewritePatternList pattern(context);
2819df3b9810SValentin Clement     pattern.insert<
2820420ad7ceSAndrzej Warzynski         AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
28211a2ec667SValentin Clement         AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion,
28221a2ec667SValentin Clement         BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion,
2823cc505c0bSKiran Chandramohan         BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion,
2824cc505c0bSKiran Chandramohan         BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion,
2825cc505c0bSKiran Chandramohan         CmpcOpConversion, ConstcOpConversion, ConvertOpConversion,
2826cc505c0bSKiran Chandramohan         DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion,
2827cc505c0bSKiran Chandramohan         DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion,
2828e6c66ef5SAndrzej Warzynski         EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion,
2829e6c66ef5SAndrzej Warzynski         FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion,
2830e6c66ef5SAndrzej Warzynski         GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion,
2831cdc476abSDiana Picus         InsertValueOpConversion, IsPresentOpConversion,
2832cdc476abSDiana Picus         LenParamIndexOpConversion, LoadOpConversion, NegcOpConversion,
2833cdc476abSDiana Picus         NoReassocOpConversion, MulcOpConversion, SelectCaseOpConversion,
2834cdc476abSDiana Picus         SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
2835cdc476abSDiana Picus         ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
2836cdc476abSDiana Picus         SliceOpConversion, StoreOpConversion, StringLitOpConversion,
2837cdc476abSDiana Picus         SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion,
28385d27abe6SValentin Clement         UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion,
2839*fa517555SKiran Chandramohan         XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(
2840*fa517555SKiran Chandramohan         typeConverter);
2841044d5b5dSValentin Clement     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
2842044d5b5dSValentin Clement     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
2843044d5b5dSValentin Clement                                                             pattern);
2844044d5b5dSValentin Clement     mlir::ConversionTarget target{*context};
2845044d5b5dSValentin Clement     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
2846044d5b5dSValentin Clement 
2847044d5b5dSValentin Clement     // required NOPs for applying a full conversion
2848044d5b5dSValentin Clement     target.addLegalOp<mlir::ModuleOp>();
2849044d5b5dSValentin Clement 
2850044d5b5dSValentin Clement     // apply the patterns
2851044d5b5dSValentin Clement     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
2852044d5b5dSValentin Clement                                                std::move(pattern)))) {
2853044d5b5dSValentin Clement       signalPassFailure();
2854044d5b5dSValentin Clement     }
2855044d5b5dSValentin Clement   }
2856044d5b5dSValentin Clement };
2857044d5b5dSValentin Clement } // namespace
2858044d5b5dSValentin Clement 
2859044d5b5dSValentin Clement std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
2860044d5b5dSValentin Clement   return std::make_unique<FIRToLLVMLowering>();
2861044d5b5dSValentin Clement }
2862