-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[CIR] Add CIR vtable attribute #154415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[CIR] Add CIR vtable attribute #154415
Conversation
This adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change.
|
@llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change. Full diff: https://github.com/llvm/llvm-project/pull/154415.diff 5 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 89b4d25b37ba6..c57fabeccc296 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -496,6 +496,46 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
}];
}
+//===----------------------------------------------------------------------===//
+// VTableAttr
+//===----------------------------------------------------------------------===//
+
+def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
+ let summary = "Represents a C++ vtable";
+ let description = [{
+ Wraps a #cir.const_record containing vtable data.
+
+ Example:
+ ```
+ cir.global linkonce_odr @_ZTV1B = #cir.vtable<<
+ {#cir.const_array<[#cir.null : !cir.ptr<i8>,
+ #cir.global_view<@_ZTI1B> : !cir.ptr<i8>,
+ #cir.global_view<@_ZN1BD1Ev> : !cir.ptr<i8>,
+ #cir.global_view<@_ZN1BD0Ev> : !cir.ptr<i8>,
+ #cir.global_view<@_ZNK1A5quackEv> : !cir.ptr<i8>]>
+ : !cir.array<!cir.ptr<i8> x 5>}>>
+ : !cir.record<"", !cir.array<!cir.ptr<i8> x 5>>
+ ```
+ }];
+
+ // `vtable_data` is a const record with one element, containing an array of
+ // vtable information.
+ let parameters = (ins AttributeSelfTypeParameter<"">:$type,
+ "mlir::ArrayAttr":$vtable_data);
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ "mlir::ArrayAttr":$vtable_data), [{
+ return $_get(type.getContext(), type, vtable_data);
+ }]>
+ ];
+
+ let genVerifyDecl = 1;
+ let assemblyFormat = [{
+ `<` custom<RecordMembers>($vtable_data) `>`
+ }];
+}
+
//===----------------------------------------------------------------------===//
// ConstComplexAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 5f53a6335f37d..9050db5efc977 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -424,6 +424,44 @@ cir::ConstVectorAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return elementTypeCheck;
}
+//===----------------------------------------------------------------------===//
+// CIR VTableAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::VTableAttr::verify(
+ llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type type,
+ mlir::ArrayAttr vtableData) {
+ auto sTy = mlir::dyn_cast_if_present<cir::RecordType>(type);
+ if (!sTy)
+ return emitError() << "expected !cir.record type result";
+ if (sTy.getMembers().empty() || vtableData.empty())
+ return emitError() << "expected record type with one or more subtype";
+
+ for (size_t i = 0; i < sTy.getMembers().size(); ++i) {
+ auto constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(vtableData[i]);
+ if (!constArrayAttr)
+ return emitError() << "expected constant array subtype";
+
+ if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed())
+ return failure();
+
+ LogicalResult eltTypeCheck = success();
+ auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
+ arrayElts.walkImmediateSubElements(
+ [&](Attribute attr) {
+ if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
+ return;
+
+ eltTypeCheck = emitError()
+ << "expected GlobalViewAttr or ConstPtrAttr";
+ },
+ [&](Type type) {});
+ if (eltTypeCheck.failed())
+ return eltTypeCheck;
+ }
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// CIR Dialect
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 72feee8709dc4..0a7a8793943aa 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -342,7 +342,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
cir::ConstComplexAttr, cir::ConstRecordAttr,
- cir::GlobalViewAttr, cir::PoisonAttr>(attrType))
+ cir::GlobalViewAttr, cir::PoisonAttr, cir::VTableAttr>(
+ attrType))
return success();
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir
index b3afb581b2048..0c8a36d26a0a1 100644
--- a/clang/test/CIR/IR/invalid-vtable.cir
+++ b/clang/test/CIR/IR/invalid-vtable.cir
@@ -1,4 +1,4 @@
-// RUN: cir-opt %s -verify-diagnostics
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
!s8i = !cir.int<s, 8>
!u32i = !cir.int<u, 32>
@@ -7,3 +7,51 @@ cir.func @reference_unknown_vtable() {
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr
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>)
+}
diff --git a/clang/test/CIR/IR/vtable-attr.cir b/clang/test/CIR/IR/vtable-attr.cir
new file mode 100644
index 0000000000000..4826bdcc90953
--- /dev/null
+++ b/clang/test/CIR/IR/vtable-attr.cir
@@ -0,0 +1,12 @@
+// RUN: cir-opt %s | FileCheck %s
+
+!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 {
+ 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.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
+ cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
+}
|
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change. Full diff: https://github.com/llvm/llvm-project/pull/154415.diff 5 Files Affected:
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 89b4d25b37ba6..c57fabeccc296 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -496,6 +496,46 @@ def CIR_GlobalViewAttr : CIR_Attr<"GlobalView", "global_view", [
}];
}
+//===----------------------------------------------------------------------===//
+// VTableAttr
+//===----------------------------------------------------------------------===//
+
+def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> {
+ let summary = "Represents a C++ vtable";
+ let description = [{
+ Wraps a #cir.const_record containing vtable data.
+
+ Example:
+ ```
+ cir.global linkonce_odr @_ZTV1B = #cir.vtable<<
+ {#cir.const_array<[#cir.null : !cir.ptr<i8>,
+ #cir.global_view<@_ZTI1B> : !cir.ptr<i8>,
+ #cir.global_view<@_ZN1BD1Ev> : !cir.ptr<i8>,
+ #cir.global_view<@_ZN1BD0Ev> : !cir.ptr<i8>,
+ #cir.global_view<@_ZNK1A5quackEv> : !cir.ptr<i8>]>
+ : !cir.array<!cir.ptr<i8> x 5>}>>
+ : !cir.record<"", !cir.array<!cir.ptr<i8> x 5>>
+ ```
+ }];
+
+ // `vtable_data` is a const record with one element, containing an array of
+ // vtable information.
+ let parameters = (ins AttributeSelfTypeParameter<"">:$type,
+ "mlir::ArrayAttr":$vtable_data);
+
+ let builders = [
+ AttrBuilderWithInferredContext<(ins "mlir::Type":$type,
+ "mlir::ArrayAttr":$vtable_data), [{
+ return $_get(type.getContext(), type, vtable_data);
+ }]>
+ ];
+
+ let genVerifyDecl = 1;
+ let assemblyFormat = [{
+ `<` custom<RecordMembers>($vtable_data) `>`
+ }];
+}
+
//===----------------------------------------------------------------------===//
// ConstComplexAttr
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index 5f53a6335f37d..9050db5efc977 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -424,6 +424,44 @@ cir::ConstVectorAttr::verify(function_ref<InFlightDiagnostic()> emitError,
return elementTypeCheck;
}
+//===----------------------------------------------------------------------===//
+// CIR VTableAttr
+//===----------------------------------------------------------------------===//
+
+LogicalResult cir::VTableAttr::verify(
+ llvm::function_ref<mlir::InFlightDiagnostic()> emitError, mlir::Type type,
+ mlir::ArrayAttr vtableData) {
+ auto sTy = mlir::dyn_cast_if_present<cir::RecordType>(type);
+ if (!sTy)
+ return emitError() << "expected !cir.record type result";
+ if (sTy.getMembers().empty() || vtableData.empty())
+ return emitError() << "expected record type with one or more subtype";
+
+ for (size_t i = 0; i < sTy.getMembers().size(); ++i) {
+ auto constArrayAttr = mlir::dyn_cast<cir::ConstArrayAttr>(vtableData[i]);
+ if (!constArrayAttr)
+ return emitError() << "expected constant array subtype";
+
+ if (cir::ConstRecordAttr::verify(emitError, type, vtableData).failed())
+ return failure();
+
+ LogicalResult eltTypeCheck = success();
+ auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts());
+ arrayElts.walkImmediateSubElements(
+ [&](Attribute attr) {
+ if (mlir::isa<ConstPtrAttr, GlobalViewAttr>(attr))
+ return;
+
+ eltTypeCheck = emitError()
+ << "expected GlobalViewAttr or ConstPtrAttr";
+ },
+ [&](Type type) {});
+ if (eltTypeCheck.failed())
+ return eltTypeCheck;
+ }
+ return success();
+}
+
//===----------------------------------------------------------------------===//
// CIR Dialect
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 72feee8709dc4..0a7a8793943aa 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -342,7 +342,8 @@ static LogicalResult checkConstantTypes(mlir::Operation *op, mlir::Type opType,
if (mlir::isa<cir::ConstArrayAttr, cir::ConstVectorAttr,
cir::ConstComplexAttr, cir::ConstRecordAttr,
- cir::GlobalViewAttr, cir::PoisonAttr>(attrType))
+ cir::GlobalViewAttr, cir::PoisonAttr, cir::VTableAttr>(
+ attrType))
return success();
assert(isa<TypedAttr>(attrType) && "What else could we be looking at here?");
diff --git a/clang/test/CIR/IR/invalid-vtable.cir b/clang/test/CIR/IR/invalid-vtable.cir
index b3afb581b2048..0c8a36d26a0a1 100644
--- a/clang/test/CIR/IR/invalid-vtable.cir
+++ b/clang/test/CIR/IR/invalid-vtable.cir
@@ -1,4 +1,4 @@
-// RUN: cir-opt %s -verify-diagnostics
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
!s8i = !cir.int<s, 8>
!u32i = !cir.int<u, 32>
@@ -7,3 +7,51 @@ cir.func @reference_unknown_vtable() {
%0 = cir.vtable.address_point(@some_vtable, address_point = <index = 0, offset = 2>) : !cir.vptr
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>)
+}
diff --git a/clang/test/CIR/IR/vtable-attr.cir b/clang/test/CIR/IR/vtable-attr.cir
new file mode 100644
index 0000000000000..4826bdcc90953
--- /dev/null
+++ b/clang/test/CIR/IR/vtable-attr.cir
@@ -0,0 +1,12 @@
+// RUN: cir-opt %s | FileCheck %s
+
+!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 {
+ 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.func private dso_local @_ZN1S3keyEv(%arg0: !cir.ptr<!rec_S>)
+ cir.func private dso_local @_ZN1S6nonKeyEv(%arg0: !cir.ptr<!rec_S>)
+}
|
| // VTableAttr | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { | |
| def CIR_VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { |
| def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { | ||
| let summary = "Represents a C++ vtable"; | ||
| let description = [{ | ||
| Wraps a #cir.const_record containing vtable data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get how this wraps record, since it wraps vtable array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can potentially wrap a record containing multiple vtable arrays. In the simple case, we create an anonymous record that has a single array element, which looks like this:
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_struct2 {alignment = 8 : i64 }
But in cases involving multiple inheritence, there can be multiple vtables within the record, like this:
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_struct4 {alignment = 8 : i64}
The index attribute in the address_point selects which of the wrapped vtables is being referenced:
` %6 = cir.vtable.address_point(@_ZTV5Child, address_point = <index = 1, offset = 2>) : !cir.vptr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah makes sense, thanks for explanation. With example this will be clearer :)
| ``` | ||
| }]; | ||
|
|
||
| // `vtable_data` is a const record with one element, containing an array of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, it is not const_record but ArrayAttr?
Wasn't the intent to say vtable_data is a single element of vtable const record?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, there can be multiple arrays. I'll update this comment to reflect that and add an example with two arrays.
| let parameters = (ins AttributeSelfTypeParameter<"">:$type, | ||
| "mlir::ArrayAttr":$vtable_data); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let parameters = (ins AttributeSelfTypeParameter<"">:$type, | |
| "mlir::ArrayAttr":$vtable_data); | |
| let parameters = (ins | |
| AttributeSelfTypeParameter<"">:$type, | |
| "mlir::ArrayAttr":$vtable_data | |
| ); |
| LogicalResult eltTypeCheck = success(); | ||
| auto arrayElts = mlir::cast<ArrayAttr>(constArrayAttr.getElts()); | ||
| arrayElts.walkImmediateSubElements( | ||
| [&](Attribute attr) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| [&](Attribute attr) { | |
| [&](mlir::Attribute attr) { |
| eltTypeCheck = emitError() | ||
| << "expected GlobalViewAttr or ConstPtrAttr"; | ||
| }, | ||
| [&](Type type) {}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| [&](Type type) {}); | |
| [&](mlir::Type type) {}); |
| if (sTy.getMembers().empty() || vtableData.empty()) | ||
| return emitError() << "expected record type with one or more subtype"; | ||
|
|
||
| for (size_t i = 0; i < sTy.getMembers().size(); ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be only one member, so the loop is a bit misleading here.
Cannot we just assert that size == 1 and that the single element is the expected array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe it is completely redundant check as cir::ConstRecordAttr::verify checks this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same thing when I was moving this over and had changed it in the way you suggest before finding the multi-vtable example (clang/test/CIR/CodeGen/multi-vtable.cpp in the incubator). It didn't occur to me until just now that, like you, I probably thought that because of what the comments in the attribute declaration said.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we use a range based for loop here, or if we need the index, use llvm::enumerate instead? We’re currently just using i for indexing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good suggestion. Thanks. It also led me to notice something I had missed.
|
Is this also meant to cover VTTs? |
As we discussed earlier, this isn't used for VTTs. Currently, the incubator emits VTTs as a constant array of global view pointers to vtables. We may want to consider a wrapper attribute similar to this for VTTs. |
| def VTableAttr : CIR_Attr<"VTable", "vtable", [TypedAttrInterface]> { | ||
| let summary = "Represents a C++ vtable"; | ||
| let description = [{ | ||
| Wraps a #cir.const_record containing vtable data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can potentially wrap a record containing multiple vtable arrays. In the simple case, we create an anonymous record that has a single array element, which looks like this:
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_struct2 {alignment = 8 : i64 }
But in cases involving multiple inheritence, there can be multiple vtables within the record, like this:
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_struct4 {alignment = 8 : i64}
The index attribute in the address_point selects which of the wrapped vtables is being referenced:
` %6 = cir.vtable.address_point(@_ZTV5Child, address_point = <index = 1, offset = 2>) : !cir.vptr
|
|
||
| Example: | ||
| ``` | ||
| cir.global linkonce_odr @_ZTV1B = #cir.vtable<< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There seem to be extra brackets in this example, which may be making it harder to understand.
| ``` | ||
| }]; | ||
|
|
||
| // `vtable_data` is a const record with one element, containing an array of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned above, there can be multiple arrays. I'll update this comment to reflect that and add an example with two arrays.
| if (sTy.getMembers().empty() || vtableData.empty()) | ||
| return emitError() << "expected record type with one or more subtype"; | ||
|
|
||
| for (size_t i = 0; i < sTy.getMembers().size(); ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same thing when I was moving this over and had changed it in the way you suggest before finding the multi-vtable example (clang/test/CIR/CodeGen/multi-vtable.cpp in the incubator). It didn't occur to me until just now that, like you, I probably thought that because of what the comments in the attribute declaration said.
bcardosolopes
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, minor suggestion
| // vtable information. | ||
| let parameters = (ins | ||
| AttributeSelfTypeParameter<"">:$type, | ||
| "mlir::ArrayAttr":$vtable_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we just call this one data instead of vtable_data? To use the param you need the casted attribute anyways, so repeating the name might not help much here.
This adds the #cir.vtable attribute definition and verification. Generation of the vtable will be implemented in a later change.