Skip to content

Commit 3b19527

Browse files
committed
[CIR] Upstream minimal support for structure types
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.
1 parent b74dbf7 commit 3b19527

File tree

11 files changed

+558
-7
lines changed

11 files changed

+558
-7
lines changed

clang/include/clang/CIR/Dialect/IR/CIRTypes.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
namespace cir {
2222

23+
namespace detail {
24+
struct StructTypeStorage;
25+
} // namespace detail
26+
2327
bool isAnyFloatingPointType(mlir::Type t);
2428
bool isFPOrFPVectorTy(mlir::Type);
2529

clang/include/clang/CIR/Dialect/IR/CIRTypes.td

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,126 @@ def VoidPtr : Type<
400400
"cir::VoidType::get($_builder.getContext()))"> {
401401
}
402402

403+
//===----------------------------------------------------------------------===//
404+
// StructType
405+
//
406+
// The base type for all RecordDecls.
407+
//===----------------------------------------------------------------------===//
408+
409+
def CIR_StructType : CIR_Type<"Struct", "struct",
410+
[
411+
DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
412+
MutableType,
413+
]> {
414+
let summary = "CIR struct type";
415+
let description = [{
416+
Each unique clang::RecordDecl is mapped to a `cir.struct` and any object in
417+
C/C++ that has a struct type will have a `cir.struct` in CIR.
418+
419+
There are three possible formats for this type:
420+
421+
- Identified and complete structs: unique name and a known body.
422+
- Identified and incomplete structs: unique name and unknown body.
423+
- Anonymous structs: no name and a known body.
424+
425+
Identified structs are uniqued by their name, and anonymous structs are
426+
uniqued by their body. This means that two anonymous structs with the same
427+
body will be the same type, and two identified structs with the same name
428+
will be the same type. Attempting to build a struct with an existing name,
429+
but a different body will result in an error.
430+
431+
A few examples:
432+
433+
```mlir
434+
!complete = !cir.struct<struct "complete" {!cir.int<u, 8>}>
435+
!incomplete = !cir.struct<struct "incomplete" incomplete>
436+
!anonymous = !cir.struct<struct {!cir.int<u, 8>}>
437+
```
438+
439+
Incomplete structs are mutable, meaning they can be later completed with a
440+
body automatically updating in place every type in the code that uses the
441+
incomplete struct. Mutability allows for recursive types to be represented,
442+
meaning the struct can have members that refer to itself. This is useful for
443+
representing recursive records and is implemented through a special syntax.
444+
In the example below, the `Node` struct has a member that is a pointer to a
445+
`Node` struct:
446+
447+
```mlir
448+
!struct = !cir.struct<struct "Node" {!cir.ptr<!cir.struct<struct
449+
"Node">>}>
450+
```
451+
}];
452+
453+
let parameters = (ins
454+
OptionalArrayRefParameter<"mlir::Type">:$members,
455+
OptionalParameter<"mlir::StringAttr">:$name,
456+
"bool":$incomplete,
457+
"bool":$packed,
458+
"bool":$padded,
459+
"StructType::RecordKind":$kind
460+
);
461+
462+
// StorageClass is defined in C++ for mutability.
463+
let storageClass = "StructTypeStorage";
464+
let genStorageClass = 0;
465+
466+
let skipDefaultBuilders = 1;
467+
let genVerifyDecl = 1;
468+
469+
let builders = [
470+
// Create an identified and incomplete struct type.
471+
TypeBuilder<(ins
472+
"mlir::StringAttr":$name,
473+
"RecordKind":$kind
474+
), [{
475+
return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name,
476+
/*incomplete=*/true, /*packed=*/false,
477+
/*padded=*/false, kind);
478+
}]>];
479+
480+
let extraClassDeclaration = [{
481+
using Base::verifyInvariants;
482+
483+
enum RecordKind : uint32_t { Class, Union, Struct };
484+
485+
bool isClass() const { return getKind() == RecordKind::Class; };
486+
bool isStruct() const { return getKind() == RecordKind::Struct; };
487+
bool isUnion() const { return getKind() == RecordKind::Union; };
488+
bool isComplete() const { return !isIncomplete(); };
489+
bool isIncomplete() const;
490+
491+
size_t getNumElements() const { return getMembers().size(); };
492+
std::string getKindAsStr() {
493+
switch (getKind()) {
494+
case RecordKind::Class:
495+
return "class";
496+
case RecordKind::Union:
497+
return "union";
498+
case RecordKind::Struct:
499+
return "struct";
500+
}
501+
llvm_unreachable("Invalid value for StructType::getKind()");
502+
}
503+
std::string getPrefixedName() {
504+
return getKindAsStr() + "." + getName().getValue().str();
505+
}
506+
}];
507+
508+
let hasCustomAssemblyFormat = 1;
509+
}
510+
511+
// Note CIRStructType is used instead of CIR_StructType
512+
// because of tablegen conflicts.
513+
def CIRStructType : Type<
514+
CPred<"::mlir::isa<::cir::StructType>($_self)">, "CIR struct type">;
515+
403516
//===----------------------------------------------------------------------===//
404517
// Global type constraints
405518
//===----------------------------------------------------------------------===//
406519

