Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions gc.c
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,14 @@ rb_gc_get_ractor_newobj_cache(void)
return GET_RACTOR()->newobj_cache;
}

#if USE_MODULAR_GC
void
rb_gc_initialize_vm_context(struct rb_gc_vm_context *context)
{
rb_native_mutex_initialize(&context->lock);
context->ec = GET_EC();
}

#if USE_MODULAR_GC
void
rb_gc_worker_thread_set_vm_context(struct rb_gc_vm_context *context)
{
Expand Down Expand Up @@ -626,6 +626,7 @@ typedef struct gc_function_map {
void (*config_set)(void *objspace_ptr, VALUE hash);
void (*stress_set)(void *objspace_ptr, VALUE flag);
VALUE (*stress_get)(void *objspace_ptr);
struct rb_gc_vm_context *(*get_vm_context)(void *objspace_ptr);
// Object allocation
VALUE (*new_obj)(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size);
size_t (*obj_slot_size)(VALUE obj);
Expand Down Expand Up @@ -804,6 +805,7 @@ ruby_modular_gc_init(void)
load_modular_gc_func(config_get);
load_modular_gc_func(stress_set);
load_modular_gc_func(stress_get);
load_modular_gc_func(get_vm_context);
// Object allocation
load_modular_gc_func(new_obj);
load_modular_gc_func(obj_slot_size);
Expand Down Expand Up @@ -891,6 +893,7 @@ ruby_modular_gc_init(void)
# define rb_gc_impl_config_set rb_gc_functions.config_set
# define rb_gc_impl_stress_set rb_gc_functions.stress_set
# define rb_gc_impl_stress_get rb_gc_functions.stress_get
# define rb_gc_impl_get_vm_context rb_gc_functions.get_vm_context
// Object allocation
# define rb_gc_impl_new_obj rb_gc_functions.new_obj
# define rb_gc_impl_obj_slot_size rb_gc_functions.obj_slot_size
Expand Down Expand Up @@ -3238,10 +3241,23 @@ gc_declarative_marking_p(const rb_data_type_t *type)
return (type->flags & RUBY_TYPED_DECL_MARKING) != 0;
}

rb_execution_context_t *
rb_gc_get_ec(void)
{
void *objspace = rb_gc_get_objspace();

if (RB_LIKELY(rb_gc_impl_during_gc_p(objspace))) {
return rb_gc_impl_get_vm_context(objspace)->ec;
}
else {
return GET_EC();
}
}

void
rb_gc_mark_roots(void *objspace, const char **categoryp)
{
rb_execution_context_t *ec = GET_EC();
rb_execution_context_t *ec = rb_gc_get_ec();
rb_vm_t *vm = rb_ec_vm_ptr(ec);

#define MARK_CHECKPOINT(category) do { \
Expand Down
12 changes: 12 additions & 0 deletions gc/default/default.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,8 @@ typedef struct rb_objspace {
int sweeping_heap_count;

int fork_vm_lock_lev;

struct rb_gc_vm_context vm_context;
} rb_objspace_t;

#ifndef HEAP_PAGE_ALIGN_LOG
Expand Down Expand Up @@ -1652,6 +1654,14 @@ rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE ptr)
!RVALUE_MARKED(objspace, ptr);
}

struct rb_gc_vm_context *
rb_gc_impl_get_vm_context(void *objspace_ptr)
{
rb_objspace_t *objspace = objspace_ptr;

return &objspace->vm_context;
}

static void free_stack_chunks(mark_stack_t *);
static void mark_stack_free_cache(mark_stack_t *);
static void heap_page_free(rb_objspace_t *objspace, struct heap_page *page);
Expand Down Expand Up @@ -6767,6 +6777,8 @@ gc_marking_enter(rb_objspace_t *objspace)
if (MEASURE_GC) {
gc_clock_start(&objspace->profile.marking_start_time);
}

rb_gc_initialize_vm_context(&objspace->vm_context);
}

static void
Expand Down
2 changes: 0 additions & 2 deletions gc/gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
*/
#include "ruby/ruby.h"

#if USE_MODULAR_GC
#include "ruby/thread_native.h"

struct rb_gc_vm_context {
rb_nativethread_lock_t lock;

struct rb_execution_context_struct *ec;
};
#endif

