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

Reply via email to