Skip to content

Commit

Permalink
Merge branch 'dev' into merging-master-into-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
udesou committed Jan 29, 2025
2 parents 2d3d545 + dfd0a0e commit ac06dbb
Show file tree
Hide file tree
Showing 12 changed files with 300 additions and 15 deletions.
25 changes: 25 additions & 0 deletions src/gc-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,26 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem);
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection);
// Returns whether the thread with `tid` is a collector thread
JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;
// Pinning objects; Returns whether the object has been pinned by this call.
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj);
// Returns which GC implementation is being used and possibly its version according to the list of supported GCs
// NB: it should clearly identify the GC by including e.g. ‘stock’ or ‘mmtk’ as a substring.
JL_DLLEXPORT const char* jl_gc_active_impl(void);
// Sweep Julia's stack pools and mtarray buffers. Note that this function has been added to the interface as
// each GC should implement it but it will most likely not be used by other code in the runtime.
// It still needs to be annotated with JL_DLLEXPORT since it is called from Rust by MMTk.
JL_DLLEXPORT void jl_gc_sweep_stack_pools_and_mtarraylist_buffers(jl_ptls_t ptls) JL_NOTSAFEPOINT;
// Notifies the GC that the given thread is about to yield for a GC. ctx is the ucontext for the thread
// if it is already fetched by the caller, otherwise it is NULL.
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx);

// TODO: The preserve hook functions may be temporary. We should see the performance impact of the change.

// Runtime hook for gc preserve begin. The GC needs to make sure that the preserved objects and its children stay alive and won't move.
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT;
// Runtime hook for gc preserve end. The GC needs to make sure that the preserved objects and its children stay alive and won't move.
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT;


// ========================================================================= //
// Metrics
Expand Down Expand Up @@ -207,10 +220,22 @@ JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align,
// the allocated object. All objects stored in fields of this object
// must be either permanently allocated or have other roots.
struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT;
// permanently allocates a symbol (jl_sym_t). The object needs to be word aligned,
// and tagged with jl_sym_tag.
// FIXME: Ideally we should merge this with jl_gc_permobj, as symbol is an object.
// Currently there are a few differences between the two functions, and refactoring is needed.
// 1. sz for this function includes the object header, and sz for jl_gc_permobj excludes the header size.
// 2. align for this function is word align, and align for jl_gc_permobj depends on the allocation size.
// 3. ty for this function is jl_symbol_tag << 4, and ty for jl_gc_permobj is a datatype pointer.
struct _jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT;
// This function notifies the GC about memory addresses that are set when loading the boot image.
// 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
114 changes: 104 additions & 10 deletions src/gc-mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,24 @@ JL_DLLEXPORT void jl_gc_prepare_to_collect(void)
errno = last_errno;
}

JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
return mmtk_pin_object(obj);
}

JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
if (ctx == NULL) {
// Save the context for the thread as it was running at the time of the call
int r = getcontext(&ptls->gc_tls.ctx_at_the_time_gc_started);
if (r == -1) {
jl_safe_printf("Failed to save context for conservative scanning\n");
abort();
}
return;
}
memcpy(&ptls->gc_tls.ctx_at_the_time_gc_started, ctx, sizeof(ucontext_t));
}


// ========================================================================= //
// GC Statistics
// ========================================================================= //
Expand Down Expand Up @@ -842,29 +860,43 @@ STATIC_INLINE void* bump_alloc_fast(MMTkMutatorContext* mutator, uintptr_t* curs
}
}

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_immix_alloc_fast(MMTkMutatorContext* mutator, size_t size, size_t align, size_t offset) {
ImmixAllocator* allocator = &mutator->allocators.immix[MMTK_DEFAULT_IMMIX_ALLOCATOR];
return bump_alloc_fast(mutator, (uintptr_t*)&allocator->cursor, (intptr_t)allocator->limit, size, align, offset, 0);
}

inline void mmtk_immix_post_alloc_slow(MMTkMutatorContext* mutator, void* obj, size_t size) {
mmtk_post_alloc(mutator, obj, size, 0);
}

STATIC_INLINE void mmtk_immix_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
// FIXME: for now, we do nothing
// but when supporting moving, this is where we set the valid object (VO) bit
if (MMTK_NEEDS_VO_BIT) {
mmtk_set_side_metadata(MMTK_SIDE_VO_BIT_BASE_ADDRESS, obj);
}
}

