Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,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
//===----------------------------------------------------------------------===//
Expand Down
38 changes: 38 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CIR/Dialect/IR/CIRDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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?");
Expand Down
66 changes: 65 additions & 1 deletion clang/test/CIR/IR/invalid-vtable.cir
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -7,3 +7,67 @@ 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>)
}

// -----

!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>)
}
19 changes: 19 additions & 0 deletions clang/test/CIR/IR/vtable-attr.cir
Original file line number Diff line number Diff line change
@@ -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>)
}