Skip to content

Commit cd7d2c3

Browse files
authored
[CIR] Upstream minimal support for structure types (#135105)
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 557e931 commit cd7d2c3

File tree

14 files changed

+622
-9
lines changed

14 files changed

+622
-9
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 RecordTypeStorage;
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: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,13 +400,122 @@ def VoidPtr : Type<
400400
"cir::VoidType::get($_builder.getContext()))"> {
401401
}
402402

403+
//===----------------------------------------------------------------------===//
404+
// RecordType
405+
//
406+
// The base type for all RecordDecls.
407+
//===----------------------------------------------------------------------===//
408+
409+
def CIR_RecordType : CIR_Type<"Record", "record",
410+
[
411+
DeclareTypeInterfaceMethods<DataLayoutTypeInterface>,
412+
MutableType,
413+
]> {
414+
let summary = "CIR record type";
415+
let description = [{
416+
Each unique clang::RecordDecl is mapped to a `cir.record` and any object in
417+
C/C++ that has a struct or class type will have a `cir.record` in CIR.
418+
419+
There are three possible formats for this type:
420+
421+
- Identified and complete records: unique name and a known body.
422+
- Identified and incomplete records: unique name and unknown body.
423+
- Anonymous records: no name and a known body.
424+
425+
Identified records are uniqued by their name, and anonymous records are
426+
uniqued by their body. This means that two anonymous records with the same
427+
body will be the same type, and two identified records with the same name
428+
will be the same type. Attempting to build a record 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.record<struct "complete" {!cir.int<u, 8>}>
435+
!incomplete = !cir.record<struct "incomplete" incomplete>
436+
!anonymous = !cir.record<struct {!cir.int<u, 8>}>
437+
```
438+
439+
Incomplete records 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 record. Mutability allows for recursive types to be represented,
442+
meaning the record 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` record has a member that is a pointer to a
445+
`Node` record:
446+
447+
```mlir
448+
!s = !cir.record<struct "Node" {!cir.ptr<!cir.record<struct "Node">>}>
449+
```
450+
}];
451+
452+
let parameters = (ins
453+
OptionalArrayRefParameter<"mlir::Type">:$members,
454+
OptionalParameter<"mlir::StringAttr">:$name,
455+
"bool":$incomplete,
456+
"bool":$packed,
457+
"bool":$padded,
458+
"RecordType::RecordKind":$kind
459+
);
460+
461+
// StorageClass is defined in C++ for mutability.
462+
let storageClass = "RecordTypeStorage";
463+
let genStorageClass = 0;
464+
465+
let skipDefaultBuilders = 1;
466+
let genVerifyDecl = 1;
467+
468+
let builders = [
469+
// Create an identified and incomplete record type.
470+
TypeBuilder<(ins
471+
"mlir::StringAttr":$name,
472+
"RecordKind":$kind
473+
), [{
474+
return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name,
475+
/*incomplete=*/true, /*packed=*/false,
476+
/*padded=*/false, kind);
477+
}]>];
478+
479+
let extraClassDeclaration = [{
480+
using Base::verifyInvariants;
481+
482+
enum RecordKind : uint32_t { Struct, Union };
483+
484+
bool isStruct() const { return getKind() == RecordKind::Struct; };
485+
bool isUnion() const { return getKind() == RecordKind::Union; };
486+
bool isComplete() const { return !isIncomplete(); };
487+
bool isIncomplete() const;
488+
489+
size_t getNumElements() const { return getMembers().size(); };
490+
std::string getKindAsStr() {
491+
switch (getKind()) {
492+
case RecordKind::Union:
493+
return "union";
494+
case RecordKind::Struct:
495+
return "struct";
496+
}
497+
llvm_unreachable("Invalid value for RecordType::getKind()");
498+
}
499+
std::string getPrefixedName() {
500+
return getKindAsStr() + "." + getName().getValue().str();
501+
}
502+
}];
503+
504+
let hasCustomAssemblyFormat = 1;
505+
}
506+
507+
// Note CIRRecordType is used instead of CIR_RecordType
508+
// because of tablegen conflicts.
509+
def CIRRecordType : Type<
510+
CPred<"::mlir::isa<::cir::RecordType>($_self)">, "CIR record type">;
511+
403512
//===----------------------------------------------------------------------===//
404513
// Global type constraints
405514
//===----------------------------------------------------------------------===//
406515

407516
def CIR_AnyType : AnyTypeOf<[
408517
CIR_VoidType, CIR_BoolType, CIR_ArrayType, CIR_IntType, CIR_AnyFloat,
409-
CIR_PointerType, CIR_FuncType
518+
CIR_PointerType, CIR_FuncType, CIR_RecordType
410519
]>;
411520

