Skip to content

Commit

Permalink
Adding sticky support and setting it as the default GC
Browse files Browse the repository at this point in the history
  • Loading branch information
udesou committed Feb 13, 2025
1 parent 6b39a81 commit d245d17
Show file tree
Hide file tree
Showing 18 changed files with 455 additions and 161 deletions.
5 changes: 4 additions & 1 deletion Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ WITH_GC_VERIFY := 0
WITH_GC_DEBUG_ENV := 0

# Use stock if MMTK_PLAN hasn't been defined
MMTK_PLAN ?= None
MMTK_PLAN ?= StickyImmix

# Enable DTrace support
WITH_DTRACE := 0
Expand Down Expand Up @@ -844,6 +844,9 @@ MMTK_BUILD ?= release
ifeq (${MMTK_PLAN},Immix)
JCXXFLAGS += -DMMTK_PLAN_IMMIX
JCFLAGS += -DMMTK_PLAN_IMMIX
else ifeq (${MMTK_PLAN},StickyImmix)
JCXXFLAGS += -DMMTK_PLAN_STICKYIMMIX
JCFLAGS += -DMMTK_PLAN_STICKYIMMIX
else
$(error "Unsupported MMTk plan: $(MMTK_PLAN)")
endif
Expand Down
3 changes: 0 additions & 3 deletions deps/checksums/mmtk_julia
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
mmtk_julia-b69acf5af7a7dd97c1cc6fd99f7c2d51b477f214.tar.gz/md5/1911cf084d26c48e2ed58af3d268b4b6
mmtk_julia-b69acf5af7a7dd97c1cc6fd99f7c2d51b477f214.tar.gz/sha512/75beab54398989c46b62e714b242cf6705d88d220f40c21e494e0f29161437f5fbe9ba05b543d2353a1ad76f4239ac4025b476be0be864649f310f14935289fe
mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/md5/38afb5db6d8c55413a4ec96aefa2ebb4
mmtk_julia-f07d66aafc86af84ea988b35335acc9bbc770fa1.tar.gz/sha512/78525582a46a6baf8d33df7b622e55cf244439afcd7192ba55489c1bc18393d1237d2903d517c610484bf9e2a7338ad31435a9cbf70889d6bcf87c40cec829e5
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/md5/631b204574da7062802dac501a4b711f
mmtk_julia.v0.30.3+1.x86_64-linux-gnu.tar.gz/sha512/daaed59d08fc49621479ed638dea0aac0cba123986e486571447e8e21e9a098776ce2e87fbd92ddea276782fc44621f23d40fa213296b28e1d4480553c7de4f7
Expand Down
6 changes: 3 additions & 3 deletions deps/mmtk_julia.mk
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
## MMTK ##

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

ifneq ($(USE_BINARYBUILDER_MMTK_JULIA),1)
Expand Down
1 change: 1 addition & 0 deletions doc/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ DevDocs = [
"devdocs/aot.md",
"devdocs/gc-sa.md",
"devdocs/gc.md",
"devdocs/gc-mmtk.md",
"devdocs/jit.md",
"devdocs/builtins.md",
"devdocs/precompile_hang.md",
Expand Down
43 changes: 43 additions & 0 deletions doc/src/devdocs/gc-mmtk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Julia + MMTk

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).

> [!NOTE]
> 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: ...`.
## Building Julia with MMTk

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.

There are different configurations supported by the following variables, which can be set in a `Make.user` file or as an environment variable.

| Variable | | |
|---------------|--------------|---------------|
| `MMTK_PLAN` | Immix | StickyImmix |
| `MMTK_MOVING` | 0 | 1 |
| `MMTK_BUILD` | release | debug |

If only `MMTK_PLAN` is set, then the default is to do a non-moving, release build.

> [!IMPORTANT]
> 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.
### Building mmtk-julia from source

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.

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.

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.

### I've got a build error when building Julia with MMTk, what should I do?

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:

(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`.

(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)).

