Skip to content

Commit c5466c6

Browse files
authored
[CIR] Add CIR vtable attribute (#154415)
This adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change.
1 parent 761125f commit c5466c6

File tree

5 files changed

+190
-2
lines changed

5 files changed

+190
-2
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,72 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
535535
}];
536536
}
537537

538+
//===----------------------------------------------------------------------===//
539+
// VTableAttr
540+
//===----------------------------------------------------------------------===//
541+
542+
def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
543+
let summary = "Represents a C++ vtable";
544+
let description = [{
545+
Wraps a #cir.const_record containing one or more vtable arrays.
546+
547+
In most cases, the anonymous record type wrapped by this attribute will
548+
contain a single array corresponding to the vtable for one class. However,
549+
in the case of multiple inheritence, the anonymous structure may contain
550+
multiple arrays, each of which is a vtable.
551+
552+
Example 1 (single vtable):
553+
```mlir
554+
cir.global linkonce_odr @_ZTV6Mother =
555+
#cir.vtable<{
556+
#cir.const_array<[
557+
#cir.ptr<null> : !cir.ptr<!u8i>,
558+
#cir.global_view<@_ZTI6Mother> : !cir.ptr<!u8i>,
559+
#cir.global_view<@_ZN6Mother9MotherFooEv> : !cir.ptr<!u8i>,
560+
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
561+
]> : !cir.array<!cir.ptr<!u8i> x 4>
562+
}> : !rec_anon_struct1
563+
```
564+
565+
Example 2 (multiple vtables):
566+
```mlir
567+
cir.global linkonce_odr @_ZTV5Child =
568+
#cir.vtable<{
569+
#cir.const_array<[
570+
#cir.ptr<null> : !cir.ptr<!u8i>,
571+
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
572+
#cir.global_view<@_ZN5Child9MotherFooEv> : !cir.ptr<!u8i>,
573+
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
574+
]> : !cir.array<!cir.ptr<!u8i> x 4>,
575+
#cir.const_array<[
576+
#cir.ptr<-8 : i64> : !cir.ptr<!u8i>,
577+
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
578+
#cir.global_view<@_ZN6Father9FatherFooEv> : !cir.ptr<!u8i>
579+
]> : !cir.array<!cir.ptr<!u8i> x 3>
580+
}> : !rec_anon_struct2
581+
```
582+
}];
583+
584+
// `data` is a const record with one element, containing an array of
585+
// vtable information.
586+
let parameters = (ins
587+
AttributeSelfTypeParameter<"">:$type,
588+
"mlir::ArrayAttr":$data
589+
);
590+
591+
let builders = [
592+
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
593+
"mlir::ArrayAttr":$data), [{
594+
return $_get(type.getContext(), type, data);
595+
}]>
596+
];
597+
598+
let genVerifyDecl = 1;
599+
let assemblyFormat = [{
600+
`<` custom<RecordMembers>($data) `>`
601+
}];
602+
}
603+
538604
//===----------------------------------------------------------------------===//
539605
// ConstComplexAttr
540606
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRAttrs.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,44 @@ cir::ConstVectorAttr::verify(function_ref<InFlightDiagnostic()> emitError,
424424
return elementTypeCheck;
425425
}
426426

427+
//===----------------------------------------------------------------------===//
428+
// CIR VTableAttr
429+
//===----------------------------------------------------------------------===//
430+
431+
LogicalResult cir::VTableAttr::verify(
432+
llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type type,
433+
mlir::ArrayAttr data) {
434+
auto sTy = mlir::dyn_cast_if_present<cir::RecordType>(type);
435+
if (!sTy)
436+
return emitError() << "expected !cir.record type result";
437+
if (sTy.getMembers().empty() || data.empty())
438+
return emitError() << "expected record type with one or more subtype";
439+
440+
if (cir::ConstRecordAttr::verify(emitError, type, data).failed())
441+
return failure();
442+
443+
for (const auto &element : data.getAsRange<mlir::Attribute>()) {
444+
const auto &constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
445+
if (!constArrayAttr)
446+
return emitError() << "expected constant array subtype";
447+
448+
LogicalResult eltTypeCheck = success();
449+
auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
450+
arrayElts.walkImmediateSubElements(
451+
[&](mlir::Attribute attr) {
452+
if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
453+
return;
454+
455+
eltTypeCheck = emitError()
456+
<< "expected GlobalViewAttr or ConstPtrAttr";
457+
},
458+
[&](mlir::Type type) {});
459+
if (eltTypeCheck.failed())
460+
return eltTypeCheck;
461+
}
462+
return success();
463+
}
464+
427465
//===----------------------------------------------------------------------===//
428466
// CIR Dialect
429467
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
342342

