Skip to content

Commit bc136cd

Browse files
XrXrkddnewton
andauthored
Port code for marking global GC objects (ruby#229)
Co-authored-by: Kevin Newton <[email protected]> Co-authored-by: Kevin Newton <[email protected]>
1 parent ba60a80 commit bc136cd

File tree

4 files changed

+51
-52
lines changed

4 files changed

+51
-52
lines changed

yjit/bindgen/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ fn main() {
8181
// From include/ruby/internal/core/rclass.h
8282
.allowlist_function("rb_class_get_superclass")
8383

84+
// From include/ruby/internal/intern/gc.h
85+
.allowlist_function("rb_gc_mark")
86+
8487
// VALUE variables for Ruby class objects
8588
// From include/ruby/internal/globals.h
8689
.allowlist_var("rb_cBasicObject")

yjit/src/cruby_bindings.inc.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ extern "C" {
130130
extern "C" {
131131
pub fn rb_intern(name: *const ::std::os::raw::c_char) -> ID;
132132
}
133+
extern "C" {
134+
pub fn rb_gc_mark(obj: VALUE);
135+
}
133136
extern "C" {
134137
pub fn rb_obj_is_kind_of(obj: VALUE, klass: VALUE) -> VALUE;
135138
}

yjit/src/invariants.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,43 @@ pub extern "C" fn rb_yjit_constant_state_changed() {
201201
}
202202
}
203203

204+
/// Callback for marking GC objects inside [Invariants].
205+
/// See `struct yjijt_root_struct` in C.
206+
#[no_mangle]
207+
pub extern "C" fn rb_yjit_root_mark() {
208+
// Comment from C YJIT:
209+
//
210+
// Why not let the GC move the cme keys in this table?
211+
// Because this is basically a compare_by_identity Hash.
212+
// If a key moves, we would need to reinsert it into the table so it is rehashed.
213+
// That is tricky to do, espcially as it could trigger allocation which could
214+
// trigger GC. Not sure if it is okay to trigger GC while the GC is updating
215+
// references.
216+
//
217+
// NOTE(alan): since we are using Rust data structures that don't interact
218+
// with the Ruby GC now, it might be feasible to allow movement.
219+
220+
let invariants = Invariants::get_instance();
221+
222+
// Mark CME imemos
223+
for cme in invariants.cme_validity.keys() {
224+
let cme = (*cme) as usize;
225+
let cme = VALUE(cme);
226+
227+
unsafe { rb_gc_mark(cme) };
228+
}
229+
230+
// Mark class and iclass objects
231+
for klass in invariants.method_lookup.keys() {
232+
// TODO: This is a leak. Unused blocks linger in the table forever, preventing the
233+
// callee class they speculate on from being collected.
234+
// We could do a bespoke weak reference scheme on classes similar to
235+
// the interpreter's call cache. See finalizer for T_CLASS and cc_table_free().
236+
237+
unsafe { rb_gc_mark(*klass) };
238+
}
239+
}
240+
204241
/*
205242
static void
206243
yjit_block_assumptions_free(block_t *block)

yjit_iface.c

Lines changed: 8 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -61,54 +61,9 @@ check_cfunc_dispatch(VALUE receiver, struct rb_callinfo *ci, void *callee, rb_ca
6161

6262
// GC root for interacting with the GC
6363
struct yjit_root_struct {
64-
int unused; // empty structs are not legal in C99
64+
bool unused; // empty structs are not legal in C99
6565
};
6666

67-
// Map klass => id_table[mid, set of blocks]
68-
// While a block `b` is in the table, b->callee_cme == rb_callable_method_entry(klass, mid).
69-
// See assume_method_lookup_stable()
70-
static st_table *method_lookup_dependency;
71-
72-
// For adding to method_lookup_dependency data with st_update
73-
struct lookup_dependency_insertion {
74-
//block_t *block;
75-
ID mid;
76-
};
77-
78-
// Map cme => set of blocks
79-
// See assume_method_lookup_stable()
80-
static st_table *cme_validity_dependency;
81-
82-
static int
83-
mark_and_pin_keys_i(st_data_t k, st_data_t v, st_data_t ignore)
84-
{
85-
rb_gc_mark((VALUE)k);
86-
87-
return ST_CONTINUE;
88-
}
89-
90-
// GC callback during mark phase
91-
static void
92-
yjit_root_mark(void *ptr)
93-
{
94-
if (method_lookup_dependency) {
95-
// TODO: This is a leak. Unused blocks linger in the table forever, preventing the
96-
// callee class they speculate on from being collected.
97-
// We could do a bespoke weak reference scheme on classes similar to
98-
// the interpreter's call cache. See finalizer for T_CLASS and cc_table_free().
99-
st_foreach(method_lookup_dependency, mark_and_pin_keys_i, 0);
100-
}
101-
102-
if (cme_validity_dependency) {
103-
// Why not let the GC move the cme keys in this table?
104-
// Because this is basically a compare_by_identity Hash.
105-
// If a key moves, we would need to reinsert it into the table so it is rehashed.
106-
// That is tricky to do, espcially as it could trigger allocation which could
107-
// trigger GC. Not sure if it is okay to trigger GC while the GC is updating
108-
// references.
109-
st_foreach(cme_validity_dependency, mark_and_pin_keys_i, 0);
110-
}
111-
}
11267

11368
static void
11469
yjit_root_free(void *ptr)
@@ -120,20 +75,23 @@ static size_t
12075
yjit_root_memsize(const void *ptr)
12176
{
12277
// Count off-gc-heap allocation size of the dependency table
123-
return st_memsize(method_lookup_dependency); // TODO: more accurate accounting
78+
return 0; // TODO: more accurate accounting
12479
}
12580

12681
// GC callback during compaction
12782
static void
12883
yjit_root_update_references(void *ptr)
12984
{
85+
// Do nothing since we use rb_gc_mark(), which pins.
13086
}
13187

88+
void rb_yjit_root_mark(void *ptr); // in Rust
89+
13290
// Custom type for interacting with the GC
13391
// TODO: make this write barrier protected
13492
static const rb_data_type_t yjit_root_type = {
13593
"yjit_root",
136-
{yjit_root_mark, yjit_root_free, yjit_root_memsize, yjit_root_update_references},
94+
{rb_yjit_root_mark, yjit_root_free, yjit_root_memsize, yjit_root_update_references},
13795
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
13896
};
13997

@@ -361,12 +319,10 @@ rb_yjit_init(void)
361319

362320
// Call the Rust initialization code
363321
void rb_yjit_init_rust(void);
364-
return rb_yjit_init_rust();
322+
rb_yjit_init_rust();
365323

366-
/*
367-
// Initialize the GC hooks
324+
// Initialize the GC hooks. Do this second as some code depend on Rust initialization.
368325
struct yjit_root_struct *root;
369326
VALUE yjit_root = TypedData_Make_Struct(0, struct yjit_root_struct, &yjit_root_type, root);
370327
rb_gc_register_mark_object(yjit_root);
371-
*/
372328
}

0 commit comments

Comments
 (0)