From f6488354387100fe37456f1cb4f24f38832e5612 Mon Sep 17 00:00:00 2001 From: Vinicius Couto Espindola Date: Wed, 1 Nov 2023 21:20:57 -0300 Subject: [PATCH] [CIR][CIRGen] Support mutable and recursive named records This allows a named StructType to be mutated after it has been created, if it is identified and incomplete. The motivation for this is to improve the codegen of CIR in certain scenarios where an incomplete type is used and later completed. These usually leave the IR in an inconsistent state, where there are two records types with the same identifier but different definitions (one complete the other incomplete). For example: ```c++ struct Node { Node *next; }; void test(struct Node n) {} ``` Generates: ```mlir !temp_struct = !cir.struct !full_struct = !cir.struct}> ``` To generate the `Node` struct type, its members must be created first. However, the `next` member is a recursive reference, so it can only be completed after its parent. This generates a temporary incomplete definition of the `Node` type that remains in the code even after the type to which it refers is completed. As a consequence, accessing the `next` member of a `Node` value fetches the old incomplete version of the type which affects CIR's type-checking capabilities. This patch ensures that, once the parent is fully visited, the `next` member can be completed in place, automatically updating any references to it at a low cost. To represent recursive types, the StructType now is equipped with self-references. These are represented by a `cir.struct` type with just the name of the parent struct that it refers to. The same snippet of code will not generate the following CIR IR: ```mlir !full_struct = !cir.struct>}> ``` Summary of the changes made: - Named records are now uniquely identified by their name. An attempt to create a new record with the same will fail. - Anonymous records are uniquely identified by members and other relevant attributes. - StructType has a new `mutate` method that allows it to be mutated after it has been created. Each type can only be mutated if it is identified and incomplete, rendering further changes impossible. - When building a new name StructType, the builder will try to first create, then complete the type, ensuring that: - Inexistent types are created - Existing incomplete types are completed - Existing complete types with matching attributes are reused - Existing complete types with different attributes raise errors - StructType now uses the CyclicParser/Printer guard to avoid infinite recursion and identify when it should print/parse a self-reference. [ghstack-poisoned] --- clang/include/clang/CIR/Dialect/IR/CIRTypes.h | 7 +- .../clang/CIR/Dialect/IR/CIRTypesDetails.h | 36 +++++++- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 16 +++- .../CIR/CodeGen/CIRRecordLayoutBuilder.cpp | 2 +- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 50 ++++++++++- clang/test/CIR/CodeGen/agg-init.cpp | 2 +- clang/test/CIR/CodeGen/atomic.cpp | 2 +- clang/test/CIR/CodeGen/bitfields.c | 8 +- clang/test/CIR/CodeGen/bitfields.cpp | 9 +- clang/test/CIR/CodeGen/coro-task.cpp | 14 +-- clang/test/CIR/CodeGen/ctor.cpp | 2 +- clang/test/CIR/CodeGen/derived-to-base.cpp | 4 +- clang/test/CIR/CodeGen/dtors.cpp | 4 +- clang/test/CIR/CodeGen/forward-decls.cpp | 89 +++++++++++++++++++ clang/test/CIR/CodeGen/lambda.cpp | 2 +- clang/test/CIR/CodeGen/move.cpp | 2 +- clang/test/CIR/CodeGen/nrvo.cpp | 2 +- clang/test/CIR/CodeGen/rangefor.cpp | 6 +- clang/test/CIR/CodeGen/struct.c | 9 +- clang/test/CIR/CodeGen/struct.cpp | 12 +-- clang/test/CIR/CodeGen/union.cpp | 21 ++--- clang/test/CIR/CodeGen/vtable-rtti.cpp | 8 +- clang/test/CIR/IR/invalid.cir | 10 +++ clang/test/CIR/IR/struct.cir | 9 ++ 24 files changed, 261 insertions(+), 65 deletions(-) create mode 100644 clang/test/CIR/CodeGen/forward-decls.cpp diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h index db5f3e258d04..41471804b1c2 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h +++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h @@ -41,7 +41,8 @@ struct StructTypeStorage; class StructType : public Type::TypeBase { + DataLayoutTypeInterface::Trait, + TypeTrait::IsMutable> { public: using Base::Base; using Base::getChecked; @@ -118,6 +119,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..5bc86b3f6bbf --- /dev/null +++ b/clang/test/CIR/CodeGen/forward-decls.cpp @@ -0,0 +1,89 @@ +// 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 + +// Foward declaration of the record is never defeined, so it is defined +// as incomplete 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; +} 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]