Skip to content

Commit 1683e50

Browse files
udesoumergify[bot]
authored andcommitted
Supporting moving immix (#93)
This PR adds support for (partially) moving objects in Julia and should be merged with mmtk/julia#27 and mmtk/mmtk-core#897. - It adds support for pinning/unpinning objects and checking if an object is pinned (the implementation uses a pin bit). (`mmtk_pin_object`, `mmtk_unpin_object` and `mmtk_is_pinned`) - It adds support for providing transitively pinned (`tpinned`) roots . - It implements the `copy` function in `object_model.rs`. Note that arrays with inlined data must be treated specially, as their `a->data` pointer needs to be updated after copying. - It uses Julia's GC bits to store forwarding information and the object's header to store the forwarding pointer. - Currently, all stack roots are transitively pinned. Note that we also need to traverse the `tls->live_tasks` to make sure that any stack root from these tasks are transitively pinned. - `scan_julia_object` had to be adapted to cover a few corner cases: - when an array object contains a pointer to the owner of the data, `a->data` needs to be updated in case the owner moves. - the `using` field inside a `jl_module_t` may also be inlined inside the module, and if that's the case, we need to make sure that field is updated if the module moves. - when marking finalizers, traversing the list of malloced arrays, and the list of live tasks at the end of GC, we need to updated these lists with objects that have possibly been moved. - Added a few debug assertions to capture scanning of misaligned objects and roots. NB: I've only tested moving immix; sticky immix is still non-moving. --------- Co-authored-by: Luis Eduardo de Souza Amorim <[email protected]> Co-authored-by: Luis Eduardo de Souza Amorim <[email protected]> Co-authored-by: mmtkgc-bot <[email protected]> (cherry picked from commit 1622162) # Conflicts: # julia/mmtk_julia.c # mmtk/Cargo.lock # mmtk/Cargo.toml # mmtk/api/mmtk.h # mmtk/src/julia_scanning.rs # mmtk/src/lib.rs # mmtk/src/scanning.rs
1 parent b78afd9 commit 1683e50

File tree

16 files changed

+440
-63
lines changed

16 files changed

+440
-63
lines changed

.github/scripts/ci-build.sh

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ fi
1212
build_type=$1
1313
# plan to use
1414
plan=$2
15+
# moving vs non-moving
16+
is_moving=$3
1517

1618
# helloworld.jl
1719
HELLO_WORLD_JL=$BINDING_PATH/.github/scripts/hello_world.jl
@@ -23,9 +25,10 @@ if [ "$build_type" == "release" ]; then
2325
fi
2426

2527
plan_feature=${plan,,}
28+
moving_feature=${is_moving,,}
2629

2730
cd $MMTK_JULIA_DIR/mmtk
28-
cargo build --features $plan_feature $build_args
31+
cargo build --features $plan_feature,$moving_feature $build_args
2932

3033
cd $JULIA_PATH
3134

.github/scripts/ci-test-patching.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ declare -a tests_to_skip=(
2424
# This test checks GC logging
2525
'@test occursin("GC: pause", read(tmppath, String))' "$JULIA_PATH/test/misc.jl"
2626

27+
# These tests check for the number of stock GC threads (which we set to 0 with mmtk)
28+
'@test (cpu_threads == 1 ? "1" : string(div(cpu_threads, 2))) ==' "$JULIA_PATH/test/cmdlineargs.jl"
29+
'@test read(`$exename --gcthreads=2 -e $code`, String) == "2"' "$JULIA_PATH/test/cmdlineargs.jl"
30+
'@test read(`$exename -e $code`, String) == "2"' "$JULIA_PATH/test/cmdlineargs.jl"
2731
# This seems to be a regression from upstream when we merge with upstream 43bf2c8.
2832
# The required string int.jl does not appear in the output even if I test with the stock Julia code.
2933
# I do not know what is wrong, but at this point, I dont want to spend time on it.

.github/workflows/binding-tests.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ on:
66
gc_plan:
77
required: true
88
type: string
9+
moving:
10+
required: true
11+
type: string
912

1013
jobs:
1114
build-debug:
@@ -18,7 +21,7 @@ jobs:
1821
./.github/scripts/ci-setup.sh
1922
- name: Build Julia (Debug)
2023
run: |
21-
./.github/scripts/ci-build.sh debug ${{ inputs.gc_plan }}
24+
./.github/scripts/ci-build.sh debug ${{ inputs.gc_plan }} ${{ inputs.moving }}
2225
- name: Style check
2326
run: |
2427
./.github/scripts/ci-style.sh
@@ -37,7 +40,7 @@ jobs:
3740
./.github/scripts/ci-test-patching.sh
3841
- name: Build Julia (Release)
3942
run: |
40-
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }}
43+
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }} ${{ inputs.moving }}
4144
- name: Test Julia
4245
run: |
4346
./.github/scripts/ci-test-other.sh
@@ -56,7 +59,7 @@ jobs:
5659
./.github/scripts/ci-test-patching.sh
5760
- name: Build Julia (Release)
5861
run: |
59-
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }}
62+
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }} ${{ inputs.moving }}
6063
- name: Test Julia
6164
run: |
6265
./.github/scripts/ci-test-stdlib.sh
@@ -72,7 +75,7 @@ jobs:
7275
./.github/scripts/ci-setup.sh
7376
- name: Build Julia (Release)
7477
run: |
75-
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }}
78+
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }} ${{ inputs.moving }}
7679
- name: Test Julia
7780
run: |
7881
./.github/scripts/ci-test-LinearAlgebra.sh

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ jobs:
1818
fail-fast: false
1919
matrix:
2020
gc_plan: [Immix, StickyImmix]
21+
moving: [Default, Non_Moving]
2122
uses: ./.github/workflows/binding-tests.yml
2223
with:
2324
gc_plan: ${{ matrix.gc_plan }}
25+
moving: ${{ matrix.moving }}

