https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/135105
This change adds minimal support for structure types. To keep the initial change small, only incomplete declarations are being supported in this patch. More complete support will follow. >From 80fa219739a4a6424ec55047fecb41f04d3cf844 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 9 Apr 2025 12:45:57 -0700 Subject: [PATCH] [CIR] Upstream minimal support for structure types This change adds minimal support for structure types. To keep the initial change small, only incomplete declarations are being supported in this patch. More complete support will follow. --- clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 4 + .../include/clang/CIR/Dialect/IR/CIRTypes.td | 115 +++++++++++- .../clang/CIR/Dialect/IR/CIRTypesDetails.h | 116 ++++++++++++ clang/include/clang/CIR/MissingFeatures.h | 3 + clang/lib/CIR/CodeGen/CIRGenBuilder.h | 39 ++++ clang/lib/CIR/CodeGen/CIRGenModule.cpp | 4 + clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 77 +++++++- clang/lib/CIR/CodeGen/CIRGenTypes.h | 8 + clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 176 +++++++++++++++++- clang/test/CIR/CodeGen/struct.c | 13 ++ clang/test/CIR/IR/struct.cir | 9 + 11 files changed, 558 insertions(+), 6 deletions(-) create mode 100644 clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h create mode 100644 clang/test/CIR/CodeGen/struct.c create mode 100644 clang/test/CIR/IR/struct.cir diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h index 7b0fcbc7cc98f..d2c407b2fd686 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h @@ -20,6 +20,10 @@ namespace cir { +namespace detail { +struct StructTypeStorage; +} // namespace detail + bool isAnyFloatingPointType(mlir::Type t); bool isFPOrFPVectorTy(mlir::Type); diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td index e285c0f28f113..bcdaf54bbd84f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -400,13 +400,126 @@ def VoidPtr : Type< "cir::VoidType::get($_builder.getContext()))"> { } +//===----------------------------------------------------------------------===// +// StructType +// +// The base type for all RecordDecls. +//===----------------------------------------------------------------------===// + +def CIR_StructType : CIR_Type<"Struct", "struct", + [ + DeclareTypeInterfaceMethods<DataLayoutTypeInterface>, + MutableType, + ]> { + let summary = "CIR struct type"; + let description = [{ + Each unique clang::RecordDecl is mapped to a `cir.struct` and any object in + C/C++ that has a struct type will have a `cir.struct` in CIR. + + There are three possible formats for this type: + + - Identified and complete structs: unique name and a known body. + - Identified and incomplete structs: unique name and unknown body. + - Anonymous structs: no name and a known body. + + Identified structs are uniqued by their name, and anonymous structs are + uniqued by their body. This means that two anonymous structs with the same + body will be the same type, and two identified structs with the same name + will be the same type. Attempting to build a struct with an existing name, + but a different body will result in an error. + + A few examples: + + ```mlir + !complete = !cir.struct<struct "complete" {!cir.int<u, 8>}> + !incomplete = !cir.struct<struct "incomplete" incomplete> + !anonymous = !cir.struct<struct {!cir.int<u, 8>}> + ``` + + Incomplete structs are mutable, meaning they can be later completed with a + body automatically updating in place every type in the code that uses the + incomplete struct. Mutability allows for recursive types to be represented, + meaning the struct can have members that refer to itself. This is useful for + representing recursive records and is implemented through a special syntax. + In the example below, the `Node` struct has a member that is a pointer to a + `Node` struct: + + ```mlir + !struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct + "Node">>}> + ``` + }]; + + let parameters = (ins + OptionalArrayRefParameter<"mlir::Type">:$members, + OptionalParameter<"mlir::StringAttr">:$name, + "bool":$incomplete, + "bool":$packed, + "bool":$padded, + "StructType::RecordKind":$kind + ); + + // StorageClass is defined in C++ for mutability. + let storageClass = "StructTypeStorage"; + let genStorageClass = 0; + + let skipDefaultBuilders = 1; + let genVerifyDecl = 1; + + let builders = [ + // Create an identified and incomplete struct type. + TypeBuilder<(ins + "mlir::StringAttr":$name, + "RecordKind":$kind + ), [{ + return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name, + /*incomplete=*/true, /*packed=*/false, + /*padded=*/false, kind); + }]>]; + + let extraClassDeclaration = [{ + using Base::verifyInvariants; + + enum RecordKind : uint32_t { Class, Union, Struct }; + + bool isClass() const { return getKind() == RecordKind::Class; }; + bool isStruct() const { return getKind() == RecordKind::Struct; }; + bool isUnion() const { return getKind() == RecordKind::Union; }; + bool isComplete() const { return !isIncomplete(); }; + bool isIncomplete() const; + + size_t getNumElements() const { return getMembers().size(); }; + std::string getKindAsStr() { + switch (getKind()) { + case RecordKind::Class: + return "class"; + case RecordKind::Union: + return "union"; + case RecordKind::Struct: + return "struct"; + } + llvm_unreachable("Invalid value for StructType::getKind()"); + } + std::string getPrefixedName() { + return getKindAsStr() + "." + getName().getValue().str(); + } + }]; + + let hasCustomAssemblyFormat = 1; +} + +// Note CIRStructType is used instead of CIR_StructType +// because of tablegen conflicts. +def CIRStructType : Type< + CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">; + //===----------------------------------------------------------------------===// // Global type constraints //===----------------------------------------------------------------------===// def CIR_AnyType : AnyTypeOf<[ CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_IntType, CIR_AnyFloat, - CIR_PointerType, CIR_FuncType + CIR_PointerType, CIR_FuncType, CIR_StructType ]>; #endif // MLIR_CIR_DIALECT_CIR_TYPES diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h new file mode 100644 index 0000000000000..91bfb814a609c --- /dev/null +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h @@ -0,0 +1,116 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file contains implementation details, such as storage structures, of +// CIR dialect types. +// +//===----------------------------------------------------------------------===// +#ifndef CIR_DIALECT_IR_CIRTYPESDETAILS_H +#define CIR_DIALECT_IR_CIRTYPESDETAILS_H + +#include "mlir/IR/BuiltinAttributes.h" +#include "mlir/Support/LogicalResult.h" +#include "clang/CIR/Dialect/IR/CIRTypes.h" +#include "llvm/ADT/Hashing.h" + +namespace cir { +namespace detail { + +//===----------------------------------------------------------------------===// +// CIR StructTypeStorage +//===----------------------------------------------------------------------===// + +/// Type storage for CIR record types. +struct StructTypeStorage : public mlir::TypeStorage { + struct KeyTy { + llvm::ArrayRef<mlir::Type> members; + mlir::StringAttr name; + bool incomplete; + bool packed; + bool padded; + StructType::RecordKind kind; + + KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + StructType::RecordKind kind) + : members(members), name(name), incomplete(incomplete), packed(packed), + padded(padded), kind(kind) {} + }; + + llvm::ArrayRef<mlir::Type> members; + mlir::StringAttr name; + bool incomplete; + bool packed; + bool padded; + StructType::RecordKind kind; + + StructTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + StructType::RecordKind kind) + : members(members), name(name), incomplete(incomplete), packed(packed), + padded(padded), kind(kind) {} + + KeyTy getAsKey() const { + return KeyTy(members, name, incomplete, packed, padded, kind); + } + + bool operator==(const KeyTy &key) const { + if (name) + return (name == key.name) && (kind == key.kind); + return (members == key.members) && (name == key.name) && + (incomplete == key.incomplete) && (packed == key.packed) && + (padded == key.padded) && (kind == key.kind); + } + + static llvm::hash_code hashKey(const KeyTy &key) { + if (key.name) + return llvm::hash_combine(key.name, key.kind); + return llvm::hash_combine(key.members, key.incomplete, key.packed, + key.padded, key.kind); + } + + static StructTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + return new (allocator.allocate<StructTypeStorage>()) + StructTypeStorage(allocator.copyInto(key.members), key.name, + key.incomplete, key.packed, key.padded, key.kind); + } + + /// Mutates the members and attributes an identified struct. + /// + /// Once a record is mutated, it is marked as complete, preventing further + /// mutations. Anonymous structs are always complete and cannot be mutated. + /// This method does not fail if a mutation of a complete struct does not + /// change the struct. + llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator, + llvm::ArrayRef<mlir::Type> members, bool packed, + bool padded) { + // Anonymous structs cannot mutate. + if (!name) + return llvm::failure(); + + // Mutation of complete structs are allowed if they change nothing. + if (!incomplete) + return mlir::success((this->members == members) && + (this->packed == packed) && + (this->padded == padded)); + + // Mutate incomplete struct. + this->members = allocator.copyInto(members); + this->packed = packed; + this->padded = padded; + + incomplete = false; + return llvm::success(); + } +}; + +} // namespace detail +} // namespace cir + +#endif // CIR_DIALECT_IR_CIRTYPESDETAILS_H diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 3188429ea3b1b..bc52988dfc750 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -82,6 +82,9 @@ struct MissingFeatures { static bool mayHaveIntegerOverflow() { return false; } static bool shouldReverseUnaryCondOnBoolExpr() { return false; } + // StructType + static bool structTypeLayoutInfo() { return false; } + // Misc static bool cxxABI() { return false; } static bool tryEmitAsConstant() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 61a747254b3d0..0a9ef5fcd5471 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -20,11 +20,24 @@ namespace clang::CIRGen { class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { const CIRGenTypeCache &typeCache; + llvm::StringMap<unsigned> recordNames; public: CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc) : CIRBaseBuilderTy(mlirContext), typeCache(tc) {} + std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); } + + std::string getUniqueRecordName(const std::string &baseName) { + auto it = recordNames.find(baseName); + if (it == recordNames.end()) { + recordNames[baseName] = 0; + return baseName; + } + + return baseName + "." + std::to_string(recordNames[baseName]++); + } + cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const { if (&format == &llvm::APFloat::IEEEdouble()) return cir::LongDoubleType::get(getContext(), typeCache.DoubleTy); @@ -37,6 +50,32 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { llvm_unreachable("Unsupported format for long double"); } + /// Get a CIR record kind from a AST declaration tag. + cir::StructType::RecordKind getRecordKind(const clang::TagTypeKind kind) { + switch (kind) { + case clang::TagTypeKind::Struct: + return cir::StructType::Struct; + case clang::TagTypeKind::Union: + return cir::StructType::Union; + case clang::TagTypeKind::Class: + return cir::StructType::Class; + case clang::TagTypeKind::Interface: + llvm_unreachable("interface records are NYI"); + case clang::TagTypeKind::Enum: + llvm_unreachable("enum records are NYI"); + } + } + + /// Get an incomplete CIR struct type. + cir::StructType getIncompleteStructTy(llvm::StringRef name, + const clang::RecordDecl *rd) { + const mlir::StringAttr nameAttr = getStringAttr(name); + cir::StructType::RecordKind kind = cir::StructType::RecordKind::Struct; + if (rd) + kind = getRecordKind(rd->getTagKind()); + return getType<cir::StructType>(nameAttr, kind); + } + bool isSized(mlir::Type ty) { if (mlir::isa<cir::PointerType, cir::ArrayType, cir::BoolType, cir::IntType>(ty)) diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index d2259a9c41d22..4bd186f68f8c9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -490,6 +490,10 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::OpenACCDeclare: emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl)); break; + + case Decl::Record: + assert(!cir::MissingFeatures::generateDebugInfo()); + break; } } diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index 68aee63c2b22d..8644b9a62320a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -86,10 +86,80 @@ mlir::Type CIRGenTypes::convertFunctionTypeInternal(QualType qft) { return cir::FuncType::get(SmallVector<mlir::Type, 1>{}, cgm.VoidTy); } +// This is CIR's version of CodeGenTypes::addRecordTypeName. It isn't shareable +// because CIR has different uniquing requirements. +std::string CIRGenTypes::getRecordTypeName(const clang::RecordDecl *recordDecl, + StringRef suffix) { + llvm::SmallString<256> typeName; + llvm::raw_svector_ostream outStream(typeName); + + PrintingPolicy policy = recordDecl->getASTContext().getPrintingPolicy(); + policy.SuppressInlineNamespace = false; + + if (recordDecl->getIdentifier()) { + if (recordDecl->getDeclContext()) + recordDecl->printQualifiedName(outStream, policy); + else + recordDecl->printName(outStream, policy); + + // Ensure each template specialization has a unique name. + if (auto *templateSpecialization = + llvm::dyn_cast<ClassTemplateSpecializationDecl>(recordDecl)) { + outStream << '<'; + const ArrayRef<TemplateArgument> args = + templateSpecialization->getTemplateArgs().asArray(); + const auto printer = [&policy, &outStream](const TemplateArgument &arg) { + /// Print this template argument to the given output stream. + arg.print(policy, outStream, /*IncludeType=*/true); + }; + llvm::interleaveComma(args, outStream, printer); + outStream << '>'; + } + } else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl()) { + if (typedefNameDecl->getDeclContext()) + typedefNameDecl->printQualifiedName(outStream, policy); + else + typedefNameDecl->printName(outStream); + } else { + outStream << builder.getUniqueAnonRecordName(); + } + + if (!suffix.empty()) + outStream << suffix; + + return builder.getUniqueRecordName(std::string(typeName)); +} + +/// Lay out a tagged decl type like struct or union. +mlir::Type CIRGenTypes::convertRecordDeclType(const clang::RecordDecl *rd) { + // TagDecl's are not necessarily unique, instead use the (clang) type + // connected to the decl. + const Type *key = astContext.getTagDeclType(rd).getTypePtr(); + cir::StructType entry = recordDeclTypes[key]; + + // Handle forward decl / incomplete types. + if (!entry) { + auto name = getRecordTypeName(rd, ""); + entry = builder.getIncompleteStructTy(name, rd); + recordDeclTypes[key] = entry; + } + + rd = rd->getDefinition(); + if (!rd || !rd->isCompleteDefinition() || entry.isComplete()) + return entry; + + cgm.errorNYI(rd->getSourceRange(), "Complete record type"); + return entry; +} + mlir::Type CIRGenTypes::convertType(QualType type) { type = astContext.getCanonicalType(type); const Type *ty = type.getTypePtr(); + // Process record types before the type cache lookup. + if (const auto *recordType = dyn_cast<RecordType>(type)) + return convertRecordDeclType(recordType->getDecl()); + // Has the type already been processed? TypeCacheTy::iterator tci = typeCache.find(ty); if (tci != typeCache.end()) @@ -100,9 +170,11 @@ mlir::Type CIRGenTypes::convertType(QualType type) { mlir::Type resultType = nullptr; switch (ty->getTypeClass()) { + case Type::Record: + llvm_unreachable("Should have been handled above"); + case Type::Builtin: { switch (cast<BuiltinType>(ty)->getKind()) { - // void case BuiltinType::Void: resultType = cgm.VoidTy; @@ -236,7 +308,8 @@ mlir::Type CIRGenTypes::convertType(QualType type) { } default: - cgm.errorNYI(SourceLocation(), "processing of type", type); + cgm.errorNYI(SourceLocation(), "processing of type", + type->getTypeClassName()); resultType = cgm.SInt32Ty; break; } diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.h b/clang/lib/CIR/CodeGen/CIRGenTypes.h index 4021206e979e1..a3b7eae6d0767 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -43,6 +43,9 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; + /// Contains the CIR type for any converted RecordDecl + llvm::DenseMap<const clang::Type *, cir::StructType> recordDeclTypes; + /// Heper for convertType. mlir::Type convertFunctionTypeInternal(clang::QualType ft); @@ -65,6 +68,11 @@ class CIRGenTypes { /// Convert a Clang type into a mlir::Type. mlir::Type convertType(clang::QualType type); + mlir::Type convertRecordDeclType(const clang::RecordDecl *recordDecl); + + std::string getRecordTypeName(const clang::RecordDecl *, + llvm::StringRef suffix); + /// Convert type T into an mlir::Type. This differs from convertType in that /// it is used to convert to the memory representation for a type. For /// example, the scalar representation for bool is i1, but the memory diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 356f7f6244db8..4ff7e1fdbb7c8 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -14,6 +14,7 @@ #include "mlir/IR/DialectImplementation.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" +#include "clang/CIR/Dialect/IR/CIRTypesDetails.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/ADT/TypeSwitch.h" @@ -53,9 +54,13 @@ Type CIRDialect::parseType(DialectAsmParser &parser) const { if (parseResult.has_value()) return genType; - // TODO(CIR) Attempt to parse as a raw C++ type. - parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic; - return Type(); + // Type is not tablegen'd: try to parse as a raw C++ type. + return StringSwitch<function_ref<Type()>>(mnemonic) + .Case("struct", [&] { return StructType::parse(parser); }) + .Default([&] { + parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic; + return Type(); + })(); } void CIRDialect::printType(Type type, DialectAsmPrinter &os) const { @@ -67,6 +72,171 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const { llvm::report_fatal_error("printer is missing a handler for this type"); } +//===----------------------------------------------------------------------===// +// StructType Definitions +//===----------------------------------------------------------------------===// + +Type StructType::parse(mlir::AsmParser &parser) { + FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard; + const auto loc = parser.getCurrentLocation(); + const auto eLoc = parser.getEncodedSourceLoc(loc); + RecordKind kind; + auto *context = parser.getContext(); + + if (parser.parseLess()) + return {}; + + // TODO(cir): in the future we should probably separate types for different + // source language declarations such as cir.class, cir.union, and cir.struct + if (parser.parseOptionalKeyword("struct").succeeded()) + kind = RecordKind::Struct; + else if (parser.parseOptionalKeyword("union").succeeded()) + kind = RecordKind::Union; + else if (parser.parseOptionalKeyword("class").succeeded()) + kind = RecordKind::Class; + else { + parser.emitError(loc, "unknown struct type"); + return {}; + } + + mlir::StringAttr name; + parser.parseOptionalAttribute(name); + + // Is a self reference: ensure referenced type was parsed. + if (name && parser.parseOptionalGreater().succeeded()) { + auto type = getChecked(eLoc, context, name, kind); + if (succeeded(parser.tryStartCyclicParse(type))) { + parser.emitError(loc, "invalid self-reference within record"); + return {}; + } + return type; + } + + // Is a named record definition: ensure name has not been parsed yet. + if (name) { + auto type = getChecked(eLoc, context, name, kind); + cyclicParseGuard = parser.tryStartCyclicParse(type); + if (failed(cyclicParseGuard)) { + parser.emitError(loc, "record already defined"); + return {}; + } + } + + // Parse record members or lack thereof. + bool incomplete = true; + llvm::SmallVector<mlir::Type> members; + if (parser.parseOptionalKeyword("incomplete").failed()) { + incomplete = false; + const auto delimiter = AsmParser::Delimiter::Braces; + const auto parseElementFn = [&parser, &members]() { + return parser.parseType(members.emplace_back()); + }; + if (parser.parseCommaSeparatedList(delimiter, parseElementFn).failed()) + return {}; + } + + if (parser.parseGreater()) + return {}; + + // Try to create the proper record type. + ArrayRef<mlir::Type> membersRef(members); // Needed for template deduction. + mlir::Type type = {}; + if (name && incomplete) { // Identified & incomplete + type = getChecked(eLoc, context, name, kind); + } else if (!incomplete) { // complete + parser.emitError(loc, "complete structs are not yet supported"); + } else { // anonymous & incomplete + parser.emitError(loc, "anonymous structs must be complete"); + return {}; + } + + return type; +} + +void StructType::print(mlir::AsmPrinter &printer) const { + FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard; + printer << '<'; + + switch (getKind()) { + case RecordKind::Struct: + printer << "struct "; + break; + case RecordKind::Union: + printer << "union "; + break; + case RecordKind::Class: + printer << "class "; + break; + } + + if (getName()) + printer << getName(); + + // Current type has already been printed: print as self reference. + cyclicPrintGuard = printer.tryStartCyclicPrint(*this); + if (failed(cyclicPrintGuard)) { + printer << '>'; + return; + } + + // Type not yet printed: continue printing the entire record. + printer << ' '; + + if (isIncomplete()) { + printer << "incomplete"; + } else { + printer << "{"; + llvm::interleaveComma(getMembers(), printer); + printer << "}"; + } + + printer << '>'; +} + +mlir::LogicalResult +StructType::verify(function_ref<mlir::InFlightDiagnostic()> emitError, + llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + StructType::RecordKind kind) { + if (name && name.getValue().empty()) { + emitError() << "identified structs cannot have an empty name"; + return mlir::failure(); + } + return mlir::success(); +} + +::llvm::ArrayRef<mlir::Type> StructType::getMembers() const { + return getImpl()->members; +} + +bool StructType::isIncomplete() const { return getImpl()->incomplete; } + +mlir::StringAttr StructType::getName() const { return getImpl()->name; } + +bool StructType::getIncomplete() const { return getImpl()->incomplete; } + +cir::StructType::RecordKind StructType::getKind() const { + return getImpl()->kind; +} + +//===----------------------------------------------------------------------===// +// Data Layout information for types +//===----------------------------------------------------------------------===// + +llvm::TypeSize +StructType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + assert(!cir::MissingFeatures::structTypeLayoutInfo()); + return llvm::TypeSize::getFixed(8); +} + +uint64_t +StructType::getABIAlignment(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + assert(!cir::MissingFeatures::structTypeLayoutInfo()); + return 4; +} + //===----------------------------------------------------------------------===// // IntType Definitions //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c new file mode 100644 index 0000000000000..4a57ee18061a5 --- /dev/null +++ b/clang/test/CIR/CodeGen/struct.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// Declaration with an incomplete struct type. +struct U *p; + +// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>> +// LLVM: @p = dso_local global ptr null +// OGCG: @p = global ptr null, align 8 diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir new file mode 100644 index 0000000000000..bfe98ba19a66e --- /dev/null +++ b/clang/test/CIR/IR/struct.cir @@ -0,0 +1,9 @@ +// RUN: cir-opt %s | FileCheck %s + +!s32i = !cir.int<s, 32> + +module { + cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>> +} + +// CHECK: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.struct<struct "U" incomplete>> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits