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

source code of flang/lib/Optimizer/CodeGen/CodeGen.cpp