Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion clang/include/clang/CIR/Dialect/IR/CIRTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 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<struct "Node" {!cir.ptr<!cir.struct<struct
/// "Node">>}>
/// ```
class StructType
: public Type::TypeBase<StructType, Type, detail::StructTypeStorage,
DataLayoutTypeInterface::Trait> {
DataLayoutTypeInterface::Trait,
TypeTrait::IsMutable> {
// FIXME(cir): migrate this type to Tablegen once mutable types are supported.
public:
using Base::Base;
Expand Down Expand Up @@ -121,6 +155,10 @@ class StructType
return getKindAsStr() + "." + getName().getValue().str();
}

/// Complete the struct type by mutating its members and attributes.
void complete(ArrayRef<Type> members, bool packed,
ASTRecordDeclInterface ast = {});

/// DataLayoutTypeInterface methods.
unsigned getTypeSizeInBits(const DataLayout &dataLayout,
DataLayoutEntryListRef params) const;
Expand Down
36 changes: 34 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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<Type> 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
Expand Down
16 changes: 13 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::Type> members,
llvm::StringRef name, bool packed,
const clang::RecordDecl *ast) {
Expand All @@ -445,8 +448,16 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
astAttr = getAttr<mlir::cir::ASTRecordDeclAttr>(ast);
kind = getRecordKind(ast->getTagKind());
}
return getType<mlir::cir::StructType>(members, nameAttr, packed, kind,
astAttr);

// Create or get the struct.
auto type = getType<mlir::cir::StructType>(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;
}

//
Expand Down Expand Up @@ -689,7 +700,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
auto flag = getBool(val, loc);
return create<mlir::cir::StoreOp>(loc, flag, dst);
}

};

} // namespace cir
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ std::unique_ptr<CIRGenRecordLayout>
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.
Expand Down
50 changes: 46 additions & 4 deletions clang/lib/CIR/Dialect/IR/CIRTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ Type StructType::getLargestMember(const ::mlir::DataLayout &dataLayout) const {
}

