[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.
This commit is contained in:
Andy Kaylor 2025-08-21 09:52:14 -07:00 committed by GitHub
parent 761125f267
commit c5466c64d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 190 additions and 2 deletions

View File

@ -535,6 +535,72 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
}]; }];
} }
//===----------------------------------------------------------------------===//
// VTableAttr
//===----------------------------------------------------------------------===//
def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
let summary = "Represents a C++ vtable";
let description = [{
Wraps a #cir.const_record containing one or more vtable arrays.
In most cases, the anonymous record type wrapped by this attribute will
contain a single array corresponding to the vtable for one class. However,
in the case of multiple inheritence, the anonymous structure may contain
multiple arrays, each of which is a vtable.
Example 1 (single vtable):
```mlir
cir.global linkonce_odr @_ZTV6Mother =
#cir.vtable<{
#cir.const_array<[
#cir.ptr<null> : !cir.ptr<!u8i>,
#cir.global_view<@_ZTI6Mother> : !cir.ptr<!u8i>,
#cir.global_view<@_ZN6Mother9MotherFooEv> : !cir.ptr<!u8i>,
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
]> : !cir.array<!cir.ptr<!u8i> x 4>
}> : !rec_anon_struct1
```
Example 2 (multiple vtables):
```mlir
cir.global linkonce_odr @_ZTV5Child =
#cir.vtable<{
#cir.const_array<[
#cir.ptr<null> : !cir.ptr<!u8i>,
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
#cir.global_view<@_ZN5Child9MotherFooEv> : !cir.ptr<!u8i>,
#cir.global_view<@_ZN6Mother10MotherFoo2Ev> : !cir.ptr<!u8i>
]> : !cir.array<!cir.ptr<!u8i> x 4>,
#cir.const_array<[
#cir.ptr<-8 : i64> : !cir.ptr<!u8i>,
#cir.global_view<@_ZTI5Child> : !cir.ptr<!u8i>,
#cir.global_view<@_ZN6Father9FatherFooEv> : !cir.ptr<!u8i>
]> : !cir.array<!cir.ptr<!u8i> x 3>
}> : !rec_anon_struct2
```
}];
// `data` is a const record with one element, containing an array of
// vtable information.
let parameters = (ins
AttributeSelfTypeParameter<"">:$type,
"mlir::ArrayAttr":$data
);
let builders = [
AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
"mlir::ArrayAttr":$data), [{
return $_get(type.getContext(), type, data);
}]>
];
let genVerifyDecl = 1;
let assemblyFormat = [{
`<` custom<RecordMembers>($data) `>`
}];
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// ConstComplexAttr // ConstComplexAttr
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -424,6 +424,44 @@ cir::ConstVectorAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return elementTypeCheck; return elementTypeCheck;
} }
//===----------------------------------------------------------------------===//
// CIR VTableAttr
//===----------------------------------------------------------------------===//
LogicalResult cir::VTableAttr::verify(
llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type type,
mlir::ArrayAttr data) {
auto sTy = mlir::dyn_cast_if_present<cir::RecordType>(type);
if (!sTy)
return emitError() << "expected !cir.record type result";
if (sTy.getMembers().empty() || data.empty())
return emitError() << "expected record type with one or more subtype";
if (cir::ConstRecordAttr::verify(emitError, type, data).failed())
return failure();
for (const auto &element : data.getAsRange<mlir::Attribute>()) {
const auto &constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(element);
if (!constArrayAttr)
return emitError() << "expected constant array subtype";
LogicalResult eltTypeCheck = success();
auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
arrayElts.walkImmediateSubElements(
[&](mlir::Attribute attr) {
if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
return;
eltTypeCheck = emitError()
<< "expected GlobalViewAttr or ConstPtrAttr";
},
[&](mlir::Type type) {});
if (eltTypeCheck.failed())
return eltTypeCheck;
}
return success();
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// CIR Dialect // CIR Dialect
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//

View File

@ -342,7 +342,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr, if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
cir::ConstComplexAttr, cir::ConstRecordAttr, cir::ConstComplexAttr, cir::ConstRecordAttr,
cir::GlobalViewAttr, cir::PoisonAttr>(attrType)) cir::GlobalViewAttr, cir::PoisonAttr, cir::VTableAttr>(
attrType))
return success(); return success();
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?"); assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");

View File

@ -1,4 +1,4 @@
// RUN: cir-opt %s -verify-diagnostics // RUN: cir-opt %s -verify-diagnostics -split-input-file
!s8i = !cir.int<s, 8> !s8i = !cir.int<s, 8>
!u32i = !cir.int<u, 32> !u32i = !cir.int<u, 32>
@ -7,3 +7,67 @@ cir.func @reference_unknown_vtable() {
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr %0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr
cir.return cir.return
} }
// -----
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
module {
// expected-error @below {{expected !cir.record type result}}
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>
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
}
// -----
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {}>
module {
// expected-error @below {{expected record type with one or more subtype}}
cir.global external @_ZTV1S = #cir.vtable<{}> : !rec_anon_struct {alignment = 8 : i64}
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
}
// -----
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {!cir.ptr<!u8i>}>
module {
// expected-error @below {{expected constant array subtype}}
cir.global external @_ZTV1S = #cir.vtable<{#cir.ptr<null> : !cir.ptr<!u8i>}> : !rec_anon_struct {alignment = 8 : i64}
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
}
// -----
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!u64i = !cir.int<u, 64>
!rec_anon_struct = !cir.record<struct {!cir.array<!u64i x 4>}>
module {
// expected-error @below {{expected GlobalViewAttr or ConstPtrAttr}}
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}
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
}
// -----
!rec_Q = !cir.record<struct "Q" {!cir.vptr}>
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!rec_S2 = !cir.record<struct "S2" {!rec_Q, !rec_S}>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.ptr<!u8i>}>
module {
// expected-error @below {{expected constant array subtype}}
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}
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr<!rec_S2>)
}

View File

@ -0,0 +1,19 @@
// RUN: cir-opt %s | FileCheck %s
!rec_Q = !cir.record<struct "Q" {!cir.vptr}>
!rec_S = !cir.record<struct "S" {!cir.vptr}>
!rec_S2 = !cir.record<struct "S2" {!rec_Q, !rec_S}>
!u8i = !cir.int<u, 8>
!rec_anon_struct = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>}>
!rec_anon_struct1 = !cir.record<struct {!cir.array<!cir.ptr<!u8i> x 4>, !cir.array<!cir.ptr<!u8i> x 3>}>
module {
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}
// 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}
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}
// 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}
cir.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
cir.func private dso_local @_ZN2S23keyEv(%arg0: !cir.ptr<!rec_S2>)
}