Skip to content

Commit bda40bd

Browse files
committed
Merge branch 'dev' into merging-master-into-dev
2 parents 2d3d545 + dfd0a0e commit bda40bd

13 files changed

+338
-23
lines changed

src/gc-interface.h

+25
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,26 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem);
101101
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection);
102102
// Returns whether the thread with `tid` is a collector thread
103103
JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;
104+
// Pinning objects; Returns whether the object has been pinned by this call.
105+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj);
104106
// Returns which GC implementation is being used and possibly its version according to the list of supported GCs
105107
// NB: it should clearly identify the GC by including e.g. ‘stock’ or ‘mmtk’ as a substring.
106108
JL_DLLEXPORT const char* jl_gc_active_impl(void);
107109
// Sweep Julia's stack pools and mtarray buffers. Note that this function has been added to the interface as
108110
// each GC should implement it but it will most likely not be used by other code in the runtime.
109111
// It still needs to be annotated with JL_DLLEXPORT since it is called from Rust by MMTk.
110112
JL_DLLEXPORT void jl_gc_sweep_stack_pools_and_mtarraylist_buffers(jl_ptls_t ptls) JL_NOTSAFEPOINT;
113+
// Notifies the GC that the given thread is about to yield for a GC. ctx is the ucontext for the thread
114+
// if it is already fetched by the caller, otherwise it is NULL.
115+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx);
116+
117+
// TODO: The preserve hook functions may be temporary. We should see the performance impact of the change.
118+
119+
// 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.
120+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT;
121+
// 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.
122+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT;
123+
111124

112125
// ========================================================================= //
113126
// Metrics
@@ -207,10 +220,22 @@ JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align,
207220
// the allocated object. All objects stored in fields of this object
208221
// must be either permanently allocated or have other roots.
209222
struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT;
223+
// permanently allocates a symbol (jl_sym_t). The object needs to be word aligned,
224+
// and tagged with jl_sym_tag.
225+
// FIXME: Ideally we should merge this with jl_gc_permobj, as symbol is an object.
226+
// Currently there are a few differences between the two functions, and refactoring is needed.
227+
// 1. sz for this function includes the object header, and sz for jl_gc_permobj excludes the header size.
228+
// 2. align for this function is word align, and align for jl_gc_permobj depends on the allocation size.
229+
// 3. ty for this function is jl_symbol_tag << 4, and ty for jl_gc_permobj is a datatype pointer.
230+
struct _jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT;
210231
// This function notifies the GC about memory addresses that are set when loading the boot image.
211232
// The GC may use that information to, for instance, determine that such objects should
212233
// be treated as marked and belonged to the old generation in nursery collections.
213234
void jl_gc_notify_image_load(const char* img_data, size_t len);
235+
// This function notifies the GC about memory addresses that are set when allocating the boot image.
236+
// The GC may use that information to, for instance, determine that all objects in that chunk of memory should
237+
// be treated as marked and belonged to the old generation in nursery collections.
238+
void jl_gc_notify_image_alloc(const char* img_data, size_t len);
214239

215240
// ========================================================================= //
216241
// Runtime Write-Barriers

src/gc-mmtk.c

+134-10
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,24 @@ JL_DLLEXPORT void jl_gc_prepare_to_collect(void)
323323
errno = last_errno;
324324
}
325325

326+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
327+
return mmtk_pin_object(obj);
328+
}
329+
330+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
331+
if (ctx == NULL) {
332+
// Save the context for the thread as it was running at the time of the call
333+
int r = getcontext(&ptls->gc_tls.ctx_at_the_time_gc_started);
334+
if (r == -1) {
335+
jl_safe_printf("Failed to save context for conservative scanning\n");
336+
abort();
337+
}
338+
return;
339+
}
340+
memcpy(&ptls->gc_tls.ctx_at_the_time_gc_started, ctx, sizeof(ucontext_t));
341+
}
342+
343+
326344
// ========================================================================= //
327345
// GC Statistics
328346
// ========================================================================= //
@@ -807,6 +825,36 @@ JL_DLLEXPORT int* jl_gc_get_have_pending_finalizers(void) {
807825
return (int*)&jl_gc_have_pending_finalizers;
808826
}
809827

