Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
6 changes: 3 additions & 3 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -2920,7 +2920,7 @@ def ExtractMemberOp : CIR_Op<"extract_member", [Pure]> {
```
}];

let arguments = (ins CIR_StructType:$record, IndexAttr:$index_attr);
let arguments = (ins CIRStructType:$record, IndexAttr:$index_attr);
let results = (outs CIR_AnyType:$result);

let assemblyFormat = [{
Expand Down Expand Up @@ -2984,9 +2984,9 @@ def InsertMemberOp : CIR_Op<"insert_member",
```
}];

let arguments = (ins CIR_StructType:$record, IndexAttr:$index_attr,
let arguments = (ins CIRStructType:$record, IndexAttr:$index_attr,
CIR_AnyType:$value);
let results = (outs CIR_StructType:$result);
let results = (outs CIRStructType:$result);

let builders = [
OpBuilder<(ins "mlir::Value":$record, "uint64_t":$index,
Expand Down
162 changes: 2 additions & 160 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,173 +16,15 @@
#include "mlir/IR/BuiltinAttributes.h"
#include "mlir/IR/Types.h"
#include "mlir/Interfaces/DataLayoutInterfaces.h"
#include "clang/CIR/Interfaces/CIRFPTypeInterface.h"

#include "clang/CIR/Interfaces/ASTAttrInterfaces.h"

#include "clang/CIR/Dialect/IR/CIROpsEnums.h"

//===----------------------------------------------------------------------===//
// CIR StructType
//
// The base type for all RecordDecls.
//===----------------------------------------------------------------------===//
#include "clang/CIR/Interfaces/ASTAttrInterfaces.h"
#include "clang/CIR/Interfaces/CIRFPTypeInterface.h"

namespace cir {

namespace detail {
struct StructTypeStorage;
} // namespace detail

/// 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">>}>
/// ```
class StructType
: public mlir::Type::TypeBase<
StructType, mlir::Type, detail::StructTypeStorage,
mlir::DataLayoutTypeInterface::Trait, mlir::TypeTrait::IsMutable> {
// FIXME(cir): migrate this type to Tablegen once mutable types are supported.
public:
using Base::Base;
using Base::getChecked;
using Base::verifyInvariants;

static constexpr llvm::StringLiteral name = "cir.struct";

enum RecordKind : uint32_t { Class, Union, Struct };

/// Create an identified and complete struct type.
static StructType get(mlir::MLIRContext *context,
llvm::ArrayRef<mlir::Type> members,
mlir::StringAttr name, bool packed, bool padded,
RecordKind kind, ASTRecordDeclInterface ast = {});
static StructType
getChecked(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
mlir::MLIRContext *context, llvm::ArrayRef<mlir::Type> members,
mlir::StringAttr name, bool packed, bool padded, RecordKind kind,
ASTRecordDeclInterface ast = {});

/// Create an identified and incomplete struct type.
static StructType get(mlir::MLIRContext *context, mlir::StringAttr name,
RecordKind kind);
static StructType
getChecked(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
mlir::MLIRContext *context, mlir::StringAttr name,
RecordKind kind);

/// Create an anonymous struct type (always complete).
static StructType get(mlir::MLIRContext *context,
llvm::ArrayRef<mlir::Type> members, bool packed,
bool padded, RecordKind kind,
ASTRecordDeclInterface ast = {});
static StructType
getChecked(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
mlir::MLIRContext *context, llvm::ArrayRef<mlir::Type> members,
bool packed, bool padded, RecordKind kind,
ASTRecordDeclInterface ast = {});

/// Validate the struct about to be constructed.
static llvm::LogicalResult
verifyInvariants(llvm::function_ref<mlir::InFlightDiagnostic()> emitError,
llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
bool incomplete, bool packed, bool padded,
StructType::RecordKind kind, ASTRecordDeclInterface ast);

// Parse/print methods.
static constexpr llvm::StringLiteral getMnemonic() { return {"struct"}; }
static mlir::Type parse(mlir::AsmParser &odsParser);
void print(mlir::AsmPrinter &odsPrinter) const;

// Accessors
ASTRecordDeclInterface getAst() const;
llvm::ArrayRef<mlir::Type> getMembers() const;
mlir::StringAttr getName() const;
StructType::RecordKind getKind() const;
bool getIncomplete() const;
bool getPacked() const;
bool getPadded() const;
void dropAst();

// Predicates
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;

// Utilities
mlir::Type getLargestMember(const mlir::DataLayout &dataLayout) 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();
}

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

/// DataLayoutTypeInterface methods.
llvm::TypeSize getTypeSizeInBits(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const;
uint64_t getABIAlignment(const mlir::DataLayout &dataLayout,
mlir::DataLayoutEntryListRef params) const;
uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
unsigned idx) const;

bool isLayoutIdentical(const StructType &other);

// Utilities for lazily computing and cacheing data layout info.
private:
// FIXME: currently opaque because there's a cycle if CIRTypes.types include
// from CIRAttrs.h. The implementation operates in terms of StructLayoutAttr
// instead.
mutable mlir::Attribute layoutInfo;
void computeSizeAndAlignment(const mlir::DataLayout &dataLayout) const;
};

bool isAnyFloatingPointType(mlir::Type t);
bool isScalarType(mlir::Type t);
bool isFPOrFPVectorTy(mlir::Type);
Expand Down
159 changes: 156 additions & 3 deletions clang/include/clang/CIR/Dialect/IR/CIRTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,164 @@ def FuncPtr : Type<
}

//===----------------------------------------------------------------------===//
// StructType (defined in cpp files)
// StructType
//
// The base type for all RecordDecls.
//===----------------------------------------------------------------------===//

def CIR_StructType : Type<CPred<"::mlir::isa<::cir::StructType>($_self)">,
"CIR struct type">;
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,
OptionalParameter<"ASTRecordDeclInterface">:$ast
);

// 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 complete struct type.
TypeBuilder<(ins
"llvm::ArrayRef<mlir::Type>":$members,
"mlir::StringAttr":$name,
"bool":$packed,
"bool":$padded,
"RecordKind":$kind,
CArg<"ASTRecordDeclInterface", "{}">:$ast
), [{
return $_get($_ctxt, members, name, /*incomplete=*/false, packed, padded,
kind, ast);
}]>,

// 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,
/*ast=*/ASTRecordDeclInterface{});
}]>,

// Create an anonymous struct type (always complete).
TypeBuilder<(ins
"llvm::ArrayRef<mlir::Type>":$members,
"bool":$packed,
"bool":$padded,
"RecordKind":$kind,
CArg<"ASTRecordDeclInterface", "{}">:$ast
), [{
return $_get($_ctxt, members, mlir::StringAttr{}, /*incomplete=*/false,
packed, padded, kind, ast);
}]>];

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;

void dropAst();

mlir::Type getLargestMember(const mlir::DataLayout &dataLayout) 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();
}

void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
bool isPadded, ASTRecordDeclInterface ast = {});

uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
unsigned idx) const;

bool isLayoutIdentical(const StructType &other);

// Utilities for lazily computing and cacheing data layout info.
// FIXME: currently opaque because there's a cycle if CIRTypes.types include
// from CIRAttrs.h. The implementation operates in terms of StructLayoutAttr
// instead.
private:
mutable mlir::Attribute layoutInfo;
void computeSizeAndAlignment(const mlir::DataLayout &dataLayout) const;
public:
}];

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
Expand Down
Loading
Loading