2 |
- |
1 |
/* Copyright (C) 1994, 1995, 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: igcref.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
|
|
|
18 |
/* ref garbage collector for Ghostscript */
|
|
|
19 |
#include "memory_.h"
|
|
|
20 |
#include "ghost.h"
|
|
|
21 |
#include "gsexit.h"
|
|
|
22 |
#include "gsstruct.h" /* for gxalloc.h included by iastate.h */
|
|
|
23 |
#include "iname.h"
|
|
|
24 |
#include "iastate.h"
|
|
|
25 |
#include "idebug.h"
|
|
|
26 |
#include "igc.h"
|
|
|
27 |
#include "ipacked.h"
|
|
|
28 |
#include "store.h" /* for ref_assign_inline */
|
|
|
29 |
|
|
|
30 |
/* Define whether to trace every step of relocating ref pointers. */
|
|
|
31 |
#if 0
|
|
|
32 |
# define rputc(c) dputc(c)
|
|
|
33 |
#else
|
|
|
34 |
# define rputc(c) DO_NOTHING
|
|
|
35 |
#endif
|
|
|
36 |
|
|
|
37 |
/* Forward references */
|
|
|
38 |
ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
|
|
|
39 |
refs_proc_reloc(igc_reloc_refs);
|
|
|
40 |
|
|
|
41 |
/*
|
|
|
42 |
* Define the 'structure' type descriptor for refs.
|
|
|
43 |
* This is special because it has different shared procs.
|
|
|
44 |
*/
|
|
|
45 |
private gc_proc_clear_reloc(refs_clear_reloc);
|
|
|
46 |
private gc_proc_set_reloc(refs_set_reloc);
|
|
|
47 |
private gc_proc_compact(refs_compact);
|
|
|
48 |
private const struct_shared_procs_t refs_shared_procs =
|
|
|
49 |
{refs_clear_reloc, refs_set_reloc, refs_compact};
|
|
|
50 |
private struct_proc_clear_marks(refs_clear_marks);
|
|
|
51 |
private struct_proc_reloc_ptrs(refs_do_reloc);
|
|
|
52 |
const gs_memory_struct_type_t st_refs =
|
|
|
53 |
{sizeof(ref), "refs", &refs_shared_procs, refs_clear_marks, 0, refs_do_reloc};
|
|
|
54 |
|
|
|
55 |
/*
|
|
|
56 |
* Define the GC procedures for structs that actually contain refs.
|
|
|
57 |
* These are special because the shared refs_* procedures
|
|
|
58 |
* are never called. Instead, we unmark the individual refs in clear_marks,
|
|
|
59 |
* disregard refs_*_reloc (because we will never relocate a ptr_ref_type
|
|
|
60 |
* pointer pointing into the structure), disregard refs_compact (because
|
|
|
61 |
* compaction is never required), and remove the marks in reloc_ptrs.
|
|
|
62 |
* See also the comment about ptr_ref_type in imemory.h.
|
|
|
63 |
*/
|
|
|
64 |
CLEAR_MARKS_PROC(ref_struct_clear_marks)
|
|
|
65 |
{
|
|
|
66 |
ref *pref = (ref *) vptr;
|
|
|
67 |
ref *end = (ref *) ((char *)vptr + size);
|
|
|
68 |
|
|
|
69 |
for (; pref < end; pref++)
|
|
|
70 |
r_clear_attrs(pref, l_mark);
|
|
|
71 |
}
|
|
|
72 |
ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
|
|
|
73 |
{
|
|
|
74 |
if (index >= size / sizeof(ref))
|
|
|
75 |
return 0;
|
|
|
76 |
pep->ptr = (const ref *)vptr + index;
|
|
|
77 |
return ptr_ref_type;
|
|
|
78 |
ENUM_PTRS_END_PROC
|
|
|
79 |
}
|
|
|
80 |
RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
|
|
|
81 |
{
|
|
|
82 |
vm_spaces spaces = gcst->spaces;
|
|
|
83 |
const gs_memory_t *cmem = space_system->stable_memory;
|
|
|
84 |
|
|
|
85 |
ref *beg = vptr;
|
|
|
86 |
ref *end = (ref *) ((char *)vptr + size);
|
|
|
87 |
|
|
|
88 |
igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
|
|
|
89 |
ref_struct_clear_marks(cmem, vptr, size, pstype);
|
|
|
90 |
} RELOC_PTRS_END
|
|
|
91 |
|
|
|
92 |
/* ------ Unmarking phase ------ */
|
|
|
93 |
|
|
|
94 |
/* Unmark a single ref. */
|
|
|
95 |
void
|
|
|
96 |
ptr_ref_unmark(enum_ptr_t *pep, gc_state_t * ignored)
|
|
|
97 |
{
|
|
|
98 |
ref_packed *rpp = (ref_packed *)pep->ptr;
|
|
|
99 |
|
|
|
100 |
if (r_is_packed(rpp))
|
|
|
101 |
r_clear_pmark(rpp);
|
|
|
102 |
else
|
|
|
103 |
r_clear_attrs((ref *)rpp, l_mark);
|
|
|
104 |
}
|
|
|
105 |
|
|
|
106 |
/* Unmarking routine for ref objects. */
|
|
|
107 |
private void
|
|
|
108 |
refs_clear_marks(const gs_memory_t *cmem,
|
|
|
109 |
void /*obj_header_t */ *vptr, uint size,
|
|
|
110 |
const gs_memory_struct_type_t * pstype)
|
|
|
111 |
{
|
|
|
112 |
ref_packed *rp = (ref_packed *) vptr;
|
|
|
113 |
ref_packed *end = (ref_packed *) ((byte *) vptr + size);
|
|
|
114 |
|
|
|
115 |
/* Since the last ref is full-size, we only need to check for */
|
|
|
116 |
/* the end of the block when we see one of those. */
|
|
|
117 |
for (;;) {
|
|
|
118 |
if (r_is_packed(rp)) {
|
|
|
119 |
#ifdef DEBUG
|
|
|
120 |
if (gs_debug_c('8')) {
|
|
|
121 |
dlprintf1(" [8]unmark packed 0x%lx ", (ulong) rp);
|
|
|
122 |
debug_print_ref(cmem, (const ref *)rp);
|
|
|
123 |
dputs("\n");
|
|
|
124 |
}
|
|
|
125 |
#endif
|
|
|
126 |
r_clear_pmark(rp);
|
|
|
127 |
rp++;
|
|
|
128 |
} else { /* full-size ref */
|
|
|
129 |
ref *const pref = (ref *)rp;
|
|
|
130 |
|
|
|
131 |
#ifdef DEBUG
|
|
|
132 |
if (gs_debug_c('8')) {
|
|
|
133 |
dlprintf1(" [8]unmark ref 0x%lx ", (ulong) rp);
|
|
|
134 |
debug_print_ref(cmem, pref);
|
|
|
135 |
dputs("\n");
|
|
|
136 |
}
|
|
|
137 |
#endif
|
|
|
138 |
r_clear_attrs(pref, l_mark);
|
|
|
139 |
rp += packed_per_ref;
|
|
|
140 |
if (rp >= (ref_packed *) end)
|
|
|
141 |
break;
|
|
|
142 |
}
|
|
|
143 |
}
|
|
|
144 |
}
|
|
|
145 |
|
|
|
146 |
/* ------ Marking phase ------ */
|
|
|
147 |
|
|
|
148 |
/* Mark a ref. Return true if new mark. */
|
|
|
149 |
bool
|
|
|
150 |
ptr_ref_mark(enum_ptr_t *pep, gc_state_t * ignored)
|
|
|
151 |
{
|
|
|
152 |
ref_packed *rpp = (void *)pep->ptr;
|
|
|
153 |
|
|
|
154 |
if (r_is_packed(rpp)) {
|
|
|
155 |
if (r_has_pmark(rpp))
|
|
|
156 |
return false;
|
|
|
157 |
r_set_pmark(rpp);
|
|
|
158 |
} else {
|
|
|
159 |
ref *const pref = (ref *)rpp;
|
|
|
160 |
|
|
|
161 |
if (r_has_attr(pref, l_mark))
|
|
|
162 |
return false;
|
|
|
163 |
r_set_attrs(pref, l_mark);
|
|
|
164 |
}
|
|
|
165 |
return true;
|
|
|
166 |
}
|
|
|
167 |
|
|
|
168 |
/* ------ Relocation planning phase ------ */
|
|
|
169 |
|
|
|
170 |
/*
|
|
|
171 |
* We store relocation in the size field of refs that don't use it,
|
|
|
172 |
* so that we don't have to scan all the way to an unmarked object.
|
|
|
173 |
* We must avoid nulls, which sometimes have useful information
|
|
|
174 |
* in their size fields, and the types above t_next_index, which are
|
|
|
175 |
* actually operators in disguise and also use the size field.
|
|
|
176 |
*/
|
|
|
177 |
|
|
|
178 |
/* Clear the relocation for a ref object. */
|
|
|
179 |
private void
|
|
|
180 |
refs_clear_reloc(obj_header_t *hdr, uint size)
|
|
|
181 |
{
|
|
|
182 |
ref_packed *rp = (ref_packed *) (hdr + 1);
|
|
|
183 |
ref_packed *end = (ref_packed *) ((byte *) rp + size);
|
|
|
184 |
|
|
|
185 |
while (rp < end) {
|
|
|
186 |
if (r_is_packed(rp))
|
|
|
187 |
rp++;
|
|
|
188 |
else {
|
|
|
189 |
/* Full-size ref. Store the relocation here if possible. */
|
|
|
190 |
ref *const pref = (ref *)rp;
|
|
|
191 |
|
|
|
192 |
if (!ref_type_uses_size_or_null(r_type(pref))) {
|
|
|
193 |
if_debug1('8', " [8]clearing reloc at 0x%lx\n", (ulong) rp);
|
|
|
194 |
r_set_size(pref, 0);
|
|
|
195 |
}
|
|
|
196 |
rp += packed_per_ref;
|
|
|
197 |
}
|
|
|
198 |
}
|
|
|
199 |
}
|
|
|
200 |
|
|
|
201 |
/* Set the relocation for a ref object. */
|
|
|
202 |
private bool
|
|
|
203 |
refs_set_reloc(obj_header_t * hdr, uint reloc, uint size)
|
|
|
204 |
{
|
|
|
205 |
ref_packed *rp = (ref_packed *) (hdr + 1);
|
|
|
206 |
ref_packed *end = (ref_packed *) ((byte *) rp + size);
|
|
|
207 |
uint freed = 0;
|
|
|
208 |
|
|
|
209 |
/*
|
|
|
210 |
* We have to be careful to keep refs aligned properly.
|
|
|
211 |
* For the moment, we do this by either keeping or discarding
|
|
|
212 |
* an entire (aligned) block of align_packed_per_ref packed elements
|
|
|
213 |
* as a unit. We know that align_packed_per_ref <= packed_per_ref,
|
|
|
214 |
* and we also know that packed refs are always allocated in blocks
|
|
|
215 |
* of align_packed_per_ref, so this makes things relatively easy.
|
|
|
216 |
*/
|
|
|
217 |
while (rp < end) {
|
|
|
218 |
if (r_is_packed(rp)) {
|
|
|
219 |
#if align_packed_per_ref == 1
|
|
|
220 |
if (r_has_pmark(rp)) {
|
|
|
221 |
if_debug1('8',
|
|
|
222 |
" [8]packed ref 0x%lx is marked\n",
|
|
|
223 |
(ulong) rp);
|
|
|
224 |
rp++;
|
|
|
225 |
} else {
|
|
|
226 |
#else
|
|
|
227 |
int i;
|
|
|
228 |
|
|
|
229 |
/*
|
|
|
230 |
* Note: align_packed_per_ref is typically
|
|
|
231 |
* 2 or 4 for 32-bit processors.
|
|
|
232 |
*/
|
|
|
233 |
#define all_marked (align_packed_per_ref * lp_mark)
|
|
|
234 |
# if align_packed_per_ref == 2
|
|
|
235 |
# if arch_sizeof_int == arch_sizeof_short * 2
|
|
|
236 |
# undef all_marked
|
|
|
237 |
# define all_marked ( (lp_mark << (sizeof(short) * 8)) + lp_mark )
|
|
|
238 |
# define marked (*(int *)rp & all_marked)
|
|
|
239 |
# else
|
|
|
240 |
# define marked ((*rp & lp_mark) + (rp[1] & lp_mark))
|
|
|
241 |
# endif
|
|
|
242 |
# else
|
|
|
243 |
# if align_packed_per_ref == 4
|
|
|
244 |
# define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
|
|
|
245 |
(rp[2] & lp_mark) + (rp[3] & lp_mark))
|
|
|
246 |
# else
|
|
|
247 |
/*
|
|
|
248 |
* The value of marked is logically a uint, not an int:
|
|
|
249 |
* we declare it as int only to avoid a compiler warning
|
|
|
250 |
* message about using a non-int value in a switch statement.
|
|
|
251 |
*/
|
|
|
252 |
int marked = *rp & lp_mark;
|
|
|
253 |
|
|
|
254 |
for (i = 1; i < align_packed_per_ref; i++)
|
|
|
255 |
marked += rp[i] & lp_mark;
|
|
|
256 |
# endif
|
|
|
257 |
# endif
|
|
|
258 |
/*
|
|
|
259 |
* Now marked is lp_mark * the number of marked
|
|
|
260 |
* packed refs in the aligned block, except for
|
|
|
261 |
* a couple of special cases above.
|
|
|
262 |
*/
|
|
|
263 |
switch (marked) {
|
|
|
264 |
case all_marked:
|
|
|
265 |
if_debug2('8',
|
|
|
266 |
" [8]packed refs 0x%lx..0x%lx are marked\n",
|
|
|
267 |
(ulong) rp,
|
|
|
268 |
(ulong) (rp + (align_packed_per_ref - 1)));
|
|
|
269 |
rp += align_packed_per_ref;
|
|
|
270 |
break;
|
|
|
271 |
default:
|
|
|
272 |
/* At least one packed ref in the block */
|
|
|
273 |
/* is marked: Keep the whole block. */
|
|
|
274 |
for (i = align_packed_per_ref; i--; rp++) {
|
|
|
275 |
r_set_pmark(rp);
|
|
|
276 |
if_debug1('8',
|
|
|
277 |
" [8]packed ref 0x%lx is marked\n",
|
|
|
278 |
(ulong) rp);
|
|
|
279 |
}
|
|
|
280 |
break;
|
|
|
281 |
case 0:
|
|
|
282 |
#endif
|
|
|
283 |
if_debug2('8', " [8]%d packed ref(s) at 0x%lx are unmarked\n",
|
|
|
284 |
align_packed_per_ref, (ulong) rp);
|
|
|
285 |
{
|
|
|
286 |
uint rel = reloc + freed;
|
|
|
287 |
|
|
|
288 |
/* Change this to an integer so we can */
|
|
|
289 |
/* store the relocation here. */
|
|
|
290 |
*rp = pt_tag(pt_integer) +
|
|
|
291 |
min(rel, packed_max_value);
|
|
|
292 |
}
|
|
|
293 |
rp += align_packed_per_ref;
|
|
|
294 |
freed += sizeof(ref_packed) * align_packed_per_ref;
|
|
|
295 |
}
|
|
|
296 |
} else { /* full-size ref */
|
|
|
297 |
uint rel = reloc + freed;
|
|
|
298 |
|
|
|
299 |
/* The following assignment is logically */
|
|
|
300 |
/* unnecessary; we do it only for convenience */
|
|
|
301 |
/* in debugging. */
|
|
|
302 |
ref *pref = (ref *) rp;
|
|
|
303 |
|
|
|
304 |
if (!r_has_attr(pref, l_mark)) {
|
|
|
305 |
if_debug1('8', " [8]ref 0x%lx is unmarked\n",
|
|
|
306 |
(ulong) pref);
|
|
|
307 |
/* Change this to a mark so we can */
|
|
|
308 |
/* store the relocation. */
|
|
|
309 |
r_set_type(pref, t_mark);
|
|
|
310 |
r_set_size(pref, rel);
|
|
|
311 |
freed += sizeof(ref);
|
|
|
312 |
} else {
|
|
|
313 |
if_debug1('8', " [8]ref 0x%lx is marked\n",
|
|
|
314 |
(ulong) pref);
|
|
|
315 |
/* Store the relocation here if possible. */
|
|
|
316 |
if (!ref_type_uses_size_or_null(r_type(pref))) {
|
|
|
317 |
if_debug2('8', " [8]storing reloc %u at 0x%lx\n",
|
|
|
318 |
rel, (ulong) pref);
|
|
|
319 |
r_set_size(pref, rel);
|
|
|
320 |
}
|
|
|
321 |
}
|
|
|
322 |
rp += packed_per_ref;
|
|
|
323 |
}
|
|
|
324 |
}
|
|
|
325 |
if_debug3('7', " [7]at end of refs 0x%lx, size = %u, freed = %u\n",
|
|
|
326 |
(ulong) (hdr + 1), size, freed);
|
|
|
327 |
if (freed == size)
|
|
|
328 |
return false;
|
|
|
329 |
#if arch_sizeof_int > arch_sizeof_short
|
|
|
330 |
/*
|
|
|
331 |
* If the final relocation can't fit in the r_size field
|
|
|
332 |
* (which can't happen if the object shares a chunk with
|
|
|
333 |
* any other objects, so we know reloc = 0 in this case),
|
|
|
334 |
* we have to keep the entire object unless there are no
|
|
|
335 |
* references to any ref in it.
|
|
|
336 |
*/
|
|
|
337 |
if (freed <= max_ushort)
|
|
|
338 |
return true;
|
|
|
339 |
/*
|
|
|
340 |
* We have to mark all surviving refs, but we also must
|
|
|
341 |
* overwrite any non-surviving refs with something that
|
|
|
342 |
* doesn't contain any pointers.
|
|
|
343 |
*/
|
|
|
344 |
rp = (ref_packed *) (hdr + 1);
|
|
|
345 |
while (rp < end) {
|
|
|
346 |
if (r_is_packed(rp)) {
|
|
|
347 |
if (!r_has_pmark(rp))
|
|
|
348 |
*rp = pt_tag(pt_integer) | lp_mark;
|
|
|
349 |
++rp;
|
|
|
350 |
} else { /* The following assignment is logically */
|
|
|
351 |
/* unnecessary; we do it only for convenience */
|
|
|
352 |
/* in debugging. */
|
|
|
353 |
ref *pref = (ref *) rp;
|
|
|
354 |
|
|
|
355 |
if (!r_has_attr(pref, l_mark)) {
|
|
|
356 |
r_set_type_attrs(pref, t_mark, l_mark);
|
|
|
357 |
r_set_size(pref, reloc);
|
|
|
358 |
} else {
|
|
|
359 |
if (!ref_type_uses_size_or_null(r_type(pref)))
|
|
|
360 |
r_set_size(pref, reloc);
|
|
|
361 |
}
|
|
|
362 |
rp += packed_per_ref;
|
|
|
363 |
}
|
|
|
364 |
}
|
|
|
365 |
/* The last ref has to remain unmarked. */
|
|
|
366 |
r_clear_attrs((ref *) rp - 1, l_mark);
|
|
|
367 |
#endif
|
|
|
368 |
return true;
|
|
|
369 |
}
|
|
|
370 |
|
|
|
371 |
/* ------ Relocation phase ------ */
|
|
|
372 |
|
|
|
373 |
/* Relocate all the pointers in a block of refs. */
|
|
|
374 |
private void
|
|
|
375 |
refs_do_reloc(void /*obj_header_t */ *vptr, uint size,
|
|
|
376 |
const gs_memory_struct_type_t * pstype, gc_state_t * gcst)
|
|
|
377 |
{
|
|
|
378 |
igc_reloc_refs((ref_packed *) vptr,
|
|
|
379 |
(ref_packed *) ((char *)vptr + size),
|
|
|
380 |
gcst);
|
|
|
381 |
}
|
|
|
382 |
/* Relocate the contents of a block of refs. */
|
|
|
383 |
/* If gcst->relocating_untraced is true, we are relocating pointers from an */
|
|
|
384 |
/* untraced space, so relocate all refs, not just marked ones. */
|
|
|
385 |
void
|
|
|
386 |
igc_reloc_refs(ref_packed * from, ref_packed * to, gc_state_t * gcst)
|
|
|
387 |
{
|
|
|
388 |
int min_trace = gcst->min_collect;
|
|
|
389 |
ref_packed *rp = from;
|
|
|
390 |
bool do_all = gcst->relocating_untraced;
|
|
|
391 |
|
|
|
392 |
vm_spaces spaces = gcst->spaces;
|
|
|
393 |
const gs_memory_t *cmem = space_system->stable_memory;
|
|
|
394 |
|
|
|
395 |
while (rp < to) {
|
|
|
396 |
ref *pref;
|
|
|
397 |
#ifdef DEBUG
|
|
|
398 |
const void *before = 0;
|
|
|
399 |
const void *after = 0;
|
|
|
400 |
# define DO_RELOC(var, stat)\
|
|
|
401 |
BEGIN before = (var); stat; after = (var); END
|
|
|
402 |
# define SET_RELOC(var, expr)\
|
|
|
403 |
BEGIN before = (var); after = (var) = (expr); END
|
|
|
404 |
#else
|
|
|
405 |
# define DO_RELOC(var, stat) stat
|
|
|
406 |
# define SET_RELOC(var, expr) var = expr
|
|
|
407 |
#endif
|
|
|
408 |
|
|
|
409 |
if (r_is_packed(rp)) {
|
|
|
410 |
rp++;
|
|
|
411 |
continue;
|
|
|
412 |
}
|
|
|
413 |
/* The following assignment is logically unnecessary; */
|
|
|
414 |
/* we do it only for convenience in debugging. */
|
|
|
415 |
pref = (ref *) rp;
|
|
|
416 |
if_debug3('8', " [8]relocating %s %d ref at 0x%lx",
|
|
|
417 |
(r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
|
|
|
418 |
r_btype(pref), (ulong) pref);
|
|
|
419 |
if ((r_has_attr(pref, l_mark) || do_all) &&
|
|
|
420 |
r_space(pref) >= min_trace
|
|
|
421 |
) {
|
|
|
422 |
switch (r_type(pref)) {
|
|
|
423 |
/* Struct cases */
|
|
|
424 |
case t_file:
|
|
|
425 |
DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
|
|
|
426 |
break;
|
|
|
427 |
case t_device:
|
|
|
428 |
DO_RELOC(pref->value.pdevice,
|
|
|
429 |
RELOC_VAR(pref->value.pdevice));
|
|
|
430 |
break;
|
|
|
431 |
case t_fontID:
|
|
|
432 |
case t_struct:
|
|
|
433 |
case t_astruct:
|
|
|
434 |
DO_RELOC(pref->value.pstruct,
|
|
|
435 |
RELOC_VAR(pref->value.pstruct));
|
|
|
436 |
break;
|
|
|
437 |
/* Non-trivial non-struct cases */
|
|
|
438 |
case t_dictionary:
|
|
|
439 |
rputc('d');
|
|
|
440 |
SET_RELOC(pref->value.pdict,
|
|
|
441 |
(dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
|
|
|
442 |
break;
|
|
|
443 |
case t_array:
|
|
|
444 |
{
|
|
|
445 |
uint size = r_size(pref);
|
|
|
446 |
|
|
|
447 |
if (size != 0) { /* value.refs might be NULL */
|
|
|
448 |
|
|
|
449 |
/*
|
|
|
450 |
* If the array is large, we allocated it in its
|
|
|
451 |
* own object (at least originally -- this might
|
|
|
452 |
* be a pointer to a subarray.) In this case,
|
|
|
453 |
* we know it is the only object in its
|
|
|
454 |
* containing st_refs object, so we know that
|
|
|
455 |
* the mark containing the relocation appears
|
|
|
456 |
* just after it.
|
|
|
457 |
*/
|
|
|
458 |
if (size < max_size_st_refs / sizeof(ref)) {
|
|
|
459 |
rputc('a');
|
|
|
460 |
SET_RELOC(pref->value.refs,
|
|
|
461 |
(ref *) igc_reloc_ref_ptr(
|
|
|
462 |
(ref_packed *) pref->value.refs, gcst));
|
|
|
463 |
} else {
|
|
|
464 |
rputc('A');
|
|
|
465 |
/*
|
|
|
466 |
* See the t_shortarray case below for why we
|
|
|
467 |
* decrement size.
|
|
|
468 |
*/
|
|
|
469 |
--size;
|
|
|
470 |
SET_RELOC(pref->value.refs,
|
|
|
471 |
(ref *) igc_reloc_ref_ptr(
|
|
|
472 |
(ref_packed *) (pref->value.refs + size),
|
|
|
473 |
gcst) - size);
|
|
|
474 |
}
|
|
|
475 |
}
|
|
|
476 |
}
|
|
|
477 |
break;
|
|
|
478 |
case t_mixedarray:
|
|
|
479 |
if (r_size(pref) != 0) { /* value.refs might be NULL */
|
|
|
480 |
rputc('m');
|
|
|
481 |
SET_RELOC(pref->value.packed,
|
|
|
482 |
igc_reloc_ref_ptr(pref->value.packed, gcst));
|
|
|
483 |
}
|
|
|
484 |
break;
|
|
|
485 |
case t_shortarray:
|
|
|
486 |
{
|
|
|
487 |
uint size = r_size(pref);
|
|
|
488 |
|
|
|
489 |
/*
|
|
|
490 |
* Since we know that igc_reloc_ref_ptr works by
|
|
|
491 |
* scanning forward, and we know that all the
|
|
|
492 |
* elements of this array itself are marked, we can
|
|
|
493 |
* save some scanning time by relocating the pointer
|
|
|
494 |
* to the end of the array rather than the
|
|
|
495 |
* beginning.
|
|
|
496 |
*/
|
|
|
497 |
if (size != 0) { /* value.refs might be NULL */
|
|
|
498 |
rputc('s');
|
|
|
499 |
/*
|
|
|
500 |
* igc_reloc_ref_ptr has to be able to determine
|
|
|
501 |
* whether the pointer points into a space that
|
|
|
502 |
* isn't being collected. It does this by
|
|
|
503 |
* checking whether the referent of the pointer
|
|
|
504 |
* is marked. For this reason, we have to pass
|
|
|
505 |
* a pointer to the last real element of the
|
|
|
506 |
* array, rather than just beyond it.
|
|
|
507 |
*/
|
|
|
508 |
--size;
|
|
|
509 |
SET_RELOC(pref->value.packed,
|
|
|
510 |
igc_reloc_ref_ptr(pref->value.packed + size,
|
|
|
511 |
gcst) - size);
|
|
|
512 |
}
|
|
|
513 |
}
|
|
|
514 |
break;
|
|
|
515 |
case t_name:
|
|
|
516 |
{
|
|
|
517 |
void *psub = name_ref_sub_table(cmem, pref);
|
|
|
518 |
void *rsub = RELOC_OBJ(psub); /* gcst implicit */
|
|
|
519 |
|
|
|
520 |
SET_RELOC(pref->value.pname,
|
|
|
521 |
(name *)
|
|
|
522 |
((char *)rsub + ((char *)pref->value.pname -
|
|
|
523 |
(char *)psub)));
|
|
|
524 |
} break;
|
|
|
525 |
case t_string:
|
|
|
526 |
{
|
|
|
527 |
gs_string str;
|
|
|
528 |
|
|
|
529 |
str.data = pref->value.bytes;
|
|
|
530 |
str.size = r_size(pref);
|
|
|
531 |
|
|
|
532 |
DO_RELOC(str.data, RELOC_STRING_VAR(str));
|
|
|
533 |
pref->value.bytes = str.data;
|
|
|
534 |
}
|
|
|
535 |
break;
|
|
|
536 |
case t_oparray:
|
|
|
537 |
rputc('o');
|
|
|
538 |
SET_RELOC(pref->value.const_refs,
|
|
|
539 |
(const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
|
|
|
540 |
break;
|
|
|
541 |
default:
|
|
|
542 |
goto no_reloc; /* don't print trace message */
|
|
|
543 |
}
|
|
|
544 |
if_debug2('8', ", 0x%lx => 0x%lx", (ulong)before, (ulong)after);
|
|
|
545 |
}
|
|
|
546 |
no_reloc:
|
|
|
547 |
if_debug0('8', "\n");
|
|
|
548 |
rp += packed_per_ref;
|
|
|
549 |
}
|
|
|
550 |
}
|
|
|
551 |
|
|
|
552 |
/* Relocate a pointer to a ref. */
|
|
|
553 |
/* See gsmemory.h for why the argument is const and the result is not. */
|
|
|
554 |
ref_packed *
|
|
|
555 |
igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
|
|
|
556 |
{
|
|
|
557 |
/*
|
|
|
558 |
* Search forward for relocation. This algorithm is intrinsically very
|
|
|
559 |
* inefficient; we hope eventually to replace it with a better one.
|
|
|
560 |
*/
|
|
|
561 |
const ref_packed *rp = prp;
|
|
|
562 |
uint dec = 0;
|
|
|
563 |
#ifdef ALIGNMENT_ALIASING_BUG
|
|
|
564 |
const ref *rpref;
|
|
|
565 |
# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
|
|
|
566 |
#else
|
|
|
567 |
# define RP_REF(rp) ((const ref *)rp)
|
|
|
568 |
#endif
|
|
|
569 |
/*
|
|
|
570 |
* Iff this pointer points into a space that wasn't traced,
|
|
|
571 |
* the referent won't be marked. In this case, we shouldn't
|
|
|
572 |
* do any relocation. Check for this first.
|
|
|
573 |
*/
|
|
|
574 |
if (r_is_packed(rp)) {
|
|
|
575 |
if (!r_has_pmark(rp))
|
|
|
576 |
goto ret_rp;
|
|
|
577 |
} else {
|
|
|
578 |
if (!r_has_attr(RP_REF(rp), l_mark))
|
|
|
579 |
goto ret_rp;
|
|
|
580 |
}
|
|
|
581 |
for (;;) {
|
|
|
582 |
|
|
|
583 |
if (r_is_packed(rp)) {
|
|
|
584 |
/*
|
|
|
585 |
* Normally, an unmarked packed ref will be an
|
|
|
586 |
* integer whose value is the amount of relocation.
|
|
|
587 |
* However, the relocation value might have been
|
|
|
588 |
* too large to fit. If this is the case, for
|
|
|
589 |
* each such unmarked packed ref we pass over,
|
|
|
590 |
* we have to decrement the final relocation.
|
|
|
591 |
*/
|
|
|
592 |
rputc((*rp & lp_mark ? '1' : '0'));
|
|
|
593 |
if (!(*rp & lp_mark)) {
|
|
|
594 |
if (*rp != pt_tag(pt_integer) + packed_max_value) {
|
|
|
595 |
/* This is a stored relocation value. */
|
|
|
596 |
rputc('\n');
|
|
|
597 |
rp = print_reloc(prp, "ref",
|
|
|
598 |
(const ref_packed *)
|
|
|
599 |
((const char *)prp -
|
|
|
600 |
(*rp & packed_value_mask) + dec));
|
|
|
601 |
break;
|
|
|
602 |
}
|
|
|
603 |
/*
|
|
|
604 |
* We know this is the first of an aligned block
|
|
|
605 |
* of packed refs. Skip over the entire block,
|
|
|
606 |
* decrementing the final relocation.
|
|
|
607 |
*/
|
|
|
608 |
dec += sizeof(ref_packed) * align_packed_per_ref;
|
|
|
609 |
rp += align_packed_per_ref;
|
|
|
610 |
} else
|
|
|
611 |
rp++;
|
|
|
612 |
continue;
|
|
|
613 |
}
|
|
|
614 |
if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
|
|
|
615 |
/* reloc is in r_size */
|
|
|
616 |
rputc('\n');
|
|
|
617 |
rp = print_reloc(prp, "ref",
|
|
|
618 |
(const ref_packed *)
|
|
|
619 |
(r_size(RP_REF(rp)) == 0 ? prp :
|
|
|
620 |
(const ref_packed *)((const char *)prp -
|
|
|
621 |
r_size(RP_REF(rp)) + dec)));
|
|
|
622 |
break;
|
|
|
623 |
}
|
|
|
624 |
rputc('u');
|
|
|
625 |
rp += packed_per_ref;
|
|
|
626 |
}
|
|
|
627 |
ret_rp:
|
|
|
628 |
/* Use a severely deprecated pun to remove the const property. */
|
|
|
629 |
{
|
|
|
630 |
union { const ref_packed *r; ref_packed *w; } u;
|
|
|
631 |
|
|
|
632 |
u.r = rp;
|
|
|
633 |
return u.w;
|
|
|
634 |
}
|
|
|
635 |
}
|
|
|
636 |
|
|
|
637 |
/* ------ Compaction phase ------ */
|
|
|
638 |
|
|
|
639 |
/* Compact a ref object. */
|
|
|
640 |
/* Remove the marks at the same time. */
|
|
|
641 |
private void
|
|
|
642 |
refs_compact(const gs_memory_t *mem, obj_header_t * pre, obj_header_t * dpre, uint size)
|
|
|
643 |
{
|
|
|
644 |
ref_packed *dest;
|
|
|
645 |
ref_packed *src;
|
|
|
646 |
ref_packed *end;
|
|
|
647 |
uint new_size;
|
|
|
648 |
|
|
|
649 |
src = (ref_packed *) (pre + 1);
|
|
|
650 |
end = (ref_packed *) ((byte *) src + size);
|
|
|
651 |
/*
|
|
|
652 |
* We know that a block of refs always ends with an unmarked
|
|
|
653 |
* full-size ref, so we only need to check for reaching the end
|
|
|
654 |
* of the block when we see one of those.
|
|
|
655 |
*/
|
|
|
656 |
if (dpre == pre) /* Loop while we don't need to copy. */
|
|
|
657 |
for (;;) {
|
|
|
658 |
if (r_is_packed(src)) {
|
|
|
659 |
if (!r_has_pmark(src))
|
|
|
660 |
break;
|
|
|
661 |
if_debug1('8', " [8]packed ref 0x%lx \"copied\"\n",
|
|
|
662 |
(ulong) src);
|
|
|
663 |
*src &= ~lp_mark;
|
|
|
664 |
src++;
|
|
|
665 |
} else { /* full-size ref */
|
|
|
666 |
ref *const pref = (ref *)src;
|
|
|
667 |
|
|
|
668 |
if (!r_has_attr(pref, l_mark))
|
|
|
669 |
break;
|
|
|
670 |
if_debug1('8', " [8]ref 0x%lx \"copied\"\n", (ulong) src);
|
|
|
671 |
r_clear_attrs(pref, l_mark);
|
|
|
672 |
src += packed_per_ref;
|
|
|
673 |
}
|
|
|
674 |
} else
|
|
|
675 |
*dpre = *pre;
|
|
|
676 |
dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
|
|
|
677 |
for (;;) {
|
|
|
678 |
if (r_is_packed(src)) {
|
|
|
679 |
if (r_has_pmark(src)) {
|
|
|
680 |
if_debug2('8', " [8]packed ref 0x%lx copied to 0x%lx\n",
|
|
|
681 |
(ulong) src, (ulong) dest);
|
|
|
682 |
*dest++ = *src & ~lp_mark;
|
|
|
683 |
}
|
|
|
684 |
src++;
|
|
|
685 |
} else { /* full-size ref */
|
|
|
686 |
if (r_has_attr((ref *) src, l_mark)) {
|
|
|
687 |
ref rtemp;
|
|
|
688 |
|
|
|
689 |
if_debug2('8', " [8]ref 0x%lx copied to 0x%lx\n",
|
|
|
690 |
(ulong) src, (ulong) dest);
|
|
|
691 |
/* We can't just use ref_assign_inline, */
|
|
|
692 |
/* because the source and destination */
|
|
|
693 |
/* might overlap! */
|
|
|
694 |
ref_assign_inline(&rtemp, (ref *) src);
|
|
|
695 |
r_clear_attrs(&rtemp, l_mark);
|
|
|
696 |
ref_assign_inline((ref *) dest, &rtemp);
|
|
|
697 |
dest += packed_per_ref;
|
|
|
698 |
src += packed_per_ref;
|
|
|
699 |
} else { /* check for end of block */
|
|
|
700 |
src += packed_per_ref;
|
|
|
701 |
if (src >= end)
|
|
|
702 |
break;
|
|
|
703 |
}
|
|
|
704 |
}
|
|
|
705 |
}
|
|
|
706 |
new_size = (byte *) dest - (byte *) (dpre + 1) + sizeof(ref);
|
|
|
707 |
#ifdef DEBUG
|
|
|
708 |
/* Check that the relocation came out OK. */
|
|
|
709 |
/* NOTE: this check only works within a single chunk. */
|
|
|
710 |
if ((byte *) src - (byte *) dest != r_size((ref *) src - 1) + sizeof(ref)) {
|
|
|
711 |
lprintf3("Reloc error for refs 0x%lx: reloc = %lu, stored = %u\n",
|
|
|
712 |
(ulong) dpre, (ulong) ((byte *) src - (byte *) dest),
|
|
|
713 |
(uint) r_size((ref *) src - 1));
|
|
|
714 |
gs_abort(mem);
|
|
|
715 |
}
|
|
|
716 |
#endif
|
|
|
717 |
/* Pad to a multiple of sizeof(ref). */
|
|
|
718 |
while (new_size & (sizeof(ref) - 1))
|
|
|
719 |
*dest++ = pt_tag(pt_integer),
|
|
|
720 |
new_size += sizeof(ref_packed);
|
|
|
721 |
/* We want to make the newly freed space into a free block, */
|
|
|
722 |
/* but we can only do this if we have enough room. */
|
|
|
723 |
if (size - new_size < sizeof(obj_header_t)) { /* Not enough room. Pad to original size. */
|
|
|
724 |
while (new_size < size)
|
|
|
725 |
*dest++ = pt_tag(pt_integer),
|
|
|
726 |
new_size += sizeof(ref_packed);
|
|
|
727 |
} else {
|
|
|
728 |
obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
|
|
|
729 |
|
|
|
730 |
pfree->o_alone = 0;
|
|
|
731 |
pfree->o_size = size - new_size - sizeof(obj_header_t);
|
|
|
732 |
pfree->o_type = &st_bytes;
|
|
|
733 |
}
|
|
|
734 |
/* Re-create the final ref. */
|
|
|
735 |
r_set_type((ref *) dest, t_integer);
|
|
|
736 |
dpre->o_size = new_size;
|
|
|
737 |
}
|