julia/mmtk_julia.c

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ extern const unsigned pool_sizes[];
2020
extern void mmtk_store_obj_size_c(void* obj, size_t size);
2121
extern void jl_gc_free_array(jl_array_t *a);
2222
extern size_t mmtk_get_obj_size(void* obj);
23+
<<<<<<< HEAD
2324
extern void jl_rng_split(uint64_t to[4], uint64_t from[4]);
25+
=======
26+
extern void jl_rng_split(uint64_t to[JL_RNG_SIZE], uint64_t from[JL_RNG_SIZE]);
27+
extern void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz);
28+
extern void free_stack(void *stkbuf, size_t bufsz);
29+
>>>>>>> 1622162 (Supporting moving immix (#93))
2430
extern jl_mutex_t finalizers_lock;
2531
extern void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads);
2632
extern void mmtk_block_thread_for_gc(int gc_n_threads);
@@ -121,6 +127,9 @@ static void mmtk_sweep_malloced_arrays(void) JL_NOTSAFEPOINT
121127
continue;
122128
}
123129
if (mmtk_is_live_object(ma->a)) {
130+
// if the array has been forwarded, the reference needs to be updated
131+
jl_array_t *maybe_forwarded = (jl_array_t*)mmtk_get_possibly_forwared(ma->a);
132+
ma->a = maybe_forwarded;
124133
pma = &ma->next;
125134
}
126135
else {
@@ -277,6 +286,19 @@ static void add_node_to_roots_buffer(RootsWorkClosure* closure, RootsWorkBuffer*
277286
}
278287
}
279288