828+
829+
// ========================================================================= //
830+
// Write barriers
831+
// ========================================================================= //
832+
833+
// No inline write barrier -- only used for debugging
834+
JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT
835+
{
836+
jl_gc_wb_back(parent);
837+
}
838+
839+
JL_DLLEXPORT void jl_gc_wb2_noinline(const void *parent, const void *ptr) JL_NOTSAFEPOINT
840+
{
841+
jl_gc_wb(parent, ptr);
842+
}
843+
844+
JL_DLLEXPORT void jl_gc_wb1_slow(const void *parent) JL_NOTSAFEPOINT
845+
{
846+
jl_task_t *ct = jl_current_task;
847+
jl_ptls_t ptls = ct->ptls;
848+
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, parent, (const void*) 0);
849+
}
850+
851+
JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFEPOINT
852+
{
853+
jl_task_t *ct = jl_current_task;
854+
jl_ptls_t ptls = ct->ptls;
855+
mmtk_object_reference_write_slow(&ptls->gc_tls.mmtk_mutator, parent, ptr);
856+
}
857+
810858
// ========================================================================= //
811859
// Allocation
812860
// ========================================================================= //
@@ -842,29 +890,43 @@ STATIC_INLINE void* bump_alloc_fast(MMTkMutatorContext* mutator, uintptr_t* curs
842890
}
843891
}
844892

893+
inline void mmtk_set_side_metadata(const void* side_metadata_base, void* obj) {
894+
intptr_t addr = (intptr_t) obj;
895+
uint8_t* meta_addr = (uint8_t*) side_metadata_base + (addr >> 6);
896+
intptr_t shift = (addr >> 3) & 0b111;
897+
while(1) {
898+
uint8_t old_val = *meta_addr;
899+
uint8_t new_val = old_val | (1 << shift);
900+
if (jl_atomic_cmpswap((_Atomic(uint8_t)*)meta_addr, &old_val, new_val)) {
901+
break;
902+
}
903+
}
904+
}
905+
845906
STATIC_INLINE void* mmtk_immix_alloc_fast(MMTkMutatorContext* mutator, size_t size, size_t align, size_t offset) {
846907
ImmixAllocator* allocator = &mutator->allocators.immix[MMTK_DEFAULT_IMMIX_ALLOCATOR];
847908
return bump_alloc_fast(mutator, (uintptr_t*)&allocator->cursor, (intptr_t)allocator->limit, size, align, offset, 0);
848909
}
849910

850-
inline void mmtk_immix_post_alloc_slow(MMTkMutatorContext* mutator, void* obj, size_t size) {
851-
mmtk_post_alloc(mutator, obj, size, 0);
852-
}
853-
854911
STATIC_INLINE void mmtk_immix_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
855-
// FIXME: for now, we do nothing
856-
// but when supporting moving, this is where we set the valid object (VO) bit
912+
if (MMTK_NEEDS_VO_BIT) {
913+
mmtk_set_side_metadata(MMTK_SIDE_VO_BIT_BASE_ADDRESS, obj);
914+
}
857915
}
858916

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

864-
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
922+
STATIC_INLINE void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
923+
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
924+
mmtk_set_side_metadata(MMTK_SIDE_LOG_BIT_BASE_ADDRESS, obj);
925+
}
926+
927+
if (MMTK_NEEDS_VO_BIT) {
928+
mmtk_set_side_metadata(MMTK_SIDE_VO_BIT_BASE_ADDRESS, obj);
929+
}
868930
}
869931

870932
JL_DLLEXPORT jl_value_t *jl_mmtk_gc_alloc_default(jl_ptls_t ptls, int osize, size_t align, void *ty)
@@ -1042,6 +1104,16 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
10421104
return jl_valueof(o);
10431105
}
10441106

1107+
jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
1108+
{
1109+
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
1110+
jl_value_t *sym = jl_valueof(tag);
1111+
jl_ptls_t ptls = jl_current_task->ptls;
1112+
jl_set_typetagof(sym, jl_symbol_tag, 0); // We need to set symbol tag. The GC tag doesnt matter.
1113+
mmtk_immortal_post_alloc_fast(&ptls->gc_tls.mmtk_mutator, sym, sz);
1114+
return sym;
1115+
}
1116+
10451117
JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz)
10461118
{
10471119
jl_ptls_t ptls = jl_current_task->ptls;
@@ -1079,6 +1151,11 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
10791151
mmtk_set_vm_space((void*)img_data, len);
10801152
}
10811153

1154+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
1155+
{
1156+
mmtk_immortal_region_post_alloc((void*)img_data, len);
1157+
}
1158+
10821159
// ========================================================================= //
10831160
// Code specific to stock that is not supported by MMTk
10841161
// ========================================================================= //
@@ -1208,6 +1285,53 @@ JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
12081285
return NULL;
12091286
}
12101287

1288+
#define jl_p_gcpreserve_stack (jl_current_task->gcpreserve_stack)
1289+
1290+
// This macro currently uses malloc instead of alloca because this function will exit
1291+
// after pushing the roots into the gc_preserve_stack, which means that the preserve_begin function's
1292+
// stack frame will be destroyed (together with its alloca variables). When we support lowering this code
1293+
// inside the same function that is doing the preserve_begin/preserve_end calls we should be able to simple use allocas.
1294+
// Note also that we use a separate stack for gc preserve roots to avoid the possibility of calling free
1295+
// on a stack that has been allocated with alloca instead of malloc, which could happen depending on the order in which
1296+
// JL_GC_POP() and jl_gc_preserve_end_hook() occurs.
1297+
1298+
#define JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(rts_var,n) \
1299+
rts_var = ((jl_value_t**)malloc(((n)+2)*sizeof(jl_value_t*)))+2; \
1300+
((void**)rts_var)[-2] = (void*)JL_GC_ENCODE_PUSHARGS(n); \
1301+
((void**)rts_var)[-1] = jl_p_gcpreserve_stack; \
1302+
memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*)); \
1303+
jl_p_gcpreserve_stack = (jl_gcframe_t*)&(((void**)rts_var)[-2]); \
1304+
1305+
#define JL_GC_POP_PRESERVE_ROOT_OBJS() \
1306+
jl_gcframe_t *curr = jl_p_gcpreserve_stack; \
1307+
if(curr) { \
1308+
(jl_p_gcpreserve_stack = jl_p_gcpreserve_stack->prev); \
1309+
free(curr); \
1310+
}
1311+
1312+
// Add each argument as a tpin root object.
1313+
// However, we cannot use JL_GC_PUSH and JL_GC_POP since the slots should live
1314+
// beyond this function. Instead, we maintain a tpin stack by mallocing/freeing
1315+
// the frames for each of the preserve regions we encounter
1316+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
1317+
{
1318+
jl_value_t** frame;
1319+
JL_GC_PUSHARGS_PRESERVE_ROOT_OBJS(frame, n);
1320+
if (n == 0) return;
1321+
1322+
va_list args;
1323+
va_start(args, n);
1324+
for (int i = 0; i < n; i++) {
1325+
frame[i] = va_arg(args, jl_value_t *);
1326+
}
1327+
va_end(args);
1328+
}
1329+
1330+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
1331+
{
1332+
JL_GC_POP_PRESERVE_ROOT_OBJS();
1333+
}
1334+
12111335
#ifdef __cplusplus
12121336
}
12131337
#endif

src/gc-stock.c

+55
Original file line numberDiff line numberDiff line change
@@ -3458,6 +3458,11 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
34583458
gc_cblist_pre_gc, (collection));
34593459