(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).

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.
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ UV_HEADERS += uv/*.h
endif
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)
ifneq (${MMTK_PLAN},None)
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-mmtk.h)
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-mmtk.h gc-wb-mmtk.h)
else
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-stock.h)
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,gc-tls-stock.h gc-wb-stock.h)
endif
ifeq ($(OS),WINNT)
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h)
Expand Down
19 changes: 18 additions & 1 deletion src/gc-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define JL_GC_INTERFACE_H

#include "dtypes.h"
#include "julia_atomics.h"

#ifdef __cplusplus
extern "C" {
Expand All @@ -17,6 +18,7 @@ struct _jl_tls_states_t;
struct _jl_value_t;
struct _jl_weakref_t;
struct _jl_datatype_t;
struct _jl_genericmemory_t;

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

// ========================================================================= //
// Runtime Write-Barriers
Expand Down Expand Up @@ -252,7 +258,18 @@ STATIC_INLINE void jl_gc_wb_knownold(const void *parent, const void *ptr) JL_NOT
// per field of the object being copied, but may be special-cased for performance reasons.
STATIC_INLINE void jl_gc_multi_wb(const void *parent,
const struct _jl_value_t *ptr) JL_NOTSAFEPOINT;

// Write-barrier function that must be used after copying fields of elements of genericmemory objects
// into another. It should be semantically equivalent to triggering multiple write barriers – one
// per field of the object being copied, but may be special-cased for performance reasons.
STATIC_INLINE void jl_gc_wb_genericmemory_copy_ptr(const struct _jl_value_t *owner, struct _jl_genericmemory_t *src, char* src_p,
size_t n, struct _jl_datatype_t *dt) JL_NOTSAFEPOINT;
// Similar to jl_gc_wb_genericmemory_copy but must be used when copying *boxed* elements of a genericmemory
// object. Note that this barrier also performs the copying unlike jl_gc_wb_genericmemory_copy_ptr.
// The parameters src_p, dest_p and n will be modified and will contain information about
// the *uncopied* data after performing this barrier, and will be copied using memmove_refs.
STATIC_INLINE void jl_gc_wb_genericmemory_copy_boxed(const struct _jl_value_t *owner, _Atomic(void*) * dest_p,
struct _jl_genericmemory_t *src, _Atomic(void*) * src_p,
size_t* n) JL_NOTSAFEPOINT;
#ifdef __cplusplus
}
#endif
Expand Down
29 changes: 25 additions & 4 deletions src/gc-mmtk.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "gc-common.h"
#include "gc-tls-mmtk.h"
#include "gc-wb-mmtk.h"
#include "mmtkMutator.h"
#include "threading.h"

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

inline void mmtk_set_side_metadata(const void* side_metadata_base, void* obj) {
intptr_t addr = (intptr_t) obj;
uint8_t* meta_addr = (uint8_t*) side_metadata_base + (addr >> 6);
intptr_t shift = (addr >> 3) & 0b111;
while(1) {
uint8_t old_val = *meta_addr;
uint8_t new_val = old_val | (1 << shift);
if (jl_atomic_cmpswap((_Atomic(uint8_t)*)meta_addr, &old_val, new_val)) {
break;
}
}
}

STATIC_INLINE void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
// FIXME: Similarly, for now, we do nothing
// but when supporting moving, this is where we set the valid object (VO) bit
// and log (old gen) bit
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
mmtk_set_side_metadata(MMTK_SIDE_LOG_BIT_BASE_ADDRESS, obj);
}
}

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

void jl_gc_notify_image_alloc(const char* img_data, size_t len)
{
mmtk_immortal_region_post_alloc((void*)img_data, len);
}

// ========================================================================= //
// Code specific to stock that is not supported by MMTk
// ========================================================================= //
Expand Down Expand Up @@ -1128,7 +1147,9 @@ _Atomic(int) gc_stack_free_idx = 0;

JL_DLLEXPORT void jl_gc_queue_root(const struct _jl_value_t *ptr) JL_NOTSAFEPOINT
{
mmtk_unreachable();
jl_task_t *ct = jl_current_task;
jl_ptls_t ptls = ct->ptls;
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, ptr, (const void*) 0);
}

JL_DLLEXPORT void jl_gc_queue_multiroot(const struct _jl_value_t *root, const void *stored,
Expand Down
5 changes: 5 additions & 0 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -4071,6 +4071,11 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
// Do nothing
}

void jl_gc_notify_image_alloc(const char* img_data, size_t len)
{
// Do nothing
}

JL_DLLEXPORT const char* jl_gc_active_impl(void) {
return "Built with stock GC";
}
Expand Down
86 changes: 86 additions & 0 deletions src/gc-wb-mmtk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

/*
write barriers which should be inlined by the compiler
*/

