Skip to content

Commit 877de98

Browse files
authored
Refactoring to be considered before adding MMTk (#55608)
This PR contains some refactoring of common functions that were moved to `gc-common.c` and should be shared between MMTk and Julia's stock GC.
1 parent b0c1525 commit 877de98

File tree

14 files changed

+402
-370
lines changed

14 files changed

+402
-370
lines changed

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ ifeq ($(USE_SYSTEM_LIBUV),0)
103103
UV_HEADERS += uv.h
104104
UV_HEADERS += uv/*.h
105105
endif
106-
PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h)
106+
PUBLIC_HEADERS := $(BUILDDIR)/julia_version.h $(wildcard $(SRCDIR)/support/*.h) $(addprefix $(SRCDIR)/,work-stealing-queue.h gc-interface.h gc-tls.h gc-tls-common.h julia.h julia_assert.h julia_threads.h julia_fasttls.h julia_locks.h julia_atomics.h jloptions.h)
107107
ifeq ($(OS),WINNT)
108108
PUBLIC_HEADERS += $(addprefix $(SRCDIR)/,win32_ucontext.h)
109109
endif

src/gc-common.c

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ extern "C" {
2020

2121
jl_gc_num_t gc_num = {0};
2222

23+
JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void)
24+
{
25+
return gc_num.total_time;
26+
}
27+
2328
// =========================================================================== //
2429
// GC Callbacks
2530
// =========================================================================== //
@@ -485,10 +490,210 @@ JL_DLLEXPORT void jl_finalize(jl_value_t *o)
485490
int gc_n_threads;
486491
jl_ptls_t* gc_all_tls_states;
487492

493+
// =========================================================================== //
494+
// Allocation
495+
// =========================================================================== //
496+
497+
JL_DLLEXPORT void * jl_gc_alloc_typed(jl_ptls_t ptls, size_t sz, void *ty)
498+
{
499+
return jl_gc_alloc(ptls, sz, ty);
500+
}
501+
502+
JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz)
503+
{
504+
jl_ptls_t ptls = jl_current_task->ptls;
505+
return jl_gc_alloc(ptls, sz, NULL);
506+
}
507+
508+
// allocation wrappers that save the size of allocations, to allow using
509+
// jl_gc_counted_* functions with a libc-compatible API.
510+
511+
JL_DLLEXPORT void *jl_malloc(size_t sz)
512+
{
513+
int64_t *p = (int64_t *)jl_gc_counted_malloc(sz + JL_SMALL_BYTE_ALIGNMENT);
514+
if (p == NULL)
515+
return NULL;
516+
p[0] = sz;
517+
return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
518+
}
519+
520+
//_unchecked_calloc does not check for potential overflow of nm*sz
521+
STATIC_INLINE void *_unchecked_calloc(size_t nm, size_t sz) {
522+
size_t nmsz = nm*sz;
523+
int64_t *p = (int64_t *)jl_gc_counted_calloc(nmsz + JL_SMALL_BYTE_ALIGNMENT, 1);
524+
if (p == NULL)
525+
return NULL;
526+
p[0] = nmsz;
527+
return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
528+
}
529+
530+
JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz)
531+
{
532+
if (nm > SSIZE_MAX/sz - JL_SMALL_BYTE_ALIGNMENT)
533+
return NULL;
534+
return _unchecked_calloc(nm, sz);
535+
}
536+
537+
JL_DLLEXPORT void jl_free(void *p)
538+
{
539+
if (p != NULL) {
540+
int64_t *pp = (int64_t *)p - 2;
541+
size_t sz = pp[0];
542+
jl_gc_counted_free_with_size(pp, sz + JL_SMALL_BYTE_ALIGNMENT);
543+
}
544+
}
545+
546+
JL_DLLEXPORT void *jl_realloc(void *p, size_t sz)
547+
{
548+
int64_t *pp;
549+
size_t szold;
550+
if (p == NULL) {
551+
pp = NULL;
552+
szold = 0;
553+
}
554+
else {
555+
pp = (int64_t *)p - 2;
556+
szold = pp[0] + JL_SMALL_BYTE_ALIGNMENT;
557+
}
558+
int64_t *pnew = (int64_t *)jl_gc_counted_realloc_with_old_size(pp, szold, sz + JL_SMALL_BYTE_ALIGNMENT);
559+
if (pnew == NULL)
560+
return NULL;
561+
pnew[0] = sz;
562+
return (void *)(pnew + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
563+
}
564+
565+
// allocator entry points
566+
567+
JL_DLLEXPORT jl_value_t *(jl_gc_alloc)(jl_ptls_t ptls, size_t sz, void *ty)
568+
{
569+
return jl_gc_alloc_(ptls, sz, ty);
570+
}
571+
572+
// =========================================================================== //
573+
// Generic Memory
574+
// =========================================================================== //
575+
576+
size_t jl_genericmemory_nbytes(jl_genericmemory_t *m) JL_NOTSAFEPOINT
577+
{
578+
const jl_datatype_layout_t *layout = ((jl_datatype_t*)jl_typetagof(m))->layout;
579+
size_t sz = layout->size * m->length;
580+
if (layout->flags.arrayelem_isunion)
581+
// account for isbits Union array selector bytes
582+
sz += m->length;
583+
return sz;
584+
}
585+
586+
// tracking Memorys with malloc'd storage
587+
void jl_gc_track_malloced_genericmemory(jl_ptls_t ptls, jl_genericmemory_t *m, int isaligned){
588+
// This is **NOT** a GC safe point.
589+
mallocmemory_t *ma;
590+
if (ptls->gc_tls_common.heap.mafreelist == NULL) {
591+
ma = (mallocmemory_t*)malloc_s(sizeof(mallocmemory_t));
592+
}
593+
else {
594+
ma = ptls->gc_tls_common.heap.mafreelist;
595+
ptls->gc_tls_common.heap.mafreelist = ma->next;
596+
}
597+
ma->a = (jl_genericmemory_t*)((uintptr_t)m | !!isaligned);
598+
ma->next = ptls->gc_tls_common.heap.mallocarrays;
599+
ptls->gc_tls_common.heap.mallocarrays = ma;
600+
}
601+
602+
// =========================================================================== //
603+
// GC Debug
604+
// =========================================================================== //
605+
606+
int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT
607+
{
608+
int nf = (int)jl_datatype_nfields(vt);
609+
for (int i = 1; i < nf; i++) {
610+
if (slot < (void*)((char*)obj + jl_field_offset(vt, i)))
611+
return i - 1;
612+
}
613+
return nf - 1;
614+
}
615+
616+
int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT
617+
{
618+
char *slot = (char*)_slot;
619+
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
620+
char *start = NULL;
621+
size_t len = 0;
622+
size_t elsize = sizeof(void*);
623+
if (vt == jl_module_type) {
624+
jl_module_t *m = (jl_module_t*)obj;
625+
start = (char*)m->usings.items;
626+
len = module_usings_length(m);
627+
elsize = sizeof(struct _jl_module_using);
628+
}
629+
else if (vt == jl_simplevector_type) {
630+
start = (char*)jl_svec_data(obj);
631+
len = jl_svec_len(obj);
632+
}
633+
if (slot < start || slot >= start + elsize * len)
634+
return -1;
635+
return (slot - start) / elsize;
636+
}
637+
638+
// =========================================================================== //
639+
// GC Control
640+
// =========================================================================== //
641+
642+
JL_DLLEXPORT uint32_t jl_get_gc_disable_counter(void) {
643+
return jl_atomic_load_acquire(&jl_gc_disable_counter);
644+
}
645+
646+
JL_DLLEXPORT int jl_gc_is_enabled(void)
647+
{
648+
jl_ptls_t ptls = jl_current_task->ptls;
649+
return !ptls->disable_gc;
650+
}
651+
652+
int gc_logging_enabled = 0;
653+
654+
JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
655+
gc_logging_enabled = enable;
656+
}
657+
658+
JL_DLLEXPORT int jl_is_gc_logging_enabled(void) {
659+
return gc_logging_enabled;
660+
}
661+
662+
663+
// collector entry point and control
664+
_Atomic(uint32_t) jl_gc_disable_counter = 1;
665+
666+
JL_DLLEXPORT int jl_gc_enable(int on)
667+
{
668+
jl_ptls_t ptls = jl_current_task->ptls;
669+
int prev = !ptls->disable_gc;
670+
ptls->disable_gc = (on == 0);
671+
if (on && !prev) {
672+
// disable -> enable
673+
if (jl_atomic_fetch_add(&jl_gc_disable_counter, -1) == 1) {
674+
gc_num.allocd += gc_num.deferred_alloc;
675+
gc_num.deferred_alloc = 0;
676+
}
677+
}
678+
else if (prev && !on) {
679+
// enable -> disable
680+
jl_atomic_fetch_add(&jl_gc_disable_counter, 1);
681+
// check if the GC is running and wait for it to finish
682+
jl_gc_safepoint_(ptls);
683+
}
684+
return prev;
685+
}
686+
488687
// =========================================================================== //
489688
// MISC
490689
// =========================================================================== //
491690

691+
JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value)
692+
{
693+
jl_ptls_t ptls = jl_current_task->ptls;
694+
return jl_gc_new_weakref_th(ptls, value);
695+
}
696+
492697
const uint64_t _jl_buff_tag[3] = {0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull, 0x4eadc0004eadc000ull}; // aka 0xHEADER00
493698
JL_DLLEXPORT uintptr_t jl_get_buff_tag(void) JL_NOTSAFEPOINT
494699
{

src/gc-common.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ extern jl_gc_callback_list_t *gc_cblist_notify_gc_pressure;
5353
// malloc wrappers, aligned allocation
5454
// =========================================================================== //
5555

56+
// data structure for tracking malloc'd genericmemory.
57+
typedef struct _mallocmemory_t {
58+
jl_genericmemory_t *a; // lowest bit is tagged if this is aligned memory
59+
struct _mallocmemory_t *next;
60+
} mallocmemory_t;
61+
5662
#if defined(_OS_WINDOWS_)
5763
STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align)
5864
{
@@ -173,4 +179,10 @@ JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o);
173179
extern int gc_n_threads;
174180
extern jl_ptls_t* gc_all_tls_states;
175181

182+
// =========================================================================== //
183+
// Logging
184+
// =========================================================================== //
185+
186+
extern int gc_logging_enabled;
187+
176188
#endif // JL_GC_COMMON_H

src/gc-debug.c

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,48 +1105,6 @@ void gc_count_pool(void)
11051105
jl_safe_printf("************************\n");
11061106
}
11071107

1108-
int gc_slot_to_fieldidx(void *obj, void *slot, jl_datatype_t *vt) JL_NOTSAFEPOINT
1109-
{
1110-
int nf = (int)jl_datatype_nfields(vt);
1111-
for (int i = 1; i < nf; i++) {
1112-
if (slot < (void*)((char*)obj + jl_field_offset(vt, i)))
1113-
return i - 1;
1114-
}
1115-
return nf - 1;
1116-
}
1117-
1118-
int gc_slot_to_arrayidx(void *obj, void *_slot) JL_NOTSAFEPOINT
1119-
{
1120-
char *slot = (char*)_slot;
1121-
jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
1122-
char *start = NULL;
1123-
size_t len = 0;
1124-
size_t elsize = sizeof(void*);
1125-
if (vt == jl_module_type) {
1126-
jl_module_t *m = (jl_module_t*)obj;
1127-
start = (char*)m->usings.items;
1128-
len = module_usings_length(m);
1129-
elsize = sizeof(struct _jl_module_using);
1130-
}
1131-
else if (vt == jl_simplevector_type) {
1132-
start = (char*)jl_svec_data(obj);
1133-
len = jl_svec_len(obj);
1134-
}
1135-
if (slot < start || slot >= start + elsize * len)
1136-
return -1;
1137-
return (slot - start) / elsize;
1138-
}
1139-
1140-
static int gc_logging_enabled = 0;
1141-
1142-
JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
1143-
gc_logging_enabled = enable;
1144-
}
1145-
1146-
JL_DLLEXPORT int jl_is_gc_logging_enabled(void) {
1147-
return gc_logging_enabled;
1148-
}
1149-
11501108
void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect, int64_t live_bytes) JL_NOTSAFEPOINT {
11511109
if (!gc_logging_enabled) {
11521110
return;

src/gc-interface.h

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ JL_DLLEXPORT void jl_gc_set_max_memory(uint64_t max_mem);
9696
// should run a collection cycle again (e.g. a full mark right after a full sweep to ensure
9797
// we do a full heap traversal).
9898
JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection);
99+
// Returns whether the thread with `tid` is a collector thread
100+
JL_DLLEXPORT int gc_is_collector_thread(int tid) JL_NOTSAFEPOINT;
99101

100102
// ========================================================================= //
101103
// Metrics
@@ -130,6 +132,13 @@ JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void);
130132
// Allocation
131133
// ========================================================================= //
132134

135+
// On GCC, this function is inlined when sz is constant (see julia_internal.h)
136+
// In general, this function should implement allocation and should use the specific GC's logic
137+
// to decide whether to allocate a small or a large object. Finally, note that this function
138+
// **must** also set the type of the returning object to be `ty`. The type `ty` may also be used to record
139+
// an allocation of that type in the allocation profiler.
140+
struct _jl_value_t *jl_gc_alloc_(struct _jl_tls_states_t * ptls, size_t sz, void *ty);
141+
133142
// Allocates small objects and increments Julia allocation counterst. Size of the object
134143
// header must be included in the object size. The (possibly unused in some implementations)
135144
// offset to the arena in which we're allocating is passed in the second parameter, and the
@@ -157,26 +166,6 @@ JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz);
157166
JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz);
158167
// Wrapper around Libc realloc that updates Julia allocation counters.
159168
JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size_t sz);
160-
// Wrapper around Libc malloc that allocates a memory region with a few additional machine
161-
// words before the actual payload that are used to record the size of the requested
162-
// allocation. Also updates Julia allocation counters. The function returns a pointer to the
163-
// payload as a result of the allocation.
164-
JL_DLLEXPORT void *jl_malloc(size_t sz);
165-
// Wrapper around Libc calloc that allocates a memory region with a few additional machine
166-
// words before the actual payload that are used to record the size of the requested
167-
// allocation. Also updates Julia allocation counters. The function returns a pointer to the
168-
// payload as a result of the allocation.
169-
JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz);
170-
// Wrapper around Libc free that takes a pointer to the payload of a memory region allocated
171-
// with jl_malloc or jl_calloc, and uses the size information stored in the first machine
172-
// words of the memory buffer update Julia allocation counters, and then frees the
173-
// corresponding memory buffer.
174-
JL_DLLEXPORT void jl_free(void *p);
175-
// Wrapper around Libc realloc that takes a memory region allocated with jl_malloc or
176-
// jl_calloc, and uses the size information stored in the first machine words of the memory
177-
// buffer to update Julia allocation counters, reallocating the corresponding memory buffer
178-
// in the end.
179-
JL_DLLEXPORT void *jl_realloc(void *p, size_t sz);
180169
// Wrapper around Libc malloc that's used to dynamically allocate memory for Arrays and
181170
// Strings. It increments Julia allocation counters and should check whether we're close to
182171
// the Julia heap target, and therefore, whether we should run a collection. Note that this
@@ -190,14 +179,6 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz);
190179
// thread-local allocator of the thread referenced by the first jl_ptls_t argument.
191180
JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref_th(struct _jl_tls_states_t *ptls,
192181
struct _jl_value_t *value);
193-
// Allocates a new weak-reference, assigns its value and increments Julia allocation
194-
// counters. If thread-local allocators are used, then this function should allocate in the
195-
// thread-local allocator of the current thread.
196-
JL_DLLEXPORT struct _jl_weakref_t *jl_gc_new_weakref(struct _jl_value_t *value);
197-
// Allocates an object whose size is specified by the function argument and increments Julia
198-
// allocation counters. If thread-local allocators are used, then this function should
199-
// allocate in the thread-local allocator of the current thread.
200-
JL_DLLEXPORT struct _jl_value_t *jl_gc_allocobj(size_t sz);
201182
// Permanently allocates a memory slot of the size specified by the first parameter. This
202183
// block of memory is allocated in an immortal region that is never swept. The second
203184
// parameter specifies whether the memory should be filled with zeros. The third and fourth

0 commit comments

Comments
 (0)