Type StructType::parse(mlir::AsmParser &parser) {
FailureOr<AsmParser::CyclicParseReset> cyclicParseGuard;
const auto loc = parser.getCurrentLocation();
const auto eLoc = parser.getEncodedSourceLoc(loc);
bool packed = false;
RecordKind kind;
auto *context = parser.getContext();
Expand All @@ -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;

Expand All @@ -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<mlir::Type> 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<StructType>().isIncomplete())
type.cast<StructType>().complete(membersRef, packed, ast);
} else if (!name && !incomplete) { // anonymous & complete
type = getChecked(eLoc, context, membersRef, packed, kind);
} else { // anonymous & incomplete
Expand All @@ -195,6 +220,7 @@ Type StructType::parse(mlir::AsmParser &parser) {
}

void StructType::print(mlir::AsmPrinter &printer) const {
FailureOr<AsmPrinter::CyclicPrintReset> cyclicPrintGuard;
printer << '<';

switch (getKind()) {
Expand All @@ -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 ";
Expand Down Expand Up @@ -308,6 +344,12 @@ mlir::cir::StructType::RecordKind StructType::getKind() const {

ASTRecordDeclInterface StructType::getAst() const { return getImpl()->ast; }

void StructType::complete(ArrayRef<Type> members, bool packed,
ASTRecordDeclInterface ast) {
if (mutate(members, packed, ast).failed())
llvm_unreachable("failed to complete struct");
}

//===----------------------------------------------------------------------===//
// Data Layout information for types
//===----------------------------------------------------------------------===//
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/agg-init.cpp
Original file line number Diff line number Diff line change
@@ -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<struct "yep_" {!u32i, !u32i}>
// CHECK: !ty_22yep_22 = !cir.struct<struct "yep_" {!cir.int<u, 32>, !cir.int<u, 32>}>

typedef enum xxy_ {
xxy_Low = 0,
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/atomic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ typedef struct _a {

void m() { at y; }

// CHECK: !ty_22_a22 = !cir.struct<struct "_a" {!s32i}>
// CHECK: !ty_22_a22 = !cir.struct<struct "_a" {!cir.int<s, 32>}>
8 changes: 4 additions & 4 deletions clang/test/CIR/CodeGen/bitfields.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ typedef struct {
int a : 3; // one bitfield with size < 8
unsigned b;
} T;
// CHECK: !ty_22S22 = !cir.struct<struct "S" {!u32i, !u32i, !u16i, !u32i}>
// CHECK: !ty_22T22 = !cir.struct<struct "T" {!u8i, !u32i} #cir.record.decl.ast>
// CHECK: !ty_22anon2E122 = !cir.struct<struct "anon.1" {!u32i} #cir.record.decl.ast>
// CHECK: !ty_22__long22 = !cir.struct<struct "__long" {!ty_22anon2E122, !u32i, !cir.ptr<!u32i>}>
// CHECK: !ty_22S22 = !cir.struct<struct "S" {!cir.int<u, 32>, !cir.int<u, 32>, !cir.int<u, 16>, !cir.int<u, 32>}>
// CHECK: !ty_22T22 = !cir.struct<struct "T" {!cir.int<u, 8>, !cir.int<u, 32>} #cir.record.decl.ast>
// CHECK: !ty_22anon2E122 = !cir.struct<struct "anon.1" {!cir.int<u, 32>} #cir.record.decl.ast>
// CHECK: !ty_22__long22 = !cir.struct<struct "__long" {!cir.struct<struct "anon.1" {!cir.int<u, 32>} #cir.record.decl.ast>, !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>}>

// CHECK: cir.func {{.*@store_field}}
// CHECK: [[TMP0:%.*]] = cir.alloca !ty_22S22, cir.ptr <!ty_22S22>
Expand Down
9 changes: 4 additions & 5 deletions clang/test/CIR/CodeGen/bitfields.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ typedef struct {
int a : 3; // one bitfield with size < 8
unsigned b;
} T;

// CHECK: !ty_22S22 = !cir.struct<struct "S" {!u32i, !u32i, !u16i, !u32i}>
// CHECK: !ty_22T22 = !cir.struct<struct "T" {!u8i, !u32i} #cir.record.decl.ast>
// CHECK: !ty_22anon2E122 = !cir.struct<struct "anon.1" {!u32i} #cir.record.decl.ast>
// CHECK: !ty_22__long22 = !cir.struct<struct "__long" {!ty_22anon2E122, !u32i, !cir.ptr<!u32i>}>
// CHECK: !ty_22S22 = !cir.struct<struct "S" {!cir.int<u, 32>, !cir.int<u, 32>, !cir.int<u, 16>, !cir.int<u, 32>}>
// CHECK: !ty_22T22 = !cir.struct<struct "T" {!cir.int<u, 8>, !cir.int<u, 32>} #cir.record.decl.ast>
// CHECK: !ty_22anon2E122 = !cir.struct<struct "anon.1" {!cir.int<u, 32>} #cir.record.decl.ast>
// CHECK: !ty_22__long22 = !cir.struct<struct "__long" {!cir.struct<struct "anon.1" {!cir.int<u, 32>} #cir.record.decl.ast>, !cir.int<u, 32>, !cir.ptr<!cir.int<u, 32>>}>

// CHECK: cir.func @_Z11store_field
// CHECK: [[TMP0:%.*]] = cir.alloca !ty_22S22, cir.ptr <!ty_22S22>,
Expand Down
14 changes: 7 additions & 7 deletions clang/test/CIR/CodeGen/coro-task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ co_invoke_fn co_invoke;

}} // namespace folly::coro

// CHECK-DAG: ![[IntTask:.*]] = !cir.struct<struct "folly::coro::Task<int>" {!u8i}>
// CHECK-DAG: ![[VoidTask:.*]] = !cir.struct<struct "folly::coro::Task<void>" {!u8i}>
// CHECK-DAG: ![[VoidPromisse:.*]] = !cir.struct<struct "folly::coro::Task<void>::promise_type" {!u8i}>
// CHECK-DAG: ![[CoroHandleVoid:.*]] = !cir.struct<struct "std::coroutine_handle<void>" {!u8i}>
// CHECK-DAG: ![[CoroHandlePromise:ty_.*]] = !cir.struct<struct "std::coroutine_handle<folly::coro::Task<void>::promise_type>" {!u8i}>
// CHECK-DAG: ![[StdString:.*]] = !cir.struct<struct "std::string" {!u8i}>
// CHECK-DAG: ![[SuspendAlways:.*]] = !cir.struct<struct "std::suspend_always" {!u8i}>
// CHECK-DAG: ![[IntTask:.*]] = !cir.struct<struct "folly::coro::Task<int>" {!cir.int<u, 8>}>
// CHECK-DAG: ![[VoidTask:.*]] = !cir.struct<struct "folly::coro::Task<void>" {!cir.int<u, 8>}>
// CHECK-DAG: ![[VoidPromisse:.*]] = !cir.struct<struct "folly::coro::Task<void>::promise_type" {!cir.int<u, 8>}>
// CHECK-DAG: ![[CoroHandleVoid:.*]] = !cir.struct<struct "std::coroutine_handle<void>" {!cir.int<u, 8>}>
// CHECK-DAG: ![[CoroHandlePromise:ty_.*]] = !cir.struct<struct "std::coroutine_handle<folly::coro::Task<void>::promise_type>" {!cir.int<u, 8>}>
// CHECK-DAG: ![[StdString:.*]] = !cir.struct<struct "std::string" {!cir.int<u, 8>}>
// CHECK-DAG: ![[SuspendAlways:.*]] = !cir.struct<struct "std::suspend_always" {!cir.int<u, 8>}>

// CHECK: module {{.*}} {
// CHECK-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !ty_22folly3A3Acoro3A3Aco_invoke_fn22
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CIR/CodeGen/ctor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ void baz() {
Struk s;
}

// CHECK: !ty_22Struk22 = !cir.struct<struct "Struk" {!s32i}>
// CHECK: !ty_22Struk22 = !cir.struct<struct "Struk" {!cir.int<s, 32>}>

// CHECK: cir.func linkonce_odr @_ZN5StrukC2Ev(%arg0: !cir.ptr<!ty_22Struk22>
// CHECK-NEXT: %0 = cir.alloca !cir.ptr<!ty_22Struk22>, cir.ptr <!cir.ptr<!ty_22Struk22>>, ["this", init] {alignment = 8 : i64}
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CIR/CodeGen/derived-to-base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ void C3::Layer::Initialize() {
}
}

// CHECK-DAG: !ty_22C23A3ALayer22 = !cir.struct<class "C2::Layer" {!ty_22C13A3ALayer22, !cir.ptr<!ty_22C222>
// CHECK-DAG: !ty_22C33A3ALayer22 = !cir.struct<struct "C3::Layer" {!ty_22C23A3ALayer22
// CHECK-DAG: !ty_22C23A3ALayer22 = !cir.struct<class "C2::Layer"
// CHECK-DAG: !ty_22C33A3ALayer22 = !cir.struct<struct "C3::Layer"

// CHECK: cir.func @_ZN2C35Layer10InitializeEv

Expand Down
4 changes: 2 additions & 2 deletions clang/test/CIR/CodeGen/dtors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ class B : public A
};

// Class A
// CHECK: ![[ClassA:ty_.*]] = !cir.struct<class "A" {!cir.ptr<!cir.ptr<!cir.func<!u32i ()>>>} #cir.record.decl.ast>
// CHECK: ![[ClassA:ty_.*]] = !cir.struct<class "A" {!cir.ptr<!cir.ptr<!cir.func<!cir.int<u, 32> ()>>>} #cir.record.decl.ast>

// Class B
// CHECK: ![[ClassB:ty_.*]] = !cir.struct<class "B" {![[ClassA]]}>
// CHECK: ![[ClassB:ty_.*]] = !cir.struct<class "B" {!cir.struct<class "A" {!cir.ptr<!cir.ptr<!cir.func<!cir.int<u, 32> ()>>>} #cir.record.decl.ast>}>

// CHECK: cir.func @_Z4bluev()
// CHECK: %0 = cir.alloca !ty_22PSEvent22, cir.ptr <!ty_22PSEvent22>, ["p", init] {alignment = 8 : i64}
Expand Down
Loading