[flang-commits] [flang] af6ee58 - [fir] Add fir.embox conversion
Valentin Clement via flang-commits
flang-commits at lists.llvm.org
Thu Nov 18 02:06:52 PST 2021
Author: Valentin Clement
Date: 2021-11-18T11:06:39+01:00
New Revision: af6ee58092a3af339ca50e2d4514c9597f65c33c
URL: https://github.com/llvm/llvm-project/commit/af6ee58092a3af339ca50e2d4514c9597f65c33c
DIFF: https://github.com/llvm/llvm-project/commit/af6ee58092a3af339ca50e2d4514c9597f65c33c.diff
LOG: [fir] Add fir.embox conversion
Convert a `fir.embox` operation to LLVM IR dialect.
A `fir.embox` is converted to a sequence of operation that
create, allocate if needed, and populate a descriptor.
Current limitiation: alignment is set by default but should be retrieved in the specific target.
This patch is part of the upstreaming effort from fir-dev branch.
Reviewed By: kiranchandramohan, awarzynski
Differential Revision: https://reviews.llvm.org/D113756
Co-authored-by: Eric Schweitz <eschweitz at nvidia.com>
Co-authored-by: Jean Perier <jperier at nvidia.com>
Added:
flang/include/flang/Optimizer/Support/TypeCode.h
Modified:
flang/include/flang/Optimizer/Dialect/FIRTypes.td
flang/lib/Optimizer/CodeGen/CodeGen.cpp
flang/lib/Optimizer/Dialect/FIRType.cpp
flang/test/Fir/convert-to-llvm.fir
Removed:
################################################################################
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<TypePair> lenPList,
llvm::ArrayRef<TypePair> 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<FromOp> {
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<mlir::LLVM::ConstantOp>(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<mlir::LLVM::ConstantOp>(loc, ity, cattr);
}
@@ -153,6 +165,28 @@ class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
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<unsigned> indexes) {
+ if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>())
+ type = t.getElementType();
+ for (auto i : indexes) {
+ if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) {
+ assert(!t.isOpaque() && i < t.getBody().size());
+ type = t.getBody()[i];
+ } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
+ type = t.getElementType();
+ } else if (auto t = type.dyn_cast<mlir::VectorType>()) {
+ type = t.getElementType();
+ } else {
+ fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()),
+ "request for invalid box element type");
+ }
+ }
+ return type;
+ }
+
template <typename... ARGS>
mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty,
mlir::ConversionPatternRewriter &rewriter,
@@ -1183,6 +1217,318 @@ struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
}
};
+/// Common base class for embox to descriptor conversion.
+template <typename OP>
+struct EmboxCommonConversion : public FIROpConversion<OP> {
+ using FIROpConversion<OP>::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<mlir::LLVM::LLVMFuncOp>(parentOp)
+ ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp)
+ : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
+ }
+
+ // 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<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment);
+ rewriter.restoreInsertionPoint(thisPt);
+ return al;
+ }
+
+ static int getCFIAttr(fir::BoxType boxTy) {
+ auto eleTy = boxTy.getEleTy();
+ if (eleTy.isa<fir::PointerType>())
+ return CFI_attribute_pointer;
+ if (eleTy.isa<fir::HeapType>())
+ 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<fir::RecordType>();
+ }
+ 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<mlir::Value, mlir::Value> getSizeAndTypeCode(
+ mlir::Location loc, mlir::ConversionPatternRewriter &rewriter,
+ mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const {
+ auto doInteger =
+ [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
+ int typeCode = fir::integerBitsToTypeCode(width);
+ return {this->genConstantOffset(loc, rewriter, width / 8),
+ this->genConstantOffset(loc, rewriter, typeCode)};
+ };
+ auto doLogical =
+ [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
+ int typeCode = fir::logicalBitsToTypeCode(width);
+ return {this->genConstantOffset(loc, rewriter, width / 8),
+ this->genConstantOffset(loc, rewriter, typeCode)};
+ };
+ auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
+ int typeCode = fir::realBitsToTypeCode(width);
+ return {this->genConstantOffset(loc, rewriter, width / 8),
+ this->genConstantOffset(loc, rewriter, typeCode)};
+ };
+ auto doComplex =
+ [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> {
+ 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<mlir::Value, mlir::Value> {
+ 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<mlir::LLVM::MulOp>(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<mlir::IntegerType>())
+ return doInteger(ty.getWidth());
+ auto ty = boxEleTy.cast<fir::IntegerType>();
+ return doInteger(getKindMap().getIntegerBitsize(ty.getFKind()));
+ }
+ // Floating point types.
+ if (fir::isa_real(boxEleTy)) {
+ if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>())
+ return doFloat(ty.getWidth());
+ auto ty = boxEleTy.cast<fir::RealType>();
+ return doFloat(getKindMap().getRealBitsize(ty.getFKind()));
+ }
+ // Complex types.
+ if (fir::isa_complex(boxEleTy)) {
+ if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>())
+ return doComplex(
+ ty.getElementType().cast<mlir::FloatType>().getWidth());
+ auto ty = boxEleTy.cast<fir::ComplexType>();
+ return doComplex(getKindMap().getRealBitsize(ty.getFKind()));
+ }
+ // Character types.
+ if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) {
+ 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<fir::LogicalType>())
+ return doLogical(getKindMap().getLogicalBitsize(ty.getFKind()));
+ // Array types.
+ if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>())
+ return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams);
+ // Derived-type types.
+ if (boxEleTy.isa<fir::RecordType>()) {
+ auto ptrTy = mlir::LLVM::LLVMPointerType::get(
+ this->lowerTy().convertType(boxEleTy));
+ auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy);
+ auto one =
+ genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1);
+ auto gep = rewriter.create<mlir::LLVM::GEPOp>(
+ loc, ptrTy, mlir::ValueRange{nullPtr, one});
+ auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>(
+ 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<unsigned> fldIndexes, mlir::Value value,
+ bool bitcast = false) const {
+ auto boxTy = dest.getType();
+ auto fldTy = this->getBoxEleTy(boxTy, fldIndexes);
+ if (bitcast)
+ value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value);
+ else
+ value = this->integerCast(loc, rewriter, fldTy, value);
+ SmallVector<mlir::Attribute, 2> attrs;
+ for (auto i : fldIndexes)
+ attrs.push_back(rewriter.getI32IntegerAttr(i));
+ auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs);
+ return rewriter.create<mlir::LLVM::InsertValueOp>(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 <typename BOX>
+ 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<mlir::ModuleOp>();
+ if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) {
+ auto ty = mlir::LLVM::LLVMPointerType::get(
+ this->lowerTy().convertType(global.getType()));
+ return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty,
+ global.sym_name());
+ }
+ if (auto global =
+ module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) {
+ // The global may have already been translated to LLVM.
+ auto ty = mlir::LLVM::LLVMPointerType::get(global.getType());
+ return rewriter.create<mlir::LLVM::AddressOfOp>(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<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false,
+ mlir::LLVM::Linkage::ExternWeak,
+ name, mlir::Attribute{});
+ auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty);
+ return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name);
+ }
+
+ template <typename BOX>
+ std::tuple<fir::BoxType, mlir::Value, mlir::Value>
+ consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter,
+ unsigned rank, mlir::ValueRange lenParams) const {
+ auto loc = box.getLoc();
+ auto boxTy = box.getType().template dyn_cast<fir::BoxType>();
+ auto convTy = this->lowerTy().convertBoxType(boxTy, rank);
+ auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>();
+ auto llvmBoxTy = llvmBoxPtrTy.getElementType();
+ mlir::Value descriptor =
+ rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy);
+
+ llvm::SmallVector<mlir::Value> typeparams = lenParams;
+ if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) {
+ 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<fir::SequenceType>();
+ 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<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()))
+ return boxValue;
+ auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType());
+ auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter);
+ rewriter.create<mlir::LLVM::StoreOp>(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<fir::EmboxOp> {
+ 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<FIRToLLVMLowering> {
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<unsigned>::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<f64>) {
// CHECK: %[[GEP:.*]] = llvm.getelementptr %[[ARG0]][%[[C0]], %[[TYPE_POS]]] : (!llvm.ptr<struct<(ptr<f64>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>, i32, i32) -> !llvm.ptr<i8>
// CHECK: %[[LOAD:.*]] = llvm.load %[[GEP]] : !llvm.ptr<i{{.*}}>
// CHECK: %{{.*}} = llvm.inttoptr %[[LOAD]] : i{{.*}} to !llvm.ptr<i{{.*}}>
+
+// -----
+
+// 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<!fir.array<100xi32>>) {
+ %0 = fir.embox %arg0() : (!fir.ref<!fir.array<100xi32>>) -> !fir.box<!fir.array<100xi32>>
+ return
+}
+
+// CHECK-LABEL: func @embox0(
+// CHECK-SAME: %[[ARG0:.*]]: !llvm.ptr<array<100 x i32>>
+// CHECK: %[[C1:.*]] = llvm.mlir.constant(1 : i32) : i32
+// CHECK: %[[ALLOCA:.*]] = llvm.alloca %[[C1]] x !llvm.struct<(ptr<array<100 x i32>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})> {alignment = 8 : i64} : (i32) -> !llvm.ptr<struct<(ptr<array<100 x i32>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>>
+// CHECK: %[[DESC:.*]] = llvm.mlir.undef : !llvm.struct<(ptr<array<100 x i32>>, 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<array<100 x i32>>, 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<array<100 x i32>>, 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<array<100 x i32>>, 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<array<100 x i32>>, 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<array<100 x i32>>, 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<array<100 x i32>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+// CHECK: %[[ADDR:.*]] = llvm.bitcast %[[ARG0]] : !llvm.ptr<array<100 x i32>> to !llvm.ptr<array<100 x i32>>
+// CHECK: %[[DESC6:.*]] = llvm.insertvalue %[[ADDR]], %[[DESC5]][0 : i32] : !llvm.struct<(ptr<array<100 x i32>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+// CHECK: llvm.store %[[DESC6]], %[[ALLOCA]] : !llvm.ptr<struct<(ptr<array<100 x i32>>, 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<!fir.array<?xi32>> {
+ %arr = fir.zero_bits !fir.ref<!fir.array<?xi32>>
+ %0 = arith.constant 0 : index
+ %3 = fir.embox %arr: (!fir.ref<!fir.array<?xi32>>) -> !fir.box<!fir.array<?xi32>>
+ fir.has_value %arr : !fir.ref<!fir.array<?xi32>>
+}
+
+// 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<i32>) {
+ %0 = fir.embox %arg0 : (!fir.ref<i32>) -> !fir.box<!fir.ptr<i32>>
+ 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<i32>, 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<!fir.array<?x!fir.char<1,10>>>) {
+ %0 = fir.embox %arg0 : (!fir.heap<!fir.array<?x!fir.char<1,10>>>) -> !fir.box<!fir.heap<!fir.array<?x!fir.char<1,10>>>>
+ 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<array<10 x i8>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+
+// Check `fir.embox` conversion of a type code.
+
+func @embox_typecode0(%arg0: !fir.ref<i64>) {
+ %0 = fir.embox %arg0 : (!fir.ref<i64>) -> !fir.box<!fir.ptr<i64>>
+ 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<i64>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+
+func @embox_typecode1(%arg0: !fir.ref<f32>) {
+ %0 = fir.embox %arg0 : (!fir.ref<f32>) -> !fir.box<!fir.ptr<f32>>
+ 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<f32>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+
+func @embox_typecode2(%arg0: !fir.ref<f128>) {
+ %0 = fir.embox %arg0 : (!fir.ref<f128>) -> !fir.box<!fir.ptr<f128>>
+ 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<f128>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+
+func @embox_typecode3(%arg0: !fir.ref<!fir.complex<4>>) {
+ %0 = fir.embox %arg0 : (!fir.ref<!fir.complex<4>>) -> !fir.box<!fir.ptr<!fir.complex<4>>>
+ 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<struct<(f32, f32)>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}})>
+
+func @embox_typecode4(%arg0: !fir.ref<!fir.logical<1>>) {
+ %0 = fir.embox %arg0 : (!fir.ref<!fir.logical<1>>) -> !fir.box<!fir.ptr<!fir.logical<1>>>
+ 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<i8>, 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<!fir.type<_QMtest_dinitTtseq{i:i32}>>) {
+ %0 = fir.embox %arg0() : (!fir.ref<!fir.type<_QMtest_dinitTtseq{i:i32}>>) -> !fir.box<!fir.type<_QMtest_dinitTtseq{i:i32}>>
+ 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<struct<"_QMtest_dinitTtseq", (i32)>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr<i{{.*}}>, 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<struct<"_QMtest_dinitTtseq", (i32)>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr<i{{.*}}>, array<1 x i{{.*}}>)>
+// CHECK: %[[TDESC:.*]] = llvm.mlir.addressof @_QMtest_dinitE.dt.tseq : !llvm.ptr<i8>
+// CHECK: %[[TDESC_CAST:.*]] = llvm.bitcast %22 : !llvm.ptr<i8> to !llvm.ptr<i8>
+// CHECK: %{{.*}} = llvm.insertvalue %[[TDESC_CAST]], %{{.*}}[7 : i32] : !llvm.struct<(ptr<struct<"_QMtest_dinitTtseq", (i32)>>, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, i{{.*}}, ptr<i{{.*}}>, array<1 x i{{.*}}>)>
More information about the flang-commits
mailing list