407520
def CIR_AnyType : AnyTypeOf<[
408521
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_IntType, CIR_AnyFloat,
409-
CIR_PointerType, CIR_FuncType
522+
CIR_PointerType, CIR_FuncType, CIR_StructType
410523
]>;
411524

412525
#endif // MLIR_CIR_DIALECT_CIR_TYPES
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file contains implementation details, such as storage structures, of
10+
// CIR dialect types.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
#ifndef CIR_DIALECT_IR_CIRTYPESDETAILS_H
14+
#define CIR_DIALECT_IR_CIRTYPESDETAILS_H
15+
16+
#include "mlir/IR/BuiltinAttributes.h"
17+
#include "mlir/Support/LogicalResult.h"
18+
#include "clang/CIR/Dialect/IR/CIRTypes.h"
19+
#include "llvm/ADT/Hashing.h"
20+
21+
namespace cir {
22+
namespace detail {
23+
24+
//===----------------------------------------------------------------------===//
25+
// CIR StructTypeStorage
26+
//===----------------------------------------------------------------------===//
27+
28+
/// Type storage for CIR record types.
29+
struct StructTypeStorage : public mlir::TypeStorage {
30+
struct KeyTy {
31+
llvm::ArrayRef<mlir::Type> members;
32+
mlir::StringAttr name;
33+
bool incomplete;
34+
bool packed;
35+
bool padded;
36+
StructType::RecordKind kind;
37+
38+
KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
39+
bool incomplete, bool packed, bool padded,
40+
StructType::RecordKind kind)
41+
: members(members), name(name), incomplete(incomplete), packed(packed),
42+
padded(padded), kind(kind) {}
43+
};
44+
45+
llvm::ArrayRef<mlir::Type> members;
46+
mlir::StringAttr name;
47+
bool incomplete;
48+
bool packed;
49+
bool padded;
50+
StructType::RecordKind kind;
51+
52+
StructTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
53+
bool incomplete, bool packed, bool padded,
54+
StructType::RecordKind kind)
55+
: members(members), name(name), incomplete(incomplete), packed(packed),
56+
padded(padded), kind(kind) {}
57+
58+
KeyTy getAsKey() const {
59+
return KeyTy(members, name, incomplete, packed, padded, kind);
60+
}
61+
62+
bool operator==(const KeyTy &key) const {
63+
if (name)
64+
return (name == key.name) && (kind == key.kind);
65+
return (members == key.members) && (name == key.name) &&
66+
(incomplete == key.incomplete) && (packed == key.packed) &&
67+
(padded == key.padded) && (kind == key.kind);
68+
}
69+
70+
static llvm::hash_code hashKey(const KeyTy &key) {
71+
if (key.name)
72+
return llvm::hash_combine(key.name, key.kind);
73+
return llvm::hash_combine(key.members, key.incomplete, key.packed,
74+
key.padded, key.kind);
75+
}
76+
77+
static StructTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
78+
const KeyTy &key) {
79+
return new (allocator.allocate<StructTypeStorage>())
80+
StructTypeStorage(allocator.copyInto(key.members), key.name,
81+
key.incomplete, key.packed, key.padded, key.kind);
82+
}
83+
84+
/// Mutates the members and attributes an identified struct.
85+
///
86+
/// Once a record is mutated, it is marked as complete, preventing further
87+
/// mutations. Anonymous structs are always complete and cannot be mutated.
88+
/// This method does not fail if a mutation of a complete struct does not
89+
/// change the struct.
90+
llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
91+
llvm::ArrayRef<mlir::Type> members, bool packed,
92+
bool padded) {
93+
// Anonymous structs cannot mutate.
94+
if (!name)
95+
return llvm::failure();
96+
97+
// Mutation of complete structs are allowed if they change nothing.
98+
if (!incomplete)
99+
return mlir::success((this->members == members) &&
100+
(this->packed == packed) &&
101+
(this->padded == padded));
102+
103+
// Mutate incomplete struct.
104+
this->members = allocator.copyInto(members);
105+
this->packed = packed;
106+
this->padded = padded;
107+
108+
incomplete = false;
109+
return llvm::success();
110+
}
111+
};
112+
113+
} // namespace detail
114+
} // namespace cir
115+
116+
#endif // CIR_DIALECT_IR_CIRTYPESDETAILS_H

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ struct MissingFeatures {
101101
static bool mayHaveIntegerOverflow() { return false; }
102102
static bool shouldReverseUnaryCondOnBoolExpr() { return false; }
103103

104+
// StructType
105+
static bool structTypeLayoutInfo() { return false; }
106+
104107
// Misc
105108
static bool cxxABI() { return false; }
106109
static bool tryEmitAsConstant() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,24 @@ namespace clang::CIRGen {
2020

2121
class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
2222
const CIRGenTypeCache &typeCache;
23+
llvm::StringMap<unsigned> recordNames;
2324

2425
public:
2526
CIRGenBuilderTy(mlir::MLIRContext &mlirContext, const CIRGenTypeCache &tc)
2627
: CIRBaseBuilderTy(mlirContext), typeCache(tc) {}
2728

29+
std::string getUniqueAnonRecordName() { return getUniqueRecordName("anon"); }
30+
31+
std::string getUniqueRecordName(const std::string &baseName) {
32+
auto it = recordNames.find(baseName);
33+
if (it == recordNames.end()) {
34+
recordNames[baseName] = 0;
35+
return baseName;
36+
}
37+
38+
return baseName + "." + std::to_string(recordNames[baseName]++);
39+
}
40+
2841
cir::LongDoubleType getLongDoubleTy(const llvm::fltSemantics &format) const {
2942
if (&format == &llvm::APFloat::IEEEdouble())
3043
return cir::LongDoubleType::get(getContext(), typeCache.DoubleTy);
@@ -37,6 +50,32 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
3750
llvm_unreachable("Unsupported format for long double");
3851
}
3952

53+
/// Get a CIR record kind from a AST declaration tag.
54+
cir::StructType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
55+
switch (kind) {
56+
case clang::TagTypeKind::Struct:
57+
return cir::StructType::Struct;
58+
case clang::TagTypeKind::Union:
59+
return cir::StructType::Union;
60+
case clang::TagTypeKind::Class:
61+
return cir::StructType::Class;
62+
case clang::TagTypeKind::Interface:
63+
llvm_unreachable("interface records are NYI");
64+
case clang::TagTypeKind::Enum:
65+
llvm_unreachable("enum records are NYI");
66+
}
67+
}
68+
69+
/// Get an incomplete CIR struct type.
70+
cir::StructType getIncompleteStructTy(llvm::StringRef name,
71+
const clang::RecordDecl *rd) {
72+
const mlir::StringAttr nameAttr = getStringAttr(name);
73+
cir::StructType::RecordKind kind = cir::StructType::RecordKind::Struct;
74+
if (rd)
75+
kind = getRecordKind(rd->getTagKind());
76+
return getType<cir::StructType>(nameAttr, kind);
77+
}
78+
4079
bool isSized(mlir::Type ty) {
4180
if (mlir::isa<cir::PointerType, cir::ArrayType, cir::BoolType,
4281
cir::IntType>(ty))

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,10 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) {
589589
case Decl::OpenACCDeclare:
590590
emitGlobalOpenACCDecl(cast<OpenACCDeclareDecl>(decl));
591591
break;
592+
593+
case Decl::Record:
594+
assert(!cir::MissingFeatures::generateDebugInfo());
595+
break;
592596
}
593597
}
594598

0 commit comments

Comments
 (0)