Coverage Report

Created: 2025-06-10 07:06

/src/ghostpdl/psi/igcref.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* ref garbage collector for Ghostscript */
18
#include "memory_.h"
19
#include "ghost.h"
20
#include "gsexit.h"
21
#include "gsstruct.h"   /* for gxalloc.h included by iastate.h */
22
#include "iname.h"
23
#include "iastate.h"
24
#include "idebug.h"
25
#include "igc.h"
26
#include "ipacked.h"
27
#include "store.h"    /* for ref_assign_inline */
28
29
/* Define whether to trace every step of relocating ref pointers. */
30
#if 0
31
#  define rputc(m,c) dmputc(m,c)
32
#else
33
6.36G
#  define rputc(m,c) DO_NOTHING
34
#endif
35
36
/* Forward references */
37
ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
38
ptr_proc_reloc(igc_reloc_ref_ptr_nocheck, 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
static gc_proc_clear_reloc(refs_clear_reloc);
46
static gc_proc_set_reloc(refs_set_reloc);
47
static gc_proc_compact(refs_compact);
48
static const struct_shared_procs_t refs_shared_procs =
49
{refs_clear_reloc, refs_set_reloc, refs_compact};
50
static struct_proc_clear_marks(refs_clear_marks);
51
static 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
210k
{
66
210k
    ref *pref = (ref *) vptr;
67
210k
    ref *end = (ref *) ((char *)vptr + size);
68
69
5.37M
    for (; pref < end; pref++)
70
5.16M
        r_clear_attrs(pref, l_mark);
71
210k
}
72
ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
73
2.67M
{
74
2.67M
    if (index >= size / sizeof(ref))
75
104k
        return 0;
76
2.57M
    pep->ptr = (const ref *)vptr + index;
77
2.57M
    return ptr_ref_type;
78
2.67M
    ENUM_PTRS_END_PROC
79
2.67M
}
80
104k
RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
81
104k
{
82
104k
    vm_spaces spaces = gcst->spaces;
83
104k
    const gs_memory_t *cmem = space_system->stable_memory;
84
85
104k
    ref *beg = vptr;
86
104k
    ref *end = (ref *) ((char *)vptr + size);
87
88
104k
    igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
89
104k
    ref_struct_clear_marks(cmem, vptr, size, pstype);
90
104k
} 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
10.0k
{
98
10.0k
    ref_packed *rpp = (ref_packed *)pep->ptr;
99
100
10.0k
    if (r_is_packed(rpp))
101
0
        r_clear_pmark(rpp);
102
10.0k
    else
103
10.0k
        r_clear_attrs((ref *)rpp, l_mark);
104
10.0k
}
105
106
/* Unmarking routine for ref objects. */
107
static 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
22.1M
{
112
22.1M
    ref_packed *rp = (ref_packed *) vptr;
113
22.1M
    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
6.31G
    for (;;) {
118
6.31G
        if (r_is_packed(rp)) {
119
#ifdef DEBUG
120
            if (gs_debug_c('8')) {
121
                dmlprintf1(cmem, "  [8]unmark packed "PRI_INTPTR" ", (intptr_t) rp);
122
                debug_print_ref(cmem, (const ref *)rp);
123
                dmputs(cmem, "\n");
124
            }
125
#endif
126
898M
            r_clear_pmark(rp);
127
898M
            rp++;
128
5.41G
        } else {   /* full-size ref */
129
5.41G
            ref *const pref = (ref *)rp;
130
131
#ifdef DEBUG
132
            if (gs_debug_c('8')) {
133
                dmlprintf1(cmem, "  [8]unmark ref "PRI_INTPTR" ", (intptr_t)rp);
134
                debug_print_ref(cmem, pref);
135
                dmputs(cmem, "\n");
136
            }
137
#endif
138
5.41G
            r_clear_attrs(pref, l_mark);
139
5.41G
            rp += packed_per_ref;
140
5.41G
            if (rp >= (ref_packed *) end)
141
22.1M
                break;
142
5.41G
        }
143
6.31G
    }
144
22.1M
}
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
0
{
152
0
    ref_packed *rpp = (void *)pep->ptr;
153
154
0
    if (r_is_packed(rpp)) {
155
0
        if (r_has_pmark(rpp))
156
0
            return false;
157
0
        r_set_pmark(rpp);
158
0
    } else {
159
0
        ref *const pref = (ref *)rpp;
160
161
0
        if (r_has_attr(pref, l_mark))
162
0
            return false;
163
0
        r_set_attrs(pref, l_mark);
164
0
    }
165
0
    return true;
166
0
}
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
static void
180
refs_clear_reloc(obj_header_t *hdr, uint size)
181
815k
{
182
815k
    ref_packed *rp = (ref_packed *) (hdr + 1);
183
815k
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
184
185
86.5M
    while (rp < end) {
186
85.7M
        if (r_is_packed(rp))
187
30.9M
            rp++;
188
54.8M
        else {
189
            /* Full-size ref.  Store the relocation here if possible. */
190
54.8M
            ref *const pref = (ref *)rp;
191
192
54.8M
            if (!ref_type_uses_size_or_null(r_type(pref))) {
193
12.7M
                if_debug1('8', "  [8]clearing reloc at "PRI_INTPTR"\n", (intptr_t)rp);
194
12.7M
                r_set_size(pref, 0);
195
12.7M
            }
196
54.8M
            rp += packed_per_ref;
197
54.8M
        }
198
85.7M
    }
199
815k
}
200
201
/* Set the relocation for a ref object. */
202
static bool
203
refs_set_reloc(obj_header_t * hdr, uint reloc, uint size)
204
21.3M
{
205
21.3M
    ref_packed *rp = (ref_packed *) (hdr + 1);
206
21.3M
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
207
21.3M
    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
5.59G
    while (rp < end) {
218
5.57G
        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 "PRI_INTPTR" is marked\n",
223
                          (intptr_t)rp);
224
                rp++;
225
            } else {
226
#else
227
216M
            int i;
228
229
            /*
230
             * Note: align_packed_per_ref is typically
231
             * 2 or 4 for 32-bit processors.
232
             */
233
216M
#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
216M
#  if align_packed_per_ref == 4
244
216M
#    define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
245
216M
                    (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
216M
# 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
216M
            switch (marked) {
264
77.3M
                case all_marked:
265
77.3M
                    if_debug2('8',
266
77.3M
                              "  [8]packed refs "PRI_INTPTR".."PRI_INTPTR" are marked\n",
267
77.3M
                              (intptr_t)rp,
268
77.3M
                              (intptr_t)(rp + (align_packed_per_ref - 1)));
269
77.3M
                    rp += align_packed_per_ref;
270
77.3M
                    break;
271
87.6M
                default:
272
                    /* At least one packed ref in the block */
273
                    /* is marked: Keep the whole block. */
274
438M
                    for (i = align_packed_per_ref; i--; rp++) {
275
350M
                        r_set_pmark(rp);
276
350M
                        if_debug1('8',
277
350M
                                  "  [8]packed ref "PRI_INTPTR" is marked\n",
278
350M
                                  (intptr_t)rp);
279
350M
                    }
280
87.6M
                    break;
281
51.9M
                case 0:
282
51.9M
#endif
283
51.9M
                    if_debug2('8', "  [8]%d packed ref(s) at "PRI_INTPTR" are unmarked\n",
284
51.9M
                              align_packed_per_ref, (intptr_t)rp);
285
51.9M
                    {
286
51.9M
                        uint rel = reloc + freed;
287
288
                        /* Change this to an integer so we can */
289
                        /* store the relocation here. */
290
51.9M
                        *rp = pt_tag(pt_integer) +
291
51.9M
                            min(rel, packed_max_value);
292
51.9M
                    }
293
51.9M
                    rp += align_packed_per_ref;
294
51.9M
                    freed += sizeof(ref_packed) * align_packed_per_ref;
295
216M
            }
296
5.36G
        } else {   /* full-size ref */
297
5.36G
            uint rel = reloc + freed;
298
299
            /* The following assignment is logically */
300
            /* unnecessary; we do it only for convenience */
301
            /* in debugging. */
302
5.36G
            ref *pref = (ref *) rp;
303
304
5.36G
            if (!r_has_attr(pref, l_mark)) {
305
4.05G
                if_debug1('8', "  [8]ref "PRI_INTPTR" is unmarked\n",
306
4.05G
                          (intptr_t)pref);
307
                /* Change this to a mark so we can */
308
                /* store the relocation. */
309
4.05G
                r_set_type(pref, t_mark);
310
4.05G
                r_set_size(pref, rel);
311
4.05G
                freed += sizeof(ref);
312
4.05G
            } else {
313
1.31G
                if_debug1('8', "  [8]ref "PRI_INTPTR" is marked\n",
314
1.31G
                          (intptr_t)pref);
315
                /* Store the relocation here if possible. */
316
1.31G
                if (!ref_type_uses_size_or_null(r_type(pref))) {
317
258M
                    if_debug2('8', "  [8]storing reloc %u at "PRI_INTPTR"\n",
318
258M
                              rel, (intptr_t)pref);
319
258M
                    r_set_size(pref, rel);
320
258M
                }
321
1.31G
            }
322
5.36G
            rp += packed_per_ref;
323
5.36G
        }
324
5.57G
    }
325
21.3M
    if_debug3('7', " [7]at end of refs "PRI_INTPTR", size = %u, freed = %u\n",
326
21.3M
              (intptr_t)(hdr + 1), size, freed);
327
21.3M
    if (freed == size)
328
3.80M
        return false;
329
17.5M
#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 clump 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
17.5M
    if (freed <= max_ushort)
338
17.5M
        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
1
    rp = (ref_packed *) (hdr + 1);
345
8.13k
    while (rp < end) {
346
8.12k
        if (r_is_packed(rp)) {
347
0
            if (!r_has_pmark(rp))
348
0
                *rp = pt_tag(pt_integer) | lp_mark;
349
0
            ++rp;
350
8.12k
        } else {   /* The following assignment is logically */
351
            /* unnecessary; we do it only for convenience */
352
            /* in debugging. */
353
8.12k
            ref *pref = (ref *) rp;
354
355
8.12k
            if (!r_has_attr(pref, l_mark)) {
356
8.12k
                r_set_type_attrs(pref, t_mark, l_mark);
357
8.12k
                r_set_size(pref, reloc);
358
8.12k
            } else {
359
1
                if (!ref_type_uses_size_or_null(r_type(pref)))
360
0
                    r_set_size(pref, reloc);
361
1
            }
362
8.12k
            rp += packed_per_ref;
363
8.12k
        }
364
8.12k
    }
365
    /* The last ref has to remain unmarked. */
366
1
    r_clear_attrs((ref *) rp - 1, l_mark);
367
1
#endif
368
1
    return true;
369
17.5M
}
370
371
/* ------ Relocation phase ------ */
372
373
/* Relocate all the pointers in a block of refs. */
374
static 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
18.3M
{
378
18.3M
    igc_reloc_refs((ref_packed *) vptr,
379
18.3M
                   (ref_packed *) ((char *)vptr + size),
380
18.3M
                   gcst);
381
18.3M
}
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
18.8M
{
388
18.8M
    int min_trace = gcst->min_collect;
389
18.8M
    ref_packed *rp = from;
390
18.8M
    bool do_all = gcst->relocating_untraced;
391
392
18.8M
    vm_spaces spaces = gcst->spaces;
393
18.8M
    const gs_memory_t *cmem = space_system->stable_memory;
394
395
2.27G
    while (rp < to) {
396
2.25G
        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
2.25G
# define DO_RELOC(var, stat) stat
406
2.25G
# define SET_RELOC(var, expr) var = expr
407
2.25G
#endif
408
409
2.25G
        if (r_is_packed(rp)) {
410
837M
            rp++;
411
837M
            continue;
412
837M
        }
413
        /* The following assignment is logically unnecessary; */
414
        /* we do it only for convenience in debugging. */
415
1.41G
        pref = (ref *) rp;
416
1.41G
        if_debug3m('8', gcst->heap, "  [8]relocating %s %d ref at "PRI_INTPTR"\n",
417
1.41G
                   (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
418
1.41G
                   r_btype(pref), (intptr_t)pref);
419
1.41G
        if ((r_has_attr(pref, l_mark) || do_all) &&
420
1.41G
            r_space(pref) >= min_trace
421
1.41G
            ) {
422
519M
            switch (r_type(pref)) {
423
                    /* Struct cases */
424
107k
                case t_file:
425
107k
                    DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
426
107k
                    break;
427
405k
                case t_device:
428
405k
                    DO_RELOC(pref->value.pdevice,
429
405k
                             RELOC_VAR(pref->value.pdevice));
430
405k
                    break;
431
19.8k
                case t_fontID:
432
79.3k
                case t_struct:
433
97.7k
                case t_astruct:
434
97.7k
                case t_pdfctx:
435
97.7k
                    DO_RELOC(pref->value.pstruct,
436
97.7k
                             RELOC_VAR(pref->value.pstruct));
437
97.7k
                    break;
438
                    /* Non-trivial non-struct cases */
439
10.0M
                case t_dictionary:
440
10.0M
                    rputc(gcst->heap, 'd');
441
10.0M
                    SET_RELOC(pref->value.pdict,
442
10.0M
                              (dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
443
10.0M
                    break;
444
92.7M
                case t_array:
445
92.7M
                    {
446
92.7M
                        uint size = r_size(pref);
447
448
92.7M
                        if (size != 0) { /* value.refs might be NULL */
449
450
                            /*
451
                             * If the array is large, we allocated it in its
452
                             * own object (at least originally -- this might
453
                             * be a pointer to a subarray.)  In this case,
454
                             * we know it is the only object in its
455
                             * containing st_refs object, so we know that
456
                             * the mark containing the relocation appears
457
                             * just after it.
458
                             */
459
92.1M
                            if (size < max_size_st_refs / sizeof(ref)) {
460
90.5M
                                rputc(gcst->heap, 'a');
461
90.5M
                                SET_RELOC(pref->value.refs,
462
90.5M
                                    (ref *) igc_reloc_ref_ptr(
463
90.5M
                                     (ref_packed *) pref->value.refs, gcst));
464
90.5M
                            } else {
465
1.55M
                                rputc(gcst->heap, 'A');
466
                                /*
467
                                 * See the t_shortarray case below for why we
468
                                 * decrement size.
469
                                 */
470
1.55M
                                --size;
471
1.55M
                                SET_RELOC(pref->value.refs,
472
1.55M
                                    (ref *) igc_reloc_ref_ptr(
473
1.55M
                                   (ref_packed *) (pref->value.refs + size),
474
1.55M
                                                               gcst) - size);
475
1.55M
                            }
476
92.1M
                        }
477
92.7M
                    }
478
92.7M
                    break;
479
67.9M
                case t_mixedarray:
480
67.9M
                    if (r_size(pref) != 0) { /* value.refs might be NULL */
481
67.9M
                        rputc(gcst->heap, 'm');
482
67.9M
                        SET_RELOC(pref->value.packed,
483
67.9M
                                  igc_reloc_ref_ptr(pref->value.packed, gcst));
484
67.9M
                    }
485
67.9M
                    break;
486
30.1M
                case t_shortarray:
487
30.1M
                    {
488
30.1M
                        uint size = r_size(pref);
489
490
                        /*
491
                         * Since we know that igc_reloc_ref_ptr works by
492
                         * scanning forward, and we know that all the
493
                         * elements of this array itself are marked, we can
494
                         * save some scanning time by relocating the pointer
495
                         * to the end of the array rather than the
496
                         * beginning.
497
                         */
498
30.1M
                        if (size != 0) { /* value.refs might be NULL */
499
28.0M
                            rputc(gcst->heap, 's');
500
                            /*
501
                             * igc_reloc_ref_ptr has to be able to determine
502
                             * whether the pointer points into a space that
503
                             * isn't being collected.  It does this by
504
                             * checking whether the referent of the pointer
505
                             * is marked.  For this reason, we have to pass
506
                             * a pointer to the last real element of the
507
                             * array, rather than just beyond it.
508
                             */
509
28.0M
                            --size;
510
28.0M
                            SET_RELOC(pref->value.packed,
511
28.0M
                                igc_reloc_ref_ptr(pref->value.packed + size,
512
28.0M
                                                  gcst) - size);
513
28.0M
                        }
514
30.1M
                    }
515
30.1M
                    break;
516
293M
                case t_name:
517
293M
                    {
518
293M
                        void *psub = name_ref_sub_table(cmem, pref);
519
293M
                        void *rsub = RELOC_OBJ(psub); /* gcst implicit */
520
521
293M
                        SET_RELOC(pref->value.pname,
522
293M
                                  (name *)
523
293M
                                  ((char *)rsub + ((char *)pref->value.pname -
524
293M
                                                   (char *)psub)));
525
293M
                    } break;
526
16.7M
                case t_string:
527
16.7M
                    {
528
16.7M
                        gs_string str;
529
530
16.7M
                        str.data = pref->value.bytes;
531
16.7M
                        str.size = r_size(pref);
532
533
16.7M
                        DO_RELOC(str.data, RELOC_STRING_VAR(str));
534
16.7M
                        pref->value.bytes = str.data;
535
16.7M
                    }
536
16.7M
                    break;
537
7.74M
                case t_oparray:
538
7.74M
                    rputc(gcst->heap, 'o');
539
7.74M
                    SET_RELOC(pref->value.const_refs,
540
7.74M
                        (const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
541
7.74M
                    break;
542
0
                default:
543
0
                    goto no_reloc; /* don't print trace message */
544
519M
            }
545
519M
            if_debug2m('8', gcst->heap, "  [8]relocated "PRI_INTPTR" => "PRI_INTPTR"\n",
546
519M
                       (intptr_t)before, (intptr_t)after);
547
519M
        }
548
1.41G
no_reloc:
549
1.41G
        rp += packed_per_ref;
550
1.41G
    }
551
18.8M
}
552
553
/* Relocate a pointer to a ref. */
554
/* See gsmemory.h for why the argument is const and the result is not. */
555
ref_packed *
556
igc_reloc_ref_ptr_nocheck(const ref_packed * prp, gc_state_t *gcst)
557
205M
{
558
    /*
559
     * Search forward for relocation.  This algorithm is intrinsically very
560
     * inefficient; we hope eventually to replace it with a better one.
561
     */
562
205M
    const ref_packed *rp = prp;
563
205M
    uint dec = 0;
564
#ifdef ALIGNMENT_ALIASING_BUG
565
    const ref *rpref;
566
# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
567
#else
568
205M
# define RP_REF(rp) ((const ref *)rp)
569
205M
#endif
570
5.95G
    for (;;) {
571
572
5.95G
        if (r_is_packed(rp)) {
573
            /*
574
             * Normally, an unmarked packed ref will be an
575
             * integer whose value is the amount of relocation.
576
             * However, the relocation value might have been
577
             * too large to fit.  If this is the case, for
578
             * each such unmarked packed ref we pass over,
579
             * we have to decrement the final relocation.
580
             */
581
1.70G
            rputc(gcst->heap, (*rp & lp_mark ? '1' : '0'));
582
1.70G
            if (!(*rp & lp_mark)) {
583
43.5M
                if (*rp != pt_tag(pt_integer) + packed_max_value) {
584
                    /* This is a stored relocation value. */
585
38.7M
                    rputc(gcst->heap, '\n');
586
38.7M
                    rp = print_reloc(prp, "ref",
587
38.7M
                                     (const ref_packed *)
588
38.7M
                                     ((const char *)prp -
589
38.7M
                                      (*rp & packed_value_mask) + dec));
590
38.7M
                    break;
591
38.7M
                }
592
                /*
593
                 * We know this is the first of an aligned block
594
                 * of packed refs.  Skip over the entire block,
595
                 * decrementing the final relocation.
596
                 */
597
4.77M
                dec += sizeof(ref_packed) * align_packed_per_ref;
598
4.77M
                rp += align_packed_per_ref;
599
4.77M
            } else
600
1.65G
                rp++;
601
1.66G
            continue;
602
1.70G
        }
603
4.25G
        if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
604
            /* reloc is in r_size */
605
167M
            rputc(gcst->heap, '\n');
606
167M
            rp = print_reloc(prp, "ref",
607
167M
                             (const ref_packed *)
608
167M
                             (r_size(RP_REF(rp)) == 0 ? prp :
609
167M
                              (const ref_packed *)((const char *)prp -
610
167M
                                                   r_size(RP_REF(rp)) + dec)));
611
167M
            break;
612
167M
        }
613
4.25G
        rputc(gcst->heap, 'u');
614
4.08G
        rp += packed_per_ref;
615
4.08G
    }
616
    /* Use a severely deprecated pun to remove the const property. */
617
205M
    {
618
205M
        union { const ref_packed *r; ref_packed *w; } u;
619
620
205M
        u.r = rp;
621
205M
        return u.w;
622
205M
    }
623
205M
#undef RP_REF
624
205M
}
625
ref_packed *
626
igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
627
205M
{
628
    /*
629
     * Search forward for relocation.  This algorithm is intrinsically very
630
     * inefficient; we hope eventually to replace it with a better one.
631
     */
632
205M
    const ref_packed *rp = prp;
633
#ifdef ALIGNMENT_ALIASING_BUG
634
    const ref *rpref;
635
# define RP_REF(rp) (rpref = (const ref *)rp, rpref)
636
#else
637
205M
# define RP_REF(rp) ((const ref *)rp)
638
205M
#endif
639
    /*
640
     * Iff this pointer points into a space that wasn't traced,
641
     * the referent won't be marked.  In this case, we shouldn't
642
     * do any relocation.  Check for this first.
643
     */
644
205M
    if (r_is_packed(rp)) {
645
70.0M
        if (!r_has_pmark(rp))
646
2
            goto ret_rp;
647
135M
    } else {
648
135M
        if (!r_has_attr(RP_REF(rp), l_mark))
649
2.31k
            goto ret_rp;
650
135M
    }
651
205M
    return igc_reloc_ref_ptr_nocheck(prp, gcst);
652
2.32k
ret_rp:
653
    /* Use a severely deprecated pun to remove the const property. */
654
2.32k
    {
655
2.32k
        union { const ref_packed *r; ref_packed *w; } u;
656
657
2.32k
        u.r = rp;
658
2.32k
        return u.w;
659
205M
    }
660
205M
}
661
662
/* ------ Compaction phase ------ */
663
664
/* Compact a ref object. */
665
/* Remove the marks at the same time. */
666
static void
667
refs_compact(const gs_memory_t *mem, obj_header_t * pre, obj_header_t * dpre, uint size)
668
17.5M
{
669
17.5M
    ref_packed *dest;
670
17.5M
    ref_packed *src;
671
17.5M
    ref_packed *end;
672
17.5M
    uint new_size;
673
674
   /* The next switch controls an optimization
675
      for the loop termination condition.
676
      It was useful during the development,
677
      when some assumptions were temporary wrong.
678
      We keep it for records. */
679
680
17.5M
    src = (ref_packed *) (pre + 1);
681
17.5M
    end = (ref_packed *) ((byte *) src + size);
682
    /*
683
     * We know that a block of refs always ends with a
684
     * full-size ref, so we only need to check for reaching the end
685
     * of the block when we see one of those.
686
     */
687
17.5M
    if (dpre == pre)    /* Loop while we don't need to copy. */
688
1.01G
        for (;;) {
689
1.01G
            if (r_is_packed(src)) {
690
108M
                if (!r_has_pmark(src))
691
2.83M
                    break;
692
108M
                if_debug1m('8', mem, "  [8]packed ref "PRI_INTPTR" \"copied\"\n",
693
105M
                          (intptr_t)src);
694
105M
                *src &= ~lp_mark;
695
105M
                src++;
696
906M
            } else {   /* full-size ref */
697
906M
                ref *const pref = (ref *)src;
698
699
906M
                if (!r_has_attr(pref, l_mark))
700
3.64M
                    break;
701
906M
                if_debug1m('8', mem, "  [8]ref "PRI_INTPTR" \"copied\"\n", (intptr_t)src);
702
902M
                r_clear_attrs(pref, l_mark);
703
902M
                src += packed_per_ref;
704
902M
            }
705
1.01G
    } else
706
11.0M
        *dpre = *pre;
707
17.5M
    dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
708
1.15G
    for (;;) {
709
1.15G
        if (r_is_packed(src)) {
710
700M
            if (r_has_pmark(src)) {
711
554M
                if_debug2m('8', mem, "  [8]packed ref "PRI_INTPTR" copied to "PRI_INTPTR"\n",
712
554M
                          (intptr_t)src, (intptr_t)dest);
713
554M
                *dest++ = *src & ~lp_mark;
714
554M
            }
715
700M
            src++;
716
700M
        } else {   /* full-size ref */
717
454M
            if (r_has_attr((ref *) src, l_mark)) {
718
407M
                ref rtemp;
719
720
407M
                if_debug2m('8', mem, "  [8]ref "PRI_INTPTR" copied to "PRI_INTPTR"\n",
721
407M
                           (intptr_t)src, (intptr_t)dest);
722
                /* We can't just use ref_assign_inline, */
723
                /* because the source and destination */
724
                /* might overlap! */
725
407M
                ref_assign_inline(&rtemp, (ref *) src);
726
407M
                r_clear_attrs(&rtemp, l_mark);
727
407M
                ref_assign_inline((ref *) dest, &rtemp);
728
407M
                src += packed_per_ref;
729
407M
                dest += packed_per_ref;
730
407M
            } else {   /* check for end of block */
731
46.5M
                src += packed_per_ref;
732
46.5M
                if (src >= end)
733
17.5M
                    break;
734
46.5M
            }
735
454M
        }
736
1.15G
    }
737
17.5M
    new_size = (byte *) dest - (byte *) (dpre + 1) + sizeof(ref);
738
#ifdef DEBUG
739
    /* Check that the relocation came out OK. */
740
    /* NOTE: this check only works within a single clump. */
741
    if ((byte *) src - (byte *) dest != r_size((ref *) src - 1) + sizeof(ref)) {
742
        mlprintf3(mem, "Reloc error for refs "PRI_INTPTR": reloc = %lu, stored = %u\n",
743
                 (intptr_t) dpre, (ulong) ((byte *) src - (byte *) dest),
744
                 (uint) r_size((ref *) src - 1));
745
        gs_abort(mem);
746
    }
747
#endif
748
    /* Pad to a multiple of sizeof(ref). */
749
39.9M
    while (new_size % sizeof(ref))
750
22.3M
        *dest++ = pt_tag(pt_integer),
751
22.3M
            new_size += sizeof(ref_packed);
752
    /* We want to make the newly freed space into a free block, */
753
    /* but we can only do this if we have enough room. */
754
17.5M
    if (size - new_size < sizeof(obj_header_t)) { /* Not enough room.  Pad to original size. */
755
11.9M
        while (new_size < size)
756
0
            *dest++ = pt_tag(pt_integer),
757
0
                new_size += sizeof(ref_packed);
758
11.9M
    } else {
759
5.58M
        obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
760
761
5.58M
        pfree->o_pad = 0;
762
5.58M
        pfree->o_alone = 0;
763
5.58M
        pfree->o_size = size - new_size - sizeof(obj_header_t);
764
5.58M
        pfree->o_type = &st_bytes;
765
5.58M
    }
766
    /* Re-create the final ref. */
767
17.5M
    r_set_type((ref *) dest, t_integer);
768
17.5M
    dpre->o_size = new_size;
769
17.5M
}