Author: Andy Kaylor Date: 2025-08-21T09:52:14-07:00 New Revision: c5466c64d42ba58d2a2a7df5f8032d2c8b017d83
URL: https://github.com/llvm/llvm-project/commit/c5466c64d42ba58d2a2a7df5f8032d2c8b017d83 DIFF: https://github.com/llvm/llvm-project/commit/c5466c64d42ba58d2a2a7df5f8032d2c8b017d83.diff LOG: [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. Added: clang/test/CIR/IR/vtable-attr.cir Modified: clang/include/clang/CIR/Dialect/IR/CIRAttrs.td clang/lib/CIR/Dialect/IR/CIRAttrs.cpp clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/test/CIR/IR/invalid-vtable.cir Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td index 8d4b6d53bc10f..16b818f851e1c 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td +++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td @@ -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 //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp index 5f53a6335f37d..95faad6746955 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 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 //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 167b970cdda12..b5d4be8da64ab 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..60aa9b29e2677 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,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>) +} diff --git a/clang/test/CIR/IR/vtable-attr.cir b/clang/test/CIR/IR/vtable-attr.cir new file mode 100644 index 0000000000000..3854208ff78cc --- /dev/null +++ b/clang/test/CIR/IR/vtable-attr.cir @@ -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>) +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits