diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h index 7f09e9fcf973..22f7ff01f53f 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h @@ -41,9 +41,43 @@ struct StructTypeStorage; /// 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 unkonwn 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 a existing name, +/// but a different body will result in an error. +/// +/// A few examples: +/// +/// ```mlir +/// !complete = !cir.struct}> +/// !incomplete = !cir.struct +/// !anonymous = !cir.struct}> +/// ``` +/// +/// Incomplete structs are mutable, meaning the 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>}> +/// ``` class StructType : public Type::TypeBase { + DataLayoutTypeInterface::Trait, + TypeTrait::IsMutable> { // FIXME(cir): migrate this type to Tablegen once mutable types are supported. public: using Base::Base; @@ -121,6 +155,10 @@ class StructType return getKindAsStr() + "." + getName().getValue().str(); } + /// Complete the struct type by mutating its members and attributes. + void complete(ArrayRef members, bool packed, + ASTRecordDeclInterface ast = {}); + /// DataLayoutTypeInterface methods. unsigned getTypeSizeInBits(const DataLayout &dataLayout, DataLayoutEntryListRef params) const; diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h index d33d43c346d5..ae9e97ce3cab 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h @@ -14,7 +14,9 @@ #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 mlir { namespace cir { @@ -58,14 +60,18 @@ struct StructTypeStorage : public TypeStorage { } 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) && (kind == key.kind) && (ast == key.ast); } static llvm::hash_code hashKey(const KeyTy &key) { - return hash_combine(key.members, key.name, key.incomplete, key.packed, - key.kind, key.ast); + if (key.name) + return llvm::hash_combine(key.name, key.kind); + return llvm::hash_combine(key.members, key.incomplete, key.packed, key.kind, + key.ast); } static StructTypeStorage *construct(TypeStorageAllocator &allocator, @@ -74,6 +80,32 @@ struct StructTypeStorage : public TypeStorage { StructTypeStorage(allocator.copyInto(key.members), key.name, key.incomplete, key.packed, key.kind, key.ast); } + + /// 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. + LogicalResult mutate(TypeStorageAllocator &allocator, ArrayRef members, + bool packed, ASTRecordDeclInterface ast) { + // Anonymous structs cannot mutate. + if (!name) + return failure(); + + // Mutation of complete structs are allowed if they change nothing. + if (!incomplete) + return mlir::success((this->members == members) && + (this->packed == packed) && (this->ast == ast)); + + // Mutate incomplete struct. + this->members = allocator.copyInto(members); + this->packed = packed; + this->ast = ast; + + incomplete = false; + return success(); + } }; } // namespace detail diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index bbd3a27d78f3..b11fa0d642df 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -435,6 +435,9 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { } /// Get a CIR named struct type. + /// + /// If a struct already exists and is complete, but the client tries to fetch + /// it with a different set of attributes, this method will crash. mlir::cir::StructType getCompleteStructTy(llvm::ArrayRef members, llvm::StringRef name, bool packed, const clang::RecordDecl *ast) { @@ -445,8 +448,16 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { astAttr = getAttr(ast); kind = getRecordKind(ast->getTagKind()); } - return getType(members, nameAttr, packed, kind, - astAttr); + + // Create or get the struct. + auto type = getType(members, nameAttr, packed, kind, + astAttr); + + // Complete an incomplete struct or ensure the existing complete struct + // matches the requested attributes. + type.complete(members, packed, astAttr); + + return type; } // @@ -689,7 +700,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy { auto flag = getBool(val, loc); return create(loc, flag, dst); } - }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp index 9e75af34c333..4d23d5c86a8c 100644 --- a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp @@ -596,7 +596,7 @@ std::unique_ptr CIRGenTypes::computeRecordLayout(const RecordDecl *D, mlir::cir::StructType *Ty) { CIRRecordLowering builder(*this, D, /*packed=*/false); - + assert(Ty->isIncomplete() && "recomputing record layout?"); builder.lower(/*nonVirtualBaseType=*/false); // If we're in C++, compute the base subobject type. diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 4a6829f45402..2ef6a371a6d6 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -128,7 +128,9 @@ Type StructType::getLargestMember(const ::mlir::DataLayout &dataLayout) const { } Type StructType::parse(mlir::AsmParser &parser) { + FailureOr cyclicParseGuard; const auto loc = parser.getCurrentLocation(); + const auto eLoc = parser.getEncodedSourceLoc(loc); bool packed = false; RecordKind kind; auto *context = parser.getContext(); @@ -152,6 +154,26 @@ Type StructType::parse(mlir::AsmParser &parser) { 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 {}; + } + } + if (parser.parseOptionalKeyword("packed").succeeded()) packed = true; @@ -176,14 +198,17 @@ Type StructType::parse(mlir::AsmParser &parser) { if (parser.parseGreater()) return {}; - // Try to create the proper type. - mlir::Type type = {}; + // Try to create the proper record type. ArrayRef membersRef(members); // Needed for template deduction. - const auto eLoc = parser.getEncodedSourceLoc(loc); + mlir::Type type = {}; if (name && incomplete) { // Identified & incomplete type = getChecked(eLoc, context, name, kind); } else if (name && !incomplete) { // Identified & complete type = getChecked(eLoc, context, membersRef, name, packed, kind); + // If the record has a self-reference, its type already exists in a + // incomplete state. In this case, we must complete it. + if (type.cast().isIncomplete()) + type.cast().complete(membersRef, packed, ast); } else if (!name && !incomplete) { // anonymous & complete type = getChecked(eLoc, context, membersRef, packed, kind); } else { // anonymous & incomplete @@ -195,6 +220,7 @@ Type StructType::parse(mlir::AsmParser &parser) { } void StructType::print(mlir::AsmPrinter &printer) const { + FailureOr cyclicPrintGuard; printer << '<'; switch (getKind()) { @@ -210,7 +236,17 @@ void StructType::print(mlir::AsmPrinter &printer) const { } if (getName()) - printer << 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 (getPacked()) printer << "packed "; @@ -308,6 +344,12 @@ mlir::cir::StructType::RecordKind StructType::getKind() const { ASTRecordDeclInterface StructType::getAst() const { return getImpl()->ast; } +void StructType::complete(ArrayRef members, bool packed, + ASTRecordDeclInterface ast) { + if (mutate(members, packed, ast).failed()) + llvm_unreachable("failed to complete struct"); +} + //===----------------------------------------------------------------------===// // Data Layout information for types //===----------------------------------------------------------------------===// diff --git a/clang/test/CIR/CodeGen/agg-init.cpp b/clang/test/CIR/CodeGen/agg-init.cpp index 7366289c86eb..9bcabc3c04b6 100644 --- a/clang/test/CIR/CodeGen/agg-init.cpp +++ b/clang/test/CIR/CodeGen/agg-init.cpp @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++17 -fclangir-enable -Wno-unused-value -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -// CHECK: !ty_22yep_22 = !cir.struct +// CHECK: !ty_22yep_22 = !cir.struct, !cir.int}> typedef enum xxy_ { xxy_Low = 0, diff --git a/clang/test/CIR/CodeGen/atomic.cpp b/clang/test/CIR/CodeGen/atomic.cpp index 0282462449c4..35a0d678e594 100644 --- a/clang/test/CIR/CodeGen/atomic.cpp +++ b/clang/test/CIR/CodeGen/atomic.cpp @@ -7,4 +7,4 @@ typedef struct _a { void m() { at y; } -// CHECK: !ty_22_a22 = !cir.struct \ No newline at end of file +// CHECK: !ty_22_a22 = !cir.struct}> \ No newline at end of file diff --git a/clang/test/CIR/CodeGen/bitfields.c b/clang/test/CIR/CodeGen/bitfields.c index fbf10e995812..7ce2d29e8179 100644 --- a/clang/test/CIR/CodeGen/bitfields.c +++ b/clang/test/CIR/CodeGen/bitfields.c @@ -27,10 +27,10 @@ typedef struct { int a : 3; // one bitfield with size < 8 unsigned b; } T; -// CHECK: !ty_22S22 = !cir.struct -// CHECK: !ty_22T22 = !cir.struct -// CHECK: !ty_22anon2E122 = !cir.struct -// CHECK: !ty_22__long22 = !cir.struct}> +// CHECK: !ty_22S22 = !cir.struct, !cir.int, !cir.int, !cir.int}> +// CHECK: !ty_22T22 = !cir.struct, !cir.int} #cir.record.decl.ast> +// CHECK: !ty_22anon2E122 = !cir.struct} #cir.record.decl.ast> +// CHECK: !ty_22__long22 = !cir.struct} #cir.record.decl.ast>, !cir.int, !cir.ptr>}> // CHECK: cir.func {{.*@store_field}} // CHECK: [[TMP0:%.*]] = cir.alloca !ty_22S22, cir.ptr diff --git a/clang/test/CIR/CodeGen/bitfields.cpp b/clang/test/CIR/CodeGen/bitfields.cpp index 5cba89b0abbb..cdf82d493ab9 100644 --- a/clang/test/CIR/CodeGen/bitfields.cpp +++ b/clang/test/CIR/CodeGen/bitfields.cpp @@ -27,11 +27,10 @@ typedef struct { int a : 3; // one bitfield with size < 8 unsigned b; } T; - -// CHECK: !ty_22S22 = !cir.struct -// CHECK: !ty_22T22 = !cir.struct -// CHECK: !ty_22anon2E122 = !cir.struct -// CHECK: !ty_22__long22 = !cir.struct}> +// CHECK: !ty_22S22 = !cir.struct, !cir.int, !cir.int, !cir.int}> +// CHECK: !ty_22T22 = !cir.struct, !cir.int} #cir.record.decl.ast> +// CHECK: !ty_22anon2E122 = !cir.struct} #cir.record.decl.ast> +// CHECK: !ty_22__long22 = !cir.struct} #cir.record.decl.ast>, !cir.int, !cir.ptr>}> // CHECK: cir.func @_Z11store_field // CHECK: [[TMP0:%.*]] = cir.alloca !ty_22S22, cir.ptr , diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp index c5e0deb23710..91f2ab6fcda4 100644 --- a/clang/test/CIR/CodeGen/coro-task.cpp +++ b/clang/test/CIR/CodeGen/coro-task.cpp @@ -126,13 +126,13 @@ co_invoke_fn co_invoke; }} // namespace folly::coro -// CHECK-DAG: ![[IntTask:.*]] = !cir.struct" {!u8i}> -// CHECK-DAG: ![[VoidTask:.*]] = !cir.struct" {!u8i}> -// CHECK-DAG: ![[VoidPromisse:.*]] = !cir.struct::promise_type" {!u8i}> -// CHECK-DAG: ![[CoroHandleVoid:.*]] = !cir.struct" {!u8i}> -// CHECK-DAG: ![[CoroHandlePromise:ty_.*]] = !cir.struct::promise_type>" {!u8i}> -// CHECK-DAG: ![[StdString:.*]] = !cir.struct -// CHECK-DAG: ![[SuspendAlways:.*]] = !cir.struct +// CHECK-DAG: ![[IntTask:.*]] = !cir.struct" {!cir.int}> +// CHECK-DAG: ![[VoidTask:.*]] = !cir.struct" {!cir.int}> +// CHECK-DAG: ![[VoidPromisse:.*]] = !cir.struct::promise_type" {!cir.int}> +// CHECK-DAG: ![[CoroHandleVoid:.*]] = !cir.struct" {!cir.int}> +// CHECK-DAG: ![[CoroHandlePromise:ty_.*]] = !cir.struct::promise_type>" {!cir.int}> +// CHECK-DAG: ![[StdString:.*]] = !cir.struct}> +// CHECK-DAG: ![[SuspendAlways:.*]] = !cir.struct}> // CHECK: module {{.*}} { // CHECK-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !ty_22folly3A3Acoro3A3Aco_invoke_fn22 diff --git a/clang/test/CIR/CodeGen/ctor.cpp b/clang/test/CIR/CodeGen/ctor.cpp index 05cd694f9d42..29c8da84ccaa 100644 --- a/clang/test/CIR/CodeGen/ctor.cpp +++ b/clang/test/CIR/CodeGen/ctor.cpp @@ -11,7 +11,7 @@ void baz() { Struk s; } -// CHECK: !ty_22Struk22 = !cir.struct +// CHECK: !ty_22Struk22 = !cir.struct}> // CHECK: cir.func linkonce_odr @_ZN5StrukC2Ev(%arg0: !cir.ptr // CHECK-NEXT: %0 = cir.alloca !cir.ptr, cir.ptr >, ["this", init] {alignment = 8 : i64} diff --git a/clang/test/CIR/CodeGen/derived-to-base.cpp b/clang/test/CIR/CodeGen/derived-to-base.cpp index ee282693a224..3fa2e245854b 100644 --- a/clang/test/CIR/CodeGen/derived-to-base.cpp +++ b/clang/test/CIR/CodeGen/derived-to-base.cpp @@ -75,8 +75,8 @@ void C3::Layer::Initialize() { } } -// CHECK-DAG: !ty_22C23A3ALayer22 = !cir.struct -// CHECK-DAG: !ty_22C33A3ALayer22 = !cir.struct>>} #cir.record.decl.ast> +// CHECK: ![[ClassA:ty_.*]] = !cir.struct ()>>>} #cir.record.decl.ast> // Class B -// CHECK: ![[ClassB:ty_.*]] = !cir.struct +// CHECK: ![[ClassB:ty_.*]] = !cir.struct ()>>>} #cir.record.decl.ast>}> // CHECK: cir.func @_Z4bluev() // CHECK: %0 = cir.alloca !ty_22PSEvent22, cir.ptr , ["p", init] {alignment = 8 : i64} diff --git a/clang/test/CIR/CodeGen/forward-decls.cpp b/clang/test/CIR/CodeGen/forward-decls.cpp new file mode 100644 index 000000000000..37a363adc1e0 --- /dev/null +++ b/clang/test/CIR/CodeGen/forward-decls.cpp @@ -0,0 +1,125 @@ +// RUN: split-file %s %t + + +//--- incomplete_struct + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %t/incomplete_struct -o %t/incomplete_struct.cir +// RUN: FileCheck %s --input-file=%t/incomplete_struct.cir --check-prefix=CHECK1 + +// Forward declaration of the record is never defined, so it is created as +// an incomplete struct in CIR and will remain as such. + +// CHECK1: ![[INC_STRUCT:.+]] = !cir.struct +struct IncompleteStruct; +// CHECK1: testIncompleteStruct(%arg0: !cir.ptr +void testIncompleteStruct(struct IncompleteStruct *s) {}; + + + +//--- mutated_struct + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %t/mutated_struct -o %t/mutated_struct.cir +// RUN: FileCheck %s --input-file=%t/mutated_struct.cir --check-prefix=CHECK2 + +// Foward declaration of the struct is followed by usage, then definition. +// This means it will initially be created as incomplete, then completed. + +// CHECK2: ![[COMPLETE:.+]] = !cir.struct} #cir.record.decl.ast> +// CHECK2: testForwardDeclaredStruct(%arg0: !cir.ptr +struct ForwardDeclaredStruct; +void testForwardDeclaredStruct(struct ForwardDeclaredStruct *fds) {}; +struct ForwardDeclaredStruct { + int testVal; +}; + + + +//--- recursive_struct + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %t/recursive_struct -o %t/recursive_struct.cir +// RUN: FileCheck --check-prefix=CHECK3 --input-file=%t/recursive_struct.cir %s + +// Struct is initially forward declared since the self-reference is generated +// first. Then, once the type is fully generated, it is completed. + +// CHECK3: ![[STRUCT:.+]] = !cir.struct, !cir.ptr>} #cir.record.decl.ast> +struct RecursiveStruct { + int value; + struct RecursiveStruct *next; +}; +// CHECK3: testRecursiveStruct(%arg0: !cir.ptr +void testRecursiveStruct(struct RecursiveStruct *arg) { + // CHECK3: %[[#NEXT:]] = cir.get_member %{{.+}}[1] {name = "next"} : !cir.ptr -> !cir.ptr> + // CHECK3: %[[#DEREF:]] = cir.load %[[#NEXT]] : cir.ptr >, !cir.ptr + // CHECK3: cir.get_member %[[#DEREF]][0] {name = "value"} : !cir.ptr -> !cir.ptr + arg->next->value; +} + + + +//--- indirect_recursive_struct + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %t/indirect_recursive_struct -o %t/indirect_recursive_struct.cir +// RUN: FileCheck --check-prefix=CHECK4 --input-file=%t/indirect_recursive_struct.cir %s + +// Node B refers to A, and vice-versa, so a forward declaration is used to +// ensure the classes can be defined. Since types alias are not yet supported +// in recursive type, each struct is expanded until there are no more recursive +// types, or all the recursive types are self references. + +// CHECK4: ![[B:.+]] = !cir.struct, !cir.ptr, !cir.ptr>} +// CHECK4: ![[A:.+]] = !cir.struct, !cir.ptr, !cir.ptr>} +struct StructNodeB; +struct StructNodeA { + int value; + struct StructNodeB *next; +}; +struct StructNodeB { + int value; + struct StructNodeA *next; +}; + +void testIndirectSelfReference(struct StructNodeA arg) { + // CHECK4: %[[#V1:]] = cir.get_member %{{.+}}[1] {name = "next"} : !cir.ptr -> !cir.ptr> + // CHECK4: %[[#V2:]] = cir.load %[[#V1]] : cir.ptr >, !cir.ptr + // CHECK4: %[[#V3:]] = cir.get_member %[[#V2]][1] {name = "next"} : !cir.ptr -> !cir.ptr> + // CHECK4: %[[#V4:]] = cir.load %[[#V3]] : cir.ptr >, !cir.ptr + // CHECK4: cir.get_member %[[#V4]][0] {name = "value"} : !cir.ptr -> !cir.ptr + arg.next->next->value; +} + + + +//--- complex_struct + +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %t/complex_struct -o %t/complex_struct.cir +// RUN: FileCheck --check-prefix=CHECK5 --input-file=%t/complex_struct.cir %s + +// A sizeable complex struct just to double check that stuff is working. + +// CHECK5: !cir.struct, !cir.struct>, !cir.struct>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct>, !cir.struct>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>>} #cir.record.decl.ast> +// CHECK5: !cir.struct>} #cir.record.decl.ast>, !cir.struct>, !cir.struct, !cir.struct>, !cir.struct>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>>, !cir.ptr>, !cir.struct, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.struct>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>>, !cir.ptr>} #cir.record.decl.ast> +// CHECK5: !cir.struct>, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.struct} #cir.record.decl.ast>} #cir.record.decl.ast>>} #cir.record.decl.ast> +// CHECK5: !cir.struct>} #cir.record.decl.ast>, !cir.struct>, !cir.struct>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>} #cir.record.decl.ast>>, !cir.struct>, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>} #cir.record.decl.ast>} #cir.record.decl.ast> +// CHECK5: !cir.struct>, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct>} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>>, !cir.struct>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast> +// CHECK5: !cir.struct>} #cir.record.decl.ast>, !cir.struct>, !cir.struct>, !cir.ptr>, !cir.ptr>} #cir.record.decl.ast>, !cir.struct>, !cir.struct>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast>} #cir.record.decl.ast> +struct A { + struct { + struct A *a1; + }; + struct B { + struct B *b1; + struct C { + struct A *a2; + struct B *b2; + struct C *c1; + } c; + union { + struct A *a2; + struct { + struct B *b3; + }; + } u; + } b; +}; +void test(struct A *a){}; diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp index 96432a42771c..a5187fa17869 100644 --- a/clang/test/CIR/CodeGen/lambda.cpp +++ b/clang/test/CIR/CodeGen/lambda.cpp @@ -6,7 +6,7 @@ void fn() { a(); } -// CHECK: !ty_22anon2E222 = !cir.struct +// CHECK: !ty_22anon2E222 = !cir.struct}> // CHECK-DAG: module // CHECK: cir.func lambda internal private @_ZZ2fnvENK3$_0clEv diff --git a/clang/test/CIR/CodeGen/move.cpp b/clang/test/CIR/CodeGen/move.cpp index 1be0869d6166..8b1a20b28999 100644 --- a/clang/test/CIR/CodeGen/move.cpp +++ b/clang/test/CIR/CodeGen/move.cpp @@ -16,7 +16,7 @@ struct string { } // std namespace -// CHECK: ![[StdString:ty_.*]] = !cir.struct +// CHECK: ![[StdString:ty_.*]] = !cir.struct}> std::string getstr(); void emplace(std::string &&s); diff --git a/clang/test/CIR/CodeGen/nrvo.cpp b/clang/test/CIR/CodeGen/nrvo.cpp index 1431753eb9e2..d55c806762dc 100644 --- a/clang/test/CIR/CodeGen/nrvo.cpp +++ b/clang/test/CIR/CodeGen/nrvo.cpp @@ -9,7 +9,7 @@ std::vector test_nrvo() { return result; } -// CHECK: ![[VEC:.*]] = !cir.struct" {!cir.ptr>, !cir.ptr>, !cir.ptr>}> +// CHECK: ![[VEC:.*]] = !cir.struct" {!cir.ptr>>, !cir.ptr>>, !cir.ptr>>}> // CHECK: cir.func @_Z9test_nrvov() -> ![[VEC]] // CHECK: %0 = cir.alloca ![[VEC]], cir.ptr , ["__retval", init] {alignment = 8 : i64} diff --git a/clang/test/CIR/CodeGen/rangefor.cpp b/clang/test/CIR/CodeGen/rangefor.cpp index 403cc6fbd6f4..d1e16503ae1f 100644 --- a/clang/test/CIR/CodeGen/rangefor.cpp +++ b/clang/test/CIR/CodeGen/rangefor.cpp @@ -21,9 +21,9 @@ void init(unsigned numImages) { } } -// CHECK-DAG: !ty_22triple22 = !cir.struct, !u32i}> -// CHECK-DAG: ![[VEC:.*]] = !cir.struct" {!cir.ptr, !cir.ptr, !cir.ptr}> -// CHECK-DAG: ![[VEC_IT:.*]] = !cir.struct" {!cir.ptr}> +// CHECK-DAG: !ty_22triple22 = !cir.struct, !cir.ptr, !cir.int}> +// CHECK-DAG: ![[VEC:.*]] = !cir.struct" {!cir.ptr, !cir.ptr, !cir.int}>>, !cir.ptr, !cir.ptr, !cir.int}>>, !cir.ptr, !cir.ptr, !cir.int}>>}> +// CHECK-DAG: ![[VEC_IT:.*]] = !cir.struct" {!cir.ptr, !cir.ptr, !cir.int}> // CHECK: cir.func @_Z4initj(%arg0: !u32i // CHECK: %0 = cir.alloca !u32i, cir.ptr , ["numImages", init] {alignment = 4 : i64} diff --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c index d19335f0e3c5..06450b0e9ea7 100644 --- a/clang/test/CIR/CodeGen/struct.c +++ b/clang/test/CIR/CodeGen/struct.c @@ -22,10 +22,9 @@ void baz(void) { struct Foo f; } -// CHECK-DAG: !ty_22Node22 = !cir.struct -// CHECK-DAG: !ty_22Node221 = !cir.struct} #cir.record.decl.ast> -// CHECK-DAG: !ty_22Bar22 = !cir.struct -// CHECK-DAG: !ty_22Foo22 = !cir.struct +// CHECK-DAG: !ty_22Node22 = !cir.struct>} #cir.record.decl.ast> +// CHECK-DAG: !ty_22Bar22 = !cir.struct, !cir.int}> +// CHECK-DAG: !ty_22Foo22 = !cir.struct, !cir.int, !cir.struct, !cir.int}>}> // CHECK-DAG: module {{.*}} { // CHECK: cir.func @baz() // CHECK-NEXT: %0 = cir.alloca !ty_22Bar22, cir.ptr , ["b"] {alignment = 4 : i64} @@ -96,7 +95,7 @@ void local_decl(void) { } // CHECK-DAG: cir.func @useRecursiveType -// CHECK-DAG: cir.get_member {{%.}}[0] {name = "next"} : !cir.ptr -> !cir.ptr> +// CHECK-DAG: cir.get_member {{%.}}[0] {name = "next"} : !cir.ptr -> !cir.ptr> void useRecursiveType(NodeStru* a) { a->next = 0; } diff --git a/clang/test/CIR/CodeGen/struct.cpp b/clang/test/CIR/CodeGen/struct.cpp index 07c5e7f70064..559aecd0ebff 100644 --- a/clang/test/CIR/CodeGen/struct.cpp +++ b/clang/test/CIR/CodeGen/struct.cpp @@ -26,13 +26,13 @@ void baz() { struct incomplete; void yoyo(incomplete *i) {} -// CHECK-DAG-DAG: !ty_22incomplete22 = !cir.struct +// CHECK-DAG: !ty_22incomplete22 = !cir.struct, !cir.int}> -// CHECK-DAG: !ty_22Foo22 = !cir.struct -// CHECK-DAG: !ty_22Mandalore22 = !cir.struct, !s32i} #cir.record.decl.ast> -// CHECK-DAG: !ty_22Adv22 = !cir.struct -// CHECK-DAG: !ty_22Entry22 = !cir.struct, !cir.ptr)>>}> +// CHECK-DAG: !ty_22Foo22 = !cir.struct, !cir.int, !cir.struct, !cir.int}>}> +// CHECK-DAG: !ty_22Mandalore22 = !cir.struct, !cir.ptr, !cir.int} #cir.record.decl.ast> +// CHECK-DAG: !ty_22Adv22 = !cir.struct, !cir.ptr, !cir.int} #cir.record.decl.ast>}> +// CHECK-DAG: !ty_22Entry22 = !cir.struct (!cir.int, !cir.ptr>, !cir.ptr)>>}> // CHECK: cir.func linkonce_odr @_ZN3Bar6methodEv(%arg0: !cir.ptr // CHECK-NEXT: %0 = cir.alloca !cir.ptr, cir.ptr >, ["this", init] {alignment = 8 : i64} diff --git a/clang/test/CIR/CodeGen/union.cpp b/clang/test/CIR/CodeGen/union.cpp index 0bf04949f583..e3751a96c644 100644 --- a/clang/test/CIR/CodeGen/union.cpp +++ b/clang/test/CIR/CodeGen/union.cpp @@ -6,14 +6,15 @@ typedef union { yolo y; struct { int lifecnt; }; } yolm; typedef union { yolo y; struct { int *lifecnt; int genpad; }; } yolm2; typedef union { yolo y; struct { bool life; int genpad; }; } yolm3; -// CHECK-DAG: !ty_22U23A3ADummy22 = !cir.struct -// CHECK-DAG: !ty_22anon2E522 = !cir.struct -// CHECK-DAG: !ty_22yolo22 = !cir.struct -// CHECK-DAG: !ty_22anon2E322 = !cir.struct, !s32i} #cir.record.decl.ast> +// CHECK-DAG: !ty_22U23A3ADummy22 = !cir.struct, f32} #cir.record.decl.ast> +// CHECK-DAG: !ty_22anon2E522 = !cir.struct} #cir.record.decl.ast> +// CHECK-DAG: !ty_22anon2E122 = !cir.struct} #cir.record.decl.ast> +// CHECK-DAG: !ty_22yolo22 = !cir.struct} #cir.record.decl.ast> +// CHECK-DAG: !ty_22anon2E322 = !cir.struct>, !cir.int} #cir.record.decl.ast> -// CHECK-DAG: !ty_22yolm22 = !cir.struct -// CHECK-DAG: !ty_22yolm322 = !cir.struct -// CHECK-DAG: !ty_22yolm222 = !cir.struct +// CHECK-DAG: !ty_22yolm22 = !cir.struct} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>}> +// CHECK-DAG: !ty_22yolm322 = !cir.struct} #cir.record.decl.ast>, !cir.struct} #cir.record.decl.ast>}> +// CHECK-DAG: !ty_22yolm222 = !cir.struct} #cir.record.decl.ast>, !cir.struct>, !cir.int} #cir.record.decl.ast>}> // Should generate a union type with all members preserved. union U { @@ -23,7 +24,7 @@ union U { float f; double d; }; -// CHECK-DAG: !ty_22U22 = !cir.struct +// CHECK-DAG: !ty_22U22 = !cir.struct, !cir.int, f32, f64}> // Should generate unions with complex members. union U2 { @@ -33,14 +34,14 @@ union U2 { float f; } s; } u2; -// CHECK-DAG: !cir.struct +// CHECK-DAG: !cir.struct, f32} #cir.record.decl.ast>} #cir.record.decl.ast> // Should genereate unions without padding. union U3 { short b; U u; } u3; -// CHECK-DAG: !ty_22U322 = !cir.struct +// CHECK-DAG: !ty_22U322 = !cir.struct, !cir.struct, !cir.int, f32, f64}>} #cir.record.decl.ast> void m() { yolm q; diff --git a/clang/test/CIR/CodeGen/vtable-rtti.cpp b/clang/test/CIR/CodeGen/vtable-rtti.cpp index e1e0cc437d42..14923ba49945 100644 --- a/clang/test/CIR/CodeGen/vtable-rtti.cpp +++ b/clang/test/CIR/CodeGen/vtable-rtti.cpp @@ -18,16 +18,16 @@ class B : public A }; // Type info B. -// CHECK: ![[TypeInfoB:ty_.*]] = !cir.struct, !cir.ptr, !cir.ptr}> +// CHECK: ![[TypeInfoB:ty_.*]] = !cir.struct>, !cir.ptr>, !cir.ptr>}> // vtable for A type -// CHECK: ![[VTableTypeA:ty_.*]] = !cir.struct x 5>}> +// CHECK: ![[VTableTypeA:ty_.*]] = !cir.struct> x 5>}> // Class A -// CHECK: ![[ClassA:ty_.*]] = !cir.struct>>} #cir.record.decl.ast> +// CHECK: ![[ClassA:ty_.*]] = !cir.struct ()>>>} #cir.record.decl.ast> // Class B -// CHECK: ![[ClassB:ty_.*]] = !cir.struct +// CHECK: ![[ClassB:ty_.*]] = !cir.struct ()>>>} #cir.record.decl.ast>}> // B ctor => @B::B() // Calls @A::A() and initialize __vptr with address of B's vtable. diff --git a/clang/test/CIR/IR/invalid.cir b/clang/test/CIR/IR/invalid.cir index 9f8e06c8c2ca..e40d2d4aab96 100644 --- a/clang/test/CIR/IR/invalid.cir +++ b/clang/test/CIR/IR/invalid.cir @@ -560,3 +560,13 @@ module { !u16i = !cir.int // expected-error@+1 {{identified structs cannot have an empty name}} !struct = !cir.struct + +// ----- + +// expected-error@+1 {{invalid self-reference within record}} +!struct = !cir.struct}> + +// ----- + +// expected-error@+1 {{record already defined}} +!struct = !cir.struct}> diff --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir index 45f31014f159..65a319538d1a 100644 --- a/clang/test/CIR/IR/struct.cir +++ b/clang/test/CIR/IR/struct.cir @@ -13,7 +13,16 @@ !ty_22S22 = !cir.struct !ty_22S122 = !cir.struct +// Test recursive struct parsing/printing. +!ty_22Node22 = !cir.struct>} #cir.record.decl.ast> +// CHECK-DAG: !cir.struct>} #cir.record.decl.ast> + module { + // Dummy function to use types and force them to be printed. + cir.func @useTypes(%arg0: !ty_22Node22) { + cir.return + } + cir.func @structs() { %0 = cir.alloca !cir.ptr>, cir.ptr >>, ["s", init] %1 = cir.alloca !cir.ptr>, cir.ptr >>, ["i", init]