From: Enes Cevik Date: Tue, 23 Jun 2026 15:42:17 +0000 (+0300) Subject: gccrs: backend: Refactor dynamic object fat pointers and vtable generation X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84db13f68e143c844b787a55e80ca4c9c07076be;p=thirdparty%2Fgcc.git 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 --- diff --git a/gcc/rust/backend/rust-compile-context.h b/gcc/rust/backend/rust-compile-context.h index 336fbeea780..c86ac349f85 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 pair, ::Bvariable *vtable) + { + compiled_vtables[pair] = vtable; + } + + bool lookup_vtable (std::pair 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 compiled_fn_map; std::map compiled_consts; std::map compiled_labels; + std::map, ::Bvariable *> compiled_vtables; std::vector<::std::vector> statements; std::vector scope_stack; std::vector<::std::vector> block_drop_candidates; diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc index 79fe6c49e85..37a9c702ae8 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 da6a8ee4a00..e71d404f1fa 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 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 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 5db998d9b62..c2ef00b8bf9 100644 --- a/gcc/rust/backend/rust-compile.cc +++ b/gcc/rust/backend/rust-compile.cc @@ -17,6 +17,7 @@ // . #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 vtable_ctor_elems; - std::vector 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 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 dyn_ctor = {address_of_compiled_ref, vtable_ctor}; + tree address_of_vtable = build_fold_addr_expr_loc (locus, vtable_decl); + std::vector 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 00000000000..d7ba7f57061 --- /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 + } +}