diff --git a/flang/include/flang/Optimizer/Support/TypeCode.h b/flang/include/flang/Optimizer/Support/TypeCode.h new file mode 100644 --- /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 bits) { + // clang-format off + switch (bits) { + 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 bits) { + // clang-format off + switch (bits) { + 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 bits) { + // clang-format off + switch (bits) { + 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 bits) { + // clang-format off + switch (bits) { + 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 bits) { + // clang-format off + switch (bits) { + 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 --- 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; @@ -153,6 +157,28 @@ loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); } + // Get the element type given an LLVM type that is of the form + // [llvm.ptr](llvm.array|llvm.struct)+ 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, @@ -1016,6 +1042,300 @@ } }; +// Common base class for lowering of embox to descriptor creation. +template +struct EmboxCommonConversion : public FIROpConversion { + using FIROpConversion::FIROpConversion; + + /// Generate an alloca of size `size` and cast it to type `toTy` + mlir::LLVM::AllocaOp + genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, + mlir::ConversionPatternRewriter &rewriter) const { + auto thisPt = rewriter.saveInsertionPoint(); + auto *thisBlock = rewriter.getInsertionBlock(); + auto op = thisBlock->getParentOp(); + // Order to find the Op in whose entry block the alloca should be inserted. + // The parent Op if it is an LLVM Function Op. + // The ancestor LLVM Function Op. + auto func = mlir::isa(op) + ? mlir::cast(op) + : op->getParentOfType(); + rewriter.setInsertionPointToStart(&func.front()); + auto sz = this->genConstantOffset(loc, rewriter, 1); + auto al = rewriter.create(loc, toTy, sz, alignment); + rewriter.restoreInsertionPoint(thisPt); + return al; + } + + int getCFIAttr(fir::BoxType boxTy) const { + 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 static_cast(unwrapIfDerived(boxTy)); + } + + // 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(); + }; + + if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) + boxEleTy = eleTy; + 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())); + } + 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())); + } + 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())); + } + 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()); + } + if (auto ty = boxEleTy.dyn_cast()) + return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); + if (auto seqTy = boxEleTy.dyn_cast()) + return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); + 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())}; + } + 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); + } + + 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 { + auto split = recType.getName().split('T'); + std::string name = (split.first + "E.dt." + split.second).str(); + 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 symbols (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 dest = 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); + dest = insertField(rewriter, loc, dest, {kElemLenPosInBox}, eleSize); + dest = insertField(rewriter, loc, dest, {kVersionPosInBox}, + this->genConstantOffset(loc, rewriter, CFI_VERSION)); + dest = insertField(rewriter, loc, dest, {kRankPosInBox}, + this->genConstantOffset(loc, rewriter, rank)); + dest = insertField(rewriter, loc, dest, {kTypePosInBox}, cfiTy); + dest = + insertField(rewriter, loc, dest, {kAttributePosInBox}, + this->genConstantOffset(loc, rewriter, getCFIAttr(boxTy))); + const bool hasAddendum = isDerivedType(boxTy); + dest = insertField(rewriter, loc, dest, {kF18AddendumPosInBox}, + this->genConstantOffset(loc, rewriter, hasAddendum)); + + if (hasAddendum) { + auto isArray = + fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa(); + unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; + auto typeDesc = + getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); + dest = insertField(rewriter, loc, dest, {typeDescFieldId}, typeDesc, + /*bitCast=*/true); + } + + return {boxTy, dest, eleSize}; + } + + /// If the embox is not in a globalOp body, allocate storage for the box and + /// store the value inside. 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 { + // There should be no dims on this embox op + assert(!embox.getShape()); + 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)) + TODO(embox.getLoc(), + "fir.embox codegen of derived with length parameters"); + 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 @@ -1416,13 +1736,14 @@ BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion, ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, - DTEntryOpConversion, DivcOpConversion, ExtractValueOpConversion, - HasValueOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, - InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, - NegcOpConversion, MulcOpConversion, SelectCaseOpConversion, - SelectOpConversion, SelectRankOpConversion, StoreOpConversion, - SubcOpConversion, UndefOpConversion, UnreachableOpConversion, - ZeroOpConversion>(typeConverter); + DTEntryOpConversion, DivcOpConversion, EmboxOpConversion, + ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion, + InsertOnRangeOpConversion, InsertValueOpConversion, + IsPresentOpConversion, LoadOpConversion, NegcOpConversion, + MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, + SelectRankOpConversion, StoreOpConversion, SubcOpConversion, + UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( + typeConverter); mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, pattern); diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir --- a/flang/test/Fir/convert-to-llvm.fir +++ b/flang/test/Fir/convert-to-llvm.fir @@ -1197,3 +1197,107 @@ // CHECK-NEXT: %[[ptr:.*]] = llvm.mlir.null : !llvm.ptr // CHECK-NEXT: %[[ret_val:.*]] = llvm.call @is_present(%[[ptr]]) : (!llvm.ptr) -> i1 // CHECK-NEXT: llvm.return %[[ret_val]] : i1 + +// ----- + +// Test `fir.embox` conversion. + +// Check basic creation of a descriptor and insertion of values. + +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 %3 : 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 %14 : i32 to i8 +// CHECK: %[[DESC4:.*]] = llvm.insertvalue %15, %[[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`. Descriptor created by `fir.embox` +// conversion are not generating `alloca` instruction. 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. + +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. + +func @embox_allocatable(%arg0: !fir.heap>>) { + %0 = fir.embox %arg0 : (!fir.heap>>) -> !fir.box>>> + return +} + +// CHECK-LABEL: llvm.func @embox_allocatable +// CHECK: %[[CFI_ATTR_POINTER:.*]] = llvm.mlir.constant(2 : 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{{.*}})> + +// ----- + +// 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 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{{.*}}>)>