289+
static void add_node_to_tpinned_roots_buffer(RootsWorkClosure* closure, RootsWorkBuffer* buf, size_t* buf_len, void* root) {
290+
if (root == NULL)
291+
return;
292+
293+
buf->ptr[*buf_len] = root;
294+
*buf_len += 1;
295+
if (*buf_len >= buf->cap) {
296+
RootsWorkBuffer new_buf = (closure->report_tpinned_nodes_func)(buf->ptr, *buf_len, buf->cap, closure->data, true);
297+
*buf = new_buf;
298+
*buf_len = 0;
299+
}
300+
}
301+
280302
void scan_vm_specific_roots(RootsWorkClosure* closure)
281303
{
282304
// Create a new buf
@@ -305,10 +327,15 @@ void scan_vm_specific_roots(RootsWorkClosure* closure)
305327
// constants
306328
add_node_to_roots_buffer(closure, &buf, &len, jl_emptytuple_type);
307329
add_node_to_roots_buffer(closure, &buf, &len, cmpswap_names);
308-
add_node_to_roots_buffer(closure, &buf, &len, jl_global_roots_table);
330+
331+
// jl_global_roots_table must be transitively pinned
332+
RootsWorkBuffer tpinned_buf = (closure->report_tpinned_nodes_func)((void**)0, 0, 0, closure->data, true);
333+
size_t tpinned_len = 0;
334+
add_node_to_tpinned_roots_buffer(closure, &tpinned_buf, &tpinned_len, jl_global_roots_table);
309335

310336
// Push the result of the work.
311337
(closure->report_nodes_func)(buf.ptr, len, buf.cap, closure->data, false);
338+
(closure->report_tpinned_nodes_func)(tpinned_buf.ptr, tpinned_len, tpinned_buf.cap, closure->data, false);
312339
}
313340

314341
JL_DLLEXPORT void scan_julia_exc_obj(void* obj_raw, void* closure, ProcessEdgeFn process_edge) {
@@ -350,9 +377,35 @@ JL_DLLEXPORT void scan_julia_exc_obj(void* obj_raw, void* closure, ProcessEdgeFn
350377
}
351378
}
352379

380+
<<<<<<< HEAD
353381
// number of stacks to always keep available per pool
354382
#define MIN_STACK_MAPPINGS_PER_POOL 5
355383