343343
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
344344
cir::ConstComplexAttr, cir::ConstRecordAttr,
345-
cir::GlobalViewAttr, cir::PoisonAttr>(attrType))
345+
cir::GlobalViewAttr, cir::PoisonAttr, cir::VTableAttr>(
346+
attrType))
346347
return success();
347348

348349
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: cir-opt %s -verify-diagnostics
1+
// RUN: cir-opt %s -verify-diagnostics -split-input-file
22

33
!s8i = !cir.int<s, 8>
44
!u32i = !cir.int<u, 32>
@@ -7,3 +7,67 @@ cir.func @reference_unknown_vtable() {
77
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr
88
cir.return
99
}
10+
11+
// -----
12+
13+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
14+
!u8i = !cir.int<u, 8>
15+
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
16+
module {
17+
// expected-error @below {{expected !cir.record type result}}
18+
cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !cir.ptr<!rec_anon_struct>
19+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
20+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
21+
}
22+
23+
// -----
24+
25+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
26+
!u8i = !cir.int<u, 8>
27+
!rec_anon_struct = !cir.record<struct {}>
28+
module {
29+
// expected-error @below {{expected record type with one or more subtype}}
30+
cir.global external @_ZTV1S = #cir.vtable<{}> : !rec_anon_struct {alignment = 8 : i64}
31+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
32+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
33+
}
34+
35+
// -----
36+
37+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
38+
!u8i = !cir.int<u, 8>
39+
!rec_anon_struct = !cir.record<struct {!cir.ptr<!u8i>}>
40+
module {
41+
// expected-error @below {{expected constant array subtype}}
42+
cir.global external @_ZTV1S = #cir.vtable<{#cir.ptr<null> : !cir.ptr<!u8i>}> : !rec_anon_struct {alignment = 8 : i64}
43+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
44+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
45+
}
46+
47+
// -----
48+
49+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
50+
!u64i = !cir.int<u, 64>
51+
!rec_anon_struct = !cir.record<struct {!cir.array<!u64i x 4>}>
52+
module {
53+
// expected-error @below {{expected GlobalViewAttr or ConstPtrAttr}}
54+
cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.int<1> : !u64i, #cir.int<1> : !u64i, #cir.int<3> : !u64i, #cir.int<4> : !u64i]> : !cir.array<!u64i x 4>}> : !rec_anon_struct {alignment = 8 : i64}
55+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
56+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
57+
}
58+
59+
// -----
60+
61+
!rec_Q = !cir.record<struct "Q" {!cir.vptr}>
62+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
63+
!rec_S2 = !cir.record<struct "S2" {!rec_Q, !rec_S}>
64+
!u8i = !cir.int<u, 8>
65+
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.ptr<!u8i>}>
66+
module {
67+
// expected-error @below {{expected constant array subtype}}
68+
cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>, #cir.ptr<null> : !cir.ptr<!u8i>}> : !rec_anon_struct {alignment = 8 : i64}
69+
70+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
71+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
72+
cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr<!rec_S2>)
73+
}

clang/test/CIR/IR/vtable-attr.cir

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RUN: cir-opt %s | FileCheck %s
2+
3+
!rec_Q = !cir.record<struct "Q" {!cir.vptr}>
4+
!rec_S = !cir.record<struct "S" {!cir.vptr}>
5+
!rec_S2 = !cir.record<struct "S2" {!rec_Q, !rec_S}>
6+
!u8i = !cir.int<u, 8>
7+
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
8+
!rec_anon_struct1 = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.array<!cir.ptr<!u8i> x 3>}>
9+
module {
10+
cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !rec_anon_struct {alignment = 8 : i64}
11+
// CHECK: cir.global external @_ZTV1S = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !rec_anon_struct {alignment = 8 : i64}
12+
13+
cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>, #cir.const_array<[#cir.ptr<-8 : i64> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN2S23keyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !rec_anon_struct1 {alignment = 8 : i64}
14+
// CHECK: cir.global external @_ZTV2S2 = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S3keyEv> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1S6nonKeyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>, #cir.const_array<[#cir.ptr<-8 : i64> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZN2S23keyEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !rec_anon_struct1 {alignment = 8 : i64}
15+
16+
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
17+
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
18+
cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr<!rec_S2>)
19+
}

0 commit comments

Comments
 (0)