diff --git a/flang/include/flang/Optimizer/Dialect/FIRTypes.td b/flang/include/flang/Optimizer/Dialect/FIRTypes.td index b57f4182b9294..556c65c30a26f 100644 --- a/flang/include/flang/Optimizer/Dialect/FIRTypes.td +++ b/flang/include/flang/Optimizer/Dialect/FIRTypes.td @@ -317,6 +317,8 @@ def fir_RecordType : FIR_Type<"Record", "type"> { void finalize(llvm::ArrayRef lenPList, llvm::ArrayRef typeList); + + std::string getLoweredName() const; detail::RecordTypeStorage const *uniqueKey() const; }]; diff --git a/flang/include/flang/Optimizer/Support/TypeCode.h b/flang/include/flang/Optimizer/Support/TypeCode.h new file mode 100644 index 0000000000000..ae9e96b8fc41e --- /dev/null +++ b/flang/include/flang/Optimizer/Support/TypeCode.h @@ -0,0 +1,90 @@ +//===-- Optimizer/Support/TypeCode.h ----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_OPTIMIZER_SUPPORT_TYPECODE_H +#define FORTRAN_OPTIMIZER_SUPPORT_TYPECODE_H + +#include "flang/ISO_Fortran_binding.h" +#include "llvm/Support/ErrorHandling.h" + +namespace fir { + +//===----------------------------------------------------------------------===// +// Translations of category and bitwidths to the type codes defined in flang's +// ISO_Fortran_binding.h. +//===----------------------------------------------------------------------===// + +inline int characterBitsToTypeCode(unsigned bitwidth) { + // clang-format off + switch (bitwidth) { + case 8: return CFI_type_char; + case 16: return CFI_type_char16_t; + case 32: return CFI_type_char32_t; + default: llvm_unreachable("unsupported character size"); + } + // clang-format on +} + +inline int complexBitsToTypeCode(unsigned bitwidth) { + // clang-format off + switch (bitwidth) { + case 32: return CFI_type_float_Complex; + case 64: return CFI_type_double_Complex; + case 80: + case 128: return CFI_type_long_double_Complex; + default: llvm_unreachable("unsupported complex size"); + } + // clang-format on +} + +inline int integerBitsToTypeCode(unsigned bitwidth) { + // clang-format off + switch (bitwidth) { + case 8: return CFI_type_int8_t; + case 16: return CFI_type_int16_t; + case 32: return CFI_type_int32_t; + case 64: return CFI_type_int64_t; + case 128: return CFI_type_int128_t; + default: llvm_unreachable("unsupported integer size"); + } + // clang-format on +} + +inline int logicalBitsToTypeCode(unsigned bitwidth) { + // clang-format off + switch (bitwidth) { + case 8: return CFI_type_Bool; + case 16: return CFI_type_int_least16_t; + case 32: return CFI_type_int_least32_t; + case 64: return CFI_type_int_least64_t; + default: llvm_unreachable("unsupported logical size"); + } + // clang-format on +} + +inline int realBitsToTypeCode(unsigned bitwidth) { + // clang-format off + switch (bitwidth) { + case 32: return CFI_type_float; + case 64: return CFI_type_double; + case 80: + case 128: return CFI_type_long_double; + default: llvm_unreachable("unsupported real size"); + } + // clang-format on +} + +static constexpr int derivedToTypeCode() { return CFI_type_struct; } + +} // namespace fir + +#endif // FORTRAN_OPTIMIZER_SUPPORT_TYPECODE_H diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 92139d33da56f..cd7dbb66c4533 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -15,6 +15,7 @@ #include "flang/ISO_Fortran_binding.h" #include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Support/TypeCode.h" #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" #include "mlir/Conversion/LLVMCommon/Pattern.h" #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" @@ -28,6 +29,9 @@ // fir::LLVMTypeConverter for converting to LLVM IR dialect types. #include "TypeConverter.h" +// TODO: This should really be recovered from the specified target. +static constexpr unsigned defaultAlign = 8; + /// `fir.box` attribute values as defined for CFI_attribute_t in /// flang/ISO_Fortran_binding.h. static constexpr unsigned kAttrPointer = CFI_attribute_pointer; @@ -61,12 +65,20 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern { return lowerTy().convertType(ty); } + mlir::LLVM::ConstantOp + genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, + int value) const { + mlir::Type i32Ty = rewriter.getI32Type(); + mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); + return rewriter.create(loc, i32Ty, attr); + } + mlir::LLVM::ConstantOp genConstantOffset(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, int offset) const { - auto ity = lowerTy().offsetType(); - auto cattr = rewriter.getI32IntegerAttr(offset); + mlir::Type ity = lowerTy().offsetType(); + mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); return rewriter.create(loc, ity, cattr); } @@ -153,6 +165,28 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern { loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); } + // Get the element type given an LLVM type that is of the form + // [llvm.ptr](array|struct|vector)+ and the provided indexes. + static mlir::Type getBoxEleTy(mlir::Type type, + llvm::ArrayRef indexes) { + if (auto t = type.dyn_cast()) + type = t.getElementType(); + for (auto i : indexes) { + if (auto t = type.dyn_cast()) { + assert(!t.isOpaque() && i < t.getBody().size()); + type = t.getBody()[i]; + } else if (auto t = type.dyn_cast()) { + type = t.getElementType(); + } else if (auto t = type.dyn_cast()) { + type = t.getElementType(); + } else { + fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), + "request for invalid box element type"); + } + } + return type; + } + template mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter, @@ -1183,6 +1217,318 @@ struct ZeroOpConversion : public FIROpConversion { } }; +/// Common base class for embox to descriptor conversion. +template +struct EmboxCommonConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + // Find the LLVMFuncOp in whose entry block the alloca should be inserted. + // The order to find the LLVMFuncOp is as follows: + // 1. The parent operation of the current block if it is a LLVMFuncOp. + // 2. The first ancestor that is a LLVMFuncOp. + mlir::LLVM::LLVMFuncOp + getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { + mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); + return mlir::isa(parentOp) + ? mlir::cast(parentOp) + : parentOp->getParentOfType(); + } + + // Generate an alloca of size 1 and type \p toTy. + mlir::LLVM::AllocaOp + genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, + mlir::ConversionPatternRewriter &rewriter) const { + auto thisPt = rewriter.saveInsertionPoint(); + mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); + rewriter.setInsertionPointToStart(&func.front()); + auto size = this->genI32Constant(loc, rewriter, 1); + auto al = rewriter.create(loc, toTy, size, alignment); + rewriter.restoreInsertionPoint(thisPt); + return al; + } + + static int getCFIAttr(fir::BoxType boxTy) { + auto eleTy = boxTy.getEleTy(); + if (eleTy.isa()) + return CFI_attribute_pointer; + if (eleTy.isa()) + return CFI_attribute_allocatable; + return CFI_attribute_other; + } + + static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { + return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) + .template dyn_cast(); + } + static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { + auto recTy = unwrapIfDerived(boxTy); + return recTy && recTy.getNumLenParams() > 0; + } + static bool isDerivedType(fir::BoxType boxTy) { + return unwrapIfDerived(boxTy) != nullptr; + } + + // Get the element size and CFI type code of the boxed value. + std::tuple getSizeAndTypeCode( + mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, + mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { + auto doInteger = + [&](unsigned width) -> std::tuple { + int typeCode = fir::integerBitsToTypeCode(width); + return {this->genConstantOffset(loc, rewriter, width / 8), + this->genConstantOffset(loc, rewriter, typeCode)}; + }; + auto doLogical = + [&](unsigned width) -> std::tuple { + int typeCode = fir::logicalBitsToTypeCode(width); + return {this->genConstantOffset(loc, rewriter, width / 8), + this->genConstantOffset(loc, rewriter, typeCode)}; + }; + auto doFloat = [&](unsigned width) -> std::tuple { + int typeCode = fir::realBitsToTypeCode(width); + return {this->genConstantOffset(loc, rewriter, width / 8), + this->genConstantOffset(loc, rewriter, typeCode)}; + }; + auto doComplex = + [&](unsigned width) -> std::tuple { + auto typeCode = fir::complexBitsToTypeCode(width); + return {this->genConstantOffset(loc, rewriter, width / 8 * 2), + this->genConstantOffset(loc, rewriter, typeCode)}; + }; + auto doCharacter = + [&](unsigned width, + mlir::Value len) -> std::tuple { + auto typeCode = fir::characterBitsToTypeCode(width); + auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); + if (width == 8) + return {len, typeCodeVal}; + auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); + auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); + auto size = + rewriter.create(loc, i64Ty, byteWidth, len); + return {size, typeCodeVal}; + }; + auto getKindMap = [&]() -> fir::KindMapping & { + return this->lowerTy().getKindMap(); + }; + // Pointer-like types. + if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) + boxEleTy = eleTy; + // Integer types. + if (fir::isa_integer(boxEleTy)) { + if (auto ty = boxEleTy.dyn_cast()) + return doInteger(ty.getWidth()); + auto ty = boxEleTy.cast(); + return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); + } + // Floating point types. + if (fir::isa_real(boxEleTy)) { + if (auto ty = boxEleTy.dyn_cast()) + return doFloat(ty.getWidth()); + auto ty = boxEleTy.cast(); + return doFloat(getKindMap().getRealBitsize(ty.getFKind())); + } + // Complex types. + if (fir::isa_complex(boxEleTy)) { + if (auto ty = boxEleTy.dyn_cast()) + return doComplex( + ty.getElementType().cast().getWidth()); + auto ty = boxEleTy.cast(); + return doComplex(getKindMap().getRealBitsize(ty.getFKind())); + } + // Character types. + if (auto ty = boxEleTy.dyn_cast()) { + auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); + if (ty.getLen() != fir::CharacterType::unknownLen()) { + auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); + return doCharacter(charWidth, len); + } + assert(!lenParams.empty()); + return doCharacter(charWidth, lenParams.back()); + } + // Logical type. + if (auto ty = boxEleTy.dyn_cast()) + return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); + // Array types. + if (auto seqTy = boxEleTy.dyn_cast()) + return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); + // Derived-type types. + if (boxEleTy.isa()) { + auto ptrTy = mlir::LLVM::LLVMPointerType::get( + this->lowerTy().convertType(boxEleTy)); + auto nullPtr = rewriter.create(loc, ptrTy); + auto one = + genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); + auto gep = rewriter.create( + loc, ptrTy, mlir::ValueRange{nullPtr, one}); + auto eleSize = rewriter.create( + loc, this->lowerTy().indexType(), gep); + return {eleSize, + this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; + } + // Reference type. + if (fir::isa_ref_type(boxEleTy)) { + // FIXME: use the target pointer size rather than sizeof(void*) + return {this->genConstantOffset(loc, rewriter, sizeof(void *)), + this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; + } + fir::emitFatalError(loc, "unhandled type in fir.box code generation"); + } + + /// Basic pattern to write a field in the descriptor + mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, + mlir::Location loc, mlir::Value dest, + ArrayRef fldIndexes, mlir::Value value, + bool bitcast = false) const { + auto boxTy = dest.getType(); + auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); + if (bitcast) + value = rewriter.create(loc, fldTy, value); + else + value = this->integerCast(loc, rewriter, fldTy, value); + SmallVector attrs; + for (auto i : fldIndexes) + attrs.push_back(rewriter.getI32IntegerAttr(i)); + auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); + return rewriter.create(loc, boxTy, dest, value, + indexesAttr); + } + + inline mlir::Value + insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, + mlir::Location loc, mlir::Value dest, + mlir::Value base) const { + return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true); + } + + /// Get the address of the type descriptor global variable that was created by + /// lowering for derived type \p recType. + template + mlir::Value + getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, + mlir::Location loc, fir::RecordType recType) const { + std::string name = recType.getLoweredName(); + auto module = box->template getParentOfType(); + if (auto global = module.template lookupSymbol(name)) { + auto ty = mlir::LLVM::LLVMPointerType::get( + this->lowerTy().convertType(global.getType())); + return rewriter.create(loc, ty, + global.sym_name()); + } + if (auto global = + module.template lookupSymbol(name)) { + // The global may have already been translated to LLVM. + auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); + return rewriter.create(loc, ty, + global.sym_name()); + } + // The global does not exist in the current translation unit, but may be + // defined elsewhere (e.g., type defined in a module). + // For now, create a extern_weak symbol (will become nullptr if unresolved) + // to support generating code without the front-end generated symbols. + // These could be made available_externally to require the symbols to be + // defined elsewhere and to cause link-time failure otherwise. + auto i8Ty = rewriter.getIntegerType(8); + mlir::OpBuilder modBuilder(module.getBodyRegion()); + // TODO: The symbol should be lowered to constant in lowering, they are read + // only. + modBuilder.create(loc, i8Ty, /*isConstant=*/false, + mlir::LLVM::Linkage::ExternWeak, + name, mlir::Attribute{}); + auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); + return rewriter.create(loc, ty, name); + } + + template + std::tuple + consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, + unsigned rank, mlir::ValueRange lenParams) const { + auto loc = box.getLoc(); + auto boxTy = box.getType().template dyn_cast(); + auto convTy = this->lowerTy().convertBoxType(boxTy, rank); + auto llvmBoxPtrTy = convTy.template cast(); + auto llvmBoxTy = llvmBoxPtrTy.getElementType(); + mlir::Value descriptor = + rewriter.create(loc, llvmBoxTy); + + llvm::SmallVector typeparams = lenParams; + if constexpr (!std::is_same_v) { + if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) + typeparams.push_back(box.substr()[1]); + } + + // Write each of the fields with the appropriate values + auto [eleSize, cfiTy] = + getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); + descriptor = + insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); + descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, + this->genI32Constant(loc, rewriter, CFI_VERSION)); + descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, + this->genI32Constant(loc, rewriter, rank)); + descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); + descriptor = + insertField(rewriter, loc, descriptor, {kAttributePosInBox}, + this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); + const bool hasAddendum = isDerivedType(boxTy); + descriptor = + insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, + this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); + + if (hasAddendum) { + auto isArray = + fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa(); + unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; + auto typeDesc = + getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); + descriptor = + insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, + /*bitCast=*/true); + } + + return {boxTy, descriptor, eleSize}; + } + + /// If the embox is not in a globalOp body, allocate storage for the box; + /// store the value inside and return the generated alloca. Return the input + /// value otherwise. + mlir::Value + placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, + mlir::Location loc, mlir::Value boxValue) const { + auto *thisBlock = rewriter.getInsertionBlock(); + if (thisBlock && mlir::isa(thisBlock->getParentOp())) + return boxValue; + auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); + auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); + rewriter.create(loc, boxValue, alloca); + return alloca; + } +}; + +/// Create a generic box on a memory reference. This conversions lowers the +/// abstract box to the appropriate, initialized descriptor. +struct EmboxOpConversion : public EmboxCommonConversion { + using EmboxCommonConversion::EmboxCommonConversion; + + mlir::LogicalResult + matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + assert(!embox.getShape() && "There should be no dims on this embox op"); + auto [boxTy, dest, eleSize] = + consDescriptorPrefix(embox, rewriter, /*rank=*/0, + /*lenParams=*/adaptor.getOperands().drop_front(1)); + dest = insertBaseAddress(rewriter, embox.getLoc(), dest, + adaptor.getOperands()[0]); + if (isDerivedTypeWithLenParams(boxTy)) + return rewriter.notifyMatchFailure( + embox, "fir.embox codegen of derived with length parameters not " + "implemented yet"); + auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); + rewriter.replaceOp(embox, result); + return success(); + } +}; + // Code shared between insert_value and extract_value Ops. struct ValueOpCommon { // Translate the arguments pertaining to any multidimensional array to @@ -1719,9 +2065,9 @@ class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase { BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, - EmboxCharOpConversion, ExtractValueOpConversion, HasValueOpConversion, - GenTypeDescOpConversion, GlobalLenOpConversion, GlobalOpConversion, - InsertOnRangeOpConversion, InsertValueOpConversion, + EmboxOpConversion, EmboxCharOpConversion, ExtractValueOpConversion, + HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion, + GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, NegcOpConversion, MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion, diff --git a/flang/lib/Optimizer/Dialect/FIRType.cpp b/flang/lib/Optimizer/Dialect/FIRType.cpp index 88890b2086984..0d087bccf57d3 100644 --- a/flang/lib/Optimizer/Dialect/FIRType.cpp +++ b/flang/lib/Optimizer/Dialect/FIRType.cpp @@ -642,6 +642,12 @@ unsigned fir::RecordType::getFieldIndex(llvm::StringRef ident) { return std::numeric_limits::max(); } +std::string fir::RecordType::getLoweredName() const { + auto split = getName().split('T'); + std::string name = (split.first + "E.dt." + split.second).str(); + return name; +} + //===----------------------------------------------------------------------===// // ReferenceType //===----------------------------------------------------------------------===// diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir index 8d0b8235b18fc..19e7248c39dff 100644 --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -1368,3 +1368,165 @@ func @box_tdesc(%arg0: !fir.box) { // CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[TYPE_POS]]] : (!llvm.ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32) -> !llvm.ptr // CHECK: %[[LOAD:.*]] = llvm.load %[[GEP]] : !llvm.ptr // CHECK: %{{.*}} = llvm.inttoptr %[[LOAD]] : i{{.*}} to !llvm.ptr + +// ----- + +// Test `fir.embox` conversion. + +// Check basic creation of a descriptor and insertion of values. +// The indices used to insert values into the descriptor correspond the +// position of the fields in the descriptor as defined in `CFI_cdesc_t` in +// flang/ISO_Fortran_binding.h. + +func @embox0(%arg0: !fir.ref>) { + %0 = fir.embox %arg0() : (!fir.ref>) -> !fir.box> + return +} + +// CHECK-LABEL: func @embox0( +// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr> +// CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[ALLOCA:.*]] = llvm.alloca %[[C1]] x !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> {alignment = 8 : i64} : (i32) -> !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>> +// CHECK: %[[DESC:.*]] = llvm.mlir.undef : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[ELEM_SIZE:.*]] = llvm.mlir.constant(4 : i32) : i32 +// CHECK: %[[TYPE_CODE:.*]] = llvm.mlir.constant(9 : i32) : i32 +// CHECK: %[[I64_ELEM_SIZE:.*]] = llvm.sext %[[ELEM_SIZE]] : i32 to i64 +// CHECK: %[[DESC0:.*]] = llvm.insertvalue %[[I64_ELEM_SIZE]], %[[DESC]][1 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[CFI_VERSION:.*]] = llvm.mlir.constant(20180515 : i32) : i32 +// CHECK: %[[DESC1:.*]] = llvm.insertvalue %[[CFI_VERSION]], %[[DESC0]][2 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[RANK:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[RANK_I8:.*]] = llvm.trunc %[[RANK]] : i32 to i8 +// CHECK: %[[DESC2:.*]] = llvm.insertvalue %[[RANK_I8]], %[[DESC1]][3 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[TYPE_CODE_I8:.*]] = llvm.trunc %[[TYPE_CODE]] : i32 to i8 +// CHECK: %[[DESC3:.*]] = llvm.insertvalue %[[TYPE_CODE_I8]], %[[DESC2]][4 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[ATTR:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[ATTR_I8:.*]] = llvm.trunc %[[ATTR]] : i32 to i8 +// CHECK: %[[DESC4:.*]] = llvm.insertvalue %[[ATTR_I8]], %[[DESC3]][5 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[F18ADDENDUM:.*]] = llvm.mlir.constant(0 : i32) : i32 +// CHECK: %[[F18ADDENDUM_I8:.*]] = llvm.trunc %[[F18ADDENDUM]] : i32 to i8 +// CHECK: %[[DESC5:.*]] = llvm.insertvalue %[[F18ADDENDUM_I8]], %[[DESC4]][6 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: %[[ADDR:.*]] = llvm.bitcast %[[ARG0]] : !llvm.ptr> to !llvm.ptr> +// CHECK: %[[DESC6:.*]] = llvm.insertvalue %[[ADDR]], %[[DESC5]][0 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> +// CHECK: llvm.store %[[DESC6]], %[[ALLOCA]] : !llvm.ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>> + +// Check `fir.embox` in a `fir.global`. Descriptors created by `fir.embox` +// conversion are not generating `alloca` instructions. This test make sure of +// that. + +fir.global @box_global : !fir.ref> { + %arr = fir.zero_bits !fir.ref> + %0 = arith.constant 0 : index + %3 = fir.embox %arr: (!fir.ref>) -> !fir.box> + fir.has_value %arr : !fir.ref> +} + +// CHECK-LABEL: llvm.mlir.global external @box_global +// CHECK-NOT: llvm.alloca + +// Check `fir.embox` conversion of a POINTER entity. Make sure that the +// attribute in the descriptor is set to 1 (value of CFI_attribute_pointer +// in flang/ISO_Fortran_binding.h). + +func @embox_pointer(%arg0: !fir.ref) { + %0 = fir.embox %arg0 : (!fir.ref) -> !fir.box> + return +} + +// CHECK-LABEL: llvm.func @embox_pointer +// Check 1st 1 constant to skip it. +// CHECK: %{{.*}} = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[CFI_ATTR_POINTER:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[ATTR_I8:.*]] = llvm.trunc %[[CFI_ATTR_POINTER]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[ATTR_I8]], %{{.*}}[5 : i32] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +// Check `fir.embox` conversion of an ALLOCATABLE entity. Make sure that the +// attribute in the descriptor is set to 2 (value of CFI_attribute_allocatable +// in flang/ISO_Fortran_binding.h). + +func @embox_allocatable(%arg0: !fir.heap>>) { + %0 = fir.embox %arg0 : (!fir.heap>>) -> !fir.box>>> + return +} + +// CHECK-LABEL: llvm.func @embox_allocatable +// CHECK: %[[CFI_ATTR_ALLOCATABLE:.*]] = llvm.mlir.constant(2 : i32) : i32 +// CHECK: %[[ATTR_I8:.*]] = llvm.trunc %[[CFI_ATTR_ALLOCATABLE]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[ATTR_I8]], %{{.*}}[5 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +// Check `fir.embox` conversion of a type code. + +func @embox_typecode0(%arg0: !fir.ref) { + %0 = fir.embox %arg0 : (!fir.ref) -> !fir.box> + return +} + +// CHECK-LABEL: llvm.func @embox_typecode0 +// CHECK: %[[TYPE_CODE_I64:.*]] = llvm.mlir.constant(10 : i32) : i32 +// CHECK: %[[TYPE_CODE_I64_I8:.*]] = llvm.trunc %[[TYPE_CODE_I64]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_I64_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +func @embox_typecode1(%arg0: !fir.ref) { + %0 = fir.embox %arg0 : (!fir.ref) -> !fir.box> + return +} + +// CHECK-LABEL: llvm.func @embox_typecode1 +// CHECK: %[[TYPE_CODE_F32:.*]] = llvm.mlir.constant(25 : i32) : i32 +// CHECK: %[[TYPE_CODE_F32_I8:.*]] = llvm.trunc %[[TYPE_CODE_I64]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_F32_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +func @embox_typecode2(%arg0: !fir.ref) { + %0 = fir.embox %arg0 : (!fir.ref) -> !fir.box> + return +} + +// CHECK-LABEL: llvm.func @embox_typecode2 +// CHECK: %[[TYPE_CODE_F128:.*]] = llvm.mlir.constant(27 : i32) : i32 +// CHECK: %[[TYPE_CODE_F128_I8:.*]] = llvm.trunc %[[TYPE_CODE_F128]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_F128_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +func @embox_typecode3(%arg0: !fir.ref>) { + %0 = fir.embox %arg0 : (!fir.ref>) -> !fir.box>> + return +} + +// CHECK-LABEL: llvm.func @embox_typecode3 +// CHECK: %[[TYPE_CODE_CPLX4:.*]] = llvm.mlir.constant(28 : i32) : i32 +// CHECK: %[[TYPE_CODE_CPLX4_I8:.*]] = llvm.trunc %[[TYPE_CODE_F128]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_CPLX4_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +func @embox_typecode4(%arg0: !fir.ref>) { + %0 = fir.embox %arg0 : (!fir.ref>) -> !fir.box>> + return +} + +// CHECK-LABEL: llvm.func @embox_typecode4 +// CHECK: %[[TYPE_CODE_I64:.*]] = llvm.mlir.constant(31 : i32) : i32 +// CHECK: %[[TYPE_CODE_I64_I8:.*]] = llvm.trunc %[[TYPE_CODE_I64]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_I64_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> + +// ----- + +// Test `fir.embox` conversion. This test creates a global so it needs to be +// split from others. + +// Check descriptor for a derived type. Check that the f18Addendum flag is set +// to 1 meaning the addendum is present (true) and the addendum values are +// inserted. + +func @embox1(%arg0: !fir.ref>) { + %0 = fir.embox %arg0() : (!fir.ref>) -> !fir.box> + return +} + +// CHECK: llvm.mlir.global extern_weak @_QMtest_dinitE.dt.tseq() : i8 +// CHECK-LABEL: llvm.func @embox1 +// CHECK: %[[TYPE_CODE:.*]] = llvm.mlir.constant(34 : i32) : i32 +// CHECK: %[[TYPE_CODE_I8:.*]] = llvm.trunc %[[TYPE_CODE]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[TYPE_CODE_I8]], %{{.*}}[4 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)> +// CHECK: %[[F18ADDENDUM:.*]] = llvm.mlir.constant(1 : i32) : i32 +// CHECK: %[[F18ADDENDUM_I8:.*]] = llvm.trunc %[[F18ADDENDUM]] : i32 to i8 +// CHECK: %{{.*}} = llvm.insertvalue %[[F18ADDENDUM_I8]], %18[6 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)> +// CHECK: %[[TDESC:.*]] = llvm.mlir.addressof @_QMtest_dinitE.dt.tseq : !llvm.ptr +// CHECK: %[[TDESC_CAST:.*]] = llvm.bitcast %22 : !llvm.ptr to !llvm.ptr +// CHECK: %{{.*}} = llvm.insertvalue %[[TDESC_CAST]], %{{.*}}[7 : i32] : !llvm.struct<(ptr>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr, array<1 x i{{.*}}>)>