Skip to content

Commit 1622162

Browse files
udesouLuis Eduardo de Souza AmorimLuis Eduardo de Souza Amorimmmtkgc-bot
authored
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 <eduardo@shrew.moma> Co-authored-by: Luis Eduardo de Souza Amorim <eduardo@bear.moma> Co-authored-by: mmtkgc-bot <mmtkgc.bot@gmail.com>
1 parent eac7e88 commit 1622162

File tree

16 files changed

+484
-120
lines changed

16 files changed

+484
-120
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
@@ -53,7 +56,7 @@ jobs:
5356
./.github/scripts/ci-setup.sh
5457
- name: Build Julia (Release)
5558
run: |
56-
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }}
59+
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }} ${{ inputs.moving }}
5760
- name: Test Julia
5861
run: |
5962
./.github/scripts/ci-test-stdlib.sh
@@ -69,7 +72,7 @@ jobs:
6972
./.github/scripts/ci-setup.sh
7073
- name: Build Julia (Release)
7174
run: |
72-
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }}
75+
./.github/scripts/ci-build.sh release ${{ inputs.gc_plan }} ${{ inputs.moving }}
7376
- name: Test Julia
7477
run: |
7578
./.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: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ 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);
2323
extern void jl_rng_split(uint64_t to[JL_RNG_SIZE], uint64_t from[JL_RNG_SIZE]);
24+
extern void _jl_free_stack(jl_ptls_t ptls, void *stkbuf, size_t bufsz);
25+
extern void free_stack(void *stkbuf, size_t bufsz);
2426
extern jl_mutex_t finalizers_lock;
2527
extern void jl_gc_wait_for_the_world(jl_ptls_t* gc_all_tls_states, int gc_n_threads);
2628
extern void mmtk_block_thread_for_gc(void);
@@ -114,6 +116,9 @@ static void mmtk_sweep_malloced_arrays(void) JL_NOTSAFEPOINT
114116
continue;
115117
}
116118
if (mmtk_is_live_object(ma->a)) {
119+
// if the array has been forwarded, the reference needs to be updated
120+
jl_array_t *maybe_forwarded = (jl_array_t*)mmtk_get_possibly_forwared(ma->a);
121+
ma->a = maybe_forwarded;
117122
pma = &ma->next;
118123
}
119124
else {
@@ -274,6 +279,19 @@ static void add_node_to_roots_buffer(RootsWorkClosure* closure, RootsWorkBuffer*
274279
}
275280
}
276281

282+
static void add_node_to_tpinned_roots_buffer(RootsWorkClosure* closure, RootsWorkBuffer* buf, size_t* buf_len, void* root) {
283+
if (root == NULL)
284+
return;
285+
286+
buf->ptr[*buf_len] = root;
287+
*buf_len += 1;
288+
if (*buf_len >= buf->cap) {
289+
RootsWorkBuffer new_buf = (closure->report_tpinned_nodes_func)(buf->ptr, *buf_len, buf->cap, closure->data, true);
290+
*buf = new_buf;
291+
*buf_len = 0;
292+
}
293+
}
294+
277295
void scan_vm_specific_roots(RootsWorkClosure* closure)
278296
{
279297
// Create a new buf
@@ -302,10 +320,15 @@ void scan_vm_specific_roots(RootsWorkClosure* closure)
302320
// constants
303321
add_node_to_roots_buffer(closure, &buf, &len, jl_emptytuple_type);
304322
add_node_to_roots_buffer(closure, &buf, &len, cmpswap_names);
305-
add_node_to_roots_buffer(closure, &buf, &len, jl_global_roots_table);
323+
324+
// jl_global_roots_table must be transitively pinned
325+
RootsWorkBuffer tpinned_buf = (closure->report_tpinned_nodes_func)((void**)0, 0, 0, closure->data, true);
326+
size_t tpinned_len = 0;
327+
add_node_to_tpinned_roots_buffer(closure, &tpinned_buf, &tpinned_len, jl_global_roots_table);
306328

307329
// Push the result of the work.
308330
(closure->report_nodes_func)(buf.ptr, len, buf.cap, closure->data, false);
331+
(closure->report_tpinned_nodes_func)(tpinned_buf.ptr, tpinned_len, tpinned_buf.cap, closure->data, false);
309332
}
310333

311334
JL_DLLEXPORT void scan_julia_exc_obj(void* obj_raw, void* closure, ProcessEdgeFn process_edge) {
@@ -349,6 +372,107 @@ JL_DLLEXPORT void scan_julia_exc_obj(void* obj_raw, void* closure, ProcessEdgeFn
349372
}
350373
}
351374

