From: Enes Cevik <[email protected]>

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]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/19e5e305e582bcc7793c97b36d73b7c976515644

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4599

 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 ++++++++++++++-----
 .../rust/execute/torture/dyn_object.rs        | 73 ++++++++++++++++
 5 files changed, 182 insertions(+), 35 deletions(-)
 create mode 100644 gcc/testsuite/rust/execute/torture/dyn_object.rs

diff --git a/gcc/rust/backend/rust-compile-context.h 
b/gcc/rust/backend/rust-compile-context.h
index 336fbeea7..c86ac349f 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 79fe6c49e..37a9c702a 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 da6a8ee4a..e71d404f1 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 5db998d9b..c2ef00b8b 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 000000000..d7ba7f570
--- /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
+    }
+}

base-commit: 26ca5e38dbf233f9baabbf46684d55986ea4c7fa
-- 
2.54.0

Reply via email to