1 /* Copyright (C) 1993, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: igc.c,v 1.15 2005/09/05 13:58:55 leonardo Exp $ */
18 /* Garbage collector for Ghostscript */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "ierrors.h"
22 #include "gsexit.h"
23 #include "gsmdebug.h"
24 #include "gsstruct.h"
25 #include "gsutil.h"
26 #include "iastate.h"
27 #include "isave.h"
28 #include "isstate.h"
29 #include "idict.h"
30 #include "ipacked.h"
31 #include "istruct.h"
32 #include "igc.h"
33 #include "igcstr.h"
34 #include "inamedef.h"
35 #include "opdef.h" /* for marking oparray names */
36
37 /* Define whether to force all garbage collections to be global. */
38 private const bool I_FORCE_GLOBAL_GC = false;
39
40 /* Define whether to bypass the collector entirely. */
41 private const bool I_BYPASS_GC = false;
42
43 /* Define an entry on the mark stack. */
44 typedef struct {
45 void *ptr;
46 uint index;
47 bool is_refs;
48 } ms_entry;
49
50 /* Define (a segment of) the mark stack. */
51 /* entries[0] has ptr = 0 to indicate the bottom of the stack. */
52 /* count additional entries follow this structure. */
53 typedef struct gc_mark_stack_s gc_mark_stack;
54 struct gc_mark_stack_s {
55 gc_mark_stack *prev;
56 gc_mark_stack *next;
57 uint count;
58 bool on_heap; /* if true, allocated during GC */
59 ms_entry entries[1];
60 };
61
62 /* Define the mark stack sizing parameters. */
63 #define ms_size_default 100 /* default, allocated on C stack */
64 /* This should probably be defined as a parameter somewhere.... */
65 #define ms_size_desired /* for additional allocation */\
66 ((max_ushort - sizeof(gc_mark_stack)) / sizeof(ms_entry) - 10)
67 #define ms_size_min 50 /* min size for segment in free block */
68
69 /* Forward references */
70
71 private void gc_init_mark_stack(gc_mark_stack *, uint);
72 private void gc_objects_clear_marks(const gs_memory_t *mem, chunk_t *);
73 private void gc_unmark_names(name_table *);
74 private int gc_trace(gs_gc_root_t *, gc_state_t *, gc_mark_stack *);
75 private int gc_rescan_chunk(chunk_t *, gc_state_t *, gc_mark_stack *);
76 private int gc_trace_chunk(const gs_memory_t *mem, chunk_t *, gc_state_t *, gc_mark_stack *);
77 private bool gc_trace_finish(gc_state_t *);
78 private void gc_clear_reloc(chunk_t *);
79 private void gc_objects_set_reloc(chunk_t *);
80 private void gc_do_reloc(chunk_t *, gs_ref_memory_t *, gc_state_t *);
81 private void gc_objects_compact(chunk_t *, gc_state_t *);
82 private void gc_free_empty_chunks(gs_ref_memory_t *);
83
84 /* Forward references for pointer types */
85 private ptr_proc_unmark(ptr_struct_unmark);
86 private ptr_proc_mark(ptr_struct_mark);
87 private ptr_proc_unmark(ptr_string_unmark);
88 private ptr_proc_mark(ptr_string_mark);
89 /*ptr_proc_unmark(ptr_ref_unmark); *//* in igc.h */
90 /*ptr_proc_mark(ptr_ref_mark); *//* in igc.h */
91 private ptr_proc_reloc(igc_reloc_struct_ptr, void);
92
93 ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed); /* in igcref.c */
94 refs_proc_reloc(igc_reloc_refs); /* in igcref.c */
95
96 /* Define this GC's procedure vector. */
97 private const gc_procs_with_refs_t igc_procs = {
98 igc_reloc_struct_ptr, igc_reloc_string, igc_reloc_const_string,
99 igc_reloc_param_string, igc_reloc_ref_ptr, igc_reloc_refs
100 };
101
102 /* Pointer type descriptors. */
103 /* Note that the trace/mark routine has special knowledge of ptr_ref_type */
104 /* and ptr_struct_type -- it assumes that no other types have embedded */
105 /* pointers. Note also that the reloc procedures for string and ref */
106 /* pointers are never called. */
107 typedef ptr_proc_reloc((*ptr_proc_reloc_t), void);
108 const gs_ptr_procs_t ptr_struct_procs =
109 {ptr_struct_unmark, ptr_struct_mark, (ptr_proc_reloc_t) igc_reloc_struct_ptr};
110 const gs_ptr_procs_t ptr_string_procs =
111 {ptr_string_unmark, ptr_string_mark, NULL};
112 const gs_ptr_procs_t ptr_const_string_procs =
113 {ptr_string_unmark, ptr_string_mark, NULL};
114 const gs_ptr_procs_t ptr_ref_procs =
115 {ptr_ref_unmark, ptr_ref_mark, NULL};
116
117 /* ------ Main program ------ */
118
119 /* Top level of garbage collector. */
120 #ifdef DEBUG
121 private void
end_phase(const char * str)122 end_phase(const char *str)
123 {
124 if (gs_debug_c('6')) {
125 dlprintf1("[6]---------------- end %s ----------------\n",
126 (const char *)str);
127 dflush();
128 }
129 }
130 static const char *const depth_dots_string = "..........";
131 private const char *
depth_dots(const ms_entry * sp,const gc_mark_stack * pms)132 depth_dots(const ms_entry * sp, const gc_mark_stack * pms)
133 {
134 int depth = sp - pms->entries - 1;
135 const gc_mark_stack *pss = pms;
136
137 while ((pss = pss->prev) != 0)
138 depth += pss->count - 1;
139 return depth_dots_string + (depth >= 10 ? 0 : 10 - depth);
140 }
141 private void
gc_validate_spaces(gs_ref_memory_t ** spaces,int max_space,gc_state_t * gcst)142 gc_validate_spaces(gs_ref_memory_t **spaces, int max_space, gc_state_t *gcst)
143 {
144 int i;
145 gs_ref_memory_t *mem;
146
147 for (i = 1; i <= max_space; ++i)
148 if ((mem = spaces[i]) != 0)
149 ialloc_validate_memory(mem, gcst);
150 }
151 #else /* !DEBUG */
152 # define end_phase(str) DO_NOTHING
153 #endif /* DEBUG */
154 void
gs_gc_reclaim(vm_spaces * pspaces,bool global)155 gs_gc_reclaim(vm_spaces * pspaces, bool global)
156 {
157 #define nspaces ((i_vm_max + 1) * 2) /* * 2 for stable allocators */
158
159 vm_spaces spaces;
160 gs_ref_memory_t *space_memories[nspaces];
161 gs_gc_root_t space_roots[nspaces];
162 int max_trace; /* max space_ to trace */
163 int min_collect; /* min space_ to collect */
164 int min_collect_vm_space; /* min VM space to collect */
165 int ispace;
166 gs_ref_memory_t *mem;
167 chunk_t *cp;
168 gs_gc_root_t *rp;
169 gc_state_t state;
170 struct _msd {
171 gc_mark_stack stack;
172 ms_entry body[ms_size_default];
173 } ms_default;
174 gc_mark_stack *mark_stack = &ms_default.stack;
175 const gs_memory_t *cmem;
176
177 /* Optionally force global GC for debugging. */
178
179 if (I_FORCE_GLOBAL_GC)
180 global = true;
181
182 /* Determine which spaces we are tracing and collecting. */
183
184 spaces = *pspaces;
185 cmem = space_system->stable_memory;
186 space_memories[1] = space_system;
187 space_memories[2] = space_global;
188 min_collect = max_trace = 2;
189 min_collect_vm_space = i_vm_global;
190 if (space_global->stable_memory != (gs_memory_t *)space_global)
191 space_memories[++max_trace] =
192 (gs_ref_memory_t *)space_global->stable_memory;
193 if (space_global != space_local) {
194 space_memories[++max_trace] = space_local;
195 min_collect = max_trace;
196 min_collect_vm_space = i_vm_local;
197 if (space_local->stable_memory != (gs_memory_t *)space_local)
198 space_memories[++max_trace] =
199 (gs_ref_memory_t *)space_local->stable_memory;
200 }
201 if (global)
202 min_collect = min_collect_vm_space = 1;
203
204 #define for_spaces(i, n)\
205 for (i = 1; i <= n; ++i)
206 #define for_collected_spaces(i)\
207 for (i = min_collect; i <= max_trace; ++i)
208 #define for_space_mems(i, mem)\
209 for (mem = space_memories[i]; mem != 0; mem = &mem->saved->state)
210 #define for_mem_chunks(mem, cp)\
211 for (cp = (mem)->cfirst; cp != 0; cp = cp->cnext)
212 #define for_space_chunks(i, mem, cp)\
213 for_space_mems(i, mem) for_mem_chunks(mem, cp)
214 #define for_chunks(n, mem, cp)\
215 for_spaces(ispace, n) for_space_chunks(ispace, mem, cp)
216 #define for_collected_chunks(mem, cp)\
217 for_collected_spaces(ispace) for_space_chunks(ispace, mem, cp)
218 #define for_roots(n, mem, rp)\
219 for_spaces(ispace, n)\
220 for (mem = space_memories[ispace], rp = mem->roots; rp != 0; rp = rp->next)
221
222 /* Initialize the state. */
223
224 state.procs = &igc_procs;
225 state.loc.memory = space_global; /* any one will do */
226
227 state.loc.cp = 0;
228 state.spaces = spaces;
229 state.min_collect = min_collect_vm_space << r_space_shift;
230 state.relocating_untraced = false;
231 state.heap = state.loc.memory->non_gc_memory;
232 state.ntable = state.heap->gs_lib_ctx->gs_name_table;
233
234 /* Register the allocators themselves as roots, */
235 /* so we mark and relocate the change and save lists properly. */
236
237 for_spaces(ispace, max_trace)
238 gs_register_struct_root((gs_memory_t *)space_memories[ispace],
239 &space_roots[ispace],
240 (void **)&space_memories[ispace],
241 "gc_top_level");
242
243 end_phase("register space roots");
244
245 #ifdef DEBUG
246
247 /* Pre-validate the state. This shouldn't be necessary.... */
248
249 gc_validate_spaces(space_memories, max_trace, &state);
250
251 end_phase("pre-validate pointers");
252
253 #endif
254
255 if (I_BYPASS_GC) { /* Don't collect at all. */
256 goto no_collect;
257 }
258
259 /* Clear marks in spaces to be collected. */
260
261 for_collected_spaces(ispace)
262 for_space_chunks(ispace, mem, cp) {
263 gc_objects_clear_marks((const gs_memory_t *)mem, cp);
264 gc_strings_set_marks(cp, false);
265 }
266
267 end_phase("clear chunk marks");
268
269 /* Clear the marks of roots. We must do this explicitly, */
270 /* since some roots are not in any chunk. */
271
272 for_roots(max_trace, mem, rp) {
273 enum_ptr_t eptr;
274
275 eptr.ptr = *rp->p;
276 if_debug_root('6', "[6]unmarking root", rp);
277 (*rp->ptype->unmark)(&eptr, &state);
278 }
279
280 end_phase("clear root marks");
281
282 if (global)
283 gc_unmark_names(state.ntable);
284
285 /* Initialize the (default) mark stack. */
286
287 gc_init_mark_stack(&ms_default.stack, ms_size_default);
288 ms_default.stack.prev = 0;
289 ms_default.stack.on_heap = false;
290
291 /* Add all large-enough free blocks to the mark stack. */
292 /* Also initialize the rescan pointers. */
293
294 {
295 gc_mark_stack *end = mark_stack;
296
297 for_chunks(max_trace, mem, cp) {
298 uint avail = cp->ctop - cp->cbot;
299
300 if (avail >= sizeof(gc_mark_stack) + sizeof(ms_entry) *
301 ms_size_min &&
302 !cp->inner_count
303 ) {
304 gc_mark_stack *pms = (gc_mark_stack *) cp->cbot;
305
306 gc_init_mark_stack(pms, (avail - sizeof(gc_mark_stack)) /
307 sizeof(ms_entry));
308 end->next = pms;
309 pms->prev = end;
310 pms->on_heap = false;
311 if_debug2('6', "[6]adding free 0x%lx(%u) to mark stack\n",
312 (ulong) pms, pms->count);
313 }
314 cp->rescan_bot = cp->cend;
315 cp->rescan_top = cp->cbase;
316 }
317 }
318
319 /* Mark reachable objects. */
320
321 {
322 int more = 0;
323
324 /* Mark from roots. */
325
326 for_roots(max_trace, mem, rp) {
327 if_debug_root('6', "[6]marking root", rp);
328 more |= gc_trace(rp, &state, mark_stack);
329 }
330
331 end_phase("mark");
332
333 /* If this is a local GC, mark from non-local chunks. */
334
335 if (!global)
336 for_chunks(min_collect - 1, mem, cp)
337 more |= gc_trace_chunk((const gs_memory_t *)mem, cp, &state, mark_stack);
338
339 /* Handle mark stack overflow. */
340
341 while (more < 0) { /* stack overflowed */
342 more = 0;
343 for_chunks(max_trace, mem, cp)
344 more |= gc_rescan_chunk(cp, &state, mark_stack);
345 }
346
347 end_phase("mark overflow");
348 }
349
350 /* Free the mark stack. */
351
352 {
353 gc_mark_stack *pms = mark_stack;
354
355 while (pms->next)
356 pms = pms->next;
357 while (pms) {
358 gc_mark_stack *prev = pms->prev;
359
360 if (pms->on_heap)
361 gs_free_object(state.heap, pms, "gc mark stack");
362 else
363 gs_alloc_fill(pms, gs_alloc_fill_free,
364 sizeof(*pms) + sizeof(ms_entry) * pms->count);
365 pms = prev;
366 }
367 }
368
369 end_phase("free mark stack");
370
371 if (global) {
372 gc_trace_finish(&state);
373 names_trace_finish(state.ntable, &state);
374
375 end_phase("finish trace");
376 }
377 /* Clear marks and relocation in spaces that are only being traced. */
378 /* We have to clear the marks first, because we want the */
379 /* relocation to wind up as o_untraced, not o_unmarked. */
380
381 for_chunks(min_collect - 1, mem, cp)
382 gc_objects_clear_marks((const gs_memory_t *)mem, cp);
383
384 end_phase("post-clear marks");
385
386 for_chunks(min_collect - 1, mem, cp)
387 gc_clear_reloc(cp);
388
389 end_phase("clear reloc");
390
391 /* Set the relocation of roots outside any chunk to o_untraced, */
392 /* so we won't try to relocate pointers to them. */
393 /* (Currently, there aren't any.) */
394
395 /* Disable freeing in the allocators of the spaces we are */
396 /* collecting, so finalization procedures won't cause problems. */
397 {
398 int i;
399
400 for_collected_spaces(i)
401 gs_enable_free((gs_memory_t *)space_memories[i], false);
402 }
403
404 /* Compute relocation based on marks, in the spaces */
405 /* we are going to compact. Also finalize freed objects. */
406
407 for_collected_chunks(mem, cp) {
408 gc_objects_set_reloc(cp);
409 gc_strings_set_reloc(cp);
410 }
411
412 /* Re-enable freeing. */
413 {
414 int i;
415
416 for_collected_spaces(i)
417 gs_enable_free((gs_memory_t *)space_memories[i], true);
418 }
419
420 end_phase("set reloc");
421
422 /* Relocate pointers. */
423
424 state.relocating_untraced = true;
425 for_chunks(min_collect - 1, mem, cp)
426 gc_do_reloc(cp, mem, &state);
427 state.relocating_untraced = false;
428 for_collected_chunks(mem, cp)
429 gc_do_reloc(cp, mem, &state);
430
431 end_phase("relocate chunks");
432
433 for_roots(max_trace, mem, rp) {
434 if_debug3('6', "[6]relocating root 0x%lx: 0x%lx -> 0x%lx\n",
435 (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
436 if (rp->ptype == ptr_ref_type) {
437 ref *pref = (ref *) * rp->p;
438
439 igc_reloc_refs((ref_packed *) pref,
440 (ref_packed *) (pref + 1),
441 &state);
442 } else
443 *rp->p = (*rp->ptype->reloc) (*rp->p, &state);
444 if_debug3('6', "[6]relocated root 0x%lx: 0x%lx -> 0x%lx\n",
445 (ulong) rp, (ulong) rp->p, (ulong) * rp->p);
446 }
447
448 end_phase("relocate roots");
449
450 /* Compact data. We only do this for spaces we are collecting. */
451
452 for_collected_spaces(ispace) {
453 for_space_mems(ispace, mem) {
454 for_mem_chunks(mem, cp) {
455 if_debug_chunk('6', "[6]compacting chunk", cp);
456 gc_objects_compact(cp, &state);
457 gc_strings_compact(cp);
458 if_debug_chunk('6', "[6]after compaction:", cp);
459 if (mem->pcc == cp)
460 mem->cc = *cp;
461 }
462 mem->saved = mem->reloc_saved;
463 ialloc_reset_free(mem);
464 }
465 }
466
467 end_phase("compact");
468
469 /* Free empty chunks. */
470
471 for_collected_spaces(ispace) {
472 for_space_mems(ispace, mem) {
473 gc_free_empty_chunks(mem);
474 }
475 }
476
477 end_phase("free empty chunks");
478
479 /*
480 * Update previous_status to reflect any freed chunks,
481 * and set inherited to the negative of allocated,
482 * so it has no effect. We must update previous_status by
483 * working back-to-front along the save chain, using pointer reversal.
484 * (We could update inherited in any order, since it only uses
485 * information local to the individual save level.)
486 */
487
488 for_collected_spaces(ispace) { /* Reverse the pointers. */
489 alloc_save_t *curr;
490 alloc_save_t *prev = 0;
491 alloc_save_t *next;
492 gs_memory_status_t total;
493
494 for (curr = space_memories[ispace]->saved; curr != 0;
495 prev = curr, curr = next
496 ) {
497 next = curr->state.saved;
498 curr->state.saved = prev;
499 }
500 /* Now work the other way, accumulating the values. */
501 total.allocated = 0, total.used = 0;
502 for (curr = prev, prev = 0; curr != 0;
503 prev = curr, curr = next
504 ) {
505 mem = &curr->state;
506 next = mem->saved;
507 mem->saved = prev;
508 mem->previous_status = total;
509 if_debug3('6',
510 "[6]0x%lx previous allocated=%lu, used=%lu\n",
511 (ulong) mem, total.allocated, total.used);
512 gs_memory_status((gs_memory_t *) mem, &total);
513 mem->gc_allocated = mem->allocated + total.allocated;
514 mem->inherited = -(int)mem->allocated;
515 }
516 mem = space_memories[ispace];
517 mem->previous_status = total;
518 mem->gc_allocated = mem->allocated + total.allocated;
519 if_debug3('6', "[6]0x%lx previous allocated=%lu, used=%lu\n",
520 (ulong) mem, total.allocated, total.used);
521 }
522
523 end_phase("update stats");
524
525 no_collect:
526
527 /* Unregister the allocator roots. */
528
529 for_spaces(ispace, max_trace)
530 gs_unregister_root((gs_memory_t *)space_memories[ispace],
531 &space_roots[ispace], "gc_top_level");
532
533 end_phase("unregister space roots");
534
535 #ifdef DEBUG
536
537 /* Validate the state. This shouldn't be necessary.... */
538
539 gc_validate_spaces(space_memories, max_trace, &state);
540
541 end_phase("validate pointers");
542
543 #endif
544 }
545
546 /* ------ Debugging utilities ------ */
547
548 /* Validate a pointer to an object header. */
549 #ifdef DEBUG
550 # define debug_check_object(pre, cp, gcst)\
551 ialloc_validate_object((pre) + 1, cp, gcst)
552 #else
553 # define debug_check_object(pre, cp, gcst) DO_NOTHING
554 #endif
555
556 /* ------ Unmarking phase ------ */
557
558 /* Unmark a single struct. */
559 private void
ptr_struct_unmark(enum_ptr_t * pep,gc_state_t * ignored)560 ptr_struct_unmark(enum_ptr_t *pep, gc_state_t * ignored)
561 {
562 void *const vptr = (void *)pep->ptr; /* break const */
563
564 if (vptr != 0)
565 o_set_unmarked(((obj_header_t *) vptr - 1));
566 }
567
568 /* Unmark a single string. */
569 private void
ptr_string_unmark(enum_ptr_t * pep,gc_state_t * gcst)570 ptr_string_unmark(enum_ptr_t *pep, gc_state_t * gcst)
571 {
572 discard(gc_string_mark(pep->ptr, pep->size, false, gcst));
573 }
574
575 /* Unmark the objects in a chunk. */
576 private void
gc_objects_clear_marks(const gs_memory_t * mem,chunk_t * cp)577 gc_objects_clear_marks(const gs_memory_t *mem, chunk_t * cp)
578 {
579 if_debug_chunk('6', "[6]unmarking chunk", cp);
580 SCAN_CHUNK_OBJECTS(cp)
581 DO_ALL
582 struct_proc_clear_marks((*proc)) =
583 pre->o_type->clear_marks;
584 #ifdef DEBUG
585 if (pre->o_type != &st_free)
586 debug_check_object(pre, cp, NULL);
587 #endif
588 if_debug3('7', " [7](un)marking %s(%lu) 0x%lx\n",
589 struct_type_name_string(pre->o_type),
590 (ulong) size, (ulong) pre);
591 o_set_unmarked(pre);
592 if (proc != 0)
593 (*proc) (mem, pre + 1, size, pre->o_type);
594 END_OBJECTS_SCAN
595 }
596
597 /* Mark 0- and 1-character names, and those referenced from the */
598 /* op_array_nx_table, and unmark all the rest. */
599 private void
gc_unmark_names(name_table * nt)600 gc_unmark_names(name_table * nt)
601 {
602 uint i;
603
604 names_unmark_all(nt);
605 for (i = 0; i < op_array_table_global.count; i++) {
606 name_index_t nidx = op_array_table_global.nx_table[i];
607
608 names_mark_index(nt, nidx);
609 }
610 for (i = 0; i < op_array_table_local.count; i++) {
611 name_index_t nidx = op_array_table_local.nx_table[i];
612
613 names_mark_index(nt, nidx);
614 }
615 }
616
617 /* ------ Marking phase ------ */
618
619 /* Initialize (a segment of) the mark stack. */
620 private void
gc_init_mark_stack(gc_mark_stack * pms,uint count)621 gc_init_mark_stack(gc_mark_stack * pms, uint count)
622 {
623 pms->next = 0;
624 pms->count = count;
625 pms->entries[0].ptr = 0;
626 pms->entries[0].index = 0;
627 pms->entries[0].is_refs = false;
628 }
629
630 /* Mark starting from all marked objects in the interval of a chunk */
631 /* needing rescanning. */
632 private int
gc_rescan_chunk(chunk_t * cp,gc_state_t * pstate,gc_mark_stack * pmstack)633 gc_rescan_chunk(chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
634 {
635 byte *sbot = cp->rescan_bot;
636 byte *stop = cp->rescan_top;
637 gs_gc_root_t root;
638 void *comp;
639 int more = 0;
640 const gs_memory_t *mem = gcst_get_memory_ptr( pstate );
641
642 if (sbot > stop)
643 return 0;
644 root.p = ∁
645 if_debug_chunk('6', "[6]rescanning chunk", cp);
646 cp->rescan_bot = cp->cend;
647 cp->rescan_top = cp->cbase;
648 SCAN_CHUNK_OBJECTS(cp)
649 DO_ALL
650 if ((byte *) (pre + 1) + size < sbot);
651 else if ((byte *) (pre + 1) > stop)
652 return more; /* 'break' won't work here */
653 else {
654 if_debug2('7', " [7]scanning/marking 0x%lx(%lu)\n",
655 (ulong) pre, (ulong) size);
656 if (pre->o_type == &st_refs) {
657 ref_packed *rp = (ref_packed *) (pre + 1);
658 char *end = (char *)rp + size;
659
660 root.ptype = ptr_ref_type;
661 while ((char *)rp < end) {
662 comp = rp;
663 if (r_is_packed(rp)) {
664 if (r_has_pmark(rp)) {
665 r_clear_pmark(rp);
666 more |= gc_trace(&root, pstate,
667 pmstack);
668 }
669 rp++;
670 } else {
671 ref *const pref = (ref *)rp;
672
673 if (r_has_attr(pref, l_mark)) {
674 r_clear_attrs(pref, l_mark);
675 more |= gc_trace(&root, pstate, pmstack);
676 }
677 rp += packed_per_ref;
678 }
679 }
680 } else if (!o_is_unmarked(pre)) {
681 struct_proc_clear_marks((*proc)) =
682 pre->o_type->clear_marks;
683 root.ptype = ptr_struct_type;
684 comp = pre + 1;
685 if (!o_is_untraced(pre))
686 o_set_unmarked(pre);
687 if (proc != 0)
688 (*proc) (mem, comp, size, pre->o_type);
689 more |= gc_trace(&root, pstate, pmstack);
690 }
691 }
692 END_OBJECTS_SCAN
693 return more;
694 }
695
696 /* Mark starting from all the objects in a chunk. */
697 /* We assume that pstate->min_collect > avm_system, */
698 /* so we don't have to trace names. */
699 private int
gc_trace_chunk(const gs_memory_t * mem,chunk_t * cp,gc_state_t * pstate,gc_mark_stack * pmstack)700 gc_trace_chunk(const gs_memory_t *mem, chunk_t * cp, gc_state_t * pstate, gc_mark_stack * pmstack)
701 {
702 gs_gc_root_t root;
703 void *comp;
704 int more = 0;
705 int min_trace = pstate->min_collect;
706
707 root.p = ∁
708 if_debug_chunk('6', "[6]marking from chunk", cp);
709 SCAN_CHUNK_OBJECTS(cp)
710 DO_ALL
711 {
712 if_debug2('7', " [7]scanning/marking 0x%lx(%lu)\n",
713 (ulong) pre, (ulong) size);
714 if (pre->o_type == &st_refs) {
715 ref_packed *rp = (ref_packed *) (pre + 1);
716 char *end = (char *)rp + size;
717
718 root.ptype = ptr_ref_type;
719 while ((char *)rp < end) {
720 comp = rp;
721 if (r_is_packed(rp)) { /* No packed refs need tracing. */
722 rp++;
723 } else {
724 ref *const pref = (ref *)rp;
725
726 if (r_space(pref) >= min_trace) {
727 r_clear_attrs(pref, l_mark);
728 more |= gc_trace(&root, pstate, pmstack);
729 }
730 rp += packed_per_ref;
731 }
732 }
733 } else if (!o_is_unmarked(pre)) {
734 if (!o_is_untraced(pre))
735 o_set_unmarked(pre);
736 if (pre->o_type != &st_free) {
737 struct_proc_clear_marks((*proc)) =
738 pre->o_type->clear_marks;
739
740 root.ptype = ptr_struct_type;
741 comp = pre + 1;
742 if (proc != 0)
743 (*proc) (mem, comp, size, pre->o_type);
744 more |= gc_trace(&root, pstate, pmstack);
745 }
746 }
747 }
748 END_OBJECTS_SCAN
749 return more;
750 }
751
752 /* Recursively mark from a (root) pointer. */
753 /* Return -1 if we overflowed the mark stack, */
754 /* 0 if we completed successfully without marking any new objects, */
755 /* 1 if we completed and marked some new objects. */
756 private int gc_extend_stack(gc_mark_stack *, gc_state_t *);
757 private int
gc_trace(gs_gc_root_t * rp,gc_state_t * pstate,gc_mark_stack * pmstack)758 gc_trace(gs_gc_root_t * rp, gc_state_t * pstate, gc_mark_stack * pmstack)
759 {
760 int min_trace = pstate->min_collect;
761 gc_mark_stack *pms = pmstack;
762 ms_entry *sp = pms->entries + 1;
763
764 /* We stop the mark stack 1 entry early, because we store into */
765 /* the entry beyond the top. */
766 ms_entry *stop = sp + pms->count - 2;
767 int new = 0;
768 enum_ptr_t nep;
769 void *nptr;
770 name_table *nt = pstate->ntable;
771
772 #define mark_name(nidx)\
773 BEGIN\
774 if (names_mark_index(nt, nidx)) {\
775 new |= 1;\
776 if_debug2('8', " [8]marked name 0x%lx(%u)\n",\
777 (ulong)names_index_ptr(nt, nidx), nidx);\
778 }\
779 END
780
781 nptr = *rp->p;
782 if (nptr == 0)
783 return 0;
784
785 /* Initialize the stack */
786 sp->ptr = nptr;
787 if (rp->ptype == ptr_ref_type)
788 sp->index = 1, sp->is_refs = true;
789 else {
790 sp->index = 0, sp->is_refs = false;
791 nep.ptr = nptr;
792 if ((*rp->ptype->mark) (&nep, pstate))
793 new |= 1;
794 }
795 for (;;) {
796 gs_ptr_type_t ptp;
797
798 /*
799 * The following should really be an if..else, but that
800 * would force unnecessary is_refs tests.
801 */
802 if (sp->is_refs)
803 goto do_refs;
804
805 /* ---------------- Structure ---------------- */
806
807 do_struct:
808 {
809 obj_header_t *ptr = sp->ptr;
810
811 struct_proc_enum_ptrs((*mproc));
812
813 if (ptr == 0) { /* We've reached the bottom of a stack segment. */
814 pms = pms->prev;
815 if (pms == 0)
816 break; /* all done */
817 stop = pms->entries + pms->count - 1;
818 sp = stop;
819 continue;
820 }
821 debug_check_object(ptr - 1, NULL, NULL);
822 ts:if_debug4('7', " [7]%smarking %s 0x%lx[%u]",
823 depth_dots(sp, pms),
824 struct_type_name_string(ptr[-1].o_type),
825 (ulong) ptr, sp->index);
826 mproc = ptr[-1].o_type->enum_ptrs;
827 if (mproc == gs_no_struct_enum_ptrs ||
828 (ptp = (*mproc)
829 (gcst_get_memory_ptr(pstate), ptr, pre_obj_contents_size(ptr - 1),
830 sp->index, &nep, ptr[-1].o_type, pstate)) == 0
831 ) {
832 if_debug0('7', " - done\n");
833 sp--;
834 continue;
835 }
836 /* The cast in the following statement is the one */
837 /* place we need to break 'const' to make the */
838 /* template for pointer enumeration work. */
839 nptr = (void *)nep.ptr;
840 sp->index++;
841 if_debug1('7', " = 0x%lx\n", (ulong) nptr);
842 /* Descend into nep.ptr, whose pointer type is ptp. */
843 if (ptp == ptr_struct_type) {
844 sp[1].index = 0;
845 sp[1].is_refs = false;
846 if (sp == stop)
847 goto push;
848 if (!ptr_struct_mark(&nep, pstate))
849 goto ts;
850 new |= 1;
851 (++sp)->ptr = nptr;
852 goto do_struct;
853 } else if (ptp == ptr_ref_type) {
854 sp[1].index = 1;
855 sp[1].is_refs = true;
856 if (sp == stop)
857 goto push;
858 new |= 1;
859 (++sp)->ptr = nptr;
860 goto do_refs;
861 } else { /* We assume this is some non-pointer- */
862 /* containing type. */
863 if ((*ptp->mark) (&nep, pstate))
864 new |= 1;
865 goto ts;
866 }
867 }
868
869 /* ---------------- Refs ---------------- */
870
871 do_refs:
872 {
873 ref_packed *pptr = sp->ptr;
874 ref *rptr;
875
876 tr:if (!sp->index) {
877 --sp;
878 continue;
879 }
880 --(sp->index);
881 if_debug3('8', " [8]%smarking refs 0x%lx[%u]\n",
882 depth_dots(sp, pms), (ulong) pptr, sp->index);
883 if (r_is_packed(pptr)) {
884 if (!r_has_pmark(pptr)) {
885 r_set_pmark(pptr);
886 new |= 1;
887 if (r_packed_is_name(pptr)) {
888 name_index_t nidx = packed_name_index(pptr);
889
890 mark_name(nidx);
891 }
892 }
893 ++pptr;
894 goto tr;
895 }
896 rptr = (ref *) pptr; /* * const beyond here */
897 if (r_has_attr(rptr, l_mark)) {
898 pptr = (ref_packed *)(rptr + 1);
899 goto tr;
900 }
901 r_set_attrs(rptr, l_mark);
902 new |= 1;
903 if (r_space(rptr) < min_trace) { /* Note that this always picks up all scalars. */
904 pptr = (ref_packed *) (rptr + 1);
905 goto tr;
906 }
907 sp->ptr = rptr + 1;
908 switch (r_type(rptr)) {
909 /* Struct cases */
910 case t_file:
911 nptr = rptr->value.pfile;
912 rs:sp[1].is_refs = false;
913 sp[1].index = 0;
914 if (sp == stop) {
915 ptp = ptr_struct_type;
916 break;
917 }
918 nep.ptr = nptr;
919 if (!ptr_struct_mark(&nep, pstate))
920 goto nr;
921 new |= 1;
922 (++sp)->ptr = nptr;
923 goto do_struct;
924 case t_device:
925 nptr = rptr->value.pdevice;
926 goto rs;
927 case t_fontID:
928 case t_struct:
929 case t_astruct:
930 nptr = rptr->value.pstruct;
931 goto rs;
932 /* Non-trivial non-struct cases */
933 case t_dictionary:
934 nptr = rptr->value.pdict;
935 sp[1].index = sizeof(dict) / sizeof(ref);
936 goto rrp;
937 case t_array:
938 nptr = rptr->value.refs;
939 rr:if ((sp[1].index = r_size(rptr)) == 0) { /* Set the base pointer to 0, */
940 /* so we never try to relocate it. */
941 rptr->value.refs = 0;
942 goto nr;
943 }
944 rrp:
945 rrc:sp[1].is_refs = true;
946 if (sp == stop) {
947 /*
948 * The following initialization is unnecessary:
949 * ptp will not be used if sp[1].is_refs = true.
950 * We put this here solely to get rid of bogus
951 * "possibly uninitialized variable" warnings
952 * from certain compilers.
953 */
954 ptp = ptr_ref_type;
955 break;
956 }
957 new |= 1;
958 (++sp)->ptr = nptr;
959 goto do_refs;
960 case t_mixedarray:
961 case t_shortarray:
962 nptr = rptr->value.writable_packed;
963 goto rr;
964 case t_name:
965 mark_name(names_index(nt, rptr));
966 nr:pptr = (ref_packed *) (rptr + 1);
967 goto tr;
968 case t_string:
969 if (gc_string_mark(rptr->value.bytes, r_size(rptr), true, pstate))
970 new |= 1;
971 goto nr;
972 case t_oparray:
973 nptr = rptr->value.refs; /* discard const */
974 sp[1].index = 1;
975 goto rrc;
976 default:
977 goto nr;
978 }
979 }
980
981 /* ---------------- Recursion ---------------- */
982
983 push:
984 if (sp == stop) { /* The current segment is full. */
985 int new_added = gc_extend_stack(pms, pstate);
986
987 if (new_added) {
988 new |= new_added;
989 continue;
990 }
991 pms = pms->next;
992 stop = pms->entries + pms->count - 1;
993 pms->entries[1] = sp[1];
994 sp = pms->entries;
995 }
996 /* index and is_refs are already set */
997 if (!sp[1].is_refs) {
998 nep.ptr = nptr;
999 if (!(*ptp->mark) (&nep, pstate))
1000 continue;
1001 new |= 1;
1002 }
1003 (++sp)->ptr = nptr;
1004 }
1005 return new;
1006 }
1007 /* Link to, attempting to allocate if necessary, */
1008 /* another chunk of mark stack. */
1009 private int
gc_extend_stack(gc_mark_stack * pms,gc_state_t * pstate)1010 gc_extend_stack(gc_mark_stack * pms, gc_state_t * pstate)
1011 {
1012 if (pms->next == 0) { /* Try to allocate another segment. */
1013 uint count;
1014
1015 for (count = ms_size_desired; count >= ms_size_min; count >>= 1) {
1016 pms->next = (gc_mark_stack *)
1017 gs_alloc_bytes_immovable(pstate->heap,
1018 sizeof(gc_mark_stack) +
1019 sizeof(ms_entry) * count,
1020 "gc mark stack");
1021 if (pms->next != 0)
1022 break;
1023 }
1024 if (pms->next == 0) { /* The mark stack overflowed. */
1025 ms_entry *sp = pms->entries + pms->count - 1;
1026 byte *cptr = sp->ptr; /* container */
1027 chunk_t *cp = gc_locate(cptr, pstate);
1028 int new = 1;
1029
1030 if (cp == 0) { /* We were tracing outside collectible */
1031 /* storage. This can't happen. */
1032 lprintf1("mark stack overflowed while outside collectible space at 0x%lx!\n",
1033 (ulong) cptr);
1034 gs_abort(pstate->heap);
1035 }
1036 if (cptr < cp->rescan_bot)
1037 cp->rescan_bot = cptr, new = -1;
1038 if (cptr > cp->rescan_top)
1039 cp->rescan_top = cptr, new = -1;
1040 return new;
1041 }
1042 gc_init_mark_stack(pms->next, count);
1043 pms->next->prev = pms;
1044 pms->next->on_heap = true;
1045 }
1046 return 0;
1047 }
1048
1049 /* Mark a struct. Return true if new mark. */
1050 private bool
ptr_struct_mark(enum_ptr_t * pep,gc_state_t * ignored)1051 ptr_struct_mark(enum_ptr_t *pep, gc_state_t * ignored)
1052 {
1053 obj_header_t *ptr = (obj_header_t *)pep->ptr;
1054
1055 if (ptr == 0)
1056 return false;
1057 ptr--; /* point to header */
1058 if (!o_is_unmarked(ptr))
1059 return false;
1060 o_mark(ptr);
1061 return true;
1062 }
1063
1064 /* Mark a string. Return true if new mark. */
1065 private bool
ptr_string_mark(enum_ptr_t * pep,gc_state_t * gcst)1066 ptr_string_mark(enum_ptr_t *pep, gc_state_t * gcst)
1067 {
1068 return gc_string_mark(pep->ptr, pep->size, true, gcst);
1069 }
1070
1071 /* Finish tracing by marking names. */
1072 private bool
gc_trace_finish(gc_state_t * pstate)1073 gc_trace_finish(gc_state_t * pstate)
1074 {
1075 name_table *nt = pstate->ntable;
1076 name_index_t nidx = 0;
1077 bool marked = false;
1078
1079 while ((nidx = names_next_valid_index(nt, nidx)) != 0) {
1080 name_string_t *pnstr = names_index_string_inline(nt, nidx);
1081
1082 if (pnstr->mark) {
1083 enum_ptr_t enst, ensst;
1084
1085 if (!pnstr->foreign_string &&
1086 gc_string_mark(pnstr->string_bytes, pnstr->string_size,
1087 true, pstate)
1088 )
1089 marked = true;
1090 enst.ptr = names_index_sub_table(nt, nidx);
1091 ensst.ptr = names_index_string_sub_table(nt, nidx);
1092 marked |=
1093 ptr_struct_mark(&enst, pstate) |
1094 ptr_struct_mark(&ensst, pstate);
1095 }
1096 }
1097 return marked;
1098 }
1099
1100 /* ------ Relocation planning phase ------ */
1101
1102 /* Initialize the relocation information in the chunk header. */
1103 private void
gc_init_reloc(chunk_t * cp)1104 gc_init_reloc(chunk_t * cp)
1105 {
1106 chunk_head_t *chead = cp->chead;
1107
1108 chead->dest = cp->cbase;
1109 chead->free.o_back =
1110 offset_of(chunk_head_t, free) >> obj_back_shift;
1111 chead->free.o_size = sizeof(obj_header_t);
1112 chead->free.o_nreloc = 0;
1113 }
1114
1115 /* Set marks and clear relocation for chunks that won't be compacted. */
1116 private void
gc_clear_reloc(chunk_t * cp)1117 gc_clear_reloc(chunk_t * cp)
1118 {
1119 byte *pfree = (byte *) & cp->chead->free;
1120
1121 gc_init_reloc(cp);
1122 SCAN_CHUNK_OBJECTS(cp)
1123 DO_ALL
1124 const struct_shared_procs_t *procs =
1125 pre->o_type->shared;
1126
1127 if (procs != 0)
1128 (*procs->clear_reloc) (pre, size);
1129 o_set_untraced(pre);
1130 pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
1131 END_OBJECTS_SCAN
1132 gc_strings_set_marks(cp, true);
1133 gc_strings_clear_reloc(cp);
1134 }
1135
1136 /* Set the relocation for the objects in a chunk. */
1137 /* This will never be called for a chunk with any o_untraced objects. */
1138 private void
gc_objects_set_reloc(chunk_t * cp)1139 gc_objects_set_reloc(chunk_t * cp)
1140 {
1141 uint reloc = 0;
1142 chunk_head_t *chead = cp->chead;
1143 byte *pfree = (byte *) & chead->free; /* most recent free object */
1144
1145 if_debug_chunk('6', "[6]setting reloc for chunk", cp);
1146 gc_init_reloc(cp);
1147 SCAN_CHUNK_OBJECTS(cp)
1148 DO_ALL
1149 struct_proc_finalize((*finalize));
1150 const struct_shared_procs_t *procs =
1151 pre->o_type->shared;
1152
1153 if ((procs == 0 ? o_is_unmarked(pre) :
1154 !(*procs->set_reloc) (pre, reloc, size))
1155 ) { /* Free object */
1156 reloc += sizeof(obj_header_t) + obj_align_round(size);
1157 if ((finalize = pre->o_type->finalize) != 0) {
1158 if_debug2('u', "[u]GC finalizing %s 0x%lx\n",
1159 struct_type_name_string(pre->o_type),
1160 (ulong) (pre + 1));
1161 (*finalize) (pre + 1);
1162 }
1163 pfree = (byte *) pre;
1164 pre->o_back = (pfree - (byte *) chead) >> obj_back_shift;
1165 pre->o_nreloc = reloc;
1166 if_debug3('7', " [7]at 0x%lx, unmarked %lu, new reloc = %u\n",
1167 (ulong) pre, (ulong) size, reloc);
1168 } else { /* Useful object */
1169 debug_check_object(pre, cp, NULL);
1170 pre->o_back = ((byte *) pre - pfree) >> obj_back_shift;
1171 }
1172 END_OBJECTS_SCAN
1173 #ifdef DEBUG
1174 if (reloc != 0) {
1175 if_debug1('6', "[6]freed %u", reloc);
1176 if_debug_chunk('6', " in", cp);
1177 }
1178 #endif
1179 }
1180
1181 /* ------ Relocation phase ------ */
1182
1183 /* Relocate the pointers in all the objects in a chunk. */
1184 private void
gc_do_reloc(chunk_t * cp,gs_ref_memory_t * mem,gc_state_t * pstate)1185 gc_do_reloc(chunk_t * cp, gs_ref_memory_t * mem, gc_state_t * pstate)
1186 {
1187 chunk_head_t *chead = cp->chead;
1188
1189 if_debug_chunk('6', "[6]relocating in chunk", cp);
1190 SCAN_CHUNK_OBJECTS(cp)
1191 DO_ALL
1192 #ifdef DEBUG
1193 pstate->container = cp;
1194 #endif
1195 /* We need to relocate the pointers in an object iff */
1196 /* it is o_untraced, or it is a useful object. */
1197 /* An object is free iff its back pointer points to */
1198 /* the chunk_head structure. */
1199 if (o_is_untraced(pre) ||
1200 pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead
1201 ) {
1202 struct_proc_reloc_ptrs((*proc)) =
1203 pre->o_type->reloc_ptrs;
1204
1205 if_debug3('7',
1206 " [7]relocating ptrs in %s(%lu) 0x%lx\n",
1207 struct_type_name_string(pre->o_type),
1208 (ulong) size, (ulong) pre);
1209 if (proc != 0)
1210 (*proc) (pre + 1, size, pre->o_type, pstate);
1211 }
1212 #ifdef DEBUG
1213 pstate->container = 0;
1214 #endif
1215 END_OBJECTS_SCAN
1216 }
1217
1218 /* Print pointer relocation if debugging. */
1219 /* We have to provide this procedure even if DEBUG is not defined, */
1220 /* in case one of the other GC modules was compiled with DEBUG. */
1221 const void *
print_reloc_proc(const void * obj,const char * cname,const void * robj)1222 print_reloc_proc(const void *obj, const char *cname, const void *robj)
1223 {
1224 if_debug3('9', " [9]relocate %s * 0x%lx to 0x%lx\n",
1225 cname, (ulong)obj, (ulong)robj);
1226 return robj;
1227 }
1228
1229 /* Relocate a pointer to an (aligned) object. */
1230 /* See gsmemory.h for why the argument is const and the result is not. */
1231 private void /*obj_header_t */ *
igc_reloc_struct_ptr(const void * obj,gc_state_t * gcst)1232 igc_reloc_struct_ptr(const void /*obj_header_t */ *obj, gc_state_t * gcst)
1233 {
1234 const obj_header_t *const optr = (const obj_header_t *)obj;
1235 const void *robj;
1236
1237 if (obj == 0) {
1238 discard(print_reloc(obj, "NULL", 0));
1239 return 0;
1240 }
1241 debug_check_object(optr - 1, NULL, gcst);
1242 {
1243 uint back = optr[-1].o_back;
1244
1245 if (back == o_untraced)
1246 robj = obj;
1247 else {
1248 #ifdef DEBUG
1249 /* Do some sanity checking. */
1250 chunk_t *cp = gcst->container;
1251
1252 if (cp != 0 && cp->cbase <= (byte *)obj && (byte *)obj <cp->ctop) {
1253 if (back > (cp->ctop - cp->cbase) >> obj_back_shift) {
1254 lprintf2("Invalid back pointer %u at 0x%lx!\n",
1255 back, (ulong) obj);
1256 gs_abort(NULL);
1257 }
1258 } else {
1259 /* Pointed to unknown chunk. Can't check it, sorry. */
1260 }
1261 #endif
1262 {
1263 const obj_header_t *pfree = (const obj_header_t *)
1264 ((const char *)(optr - 1) -
1265 (back << obj_back_shift));
1266 const chunk_head_t *chead = (const chunk_head_t *)
1267 ((const char *)pfree -
1268 (pfree->o_back << obj_back_shift));
1269
1270 robj = chead->dest +
1271 ((const char *)obj - (const char *)(chead + 1) -
1272 pfree->o_nreloc);
1273 }
1274 }
1275 }
1276 /* Use a severely deprecated pun to remove the const property. */
1277 {
1278 union { const void *r; void *w; } u;
1279
1280 u.r = print_reloc(obj, struct_type_name_string(optr[-1].o_type), robj);
1281 return u.w;
1282 }
1283 }
1284
1285 /* ------ Compaction phase ------ */
1286
1287 /* Compact the objects in a chunk. */
1288 /* This will never be called for a chunk with any o_untraced objects. */
1289 private void
gc_objects_compact(chunk_t * cp,gc_state_t * gcst)1290 gc_objects_compact(chunk_t * cp, gc_state_t * gcst)
1291 {
1292 chunk_head_t *chead = cp->chead;
1293 obj_header_t *dpre = (obj_header_t *) chead->dest;
1294 const gs_memory_t *cmem = gcst->spaces.memories.named.system->stable_memory;
1295
1296 SCAN_CHUNK_OBJECTS(cp)
1297 DO_ALL
1298 /* An object is free iff its back pointer points to */
1299 /* the chunk_head structure. */
1300 if (pre->o_back << obj_back_shift != (byte *) pre - (byte *) chead) {
1301 const struct_shared_procs_t *procs = pre->o_type->shared;
1302
1303 debug_check_object(pre, cp, gcst);
1304 if_debug4('7',
1305 " [7]compacting %s 0x%lx(%lu) to 0x%lx\n",
1306 struct_type_name_string(pre->o_type),
1307 (ulong) pre, (ulong) size, (ulong) dpre);
1308 if (procs == 0) {
1309 if (dpre != pre)
1310 memmove(dpre, pre,
1311 sizeof(obj_header_t) + size);
1312 } else
1313 (*procs->compact) (cmem, pre, dpre, size);
1314 dpre = (obj_header_t *)
1315 ((byte *) dpre + obj_size_round(size));
1316 }
1317 END_OBJECTS_SCAN
1318 if (cp->outer == 0 && chead->dest != cp->cbase)
1319 dpre = (obj_header_t *) cp->cbase; /* compacted this chunk into another */
1320 gs_alloc_fill(dpre, gs_alloc_fill_collected, cp->cbot - (byte *) dpre);
1321 cp->cbot = (byte *) dpre;
1322 cp->rcur = 0;
1323 cp->rtop = 0; /* just to be sure */
1324 }
1325
1326 /* ------ Cleanup ------ */
1327
1328 /* Free empty chunks. */
1329 private void
gc_free_empty_chunks(gs_ref_memory_t * mem)1330 gc_free_empty_chunks(gs_ref_memory_t * mem)
1331 {
1332 chunk_t *cp;
1333 chunk_t *csucc;
1334
1335 /* Free the chunks in reverse order, */
1336 /* to encourage LIFO behavior. */
1337 for (cp = mem->clast; cp != 0; cp = csucc) { /* Make sure this isn't an inner chunk, */
1338 /* or a chunk that has inner chunks. */
1339 csucc = cp->cprev; /* save before freeing */
1340 if (cp->cbot == cp->cbase && cp->ctop == cp->climit &&
1341 cp->outer == 0 && cp->inner_count == 0
1342 ) {
1343 alloc_free_chunk(cp, mem);
1344 if (mem->pcc == cp)
1345 mem->pcc = 0;
1346 }
1347 }
1348 }
1349
1350
gcst_get_memory_ptr(gc_state_t * gcst)1351 const gs_memory_t * gcst_get_memory_ptr(gc_state_t *gcst)
1352 {
1353 vm_spaces spaces = gcst->spaces;
1354 const gs_memory_t *cmem = space_system->stable_memory;
1355 return cmem;
1356 }
1357