llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> 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. --- Patch is 25.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135105.diff 11 Files Affected: - (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.h (+4) - (modified) clang/include/clang/CIR/Dialect/IR/CIRTypes.td (+114-1) - (added) clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h (+116) - (modified) clang/include/clang/CIR/MissingFeatures.h (+3) - (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+39) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+4) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.cpp (+75-2) - (modified) clang/lib/CIR/CodeGen/CIRGenTypes.h (+8) - (modified) clang/lib/CIR/Dialect/IR/CIRTypes.cpp (+173-3) - (added) clang/test/CIR/CodeGen/struct.c (+13) - (added) clang/test/CIR/IR/struct.cir (+9) ``````````diff 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... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/135105 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits