https://gcc.gnu.org/g:84db13f68e143c844b787a55e80ca4c9c07076be

commit r17-2248-g84db13f68e143c844b787a55e80ca4c9c07076be
Author: Enes Cevik <[email protected]>
Date:   Tue Jun 23 18:42:17 2026 +0300

    gccrs: backend: Refactor dynamic object fat pointers and vtable generation
    
    Previously, the compiler incorrectly embedded the vtable as an array
    directly inside the fat pointer. Furthermore, it lacked essential trait
    object metadata such as 'size', 'align' and 'drop_in_place'.
    
    Now, the fat pointer is strictly fixed to 2 words. The vtable is
    generated as a separate global static struct and it's structure
    correctly includes 'drop_in_place', 'size' and 'align' fields, followed
    by trait methods. Also implemented a caching mechanism in the
    compilation context to prevent duplicate vtable generation and linker
    conflicts for the same Type-Trait combinations.
    
    gcc/rust/ChangeLog:
    
            * backend/rust-compile-context.h (class Context): Add
            insert_vtable and lookup_vtable methods.
            * backend/rust-compile-expr.cc
            (CompileExpr::get_fn_addr_from_dyn): Change access method
            array access to struct field access.
            * backend/rust-compile-type.cc
            (TyTyResolveCompile::create_dyn_obj_record): Create vtable type
            as a record instead of an array.
            * backend/rust-compile.cc
            (HIRCompileBase::coerce_to_dyn_object): Change vtable structure.
    
    gcc/testsuite/ChangeLog:
    
            * rust/execute/torture/dyn_object.rs: New test.
    
    Signed-off-by: Enes Cevik <[email protected]>

Diff:
---
 gcc/rust/backend/rust-compile-context.h          | 16 +++++
 gcc/rust/backend/rust-compile-expr.cc            | 15 +++--
 gcc/rust/backend/rust-compile-type.cc            | 30 +++++----
 gcc/rust/backend/rust-compile.cc                 | 83 ++++++++++++++++++------
 gcc/testsuite/rust/execute/torture/dyn_object.rs | 73 +++++++++++++++++++++
 5 files changed, 182 insertions(+), 35 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-context.h 
b/gcc/rust/backend/rust-compile-context.h
index 336fbeea7807..c86ac349f850 100644
--- a/gcc/rust/backend/rust-compile-context.h
+++ b/gcc/rust/backend/rust-compile-context.h
@@ -298,6 +298,21 @@ public:
     return true;
   }
 
+  void insert_vtable (std::pair<size_t, size_t> pair, ::Bvariable *vtable)
+  {
+    compiled_vtables[pair] = vtable;
+  }
+
+  bool lookup_vtable (std::pair<size_t, size_t> pair, ::Bvariable **vtable)
+  {
+    auto it = compiled_vtables.find (pair);
+    if (it == compiled_vtables.end ())
+      return false;
+
+    *vtable = it->second;
+    return true;
+  }
+
   void push_fn (tree fn, ::Bvariable *ret_addr, TyTy::BaseType *retty)
   {
     fn_stack.emplace_back (fn, ret_addr, retty);
@@ -434,6 +449,7 @@ private:
   std::map<HirId, tree> compiled_fn_map;
   std::map<HirId, tree> compiled_consts;
   std::map<HirId, tree> compiled_labels;
+  std::map<std::pair<size_t, size_t>, ::Bvariable *> compiled_vtables;
   std::vector<::std::vector<tree>> statements;
   std::vector<tree> scope_stack;
   std::vector<::std::vector<DropCandidate>> block_drop_candidates;
diff --git a/gcc/rust/backend/rust-compile-expr.cc 
b/gcc/rust/backend/rust-compile-expr.cc
index 79fe6c49e857..37a9c702ae81 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -1646,12 +1646,19 @@ CompileExpr::get_fn_addr_from_dyn (const 
TyTy::DynamicObjectType *dyn,
 
   tree vtable_ptr
     = Backend::struct_field_expression (receiver_ref, 1, expr_locus);
-  tree vtable_array_access
-    = build4_loc (expr_locus, ARRAY_REF, TREE_TYPE (TREE_TYPE (vtable_ptr)),
-                 vtable_ptr, idx, NULL_TREE, NULL_TREE);
+  tree vtable_struct_type = TREE_TYPE (TREE_TYPE (vtable_ptr));
+  rust_assert (TREE_CODE (vtable_struct_type) == RECORD_TYPE);
+  tree vtable_field = TYPE_FIELDS (vtable_struct_type);
+  for (size_t i = 0; i < offs + 3; i++) // drop, size, align, [methods]
+    vtable_field = DECL_CHAIN (vtable_field);
+  rust_assert (vtable_field != NULL_TREE);
+  tree vtable = build_fold_indirect_ref_loc (expr_locus, vtable_ptr);
+  tree vtable_field_access
+    = build3_loc (expr_locus, COMPONENT_REF, TREE_TYPE (vtable_field), vtable,
+                 vtable_field, NULL_TREE);
 
   tree vcall = build3_loc (expr_locus, OBJ_TYPE_REF, expected_fntype,
-                          vtable_array_access, receiver_ref, idx);
+                          vtable_field_access, receiver_ref, idx);
 
   return vcall;
 }
diff --git a/gcc/rust/backend/rust-compile-type.cc 
b/gcc/rust/backend/rust-compile-type.cc
index da6a8ee4a000..e71d404f1fac 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -828,22 +828,30 @@ TyTyResolveCompile::visit (const TyTy::OpaqueType &type)
 tree
 TyTyResolveCompile::create_dyn_obj_record (const TyTy::DynamicObjectType &type)
 {
+  location_t locus = ctx->get_mappings ().lookup_location (type.get_ty_ref ());
   // create implicit struct
-  auto items = type.get_object_items ();
   std::vector<Backend::typed_identifier> fields;
 
-  tree uint = Backend::integer_type (true, Backend::get_pointer_size ());
-  tree uintptr_ty = build_pointer_type (uint);
+  tree voidptr_ty = build_pointer_type (void_type_node);
 
-  fields.emplace_back ("pointer", uintptr_ty,
-                      ctx->get_mappings ().lookup_location (
-                        type.get_ty_ref ()));
+  fields.emplace_back ("data", voidptr_ty, locus);
 
-  tree vtable_size = build_int_cst (size_type_node, items.size ());
-  tree vtable_type = Backend::array_type (uintptr_ty, vtable_size);
-  fields.emplace_back ("vtable", vtable_type,
-                      ctx->get_mappings ().lookup_location (
-                        type.get_ty_ref ()));
+  std::vector<Backend::typed_identifier> vtable_fields;
+
+  // drop_in_place is not implemented yet!
+  vtable_fields.emplace_back ("__drop_in_place", voidptr_ty, locus);
+  vtable_fields.emplace_back ("__size", size_type_node, locus);
+  vtable_fields.emplace_back ("__align", size_type_node, locus);
+
+  size_t items_size = type.get_object_items ().size ();
+  for (size_t method_idx = 0; method_idx < items_size; method_idx++)
+    vtable_fields.emplace_back ("__method_" + std::to_string (method_idx),
+                               voidptr_ty, locus);
+
+  tree vtable_record = Backend::struct_type (vtable_fields);
+  tree vtable_ptr_ty = build_pointer_type (vtable_record);
+
+  fields.emplace_back ("vtable", vtable_ptr_ty, locus);
 
   tree record = Backend::struct_type (fields);
   RS_DST_FLAG (record) = 1;
diff --git a/gcc/rust/backend/rust-compile.cc b/gcc/rust/backend/rust-compile.cc
index 5db998d9b623..c2ef00b8bf95 100644
--- a/gcc/rust/backend/rust-compile.cc
+++ b/gcc/rust/backend/rust-compile.cc
@@ -17,6 +17,7 @@
 // <http://www.gnu.org/licenses/>.
 
 #include "rust-compile.h"
+#include "fold-const.h"
 #include "rust-compile-item.h"
 #include "rust-compile-implitem.h"
 #include "rust-hir-type-bounds.h"
@@ -193,39 +194,81 @@ HIRCompileBase::coerce_to_dyn_object (tree compiled_ref, 
TyTy::BaseType *actual,
 
   tree dynamic_object = TyTyResolveCompile::compile (ctx, &r);
   tree dynamic_object_fields = TYPE_FIELDS (dynamic_object);
-  tree vtable_field = DECL_CHAIN (dynamic_object_fields);
-  rust_assert (TREE_CODE (TREE_TYPE (vtable_field)) == ARRAY_TYPE);
+  tree vtableptr_field = DECL_CHAIN (dynamic_object_fields);
+  rust_assert (TREE_CODE (TREE_TYPE (vtableptr_field)) == POINTER_TYPE);
+  rust_assert (TREE_CODE (TREE_TYPE (TREE_TYPE (vtableptr_field))));
 
   //' this assumes ordering and current the structure is
   // __trait_object_ptr
-  // [list of function ptrs]
+  // __trait_vtable_ptr
 
   auto probed_bounds_for_receiver = Resolver::TypeBoundsProbe::Probe (actual);
   tree address_of_compiled_ref = null_pointer_node;
   if (!actual->is_unit ())
     address_of_compiled_ref = address_expression (compiled_ref, locus);
 
-  std::vector<tree> vtable_ctor_elems;
-  std::vector<unsigned long> vtable_ctor_idx;
-  unsigned long i = 0;
-  for (auto &bound : ty->get_object_items ())
+  size_t dyn_obj_ty_hash = TYPE_HASH (dynamic_object);
+  size_t compiled_ref_ty_hash = TYPE_HASH (TREE_TYPE (compiled_ref));
+  auto pair = std::make_pair (compiled_ref_ty_hash, dyn_obj_ty_hash);
+
+  tree vtable_decl = NULL_TREE;
+  Bvariable *cached_vtable = nullptr;
+  if (ctx->lookup_vtable (pair, &cached_vtable))
     {
-      const Resolver::TraitItemReference *item = bound.first;
-      const TyTy::TypeBoundPredicate *predicate = bound.second;
-
-      auto address = compute_address_for_trait_item (item, predicate,
-                                                    probed_bounds_for_receiver,
-                                                    actual, actual, locus);
-      vtable_ctor_elems.push_back (address);
-      vtable_ctor_idx.push_back (i++);
+      vtable_decl = Backend::var_expression (cached_vtable, locus);
     }
+  else
+    {
+      tree compiled_ref_ty = TREE_TYPE (compiled_ref);
+      // drop_in_place is not implemented yet!
+      tree drop_in_place = null_pointer_node;
+      tree size = TYPE_SIZE_UNIT (compiled_ref_ty);
+      tree align
+       = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (compiled_ref_ty));
+
+      std::vector<tree> vtable_ctor_elems;
+      vtable_ctor_elems.emplace_back (drop_in_place);
+      vtable_ctor_elems.emplace_back (size);
+      vtable_ctor_elems.emplace_back (align);
+
+      for (auto &bound : ty->get_object_items ())
+       {
+         const Resolver::TraitItemReference *item = bound.first;
+         const TyTy::TypeBoundPredicate *predicate = bound.second;
+
+         auto address
+           = compute_address_for_trait_item (item, predicate,
+                                             probed_bounds_for_receiver,
+                                             actual, actual, locus);
+         vtable_ctor_elems.push_back (address);
+       }
+      tree vtable_ctor
+       = Backend::constructor_expression (TREE_TYPE (
+                                            TREE_TYPE (vtableptr_field)),
+                                          false, vtable_ctor_elems, -1, locus);
+
+      std::string vtable_name = "__R_vtable_"
+                               + std::to_string (compiled_ref_ty_hash) + "_"
+                               + std::to_string (dyn_obj_ty_hash);
+
+      // 
https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_mir/src/interpret/traits.rs#L17
+      Bvariable *vtable_bvar
+       = Backend::global_variable (vtable_name, vtable_name,
+                                   TREE_TYPE (vtable_ctor), false, true, true,
+                                   locus);
+      Backend::global_variable_set_init (vtable_bvar, vtable_ctor);
+      ctx->push_var (vtable_bvar);
+      ctx->insert_vtable (pair, vtable_bvar);
+
+      vtable_decl = Backend::var_expression (vtable_bvar, locus);
 
-  tree vtable_ctor
-    = Backend::array_constructor_expression (TREE_TYPE (vtable_field),
-                                            vtable_ctor_idx, vtable_ctor_elems,
-                                            locus);
+      DECL_ARTIFICIAL (vtable_decl) = 1;
+      TREE_READONLY (vtable_decl) = 1;
+      DECL_IGNORED_P (vtable_decl) = 1;
+    }
 
-  std::vector<tree> dyn_ctor = {address_of_compiled_ref, vtable_ctor};
+  tree address_of_vtable = build_fold_addr_expr_loc (locus, vtable_decl);
+  std::vector<tree> dyn_ctor = {address_of_compiled_ref, address_of_vtable};
   return Backend::constructor_expression (dynamic_object, false, dyn_ctor, -1,
                                          locus);
 }
diff --git a/gcc/testsuite/rust/execute/torture/dyn_object.rs 
b/gcc/testsuite/rust/execute/torture/dyn_object.rs
new file mode 100644
index 000000000000..d7ba7f570619
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/dyn_object.rs
@@ -0,0 +1,73 @@
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+trait TraitA {
+    fn do_a(&self) -> i32;
+}
+
+trait TraitB {
+    fn do_b(&self) -> i32;
+}
+
+trait TraitC {}
+
+struct StructX {
+    val: i32,
+}
+struct StructY {
+    val: i32,
+}
+
+impl TraitA for StructX {
+    fn do_a(&self) -> i32 {
+        self.val
+    }
+}
+
+impl TraitB for StructX {
+    fn do_b(&self) -> i32 {
+        self.val * 2
+    }
+}
+
+impl TraitC for StructX {}
+
+impl TraitA for StructY {
+    fn do_a(&self) -> i32 {
+        self.val + 10
+    }
+}
+
+fn main() -> i32 {
+    let x = StructX { val: 0 };
+    let x1 = StructX { val: 1 };
+    let x2 = StructX { val: 2 };
+    let y = StructY { val: 3 };
+
+    let _dyn_a_x: &dyn TraitC = &x;
+
+    let dyn_a_x1: &dyn TraitA = &x1;
+
+    let dyn_a_x2: &dyn TraitA = &x2;
+
+    let dyn_a_y: &dyn TraitA = &y;
+
+    let dyn_b_x1: &dyn TraitB = &x1;
+
+    if x1.val != 1 || x2.val != 2 || y.val != 3 {
+        1
+    } else if dyn_a_x1.do_a() != 1 {
+        2
+    } else if dyn_a_x2.do_a() != 2 {
+        3
+    } else if dyn_a_y.do_a() != 13 {
+        4
+    } else if dyn_b_x1.do_b() != 2 {
+        5
+    } else {
+        0
+    }
+}

Reply via email to