Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "gc.h"
4 : : #include "julia_gcext.h"
5 : : #include "julia_assert.h"
6 : : #ifdef __GLIBC__
7 : : #include <malloc.h> // for malloc_trim
8 : : #endif
9 : :
10 : : #ifdef __cplusplus
11 : : extern "C" {
12 : : #endif
13 : :
14 : : // Linked list of callback functions
15 : :
16 : : typedef void (*jl_gc_cb_func_t)(void);
17 : :
18 : : typedef struct jl_gc_callback_list_t {
19 : : struct jl_gc_callback_list_t *next;
20 : : jl_gc_cb_func_t func;
21 : : } jl_gc_callback_list_t;
22 : :
23 : : static jl_gc_callback_list_t *gc_cblist_root_scanner;
24 : : static jl_gc_callback_list_t *gc_cblist_task_scanner;
25 : : static jl_gc_callback_list_t *gc_cblist_pre_gc;
26 : : static jl_gc_callback_list_t *gc_cblist_post_gc;
27 : : static jl_gc_callback_list_t *gc_cblist_notify_external_alloc;
28 : : static jl_gc_callback_list_t *gc_cblist_notify_external_free;
29 : :
30 : : #define gc_invoke_callbacks(ty, list, args) \
31 : : do { \
32 : : for (jl_gc_callback_list_t *cb = list; \
33 : : cb != NULL; \
34 : : cb = cb->next) \
35 : : { \
36 : : ((ty)(cb->func)) args; \
37 : : } \
38 : : } while (0)
39 : :
40 : 0 : static void jl_gc_register_callback(jl_gc_callback_list_t **list,
41 : : jl_gc_cb_func_t func)
42 : : {
43 [ # # ]: 0 : while (*list != NULL) {
44 [ # # ]: 0 : if ((*list)->func == func)
45 : 0 : return;
46 : 0 : list = &((*list)->next);
47 : : }
48 : 0 : *list = (jl_gc_callback_list_t *)malloc_s(sizeof(jl_gc_callback_list_t));
49 : 0 : (*list)->next = NULL;
50 : 0 : (*list)->func = func;
51 : : }
52 : :
53 : 0 : static void jl_gc_deregister_callback(jl_gc_callback_list_t **list,
54 : : jl_gc_cb_func_t func)
55 : : {
56 [ # # ]: 0 : while (*list != NULL) {
57 [ # # ]: 0 : if ((*list)->func == func) {
58 : 0 : jl_gc_callback_list_t *tmp = *list;
59 : 0 : (*list) = (*list)->next;
60 : 0 : free(tmp);
61 : 0 : return;
62 : : }
63 : 0 : list = &((*list)->next);
64 : : }
65 : : }
66 : :
67 : 0 : JL_DLLEXPORT void jl_gc_set_cb_root_scanner(jl_gc_cb_root_scanner_t cb, int enable)
68 : : {
69 [ # # ]: 0 : if (enable)
70 : 0 : jl_gc_register_callback(&gc_cblist_root_scanner, (jl_gc_cb_func_t)cb);
71 : : else
72 : 0 : jl_gc_deregister_callback(&gc_cblist_root_scanner, (jl_gc_cb_func_t)cb);
73 : 0 : }
74 : :
75 : 0 : JL_DLLEXPORT void jl_gc_set_cb_task_scanner(jl_gc_cb_task_scanner_t cb, int enable)
76 : : {
77 [ # # ]: 0 : if (enable)
78 : 0 : jl_gc_register_callback(&gc_cblist_task_scanner, (jl_gc_cb_func_t)cb);
79 : : else
80 : 0 : jl_gc_deregister_callback(&gc_cblist_task_scanner, (jl_gc_cb_func_t)cb);
81 : 0 : }
82 : :
83 : 0 : JL_DLLEXPORT void jl_gc_set_cb_pre_gc(jl_gc_cb_pre_gc_t cb, int enable)
84 : : {
85 [ # # ]: 0 : if (enable)
86 : 0 : jl_gc_register_callback(&gc_cblist_pre_gc, (jl_gc_cb_func_t)cb);
87 : : else
88 : 0 : jl_gc_deregister_callback(&gc_cblist_pre_gc, (jl_gc_cb_func_t)cb);
89 : 0 : }
90 : :
91 : 0 : JL_DLLEXPORT void jl_gc_set_cb_post_gc(jl_gc_cb_post_gc_t cb, int enable)
92 : : {
93 [ # # ]: 0 : if (enable)
94 : 0 : jl_gc_register_callback(&gc_cblist_post_gc, (jl_gc_cb_func_t)cb);
95 : : else
96 : 0 : jl_gc_deregister_callback(&gc_cblist_post_gc, (jl_gc_cb_func_t)cb);
97 : 0 : }
98 : :
99 : 0 : JL_DLLEXPORT void jl_gc_set_cb_notify_external_alloc(jl_gc_cb_notify_external_alloc_t cb, int enable)
100 : : {
101 [ # # ]: 0 : if (enable)
102 : 0 : jl_gc_register_callback(&gc_cblist_notify_external_alloc, (jl_gc_cb_func_t)cb);
103 : : else
104 : 0 : jl_gc_deregister_callback(&gc_cblist_notify_external_alloc, (jl_gc_cb_func_t)cb);
105 : 0 : }
106 : :
107 : 0 : JL_DLLEXPORT void jl_gc_set_cb_notify_external_free(jl_gc_cb_notify_external_free_t cb, int enable)
108 : : {
109 [ # # ]: 0 : if (enable)
110 : 0 : jl_gc_register_callback(&gc_cblist_notify_external_free, (jl_gc_cb_func_t)cb);
111 : : else
112 : 0 : jl_gc_deregister_callback(&gc_cblist_notify_external_free, (jl_gc_cb_func_t)cb);
113 : 0 : }
114 : :
115 : : // Save/restore local mark stack to/from thread-local storage.
116 : :
117 : 0 : STATIC_INLINE void export_gc_state(jl_ptls_t ptls, jl_gc_mark_sp_t *sp) {
118 : 0 : ptls->gc_mark_sp = *sp;
119 : 0 : }
120 : :
121 : 0 : STATIC_INLINE void import_gc_state(jl_ptls_t ptls, jl_gc_mark_sp_t *sp) {
122 : : // Has the stack been reallocated in the meantime?
123 : 0 : *sp = ptls->gc_mark_sp;
124 : 0 : }
125 : :
126 : : // Protect all access to `finalizer_list_marked` and `to_finalize`.
127 : : // For accessing `ptls->finalizers`, the lock is needed if a thread
128 : : // is going to realloc the buffer (of its own list) or accessing the
129 : : // list of another thread
130 : : static jl_mutex_t finalizers_lock;
131 : : static uv_mutex_t gc_cache_lock;
132 : :
133 : : // Flag that tells us whether we need to support conservative marking
134 : : // of objects.
135 : : static _Atomic(int) support_conservative_marking = 0;
136 : :
137 : : /**
138 : : * Note about GC synchronization:
139 : : *
140 : : * When entering `jl_gc_collect()`, `jl_gc_running` is atomically changed from
141 : : * `0` to `1` to make sure that only one thread can be running the GC. Other
142 : : * threads that enters `jl_gc_collect()` at the same time (or later calling
143 : : * from unmanaged code) will wait in `jl_gc_collect()` until the GC is finished.
144 : : *
145 : : * Before starting the mark phase the GC thread calls `jl_safepoint_gc_start()`
146 : : * and `jl_gc_wait_for_the_world()`
147 : : * to make sure all the thread are in a safe state for the GC. The function
148 : : * activates the safepoint and wait for all the threads to get ready for the
149 : : * GC (`gc_state != 0`). It also acquires the `finalizers` lock so that no
150 : : * other thread will access them when the GC is running.
151 : : *
152 : : * During the mark and sweep phase of the GC, the threads that are not running
153 : : * the GC should either be running unmanaged code (or code section that does
154 : : * not have a GC critical region mainly including storing to the stack or
155 : : * another object) or paused at a safepoint and wait for the GC to finish.
156 : : * If a thread want to switch from running unmanaged code to running managed
157 : : * code, it has to perform a GC safepoint check after setting the `gc_state`
158 : : * flag (see `jl_gc_state_save_and_set()`. it is possible that the thread might
159 : : * have `gc_state == 0` in the middle of the GC transition back before entering
160 : : * the safepoint. This is fine since the thread won't be executing any GC
161 : : * critical region during that time).
162 : : *
163 : : * The finalizers are run after the GC finishes in normal mode (the `gc_state`
164 : : * when `jl_gc_collect` is called) with `jl_in_finalizer = 1`. (TODO:) When we
165 : : * have proper support of GC transition in codegen, we should execute the
166 : : * finalizers in unmanaged (GC safe) mode.
167 : : */
168 : :
169 : : jl_gc_num_t gc_num = {0};
170 : : static size_t last_long_collect_interval;
171 : :
172 : : pagetable_t memory_map;
173 : :
174 : : // List of marked big objects. Not per-thread. Accessed only by master thread.
175 : : bigval_t *big_objects_marked = NULL;
176 : :
177 : : // finalization
178 : : // `ptls->finalizers` and `finalizer_list_marked` might have tagged pointers.
179 : : // If an object pointer has the lowest bit set, the next pointer is an unboxed
180 : : // c function pointer.
181 : : // `to_finalize` should not have tagged pointers.
182 : : arraylist_t finalizer_list_marked;
183 : : arraylist_t to_finalize;
184 : : JL_DLLEXPORT _Atomic(int) jl_gc_have_pending_finalizers = 0;
185 : :
186 : 0 : NOINLINE uintptr_t gc_get_stack_ptr(void)
187 : : {
188 : 0 : return (uintptr_t)jl_get_frame_addr();
189 : : }
190 : :
191 : : #define should_timeout() 0
192 : :
193 : 13380 : static void jl_gc_wait_for_the_world(void)
194 : : {
195 [ + + ]: 13380 : if (jl_n_threads > 1)
196 : 78 : jl_wake_libuv();
197 [ + + ]: 26838 : for (int i = 0; i < jl_n_threads; i++) {
198 : 13458 : jl_ptls_t ptls2 = jl_all_tls_states[i];
199 : : // This acquire load pairs with the release stores
200 : : // in the signal handler of safepoint so we are sure that
201 : : // all the stores on those threads are visible.
202 : : // We're currently also using atomic store release in mutator threads
203 : : // (in jl_gc_state_set), but we may want to use signals to flush the
204 : : // memory operations on those threads lazily instead.
205 [ + + - + ]: 33565 : while (!jl_atomic_load_relaxed(&ptls2->gc_state) || !jl_atomic_load_acquire(&ptls2->gc_state))
206 : : jl_cpu_pause(); // yield?
207 : : }
208 : 13380 : }
209 : :
210 : : // malloc wrappers, aligned allocation
211 : :
212 : : #if defined(_OS_WINDOWS_)
213 : : STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align)
214 : : {
215 : : return _aligned_malloc(sz ? sz : 1, align);
216 : : }
217 : : STATIC_INLINE void *jl_realloc_aligned(void *p, size_t sz, size_t oldsz,
218 : : size_t align)
219 : : {
220 : : (void)oldsz;
221 : : return _aligned_realloc(p, sz ? sz : 1, align);
222 : : }
223 : : STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT
224 : : {
225 : : _aligned_free(p);
226 : : }
227 : : #else
228 : 9369510 : STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align)
229 : : {
230 : : #if defined(_P64) || defined(__APPLE__)
231 [ - + ]: 9369510 : if (align <= 16)
232 : 0 : return malloc(sz);
233 : : #endif
234 : : void *ptr;
235 [ - + ]: 9369510 : if (posix_memalign(&ptr, align, sz))
236 : 0 : return NULL;
237 : 9369510 : return ptr;
238 : : }
239 : 26613 : STATIC_INLINE void *jl_realloc_aligned(void *d, size_t sz, size_t oldsz,
240 : : size_t align)
241 : : {
242 : : #if defined(_P64) || defined(__APPLE__)
243 [ - + ]: 26613 : if (align <= 16)
244 : 0 : return realloc(d, sz);
245 : : #endif
246 : 26613 : void *b = jl_malloc_aligned(sz, align);
247 [ + - ]: 26613 : if (b != NULL) {
248 : 26613 : memcpy(b, d, oldsz > sz ? sz : oldsz);
249 : 26613 : free(d);
250 : : }
251 : 26613 : return b;
252 : : }
253 : 9031230 : STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT
254 : : {
255 : 9031230 : free(p);
256 : 9031230 : }
257 : : #endif
258 : : #define malloc_cache_align(sz) jl_malloc_aligned(sz, JL_CACHE_BYTE_ALIGNMENT)
259 : : #define realloc_cache_align(p, sz, oldsz) jl_realloc_aligned(p, sz, oldsz, JL_CACHE_BYTE_ALIGNMENT)
260 : :
261 : 1488710 : static void schedule_finalization(void *o, void *f) JL_NOTSAFEPOINT
262 : : {
263 : 1488710 : arraylist_push(&to_finalize, o);
264 : 1488710 : arraylist_push(&to_finalize, f);
265 : : // doesn't need release, since we'll keep checking (on the reader) until we see the work and
266 : : // release our lock, and that will have a release barrier by then
267 : 1488710 : jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 1);
268 : 1488710 : }
269 : :
270 : 1489820 : static void run_finalizer(jl_task_t *ct, jl_value_t *o, jl_value_t *ff)
271 : : {
272 [ + + ]: 1489820 : if (gc_ptr_tag(o, 1)) {
273 : 1159530 : ((void (*)(void*))ff)(gc_ptr_clear_tag(o, 1));
274 : 1159530 : return;
275 : : }
276 : 330291 : jl_value_t *args[2] = {ff,o};
277 [ + + + + ]: 660579 : JL_TRY {
278 : 330291 : size_t last_age = ct->world_age;
279 : 330291 : ct->world_age = jl_atomic_load_acquire(&jl_world_counter);
280 : 330291 : jl_apply(args, 2);
281 : 330288 : ct->world_age = last_age;
282 : : }
283 [ + + ]: 6 : JL_CATCH {
284 : 3 : jl_printf((JL_STREAM*)STDERR_FILENO, "error in running finalizer: ");
285 : 3 : jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
286 : 3 : jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
287 : 3 : jlbacktrace(); // written to STDERR_FILENO
288 : : }
289 : : }
290 : :
291 : : // if `need_sync` is true, the `list` is the `finalizers` list of another
292 : : // thread and we need additional synchronizations
293 : 4456 : static void finalize_object(arraylist_t *list, jl_value_t *o,
294 : : arraylist_t *copied_list, int need_sync) JL_NOTSAFEPOINT
295 : : {
296 : : // The acquire load makes sure that the first `len` objects are valid.
297 : : // If `need_sync` is true, all mutations of the content should be limited
298 : : // to the first `oldlen` elements and no mutation is allowed after the
299 : : // new length is published with the `cmpxchg` at the end of the function.
300 : : // This way, the mutation should not conflict with the owning thread,
301 : : // which only writes to locations later than `len`
302 : : // and will not resize the buffer without acquiring the lock.
303 [ - + ]: 4456 : size_t len = need_sync ? jl_atomic_load_acquire((_Atomic(size_t)*)&list->len) : list->len;
304 : 4456 : size_t oldlen = len;
305 : 4456 : void **items = list->items;
306 : 4456 : size_t j = 0;
307 [ + + ]: 3559680 : for (size_t i = 0; i < len; i += 2) {
308 : 3555220 : void *v = items[i];
309 : 3555220 : int move = 0;
310 [ + + ]: 3555220 : if (o == (jl_value_t*)gc_ptr_clear_tag(v, 1)) {
311 : 1118 : void *f = items[i + 1];
312 : 1118 : move = 1;
313 : 1118 : arraylist_push(copied_list, v);
314 : 1118 : arraylist_push(copied_list, f);
315 : : }
316 [ + + + - ]: 3555220 : if (move || __unlikely(!v)) {
317 : : // remove item
318 : : }
319 : : else {
320 [ + + ]: 3554100 : if (j < i) {
321 : 1500570 : items[j] = items[i];
322 : 1500570 : items[j+1] = items[i+1];
323 : : }
324 : 3554100 : j += 2;
325 : : }
326 : : }
327 : 4456 : len = j;
328 [ + + ]: 4456 : if (oldlen == len)
329 : 3379 : return;
330 [ - + ]: 1077 : if (need_sync) {
331 : : // The memset needs to be unconditional since the thread might have
332 : : // already read the length.
333 : : // The `memset` (like any other content mutation) has to be done
334 : : // **before** the `cmpxchg` which publishes the length.
335 : 0 : memset(&items[len], 0, (oldlen - len) * sizeof(void*));
336 : 0 : jl_atomic_cmpswap((_Atomic(size_t)*)&list->len, &oldlen, len);
337 : : }
338 : : else {
339 : 1077 : list->len = len;
340 : : }
341 : : }
342 : :
343 : : // The first two entries are assumed to be empty and the rest are assumed to
344 : : // be pointers to `jl_value_t` objects
345 : 3044 : static void jl_gc_push_arraylist(jl_task_t *ct, arraylist_t *list)
346 : : {
347 : 3044 : void **items = list->items;
348 : 3044 : items[0] = (void*)JL_GC_ENCODE_PUSHARGS(list->len - 2);
349 : 3044 : items[1] = ct->gcstack;
350 : 3044 : ct->gcstack = (jl_gcframe_t*)items;
351 : 3044 : }
352 : :
353 : : // Same assumption as `jl_gc_push_arraylist`. Requires the finalizers lock
354 : : // to be hold for the current thread and will release the lock when the
355 : : // function returns.
356 : 3044 : static void jl_gc_run_finalizers_in_list(jl_task_t *ct, arraylist_t *list)
357 : : {
358 : : // Avoid marking `ct` as non-migratable via an `@async` task (as noted in the docstring
359 : : // of `finalizer`) in a finalizer:
360 : 3044 : uint8_t sticky = ct->sticky;
361 : : // empty out the first two entries for the GC frame
362 : 3044 : arraylist_push(list, list->items[0]);
363 : 3044 : arraylist_push(list, list->items[1]);
364 : 3044 : jl_gc_push_arraylist(ct, list);
365 : 3044 : jl_value_t **items = (jl_value_t**)list->items;
366 : 3044 : size_t len = list->len;
367 : 3044 : JL_UNLOCK_NOGC(&finalizers_lock);
368 : : // run finalizers in reverse order they were added, so lower-level finalizers run last
369 [ + + ]: 1489820 : for (size_t i = len-4; i >= 2; i -= 2)
370 : 1486780 : run_finalizer(ct, items[i], items[i + 1]);
371 : : // first entries were moved last to make room for GC frame metadata
372 : 3044 : run_finalizer(ct, items[len-2], items[len-1]);
373 : : // matches the jl_gc_push_arraylist above
374 : 3044 : JL_GC_POP();
375 : 3044 : ct->sticky = sticky;
376 : 3044 : }
377 : :
378 : : static uint64_t finalizer_rngState[4];
379 : :
380 : : void jl_rng_split(uint64_t to[4], uint64_t from[4]);
381 : :
382 : 563 : JL_DLLEXPORT void jl_gc_init_finalizer_rng_state(void)
383 : : {
384 : 563 : jl_rng_split(finalizer_rngState, jl_current_task->rngState);
385 : 563 : }
386 : :
387 : 3384 : static void run_finalizers(jl_task_t *ct)
388 : : {
389 : : // Racy fast path:
390 : : // The race here should be OK since the race can only happen if
391 : : // another thread is writing to it with the lock held. In such case,
392 : : // we don't need to run pending finalizers since the writer thread
393 : : // will flush it.
394 [ + + ]: 3384 : if (to_finalize.len == 0)
395 : 1417 : return;
396 : 1967 : JL_LOCK_NOGC(&finalizers_lock);
397 [ - + ]: 1967 : if (to_finalize.len == 0) {
398 : 0 : JL_UNLOCK_NOGC(&finalizers_lock);
399 : 0 : return;
400 : : }
401 : : arraylist_t copied_list;
402 : 1967 : memcpy(&copied_list, &to_finalize, sizeof(copied_list));
403 [ + + ]: 1967 : if (to_finalize.items == to_finalize._space) {
404 : 1478 : copied_list.items = copied_list._space;
405 : : }
406 : 1967 : jl_atomic_store_relaxed(&jl_gc_have_pending_finalizers, 0);
407 : 1967 : arraylist_new(&to_finalize, 0);
408 : :
409 : : uint64_t save_rngState[4];
410 : 1967 : memcpy(&save_rngState[0], &ct->rngState[0], sizeof(save_rngState));
411 : 1967 : jl_rng_split(ct->rngState, finalizer_rngState);
412 : :
413 : : // This releases the finalizers lock.
414 : 1967 : jl_gc_run_finalizers_in_list(ct, &copied_list);
415 : 1967 : arraylist_free(&copied_list);
416 : :
417 : 1967 : memcpy(&ct->rngState[0], &save_rngState[0], sizeof(save_rngState));
418 : : }
419 : :
420 : 152944000 : JL_DLLEXPORT void jl_gc_run_pending_finalizers(jl_task_t *ct)
421 : : {
422 [ + + ]: 152944000 : if (ct == NULL)
423 : 1146 : ct = jl_current_task;
424 : 152944000 : jl_ptls_t ptls = ct->ptls;
425 [ + - + + : 152944000 : if (!ptls->in_finalizer && ptls->locks.len == 0 && ptls->finalizers_inhibited == 0) {
+ + ]
426 : 1132 : ptls->in_finalizer = 1;
427 : 1132 : run_finalizers(ct);
428 : 1132 : ptls->in_finalizer = 0;
429 : : }
430 : 152944000 : }
431 : :
432 : 24 : JL_DLLEXPORT int jl_gc_get_finalizers_inhibited(jl_ptls_t ptls)
433 : : {
434 [ + - ]: 24 : if (ptls == NULL)
435 : 24 : ptls = jl_current_task->ptls;
436 : 24 : return ptls->finalizers_inhibited;
437 : : }
438 : :
439 : 0 : JL_DLLEXPORT void jl_gc_disable_finalizers_internal(void)
440 : : {
441 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
442 : 0 : ptls->finalizers_inhibited++;
443 : 0 : }
444 : :
445 : 0 : JL_DLLEXPORT void jl_gc_enable_finalizers_internal(void)
446 : : {
447 : 0 : jl_task_t *ct = jl_current_task;
448 : : #ifdef NDEBUG
449 : : ct->ptls->finalizers_inhibited--;
450 : : #else
451 : 0 : jl_gc_enable_finalizers(ct, 1);
452 : : #endif
453 : 0 : }
454 : :
455 : 260 : JL_DLLEXPORT void jl_gc_enable_finalizers(jl_task_t *ct, int on)
456 : : {
457 [ - + ]: 260 : if (ct == NULL)
458 : 0 : ct = jl_current_task;
459 : 260 : jl_ptls_t ptls = ct->ptls;
460 : 260 : int old_val = ptls->finalizers_inhibited;
461 [ + + ]: 260 : int new_val = old_val + (on ? -1 : 1);
462 [ - + ]: 260 : if (new_val < 0) {
463 [ # # # # ]: 0 : JL_TRY {
464 : 0 : jl_error(""); // get a backtrace
465 : : }
466 [ # # ]: 0 : JL_CATCH {
467 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "WARNING: GC finalizers already enabled on this thread.\n");
468 : : // Only print the backtrace once, to avoid spamming the logs
469 : : static int backtrace_printed = 0;
470 [ # # ]: 0 : if (backtrace_printed == 0) {
471 : 0 : backtrace_printed = 1;
472 : 0 : jlbacktrace(); // written to STDERR_FILENO
473 : : }
474 : : }
475 : 0 : return;
476 : : }
477 : 260 : ptls->finalizers_inhibited = new_val;
478 [ + + ]: 260 : if (jl_atomic_load_relaxed(&jl_gc_have_pending_finalizers)) {
479 : 3 : jl_gc_run_pending_finalizers(ct);
480 : : }
481 : : }
482 : :
483 : 1261 : static void schedule_all_finalizers(arraylist_t *flist) JL_NOTSAFEPOINT
484 : : {
485 : 1261 : void **items = flist->items;
486 : 1261 : size_t len = flist->len;
487 [ + + ]: 29371 : for(size_t i = 0; i < len; i+=2) {
488 : 28110 : void *v = items[i];
489 : 28110 : void *f = items[i + 1];
490 [ - + ]: 28110 : if (__unlikely(!v))
491 : 0 : continue;
492 : 28110 : schedule_finalization(v, f);
493 : : }
494 : 1261 : flist->len = 0;
495 : 1261 : }
496 : :
497 : 571 : void jl_gc_run_all_finalizers(jl_task_t *ct)
498 : : {
499 : 571 : schedule_all_finalizers(&finalizer_list_marked);
500 : : // This could be run before we had a chance to setup all threads
501 [ + + ]: 1262 : for (int i = 0;i < jl_n_threads;i++) {
502 : 691 : jl_ptls_t ptls2 = jl_all_tls_states[i];
503 [ + + ]: 691 : if (ptls2)
504 : 690 : schedule_all_finalizers(&ptls2->finalizers);
505 : : }
506 : 571 : run_finalizers(ct);
507 : 571 : }
508 : :
509 : 1489820 : void jl_gc_add_finalizer_(jl_ptls_t ptls, void *v, void *f) JL_NOTSAFEPOINT
510 : : {
511 [ - + ]: 1489820 : assert(jl_atomic_load_relaxed(&ptls->gc_state) == 0);
512 : 1489820 : arraylist_t *a = &ptls->finalizers;
513 : : // This acquire load and the release store at the end are used to
514 : : // synchronize with `finalize_object` on another thread. Apart from the GC,
515 : : // which is blocked by entering a unsafe region, there might be only
516 : : // one other thread accessing our list in `finalize_object`
517 : : // (only one thread since it needs to acquire the finalizer lock).
518 : : // Similar to `finalize_object`, all content mutation has to be done
519 : : // between the acquire and the release of the length.
520 : 1489820 : size_t oldlen = jl_atomic_load_acquire((_Atomic(size_t)*)&a->len);
521 [ + + ]: 1489820 : if (__unlikely(oldlen + 2 > a->max)) {
522 : 261 : JL_LOCK_NOGC(&finalizers_lock);
523 : : // `a->len` might have been modified.
524 : : // Another possibility is to always grow the array to `oldlen + 2` but
525 : : // it's simpler this way and uses slightly less memory =)
526 : 261 : oldlen = a->len;
527 : 261 : arraylist_grow(a, 2);
528 : 261 : a->len = oldlen;
529 : 261 : JL_UNLOCK_NOGC(&finalizers_lock);
530 : : }
531 : 1489820 : void **items = a->items;
532 : 1489820 : items[oldlen] = v;
533 : 1489820 : items[oldlen + 1] = f;
534 : 1489820 : jl_atomic_store_release((_Atomic(size_t)*)&a->len, oldlen + 2);
535 : 1489820 : }
536 : :
537 : 1159520 : JL_DLLEXPORT void jl_gc_add_ptr_finalizer(jl_ptls_t ptls, jl_value_t *v, void *f) JL_NOTSAFEPOINT
538 : : {
539 : 1159520 : jl_gc_add_finalizer_(ptls, (void*)(((uintptr_t)v) | 1), f);
540 : 1159520 : }
541 : :
542 : 0 : JL_DLLEXPORT void jl_gc_add_finalizer_th(jl_ptls_t ptls, jl_value_t *v, jl_function_t *f) JL_NOTSAFEPOINT
543 : : {
544 [ # # ]: 0 : if (__unlikely(jl_typeis(f, jl_voidpointer_type))) {
545 : 0 : jl_gc_add_ptr_finalizer(ptls, v, jl_unbox_voidpointer(f));
546 : : }
547 : : else {
548 : 0 : jl_gc_add_finalizer_(ptls, v, f);
549 : : }
550 : 0 : }
551 : :
552 : 2228 : JL_DLLEXPORT void jl_finalize_th(jl_task_t *ct, jl_value_t *o)
553 : : {
554 : 2228 : JL_LOCK_NOGC(&finalizers_lock);
555 : : // Copy the finalizers into a temporary list so that code in the finalizer
556 : : // won't change the list as we loop through them.
557 : : // This list is also used as the GC frame when we are running the finalizers
558 : : arraylist_t copied_list;
559 : 2228 : arraylist_new(&copied_list, 0);
560 : : // No need to check the to_finalize list since the user is apparently
561 : : // still holding a reference to the object
562 [ + + ]: 4456 : for (int i = 0; i < jl_n_threads; i++) {
563 : 2228 : jl_ptls_t ptls2 = jl_all_tls_states[i];
564 : 2228 : finalize_object(&ptls2->finalizers, o, &copied_list, jl_atomic_load_relaxed(&ct->tid) != i);
565 : : }
566 : 2228 : finalize_object(&finalizer_list_marked, o, &copied_list, 0);
567 [ + + ]: 2228 : if (copied_list.len > 0) {
568 : : // This releases the finalizers lock.
569 : 1077 : jl_gc_run_finalizers_in_list(ct, &copied_list);
570 : : }
571 : : else {
572 : 1151 : JL_UNLOCK_NOGC(&finalizers_lock);
573 : : }
574 : 2228 : arraylist_free(&copied_list);
575 : 2228 : }
576 : :
577 : : // explicitly scheduled objects for the sweepfunc callback
578 : 14102 : static void gc_sweep_foreign_objs_in_list(arraylist_t *objs)
579 : : {
580 : 14102 : size_t p = 0;
581 [ - + ]: 14102 : for (size_t i = 0; i < objs->len; i++) {
582 : 0 : jl_value_t *v = (jl_value_t*)(objs->items[i]);
583 : 0 : jl_datatype_t *t = (jl_datatype_t*)(jl_typeof(v));
584 : 0 : const jl_datatype_layout_t *layout = t->layout;
585 : 0 : jl_fielddescdyn_t *desc = (jl_fielddescdyn_t*)jl_dt_layout_fields(layout);
586 : :
587 : 0 : int bits = jl_astaggedvalue(v)->bits.gc;
588 [ # # ]: 0 : if (!gc_marked(bits))
589 : 0 : desc->sweepfunc(v);
590 : : else
591 : 0 : objs->items[p++] = v;
592 : : }
593 : 14102 : objs->len = p;
594 : 14102 : }
595 : :
596 : 14024 : static void gc_sweep_foreign_objs(void)
597 : : {
598 [ + + ]: 28126 : for (int i = 0;i < jl_n_threads; i++) {
599 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[i];
600 : 14102 : gc_sweep_foreign_objs_in_list(&ptls2->sweep_objs);
601 : : }
602 : 14024 : }
603 : :
604 : : // GC knobs and self-measurement variables
605 : : static int64_t last_gc_total_bytes = 0;
606 : :
607 : : // max_total_memory is a suggestion. We try very hard to stay
608 : : // under this limit, but we will go above it rather than halting.
609 : : #ifdef _P64
610 : : typedef uint64_t memsize_t;
611 : : #define default_collect_interval (5600*1024*sizeof(void*))
612 : : static size_t max_collect_interval = 1250000000UL;
613 : : // Eventually we can expose this to the user/ci.
614 : : memsize_t max_total_memory = (memsize_t) 2 * 1024 * 1024 * 1024 * 1024 * 1024;
615 : : #else
616 : : typedef uint32_t memsize_t;
617 : : #define default_collect_interval (3200*1024*sizeof(void*))
618 : : static size_t max_collect_interval = 500000000UL;
619 : : // Work really hard to stay within 2GB
620 : : // Alternative is to risk running out of address space
621 : : // on 32 bit architectures.
622 : : memsize_t max_total_memory = (memsize_t) 2 * 1024 * 1024 * 1024;
623 : : #endif
624 : :
625 : : // global variables for GC stats
626 : :
627 : : // Resetting the object to a young object, this is used when marking the
628 : : // finalizer list to collect them the next time because the object is very
629 : : // likely dead. This also won't break the GC invariance since these objects
630 : : // are not reachable from anywhere else.
631 : : static int mark_reset_age = 0;
632 : :
633 : : /*
634 : : * The state transition looks like :
635 : : *
636 : : * ([(quick)sweep] means either a sweep or a quicksweep)
637 : : *
638 : : * <-[(quick)sweep]-
639 : : * |
640 : : * ----> GC_OLD <--[(quick)sweep && age>promotion]--
641 : : * | | |
642 : : * | | GC_MARKED (in remset) |
643 : : * | | ^ | |
644 : : * | [mark] | [mark] |
645 : : * | | | | |
646 : : * | | | | |
647 : : * [sweep] | [write barrier] | |
648 : : * | v | v |
649 : : * ----- GC_OLD_MARKED <---- |
650 : : * | ^ |
651 : : * | | |
652 : : * --[quicksweep]--- |
653 : : * |
654 : : * ========= above this line objects are old ========= |
655 : : * |
656 : : * ----[new]------> GC_CLEAN ------[mark]-----------> GC_MARKED
657 : : * | ^ |
658 : : * <-[(quick)sweep]--- | |
659 : : * --[(quick)sweep && age<=promotion]---
660 : : */
661 : :
662 : : // A quick sweep is a sweep where `!sweep_full`
663 : : // It means we won't touch GC_OLD_MARKED objects (old gen).
664 : :
665 : : // When a reachable object has survived more than PROMOTE_AGE+1 collections
666 : : // it is tagged with GC_OLD during sweep and will be promoted on next mark
667 : : // because at that point we can know easily if it references young objects.
668 : : // Marked old objects that reference young ones are kept in the remset.
669 : :
670 : : // When a write barrier triggers, the offending marked object is both queued,
671 : : // so as not to trigger the barrier again, and put in the remset.
672 : :
673 : :
674 : : #define PROMOTE_AGE 1
675 : : // this cannot be increased as is without changing :
676 : : // - sweep_page which is specialized for 1bit age
677 : : // - the size of the age storage in jl_gc_pagemeta_t
678 : :
679 : :
680 : : static int64_t scanned_bytes; // young bytes scanned while marking
681 : : static int64_t perm_scanned_bytes; // old bytes scanned while marking
682 : : static int prev_sweep_full = 1;
683 : :
684 : : #define inc_sat(v,s) v = (v) >= s ? s : (v)+1
685 : :
686 : : // Full collection heuristics
687 : : static int64_t live_bytes = 0;
688 : : static int64_t promoted_bytes = 0;
689 : : static int64_t last_live_bytes = 0; // live_bytes at last collection
690 : : static int64_t t_start = 0; // Time GC starts;
691 : : #ifdef __GLIBC__
692 : : // maxrss at last malloc_trim
693 : : static int64_t last_trim_maxrss = 0;
694 : : #endif
695 : :
696 : 14670 : static void gc_sync_cache_nolock(jl_ptls_t ptls, jl_gc_mark_cache_t *gc_cache) JL_NOTSAFEPOINT
697 : : {
698 : 14670 : const int nbig = gc_cache->nbig_obj;
699 [ + + ]: 1212580 : for (int i = 0; i < nbig; i++) {
700 : 1197910 : void *ptr = gc_cache->big_obj[i];
701 : 1197910 : bigval_t *hdr = (bigval_t*)gc_ptr_clear_tag(ptr, 1);
702 : 1197910 : gc_big_object_unlink(hdr);
703 [ + + ]: 1197910 : if (gc_ptr_tag(ptr, 1)) {
704 : 860 : gc_big_object_link(hdr, &ptls->heap.big_objects);
705 : : }
706 : : else {
707 : : // Move hdr from `big_objects` list to `big_objects_marked list`
708 : 1197050 : gc_big_object_link(hdr, &big_objects_marked);
709 : : }
710 : : }
711 : 14670 : gc_cache->nbig_obj = 0;
712 : 14670 : perm_scanned_bytes += gc_cache->perm_scanned_bytes;
713 : 14670 : scanned_bytes += gc_cache->scanned_bytes;
714 : 14670 : gc_cache->perm_scanned_bytes = 0;
715 : 14670 : gc_cache->scanned_bytes = 0;
716 : 14670 : }
717 : :
718 : 568 : static void gc_sync_cache(jl_ptls_t ptls) JL_NOTSAFEPOINT
719 : : {
720 : 568 : uv_mutex_lock(&gc_cache_lock);
721 : 568 : gc_sync_cache_nolock(ptls, &ptls->gc_cache);
722 : 568 : uv_mutex_unlock(&gc_cache_lock);
723 : 568 : }
724 : :
725 : : // No other threads can be running marking at the same time
726 : 14024 : static void gc_sync_all_caches_nolock(jl_ptls_t ptls)
727 : : {
728 [ + + ]: 28126 : for (int t_i = 0; t_i < jl_n_threads; t_i++) {
729 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
730 : 14102 : gc_sync_cache_nolock(ptls, &ptls2->gc_cache);
731 : : }
732 : 14024 : }
733 : :
734 : 1207110 : STATIC_INLINE void gc_queue_big_marked(jl_ptls_t ptls, bigval_t *hdr,
735 : : int toyoung) JL_NOTSAFEPOINT
736 : : {
737 : 1207110 : const int nentry = sizeof(ptls->gc_cache.big_obj) / sizeof(void*);
738 : 1207110 : size_t nobj = ptls->gc_cache.nbig_obj;
739 [ + + ]: 1207110 : if (__unlikely(nobj >= nentry)) {
740 : 568 : gc_sync_cache(ptls);
741 : 568 : nobj = 0;
742 : : }
743 : 1207110 : uintptr_t v = (uintptr_t)hdr;
744 [ + + ]: 1207110 : ptls->gc_cache.big_obj[nobj] = (void*)(toyoung ? (v | 1) : v);
745 : 1207110 : ptls->gc_cache.nbig_obj = nobj + 1;
746 : 1207110 : }
747 : :
748 : : // `gc_setmark_tag` can be called concurrently on multiple threads.
749 : : // In all cases, the function atomically sets the mark bits and returns
750 : : // the GC bits set as well as if the tag was unchanged by this thread.
751 : : // All concurrent calls on the same object are guaranteed to be setting the
752 : : // bits to the same value.
753 : : // For normal objects, this is the bits with only `GC_MARKED` changed to `1`
754 : : // For buffers, this is the bits of the owner object.
755 : : // For `mark_reset_age`, this is `GC_MARKED` with `GC_OLD` cleared.
756 : : // The return value is `1` if the object was not marked before.
757 : : // Returning `0` can happen if another thread marked it in parallel.
758 : 2082960000 : STATIC_INLINE int gc_setmark_tag(jl_taggedvalue_t *o, uint8_t mark_mode,
759 : : uintptr_t tag, uint8_t *bits) JL_NOTSAFEPOINT
760 : : {
761 [ - + ]: 2082960000 : assert(!gc_marked(tag));
762 [ - + ]: 2082960000 : assert(gc_marked(mark_mode));
763 [ + + ]: 2082960000 : if (mark_reset_age) {
764 : : // Reset the object as if it was just allocated
765 : 2304200 : mark_mode = GC_MARKED;
766 : 2304200 : tag = gc_set_bits(tag, mark_mode);
767 : : }
768 : : else {
769 [ + + ]: 2080660000 : if (gc_old(tag))
770 : 1549220000 : mark_mode = GC_OLD_MARKED;
771 : 2080660000 : tag = tag | mark_mode;
772 [ - + ]: 2080660000 : assert((tag & 0x3) == mark_mode);
773 : : }
774 : 2082960000 : *bits = mark_mode;
775 : 2082960000 : tag = jl_atomic_exchange_relaxed((_Atomic(uintptr_t)*)&o->header, tag);
776 : : verify_val(jl_valueof(o));
777 : 2082960000 : return !gc_marked(tag);
778 : : }
779 : :
780 : : // This function should be called exactly once during marking for each big
781 : : // object being marked to update the big objects metadata.
782 : 2171630 : STATIC_INLINE void gc_setmark_big(jl_ptls_t ptls, jl_taggedvalue_t *o,
783 : : uint8_t mark_mode) JL_NOTSAFEPOINT
784 : : {
785 [ - + ]: 2171630 : assert(!page_metadata(o));
786 : 2171630 : bigval_t *hdr = bigval_header(o);
787 [ + + ]: 2171630 : if (mark_mode == GC_OLD_MARKED) {
788 : 1206250 : ptls->gc_cache.perm_scanned_bytes += hdr->sz & ~3;
789 : 1206250 : gc_queue_big_marked(ptls, hdr, 0);
790 : : }
791 : : else {
792 : 965375 : ptls->gc_cache.scanned_bytes += hdr->sz & ~3;
793 : : // We can't easily tell if the object is old or being promoted
794 : : // from the gc bits but if the `age` is `0` then the object
795 : : // must be already on a young list.
796 [ + + + + ]: 965375 : if (mark_reset_age && hdr->age) {
797 : : // Reset the object as if it was just allocated
798 : 860 : hdr->age = 0;
799 : 860 : gc_queue_big_marked(ptls, hdr, 1);
800 : : }
801 : : }
802 : 2171630 : objprofile_count(jl_typeof(jl_valueof(o)),
803 : 2171630 : mark_mode == GC_OLD_MARKED, hdr->sz & ~3);
804 : 2171630 : }
805 : :
806 : : // This function should be called exactly once during marking for each pool
807 : : // object being marked to update the page metadata.
808 : 1364170000 : STATIC_INLINE void gc_setmark_pool_(jl_ptls_t ptls, jl_taggedvalue_t *o,
809 : : uint8_t mark_mode,
810 : : jl_gc_pagemeta_t *page) JL_NOTSAFEPOINT
811 : : {
812 : : #ifdef MEMDEBUG
813 : : gc_setmark_big(ptls, o, mark_mode);
814 : : #else
815 : 1364170000 : jl_assume(page);
816 [ + + ]: 1364170000 : if (mark_mode == GC_OLD_MARKED) {
817 : 832175000 : ptls->gc_cache.perm_scanned_bytes += page->osize;
818 : : static_assert(sizeof(_Atomic(uint16_t)) == sizeof(page->nold), "");
819 : 832175000 : jl_atomic_fetch_add_relaxed((_Atomic(uint16_t)*)&page->nold, 1);
820 : : }
821 : : else {
822 : 531994000 : ptls->gc_cache.scanned_bytes += page->osize;
823 [ + + ]: 531994000 : if (mark_reset_age) {
824 : 2301520 : page->has_young = 1;
825 : 2301520 : char *page_begin = gc_page_data(o) + GC_PAGE_OFFSET;
826 : 2301520 : int obj_id = (((char*)o) - page_begin) / page->osize;
827 : 2301520 : uint8_t *ages = page->ages + obj_id / 8;
828 : 2301520 : jl_atomic_fetch_and_relaxed((_Atomic(uint8_t)*)ages, ~(1 << (obj_id % 8)));
829 : : }
830 : : }
831 : 1364170000 : objprofile_count(jl_typeof(jl_valueof(o)),
832 : 1364170000 : mark_mode == GC_OLD_MARKED, page->osize);
833 : 1364170000 : page->has_marked = 1;
834 : : #endif
835 : 1364170000 : }
836 : :
837 : 1313590000 : STATIC_INLINE void gc_setmark_pool(jl_ptls_t ptls, jl_taggedvalue_t *o,
838 : : uint8_t mark_mode) JL_NOTSAFEPOINT
839 : : {
840 : 1313590000 : gc_setmark_pool_(ptls, o, mark_mode, page_metadata(o));
841 : 1313590000 : }
842 : :
843 : 996294000 : STATIC_INLINE void gc_setmark(jl_ptls_t ptls, jl_taggedvalue_t *o,
844 : : uint8_t mark_mode, size_t sz) JL_NOTSAFEPOINT
845 : : {
846 [ + + ]: 996294000 : if (sz <= GC_MAX_SZCLASS) {
847 : 996024000 : gc_setmark_pool(ptls, o, mark_mode);
848 : : }
849 : : else {
850 : 270125 : gc_setmark_big(ptls, o, mark_mode);
851 : : }
852 : 996294000 : }
853 : :
854 : 59012100 : STATIC_INLINE void gc_setmark_buf_(jl_ptls_t ptls, void *o, uint8_t mark_mode, size_t minsz) JL_NOTSAFEPOINT
855 : : {
856 : 59012100 : jl_taggedvalue_t *buf = jl_astaggedvalue(o);
857 : 59012100 : uintptr_t tag = buf->header;
858 [ + + ]: 59012100 : if (gc_marked(tag))
859 : 57880000 : return;
860 : : uint8_t bits;
861 : : // If the object is larger than the max pool size it can't be a pool object.
862 : : // This should be accurate most of the time but there might be corner cases
863 : : // where the size estimate is a little off so we do a pool lookup to make
864 : : // sure.
865 [ + - ]: 51710700 : if (__likely(gc_setmark_tag(buf, mark_mode, tag, &bits)) && !gc_verifying) {
866 [ + + ]: 51710700 : if (minsz <= GC_MAX_SZCLASS) {
867 : 50578600 : jl_gc_pagemeta_t *page = page_metadata(buf);
868 [ + - ]: 50578600 : if (page) {
869 : 50578600 : gc_setmark_pool_(ptls, buf, bits, page);
870 : 50578600 : return;
871 : : }
872 : : }
873 : 1132170 : gc_setmark_big(ptls, buf, bits);
874 : : }
875 : : }
876 : :
877 : 631962 : void gc_setmark_buf(jl_ptls_t ptls, void *o, uint8_t mark_mode, size_t minsz) JL_NOTSAFEPOINT
878 : : {
879 : 631962 : gc_setmark_buf_(ptls, o, mark_mode, minsz);
880 : 631962 : }
881 : :
882 : 0 : void jl_gc_force_mark_old(jl_ptls_t ptls, jl_value_t *v) JL_NOTSAFEPOINT
883 : : {
884 : 0 : jl_taggedvalue_t *o = jl_astaggedvalue(v);
885 : 0 : jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(v);
886 : 0 : size_t dtsz = jl_datatype_size(dt);
887 [ # # ]: 0 : if (o->bits.gc == GC_OLD_MARKED)
888 : 0 : return;
889 : 0 : o->bits.gc = GC_OLD_MARKED;
890 [ # # ]: 0 : if (dt == jl_simplevector_type) {
891 : 0 : size_t l = jl_svec_len(v);
892 : 0 : dtsz = l * sizeof(void*) + sizeof(jl_svec_t);
893 : : }
894 [ # # ]: 0 : else if (dt->name == jl_array_typename) {
895 : 0 : jl_array_t *a = (jl_array_t*)v;
896 [ # # ]: 0 : if (!a->flags.pooled)
897 : 0 : dtsz = GC_MAX_SZCLASS + 1;
898 : : }
899 [ # # ]: 0 : else if (dt == jl_module_type) {
900 : 0 : dtsz = sizeof(jl_module_t);
901 : : }
902 [ # # ]: 0 : else if (dt == jl_task_type) {
903 : 0 : dtsz = sizeof(jl_task_t);
904 : : }
905 [ # # ]: 0 : else if (dt == jl_symbol_type) {
906 : 0 : return;
907 : : }
908 : 0 : gc_setmark(ptls, o, GC_OLD_MARKED, dtsz);
909 [ # # ]: 0 : if (dt->layout->npointers != 0)
910 : 0 : jl_gc_queue_root(v);
911 : : }
912 : :
913 :14829900000 : static inline void maybe_collect(jl_ptls_t ptls)
914 : : {
915 [ + + # + ]:14829900000 : if (jl_atomic_load_relaxed(&ptls->gc_num.allocd) >= 0 || jl_gc_debug_check_other()) {
916 : 10103 : jl_gc_collect(JL_GC_AUTO);
917 : : }
918 : : else {
919 :14829900000 : jl_gc_safepoint_(ptls);
920 : : }
921 :14829900000 : }
922 : :
923 : : // weak references
924 : :
925 : 101857 : JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref_th(jl_ptls_t ptls,
926 : : jl_value_t *value)
927 : : {
928 : 101857 : jl_weakref_t *wr = (jl_weakref_t*)jl_gc_alloc(ptls, sizeof(void*),
929 : : jl_weakref_type);
930 : 101857 : wr->value = value; // NOTE: wb not needed here
931 : 101857 : arraylist_push(&ptls->heap.weak_refs, wr);
932 : 101857 : return wr;
933 : : }
934 : :
935 : 14024 : static void clear_weak_refs(void)
936 : : {
937 [ + + ]: 28126 : for (int i = 0; i < jl_n_threads; i++) {
938 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[i];
939 : 14102 : size_t n, l = ptls2->heap.weak_refs.len;
940 : 14102 : void **lst = ptls2->heap.weak_refs.items;
941 [ + + ]: 308682 : for (n = 0; n < l; n++) {
942 : 294580 : jl_weakref_t *wr = (jl_weakref_t*)lst[n];
943 [ + + ]: 294580 : if (!gc_marked(jl_astaggedvalue(wr->value)->bits.gc))
944 : 100924 : wr->value = (jl_value_t*)jl_nothing;
945 : : }
946 : : }
947 : 14024 : }
948 : :
949 : 14024 : static void sweep_weak_refs(void)
950 : : {
951 [ + + ]: 28126 : for (int i = 0; i < jl_n_threads; i++) {
952 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[i];
953 : 14102 : size_t n = 0;
954 : 14102 : size_t ndel = 0;
955 : 14102 : size_t l = ptls2->heap.weak_refs.len;
956 : 14102 : void **lst = ptls2->heap.weak_refs.items;
957 [ + + ]: 14102 : if (l == 0)
958 : 1653 : continue;
959 : 282131 : while (1) {
960 : 294580 : jl_weakref_t *wr = (jl_weakref_t*)lst[n];
961 [ + + ]: 294580 : if (gc_marked(jl_astaggedvalue(wr)->bits.gc))
962 : 194087 : n++;
963 : : else
964 : 100493 : ndel++;
965 [ + + ]: 294580 : if (n >= l - ndel)
966 : 12449 : break;
967 : 282131 : void *tmp = lst[n];
968 : 282131 : lst[n] = lst[n + ndel];
969 : 282131 : lst[n + ndel] = tmp;
970 : : }
971 : 12449 : ptls2->heap.weak_refs.len -= ndel;
972 : : }
973 : 14024 : }
974 : :
975 : :
976 : : // big value list
977 : :
978 : : // Size includes the tag and the tag is not cleared!!
979 : 9185990 : static inline jl_value_t *jl_gc_big_alloc_inner(jl_ptls_t ptls, size_t sz)
980 : : {
981 : 9185990 : maybe_collect(ptls);
982 : 9185990 : size_t offs = offsetof(bigval_t, header);
983 [ - + ]: 9185990 : assert(sz >= sizeof(jl_taggedvalue_t) && "sz must include tag");
984 : : static_assert(offsetof(bigval_t, header) >= sizeof(void*), "Empty bigval header?");
985 : : static_assert(sizeof(bigval_t) % JL_HEAP_ALIGNMENT == 0, "");
986 : 9185990 : size_t allocsz = LLT_ALIGN(sz + offs, JL_CACHE_BYTE_ALIGNMENT);
987 [ - + ]: 9185990 : if (allocsz < sz) // overflow in adding offs, size was "negative"
988 : 0 : jl_throw(jl_memory_exception);
989 : 9185990 : bigval_t *v = (bigval_t*)malloc_cache_align(allocsz);
990 [ - + ]: 9185990 : if (v == NULL)
991 : 0 : jl_throw(jl_memory_exception);
992 [ - + ]: 9185990 : gc_invoke_callbacks(jl_gc_cb_notify_external_alloc_t,
993 : : gc_cblist_notify_external_alloc, (v, allocsz));
994 : 9185990 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
995 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz);
996 : 9185990 : jl_atomic_store_relaxed(&ptls->gc_num.bigalloc,
997 : : jl_atomic_load_relaxed(&ptls->gc_num.bigalloc) + 1);
998 : : #ifdef MEMDEBUG
999 : : memset(v, 0xee, allocsz);
1000 : : #endif
1001 : 9185990 : v->sz = allocsz;
1002 : 9185990 : v->age = 0;
1003 : 9185990 : gc_big_object_link(v, &ptls->heap.big_objects);
1004 : 9185990 : return jl_valueof(&v->header);
1005 : : }
1006 : :
1007 : : // Instrumented version of jl_gc_big_alloc_inner, called into by LLVM-generated code.
1008 : 12037 : JL_DLLEXPORT jl_value_t *jl_gc_big_alloc(jl_ptls_t ptls, size_t sz)
1009 : : {
1010 : 12037 : jl_value_t *val = jl_gc_big_alloc_inner(ptls, sz);
1011 : :
1012 : 12037 : maybe_record_alloc_to_profile(val, sz, jl_gc_unknown_type_tag);
1013 : 12037 : return val;
1014 : : }
1015 : :
1016 : : // This wrapper exists only to prevent `jl_gc_big_alloc_inner` from being inlined into
1017 : : // its callers. We provide an external-facing interface for callers, and inline `jl_gc_big_alloc_inner`
1018 : : // into this. (See https://github.com/JuliaLang/julia/pull/43868 for more details.)
1019 : 9173960 : jl_value_t *jl_gc_big_alloc_noinline(jl_ptls_t ptls, size_t sz) {
1020 : 9173960 : return jl_gc_big_alloc_inner(ptls, sz);
1021 : : }
1022 : :
1023 : : // Sweep list rooted at *pv, removing and freeing any unmarked objects.
1024 : : // Return pointer to last `next` field in the culled list.
1025 : 14746 : static bigval_t **sweep_big_list(int sweep_full, bigval_t **pv) JL_NOTSAFEPOINT
1026 : : {
1027 : 14746 : bigval_t *v = *pv;
1028 [ + + ]: 10903200 : while (v != NULL) {
1029 : 10888500 : bigval_t *nxt = v->next;
1030 : 10888500 : int bits = v->bits.gc;
1031 : 10888500 : int old_bits = bits;
1032 [ + + ]: 10888500 : if (gc_marked(bits)) {
1033 : 2007590 : pv = &v->next;
1034 : 2007590 : int age = v->age;
1035 [ + + + + ]: 2007590 : if (age >= PROMOTE_AGE || bits == GC_OLD_MARKED) {
1036 [ + + + - ]: 1309830 : if (sweep_full || bits == GC_MARKED) {
1037 : 1309830 : bits = GC_OLD;
1038 : : }
1039 : : }
1040 : : else {
1041 : 697755 : inc_sat(age, PROMOTE_AGE);
1042 : 697755 : v->age = age;
1043 : 697755 : bits = GC_CLEAN;
1044 : : }
1045 : 2007590 : v->bits.gc = bits;
1046 : : }
1047 : : else {
1048 : : // Remove v from list and free it
1049 : 8880860 : *pv = nxt;
1050 [ + + ]: 8880860 : if (nxt)
1051 : 8876390 : nxt->prev = pv;
1052 : 8880860 : gc_num.freed += v->sz&~3;
1053 : : #ifdef MEMDEBUG
1054 : : memset(v, 0xbb, v->sz&~3);
1055 : : #endif
1056 [ - + ]: 8880860 : gc_invoke_callbacks(jl_gc_cb_notify_external_free_t,
1057 : : gc_cblist_notify_external_free, (v));
1058 : 8880860 : jl_free_aligned(v);
1059 : : }
1060 : 10888500 : gc_time_count_big(old_bits, bits);
1061 : 10888500 : v = nxt;
1062 : : }
1063 : 14746 : return pv;
1064 : : }
1065 : :
1066 : 14024 : static void sweep_big(jl_ptls_t ptls, int sweep_full) JL_NOTSAFEPOINT
1067 : : {
1068 : : gc_time_big_start();
1069 [ + + ]: 28126 : for (int i = 0;i < jl_n_threads;i++)
1070 : 14102 : sweep_big_list(sweep_full, &jl_all_tls_states[i]->heap.big_objects);
1071 [ + + ]: 14024 : if (sweep_full) {
1072 : 644 : bigval_t **last_next = sweep_big_list(sweep_full, &big_objects_marked);
1073 : : // Move all survivors from big_objects_marked list to big_objects list.
1074 [ + + ]: 644 : if (ptls->heap.big_objects)
1075 : 75 : ptls->heap.big_objects->prev = last_next;
1076 : 644 : *last_next = ptls->heap.big_objects;
1077 : 644 : ptls->heap.big_objects = big_objects_marked;
1078 [ + - ]: 644 : if (ptls->heap.big_objects)
1079 : 644 : ptls->heap.big_objects->prev = &ptls->heap.big_objects;
1080 : 644 : big_objects_marked = NULL;
1081 : : }
1082 : : gc_time_big_end();
1083 : 14024 : }
1084 : :
1085 : : // tracking Arrays with malloc'd storage
1086 : :
1087 : 2189270 : void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT
1088 : : {
1089 : : // This is **NOT** a GC safe point.
1090 : : mallocarray_t *ma;
1091 [ + + ]: 2189270 : if (ptls->heap.mafreelist == NULL) {
1092 : 1756160 : ma = (mallocarray_t*)malloc_s(sizeof(mallocarray_t));
1093 : : }
1094 : : else {
1095 : 433112 : ma = ptls->heap.mafreelist;
1096 : 433112 : ptls->heap.mafreelist = ma->next;
1097 : : }
1098 : 2189270 : ma->a = a;
1099 : 2189270 : ma->next = ptls->heap.mallocarrays;
1100 : 2189270 : ptls->heap.mallocarrays = ma;
1101 : 2189270 : }
1102 : :
1103 : 2032370 : void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT
1104 : : {
1105 : 2032370 : jl_ptls_t ptls = jl_current_task->ptls;
1106 : 2032370 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
1107 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz);
1108 : 2032370 : }
1109 : :
1110 : 49777 : static void combine_thread_gc_counts(jl_gc_num_t *dest) JL_NOTSAFEPOINT
1111 : : {
1112 [ + + ]: 99751 : for (int i = 0; i < jl_n_threads; i++) {
1113 : 49974 : jl_ptls_t ptls = jl_all_tls_states[i];
1114 [ + + ]: 49974 : if (ptls) {
1115 : 49855 : dest->allocd += (jl_atomic_load_relaxed(&ptls->gc_num.allocd) + gc_num.interval);
1116 : 49855 : dest->freed += jl_atomic_load_relaxed(&ptls->gc_num.freed);
1117 : 49855 : dest->malloc += jl_atomic_load_relaxed(&ptls->gc_num.malloc);
1118 : 49855 : dest->realloc += jl_atomic_load_relaxed(&ptls->gc_num.realloc);
1119 : 49855 : dest->poolalloc += jl_atomic_load_relaxed(&ptls->gc_num.poolalloc);
1120 : 49855 : dest->bigalloc += jl_atomic_load_relaxed(&ptls->gc_num.bigalloc);
1121 : 49855 : dest->freecall += jl_atomic_load_relaxed(&ptls->gc_num.freecall);
1122 : : }
1123 : : }
1124 : 49777 : }
1125 : :
1126 : 14589 : static void reset_thread_gc_counts(void) JL_NOTSAFEPOINT
1127 : : {
1128 [ + + ]: 29375 : for (int i = 0; i < jl_n_threads; i++) {
1129 : 14786 : jl_ptls_t ptls = jl_all_tls_states[i];
1130 [ + + ]: 14786 : if (ptls) {
1131 : 14667 : memset(&ptls->gc_num, 0, sizeof(ptls->gc_num));
1132 : 14667 : jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval);
1133 : : }
1134 : : }
1135 : 14589 : }
1136 : :
1137 : 565 : void jl_gc_reset_alloc_count(void) JL_NOTSAFEPOINT
1138 : : {
1139 : 565 : combine_thread_gc_counts(&gc_num);
1140 : 565 : live_bytes += (gc_num.deferred_alloc + gc_num.allocd);
1141 : 565 : gc_num.allocd = 0;
1142 : 565 : gc_num.deferred_alloc = 0;
1143 : 565 : reset_thread_gc_counts();
1144 : 565 : }
1145 : :
1146 : 167894000 : size_t jl_array_nbytes(jl_array_t *a) JL_NOTSAFEPOINT
1147 : : {
1148 : 167894000 : size_t sz = 0;
1149 [ + + + + ]: 167894000 : int isbitsunion = jl_array_isbitsunion(a);
1150 [ + + ]: 167894000 : if (jl_array_ndims(a) == 1)
1151 [ + + + + ]: 167890000 : sz = a->elsize * a->maxsize + ((a->elsize == 1 && !isbitsunion) ? 1 : 0);
1152 : : else
1153 : 3104 : sz = a->elsize * jl_array_len(a);
1154 [ + + ]: 167894000 : if (isbitsunion)
1155 : : // account for isbits Union array selector bytes
1156 : 293 : sz += jl_array_len(a);
1157 : 167894000 : return sz;
1158 : : }
1159 : :
1160 : 437879 : static void jl_gc_free_array(jl_array_t *a) JL_NOTSAFEPOINT
1161 : : {
1162 [ + - ]: 437879 : if (a->flags.how == 2) {
1163 : 437879 : char *d = (char*)a->data - a->offset*a->elsize;
1164 [ + + ]: 437879 : if (a->flags.isaligned)
1165 : 150368 : jl_free_aligned(d);
1166 : : else
1167 : 287511 : free(d);
1168 : 437879 : gc_num.freed += jl_array_nbytes(a);
1169 : 437879 : gc_num.freecall++;
1170 : : }
1171 : 437879 : }
1172 : :
1173 : 14024 : static void sweep_malloced_arrays(void) JL_NOTSAFEPOINT
1174 : : {
1175 : : gc_time_mallocd_array_start();
1176 [ + + ]: 28126 : for (int t_i = 0;t_i < jl_n_threads;t_i++) {
1177 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
1178 : 14102 : mallocarray_t *ma = ptls2->heap.mallocarrays;
1179 : 14102 : mallocarray_t **pma = &ptls2->heap.mallocarrays;
1180 [ + + ]: 919321000 : while (ma != NULL) {
1181 : 919307000 : mallocarray_t *nxt = ma->next;
1182 : 919307000 : int bits = jl_astaggedvalue(ma->a)->bits.gc;
1183 [ + + ]: 919307000 : if (gc_marked(bits)) {
1184 : 918869000 : pma = &ma->next;
1185 : : }
1186 : : else {
1187 : 437879 : *pma = nxt;
1188 [ - + ]: 437879 : assert(ma->a->flags.how == 2);
1189 : 437879 : jl_gc_free_array(ma->a);
1190 : 437879 : ma->next = ptls2->heap.mafreelist;
1191 : 437879 : ptls2->heap.mafreelist = ma;
1192 : : }
1193 : 919307000 : gc_time_count_mallocd_array(bits);
1194 : 919307000 : ma = nxt;
1195 : : }
1196 : : }
1197 : : gc_time_mallocd_array_end();
1198 : 14024 : }
1199 : :
1200 : : // pool allocation
1201 : 36079800 : static inline jl_taggedvalue_t *reset_page(const jl_gc_pool_t *p, jl_gc_pagemeta_t *pg, jl_taggedvalue_t *fl) JL_NOTSAFEPOINT
1202 : : {
1203 : : assert(GC_PAGE_OFFSET >= sizeof(void*));
1204 : 36079800 : pg->nfree = (GC_PAGE_SZ - GC_PAGE_OFFSET) / p->osize;
1205 : 36079800 : jl_ptls_t ptls2 = jl_all_tls_states[pg->thread_n];
1206 : 36079800 : pg->pool_n = p - ptls2->heap.norm_pools;
1207 : 36079800 : memset(pg->ages, 0, GC_PAGE_SZ / 8 / p->osize + 1);
1208 : 36079800 : jl_taggedvalue_t *beg = (jl_taggedvalue_t*)(pg->data + GC_PAGE_OFFSET);
1209 : 36079800 : jl_taggedvalue_t *next = (jl_taggedvalue_t*)pg->data;
1210 [ + + ]: 36079800 : if (fl == NULL) {
1211 : 6403200 : next->next = NULL;
1212 : : }
1213 : : else {
1214 : : // Insert free page after first page.
1215 : : // This prevents unnecessary fragmentation from multiple pages
1216 : : // being allocated from at the same time. Instead, objects will
1217 : : // only ever be allocated from the first object in the list.
1218 : : // This is specifically being relied on by the implementation
1219 : : // of jl_gc_internal_obj_base_ptr() so that the function does
1220 : : // not have to traverse the entire list.
1221 : 29676600 : jl_taggedvalue_t *flpage = (jl_taggedvalue_t *)gc_page_data(fl);
1222 : 29676600 : next->next = flpage->next;
1223 : 29676600 : flpage->next = beg;
1224 : 29676600 : beg = fl;
1225 : : }
1226 : 36079800 : pg->has_young = 0;
1227 : 36079800 : pg->has_marked = 0;
1228 : 36079800 : pg->fl_begin_offset = -1;
1229 : 36079800 : pg->fl_end_offset = -1;
1230 : 36079800 : return beg;
1231 : : }
1232 : :
1233 : : // Add a new page to the pool. Discards any pages in `p->newpages` before.
1234 : 5921700 : static NOINLINE jl_taggedvalue_t *add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT
1235 : : {
1236 : : // Do not pass in `ptls` as argument. This slows down the fast path
1237 : : // in pool_alloc significantly
1238 : 5921700 : jl_ptls_t ptls = jl_current_task->ptls;
1239 : 5921700 : jl_gc_pagemeta_t *pg = jl_gc_alloc_page();
1240 : 5921700 : pg->osize = p->osize;
1241 : 5921700 : pg->ages = (uint8_t*)malloc_s(GC_PAGE_SZ / 8 / p->osize + 1);
1242 : 5921700 : pg->thread_n = ptls->tid;
1243 : 5921700 : jl_taggedvalue_t *fl = reset_page(p, pg, NULL);
1244 : 5921700 : p->newpages = fl;
1245 : 5921700 : return fl;
1246 : : }
1247 : :
1248 : : // Size includes the tag and the tag is not cleared!!
1249 :14815300000 : static inline jl_value_t *jl_gc_pool_alloc_inner(jl_ptls_t ptls, int pool_offset,
1250 : : int osize)
1251 : : {
1252 : : // Use the pool offset instead of the pool address as the argument
1253 : : // to workaround a llvm bug.
1254 : : // Ref https://llvm.org/bugs/show_bug.cgi?id=27190
1255 :14815300000 : jl_gc_pool_t *p = (jl_gc_pool_t*)((char*)ptls + pool_offset);
1256 [ - + ]:14815300000 : assert(jl_atomic_load_relaxed(&ptls->gc_state) == 0);
1257 : : #ifdef MEMDEBUG
1258 : : return jl_gc_big_alloc(ptls, osize);
1259 : : #endif
1260 :14815300000 : maybe_collect(ptls);
1261 :14815300000 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
1262 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + osize);
1263 :14815300000 : jl_atomic_store_relaxed(&ptls->gc_num.poolalloc,
1264 : : jl_atomic_load_relaxed(&ptls->gc_num.poolalloc) + 1);
1265 : : // first try to use the freelist
1266 :14815300000 : jl_taggedvalue_t *v = p->freelist;
1267 [ + + ]:14815300000 : if (v) {
1268 :10112700000 : jl_taggedvalue_t *next = v->next;
1269 :10112700000 : p->freelist = next;
1270 [ + + ]:10112700000 : if (__unlikely(gc_page_data(v) != gc_page_data(next))) {
1271 : : // we only update pg's fields when the freelist changes page
1272 : : // since pg's metadata is likely not in cache
1273 : 79494000 : jl_gc_pagemeta_t *pg = jl_assume(page_metadata(v));
1274 [ - + ]: 79494000 : assert(pg->osize == p->osize);
1275 : 79494000 : pg->nfree = 0;
1276 : 79494000 : pg->has_young = 1;
1277 : : }
1278 :10112700000 : return jl_valueof(v);
1279 : : }
1280 : : // if the freelist is empty we reuse empty but not freed pages
1281 : 4702600000 : v = p->newpages;
1282 : 4702600000 : jl_taggedvalue_t *next = (jl_taggedvalue_t*)((char*)v + osize);
1283 : : // If there's no pages left or the current page is used up,
1284 : : // we need to use the slow path.
1285 : 4702600000 : char *cur_page = gc_page_data((char*)v - 1);
1286 [ + + + + ]: 4702620000 : if (__unlikely(!v || cur_page + GC_PAGE_SZ < (char*)next)) {
1287 [ + + ]: 9539700 : if (v) {
1288 : : // like the freelist case,
1289 : : // but only update the page metadata when it is full
1290 : 9510180 : jl_gc_pagemeta_t *pg = jl_assume(page_metadata((char*)v - 1));
1291 [ - + ]: 9510180 : assert(pg->osize == p->osize);
1292 : 9510180 : pg->nfree = 0;
1293 : 9510180 : pg->has_young = 1;
1294 : 9510180 : v = *(jl_taggedvalue_t**)cur_page;
1295 : : }
1296 : : // Not an else!!
1297 [ + + ]: 9539700 : if (!v)
1298 : 5921700 : v = add_page(p);
1299 : 9540000 : next = (jl_taggedvalue_t*)((char*)v + osize);
1300 : : }
1301 : 4702620000 : p->newpages = next;
1302 : 4702620000 : return jl_valueof(v);
1303 : : }
1304 : :
1305 : : // Instrumented version of jl_gc_pool_alloc_inner, called into by LLVM-generated code.
1306 : 5600590000 : JL_DLLEXPORT jl_value_t *jl_gc_pool_alloc(jl_ptls_t ptls, int pool_offset,
1307 : : int osize)
1308 : : {
1309 : 5600590000 : jl_value_t *val = jl_gc_pool_alloc_inner(ptls, pool_offset, osize);
1310 : :
1311 : 5600590000 : maybe_record_alloc_to_profile(val, osize, jl_gc_unknown_type_tag);
1312 : 5600590000 : return val;
1313 : : }
1314 : :
1315 : : // This wrapper exists only to prevent `jl_gc_pool_alloc_inner` from being inlined into
1316 : : // its callers. We provide an external-facing interface for callers, and inline `jl_gc_pool_alloc_inner`
1317 : : // into this. (See https://github.com/JuliaLang/julia/pull/43868 for more details.)
1318 : 9214730000 : jl_value_t *jl_gc_pool_alloc_noinline(jl_ptls_t ptls, int pool_offset, int osize) {
1319 : 9214730000 : return jl_gc_pool_alloc_inner(ptls, pool_offset, osize);
1320 : : }
1321 : :
1322 : 405289 : int jl_gc_classify_pools(size_t sz, int *osize)
1323 : : {
1324 [ + + ]: 405289 : if (sz > GC_MAX_SZCLASS)
1325 : 24 : return -1;
1326 : 405265 : size_t allocsz = sz + sizeof(jl_taggedvalue_t);
1327 : 405265 : int klass = jl_gc_szclass(allocsz);
1328 : 405265 : *osize = jl_gc_sizeclasses[klass];
1329 : 405265 : return (int)(intptr_t)(&((jl_ptls_t)0)->heap.norm_pools[klass]);
1330 : : }
1331 : :
1332 : : // sweep phase
1333 : :
1334 : : int64_t lazy_freed_pages = 0;
1335 : :
1336 : : // Returns pointer to terminal pointer of list rooted at *pfl.
1337 : 432737000 : static jl_taggedvalue_t **sweep_page(jl_gc_pool_t *p, jl_gc_pagemeta_t *pg, jl_taggedvalue_t **pfl, int sweep_full, int osize) JL_NOTSAFEPOINT
1338 : : {
1339 : 432737000 : char *data = pg->data;
1340 : 432737000 : uint8_t *ages = pg->ages;
1341 : 432737000 : jl_taggedvalue_t *v = (jl_taggedvalue_t*)(data + GC_PAGE_OFFSET);
1342 : 432737000 : char *lim = (char*)v + GC_PAGE_SZ - GC_PAGE_OFFSET - osize;
1343 : 432737000 : size_t old_nfree = pg->nfree;
1344 : : size_t nfree;
1345 : :
1346 : 432737000 : int freedall = 1;
1347 : 432737000 : int pg_skpd = 1;
1348 [ + + ]: 432737000 : if (!pg->has_marked) {
1349 : : // lazy version: (empty) if the whole page was already unused, free it (return it to the pool)
1350 : : // eager version: (freedall) free page as soon as possible
1351 : : // the eager one uses less memory.
1352 : : // FIXME - need to do accounting on a per-thread basis
1353 : : // on quick sweeps, keep a few pages empty but allocated for performance
1354 [ + + + + ]: 34623900 : if (!sweep_full && lazy_freed_pages <= default_collect_interval / GC_PAGE_SZ) {
1355 : 30158100 : jl_taggedvalue_t *begin = reset_page(p, pg, p->newpages);
1356 : 30158100 : p->newpages = begin;
1357 : 30158100 : begin->next = (jl_taggedvalue_t*)0;
1358 : 30158100 : lazy_freed_pages++;
1359 : : }
1360 : : else {
1361 : 4465760 : jl_gc_free_page(data);
1362 : : }
1363 : 34623900 : nfree = (GC_PAGE_SZ - GC_PAGE_OFFSET) / osize;
1364 : 34623900 : goto done;
1365 : : }
1366 : : // For quick sweep, we might be able to skip the page if the page doesn't
1367 : : // have any young live cell before marking.
1368 [ + + + + ]: 398113000 : if (!sweep_full && !pg->has_young) {
1369 [ + + - + ]: 298587000 : assert(!prev_sweep_full || pg->prev_nold >= pg->nold);
1370 [ + + + + ]: 298587000 : if (!prev_sweep_full || pg->prev_nold == pg->nold) {
1371 : : // the position of the freelist begin/end in this page
1372 : : // is stored in its metadata
1373 [ + + ]: 298192000 : if (pg->fl_begin_offset != (uint16_t)-1) {
1374 : 97379800 : *pfl = page_pfl_beg(pg);
1375 : 97379800 : pfl = (jl_taggedvalue_t**)page_pfl_end(pg);
1376 : : }
1377 : 298192000 : freedall = 0;
1378 : 298192000 : nfree = pg->nfree;
1379 : 298192000 : goto done;
1380 : : }
1381 : : }
1382 : :
1383 : 99920600 : pg_skpd = 0;
1384 : : { // scope to avoid clang goto errors
1385 : 99920600 : int has_marked = 0;
1386 : 99920600 : int has_young = 0;
1387 : 99920600 : int16_t prev_nold = 0;
1388 : 99920600 : int pg_nfree = 0;
1389 : 99920600 : jl_taggedvalue_t **pfl_begin = NULL;
1390 : 99920600 : uint8_t msk = 1; // mask for the age bit in the current age byte
1391 [ + + ]:27772800000 : while ((char*)v <= lim) {
1392 :27672900000 : int bits = v->bits.gc;
1393 [ + + ]:27672900000 : if (!gc_marked(bits)) {
1394 :14364100000 : *pfl = v;
1395 :14364100000 : pfl = &v->next;
1396 [ + + ]:14364100000 : pfl_begin = pfl_begin ? pfl_begin : pfl;
1397 :14364100000 : pg_nfree++;
1398 :14364100000 : *ages &= ~msk;
1399 : : }
1400 : : else { // marked young or old
1401 [ + + + + ]:13308800000 : if (*ages & msk || bits == GC_OLD_MARKED) { // old enough
1402 : : // `!age && bits == GC_OLD_MARKED` is possible for
1403 : : // non-first-class objects like `jl_binding_t`
1404 [ + + + + ]:12952700000 : if (sweep_full || bits == GC_MARKED) {
1405 : 945440000 : bits = v->bits.gc = GC_OLD; // promote
1406 : : }
1407 :12952700000 : prev_nold++;
1408 : : }
1409 : : else {
1410 [ - + ]: 356149000 : assert(bits == GC_MARKED);
1411 : 356149000 : bits = v->bits.gc = GC_CLEAN; // unmark
1412 : 356149000 : has_young = 1;
1413 : : }
1414 :13308800000 : has_marked |= gc_marked(bits);
1415 :13308800000 : *ages |= msk;
1416 :13308800000 : freedall = 0;
1417 : : }
1418 :27672900000 : v = (jl_taggedvalue_t*)((char*)v + osize);
1419 :27672900000 : msk <<= 1;
1420 [ + + ]:27672900000 : if (!msk) {
1421 : 3396870000 : msk = 1;
1422 : 3396870000 : ages++;
1423 : : }
1424 : : }
1425 : :
1426 [ - + ]: 99920600 : assert(!freedall);
1427 : 99920600 : pg->has_marked = has_marked;
1428 : 99920600 : pg->has_young = has_young;
1429 [ + + ]: 99920600 : if (pfl_begin) {
1430 : 98494100 : pg->fl_begin_offset = (char*)pfl_begin - data;
1431 : 98494100 : pg->fl_end_offset = (char*)pfl - data;
1432 : : }
1433 : : else {
1434 : 1426430 : pg->fl_begin_offset = -1;
1435 : 1426430 : pg->fl_end_offset = -1;
1436 : : }
1437 : :
1438 : 99920600 : pg->nfree = pg_nfree;
1439 [ + + ]: 99920600 : if (sweep_full) {
1440 : 15872000 : pg->nold = 0;
1441 : 15872000 : pg->prev_nold = prev_nold;
1442 : : }
1443 : : }
1444 : 99920600 : nfree = pg->nfree;
1445 : :
1446 : 432737000 : done:
1447 : 432737000 : gc_time_count_page(freedall, pg_skpd);
1448 : 432737000 : gc_num.freed += (nfree - old_nfree) * osize;
1449 : 432737000 : return pfl;
1450 : : }
1451 : :
1452 : : // the actual sweeping over all allocated pages in a memory pool
1453 : 432737000 : static inline void sweep_pool_page(jl_taggedvalue_t ***pfl, jl_gc_pagemeta_t *pg, int sweep_full) JL_NOTSAFEPOINT
1454 : : {
1455 : 432737000 : int p_n = pg->pool_n;
1456 : 432737000 : int t_n = pg->thread_n;
1457 : 432737000 : jl_ptls_t ptls2 = jl_all_tls_states[t_n];
1458 : 432737000 : jl_gc_pool_t *p = &ptls2->heap.norm_pools[p_n];
1459 : 432737000 : int osize = pg->osize;
1460 : 432737000 : pfl[t_n * JL_GC_N_POOLS + p_n] = sweep_page(p, pg, pfl[t_n * JL_GC_N_POOLS + p_n], sweep_full, osize);
1461 : 432737000 : }
1462 : :
1463 : : // sweep over a pagetable0 for all allocated pages
1464 : 38162 : static inline int sweep_pool_pagetable0(jl_taggedvalue_t ***pfl, pagetable0_t *pagetable0, int sweep_full) JL_NOTSAFEPOINT
1465 : : {
1466 : 38162 : unsigned ub = 0;
1467 : 38162 : unsigned alloc = 0;
1468 [ + + ]: 51061800 : for (unsigned pg_i = 0; pg_i <= pagetable0->ub; pg_i++) {
1469 : 51023600 : uint32_t line = pagetable0->allocmap[pg_i];
1470 : : unsigned j;
1471 [ + + ]: 51023600 : if (!line)
1472 : 36019200 : continue;
1473 : 15004400 : ub = pg_i;
1474 : 15004400 : alloc = 1;
1475 [ + + ]: 447741000 : for (j = 0; line; j++, line >>= 1) {
1476 : 432737000 : unsigned next = ffs_u32(line);
1477 : 432737000 : j += next;
1478 : 432737000 : line >>= next;
1479 : 432737000 : jl_gc_pagemeta_t *pg = pagetable0->meta[pg_i * 32 + j];
1480 : 432737000 : sweep_pool_page(pfl, pg, sweep_full);
1481 : : }
1482 : : }
1483 : 38162 : pagetable0->ub = ub;
1484 : 38162 : return alloc;
1485 : : }
1486 : :
1487 : : // sweep over pagetable1 for all pagetable0 that may contain allocated pages
1488 : 14024 : static inline int sweep_pool_pagetable1(jl_taggedvalue_t ***pfl, pagetable1_t *pagetable1, int sweep_full) JL_NOTSAFEPOINT
1489 : : {
1490 : 14024 : unsigned ub = 0;
1491 : 14024 : unsigned alloc = 0;
1492 [ + + ]: 28444400 : for (unsigned pg_i = 0; pg_i <= pagetable1->ub; pg_i++) {
1493 : 28430400 : uint32_t line = pagetable1->allocmap0[pg_i];
1494 : : unsigned j;
1495 [ + + ]: 28468500 : for (j = 0; line; j++, line >>= 1) {
1496 : 38162 : unsigned next = ffs_u32(line);
1497 : 38162 : j += next;
1498 : 38162 : line >>= next;
1499 : 38162 : pagetable0_t *pagetable0 = pagetable1->meta0[pg_i * 32 + j];
1500 [ + - - + ]: 38162 : if (pagetable0 && !sweep_pool_pagetable0(pfl, pagetable0, sweep_full))
1501 : 0 : pagetable1->allocmap0[pg_i] &= ~(1 << j); // no allocations found, remember that for next time
1502 : : }
1503 [ + + ]: 28430400 : if (pagetable1->allocmap0[pg_i]) {
1504 : 14154 : ub = pg_i;
1505 : 14154 : alloc = 1;
1506 : : }
1507 : : }
1508 : 14024 : pagetable1->ub = ub;
1509 : 14024 : return alloc;
1510 : : }
1511 : :
1512 : : // sweep over all memory for all pagetable1 that may contain allocated pages
1513 : 14024 : static void sweep_pool_pagetable(jl_taggedvalue_t ***pfl, int sweep_full) JL_NOTSAFEPOINT
1514 : : {
1515 : : if (REGION2_PG_COUNT == 1) { // compile-time optimization
1516 : : pagetable1_t *pagetable1 = memory_map.meta1[0];
1517 : : if (pagetable1)
1518 : : sweep_pool_pagetable1(pfl, pagetable1, sweep_full);
1519 : : return;
1520 : : }
1521 : 14024 : unsigned ub = 0;
1522 [ + + ]: 28048 : for (unsigned pg_i = 0; pg_i <= memory_map.ub; pg_i++) {
1523 : 14024 : uint32_t line = memory_map.allocmap1[pg_i];
1524 : : unsigned j;
1525 [ + + ]: 28048 : for (j = 0; line; j++, line >>= 1) {
1526 : 14024 : unsigned next = ffs_u32(line);
1527 : 14024 : j += next;
1528 : 14024 : line >>= next;
1529 : 14024 : pagetable1_t *pagetable1 = memory_map.meta1[pg_i * 32 + j];
1530 [ + - - + ]: 14024 : if (pagetable1 && !sweep_pool_pagetable1(pfl, pagetable1, sweep_full))
1531 : 0 : memory_map.allocmap1[pg_i] &= ~(1 << j); // no allocations found, remember that for next time
1532 : : }
1533 [ + - ]: 14024 : if (memory_map.allocmap1[pg_i]) {
1534 : 14024 : ub = pg_i;
1535 : : }
1536 : : }
1537 : 14024 : memory_map.ub = ub;
1538 : : }
1539 : :
1540 : : // sweep over all memory that is being used and not in a pool
1541 : 14024 : static void gc_sweep_other(jl_ptls_t ptls, int sweep_full) JL_NOTSAFEPOINT
1542 : : {
1543 : 14024 : sweep_malloced_arrays();
1544 : 14024 : sweep_big(ptls, sweep_full);
1545 : 14024 : }
1546 : :
1547 : 524246 : static void gc_pool_sync_nfree(jl_gc_pagemeta_t *pg, jl_taggedvalue_t *last) JL_NOTSAFEPOINT
1548 : : {
1549 [ - + ]: 524246 : assert(pg->fl_begin_offset != (uint16_t)-1);
1550 : 524246 : char *cur_pg = gc_page_data(last);
1551 : : // Fast path for page that has no allocation
1552 : 524246 : jl_taggedvalue_t *fl_beg = (jl_taggedvalue_t*)(cur_pg + pg->fl_begin_offset);
1553 [ + + ]: 524246 : if (last == fl_beg)
1554 : 195223 : return;
1555 : 329023 : int nfree = 0;
1556 : : do {
1557 : 18432400 : nfree++;
1558 : 18432400 : last = last->next;
1559 [ + + ]: 18432400 : } while (gc_page_data(last) == cur_pg);
1560 : 329023 : pg->nfree = nfree;
1561 : : }
1562 : :
1563 : : // setup the data-structures for a sweep over all memory pools
1564 : 14024 : static void gc_sweep_pool(int sweep_full)
1565 : : {
1566 : : gc_time_pool_start();
1567 : 14024 : lazy_freed_pages = 0;
1568 : :
1569 : : // For the benfit of the analyzer, which doesn't know that jl_n_threads
1570 : : // doesn't change over the course of this function
1571 : 14024 : size_t n_threads = jl_n_threads;
1572 : :
1573 : : // allocate enough space to hold the end of the free list chain
1574 : : // for every thread and pool size
1575 : 14024 : jl_taggedvalue_t ***pfl = (jl_taggedvalue_t ***) alloca(n_threads * JL_GC_N_POOLS * sizeof(jl_taggedvalue_t**));
1576 : :
1577 : : // update metadata of pages that were pointed to by freelist or newpages from a pool
1578 : : // i.e. pages being the current allocation target
1579 [ + + ]: 28126 : for (int t_i = 0; t_i < n_threads; t_i++) {
1580 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
1581 [ + + ]: 705100 : for (int i = 0; i < JL_GC_N_POOLS; i++) {
1582 : 690998 : jl_gc_pool_t *p = &ptls2->heap.norm_pools[i];
1583 : 690998 : jl_taggedvalue_t *last = p->freelist;
1584 [ + + ]: 690998 : if (last) {
1585 : 524246 : jl_gc_pagemeta_t *pg = jl_assume(page_metadata(last));
1586 : 524246 : gc_pool_sync_nfree(pg, last);
1587 : 524246 : pg->has_young = 1;
1588 : : }
1589 : 690998 : p->freelist = NULL;
1590 : 690998 : pfl[t_i * JL_GC_N_POOLS + i] = &p->freelist;
1591 : :
1592 : 690998 : last = p->newpages;
1593 [ + + ]: 690998 : if (last) {
1594 : 488653 : char *last_p = (char*)last;
1595 : 488653 : jl_gc_pagemeta_t *pg = jl_assume(page_metadata(last_p - 1));
1596 [ - + ]: 488653 : assert(last_p - gc_page_data(last_p - 1) >= GC_PAGE_OFFSET);
1597 : 488653 : pg->nfree = (GC_PAGE_SZ - (last_p - gc_page_data(last_p - 1))) / p->osize;
1598 : 488653 : pg->has_young = 1;
1599 : : }
1600 : 690998 : p->newpages = NULL;
1601 : : }
1602 : : }
1603 : :
1604 : : // the actual sweeping
1605 : 14024 : sweep_pool_pagetable(pfl, sweep_full);
1606 : :
1607 : : // null out terminal pointers of free lists
1608 [ + + ]: 28126 : for (int t_i = 0; t_i < n_threads; t_i++) {
1609 [ + + ]: 705100 : for (int i = 0; i < JL_GC_N_POOLS; i++) {
1610 : 690998 : *pfl[t_i * JL_GC_N_POOLS + i] = NULL;
1611 : : }
1612 : : }
1613 : :
1614 : : gc_time_pool_end(sweep_full);
1615 : 14024 : }
1616 : :
1617 : 644 : static void gc_sweep_perm_alloc(void)
1618 : : {
1619 : 644 : uint64_t t0 = jl_hrtime();
1620 : 644 : gc_sweep_sysimg();
1621 : : gc_time_sysimg_end(t0);
1622 : 644 : }
1623 : :
1624 : : // mark phase
1625 : :
1626 : 4119060 : JL_DLLEXPORT void jl_gc_queue_root(const jl_value_t *ptr)
1627 : : {
1628 : 4119060 : jl_ptls_t ptls = jl_current_task->ptls;
1629 : 4119060 : jl_taggedvalue_t *o = jl_astaggedvalue(ptr);
1630 : : // The modification of the `gc_bits` is not atomic but it
1631 : : // should be safe here since GC is not allowed to run here and we only
1632 : : // write GC_OLD to the GC bits outside GC. This could cause
1633 : : // duplicated objects in the remset but that shouldn't be a problem.
1634 : 4119060 : o->bits.gc = GC_MARKED;
1635 : 4119060 : arraylist_push(ptls->heap.remset, (jl_value_t*)ptr);
1636 : 4119060 : ptls->heap.remset_nptr++; // conservative
1637 : 4119060 : }
1638 : :
1639 : 6441 : void jl_gc_queue_multiroot(const jl_value_t *parent, const jl_value_t *ptr) JL_NOTSAFEPOINT
1640 : : {
1641 : : // first check if this is really necessary
1642 : : // TODO: should we store this info in one of the extra gc bits?
1643 : 6441 : jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(ptr);
1644 : 6441 : const jl_datatype_layout_t *ly = dt->layout;
1645 : 6441 : uint32_t npointers = ly->npointers;
1646 : : //if (npointers == 0) // this was checked by the caller
1647 : : // return;
1648 : 6441 : jl_value_t *ptrf = ((jl_value_t**)ptr)[ly->first_ptr];
1649 [ + - + + ]: 6441 : if (ptrf && (jl_astaggedvalue(ptrf)->bits.gc & 1) == 0) {
1650 : : // this pointer was young, move the barrier back now
1651 : 172 : jl_gc_wb_back(parent);
1652 : 172 : return;
1653 : : }
1654 : 6269 : const uint8_t *ptrs8 = (const uint8_t *)jl_dt_layout_ptrs(ly);
1655 : 6269 : const uint16_t *ptrs16 = (const uint16_t *)jl_dt_layout_ptrs(ly);
1656 : 6269 : const uint32_t *ptrs32 = (const uint32_t*)jl_dt_layout_ptrs(ly);
1657 [ - + ]: 6269 : for (size_t i = 1; i < npointers; i++) {
1658 : : uint32_t fld;
1659 [ # # ]: 0 : if (ly->fielddesc_type == 0) {
1660 : 0 : fld = ptrs8[i];
1661 : : }
1662 [ # # ]: 0 : else if (ly->fielddesc_type == 1) {
1663 : 0 : fld = ptrs16[i];
1664 : : }
1665 : : else {
1666 [ # # ]: 0 : assert(ly->fielddesc_type == 2);
1667 : 0 : fld = ptrs32[i];
1668 : : }
1669 : 0 : jl_value_t *ptrf = ((jl_value_t**)ptr)[fld];
1670 [ # # # # ]: 0 : if (ptrf && (jl_astaggedvalue(ptrf)->bits.gc & 1) == 0) {
1671 : : // this pointer was young, move the barrier back now
1672 : 0 : jl_gc_wb_back(parent);
1673 : 0 : return;
1674 : : }
1675 : : }
1676 : : }
1677 : :
1678 : 38367 : JL_DLLEXPORT void jl_gc_queue_binding(jl_binding_t *bnd)
1679 : : {
1680 : 38367 : jl_ptls_t ptls = jl_current_task->ptls;
1681 : 38367 : jl_taggedvalue_t *buf = jl_astaggedvalue(bnd);
1682 : 38367 : buf->bits.gc = GC_MARKED;
1683 : 38367 : arraylist_push(&ptls->heap.rem_bindings, bnd);
1684 : 38367 : }
1685 : :
1686 : :
1687 : : #ifdef JL_DEBUG_BUILD
1688 : : static void *volatile gc_findval; // for usage from gdb, for finding the gc-root for a value
1689 : : #endif
1690 : :
1691 : : static void *sysimg_base;
1692 : : static void *sysimg_end;
1693 : 566 : void jl_gc_set_permalloc_region(void *start, void *end)
1694 : : {
1695 : 566 : sysimg_base = start;
1696 : 566 : sysimg_end = end;
1697 : 566 : }
1698 : :
1699 : :
1700 : : // Handle the case where the stack is only partially copied.
1701 : 62121300 : STATIC_INLINE uintptr_t gc_get_stack_addr(void *_addr, uintptr_t offset,
1702 : : uintptr_t lb, uintptr_t ub)
1703 : : {
1704 : 62121300 : uintptr_t addr = (uintptr_t)_addr;
1705 [ + - + - ]: 62121300 : if (addr >= lb && addr < ub)
1706 : 62121300 : return addr + offset;
1707 : 0 : return addr;
1708 : : }
1709 : :
1710 : 62121300 : STATIC_INLINE uintptr_t gc_read_stack(void *_addr, uintptr_t offset,
1711 : : uintptr_t lb, uintptr_t ub)
1712 : : {
1713 : 62121300 : uintptr_t real_addr = gc_get_stack_addr(_addr, offset, lb, ub);
1714 : 62121300 : return *(uintptr_t*)real_addr;
1715 : : }
1716 : :
1717 : 0 : JL_NORETURN NOINLINE void gc_assert_datatype_fail(jl_ptls_t ptls, jl_datatype_t *vt,
1718 : : jl_gc_mark_sp_t sp)
1719 : : {
1720 : 0 : jl_safe_printf("GC error (probable corruption) :\n");
1721 : 0 : jl_gc_debug_print_status();
1722 : 0 : jl_(vt);
1723 : 0 : jl_gc_debug_critical_error();
1724 : 0 : gc_mark_loop_unwind(ptls, sp, 0);
1725 : 0 : abort();
1726 : : }
1727 : :
1728 : : // This stores the label address in the mark loop function.
1729 : : // We can't directly store that to a global array so we need some hack to get that.
1730 : : // See the call to `gc_mark_loop` in init with a `NULL` `ptls`.
1731 : : void *gc_mark_label_addrs[_GC_MARK_L_MAX];
1732 : :
1733 : : // Double the local mark stack (both pc and data)
1734 : 166 : static void NOINLINE gc_mark_stack_resize(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp) JL_NOTSAFEPOINT
1735 : : {
1736 : 166 : jl_gc_mark_data_t *old_data = gc_cache->data_stack;
1737 : 166 : void **pc_stack = sp->pc_start;
1738 : 166 : size_t stack_size = (char*)sp->pc_end - (char*)pc_stack;
1739 : 166 : gc_cache->data_stack = (jl_gc_mark_data_t *)realloc_s(old_data, stack_size * 2 * sizeof(jl_gc_mark_data_t));
1740 : 166 : sp->data = (jl_gc_mark_data_t *)(((char*)sp->data) + (((char*)gc_cache->data_stack) - ((char*)old_data)));
1741 : :
1742 : 166 : sp->pc_start = gc_cache->pc_stack = (void**)realloc_s(pc_stack, stack_size * 2 * sizeof(void*));
1743 : 166 : gc_cache->pc_stack_end = sp->pc_end = sp->pc_start + stack_size * 2;
1744 : 166 : sp->pc = sp->pc_start + (sp->pc - pc_stack);
1745 : 166 : }
1746 : :
1747 : : // Push a work item to the stack. The type of the work item is marked with `pc`.
1748 : : // The data needed is in `data` and is of size `data_size`.
1749 : : // If there isn't enough space on the stack, the stack will be resized with the stack
1750 : : // lock held. The caller should invalidate any local cache of the stack addresses that's not
1751 : : // in `gc_cache` or `sp`
1752 : : // The `sp` will be updated on return if `inc` is true.
1753 : 1729670000 : STATIC_INLINE void gc_mark_stack_push(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp,
1754 : : void *pc, void *data, size_t data_size, int inc) JL_NOTSAFEPOINT
1755 : : {
1756 [ - + ]: 1729670000 : assert(data_size <= sizeof(jl_gc_mark_data_t));
1757 [ + + ]: 1729670000 : if (__unlikely(sp->pc == sp->pc_end))
1758 : 166 : gc_mark_stack_resize(gc_cache, sp);
1759 : 1729670000 : *sp->pc = pc;
1760 : 1729670000 : memcpy(sp->data, data, data_size);
1761 [ + + ]: 1729670000 : if (inc) {
1762 : 14474400 : sp->data = (jl_gc_mark_data_t *)(((char*)sp->data) + data_size);
1763 : 14474400 : sp->pc++;
1764 : : }
1765 : 1729670000 : }
1766 : :
1767 : : // Check if the reference is non-NULL and atomically set the mark bit.
1768 : : // Update `*nptr`, which is the `nptr` field of the parent item, if the object is young.
1769 : : // Return the tag (with GC bits cleared) and the GC bits in `*ptag` and `*pbits`.
1770 : : // Return whether the object needs to be scanned / have metadata updated.
1771 :18760600000 : STATIC_INLINE int gc_try_setmark(jl_value_t *obj, uintptr_t *nptr,
1772 : : uintptr_t *ptag, uint8_t *pbits) JL_NOTSAFEPOINT
1773 : : {
1774 [ + + ]:18760600000 : if (!obj)
1775 : 6112410000 : return 0;
1776 :12648200000 : jl_taggedvalue_t *o = jl_astaggedvalue(obj);
1777 :12648200000 : uintptr_t tag = o->header;
1778 [ + + ]:12648200000 : if (!gc_marked(tag)) {
1779 : : uint8_t bits;
1780 : 2009260000 : int res = gc_setmark_tag(o, GC_MARKED, tag, &bits);
1781 [ + + ]: 2009260000 : if (!gc_old(bits))
1782 : 511635000 : *nptr = *nptr | 1;
1783 : 2009260000 : *ptag = tag & ~(uintptr_t)0xf;
1784 : 2009260000 : *pbits = bits;
1785 : 2009260000 : return __likely(res);
1786 : : }
1787 [ + + ]:10639000000 : else if (!gc_old(tag)) {
1788 : 128532000 : *nptr = *nptr | 1;
1789 : : }
1790 :10639000000 : return 0;
1791 : : }
1792 : :
1793 : : // Queue a finalizer list to be scanned in the mark loop. Start marking from index `start`.
1794 : 42150 : void gc_mark_queue_finlist(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp,
1795 : : arraylist_t *list, size_t start)
1796 : : {
1797 : 42150 : size_t len = list->len;
1798 [ + + ]: 42150 : if (len <= start)
1799 : 36964 : return;
1800 : 5186 : jl_value_t **items = (jl_value_t**)list->items;
1801 : 5186 : gc_mark_finlist_t markdata = {items + start, items + len};
1802 : 5186 : gc_mark_stack_push(gc_cache, sp, gc_mark_label_addrs[GC_MARK_L_finlist],
1803 : : &markdata, sizeof(markdata), 1);
1804 : : }
1805 : :
1806 : : // Queue a object to be scanned. The object should already be marked and the GC metadata
1807 : : // should already be updated for it. Only scanning of the object should be performed.
1808 : 11522300 : STATIC_INLINE void gc_mark_queue_scan_obj(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp,
1809 : : jl_value_t *obj)
1810 : : {
1811 : 11522300 : jl_taggedvalue_t *o = jl_astaggedvalue(obj);
1812 : 11522300 : uintptr_t tag = o->header;
1813 : 11522300 : uint8_t bits = tag & 0xf;
1814 : 11522300 : tag = tag & ~(uintptr_t)0xf;
1815 : 11522300 : gc_mark_marked_obj_t data = {obj, tag, bits};
1816 : 11522300 : gc_mark_stack_push(gc_cache, sp, gc_mark_label_addrs[GC_MARK_L_scan_only],
1817 : : &data, sizeof(data), 1);
1818 : 11522300 : }
1819 : :
1820 : : // Mark and queue a object to be scanned.
1821 : : // The object will be marked atomically which can also happen concurrently.
1822 : : // It will be queued if the object wasn't marked already (or concurrently by another thread)
1823 : : // Returns whether the object is young.
1824 : 15300400 : STATIC_INLINE int gc_mark_queue_obj(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, void *_obj) JL_NOTSAFEPOINT
1825 : : {
1826 : 15300400 : jl_value_t *obj = (jl_value_t*)jl_assume(_obj);
1827 : 15300400 : uintptr_t nptr = 0;
1828 : 15300400 : uintptr_t tag = 0;
1829 : 15300400 : uint8_t bits = 0;
1830 [ + + ]: 15300400 : if (!gc_try_setmark(obj, &nptr, &tag, &bits))
1831 : 14724900 : return (int)nptr;
1832 : 575493 : gc_mark_marked_obj_t data = {obj, tag, bits};
1833 : 575493 : gc_mark_stack_push(gc_cache, sp, gc_mark_label_addrs[GC_MARK_L_marked_obj],
1834 : : &data, sizeof(data), 1);
1835 : 575493 : return (int)nptr;
1836 : : }
1837 : :
1838 : 0 : int jl_gc_mark_queue_obj_explicit(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, jl_value_t *obj)
1839 : : {
1840 : 0 : return gc_mark_queue_obj(gc_cache, sp, obj);
1841 : : }
1842 : :
1843 : 0 : JL_DLLEXPORT int jl_gc_mark_queue_obj(jl_ptls_t ptls, jl_value_t *obj)
1844 : : {
1845 : 0 : return gc_mark_queue_obj(&ptls->gc_cache, &ptls->gc_mark_sp, obj);
1846 : : }
1847 : :
1848 : 0 : JL_DLLEXPORT void jl_gc_mark_queue_objarray(jl_ptls_t ptls, jl_value_t *parent,
1849 : : jl_value_t **objs, size_t nobjs)
1850 : : {
1851 : 0 : gc_mark_objarray_t data = { parent, objs, objs + nobjs, 1,
1852 : 0 : jl_astaggedvalue(parent)->bits.gc & 2 };
1853 : 0 : gc_mark_stack_push(&ptls->gc_cache, &ptls->gc_mark_sp,
1854 : : gc_mark_label_addrs[GC_MARK_L_objarray],
1855 : : &data, sizeof(data), 1);
1856 : 0 : }
1857 : :
1858 : :
1859 : : // Check if `nptr` is tagged for `old + refyoung`,
1860 : : // Push the object to the remset and update the `nptr` counter if necessary.
1861 : 1715080000 : STATIC_INLINE void gc_mark_push_remset(jl_ptls_t ptls, jl_value_t *obj, uintptr_t nptr) JL_NOTSAFEPOINT
1862 : : {
1863 [ + + ]: 1715080000 : if (__unlikely((nptr & 0x3) == 0x3)) {
1864 : 8982230 : ptls->heap.remset_nptr += nptr >> 2;
1865 : 8982230 : arraylist_t *remset = ptls->heap.remset;
1866 : 8982230 : size_t len = remset->len;
1867 [ + + ]: 8982230 : if (__unlikely(len >= remset->max)) {
1868 : 1323 : arraylist_push(remset, obj);
1869 : : }
1870 : : else {
1871 : 8980910 : remset->len = len + 1;
1872 : 8980910 : remset->items[len] = obj;
1873 : : }
1874 : : }
1875 : 1715080000 : }
1876 : :
1877 : : // Scan a dense array of object references, see `gc_mark_objarray_t`
1878 : 1218220000 : STATIC_INLINE int gc_mark_scan_objarray(jl_ptls_t ptls, jl_gc_mark_sp_t *sp,
1879 : : gc_mark_objarray_t *objary,
1880 : : jl_value_t **begin, jl_value_t **end,
1881 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits)
1882 : : {
1883 : 1218220000 : (void)jl_assume(objary == (gc_mark_objarray_t*)sp->data);
1884 [ + + ]:13844200000 : for (; begin < end; begin += objary->step) {
1885 :13378700000 : *pnew_obj = *begin;
1886 :13378700000 : if (*pnew_obj)
1887 : : verify_parent2("obj array", objary->parent, begin, "elem(%d)",
1888 : : gc_slot_to_arrayidx(objary->parent, begin));
1889 [ + + ]:13378700000 : if (!gc_try_setmark(*pnew_obj, &objary->nptr, ptag, pbits))
1890 :12625900000 : continue;
1891 : 752806000 : begin += objary->step;
1892 : : // Found an object to mark
1893 [ + + ]: 752806000 : if (begin < end) {
1894 : : // Haven't done with this one yet. Update the content and push it back
1895 : 659594000 : objary->begin = begin;
1896 : 659594000 : gc_repush_markdata(sp, gc_mark_objarray_t);
1897 : : }
1898 : : else {
1899 : : // Finished scanning this one, finish up by checking the GC invariance
1900 : : // and let the next item replacing the current one directly.
1901 : 93211900 : gc_mark_push_remset(ptls, objary->parent, objary->nptr);
1902 : : }
1903 : 752806000 : return 1;
1904 : : }
1905 : 465414000 : gc_mark_push_remset(ptls, objary->parent, objary->nptr);
1906 : 465414000 : return 0;
1907 : : }
1908 : :
1909 : : // Scan a sparse array of object references, see `gc_mark_objarray_t`
1910 : 16265200 : STATIC_INLINE int gc_mark_scan_array8(jl_ptls_t ptls, jl_gc_mark_sp_t *sp,
1911 : : gc_mark_array8_t *ary8,
1912 : : jl_value_t **begin, jl_value_t **end,
1913 : : uint8_t *elem_begin, uint8_t *elem_end,
1914 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits)
1915 : : {
1916 : 16265200 : (void)jl_assume(ary8 == (gc_mark_array8_t*)sp->data);
1917 : 16265200 : size_t elsize = ((jl_array_t*)ary8->elem.parent)->elsize / sizeof(jl_value_t*);
1918 [ + + ]: 40776100 : for (; begin < end; begin += elsize) {
1919 [ + + ]: 111127000 : for (; elem_begin < elem_end; elem_begin++) {
1920 : 86616600 : jl_value_t **slot = &begin[*elem_begin];
1921 : 86616600 : *pnew_obj = *slot;
1922 : 86616600 : if (*pnew_obj)
1923 : : verify_parent2("array", ary8->elem.parent, slot, "elem(%d)",
1924 : : gc_slot_to_arrayidx(ary8->elem.parent, begin));
1925 [ + + ]: 86616600 : if (!gc_try_setmark(*pnew_obj, &ary8->elem.nptr, ptag, pbits))
1926 : 70627500 : continue;
1927 : 15989100 : elem_begin++;
1928 : : // Found an object to mark
1929 [ + + ]: 15989100 : if (elem_begin < elem_end) {
1930 : : // Haven't done with this one yet. Update the content and push it back
1931 : 7697820 : ary8->elem.begin = elem_begin;
1932 : 7697820 : ary8->begin = begin;
1933 : 7697820 : gc_repush_markdata(sp, gc_mark_array8_t);
1934 : : }
1935 : : else {
1936 : 8291270 : begin += elsize;
1937 [ + + ]: 8291270 : if (begin < end) {
1938 : : // Haven't done with this array yet. Reset the content and push it back
1939 : 6692130 : ary8->elem.begin = ary8->rebegin;
1940 : 6692130 : ary8->begin = begin;
1941 : 6692130 : gc_repush_markdata(sp, gc_mark_array8_t);
1942 : : }
1943 : : else {
1944 : : // Finished scanning this one, finish up by checking the GC invariance
1945 : : // and let the next item replacing the current one directly.
1946 : 1599140 : gc_mark_push_remset(ptls, ary8->elem.parent, ary8->elem.nptr);
1947 : : }
1948 : : }
1949 : 15989100 : return 1;
1950 : : }
1951 : 24510800 : elem_begin = ary8->rebegin;
1952 : : }
1953 : 276152 : gc_mark_push_remset(ptls, ary8->elem.parent, ary8->elem.nptr);
1954 : 276152 : return 0;
1955 : : }
1956 : :
1957 : : // Scan a sparse array of object references, see `gc_mark_objarray_t`
1958 : 0 : STATIC_INLINE int gc_mark_scan_array16(jl_ptls_t ptls, jl_gc_mark_sp_t *sp,
1959 : : gc_mark_array16_t *ary16,
1960 : : jl_value_t **begin, jl_value_t **end,
1961 : : uint16_t *elem_begin, uint16_t *elem_end,
1962 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits)
1963 : : {
1964 : 0 : (void)jl_assume(ary16 == (gc_mark_array16_t*)sp->data);
1965 : 0 : size_t elsize = ((jl_array_t*)ary16->elem.parent)->elsize / sizeof(jl_value_t*);
1966 [ # # ]: 0 : for (; begin < end; begin += elsize) {
1967 [ # # ]: 0 : for (; elem_begin < elem_end; elem_begin++) {
1968 : 0 : jl_value_t **slot = &begin[*elem_begin];
1969 : 0 : *pnew_obj = *slot;
1970 : 0 : if (*pnew_obj)
1971 : : verify_parent2("array", ary16->elem.parent, slot, "elem(%d)",
1972 : : gc_slot_to_arrayidx(ary16->elem.parent, begin));
1973 [ # # ]: 0 : if (!gc_try_setmark(*pnew_obj, &ary16->elem.nptr, ptag, pbits))
1974 : 0 : continue;
1975 : 0 : elem_begin++;
1976 : : // Found an object to mark
1977 [ # # ]: 0 : if (elem_begin < elem_end) {
1978 : : // Haven't done with this one yet. Update the content and push it back
1979 : 0 : ary16->elem.begin = elem_begin;
1980 : 0 : ary16->begin = begin;
1981 : 0 : gc_repush_markdata(sp, gc_mark_array16_t);
1982 : : }
1983 : : else {
1984 : 0 : begin += elsize;
1985 [ # # ]: 0 : if (begin < end) {
1986 : : // Haven't done with this array yet. Reset the content and push it back
1987 : 0 : ary16->elem.begin = ary16->rebegin;
1988 : 0 : ary16->begin = begin;
1989 : 0 : gc_repush_markdata(sp, gc_mark_array16_t);
1990 : : }
1991 : : else {
1992 : : // Finished scanning this one, finish up by checking the GC invariance
1993 : : // and let the next item replacing the current one directly.
1994 : 0 : gc_mark_push_remset(ptls, ary16->elem.parent, ary16->elem.nptr);
1995 : : }
1996 : : }
1997 : 0 : return 1;
1998 : : }
1999 : 0 : elem_begin = ary16->rebegin;
2000 : : }
2001 : 0 : gc_mark_push_remset(ptls, ary16->elem.parent, ary16->elem.nptr);
2002 : 0 : return 0;
2003 : : }
2004 : :
2005 : :
2006 : : // Scan an object with 8bits field descriptors. see `gc_mark_obj8_t`
2007 : 2034110000 : STATIC_INLINE int gc_mark_scan_obj8(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj8_t *obj8,
2008 : : char *parent, uint8_t *begin, uint8_t *end,
2009 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits)
2010 : : {
2011 : 2034110000 : (void)jl_assume(obj8 == (gc_mark_obj8_t*)sp->data);
2012 : 2034110000 : (void)jl_assume(begin < end);
2013 [ + + ]: 5967760000 : for (; begin < end; begin++) {
2014 : 5145650000 : jl_value_t **slot = &((jl_value_t**)parent)[*begin];
2015 : 5145650000 : *pnew_obj = *slot;
2016 : 5145650000 : if (*pnew_obj)
2017 : : verify_parent2("object", parent, slot, "field(%d)",
2018 : : gc_slot_to_fieldidx(parent, slot));
2019 [ + + ]: 5145650000 : if (!gc_try_setmark(*pnew_obj, &obj8->nptr, ptag, pbits))
2020 : 3933650000 : continue;
2021 : 1212000000 : begin++;
2022 : : // Found an object to mark
2023 [ + + ]: 1212000000 : if (begin < end) {
2024 : : // Haven't done with this one yet. Update the content and push it back
2025 : 881012000 : obj8->begin = begin;
2026 : 881012000 : gc_repush_markdata(sp, gc_mark_obj8_t);
2027 : : }
2028 : : else {
2029 : : // Finished scanning this one, finish up by checking the GC invariance
2030 : : // and let the next item replacing the current one directly.
2031 : 330987000 : gc_mark_push_remset(ptls, obj8->parent, obj8->nptr);
2032 : : }
2033 : 1212000000 : return 1;
2034 : : }
2035 : 822107000 : gc_mark_push_remset(ptls, obj8->parent, obj8->nptr);
2036 : 822107000 : return 0;
2037 : : }
2038 : :
2039 : : // Scan an object with 16bits field descriptors. see `gc_mark_obj16_t`
2040 : 7120000 : STATIC_INLINE int gc_mark_scan_obj16(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj16_t *obj16,
2041 : : char *parent, uint16_t *begin, uint16_t *end,
2042 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits) JL_NOTSAFEPOINT
2043 : : {
2044 : 7120000 : (void)jl_assume(obj16 == (gc_mark_obj16_t*)sp->data);
2045 : 7120000 : (void)jl_assume(begin < end);
2046 [ + + ]: 9149560 : for (; begin < end; begin++) {
2047 : 7867650 : jl_value_t **slot = &((jl_value_t**)parent)[*begin];
2048 : 7867650 : *pnew_obj = *slot;
2049 : 7867650 : if (*pnew_obj)
2050 : : verify_parent2("object", parent, slot, "field(%d)",
2051 : : gc_slot_to_fieldidx(parent, slot));
2052 [ + + ]: 7867650 : if (!gc_try_setmark(*pnew_obj, &obj16->nptr, ptag, pbits))
2053 : 2029570 : continue;
2054 : 5838080 : begin++;
2055 : : // Found an object to mark
2056 [ + + ]: 5838080 : if (begin < end) {
2057 : : // Haven't done with this one yet. Update the content and push it back
2058 : 5639880 : obj16->begin = begin;
2059 : 5639880 : gc_repush_markdata(sp, gc_mark_obj16_t);
2060 : : }
2061 : : else {
2062 : : // Finished scanning this one, finish up by checking the GC invariance
2063 : : // and let the next item replacing the current one directly.
2064 : 198200 : gc_mark_push_remset(ptls, obj16->parent, obj16->nptr);
2065 : : }
2066 : 5838080 : return 1;
2067 : : }
2068 : 1281910 : gc_mark_push_remset(ptls, obj16->parent, obj16->nptr);
2069 : 1281910 : return 0;
2070 : : }
2071 : :
2072 : : // Scan an object with 32bits field descriptors. see `gc_mark_obj32_t`
2073 : 0 : STATIC_INLINE int gc_mark_scan_obj32(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj32_t *obj32,
2074 : : char *parent, uint32_t *begin, uint32_t *end,
2075 : : jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits)
2076 : : {
2077 : 0 : (void)jl_assume(obj32 == (gc_mark_obj32_t*)sp->data);
2078 : 0 : (void)jl_assume(begin < end);
2079 [ # # ]: 0 : for (; begin < end; begin++) {
2080 : 0 : jl_value_t **slot = &((jl_value_t**)parent)[*begin];
2081 : 0 : *pnew_obj = *slot;
2082 : 0 : if (*pnew_obj)
2083 : : verify_parent2("object", parent, slot, "field(%d)",
2084 : : gc_slot_to_fieldidx(parent, slot));
2085 [ # # ]: 0 : if (!gc_try_setmark(*pnew_obj, &obj32->nptr, ptag, pbits))
2086 : 0 : continue;
2087 : 0 : begin++;
2088 : : // Found an object to mark
2089 [ # # ]: 0 : if (begin < end) {
2090 : : // Haven't done with this one yet. Update the content and push it back
2091 : 0 : obj32->begin = begin;
2092 : 0 : gc_repush_markdata(sp, gc_mark_obj32_t);
2093 : : }
2094 : : else {
2095 : : // Finished scanning this one, finish up by checking the GC invariance
2096 : : // and let the next item replacing the current one directly.
2097 : 0 : gc_mark_push_remset(ptls, obj32->parent, obj32->nptr);
2098 : : }
2099 : 0 : return 1;
2100 : : }
2101 : 0 : gc_mark_push_remset(ptls, obj32->parent, obj32->nptr);
2102 : 0 : return 0;
2103 : : }
2104 : :
2105 : : #if defined(__GNUC__) && !defined(_OS_EMSCRIPTEN_)
2106 : : # define gc_mark_laddr(name) (&&name)
2107 : : # define gc_mark_jmp(ptr) goto *(ptr)
2108 : : #else
2109 : : #define gc_mark_laddr(name) ((void*)(uintptr_t)GC_MARK_L_##name)
2110 : : #define gc_mark_jmp(ptr) do { \
2111 : : switch ((int)(uintptr_t)ptr) { \
2112 : : case GC_MARK_L_marked_obj: \
2113 : : goto marked_obj; \
2114 : : case GC_MARK_L_scan_only: \
2115 : : goto scan_only; \
2116 : : case GC_MARK_L_finlist: \
2117 : : goto finlist; \
2118 : : case GC_MARK_L_objarray: \
2119 : : goto objarray; \
2120 : : case GC_MARK_L_array8: \
2121 : : goto array8; \
2122 : : case GC_MARK_L_array16: \
2123 : : goto array16; \
2124 : : case GC_MARK_L_obj8: \
2125 : : goto obj8; \
2126 : : case GC_MARK_L_obj16: \
2127 : : goto obj16; \
2128 : : case GC_MARK_L_obj32: \
2129 : : goto obj32; \
2130 : : case GC_MARK_L_stack: \
2131 : : goto stack; \
2132 : : case GC_MARK_L_excstack: \
2133 : : goto excstack; \
2134 : : case GC_MARK_L_module_binding: \
2135 : : goto module_binding; \
2136 : : default: \
2137 : : abort(); \
2138 : : } \
2139 : : } while (0)
2140 : : #endif
2141 : :
2142 : : // This is the main marking loop.
2143 : : // It uses an iterative (mostly) Depth-first search (DFS) to mark all the objects.
2144 : : // Instead of using the native stack, two stacks are manually maintained,
2145 : : // one (fixed-size) pc stack which stores the return address and one (variable-size)
2146 : : // data stack which stores the local variables needed by the scanning code.
2147 : : // Using a manually maintained stack has a few advantages
2148 : : //
2149 : : // 1. We can resize the stack as we go and never worry about stack overflow
2150 : : // This is especitally useful when enters the GC in a deep call stack.
2151 : : // It also removes the very deep GC call stack in a profile.
2152 : : // 2. We can minimize the number of local variables to save on the stack.
2153 : : // This includes minimizing the sizes of the stack frames and only saving variables
2154 : : // that have been changed before making "function calls" (i.e. `goto mark;`)
2155 : : // 3. We can perform end-of-loop tail-call optimization for common cases.
2156 : : // 4. The marking can be interrupted more easily since all the states are maintained
2157 : : // in a well-defined format already.
2158 : : // This will be useful if we want to have incremental marking again.
2159 : : // 5. The frames can be stolen by another thread more easily and it is not necessary
2160 : : // to copy works to be stolen to another queue. Useful for parallel marking.
2161 : : // (Will still require synchronization in stack popping of course.)
2162 : : // 6. A flat function (i.e. no or very few function calls) also give the compiler
2163 : : // opportunity to keep more states in registers that doesn't have to be spilled as often.
2164 : : //
2165 : : // We use two stacks so that the thief on another thread can steal the fixed sized pc stack
2166 : : // and use that to figure out the size of the struct on the variable size data stack.
2167 : : //
2168 : : // The main disadvantages are that we bypass some stack-based CPU optimizations including the
2169 : : // stack engine and return address prediction.
2170 : : // Using two stacks also double the number of operations on the stack pointer
2171 : : // though we still only need to use one of them (the pc stack pointer) for bounds check.
2172 : : // In general, it seems that the reduction of stack memory ops and instructions count
2173 : : // have a larger positive effect on the performance. =)
2174 : :
2175 : : // As a general guide we do not want to make non-inlined function calls in this function
2176 : : // if possible since a large number of registers has to be spilled when that happens.
2177 : : // This is especially true on on X86 which doesn't have many (any?)
2178 : : // callee saved general purpose registers.
2179 : : // (OTOH, the spill will likely make use of the stack engine which is otherwise idle so
2180 : : // the performance impact is minimum as long as it's not in the hottest path)
2181 : :
2182 : : // There are three external entry points to the loop, corresponding to label
2183 : : // `marked_obj`, `scan_only` and `finlist` (see the corresponding functions
2184 : : // `gc_mark_queue_obj`, `gc_mark_queue_scan_obj` and `gc_mark_queue_finlist` above).
2185 : : // The scanning of the object starts with `goto mark`, which updates the metadata and scans
2186 : : // the object whose information is stored in `new_obj`, `tag` and `bits`.
2187 : : // The branches in `mark` will dispatch the object to one of the scan "loop"s to be scanned
2188 : : // as either a normal julia object or one of the special objects with specific storage format.
2189 : : // Each of the scan "loop" will perform a DFS of the object in the following way
2190 : : //
2191 : : // 1. When encountering an pointer (julia object reference) slots, load, perform NULL check
2192 : : // and atomically set the mark bits to determine if the object needs to be scanned.
2193 : : // 2. If yes, it'll push itself back onto the mark stack (after updating fields that are changed)
2194 : : // using `gc_repush_markdata` to increment the stack pointers.
2195 : : // This step can also be replaced by a tail call by finishing up the marking of the current
2196 : : // object when the end of the current object is reached.
2197 : : // 3. Jump to `mark`. The marking of the current object will be resumed after the child is
2198 : : // scanned by popping the stack frame back.
2199 : : //
2200 : : // Some of the special object scannings use BFS to simplify the code (Task and Module).
2201 : :
2202 : : // The jumps from the dispatch to the scan "loop"s are done by first pushing a frame
2203 : : // to the stacks while only increment the data stack pointer before jumping to the loop
2204 : : // This way the scan "loop" gets exactly what it expects after a stack pop.
2205 : : // Additional optimizations are done for some of the common cases by skipping
2206 : : // the unnecessary data stack pointer increment and the load from the stack
2207 : : // (i.e. store to load forwaring). See `objary_loaded`, `obj8_loaded` and `obj16_loaded`.
2208 : 42645 : JL_EXTENSION NOINLINE void gc_mark_loop(jl_ptls_t ptls, jl_gc_mark_sp_t sp)
2209 : : {
2210 [ + + ]: 42645 : if (__unlikely(ptls == NULL)) {
2211 : 573 : gc_mark_label_addrs[GC_MARK_L_marked_obj] = gc_mark_laddr(marked_obj);
2212 : 573 : gc_mark_label_addrs[GC_MARK_L_scan_only] = gc_mark_laddr(scan_only);
2213 : 573 : gc_mark_label_addrs[GC_MARK_L_finlist] = gc_mark_laddr(finlist);
2214 : 573 : gc_mark_label_addrs[GC_MARK_L_objarray] = gc_mark_laddr(objarray);
2215 : 573 : gc_mark_label_addrs[GC_MARK_L_array8] = gc_mark_laddr(array8);
2216 : 573 : gc_mark_label_addrs[GC_MARK_L_array16] = gc_mark_laddr(array16);
2217 : 573 : gc_mark_label_addrs[GC_MARK_L_obj8] = gc_mark_laddr(obj8);
2218 : 573 : gc_mark_label_addrs[GC_MARK_L_obj16] = gc_mark_laddr(obj16);
2219 : 573 : gc_mark_label_addrs[GC_MARK_L_obj32] = gc_mark_laddr(obj32);
2220 : 573 : gc_mark_label_addrs[GC_MARK_L_stack] = gc_mark_laddr(stack);
2221 : 573 : gc_mark_label_addrs[GC_MARK_L_excstack] = gc_mark_laddr(excstack);
2222 : 573 : gc_mark_label_addrs[GC_MARK_L_module_binding] = gc_mark_laddr(module_binding);
2223 : 573 : return;
2224 : : }
2225 : :
2226 : 42072 : jl_value_t *new_obj = NULL;
2227 : 42072 : uintptr_t tag = 0;
2228 : 42072 : uint8_t bits = 0;
2229 : 42072 : int meta_updated = 0;
2230 : :
2231 : : gc_mark_objarray_t *objary;
2232 : : jl_value_t **objary_begin;
2233 : : jl_value_t **objary_end;
2234 : :
2235 : : gc_mark_array8_t *ary8;
2236 : : gc_mark_array16_t *ary16;
2237 : :
2238 : : gc_mark_obj8_t *obj8;
2239 : : char *obj8_parent;
2240 : : uint8_t *obj8_begin;
2241 : : uint8_t *obj8_end;
2242 : :
2243 : : gc_mark_obj16_t *obj16;
2244 : : char *obj16_parent;
2245 : : uint16_t *obj16_begin;
2246 : : uint16_t *obj16_end;
2247 : :
2248 : 1595220000 : pop:
2249 [ + + ]: 1595220000 : if (sp.pc == sp.pc_start) {
2250 : : // TODO: stealing form another thread
2251 : 42072 : return;
2252 : : }
2253 : 1595180000 : sp.pc--;
2254 : 1595180000 : gc_mark_jmp(*sp.pc); // computed goto
2255 : :
2256 : 2484010 : marked_obj: {
2257 : : // An object that has been marked and needs have metadata updated and scanned.
2258 : 2484010 : gc_mark_marked_obj_t *obj = gc_pop_markdata(&sp, gc_mark_marked_obj_t);
2259 : 2484010 : new_obj = obj->obj;
2260 : 2484010 : tag = obj->tag;
2261 : 2484010 : bits = obj->bits;
2262 : 2484010 : goto mark;
2263 : : }
2264 : :
2265 : 11522300 : scan_only: {
2266 : : // An object that has been marked and needs to be scanned.
2267 : 11522300 : gc_mark_marked_obj_t *obj = gc_pop_markdata(&sp, gc_mark_marked_obj_t);
2268 : 11522300 : new_obj = obj->obj;
2269 : 11522300 : tag = obj->tag;
2270 : 11522300 : bits = obj->bits;
2271 : 11522300 : meta_updated = 1;
2272 : 11522300 : goto mark;
2273 : : }
2274 : :
2275 : 659596000 : objarray:
2276 : 659596000 : objary = gc_pop_markdata(&sp, gc_mark_objarray_t);
2277 : 659596000 : objary_begin = objary->begin;
2278 : 659596000 : objary_end = objary->end;
2279 : 1218220000 : objarray_loaded:
2280 [ + + ]: 1218220000 : if (gc_mark_scan_objarray(ptls, &sp, objary, objary_begin, objary_end,
2281 : : &new_obj, &tag, &bits))
2282 : 752806000 : goto mark;
2283 : 465414000 : goto pop;
2284 : :
2285 : 14389900 : array8:
2286 : 14389900 : ary8 = gc_pop_markdata(&sp, gc_mark_array8_t);
2287 : 14389900 : objary_begin = ary8->begin;
2288 : 14389900 : objary_end = ary8->end;
2289 : 14389900 : obj8_begin = ary8->elem.begin;
2290 : 14389900 : obj8_end = ary8->elem.end;
2291 : 16265200 : array8_loaded:
2292 [ + + ]: 16265200 : if (gc_mark_scan_array8(ptls, &sp, ary8, objary_begin, objary_end, obj8_begin, obj8_end,
2293 : : &new_obj, &tag, &bits))
2294 : 15989100 : goto mark;
2295 : 276152 : goto pop;
2296 : :
2297 : 0 : array16:
2298 : 0 : ary16 = gc_pop_markdata(&sp, gc_mark_array16_t);
2299 : 0 : objary_begin = ary16->begin;
2300 : 0 : objary_end = ary16->end;
2301 : 0 : obj16_begin = ary16->elem.begin;
2302 : 0 : obj16_end = ary16->elem.end;
2303 : 0 : array16_loaded:
2304 [ # # ]: 0 : if (gc_mark_scan_array16(ptls, &sp, ary16, objary_begin, objary_end, obj16_begin, obj16_end,
2305 : : &new_obj, &tag, &bits))
2306 : 0 : goto mark;
2307 : 0 : goto pop;
2308 : :
2309 : 881012000 : obj8:
2310 : 881012000 : obj8 = gc_pop_markdata(&sp, gc_mark_obj8_t);
2311 : 881012000 : obj8_parent = (char*)obj8->parent;
2312 : 881012000 : obj8_begin = obj8->begin;
2313 : 881012000 : obj8_end = obj8->end;
2314 : 2034110000 : obj8_loaded:
2315 [ + + ]: 2034110000 : if (gc_mark_scan_obj8(ptls, &sp, obj8, obj8_parent, obj8_begin, obj8_end,
2316 : : &new_obj, &tag, &bits))
2317 : 1212000000 : goto mark;
2318 : 822107000 : goto pop;
2319 : :
2320 : 5639880 : obj16:
2321 : 5639880 : obj16 = gc_pop_markdata(&sp, gc_mark_obj16_t);
2322 : 5639880 : obj16_parent = (char*)obj16->parent;
2323 : 5639880 : obj16_begin = obj16->begin;
2324 : 5639880 : obj16_end = obj16->end;
2325 : 7120000 : obj16_loaded:
2326 [ + + ]: 7120000 : if (gc_mark_scan_obj16(ptls, &sp, obj16, obj16_parent, obj16_begin, obj16_end,
2327 : : &new_obj, &tag, &bits))
2328 : 5838080 : goto mark;
2329 : 1281910 : goto pop;
2330 : :
2331 : 0 : obj32: {
2332 : 0 : gc_mark_obj32_t *obj32 = gc_pop_markdata(&sp, gc_mark_obj32_t);
2333 : 0 : char *parent = (char*)obj32->parent;
2334 : 0 : uint32_t *begin = obj32->begin;
2335 : 0 : uint32_t *end = obj32->end;
2336 [ # # ]: 0 : if (gc_mark_scan_obj32(ptls, &sp, obj32, parent, begin, end, &new_obj, &tag, &bits))
2337 : 0 : goto mark;
2338 : 0 : goto pop;
2339 : : }
2340 : :
2341 : 2185480 : stack: {
2342 : : // Scan the stack. see `gc_mark_stackframe_t`
2343 : : // The task object this stack belongs to is being scanned separately as a normal
2344 : : // 8bit field descriptor object.
2345 : 2185480 : gc_mark_stackframe_t *stack = gc_pop_markdata(&sp, gc_mark_stackframe_t);
2346 : 2185480 : jl_gcframe_t *s = stack->s;
2347 : 2185480 : uint32_t i = stack->i;
2348 : 2185480 : uint32_t nroots = stack->nroots;
2349 : 2185480 : uintptr_t offset = stack->offset;
2350 : 2185480 : uintptr_t lb = stack->lb;
2351 : 2185480 : uintptr_t ub = stack->ub;
2352 : 2185480 : uint32_t nr = nroots >> 2;
2353 : 2185480 : uintptr_t nptr = 0;
2354 : 3008170 : while (1) {
2355 : 5193650 : jl_value_t ***rts = (jl_value_t***)(((void**)s) + 2);
2356 [ + + ]: 58295000 : for (; i < nr; i++) {
2357 [ + + ]: 54967200 : if (nroots & 1) {
2358 : 181132 : void **slot = (void**)gc_read_stack(&rts[i], offset, lb, ub);
2359 : 181132 : new_obj = (jl_value_t*)gc_read_stack(slot, offset, lb, ub);
2360 : : }
2361 : : else {
2362 : 54786100 : new_obj = (jl_value_t*)gc_read_stack(&rts[i], offset, lb, ub);
2363 [ - + ]: 54786100 : if (gc_ptr_tag(new_obj, 1)) {
2364 : : // handle tagged pointers in finalizer list
2365 : 0 : new_obj = gc_ptr_clear_tag(new_obj, 1);
2366 : 0 : i++;
2367 : : }
2368 : : }
2369 [ + + ]: 54967200 : if (!gc_try_setmark(new_obj, &nptr, &tag, &bits))
2370 : 53101300 : continue;
2371 : 1865890 : i++;
2372 [ + + ]: 1865890 : if (i < nr) {
2373 : : // Haven't done with this one yet. Update the content and push it back
2374 : 1707170 : stack->i = i;
2375 : 1707170 : gc_repush_markdata(&sp, gc_mark_stackframe_t);
2376 : : }
2377 [ + + ]: 158727 : else if ((s = (jl_gcframe_t*)gc_read_stack(&s->prev, offset, lb, ub))) {
2378 : 93088 : stack->s = s;
2379 : 93088 : stack->i = 0;
2380 : 93088 : uintptr_t new_nroots = gc_read_stack(&s->nroots, offset, lb, ub);
2381 [ - + ]: 93088 : assert(new_nroots <= UINT32_MAX);
2382 : 93088 : stack->nroots = (uint32_t)new_nroots;
2383 : 93088 : gc_repush_markdata(&sp, gc_mark_stackframe_t);
2384 : : }
2385 : 1865890 : goto mark;
2386 : : }
2387 : 3327750 : s = (jl_gcframe_t*)gc_read_stack(&s->prev, offset, lb, ub);
2388 [ + + ]: 3327750 : if (s != 0) {
2389 : 3008170 : stack->s = s;
2390 : 3008170 : i = 0;
2391 : 3008170 : uintptr_t new_nroots = gc_read_stack(&s->nroots, offset, lb, ub);
2392 [ - + ]: 3008170 : assert(new_nroots <= UINT32_MAX);
2393 : 3008170 : nroots = stack->nroots = (uint32_t)new_nroots;
2394 : 3008170 : nr = nroots >> 2;
2395 : 3008170 : continue;
2396 : : }
2397 : 319584 : goto pop;
2398 : : }
2399 : : }
2400 : :
2401 : 77758 : excstack: {
2402 : : // Scan an exception stack
2403 : 77758 : gc_mark_excstack_t *stackitr = gc_pop_markdata(&sp, gc_mark_excstack_t);
2404 : 77758 : jl_excstack_t *excstack = stackitr->s;
2405 : 77758 : size_t itr = stackitr->itr;
2406 : 77758 : size_t bt_index = stackitr->bt_index;
2407 : 77758 : size_t jlval_index = stackitr->jlval_index;
2408 [ + + ]: 78287 : while (itr > 0) {
2409 : 639 : size_t bt_size = jl_excstack_bt_size(excstack, itr);
2410 : 639 : jl_bt_element_t *bt_data = jl_excstack_bt_data(excstack, itr);
2411 [ + + ]: 12838 : for (; bt_index < bt_size; bt_index += jl_bt_entry_size(bt_data + bt_index)) {
2412 : 12249 : jl_bt_element_t *bt_entry = bt_data + bt_index;
2413 [ + + ]: 12249 : if (jl_bt_is_native(bt_entry))
2414 : 12143 : continue;
2415 : : // Found an extended backtrace entry: iterate over any
2416 : : // GC-managed values inside.
2417 : 106 : size_t njlvals = jl_bt_num_jlvals(bt_entry);
2418 [ + + ]: 168 : while (jlval_index < njlvals) {
2419 : 112 : new_obj = jl_bt_entry_jlvalue(bt_entry, jlval_index);
2420 : 112 : uintptr_t nptr = 0;
2421 : 112 : jlval_index += 1;
2422 [ + + ]: 112 : if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) {
2423 : 50 : stackitr->itr = itr;
2424 : 50 : stackitr->bt_index = bt_index;
2425 : 50 : stackitr->jlval_index = jlval_index;
2426 : 50 : gc_repush_markdata(&sp, gc_mark_excstack_t);
2427 : 50 : goto mark;
2428 : : }
2429 : : }
2430 : 56 : jlval_index = 0;
2431 : : }
2432 : : // The exception comes last - mark it
2433 : 589 : new_obj = jl_excstack_exception(excstack, itr);
2434 : 589 : itr = jl_excstack_next(excstack, itr);
2435 : 589 : bt_index = 0;
2436 : 589 : jlval_index = 0;
2437 : 589 : uintptr_t nptr = 0;
2438 [ + + ]: 589 : if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) {
2439 : 60 : stackitr->itr = itr;
2440 : 60 : stackitr->bt_index = bt_index;
2441 : 60 : stackitr->jlval_index = jlval_index;
2442 : 60 : gc_repush_markdata(&sp, gc_mark_excstack_t);
2443 : 60 : goto mark;
2444 : : }
2445 : : }
2446 : 77648 : goto pop;
2447 : : }
2448 : :
2449 : 16978400 : module_binding: {
2450 : : // Scan a module. see `gc_mark_binding_t`
2451 : : // Other fields of the module will be scanned after the bindings are scanned
2452 : 16978400 : gc_mark_binding_t *binding = gc_pop_markdata(&sp, gc_mark_binding_t);
2453 : 16978400 : jl_binding_t **begin = binding->begin;
2454 : 16978400 : jl_binding_t **end = binding->end;
2455 : 16978400 : uint8_t mbits = binding->bits;
2456 [ + + ]: 109603000 : for (; begin < end; begin += 2) {
2457 : 109481000 : jl_binding_t *b = *begin;
2458 [ + + ]: 109481000 : if (b == (jl_binding_t*)HT_NOTFOUND)
2459 : 70000300 : continue;
2460 [ + + + + ]: 73285400 : if ((void*)b >= sysimg_base && (void*)b < sysimg_end) {
2461 : 33804400 : jl_taggedvalue_t *buf = jl_astaggedvalue(b);
2462 : 33804400 : uintptr_t tag = buf->header;
2463 : : uint8_t bits;
2464 [ + + ]: 33804400 : if (!gc_marked(tag))
2465 : 21996700 : gc_setmark_tag(buf, GC_OLD_MARKED, tag, &bits);
2466 : : }
2467 : : else {
2468 : 5676460 : gc_setmark_buf_(ptls, b, mbits, sizeof(jl_binding_t));
2469 : : }
2470 : 39480900 : void *vb = jl_astaggedvalue(b);
2471 : : verify_parent1("module", binding->parent, &vb, "binding_buff");
2472 : : (void)vb;
2473 : 39480900 : jl_value_t *value = jl_atomic_load_relaxed(&b->value);
2474 : 39480900 : jl_value_t *globalref = jl_atomic_load_relaxed(&b->globalref);
2475 [ + + ]: 39480900 : if (value) {
2476 : : verify_parent2("module", binding->parent,
2477 : : &b->value, "binding(%s)", jl_symbol_name(b->name));
2478 [ + + ]: 29732400 : if (gc_try_setmark(value, &binding->nptr, &tag, &bits)) {
2479 : 6702450 : new_obj = value;
2480 : 6702450 : begin += 2;
2481 : 6702450 : binding->begin = begin;
2482 : 6702450 : gc_repush_markdata(&sp, gc_mark_binding_t);
2483 : : uintptr_t gr_tag;
2484 : : uint8_t gr_bits;
2485 [ + + ]: 6702450 : if (gc_try_setmark(globalref, &binding->nptr, &gr_tag, &gr_bits)) {
2486 : 1908510 : gc_mark_marked_obj_t data = {globalref, gr_tag, gr_bits};
2487 : 1908510 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(marked_obj),
2488 : : &data, sizeof(data), 1);
2489 : : }
2490 : 6702450 : goto mark;
2491 : : }
2492 : : }
2493 [ + + ]: 32778500 : if (gc_try_setmark(globalref, &binding->nptr, &tag, &bits)) {
2494 : 10154500 : begin += 2;
2495 : 10154500 : binding->begin = begin;
2496 : 10154500 : gc_repush_markdata(&sp, gc_mark_binding_t);
2497 : 10154500 : new_obj = globalref;
2498 : 10154500 : goto mark;
2499 : : }
2500 : : }
2501 : 121422 : jl_module_t *m = binding->parent;
2502 : 121422 : int scanparent = gc_try_setmark((jl_value_t*)m->parent, &binding->nptr, &tag, &bits);
2503 : 121422 : size_t nusings = m->usings.len;
2504 [ + + ]: 121422 : if (nusings) {
2505 : : // this is only necessary because bindings for "using" modules
2506 : : // are added only when accessed. therefore if a module is replaced
2507 : : // after "using" it but before accessing it, this array might
2508 : : // contain the only reference.
2509 : 120710 : objary_begin = (jl_value_t**)m->usings.items;
2510 : 120710 : objary_end = objary_begin + nusings;
2511 : 120710 : gc_mark_objarray_t data = {(jl_value_t*)m, objary_begin, objary_end, 1, binding->nptr};
2512 : 120710 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(objarray),
2513 : : &data, sizeof(data), 0);
2514 [ + + ]: 120710 : if (!scanparent) {
2515 : 118721 : objary = (gc_mark_objarray_t*)sp.data;
2516 : 118721 : goto objarray_loaded;
2517 : : }
2518 : 1989 : sp.data = (jl_gc_mark_data_t *)(((char*)sp.data) + sizeof(data));
2519 : 1989 : sp.pc++;
2520 : : }
2521 : : else {
2522 : 712 : gc_mark_push_remset(ptls, (jl_value_t*)m, binding->nptr);
2523 : : }
2524 [ + + ]: 2701 : if (scanparent) {
2525 : 1989 : new_obj = (jl_value_t*)m->parent;
2526 : 1989 : goto mark;
2527 : : }
2528 : 712 : goto pop;
2529 : : }
2530 : :
2531 : 1418760 : finlist: {
2532 : : // Scan a finalizer (or format compatible) list. see `gc_mark_finlist_t`
2533 : 1418760 : gc_mark_finlist_t *finlist = gc_pop_markdata(&sp, gc_mark_finlist_t);
2534 : 1418760 : jl_value_t **begin = finlist->begin;
2535 : 1418760 : jl_value_t **end = finlist->end;
2536 [ + + ]: 2136540 : for (; begin < end; begin++) {
2537 : 2132250 : new_obj = *begin;
2538 [ - + ]: 2132250 : if (__unlikely(!new_obj))
2539 : 717778 : continue;
2540 [ + + ]: 2132250 : if (gc_ptr_tag(new_obj, 1)) {
2541 : 1350490 : new_obj = (jl_value_t*)gc_ptr_clear_tag(new_obj, 1);
2542 : 1350490 : begin++;
2543 [ - + ]: 1350490 : assert(begin < end);
2544 : : }
2545 : 2132250 : uintptr_t nptr = 0;
2546 [ + + ]: 2132250 : if (!gc_try_setmark(new_obj, &nptr, &tag, &bits))
2547 : 717778 : continue;
2548 : 1414470 : begin++;
2549 : : // Found an object to mark
2550 [ + + ]: 1414470 : if (begin < end) {
2551 : : // Haven't done with this one yet. Update the content and push it back
2552 : 1413580 : finlist->begin = begin;
2553 : 1413580 : gc_repush_markdata(&sp, gc_mark_finlist_t);
2554 : : }
2555 : 1414470 : goto mark;
2556 : : }
2557 : 4289 : goto pop;
2558 : : }
2559 : :
2560 : 17286800 : mark: {
2561 : : // Generic scanning entry point.
2562 : : // Expects `new_obj`, `tag` and `bits` to be set correctly.
2563 : : #ifdef JL_DEBUG_BUILD
2564 [ - + ]: 2020780000 : if (new_obj == gc_findval)
2565 : 0 : jl_raise_debugger();
2566 : : #endif
2567 : 2020780000 : jl_taggedvalue_t *o = jl_astaggedvalue(new_obj);
2568 : 2020780000 : jl_datatype_t *vt = (jl_datatype_t*)tag;
2569 : 2020780000 : int foreign_alloc = 0;
2570 : 2020780000 : int update_meta = __likely(!meta_updated && !gc_verifying);
2571 [ + + + + : 2020780000 : if (update_meta && (void*)o >= sysimg_base && (void*)o < sysimg_end) {
+ + ]
2572 : 694627000 : foreign_alloc = 1;
2573 : 694627000 : update_meta = 0;
2574 : : }
2575 : 2020780000 : meta_updated = 0;
2576 : : // Symbols are always marked
2577 [ - + ]: 2020780000 : assert(vt != jl_symbol_type);
2578 [ + + ]: 2020780000 : if (vt == jl_simplevector_type) {
2579 : 278916000 : size_t l = jl_svec_len(new_obj);
2580 : 278916000 : jl_value_t **data = jl_svec_data(new_obj);
2581 : 278916000 : size_t dtsz = l * sizeof(void*) + sizeof(jl_svec_t);
2582 [ + + ]: 278916000 : if (update_meta)
2583 : 149817000 : gc_setmark(ptls, o, bits, dtsz);
2584 [ + + ]: 129100000 : else if (foreign_alloc)
2585 : 127237000 : objprofile_count(vt, bits == GC_OLD_MARKED, dtsz);
2586 : 278916000 : uintptr_t nptr = (l << 2) | (bits & GC_OLD);
2587 : 278916000 : objary_begin = data;
2588 : 278916000 : objary_end = data + l;
2589 : 278916000 : gc_mark_objarray_t markdata = {new_obj, objary_begin, objary_end, 1, nptr};
2590 : 278916000 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(objarray),
2591 : : &markdata, sizeof(markdata), 0);
2592 : 278916000 : objary = (gc_mark_objarray_t*)sp.data;
2593 : 278916000 : goto objarray_loaded;
2594 : : }
2595 [ + + ]: 1741860000 : else if (vt->name == jl_array_typename) {
2596 : 462983000 : jl_array_t *a = (jl_array_t*)new_obj;
2597 : 462983000 : jl_array_flags_t flags = a->flags;
2598 [ + + ]: 462983000 : if (update_meta) {
2599 [ + + ]: 318336000 : if (flags.pooled)
2600 : 317567000 : gc_setmark_pool(ptls, o, bits);
2601 : : else
2602 : 769336 : gc_setmark_big(ptls, o, bits);
2603 : : }
2604 [ + + ]: 144647000 : else if (foreign_alloc)
2605 : 137802000 : objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_array_t));
2606 [ + + ]: 462983000 : if (flags.how == 1) {
2607 : 52492000 : void *val_buf = jl_astaggedvalue((char*)a->data - a->offset * a->elsize);
2608 : : verify_parent1("array", new_obj, &val_buf, "buffer ('loc' addr is meaningless)");
2609 : : (void)val_buf;
2610 : 52492000 : gc_setmark_buf_(ptls, (char*)a->data - a->offset * a->elsize,
2611 : : bits, jl_array_nbytes(a));
2612 : : }
2613 [ + + ]: 410491000 : else if (flags.how == 2) {
2614 [ + + - + ]: 57619600 : if (update_meta || foreign_alloc) {
2615 : 57481800 : objprofile_count(jl_malloc_tag, bits == GC_OLD_MARKED,
2616 : 57481800 : jl_array_nbytes(a));
2617 [ + + ]: 57481800 : if (bits == GC_OLD_MARKED) {
2618 : 53958700 : ptls->gc_cache.perm_scanned_bytes += jl_array_nbytes(a);
2619 : : }
2620 : : else {
2621 : 3523090 : ptls->gc_cache.scanned_bytes += jl_array_nbytes(a);
2622 : : }
2623 : : }
2624 : : }
2625 [ + + ]: 352871000 : else if (flags.how == 3) {
2626 : 4359 : jl_value_t *owner = jl_array_data_owner(a);
2627 : 4359 : uintptr_t nptr = (1 << 2) | (bits & GC_OLD);
2628 : 4359 : int markowner = gc_try_setmark(owner, &nptr, &tag, &bits);
2629 : 4359 : gc_mark_push_remset(ptls, new_obj, nptr);
2630 [ + + ]: 4359 : if (markowner) {
2631 : 2538 : new_obj = owner;
2632 : 2538 : goto mark;
2633 : : }
2634 : 1821 : goto pop;
2635 : : }
2636 [ + - + + ]: 462979000 : if (a->data == NULL || jl_array_len(a) == 0)
2637 : 9324440 : goto pop;
2638 [ + + ]: 453654000 : if (flags.ptrarray) {
2639 [ + + ]: 284269000 : if ((jl_datatype_t*)jl_tparam0(vt) == jl_symbol_type)
2640 : 5786940 : goto pop;
2641 : 278483000 : size_t l = jl_array_len(a);
2642 : 278483000 : uintptr_t nptr = (l << 2) | (bits & GC_OLD);
2643 : 278483000 : objary_begin = (jl_value_t**)a->data;
2644 : 278483000 : objary_end = objary_begin + l;
2645 : 278483000 : gc_mark_objarray_t markdata = {new_obj, objary_begin, objary_end, 1, nptr};
2646 : 278483000 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(objarray),
2647 : : &markdata, sizeof(markdata), 0);
2648 : 278483000 : objary = (gc_mark_objarray_t*)sp.data;
2649 : 278483000 : goto objarray_loaded;
2650 : : }
2651 [ + + ]: 169385000 : else if (flags.hasptr) {
2652 : 2981040 : jl_datatype_t *et = (jl_datatype_t*)jl_tparam0(vt);
2653 : 2981040 : const jl_datatype_layout_t *layout = et->layout;
2654 : 2981040 : unsigned npointers = layout->npointers;
2655 : 2981040 : unsigned elsize = a->elsize / sizeof(jl_value_t*);
2656 : 2981040 : size_t l = jl_array_len(a);
2657 : 2981040 : uintptr_t nptr = ((l * npointers) << 2) | (bits & GC_OLD);
2658 : 2981040 : objary_begin = (jl_value_t**)a->data;
2659 : 2981040 : objary_end = objary_begin + l * elsize;
2660 [ + + ]: 2981040 : if (npointers == 1) { // TODO: detect anytime time stride is uniform?
2661 : 1105750 : objary_begin += layout->first_ptr;
2662 : 1105750 : gc_mark_objarray_t markdata = {new_obj, objary_begin, objary_end, elsize, nptr};
2663 : 1105750 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(objarray),
2664 : : &markdata, sizeof(markdata), 0);
2665 : 1105750 : objary = (gc_mark_objarray_t*)sp.data;
2666 : 1105750 : goto objarray_loaded;
2667 : : }
2668 [ + - ]: 1875300 : else if (layout->fielddesc_type == 0) {
2669 : 1875300 : obj8_begin = (uint8_t*)jl_dt_layout_ptrs(layout);
2670 : 1875300 : obj8_end = obj8_begin + npointers;
2671 : 1875300 : gc_mark_array8_t markdata = {objary_begin, objary_end, obj8_begin, {new_obj, obj8_begin, obj8_end, nptr}};
2672 : 1875300 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(array8),
2673 : : &markdata, sizeof(markdata), 0);
2674 : 1875300 : ary8 = (gc_mark_array8_t*)sp.data;
2675 : 1875300 : goto array8_loaded;
2676 : : }
2677 [ # # ]: 0 : else if (layout->fielddesc_type == 1) {
2678 : 0 : obj16_begin = (uint16_t*)jl_dt_layout_ptrs(layout);
2679 : 0 : obj16_end = obj16_begin + npointers;
2680 : 0 : gc_mark_array16_t markdata = {objary_begin, objary_end, obj16_begin, {new_obj, obj16_begin, obj16_end, nptr}};
2681 : 0 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(array16),
2682 : : &markdata, sizeof(markdata), 0);
2683 : 0 : ary16 = (gc_mark_array16_t*)sp.data;
2684 : 0 : goto array16_loaded;
2685 : : }
2686 : : else {
2687 : 0 : assert(0 && "unimplemented");
2688 : : }
2689 : : }
2690 : 166404000 : goto pop;
2691 : : }
2692 [ + + ]: 1278880000 : else if (vt == jl_module_type) {
2693 [ + + ]: 121422 : if (update_meta)
2694 : 22998 : gc_setmark(ptls, o, bits, sizeof(jl_module_t));
2695 [ + + ]: 98424 : else if (foreign_alloc)
2696 : 83470 : objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_module_t));
2697 : 121422 : jl_module_t *m = (jl_module_t*)new_obj;
2698 : 121422 : jl_binding_t **table = (jl_binding_t**)m->bindings.table;
2699 : 121422 : size_t bsize = m->bindings.size;
2700 : 121422 : uintptr_t nptr = ((bsize + m->usings.len + 1) << 2) | (bits & GC_OLD);
2701 : 121422 : gc_mark_binding_t markdata = {m, table + 1, table + bsize, nptr, bits};
2702 : 121422 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(module_binding),
2703 : : &markdata, sizeof(markdata), 0);
2704 : 121422 : sp.data = (jl_gc_mark_data_t *)(((char*)sp.data) + sizeof(markdata));
2705 : 121422 : goto module_binding;
2706 : : }
2707 [ + + ]: 1278760000 : else if (vt == jl_task_type) {
2708 [ + + ]: 1231860 : if (update_meta)
2709 : 790264 : gc_setmark(ptls, o, bits, sizeof(jl_task_t));
2710 [ - + ]: 441595 : else if (foreign_alloc)
2711 : 0 : objprofile_count(vt, bits == GC_OLD_MARKED, sizeof(jl_task_t));
2712 : 1231860 : jl_task_t *ta = (jl_task_t*)new_obj;
2713 : 1231860 : gc_scrub_record_task(ta);
2714 [ - + ]: 1231860 : if (gc_cblist_task_scanner) {
2715 : 0 : export_gc_state(ptls, &sp);
2716 : 0 : int16_t tid = jl_atomic_load_relaxed(&ta->tid);
2717 [ # # # # : 0 : gc_invoke_callbacks(jl_gc_cb_task_scanner_t,
# # ]
2718 : : gc_cblist_task_scanner,
2719 : : (ta, tid != -1 && ta == jl_all_tls_states[tid]->root_task));
2720 : 0 : import_gc_state(ptls, &sp);
2721 : : }
2722 : : #ifdef COPY_STACKS
2723 : 1231860 : void *stkbuf = ta->stkbuf;
2724 [ + + + + ]: 1231860 : if (stkbuf && ta->copy_stack)
2725 : 134068 : gc_setmark_buf_(ptls, stkbuf, bits, ta->bufsz);
2726 : : #endif
2727 : 1231860 : jl_gcframe_t *s = ta->gcstack;
2728 : : size_t nroots;
2729 : 1231860 : uintptr_t offset = 0;
2730 : 1231860 : uintptr_t lb = 0;
2731 : 1231860 : uintptr_t ub = (uintptr_t)-1;
2732 : : #ifdef COPY_STACKS
2733 [ + + + + : 1231860 : if (stkbuf && ta->copy_stack && ta->ptls == NULL) {
+ - ]
2734 : 134068 : int16_t tid = jl_atomic_load_relaxed(&ta->tid);
2735 [ - + ]: 134068 : assert(tid >= 0);
2736 : 134068 : jl_ptls_t ptls2 = jl_all_tls_states[tid];
2737 : 134068 : ub = (uintptr_t)ptls2->stackbase;
2738 : 134068 : lb = ub - ta->copy_stack;
2739 : 134068 : offset = (uintptr_t)stkbuf - lb;
2740 : : }
2741 : : #endif
2742 [ + + ]: 1231860 : if (s) {
2743 : 385223 : nroots = gc_read_stack(&s->nroots, offset, lb, ub);
2744 [ - + ]: 385223 : assert(nroots <= UINT32_MAX);
2745 : 385223 : gc_mark_stackframe_t stackdata = {s, 0, (uint32_t)nroots, offset, lb, ub};
2746 : 385223 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(stack),
2747 : : &stackdata, sizeof(stackdata), 1);
2748 : : }
2749 [ + + ]: 1231860 : if (ta->excstack) {
2750 : 77648 : gc_setmark_buf_(ptls, ta->excstack, bits, sizeof(jl_excstack_t) +
2751 : 77648 : sizeof(uintptr_t)*ta->excstack->reserved_size);
2752 : 77648 : gc_mark_excstack_t stackdata = {ta->excstack, ta->excstack->top, 0, 0};
2753 : 77648 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(excstack),
2754 : : &stackdata, sizeof(stackdata), 1);
2755 : : }
2756 : 1231860 : const jl_datatype_layout_t *layout = jl_task_type->layout;
2757 [ - + ]: 1231860 : assert(layout->fielddesc_type == 0);
2758 [ - + ]: 1231860 : assert(layout->nfields > 0);
2759 : 1231860 : uint32_t npointers = layout->npointers;
2760 : 1231860 : obj8_begin = (uint8_t*)jl_dt_layout_ptrs(layout);
2761 : 1231860 : obj8_end = obj8_begin + npointers;
2762 : : // assume tasks always reference young objects: set lowest bit
2763 : 1231860 : uintptr_t nptr = (npointers << 2) | 1 | bits;
2764 : 1231860 : gc_mark_obj8_t markdata = {new_obj, obj8_begin, obj8_end, nptr};
2765 : 1231860 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj8),
2766 : : &markdata, sizeof(markdata), 0);
2767 : 1231860 : obj8 = (gc_mark_obj8_t*)sp.data;
2768 : 1231860 : obj8_parent = (char*)ta;
2769 : 1231860 : goto obj8_loaded;
2770 : : }
2771 [ + + ]: 1277530000 : else if (vt == jl_string_type) {
2772 : 53488100 : size_t dtsz = jl_string_len(new_obj) + sizeof(size_t) + 1;
2773 [ + + ]: 53488100 : if (update_meta)
2774 : 16197700 : gc_setmark(ptls, o, bits, dtsz);
2775 [ + - ]: 37290500 : else if (foreign_alloc)
2776 : 37290500 : objprofile_count(vt, bits == GC_OLD_MARKED, dtsz);
2777 : 53488100 : goto pop;
2778 : : }
2779 : : else {
2780 [ - + ]: 1224040000 : if (__unlikely(!jl_is_datatype(vt)))
2781 : 0 : gc_assert_datatype_fail(ptls, vt, sp);
2782 : 1224040000 : size_t dtsz = jl_datatype_size(vt);
2783 [ + + ]: 1224040000 : if (update_meta)
2784 : 829467000 : gc_setmark(ptls, o, bits, dtsz);
2785 [ + + ]: 394572000 : else if (foreign_alloc)
2786 : 392214000 : objprofile_count(vt, bits == GC_OLD_MARKED, dtsz);
2787 [ + + ]: 1224040000 : if (vt == jl_weakref_type)
2788 : 107193 : goto pop;
2789 : 1223930000 : const jl_datatype_layout_t *layout = vt->layout;
2790 : 1223930000 : uint32_t npointers = layout->npointers;
2791 [ + + ]: 1223930000 : if (npointers == 0)
2792 : 70590100 : goto pop;
2793 : 1153340000 : uintptr_t nptr = npointers << 2 | (bits & GC_OLD);
2794 [ - + - - ]: 1153340000 : assert((layout->nfields > 0 || layout->fielddesc_type == 3) && "opaque types should have been handled specially");
2795 [ + + ]: 1153340000 : if (layout->fielddesc_type == 0) {
2796 : 1151860000 : obj8_parent = (char*)new_obj;
2797 : 1151860000 : obj8_begin = (uint8_t*)jl_dt_layout_ptrs(layout);
2798 : 1151860000 : obj8_end = obj8_begin + npointers;
2799 [ - + ]: 1151860000 : assert(obj8_begin < obj8_end);
2800 : 1151860000 : gc_mark_obj8_t markdata = {new_obj, obj8_begin, obj8_end, nptr};
2801 : 1151860000 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj8),
2802 : : &markdata, sizeof(markdata), 0);
2803 : 1151860000 : obj8 = (gc_mark_obj8_t*)sp.data;
2804 : 1151860000 : goto obj8_loaded;
2805 : : }
2806 [ + - ]: 1480110 : else if (layout->fielddesc_type == 1) {
2807 : 1480110 : obj16_parent = (char*)new_obj;
2808 : 1480110 : obj16_begin = (uint16_t*)jl_dt_layout_ptrs(layout);
2809 : 1480110 : obj16_end = obj16_begin + npointers;
2810 [ - + ]: 1480110 : assert(obj16_begin < obj16_end);
2811 : 1480110 : gc_mark_obj16_t markdata = {new_obj, obj16_begin, obj16_end, nptr};
2812 : 1480110 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj16),
2813 : : &markdata, sizeof(markdata), 0);
2814 : 1480110 : obj16 = (gc_mark_obj16_t*)sp.data;
2815 : 1480110 : goto obj16_loaded;
2816 : : }
2817 [ # # ]: 0 : else if (layout->fielddesc_type == 2) {
2818 : : // This is very uncommon
2819 : : // Do not do store to load forwarding to save some code size
2820 : 0 : uint32_t *obj32_begin = (uint32_t*)jl_dt_layout_ptrs(layout);
2821 : 0 : uint32_t *obj32_end = obj32_begin + npointers;
2822 : 0 : gc_mark_obj32_t markdata = {new_obj, obj32_begin, obj32_end, nptr};
2823 : 0 : gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj32),
2824 : : &markdata, sizeof(markdata), 0);
2825 : 0 : sp.data = (jl_gc_mark_data_t *)(((char*)sp.data) + sizeof(markdata));
2826 : 0 : goto obj32;
2827 : : }
2828 : : else {
2829 [ # # ]: 0 : assert(layout->fielddesc_type == 3);
2830 : 0 : jl_fielddescdyn_t *desc = (jl_fielddescdyn_t*)jl_dt_layout_fields(layout);
2831 : 0 : int old = jl_astaggedvalue(new_obj)->bits.gc & 2;
2832 : 0 : export_gc_state(ptls, &sp);
2833 : 0 : uintptr_t young = desc->markfunc(ptls, new_obj);
2834 : 0 : import_gc_state(ptls, &sp);
2835 [ # # # # ]: 0 : if (old && young)
2836 : 0 : gc_mark_push_remset(ptls, new_obj, young * 4 + 3);
2837 : 0 : goto pop;
2838 : : }
2839 : : }
2840 : : }
2841 : : }
2842 : :
2843 : 14102 : static void jl_gc_queue_thread_local(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp,
2844 : : jl_ptls_t ptls2)
2845 : : {
2846 : 14102 : gc_mark_queue_obj(gc_cache, sp, jl_atomic_load_relaxed(&ptls2->current_task));
2847 : 14102 : gc_mark_queue_obj(gc_cache, sp, ptls2->root_task);
2848 [ + + ]: 14102 : if (ptls2->next_task)
2849 : 539 : gc_mark_queue_obj(gc_cache, sp, ptls2->next_task);
2850 [ - + ]: 14102 : if (ptls2->previous_task) // shouldn't be necessary, but no reason not to
2851 : 0 : gc_mark_queue_obj(gc_cache, sp, ptls2->previous_task);
2852 [ - + ]: 14102 : if (ptls2->previous_exception)
2853 : 0 : gc_mark_queue_obj(gc_cache, sp, ptls2->previous_exception);
2854 : 14102 : }
2855 : :
2856 : : extern jl_value_t *cmpswap_names JL_GLOBALLY_ROOTED;
2857 : :
2858 : : // mark the initial root set
2859 : 14024 : static void mark_roots(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp)
2860 : : {
2861 : : // modules
2862 : 14024 : gc_mark_queue_obj(gc_cache, sp, jl_main_module);
2863 : :
2864 : : // invisible builtin values
2865 [ + - ]: 14024 : if (jl_an_empty_vec_any != NULL)
2866 : 14024 : gc_mark_queue_obj(gc_cache, sp, jl_an_empty_vec_any);
2867 [ + + ]: 14024 : if (jl_module_init_order != NULL)
2868 : 12702 : gc_mark_queue_obj(gc_cache, sp, jl_module_init_order);
2869 [ + + ]: 238408 : for (size_t i = 0; i < jl_current_modules.size; i += 2) {
2870 [ + + ]: 224384 : if (jl_current_modules.table[i + 1] != HT_NOTFOUND) {
2871 : 9733 : gc_mark_queue_obj(gc_cache, sp, jl_current_modules.table[i]);
2872 : : }
2873 : : }
2874 : 14024 : gc_mark_queue_obj(gc_cache, sp, jl_anytuple_type_type);
2875 [ + + ]: 57456300 : for (size_t i = 0; i < N_CALL_CACHE; i++) {
2876 : 57442300 : jl_typemap_entry_t *v = jl_atomic_load_relaxed(&call_cache[i]);
2877 [ + + ]: 57442300 : if (v != NULL)
2878 : 15121500 : gc_mark_queue_obj(gc_cache, sp, v);
2879 : : }
2880 [ - + ]: 14024 : if (jl_all_methods != NULL)
2881 : 0 : gc_mark_queue_obj(gc_cache, sp, jl_all_methods);
2882 [ - + ]: 14024 : if (_jl_debug_method_invalidation != NULL)
2883 : 0 : gc_mark_queue_obj(gc_cache, sp, _jl_debug_method_invalidation);
2884 : :
2885 : : // constants
2886 : 14024 : gc_mark_queue_obj(gc_cache, sp, jl_emptytuple_type);
2887 [ + + ]: 14024 : if (cmpswap_names != NULL)
2888 : 11653 : gc_mark_queue_obj(gc_cache, sp, cmpswap_names);
2889 : 14024 : }
2890 : :
2891 : : // find unmarked objects that need to be finalized from the finalizer list "list".
2892 : : // this must happen last in the mark phase.
2893 : 14897 : static void sweep_finalizer_list(arraylist_t *list)
2894 : : {
2895 : 14897 : void **items = list->items;
2896 : 14897 : size_t len = list->len;
2897 : 14897 : size_t j = 0;
2898 [ + + ]: 1704660 : for (size_t i=0; i < len; i+=2) {
2899 : 1689760 : void *v0 = items[i];
2900 : 1689760 : void *v = gc_ptr_clear_tag(v0, 1);
2901 [ - + ]: 1689760 : if (__unlikely(!v0)) {
2902 : : // remove from this list
2903 : 0 : continue;
2904 : : }
2905 : :
2906 : 1689760 : void *fin = items[i+1];
2907 : 1689760 : int isfreed = !gc_marked(jl_astaggedvalue(v)->bits.gc);
2908 : 3272620 : int isold = (list != &finalizer_list_marked &&
2909 [ + + + + ]: 1730720 : jl_astaggedvalue(v)->bits.gc == GC_OLD_MARKED &&
2910 [ + + ]: 40958 : jl_astaggedvalue(fin)->bits.gc == GC_OLD_MARKED);
2911 [ + + + + ]: 1689760 : if (isfreed || isold) {
2912 : : // remove from this list
2913 : : }
2914 : : else {
2915 [ + + ]: 189098 : if (j < i) {
2916 : 109928 : items[j] = items[i];
2917 : 109928 : items[j+1] = items[i+1];
2918 : : }
2919 : 189098 : j += 2;
2920 : : }
2921 [ + + ]: 1689760 : if (isfreed) {
2922 : 1460600 : schedule_finalization(v0, fin);
2923 : : }
2924 [ + + ]: 1689760 : if (isold) {
2925 : : // The caller relies on the new objects to be pushed to the end of
2926 : : // the list!!
2927 : 40069 : arraylist_push(&finalizer_list_marked, v0);
2928 : 40069 : arraylist_push(&finalizer_list_marked, fin);
2929 : : }
2930 : : }
2931 : 14897 : list->len = j;
2932 : 14897 : }
2933 : :
2934 : : // collector entry point and control
2935 : : static _Atomic(uint32_t) jl_gc_disable_counter = 1;
2936 : :
2937 : 33048800 : JL_DLLEXPORT int jl_gc_enable(int on)
2938 : : {
2939 : 33048800 : jl_ptls_t ptls = jl_current_task->ptls;
2940 : 33048800 : int prev = !ptls->disable_gc;
2941 : 33048800 : ptls->disable_gc = (on == 0);
2942 [ + + + + ]: 33048800 : if (on && !prev) {
2943 : : // disable -> enable
2944 [ + - ]: 16523600 : if (jl_atomic_fetch_add(&jl_gc_disable_counter, -1) == 1) {
2945 : 16523600 : gc_num.allocd += gc_num.deferred_alloc;
2946 : 16523600 : gc_num.deferred_alloc = 0;
2947 : : }
2948 : : }
2949 [ + + + + ]: 16525200 : else if (prev && !on) {
2950 : : // enable -> disable
2951 : 16523100 : jl_atomic_fetch_add(&jl_gc_disable_counter, 1);
2952 : : // check if the GC is running and wait for it to finish
2953 : 16523100 : jl_gc_safepoint_(ptls);
2954 : : }
2955 : 33048800 : return prev;
2956 : : }
2957 : :
2958 : 0 : JL_DLLEXPORT int jl_gc_is_enabled(void)
2959 : : {
2960 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
2961 : 0 : return !ptls->disable_gc;
2962 : : }
2963 : :
2964 : 14339 : JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) JL_NOTSAFEPOINT
2965 : : {
2966 : 14339 : jl_gc_num_t num = gc_num;
2967 : 14339 : combine_thread_gc_counts(&num);
2968 : : // Sync this logic with `base/util.jl:GC_Diff`
2969 : 14339 : *bytes = (num.total_allocd + num.deferred_alloc + num.allocd);
2970 : 14339 : }
2971 : :
2972 : 0 : JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void)
2973 : : {
2974 : 0 : return gc_num.total_time;
2975 : : }
2976 : :
2977 : 20849 : JL_DLLEXPORT jl_gc_num_t jl_gc_num(void)
2978 : : {
2979 : 20849 : jl_gc_num_t num = gc_num;
2980 : 20849 : combine_thread_gc_counts(&num);
2981 : 20849 : return num;
2982 : : }
2983 : :
2984 : 0 : JL_DLLEXPORT void jl_gc_reset_stats(void)
2985 : : {
2986 : 0 : gc_num.max_pause = 0;
2987 : 0 : gc_num.max_memory = 0;
2988 : 0 : gc_num.max_time_to_safepoint = 0;
2989 : 0 : }
2990 : :
2991 : : // TODO: these were supposed to be thread local
2992 : 443 : JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void) JL_NOTSAFEPOINT
2993 : : {
2994 : 443 : int64_t oldtb = last_gc_total_bytes;
2995 : : int64_t newtb;
2996 : 443 : jl_gc_get_total_bytes(&newtb);
2997 : 443 : last_gc_total_bytes = newtb;
2998 : 443 : return newtb - oldtb;
2999 : : }
3000 : :
3001 : 107 : JL_DLLEXPORT int64_t jl_gc_sync_total_bytes(int64_t offset) JL_NOTSAFEPOINT
3002 : : {
3003 : 107 : int64_t oldtb = last_gc_total_bytes;
3004 : : int64_t newtb;
3005 : 107 : jl_gc_get_total_bytes(&newtb);
3006 : 107 : last_gc_total_bytes = newtb - offset;
3007 : 107 : return newtb - oldtb;
3008 : : }
3009 : :
3010 : 0 : JL_DLLEXPORT int64_t jl_gc_live_bytes(void)
3011 : : {
3012 : 0 : return live_bytes;
3013 : : }
3014 : :
3015 : 14102 : static void jl_gc_premark(jl_ptls_t ptls2)
3016 : : {
3017 : 14102 : arraylist_t *remset = ptls2->heap.remset;
3018 : 14102 : ptls2->heap.remset = ptls2->heap.last_remset;
3019 : 14102 : ptls2->heap.last_remset = remset;
3020 : 14102 : ptls2->heap.remset->len = 0;
3021 : 14102 : ptls2->heap.remset_nptr = 0;
3022 : :
3023 : : // avoid counting remembered objects & bindings twice
3024 : : // in `perm_scanned_bytes`
3025 : 14102 : size_t len = remset->len;
3026 : 14102 : void **items = remset->items;
3027 [ + + ]: 11536400 : for (size_t i = 0; i < len; i++) {
3028 : 11522300 : jl_value_t *item = (jl_value_t*)items[i];
3029 : 11522300 : objprofile_count(jl_typeof(item), 2, 0);
3030 : 11522300 : jl_astaggedvalue(item)->bits.gc = GC_OLD_MARKED;
3031 : : }
3032 : 14102 : len = ptls2->heap.rem_bindings.len;
3033 : 14102 : items = ptls2->heap.rem_bindings.items;
3034 [ + + ]: 74117 : for (size_t i = 0; i < len; i++) {
3035 : 60015 : void *ptr = items[i];
3036 : 60015 : jl_astaggedvalue(ptr)->bits.gc = GC_OLD_MARKED;
3037 : : }
3038 : 14102 : }
3039 : :
3040 : 14102 : static void jl_gc_queue_remset(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, jl_ptls_t ptls2)
3041 : : {
3042 : 14102 : size_t len = ptls2->heap.last_remset->len;
3043 : 14102 : void **items = ptls2->heap.last_remset->items;
3044 [ + + ]: 11536400 : for (size_t i = 0; i < len; i++)
3045 : 11522300 : gc_mark_queue_scan_obj(gc_cache, sp, (jl_value_t*)items[i]);
3046 : 14102 : int n_bnd_refyoung = 0;
3047 : 14102 : len = ptls2->heap.rem_bindings.len;
3048 : 14102 : items = ptls2->heap.rem_bindings.items;
3049 [ + + ]: 74117 : for (size_t i = 0; i < len; i++) {
3050 : 60015 : jl_binding_t *ptr = (jl_binding_t*)items[i];
3051 : : // A null pointer can happen here when the binding is cleaned up
3052 : : // as an exception is thrown after it was already queued (#10221)
3053 : 60015 : jl_value_t *v = jl_atomic_load_relaxed(&ptr->value);
3054 [ + - + + ]: 60015 : if (v != NULL && gc_mark_queue_obj(gc_cache, sp, v)) {
3055 : 41244 : items[n_bnd_refyoung] = ptr;
3056 : 41244 : n_bnd_refyoung++;
3057 : : }
3058 : : }
3059 : 14102 : ptls2->heap.rem_bindings.len = n_bnd_refyoung;
3060 : 14102 : }
3061 : :
3062 : 14102 : static void jl_gc_queue_bt_buf(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, jl_ptls_t ptls2)
3063 : : {
3064 : 14102 : jl_bt_element_t *bt_data = ptls2->bt_data;
3065 : 14102 : size_t bt_size = ptls2->bt_size;
3066 [ - + ]: 14102 : for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) {
3067 : 0 : jl_bt_element_t *bt_entry = bt_data + i;
3068 [ # # ]: 0 : if (jl_bt_is_native(bt_entry))
3069 : 0 : continue;
3070 : 0 : size_t njlvals = jl_bt_num_jlvals(bt_entry);
3071 [ # # ]: 0 : for (size_t j = 0; j < njlvals; j++)
3072 : 0 : gc_mark_queue_obj(gc_cache, sp, jl_bt_entry_jlvalue(bt_entry, j));
3073 : : }
3074 : 14102 : }
3075 : :
3076 : : size_t jl_maxrss(void);
3077 : :
3078 : : // Only one thread should be running in this function
3079 : 14024 : static int _jl_gc_collect(jl_ptls_t ptls, jl_gc_collection_t collection)
3080 : : {
3081 : 14024 : combine_thread_gc_counts(&gc_num);
3082 : :
3083 : 14024 : jl_gc_mark_cache_t *gc_cache = &ptls->gc_cache;
3084 : : jl_gc_mark_sp_t sp;
3085 : 14024 : gc_mark_sp_init(gc_cache, &sp);
3086 : :
3087 : 14024 : uint64_t gc_start_time = jl_hrtime();
3088 : 14024 : int64_t last_perm_scanned_bytes = perm_scanned_bytes;
3089 : : JL_PROBE_GC_MARK_BEGIN();
3090 : 14024 : uint64_t start_mark_time = jl_hrtime();
3091 : :
3092 : : // 1. fix GC bits of objects in the remset.
3093 [ + + ]: 28126 : for (int t_i = 0; t_i < jl_n_threads; t_i++)
3094 : 14102 : jl_gc_premark(jl_all_tls_states[t_i]);
3095 : :
3096 [ + + ]: 28126 : for (int t_i = 0; t_i < jl_n_threads; t_i++) {
3097 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
3098 : : // 2.1. mark every object in the `last_remsets` and `rem_binding`
3099 : 14102 : jl_gc_queue_remset(gc_cache, &sp, ptls2);
3100 : : // 2.2. mark every thread local root
3101 : 14102 : jl_gc_queue_thread_local(gc_cache, &sp, ptls2);
3102 : : // 2.3. mark any managed objects in the backtrace buffer
3103 : 14102 : jl_gc_queue_bt_buf(gc_cache, &sp, ptls2);
3104 : : }
3105 : :
3106 : : // 3. walk roots
3107 : 14024 : mark_roots(gc_cache, &sp);
3108 [ - + ]: 14024 : if (gc_cblist_root_scanner) {
3109 : 0 : export_gc_state(ptls, &sp);
3110 [ # # ]: 0 : gc_invoke_callbacks(jl_gc_cb_root_scanner_t,
3111 : : gc_cblist_root_scanner, (collection));
3112 : 0 : import_gc_state(ptls, &sp);
3113 : : }
3114 : 14024 : gc_mark_loop(ptls, sp);
3115 : 14024 : gc_mark_sp_init(gc_cache, &sp);
3116 : 14024 : gc_num.since_sweep += gc_num.allocd;
3117 : : JL_PROBE_GC_MARK_END(scanned_bytes, perm_scanned_bytes);
3118 : : gc_settime_premark_end();
3119 : : gc_time_mark_pause(gc_start_time, scanned_bytes, perm_scanned_bytes);
3120 : 14024 : uint64_t end_mark_time = jl_hrtime();
3121 : 14024 : uint64_t mark_time = end_mark_time - start_mark_time;
3122 : 14024 : gc_num.mark_time = mark_time;
3123 : 14024 : gc_num.total_mark_time += mark_time;
3124 : 14024 : int64_t actual_allocd = gc_num.since_sweep;
3125 : : // marking is over
3126 : :
3127 : : // 4. check for objects to finalize
3128 : 14024 : clear_weak_refs();
3129 : : // Record the length of the marked list since we need to
3130 : : // mark the object moved to the marked list from the
3131 : : // `finalizer_list` by `sweep_finalizer_list`
3132 : 14024 : size_t orig_marked_len = finalizer_list_marked.len;
3133 [ + + ]: 28126 : for (int i = 0;i < jl_n_threads;i++) {
3134 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[i];
3135 : 14102 : sweep_finalizer_list(&ptls2->finalizers);
3136 : : }
3137 [ + + ]: 14024 : if (prev_sweep_full) {
3138 : 795 : sweep_finalizer_list(&finalizer_list_marked);
3139 : 795 : orig_marked_len = 0;
3140 : : }
3141 [ + + ]: 28126 : for (int i = 0;i < jl_n_threads;i++) {
3142 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[i];
3143 : 14102 : gc_mark_queue_finlist(gc_cache, &sp, &ptls2->finalizers, 0);
3144 : : }
3145 : 14024 : gc_mark_queue_finlist(gc_cache, &sp, &finalizer_list_marked, orig_marked_len);
3146 : : // "Flush" the mark stack before flipping the reset_age bit
3147 : : // so that the objects are not incorrectly reset.
3148 : 14024 : gc_mark_loop(ptls, sp);
3149 : 14024 : gc_mark_sp_init(gc_cache, &sp);
3150 : : // Conservative marking relies on age to tell allocated objects
3151 : : // and freelist entries apart.
3152 : 14024 : mark_reset_age = !jl_gc_conservative_gc_support_enabled();
3153 : : // Reset the age and old bit for any unmarked objects referenced by the
3154 : : // `to_finalize` list. These objects are only reachable from this list
3155 : : // and should not be referenced by any old objects so this won't break
3156 : : // the GC invariant.
3157 : 14024 : gc_mark_queue_finlist(gc_cache, &sp, &to_finalize, 0);
3158 : 14024 : gc_mark_loop(ptls, sp);
3159 : 14024 : mark_reset_age = 0;
3160 : : gc_settime_postmark_end();
3161 : :
3162 : : // Flush everything in mark cache
3163 : 14024 : gc_sync_all_caches_nolock(ptls);
3164 : :
3165 : 14024 : int64_t live_sz_ub = live_bytes + actual_allocd;
3166 : 14024 : int64_t live_sz_est = scanned_bytes + perm_scanned_bytes;
3167 : 14024 : int64_t estimate_freed = live_sz_ub - live_sz_est;
3168 : :
3169 : : gc_verify(ptls);
3170 : :
3171 : : gc_stats_all_pool();
3172 : : gc_stats_big_obj();
3173 : 14024 : objprofile_printall();
3174 : 14024 : objprofile_reset();
3175 : 14024 : gc_num.total_allocd += gc_num.since_sweep;
3176 [ + + ]: 14024 : if (!prev_sweep_full)
3177 : 13229 : promoted_bytes += perm_scanned_bytes - last_perm_scanned_bytes;
3178 : : // 5. next collection decision
3179 [ + + + + ]: 14024 : int not_freed_enough = (collection == JL_GC_AUTO) && estimate_freed < (7*(actual_allocd/10));
3180 : 14024 : int nptr = 0;
3181 [ + + ]: 28126 : for (int i = 0;i < jl_n_threads;i++)
3182 : 14102 : nptr += jl_all_tls_states[i]->heap.remset_nptr;
3183 : :
3184 : : // many pointers in the intergen frontier => "quick" mark is not quick
3185 : 14024 : int large_frontier = nptr*sizeof(void*) >= default_collect_interval;
3186 : 14024 : int sweep_full = 0;
3187 : 14024 : int recollect = 0;
3188 : :
3189 : : // update heuristics only if this GC was automatically triggered
3190 [ + + ]: 14024 : if (collection == JL_GC_AUTO) {
3191 [ + + ]: 13298 : if (not_freed_enough) {
3192 : 30 : gc_num.interval = gc_num.interval * 2;
3193 : : }
3194 [ - + ]: 13298 : if (large_frontier) {
3195 : 0 : sweep_full = 1;
3196 : : }
3197 [ - + ]: 13298 : if (gc_num.interval > max_collect_interval) {
3198 : 0 : sweep_full = 1;
3199 : 0 : gc_num.interval = max_collect_interval;
3200 : : }
3201 : : }
3202 : :
3203 : :
3204 : : // If the live data outgrows the suggested max_total_memory
3205 : : // we keep going with minimum intervals and full gcs until
3206 : : // we either free some space or get an OOM error.
3207 [ - + ]: 14024 : if (live_bytes > max_total_memory) {
3208 : 0 : sweep_full = 1;
3209 : : }
3210 : : if (gc_sweep_always_full) {
3211 : : sweep_full = 1;
3212 : : }
3213 [ + + ]: 14024 : if (collection == JL_GC_FULL) {
3214 : 644 : sweep_full = 1;
3215 : 644 : recollect = 1;
3216 : : }
3217 [ + + ]: 14024 : if (sweep_full) {
3218 : : // these are the difference between the number of gc-perm bytes scanned
3219 : : // on the first collection after sweep_full, and the current scan
3220 : 644 : perm_scanned_bytes = 0;
3221 : 644 : promoted_bytes = 0;
3222 : : }
3223 : 14024 : scanned_bytes = 0;
3224 : : // 5. start sweeping
3225 : 14024 : uint64_t start_sweep_time = jl_hrtime();
3226 : : JL_PROBE_GC_SWEEP_BEGIN(sweep_full);
3227 : 14024 : sweep_weak_refs();
3228 : 14024 : sweep_stack_pools();
3229 : 14024 : gc_sweep_foreign_objs();
3230 : 14024 : gc_sweep_other(ptls, sweep_full);
3231 : 14024 : gc_scrub();
3232 : 14024 : gc_verify_tags();
3233 : 14024 : gc_sweep_pool(sweep_full);
3234 [ + + ]: 14024 : if (sweep_full)
3235 : 644 : gc_sweep_perm_alloc();
3236 : : JL_PROBE_GC_SWEEP_END();
3237 : :
3238 : 14024 : uint64_t gc_end_time = jl_hrtime();
3239 : 14024 : uint64_t pause = gc_end_time - gc_start_time;
3240 : 14024 : uint64_t sweep_time = gc_end_time - start_sweep_time;
3241 : 14024 : gc_num.total_sweep_time += sweep_time;
3242 : 14024 : gc_num.sweep_time = sweep_time;
3243 : :
3244 : : // sweeping is over
3245 : : // 6. if it is a quick sweep, put back the remembered objects in queued state
3246 : : // so that we don't trigger the barrier again on them.
3247 [ + + ]: 28126 : for (int t_i = 0;t_i < jl_n_threads;t_i++) {
3248 : 14102 : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
3249 [ + + ]: 14102 : if (!sweep_full) {
3250 [ + + ]: 8954500 : for (int i = 0; i < ptls2->heap.remset->len; i++) {
3251 : 8941040 : jl_astaggedvalue(ptls2->heap.remset->items[i])->bits.gc = GC_MARKED;
3252 : : }
3253 [ + + ]: 54003 : for (int i = 0; i < ptls2->heap.rem_bindings.len; i++) {
3254 : 40545 : void *ptr = ptls2->heap.rem_bindings.items[i];
3255 : 40545 : jl_astaggedvalue(ptr)->bits.gc = GC_MARKED;
3256 : : }
3257 : : }
3258 : : else {
3259 : 644 : ptls2->heap.remset->len = 0;
3260 : 644 : ptls2->heap.rem_bindings.len = 0;
3261 : : }
3262 : : }
3263 : :
3264 : : #ifdef __GLIBC__
3265 [ + + ]: 14024 : if (sweep_full) {
3266 : : // issue #30653
3267 : : // empirically, the malloc runaway seemed to occur within a growth gap
3268 : : // of about 20-25%
3269 [ + + ]: 644 : if (jl_maxrss() > (last_trim_maxrss/4)*5) {
3270 : 19 : malloc_trim(0);
3271 : 19 : last_trim_maxrss = jl_maxrss();
3272 : : }
3273 : : }
3274 : : #endif
3275 : :
3276 : :
3277 : 14024 : _report_gc_finished(pause, gc_num.freed, sweep_full, recollect);
3278 : :
3279 : : gc_final_pause_end(gc_start_time, gc_end_time);
3280 : : gc_time_sweep_pause(gc_end_time, actual_allocd, live_bytes,
3281 : : estimate_freed, sweep_full);
3282 : 14024 : gc_num.full_sweep += sweep_full;
3283 : 14024 : uint64_t max_memory = last_live_bytes + gc_num.allocd;
3284 [ + + ]: 14024 : if (max_memory > gc_num.max_memory) {
3285 : 1488 : gc_num.max_memory = max_memory;
3286 : : }
3287 : :
3288 : 14024 : gc_num.allocd = 0;
3289 : 14024 : last_live_bytes = live_bytes;
3290 : 14024 : live_bytes += -gc_num.freed + gc_num.since_sweep;
3291 : :
3292 [ + + ]: 14024 : if (collection == JL_GC_AUTO) {
3293 : : // If the current interval is larger than half the live data decrease the interval
3294 : 13298 : int64_t half = live_bytes/2;
3295 [ + + ]: 13298 : if (gc_num.interval > half) gc_num.interval = half;
3296 : : // But never go below default
3297 [ + + ]: 13298 : if (gc_num.interval < default_collect_interval) gc_num.interval = default_collect_interval;
3298 : : }
3299 : :
3300 [ - + ]: 14024 : if (gc_num.interval + live_bytes > max_total_memory) {
3301 [ # # ]: 0 : if (live_bytes < max_total_memory) {
3302 : 0 : gc_num.interval = max_total_memory - live_bytes;
3303 : : } else {
3304 : : // We can't stay under our goal so let's go back to
3305 : : // the minimum interval and hope things get better
3306 : 0 : gc_num.interval = default_collect_interval;
3307 : : }
3308 : : }
3309 : :
3310 : : gc_time_summary(sweep_full, t_start, gc_end_time, gc_num.freed,
3311 : : live_bytes, gc_num.interval, pause,
3312 : : gc_num.time_to_safepoint,
3313 : : gc_num.mark_time, gc_num.sweep_time);
3314 : :
3315 : 14024 : prev_sweep_full = sweep_full;
3316 : 14024 : gc_num.pause += !recollect;
3317 : 14024 : gc_num.total_time += pause;
3318 : 14024 : gc_num.since_sweep = 0;
3319 : 14024 : gc_num.freed = 0;
3320 [ + + ]: 14024 : if (pause > gc_num.max_pause) {
3321 : 525 : gc_num.max_pause = pause;
3322 : : }
3323 : 14024 : reset_thread_gc_counts();
3324 : :
3325 : 14024 : return recollect;
3326 : : }
3327 : :
3328 : 14274 : JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection)
3329 : : {
3330 : : JL_PROBE_GC_BEGIN(collection);
3331 : :
3332 : 14274 : jl_task_t *ct = jl_current_task;
3333 : 14274 : jl_ptls_t ptls = ct->ptls;
3334 [ + + ]: 14274 : if (jl_atomic_load_relaxed(&jl_gc_disable_counter)) {
3335 : 894 : size_t localbytes = jl_atomic_load_relaxed(&ptls->gc_num.allocd) + gc_num.interval;
3336 : 894 : jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval);
3337 : : static_assert(sizeof(_Atomic(uint64_t)) == sizeof(gc_num.deferred_alloc), "");
3338 : 894 : jl_atomic_fetch_add((_Atomic(uint64_t)*)&gc_num.deferred_alloc, localbytes);
3339 : 894 : return;
3340 : : }
3341 : 13380 : jl_gc_debug_print();
3342 : :
3343 : 13380 : int8_t old_state = jl_atomic_load_relaxed(&ptls->gc_state);
3344 : 13380 : jl_atomic_store_release(&ptls->gc_state, JL_GC_STATE_WAITING);
3345 : : // `jl_safepoint_start_gc()` makes sure only one thread can
3346 : : // run the GC.
3347 : 13380 : uint64_t t0 = jl_hrtime();
3348 [ - + ]: 13380 : if (!jl_safepoint_start_gc()) {
3349 : : // Multithread only. See assertion in `safepoint.c`
3350 : 0 : jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING);
3351 : 0 : return;
3352 : : }
3353 : : JL_TIMING(GC);
3354 : 13380 : int last_errno = errno;
3355 : : #ifdef _OS_WINDOWS_
3356 : : DWORD last_error = GetLastError();
3357 : : #endif
3358 : : // Now we are ready to wait for other threads to hit the safepoint,
3359 : : // we can do a few things that doesn't require synchronization.
3360 : : // TODO (concurrently queue objects)
3361 : : // no-op for non-threading
3362 : 13380 : jl_gc_wait_for_the_world();
3363 : : JL_PROBE_GC_STOP_THE_WORLD();
3364 : :
3365 : 13380 : uint64_t t1 = jl_hrtime();
3366 : 13380 : uint64_t duration = t1 - t0;
3367 [ + + ]: 13380 : if (duration > gc_num.max_time_to_safepoint)
3368 : 465 : gc_num.max_time_to_safepoint = duration;
3369 : 13380 : gc_num.time_to_safepoint = duration;
3370 : :
3371 [ - + ]: 13380 : gc_invoke_callbacks(jl_gc_cb_pre_gc_t,
3372 : : gc_cblist_pre_gc, (collection));
3373 : :
3374 [ + - ]: 13380 : if (!jl_atomic_load_relaxed(&jl_gc_disable_counter)) {
3375 : 13380 : JL_LOCK_NOGC(&finalizers_lock);
3376 [ + + ]: 13380 : if (_jl_gc_collect(ptls, collection)) {
3377 : : // recollect
3378 : 644 : int ret = _jl_gc_collect(ptls, JL_GC_AUTO);
3379 : : (void)ret;
3380 [ - + ]: 644 : assert(!ret);
3381 : : }
3382 : 13380 : JL_UNLOCK_NOGC(&finalizers_lock);
3383 : : }
3384 : :
3385 : : // no-op for non-threading
3386 : 13380 : jl_safepoint_end_gc();
3387 : 13380 : jl_gc_state_set(ptls, old_state, JL_GC_STATE_WAITING);
3388 : : JL_PROBE_GC_END();
3389 : :
3390 : : // Only disable finalizers on current thread
3391 : : // Doing this on all threads is racy (it's impossible to check
3392 : : // or wait for finalizers on other threads without dead lock).
3393 [ + + + + ]: 13380 : if (!ptls->finalizers_inhibited && ptls->locks.len == 0) {
3394 : 1681 : int8_t was_in_finalizer = ptls->in_finalizer;
3395 : 1681 : ptls->in_finalizer = 1;
3396 : 1681 : run_finalizers(ct);
3397 : 1681 : ptls->in_finalizer = was_in_finalizer;
3398 : : }
3399 : : JL_PROBE_GC_FINALIZER();
3400 : :
3401 [ - + ]: 13380 : gc_invoke_callbacks(jl_gc_cb_post_gc_t,
3402 : : gc_cblist_post_gc, (collection));
3403 : : #ifdef _OS_WINDOWS_
3404 : : SetLastError(last_error);
3405 : : #endif
3406 : 13380 : errno = last_errno;
3407 : : }
3408 : :
3409 : 0 : void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_mark_sp_t *sp)
3410 : : {
3411 : 0 : jl_gc_mark_cache_t *gc_cache = &ptls->gc_cache;
3412 [ # # ]: 0 : for (size_t i = 0; i < jl_n_threads; i++)
3413 : 0 : jl_gc_queue_thread_local(gc_cache, sp, jl_all_tls_states[i]);
3414 : 0 : mark_roots(gc_cache, sp);
3415 : 0 : }
3416 : :
3417 : : // allocator entry points
3418 : :
3419 : 6135830000 : JL_DLLEXPORT jl_value_t *(jl_gc_alloc)(jl_ptls_t ptls, size_t sz, void *ty)
3420 : : {
3421 : 6135830000 : return jl_gc_alloc_(ptls, sz, ty);
3422 : : }
3423 : :
3424 : : // Per-thread initialization
3425 : 692 : void jl_init_thread_heap(jl_ptls_t ptls)
3426 : : {
3427 [ + + ]: 692 : if (ptls->tid == 0)
3428 : 573 : ptls->disable_gc = 1;
3429 : 692 : jl_thread_heap_t *heap = &ptls->heap;
3430 : 692 : jl_gc_pool_t *p = heap->norm_pools;
3431 [ + + ]: 34580 : for (int i = 0; i < JL_GC_N_POOLS; i++) {
3432 : 33888 : p[i].osize = jl_gc_sizeclasses[i];
3433 : 33888 : p[i].freelist = NULL;
3434 : 33888 : p[i].newpages = NULL;
3435 : : }
3436 : 692 : arraylist_new(&heap->weak_refs, 0);
3437 : 692 : arraylist_new(&heap->live_tasks, 0);
3438 : 692 : heap->mallocarrays = NULL;
3439 : 692 : heap->mafreelist = NULL;
3440 : 692 : heap->big_objects = NULL;
3441 : 692 : arraylist_new(&heap->rem_bindings, 0);
3442 : 692 : heap->remset = &heap->_remset[0];
3443 : 692 : heap->last_remset = &heap->_remset[1];
3444 : 692 : arraylist_new(heap->remset, 0);
3445 : 692 : arraylist_new(heap->last_remset, 0);
3446 : 692 : arraylist_new(&ptls->finalizers, 0);
3447 : 692 : arraylist_new(&ptls->sweep_objs, 0);
3448 : :
3449 : 692 : jl_gc_mark_cache_t *gc_cache = &ptls->gc_cache;
3450 : 692 : gc_cache->perm_scanned_bytes = 0;
3451 : 692 : gc_cache->scanned_bytes = 0;
3452 : 692 : gc_cache->nbig_obj = 0;
3453 : 692 : size_t init_size = 1024;
3454 : 692 : gc_cache->pc_stack = (void**)malloc_s(init_size * sizeof(void*));
3455 : 692 : gc_cache->pc_stack_end = gc_cache->pc_stack + init_size;
3456 : 692 : gc_cache->data_stack = (jl_gc_mark_data_t *)malloc_s(init_size * sizeof(jl_gc_mark_data_t));
3457 : :
3458 : 692 : memset(&ptls->gc_num, 0, sizeof(ptls->gc_num));
3459 [ - + ]: 692 : assert(gc_num.interval == default_collect_interval);
3460 : 692 : jl_atomic_store_relaxed(&ptls->gc_num.allocd, -(int64_t)gc_num.interval);
3461 : 692 : }
3462 : :
3463 : : // System-wide initializations
3464 : 573 : void jl_gc_init(void)
3465 : : {
3466 : 573 : JL_MUTEX_INIT(&finalizers_lock);
3467 : 573 : uv_mutex_init(&gc_cache_lock);
3468 : 573 : uv_mutex_init(&gc_perm_lock);
3469 : :
3470 : 573 : jl_gc_init_page();
3471 : 573 : jl_gc_debug_init();
3472 : :
3473 : 573 : arraylist_new(&finalizer_list_marked, 0);
3474 : 573 : arraylist_new(&to_finalize, 0);
3475 : :
3476 : 573 : gc_num.interval = default_collect_interval;
3477 : 573 : last_long_collect_interval = default_collect_interval;
3478 : 573 : gc_num.allocd = 0;
3479 : 573 : gc_num.max_pause = 0;
3480 : 573 : gc_num.max_memory = 0;
3481 : :
3482 : : #ifdef _P64
3483 : : // on a big memory machine, set max_collect_interval to totalmem / nthreads / 2
3484 : 573 : uint64_t total_mem = uv_get_total_memory();
3485 : 573 : uint64_t constrained_mem = uv_get_constrained_memory();
3486 [ + - - + ]: 573 : if (constrained_mem > 0 && constrained_mem < total_mem)
3487 : 0 : total_mem = constrained_mem;
3488 : 573 : size_t maxmem = total_mem / jl_n_threads / 2;
3489 [ + + ]: 573 : if (maxmem > max_collect_interval)
3490 : 571 : max_collect_interval = maxmem;
3491 : : #endif
3492 : :
3493 : : // We allocate with abandon until we get close to the free memory on the machine.
3494 : 573 : uint64_t free_mem = uv_get_free_memory();
3495 : 573 : uint64_t high_water_mark = free_mem / 10 * 7; // 70% high water mark
3496 : :
3497 [ + - ]: 573 : if (high_water_mark < max_total_memory)
3498 : 573 : max_total_memory = high_water_mark;
3499 : :
3500 : 573 : jl_gc_mark_sp_t sp = {NULL, NULL, NULL, NULL};
3501 : 573 : gc_mark_loop(NULL, sp);
3502 : 573 : t_start = jl_hrtime();
3503 : 573 : }
3504 : :
3505 : 0 : void jl_gc_set_max_memory(uint64_t max_mem) {
3506 [ # # ]: 0 : if (max_mem > 0
3507 [ # # ]: 0 : && max_mem < (uint64_t)1 << (sizeof(memsize_t) * 8 - 1)) {
3508 : 0 : max_total_memory = max_mem;
3509 : : }
3510 : 0 : }
3511 : :
3512 : : // callback for passing OOM errors from gmp
3513 : 1 : JL_DLLEXPORT void jl_throw_out_of_memory_error(void)
3514 : : {
3515 : 1 : jl_throw(jl_memory_exception);
3516 : : }
3517 : :
3518 : : // allocation wrappers that track allocation and let collection run
3519 : :
3520 : 4121580 : JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz)
3521 : : {
3522 : 4121580 : jl_gcframe_t **pgcstack = jl_get_pgcstack();
3523 : 4121580 : jl_task_t *ct = jl_current_task;
3524 [ + + + - ]: 4121580 : if (pgcstack && ct->world_age) {
3525 : 4121580 : jl_ptls_t ptls = ct->ptls;
3526 : 4121580 : maybe_collect(ptls);
3527 : 4121580 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
3528 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + sz);
3529 : 4121580 : jl_atomic_store_relaxed(&ptls->gc_num.malloc,
3530 : : jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1);
3531 : : }
3532 : 4121580 : return malloc(sz);
3533 : : }
3534 : :
3535 : 402 : JL_DLLEXPORT void *jl_gc_counted_calloc(size_t nm, size_t sz)
3536 : : {
3537 : 402 : jl_gcframe_t **pgcstack = jl_get_pgcstack();
3538 : 402 : jl_task_t *ct = jl_current_task;
3539 [ + - + - ]: 402 : if (pgcstack && ct->world_age) {
3540 : 402 : jl_ptls_t ptls = ct->ptls;
3541 : 402 : maybe_collect(ptls);
3542 : 402 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
3543 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + nm*sz);
3544 : 402 : jl_atomic_store_relaxed(&ptls->gc_num.malloc,
3545 : : jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1);
3546 : : }
3547 : 402 : return calloc(nm, sz);
3548 : : }
3549 : :
3550 : 4121790 : JL_DLLEXPORT void jl_gc_counted_free_with_size(void *p, size_t sz)
3551 : : {
3552 : 4121790 : jl_gcframe_t **pgcstack = jl_get_pgcstack();
3553 : 4121790 : jl_task_t *ct = jl_current_task;
3554 : 4121790 : free(p);
3555 [ + + + - ]: 4121790 : if (pgcstack && ct->world_age) {
3556 : 4121780 : jl_ptls_t ptls = ct->ptls;
3557 : 4121780 : jl_atomic_store_relaxed(&ptls->gc_num.freed,
3558 : : jl_atomic_load_relaxed(&ptls->gc_num.freed) + sz);
3559 : 4121780 : jl_atomic_store_relaxed(&ptls->gc_num.freecall,
3560 : : jl_atomic_load_relaxed(&ptls->gc_num.freecall) + 1);
3561 : : }
3562 : 4121790 : }
3563 : :
3564 : 1121850 : JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size_t sz)
3565 : : {
3566 : 1121850 : jl_gcframe_t **pgcstack = jl_get_pgcstack();
3567 : 1121850 : jl_task_t *ct = jl_current_task;
3568 [ + + + - ]: 1121850 : if (pgcstack && ct->world_age) {
3569 : 1121840 : jl_ptls_t ptls = ct->ptls;
3570 : 1121840 : maybe_collect(ptls);
3571 [ + + ]: 1121840 : if (sz < old)
3572 : 234908 : jl_atomic_store_relaxed(&ptls->gc_num.freed,
3573 : : jl_atomic_load_relaxed(&ptls->gc_num.freed) + (old - sz));
3574 : : else
3575 : 886933 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
3576 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + (sz - old));
3577 : 1121840 : jl_atomic_store_relaxed(&ptls->gc_num.realloc,
3578 : : jl_atomic_load_relaxed(&ptls->gc_num.realloc) + 1);
3579 : : }
3580 : 1121850 : return realloc(p, sz);
3581 : : }
3582 : :
3583 : : // allocation wrappers that save the size of allocations, to allow using
3584 : : // jl_gc_counted_* functions with a libc-compatible API.
3585 : :
3586 : 27089 : JL_DLLEXPORT void *jl_malloc(size_t sz)
3587 : : {
3588 : 27089 : int64_t *p = (int64_t *)jl_gc_counted_malloc(sz + JL_SMALL_BYTE_ALIGNMENT);
3589 [ - + ]: 27089 : if (p == NULL)
3590 : 0 : return NULL;
3591 : 27089 : p[0] = sz;
3592 : 27089 : return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
3593 : : }
3594 : :
3595 : : //_unchecked_calloc does not check for potential overflow of nm*sz
3596 : 402 : STATIC_INLINE void *_unchecked_calloc(size_t nm, size_t sz) {
3597 : 402 : size_t nmsz = nm*sz;
3598 : 402 : int64_t *p = (int64_t *)jl_gc_counted_calloc(nmsz + JL_SMALL_BYTE_ALIGNMENT, 1);
3599 [ - + ]: 402 : if (p == NULL)
3600 : 0 : return NULL;
3601 : 402 : p[0] = nmsz;
3602 : 402 : return (void *)(p + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
3603 : : }
3604 : :
3605 : 402 : JL_DLLEXPORT void *jl_calloc(size_t nm, size_t sz)
3606 : : {
3607 [ - + ]: 402 : if (nm > SSIZE_MAX/sz - JL_SMALL_BYTE_ALIGNMENT)
3608 : 0 : return NULL;
3609 : 402 : return _unchecked_calloc(nm, sz);
3610 : : }
3611 : :
3612 : 27485 : JL_DLLEXPORT void jl_free(void *p)
3613 : : {
3614 [ + + ]: 27485 : if (p != NULL) {
3615 : 27484 : int64_t *pp = (int64_t *)p - 2;
3616 : 27484 : size_t sz = pp[0];
3617 : 27484 : jl_gc_counted_free_with_size(pp, sz + JL_SMALL_BYTE_ALIGNMENT);
3618 : : }
3619 : 27485 : }
3620 : :
3621 : 640 : JL_DLLEXPORT void *jl_realloc(void *p, size_t sz)
3622 : : {
3623 : : int64_t *pp;
3624 : : size_t szold;
3625 [ + + ]: 640 : if (p == NULL) {
3626 : 1 : pp = NULL;
3627 : 1 : szold = 0;
3628 : : }
3629 : : else {
3630 : 639 : pp = (int64_t *)p - 2;
3631 : 639 : szold = pp[0] + JL_SMALL_BYTE_ALIGNMENT;
3632 : : }
3633 : 640 : int64_t *pnew = (int64_t *)jl_gc_counted_realloc_with_old_size(pp, szold, sz + JL_SMALL_BYTE_ALIGNMENT);
3634 [ - + ]: 640 : if (pnew == NULL)
3635 : 0 : return NULL;
3636 : 640 : pnew[0] = sz;
3637 : 640 : return (void *)(pnew + 2); // assumes JL_SMALL_BYTE_ALIGNMENT == 16
3638 : : }
3639 : :
3640 : : // allocating blocks for Arrays and Strings
3641 : :
3642 : 156902 : JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz)
3643 : : {
3644 : 156902 : jl_ptls_t ptls = jl_current_task->ptls;
3645 : 156902 : maybe_collect(ptls);
3646 : 156902 : size_t allocsz = LLT_ALIGN(sz, JL_CACHE_BYTE_ALIGNMENT);
3647 [ - + ]: 156902 : if (allocsz < sz) // overflow in adding offs, size was "negative"
3648 : 0 : jl_throw(jl_memory_exception);
3649 : 156902 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
3650 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + allocsz);
3651 : 156902 : jl_atomic_store_relaxed(&ptls->gc_num.malloc,
3652 : : jl_atomic_load_relaxed(&ptls->gc_num.malloc) + 1);
3653 : 156902 : int last_errno = errno;
3654 : : #ifdef _OS_WINDOWS_
3655 : : DWORD last_error = GetLastError();
3656 : : #endif
3657 : 156902 : void *b = malloc_cache_align(allocsz);
3658 [ - + ]: 156902 : if (b == NULL)
3659 : 0 : jl_throw(jl_memory_exception);
3660 : : #ifdef _OS_WINDOWS_
3661 : : SetLastError(last_error);
3662 : : #endif
3663 : 156902 : errno = last_errno;
3664 : : // jl_gc_managed_malloc is currently always used for allocating array buffers.
3665 : 156902 : maybe_record_alloc_to_profile((jl_value_t*)b, sz, (jl_datatype_t*)jl_buff_tag);
3666 : 156902 : return b;
3667 : : }
3668 : :
3669 : 26613 : static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t oldsz,
3670 : : int isaligned, jl_value_t *owner, int8_t can_collect)
3671 : : {
3672 [ + + ]: 26613 : if (can_collect)
3673 : 22157 : maybe_collect(ptls);
3674 : :
3675 : 26613 : size_t allocsz = LLT_ALIGN(sz, JL_CACHE_BYTE_ALIGNMENT);
3676 [ - + ]: 26613 : if (allocsz < sz) // overflow in adding offs, size was "negative"
3677 : 0 : jl_throw(jl_memory_exception);
3678 : :
3679 [ + + ]: 26613 : if (jl_astaggedvalue(owner)->bits.gc == GC_OLD_MARKED) {
3680 : 10 : ptls->gc_cache.perm_scanned_bytes += allocsz - oldsz;
3681 : 10 : live_bytes += allocsz - oldsz;
3682 : : }
3683 [ + + ]: 26603 : else if (allocsz < oldsz)
3684 : 11 : jl_atomic_store_relaxed(&ptls->gc_num.freed,
3685 : : jl_atomic_load_relaxed(&ptls->gc_num.freed) + (oldsz - allocsz));
3686 : : else
3687 : 26592 : jl_atomic_store_relaxed(&ptls->gc_num.allocd,
3688 : : jl_atomic_load_relaxed(&ptls->gc_num.allocd) + (allocsz - oldsz));
3689 : 26613 : jl_atomic_store_relaxed(&ptls->gc_num.realloc,
3690 : : jl_atomic_load_relaxed(&ptls->gc_num.realloc) + 1);
3691 : :
3692 : 26613 : int last_errno = errno;
3693 : : #ifdef _OS_WINDOWS_
3694 : : DWORD last_error = GetLastError();
3695 : : #endif
3696 : : void *b;
3697 [ + - ]: 26613 : if (isaligned)
3698 : 26613 : b = realloc_cache_align(d, allocsz, oldsz);
3699 : : else
3700 : 0 : b = realloc(d, allocsz);
3701 [ - + ]: 26613 : if (b == NULL)
3702 : 0 : jl_throw(jl_memory_exception);
3703 : : #ifdef _OS_WINDOWS_
3704 : : SetLastError(last_error);
3705 : : #endif
3706 : 26613 : errno = last_errno;
3707 : 26613 : maybe_record_alloc_to_profile((jl_value_t*)b, sz, jl_gc_unknown_type_tag);
3708 : 26613 : return b;
3709 : : }
3710 : :
3711 : 22157 : JL_DLLEXPORT void *jl_gc_managed_realloc(void *d, size_t sz, size_t oldsz,
3712 : : int isaligned, jl_value_t *owner)
3713 : : {
3714 : 22157 : jl_ptls_t ptls = jl_current_task->ptls;
3715 : 22157 : return gc_managed_realloc_(ptls, d, sz, oldsz, isaligned, owner, 1);
3716 : : }
3717 : :
3718 : 1211980 : jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz)
3719 : : {
3720 : 1211980 : size_t len = jl_string_len(s);
3721 [ - + ]: 1211980 : if (sz <= len) return s;
3722 : 1211980 : jl_taggedvalue_t *v = jl_astaggedvalue(s);
3723 : 1211980 : size_t strsz = len + sizeof(size_t) + 1;
3724 [ + + - + ]: 1216440 : if (strsz <= GC_MAX_SZCLASS ||
3725 : : // TODO: because of issue #17971 we can't resize old objects
3726 : 4456 : gc_marked(v->bits.gc)) {
3727 : : // pool allocated; can't be grown in place so allocate a new object.
3728 : 1207520 : jl_value_t *snew = jl_alloc_string(sz);
3729 : 1207520 : memcpy(jl_string_data(snew), jl_string_data(s), len);
3730 : 1207520 : return snew;
3731 : : }
3732 : 4456 : size_t newsz = sz + sizeof(size_t) + 1;
3733 : 4456 : size_t offs = sizeof(bigval_t);
3734 : 4456 : size_t oldsz = LLT_ALIGN(strsz + offs, JL_CACHE_BYTE_ALIGNMENT);
3735 : 4456 : size_t allocsz = LLT_ALIGN(newsz + offs, JL_CACHE_BYTE_ALIGNMENT);
3736 [ - + ]: 4456 : if (allocsz < sz) // overflow in adding offs, size was "negative"
3737 : 0 : jl_throw(jl_memory_exception);
3738 : 4456 : bigval_t *hdr = bigval_header(v);
3739 : 4456 : jl_ptls_t ptls = jl_current_task->ptls;
3740 : 4456 : maybe_collect(ptls); // don't want this to happen during jl_gc_managed_realloc
3741 : 4456 : gc_big_object_unlink(hdr);
3742 : : // TODO: this is not safe since it frees the old pointer. ideally we'd like
3743 : : // the old pointer to be left alone if we can't grow in place.
3744 : : // for now it's up to the caller to make sure there are no references to the
3745 : : // old pointer.
3746 : 4456 : bigval_t *newbig = (bigval_t*)gc_managed_realloc_(ptls, hdr, allocsz, oldsz, 1, s, 0);
3747 : 4456 : newbig->sz = allocsz;
3748 : 4456 : newbig->age = 0;
3749 : 4456 : gc_big_object_link(newbig, &ptls->heap.big_objects);
3750 : 4456 : jl_value_t *snew = jl_valueof(&newbig->header);
3751 : 4456 : *(size_t*)snew = sz;
3752 : 4456 : return snew;
3753 : : }
3754 : :
3755 : : // Perm gen allocator
3756 : : // 2M pool
3757 : : #define GC_PERM_POOL_SIZE (2 * 1024 * 1024)
3758 : : // 20k limit for pool allocation. At most 1% fragmentation
3759 : : #define GC_PERM_POOL_LIMIT (20 * 1024)
3760 : : uv_mutex_t gc_perm_lock;
3761 : : static uintptr_t gc_perm_pool = 0;
3762 : : static uintptr_t gc_perm_end = 0;
3763 : :
3764 : 2165 : static void *gc_perm_alloc_large(size_t sz, int zero, unsigned align, unsigned offset) JL_NOTSAFEPOINT
3765 : : {
3766 : : // `align` must be power of two
3767 [ - + - - ]: 2165 : assert(offset == 0 || offset < align);
3768 : 2165 : const size_t malloc_align = sizeof(void*) == 8 ? 16 : 4;
3769 [ + - + - : 2165 : if (align > 1 && (offset != 0 || align > malloc_align))
+ + ]
3770 : 6 : sz += align - 1;
3771 : 2165 : int last_errno = errno;
3772 : : #ifdef _OS_WINDOWS_
3773 : : DWORD last_error = GetLastError();
3774 : : #endif
3775 [ + + ]: 2165 : uintptr_t base = (uintptr_t)(zero ? calloc(1, sz) : malloc(sz));
3776 [ - + ]: 2165 : if (base == 0)
3777 : 0 : jl_throw(jl_memory_exception);
3778 : : #ifdef _OS_WINDOWS_
3779 : : SetLastError(last_error);
3780 : : #endif
3781 : 2165 : errno = last_errno;
3782 : : jl_may_leak(base);
3783 [ - + ]: 2165 : assert(align > 0);
3784 : 2165 : unsigned diff = (offset - base) % align;
3785 : 2165 : return (void*)(base + diff);
3786 : : }
3787 : :
3788 : 22579700 : STATIC_INLINE void *gc_try_perm_alloc_pool(size_t sz, unsigned align, unsigned offset) JL_NOTSAFEPOINT
3789 : : {
3790 : 22579700 : uintptr_t pool = LLT_ALIGN(gc_perm_pool + offset, (uintptr_t)align) - offset;
3791 : 22579700 : uintptr_t end = pool + sz;
3792 [ + + ]: 22579700 : if (end > gc_perm_end)
3793 : 605 : return NULL;
3794 : 22579100 : gc_perm_pool = end;
3795 : 22579100 : return (void*)jl_assume(pool);
3796 : : }
3797 : :
3798 : : // **NOT** a safepoint
3799 : 22581300 : void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offset)
3800 : : {
3801 : : // The caller should have acquired `gc_perm_lock`
3802 [ - + ]: 22581300 : assert(align < GC_PERM_POOL_LIMIT);
3803 : : #ifndef MEMDEBUG
3804 [ + + ]: 22581300 : if (__unlikely(sz > GC_PERM_POOL_LIMIT))
3805 : : #endif
3806 : 2155 : return gc_perm_alloc_large(sz, zero, align, offset);
3807 : 22579100 : void *ptr = gc_try_perm_alloc_pool(sz, align, offset);
3808 [ + + ]: 22579100 : if (__likely(ptr))
3809 : 22578500 : return ptr;
3810 : 605 : int last_errno = errno;
3811 : : #ifdef _OS_WINDOWS_
3812 : : DWORD last_error = GetLastError();
3813 : : void *pool = VirtualAlloc(NULL, GC_PERM_POOL_SIZE, MEM_COMMIT, PAGE_READWRITE);
3814 : : SetLastError(last_error);
3815 : : errno = last_errno;
3816 : : if (__unlikely(pool == NULL))
3817 : : return NULL;
3818 : : #else
3819 : 605 : void *pool = mmap(0, GC_PERM_POOL_SIZE, PROT_READ | PROT_WRITE,
3820 : : MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
3821 : 605 : errno = last_errno;
3822 [ - + ]: 605 : if (__unlikely(pool == MAP_FAILED))
3823 : 0 : return NULL;
3824 : : #endif
3825 : 605 : gc_perm_pool = (uintptr_t)pool;
3826 : 605 : gc_perm_end = gc_perm_pool + GC_PERM_POOL_SIZE;
3827 : 605 : return gc_try_perm_alloc_pool(sz, align, offset);
3828 : : }
3829 : :
3830 : : // **NOT** a safepoint
3831 : 6987690 : void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align, unsigned offset)
3832 : : {
3833 [ - + ]: 6987690 : assert(align < GC_PERM_POOL_LIMIT);
3834 : : #ifndef MEMDEBUG
3835 [ + + ]: 6987690 : if (__unlikely(sz > GC_PERM_POOL_LIMIT))
3836 : : #endif
3837 : 10 : return gc_perm_alloc_large(sz, zero, align, offset);
3838 : 6987680 : uv_mutex_lock(&gc_perm_lock);
3839 : 6987680 : void *p = jl_gc_perm_alloc_nolock(sz, zero, align, offset);
3840 : 6987680 : uv_mutex_unlock(&gc_perm_lock);
3841 : 6987680 : return p;
3842 : : }
3843 : :
3844 : 0 : JL_DLLEXPORT void jl_gc_add_finalizer(jl_value_t *v, jl_function_t *f)
3845 : : {
3846 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3847 : 0 : jl_gc_add_finalizer_th(ptls, v, f);
3848 : 0 : }
3849 : :
3850 : 0 : JL_DLLEXPORT void jl_finalize(jl_value_t *o)
3851 : : {
3852 : 0 : jl_finalize_th(jl_current_task, o);
3853 : 0 : }
3854 : :
3855 : 0 : JL_DLLEXPORT jl_weakref_t *jl_gc_new_weakref(jl_value_t *value)
3856 : : {
3857 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3858 : 0 : return jl_gc_new_weakref_th(ptls, value);
3859 : : }
3860 : :
3861 : 0 : JL_DLLEXPORT jl_value_t *jl_gc_allocobj(size_t sz)
3862 : : {
3863 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3864 : 0 : return jl_gc_alloc(ptls, sz, NULL);
3865 : : }
3866 : :
3867 : 0 : JL_DLLEXPORT jl_value_t *jl_gc_alloc_0w(void)
3868 : : {
3869 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3870 : 0 : return jl_gc_alloc(ptls, 0, NULL);
3871 : : }
3872 : :
3873 : 2 : JL_DLLEXPORT jl_value_t *jl_gc_alloc_1w(void)
3874 : : {
3875 : 2 : jl_ptls_t ptls = jl_current_task->ptls;
3876 : 2 : return jl_gc_alloc(ptls, sizeof(void*), NULL);
3877 : : }
3878 : :
3879 : 0 : JL_DLLEXPORT jl_value_t *jl_gc_alloc_2w(void)
3880 : : {
3881 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3882 : 0 : return jl_gc_alloc(ptls, sizeof(void*) * 2, NULL);
3883 : : }
3884 : :
3885 : 0 : JL_DLLEXPORT jl_value_t *jl_gc_alloc_3w(void)
3886 : : {
3887 : 0 : jl_ptls_t ptls = jl_current_task->ptls;
3888 : 0 : return jl_gc_alloc(ptls, sizeof(void*) * 3, NULL);
3889 : : }
3890 : :
3891 : 0 : JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void)
3892 : : {
3893 : : static_assert(jl_buff_tag % GC_PAGE_SZ == 0,
3894 : : "jl_buff_tag must be a multiple of GC_PAGE_SZ");
3895 [ # # ]: 0 : if (jl_is_initialized()) {
3896 : 0 : int result = jl_atomic_fetch_or(&support_conservative_marking, 1);
3897 [ # # ]: 0 : if (!result) {
3898 : : // Do a full collection to ensure that age bits are updated
3899 : : // properly. We don't have to worry about race conditions
3900 : : // for this part, as allocation itself is unproblematic and
3901 : : // a collection will wait for safepoints.
3902 : 0 : jl_gc_collect(JL_GC_FULL);
3903 : : }
3904 : 0 : return result;
3905 : : } else {
3906 : 0 : int result = jl_atomic_load(&support_conservative_marking);
3907 : 0 : jl_atomic_store(&support_conservative_marking, 1);
3908 : 0 : return result;
3909 : : }
3910 : : }
3911 : :
3912 : 14024 : JL_DLLEXPORT int jl_gc_conservative_gc_support_enabled(void)
3913 : : {
3914 : 14024 : return jl_atomic_load(&support_conservative_marking);
3915 : : }
3916 : :
3917 : 0 : JL_DLLEXPORT jl_value_t *jl_gc_internal_obj_base_ptr(void *p)
3918 : : {
3919 : 0 : p = (char *) p - 1;
3920 : 0 : jl_gc_pagemeta_t *meta = page_metadata(p);
3921 [ # # # # ]: 0 : if (meta && meta->ages) {
3922 : 0 : char *page = gc_page_data(p);
3923 : : // offset within page.
3924 : 0 : size_t off = (char *)p - page;
3925 [ # # ]: 0 : if (off < GC_PAGE_OFFSET)
3926 : 0 : return NULL;
3927 : : // offset within object
3928 : 0 : size_t off2 = (off - GC_PAGE_OFFSET);
3929 : 0 : size_t osize = meta->osize;
3930 : 0 : off2 %= osize;
3931 [ # # ]: 0 : if (off - off2 + osize > GC_PAGE_SZ)
3932 : 0 : return NULL;
3933 : 0 : jl_taggedvalue_t *cell = (jl_taggedvalue_t *)((char *)p - off2);
3934 : : // We have to distinguish between three cases:
3935 : : // 1. We are on a page where every cell is allocated.
3936 : : // 2. We are on a page where objects are currently bump-allocated
3937 : : // from the corresponding pool->newpages list.
3938 : : // 3. We are on a page with a freelist that is used for object
3939 : : // allocation.
3940 [ # # ]: 0 : if (meta->nfree == 0) {
3941 : : // case 1: full page; `cell` must be an object
3942 : 0 : goto valid_object;
3943 : : }
3944 : 0 : jl_gc_pool_t *pool =
3945 : 0 : jl_all_tls_states[meta->thread_n]->heap.norm_pools +
3946 : 0 : meta->pool_n;
3947 [ # # ]: 0 : if (meta->fl_begin_offset == (uint16_t) -1) {
3948 : : // case 2: this is a page on the newpages list
3949 : 0 : jl_taggedvalue_t *newpages = pool->newpages;
3950 : : // Check if the page is being allocated from via newpages
3951 [ # # ]: 0 : if (!newpages)
3952 : 0 : return NULL;
3953 : 0 : char *data = gc_page_data(newpages);
3954 [ # # ]: 0 : if (data != meta->data) {
3955 : : // Pages on newpages form a linked list where only the
3956 : : // first one is allocated from (see reset_page()).
3957 : : // All other pages are empty.
3958 : 0 : return NULL;
3959 : : }
3960 : : // This is the first page on the newpages list, where objects
3961 : : // are allocated from.
3962 [ # # ]: 0 : if ((char *)cell >= (char *)newpages) // past allocation pointer
3963 : 0 : return NULL;
3964 : 0 : goto valid_object;
3965 : : }
3966 : : // case 3: this is a page with a freelist
3967 : : // marked or old objects can't be on the freelist
3968 [ # # ]: 0 : if (cell->bits.gc)
3969 : 0 : goto valid_object;
3970 : : // When allocating from a freelist, three subcases are possible:
3971 : : // * The freelist of a page has been exhausted; this was handled
3972 : : // under case 1, as nfree == 0.
3973 : : // * The freelist of the page has not been used, and the age bits
3974 : : // reflect whether a cell is on the freelist or an object.
3975 : : // * The freelist is currently being allocated from. In this case,
3976 : : // pool->freelist will point to the current page; any cell with
3977 : : // a lower address will be an allocated object, and for cells
3978 : : // with the same or a higher address, the corresponding age
3979 : : // bit will reflect whether it's on the freelist.
3980 : : // Age bits are set in sweep_page() and are 0 for freelist
3981 : : // entries and 1 for live objects. The above subcases arise
3982 : : // because allocating a cell will not update the age bit, so we
3983 : : // need extra logic for pages that have been allocated from.
3984 : 0 : unsigned obj_id = (off - off2) / osize;
3985 : : // We now distinguish between the second and third subcase.
3986 : : // Freelist entries are consumed in ascending order. Anything
3987 : : // before the freelist pointer was either live during the last
3988 : : // sweep or has been allocated since.
3989 [ # # ]: 0 : if (gc_page_data(cell) == gc_page_data(pool->freelist)
3990 [ # # ]: 0 : && (char *)cell < (char *)pool->freelist)
3991 : 0 : goto valid_object;
3992 : : // We know now that the age bit reflects liveness status during
3993 : : // the last sweep and that the cell has not been reused since.
3994 [ # # ]: 0 : if (!(meta->ages[obj_id / 8] & (1 << (obj_id % 8)))) {
3995 : 0 : return NULL;
3996 : : }
3997 : : // Not a freelist entry, therefore a valid object.
3998 : 0 : valid_object:
3999 : : // We have to treat objects with type `jl_buff_tag` differently,
4000 : : // as they must not be passed to the usual marking functions.
4001 : : // Note that jl_buff_tag is a multiple of GC_PAGE_SZ, thus it
4002 : : // cannot be a type reference.
4003 [ # # ]: 0 : if ((cell->header & ~(uintptr_t) 3) == jl_buff_tag)
4004 : 0 : return NULL;
4005 : 0 : return jl_valueof(cell);
4006 : : }
4007 : 0 : return NULL;
4008 : : }
4009 : :
4010 : 0 : JL_DLLEXPORT size_t jl_gc_max_internal_obj_size(void)
4011 : : {
4012 : 0 : return GC_MAX_SZCLASS;
4013 : : }
4014 : :
4015 : 0 : JL_DLLEXPORT size_t jl_gc_external_obj_hdr_size(void)
4016 : : {
4017 : 0 : return sizeof(bigval_t);
4018 : : }
4019 : :
4020 : :
4021 : 0 : JL_DLLEXPORT void * jl_gc_alloc_typed(jl_ptls_t ptls, size_t sz, void *ty)
4022 : : {
4023 : 0 : return jl_gc_alloc(ptls, sz, ty);
4024 : : }
4025 : :
4026 : 0 : JL_DLLEXPORT void jl_gc_schedule_foreign_sweepfunc(jl_ptls_t ptls, jl_value_t *obj)
4027 : : {
4028 : 0 : arraylist_push(&ptls->sweep_objs, obj);
4029 : 0 : }
4030 : :
4031 : : #ifdef __cplusplus
4032 : : }
4033 : : #endif
|