17dd7cddfSDavid du Colombier /* Copyright (C) 1993, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
27dd7cddfSDavid du Colombier
3*593dc095SDavid du Colombier This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier implied.
57dd7cddfSDavid du Colombier
6*593dc095SDavid du Colombier This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier
10*593dc095SDavid du Colombier For more information about licensing, please refer to
11*593dc095SDavid du Colombier http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier San Rafael, CA 94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier
17*593dc095SDavid du Colombier /* $Id: igc.c,v 1.15 2005/09/05 13:58:55 leonardo Exp $ */
187dd7cddfSDavid du Colombier /* Garbage collector for Ghostscript */
197dd7cddfSDavid du Colombier #include "memory_.h"
207dd7cddfSDavid du Colombier #include "ghost.h"
21*593dc095SDavid du Colombier #include "ierrors.h"
227dd7cddfSDavid du Colombier #include "gsexit.h"
237dd7cddfSDavid du Colombier #include "gsmdebug.h"
247dd7cddfSDavid du Colombier #include "gsstruct.h"
257dd7cddfSDavid du Colombier #include "gsutil.h"
267dd7cddfSDavid du Colombier #include "iastate.h"
277dd7cddfSDavid du Colombier #include "isave.h"
287dd7cddfSDavid du Colombier #include "isstate.h"
297dd7cddfSDavid du Colombier #include "idict.h"
307dd7cddfSDavid du Colombier #include "ipacked.h"
317dd7cddfSDavid du Colombier #include "istruct.h"
327dd7cddfSDavid du Colombier #include "igc.h"
337dd7cddfSDavid du Colombier #include "igcstr.h"
347dd7cddfSDavid du Colombier #include "inamedef.h"
357dd7cddfSDavid du Colombier #include "opdef.h" /* for marking oparray names */
367dd7cddfSDavid du Colombier
377dd7cddfSDavid du Colombier /* Define whether to force all garbage collections to be global. */
383ff48bf5SDavid du Colombier private const bool I_FORCE_GLOBAL_GC = false;
397dd7cddfSDavid du Colombier
407dd7cddfSDavid du Colombier /* Define whether to bypass the collector entirely. */
413ff48bf5SDavid du Colombier private const bool I_BYPASS_GC = false;
427dd7cddfSDavid du Colombier
437dd7cddfSDavid du Colombier /* Define an entry on the mark stack. */
447dd7cddfSDavid du Colombier typedef struct {
457dd7cddfSDavid du Colombier void *ptr;
467dd7cddfSDavid du Colombier uint index;
477dd7cddfSDavid du Colombier bool is_refs;
487dd7cddfSDavid du Colombier } ms_entry;
497dd7cddfSDavid du Colombier
507dd7cddfSDavid du Colombier /* Define (a segment of) the mark stack. */
517dd7cddfSDavid du Colombier /* entries[0] has ptr = 0 to indicate the bottom of the stack. */
527dd7cddfSDavid du Colombier /* count additional entries follow this structure. */
537dd7cddfSDavid du Colombier typedef struct gc_mark_stack_s gc_mark_stack;
547dd7cddfSDavid du Colombier struct gc_mark_stack_s {
557dd7cddfSDavid du Colombier gc_mark_stack *prev;
567dd7cddfSDavid du Colombier gc_mark_stack *next;
577dd7cddfSDavid du Colombier uint count;
587dd7cddfSDavid du Colombier bool on_heap; /* if true, allocated during GC */
597dd7cddfSDavid du Colombier ms_entry entries[1];
607dd7cddfSDavid du Colombier };
617dd7cddfSDavid du Colombier
627dd7cddfSDavid du Colombier /* Define the mark stack sizing parameters. */
637dd7cddfSDavid du Colombier #define ms_size_default 100 /* default, allocated on C stack */
647dd7cddfSDavid du Colombier /* This should probably be defined as a parameter somewhere.... */
657dd7cddfSDavid du Colombier #define ms_size_desired /* for additional allocation */\
667dd7cddfSDavid du Colombier ((max_ushort - sizeof(gc_mark_stack)) / sizeof(ms_entry) - 10)
677dd7cddfSDavid du Colombier #define ms_size_min 50 /* min size for segment in free block */
687dd7cddfSDavid du Colombier
697dd7cddfSDavid du Colombier /* Forward references */
70*593dc095SDavid du Colombier
71*593dc095SDavid du Colombier private void gc_init_mark_stack(gc_mark_stack *, uint);
72*593dc095SDavid du Colombier private void gc_objects_clear_marks(const gs_memory_t *mem, chunk_t *);
73*593dc095SDavid du Colombier private void gc_unmark_names(name_table *);
74*593dc095SDavid du Colombier private int gc_trace(gs_gc_root_t *, gc_state_t *, gc_mark_stack *);
75*593dc095SDavid du Colombier private int gc_rescan_chunk(chunk_t *, gc_state_t *, gc_mark_stack *);
76*593dc095SDavid du Colombier private int gc_trace_chunk(const gs_memory_t *mem, chunk_t *, gc_state_t *, gc_mark_stack *);
77*593dc095SDavid du Colombier private bool gc_trace_finish(gc_state_t *);
78*593dc095SDavid du Colombier private void gc_clear_reloc(chunk_t *);
79*593dc095SDavid du Colombier private void gc_objects_set_reloc(chunk_t *);
80*593dc095SDavid du Colombier private void gc_do_reloc(chunk_t *, gs_ref_memory_t *, gc_state_t *);
81*593dc095SDavid du Colombier private void gc_objects_compact(chunk_t *, gc_state_t *);
82*593dc095SDavid du Colombier private void gc_free_empty_chunks(gs_ref_memory_t *);
837dd7cddfSDavid du Colombier
847dd7cddfSDavid du Colombier /* Forward references for pointer types */
857dd7cddfSDavid du Colombier private ptr_proc_unmark(ptr_struct_unmark);
867dd7cddfSDavid du Colombier private ptr_proc_mark(ptr_struct_mark);
877dd7cddfSDavid du Colombier private ptr_proc_unmark(ptr_string_unmark);
887dd7cddfSDavid du Colombier private ptr_proc_mark(ptr_string_mark);
897dd7cddfSDavid du Colombier /*ptr_proc_unmark(ptr_ref_unmark); *//* in igc.h */
907dd7cddfSDavid du Colombier /*ptr_proc_mark(ptr_ref_mark); *//* in igc.h */
917dd7cddfSDavid du Colombier private ptr_proc_reloc(igc_reloc_struct_ptr, void);
927dd7cddfSDavid du Colombier
937dd7cddfSDavid du Colombier ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed); /* in igcref.c */
947dd7cddfSDavid du Colombier refs_proc_reloc(igc_reloc_refs); /* in igcref.c */
957dd7cddfSDavid du Colombier
967dd7cddfSDavid du Colombier /* Define this GC's procedure vector. */
977dd7cddfSDavid du Colombier private const gc_procs_with_refs_t igc_procs = {
987dd7cddfSDavid du Colombier igc_reloc_struct_ptr, igc_reloc_string, igc_reloc_const_string,
99*593dc095SDavid du Colombier igc_reloc_param_string, igc_reloc_ref_ptr, igc_reloc_refs
1007dd7cddfSDavid du Colombier };
1017dd7cddfSDavid du Colombier
1027dd7cddfSDavid du Colombier /* Pointer type descriptors. */
1037dd7cddfSDavid du Colombier /* Note that the trace/mark routine has special knowledge of ptr_ref_type */
1047dd7cddfSDavid du Colombier /* and ptr_struct_type -- it assumes that no other types have embedded */
1057dd7cddfSDavid du Colombier /* pointers. Note also that the reloc procedures for string and ref */
1067dd7cddfSDavid du Colombier /* pointers are never called. */
1077dd7cddfSDavid du Colombier typedef ptr_proc_reloc((*ptr_proc_reloc_t), void);
1087dd7cddfSDavid du Colombier const gs_ptr_procs_t ptr_struct_procs =
1097dd7cddfSDavid du Colombier {ptr_struct_unmark, ptr_struct_mark, (ptr_proc_reloc_t) igc_reloc_struct_ptr};
1107dd7cddfSDavid du Colombier const gs_ptr_procs_t ptr_string_procs =
1117dd7cddfSDavid du Colombier {ptr_string_unmark, ptr_string_mark, NULL};
1127dd7cddfSDavid du Colombier const gs_ptr_procs_t ptr_const_string_procs =
1137dd7cddfSDavid du Colombier {ptr_string_unmark, ptr_string_mark, NULL};
1147dd7cddfSDavid du Colombier const gs_ptr_procs_t ptr_ref_procs =
1157dd7cddfSDavid du Colombier {ptr_ref_unmark, ptr_ref_mark, NULL};
1167dd7cddfSDavid du Colombier
1177dd7cddfSDavid du Colombier /* ------ Main program ------ */
1187dd7cddfSDavid du Colombier
1197dd7cddfSDavid du Colombier /* Top level of garbage collector. */
1207dd7cddfSDavid du Colombier #ifdef DEBUG
1217dd7cddfSDavid du Colombier private void
end_phase(const char * str)1227dd7cddfSDavid du Colombier end_phase(const char *str)
1237dd7cddfSDavid du Colombier {
1247dd7cddfSDavid du Colombier if (gs_debug_c('6')) {
1257dd7cddfSDavid du Colombier dlprintf1("[6]---------------- end %s ----------------\n",
1267dd7cddfSDavid du Colombier (const char *)str);
1273ff48bf5SDavid du Colombier dflush();
1287dd7cddfSDavid du Colombier }
1297dd7cddfSDavid du Colombier }
1307dd7cddfSDavid du Colombier static const char *const depth_dots_string = "..........";
1317dd7cddfSDavid du Colombier private const char *
depth_dots(const ms_entry * sp,const gc_mark_stack * pms)1327dd7cddfSDavid du Colombier depth_dots(const ms_entry * sp, const gc_mark_stack * pms)
1337dd7cddfSDavid du Colombier {
1347dd7cddfSDavid du Colombier int depth = sp - pms->entries - 1;
1357dd7cddfSDavid du Colombier const gc_mark_stack *pss = pms;
1367dd7cddfSDavid du Colombier
1377dd7cddfSDavid du Colombier while ((pss = pss->prev) != 0)
1387dd7cddfSDavid du Colombier depth += pss->count - 1;
1397dd7cddfSDavid du Colombier return depth_dots_string + (depth >= 10 ? 0 : 10 - depth);
1407dd7cddfSDavid du Colombier }
1417dd7cddfSDavid du Colombier private void
gc_validate_spaces(gs_ref_memory_t ** spaces,int max_space,gc_state_t * gcst)1427dd7cddfSDavid du Colombier gc_validate_spaces(gs_ref_memory_t **spaces, int max_space, gc_state_t *gcst)
1437dd7cddfSDavid du Colombier {
1447dd7cddfSDavid du Colombier int i;
1457dd7cddfSDavid du Colombier gs_ref_memory_t *mem;
1467dd7cddfSDavid du Colombier
1477dd7cddfSDavid du Colombier for (i = 1; i <= max_space; ++i)
1487dd7cddfSDavid du Colombier if ((mem = spaces[i]) != 0)
1497dd7cddfSDavid du Colombier ialloc_validate_memory(mem, gcst);
1507dd7cddfSDavid du Colombier }
1517dd7cddfSDavid du Colombier #else /* !DEBUG */
1527dd7cddfSDavid du Colombier # define end_phase(str) DO_NOTHING
1537dd7cddfSDavid du Colombier #endif /* DEBUG */
1547dd7cddfSDavid du Colombier void
gs_gc_reclaim(vm_spaces * pspaces,bool global)1557dd7cddfSDavid du Colombier gs_gc_reclaim(vm_spaces * pspaces, bool global)
1567dd7cddfSDavid du Colombier {
1577dd7cddfSDavid du Colombier #define nspaces ((i_vm_max + 1) * 2) /* * 2 for stable allocators */
1587dd7cddfSDavid du Colombier
1597dd7cddfSDavid du Colombier vm_spaces spaces;
1607dd7cddfSDavid du Colombier gs_ref_memory_t *space_memories[nspaces];
1617dd7cddfSDavid du Colombier gs_gc_root_t space_roots[nspaces];
1627dd7cddfSDavid du Colombier int max_trace; /* max space_ to trace */
1637dd7cddfSDavid du Colombier int min_collect; /* min space_ to collect */
1647dd7cddfSDavid du Colombier int min_collect_vm_space; /* min VM space to collect */
1657dd7cddfSDavid du Colombier int ispace;
1667dd7cddfSDavid du Colombier gs_ref_memory_t *mem;
1677dd7cddfSDavid du Colombier chunk_t *cp;
1687dd7cddfSDavid du Colombier gs_gc_root_t *rp;
1697dd7cddfSDavid du Colombier gc_state_t state;
1707dd7cddfSDavid du Colombier struct _msd {
1717dd7cddfSDavid du Colombier gc_mark_stack stack;
1727dd7cddfSDavid du Colombier ms_entry body[ms_size_default];
1737dd7cddfSDavid du Colombier } ms_default;
1747dd7cddfSDavid du Colombier gc_mark_stack *mark_stack = &ms_default.stack;
175*593dc095SDavid du Colombier const gs_memory_t *cmem;
1767dd7cddfSDavid du Colombier
1777dd7cddfSDavid du Colombier /* Optionally force global GC for debugging. */
1787dd7cddfSDavid du Colombier
1797dd7cddfSDavid du Colombier if (I_FORCE_GLOBAL_GC)
1807dd7cddfSDavid du Colombier global = true;
1817dd7cddfSDavid du Colombier
1827dd7cddfSDavid du Colombier /* Determine which spaces we are tracing and collecting. */
1837dd7cddfSDavid du Colombier
1847dd7cddfSDavid du Colombier spaces = *pspaces;
185*593dc095SDavid du Colombier cmem = space_system->stable_memory;
1867dd7cddfSDavid du Colombier space_memories[1] = space_system;
1877dd7cddfSDavid du Colombier space_memories[2] = space_global;
1887dd7cddfSDavid du Colombier min_collect = max_trace = 2;
1897dd7cddfSDavid du Colombier min_collect_vm_space = i_vm_global;
1907dd7cddfSDavid du Colombier if (space_global->stable_memory != (gs_memory_t *)space_global)
1917dd7cddfSDavid du Colombier space_memories[++max_trace] =
1927dd7cddfSDavid du Colombier (gs_ref_memory_t *)space_global->stable_memory;
1937dd7cddfSDavid du Colombier if (space_global != space_local) {
1947dd7cddfSDavid du Colombier space_memories[++max_trace] = space_local;
1957dd7cddfSDavid du Colombier min_collect = max_trace;
1967dd7cddfSDavid du Colombier min_collect_vm_space = i_vm_local;
1977dd7cddfSDavid du Colombier if (space_local->stable_memory != (gs_memory_t *)space_local)
1987dd7cddfSDavid du Colombier space_memories[++max_trace] =
1997dd7cddfSDavid du Colombier (gs_ref_memory_t *)space_local->stable_memory;
2007dd7cddfSDavid du Colombier }
2017dd7cddfSDavid du Colombier if (global)
2027dd7cddfSDavid du Colombier min_collect = min_collect_vm_space = 1;
2037dd7cddfSDavid du Colombier
2047dd7cddfSDavid du Colombier #define for_spaces(i, n)\
2057dd7cddfSDavid du Colombier for (i = 1; i <= n; ++i)
2067dd7cddfSDavid du Colombier #define for_collected_spaces(i)\
2077dd7cddfSDavid du Colombier for (i = min_collect; i <= max_trace; ++i)
2087dd7cddfSDavid du Colombier #define for_space_mems(i, mem)\
2097dd7cddfSDavid du Colombier for (mem = space_memories[i]; mem != 0; mem = &mem->saved->state)
2107dd7cddfSDavid du Colombier #define for_mem_chunks(mem, cp)\
2117dd7cddfSDavid du Colombier for (cp = (mem)->cfirst; cp != 0; cp = cp->cnext)
2127dd7cddfSDavid du Colombier #define for_space_chunks(i, mem, cp)\
2137dd7cddfSDavid du Colombier for_space_mems(i, mem) for_mem_chunks(mem, cp)
2147dd7cddfSDavid du Colombier #define for_chunks(n, mem, cp)\
2157dd7cddfSDavid du Colombier for_spaces(ispace, n) for_space_chunks(ispace, mem, cp)
2167dd7cddfSDavid du Colombier #define for_collected_chunks(mem, cp)\
2177dd7cddfSDavid du Colombier for_collected_spaces(ispace) for_space_chunks(ispace, mem, cp)
2187dd7cddfSDavid du Colombier #define for_roots(n, mem, rp)\
2197dd7cddfSDavid du Colombier for_spaces(ispace, n)\
2207dd7cddfSDavid du Colombier for (mem = space_memories[ispace], rp = mem->roots; rp != 0; rp = rp->next)
2217dd7cddfSDavid du Colombier
2227dd7cddfSDavid du Colombier /* Initialize the state. */
2237dd7cddfSDavid du Colombier
2247dd7cddfSDavid du Colombier state.procs = &igc_procs;
2257dd7cddfSDavid du Colombier state.loc.memory = space_global; /* any one will do */
2267dd7cddfSDavid du Colombier
2277dd7cddfSDavid du Colombier state.loc.cp = 0;
2287dd7cddfSDavid du Colombier state.spaces = spaces;
2297dd7cddfSDavid du Colombier state.min_collect = min_collect_vm_space << r_space_shift;
2307dd7cddfSDavid du Colombier state.relocating_untraced = false;
231*593dc095SDavid du Colombier state.heap = state.loc.memory->non_gc_memory;
232*593dc095SDavid du Colombier state.ntable = state.heap->gs_lib_ctx->gs_name_table;
2337dd7cddfSDavid du Colombier
2347dd7cddfSDavid du Colombier /* Register the allocators themselves as roots, */
2357dd7cddfSDavid du Colombier /* so we mark and relocate the change and save lists properly. */
2367dd7cddfSDavid du Colombier
2377dd7cddfSDavid du Colombier for_spaces(ispace, max_trace)
2387dd7cddfSDavid du Colombier gs_register_struct_root((gs_memory_t *)space_memories[ispace],
2397dd7cddfSDavid du Colombier &space_roots[ispace],
2407dd7cddfSDavid du Colombier (void **)&space_memories[ispace],
2417dd7cddfSDavid du Colombier "gc_top_level");
2427dd7cddfSDavid du Colombier
2437dd7cddfSDavid du Colombier end_phase("register space roots");
2447dd7cddfSDavid du Colombier
2457dd7cddfSDavid du Colombier #ifdef DEBUG
2467dd7cddfSDavid du Colombier
2477dd7cddfSDavid du Colombier /* Pre-validate the state. This shouldn't be necessary.... */
2487dd7cddfSDavid du Colombier
2497dd7cddfSDavid du Colombier gc_validate_spaces(space_memories, max_trace, &state);
2507dd7cddfSDavid du Colombier
2517dd7cddfSDavid du Colombier end_phase("pre-validate pointers");
2527dd7cddfSDavid du Colombier
2537dd7cddfSDavid du Colombier #endif
2547dd7cddfSDavid du Colombier
2557dd7cddfSDavid du Colombier if (I_BYPASS_GC) { /* Don't collect at all. */
2567dd7cddfSDavid du Colombier goto no_collect;
2577dd7cddfSDavid du Colombier }
2587dd7cddfSDavid du Colombier
2597dd7cddfSDavid du Colombier /* Clear marks in spaces to be collected. */
2607dd7cddfSDavid du Colombier
2617dd7cddfSDavid du Colombier for_collected_spaces(ispace)
2627dd7cddfSDavid du Colombier for_space_chunks(ispace, mem, cp) {
263*593dc095SDavid du Colombier gc_objects_clear_marks((const gs_memory_t *)mem, cp);
2647dd7cddfSDavid du Colombier gc_strings_set_marks(cp, false);
2657dd7cddfSDavid du Colombier }
2667dd7cddfSDavid du Colombier
2677dd7cddfSDavid du Colombier end_phase("clear chunk marks");
2687dd7cddfSDavid du Colombier
2697dd7cddfSDavid du Colombier /* Clear the marks of roots. We must do this explicitly, */
2707dd7cddfSDavid du Colombier /* since some roots are not in any chunk. */
2717dd7cddfSDavid du Colombier
2727dd7cddfSDavid du Colombier for_roots(max_trace, mem, rp) {
2737dd7cddfSDavid du Colombier enum_ptr_t eptr;
2747dd7cddfSDavid du Colombier
2757dd7cddfSDavid du Colombier eptr.ptr = *rp->p;
2767dd7cddfSDavid du Colombier if_debug_root('6', "[6]unmarking root", rp);
2777dd7cddfSDavid du Colombier (*rp->ptype->unmark)(&eptr, &state);
2787dd7cddfSDavid du Colombier }
2797dd7cddfSDavid du Colombier
2807dd7cddfSDavid du Colombier end_phase("clear root marks");
2817dd7cddfSDavid du Colombier
2827dd7cddfSDavid du Colombier if (global)
2837dd7cddfSDavid du Colombier gc_unmark_names(state.ntable);
2847dd7cddfSDavid du Colombier
2857dd7cddfSDavid du Colombier /* Initialize the (default) mark stack. */
2867dd7cddfSDavid du Colombier
2877dd7cddfSDavid du Colombier gc_init_mark_stack(&ms_default.stack, ms_size_default);
2887dd7cddfSDavid du Colombier ms_default.stack.prev = 0;
2897dd7cddfSDavid du Colombier ms_default.stack.on_heap = false;
2907dd7cddfSDavid du Colombier
2917dd7cddfSDavid du Colombier /* Add all large-enough free blocks to the mark stack. */
2927dd7cddfSDavid du Colombier /* Also initialize the rescan pointers. */
2937dd7cddfSDavid du Colombier
2947dd7cddfSDavid du Colombier {
2957dd7cddfSDavid du Colombier gc_mark_stack *end = mark_stack;
2967dd7cddfSDavid du Colombier
2977dd7cddfSDavid du Colombier for_chunks(max_trace, mem, cp) {
2987dd7cddfSDavid du Colombier uint avail = cp->ctop - cp->cbot;
2997dd7cddfSDavid du Colombier
3007dd7cddfSDavid du Colombier if (avail >= sizeof(gc_mark_stack) + sizeof(ms_entry) *
3017dd7cddfSDavid du Colombier ms_size_min &&
3027dd7cddfSDavid du Colombier !cp->inner_count
3037dd7cddfSDavid du Colombier ) {
3047dd7cddfSDavid du Colombier gc_mark_stack *pms = (gc_mark_stack *) cp->cbot;
3057dd7cddfSDavid du Colombier
3067dd7cddfSDavid du Colombier gc_init_mark_stack(pms, (avail - sizeof(gc_mark_stack)) /
3077dd7cddfSDavid du Colombier sizeof(ms_entry));
3087dd7cddfSDavid du Colombier end->next = pms;
3097dd7cddfSDavid du Colombier pms->prev = end;
3107dd7cddfSDavid du Colombier pms->on_heap = false;
3117dd7cddfSDavid du Colombier if_debug2('6', "[6]adding free 0x%lx(%u) to mark stack\n",
3127dd7cddfSDavid du Colombier (ulong) pms, pms->count);
3137dd7cddfSDavid du Colombier }
3147dd7cddfSDavid du Colombier cp->rescan_bot = cp->cend;
3157dd7cddfSDavid du Colombier cp->rescan_top = cp->cbase;
3167dd7cddfSDavid du Colombier }
3177dd7cddfSDavid du Colombier }
3187dd7cddfSDavid du Colombier
3197dd7cddfSDavid du Colombier /* Mark reachable objects. */
3207dd7cddfSDavid du Colombier
3217dd7cddfSDavid du Colombier {
3227dd7cddfSDavid du Colombier int more = 0;
3237dd7cddfSDavid du Colombier
3247dd7cddfSDavid du Colombier /* Mark from roots. */
3257dd7cddfSDavid du Colombier
3267dd7cddfSDavid du Colombier for_roots(max_trace, mem, rp) {
3277dd7cddfSDavid du Colombier if_debug_root('6', "[6]marking root", rp);
3287dd7cddfSDavid du Colombier more |= gc_trace(rp, &state, mark_stack);
3297dd7cddfSDavid du Colombier }
3307dd7cddfSDavid du Colombier
3317dd7cddfSDavid du Colombier end_phase("mark");
3327dd7cddfSDavid du Colombier
3337dd7cddfSDavid du Colombier /* If this is a local GC, mark from non-local chunks. */
3347dd7cddfSDavid du Colombier
3357dd7cddfSDavid du Colombier if (!global)
3367dd7cddfSDavid du Colombier for_chunks(min_collect - 1, mem, cp)
337*593dc095SDavid du Colombier more |= gc_trace_chunk((const gs_memory_t *)mem, cp, &state, mark_stack);
3387dd7cddfSDavid du Colombier
3397dd7cddfSDavid du Colombier /* Handle mark stack overflow. */
3407dd7cddfSDavid du Colombier
3417dd7cddfSDavid du Colombier while (more < 0) { /* stack overflowed */
3427dd7cddfSDavid du Colombier more = 0;
3437dd7cddfSDavid du Colombier for_chunks(max_trace, mem, cp)
3447dd7cddfSDavid du Colombier more |= gc_rescan_chunk(cp, &state, mark_stack);
3457dd7cddfSDavid du Colombier }
3467dd7cddfSDavid du Colombier
3477dd7cddfSDavid du Colombier end_phase("mark overflow");
3487dd7cddfSDavid du Colombier }
3497dd7cddfSDavid du Colombier
3507dd7cddfSDavid du Colombier /* Free the mark stack. */
3517dd7cddfSDavid du Colombier
3527dd7cddfSDavid du Colombier {
3537dd7cddfSDavid du Colombier gc_mark_stack *pms = mark_stack;
3547dd7cddfSDavid du Colombier
3557dd7cddfSDavid du Colombier while (pms->next)
3567dd7cddfSDavid du Colombier pms = pms->next;
3577dd7cddfSDavid du Colombier while (pms) {
3587dd7cddfSDavid du Colombier gc_mark_stack *prev = pms->prev;
3597dd7cddfSDavid du Colombier
3607dd7cddfSDavid du Colombier if (pms->on_heap)
3617dd7cddfSDavid du Colombier gs_free_object(state.heap, pms, "gc mark stack");
3627dd7cddfSDavid du Colombier else
3637dd7cddfSDavid du Colombier gs_alloc_fill(pms, gs_alloc_fill_free,
3647dd7cddfSDavid du Colombier sizeof(*pms) + sizeof(ms_entry) * pms->count);
3657dd7cddfSDavid du Colombier pms = prev;
3667dd7cddfSDavid du Colombier }
3677dd7cddfSDavid du Colombier }
3687dd7cddfSDavid du Colombier
3697dd7cddfSDavid du Colombier end_phase("free mark stack");
3707dd7cddfSDavid du Colombier
3717dd7cddfSDavid du Colombier if (global) {
3727dd7cddfSDavid du Colombier gc_trace_finish(&state);
3737dd7cddfSDavid du Colombier names_trace_finish(state.ntable, &state);
3747dd7cddfSDavid du Colombier
3757dd7cddfSDavid du Colombier end_phase("finish trace");
3767dd7cddfSDavid du Colombier }
3777dd7cddfSDavid du Colombier /* Clear marks and relocation in spaces that are only being traced. */
3787dd7cddfSDavid du Colombier /* We have to clear the marks first, because we want the */
3797dd7cddfSDavid du Colombier /* relocation to wind up as o_untraced, not o_unmarked. */
3807dd7cddfSDavid du Colombier
3817dd7cddfSDavid du Colombier for_chunks(min_collect - 1, mem, cp)
382*593dc095SDavid du Colombier gc_objects_clear_marks((const gs_memory_t *)mem, cp);
3837dd7cddfSDavid du Colombier
3847dd7cddfSDavid du Colombier end_phase("post-clear marks");
3857dd7cddfSDavid du Colombier
3867dd7cddfSDavid du Colombier for_chunks(min_collect - 1, mem, cp)
3877dd7cddfSDavid du Colombier gc_clear_reloc(cp);
3887dd7cddfSDavid du Colombier
3897dd7cddfSDavid du Colombier end_phase("clear reloc");
3907dd7cddfSDavid du Colombier
3917dd7cddfSDavid du Colombier /* Set the relocation of roots outside any chunk to o_untraced, */
3927dd7cddfSDavid du Colombier /* so we won't try to relocate pointers to them. */
3937dd7cddfSDavid du Colombier /* (Currently, there aren't any.) */
3947dd7cddfSDavid du Colombier
3957dd7cddfSDavid du Colombier /* Disable freeing in the allocators of the spaces we are */
3967dd7cddfSDavid du Colombier /* collecting, so finalization procedures won't cause problems. */
3977dd7cddfSDavid du Colombier {
3987dd7cddfSDavid du Colombier int i;
3997dd7cddfSDavid du Colombier
4007dd7cddfSDavid du Colombier for_collected_spaces(i)
4017dd7cddfSDavid du Colombier gs_enable_free((gs_memory_t *)space_memories[i], false);
4027dd7cddfSDavid du Colombier }
4037dd7cddfSDavid du Colombier
4047dd7cddfSDavid du Colombier /* Compute relocation based on marks, in the spaces */
4057dd7cddfSDavid du Colombier /* we are going to compact. Also finalize freed objects. */
4067dd7cddfSDavid du Colombier
4077dd7cddfSDavid du Colombier for_collected_chunks(mem, cp) {
4087dd7cddfSDavid du Colombier gc_objects_set_reloc(cp);
4097dd7cddfSDavid du Colombier gc_strings_set_reloc(cp);
4107dd7cddfSDavid du Colombier }
4117dd7cddfSDavid du Colombier
4127dd7cddfSDavid du Colombier /* Re-enable freeing. */
4137dd7cddfSDavid du Colombier {
4147dd7cddfSDavid du Colombier int i;
4157dd7cddfSDavid du Colombier
4167dd7cddfSDavid du Colombier for_collected_spaces(i)
4177dd7cddfSDavid du Colombier gs_enable_free((gs_memory_t *)space_memories[i], true);
4187dd7cddfSDavid du Colombier }
4197dd7cddfSDavid du Colombier
4207dd7cddfSDavid du Colombier end_phase("set reloc");
4217dd7cddfSDavid du Colombier
4227dd7cddfSDavid du Colombier /* Relocate pointers. */
4237dd7cddfSDavid du Colombier
4247dd7cddfSDavid du Colombier state.relocating_untraced = true;
4257dd7cddfSDavid du Colombier for_chunks(min_collect - 1, mem, cp)
4267dd7cddfSDavid du Colombier gc_do_reloc(cp, mem, &state);
4277dd7cddfSDavid du Colombier state.relocating_untraced = false;
4287dd7cddfSDavid du Colombier for_collected_chunks(mem, cp)
4297dd7cddfSDavid du Colombier gc_do_reloc(cp, mem, &state);
4307dd7cddfSDavid du Colombier
4317dd7cddfSDavid du Colombier end_phase("relocate chunks");
4327dd7cddfSDavid du Colombier
4337dd7cddfSDavid du Colombier for_roots(max_trace, mem, rp) {
4347dd7cddfSDavid du Colombier if_debug3('6', "[6]relocating root 0x%lx: 0x%lx -> 0x%lx\n",
4357dd7cddfSDavid du Colombier (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
4367dd7cddfSDavid du Colombier if (rp->ptype == ptr_ref_type) {
4377dd7cddfSDavid du Colombier ref *pref = (ref *) * rp->p;
4387dd7cddfSDavid du Colombier
4397dd7cddfSDavid du Colombier igc_reloc_refs((ref_packed *) pref,
4407dd7cddfSDavid du Colombier (ref_packed *) (pref + 1),
4417dd7cddfSDavid du Colombier &state);
4427dd7cddfSDavid du Colombier } else
4437dd7cddfSDavid du Colombier *rp->p = (*rp->ptype->reloc) (*rp->p, &state);
4447dd7cddfSDavid du Colombier if_debug3('6', "[6]relocated root 0x%lx: 0x%lx -> 0x%lx\n",
4457dd7cddfSDavid du Colombier (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
4467dd7cddfSDavid du Colombier }
4477dd7cddfSDavid du Colombier
4487dd7cddfSDavid du Colombier end_phase("relocate roots");
4497dd7cddfSDavid du Colombier
4507dd7cddfSDavid du Colombier /* Compact data. We only do this for spaces we are collecting. */
4517dd7cddfSDavid du Colombier
4527dd7cddfSDavid du Colombier for_collected_spaces(ispace) {
4537dd7cddfSDavid du Colombier for_space_mems(ispace, mem) {
4547dd7cddfSDavid du Colombier for_mem_chunks(mem, cp) {
4557dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]compacting chunk", cp);
4567dd7cddfSDavid du Colombier gc_objects_compact(cp, &state);
4577dd7cddfSDavid du Colombier gc_strings_compact(cp);
4587dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]after compaction:", cp);
4597dd7cddfSDavid du Colombier if (mem->pcc == cp)
4607dd7cddfSDavid du Colombier mem->cc = *cp;
4617dd7cddfSDavid du Colombier }
4627dd7cddfSDavid du Colombier mem->saved = mem->reloc_saved;
4637dd7cddfSDavid du Colombier ialloc_reset_free(mem);
4647dd7cddfSDavid du Colombier }
4657dd7cddfSDavid du Colombier }
4667dd7cddfSDavid du Colombier
4677dd7cddfSDavid du Colombier end_phase("compact");
4687dd7cddfSDavid du Colombier
4697dd7cddfSDavid du Colombier /* Free empty chunks. */
4707dd7cddfSDavid du Colombier
4717dd7cddfSDavid du Colombier for_collected_spaces(ispace) {
4727dd7cddfSDavid du Colombier for_space_mems(ispace, mem) {
4737dd7cddfSDavid du Colombier gc_free_empty_chunks(mem);
4747dd7cddfSDavid du Colombier }
4757dd7cddfSDavid du Colombier }
4767dd7cddfSDavid du Colombier
4777dd7cddfSDavid du Colombier end_phase("free empty chunks");
4787dd7cddfSDavid du Colombier
4797dd7cddfSDavid du Colombier /*
4807dd7cddfSDavid du Colombier * Update previous_status to reflect any freed chunks,
4817dd7cddfSDavid du Colombier * and set inherited to the negative of allocated,
4827dd7cddfSDavid du Colombier * so it has no effect. We must update previous_status by
4837dd7cddfSDavid du Colombier * working back-to-front along the save chain, using pointer reversal.
4847dd7cddfSDavid du Colombier * (We could update inherited in any order, since it only uses
4857dd7cddfSDavid du Colombier * information local to the individual save level.)
4867dd7cddfSDavid du Colombier */
4877dd7cddfSDavid du Colombier
4887dd7cddfSDavid du Colombier for_collected_spaces(ispace) { /* Reverse the pointers. */
4897dd7cddfSDavid du Colombier alloc_save_t *curr;
4907dd7cddfSDavid du Colombier alloc_save_t *prev = 0;
4917dd7cddfSDavid du Colombier alloc_save_t *next;
4927dd7cddfSDavid du Colombier gs_memory_status_t total;
4937dd7cddfSDavid du Colombier
4947dd7cddfSDavid du Colombier for (curr = space_memories[ispace]->saved; curr != 0;
4957dd7cddfSDavid du Colombier prev = curr, curr = next
4967dd7cddfSDavid du Colombier ) {
4977dd7cddfSDavid du Colombier next = curr->state.saved;
4987dd7cddfSDavid du Colombier curr->state.saved = prev;
4997dd7cddfSDavid du Colombier }
5007dd7cddfSDavid du Colombier /* Now work the other way, accumulating the values. */
5017dd7cddfSDavid du Colombier total.allocated = 0, total.used = 0;
5027dd7cddfSDavid du Colombier for (curr = prev, prev = 0; curr != 0;
5037dd7cddfSDavid du Colombier prev = curr, curr = next
5047dd7cddfSDavid du Colombier ) {
5057dd7cddfSDavid du Colombier mem = &curr->state;
5067dd7cddfSDavid du Colombier next = mem->saved;
5077dd7cddfSDavid du Colombier mem->saved = prev;
5087dd7cddfSDavid du Colombier mem->previous_status = total;
5097dd7cddfSDavid du Colombier if_debug3('6',
5107dd7cddfSDavid du Colombier "[6]0x%lx previous allocated=%lu, used=%lu\n",
5117dd7cddfSDavid du Colombier (ulong) mem, total.allocated, total.used);
5127dd7cddfSDavid du Colombier gs_memory_status((gs_memory_t *) mem, &total);
5137dd7cddfSDavid du Colombier mem->gc_allocated = mem->allocated + total.allocated;
514*593dc095SDavid du Colombier mem->inherited = -(int)mem->allocated;
5157dd7cddfSDavid du Colombier }
5167dd7cddfSDavid du Colombier mem = space_memories[ispace];
5177dd7cddfSDavid du Colombier mem->previous_status = total;
5187dd7cddfSDavid du Colombier mem->gc_allocated = mem->allocated + total.allocated;
5197dd7cddfSDavid du Colombier if_debug3('6', "[6]0x%lx previous allocated=%lu, used=%lu\n",
5207dd7cddfSDavid du Colombier (ulong) mem, total.allocated, total.used);
5217dd7cddfSDavid du Colombier }
5227dd7cddfSDavid du Colombier
5237dd7cddfSDavid du Colombier end_phase("update stats");
5247dd7cddfSDavid du Colombier
5257dd7cddfSDavid du Colombier no_collect:
5267dd7cddfSDavid du Colombier
5277dd7cddfSDavid du Colombier /* Unregister the allocator roots. */
5287dd7cddfSDavid du Colombier
5297dd7cddfSDavid du Colombier for_spaces(ispace, max_trace)
5307dd7cddfSDavid du Colombier gs_unregister_root((gs_memory_t *)space_memories[ispace],
5317dd7cddfSDavid du Colombier &space_roots[ispace], "gc_top_level");
5327dd7cddfSDavid du Colombier
5337dd7cddfSDavid du Colombier end_phase("unregister space roots");
5347dd7cddfSDavid du Colombier
5357dd7cddfSDavid du Colombier #ifdef DEBUG
5367dd7cddfSDavid du Colombier
5377dd7cddfSDavid du Colombier /* Validate the state. This shouldn't be necessary.... */
5387dd7cddfSDavid du Colombier
5397dd7cddfSDavid du Colombier gc_validate_spaces(space_memories, max_trace, &state);
5407dd7cddfSDavid du Colombier
5417dd7cddfSDavid du Colombier end_phase("validate pointers");
5427dd7cddfSDavid du Colombier
5437dd7cddfSDavid du Colombier #endif
5447dd7cddfSDavid du Colombier }
5457dd7cddfSDavid du Colombier
5467dd7cddfSDavid du Colombier /* ------ Debugging utilities ------ */
5477dd7cddfSDavid du Colombier
5487dd7cddfSDavid du Colombier /* Validate a pointer to an object header. */
5497dd7cddfSDavid du Colombier #ifdef DEBUG
5507dd7cddfSDavid du Colombier # define debug_check_object(pre, cp, gcst)\
5517dd7cddfSDavid du Colombier ialloc_validate_object((pre) + 1, cp, gcst)
5527dd7cddfSDavid du Colombier #else
5537dd7cddfSDavid du Colombier # define debug_check_object(pre, cp, gcst) DO_NOTHING
5547dd7cddfSDavid du Colombier #endif
5557dd7cddfSDavid du Colombier
5567dd7cddfSDavid du Colombier /* ------ Unmarking phase ------ */
5577dd7cddfSDavid du Colombier
5587dd7cddfSDavid du Colombier /* Unmark a single struct. */
5597dd7cddfSDavid du Colombier private void
ptr_struct_unmark(enum_ptr_t * pep,gc_state_t * ignored)5607dd7cddfSDavid du Colombier ptr_struct_unmark(enum_ptr_t *pep, gc_state_t * ignored)
5617dd7cddfSDavid du Colombier {
5627dd7cddfSDavid du Colombier void *const vptr = (void *)pep->ptr; /* break const */
5637dd7cddfSDavid du Colombier
5647dd7cddfSDavid du Colombier if (vptr != 0)
5657dd7cddfSDavid du Colombier o_set_unmarked(((obj_header_t *) vptr - 1));
5667dd7cddfSDavid du Colombier }
5677dd7cddfSDavid du Colombier
5687dd7cddfSDavid du Colombier /* Unmark a single string. */
5697dd7cddfSDavid du Colombier private void
ptr_string_unmark(enum_ptr_t * pep,gc_state_t * gcst)5707dd7cddfSDavid du Colombier ptr_string_unmark(enum_ptr_t *pep, gc_state_t * gcst)
5717dd7cddfSDavid du Colombier {
5727dd7cddfSDavid du Colombier discard(gc_string_mark(pep->ptr, pep->size, false, gcst));
5737dd7cddfSDavid du Colombier }
5747dd7cddfSDavid du Colombier
5757dd7cddfSDavid du Colombier /* Unmark the objects in a chunk. */
5767dd7cddfSDavid du Colombier private void
gc_objects_clear_marks(const gs_memory_t * mem,chunk_t * cp)577*593dc095SDavid du Colombier gc_objects_clear_marks(const gs_memory_t *mem, chunk_t * cp)
5787dd7cddfSDavid du Colombier {
5797dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]unmarking chunk", cp);
5807dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
5817dd7cddfSDavid du Colombier DO_ALL
5827dd7cddfSDavid du Colombier struct_proc_clear_marks((*proc)) =
5837dd7cddfSDavid du Colombier pre->o_type->clear_marks;
5847dd7cddfSDavid du Colombier #ifdef DEBUG
5857dd7cddfSDavid du Colombier if (pre->o_type != &st_free)
5867dd7cddfSDavid du Colombier debug_check_object(pre, cp, NULL);
5877dd7cddfSDavid du Colombier #endif
5887dd7cddfSDavid du Colombier if_debug3('7', " [7](un)marking %s(%lu) 0x%lx\n",
5897dd7cddfSDavid du Colombier struct_type_name_string(pre->o_type),
5907dd7cddfSDavid du Colombier (ulong) size, (ulong) pre);
5917dd7cddfSDavid du Colombier o_set_unmarked(pre);
5927dd7cddfSDavid du Colombier if (proc != 0)
593*593dc095SDavid du Colombier (*proc) (mem, pre + 1, size, pre->o_type);
5947dd7cddfSDavid du Colombier END_OBJECTS_SCAN
5957dd7cddfSDavid du Colombier }
5967dd7cddfSDavid du Colombier
5977dd7cddfSDavid du Colombier /* Mark 0- and 1-character names, and those referenced from the */
5987dd7cddfSDavid du Colombier /* op_array_nx_table, and unmark all the rest. */
5997dd7cddfSDavid du Colombier private void
gc_unmark_names(name_table * nt)6007dd7cddfSDavid du Colombier gc_unmark_names(name_table * nt)
6017dd7cddfSDavid du Colombier {
6027dd7cddfSDavid du Colombier uint i;
6037dd7cddfSDavid du Colombier
6047dd7cddfSDavid du Colombier names_unmark_all(nt);
6057dd7cddfSDavid du Colombier for (i = 0; i < op_array_table_global.count; i++) {
6067dd7cddfSDavid du Colombier name_index_t nidx = op_array_table_global.nx_table[i];
6077dd7cddfSDavid du Colombier
6087dd7cddfSDavid du Colombier names_mark_index(nt, nidx);
6097dd7cddfSDavid du Colombier }
6107dd7cddfSDavid du Colombier for (i = 0; i < op_array_table_local.count; i++) {
6117dd7cddfSDavid du Colombier name_index_t nidx = op_array_table_local.nx_table[i];
6127dd7cddfSDavid du Colombier
6137dd7cddfSDavid du Colombier names_mark_index(nt, nidx);
6147dd7cddfSDavid du Colombier }
6157dd7cddfSDavid du Colombier }
6167dd7cddfSDavid du Colombier
6177dd7cddfSDavid du Colombier /* ------ Marking phase ------ */
6187dd7cddfSDavid du Colombier
6197dd7cddfSDavid du Colombier /* Initialize (a segment of) the mark stack. */
6207dd7cddfSDavid du Colombier private void
gc_init_mark_stack(gc_mark_stack * pms,uint count)6217dd7cddfSDavid du Colombier gc_init_mark_stack(gc_mark_stack * pms, uint count)
6227dd7cddfSDavid du Colombier {
6237dd7cddfSDavid du Colombier pms->next = 0;
6247dd7cddfSDavid du Colombier pms->count = count;
6257dd7cddfSDavid du Colombier pms->entries[0].ptr = 0;
6267dd7cddfSDavid du Colombier pms->entries[0].index = 0;
6277dd7cddfSDavid du Colombier pms->entries[0].is_refs = false;
6287dd7cddfSDavid du Colombier }
6297dd7cddfSDavid du Colombier
6307dd7cddfSDavid du Colombier /* Mark starting from all marked objects in the interval of a chunk */
6317dd7cddfSDavid du Colombier /* needing rescanning. */
6327dd7cddfSDavid du Colombier private int
gc_rescan_chunk(chunk_t * cp,gc_state_t * pstate,gc_mark_stack * pmstack)6337dd7cddfSDavid du Colombier gc_rescan_chunk(chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
6347dd7cddfSDavid du Colombier {
6357dd7cddfSDavid du Colombier byte *sbot = cp->rescan_bot;
6367dd7cddfSDavid du Colombier byte *stop = cp->rescan_top;
6377dd7cddfSDavid du Colombier gs_gc_root_t root;
6387dd7cddfSDavid du Colombier void *comp;
6397dd7cddfSDavid du Colombier int more = 0;
640*593dc095SDavid du Colombier const gs_memory_t *mem = gcst_get_memory_ptr( pstate );
6417dd7cddfSDavid du Colombier
6427dd7cddfSDavid du Colombier if (sbot > stop)
6437dd7cddfSDavid du Colombier return 0;
6447dd7cddfSDavid du Colombier root.p = ∁
6457dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]rescanning chunk", cp);
6467dd7cddfSDavid du Colombier cp->rescan_bot = cp->cend;
6477dd7cddfSDavid du Colombier cp->rescan_top = cp->cbase;
6487dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
6497dd7cddfSDavid du Colombier DO_ALL
6507dd7cddfSDavid du Colombier if ((byte *) (pre + 1) + size < sbot);
6517dd7cddfSDavid du Colombier else if ((byte *) (pre + 1) > stop)
6527dd7cddfSDavid du Colombier return more; /* 'break' won't work here */
6537dd7cddfSDavid du Colombier else {
6547dd7cddfSDavid du Colombier if_debug2('7', " [7]scanning/marking 0x%lx(%lu)\n",
6557dd7cddfSDavid du Colombier (ulong) pre, (ulong) size);
6567dd7cddfSDavid du Colombier if (pre->o_type == &st_refs) {
6577dd7cddfSDavid du Colombier ref_packed *rp = (ref_packed *) (pre + 1);
6587dd7cddfSDavid du Colombier char *end = (char *)rp + size;
6597dd7cddfSDavid du Colombier
6607dd7cddfSDavid du Colombier root.ptype = ptr_ref_type;
6617dd7cddfSDavid du Colombier while ((char *)rp < end) {
6627dd7cddfSDavid du Colombier comp = rp;
6637dd7cddfSDavid du Colombier if (r_is_packed(rp)) {
6647dd7cddfSDavid du Colombier if (r_has_pmark(rp)) {
6657dd7cddfSDavid du Colombier r_clear_pmark(rp);
6667dd7cddfSDavid du Colombier more |= gc_trace(&root, pstate,
6677dd7cddfSDavid du Colombier pmstack);
6687dd7cddfSDavid du Colombier }
6697dd7cddfSDavid du Colombier rp++;
6707dd7cddfSDavid du Colombier } else {
6717dd7cddfSDavid du Colombier ref *const pref = (ref *)rp;
6727dd7cddfSDavid du Colombier
6737dd7cddfSDavid du Colombier if (r_has_attr(pref, l_mark)) {
6747dd7cddfSDavid du Colombier r_clear_attrs(pref, l_mark);
6757dd7cddfSDavid du Colombier more |= gc_trace(&root, pstate, pmstack);
6767dd7cddfSDavid du Colombier }
6777dd7cddfSDavid du Colombier rp += packed_per_ref;
6787dd7cddfSDavid du Colombier }
6797dd7cddfSDavid du Colombier }
6807dd7cddfSDavid du Colombier } else if (!o_is_unmarked(pre)) {
6817dd7cddfSDavid du Colombier struct_proc_clear_marks((*proc)) =
6827dd7cddfSDavid du Colombier pre->o_type->clear_marks;
6837dd7cddfSDavid du Colombier root.ptype = ptr_struct_type;
6847dd7cddfSDavid du Colombier comp = pre + 1;
6857dd7cddfSDavid du Colombier if (!o_is_untraced(pre))
6867dd7cddfSDavid du Colombier o_set_unmarked(pre);
6877dd7cddfSDavid du Colombier if (proc != 0)
688*593dc095SDavid du Colombier (*proc) (mem, comp, size, pre->o_type);
6897dd7cddfSDavid du Colombier more |= gc_trace(&root, pstate, pmstack);
6907dd7cddfSDavid du Colombier }
6917dd7cddfSDavid du Colombier }
6927dd7cddfSDavid du Colombier END_OBJECTS_SCAN
6937dd7cddfSDavid du Colombier return more;
6947dd7cddfSDavid du Colombier }
6957dd7cddfSDavid du Colombier
6967dd7cddfSDavid du Colombier /* Mark starting from all the objects in a chunk. */
6977dd7cddfSDavid du Colombier /* We assume that pstate->min_collect > avm_system, */
6987dd7cddfSDavid du Colombier /* so we don't have to trace names. */
6997dd7cddfSDavid du Colombier private int
gc_trace_chunk(const gs_memory_t * mem,chunk_t * cp,gc_state_t * pstate,gc_mark_stack * pmstack)700*593dc095SDavid du Colombier gc_trace_chunk(const gs_memory_t *mem, chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
7017dd7cddfSDavid du Colombier {
7027dd7cddfSDavid du Colombier gs_gc_root_t root;
7037dd7cddfSDavid du Colombier void *comp;
7047dd7cddfSDavid du Colombier int more = 0;
7057dd7cddfSDavid du Colombier int min_trace = pstate->min_collect;
7067dd7cddfSDavid du Colombier
7077dd7cddfSDavid du Colombier root.p = ∁
7087dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]marking from chunk", cp);
7097dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
7107dd7cddfSDavid du Colombier DO_ALL
7117dd7cddfSDavid du Colombier {
7127dd7cddfSDavid du Colombier if_debug2('7', " [7]scanning/marking 0x%lx(%lu)\n",
7137dd7cddfSDavid du Colombier (ulong) pre, (ulong) size);
7147dd7cddfSDavid du Colombier if (pre->o_type == &st_refs) {
7157dd7cddfSDavid du Colombier ref_packed *rp = (ref_packed *) (pre + 1);
7167dd7cddfSDavid du Colombier char *end = (char *)rp + size;
7177dd7cddfSDavid du Colombier
7187dd7cddfSDavid du Colombier root.ptype = ptr_ref_type;
7197dd7cddfSDavid du Colombier while ((char *)rp < end) {
7207dd7cddfSDavid du Colombier comp = rp;
7217dd7cddfSDavid du Colombier if (r_is_packed(rp)) { /* No packed refs need tracing. */
7227dd7cddfSDavid du Colombier rp++;
7237dd7cddfSDavid du Colombier } else {
7247dd7cddfSDavid du Colombier ref *const pref = (ref *)rp;
7257dd7cddfSDavid du Colombier
7267dd7cddfSDavid du Colombier if (r_space(pref) >= min_trace) {
7277dd7cddfSDavid du Colombier r_clear_attrs(pref, l_mark);
7287dd7cddfSDavid du Colombier more |= gc_trace(&root, pstate, pmstack);
7297dd7cddfSDavid du Colombier }
7307dd7cddfSDavid du Colombier rp += packed_per_ref;
7317dd7cddfSDavid du Colombier }
7327dd7cddfSDavid du Colombier }
7337dd7cddfSDavid du Colombier } else if (!o_is_unmarked(pre)) {
7347dd7cddfSDavid du Colombier if (!o_is_untraced(pre))
7357dd7cddfSDavid du Colombier o_set_unmarked(pre);
7367dd7cddfSDavid du Colombier if (pre->o_type != &st_free) {
7377dd7cddfSDavid du Colombier struct_proc_clear_marks((*proc)) =
7387dd7cddfSDavid du Colombier pre->o_type->clear_marks;
7397dd7cddfSDavid du Colombier
7407dd7cddfSDavid du Colombier root.ptype = ptr_struct_type;
7417dd7cddfSDavid du Colombier comp = pre + 1;
7427dd7cddfSDavid du Colombier if (proc != 0)
743*593dc095SDavid du Colombier (*proc) (mem, comp, size, pre->o_type);
7447dd7cddfSDavid du Colombier more |= gc_trace(&root, pstate, pmstack);
7457dd7cddfSDavid du Colombier }
7467dd7cddfSDavid du Colombier }
7477dd7cddfSDavid du Colombier }
7487dd7cddfSDavid du Colombier END_OBJECTS_SCAN
7497dd7cddfSDavid du Colombier return more;
7507dd7cddfSDavid du Colombier }
7517dd7cddfSDavid du Colombier
7527dd7cddfSDavid du Colombier /* Recursively mark from a (root) pointer. */
7537dd7cddfSDavid du Colombier /* Return -1 if we overflowed the mark stack, */
7547dd7cddfSDavid du Colombier /* 0 if we completed successfully without marking any new objects, */
7557dd7cddfSDavid du Colombier /* 1 if we completed and marked some new objects. */
756*593dc095SDavid du Colombier private int gc_extend_stack(gc_mark_stack *, gc_state_t *);
7577dd7cddfSDavid du Colombier private int
gc_trace(gs_gc_root_t * rp,gc_state_t * pstate,gc_mark_stack * pmstack)7587dd7cddfSDavid du Colombier gc_trace(gs_gc_root_t * rp, gc_state_t * pstate, gc_mark_stack * pmstack)
7597dd7cddfSDavid du Colombier {
7607dd7cddfSDavid du Colombier int min_trace = pstate->min_collect;
7617dd7cddfSDavid du Colombier gc_mark_stack *pms = pmstack;
7627dd7cddfSDavid du Colombier ms_entry *sp = pms->entries + 1;
7637dd7cddfSDavid du Colombier
7647dd7cddfSDavid du Colombier /* We stop the mark stack 1 entry early, because we store into */
7657dd7cddfSDavid du Colombier /* the entry beyond the top. */
7667dd7cddfSDavid du Colombier ms_entry *stop = sp + pms->count - 2;
7677dd7cddfSDavid du Colombier int new = 0;
7687dd7cddfSDavid du Colombier enum_ptr_t nep;
7697dd7cddfSDavid du Colombier void *nptr;
7707dd7cddfSDavid du Colombier name_table *nt = pstate->ntable;
7717dd7cddfSDavid du Colombier
7727dd7cddfSDavid du Colombier #define mark_name(nidx)\
7737dd7cddfSDavid du Colombier BEGIN\
7747dd7cddfSDavid du Colombier if (names_mark_index(nt, nidx)) {\
7757dd7cddfSDavid du Colombier new |= 1;\
7767dd7cddfSDavid du Colombier if_debug2('8', " [8]marked name 0x%lx(%u)\n",\
7777dd7cddfSDavid du Colombier (ulong)names_index_ptr(nt, nidx), nidx);\
7787dd7cddfSDavid du Colombier }\
7797dd7cddfSDavid du Colombier END
7807dd7cddfSDavid du Colombier
7817dd7cddfSDavid du Colombier nptr = *rp->p;
7827dd7cddfSDavid du Colombier if (nptr == 0)
7837dd7cddfSDavid du Colombier return 0;
7847dd7cddfSDavid du Colombier
7857dd7cddfSDavid du Colombier /* Initialize the stack */
7867dd7cddfSDavid du Colombier sp->ptr = nptr;
7877dd7cddfSDavid du Colombier if (rp->ptype == ptr_ref_type)
7887dd7cddfSDavid du Colombier sp->index = 1, sp->is_refs = true;
7897dd7cddfSDavid du Colombier else {
7907dd7cddfSDavid du Colombier sp->index = 0, sp->is_refs = false;
7917dd7cddfSDavid du Colombier nep.ptr = nptr;
7927dd7cddfSDavid du Colombier if ((*rp->ptype->mark) (&nep, pstate))
7937dd7cddfSDavid du Colombier new |= 1;
7947dd7cddfSDavid du Colombier }
7957dd7cddfSDavid du Colombier for (;;) {
7967dd7cddfSDavid du Colombier gs_ptr_type_t ptp;
7977dd7cddfSDavid du Colombier
7987dd7cddfSDavid du Colombier /*
7997dd7cddfSDavid du Colombier * The following should really be an if..else, but that
8007dd7cddfSDavid du Colombier * would force unnecessary is_refs tests.
8017dd7cddfSDavid du Colombier */
8027dd7cddfSDavid du Colombier if (sp->is_refs)
8037dd7cddfSDavid du Colombier goto do_refs;
8047dd7cddfSDavid du Colombier
8057dd7cddfSDavid du Colombier /* ---------------- Structure ---------------- */
8067dd7cddfSDavid du Colombier
8077dd7cddfSDavid du Colombier do_struct:
8087dd7cddfSDavid du Colombier {
8097dd7cddfSDavid du Colombier obj_header_t *ptr = sp->ptr;
8107dd7cddfSDavid du Colombier
8117dd7cddfSDavid du Colombier struct_proc_enum_ptrs((*mproc));
8127dd7cddfSDavid du Colombier
8137dd7cddfSDavid du Colombier if (ptr == 0) { /* We've reached the bottom of a stack segment. */
8147dd7cddfSDavid du Colombier pms = pms->prev;
8157dd7cddfSDavid du Colombier if (pms == 0)
8167dd7cddfSDavid du Colombier break; /* all done */
8177dd7cddfSDavid du Colombier stop = pms->entries + pms->count - 1;
8187dd7cddfSDavid du Colombier sp = stop;
8197dd7cddfSDavid du Colombier continue;
8207dd7cddfSDavid du Colombier }
8217dd7cddfSDavid du Colombier debug_check_object(ptr - 1, NULL, NULL);
8227dd7cddfSDavid du Colombier ts:if_debug4('7', " [7]%smarking %s 0x%lx[%u]",
8237dd7cddfSDavid du Colombier depth_dots(sp, pms),
8247dd7cddfSDavid du Colombier struct_type_name_string(ptr[-1].o_type),
8257dd7cddfSDavid du Colombier (ulong) ptr, sp->index);
8267dd7cddfSDavid du Colombier mproc = ptr[-1].o_type->enum_ptrs;
8277dd7cddfSDavid du Colombier if (mproc == gs_no_struct_enum_ptrs ||
8287dd7cddfSDavid du Colombier (ptp = (*mproc)
829*593dc095SDavid du Colombier (gcst_get_memory_ptr(pstate), ptr, pre_obj_contents_size(ptr - 1),
8307dd7cddfSDavid du Colombier sp->index, &nep, ptr[-1].o_type, pstate)) == 0
8317dd7cddfSDavid du Colombier ) {
8327dd7cddfSDavid du Colombier if_debug0('7', " - done\n");
8337dd7cddfSDavid du Colombier sp--;
8347dd7cddfSDavid du Colombier continue;
8357dd7cddfSDavid du Colombier }
8367dd7cddfSDavid du Colombier /* The cast in the following statement is the one */
8377dd7cddfSDavid du Colombier /* place we need to break 'const' to make the */
8387dd7cddfSDavid du Colombier /* template for pointer enumeration work. */
8397dd7cddfSDavid du Colombier nptr = (void *)nep.ptr;
8407dd7cddfSDavid du Colombier sp->index++;
8417dd7cddfSDavid du Colombier if_debug1('7', " = 0x%lx\n", (ulong) nptr);
8427dd7cddfSDavid du Colombier /* Descend into nep.ptr, whose pointer type is ptp. */
8437dd7cddfSDavid du Colombier if (ptp == ptr_struct_type) {
8447dd7cddfSDavid du Colombier sp[1].index = 0;
8457dd7cddfSDavid du Colombier sp[1].is_refs = false;
8467dd7cddfSDavid du Colombier if (sp == stop)
8477dd7cddfSDavid du Colombier goto push;
8487dd7cddfSDavid du Colombier if (!ptr_struct_mark(&nep, pstate))
8497dd7cddfSDavid du Colombier goto ts;
8507dd7cddfSDavid du Colombier new |= 1;
8517dd7cddfSDavid du Colombier (++sp)->ptr = nptr;
8527dd7cddfSDavid du Colombier goto do_struct;
8537dd7cddfSDavid du Colombier } else if (ptp == ptr_ref_type) {
8547dd7cddfSDavid du Colombier sp[1].index = 1;
8557dd7cddfSDavid du Colombier sp[1].is_refs = true;
8567dd7cddfSDavid du Colombier if (sp == stop)
8577dd7cddfSDavid du Colombier goto push;
8587dd7cddfSDavid du Colombier new |= 1;
8597dd7cddfSDavid du Colombier (++sp)->ptr = nptr;
8607dd7cddfSDavid du Colombier goto do_refs;
8617dd7cddfSDavid du Colombier } else { /* We assume this is some non-pointer- */
8627dd7cddfSDavid du Colombier /* containing type. */
8637dd7cddfSDavid du Colombier if ((*ptp->mark) (&nep, pstate))
8647dd7cddfSDavid du Colombier new |= 1;
8657dd7cddfSDavid du Colombier goto ts;
8667dd7cddfSDavid du Colombier }
8677dd7cddfSDavid du Colombier }
8687dd7cddfSDavid du Colombier
8697dd7cddfSDavid du Colombier /* ---------------- Refs ---------------- */
8707dd7cddfSDavid du Colombier
8717dd7cddfSDavid du Colombier do_refs:
8727dd7cddfSDavid du Colombier {
8737dd7cddfSDavid du Colombier ref_packed *pptr = sp->ptr;
8747dd7cddfSDavid du Colombier ref *rptr;
8757dd7cddfSDavid du Colombier
8767dd7cddfSDavid du Colombier tr:if (!sp->index) {
8777dd7cddfSDavid du Colombier --sp;
8787dd7cddfSDavid du Colombier continue;
8797dd7cddfSDavid du Colombier }
8807dd7cddfSDavid du Colombier --(sp->index);
8817dd7cddfSDavid du Colombier if_debug3('8', " [8]%smarking refs 0x%lx[%u]\n",
8827dd7cddfSDavid du Colombier depth_dots(sp, pms), (ulong) pptr, sp->index);
8837dd7cddfSDavid du Colombier if (r_is_packed(pptr)) {
8847dd7cddfSDavid du Colombier if (!r_has_pmark(pptr)) {
8857dd7cddfSDavid du Colombier r_set_pmark(pptr);
8867dd7cddfSDavid du Colombier new |= 1;
8877dd7cddfSDavid du Colombier if (r_packed_is_name(pptr)) {
8887dd7cddfSDavid du Colombier name_index_t nidx = packed_name_index(pptr);
8897dd7cddfSDavid du Colombier
8907dd7cddfSDavid du Colombier mark_name(nidx);
8917dd7cddfSDavid du Colombier }
8927dd7cddfSDavid du Colombier }
8937dd7cddfSDavid du Colombier ++pptr;
8947dd7cddfSDavid du Colombier goto tr;
8957dd7cddfSDavid du Colombier }
8967dd7cddfSDavid du Colombier rptr = (ref *) pptr; /* * const beyond here */
8977dd7cddfSDavid du Colombier if (r_has_attr(rptr, l_mark)) {
8987dd7cddfSDavid du Colombier pptr = (ref_packed *)(rptr + 1);
8997dd7cddfSDavid du Colombier goto tr;
9007dd7cddfSDavid du Colombier }
9017dd7cddfSDavid du Colombier r_set_attrs(rptr, l_mark);
9027dd7cddfSDavid du Colombier new |= 1;
9037dd7cddfSDavid du Colombier if (r_space(rptr) < min_trace) { /* Note that this always picks up all scalars. */
9047dd7cddfSDavid du Colombier pptr = (ref_packed *) (rptr + 1);
9057dd7cddfSDavid du Colombier goto tr;
9067dd7cddfSDavid du Colombier }
9077dd7cddfSDavid du Colombier sp->ptr = rptr + 1;
9087dd7cddfSDavid du Colombier switch (r_type(rptr)) {
9097dd7cddfSDavid du Colombier /* Struct cases */
9107dd7cddfSDavid du Colombier case t_file:
9117dd7cddfSDavid du Colombier nptr = rptr->value.pfile;
9127dd7cddfSDavid du Colombier rs:sp[1].is_refs = false;
9137dd7cddfSDavid du Colombier sp[1].index = 0;
9147dd7cddfSDavid du Colombier if (sp == stop) {
9157dd7cddfSDavid du Colombier ptp = ptr_struct_type;
9167dd7cddfSDavid du Colombier break;
9177dd7cddfSDavid du Colombier }
9187dd7cddfSDavid du Colombier nep.ptr = nptr;
9197dd7cddfSDavid du Colombier if (!ptr_struct_mark(&nep, pstate))
9207dd7cddfSDavid du Colombier goto nr;
9217dd7cddfSDavid du Colombier new |= 1;
9227dd7cddfSDavid du Colombier (++sp)->ptr = nptr;
9237dd7cddfSDavid du Colombier goto do_struct;
9247dd7cddfSDavid du Colombier case t_device:
9257dd7cddfSDavid du Colombier nptr = rptr->value.pdevice;
9267dd7cddfSDavid du Colombier goto rs;
9277dd7cddfSDavid du Colombier case t_fontID:
9287dd7cddfSDavid du Colombier case t_struct:
9297dd7cddfSDavid du Colombier case t_astruct:
9307dd7cddfSDavid du Colombier nptr = rptr->value.pstruct;
9317dd7cddfSDavid du Colombier goto rs;
9327dd7cddfSDavid du Colombier /* Non-trivial non-struct cases */
9337dd7cddfSDavid du Colombier case t_dictionary:
9347dd7cddfSDavid du Colombier nptr = rptr->value.pdict;
9357dd7cddfSDavid du Colombier sp[1].index = sizeof(dict) / sizeof(ref);
9367dd7cddfSDavid du Colombier goto rrp;
9377dd7cddfSDavid du Colombier case t_array:
9387dd7cddfSDavid du Colombier nptr = rptr->value.refs;
9397dd7cddfSDavid du Colombier rr:if ((sp[1].index = r_size(rptr)) == 0) { /* Set the base pointer to 0, */
9407dd7cddfSDavid du Colombier /* so we never try to relocate it. */
9417dd7cddfSDavid du Colombier rptr->value.refs = 0;
9427dd7cddfSDavid du Colombier goto nr;
9437dd7cddfSDavid du Colombier }
9447dd7cddfSDavid du Colombier rrp:
9457dd7cddfSDavid du Colombier rrc:sp[1].is_refs = true;
9467dd7cddfSDavid du Colombier if (sp == stop) {
9477dd7cddfSDavid du Colombier /*
9487dd7cddfSDavid du Colombier * The following initialization is unnecessary:
9497dd7cddfSDavid du Colombier * ptp will not be used if sp[1].is_refs = true.
9507dd7cddfSDavid du Colombier * We put this here solely to get rid of bogus
9517dd7cddfSDavid du Colombier * "possibly uninitialized variable" warnings
9527dd7cddfSDavid du Colombier * from certain compilers.
9537dd7cddfSDavid du Colombier */
9547dd7cddfSDavid du Colombier ptp = ptr_ref_type;
9557dd7cddfSDavid du Colombier break;
9567dd7cddfSDavid du Colombier }
9577dd7cddfSDavid du Colombier new |= 1;
9587dd7cddfSDavid du Colombier (++sp)->ptr = nptr;
9597dd7cddfSDavid du Colombier goto do_refs;
9607dd7cddfSDavid du Colombier case t_mixedarray:
9617dd7cddfSDavid du Colombier case t_shortarray:
9627dd7cddfSDavid du Colombier nptr = rptr->value.writable_packed;
9637dd7cddfSDavid du Colombier goto rr;
9647dd7cddfSDavid du Colombier case t_name:
9657dd7cddfSDavid du Colombier mark_name(names_index(nt, rptr));
9667dd7cddfSDavid du Colombier nr:pptr = (ref_packed *) (rptr + 1);
9677dd7cddfSDavid du Colombier goto tr;
9687dd7cddfSDavid du Colombier case t_string:
9697dd7cddfSDavid du Colombier if (gc_string_mark(rptr->value.bytes, r_size(rptr), true, pstate))
9707dd7cddfSDavid du Colombier new |= 1;
9717dd7cddfSDavid du Colombier goto nr;
9727dd7cddfSDavid du Colombier case t_oparray:
9737dd7cddfSDavid du Colombier nptr = rptr->value.refs; /* discard const */
9747dd7cddfSDavid du Colombier sp[1].index = 1;
9757dd7cddfSDavid du Colombier goto rrc;
9767dd7cddfSDavid du Colombier default:
9777dd7cddfSDavid du Colombier goto nr;
9787dd7cddfSDavid du Colombier }
9797dd7cddfSDavid du Colombier }
9807dd7cddfSDavid du Colombier
9817dd7cddfSDavid du Colombier /* ---------------- Recursion ---------------- */
9827dd7cddfSDavid du Colombier
9837dd7cddfSDavid du Colombier push:
9847dd7cddfSDavid du Colombier if (sp == stop) { /* The current segment is full. */
9857dd7cddfSDavid du Colombier int new_added = gc_extend_stack(pms, pstate);
9867dd7cddfSDavid du Colombier
9877dd7cddfSDavid du Colombier if (new_added) {
9887dd7cddfSDavid du Colombier new |= new_added;
9897dd7cddfSDavid du Colombier continue;
9907dd7cddfSDavid du Colombier }
9917dd7cddfSDavid du Colombier pms = pms->next;
9927dd7cddfSDavid du Colombier stop = pms->entries + pms->count - 1;
9937dd7cddfSDavid du Colombier pms->entries[1] = sp[1];
9947dd7cddfSDavid du Colombier sp = pms->entries;
9957dd7cddfSDavid du Colombier }
9967dd7cddfSDavid du Colombier /* index and is_refs are already set */
9977dd7cddfSDavid du Colombier if (!sp[1].is_refs) {
9987dd7cddfSDavid du Colombier nep.ptr = nptr;
9997dd7cddfSDavid du Colombier if (!(*ptp->mark) (&nep, pstate))
10007dd7cddfSDavid du Colombier continue;
10017dd7cddfSDavid du Colombier new |= 1;
10027dd7cddfSDavid du Colombier }
10037dd7cddfSDavid du Colombier (++sp)->ptr = nptr;
10047dd7cddfSDavid du Colombier }
10057dd7cddfSDavid du Colombier return new;
10067dd7cddfSDavid du Colombier }
10077dd7cddfSDavid du Colombier /* Link to, attempting to allocate if necessary, */
10087dd7cddfSDavid du Colombier /* another chunk of mark stack. */
10097dd7cddfSDavid du Colombier private int
gc_extend_stack(gc_mark_stack * pms,gc_state_t * pstate)10107dd7cddfSDavid du Colombier gc_extend_stack(gc_mark_stack * pms, gc_state_t * pstate)
10117dd7cddfSDavid du Colombier {
10127dd7cddfSDavid du Colombier if (pms->next == 0) { /* Try to allocate another segment. */
10137dd7cddfSDavid du Colombier uint count;
10147dd7cddfSDavid du Colombier
10157dd7cddfSDavid du Colombier for (count = ms_size_desired; count >= ms_size_min; count >>= 1) {
10167dd7cddfSDavid du Colombier pms->next = (gc_mark_stack *)
10177dd7cddfSDavid du Colombier gs_alloc_bytes_immovable(pstate->heap,
10187dd7cddfSDavid du Colombier sizeof(gc_mark_stack) +
10197dd7cddfSDavid du Colombier sizeof(ms_entry) * count,
10207dd7cddfSDavid du Colombier "gc mark stack");
10217dd7cddfSDavid du Colombier if (pms->next != 0)
10227dd7cddfSDavid du Colombier break;
10237dd7cddfSDavid du Colombier }
10247dd7cddfSDavid du Colombier if (pms->next == 0) { /* The mark stack overflowed. */
10257dd7cddfSDavid du Colombier ms_entry *sp = pms->entries + pms->count - 1;
10267dd7cddfSDavid du Colombier byte *cptr = sp->ptr; /* container */
10277dd7cddfSDavid du Colombier chunk_t *cp = gc_locate(cptr, pstate);
10287dd7cddfSDavid du Colombier int new = 1;
10297dd7cddfSDavid du Colombier
10307dd7cddfSDavid du Colombier if (cp == 0) { /* We were tracing outside collectible */
10317dd7cddfSDavid du Colombier /* storage. This can't happen. */
10327dd7cddfSDavid du Colombier lprintf1("mark stack overflowed while outside collectible space at 0x%lx!\n",
10337dd7cddfSDavid du Colombier (ulong) cptr);
1034*593dc095SDavid du Colombier gs_abort(pstate->heap);
10357dd7cddfSDavid du Colombier }
10367dd7cddfSDavid du Colombier if (cptr < cp->rescan_bot)
10377dd7cddfSDavid du Colombier cp->rescan_bot = cptr, new = -1;
10387dd7cddfSDavid du Colombier if (cptr > cp->rescan_top)
10397dd7cddfSDavid du Colombier cp->rescan_top = cptr, new = -1;
10407dd7cddfSDavid du Colombier return new;
10417dd7cddfSDavid du Colombier }
10427dd7cddfSDavid du Colombier gc_init_mark_stack(pms->next, count);
10437dd7cddfSDavid du Colombier pms->next->prev = pms;
10447dd7cddfSDavid du Colombier pms->next->on_heap = true;
10457dd7cddfSDavid du Colombier }
10467dd7cddfSDavid du Colombier return 0;
10477dd7cddfSDavid du Colombier }
10487dd7cddfSDavid du Colombier
10497dd7cddfSDavid du Colombier /* Mark a struct. Return true if new mark. */
10507dd7cddfSDavid du Colombier private bool
ptr_struct_mark(enum_ptr_t * pep,gc_state_t * ignored)10517dd7cddfSDavid du Colombier ptr_struct_mark(enum_ptr_t *pep, gc_state_t * ignored)
10527dd7cddfSDavid du Colombier {
10537dd7cddfSDavid du Colombier obj_header_t *ptr = (obj_header_t *)pep->ptr;
10547dd7cddfSDavid du Colombier
10557dd7cddfSDavid du Colombier if (ptr == 0)
10567dd7cddfSDavid du Colombier return false;
10577dd7cddfSDavid du Colombier ptr--; /* point to header */
10587dd7cddfSDavid du Colombier if (!o_is_unmarked(ptr))
10597dd7cddfSDavid du Colombier return false;
10607dd7cddfSDavid du Colombier o_mark(ptr);
10617dd7cddfSDavid du Colombier return true;
10627dd7cddfSDavid du Colombier }
10637dd7cddfSDavid du Colombier
10647dd7cddfSDavid du Colombier /* Mark a string. Return true if new mark. */
10657dd7cddfSDavid du Colombier private bool
ptr_string_mark(enum_ptr_t * pep,gc_state_t * gcst)10667dd7cddfSDavid du Colombier ptr_string_mark(enum_ptr_t *pep, gc_state_t * gcst)
10677dd7cddfSDavid du Colombier {
10687dd7cddfSDavid du Colombier return gc_string_mark(pep->ptr, pep->size, true, gcst);
10697dd7cddfSDavid du Colombier }
10707dd7cddfSDavid du Colombier
10717dd7cddfSDavid du Colombier /* Finish tracing by marking names. */
10727dd7cddfSDavid du Colombier private bool
gc_trace_finish(gc_state_t * pstate)10737dd7cddfSDavid du Colombier gc_trace_finish(gc_state_t * pstate)
10747dd7cddfSDavid du Colombier {
10757dd7cddfSDavid du Colombier name_table *nt = pstate->ntable;
10767dd7cddfSDavid du Colombier name_index_t nidx = 0;
10777dd7cddfSDavid du Colombier bool marked = false;
10787dd7cddfSDavid du Colombier
10797dd7cddfSDavid du Colombier while ((nidx = names_next_valid_index(nt, nidx)) != 0) {
10807dd7cddfSDavid du Colombier name_string_t *pnstr = names_index_string_inline(nt, nidx);
10817dd7cddfSDavid du Colombier
10827dd7cddfSDavid du Colombier if (pnstr->mark) {
10837dd7cddfSDavid du Colombier enum_ptr_t enst, ensst;
10847dd7cddfSDavid du Colombier
10857dd7cddfSDavid du Colombier if (!pnstr->foreign_string &&
10867dd7cddfSDavid du Colombier gc_string_mark(pnstr->string_bytes, pnstr->string_size,
10877dd7cddfSDavid du Colombier true, pstate)
10887dd7cddfSDavid du Colombier )
10897dd7cddfSDavid du Colombier marked = true;
10907dd7cddfSDavid du Colombier enst.ptr = names_index_sub_table(nt, nidx);
10917dd7cddfSDavid du Colombier ensst.ptr = names_index_string_sub_table(nt, nidx);
10927dd7cddfSDavid du Colombier marked |=
10937dd7cddfSDavid du Colombier ptr_struct_mark(&enst, pstate) |
10947dd7cddfSDavid du Colombier ptr_struct_mark(&ensst, pstate);
10957dd7cddfSDavid du Colombier }
10967dd7cddfSDavid du Colombier }
10977dd7cddfSDavid du Colombier return marked;
10987dd7cddfSDavid du Colombier }
10997dd7cddfSDavid du Colombier
11007dd7cddfSDavid du Colombier /* ------ Relocation planning phase ------ */
11017dd7cddfSDavid du Colombier
11027dd7cddfSDavid du Colombier /* Initialize the relocation information in the chunk header. */
11037dd7cddfSDavid du Colombier private void
gc_init_reloc(chunk_t * cp)11047dd7cddfSDavid du Colombier gc_init_reloc(chunk_t * cp)
11057dd7cddfSDavid du Colombier {
11067dd7cddfSDavid du Colombier chunk_head_t *chead = cp->chead;
11077dd7cddfSDavid du Colombier
11087dd7cddfSDavid du Colombier chead->dest = cp->cbase;
11097dd7cddfSDavid du Colombier chead->free.o_back =
11107dd7cddfSDavid du Colombier offset_of(chunk_head_t, free) >> obj_back_shift;
11117dd7cddfSDavid du Colombier chead->free.o_size = sizeof(obj_header_t);
11127dd7cddfSDavid du Colombier chead->free.o_nreloc = 0;
11137dd7cddfSDavid du Colombier }
11147dd7cddfSDavid du Colombier
11157dd7cddfSDavid du Colombier /* Set marks and clear relocation for chunks that won't be compacted. */
11167dd7cddfSDavid du Colombier private void
gc_clear_reloc(chunk_t * cp)11177dd7cddfSDavid du Colombier gc_clear_reloc(chunk_t * cp)
11187dd7cddfSDavid du Colombier {
11197dd7cddfSDavid du Colombier byte *pfree = (byte *) & cp->chead->free;
11207dd7cddfSDavid du Colombier
11217dd7cddfSDavid du Colombier gc_init_reloc(cp);
11227dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
11237dd7cddfSDavid du Colombier DO_ALL
11247dd7cddfSDavid du Colombier const struct_shared_procs_t *procs =
11257dd7cddfSDavid du Colombier pre->o_type->shared;
11267dd7cddfSDavid du Colombier
11277dd7cddfSDavid du Colombier if (procs != 0)
11287dd7cddfSDavid du Colombier (*procs->clear_reloc) (pre, size);
11297dd7cddfSDavid du Colombier o_set_untraced(pre);
11307dd7cddfSDavid du Colombier pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
11317dd7cddfSDavid du Colombier END_OBJECTS_SCAN
11327dd7cddfSDavid du Colombier gc_strings_set_marks(cp, true);
11337dd7cddfSDavid du Colombier gc_strings_clear_reloc(cp);
11347dd7cddfSDavid du Colombier }
11357dd7cddfSDavid du Colombier
11367dd7cddfSDavid du Colombier /* Set the relocation for the objects in a chunk. */
11377dd7cddfSDavid du Colombier /* This will never be called for a chunk with any o_untraced objects. */
11387dd7cddfSDavid du Colombier private void
gc_objects_set_reloc(chunk_t * cp)11397dd7cddfSDavid du Colombier gc_objects_set_reloc(chunk_t * cp)
11407dd7cddfSDavid du Colombier {
11417dd7cddfSDavid du Colombier uint reloc = 0;
11427dd7cddfSDavid du Colombier chunk_head_t *chead = cp->chead;
11437dd7cddfSDavid du Colombier byte *pfree = (byte *) & chead->free; /* most recent free object */
11447dd7cddfSDavid du Colombier
11457dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]setting reloc for chunk", cp);
11467dd7cddfSDavid du Colombier gc_init_reloc(cp);
11477dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
11487dd7cddfSDavid du Colombier DO_ALL
11497dd7cddfSDavid du Colombier struct_proc_finalize((*finalize));
11507dd7cddfSDavid du Colombier const struct_shared_procs_t *procs =
11517dd7cddfSDavid du Colombier pre->o_type->shared;
11527dd7cddfSDavid du Colombier
11537dd7cddfSDavid du Colombier if ((procs == 0 ? o_is_unmarked(pre) :
11547dd7cddfSDavid du Colombier !(*procs->set_reloc) (pre, reloc, size))
11557dd7cddfSDavid du Colombier ) { /* Free object */
11567dd7cddfSDavid du Colombier reloc += sizeof(obj_header_t) + obj_align_round(size);
11577dd7cddfSDavid du Colombier if ((finalize = pre->o_type->finalize) != 0) {
11587dd7cddfSDavid du Colombier if_debug2('u', "[u]GC finalizing %s 0x%lx\n",
11597dd7cddfSDavid du Colombier struct_type_name_string(pre->o_type),
11607dd7cddfSDavid du Colombier (ulong) (pre + 1));
11617dd7cddfSDavid du Colombier (*finalize) (pre + 1);
11627dd7cddfSDavid du Colombier }
11637dd7cddfSDavid du Colombier pfree = (byte *) pre;
11647dd7cddfSDavid du Colombier pre->o_back = (pfree - (byte *) chead) >> obj_back_shift;
11657dd7cddfSDavid du Colombier pre->o_nreloc = reloc;
11667dd7cddfSDavid du Colombier if_debug3('7', " [7]at 0x%lx, unmarked %lu, new reloc = %u\n",
11677dd7cddfSDavid du Colombier (ulong) pre, (ulong) size, reloc);
11687dd7cddfSDavid du Colombier } else { /* Useful object */
11697dd7cddfSDavid du Colombier debug_check_object(pre, cp, NULL);
11707dd7cddfSDavid du Colombier pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
11717dd7cddfSDavid du Colombier }
11727dd7cddfSDavid du Colombier END_OBJECTS_SCAN
11737dd7cddfSDavid du Colombier #ifdef DEBUG
11747dd7cddfSDavid du Colombier if (reloc != 0) {
11757dd7cddfSDavid du Colombier if_debug1('6', "[6]freed %u", reloc);
11767dd7cddfSDavid du Colombier if_debug_chunk('6', " in", cp);
11777dd7cddfSDavid du Colombier }
11787dd7cddfSDavid du Colombier #endif
11797dd7cddfSDavid du Colombier }
11807dd7cddfSDavid du Colombier
11817dd7cddfSDavid du Colombier /* ------ Relocation phase ------ */
11827dd7cddfSDavid du Colombier
11837dd7cddfSDavid du Colombier /* Relocate the pointers in all the objects in a chunk. */
11847dd7cddfSDavid du Colombier private void
gc_do_reloc(chunk_t * cp,gs_ref_memory_t * mem,gc_state_t * pstate)11857dd7cddfSDavid du Colombier gc_do_reloc(chunk_t * cp, gs_ref_memory_t * mem, gc_state_t * pstate)
11867dd7cddfSDavid du Colombier {
11877dd7cddfSDavid du Colombier chunk_head_t *chead = cp->chead;
11887dd7cddfSDavid du Colombier
11897dd7cddfSDavid du Colombier if_debug_chunk('6', "[6]relocating in chunk", cp);
11907dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
11917dd7cddfSDavid du Colombier DO_ALL
1192*593dc095SDavid du Colombier #ifdef DEBUG
1193*593dc095SDavid du Colombier pstate->container = cp;
1194*593dc095SDavid du Colombier #endif
11957dd7cddfSDavid du Colombier /* We need to relocate the pointers in an object iff */
11967dd7cddfSDavid du Colombier /* it is o_untraced, or it is a useful object. */
11977dd7cddfSDavid du Colombier /* An object is free iff its back pointer points to */
11987dd7cddfSDavid du Colombier /* the chunk_head structure. */
11997dd7cddfSDavid du Colombier if (o_is_untraced(pre) ||
12007dd7cddfSDavid du Colombier pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead
12017dd7cddfSDavid du Colombier ) {
12027dd7cddfSDavid du Colombier struct_proc_reloc_ptrs((*proc)) =
12037dd7cddfSDavid du Colombier pre->o_type->reloc_ptrs;
12047dd7cddfSDavid du Colombier
12057dd7cddfSDavid du Colombier if_debug3('7',
12067dd7cddfSDavid du Colombier " [7]relocating ptrs in %s(%lu) 0x%lx\n",
12077dd7cddfSDavid du Colombier struct_type_name_string(pre->o_type),
12087dd7cddfSDavid du Colombier (ulong) size, (ulong) pre);
12097dd7cddfSDavid du Colombier if (proc != 0)
12107dd7cddfSDavid du Colombier (*proc) (pre + 1, size, pre->o_type, pstate);
12117dd7cddfSDavid du Colombier }
1212*593dc095SDavid du Colombier #ifdef DEBUG
1213*593dc095SDavid du Colombier pstate->container = 0;
1214*593dc095SDavid du Colombier #endif
12157dd7cddfSDavid du Colombier END_OBJECTS_SCAN
12167dd7cddfSDavid du Colombier }
12177dd7cddfSDavid du Colombier
12187dd7cddfSDavid du Colombier /* Print pointer relocation if debugging. */
12197dd7cddfSDavid du Colombier /* We have to provide this procedure even if DEBUG is not defined, */
12207dd7cddfSDavid du Colombier /* in case one of the other GC modules was compiled with DEBUG. */
12217dd7cddfSDavid du Colombier const void *
print_reloc_proc(const void * obj,const char * cname,const void * robj)12227dd7cddfSDavid du Colombier print_reloc_proc(const void *obj, const char *cname, const void *robj)
12237dd7cddfSDavid du Colombier {
12247dd7cddfSDavid du Colombier if_debug3('9', " [9]relocate %s * 0x%lx to 0x%lx\n",
12257dd7cddfSDavid du Colombier cname, (ulong)obj, (ulong)robj);
12267dd7cddfSDavid du Colombier return robj;
12277dd7cddfSDavid du Colombier }
12287dd7cddfSDavid du Colombier
12297dd7cddfSDavid du Colombier /* Relocate a pointer to an (aligned) object. */
12307dd7cddfSDavid du Colombier /* See gsmemory.h for why the argument is const and the result is not. */
12317dd7cddfSDavid du Colombier private void /*obj_header_t */ *
igc_reloc_struct_ptr(const void * obj,gc_state_t * gcst)12327dd7cddfSDavid du Colombier igc_reloc_struct_ptr(const void /*obj_header_t */ *obj, gc_state_t * gcst)
12337dd7cddfSDavid du Colombier {
12347dd7cddfSDavid du Colombier const obj_header_t *const optr = (const obj_header_t *)obj;
12357dd7cddfSDavid du Colombier const void *robj;
12367dd7cddfSDavid du Colombier
12377dd7cddfSDavid du Colombier if (obj == 0) {
12387dd7cddfSDavid du Colombier discard(print_reloc(obj, "NULL", 0));
12397dd7cddfSDavid du Colombier return 0;
12407dd7cddfSDavid du Colombier }
12417dd7cddfSDavid du Colombier debug_check_object(optr - 1, NULL, gcst);
12427dd7cddfSDavid du Colombier {
12437dd7cddfSDavid du Colombier uint back = optr[-1].o_back;
12447dd7cddfSDavid du Colombier
12457dd7cddfSDavid du Colombier if (back == o_untraced)
12467dd7cddfSDavid du Colombier robj = obj;
12477dd7cddfSDavid du Colombier else {
12487dd7cddfSDavid du Colombier #ifdef DEBUG
12497dd7cddfSDavid du Colombier /* Do some sanity checking. */
1250*593dc095SDavid du Colombier chunk_t *cp = gcst->container;
1251*593dc095SDavid du Colombier
1252*593dc095SDavid du Colombier if (cp != 0 && cp->cbase <= (byte *)obj && (byte *)obj <cp->ctop) {
1253*593dc095SDavid du Colombier if (back > (cp->ctop - cp->cbase) >> obj_back_shift) {
12547dd7cddfSDavid du Colombier lprintf2("Invalid back pointer %u at 0x%lx!\n",
12557dd7cddfSDavid du Colombier back, (ulong) obj);
1256*593dc095SDavid du Colombier gs_abort(NULL);
1257*593dc095SDavid du Colombier }
1258*593dc095SDavid du Colombier } else {
1259*593dc095SDavid du Colombier /* Pointed to unknown chunk. Can't check it, sorry. */
12607dd7cddfSDavid du Colombier }
12617dd7cddfSDavid du Colombier #endif
12627dd7cddfSDavid du Colombier {
12637dd7cddfSDavid du Colombier const obj_header_t *pfree = (const obj_header_t *)
12647dd7cddfSDavid du Colombier ((const char *)(optr - 1) -
12657dd7cddfSDavid du Colombier (back << obj_back_shift));
12667dd7cddfSDavid du Colombier const chunk_head_t *chead = (const chunk_head_t *)
12677dd7cddfSDavid du Colombier ((const char *)pfree -
12687dd7cddfSDavid du Colombier (pfree->o_back << obj_back_shift));
12697dd7cddfSDavid du Colombier
12707dd7cddfSDavid du Colombier robj = chead->dest +
12717dd7cddfSDavid du Colombier ((const char *)obj - (const char *)(chead + 1) -
12727dd7cddfSDavid du Colombier pfree->o_nreloc);
12737dd7cddfSDavid du Colombier }
12747dd7cddfSDavid du Colombier }
12757dd7cddfSDavid du Colombier }
12767dd7cddfSDavid du Colombier /* Use a severely deprecated pun to remove the const property. */
12777dd7cddfSDavid du Colombier {
12787dd7cddfSDavid du Colombier union { const void *r; void *w; } u;
12797dd7cddfSDavid du Colombier
12807dd7cddfSDavid du Colombier u.r = print_reloc(obj, struct_type_name_string(optr[-1].o_type), robj);
12817dd7cddfSDavid du Colombier return u.w;
12827dd7cddfSDavid du Colombier }
12837dd7cddfSDavid du Colombier }
12847dd7cddfSDavid du Colombier
12857dd7cddfSDavid du Colombier /* ------ Compaction phase ------ */
12867dd7cddfSDavid du Colombier
12877dd7cddfSDavid du Colombier /* Compact the objects in a chunk. */
12887dd7cddfSDavid du Colombier /* This will never be called for a chunk with any o_untraced objects. */
12897dd7cddfSDavid du Colombier private void
gc_objects_compact(chunk_t * cp,gc_state_t * gcst)12907dd7cddfSDavid du Colombier gc_objects_compact(chunk_t * cp, gc_state_t * gcst)
12917dd7cddfSDavid du Colombier {
12927dd7cddfSDavid du Colombier chunk_head_t *chead = cp->chead;
12937dd7cddfSDavid du Colombier obj_header_t *dpre = (obj_header_t *) chead->dest;
1294*593dc095SDavid du Colombier const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
12957dd7cddfSDavid du Colombier
12967dd7cddfSDavid du Colombier SCAN_CHUNK_OBJECTS(cp)
12977dd7cddfSDavid du Colombier DO_ALL
12987dd7cddfSDavid du Colombier /* An object is free iff its back pointer points to */
12997dd7cddfSDavid du Colombier /* the chunk_head structure. */
13007dd7cddfSDavid du Colombier if (pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead) {
13017dd7cddfSDavid du Colombier const struct_shared_procs_t *procs = pre->o_type->shared;
13027dd7cddfSDavid du Colombier
13037dd7cddfSDavid du Colombier debug_check_object(pre, cp, gcst);
13047dd7cddfSDavid du Colombier if_debug4('7',
13057dd7cddfSDavid du Colombier " [7]compacting %s 0x%lx(%lu) to 0x%lx\n",
13067dd7cddfSDavid du Colombier struct_type_name_string(pre->o_type),
13077dd7cddfSDavid du Colombier (ulong) pre, (ulong) size, (ulong) dpre);
13087dd7cddfSDavid du Colombier if (procs == 0) {
13097dd7cddfSDavid du Colombier if (dpre != pre)
13107dd7cddfSDavid du Colombier memmove(dpre, pre,
13117dd7cddfSDavid du Colombier sizeof(obj_header_t) + size);
13127dd7cddfSDavid du Colombier } else
1313*593dc095SDavid du Colombier (*procs->compact) (cmem, pre, dpre, size);
13147dd7cddfSDavid du Colombier dpre = (obj_header_t *)
13157dd7cddfSDavid du Colombier ((byte *) dpre + obj_size_round(size));
13167dd7cddfSDavid du Colombier }
13177dd7cddfSDavid du Colombier END_OBJECTS_SCAN
13187dd7cddfSDavid du Colombier if (cp->outer == 0 && chead->dest != cp->cbase)
13197dd7cddfSDavid du Colombier dpre = (obj_header_t *) cp->cbase; /* compacted this chunk into another */
13207dd7cddfSDavid du Colombier gs_alloc_fill(dpre, gs_alloc_fill_collected, cp->cbot - (byte *) dpre);
13217dd7cddfSDavid du Colombier cp->cbot = (byte *) dpre;
13227dd7cddfSDavid du Colombier cp->rcur = 0;
13237dd7cddfSDavid du Colombier cp->rtop = 0; /* just to be sure */
13247dd7cddfSDavid du Colombier }
13257dd7cddfSDavid du Colombier
13267dd7cddfSDavid du Colombier /* ------ Cleanup ------ */
13277dd7cddfSDavid du Colombier
13287dd7cddfSDavid du Colombier /* Free empty chunks. */
13297dd7cddfSDavid du Colombier private void
gc_free_empty_chunks(gs_ref_memory_t * mem)13307dd7cddfSDavid du Colombier gc_free_empty_chunks(gs_ref_memory_t * mem)
13317dd7cddfSDavid du Colombier {
13327dd7cddfSDavid du Colombier chunk_t *cp;
13337dd7cddfSDavid du Colombier chunk_t *csucc;
13347dd7cddfSDavid du Colombier
13357dd7cddfSDavid du Colombier /* Free the chunks in reverse order, */
13367dd7cddfSDavid du Colombier /* to encourage LIFO behavior. */
13377dd7cddfSDavid du Colombier for (cp = mem->clast; cp != 0; cp = csucc) { /* Make sure this isn't an inner chunk, */
13387dd7cddfSDavid du Colombier /* or a chunk that has inner chunks. */
13397dd7cddfSDavid du Colombier csucc = cp->cprev; /* save before freeing */
13407dd7cddfSDavid du Colombier if (cp->cbot == cp->cbase && cp->ctop == cp->climit &&
13417dd7cddfSDavid du Colombier cp->outer == 0 && cp->inner_count == 0
13427dd7cddfSDavid du Colombier ) {
13437dd7cddfSDavid du Colombier alloc_free_chunk(cp, mem);
13447dd7cddfSDavid du Colombier if (mem->pcc == cp)
13457dd7cddfSDavid du Colombier mem->pcc = 0;
13467dd7cddfSDavid du Colombier }
13477dd7cddfSDavid du Colombier }
13487dd7cddfSDavid du Colombier }
1349*593dc095SDavid du Colombier
1350*593dc095SDavid du Colombier
gcst_get_memory_ptr(gc_state_t * gcst)1351*593dc095SDavid du Colombier const gs_memory_t * gcst_get_memory_ptr(gc_state_t *gcst)
1352*593dc095SDavid du Colombier {
1353*593dc095SDavid du Colombier vm_spaces spaces = gcst->spaces;
1354*593dc095SDavid du Colombier const gs_memory_t *cmem = space_system->stable_memory;
1355*593dc095SDavid du Colombier return cmem;
1356*593dc095SDavid du Colombier }
1357