34603460
if (!jl_atomic_load_acquire(&jl_gc_disable_counter)) {
3461+
// This thread will yield.
3462+
// jl_gc_notify_thread_yield does nothing for the stock GC at the point, but it may be non empty in the future,
3463+
// and this is a place where we should call jl_gc_notify_thread_yield.
3464+
// TODO: This call can be removed if requested.
3465+
jl_gc_notify_thread_yield(ptls, NULL);
34613466
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
34623467
#ifndef __clang_gcanalyzer__
34633468
if (_jl_gc_collect(ptls, collection)) {
@@ -3940,6 +3945,15 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
39403945
return jl_valueof(o);
39413946
}
39423947

3948+
jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
3949+
{
3950+
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
3951+
jl_value_t *sym = jl_valueof(tag);
3952+
// set to old marked so that we won't look at it in the GC or write barrier.
3953+
jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED);
3954+
return sym;
3955+
}
3956+
39433957
JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void)
39443958
{
39453959
if (jl_is_initialized()) {
@@ -4076,6 +4090,47 @@ void jl_gc_notify_image_load(const char* img_data, size_t len)
40764090
// Do nothing
40774091
}
40784092

4093+
void jl_gc_notify_image_alloc(const char* img_data, size_t len)
4094+
{
4095+
// Do nothing
4096+
}
4097+
4098+
JL_DLLEXPORT unsigned char jl_gc_pin_object(void* obj) {
4099+
return 0;
4100+
}
4101+
4102+
// added for MMTk integration
4103+
4104+
JL_DLLEXPORT void jl_gc_wb1_noinline(const void *parent) JL_NOTSAFEPOINT
4105+
{
4106+
}
4107+
4108+
JL_DLLEXPORT void jl_gc_wb2_noinline(const void *parent, const void *ptr) JL_NOTSAFEPOINT
4109+
{
4110+
}
4111+
4112+
JL_DLLEXPORT void jl_gc_wb1_slow(const void *parent) JL_NOTSAFEPOINT
4113+
{
4114+
}
4115+
4116+
JL_DLLEXPORT void jl_gc_wb2_slow(const void *parent, const void* ptr) JL_NOTSAFEPOINT
4117+
{
4118+
}
4119+
4120+
JL_DLLEXPORT void jl_gc_preserve_begin_hook(int n, ...) JL_NOTSAFEPOINT
4121+
{
4122+
jl_unreachable();
4123+
}
4124+
4125+
JL_DLLEXPORT void jl_gc_preserve_end_hook(void) JL_NOTSAFEPOINT
4126+
{
4127+
jl_unreachable();
4128+
}
4129+
4130+
JL_DLLEXPORT void jl_gc_notify_thread_yield(jl_ptls_t ptls, void* ctx) {
4131+
// Do nothing before a thread yields
4132+
}
4133+
40794134
JL_DLLEXPORT const char* jl_gc_active_impl(void) {
40804135
return "Built with stock GC";
40814136
}

src/gc-tls-mmtk.h

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ extern "C" {
1414
typedef struct {
1515
MMTkMutatorContext mmtk_mutator;
1616
_Atomic(size_t) malloc_sz_since_last_poll;
17+
ucontext_t ctx_at_the_time_gc_started;
1718
} jl_gc_tls_states_t;
1819

1920
#ifdef __cplusplus

src/genericmemory.c

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_string_to_genericmemory(jl_value_t *str)
111111
m->length = jl_string_len(str);
112112
m->ptr = jl_string_data(str);
113113
jl_genericmemory_data_owner_field(m) = str;
114+
PTR_PIN(str);
114115
return m;
115116
}
116117

@@ -166,6 +167,7 @@ JL_DLLEXPORT jl_genericmemory_t *jl_ptr_to_genericmemory(jl_value_t *mtype, void
166167
m->length = nel;
167168
jl_genericmemory_data_owner_field(m) = own_buffer ? (jl_value_t*)m : NULL;
168169
if (own_buffer) {
170+
PTR_PIN(m);
169171
int isaligned = 0; // TODO: allow passing memalign'd buffers
170172
jl_gc_track_malloced_genericmemory(ct->ptls, m, isaligned);
171173
size_t allocated_bytes = memory_block_usable_size(data, isaligned);
@@ -235,6 +237,10 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
235237
_Atomic(void*) * dest_p = (_Atomic(void*)*)destdata;
236238
_Atomic(void*) * src_p = (_Atomic(void*)*)srcdata;
237239
jl_value_t *owner = jl_genericmemory_owner(dest);
240+
// FIXME: The following should be a write barrier impl provided by the GC.
241+
#ifdef MMTK_GC
242+
jl_gc_wb(owner, NULL);
243+
#else
238244
if (__unlikely(jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED)) {
239245
jl_value_t *src_owner = jl_genericmemory_owner(src);
240246
ssize_t done = 0;
@@ -265,6 +271,7 @@ JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destda
265271
n -= done;
266272
}
267273
}
274+
#endif
268275
return memmove_refs(dest_p, src_p, n);
269276
}
270277
size_t elsz = layout->size;

src/interpreter.c

+2
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
891891
unsigned nroots = jl_source_nslots(src) + jl_source_nssavalues(src);
892892
JL_GC_PUSHFRAME(s, s->locals, nroots);
893893
jl_array_t *stmts = src->code;
894+
JL_GC_PUSH1(&stmts);
894895
assert(jl_typetagis(stmts, jl_array_any_type));
895896
s->src = src;
896897
s->module = m;
@@ -901,6 +902,7 @@ jl_value_t *NOINLINE jl_interpret_toplevel_thunk(jl_module_t *m, jl_code_info_t
901902
JL_GC_ENABLEFRAME(s);
902903
jl_value_t *r = eval_body(stmts, s, 0, 1);
903904
JL_GC_POP();
905+
JL_GC_POP();
904906
return r;
905907
}
906908

0 commit comments

Comments
 (0)