Skip to content

Commit d245d17

Browse files
committed
Adding sticky support and setting it as the default GC
1 parent 6b39a81 commit d245d17

18 files changed

+455
-161
lines changed

Make.inc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ WITH_GC_VERIFY := 0
8181
WITH_GC_DEBUG_ENV := 0
8282

8383
# Use stock if MMTK_PLAN hasn't been defined
84-
MMTK_PLAN ?= None
84+
MMTK_PLAN ?= StickyImmix
8585

8686
# Enable DTrace support
8787
WITH_DTRACE := 0
@@ -844,6 +844,9 @@ MMTK_BUILD ?= release
844844
ifeq (${MMTK_PLAN},Immix)
845845
JCXXFLAGS += -DMMTK_PLAN_IMMIX
846846
JCFLAGS += -DMMTK_PLAN_IMMIX
847+
else ifeq (${MMTK_PLAN},StickyImmix)
848+
JCXXFLAGS += -DMMTK_PLAN_STICKYIMMIX
849+
JCFLAGS += -DMMTK_PLAN_STICKYIMMIX
847850
else
848851
$(error "Unsupported MMTk plan: $(MMTK_PLAN)")
849852
endif

deps/checksums/mmtk_julia

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
mmtk_julia-b69acf5af7a7dd97c1cc6fd99f7c2d51b477f214.tar.gz/md5/1911cf084d26c48e2ed58af3d268b4b6
2-
mmtk_julia-b69acf5af7a7dd97c1cc6fd99f7c2d51b477f214.tar.gz/sha512/75beab54398989c46b62e714b242cf6705d88d220f40c21e494e0f29161437f5fbe9ba05b543d2353a1ad76f4239ac4025b476be0be864649f310f14935289fe
3-
mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/md5/38afb5db6d8c55413a4ec96aefa2ebb4
41
mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/sha512/78525582a46a6baf8d33df7b622e55cf244439afcd7192ba55489c1bc18393d1237d2903d517c610484bf9e2a7338ad31435a9cbf70889d6bcf87c40cec829e5
52
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/md5/631b204574da7062802dac501a4b711f
63
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/sha512/daaed59d08fc49621479ed638dea0aac0cba123986e486571447e8e21e9a098776ce2e87fbd92ddea276782fc44621f23d40fa213296b28e1d4480553c7de4f7

deps/mmtk_julia.mk

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
## MMTK ##
22

33
# Both MMTK_MOVING and MMTK_PLAN should be specified in the Make.user file.
4-
# At this point, since we only support non-moving this is always set to 0
5-
# FIXME: change it to `?:` when introducing moving plans
6-
MMTK_MOVING := 0
4+
# FIXME: By default we do a non-moving build. We should change the default to 1
5+
# once we support moving plans.
6+
MMTK_MOVING ?= 0
77
MMTK_VARS := MMTK_PLAN=$(MMTK_PLAN) MMTK_MOVING=$(MMTK_MOVING)
88

99
ifneq ($(USE_BINARYBUILDER_MMTK_JULIA),1)

doc/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ DevDocs = [
229229
"devdocs/aot.md",
230230
"devdocs/gc-sa.md",
231231
"devdocs/gc.md",
232+
"devdocs/gc-mmtk.md",
232233
"devdocs/jit.md",
233234
"devdocs/builtins.md",
234235
"devdocs/precompile_hang.md",

doc/src/devdocs/gc-mmtk.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Julia + MMTk
2+
3+
There has been quite a lot of effort to refactor the GC code inside Julia to support external GCs. The first step to enable using different GC algorithms for Julia was the design and implementation of a [GC interface](https://docs.google.com/document/d/1v0jtSrIpdEDNOxj5S9g1jPqSpuAkNWhr_T8ToFC9RLI/edit?usp=sharing). To drive that interface, we added support for building Julia with [MMTk](https://www.mmtk.io). MMTk is a memory management toolkit providing language implementers with a framework to implement flexible and performant GCs. The flexibility comes from the fact that it is possible to switch implementations fairly easily. MMTk supports state-of-the-art high-performance implementations that are continuously added and maintained in the core part of the framework. MMTk is under active development and has been used by other programming languages such as [Java](https://github.com/mmtk/mmtk-openjdk) and [Ruby](https://github.com/ruby/mmtk). To support a language, it is necessary to implement an *MMTk binding*, which contains the code that connects the language to [mmtk-core](https://github.com/mmtk/mmtk-core). The mmtk-julia binding can be found in [this repository](https://github.com/mmtk/mmtk-julia).
4+
5+
> [!NOTE]
6+
> Using a different GC requires building Julia from source. It is not possible to switch implementations at runtime. To see what version of the GC is currently being used, run `versioninfo()` from the Julia REPL and it should show the version under `GC: ...`.
7+
8+
## Building Julia with MMTk
9+
10+
There are 3 different ways of building Julia with MMTk: building from source using a fixed release of the binding, checking out a custom version in the mmtk-julia [repository](https://github.com/mmtk/mmtk-julia) or using a precompiled binary from Julia's BinaryBuilder. The easiest way is to use the BinaryBuilder binary. Simply set the variable `MMTK_PLAN` to one of the supported plans below and build Julia as usual.
11+
12+
There are different configurations supported by the following variables, which can be set in a `Make.user` file or as an environment variable.
13+
14+
| Variable | | |
15+
|---------------|--------------|---------------|
16+
| `MMTK_PLAN` | Immix | StickyImmix |
17+
| `MMTK_MOVING` | 0 | 1 |
18+
| `MMTK_BUILD` | release | debug |
19+
20+
If only `MMTK_PLAN` is set, then the default is to do a non-moving, release build.
21+
22+
> [!IMPORTANT]
23+
> While the binding supports building all versions above, we have only integrated non-moving Immix into Julia. Support for the other versions should be added in the near future.
24+
25+
### Building mmtk-julia from source
26+
27+
It is also possible to build the binding from source. To do so, set the variable `USE_BINARYBUILDER_MMTK_JULIA=0` and the latest release version of the binding will be downloaded and built as part of building Julia. Note that this requires an installation of the rust toolchain.
28+
29+
It is also possible to build a custom version of binding by checking it out from the [git repository](https://github.com/mmtk/mmtk-julia) and setting a variable named `MMTK_JULIA_DIR` as the path that contains the binding.
30+
31+
For more information on building Julia with MMTk, please refer to the [README](https://github.com/mmtk/mmtk-julia/blob/master/README.md) file in the binding repo.
32+
33+
### I've got a build error when building Julia with MMTk, what should I do?
34+
35+
If you try to build Julia with MMTk and get an error it is likely due to a change to Julia that has not been yet propagated to the binding or to the code in Julia that is specific to MMTk. Some changes include:
36+
37+
(1) **Changing the memory layout of objects in Julia**. The binding relies on automatically generated Rust FFI bindings from Julia code. These files are generated using a crate named [`rust-bindgen`](https://github.com/rust-lang/rust-bindgen). To regenerate those files, check out the latest version of the `mmtk-julia` binding, set the variable `JULIA_PATH` to the path of the Julia version you are trying to build and run `make regen-bindgen-ffi` from the directory containing the binding. This should delete the current version of the FFI bindings and generate a new version based on the Julia code from `JULIA_PATH`.
38+
39+
(2) **Changing the root objects passed to the GC**. Julia passes a set of objects to the GC as roots in the function [gc_mark_roots](https://github.com/JuliaLang/julia/blob/fbe865657942da7d73cc02f76064f9ba9cdef56c/src/gc-stock.c#L2846). At the moment, this set needs to be consistent between both the Stock GC and MMTk (in the function [`jl_gc_scan_vm_specific_roots`](https://github.com/JuliaLang/julia/blob/fbe865657942da7d73cc02f76064f9ba9cdef56c/src/gc-mmtk.c#L496)).
40+
41+
(3) **Changing how objects are scanned**. MMTk uses the same strategy to find references in Julia objects as the stock GC (see [gc_mark_outrefs](https://github.com/JuliaLang/julia/blob/fbe865657942da7d73cc02f76064f9ba9cdef56c/src/gc-stock.c#L2227C19-L2227C34)). Changing the logic from this function should be reflected in the Rust code in the binding that [scan Julia objects](https://github.com/mmtk/mmtk-julia/blob/c9e046baf3a0d52fe75d6c8b28f6afd69b045d95/mmtk/src/julia_scanning.rs#L68).
42+
43+
If your case is not included in one of the alternatives above, please create an issue in the Julia repository tagging it with the `GC: MMTK` label.

src/Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ UV_HEADERS += uv/*.h
123123
endif
124124
PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls-common.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h)
125125
ifneq (${MMTK_PLAN},None)
126-
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-mmtk.h)
126+
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-mmtk.h gc-wb-mmtk.h)
127127
else
128-
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-stock.h)
128+
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-stock.h gc-wb-stock.h)
129129
endif
130130
ifeq ($(OS),WINNT)
131131
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h)

src/gc-interface.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#define JL_GC_INTERFACE_H
99

1010
#include "dtypes.h"
11+
#include "julia_atomics.h"
1112

1213
#ifdef __cplusplus
1314
extern "C" {
@@ -17,6 +18,7 @@ struct _jl_tls_states_t;
1718
struct _jl_value_t;
1819
struct _jl_weakref_t;
1920
struct _jl_datatype_t;
21+
struct _jl_genericmemory_t;
2022

2123
// ========================================================================= //
2224
// GC Metrics
@@ -214,6 +216,10 @@ struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty, unsigned align) JL_NOTSAF
214216
// The GC may use that information to, for instance, determine that such objects should
215217
// be treated as marked and belonged to the old generation in nursery collections.
216218
void jl_gc_notify_image_load(const char* img_data, size_t len);
219+
// This function notifies the GC about memory addresses that are set when allocating the boot image.
220+
// The GC may use that information to, for instance, determine that all objects in that chunk of memory should
221+
// be treated as marked and belonged to the old generation in nursery collections.
222+
void jl_gc_notify_image_alloc(const char* img_data, size_t len);
217223

218224
// ========================================================================= //
219225
// Runtime Write-Barriers
@@ -252,7 +258,18 @@ STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOT
252258
// per field of the object being copied, but may be special-cased for performance reasons.
253259
STATIC_INLINE void jl_gc_multi_wb(const void *parent,
254260
const struct _jl_value_t *ptr) JL_NOTSAFEPOINT;
255-
261+
// Write-barrier function that must be used after copying fields of elements of genericmemory objects
262+
// into another. It should be semantically equivalent to triggering multiple write barriers – one
263+
// per field of the object being copied, but may be special-cased for performance reasons.
264+
STATIC_INLINE void jl_gc_wb_genericmemory_copy_ptr(const struct _jl_value_t *owner, struct _jl_genericmemory_t *src, char* src_p,
265+
size_t n, struct _jl_datatype_t *dt) JL_NOTSAFEPOINT;
266+
// Similar to jl_gc_wb_genericmemory_copy but must be used when copying *boxed* elements of a genericmemory
267+
// object. Note that this barrier also performs the copying unlike jl_gc_wb_genericmemory_copy_ptr.
268+
// The parameters src_p, dest_p and n will be modified and will contain information about
269+
// the *uncopied* data after performing this barrier, and will be copied using memmove_refs.
270+
STATIC_INLINE void jl_gc_wb_genericmemory_copy_boxed(const struct _jl_value_t *owner, _Atomic(void*) * dest_p,
271+
struct _jl_genericmemory_t *src, _Atomic(void*) * src_p,
272+
size_t* n) JL_NOTSAFEPOINT;
256273
#ifdef __cplusplus
257274
}
258275
#endif

src/gc-mmtk.c

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "gc-common.h"
22
#include "gc-tls-mmtk.h"
3+
#include "gc-wb-mmtk.h"
34
#include "mmtkMutator.h"
45
#include "threading.h"
56

@@ -861,10 +862,23 @@ STATIC_INLINE void* mmtk_immortal_alloc_fast(MMTkMutatorContext* mutator, size_t
861862
return bump_alloc_fast(mutator, (uintptr_t*)&allocator->cursor, (uintptr_t)allocator->limit, size, align, offset, 1);
862863
}
863864

865+
inline void mmtk_set_side_metadata(const void* side_metadata_base, void* obj) {
866+
intptr_t addr = (intptr_t) obj;
867+
uint8_t* meta_addr = (uint8_t*) side_metadata_base + (addr >> 6);
868+
intptr_t shift = (addr >> 3) & 0b111;
869+
while(1) {
870+
uint8_t old_val = *meta_addr;
871+
uint8_t new_val = old_val | (1 << shift);
872+
if (jl_atomic_cmpswap((_Atomic(uint8_t)*)meta_addr, &old_val, new_val)) {
873+
break;
874+
}
875+
}
876+
}
877+
864878
STATIC_INLINE void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
865-
// FIXME: Similarly, for now, we do nothing
866-
// but when supporting moving, this is where we set the valid object (VO) bit
867-
// and log (old gen) bit
879+
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
880+
mmtk_set_side_metadata(MMTK_SIDE_LOG_BIT_BASE_ADDRESS, obj);
881+
}
868882
}
869883

870884
JL_DLLEXPORT jl_value_t *jl_mmtk_gc_alloc_default(jl_ptls_t ptls, int osize, size_t align, void *ty)
@@ -1081,6 +1095,11 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
10811095
mmtk_set_vm_space((void*)img_data, len);
10821096
}
10831097

1098+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
1099+
{
1100+
mmtk_immortal_region_post_alloc((void*)img_data, len);
1101+
}
1102+
10841103
// ========================================================================= //
10851104
// Code specific to stock that is not supported by MMTk
10861105
// ========================================================================= //
@@ -1128,7 +1147,9 @@ _Atomic(int) gc_stack_free_idx = 0;
11281147

11291148
JL_DLLEXPORT void jl_gc_queue_root(const struct _jl_value_t *ptr) JL_NOTSAFEPOINT
11301149
{
1131-
mmtk_unreachable();
1150+
jl_task_t *ct = jl_current_task;
1151+
jl_ptls_t ptls = ct->ptls;
1152+
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, ptr, (const void*) 0);
11321153
}
11331154

11341155
JL_DLLEXPORT void jl_gc_queue_multiroot(const struct _jl_value_t *root, const void *stored,

src/gc-stock.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4071,6 +4071,11 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
40714071
// Do nothing
40724072
}
40734073

4074+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
4075+
{
4076+
// Do nothing
4077+
}
4078+
40744079
JL_DLLEXPORT const char* jl_gc_active_impl(void) {
40754080
return "Built with stock GC";
40764081
}

src/gc-wb-mmtk.h

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// This file is a part of Julia. License is MIT: https://julialang.org/license
2+
3+
/*
4+
write barriers which should be inlined by the compiler
5+
*/
6+
7+
#ifndef JL_GC_WB_H
8+
#define JL_GC_WB_H
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
extern void mmtk_object_reference_write_post(void* mutator, const void* parent, const void* ptr);
15+
extern void mmtk_object_reference_write_slow(void* mutator, const void* parent, const void* ptr);
16+
extern const void* MMTK_SIDE_LOG_BIT_BASE_ADDRESS;
17+
18+
#define MMTK_OBJECT_BARRIER (1)
19+
// Stickyimmix needs write barrier. Immix does not need write barrier.
20+
#ifdef MMTK_PLAN_IMMIX
21+
#define MMTK_NEEDS_WRITE_BARRIER (0)
22+
#endif
23+
#ifdef MMTK_PLAN_STICKYIMMIX
24+
#define MMTK_NEEDS_WRITE_BARRIER (1)
25+
#endif
26+
27+
// GC write barriers
28+
29+
// Directly call into MMTk for write barrier (debugging only)
30+
STATIC_INLINE void mmtk_gc_wb_full(const void *parent, const void *ptr) JL_NOTSAFEPOINT
31+
{
32+
jl_task_t *ct = jl_current_task;
33+
jl_ptls_t ptls = ct->ptls;
34+
mmtk_object_reference_write_post(&ptls->gc_tls.mmtk_mutator, parent, ptr);
35+
}
36+
37+
// Inlined fastpath
38+
STATIC_INLINE void mmtk_gc_wb_fast(const void *parent, const void *ptr) JL_NOTSAFEPOINT
39+
{
40+
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
41+
intptr_t addr = (intptr_t) (void*) parent;
42+
uint8_t* meta_addr = (uint8_t*) (MMTK_SIDE_LOG_BIT_BASE_ADDRESS) + (addr >> 6);
43+
intptr_t shift = (addr >> 3) & 0b111;
44+
uint8_t byte_val = *meta_addr;
45+
if (((byte_val >> shift) & 1) == 1) {
46+
jl_task_t *ct = jl_current_task;
47+
jl_ptls_t ptls = ct->ptls;
48+
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, parent, ptr);
49+
}
50+
}
51+
}
52+
53+
STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT
54+
{
55+
mmtk_gc_wb_fast(parent, ptr);
56+
}
57+
58+
STATIC_INLINE void jl_gc_wb_back(const void *ptr) JL_NOTSAFEPOINT // ptr isa jl_value_t*
59+
{
60+
mmtk_gc_wb_fast(ptr, (void*)0);
61+
}
62+
63+
STATIC_INLINE void jl_gc_multi_wb(const void *parent, const jl_value_t *ptr) JL_NOTSAFEPOINT
64+
{
65+
mmtk_gc_wb_fast(parent, (void*)0);
66+
}
67+
68+
STATIC_INLINE void jl_gc_wb_genericmemory_copy_boxed(const jl_value_t *dest_owner, _Atomic(void*) * dest_p,
69+
jl_genericmemory_t *src, _Atomic(void*) * src_p,
70+
size_t* n) JL_NOTSAFEPOINT
71+
{
72+
mmtk_gc_wb_fast(dest_owner, (void*)0);
73+
}
74+
75+
STATIC_INLINE void jl_gc_wb_genericmemory_copy_ptr(const jl_value_t *owner, jl_genericmemory_t *src, char* src_p,
76+
size_t n, jl_datatype_t *dt) JL_NOTSAFEPOINT
77+
{
78+
mmtk_gc_wb_fast(owner, (void*)0);
79+
}
80+
81+
82+
#ifdef __cplusplus
83+
}
84+
#endif
85+
86+
#endif

0 commit comments

Comments
 (0)