typedef int (*vm_table_foreach_callback_func)(VALUE value, void *data);
typedef int (*vm_table_update_callback_func)(VALUE *value, void *data);
Expand Down
1 change: 1 addition & 0 deletions gc/gc_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ GC_IMPL_FN void rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag);
GC_IMPL_FN VALUE rb_gc_impl_stress_get(void *objspace_ptr);
GC_IMPL_FN VALUE rb_gc_impl_config_get(void *objspace_ptr);
GC_IMPL_FN void rb_gc_impl_config_set(void *objspace_ptr, VALUE hash);
GC_IMPL_FN struct rb_gc_vm_context *rb_gc_impl_get_vm_context(void *objspace_ptr);
// Object allocation
GC_IMPL_FN VALUE rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size);
GC_IMPL_FN size_t rb_gc_impl_obj_slot_size(VALUE obj);
Expand Down
12 changes: 8 additions & 4 deletions gc/mmtk/mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,7 @@ rb_mmtk_scan_gc_roots(void)
{
struct objspace *objspace = rb_gc_get_objspace();

// FIXME: Make `rb_gc_mark_roots` aware that the current thread may not have EC.
// See: https://github.com/ruby/mmtk/issues/22
rb_gc_worker_thread_set_vm_context(&objspace->vm_context);
rb_gc_mark_roots(objspace, NULL);
rb_gc_worker_thread_unset_vm_context(&objspace->vm_context);
}

static int
Expand Down Expand Up @@ -784,6 +780,14 @@ rb_gc_impl_config_set(void *objspace_ptr, VALUE hash)
// TODO
}

struct rb_gc_vm_context *
rb_gc_impl_get_vm_context(void *objspace_ptr)
{
struct objspace *objspace = objspace_ptr;

return &objspace->vm_context;
}

// Object allocation

static VALUE
Expand Down
1 change: 1 addition & 0 deletions internal/gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ RUBY_ATTR_MALLOC void *rb_xmalloc_mul_add_mul(size_t, size_t, size_t, size_t);
RUBY_ATTR_MALLOC void *rb_xcalloc_mul_add_mul(size_t, size_t, size_t, size_t);
void rb_gc_obj_id_moved(VALUE obj);
void rb_gc_register_pinning_obj(VALUE obj);
rb_execution_context_t *rb_gc_get_ec(void);

void *rb_gc_ractor_cache_alloc(rb_ractor_t *ractor);
void rb_gc_ractor_cache_free(void *cache);
Expand Down
4 changes: 2 additions & 2 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -3763,8 +3763,8 @@ rb_execution_context_mark(const rb_execution_context_t *ec)

/* mark machine stack */
if (ec->machine.stack_start && ec->machine.stack_end &&
ec != GET_EC() /* marked for current ec at the first stage of marking */
) {
/* marked for current ec at the first stage of marking */
ec != rb_gc_get_ec()) {
rb_gc_mark_machine_context(ec);
}