412521
#endif // MLIR_CIR_DIALECT_CIR_TYPES
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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 RecordTypeStorage
26+
//===----------------------------------------------------------------------===//
27+
28+
/// Type storage for CIR record types.
29+
struct RecordTypeStorage : 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+
RecordType::RecordKind kind;
37+
38+
KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
39+
bool incomplete, bool packed, bool padded,
40+
RecordType::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+
RecordType::RecordKind kind;
51+
52+
RecordTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
53+
bool incomplete, bool packed, bool padded,
54+
RecordType::RecordKind kind)
55+
: members(members), name(name), incomplete(incomplete), packed(packed),
56+
padded(padded), kind(kind) {
57+
assert(name || !incomplete && "Incomplete records must have a name");
58+
}
59+
60+
KeyTy getAsKey() const {
61+
return KeyTy(members, name, incomplete, packed, padded, kind);
62+
}
63+
64+
bool operator==(const KeyTy &key) const {
65+
if (name)
66+
return (name == key.name) && (kind == key.kind);
67+
return std::tie(members, name, incomplete, packed, padded, kind) ==
68+
std::tie(key.members, key.name, key.incomplete, key.packed,
69+
key.padded, key.kind);
70+
}
71+
72+
static llvm::hash_code hashKey(const KeyTy &key) {
73+
if (key.name)
74+
return llvm::hash_combine(key.name, key.kind);
75+
return llvm::hash_combine(key.members, key.incomplete, key.packed,
76+
key.padded, key.kind);
77+
}
78+
79+
static RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
80+
const KeyTy &key) {
81+
return new (allocator.allocate<RecordTypeStorage>())
82+
RecordTypeStorage(allocator.copyInto(key.members), key.name,
83+
key.incomplete, key.packed, key.padded, key.kind);
84+
}
85+
86+
/// Mutates the members and attributes an identified record.
87+
///
88+
/// Once a record is mutated, it is marked as complete, preventing further
89+
/// mutations. Anonymous records are always complete and cannot be mutated.
90+
/// This method does not fail if a mutation of a complete record does not
91+
/// change the record.
92+
llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
93+
llvm::ArrayRef<mlir::Type> members, bool packed,
94+
bool padded) {
95+
// Anonymous records cannot mutate.
96+
if (!name)
97+
return llvm::failure();
98+
99+
// Mutation of complete records are allowed if they change nothing.
100+
if (!incomplete)
101+
return mlir::success((this->members == members) &&
102+
(this->packed == packed) &&
103+
(this->padded == padded));
104+
105+
// Mutate incomplete record.
106+
this->members = allocator.copyInto(members);
107+
this->packed = packed;
108+
this->padded = padded;
109+
110+
incomplete = false;
111+
return llvm::success();
112+
}
113+
};
114+
115+
} // namespace detail
116+
} // namespace cir
117+
118+
#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+
// RecordType
105+
static bool recordTypeLayoutInfo() { 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: 40 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,33 @@ 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::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) {
55+
switch (kind) {
56+
case clang::TagTypeKind::Class:
57+
case clang::TagTypeKind::Struct:
58+
return cir::RecordType::Struct;
59+
case clang::TagTypeKind::Union:
60+
return cir::RecordType::Union;
61+
case clang::TagTypeKind::Interface:
62+
llvm_unreachable("interface records are NYI");
63+
case clang::TagTypeKind::Enum:
64+
llvm_unreachable("enums are not records");
65+
}
66+
}
67+
68+
/// Get an incomplete CIR struct type. If we have a complete record
69+
/// declaration, we may create an incomplete type and then add the
70+
/// members, so \p rd here may be complete.
71+
cir::RecordType getIncompleteRecordTy(llvm::StringRef name,
72+
const clang::RecordDecl *rd) {
73+
const mlir::StringAttr nameAttr = getStringAttr(name);
74+
cir::RecordType::RecordKind kind = cir::RecordType::RecordKind::Struct;
75+
if (rd)
76+
kind = getRecordKind(rd->getTagKind());
77+
return getType<cir::RecordType>(nameAttr, kind);
78+
}
79+
4080
bool isSized(mlir::Type ty) {
4181
if (mlir::isa<cir::PointerType, cir::ArrayType, cir::BoolType,
4282
cir::IntType>(ty))

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,9 @@ void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
260260

261261
void CIRGenFunction::emitDecl(const Decl &d) {
262262
switch (d.getKind()) {
263+
case Decl::Record: // struct/union/class X;
264+
assert(!cir::MissingFeatures::generateDebugInfo());
265+
return;
263266
case Decl::Var: {
264267
const VarDecl &vd = cast<VarDecl>(d);
265268
assert(vd.isLocalVarDecl() &&
@@ -274,7 +277,9 @@ void CIRGenFunction::emitDecl(const Decl &d) {
274277
emitOpenACCRoutine(cast<OpenACCRoutineDecl>(d));
275278
return;
276279
default:
277-
cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type");
280+
cgm.errorNYI(d.getSourceRange(),
281+
std::string("emitDecl: unhandled decl type: ") +
282+
d.getDeclKindName());
278283
}
279284
}
280285

clang/lib/CIR/CodeGen/CIRGenModule.cpp

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

0 commit comments

Comments
 (0)