STATIC_INLINE void* mmtk_immortal_alloc_fast(MMTkMutatorContext* mutator, size_t size, size_t align, size_t offset) {
BumpAllocator* allocator = &mutator->allocators.bump_pointer[MMTK_IMMORTAL_BUMP_ALLOCATOR];
return bump_alloc_fast(mutator, (uintptr_t*)&allocator->cursor, (uintptr_t)allocator->limit, size, align, offset, 1);
}

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
STATIC_INLINE void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
mmtk_set_side_metadata(MMTK_SIDE_LOG_BIT_BASE_ADDRESS, obj);
}

if (MMTK_NEEDS_VO_BIT) {
mmtk_set_side_metadata(MMTK_SIDE_VO_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 @@ -1042,6 +1074,16 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
return jl_valueof(o);
}

jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
{
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
jl_value_t *sym = jl_valueof(tag);
jl_ptls_t ptls = jl_current_task->ptls;
jl_set_typetagof(sym, jl_symbol_tag, 0); // We need to set symbol tag. The GC tag doesnt matter.
mmtk_immortal_post_alloc_fast(&ptls->gc_tls.mmtk_mutator, sym, sz);
return sym;
}

JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz)
{
jl_ptls_t ptls = jl_current_task->ptls;
Expand Down Expand Up @@ -1079,6 +1121,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 @@ -1208,6 +1255,53 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
return NULL;
}

#define jl_p_gcpreserve_stack (jl_current_task->gcpreserve_stack)

// This macro currently uses malloc instead of alloca because this function will exit
// after pushing the roots into the gc_preserve_stack, which means that the preserve_begin function's
// stack frame will be destroyed (together with its alloca variables). When we support lowering this code
// inside the same function that is doing the preserve_begin/preserve_end calls we should be able to simple use allocas.
// Note also that we use a separate stack for gc preserve roots to avoid the possibility of calling free
// on a stack that has been allocated with alloca instead of malloc, which could happen depending on the order in which
// JL_GC_POP() and jl_gc_preserve_end_hook() occurs.

#define JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(rts_var,n) \
rts_var = ((jl_value_t**)malloc(((n)+2)*sizeof(jl_value_t*)))+2; \
((void**)rts_var)[-2] = (void*)JL_GC_ENCODE_PUSHARGS(n); \
((void**)rts_var)[-1] = jl_p_gcpreserve_stack; \
memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*)); \
jl_p_gcpreserve_stack = (jl_gcframe_t*)&(((void**)rts_var)[-2]); \

#define JL_GC_POP_PRESERVE_ROOT_OBJS() \
jl_gcframe_t *curr = jl_p_gcpreserve_stack; \
if(curr) { \
(jl_p_gcpreserve_stack = jl_p_gcpreserve_stack->prev); \
free(curr); \
}

// Add each argument as a tpin root object.
// However, we cannot use JL_GC_PUSH and JL_GC_POP since the slots should live
// beyond this function. Instead, we maintain a tpin stack by mallocing/freeing
// the frames for each of the preserve regions we encounter
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
{
jl_value_t** frame;
JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(frame, n);
if (n == 0) return;

va_list args;
va_start(args, n);
for (int i = 0; i < n; i++) {
frame[i] = va_arg(args, jl_value_t *);
}
va_end(args);
}

JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
{
JL_GC_POP_PRESERVE_ROOT_OBJS();
}

#ifdef __cplusplus
}
#endif
55 changes: 55 additions & 0 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3458,6 +3458,11 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
gc_cblist_pre_gc, (collection));

if (!jl_atomic_load_acquire(&jl_gc_disable_counter)) {
// This thread will yield.
// jl_gc_notify_thread_yield does nothing for the stock GC at the point, but it may be non empty in the future,
// and this is a place where we should call jl_gc_notify_thread_yield.
// TODO: This call can be removed if requested.
jl_gc_notify_thread_yield(ptls, NULL);
JL_LOCK_NOGC(&finalizers_lock); // all the other threads are stopped, so this does not make sense, right? otherwise, failing that, this seems like plausibly a deadlock
#ifndef __clang_gcanalyzer__
if (_jl_gc_collect(ptls, collection)) {
Expand Down Expand Up @@ -3940,6 +3945,15 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
return jl_valueof(o);
}

jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
{
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
jl_value_t *sym = jl_valueof(tag);
// set to old marked so that we won't look at it in the GC or write barrier.
jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED);
return sym;
}

JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void)
{
if (jl_is_initialized()) {
Expand Down Expand Up @@ -4076,6 +4090,47 @@ 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 unsigned char jl_gc_pin_object(void* obj) {
return 0;
}

// added for MMTk integration

JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT
{
}

JL_DLLEXPORT void jl_gc_wb2_noinline(const void *parent, const void *ptr) JL_NOTSAFEPOINT
{
}

JL_DLLEXPORT void jl_gc_wb1_slow(const void *parent) JL_NOTSAFEPOINT
{
}

JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFEPOINT
{
}

JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
{
jl_unreachable();
}

JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
{
jl_unreachable();
}

JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
// Do nothing before a thread yields
}

JL_DLLEXPORT const char* jl_gc_active_impl(void) {
return "Built with stock GC";
}
Expand Down
1 change: 1 addition & 0 deletions src/gc-tls-mmtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extern "C" {
typedef struct {
MMTkMutatorContext mmtk_mutator;
_Atomic(size_t) malloc_sz_since_last_poll;
ucontext_t ctx_at_the_time_gc_started;
} jl_gc_tls_states_t;

#ifdef __cplusplus
Expand Down
7 changes: 7 additions & 0 deletions src/genericmemory.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_string_to_genericmemory(jl_value_t *str)
m->length = jl_string_len(str);
m->ptr = jl_string_data(str);
jl_genericmemory_data_owner_field(m) = str;
PTR_PIN(str);
return m;
}

Expand Down Expand Up @@ -166,6 +167,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_ptr_to_genericmemory(jl_value_t *mtype, void
m->length = nel;
jl_genericmemory_data_owner_field(m) = own_buffer ? (jl_value_t*)m : NULL;
if (own_buffer) {
PTR_PIN(m);
int isaligned = 0; // TODO: allow passing memalign'd buffers
jl_gc_track_malloced_genericmemory(ct->ptls, m, isaligned);
size_t allocated_bytes = memory_block_usable_size(data, isaligned);
Expand Down Expand Up @@ -235,6 +237,10 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
_Atomic(void*) * dest_p = (_Atomic(void*)*)destdata;
_Atomic(void*) * src_p = (_Atomic(void*)*)srcdata;
jl_value_t *owner = jl_genericmemory_owner(dest);
// FIXME: The following should be a write barrier impl provided by the GC.
#ifdef MMTK_GC
jl_gc_wb(owner, NULL);
#else
if (__unlikely(jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED)) {
jl_value_t *src_owner = jl_genericmemory_owner(src);
ssize_t done = 0;
Expand Down Expand Up @@ -265,6 +271,7 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
n -= done;
}
}
#endif
return memmove_refs(dest_p, src_p, n);
}
size_t elsz = layout->size;
Expand Down
2 changes: 2 additions & 0 deletions src/interpreter.c
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src);
JL_GC_PUSHFRAME(s, s->locals, nroots);
jl_array_t *stmts = src->code;
JL_GC_PUSH1(&stmts);
assert(jl_typetagis(stmts, jl_array_any_type));
s->src = src;
s->module = m;
Expand All @@ -901,6 +902,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
JL_GC_ENABLEFRAME(s);
jl_value_t *r = eval_body(stmts, s, 0, 1);
JL_GC_POP();
JL_GC_POP();
return r;
}

Expand Down
7 changes: 5 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -1179,7 +1179,7 @@ JL_DLLEXPORT void jl_free_stack(void *stkbuf, size_t bufsz);
JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value);

// GC write barriers

#ifndef MMTK_GC
STATIC_INLINE void jl_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOINT
{
// parent and ptr isa jl_value_t*
Expand Down Expand Up @@ -2715,7 +2715,9 @@ extern void mmtk_object_reference_write_slow(void* mutator, const void* parent,
// will complain about seeing objects without VO bit.
#define MMTK_NEEDS_VO_BIT (1)

void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size);
// ========================================================================= //
// Write Barriers
// ========================================================================= //

extern const void* MMTK_SIDE_LOG_BIT_BASE_ADDRESS;
extern const void* MMTK_SIDE_VO_BIT_BASE_ADDRESS;
Expand Down Expand Up @@ -2749,6 +2751,7 @@ STATIC_INLINE void mmtk_gc_wb(const void *parent, const void *ptr) JL_NOTSAFEPOI
mmtk_gc_wb_fast(parent, ptr);
}


#endif

#ifdef __cplusplus
Expand Down
3 changes: 3 additions & 0 deletions src/julia_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ typedef struct _jl_task_t {
// uint48_t padding2_64;
// saved gc stack top for context switches
jl_gcframe_t *gcstack;
// GC stack of objects from gc preserve regions
// These must always be transitively pinned. Only used by MMTK.
jl_gcframe_t *gcpreserve_stack;
size_t world_age;
// quick lookup for current ptls
jl_ptls_t ptls; // == jl_all_tls_states[tid]
Expand Down
Loading

0 comments on commit ac06dbb

Please sign in to comment.