Expand Down
137 changes: 84 additions & 53 deletions zjit/src/hir/opt_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5645,15 +5645,26 @@ mod hir_opt_tests {

#[test]
fn test_specialize_multiple_monomorphic_setivar_with_shape_transition() {
eval("
def test
@foo = 1
@bar = 2
eval(r#"
klass = Class.new do
def test
@foo = 1
@bar = 2
end
end
test
");
assert_snapshot!(hir_string("test"), @"
fn test@<compiled>:3:

# Grow class max_iv_count so fresh instances can keep both writes
# on the embedded fast path.
warm = klass.new
warm.instance_variable_set(:@warm1, 1)
warm.instance_variable_set(:@warm2, 2)

obj = klass.new
obj.test
TEST = klass.instance_method(:test)
"#);
assert_snapshot!(hir_string_proc("TEST"), @"
fn test@<compiled>:4:
bb1():
EntryPoint interpreter
v1:BasicObject = LoadSelf
Expand All @@ -5675,7 +5686,10 @@ mod hir_opt_tests {
v14:HeapBasicObject = RefineType v6, HeapBasicObject
v17:Fixnum[2] = Const Value(2)
PatchPoint SingleRactorMode
SetIvar v14, :@bar, v17
StoreField v14, :@bar@0x1004, v17
WriteBarrier v14, v17
v40:CShape[0x1005] = Const CShape(0x1005)
StoreField v14, :_shape_id@0x1000, v40
CheckInterrupts
Return v17
");
Expand Down Expand Up @@ -7912,12 +7926,13 @@ mod hir_opt_tests {
set_call_threshold(3);
eval(r#"
class C
def foo_then_bar
def foo_then_many
@foo = 1
1000.times { |i| instance_variable_set(:"@v#{i}", i) }
@bar = 2
end

def bar_then_foo
def many_then_foo
1000.times { |i| instance_variable_set(:"@v#{i}", i) }
@bar = 3
@foo = 4
Expand All @@ -7927,14 +7942,14 @@ mod hir_opt_tests {
end

O1 = C.new
O1.foo_then_bar
O1.foo_then_many
O2 = C.new
O2.bar_then_foo
O2.many_then_foo
O1.foo
O2.foo
"#);
assert_snapshot!(hir_string_proc("C.instance_method(:foo)"), @"
fn foo@<compiled>:14:
fn foo@<compiled>:15:
bb1():
EntryPoint interpreter
v1:BasicObject = LoadSelf
Expand Down Expand Up @@ -7986,12 +8001,13 @@ mod hir_opt_tests {
set_call_threshold(6);
eval(r#"
class C
def foo_then_bar
def foo_then_many
@foo = 1
1000.times { |i| instance_variable_set(:"@v#{i}", i) }
@bar = 2
end

def bar_then_foo
def many_then_foo
1000.times { |i| instance_variable_set(:"@v#{i}", i) }
@bar = 3
@foo = 4
Expand All @@ -8001,17 +8017,17 @@ mod hir_opt_tests {
end

O1 = C.new
O1.foo_then_bar
O1.foo_then_many
O2 = C.new
O2.bar_then_foo
O2.many_then_foo
O1.foo
O1.foo
O1.foo
O1.foo
O2.foo
"#);
assert_snapshot!(hir_string_proc("C.instance_method(:foo)"), @"
fn foo@<compiled>:14:
fn foo@<compiled>:15:
bb1():
EntryPoint interpreter
v1:BasicObject = LoadSelf
Expand Down Expand Up @@ -15111,17 +15127,46 @@ mod hir_opt_tests {

#[test]
fn upgrade_self_type_to_heap_after_setivar() {
eval("
def test
@a = 1
@b = 2
@c = 3
@d = 4
end
test
");
assert_snapshot!(hir_string("test"), @"
fn test@<compiled>:3:
// Snapshot the overflow path only when this build naturally keeps five
// ivars embedded and overflows on the next write.
let obj = eval(r#"
klass = Class.new do
def initialize
@v0 = 0
@v1 = 1
@v2 = 2
@v3 = 3
@v4 = 4
end

def test
@overflow = 1
@after = 2
end
end

TEST = klass.instance_method(:test)
OBJ = klass.new
OBJ
"#);
// Skip builds where five ivars already force heap-backed storage.
if !obj.embedded_p() {
return;
}

// Make sure the next write is the one that overflows into heap-backed
// storage, so this snapshot still exercises the self-type upgrade path.
let probe = eval(r#"
probe = OBJ.class.new
probe.instance_variable_set(:@overflow, 1)
probe
"#);
if probe.embedded_p() {
return;
}
eval("OBJ.test");
assert_snapshot!(hir_string_proc("TEST"), @"
fn test@<compiled>:12:
bb1():
EntryPoint interpreter
v1:BasicObject = LoadSelf
Expand All @@ -15133,33 +15178,19 @@ mod hir_opt_tests {
bb3(v6:BasicObject):
v10:Fixnum[1] = Const Value(1)
PatchPoint SingleRactorMode
v42:HeapBasicObject = GuardType v6, HeapBasicObject
v43:CShape = LoadField v42, :_shape_id@0x1000
v44:CShape[0x1001] = GuardBitEquals v43, CShape(0x1001)
StoreField v42, :@a@0x1002, v10
WriteBarrier v42, v10
v47:CShape[0x1003] = Const CShape(0x1003)
StoreField v42, :_shape_id@0x1000, v47
SetIvar v6, :@overflow, v10
v14:HeapBasicObject = RefineType v6, HeapBasicObject
v17:Fixnum[2] = Const Value(2)
PatchPoint SingleRactorMode
SetIvar v14, :@b, v17
v21:HeapBasicObject = RefineType v14, HeapBasicObject
v24:Fixnum[3] = Const Value(3)
PatchPoint SingleRactorMode
SetIvar v21, :@c, v24
v28:HeapBasicObject = RefineType v21, HeapBasicObject
v31:Fixnum[4] = Const Value(4)
PatchPoint SingleRactorMode
v50:CShape = LoadField v28, :_shape_id@0x1000
v51:CShape[0x1004] = GuardBitEquals v50, CShape(0x1004)
v52:CPtr = LoadField v28, :_as_heap@0x1002
StoreField v52, :@d@0x1005, v31
WriteBarrier v28, v31
v55:CShape[0x1006] = Const CShape(0x1006)
StoreField v28, :_shape_id@0x1000, v55
v29:CShape = LoadField v14, :_shape_id@0x1000
v30:CShape[0x1001] = GuardBitEquals v29, CShape(0x1001)
v31:CPtr = LoadField v14, :_as_heap@0x1002
StoreField v31, :@after@0x1003, v17
WriteBarrier v14, v17
v34:CShape[0x1004] = Const CShape(0x1004)
StoreField v14, :_shape_id@0x1000, v34
CheckInterrupts
Return v31
Return v17
");
}

Expand Down