384+
=======
385+
// number of stacks to always keep available per pool - from gc-stacks.c
386+
#define MIN_STACK_MAPPINGS_PER_POOL 5
387+
388+
// if data is inlined inside the array object --- to->data needs to be updated when copying the array
389+
void update_inlined_array(void* from, void* to) {
390+
jl_value_t* jl_from = (jl_value_t*) from;
391+
jl_value_t* jl_to = (jl_value_t*) to;
392+
393+
uintptr_t tag_to = (uintptr_t)jl_typeof(jl_to);
394+
jl_datatype_t *vt = (jl_datatype_t*)tag_to;
395+
396+
if(vt->name == jl_array_typename) {
397+
jl_array_t *a = (jl_array_t*)jl_from;
398+
jl_array_t *b = (jl_array_t*)jl_to;
399+
if (a->flags.how == 0 && mmtk_object_is_managed_by_mmtk(a->data)) { // a is inlined (a->data is an mmtk object)
400+
size_t offset_of_data = ((size_t)a->data - a->offset*a->elsize) - (size_t)a;
401+
if (offset_of_data > 0 && offset_of_data <= ARRAY_INLINE_NBYTES) {
402+
b->data = (void*)((size_t) b + offset_of_data);
403+
}
404+
}
405+
}
406+
}
407+
408+
>>>>>>> 1622162 (Supporting moving immix (#93))
356409
// modified sweep_stack_pools from gc-stacks.c
357410
void mmtk_sweep_stack_pools(void)
358411
{
@@ -367,13 +420,21 @@ void mmtk_sweep_stack_pools(void)
367420
// bufsz = t->bufsz
368421
// if (stkbuf)
369422
// push(free_stacks[sz], stkbuf)
423+
<<<<<<< HEAD
370424
assert(gc_n_threads);
371425
for (int i = 0; i < gc_n_threads; i++) {
426+
=======
427+
for (int i = 0; i < jl_n_threads; i++) {
428+
>>>>>>> 1622162 (Supporting moving immix (#93))
372429
jl_ptls_t ptls2 = jl_all_tls_states[i];
373430

374431
// free half of stacks that remain unused since last sweep
375432
for (int p = 0; p < JL_N_STACK_POOLS; p++) {
433+
<<<<<<< HEAD
376434
small_arraylist_t *al = &ptls2->heap.free_stacks[p];
435+
=======
436+
arraylist_t *al = &ptls2->heap.free_stacks[p];
437+
>>>>>>> 1622162 (Supporting moving immix (#93))
377438
size_t n_to_free;
378439
if (al->len > MIN_STACK_MAPPINGS_PER_POOL) {
379440
n_to_free = al->len / 2;
@@ -384,12 +445,20 @@ void mmtk_sweep_stack_pools(void)
384445
n_to_free = 0;
385446
}
386447
for (int n = 0; n < n_to_free; n++) {
448+
<<<<<<< HEAD
387449
void *stk = small_arraylist_pop(al);
450+
=======
451+
void *stk = arraylist_pop(al);
452+
>>>>>>> 1622162 (Supporting moving immix (#93))
388453
free_stack(stk, pool_sizes[p]);
389454
}
390455
}
391456

457+
<<<<<<< HEAD
392458
small_arraylist_t *live_tasks = &ptls2->heap.live_tasks;
459+
=======
460+
arraylist_t *live_tasks = &ptls2->heap.live_tasks;
461+
>>>>>>> 1622162 (Supporting moving immix (#93))
393462
size_t n = 0;
394463
size_t ndel = 0;
395464
size_t l = live_tasks->len;
@@ -398,9 +467,17 @@ void mmtk_sweep_stack_pools(void)
398467
continue;
399468
while (1) {
400469
jl_task_t *t = (jl_task_t*)lst[n];
470+
<<<<<<< HEAD
401471
assert(jl_is_task(t));
402472
if (mmtk_is_live_object(t)) {
403473
live_tasks->items[n] = t;
474+
=======
475+
if (mmtk_is_live_object(t)) {
476+
jl_task_t *maybe_forwarded = (jl_task_t*)mmtk_get_possibly_forwared(t);
477+
live_tasks->items[n] = maybe_forwarded;
478+
t = maybe_forwarded;
479+
assert(jl_is_task(t));
480+
>>>>>>> 1622162 (Supporting moving immix (#93))
404481
if (t->stkbuf == NULL)
405482
ndel++; // jl_release_task_stack called
406483
else
@@ -534,8 +611,11 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) {
534611
.mmtk_jl_throw_out_of_memory_error = mmtk_jl_throw_out_of_memory_error,
535612
.sweep_malloced_array = mmtk_sweep_malloced_arrays,
536613
.sweep_stack_pools = mmtk_sweep_stack_pools,
614+
<<<<<<< HEAD
537615
.clear_weak_refs = clear_weak_refs,
538616
.sweep_weak_refs = sweep_weak_refs,
617+
=======
618+
>>>>>>> 1622162 (Supporting moving immix (#93))
539619
.wait_in_a_safepoint = mmtk_wait_in_a_safepoint,
540620
.exit_from_safepoint = mmtk_exit_from_safepoint,
541621
.mmtk_jl_hrtime = mmtk_jl_hrtime,
@@ -547,6 +627,7 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) {
547627
.arraylist_grow = (void (*)(void*, long unsigned int))arraylist_grow,
548628
.get_jl_gc_have_pending_finalizers = get_jl_gc_have_pending_finalizers,
549629
.scan_vm_specific_roots = scan_vm_specific_roots,
630+
.update_inlined_array = update_inlined_array,
550631
.prepare_to_collect = jl_gc_prepare_to_collect,
551632
.check_is_collection_disabled = check_is_collection_disabled,
552633
};

0 commit comments

Comments
 (0)