Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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 *
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
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
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
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
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
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
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
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
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 = &comp;
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
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 = &comp;
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
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
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
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
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
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
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
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
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
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 *
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 */ *
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
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
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
 
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
}