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