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