diff options
-rw-r--r-- | clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 4 | ||||
-rw-r--r-- | clang/include/clang/CIR/Dialect/IR/CIRTypes.td | 111 | ||||
-rw-r--r-- | clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h | 118 | ||||
-rw-r--r-- | clang/include/clang/CIR/MissingFeatures.h | 3 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenBuilder.h | 40 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 7 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.cpp | 61 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenTypes.h | 9 | ||||
-rw-r--r-- | clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 173 | ||||
-rw-r--r-- | clang/test/CIR/CodeGen/struct.c | 30 | ||||
-rw-r--r-- | clang/test/CIR/CodeGen/struct.cpp | 31 | ||||
-rw-r--r-- | clang/test/CIR/CodeGen/union.c | 30 | ||||
-rw-r--r-- | clang/test/CIR/IR/struct.cir | 9 |
14 files changed, 622 insertions, 9 deletions
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h index 7b0fcbc..074b8d0 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 RecordTypeStorage; +} // 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 e285c0f..c60af47 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td @@ -401,12 +401,121 @@ def VoidPtr : Type< } //===----------------------------------------------------------------------===// +// RecordType +// +// The base type for all RecordDecls. +//===----------------------------------------------------------------------===// + +def CIR_RecordType : CIR_Type<"Record", "record", + [ + DeclareTypeInterfaceMethods<DataLayoutTypeInterface>, + MutableType, + ]> { + let summary = "CIR record type"; + let description = [{ + Each unique clang::RecordDecl is mapped to a `cir.record` and any object in + C/C++ that has a struct or class type will have a `cir.record` in CIR. + + There are three possible formats for this type: + + - Identified and complete records: unique name and a known body. + - Identified and incomplete records: unique name and unknown body. + - Anonymous records: no name and a known body. + + Identified records are uniqued by their name, and anonymous records are + uniqued by their body. This means that two anonymous records with the same + body will be the same type, and two identified records with the same name + will be the same type. Attempting to build a record with an existing name, + but a different body will result in an error. + + A few examples: + + ```mlir + !complete = !cir.record<struct "complete" {!cir.int<u, 8>}> + !incomplete = !cir.record<struct "incomplete" incomplete> + !anonymous = !cir.record<struct {!cir.int<u, 8>}> + ``` + + Incomplete records are mutable, meaning they can be later completed with a + body automatically updating in place every type in the code that uses the + incomplete record. Mutability allows for recursive types to be represented, + meaning the record 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` record has a member that is a pointer to a + `Node` record: + + ```mlir + !s = !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}> + ``` + }]; + + let parameters = (ins + OptionalArrayRefParameter<"mlir::Type">:$members, + OptionalParameter<"mlir::StringAttr">:$name, + "bool":$incomplete, + "bool":$packed, + "bool":$padded, + "RecordType::RecordKind":$kind + ); + + // StorageClass is defined in C++ for mutability. + let storageClass = "RecordTypeStorage"; + let genStorageClass = 0; + + let skipDefaultBuilders = 1; + let genVerifyDecl = 1; + + let builders = [ + // Create an identified and incomplete record 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 { Struct, Union }; + + 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::Union: + return "union"; + case RecordKind::Struct: + return "struct"; + } + llvm_unreachable("Invalid value for RecordType::getKind()"); + } + std::string getPrefixedName() { + return getKindAsStr() + "." + getName().getValue().str(); + } + }]; + + let hasCustomAssemblyFormat = 1; +} + +// Note CIRRecordType is used instead of CIR_RecordType +// because of tablegen conflicts. +def CIRRecordType : Type< + CPred<"::mlir::isa<::cir::RecordType>($_self)">, "CIR record 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_RecordType ]>; #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 0000000..72ead23 --- /dev/null +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// 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 RecordTypeStorage +//===----------------------------------------------------------------------===// + +/// Type storage for CIR record types. +struct RecordTypeStorage : public mlir::TypeStorage { + struct KeyTy { + llvm::ArrayRef<mlir::Type> members; + mlir::StringAttr name; + bool incomplete; + bool packed; + bool padded; + RecordType::RecordKind kind; + + KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + RecordType::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; + RecordType::RecordKind kind; + + RecordTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + RecordType::RecordKind kind) + : members(members), name(name), incomplete(incomplete), packed(packed), + padded(padded), kind(kind) { + assert(name || !incomplete && "Incomplete records must have a name"); + } + + 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 std::tie(members, name, incomplete, packed, padded, kind) == + std::tie(key.members, key.name, key.incomplete, key.packed, + key.padded, 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 RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator, + const KeyTy &key) { + return new (allocator.allocate<RecordTypeStorage>()) + RecordTypeStorage(allocator.copyInto(key.members), key.name, + key.incomplete, key.packed, key.padded, key.kind); + } + + /// Mutates the members and attributes an identified record. + /// + /// Once a record is mutated, it is marked as complete, preventing further + /// mutations. Anonymous records are always complete and cannot be mutated. + /// This method does not fail if a mutation of a complete record does not + /// change the record. + llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator, + llvm::ArrayRef<mlir::Type> members, bool packed, + bool padded) { + // Anonymous records cannot mutate. + if (!name) + return llvm::failure(); + + // Mutation of complete records are allowed if they change nothing. + if (!incomplete) + return mlir::success((this->members == members) && + (this->packed == packed) && + (this->padded == padded)); + + // Mutate incomplete record. + 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 d6a28d43..6f2fd2c 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -101,6 +101,9 @@ struct MissingFeatures { static bool mayHaveIntegerOverflow() { return false; } static bool shouldReverseUnaryCondOnBoolExpr() { return false; } + // RecordType + static bool recordTypeLayoutInfo() { 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 0d85687..d984167 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,33 @@ public: llvm_unreachable("Unsupported format for long double"); } + /// Get a CIR record kind from a AST declaration tag. + cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) { + switch (kind) { + case clang::TagTypeKind::Class: + case clang::TagTypeKind::Struct: + return cir::RecordType::Struct; + case clang::TagTypeKind::Union: + return cir::RecordType::Union; + case clang::TagTypeKind::Interface: + llvm_unreachable("interface records are NYI"); + case clang::TagTypeKind::Enum: + llvm_unreachable("enums are not records"); + } + } + + /// Get an incomplete CIR struct type. If we have a complete record + /// declaration, we may create an incomplete type and then add the + /// members, so \p rd here may be complete. + cir::RecordType getIncompleteRecordTy(llvm::StringRef name, + const clang::RecordDecl *rd) { + const mlir::StringAttr nameAttr = getStringAttr(name); + cir::RecordType::RecordKind kind = cir::RecordType::RecordKind::Struct; + if (rd) + kind = getRecordKind(rd->getTagKind()); + return getType<cir::RecordType>(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/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 58797c5..3d15a70 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -260,6 +260,9 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d, void CIRGenFunction::emitDecl(const Decl &d) { switch (d.getKind()) { + case Decl::Record: // struct/union/class X; + assert(!cir::MissingFeatures::generateDebugInfo()); + return; case Decl::Var: { const VarDecl &vd = cast<VarDecl>(d); assert(vd.isLocalVarDecl() && @@ -274,7 +277,9 @@ void CIRGenFunction::emitDecl(const Decl &d) { emitOpenACCRoutine(cast<OpenACCRoutineDecl>(d)); return; default: - cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type"); + cgm.errorNYI(d.getSourceRange(), + std::string("emitDecl: unhandled decl type: ") + + d.getDeclKindName()); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index fd11523..ddcdc0d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -589,6 +589,11 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::OpenACCDeclare: emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl)); break; + + case Decl::Record: + case Decl::CXXRecord: + assert(!cir::MissingFeatures::generateDebugInfo()); + break; } } diff --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp index a5978a4..2635062 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp @@ -86,10 +86,64 @@ 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; + policy.AlwaysIncludeTypeForTemplateArgument = true; + policy.PrintCanonicalTypes = true; + policy.SuppressTagKeyword = true; + + if (recordDecl->getIdentifier()) + astContext.getRecordType(recordDecl).print(outStream, policy); + else if (auto *typedefNameDecl = recordDecl->getTypedefNameForAnonDecl()) + typedefNameDecl->printQualifiedName(outStream, policy); + 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::RecordType entry = recordDeclTypes[key]; + + // If we don't have an entry for this record yet, create one. + // We create an incomplete type initially. If `rd` is complete, we will + // add the members below. + if (!entry) { + auto name = getRecordTypeName(rd, ""); + entry = builder.getIncompleteRecordTy(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 +154,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 +292,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 60661ba..fd855bf 100644 --- a/clang/lib/CIR/CodeGen/CIRGenTypes.h +++ b/clang/lib/CIR/CodeGen/CIRGenTypes.h @@ -45,11 +45,13 @@ class CIRGenTypes { clang::ASTContext &astContext; CIRGenBuilderTy &builder; + /// Contains the CIR type for any converted RecordDecl + llvm::DenseMap<const clang::Type *, cir::RecordType> recordDeclTypes; + /// Hold memoized CIRGenFunctionInfo results llvm::FoldingSet<CIRGenFunctionInfo> functionInfos; llvm::SmallPtrSet<const CIRGenFunctionInfo *, 4> functionsBeingProcessed; - /// Heper for convertType. mlir::Type convertFunctionTypeInternal(clang::QualType ft); @@ -72,6 +74,11 @@ public: /// 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 356f7f6..9b0177f 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("record", [&] { return RecordType::parse(parser); }) + .Default([&] { + parser.emitError(typeLoc) << "unknown CIR type: " << mnemonic; + return Type(); + })(); } void CIRDialect::printType(Type type, DialectAsmPrinter &os) const { @@ -68,6 +73,166 @@ void CIRDialect::printType(Type type, DialectAsmPrinter &os) const { } //===----------------------------------------------------------------------===// +// RecordType Definitions +//===----------------------------------------------------------------------===// + +Type RecordType::parse(mlir::AsmParser &parser) { + FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard; + const llvm::SMLoc loc = parser.getCurrentLocation(); + const mlir::Location eLoc = parser.getEncodedSourceLoc(loc); + RecordKind kind; + mlir::MLIRContext *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.record and cir.union + if (parser.parseOptionalKeyword("struct").succeeded()) + kind = RecordKind::Struct; + else if (parser.parseOptionalKeyword("union").succeeded()) + kind = RecordKind::Union; + else { + parser.emitError(loc, "unknown record type"); + return {}; + } + + mlir::StringAttr name; + parser.parseOptionalAttribute(name); + + // Is a self reference: ensure referenced type was parsed. + if (name && parser.parseOptionalGreater().succeeded()) { + RecordType 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) { + RecordType 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 records are not yet supported"); + } else { // anonymous & incomplete + parser.emitError(loc, "anonymous records must be complete"); + return {}; + } + + return type; +} + +void RecordType::print(mlir::AsmPrinter &printer) const { + FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard; + printer << '<'; + + switch (getKind()) { + case RecordKind::Struct: + printer << "struct "; + break; + case RecordKind::Union: + printer << "union "; + 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 +RecordType::verify(function_ref<mlir::InFlightDiagnostic()> emitError, + llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, + bool incomplete, bool packed, bool padded, + RecordType::RecordKind kind) { + if (name && name.getValue().empty()) { + emitError() << "identified records cannot have an empty name"; + return mlir::failure(); + } + return mlir::success(); +} + +::llvm::ArrayRef<mlir::Type> RecordType::getMembers() const { + return getImpl()->members; +} + +bool RecordType::isIncomplete() const { return getImpl()->incomplete; } + +mlir::StringAttr RecordType::getName() const { return getImpl()->name; } + +bool RecordType::getIncomplete() const { return getImpl()->incomplete; } + +cir::RecordType::RecordKind RecordType::getKind() const { + return getImpl()->kind; +} + +//===----------------------------------------------------------------------===// +// Data Layout information for types +//===----------------------------------------------------------------------===// + +llvm::TypeSize +RecordType::getTypeSizeInBits(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + assert(!cir::MissingFeatures::recordTypeLayoutInfo()); + return llvm::TypeSize::getFixed(8); +} + +uint64_t +RecordType::getABIAlignment(const ::mlir::DataLayout &dataLayout, + ::mlir::DataLayoutEntryListRef params) const { + assert(!cir::MissingFeatures::recordTypeLayoutInfo()); + return 4; +} + +//===----------------------------------------------------------------------===// // IntType Definitions //===----------------------------------------------------------------------===// @@ -432,5 +597,5 @@ void CIRDialect::registerTypes() { >(); // Register raw C++ types. - // TODO(CIR) addTypes<StructType>(); + // TODO(CIR) addTypes<RecordType>(); } diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c new file mode 100644 index 0000000..4edd591e60 --- /dev/null +++ b/clang/test/CIR/CodeGen/struct.c @@ -0,0 +1,30 @@ +// 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 + +struct IncompleteS *p; + +// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<struct "IncompleteS" incomplete>> +// LLVM: @p = dso_local global ptr null +// OGCG: @p = global ptr null, align 8 + +void f(void) { + struct IncompleteS *p; +} + +// CIR: cir.func @f() +// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, +// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"] +// CIR-NEXT: cir.return + +// LLVM: define void @f() +// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 +// LLVM-NEXT: ret void + +// OGCG: define{{.*}} void @f() +// OGCG-NEXT: entry: +// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 +// OGCG-NEXT: ret void diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp new file mode 100644 index 0000000..6197340 --- /dev/null +++ b/clang/test/CIR/CodeGen/struct.cpp @@ -0,0 +1,31 @@ +// 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 + +struct IncompleteS; +IncompleteS *p; + +// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<struct "IncompleteS" incomplete>> +// LLVM: @p = dso_local global ptr null +// OGCG: @p = global ptr null, align 8 + +void f(void) { + IncompleteS *p; +} + +// CIR: cir.func @f() +// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<struct "IncompleteS" incomplete>>, +// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<struct "IncompleteS" incomplete>>>, ["p"] +// CIR-NEXT: cir.return + +// LLVM: define void @f() +// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 +// LLVM-NEXT: ret void + +// OGCG: define{{.*}} void @_Z1fv() +// OGCG-NEXT: entry: +// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 +// OGCG-NEXT: ret void diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c new file mode 100644 index 0000000..075d0d2 --- /dev/null +++ b/clang/test/CIR/CodeGen/union.c @@ -0,0 +1,30 @@ +// 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 + +union IncompleteU *p; + +// CIR: cir.global external @p = #cir.ptr<null> : !cir.ptr<!cir.record<union "IncompleteU" incomplete>> +// LLVM: @p = dso_local global ptr null +// OGCG: @p = global ptr null, align 8 + +void f(void) { + union IncompleteU *p; +} + +// CIR: cir.func @f() +// CIR-NEXT: cir.alloca !cir.ptr<!cir.record<union "IncompleteU" incomplete>>, +// CIR-SAME: !cir.ptr<!cir.ptr<!cir.record<union "IncompleteU" incomplete>>>, ["p"] +// CIR-NEXT: cir.return + +// LLVM: define void @f() +// LLVM-NEXT: %[[P:.*]] = alloca ptr, i64 1, align 8 +// LLVM-NEXT: ret void + +// OGCG: define{{.*}} void @f() +// OGCG-NEXT: entry: +// OGCG-NEXT: %[[P:.*]] = alloca ptr, align 8 +// OGCG-NEXT: ret void diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir new file mode 100644 index 0000000..b6ed1d7 --- /dev/null +++ b/clang/test/CIR/IR/struct.cir @@ -0,0 +1,9 @@ +// RUN: cir-opt %s | FileCheck %s + +module { + cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.record<struct "S" incomplete>> + cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.record<union "U" incomplete>> +} + +// CHECK: cir.global external @p1 = #cir.ptr<null> : !cir.ptr<!cir.record<struct "S" incomplete>> +// CHECK: cir.global external @p2 = #cir.ptr<null> : !cir.ptr<!cir.record<union "U" incomplete>> |