375+
// number of stacks to always keep available per pool - from gc-stacks.c
376+
#define MIN_STACK_MAPPINGS_PER_POOL 5
377+
378+
// if data is inlined inside the array object --- to->data needs to be updated when copying the array
379+
void update_inlined_array(void* from, void* to) {
380+
jl_value_t* jl_from = (jl_value_t*) from;
381+
jl_value_t* jl_to = (jl_value_t*) to;
382+
383+
uintptr_t tag_to = (uintptr_t)jl_typeof(jl_to);
384+
jl_datatype_t *vt = (jl_datatype_t*)tag_to;
385+
386+
if(vt->name == jl_array_typename) {
387+
jl_array_t *a = (jl_array_t*)jl_from;
388+
jl_array_t *b = (jl_array_t*)jl_to;
389+
if (a->flags.how == 0 && mmtk_object_is_managed_by_mmtk(a->data)) { // a is inlined (a->data is an mmtk object)
390+
size_t offset_of_data = ((size_t)a->data - a->offset*a->elsize) - (size_t)a;
391+
if (offset_of_data > 0 && offset_of_data <= ARRAY_INLINE_NBYTES) {
392+
b->data = (void*)((size_t) b + offset_of_data);
393+
}
394+
}
395+
}
396+
}
397+
398+
// modified sweep_stack_pools from gc-stacks.c
399+
void mmtk_sweep_stack_pools(void)
400+
{
401+
// Stack sweeping algorithm:
402+
// // deallocate stacks if we have too many sitting around unused
403+
// for (stk in halfof(free_stacks))
404+
// free_stack(stk, pool_sz);
405+
// // then sweep the task stacks
406+
// for (t in live_tasks)
407+
// if (!gc-marked(t))
408+
// stkbuf = t->stkbuf
409+
// bufsz = t->bufsz
410+
// if (stkbuf)
411+
// push(free_stacks[sz], stkbuf)
412+
for (int i = 0; i < jl_n_threads; i++) {
413+
jl_ptls_t ptls2 = jl_all_tls_states[i];
414+
415+
// free half of stacks that remain unused since last sweep
416+
for (int p = 0; p < JL_N_STACK_POOLS; p++) {
417+
arraylist_t *al = &ptls2->heap.free_stacks[p];
418+
size_t n_to_free;
419+
if (al->len > MIN_STACK_MAPPINGS_PER_POOL) {
420+
n_to_free = al->len / 2;
421+
if (n_to_free > (al->len - MIN_STACK_MAPPINGS_PER_POOL))
422+
n_to_free = al->len - MIN_STACK_MAPPINGS_PER_POOL;
423+
}
424+
else {
425+
n_to_free = 0;
426+
}
427+
for (int n = 0; n < n_to_free; n++) {
428+
void *stk = arraylist_pop(al);
429+
free_stack(stk, pool_sizes[p]);
430+
}
431+
}
432+
433+
arraylist_t *live_tasks = &ptls2->heap.live_tasks;
434+
size_t n = 0;
435+
size_t ndel = 0;
436+
size_t l = live_tasks->len;
437+
void **lst = live_tasks->items;
438+
if (l == 0)
439+
continue;
440+
while (1) {
441+
jl_task_t *t = (jl_task_t*)lst[n];
442+
if (mmtk_is_live_object(t)) {
443+
jl_task_t *maybe_forwarded = (jl_task_t*)mmtk_get_possibly_forwared(t);
444+
live_tasks->items[n] = maybe_forwarded;
445+
t = maybe_forwarded;
446+
assert(jl_is_task(t));
447+
if (t->stkbuf == NULL)
448+
ndel++; // jl_release_task_stack called
449+
else
450+
n++;
451+
} else {
452+
ndel++;
453+
void *stkbuf = t->stkbuf;
454+
size_t bufsz = t->bufsz;
455+
if (stkbuf) {
456+
t->stkbuf = NULL;
457+
_jl_free_stack(ptls2, stkbuf, bufsz);
458+
}
459+
#ifdef _COMPILER_TSAN_ENABLED_
460+
if (t->ctx.tsan_state) {
461+
__tsan_destroy_fiber(t->ctx.tsan_state);
462+
t->ctx.tsan_state = NULL;
463+
}
464+
#endif
465+
}
466+
if (n >= l - ndel)
467+
break;
468+
void *tmp = lst[n];
469+
lst[n] = lst[n + ndel];
470+
lst[n + ndel] = tmp;
471+
}
472+
live_tasks->len -= ndel;
473+
}
474+
}
475+
352476
#define jl_array_data_owner_addr(a) (((jl_value_t**)((char*)a + jl_array_data_owner_offset(jl_array_ndims(a)))))
353477

354478
JL_DLLEXPORT void* get_stackbase(int16_t tid) {
@@ -431,6 +555,7 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) {
431555
.mmtk_jl_run_finalizers = mmtk_jl_run_finalizers,
432556
.jl_throw_out_of_memory_error = jl_throw_out_of_memory_error,
433557
.sweep_malloced_array = mmtk_sweep_malloced_arrays,
558+
.sweep_stack_pools = mmtk_sweep_stack_pools,
434559
.wait_in_a_safepoint = mmtk_wait_in_a_safepoint,
435560
.exit_from_safepoint = mmtk_exit_from_safepoint,
436561
.jl_hrtime = jl_hrtime,
@@ -442,5 +567,6 @@ Julia_Upcalls mmtk_upcalls = (Julia_Upcalls) {
442567
.arraylist_grow = (void (*)(void*, long unsigned int))arraylist_grow,
443568
.get_jl_gc_have_pending_finalizers = get_jl_gc_have_pending_finalizers,
444569
.scan_vm_specific_roots = scan_vm_specific_roots,
570+
.update_inlined_array = update_inlined_array,
445571
.prepare_to_collect = jl_gc_prepare_to_collect,
446572
};

0 commit comments

Comments
 (0)