#ifndef JL_GC_WB_H
#define JL_GC_WB_H

#ifdef __cplusplus
extern "C" {
#endif

extern void mmtk_object_reference_write_post(void* mutator, const void* parent, const void* ptr);
extern void mmtk_object_reference_write_slow(void* mutator, const void* parent, const void* ptr);
extern const void* MMTK_SIDE_LOG_BIT_BASE_ADDRESS;

#define MMTK_OBJECT_BARRIER (1)
// Stickyimmix needs write barrier. Immix does not need write barrier.
#ifdef MMTK_PLAN_IMMIX
#define MMTK_NEEDS_WRITE_BARRIER (0)
#endif
#ifdef MMTK_PLAN_STICKYIMMIX
#define MMTK_NEEDS_WRITE_BARRIER (1)
#endif

// GC write barriers

// Directly call into MMTk for write barrier (debugging only)
STATIC_INLINE void mmtk_gc_wb_full(const void *parent, const void *ptr) JL_NOTSAFEPOINT
{
jl_task_t *ct = jl_current_task;
jl_ptls_t ptls = ct->ptls;
mmtk_object_reference_write_post(&ptls->gc_tls.mmtk_mutator, parent, ptr);
}

// Inlined fastpath
STATIC_INLINE void mmtk_gc_wb_fast(const void *parent, const void *ptr) JL_NOTSAFEPOINT
{
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
intptr_t addr = (intptr_t) (void*) parent;
uint8_t* meta_addr = (uint8_t*) (MMTK_SIDE_LOG_BIT_BASE_ADDRESS) + (addr >> 6);
intptr_t shift = (addr >> 3) & 0b111;
uint8_t byte_val = *meta_addr;
if (((byte_val >> shift) & 1) == 1) {
jl_task_t *ct = jl_current_task;
jl_ptls_t ptls = ct->ptls;
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, parent, ptr);
}
}
}

STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT
{
mmtk_gc_wb_fast(parent, ptr);
}

STATIC_INLINE void jl_gc_wb_back(const void *ptr) JL_NOTSAFEPOINT // ptr isa jl_value_t*
{
mmtk_gc_wb_fast(ptr, (void*)0);
}

STATIC_INLINE void jl_gc_multi_wb(const void *parent, const jl_value_t *ptr) JL_NOTSAFEPOINT
{
mmtk_gc_wb_fast(parent, (void*)0);
}

STATIC_INLINE void jl_gc_wb_genericmemory_copy_boxed(const jl_value_t *dest_owner, _Atomic(void*) * dest_p,
jl_genericmemory_t *src, _Atomic(void*) * src_p,
size_t* n) JL_NOTSAFEPOINT
{
mmtk_gc_wb_fast(dest_owner, (void*)0);
}

STATIC_INLINE void jl_gc_wb_genericmemory_copy_ptr(const jl_value_t *owner, jl_genericmemory_t *src, char* src_p,
size_t n, jl_datatype_t *dt) JL_NOTSAFEPOINT
{
mmtk_gc_wb_fast(owner, (void*)0);
}


#ifdef __cplusplus
}
#endif

#endif
Loading

0 comments on commit d245d17

Please sign in to comment.