[MemProf] Templatize CallStackRadixTreeBuilder (NFC) (#117014)
[llvm-project.git] / flang / lib / Optimizer / CodeGen / CodeGen.cpp
blob2eeb182735094fc1e802581278e95ee0bd94257b
1 //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
13 #include "flang/Optimizer/CodeGen/CodeGen.h"
15 #include "flang/Optimizer/CodeGen/CGOps.h"
16 #include "flang/Optimizer/CodeGen/CodeGenOpenMP.h"
17 #include "flang/Optimizer/CodeGen/FIROpPatterns.h"
18 #include "flang/Optimizer/CodeGen/TypeConverter.h"
19 #include "flang/Optimizer/Dialect/FIRAttr.h"
20 #include "flang/Optimizer/Dialect/FIROps.h"
21 #include "flang/Optimizer/Dialect/FIRType.h"
22 #include "flang/Optimizer/Support/DataLayout.h"
23 #include "flang/Optimizer/Support/InternalNames.h"
24 #include "flang/Optimizer/Support/TypeCode.h"
25 #include "flang/Optimizer/Support/Utils.h"
26 #include "flang/Runtime/allocator-registry.h"
27 #include "flang/Runtime/descriptor.h"
28 #include "flang/Semantics/runtime-type-info.h"
29 #include "mlir/Conversion/ArithCommon/AttrToLLVMConverter.h"
30 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h"
31 #include "mlir/Conversion/ComplexToLLVM/ComplexToLLVM.h"
32 #include "mlir/Conversion/ComplexToStandard/ComplexToStandard.h"
33 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h"
34 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h"
35 #include "mlir/Conversion/LLVMCommon/Pattern.h"
36 #include "mlir/Conversion/MathToFuncs/MathToFuncs.h"
37 #include "mlir/Conversion/MathToLLVM/MathToLLVM.h"
38 #include "mlir/Conversion/MathToLibm/MathToLibm.h"
39 #include "mlir/Conversion/MathToROCDL/MathToROCDL.h"
40 #include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
41 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h"
42 #include "mlir/Dialect/Arith/IR/Arith.h"
43 #include "mlir/Dialect/DLTI/DLTI.h"
44 #include "mlir/Dialect/GPU/IR/GPUDialect.h"
45 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h"
46 #include "mlir/Dialect/LLVMIR/LLVMDialect.h"
47 #include "mlir/Dialect/LLVMIR/Transforms/AddComdats.h"
48 #include "mlir/Dialect/OpenACC/OpenACC.h"
49 #include "mlir/Dialect/OpenMP/OpenMPDialect.h"
50 #include "mlir/IR/BuiltinTypes.h"
51 #include "mlir/IR/Matchers.h"
52 #include "mlir/Pass/Pass.h"
53 #include "mlir/Pass/PassManager.h"
54 #include "mlir/Target/LLVMIR/Import.h"
55 #include "mlir/Target/LLVMIR/ModuleTranslation.h"
56 #include "llvm/ADT/ArrayRef.h"
57 #include "llvm/ADT/TypeSwitch.h"
59 namespace fir {
60 #define GEN_PASS_DEF_FIRTOLLVMLOWERING
61 #include "flang/Optimizer/CodeGen/CGPasses.h.inc"
62 } // namespace fir
64 #define DEBUG_TYPE "flang-codegen"
66 // TODO: This should really be recovered from the specified target.
67 static constexpr unsigned defaultAlign = 8;
69 /// `fir.box` attribute values as defined for CFI_attribute_t in
70 /// flang/ISO_Fortran_binding.h.
71 static constexpr unsigned kAttrPointer = CFI_attribute_pointer;
72 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable;
74 static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context,
75 unsigned addressSpace = 0) {
76 return mlir::LLVM::LLVMPointerType::get(context, addressSpace);
79 static inline mlir::Type getI8Type(mlir::MLIRContext *context) {
80 return mlir::IntegerType::get(context, 8);
83 static mlir::LLVM::ConstantOp
84 genConstantIndex(mlir::Location loc, mlir::Type ity,
85 mlir::ConversionPatternRewriter &rewriter,
86 std::int64_t offset) {
87 auto cattr = rewriter.getI64IntegerAttr(offset);
88 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr);
91 static mlir::Block *createBlock(mlir::ConversionPatternRewriter &rewriter,
92 mlir::Block *insertBefore) {
93 assert(insertBefore && "expected valid insertion block");
94 return rewriter.createBlock(insertBefore->getParent(),
95 mlir::Region::iterator(insertBefore));
98 /// Extract constant from a value that must be the result of one of the
99 /// ConstantOp operations.
100 static int64_t getConstantIntValue(mlir::Value val) {
101 if (auto constVal = fir::getIntIfConstant(val))
102 return *constVal;
103 fir::emitFatalError(val.getLoc(), "must be a constant");
106 static unsigned getTypeDescFieldId(mlir::Type ty) {
107 auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty));
108 return isArray ? kOptTypePtrPosInBox : kDimsPosInBox;
110 static unsigned getLenParamFieldId(mlir::Type ty) {
111 return getTypeDescFieldId(ty) + 1;
114 static llvm::SmallVector<mlir::NamedAttribute>
115 addLLVMOpBundleAttrs(mlir::ConversionPatternRewriter &rewriter,
116 llvm::ArrayRef<mlir::NamedAttribute> attrs,
117 int32_t numCallOperands) {
118 llvm::SmallVector<mlir::NamedAttribute> newAttrs;
119 newAttrs.reserve(attrs.size() + 2);
121 for (mlir::NamedAttribute attr : attrs) {
122 if (attr.getName() != "operandSegmentSizes")
123 newAttrs.push_back(attr);
126 newAttrs.push_back(rewriter.getNamedAttr(
127 "operandSegmentSizes",
128 rewriter.getDenseI32ArrayAttr({numCallOperands, 0})));
129 newAttrs.push_back(rewriter.getNamedAttr("op_bundle_sizes",
130 rewriter.getDenseI32ArrayAttr({})));
131 return newAttrs;
134 namespace {
135 /// Lower `fir.address_of` operation to `llvm.address_of` operation.
136 struct AddrOfOpConversion : public fir::FIROpConversion<fir::AddrOfOp> {
137 using FIROpConversion::FIROpConversion;
139 llvm::LogicalResult
140 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
141 mlir::ConversionPatternRewriter &rewriter) const override {
142 auto ty = convertType(addr.getType());
143 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
144 addr, ty, addr.getSymbol().getRootReference().getValue());
145 return mlir::success();
148 } // namespace
150 /// Lookup the function to compute the memory size of this parametric derived
151 /// type. The size of the object may depend on the LEN type parameters of the
152 /// derived type.
153 static mlir::LLVM::LLVMFuncOp
154 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op,
155 mlir::ConversionPatternRewriter &rewriter) {
156 auto module = op->getParentOfType<mlir::ModuleOp>();
157 std::string name = recTy.getName().str() + "P.mem.size";
158 if (auto memSizeFunc = module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name))
159 return memSizeFunc;
160 TODO(op.getLoc(), "did not find allocation function");
163 // Compute the alloc scale size (constant factors encoded in the array type).
164 // We do this for arrays without a constant interior or arrays of character with
165 // dynamic length arrays, since those are the only ones that get decayed to a
166 // pointer to the element type.
167 template <typename OP>
168 static mlir::Value
169 genAllocationScaleSize(OP op, mlir::Type ity,
170 mlir::ConversionPatternRewriter &rewriter) {
171 mlir::Location loc = op.getLoc();
172 mlir::Type dataTy = op.getInType();
173 auto seqTy = mlir::dyn_cast<fir::SequenceType>(dataTy);
174 fir::SequenceType::Extent constSize = 1;
175 if (seqTy) {
176 int constRows = seqTy.getConstantRows();
177 const fir::SequenceType::ShapeRef &shape = seqTy.getShape();
178 if (constRows != static_cast<int>(shape.size())) {
179 for (auto extent : shape) {
180 if (constRows-- > 0)
181 continue;
182 if (extent != fir::SequenceType::getUnknownExtent())
183 constSize *= extent;
188 if (constSize != 1) {
189 mlir::Value constVal{
190 genConstantIndex(loc, ity, rewriter, constSize).getResult()};
191 return constVal;
193 return nullptr;
196 namespace {
197 struct DeclareOpConversion : public fir::FIROpConversion<fir::cg::XDeclareOp> {
198 public:
199 using FIROpConversion::FIROpConversion;
200 llvm::LogicalResult
201 matchAndRewrite(fir::cg::XDeclareOp declareOp, OpAdaptor adaptor,
202 mlir::ConversionPatternRewriter &rewriter) const override {
203 auto memRef = adaptor.getOperands()[0];
204 if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(declareOp.getLoc())) {
205 if (auto varAttr =
206 mlir::dyn_cast_or_null<mlir::LLVM::DILocalVariableAttr>(
207 fusedLoc.getMetadata())) {
208 rewriter.create<mlir::LLVM::DbgDeclareOp>(memRef.getLoc(), memRef,
209 varAttr, nullptr);
212 rewriter.replaceOp(declareOp, memRef);
213 return mlir::success();
216 } // namespace
218 namespace {
219 /// convert to LLVM IR dialect `alloca`
220 struct AllocaOpConversion : public fir::FIROpConversion<fir::AllocaOp> {
221 using FIROpConversion::FIROpConversion;
223 llvm::LogicalResult
224 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor,
225 mlir::ConversionPatternRewriter &rewriter) const override {
226 mlir::ValueRange operands = adaptor.getOperands();
227 auto loc = alloc.getLoc();
228 mlir::Type ity = lowerTy().indexType();
229 unsigned i = 0;
230 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult();
231 mlir::Type firObjType = fir::unwrapRefType(alloc.getType());
232 mlir::Type llvmObjectType = convertObjectType(firObjType);
233 if (alloc.hasLenParams()) {
234 unsigned end = alloc.numLenParams();
235 llvm::SmallVector<mlir::Value> lenParams;
236 for (; i < end; ++i)
237 lenParams.push_back(operands[i]);
238 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType());
239 if (auto chrTy = mlir::dyn_cast<fir::CharacterType>(scalarType)) {
240 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen(
241 chrTy.getContext(), chrTy.getFKind());
242 llvmObjectType = convertType(rawCharTy);
243 assert(end == 1);
244 size = integerCast(loc, rewriter, ity, lenParams[0], /*fold=*/true);
245 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(scalarType)) {
246 mlir::LLVM::LLVMFuncOp memSizeFn =
247 getDependentTypeMemSizeFn(recTy, alloc, rewriter);
248 if (!memSizeFn)
249 emitError(loc, "did not find allocation function");
250 mlir::NamedAttribute attr = rewriter.getNamedAttr(
251 "callee", mlir::SymbolRefAttr::get(memSizeFn));
252 auto call = rewriter.create<mlir::LLVM::CallOp>(
253 loc, ity, lenParams,
254 addLLVMOpBundleAttrs(rewriter, {attr}, lenParams.size()));
255 size = call.getResult();
256 llvmObjectType = ::getI8Type(alloc.getContext());
257 } else {
258 return emitError(loc, "unexpected type ")
259 << scalarType << " with type parameters";
262 if (auto scaleSize = genAllocationScaleSize(alloc, ity, rewriter))
263 size =
264 rewriter.createOrFold<mlir::LLVM::MulOp>(loc, ity, size, scaleSize);
265 if (alloc.hasShapeOperands()) {
266 unsigned end = operands.size();
267 for (; i < end; ++i)
268 size = rewriter.createOrFold<mlir::LLVM::MulOp>(
269 loc, ity, size,
270 integerCast(loc, rewriter, ity, operands[i], /*fold=*/true));
273 unsigned allocaAs = getAllocaAddressSpace(rewriter);
274 unsigned programAs = getProgramAddressSpace(rewriter);
276 if (mlir::isa<mlir::LLVM::ConstantOp>(size.getDefiningOp())) {
277 // Set the Block in which the llvm alloca should be inserted.
278 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp();
279 mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent();
280 mlir::Block *insertBlock =
281 getBlockForAllocaInsert(parentOp, parentRegion);
283 // The old size might have had multiple users, some at a broader scope
284 // than we can safely outline the alloca to. As it is only an
285 // llvm.constant operation, it is faster to clone it than to calculate the
286 // dominance to see if it really should be moved.
287 mlir::Operation *clonedSize = rewriter.clone(*size.getDefiningOp());
288 size = clonedSize->getResult(0);
289 clonedSize->moveBefore(&insertBlock->front());
290 rewriter.setInsertionPointAfter(size.getDefiningOp());
293 // NOTE: we used to pass alloc->getAttrs() in the builder for non opaque
294 // pointers! Only propagate pinned and bindc_name to help debugging, but
295 // this should have no functional purpose (and passing the operand segment
296 // attribute like before is certainly bad).
297 auto llvmAlloc = rewriter.create<mlir::LLVM::AllocaOp>(
298 loc, ::getLlvmPtrType(alloc.getContext(), allocaAs), llvmObjectType,
299 size);
300 if (alloc.getPinned())
301 llvmAlloc->setDiscardableAttr(alloc.getPinnedAttrName(),
302 alloc.getPinnedAttr());
303 if (alloc.getBindcName())
304 llvmAlloc->setDiscardableAttr(alloc.getBindcNameAttrName(),
305 alloc.getBindcNameAttr());
306 if (allocaAs == programAs) {
307 rewriter.replaceOp(alloc, llvmAlloc);
308 } else {
309 // if our allocation address space, is not the same as the program address
310 // space, then we must emit a cast to the program address space before
311 // use. An example case would be on AMDGPU, where the allocation address
312 // space is the numeric value 5 (private), and the program address space
313 // is 0 (generic).
314 rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(
315 alloc, ::getLlvmPtrType(alloc.getContext(), programAs), llvmAlloc);
317 return mlir::success();
320 } // namespace
322 namespace {
323 /// Lower `fir.box_addr` to the sequence of operations to extract the first
324 /// element of the box.
325 struct BoxAddrOpConversion : public fir::FIROpConversion<fir::BoxAddrOp> {
326 using FIROpConversion::FIROpConversion;
328 llvm::LogicalResult
329 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor,
330 mlir::ConversionPatternRewriter &rewriter) const override {
331 mlir::Value a = adaptor.getOperands()[0];
332 auto loc = boxaddr.getLoc();
333 if (auto argty =
334 mlir::dyn_cast<fir::BaseBoxType>(boxaddr.getVal().getType())) {
335 TypePair boxTyPair = getBoxTypePair(argty);
336 rewriter.replaceOp(boxaddr,
337 getBaseAddrFromBox(loc, boxTyPair, a, rewriter));
338 } else {
339 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, a, 0);
341 return mlir::success();
345 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the
346 /// boxchar.
347 struct BoxCharLenOpConversion : public fir::FIROpConversion<fir::BoxCharLenOp> {
348 using FIROpConversion::FIROpConversion;
350 llvm::LogicalResult
351 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor,
352 mlir::ConversionPatternRewriter &rewriter) const override {
353 mlir::Value boxChar = adaptor.getOperands()[0];
354 mlir::Location loc = boxChar.getLoc();
355 mlir::Type returnValTy = boxCharLen.getResult().getType();
357 constexpr int boxcharLenIdx = 1;
358 auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, boxChar,
359 boxcharLenIdx);
360 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len);
361 rewriter.replaceOp(boxCharLen, lenAfterCast);
363 return mlir::success();
367 /// Lower `fir.box_dims` to a sequence of operations to extract the requested
368 /// dimension information from the boxed value.
369 /// Result in a triple set of GEPs and loads.
370 struct BoxDimsOpConversion : public fir::FIROpConversion<fir::BoxDimsOp> {
371 using FIROpConversion::FIROpConversion;
373 llvm::LogicalResult
374 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor,
375 mlir::ConversionPatternRewriter &rewriter) const override {
376 llvm::SmallVector<mlir::Type, 3> resultTypes = {
377 convertType(boxdims.getResult(0).getType()),
378 convertType(boxdims.getResult(1).getType()),
379 convertType(boxdims.getResult(2).getType()),
381 TypePair boxTyPair = getBoxTypePair(boxdims.getVal().getType());
382 auto results = getDimsFromBox(boxdims.getLoc(), resultTypes, boxTyPair,
383 adaptor.getOperands()[0],
384 adaptor.getOperands()[1], rewriter);
385 rewriter.replaceOp(boxdims, results);
386 return mlir::success();
390 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of
391 /// an element in the boxed value.
392 struct BoxEleSizeOpConversion : public fir::FIROpConversion<fir::BoxEleSizeOp> {
393 using FIROpConversion::FIROpConversion;
395 llvm::LogicalResult
396 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor,
397 mlir::ConversionPatternRewriter &rewriter) const override {
398 mlir::Value box = adaptor.getOperands()[0];
399 auto loc = boxelesz.getLoc();
400 auto ty = convertType(boxelesz.getType());
401 TypePair boxTyPair = getBoxTypePair(boxelesz.getVal().getType());
402 auto elemSize = getElementSizeFromBox(loc, ty, boxTyPair, box, rewriter);
403 rewriter.replaceOp(boxelesz, elemSize);
404 return mlir::success();
408 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the
409 /// boxed value was from an ALLOCATABLE entity.
410 struct BoxIsAllocOpConversion : public fir::FIROpConversion<fir::BoxIsAllocOp> {
411 using FIROpConversion::FIROpConversion;
413 llvm::LogicalResult
414 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor,
415 mlir::ConversionPatternRewriter &rewriter) const override {
416 mlir::Value box = adaptor.getOperands()[0];
417 auto loc = boxisalloc.getLoc();
418 TypePair boxTyPair = getBoxTypePair(boxisalloc.getVal().getType());
419 mlir::Value check =
420 genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrAllocatable);
421 rewriter.replaceOp(boxisalloc, check);
422 return mlir::success();
426 /// Lower `fir.box_isarray` to a sequence of operations to determine if the
427 /// boxed is an array.
428 struct BoxIsArrayOpConversion : public fir::FIROpConversion<fir::BoxIsArrayOp> {
429 using FIROpConversion::FIROpConversion;
431 llvm::LogicalResult
432 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor,
433 mlir::ConversionPatternRewriter &rewriter) const override {
434 mlir::Value a = adaptor.getOperands()[0];
435 auto loc = boxisarray.getLoc();
436 TypePair boxTyPair = getBoxTypePair(boxisarray.getVal().getType());
437 mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter);
438 mlir::Value c0 = genConstantIndex(loc, rank.getType(), rewriter, 0);
439 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
440 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0);
441 return mlir::success();
445 /// Lower `fir.box_isptr` to a sequence of operations to determined if the
446 /// boxed value was from a POINTER entity.
447 struct BoxIsPtrOpConversion : public fir::FIROpConversion<fir::BoxIsPtrOp> {
448 using FIROpConversion::FIROpConversion;
450 llvm::LogicalResult
451 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor,
452 mlir::ConversionPatternRewriter &rewriter) const override {
453 mlir::Value box = adaptor.getOperands()[0];
454 auto loc = boxisptr.getLoc();
455 TypePair boxTyPair = getBoxTypePair(boxisptr.getVal().getType());
456 mlir::Value check =
457 genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrPointer);
458 rewriter.replaceOp(boxisptr, check);
459 return mlir::success();
463 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from
464 /// the box.
465 struct BoxRankOpConversion : public fir::FIROpConversion<fir::BoxRankOp> {
466 using FIROpConversion::FIROpConversion;
468 llvm::LogicalResult
469 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor,
470 mlir::ConversionPatternRewriter &rewriter) const override {
471 mlir::Value a = adaptor.getOperands()[0];
472 auto loc = boxrank.getLoc();
473 mlir::Type ty = convertType(boxrank.getType());
474 TypePair boxTyPair =
475 getBoxTypePair(fir::unwrapRefType(boxrank.getBox().getType()));
476 mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter);
477 mlir::Value result = integerCast(loc, rewriter, ty, rank);
478 rewriter.replaceOp(boxrank, result);
479 return mlir::success();
483 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the
484 /// boxproc.
485 /// TODO: Part of supporting Fortran 2003 procedure pointers.
486 struct BoxProcHostOpConversion
487 : public fir::FIROpConversion<fir::BoxProcHostOp> {
488 using FIROpConversion::FIROpConversion;
490 llvm::LogicalResult
491 matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor,
492 mlir::ConversionPatternRewriter &rewriter) const override {
493 TODO(boxprochost.getLoc(), "fir.boxproc_host codegen");
494 return mlir::failure();
498 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type
499 /// descriptor from the box.
500 struct BoxTypeDescOpConversion
501 : public fir::FIROpConversion<fir::BoxTypeDescOp> {
502 using FIROpConversion::FIROpConversion;
504 llvm::LogicalResult
505 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor,
506 mlir::ConversionPatternRewriter &rewriter) const override {
507 mlir::Value box = adaptor.getOperands()[0];
508 TypePair boxTyPair = getBoxTypePair(boxtypedesc.getBox().getType());
509 auto typeDescAddr =
510 loadTypeDescAddress(boxtypedesc.getLoc(), boxTyPair, box, rewriter);
511 rewriter.replaceOp(boxtypedesc, typeDescAddr);
512 return mlir::success();
516 /// Lower `fir.box_typecode` to a sequence of operations to extract the type
517 /// code in the boxed value.
518 struct BoxTypeCodeOpConversion
519 : public fir::FIROpConversion<fir::BoxTypeCodeOp> {
520 using FIROpConversion::FIROpConversion;
522 llvm::LogicalResult
523 matchAndRewrite(fir::BoxTypeCodeOp op, OpAdaptor adaptor,
524 mlir::ConversionPatternRewriter &rewriter) const override {
525 mlir::Value box = adaptor.getOperands()[0];
526 auto loc = box.getLoc();
527 auto ty = convertType(op.getType());
528 TypePair boxTyPair = getBoxTypePair(op.getBox().getType());
529 auto typeCode =
530 getValueFromBox(loc, boxTyPair, box, ty, rewriter, kTypePosInBox);
531 rewriter.replaceOp(op, typeCode);
532 return mlir::success();
536 /// Lower `fir.string_lit` to LLVM IR dialect operation.
537 struct StringLitOpConversion : public fir::FIROpConversion<fir::StringLitOp> {
538 using FIROpConversion::FIROpConversion;
540 llvm::LogicalResult
541 matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor,
542 mlir::ConversionPatternRewriter &rewriter) const override {
543 auto ty = convertType(constop.getType());
544 auto attr = constop.getValue();
545 if (mlir::isa<mlir::StringAttr>(attr)) {
546 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr);
547 return mlir::success();
550 auto charTy = mlir::cast<fir::CharacterType>(constop.getType());
551 unsigned bits = lowerTy().characterBitsize(charTy);
552 mlir::Type intTy = rewriter.getIntegerType(bits);
553 mlir::Location loc = constop.getLoc();
554 mlir::Value cst = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
555 if (auto arr = mlir::dyn_cast<mlir::DenseElementsAttr>(attr)) {
556 cst = rewriter.create<mlir::LLVM::ConstantOp>(loc, ty, arr);
557 } else if (auto arr = mlir::dyn_cast<mlir::ArrayAttr>(attr)) {
558 for (auto a : llvm::enumerate(arr.getValue())) {
559 // convert each character to a precise bitsize
560 auto elemAttr = mlir::IntegerAttr::get(
561 intTy,
562 mlir::cast<mlir::IntegerAttr>(a.value()).getValue().zextOrTrunc(
563 bits));
564 auto elemCst =
565 rewriter.create<mlir::LLVM::ConstantOp>(loc, intTy, elemAttr);
566 cst = rewriter.create<mlir::LLVM::InsertValueOp>(loc, cst, elemCst,
567 a.index());
569 } else {
570 return mlir::failure();
572 rewriter.replaceOp(constop, cst);
573 return mlir::success();
577 /// `fir.call` -> `llvm.call`
578 struct CallOpConversion : public fir::FIROpConversion<fir::CallOp> {
579 using FIROpConversion::FIROpConversion;
581 llvm::LogicalResult
582 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
583 mlir::ConversionPatternRewriter &rewriter) const override {
584 llvm::SmallVector<mlir::Type> resultTys;
585 for (auto r : call.getResults())
586 resultTys.push_back(convertType(r.getType()));
587 // Convert arith::FastMathFlagsAttr to LLVM::FastMathFlagsAttr.
588 mlir::arith::AttrConvertFastMathToLLVM<fir::CallOp, mlir::LLVM::CallOp>
589 attrConvert(call);
590 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
591 call, resultTys, adaptor.getOperands(),
592 addLLVMOpBundleAttrs(rewriter, attrConvert.getAttrs(),
593 adaptor.getOperands().size()));
594 return mlir::success();
597 } // namespace
599 static mlir::Type getComplexEleTy(mlir::Type complex) {
600 return mlir::cast<mlir::ComplexType>(complex).getElementType();
603 namespace {
604 /// Compare complex values
606 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une).
608 /// For completeness, all other comparison are done on the real component only.
609 struct CmpcOpConversion : public fir::FIROpConversion<fir::CmpcOp> {
610 using FIROpConversion::FIROpConversion;
612 llvm::LogicalResult
613 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor,
614 mlir::ConversionPatternRewriter &rewriter) const override {
615 mlir::ValueRange operands = adaptor.getOperands();
616 mlir::Type resTy = convertType(cmp.getType());
617 mlir::Location loc = cmp.getLoc();
618 mlir::LLVM::FastmathFlags fmf =
619 mlir::arith::convertArithFastMathFlagsToLLVM(cmp.getFastmath());
620 mlir::LLVM::FCmpPredicate pred =
621 static_cast<mlir::LLVM::FCmpPredicate>(cmp.getPredicate());
622 auto rcp = rewriter.create<mlir::LLVM::FCmpOp>(
623 loc, resTy, pred,
624 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 0),
625 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 0), fmf);
626 auto icp = rewriter.create<mlir::LLVM::FCmpOp>(
627 loc, resTy, pred,
628 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 1),
629 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 1), fmf);
630 llvm::SmallVector<mlir::Value, 2> cp = {rcp, icp};
631 switch (cmp.getPredicate()) {
632 case mlir::arith::CmpFPredicate::OEQ: // .EQ.
633 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp);
634 break;
635 case mlir::arith::CmpFPredicate::UNE: // .NE.
636 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp);
637 break;
638 default:
639 rewriter.replaceOp(cmp, rcp.getResult());
640 break;
642 return mlir::success();
646 /// convert value of from-type to value of to-type
647 struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> {
648 using FIROpConversion::FIROpConversion;
650 static bool isFloatingPointTy(mlir::Type ty) {
651 return mlir::isa<mlir::FloatType>(ty);
654 llvm::LogicalResult
655 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
656 mlir::ConversionPatternRewriter &rewriter) const override {
657 auto fromFirTy = convert.getValue().getType();
658 auto toFirTy = convert.getRes().getType();
659 auto fromTy = convertType(fromFirTy);
660 auto toTy = convertType(toFirTy);
661 mlir::Value op0 = adaptor.getOperands()[0];
663 if (fromFirTy == toFirTy) {
664 rewriter.replaceOp(convert, op0);
665 return mlir::success();
668 auto loc = convert.getLoc();
669 auto i1Type = mlir::IntegerType::get(convert.getContext(), 1);
671 if (mlir::isa<fir::RecordType>(toFirTy)) {
672 // Convert to compatible BIND(C) record type.
673 // Double check that the record types are compatible (it should have
674 // already been checked by the verifier).
675 assert(mlir::cast<fir::RecordType>(fromFirTy).getTypeList() ==
676 mlir::cast<fir::RecordType>(toFirTy).getTypeList() &&
677 "incompatible record types");
679 auto toStTy = mlir::cast<mlir::LLVM::LLVMStructType>(toTy);
680 mlir::Value val = rewriter.create<mlir::LLVM::UndefOp>(loc, toStTy);
681 auto indexTypeMap = toStTy.getSubelementIndexMap();
682 assert(indexTypeMap.has_value() && "invalid record type");
684 for (auto [attr, type] : indexTypeMap.value()) {
685 int64_t index = mlir::cast<mlir::IntegerAttr>(attr).getInt();
686 auto extVal =
687 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, index);
688 val =
689 rewriter.create<mlir::LLVM::InsertValueOp>(loc, val, extVal, index);
692 rewriter.replaceOp(convert, val);
693 return mlir::success();
696 if (mlir::isa<fir::LogicalType>(fromFirTy) ||
697 mlir::isa<fir::LogicalType>(toFirTy)) {
698 // By specification fir::LogicalType value may be any number,
699 // where non-zero value represents .true. and zero value represents
700 // .false.
702 // integer<->logical conversion requires value normalization.
703 // Conversion from wide logical to narrow logical must set the result
704 // to non-zero iff the input is non-zero - the easiest way to implement
705 // it is to compare the input agains zero and set the result to
706 // the canonical 0/1.
707 // Conversion from narrow logical to wide logical may be implemented
708 // as a zero or sign extension of the input, but it may use value
709 // normalization as well.
710 if (!mlir::isa<mlir::IntegerType>(fromTy) ||
711 !mlir::isa<mlir::IntegerType>(toTy))
712 return mlir::emitError(loc)
713 << "unsupported types for logical conversion: " << fromTy
714 << " -> " << toTy;
716 // Do folding for constant inputs.
717 if (auto constVal = fir::getIntIfConstant(op0)) {
718 mlir::Value normVal =
719 genConstantIndex(loc, toTy, rewriter, *constVal ? 1 : 0);
720 rewriter.replaceOp(convert, normVal);
721 return mlir::success();
724 // If the input is i1, then we can just zero extend it, and
725 // the result will be normalized.
726 if (fromTy == i1Type) {
727 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0);
728 return mlir::success();
731 // Compare the input with zero.
732 mlir::Value zero = genConstantIndex(loc, fromTy, rewriter, 0);
733 auto isTrue = rewriter.create<mlir::LLVM::ICmpOp>(
734 loc, mlir::LLVM::ICmpPredicate::ne, op0, zero);
736 // Zero extend the i1 isTrue result to the required type (unless it is i1
737 // itself).
738 if (toTy != i1Type)
739 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, isTrue);
740 else
741 rewriter.replaceOp(convert, isTrue.getResult());
743 return mlir::success();
746 if (fromTy == toTy) {
747 rewriter.replaceOp(convert, op0);
748 return mlir::success();
750 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
751 unsigned toBits, mlir::Type toTy) -> mlir::Value {
752 if (fromBits == toBits) {
753 // TODO: Converting between two floating-point representations with the
754 // same bitwidth is not allowed for now.
755 mlir::emitError(loc,
756 "cannot implicitly convert between two floating-point "
757 "representations of the same bitwidth");
758 return {};
760 if (fromBits > toBits)
761 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
762 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
764 // Complex to complex conversion.
765 if (fir::isa_complex(fromFirTy) && fir::isa_complex(toFirTy)) {
766 // Special case: handle the conversion of a complex such that both the
767 // real and imaginary parts are converted together.
768 auto ty = convertType(getComplexEleTy(convert.getValue().getType()));
769 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 0);
770 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 1);
771 auto nt = convertType(getComplexEleTy(convert.getRes().getType()));
772 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
773 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
774 auto rc = convertFpToFp(rp, fromBits, toBits, nt);
775 auto ic = convertFpToFp(ip, fromBits, toBits, nt);
776 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
777 auto i1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, un, rc, 0);
778 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, i1, ic,
780 return mlir::success();
783 // Floating point to floating point conversion.
784 if (isFloatingPointTy(fromTy)) {
785 if (isFloatingPointTy(toTy)) {
786 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
787 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
788 auto v = convertFpToFp(op0, fromBits, toBits, toTy);
789 rewriter.replaceOp(convert, v);
790 return mlir::success();
792 if (mlir::isa<mlir::IntegerType>(toTy)) {
793 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
794 return mlir::success();
796 } else if (mlir::isa<mlir::IntegerType>(fromTy)) {
797 // Integer to integer conversion.
798 if (mlir::isa<mlir::IntegerType>(toTy)) {
799 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
800 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
801 assert(fromBits != toBits);
802 if (fromBits > toBits) {
803 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
804 return mlir::success();
806 if (fromFirTy == i1Type) {
807 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0);
808 return mlir::success();
810 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
811 return mlir::success();
813 // Integer to floating point conversion.
814 if (isFloatingPointTy(toTy)) {
815 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
816 return mlir::success();
818 // Integer to pointer conversion.
819 if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) {
820 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
821 return mlir::success();
823 } else if (mlir::isa<mlir::LLVM::LLVMPointerType>(fromTy)) {
824 // Pointer to integer conversion.
825 if (mlir::isa<mlir::IntegerType>(toTy)) {
826 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
827 return mlir::success();
829 // Pointer to pointer conversion.
830 if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) {
831 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
832 return mlir::success();
835 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
839 /// `fir.type_info` operation has no specific CodeGen. The operation is
840 /// only used to carry information during FIR to FIR passes. It may be used
841 /// in the future to generate the runtime type info data structures instead
842 /// of generating them in lowering.
843 struct TypeInfoOpConversion : public fir::FIROpConversion<fir::TypeInfoOp> {
844 using FIROpConversion::FIROpConversion;
846 llvm::LogicalResult
847 matchAndRewrite(fir::TypeInfoOp op, OpAdaptor,
848 mlir::ConversionPatternRewriter &rewriter) const override {
849 rewriter.eraseOp(op);
850 return mlir::success();
854 /// `fir.dt_entry` operation has no specific CodeGen. The operation is only used
855 /// to carry information during FIR to FIR passes.
856 struct DTEntryOpConversion : public fir::FIROpConversion<fir::DTEntryOp> {
857 using FIROpConversion::FIROpConversion;
859 llvm::LogicalResult
860 matchAndRewrite(fir::DTEntryOp op, OpAdaptor,
861 mlir::ConversionPatternRewriter &rewriter) const override {
862 rewriter.eraseOp(op);
863 return mlir::success();
867 /// Lower `fir.global_len` operation.
868 struct GlobalLenOpConversion : public fir::FIROpConversion<fir::GlobalLenOp> {
869 using FIROpConversion::FIROpConversion;
871 llvm::LogicalResult
872 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor,
873 mlir::ConversionPatternRewriter &rewriter) const override {
874 TODO(globalLen.getLoc(), "fir.global_len codegen");
875 return mlir::failure();
879 /// Lower fir.len_param_index
880 struct LenParamIndexOpConversion
881 : public fir::FIROpConversion<fir::LenParamIndexOp> {
882 using FIROpConversion::FIROpConversion;
884 // FIXME: this should be specialized by the runtime target
885 llvm::LogicalResult
886 matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor,
887 mlir::ConversionPatternRewriter &rewriter) const override {
888 TODO(lenp.getLoc(), "fir.len_param_index codegen");
892 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of
893 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element
894 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd
895 /// element is the length of the character buffer (`#n`).
896 struct EmboxCharOpConversion : public fir::FIROpConversion<fir::EmboxCharOp> {
897 using FIROpConversion::FIROpConversion;
899 llvm::LogicalResult
900 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor,
901 mlir::ConversionPatternRewriter &rewriter) const override {
902 mlir::ValueRange operands = adaptor.getOperands();
904 mlir::Value charBuffer = operands[0];
905 mlir::Value charBufferLen = operands[1];
907 mlir::Location loc = emboxChar.getLoc();
908 mlir::Type llvmStructTy = convertType(emboxChar.getType());
909 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy);
911 mlir::Type lenTy =
912 mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[1];
913 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen);
915 mlir::Type addrTy =
916 mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[0];
917 if (addrTy != charBuffer.getType())
918 charBuffer =
919 rewriter.create<mlir::LLVM::BitcastOp>(loc, addrTy, charBuffer);
921 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>(
922 loc, llvmStruct, charBuffer, 0);
923 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
924 emboxChar, insertBufferOp, lenAfterCast, 1);
926 return mlir::success();
929 } // namespace
931 template <typename ModuleOp>
932 static mlir::SymbolRefAttr
933 getMallocInModule(ModuleOp mod, fir::AllocMemOp op,
934 mlir::ConversionPatternRewriter &rewriter) {
935 static constexpr char mallocName[] = "malloc";
936 if (auto mallocFunc =
937 mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(mallocName))
938 return mlir::SymbolRefAttr::get(mallocFunc);
939 if (auto userMalloc =
940 mod.template lookupSymbol<mlir::func::FuncOp>(mallocName))
941 return mlir::SymbolRefAttr::get(userMalloc);
943 mlir::OpBuilder moduleBuilder(mod.getBodyRegion());
944 auto indexType = mlir::IntegerType::get(op.getContext(), 64);
945 auto mallocDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
946 op.getLoc(), mallocName,
947 mlir::LLVM::LLVMFunctionType::get(getLlvmPtrType(op.getContext()),
948 indexType,
949 /*isVarArg=*/false));
950 return mlir::SymbolRefAttr::get(mallocDecl);
953 /// Return the LLVMFuncOp corresponding to the standard malloc call.
954 static mlir::SymbolRefAttr
955 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) {
956 if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>())
957 return getMallocInModule(mod, op, rewriter);
958 auto mod = op->getParentOfType<mlir::ModuleOp>();
959 return getMallocInModule(mod, op, rewriter);
962 /// Helper function for generating the LLVM IR that computes the distance
963 /// in bytes between adjacent elements pointed to by a pointer
964 /// of type \p ptrTy. The result is returned as a value of \p idxTy integer
965 /// type.
966 static mlir::Value
967 computeElementDistance(mlir::Location loc, mlir::Type llvmObjectType,
968 mlir::Type idxTy,
969 mlir::ConversionPatternRewriter &rewriter) {
970 // Note that we cannot use something like
971 // mlir::LLVM::getPrimitiveTypeSizeInBits() for the element type here. For
972 // example, it returns 10 bytes for mlir::Float80Type for targets where it
973 // occupies 16 bytes. Proper solution is probably to use
974 // mlir::DataLayout::getTypeABIAlignment(), but DataLayout is not being set
975 // yet (see llvm-project#57230). For the time being use the '(intptr_t)((type
976 // *)0 + 1)' trick for all types. The generated instructions are optimized
977 // into constant by the first pass of InstCombine, so it should not be a
978 // performance issue.
979 auto llvmPtrTy = ::getLlvmPtrType(llvmObjectType.getContext());
980 auto nullPtr = rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
981 auto gep = rewriter.create<mlir::LLVM::GEPOp>(
982 loc, llvmPtrTy, llvmObjectType, nullPtr,
983 llvm::ArrayRef<mlir::LLVM::GEPArg>{1});
984 return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep);
987 /// Return value of the stride in bytes between adjacent elements
988 /// of LLVM type \p llTy. The result is returned as a value of
989 /// \p idxTy integer type.
990 static mlir::Value
991 genTypeStrideInBytes(mlir::Location loc, mlir::Type idxTy,
992 mlir::ConversionPatternRewriter &rewriter,
993 mlir::Type llTy) {
994 // Create a pointer type and use computeElementDistance().
995 return computeElementDistance(loc, llTy, idxTy, rewriter);
998 namespace {
999 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc`
1000 struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> {
1001 using FIROpConversion::FIROpConversion;
1003 llvm::LogicalResult
1004 matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor,
1005 mlir::ConversionPatternRewriter &rewriter) const override {
1006 mlir::Type heapTy = heap.getType();
1007 mlir::Location loc = heap.getLoc();
1008 auto ity = lowerTy().indexType();
1009 mlir::Type dataTy = fir::unwrapRefType(heapTy);
1010 mlir::Type llvmObjectTy = convertObjectType(dataTy);
1011 if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy)))
1012 TODO(loc, "fir.allocmem codegen of derived type with length parameters");
1013 mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, llvmObjectTy);
1014 if (auto scaleSize = genAllocationScaleSize(heap, ity, rewriter))
1015 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, scaleSize);
1016 for (mlir::Value opnd : adaptor.getOperands())
1017 size = rewriter.create<mlir::LLVM::MulOp>(
1018 loc, ity, size, integerCast(loc, rewriter, ity, opnd));
1019 heap->setAttr("callee", getMalloc(heap, rewriter));
1020 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
1021 heap, ::getLlvmPtrType(heap.getContext()), size,
1022 addLLVMOpBundleAttrs(rewriter, heap->getAttrs(), 1));
1023 return mlir::success();
1026 /// Compute the allocation size in bytes of the element type of
1027 /// \p llTy pointer type. The result is returned as a value of \p idxTy
1028 /// integer type.
1029 mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy,
1030 mlir::ConversionPatternRewriter &rewriter,
1031 mlir::Type llTy) const {
1032 return computeElementDistance(loc, llTy, idxTy, rewriter);
1035 } // namespace
1037 /// Return the LLVMFuncOp corresponding to the standard free call.
1038 template <typename ModuleOp>
1039 static mlir::SymbolRefAttr
1040 getFreeInModule(ModuleOp mod, fir::FreeMemOp op,
1041 mlir::ConversionPatternRewriter &rewriter) {
1042 static constexpr char freeName[] = "free";
1043 // Check if free already defined in the module.
1044 if (auto freeFunc =
1045 mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(freeName))
1046 return mlir::SymbolRefAttr::get(freeFunc);
1047 if (auto freeDefinedByUser =
1048 mod.template lookupSymbol<mlir::func::FuncOp>(freeName))
1049 return mlir::SymbolRefAttr::get(freeDefinedByUser);
1050 // Create llvm declaration for free.
1051 mlir::OpBuilder moduleBuilder(mod.getBodyRegion());
1052 auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext());
1053 auto freeDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>(
1054 rewriter.getUnknownLoc(), freeName,
1055 mlir::LLVM::LLVMFunctionType::get(voidType,
1056 getLlvmPtrType(op.getContext()),
1057 /*isVarArg=*/false));
1058 return mlir::SymbolRefAttr::get(freeDecl);
1061 static mlir::SymbolRefAttr getFree(fir::FreeMemOp op,
1062 mlir::ConversionPatternRewriter &rewriter) {
1063 if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>())
1064 return getFreeInModule(mod, op, rewriter);
1065 auto mod = op->getParentOfType<mlir::ModuleOp>();
1066 return getFreeInModule(mod, op, rewriter);
1069 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
1070 unsigned result = 1;
1071 for (auto eleTy =
1072 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty.getElementType());
1073 eleTy; eleTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(
1074 eleTy.getElementType()))
1075 ++result;
1076 return result;
1079 namespace {
1080 /// Lower a `fir.freemem` instruction into `llvm.call @free`
1081 struct FreeMemOpConversion : public fir::FIROpConversion<fir::FreeMemOp> {
1082 using FIROpConversion::FIROpConversion;
1084 llvm::LogicalResult
1085 matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor,
1086 mlir::ConversionPatternRewriter &rewriter) const override {
1087 mlir::Location loc = freemem.getLoc();
1088 freemem->setAttr("callee", getFree(freemem, rewriter));
1089 rewriter.create<mlir::LLVM::CallOp>(
1090 loc, mlir::TypeRange{}, mlir::ValueRange{adaptor.getHeapref()},
1091 addLLVMOpBundleAttrs(rewriter, freemem->getAttrs(), 1));
1092 rewriter.eraseOp(freemem);
1093 return mlir::success();
1096 } // namespace
1098 // Convert subcomponent array indices from column-major to row-major ordering.
1099 static llvm::SmallVector<mlir::Value>
1100 convertSubcomponentIndices(mlir::Location loc, mlir::Type eleTy,
1101 mlir::ValueRange indices,
1102 mlir::Type *retTy = nullptr) {
1103 llvm::SmallVector<mlir::Value> result;
1104 llvm::SmallVector<mlir::Value> arrayIndices;
1106 auto appendArrayIndices = [&] {
1107 if (arrayIndices.empty())
1108 return;
1109 std::reverse(arrayIndices.begin(), arrayIndices.end());
1110 result.append(arrayIndices.begin(), arrayIndices.end());
1111 arrayIndices.clear();
1114 for (mlir::Value index : indices) {
1115 // Component indices can be field index to select a component, or array
1116 // index, to select an element in an array component.
1117 if (auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(eleTy)) {
1118 std::int64_t cstIndex = getConstantIntValue(index);
1119 assert(cstIndex < (int64_t)structTy.getBody().size() &&
1120 "out-of-bounds struct field index");
1121 eleTy = structTy.getBody()[cstIndex];
1122 appendArrayIndices();
1123 result.push_back(index);
1124 } else if (auto arrayTy =
1125 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)) {
1126 eleTy = arrayTy.getElementType();
1127 arrayIndices.push_back(index);
1128 } else
1129 fir::emitFatalError(loc, "Unexpected subcomponent type");
1131 appendArrayIndices();
1132 if (retTy)
1133 *retTy = eleTy;
1134 return result;
1137 /// Common base class for embox to descriptor conversion.
1138 template <typename OP>
1139 struct EmboxCommonConversion : public fir::FIROpConversion<OP> {
1140 using fir::FIROpConversion<OP>::FIROpConversion;
1141 using TypePair = typename fir::FIROpConversion<OP>::TypePair;
1143 static int getCFIAttr(fir::BaseBoxType boxTy) {
1144 auto eleTy = boxTy.getEleTy();
1145 if (mlir::isa<fir::PointerType>(eleTy))
1146 return CFI_attribute_pointer;
1147 if (mlir::isa<fir::HeapType>(eleTy))
1148 return CFI_attribute_allocatable;
1149 return CFI_attribute_other;
1152 mlir::Value getCharacterByteSize(mlir::Location loc,
1153 mlir::ConversionPatternRewriter &rewriter,
1154 fir::CharacterType charTy,
1155 mlir::ValueRange lenParams) const {
1156 auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
1157 mlir::Value size =
1158 genTypeStrideInBytes(loc, i64Ty, rewriter, this->convertType(charTy));
1159 if (charTy.hasConstantLen())
1160 return size; // Length accounted for in the genTypeStrideInBytes GEP.
1161 // Otherwise, multiply the single character size by the length.
1162 assert(!lenParams.empty());
1163 auto len64 = fir::FIROpConversion<OP>::integerCast(loc, rewriter, i64Ty,
1164 lenParams.back());
1165 return rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, size, len64);
1168 // Get the element size and CFI type code of the boxed value.
1169 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode(
1170 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
1171 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
1172 auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
1173 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy))
1174 boxEleTy = eleTy;
1175 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(boxEleTy))
1176 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
1177 if (mlir::isa<mlir::NoneType>(
1178 boxEleTy)) // unlimited polymorphic or assumed type
1179 return {rewriter.create<mlir::LLVM::ConstantOp>(loc, i64Ty, 0),
1180 this->genConstantOffset(loc, rewriter, CFI_type_other)};
1181 mlir::Value typeCodeVal = this->genConstantOffset(
1182 loc, rewriter,
1183 fir::getTypeCode(boxEleTy, this->lowerTy().getKindMap()));
1184 if (fir::isa_integer(boxEleTy) ||
1185 mlir::dyn_cast<fir::LogicalType>(boxEleTy) || fir::isa_real(boxEleTy) ||
1186 fir::isa_complex(boxEleTy))
1187 return {genTypeStrideInBytes(loc, i64Ty, rewriter,
1188 this->convertType(boxEleTy)),
1189 typeCodeVal};
1190 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(boxEleTy))
1191 return {getCharacterByteSize(loc, rewriter, charTy, lenParams),
1192 typeCodeVal};
1193 if (fir::isa_ref_type(boxEleTy)) {
1194 auto ptrTy = ::getLlvmPtrType(rewriter.getContext());
1195 return {genTypeStrideInBytes(loc, i64Ty, rewriter, ptrTy), typeCodeVal};
1197 if (mlir::isa<fir::RecordType>(boxEleTy))
1198 return {genTypeStrideInBytes(loc, i64Ty, rewriter,
1199 this->convertType(boxEleTy)),
1200 typeCodeVal};
1201 fir::emitFatalError(loc, "unhandled type in fir.box code generation");
1204 /// Basic pattern to write a field in the descriptor
1205 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter,
1206 mlir::Location loc, mlir::Value dest,
1207 llvm::ArrayRef<std::int64_t> fldIndexes,
1208 mlir::Value value, bool bitcast = false) const {
1209 auto boxTy = dest.getType();
1210 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
1211 if (!bitcast)
1212 value = this->integerCast(loc, rewriter, fldTy, value);
1213 // bitcast are no-ops with LLVM opaque pointers.
1214 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, dest, value,
1215 fldIndexes);
1218 inline mlir::Value
1219 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter,
1220 mlir::Location loc, mlir::Value dest,
1221 mlir::Value base) const {
1222 return insertField(rewriter, loc, dest, {kAddrPosInBox}, base,
1223 /*bitCast=*/true);
1226 inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter,
1227 mlir::Location loc, mlir::Value dest,
1228 unsigned dim, mlir::Value lb) const {
1229 return insertField(rewriter, loc, dest,
1230 {kDimsPosInBox, dim, kDimLowerBoundPos}, lb);
1233 inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter,
1234 mlir::Location loc, mlir::Value dest,
1235 unsigned dim, mlir::Value extent) const {
1236 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos},
1237 extent);
1240 inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter,
1241 mlir::Location loc, mlir::Value dest,
1242 unsigned dim, mlir::Value stride) const {
1243 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos},
1244 stride);
1247 /// Get the address of the type descriptor global variable that was created by
1248 /// lowering for derived type \p recType.
1249 mlir::Value getTypeDescriptor(mlir::ModuleOp mod,
1250 mlir::ConversionPatternRewriter &rewriter,
1251 mlir::Location loc,
1252 fir::RecordType recType) const {
1253 std::string name =
1254 this->options.typeDescriptorsRenamedForAssembly
1255 ? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName())
1256 : fir::NameUniquer::getTypeDescriptorName(recType.getName());
1257 mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext());
1258 if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) {
1259 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1260 global.getSymName());
1262 if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
1263 // The global may have already been translated to LLVM.
1264 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy,
1265 global.getSymName());
1267 // Type info derived types do not have type descriptors since they are the
1268 // types defining type descriptors.
1269 if (!this->options.ignoreMissingTypeDescriptors &&
1270 !fir::NameUniquer::belongsToModule(
1271 name, Fortran::semantics::typeInfoBuiltinModule))
1272 fir::emitFatalError(
1273 loc, "runtime derived type info descriptor was not generated");
1274 return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy);
1277 mlir::Value populateDescriptor(mlir::Location loc, mlir::ModuleOp mod,
1278 fir::BaseBoxType boxTy, mlir::Type inputType,
1279 mlir::ConversionPatternRewriter &rewriter,
1280 unsigned rank, mlir::Value eleSize,
1281 mlir::Value cfiTy, mlir::Value typeDesc,
1282 int allocatorIdx = kDefaultAllocator,
1283 mlir::Value extraField = {}) const {
1284 auto llvmBoxTy = this->lowerTy().convertBoxTypeAsStruct(boxTy, rank);
1285 bool isUnlimitedPolymorphic = fir::isUnlimitedPolymorphicType(boxTy);
1286 bool useInputType = fir::isPolymorphicType(boxTy) || isUnlimitedPolymorphic;
1287 mlir::Value descriptor =
1288 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
1289 descriptor =
1290 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize);
1291 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox},
1292 this->genI32Constant(loc, rewriter, CFI_VERSION));
1293 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox},
1294 this->genI32Constant(loc, rewriter, rank));
1295 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy);
1296 descriptor =
1297 insertField(rewriter, loc, descriptor, {kAttributePosInBox},
1298 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy)));
1300 const bool hasAddendum = fir::boxHasAddendum(boxTy);
1302 if (extraField) {
1303 // Make sure to set the addendum presence flag according to the
1304 // destination box.
1305 if (hasAddendum) {
1306 auto maskAttr = mlir::IntegerAttr::get(
1307 rewriter.getIntegerType(8, /*isSigned=*/false),
1308 llvm::APInt(8, (uint64_t)_CFI_ADDENDUM_FLAG, /*isSigned=*/false));
1309 mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>(
1310 loc, rewriter.getI8Type(), maskAttr);
1311 extraField = rewriter.create<mlir::LLVM::OrOp>(loc, extraField, mask);
1312 } else {
1313 auto maskAttr = mlir::IntegerAttr::get(
1314 rewriter.getIntegerType(8, /*isSigned=*/false),
1315 llvm::APInt(8, (uint64_t)~_CFI_ADDENDUM_FLAG, /*isSigned=*/true));
1316 mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>(
1317 loc, rewriter.getI8Type(), maskAttr);
1318 extraField = rewriter.create<mlir::LLVM::AndOp>(loc, extraField, mask);
1320 // Extra field value is provided so just use it.
1321 descriptor =
1322 insertField(rewriter, loc, descriptor, {kExtraPosInBox}, extraField);
1323 } else {
1324 // Compute the value of the extra field based on allocator_idx and
1325 // addendum present using a Descriptor object.
1326 Fortran::runtime::StaticDescriptor staticDescriptor;
1327 Fortran::runtime::Descriptor &desc{staticDescriptor.descriptor()};
1328 desc.raw().extra = 0;
1329 desc.SetAllocIdx(allocatorIdx);
1330 if (hasAddendum)
1331 desc.SetHasAddendum();
1332 descriptor =
1333 insertField(rewriter, loc, descriptor, {kExtraPosInBox},
1334 this->genI32Constant(loc, rewriter, desc.raw().extra));
1337 if (hasAddendum) {
1338 unsigned typeDescFieldId = getTypeDescFieldId(boxTy);
1339 if (!typeDesc) {
1340 if (useInputType) {
1341 mlir::Type innerType = fir::unwrapInnerType(inputType);
1342 if (innerType && mlir::isa<fir::RecordType>(innerType)) {
1343 auto recTy = mlir::dyn_cast<fir::RecordType>(innerType);
1344 typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy);
1345 } else {
1346 // Unlimited polymorphic type descriptor with no record type. Set
1347 // type descriptor address to a clean state.
1348 typeDesc = rewriter.create<mlir::LLVM::ZeroOp>(
1349 loc, ::getLlvmPtrType(mod.getContext()));
1351 } else {
1352 typeDesc = getTypeDescriptor(mod, rewriter, loc,
1353 fir::unwrapIfDerived(boxTy));
1356 if (typeDesc)
1357 descriptor =
1358 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc,
1359 /*bitCast=*/true);
1360 // Always initialize the length parameter field to zero to avoid issues
1361 // with uninitialized values in Fortran code trying to compare physical
1362 // representation of derived types with pointer/allocatable components.
1363 // This has been seen in hashing algorithms using TRANSFER.
1364 mlir::Value zero =
1365 genConstantIndex(loc, rewriter.getI64Type(), rewriter, 0);
1366 descriptor = insertField(rewriter, loc, descriptor,
1367 {getLenParamFieldId(boxTy), 0}, zero);
1369 return descriptor;
1372 // Template used for fir::EmboxOp and fir::cg::XEmboxOp
1373 template <typename BOX>
1374 std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value>
1375 consDescriptorPrefix(BOX box, mlir::Type inputType,
1376 mlir::ConversionPatternRewriter &rewriter, unsigned rank,
1377 [[maybe_unused]] mlir::ValueRange substrParams,
1378 mlir::ValueRange lenParams, mlir::Value sourceBox = {},
1379 mlir::Type sourceBoxType = {}) const {
1380 auto loc = box.getLoc();
1381 auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType());
1382 bool useInputType = fir::isPolymorphicType(boxTy) &&
1383 !fir::isUnlimitedPolymorphicType(inputType);
1384 llvm::SmallVector<mlir::Value> typeparams = lenParams;
1385 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
1386 if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1387 typeparams.push_back(substrParams[1]);
1390 int allocatorIdx = 0;
1391 if constexpr (std::is_same_v<BOX, fir::EmboxOp> ||
1392 std::is_same_v<BOX, fir::cg::XEmboxOp>) {
1393 if (box.getAllocatorIdx())
1394 allocatorIdx = *box.getAllocatorIdx();
1397 // Write each of the fields with the appropriate values.
1398 // When emboxing an element to a polymorphic descriptor, use the
1399 // input type since the destination descriptor type has not the exact
1400 // information.
1401 auto [eleSize, cfiTy] = getSizeAndTypeCode(
1402 loc, rewriter, useInputType ? inputType : boxTy.getEleTy(), typeparams);
1404 mlir::Value typeDesc;
1405 mlir::Value extraField;
1406 // When emboxing to a polymorphic box, get the type descriptor, type code
1407 // and element size from the source box if any.
1408 if (fir::isPolymorphicType(boxTy) && sourceBox) {
1409 TypePair sourceBoxTyPair = this->getBoxTypePair(sourceBoxType);
1410 typeDesc =
1411 this->loadTypeDescAddress(loc, sourceBoxTyPair, sourceBox, rewriter);
1412 mlir::Type idxTy = this->lowerTy().indexType();
1413 eleSize = this->getElementSizeFromBox(loc, idxTy, sourceBoxTyPair,
1414 sourceBox, rewriter);
1415 cfiTy = this->getValueFromBox(loc, sourceBoxTyPair, sourceBox,
1416 cfiTy.getType(), rewriter, kTypePosInBox);
1417 extraField =
1418 this->getExtraFromBox(loc, sourceBoxTyPair, sourceBox, rewriter);
1420 auto mod = box->template getParentOfType<mlir::ModuleOp>();
1421 mlir::Value descriptor =
1422 populateDescriptor(loc, mod, boxTy, inputType, rewriter, rank, eleSize,
1423 cfiTy, typeDesc, allocatorIdx, extraField);
1425 return {boxTy, descriptor, eleSize};
1428 std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value>
1429 consDescriptorPrefix(fir::cg::XReboxOp box, mlir::Value loweredBox,
1430 mlir::ConversionPatternRewriter &rewriter, unsigned rank,
1431 mlir::ValueRange substrParams,
1432 mlir::ValueRange lenParams,
1433 mlir::Value typeDesc = {}) const {
1434 auto loc = box.getLoc();
1435 auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType());
1436 auto inputBoxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getBox().getType());
1437 auto inputBoxTyPair = this->getBoxTypePair(inputBoxTy);
1438 llvm::SmallVector<mlir::Value> typeparams = lenParams;
1439 if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy()))
1440 typeparams.push_back(substrParams[1]);
1442 auto [eleSize, cfiTy] =
1443 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams);
1445 // Reboxing to a polymorphic entity. eleSize and type code need to
1446 // be retrieved from the initial box and propagated to the new box.
1447 // If the initial box has an addendum, the type desc must be propagated as
1448 // well.
1449 if (fir::isPolymorphicType(boxTy)) {
1450 mlir::Type idxTy = this->lowerTy().indexType();
1451 eleSize = this->getElementSizeFromBox(loc, idxTy, inputBoxTyPair,
1452 loweredBox, rewriter);
1453 cfiTy = this->getValueFromBox(loc, inputBoxTyPair, loweredBox,
1454 cfiTy.getType(), rewriter, kTypePosInBox);
1455 // TODO: For initial box that are unlimited polymorphic entities, this
1456 // code must be made conditional because unlimited polymorphic entities
1457 // with intrinsic type spec does not have addendum.
1458 if (fir::boxHasAddendum(inputBoxTy))
1459 typeDesc = this->loadTypeDescAddress(loc, inputBoxTyPair, loweredBox,
1460 rewriter);
1463 mlir::Value extraField =
1464 this->getExtraFromBox(loc, inputBoxTyPair, loweredBox, rewriter);
1466 auto mod = box->template getParentOfType<mlir::ModuleOp>();
1467 mlir::Value descriptor =
1468 populateDescriptor(loc, mod, boxTy, box.getBox().getType(), rewriter,
1469 rank, eleSize, cfiTy, typeDesc,
1470 /*allocatorIdx=*/kDefaultAllocator, extraField);
1472 return {boxTy, descriptor, eleSize};
1475 // Compute the base address of a fir.box given the indices from the slice.
1476 // The indices from the "outer" dimensions (every dimension after the first
1477 // one (included) that is not a compile time constant) must have been
1478 // multiplied with the related extents and added together into \p outerOffset.
1479 mlir::Value
1480 genBoxOffsetGep(mlir::ConversionPatternRewriter &rewriter, mlir::Location loc,
1481 mlir::Value base, mlir::Type llvmBaseObjectType,
1482 mlir::Value outerOffset, mlir::ValueRange cstInteriorIndices,
1483 mlir::ValueRange componentIndices,
1484 std::optional<mlir::Value> substringOffset) const {
1485 llvm::SmallVector<mlir::LLVM::GEPArg> gepArgs{outerOffset};
1486 mlir::Type resultTy = llvmBaseObjectType;
1487 // Fortran is column major, llvm GEP is row major: reverse the indices here.
1488 for (mlir::Value interiorIndex : llvm::reverse(cstInteriorIndices)) {
1489 auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy);
1490 if (!arrayTy)
1491 fir::emitFatalError(
1492 loc,
1493 "corrupted GEP generated being generated in fir.embox/fir.rebox");
1494 resultTy = arrayTy.getElementType();
1495 gepArgs.push_back(interiorIndex);
1497 llvm::SmallVector<mlir::Value> gepIndices =
1498 convertSubcomponentIndices(loc, resultTy, componentIndices, &resultTy);
1499 gepArgs.append(gepIndices.begin(), gepIndices.end());
1500 if (substringOffset) {
1501 if (auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy)) {
1502 gepArgs.push_back(*substringOffset);
1503 resultTy = arrayTy.getElementType();
1504 } else {
1505 // If the CHARACTER length is dynamic, the whole base type should have
1506 // degenerated to an llvm.ptr<i[width]>, and there should not be any
1507 // cstInteriorIndices/componentIndices. The substring offset can be
1508 // added to the outterOffset since it applies on the same LLVM type.
1509 if (gepArgs.size() != 1)
1510 fir::emitFatalError(loc,
1511 "corrupted substring GEP in fir.embox/fir.rebox");
1512 mlir::Type outterOffsetTy = gepArgs[0].get<mlir::Value>().getType();
1513 mlir::Value cast =
1514 this->integerCast(loc, rewriter, outterOffsetTy, *substringOffset);
1516 gepArgs[0] = rewriter.create<mlir::LLVM::AddOp>(
1517 loc, outterOffsetTy, gepArgs[0].get<mlir::Value>(), cast);
1520 mlir::Type llvmPtrTy = ::getLlvmPtrType(resultTy.getContext());
1521 return rewriter.create<mlir::LLVM::GEPOp>(
1522 loc, llvmPtrTy, llvmBaseObjectType, base, gepArgs);
1525 template <typename BOX>
1526 void
1527 getSubcomponentIndices(BOX xbox, mlir::Value memref,
1528 mlir::ValueRange operands,
1529 mlir::SmallVectorImpl<mlir::Value> &indices) const {
1530 // For each field in the path add the offset to base via the args list.
1531 // In the most general case, some offsets must be computed since
1532 // they are not be known until runtime.
1533 if (fir::hasDynamicSize(fir::unwrapSequenceType(
1534 fir::unwrapPassByRefType(memref.getType()))))
1535 TODO(xbox.getLoc(),
1536 "fir.embox codegen dynamic size component in derived type");
1537 indices.append(operands.begin() + xbox.getSubcomponentOperandIndex(),
1538 operands.begin() + xbox.getSubcomponentOperandIndex() +
1539 xbox.getSubcomponent().size());
1542 static bool isInGlobalOp(mlir::ConversionPatternRewriter &rewriter) {
1543 auto *thisBlock = rewriter.getInsertionBlock();
1544 return thisBlock &&
1545 mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp());
1548 /// If the embox is not in a globalOp body, allocate storage for the box;
1549 /// store the value inside and return the generated alloca. Return the input
1550 /// value otherwise.
1551 mlir::Value
1552 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter,
1553 mlir::Location loc, mlir::Type boxTy,
1554 mlir::Value boxValue) const {
1555 if (isInGlobalOp(rewriter))
1556 return boxValue;
1557 mlir::Type llvmBoxTy = boxValue.getType();
1558 auto alloca = this->genAllocaAndAddrCastWithType(loc, llvmBoxTy,
1559 defaultAlign, rewriter);
1560 auto storeOp = rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca);
1561 this->attachTBAATag(storeOp, boxTy, boxTy, nullptr);
1562 return alloca;
1566 /// Compute the extent of a triplet slice (lb:ub:step).
1567 static mlir::Value
1568 computeTripletExtent(mlir::ConversionPatternRewriter &rewriter,
1569 mlir::Location loc, mlir::Value lb, mlir::Value ub,
1570 mlir::Value step, mlir::Value zero, mlir::Type type) {
1571 mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb);
1572 extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step);
1573 extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step);
1574 // If the resulting extent is negative (`ub-lb` and `step` have different
1575 // signs), zero must be returned instead.
1576 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
1577 loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero);
1578 return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero);
1581 /// Create a generic box on a memory reference. This conversions lowers the
1582 /// abstract box to the appropriate, initialized descriptor.
1583 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> {
1584 using EmboxCommonConversion::EmboxCommonConversion;
1586 llvm::LogicalResult
1587 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor,
1588 mlir::ConversionPatternRewriter &rewriter) const override {
1589 mlir::ValueRange operands = adaptor.getOperands();
1590 mlir::Value sourceBox;
1591 mlir::Type sourceBoxType;
1592 if (embox.getSourceBox()) {
1593 sourceBox = operands[embox.getSourceBoxOperandIndex()];
1594 sourceBoxType = embox.getSourceBox().getType();
1596 assert(!embox.getShape() && "There should be no dims on this embox op");
1597 auto [boxTy, dest, eleSize] = consDescriptorPrefix(
1598 embox, fir::unwrapRefType(embox.getMemref().getType()), rewriter,
1599 /*rank=*/0, /*substrParams=*/mlir::ValueRange{},
1600 adaptor.getTypeparams(), sourceBox, sourceBoxType);
1601 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, operands[0]);
1602 if (fir::isDerivedTypeWithLenParams(boxTy)) {
1603 TODO(embox.getLoc(),
1604 "fir.embox codegen of derived with length parameters");
1605 return mlir::failure();
1607 auto result =
1608 placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), boxTy, dest);
1609 rewriter.replaceOp(embox, result);
1610 return mlir::success();
1614 /// Create a generic box on a memory reference.
1615 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> {
1616 using EmboxCommonConversion::EmboxCommonConversion;
1618 llvm::LogicalResult
1619 matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor,
1620 mlir::ConversionPatternRewriter &rewriter) const override {
1621 mlir::ValueRange operands = adaptor.getOperands();
1622 mlir::Value sourceBox;
1623 mlir::Type sourceBoxType;
1624 if (xbox.getSourceBox()) {
1625 sourceBox = operands[xbox.getSourceBoxOperandIndex()];
1626 sourceBoxType = xbox.getSourceBox().getType();
1628 auto [boxTy, dest, resultEleSize] = consDescriptorPrefix(
1629 xbox, fir::unwrapRefType(xbox.getMemref().getType()), rewriter,
1630 xbox.getOutRank(), adaptor.getSubstr(), adaptor.getLenParams(),
1631 sourceBox, sourceBoxType);
1632 // Generate the triples in the dims field of the descriptor
1633 auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64);
1634 assert(!xbox.getShape().empty() && "must have a shape");
1635 unsigned shapeOffset = xbox.getShapeOperandIndex();
1636 bool hasShift = !xbox.getShift().empty();
1637 unsigned shiftOffset = xbox.getShiftOperandIndex();
1638 bool hasSlice = !xbox.getSlice().empty();
1639 unsigned sliceOffset = xbox.getSliceOperandIndex();
1640 mlir::Location loc = xbox.getLoc();
1641 mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0);
1642 mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1);
1643 mlir::Value prevPtrOff = one;
1644 mlir::Type eleTy = boxTy.getEleTy();
1645 const unsigned rank = xbox.getRank();
1646 llvm::SmallVector<mlir::Value> cstInteriorIndices;
1647 unsigned constRows = 0;
1648 mlir::Value ptrOffset = zero;
1649 mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.getMemref().getType());
1650 assert(mlir::isa<fir::SequenceType>(memEleTy));
1651 auto seqTy = mlir::cast<fir::SequenceType>(memEleTy);
1652 mlir::Type seqEleTy = seqTy.getEleTy();
1653 // Adjust the element scaling factor if the element is a dependent type.
1654 if (fir::hasDynamicSize(seqEleTy)) {
1655 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(seqEleTy)) {
1656 // The GEP pointer type decays to llvm.ptr<i[width]>.
1657 // The scaling factor is the runtime value of the length.
1658 assert(!adaptor.getLenParams().empty());
1659 prevPtrOff = FIROpConversion::integerCast(
1660 loc, rewriter, i64Ty, adaptor.getLenParams().back());
1661 } else if (mlir::isa<fir::RecordType>(seqEleTy)) {
1662 // prevPtrOff = ;
1663 TODO(loc, "generate call to calculate size of PDT");
1664 } else {
1665 fir::emitFatalError(loc, "unexpected dynamic type");
1667 } else {
1668 constRows = seqTy.getConstantRows();
1671 const auto hasSubcomp = !xbox.getSubcomponent().empty();
1672 const bool hasSubstr = !xbox.getSubstr().empty();
1673 // Initial element stride that will be use to compute the step in
1674 // each dimension. Initially, this is the size of the input element.
1675 // Note that when there are no components/substring, the resultEleSize
1676 // that was previously computed matches the input element size.
1677 mlir::Value prevDimByteStride = resultEleSize;
1678 if (hasSubcomp) {
1679 // We have a subcomponent. The step value needs to be the number of
1680 // bytes per element (which is a derived type).
1681 prevDimByteStride =
1682 genTypeStrideInBytes(loc, i64Ty, rewriter, convertType(seqEleTy));
1683 } else if (hasSubstr) {
1684 // We have a substring. The step value needs to be the number of bytes
1685 // per CHARACTER element.
1686 auto charTy = mlir::cast<fir::CharacterType>(seqEleTy);
1687 if (fir::hasDynamicSize(charTy)) {
1688 prevDimByteStride =
1689 getCharacterByteSize(loc, rewriter, charTy, adaptor.getLenParams());
1690 } else {
1691 prevDimByteStride = genConstantIndex(
1692 loc, i64Ty, rewriter,
1693 charTy.getLen() * lowerTy().characterBitsize(charTy) / 8);
1697 // Process the array subspace arguments (shape, shift, etc.), if any,
1698 // translating everything to values in the descriptor wherever the entity
1699 // has a dynamic array dimension.
1700 for (unsigned di = 0, descIdx = 0; di < rank; ++di) {
1701 mlir::Value extent = operands[shapeOffset];
1702 mlir::Value outerExtent = extent;
1703 bool skipNext = false;
1704 if (hasSlice) {
1705 mlir::Value off = operands[sliceOffset];
1706 mlir::Value adj = one;
1707 if (hasShift)
1708 adj = operands[shiftOffset];
1709 auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj);
1710 if (constRows > 0) {
1711 cstInteriorIndices.push_back(ao);
1712 } else {
1713 auto dimOff =
1714 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff);
1715 ptrOffset =
1716 rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset);
1718 if (mlir::isa_and_nonnull<fir::UndefOp>(
1719 xbox.getSlice()[3 * di + 1].getDefiningOp())) {
1720 // This dimension contains a scalar expression in the array slice op.
1721 // The dimension is loop invariant, will be dropped, and will not
1722 // appear in the descriptor.
1723 skipNext = true;
1726 if (!skipNext) {
1727 // store extent
1728 if (hasSlice)
1729 extent = computeTripletExtent(rewriter, loc, operands[sliceOffset],
1730 operands[sliceOffset + 1],
1731 operands[sliceOffset + 2], zero, i64Ty);
1732 // Lower bound is normalized to 0 for BIND(C) interoperability.
1733 mlir::Value lb = zero;
1734 const bool isaPointerOrAllocatable =
1735 mlir::isa<fir::PointerType, fir::HeapType>(eleTy);
1736 // Lower bound is defaults to 1 for POINTER, ALLOCATABLE, and
1737 // denormalized descriptors.
1738 if (isaPointerOrAllocatable || !normalizedLowerBound(xbox))
1739 lb = one;
1740 // If there is a shifted origin, and no fir.slice, and this is not
1741 // a normalized descriptor then use the value from the shift op as
1742 // the lower bound.
1743 if (hasShift && !(hasSlice || hasSubcomp || hasSubstr) &&
1744 (isaPointerOrAllocatable || !normalizedLowerBound(xbox))) {
1745 lb = operands[shiftOffset];
1746 auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
1747 loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
1748 lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one,
1749 lb);
1751 dest = insertLowerBound(rewriter, loc, dest, descIdx, lb);
1753 dest = insertExtent(rewriter, loc, dest, descIdx, extent);
1755 // store step (scaled by shaped extent)
1756 mlir::Value step = prevDimByteStride;
1757 if (hasSlice)
1758 step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step,
1759 operands[sliceOffset + 2]);
1760 dest = insertStride(rewriter, loc, dest, descIdx, step);
1761 ++descIdx;
1764 // compute the stride and offset for the next natural dimension
1765 prevDimByteStride = rewriter.create<mlir::LLVM::MulOp>(
1766 loc, i64Ty, prevDimByteStride, outerExtent);
1767 if (constRows == 0)
1768 prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff,
1769 outerExtent);
1770 else
1771 --constRows;
1773 // increment iterators
1774 ++shapeOffset;
1775 if (hasShift)
1776 ++shiftOffset;
1777 if (hasSlice)
1778 sliceOffset += 3;
1780 mlir::Value base = adaptor.getMemref();
1781 if (hasSlice || hasSubcomp || hasSubstr) {
1782 // Shift the base address.
1783 llvm::SmallVector<mlir::Value> fieldIndices;
1784 std::optional<mlir::Value> substringOffset;
1785 if (hasSubcomp)
1786 getSubcomponentIndices(xbox, xbox.getMemref(), operands, fieldIndices);
1787 if (hasSubstr)
1788 substringOffset = operands[xbox.getSubstrOperandIndex()];
1789 mlir::Type llvmBaseType =
1790 convertType(fir::unwrapRefType(xbox.getMemref().getType()));
1791 base = genBoxOffsetGep(rewriter, loc, base, llvmBaseType, ptrOffset,
1792 cstInteriorIndices, fieldIndices, substringOffset);
1794 dest = insertBaseAddress(rewriter, loc, dest, base);
1795 if (fir::isDerivedTypeWithLenParams(boxTy))
1796 TODO(loc, "fir.embox codegen of derived with length parameters");
1798 mlir::Value result =
1799 placeInMemoryIfNotGlobalInit(rewriter, loc, boxTy, dest);
1800 rewriter.replaceOp(xbox, result);
1801 return mlir::success();
1804 /// Return true if `xbox` has a normalized lower bounds attribute. A box value
1805 /// that is neither a POINTER nor an ALLOCATABLE should be normalized to a
1806 /// zero origin lower bound for interoperability with BIND(C).
1807 inline static bool normalizedLowerBound(fir::cg::XEmboxOp xbox) {
1808 return xbox->hasAttr(fir::getNormalizedLowerBoundAttrName());
1812 /// Create a new box given a box reference.
1813 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> {
1814 using EmboxCommonConversion::EmboxCommonConversion;
1816 llvm::LogicalResult
1817 matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor,
1818 mlir::ConversionPatternRewriter &rewriter) const override {
1819 mlir::Location loc = rebox.getLoc();
1820 mlir::Type idxTy = lowerTy().indexType();
1821 mlir::Value loweredBox = adaptor.getOperands()[0];
1822 mlir::ValueRange operands = adaptor.getOperands();
1824 // Inside a fir.global, the input box was produced as an llvm.struct<>
1825 // because objects cannot be handled in memory inside a fir.global body that
1826 // must be constant foldable. However, the type translation are not
1827 // contextual, so the fir.box<T> type of the operation that produced the
1828 // fir.box was translated to an llvm.ptr<llvm.struct<>> and the MLIR pass
1829 // manager inserted a builtin.unrealized_conversion_cast that was inserted
1830 // and needs to be removed here.
1831 if (isInGlobalOp(rewriter))
1832 if (auto unrealizedCast =
1833 loweredBox.getDefiningOp<mlir::UnrealizedConversionCastOp>())
1834 loweredBox = unrealizedCast.getInputs()[0];
1836 TypePair inputBoxTyPair = getBoxTypePair(rebox.getBox().getType());
1838 // Create new descriptor and fill its non-shape related data.
1839 llvm::SmallVector<mlir::Value, 2> lenParams;
1840 mlir::Type inputEleTy = getInputEleTy(rebox);
1841 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(inputEleTy)) {
1842 if (charTy.hasConstantLen()) {
1843 mlir::Value len =
1844 genConstantIndex(loc, idxTy, rewriter, charTy.getLen());
1845 lenParams.emplace_back(len);
1846 } else {
1847 mlir::Value len = getElementSizeFromBox(loc, idxTy, inputBoxTyPair,
1848 loweredBox, rewriter);
1849 if (charTy.getFKind() != 1) {
1850 assert(!isInGlobalOp(rewriter) &&
1851 "character target in global op must have constant length");
1852 mlir::Value width =
1853 genConstantIndex(loc, idxTy, rewriter, charTy.getFKind());
1854 len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width);
1856 lenParams.emplace_back(len);
1858 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(inputEleTy)) {
1859 if (recTy.getNumLenParams() != 0)
1860 TODO(loc, "reboxing descriptor of derived type with length parameters");
1863 // Rebox on polymorphic entities needs to carry over the dynamic type.
1864 mlir::Value typeDescAddr;
1865 if (mlir::isa<fir::ClassType>(inputBoxTyPair.fir) &&
1866 mlir::isa<fir::ClassType>(rebox.getType()))
1867 typeDescAddr =
1868 loadTypeDescAddress(loc, inputBoxTyPair, loweredBox, rewriter);
1870 auto [boxTy, dest, eleSize] =
1871 consDescriptorPrefix(rebox, loweredBox, rewriter, rebox.getOutRank(),
1872 adaptor.getSubstr(), lenParams, typeDescAddr);
1874 // Read input extents, strides, and base address
1875 llvm::SmallVector<mlir::Value> inputExtents;
1876 llvm::SmallVector<mlir::Value> inputStrides;
1877 const unsigned inputRank = rebox.getRank();
1878 for (unsigned dim = 0; dim < inputRank; ++dim) {
1879 llvm::SmallVector<mlir::Value, 3> dimInfo =
1880 getDimsFromBox(loc, {idxTy, idxTy, idxTy}, inputBoxTyPair, loweredBox,
1881 dim, rewriter);
1882 inputExtents.emplace_back(dimInfo[1]);
1883 inputStrides.emplace_back(dimInfo[2]);
1886 mlir::Value baseAddr =
1887 getBaseAddrFromBox(loc, inputBoxTyPair, loweredBox, rewriter);
1889 if (!rebox.getSlice().empty() || !rebox.getSubcomponent().empty())
1890 return sliceBox(rebox, boxTy, dest, baseAddr, inputExtents, inputStrides,
1891 operands, rewriter);
1892 return reshapeBox(rebox, boxTy, dest, baseAddr, inputExtents, inputStrides,
1893 operands, rewriter);
1896 private:
1897 /// Write resulting shape and base address in descriptor, and replace rebox
1898 /// op.
1899 llvm::LogicalResult
1900 finalizeRebox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
1901 mlir::Value base, mlir::ValueRange lbounds,
1902 mlir::ValueRange extents, mlir::ValueRange strides,
1903 mlir::ConversionPatternRewriter &rewriter) const {
1904 mlir::Location loc = rebox.getLoc();
1905 mlir::Value zero =
1906 genConstantIndex(loc, lowerTy().indexType(), rewriter, 0);
1907 mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1);
1908 for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) {
1909 mlir::Value extent = std::get<0>(iter.value());
1910 unsigned dim = iter.index();
1911 mlir::Value lb = one;
1912 if (!lbounds.empty()) {
1913 lb = lbounds[dim];
1914 auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>(
1915 loc, mlir::LLVM::ICmpPredicate::eq, extent, zero);
1916 lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one, lb);
1918 dest = insertLowerBound(rewriter, loc, dest, dim, lb);
1919 dest = insertExtent(rewriter, loc, dest, dim, extent);
1920 dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value()));
1922 dest = insertBaseAddress(rewriter, loc, dest, base);
1923 mlir::Value result =
1924 placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), destBoxTy, dest);
1925 rewriter.replaceOp(rebox, result);
1926 return mlir::success();
1929 // Apply slice given the base address, extents and strides of the input box.
1930 llvm::LogicalResult
1931 sliceBox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
1932 mlir::Value base, mlir::ValueRange inputExtents,
1933 mlir::ValueRange inputStrides, mlir::ValueRange operands,
1934 mlir::ConversionPatternRewriter &rewriter) const {
1935 mlir::Location loc = rebox.getLoc();
1936 mlir::Type byteTy = ::getI8Type(rebox.getContext());
1937 mlir::Type idxTy = lowerTy().indexType();
1938 mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0);
1939 // Apply subcomponent and substring shift on base address.
1940 if (!rebox.getSubcomponent().empty() || !rebox.getSubstr().empty()) {
1941 // Cast to inputEleTy* so that a GEP can be used.
1942 mlir::Type inputEleTy = getInputEleTy(rebox);
1943 mlir::Type llvmBaseObjectType = convertType(inputEleTy);
1944 llvm::SmallVector<mlir::Value> fieldIndices;
1945 std::optional<mlir::Value> substringOffset;
1946 if (!rebox.getSubcomponent().empty())
1947 getSubcomponentIndices(rebox, rebox.getBox(), operands, fieldIndices);
1948 if (!rebox.getSubstr().empty())
1949 substringOffset = operands[rebox.getSubstrOperandIndex()];
1950 base = genBoxOffsetGep(rewriter, loc, base, llvmBaseObjectType, zero,
1951 /*cstInteriorIndices=*/std::nullopt, fieldIndices,
1952 substringOffset);
1955 if (rebox.getSlice().empty())
1956 // The array section is of the form array[%component][substring], keep
1957 // the input array extents and strides.
1958 return finalizeRebox(rebox, destBoxTy, dest, base,
1959 /*lbounds*/ std::nullopt, inputExtents, inputStrides,
1960 rewriter);
1962 // The slice is of the form array(i:j:k)[%component]. Compute new extents
1963 // and strides.
1964 llvm::SmallVector<mlir::Value> slicedExtents;
1965 llvm::SmallVector<mlir::Value> slicedStrides;
1966 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
1967 const bool sliceHasOrigins = !rebox.getShift().empty();
1968 unsigned sliceOps = rebox.getSliceOperandIndex();
1969 unsigned shiftOps = rebox.getShiftOperandIndex();
1970 auto strideOps = inputStrides.begin();
1971 const unsigned inputRank = inputStrides.size();
1972 for (unsigned i = 0; i < inputRank;
1973 ++i, ++strideOps, ++shiftOps, sliceOps += 3) {
1974 mlir::Value sliceLb =
1975 integerCast(loc, rewriter, idxTy, operands[sliceOps]);
1976 mlir::Value inputStride = *strideOps; // already idxTy
1977 // Apply origin shift: base += (lb-shift)*input_stride
1978 mlir::Value sliceOrigin =
1979 sliceHasOrigins
1980 ? integerCast(loc, rewriter, idxTy, operands[shiftOps])
1981 : one;
1982 mlir::Value diff =
1983 rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin);
1984 mlir::Value offset =
1985 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride);
1986 // Strides from the fir.box are in bytes.
1987 base = genGEP(loc, byteTy, rewriter, base, offset);
1988 // Apply upper bound and step if this is a triplet. Otherwise, the
1989 // dimension is dropped and no extents/strides are computed.
1990 mlir::Value upper = operands[sliceOps + 1];
1991 const bool isTripletSlice =
1992 !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp());
1993 if (isTripletSlice) {
1994 mlir::Value step =
1995 integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]);
1996 // extent = ub-lb+step/step
1997 mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper);
1998 mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb,
1999 sliceUb, step, zero, idxTy);
2000 slicedExtents.emplace_back(extent);
2001 // stride = step*input_stride
2002 mlir::Value stride =
2003 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride);
2004 slicedStrides.emplace_back(stride);
2007 return finalizeRebox(rebox, destBoxTy, dest, base, /*lbounds*/ std::nullopt,
2008 slicedExtents, slicedStrides, rewriter);
2011 /// Apply a new shape to the data described by a box given the base address,
2012 /// extents and strides of the box.
2013 llvm::LogicalResult
2014 reshapeBox(fir::cg::XReboxOp rebox, mlir::Type destBoxTy, mlir::Value dest,
2015 mlir::Value base, mlir::ValueRange inputExtents,
2016 mlir::ValueRange inputStrides, mlir::ValueRange operands,
2017 mlir::ConversionPatternRewriter &rewriter) const {
2018 mlir::ValueRange reboxShifts{
2019 operands.begin() + rebox.getShiftOperandIndex(),
2020 operands.begin() + rebox.getShiftOperandIndex() +
2021 rebox.getShift().size()};
2022 if (rebox.getShape().empty()) {
2023 // Only setting new lower bounds.
2024 return finalizeRebox(rebox, destBoxTy, dest, base, reboxShifts,
2025 inputExtents, inputStrides, rewriter);
2028 mlir::Location loc = rebox.getLoc();
2030 llvm::SmallVector<mlir::Value> newStrides;
2031 llvm::SmallVector<mlir::Value> newExtents;
2032 mlir::Type idxTy = lowerTy().indexType();
2033 // First stride from input box is kept. The rest is assumed contiguous
2034 // (it is not possible to reshape otherwise). If the input is scalar,
2035 // which may be OK if all new extents are ones, the stride does not
2036 // matter, use one.
2037 mlir::Value stride = inputStrides.empty()
2038 ? genConstantIndex(loc, idxTy, rewriter, 1)
2039 : inputStrides[0];
2040 for (unsigned i = 0; i < rebox.getShape().size(); ++i) {
2041 mlir::Value rawExtent = operands[rebox.getShapeOperandIndex() + i];
2042 mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent);
2043 newExtents.emplace_back(extent);
2044 newStrides.emplace_back(stride);
2045 // nextStride = extent * stride;
2046 stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride);
2048 return finalizeRebox(rebox, destBoxTy, dest, base, reboxShifts, newExtents,
2049 newStrides, rewriter);
2052 /// Return scalar element type of the input box.
2053 static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) {
2054 auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.getBox().getType());
2055 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty))
2056 return seqTy.getEleTy();
2057 return ty;
2061 /// Lower `fir.emboxproc` operation. Creates a procedure box.
2062 /// TODO: Part of supporting Fortran 2003 procedure pointers.
2063 struct EmboxProcOpConversion : public fir::FIROpConversion<fir::EmboxProcOp> {
2064 using FIROpConversion::FIROpConversion;
2066 llvm::LogicalResult
2067 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor,
2068 mlir::ConversionPatternRewriter &rewriter) const override {
2069 TODO(emboxproc.getLoc(), "fir.emboxproc codegen");
2070 return mlir::failure();
2074 // Code shared between insert_value and extract_value Ops.
2075 struct ValueOpCommon {
2076 // Translate the arguments pertaining to any multidimensional array to
2077 // row-major order for LLVM-IR.
2078 static void toRowMajor(llvm::SmallVectorImpl<int64_t> &indices,
2079 mlir::Type ty) {
2080 assert(ty && "type is null");
2081 const auto end = indices.size();
2082 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
2083 if (auto seq = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty)) {
2084 const auto dim = getDimension(seq);
2085 if (dim > 1) {
2086 auto ub = std::min(i + dim, end);
2087 std::reverse(indices.begin() + i, indices.begin() + ub);
2088 i += dim - 1;
2090 ty = getArrayElementType(seq);
2091 } else if (auto st = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(ty)) {
2092 ty = st.getBody()[indices[i]];
2093 } else {
2094 llvm_unreachable("index into invalid type");
2099 static llvm::SmallVector<int64_t>
2100 collectIndices(mlir::ConversionPatternRewriter &rewriter,
2101 mlir::ArrayAttr arrAttr) {
2102 llvm::SmallVector<int64_t> indices;
2103 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
2104 if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(*i)) {
2105 indices.push_back(intAttr.getInt());
2106 } else {
2107 auto fieldName = mlir::cast<mlir::StringAttr>(*i).getValue();
2108 ++i;
2109 auto ty = mlir::cast<mlir::TypeAttr>(*i).getValue();
2110 auto index = mlir::cast<fir::RecordType>(ty).getFieldIndex(fieldName);
2111 indices.push_back(index);
2114 return indices;
2117 private:
2118 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
2119 auto eleTy = ty.getElementType();
2120 while (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy))
2121 eleTy = arrTy.getElementType();
2122 return eleTy;
2126 namespace {
2127 /// Extract a subobject value from an ssa-value of aggregate type
2128 struct ExtractValueOpConversion
2129 : public fir::FIROpAndTypeConversion<fir::ExtractValueOp>,
2130 public ValueOpCommon {
2131 using FIROpAndTypeConversion::FIROpAndTypeConversion;
2133 llvm::LogicalResult
2134 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
2135 mlir::ConversionPatternRewriter &rewriter) const override {
2136 mlir::ValueRange operands = adaptor.getOperands();
2137 auto indices = collectIndices(rewriter, extractVal.getCoor());
2138 toRowMajor(indices, operands[0].getType());
2139 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
2140 extractVal, operands[0], indices);
2141 return mlir::success();
2145 /// InsertValue is the generalized instruction for the composition of new
2146 /// aggregate type values.
2147 struct InsertValueOpConversion
2148 : public mlir::OpConversionPattern<fir::InsertValueOp>,
2149 public ValueOpCommon {
2150 using OpConversionPattern::OpConversionPattern;
2152 llvm::LogicalResult
2153 matchAndRewrite(fir::InsertValueOp insertVal, OpAdaptor adaptor,
2154 mlir::ConversionPatternRewriter &rewriter) const override {
2155 mlir::ValueRange operands = adaptor.getOperands();
2156 auto indices = collectIndices(rewriter, insertVal.getCoor());
2157 toRowMajor(indices, operands[0].getType());
2158 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2159 insertVal, operands[0], operands[1], indices);
2160 return mlir::success();
2164 /// InsertOnRange inserts a value into a sequence over a range of offsets.
2165 struct InsertOnRangeOpConversion
2166 : public fir::FIROpAndTypeConversion<fir::InsertOnRangeOp> {
2167 using FIROpAndTypeConversion::FIROpAndTypeConversion;
2169 // Increments an array of subscripts in a row major fasion.
2170 void incrementSubscripts(llvm::ArrayRef<int64_t> dims,
2171 llvm::SmallVectorImpl<int64_t> &subscripts) const {
2172 for (size_t i = dims.size(); i > 0; --i) {
2173 if (++subscripts[i - 1] < dims[i - 1]) {
2174 return;
2176 subscripts[i - 1] = 0;
2180 llvm::LogicalResult
2181 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
2182 mlir::ConversionPatternRewriter &rewriter) const override {
2184 llvm::SmallVector<std::int64_t> dims;
2185 auto type = adaptor.getOperands()[0].getType();
2187 // Iteratively extract the array dimensions from the type.
2188 while (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) {
2189 dims.push_back(t.getNumElements());
2190 type = t.getElementType();
2193 llvm::SmallVector<std::int64_t> lBounds;
2194 llvm::SmallVector<std::int64_t> uBounds;
2196 // Unzip the upper and lower bound and convert to a row major format.
2197 mlir::DenseIntElementsAttr coor = range.getCoor();
2198 auto reversedCoor = llvm::reverse(coor.getValues<int64_t>());
2199 for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) {
2200 uBounds.push_back(*i++);
2201 lBounds.push_back(*i);
2204 auto &subscripts = lBounds;
2205 auto loc = range.getLoc();
2206 mlir::Value lastOp = adaptor.getOperands()[0];
2207 mlir::Value insertVal = adaptor.getOperands()[1];
2209 while (subscripts != uBounds) {
2210 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
2211 loc, lastOp, insertVal, subscripts);
2213 incrementSubscripts(dims, subscripts);
2216 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
2217 range, lastOp, insertVal, subscripts);
2219 return mlir::success();
2222 } // namespace
2224 namespace {
2225 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced,
2226 /// shifted etc. array.
2227 /// (See the static restriction on coordinate_of.) array_coor determines the
2228 /// coordinate (location) of a specific element.
2229 struct XArrayCoorOpConversion
2230 : public fir::FIROpAndTypeConversion<fir::cg::XArrayCoorOp> {
2231 using FIROpAndTypeConversion::FIROpAndTypeConversion;
2233 llvm::LogicalResult
2234 doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type llvmPtrTy, OpAdaptor adaptor,
2235 mlir::ConversionPatternRewriter &rewriter) const override {
2236 auto loc = coor.getLoc();
2237 mlir::ValueRange operands = adaptor.getOperands();
2238 unsigned rank = coor.getRank();
2239 assert(coor.getIndices().size() == rank);
2240 assert(coor.getShape().empty() || coor.getShape().size() == rank);
2241 assert(coor.getShift().empty() || coor.getShift().size() == rank);
2242 assert(coor.getSlice().empty() || coor.getSlice().size() == 3 * rank);
2243 mlir::Type idxTy = lowerTy().indexType();
2244 unsigned indexOffset = coor.getIndicesOperandIndex();
2245 unsigned shapeOffset = coor.getShapeOperandIndex();
2246 unsigned shiftOffset = coor.getShiftOperandIndex();
2247 unsigned sliceOffset = coor.getSliceOperandIndex();
2248 auto sliceOps = coor.getSlice().begin();
2249 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1);
2250 mlir::Value prevExt = one;
2251 mlir::Value offset = genConstantIndex(loc, idxTy, rewriter, 0);
2252 const bool isShifted = !coor.getShift().empty();
2253 const bool isSliced = !coor.getSlice().empty();
2254 const bool baseIsBoxed =
2255 mlir::isa<fir::BaseBoxType>(coor.getMemref().getType());
2256 TypePair baseBoxTyPair =
2257 baseIsBoxed ? getBoxTypePair(coor.getMemref().getType()) : TypePair{};
2258 mlir::LLVM::IntegerOverflowFlags nsw =
2259 mlir::LLVM::IntegerOverflowFlags::nsw;
2261 // For each dimension of the array, generate the offset calculation.
2262 for (unsigned i = 0; i < rank; ++i, ++indexOffset, ++shapeOffset,
2263 ++shiftOffset, sliceOffset += 3, sliceOps += 3) {
2264 mlir::Value index =
2265 integerCast(loc, rewriter, idxTy, operands[indexOffset]);
2266 mlir::Value lb =
2267 isShifted ? integerCast(loc, rewriter, idxTy, operands[shiftOffset])
2268 : one;
2269 mlir::Value step = one;
2270 bool normalSlice = isSliced;
2271 // Compute zero based index in dimension i of the element, applying
2272 // potential triplets and lower bounds.
2273 if (isSliced) {
2274 mlir::Value originalUb = *(sliceOps + 1);
2275 normalSlice =
2276 !mlir::isa_and_nonnull<fir::UndefOp>(originalUb.getDefiningOp());
2277 if (normalSlice)
2278 step = integerCast(loc, rewriter, idxTy, operands[sliceOffset + 2]);
2280 auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb, nsw);
2281 mlir::Value diff =
2282 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step, nsw);
2283 if (normalSlice) {
2284 mlir::Value sliceLb =
2285 integerCast(loc, rewriter, idxTy, operands[sliceOffset]);
2286 auto adj =
2287 rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb, nsw);
2288 diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj, nsw);
2290 // Update the offset given the stride and the zero based index `diff`
2291 // that was just computed.
2292 if (baseIsBoxed) {
2293 // Use stride in bytes from the descriptor.
2294 mlir::Value stride =
2295 getStrideFromBox(loc, baseBoxTyPair, operands[0], i, rewriter);
2296 auto sc =
2297 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride, nsw);
2298 offset =
2299 rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw);
2300 } else {
2301 // Use stride computed at last iteration.
2302 auto sc =
2303 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt, nsw);
2304 offset =
2305 rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw);
2306 // Compute next stride assuming contiguity of the base array
2307 // (in element number).
2308 auto nextExt = integerCast(loc, rewriter, idxTy, operands[shapeOffset]);
2309 prevExt = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt,
2310 nextExt, nsw);
2314 // Add computed offset to the base address.
2315 if (baseIsBoxed) {
2316 // Working with byte offsets. The base address is read from the fir.box.
2317 // and used in i8* GEP to do the pointer arithmetic.
2318 mlir::Type byteTy = ::getI8Type(coor.getContext());
2319 mlir::Value base =
2320 getBaseAddrFromBox(loc, baseBoxTyPair, operands[0], rewriter);
2321 llvm::SmallVector<mlir::LLVM::GEPArg> args{offset};
2322 auto addr = rewriter.create<mlir::LLVM::GEPOp>(loc, llvmPtrTy, byteTy,
2323 base, args);
2324 if (coor.getSubcomponent().empty()) {
2325 rewriter.replaceOp(coor, addr);
2326 return mlir::success();
2328 // Cast the element address from void* to the derived type so that the
2329 // derived type members can be addresses via a GEP using the index of
2330 // components.
2331 mlir::Type elementType =
2332 getLlvmObjectTypeFromBoxType(coor.getMemref().getType());
2333 while (auto arrayTy =
2334 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(elementType))
2335 elementType = arrayTy.getElementType();
2336 args.clear();
2337 args.push_back(0);
2338 if (!coor.getLenParams().empty()) {
2339 // If type parameters are present, then we don't want to use a GEPOp
2340 // as below, as the LLVM struct type cannot be statically defined.
2341 TODO(loc, "derived type with type parameters");
2343 llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices(
2344 loc, elementType,
2345 operands.slice(coor.getSubcomponentOperandIndex(),
2346 coor.getSubcomponent().size()));
2347 args.append(indices.begin(), indices.end());
2348 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, llvmPtrTy,
2349 elementType, addr, args);
2350 return mlir::success();
2353 // The array was not boxed, so it must be contiguous. offset is therefore an
2354 // element offset and the base type is kept in the GEP unless the element
2355 // type size is itself dynamic.
2356 mlir::Type objectTy = fir::unwrapRefType(coor.getMemref().getType());
2357 mlir::Type eleType = fir::unwrapSequenceType(objectTy);
2358 mlir::Type gepObjectType = convertType(eleType);
2359 llvm::SmallVector<mlir::LLVM::GEPArg> args;
2360 if (coor.getSubcomponent().empty()) {
2361 // No subcomponent.
2362 if (!coor.getLenParams().empty()) {
2363 // Type parameters. Adjust element size explicitly.
2364 auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType());
2365 assert(eleTy && "result must be a reference-like type");
2366 if (fir::characterWithDynamicLen(eleTy)) {
2367 assert(coor.getLenParams().size() == 1);
2368 auto length = integerCast(loc, rewriter, idxTy,
2369 operands[coor.getLenParamsOperandIndex()]);
2370 offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset,
2371 length, nsw);
2372 } else {
2373 TODO(loc, "compute size of derived type with type parameters");
2376 args.push_back(offset);
2377 } else {
2378 // There are subcomponents.
2379 args.push_back(offset);
2380 llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices(
2381 loc, gepObjectType,
2382 operands.slice(coor.getSubcomponentOperandIndex(),
2383 coor.getSubcomponent().size()));
2384 args.append(indices.begin(), indices.end());
2386 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
2387 coor, llvmPtrTy, gepObjectType, adaptor.getMemref(), args);
2388 return mlir::success();
2391 } // namespace
2393 /// Convert to (memory) reference to a reference to a subobject.
2394 /// The coordinate_of op is a Swiss army knife operation that can be used on
2395 /// (memory) references to records, arrays, complex, etc. as well as boxes.
2396 /// With unboxed arrays, there is the restriction that the array have a static
2397 /// shape in all but the last column.
2398 struct CoordinateOpConversion
2399 : public fir::FIROpAndTypeConversion<fir::CoordinateOp> {
2400 using FIROpAndTypeConversion::FIROpAndTypeConversion;
2402 llvm::LogicalResult
2403 doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor,
2404 mlir::ConversionPatternRewriter &rewriter) const override {
2405 mlir::ValueRange operands = adaptor.getOperands();
2407 mlir::Location loc = coor.getLoc();
2408 mlir::Value base = operands[0];
2409 mlir::Type baseObjectTy = coor.getBaseType();
2410 mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2411 assert(objectTy && "fir.coordinate_of expects a reference type");
2412 mlir::Type llvmObjectTy = convertType(objectTy);
2414 // Complex type - basically, extract the real or imaginary part
2415 // FIXME: double check why this is done before the fir.box case below.
2416 if (fir::isa_complex(objectTy)) {
2417 mlir::Value gep =
2418 genGEP(loc, llvmObjectTy, rewriter, base, 0, operands[1]);
2419 rewriter.replaceOp(coor, gep);
2420 return mlir::success();
2423 // Boxed type - get the base pointer from the box
2424 if (mlir::dyn_cast<fir::BaseBoxType>(baseObjectTy))
2425 return doRewriteBox(coor, operands, loc, rewriter);
2427 // Reference, pointer or a heap type
2428 if (mlir::isa<fir::ReferenceType, fir::PointerType, fir::HeapType>(
2429 baseObjectTy))
2430 return doRewriteRefOrPtr(coor, llvmObjectTy, operands, loc, rewriter);
2432 return rewriter.notifyMatchFailure(
2433 coor, "fir.coordinate_of base operand has unsupported type");
2436 static unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) {
2437 return fir::hasDynamicSize(ty)
2438 ? op.getDefiningOp()
2439 ->getAttrOfType<mlir::IntegerAttr>("field")
2440 .getInt()
2441 : getConstantIntValue(op);
2444 static bool hasSubDimensions(mlir::Type type) {
2445 return mlir::isa<fir::SequenceType, fir::RecordType, mlir::TupleType>(type);
2448 /// Check whether this form of `!fir.coordinate_of` is supported. These
2449 /// additional checks are required, because we are not yet able to convert
2450 /// all valid forms of `!fir.coordinate_of`.
2451 /// TODO: Either implement the unsupported cases or extend the verifier
2452 /// in FIROps.cpp instead.
2453 static bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) {
2454 const std::size_t numOfCoors = coors.size();
2455 std::size_t i = 0;
2456 bool subEle = false;
2457 bool ptrEle = false;
2458 for (; i < numOfCoors; ++i) {
2459 mlir::Value nxtOpnd = coors[i];
2460 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) {
2461 subEle = true;
2462 i += arrTy.getDimension() - 1;
2463 type = arrTy.getEleTy();
2464 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(type)) {
2465 subEle = true;
2466 type = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2467 } else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(type)) {
2468 subEle = true;
2469 type = tupTy.getType(getConstantIntValue(nxtOpnd));
2470 } else {
2471 ptrEle = true;
2474 if (ptrEle)
2475 return (!subEle) && (numOfCoors == 1);
2476 return subEle && (i >= numOfCoors);
2479 /// Walk the abstract memory layout and determine if the path traverses any
2480 /// array types with unknown shape. Return true iff all the array types have a
2481 /// constant shape along the path.
2482 static bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) {
2483 for (std::size_t i = 0, sz = coors.size(); i < sz; ++i) {
2484 mlir::Value nxtOpnd = coors[i];
2485 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) {
2486 if (fir::sequenceWithNonConstantShape(arrTy))
2487 return false;
2488 i += arrTy.getDimension() - 1;
2489 type = arrTy.getEleTy();
2490 } else if (auto strTy = mlir::dyn_cast<fir::RecordType>(type)) {
2491 type = strTy.getType(getFieldNumber(strTy, nxtOpnd));
2492 } else if (auto strTy = mlir::dyn_cast<mlir::TupleType>(type)) {
2493 type = strTy.getType(getConstantIntValue(nxtOpnd));
2494 } else {
2495 return true;
2498 return true;
2501 private:
2502 llvm::LogicalResult
2503 doRewriteBox(fir::CoordinateOp coor, mlir::ValueRange operands,
2504 mlir::Location loc,
2505 mlir::ConversionPatternRewriter &rewriter) const {
2506 mlir::Type boxObjTy = coor.getBaseType();
2507 assert(mlir::dyn_cast<fir::BaseBoxType>(boxObjTy) &&
2508 "This is not a `fir.box`");
2509 TypePair boxTyPair = getBoxTypePair(boxObjTy);
2511 mlir::Value boxBaseAddr = operands[0];
2513 // 1. SPECIAL CASE (uses `fir.len_param_index`):
2514 // %box = ... : !fir.box<!fir.type<derived{len1:i32}>>
2515 // %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}>
2516 // %addr = coordinate_of %box, %lenp
2517 if (coor.getNumOperands() == 2) {
2518 mlir::Operation *coordinateDef =
2519 (*coor.getCoor().begin()).getDefiningOp();
2520 if (mlir::isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef))
2521 TODO(loc,
2522 "fir.coordinate_of - fir.len_param_index is not supported yet");
2525 // 2. GENERAL CASE:
2526 // 2.1. (`fir.array`)
2527 // %box = ... : !fix.box<!fir.array<?xU>>
2528 // %idx = ... : index
2529 // %resultAddr = coordinate_of %box, %idx : !fir.ref<U>
2530 // 2.2 (`fir.derived`)
2531 // %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>>
2532 // %idx = ... : i32
2533 // %resultAddr = coordinate_of %box, %idx : !fir.ref<i32>
2534 // 2.3 (`fir.derived` inside `fir.array`)
2535 // %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32,
2536 // field_2:f32}>>> %idx1 = ... : index %idx2 = ... : i32 %resultAddr =
2537 // coordinate_of %box, %idx1, %idx2 : !fir.ref<f32>
2538 // 2.4. TODO: Either document or disable any other case that the following
2539 // implementation might convert.
2540 mlir::Value resultAddr =
2541 getBaseAddrFromBox(loc, boxTyPair, boxBaseAddr, rewriter);
2542 // Component Type
2543 auto cpnTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy);
2544 mlir::Type llvmPtrTy = ::getLlvmPtrType(coor.getContext());
2545 mlir::Type byteTy = ::getI8Type(coor.getContext());
2546 mlir::LLVM::IntegerOverflowFlags nsw =
2547 mlir::LLVM::IntegerOverflowFlags::nsw;
2549 for (unsigned i = 1, last = operands.size(); i < last; ++i) {
2550 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2551 if (i != 1)
2552 TODO(loc, "fir.array nested inside other array and/or derived type");
2553 // Applies byte strides from the box. Ignore lower bound from box
2554 // since fir.coordinate_of indexes are zero based. Lowering takes care
2555 // of lower bound aspects. This both accounts for dynamically sized
2556 // types and non contiguous arrays.
2557 auto idxTy = lowerTy().indexType();
2558 mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0);
2559 for (unsigned index = i, lastIndex = i + arrTy.getDimension();
2560 index < lastIndex; ++index) {
2561 mlir::Value stride = getStrideFromBox(loc, boxTyPair, operands[0],
2562 index - i, rewriter);
2563 auto sc = rewriter.create<mlir::LLVM::MulOp>(
2564 loc, idxTy, operands[index], stride, nsw);
2565 off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off, nsw);
2567 resultAddr = rewriter.create<mlir::LLVM::GEPOp>(
2568 loc, llvmPtrTy, byteTy, resultAddr,
2569 llvm::ArrayRef<mlir::LLVM::GEPArg>{off});
2570 i += arrTy.getDimension() - 1;
2571 cpnTy = arrTy.getEleTy();
2572 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy)) {
2573 mlir::Value nxtOpnd = operands[i];
2574 cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2575 auto llvmRecTy = lowerTy().convertType(recTy);
2576 resultAddr = rewriter.create<mlir::LLVM::GEPOp>(
2577 loc, llvmPtrTy, llvmRecTy, resultAddr,
2578 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, nxtOpnd});
2579 } else {
2580 fir::emitFatalError(loc, "unexpected type in coordinate_of");
2584 rewriter.replaceOp(coor, resultAddr);
2585 return mlir::success();
2588 llvm::LogicalResult
2589 doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type llvmObjectTy,
2590 mlir::ValueRange operands, mlir::Location loc,
2591 mlir::ConversionPatternRewriter &rewriter) const {
2592 mlir::Type baseObjectTy = coor.getBaseType();
2594 // Component Type
2595 mlir::Type cpnTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy);
2596 bool hasSubdimension = hasSubDimensions(cpnTy);
2597 bool columnIsDeferred = !hasSubdimension;
2599 if (!supportedCoordinate(cpnTy, operands.drop_front(1)))
2600 TODO(loc, "unsupported combination of coordinate operands");
2602 const bool hasKnownShape =
2603 arraysHaveKnownShape(cpnTy, operands.drop_front(1));
2605 // If only the column is `?`, then we can simply place the column value in
2606 // the 0-th GEP position.
2607 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2608 if (!hasKnownShape) {
2609 const unsigned sz = arrTy.getDimension();
2610 if (arraysHaveKnownShape(arrTy.getEleTy(),
2611 operands.drop_front(1 + sz))) {
2612 fir::SequenceType::ShapeRef shape = arrTy.getShape();
2613 bool allConst = true;
2614 for (unsigned i = 0; i < sz - 1; ++i) {
2615 if (shape[i] < 0) {
2616 allConst = false;
2617 break;
2620 if (allConst)
2621 columnIsDeferred = true;
2626 if (fir::hasDynamicSize(fir::unwrapSequenceType(cpnTy)))
2627 return mlir::emitError(
2628 loc, "fir.coordinate_of with a dynamic element size is unsupported");
2630 if (hasKnownShape || columnIsDeferred) {
2631 llvm::SmallVector<mlir::LLVM::GEPArg> offs;
2632 if (hasKnownShape && hasSubdimension) {
2633 offs.push_back(0);
2635 std::optional<int> dims;
2636 llvm::SmallVector<mlir::Value> arrIdx;
2637 for (std::size_t i = 1, sz = operands.size(); i < sz; ++i) {
2638 mlir::Value nxtOpnd = operands[i];
2640 if (!cpnTy)
2641 return mlir::emitError(loc, "invalid coordinate/check failed");
2643 // check if the i-th coordinate relates to an array
2644 if (dims) {
2645 arrIdx.push_back(nxtOpnd);
2646 int dimsLeft = *dims;
2647 if (dimsLeft > 1) {
2648 dims = dimsLeft - 1;
2649 continue;
2651 cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType();
2652 // append array range in reverse (FIR arrays are column-major)
2653 offs.append(arrIdx.rbegin(), arrIdx.rend());
2654 arrIdx.clear();
2655 dims.reset();
2656 continue;
2658 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) {
2659 int d = arrTy.getDimension() - 1;
2660 if (d > 0) {
2661 dims = d;
2662 arrIdx.push_back(nxtOpnd);
2663 continue;
2665 cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType();
2666 offs.push_back(nxtOpnd);
2667 continue;
2670 // check if the i-th coordinate relates to a field
2671 if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy))
2672 cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd));
2673 else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(cpnTy))
2674 cpnTy = tupTy.getType(getConstantIntValue(nxtOpnd));
2675 else
2676 cpnTy = nullptr;
2678 offs.push_back(nxtOpnd);
2680 if (dims)
2681 offs.append(arrIdx.rbegin(), arrIdx.rend());
2682 mlir::Value base = operands[0];
2683 mlir::Value retval = genGEP(loc, llvmObjectTy, rewriter, base, offs);
2684 rewriter.replaceOp(coor, retval);
2685 return mlir::success();
2688 return mlir::emitError(
2689 loc, "fir.coordinate_of base operand has unsupported type");
2693 /// Convert `fir.field_index`. The conversion depends on whether the size of
2694 /// the record is static or dynamic.
2695 struct FieldIndexOpConversion : public fir::FIROpConversion<fir::FieldIndexOp> {
2696 using FIROpConversion::FIROpConversion;
2698 // NB: most field references should be resolved by this point
2699 llvm::LogicalResult
2700 matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor,
2701 mlir::ConversionPatternRewriter &rewriter) const override {
2702 auto recTy = mlir::cast<fir::RecordType>(field.getOnType());
2703 unsigned index = recTy.getFieldIndex(field.getFieldId());
2705 if (!fir::hasDynamicSize(recTy)) {
2706 // Derived type has compile-time constant layout. Return index of the
2707 // component type in the parent type (to be used in GEP).
2708 rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset(
2709 field.getLoc(), rewriter, index)});
2710 return mlir::success();
2713 // Derived type has compile-time constant layout. Call the compiler
2714 // generated function to determine the byte offset of the field at runtime.
2715 // This returns a non-constant.
2716 mlir::FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get(
2717 field.getContext(), getOffsetMethodName(recTy, field.getFieldId()));
2718 mlir::NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr);
2719 mlir::NamedAttribute fieldAttr = rewriter.getNamedAttr(
2720 "field", mlir::IntegerAttr::get(lowerTy().indexType(), index));
2721 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2722 field, lowerTy().offsetType(), adaptor.getOperands(),
2723 addLLVMOpBundleAttrs(rewriter, {callAttr, fieldAttr},
2724 adaptor.getOperands().size()));
2725 return mlir::success();
2728 // Re-Construct the name of the compiler generated method that calculates the
2729 // offset
2730 inline static std::string getOffsetMethodName(fir::RecordType recTy,
2731 llvm::StringRef field) {
2732 return recTy.getName().str() + "P." + field.str() + ".offset";
2736 /// Convert `fir.end`
2737 struct FirEndOpConversion : public fir::FIROpConversion<fir::FirEndOp> {
2738 using FIROpConversion::FIROpConversion;
2740 llvm::LogicalResult
2741 matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor,
2742 mlir::ConversionPatternRewriter &rewriter) const override {
2743 TODO(firEnd.getLoc(), "fir.end codegen");
2744 return mlir::failure();
2748 /// Lower `fir.type_desc` to a global addr.
2749 struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> {
2750 using FIROpConversion::FIROpConversion;
2752 llvm::LogicalResult
2753 matchAndRewrite(fir::TypeDescOp typeDescOp, OpAdaptor adaptor,
2754 mlir::ConversionPatternRewriter &rewriter) const override {
2755 mlir::Type inTy = typeDescOp.getInType();
2756 assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type");
2757 auto recordType = mlir::dyn_cast<fir::RecordType>(inTy);
2758 auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>();
2759 std::string typeDescName =
2760 this->options.typeDescriptorsRenamedForAssembly
2761 ? fir::NameUniquer::getTypeDescriptorAssemblyName(
2762 recordType.getName())
2763 : fir::NameUniquer::getTypeDescriptorName(recordType.getName());
2764 auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext());
2765 if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) {
2766 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
2767 typeDescOp, llvmPtrTy, global.getSymName());
2768 return mlir::success();
2769 } else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) {
2770 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
2771 typeDescOp, llvmPtrTy, global.getSymName());
2772 return mlir::success();
2774 return mlir::failure();
2778 /// Lower `fir.has_value` operation to `llvm.return` operation.
2779 struct HasValueOpConversion
2780 : public mlir::OpConversionPattern<fir::HasValueOp> {
2781 using OpConversionPattern::OpConversionPattern;
2783 llvm::LogicalResult
2784 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
2785 mlir::ConversionPatternRewriter &rewriter) const override {
2786 rewriter.replaceOpWithNewOp<mlir::LLVM::ReturnOp>(op,
2787 adaptor.getOperands());
2788 return mlir::success();
2792 #ifndef NDEBUG
2793 // Check if attr's type is compatible with ty.
2795 // This is done by comparing attr's element type, converted to LLVM type,
2796 // with ty's element type.
2798 // Only integer and floating point (including complex) attributes are
2799 // supported. Also, attr is expected to have a TensorType and ty is expected
2800 // to be of LLVMArrayType. If any of the previous conditions is false, then
2801 // the specified attr and ty are not supported by this function and are
2802 // assumed to be compatible.
2803 static inline bool attributeTypeIsCompatible(mlir::MLIRContext *ctx,
2804 mlir::Attribute attr,
2805 mlir::Type ty) {
2806 // Get attr's LLVM element type.
2807 if (!attr)
2808 return true;
2809 auto intOrFpEleAttr = mlir::dyn_cast<mlir::DenseIntOrFPElementsAttr>(attr);
2810 if (!intOrFpEleAttr)
2811 return true;
2812 auto tensorTy = mlir::dyn_cast<mlir::TensorType>(intOrFpEleAttr.getType());
2813 if (!tensorTy)
2814 return true;
2815 mlir::Type attrEleTy =
2816 mlir::LLVMTypeConverter(ctx).convertType(tensorTy.getElementType());
2818 // Get ty's element type.
2819 auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty);
2820 if (!arrTy)
2821 return true;
2822 mlir::Type eleTy = arrTy.getElementType();
2823 while ((arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)))
2824 eleTy = arrTy.getElementType();
2826 return attrEleTy == eleTy;
2828 #endif
2830 /// Lower `fir.global` operation to `llvm.global` operation.
2831 /// `fir.insert_on_range` operations are replaced with constant dense attribute
2832 /// if they are applied on the full range.
2833 struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
2834 using FIROpConversion::FIROpConversion;
2836 llvm::LogicalResult
2837 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
2838 mlir::ConversionPatternRewriter &rewriter) const override {
2840 llvm::SmallVector<mlir::Attribute> dbgExprs;
2842 if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(global.getLoc())) {
2843 if (auto gvAttr =
2844 mlir::dyn_cast_or_null<mlir::LLVM::DIGlobalVariableAttr>(
2845 fusedLoc.getMetadata())) {
2846 dbgExprs.push_back(mlir::LLVM::DIGlobalVariableExpressionAttr::get(
2847 global.getContext(), gvAttr, mlir::LLVM::DIExpressionAttr()));
2851 auto tyAttr = convertType(global.getType());
2852 if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType()))
2853 tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType);
2854 auto loc = global.getLoc();
2855 mlir::Attribute initAttr = global.getInitVal().value_or(mlir::Attribute());
2856 assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr));
2857 auto linkage = convertLinkage(global.getLinkName());
2858 auto isConst = global.getConstant().has_value();
2859 mlir::SymbolRefAttr comdat;
2860 llvm::ArrayRef<mlir::NamedAttribute> attrs;
2861 auto g = rewriter.create<mlir::LLVM::GlobalOp>(
2862 loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0,
2863 false, false, comdat, attrs, dbgExprs);
2865 if (global.getAlignment() && *global.getAlignment() > 0)
2866 g.setAlignment(*global.getAlignment());
2868 auto module = global->getParentOfType<mlir::ModuleOp>();
2869 // Add comdat if necessary
2870 if (fir::getTargetTriple(module).supportsCOMDAT() &&
2871 (linkage == mlir::LLVM::Linkage::Linkonce ||
2872 linkage == mlir::LLVM::Linkage::LinkonceODR)) {
2873 addComdat(g, rewriter, module);
2876 // Apply all non-Fir::GlobalOp attributes to the LLVM::GlobalOp, preserving
2877 // them; whilst taking care not to apply attributes that are lowered in
2878 // other ways.
2879 llvm::SmallDenseSet<llvm::StringRef> elidedAttrsSet(
2880 global.getAttributeNames().begin(), global.getAttributeNames().end());
2881 for (auto &attr : global->getAttrs())
2882 if (!elidedAttrsSet.contains(attr.getName().strref()))
2883 g->setAttr(attr.getName(), attr.getValue());
2885 auto &gr = g.getInitializerRegion();
2886 rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end());
2887 if (!gr.empty()) {
2888 // Replace insert_on_range with a constant dense attribute if the
2889 // initialization is on the full range.
2890 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
2891 for (auto insertOp : insertOnRangeOps) {
2892 if (isFullRange(insertOp.getCoor(), insertOp.getType())) {
2893 auto seqTyAttr = convertType(insertOp.getType());
2894 auto *op = insertOp.getVal().getDefiningOp();
2895 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
2896 if (!constant) {
2897 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
2898 if (!convertOp)
2899 continue;
2900 constant = mlir::cast<mlir::arith::ConstantOp>(
2901 convertOp.getValue().getDefiningOp());
2903 mlir::Type vecType = mlir::VectorType::get(
2904 insertOp.getType().getShape(), constant.getType());
2905 auto denseAttr = mlir::DenseElementsAttr::get(
2906 mlir::cast<mlir::ShapedType>(vecType), constant.getValue());
2907 rewriter.setInsertionPointAfter(insertOp);
2908 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
2909 insertOp, seqTyAttr, denseAttr);
2913 rewriter.eraseOp(global);
2914 return mlir::success();
2917 bool isFullRange(mlir::DenseIntElementsAttr indexes,
2918 fir::SequenceType seqTy) const {
2919 auto extents = seqTy.getShape();
2920 if (indexes.size() / 2 != static_cast<int64_t>(extents.size()))
2921 return false;
2922 auto cur_index = indexes.value_begin<int64_t>();
2923 for (unsigned i = 0; i < indexes.size(); i += 2) {
2924 if (*(cur_index++) != 0)
2925 return false;
2926 if (*(cur_index++) != extents[i / 2] - 1)
2927 return false;
2929 return true;
2932 // TODO: String comparaison should be avoided. Replace linkName with an
2933 // enumeration.
2934 mlir::LLVM::Linkage
2935 convertLinkage(std::optional<llvm::StringRef> optLinkage) const {
2936 if (optLinkage) {
2937 auto name = *optLinkage;
2938 if (name == "internal")
2939 return mlir::LLVM::Linkage::Internal;
2940 if (name == "linkonce")
2941 return mlir::LLVM::Linkage::Linkonce;
2942 if (name == "linkonce_odr")
2943 return mlir::LLVM::Linkage::LinkonceODR;
2944 if (name == "common")
2945 return mlir::LLVM::Linkage::Common;
2946 if (name == "weak")
2947 return mlir::LLVM::Linkage::Weak;
2949 return mlir::LLVM::Linkage::External;
2952 private:
2953 static void addComdat(mlir::LLVM::GlobalOp &global,
2954 mlir::ConversionPatternRewriter &rewriter,
2955 mlir::ModuleOp &module) {
2956 const char *comdatName = "__llvm_comdat";
2957 mlir::LLVM::ComdatOp comdatOp =
2958 module.lookupSymbol<mlir::LLVM::ComdatOp>(comdatName);
2959 if (!comdatOp) {
2960 comdatOp =
2961 rewriter.create<mlir::LLVM::ComdatOp>(module.getLoc(), comdatName);
2963 if (auto select = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(
2964 global.getSymName()))
2965 return;
2966 mlir::OpBuilder::InsertionGuard guard(rewriter);
2967 rewriter.setInsertionPointToEnd(&comdatOp.getBody().back());
2968 auto selectorOp = rewriter.create<mlir::LLVM::ComdatSelectorOp>(
2969 comdatOp.getLoc(), global.getSymName(),
2970 mlir::LLVM::comdat::Comdat::Any);
2971 global.setComdatAttr(mlir::SymbolRefAttr::get(
2972 rewriter.getContext(), comdatName,
2973 mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr())));
2977 /// `fir.load` --> `llvm.load`
2978 struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> {
2979 using FIROpConversion::FIROpConversion;
2981 llvm::LogicalResult
2982 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor,
2983 mlir::ConversionPatternRewriter &rewriter) const override {
2985 mlir::Type llvmLoadTy = convertObjectType(load.getType());
2986 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(load.getType())) {
2987 // fir.box is a special case because it is considered an ssa value in
2988 // fir, but it is lowered as a pointer to a descriptor. So
2989 // fir.ref<fir.box> and fir.box end up being the same llvm types and
2990 // loading a fir.ref<fir.box> is implemented as taking a snapshot of the
2991 // descriptor value into a new descriptor temp.
2992 auto inputBoxStorage = adaptor.getOperands()[0];
2993 mlir::Location loc = load.getLoc();
2994 auto newBoxStorage =
2995 genAllocaAndAddrCastWithType(loc, llvmLoadTy, defaultAlign, rewriter);
2997 TypePair boxTypePair{boxTy, llvmLoadTy};
2998 mlir::Value boxSize =
2999 computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter);
3000 auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>(
3001 loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false);
3003 if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
3004 memcpy.setTBAATags(*optionalTag);
3005 else
3006 attachTBAATag(memcpy, boxTy, boxTy, nullptr);
3007 rewriter.replaceOp(load, newBoxStorage);
3008 } else {
3009 auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(
3010 load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs());
3011 if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa())
3012 loadOp.setTBAATags(*optionalTag);
3013 else
3014 attachTBAATag(loadOp, load.getType(), load.getType(), nullptr);
3015 rewriter.replaceOp(load, loadOp.getResult());
3017 return mlir::success();
3021 /// Lower `fir.no_reassoc` to LLVM IR dialect.
3022 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast
3023 /// math flags?
3024 struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> {
3025 using FIROpConversion::FIROpConversion;
3027 llvm::LogicalResult
3028 matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor,
3029 mlir::ConversionPatternRewriter &rewriter) const override {
3030 rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]);
3031 return mlir::success();
3035 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest,
3036 std::optional<mlir::ValueRange> destOps,
3037 mlir::ConversionPatternRewriter &rewriter,
3038 mlir::Block *newBlock) {
3039 if (destOps)
3040 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, *destOps, newBlock,
3041 mlir::ValueRange());
3042 else
3043 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock);
3046 template <typename A, typename B>
3047 static void genBrOp(A caseOp, mlir::Block *dest, std::optional<B> destOps,
3048 mlir::ConversionPatternRewriter &rewriter) {
3049 if (destOps)
3050 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, *destOps, dest);
3051 else
3052 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, std::nullopt, dest);
3055 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp,
3056 mlir::Block *dest,
3057 std::optional<mlir::ValueRange> destOps,
3058 mlir::ConversionPatternRewriter &rewriter) {
3059 auto *thisBlock = rewriter.getInsertionBlock();
3060 auto *newBlock = createBlock(rewriter, dest);
3061 rewriter.setInsertionPointToEnd(thisBlock);
3062 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock);
3063 rewriter.setInsertionPointToEnd(newBlock);
3066 /// Conversion of `fir.select_case`
3068 /// The `fir.select_case` operation is converted to a if-then-else ladder.
3069 /// Depending on the case condition type, one or several comparison and
3070 /// conditional branching can be generated.
3072 /// A point value case such as `case(4)`, a lower bound case such as
3073 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a
3074 /// simple comparison between the selector value and the constant value in the
3075 /// case. The block associated with the case condition is then executed if
3076 /// the comparison succeed otherwise it branch to the next block with the
3077 /// comparison for the next case conditon.
3079 /// A closed interval case condition such as `case(7:10)` is converted with a
3080 /// first comparison and conditional branching for the lower bound. If
3081 /// successful, it branch to a second block with the comparison for the
3082 /// upper bound in the same case condition.
3084 /// TODO: lowering of CHARACTER type cases is not handled yet.
3085 struct SelectCaseOpConversion : public fir::FIROpConversion<fir::SelectCaseOp> {
3086 using FIROpConversion::FIROpConversion;
3088 llvm::LogicalResult
3089 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor,
3090 mlir::ConversionPatternRewriter &rewriter) const override {
3091 unsigned conds = caseOp.getNumConditions();
3092 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue();
3093 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145)
3094 auto ty = caseOp.getSelector().getType();
3095 if (mlir::isa<fir::CharacterType>(ty)) {
3096 TODO(caseOp.getLoc(), "fir.select_case codegen with character type");
3097 return mlir::failure();
3099 mlir::Value selector = caseOp.getSelector(adaptor.getOperands());
3100 auto loc = caseOp.getLoc();
3101 for (unsigned t = 0; t != conds; ++t) {
3102 mlir::Block *dest = caseOp.getSuccessor(t);
3103 std::optional<mlir::ValueRange> destOps =
3104 caseOp.getSuccessorOperands(adaptor.getOperands(), t);
3105 std::optional<mlir::ValueRange> cmpOps =
3106 *caseOp.getCompareOperands(adaptor.getOperands(), t);
3107 mlir::Attribute attr = cases[t];
3108 assert(mlir::isa<mlir::UnitAttr>(attr) || cmpOps.has_value());
3109 if (mlir::isa<fir::PointIntervalAttr>(attr)) {
3110 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3111 loc, mlir::LLVM::ICmpPredicate::eq, selector, cmpOps->front());
3112 genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3113 continue;
3115 if (mlir::isa<fir::LowerBoundAttr>(attr)) {
3116 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3117 loc, mlir::LLVM::ICmpPredicate::sle, cmpOps->front(), selector);
3118 genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3119 continue;
3121 if (mlir::isa<fir::UpperBoundAttr>(attr)) {
3122 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>(
3123 loc, mlir::LLVM::ICmpPredicate::sle, selector, cmpOps->front());
3124 genCaseLadderStep(loc, cmp, dest, destOps, rewriter);
3125 continue;
3127 if (mlir::isa<fir::ClosedIntervalAttr>(attr)) {
3128 mlir::Value caseArg0 = *cmpOps->begin();
3129 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>(
3130 loc, mlir::LLVM::ICmpPredicate::sle, caseArg0, selector);
3131 auto *thisBlock = rewriter.getInsertionBlock();
3132 auto *newBlock1 = createBlock(rewriter, dest);
3133 auto *newBlock2 = createBlock(rewriter, dest);
3134 rewriter.setInsertionPointToEnd(thisBlock);
3135 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp0, newBlock1, newBlock2);
3136 rewriter.setInsertionPointToEnd(newBlock1);
3137 mlir::Value caseArg1 = *(cmpOps->begin() + 1);
3138 auto cmp1 = rewriter.create<mlir::LLVM::ICmpOp>(
3139 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg1);
3140 genCondBrOp(loc, cmp1, dest, destOps, rewriter, newBlock2);
3141 rewriter.setInsertionPointToEnd(newBlock2);
3142 continue;
3144 assert(mlir::isa<mlir::UnitAttr>(attr));
3145 assert((t + 1 == conds) && "unit must be last");
3146 genBrOp(caseOp, dest, destOps, rewriter);
3148 return mlir::success();
3152 template <typename OP>
3153 static void selectMatchAndRewrite(const fir::LLVMTypeConverter &lowering,
3154 OP select, typename OP::Adaptor adaptor,
3155 mlir::ConversionPatternRewriter &rewriter) {
3156 unsigned conds = select.getNumConditions();
3157 auto cases = select.getCases().getValue();
3158 mlir::Value selector = adaptor.getSelector();
3159 auto loc = select.getLoc();
3160 assert(conds > 0 && "select must have cases");
3162 llvm::SmallVector<mlir::Block *> destinations;
3163 llvm::SmallVector<mlir::ValueRange> destinationsOperands;
3164 mlir::Block *defaultDestination;
3165 mlir::ValueRange defaultOperands;
3166 llvm::SmallVector<int32_t> caseValues;
3168 for (unsigned t = 0; t != conds; ++t) {
3169 mlir::Block *dest = select.getSuccessor(t);
3170 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
3171 const mlir::Attribute &attr = cases[t];
3172 if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attr)) {
3173 destinations.push_back(dest);
3174 destinationsOperands.push_back(destOps ? *destOps : mlir::ValueRange{});
3175 caseValues.push_back(intAttr.getInt());
3176 continue;
3178 assert(mlir::dyn_cast_or_null<mlir::UnitAttr>(attr));
3179 assert((t + 1 == conds) && "unit must be last");
3180 defaultDestination = dest;
3181 defaultOperands = destOps ? *destOps : mlir::ValueRange{};
3184 // LLVM::SwitchOp takes a i32 type for the selector.
3185 if (select.getSelector().getType() != rewriter.getI32Type())
3186 selector = rewriter.create<mlir::LLVM::TruncOp>(loc, rewriter.getI32Type(),
3187 selector);
3189 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
3190 select, selector,
3191 /*defaultDestination=*/defaultDestination,
3192 /*defaultOperands=*/defaultOperands,
3193 /*caseValues=*/caseValues,
3194 /*caseDestinations=*/destinations,
3195 /*caseOperands=*/destinationsOperands,
3196 /*branchWeights=*/llvm::ArrayRef<std::int32_t>());
3199 /// conversion of fir::SelectOp to an if-then-else ladder
3200 struct SelectOpConversion : public fir::FIROpConversion<fir::SelectOp> {
3201 using FIROpConversion::FIROpConversion;
3203 llvm::LogicalResult
3204 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
3205 mlir::ConversionPatternRewriter &rewriter) const override {
3206 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
3207 return mlir::success();
3211 /// conversion of fir::SelectRankOp to an if-then-else ladder
3212 struct SelectRankOpConversion : public fir::FIROpConversion<fir::SelectRankOp> {
3213 using FIROpConversion::FIROpConversion;
3215 llvm::LogicalResult
3216 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
3217 mlir::ConversionPatternRewriter &rewriter) const override {
3218 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
3219 return mlir::success();
3223 /// Lower `fir.select_type` to LLVM IR dialect.
3224 struct SelectTypeOpConversion : public fir::FIROpConversion<fir::SelectTypeOp> {
3225 using FIROpConversion::FIROpConversion;
3227 llvm::LogicalResult
3228 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor,
3229 mlir::ConversionPatternRewriter &rewriter) const override {
3230 mlir::emitError(select.getLoc(),
3231 "fir.select_type should have already been converted");
3232 return mlir::failure();
3236 /// `fir.store` --> `llvm.store`
3237 struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> {
3238 using FIROpConversion::FIROpConversion;
3240 llvm::LogicalResult
3241 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor,
3242 mlir::ConversionPatternRewriter &rewriter) const override {
3243 mlir::Location loc = store.getLoc();
3244 mlir::Type storeTy = store.getValue().getType();
3245 mlir::Value llvmValue = adaptor.getValue();
3246 mlir::Value llvmMemref = adaptor.getMemref();
3247 mlir::LLVM::AliasAnalysisOpInterface newOp;
3248 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) {
3249 mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy);
3250 // Always use memcpy because LLVM is not as effective at optimizing
3251 // aggregate loads/stores as it is optimizing memcpy.
3252 TypePair boxTypePair{boxTy, llvmBoxTy};
3253 mlir::Value boxSize =
3254 computeBoxSize(loc, boxTypePair, llvmValue, rewriter);
3255 newOp = rewriter.create<mlir::LLVM::MemcpyOp>(
3256 loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false);
3257 } else {
3258 newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref);
3260 if (std::optional<mlir::ArrayAttr> optionalTag = store.getTbaa())
3261 newOp.setTBAATags(*optionalTag);
3262 else
3263 attachTBAATag(newOp, storeTy, storeTy, nullptr);
3264 rewriter.eraseOp(store);
3265 return mlir::success();
3269 namespace {
3271 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for
3272 /// the character buffer and one for the buffer length.
3273 struct UnboxCharOpConversion : public fir::FIROpConversion<fir::UnboxCharOp> {
3274 using FIROpConversion::FIROpConversion;
3276 llvm::LogicalResult
3277 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor,
3278 mlir::ConversionPatternRewriter &rewriter) const override {
3279 mlir::Type lenTy = convertType(unboxchar.getType(1));
3280 mlir::Value tuple = adaptor.getOperands()[0];
3282 mlir::Location loc = unboxchar.getLoc();
3283 mlir::Value ptrToBuffer =
3284 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 0);
3286 auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 1);
3287 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len);
3289 rewriter.replaceOp(unboxchar,
3290 llvm::ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast});
3291 return mlir::success();
3295 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its
3296 /// components.
3297 /// TODO: Part of supporting Fortran 2003 procedure pointers.
3298 struct UnboxProcOpConversion : public fir::FIROpConversion<fir::UnboxProcOp> {
3299 using FIROpConversion::FIROpConversion;
3301 llvm::LogicalResult
3302 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor,
3303 mlir::ConversionPatternRewriter &rewriter) const override {
3304 TODO(unboxproc.getLoc(), "fir.unboxproc codegen");
3305 return mlir::failure();
3309 /// convert to LLVM IR dialect `undef`
3310 struct UndefOpConversion : public fir::FIROpConversion<fir::UndefOp> {
3311 using FIROpConversion::FIROpConversion;
3313 llvm::LogicalResult
3314 matchAndRewrite(fir::UndefOp undef, OpAdaptor,
3315 mlir::ConversionPatternRewriter &rewriter) const override {
3316 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
3317 undef, convertType(undef.getType()));
3318 return mlir::success();
3322 struct ZeroOpConversion : public fir::FIROpConversion<fir::ZeroOp> {
3323 using FIROpConversion::FIROpConversion;
3325 llvm::LogicalResult
3326 matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
3327 mlir::ConversionPatternRewriter &rewriter) const override {
3328 mlir::Type ty = convertType(zero.getType());
3329 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(zero, ty);
3330 return mlir::success();
3334 /// `fir.unreachable` --> `llvm.unreachable`
3335 struct UnreachableOpConversion
3336 : public fir::FIROpConversion<fir::UnreachableOp> {
3337 using FIROpConversion::FIROpConversion;
3339 llvm::LogicalResult
3340 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
3341 mlir::ConversionPatternRewriter &rewriter) const override {
3342 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
3343 return mlir::success();
3347 /// `fir.is_present` -->
3348 /// ```
3349 /// %0 = llvm.mlir.constant(0 : i64)
3350 /// %1 = llvm.ptrtoint %0
3351 /// %2 = llvm.icmp "ne" %1, %0 : i64
3352 /// ```
3353 struct IsPresentOpConversion : public fir::FIROpConversion<fir::IsPresentOp> {
3354 using FIROpConversion::FIROpConversion;
3356 llvm::LogicalResult
3357 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor,
3358 mlir::ConversionPatternRewriter &rewriter) const override {
3359 mlir::Type idxTy = lowerTy().indexType();
3360 mlir::Location loc = isPresent.getLoc();
3361 auto ptr = adaptor.getOperands()[0];
3363 if (mlir::isa<fir::BoxCharType>(isPresent.getVal().getType())) {
3364 [[maybe_unused]] auto structTy =
3365 mlir::cast<mlir::LLVM::LLVMStructType>(ptr.getType());
3366 assert(!structTy.isOpaque() && !structTy.getBody().empty());
3368 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ptr, 0);
3370 mlir::LLVM::ConstantOp c0 =
3371 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0);
3372 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr);
3373 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3374 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0);
3376 return mlir::success();
3380 /// Create value signaling an absent optional argument in a call, e.g.
3381 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.zero : !llvm.ptr<i64>`
3382 struct AbsentOpConversion : public fir::FIROpConversion<fir::AbsentOp> {
3383 using FIROpConversion::FIROpConversion;
3385 llvm::LogicalResult
3386 matchAndRewrite(fir::AbsentOp absent, OpAdaptor,
3387 mlir::ConversionPatternRewriter &rewriter) const override {
3388 mlir::Type ty = convertType(absent.getType());
3389 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(absent, ty);
3390 return mlir::success();
3395 // Primitive operations on Complex types
3398 template <typename OPTY>
3399 static inline mlir::LLVM::FastmathFlagsAttr getLLVMFMFAttr(OPTY op) {
3400 return mlir::LLVM::FastmathFlagsAttr::get(
3401 op.getContext(),
3402 mlir::arith::convertArithFastMathFlagsToLLVM(op.getFastmath()));
3405 /// Generate inline code for complex addition/subtraction
3406 template <typename LLVMOP, typename OPTY>
3407 static mlir::LLVM::InsertValueOp
3408 complexSum(OPTY sumop, mlir::ValueRange opnds,
3409 mlir::ConversionPatternRewriter &rewriter,
3410 const fir::LLVMTypeConverter &lowering) {
3411 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(sumop);
3412 mlir::Value a = opnds[0];
3413 mlir::Value b = opnds[1];
3414 auto loc = sumop.getLoc();
3415 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
3416 mlir::Type ty = lowering.convertType(sumop.getType());
3417 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3418 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3419 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3420 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3421 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1, fmf);
3422 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1, fmf);
3423 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3424 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r0, rx, 0);
3425 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ry, 1);
3427 } // namespace
3429 namespace {
3430 struct AddcOpConversion : public fir::FIROpConversion<fir::AddcOp> {
3431 using FIROpConversion::FIROpConversion;
3433 llvm::LogicalResult
3434 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
3435 mlir::ConversionPatternRewriter &rewriter) const override {
3436 // given: (x + iy) + (x' + iy')
3437 // result: (x + x') + i(y + y')
3438 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
3439 rewriter, lowerTy());
3440 rewriter.replaceOp(addc, r.getResult());
3441 return mlir::success();
3445 struct SubcOpConversion : public fir::FIROpConversion<fir::SubcOp> {
3446 using FIROpConversion::FIROpConversion;
3448 llvm::LogicalResult
3449 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
3450 mlir::ConversionPatternRewriter &rewriter) const override {
3451 // given: (x + iy) - (x' + iy')
3452 // result: (x - x') + i(y - y')
3453 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
3454 rewriter, lowerTy());
3455 rewriter.replaceOp(subc, r.getResult());
3456 return mlir::success();
3460 /// Inlined complex multiply
3461 struct MulcOpConversion : public fir::FIROpConversion<fir::MulcOp> {
3462 using FIROpConversion::FIROpConversion;
3464 llvm::LogicalResult
3465 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
3466 mlir::ConversionPatternRewriter &rewriter) const override {
3467 // TODO: Can we use a call to __muldc3 ?
3468 // given: (x + iy) * (x' + iy')
3469 // result: (xx'-yy')+i(xy'+yx')
3470 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(mulc);
3471 mlir::Value a = adaptor.getOperands()[0];
3472 mlir::Value b = adaptor.getOperands()[1];
3473 auto loc = mulc.getLoc();
3474 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
3475 mlir::Type ty = convertType(mulc.getType());
3476 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3477 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3478 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3479 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3480 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf);
3481 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf);
3482 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf);
3483 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx, fmf);
3484 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf);
3485 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy, fmf);
3486 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3487 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0);
3488 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1);
3489 rewriter.replaceOp(mulc, r0.getResult());
3490 return mlir::success();
3494 /// Inlined complex division
3495 struct DivcOpConversion : public fir::FIROpConversion<fir::DivcOp> {
3496 using FIROpConversion::FIROpConversion;
3498 llvm::LogicalResult
3499 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
3500 mlir::ConversionPatternRewriter &rewriter) const override {
3501 // TODO: Can we use a call to __divdc3 instead?
3502 // Just generate inline code for now.
3503 // given: (x + iy) / (x' + iy')
3504 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
3505 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(divc);
3506 mlir::Value a = adaptor.getOperands()[0];
3507 mlir::Value b = adaptor.getOperands()[1];
3508 auto loc = divc.getLoc();
3509 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
3510 mlir::Type ty = convertType(divc.getType());
3511 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0);
3512 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1);
3513 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0);
3514 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1);
3515 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf);
3516 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1, fmf);
3517 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf);
3518 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf);
3519 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf);
3520 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1, fmf);
3521 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1, fmf);
3522 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy, fmf);
3523 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy, fmf);
3524 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d, fmf);
3525 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d, fmf);
3526 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
3527 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0);
3528 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1);
3529 rewriter.replaceOp(divc, r0.getResult());
3530 return mlir::success();
3534 /// Inlined complex negation
3535 struct NegcOpConversion : public fir::FIROpConversion<fir::NegcOp> {
3536 using FIROpConversion::FIROpConversion;
3538 llvm::LogicalResult
3539 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
3540 mlir::ConversionPatternRewriter &rewriter) const override {
3541 // given: -(x + iy)
3542 // result: -x - iy
3543 auto eleTy = convertType(getComplexEleTy(neg.getType()));
3544 auto loc = neg.getLoc();
3545 mlir::Value o0 = adaptor.getOperands()[0];
3546 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 0);
3547 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 1);
3548 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
3549 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
3550 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, o0, nrp, 0);
3551 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, r, nip, 1);
3552 return mlir::success();
3556 struct BoxOffsetOpConversion : public fir::FIROpConversion<fir::BoxOffsetOp> {
3557 using FIROpConversion::FIROpConversion;
3559 llvm::LogicalResult
3560 matchAndRewrite(fir::BoxOffsetOp boxOffset, OpAdaptor adaptor,
3561 mlir::ConversionPatternRewriter &rewriter) const override {
3563 mlir::Type pty = ::getLlvmPtrType(boxOffset.getContext());
3564 mlir::Type boxType = fir::unwrapRefType(boxOffset.getBoxRef().getType());
3565 mlir::Type llvmBoxTy =
3566 lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(boxType));
3567 int fieldId = boxOffset.getField() == fir::BoxFieldAttr::derived_type
3568 ? getTypeDescFieldId(boxType)
3569 : kAddrPosInBox;
3570 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
3571 boxOffset, pty, llvmBoxTy, adaptor.getBoxRef(),
3572 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, fieldId});
3573 return mlir::success();
3577 /// Conversion pattern for operation that must be dead. The information in these
3578 /// operations is used by other operation. At this point they should not have
3579 /// anymore uses.
3580 /// These operations are normally dead after the pre-codegen pass.
3581 template <typename FromOp>
3582 struct MustBeDeadConversion : public fir::FIROpConversion<FromOp> {
3583 explicit MustBeDeadConversion(const fir::LLVMTypeConverter &lowering,
3584 const fir::FIRToLLVMPassOptions &options)
3585 : fir::FIROpConversion<FromOp>(lowering, options) {}
3586 using OpAdaptor = typename FromOp::Adaptor;
3588 llvm::LogicalResult
3589 matchAndRewrite(FromOp op, OpAdaptor adaptor,
3590 mlir::ConversionPatternRewriter &rewriter) const final {
3591 if (!op->getUses().empty())
3592 return rewriter.notifyMatchFailure(op, "op must be dead");
3593 rewriter.eraseOp(op);
3594 return mlir::success();
3598 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> {
3599 using MustBeDeadConversion::MustBeDeadConversion;
3602 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> {
3603 using MustBeDeadConversion::MustBeDeadConversion;
3606 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> {
3607 using MustBeDeadConversion::MustBeDeadConversion;
3610 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> {
3611 using MustBeDeadConversion::MustBeDeadConversion;
3614 } // namespace
3616 namespace {
3617 class RenameMSVCLibmCallees
3618 : public mlir::OpRewritePattern<mlir::LLVM::CallOp> {
3619 public:
3620 using OpRewritePattern::OpRewritePattern;
3622 llvm::LogicalResult
3623 matchAndRewrite(mlir::LLVM::CallOp op,
3624 mlir::PatternRewriter &rewriter) const override {
3625 rewriter.startOpModification(op);
3626 auto callee = op.getCallee();
3627 if (callee)
3628 if (*callee == "hypotf")
3629 op.setCalleeAttr(mlir::SymbolRefAttr::get(op.getContext(), "_hypotf"));
3631 rewriter.finalizeOpModification(op);
3632 return mlir::success();
3636 class RenameMSVCLibmFuncs
3637 : public mlir::OpRewritePattern<mlir::LLVM::LLVMFuncOp> {
3638 public:
3639 using OpRewritePattern::OpRewritePattern;
3641 llvm::LogicalResult
3642 matchAndRewrite(mlir::LLVM::LLVMFuncOp op,
3643 mlir::PatternRewriter &rewriter) const override {
3644 rewriter.startOpModification(op);
3645 if (op.getSymName() == "hypotf")
3646 op.setSymNameAttr(rewriter.getStringAttr("_hypotf"));
3647 rewriter.finalizeOpModification(op);
3648 return mlir::success();
3651 } // namespace
3653 namespace {
3654 /// Convert FIR dialect to LLVM dialect
3656 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
3657 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
3658 class FIRToLLVMLowering
3659 : public fir::impl::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
3660 public:
3661 FIRToLLVMLowering() = default;
3662 FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {}
3663 mlir::ModuleOp getModule() { return getOperation(); }
3665 void runOnOperation() override final {
3666 auto mod = getModule();
3667 if (!forcedTargetTriple.empty())
3668 fir::setTargetTriple(mod, forcedTargetTriple);
3670 if (!forcedDataLayout.empty()) {
3671 llvm::DataLayout dl(forcedDataLayout);
3672 fir::support::setMLIRDataLayout(mod, dl);
3675 if (!forcedTargetCPU.empty())
3676 fir::setTargetCPU(mod, forcedTargetCPU);
3678 if (!forcedTuneCPU.empty())
3679 fir::setTuneCPU(mod, forcedTuneCPU);
3681 if (!forcedTargetFeatures.empty())
3682 fir::setTargetFeatures(mod, forcedTargetFeatures);
3684 if (typeDescriptorsRenamedForAssembly)
3685 options.typeDescriptorsRenamedForAssembly =
3686 typeDescriptorsRenamedForAssembly;
3688 // Run dynamic pass pipeline for converting Math dialect
3689 // operations into other dialects (llvm, func, etc.).
3690 // Some conversions of Math operations cannot be done
3691 // by just using conversion patterns. This is true for
3692 // conversions that affect the ModuleOp, e.g. create new
3693 // function operations in it. We have to run such conversions
3694 // as passes here.
3695 mlir::OpPassManager mathConvertionPM("builtin.module");
3697 bool isAMDGCN = fir::getTargetTriple(mod).isAMDGCN();
3698 // If compiling for AMD target some math operations must be lowered to AMD
3699 // GPU library calls, the rest can be converted to LLVM intrinsics, which
3700 // is handled in the mathToLLVM conversion. The lowering to libm calls is
3701 // not needed since all math operations are handled this way.
3702 if (isAMDGCN)
3703 mathConvertionPM.addPass(mlir::createConvertMathToROCDL());
3705 // Convert math::FPowI operations to inline implementation
3706 // only if the exponent's width is greater than 32, otherwise,
3707 // it will be lowered to LLVM intrinsic operation by a later conversion.
3708 mlir::ConvertMathToFuncsOptions mathToFuncsOptions{};
3709 mathToFuncsOptions.minWidthOfFPowIExponent = 33;
3710 mathConvertionPM.addPass(
3711 mlir::createConvertMathToFuncs(mathToFuncsOptions));
3712 mathConvertionPM.addPass(mlir::createConvertComplexToStandardPass());
3713 // Convert Math dialect operations into LLVM dialect operations.
3714 // There is no way to prefer MathToLLVM patterns over MathToLibm
3715 // patterns (applied below), so we have to run MathToLLVM conversion here.
3716 mathConvertionPM.addNestedPass<mlir::func::FuncOp>(
3717 mlir::createConvertMathToLLVMPass());
3718 if (mlir::failed(runPipeline(mathConvertionPM, mod)))
3719 return signalPassFailure();
3721 std::optional<mlir::DataLayout> dl =
3722 fir::support::getOrSetDataLayout(mod, /*allowDefaultLayout=*/true);
3723 if (!dl) {
3724 mlir::emitError(mod.getLoc(),
3725 "module operation must carry a data layout attribute "
3726 "to generate llvm IR from FIR");
3727 signalPassFailure();
3728 return;
3731 auto *context = getModule().getContext();
3732 fir::LLVMTypeConverter typeConverter{getModule(),
3733 options.applyTBAA || applyTBAA,
3734 options.forceUnifiedTBAATree, *dl};
3735 mlir::RewritePatternSet pattern(context);
3736 fir::populateFIRToLLVMConversionPatterns(typeConverter, pattern, options);
3737 mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern);
3738 mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern);
3739 mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, pattern);
3740 mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter,
3741 pattern);
3742 // Math operations that have not been converted yet must be converted
3743 // to Libm.
3744 if (!isAMDGCN)
3745 mlir::populateMathToLibmConversionPatterns(pattern);
3746 mlir::populateComplexToLLVMConversionPatterns(typeConverter, pattern);
3747 mlir::populateVectorToLLVMConversionPatterns(typeConverter, pattern);
3749 // Flang specific overloads for OpenMP operations, to allow for special
3750 // handling of things like Box types.
3751 fir::populateOpenMPFIRToLLVMConversionPatterns(typeConverter, pattern);
3753 mlir::ConversionTarget target{*context};
3754 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3755 // The OpenMP dialect is legal for Operations without regions, for those
3756 // which contains regions it is legal if the region contains only the
3757 // LLVM dialect. Add OpenMP dialect as a legal dialect for conversion and
3758 // legalize conversion of OpenMP operations without regions.
3759 mlir::configureOpenMPToLLVMConversionLegality(target, typeConverter);
3760 target.addLegalDialect<mlir::omp::OpenMPDialect>();
3761 target.addLegalDialect<mlir::acc::OpenACCDialect>();
3762 target.addLegalDialect<mlir::gpu::GPUDialect>();
3764 // required NOPs for applying a full conversion
3765 target.addLegalOp<mlir::ModuleOp>();
3767 // If we're on Windows, we might need to rename some libm calls.
3768 bool isMSVC = fir::getTargetTriple(mod).isOSMSVCRT();
3769 if (isMSVC) {
3770 pattern.insert<RenameMSVCLibmCallees, RenameMSVCLibmFuncs>(context);
3772 target.addDynamicallyLegalOp<mlir::LLVM::CallOp>(
3773 [](mlir::LLVM::CallOp op) {
3774 auto callee = op.getCallee();
3775 if (!callee)
3776 return true;
3777 return *callee != "hypotf";
3779 target.addDynamicallyLegalOp<mlir::LLVM::LLVMFuncOp>(
3780 [](mlir::LLVM::LLVMFuncOp op) {
3781 return op.getSymName() != "hypotf";
3785 // apply the patterns
3786 if (mlir::failed(mlir::applyFullConversion(getModule(), target,
3787 std::move(pattern)))) {
3788 signalPassFailure();
3791 // Run pass to add comdats to functions that have weak linkage on relevant
3792 // platforms
3793 if (fir::getTargetTriple(mod).supportsCOMDAT()) {
3794 mlir::OpPassManager comdatPM("builtin.module");
3795 comdatPM.addPass(mlir::LLVM::createLLVMAddComdats());
3796 if (mlir::failed(runPipeline(comdatPM, mod)))
3797 return signalPassFailure();
3801 private:
3802 fir::FIRToLLVMPassOptions options;
3805 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module
3806 struct LLVMIRLoweringPass
3807 : public mlir::PassWrapper<LLVMIRLoweringPass,
3808 mlir::OperationPass<mlir::ModuleOp>> {
3809 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LLVMIRLoweringPass)
3811 LLVMIRLoweringPass(llvm::raw_ostream &output, fir::LLVMIRLoweringPrinter p)
3812 : output{output}, printer{p} {}
3814 mlir::ModuleOp getModule() { return getOperation(); }
3816 void runOnOperation() override final {
3817 auto *ctx = getModule().getContext();
3818 auto optName = getModule().getName();
3819 llvm::LLVMContext llvmCtx;
3820 if (auto llvmModule = mlir::translateModuleToLLVMIR(
3821 getModule(), llvmCtx, optName ? *optName : "FIRModule")) {
3822 printer(*llvmModule, output);
3823 return;
3826 mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n");
3827 signalPassFailure();
3830 private:
3831 llvm::raw_ostream &output;
3832 fir::LLVMIRLoweringPrinter printer;
3835 } // namespace
3837 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
3838 return std::make_unique<FIRToLLVMLowering>();
3841 std::unique_ptr<mlir::Pass>
3842 fir::createFIRToLLVMPass(fir::FIRToLLVMPassOptions options) {
3843 return std::make_unique<FIRToLLVMLowering>(options);
3846 std::unique_ptr<mlir::Pass>
3847 fir::createLLVMDialectToLLVMPass(llvm::raw_ostream &output,
3848 fir::LLVMIRLoweringPrinter printer) {
3849 return std::make_unique<LLVMIRLoweringPass>(output, printer);
3852 void fir::populateFIRToLLVMConversionPatterns(
3853 const fir::LLVMTypeConverter &converter, mlir::RewritePatternSet &patterns,
3854 fir::FIRToLLVMPassOptions &options) {
3855 patterns.insert<
3856 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion,
3857 AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion,
3858 BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion,
3859 BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion,
3860 BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion,
3861 BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion,
3862 CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion,
3863 DTEntryOpConversion, DeclareOpConversion, DivcOpConversion,
3864 EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion,
3865 ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion,
3866 FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion,
3867 InsertOnRangeOpConversion, IsPresentOpConversion,
3868 LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion,
3869 NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion,
3870 SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion,
3871 ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion,
3872 SliceOpConversion, StoreOpConversion, StringLitOpConversion,
3873 SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion,
3874 UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion,
3875 UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion,
3876 XReboxOpConversion, ZeroOpConversion>(converter, options);
3878 // Patterns that are populated without a type converter do not trigger
3879 // target materializations for the operands of the root op.
3880 patterns.insert<HasValueOpConversion, InsertValueOpConversion>(
3881 patterns.getContext());