Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/igcref.c
Line
Count
Source
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
132G
#  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
39.8M
{
66
39.8M
    ref *pref = (ref *) vptr;
67
39.8M
    ref *end = (ref *) ((char *)vptr + size);
68
69
1.45G
    for (; pref < end; pref++)
70
1.41G
        r_clear_attrs(pref, l_mark);
71
39.8M
}
72
ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
73
725M
{
74
725M
    if (index >= size / sizeof(ref))
75
19.9M
        return 0;
76
705M
    pep->ptr = (const ref *)vptr + index;
77
705M
    return ptr_ref_type;
78
725M
    ENUM_PTRS_END_PROC
79
725M
}
80
19.9M
RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
81
19.9M
{
82
19.9M
    vm_spaces spaces = gcst->spaces;
83
19.9M
    const gs_memory_t *cmem = space_system->stable_memory;
84
85
19.9M
    ref *beg = vptr;
86
19.9M
    ref *end = (ref *) ((char *)vptr + size);
87
88
19.9M
    igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
89
19.9M
    ref_struct_clear_marks(cmem, vptr, size, pstype);
90
19.9M
} 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
209k
{
98
209k
    ref_packed *rpp = (ref_packed *)pep->ptr;
99
100
209k
    if (r_is_packed(rpp))
101
0
        r_clear_pmark(rpp);
102
209k
    else
103
209k
        r_clear_attrs((ref *)rpp, l_mark);
104
209k
}
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
450M
{
112
450M
    ref_packed *rp = (ref_packed *) vptr;
113
450M
    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
92.5G
    for (;;) {
118
92.5G
        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
18.7G
            r_clear_pmark(rp);
127
18.7G
            rp++;
128
73.8G
        } else {   /* full-size ref */
129
73.8G
            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
73.8G
            r_clear_attrs(pref, l_mark);
139
73.8G
            rp += packed_per_ref;
140
73.8G
            if (rp >= (ref_packed *) end)
141
450M
                break;
142
73.8G
        }
143
92.5G
    }
144
450M
}
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
15.1M
{
182
15.1M
    ref_packed *rp = (ref_packed *) (hdr + 1);
183
15.1M
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
184
185
1.66G
    while (rp < end) {
186
1.64G
        if (r_is_packed(rp))
187
562M
            rp++;
188
1.08G
        else {
189
            /* Full-size ref.  Store the relocation here if possible. */
190
1.08G
            ref *const pref = (ref *)rp;
191
192
1.08G
            if (!ref_type_uses_size_or_null(r_type(pref))) {
193
250M
                if_debug1('8', "  [8]clearing reloc at "PRI_INTPTR"\n", (intptr_t)rp);
194
250M
                r_set_size(pref, 0);
195
250M
            }
196
1.08G
            rp += packed_per_ref;
197
1.08G
        }
198
1.64G
    }
199
15.1M
}
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
435M
{
205
435M
    ref_packed *rp = (ref_packed *) (hdr + 1);
206
435M
    ref_packed *end = (ref_packed *) ((byte *) rp + size);
207
435M
    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
77.7G
    while (rp < end) {
218
77.2G
        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
4.55G
            int i;
228
229
            /*
230
             * Note: align_packed_per_ref is typically
231
             * 2 or 4 for 32-bit processors.
232
             */
233
4.55G
#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
4.55G
#  if align_packed_per_ref == 4
244
4.55G
#    define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
245
4.55G
                    (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
4.55G
# 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
4.55G
            switch (marked) {
264
1.64G
                case all_marked:
265
1.64G
                    if_debug2('8',
266
1.64G
                              "  [8]packed refs "PRI_INTPTR".."PRI_INTPTR" are marked\n",
267
1.64G
                              (intptr_t)rp,
268
1.64G
                              (intptr_t)(rp + (align_packed_per_ref - 1)));
269
1.64G
                    rp += align_packed_per_ref;
270
1.64G
                    break;
271
1.82G
                default:
272
                    /* At least one packed ref in the block */
273
                    /* is marked: Keep the whole block. */
274
9.11G
                    for (i = align_packed_per_ref; i--; rp++) {
275
7.29G
                        r_set_pmark(rp);
276
7.29G
                        if_debug1('8',
277
7.29G
                                  "  [8]packed ref "PRI_INTPTR" is marked\n",
278
7.29G
                                  (intptr_t)rp);
279
7.29G
                    }
280
1.82G
                    break;
281
1.08G
                case 0:
282
1.08G
#endif
283
1.08G
                    if_debug2('8', "  [8]%d packed ref(s) at "PRI_INTPTR" are unmarked\n",
284
1.08G
                              align_packed_per_ref, (intptr_t)rp);
285
1.08G
                    {
286
1.08G
                        uint rel = reloc + freed;
287
288
                        /* Change this to an integer so we can */
289
                        /* store the relocation here. */
290
1.08G
                        *rp = pt_tag(pt_integer) +
291
1.08G
                            min(rel, packed_max_value);
292
1.08G
                    }
293
1.08G
                    rp += align_packed_per_ref;
294
1.08G
                    freed += sizeof(ref_packed) * align_packed_per_ref;
295
4.55G
            }
296
72.7G
        } else {   /* full-size ref */
297
72.7G
            uint rel = reloc + freed;
298
299
            /* The following assignment is logically */
300
            /* unnecessary; we do it only for convenience */
301
            /* in debugging. */
302
72.7G
            ref *pref = (ref *) rp;
303
304
72.7G
            if (!r_has_attr(pref, l_mark)) {
305
43.3G
                if_debug1('8', "  [8]ref "PRI_INTPTR" is unmarked\n",
306
43.3G
                          (intptr_t)pref);
307
                /* Change this to a mark so we can */
308
                /* store the relocation. */
309
43.3G
                r_set_type(pref, t_mark);
310
43.3G
                r_set_size(pref, rel);
311
43.3G
                freed += sizeof(ref);
312
43.3G
            } else {
313
29.3G
                if_debug1('8', "  [8]ref "PRI_INTPTR" is marked\n",
314
29.3G
                          (intptr_t)pref);
315
                /* Store the relocation here if possible. */
316
29.3G
                if (!ref_type_uses_size_or_null(r_type(pref))) {
317
5.80G
                    if_debug2('8', "  [8]storing reloc %u at "PRI_INTPTR"\n",
318
5.80G
                              rel, (intptr_t)pref);
319
5.80G
                    r_set_size(pref, rel);
320
5.80G
                }
321
29.3G
            }
322
72.7G
            rp += packed_per_ref;
323
72.7G
        }
324
77.2G
    }
325
435M
    if_debug3('7', " [7]at end of refs "PRI_INTPTR", size = %u, freed = %u\n",
326
435M
              (intptr_t)(hdr + 1), size, freed);
327
435M
    if (freed == size)
328
66.6M
        return false;
329
368M
#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
368M
    if (freed <= max_ushort)
338
368M
        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
7
    rp = (ref_packed *) (hdr + 1);
345
56.9k
    while (rp < end) {
346
56.9k
        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
56.9k
        } else {   /* The following assignment is logically */
351
            /* unnecessary; we do it only for convenience */
352
            /* in debugging. */
353
56.9k
            ref *pref = (ref *) rp;
354
355
56.9k
            if (!r_has_attr(pref, l_mark)) {
356
56.8k
                r_set_type_attrs(pref, t_mark, l_mark);
357
56.8k
                r_set_size(pref, reloc);
358
56.8k
            } else {
359
7
                if (!ref_type_uses_size_or_null(r_type(pref)))
360
0
                    r_set_size(pref, reloc);
361
7
            }
362
56.9k
            rp += packed_per_ref;
363
56.9k
        }
364
56.9k
    }
365
    /* The last ref has to remain unmarked. */
366
7
    r_clear_attrs((ref *) rp - 1, l_mark);
367
7
#endif
368
7
    return true;
369
368M
}
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
383M
{
378
383M
    igc_reloc_refs((ref_packed *) vptr,
379
383M
                   (ref_packed *) ((char *)vptr + size),
380
383M
                   gcst);
381
383M
}
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
419M
{
388
419M
    int min_trace = gcst->min_collect;
389
419M
    ref_packed *rp = from;
390
419M
    bool do_all = gcst->relocating_untraced;
391
392
419M
    vm_spaces spaces = gcst->spaces;
393
419M
    const gs_memory_t *cmem = space_system->stable_memory;
394
395
50.1G
    while (rp < to) {
396
49.7G
        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
49.7G
# define DO_RELOC(var, stat) stat
406
49.7G
# define SET_RELOC(var, expr) var = expr
407
49.7G
#endif
408
409
49.7G
        if (r_is_packed(rp)) {
410
17.5G
            rp++;
411
17.5G
            continue;
412
17.5G
        }
413
        /* The following assignment is logically unnecessary; */
414
        /* we do it only for convenience in debugging. */
415
32.1G
        pref = (ref *) rp;
416
32.1G
        if_debug3m('8', gcst->heap, "  [8]relocating %s %d ref at "PRI_INTPTR"\n",
417
32.1G
                   (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
418
32.1G
                   r_btype(pref), (intptr_t)pref);
419
32.1G
        if ((r_has_attr(pref, l_mark) || do_all) &&
420
31.1G
            r_space(pref) >= min_trace
421
32.1G
            ) {
422
11.3G
            switch (r_type(pref)) {
423
                    /* Struct cases */
424
2.25M
                case t_file:
425
2.25M
                    DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
426
2.25M
                    break;
427
8.43M
                case t_device:
428
8.43M
                    DO_RELOC(pref->value.pdevice,
429
8.43M
                             RELOC_VAR(pref->value.pdevice));
430
8.43M
                    break;
431
565k
                case t_fontID:
432
4.66M
                case t_struct:
433
5.05M
                case t_astruct:
434
5.05M
                case t_pdfctx:
435
5.05M
                    DO_RELOC(pref->value.pstruct,
436
5.05M
                             RELOC_VAR(pref->value.pstruct));
437
5.05M
                    break;
438
                    /* Non-trivial non-struct cases */
439
233M
                case t_dictionary:
440
233M
                    rputc(gcst->heap, 'd');
441
233M
                    SET_RELOC(pref->value.pdict,
442
233M
                              (dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
443
233M
                    break;
444
1.97G
                case t_array:
445
1.97G
                    {
446
1.97G
                        uint size = r_size(pref);
447
448
1.97G
                        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
1.95G
                            if (size < max_size_st_refs / sizeof(ref)) {
460
1.92G
                                rputc(gcst->heap, 'a');
461
1.92G
                                SET_RELOC(pref->value.refs,
462
1.92G
                                    (ref *) igc_reloc_ref_ptr(
463
1.92G
                                     (ref_packed *) pref->value.refs, gcst));
464
1.92G
                            } else {
465
33.7M
                                rputc(gcst->heap, 'A');
466
                                /*
467
                                 * See the t_shortarray case below for why we
468
                                 * decrement size.
469
                                 */
470
33.7M
                                --size;
471
33.7M
                                SET_RELOC(pref->value.refs,
472
33.7M
                                    (ref *) igc_reloc_ref_ptr(
473
33.7M
                                   (ref_packed *) (pref->value.refs + size),
474
33.7M
                                                               gcst) - size);
475
33.7M
                            }
476
1.95G
                        }
477
1.97G
                    }
478
1.97G
                    break;
479
1.42G
                case t_mixedarray:
480
1.42G
                    if (r_size(pref) != 0) { /* value.refs might be NULL */
481
1.42G
                        rputc(gcst->heap, 'm');
482
1.42G
                        SET_RELOC(pref->value.packed,
483
1.42G
                                  igc_reloc_ref_ptr(pref->value.packed, gcst));
484
1.42G
                    }
485
1.42G
                    break;
486
630M
                case t_shortarray:
487
630M
                    {
488
630M
                        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
630M
                        if (size != 0) { /* value.refs might be NULL */
499
586M
                            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
586M
                            --size;
510
586M
                            SET_RELOC(pref->value.packed,
511
586M
                                igc_reloc_ref_ptr(pref->value.packed + size,
512
586M
                                                  gcst) - size);
513
586M
                        }
514
630M
                    }
515
630M
                    break;
516
6.48G
                case t_name:
517
6.48G
                    {
518
6.48G
                        void *psub = name_ref_sub_table(cmem, pref);
519
6.48G
                        void *rsub = RELOC_OBJ(psub); /* gcst implicit */
520
521
6.48G
                        SET_RELOC(pref->value.pname,
522
6.48G
                                  (name *)
523
6.48G
                                  ((char *)rsub + ((char *)pref->value.pname -
524
6.48G
                                                   (char *)psub)));
525
6.48G
                    } break;
526
383M
                case t_string:
527
383M
                    {
528
383M
                        gs_string str;
529
530
383M
                        str.data = pref->value.bytes;
531
383M
                        str.size = r_size(pref);
532
533
383M
                        DO_RELOC(str.data, RELOC_STRING_VAR(str));
534
383M
                        pref->value.bytes = str.data;
535
383M
                    }
536
383M
                    break;
537
158M
                case t_oparray:
538
158M
                    rputc(gcst->heap, 'o');
539
158M
                    SET_RELOC(pref->value.const_refs,
540
158M
                        (const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
541
158M
                    break;
542
0
                default:
543
0
                    goto no_reloc; /* don't print trace message */
544
11.3G
            }
545
11.3G
            if_debug2m('8', gcst->heap, "  [8]relocated "PRI_INTPTR" => "PRI_INTPTR"\n",
546
11.3G
                       (intptr_t)before, (intptr_t)after);
547
11.3G
        }
548
32.1G
no_reloc:
549
32.1G
        rp += packed_per_ref;
550
32.1G
    }
551
419M
}
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
4.36G
{
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
4.36G
    const ref_packed *rp = prp;
563
4.36G
    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
4.36G
# define RP_REF(rp) ((const ref *)rp)
569
4.36G
#endif
570
123G
    for (;;) {
571
572
123G
        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
35.5G
            rputc(gcst->heap, (*rp & lp_mark ? '1' : '0'));
582
35.5G
            if (!(*rp & lp_mark)) {
583
926M
                if (*rp != pt_tag(pt_integer) + packed_max_value) {
584
                    /* This is a stored relocation value. */
585
836M
                    rputc(gcst->heap, '\n');
586
836M
                    rp = print_reloc(prp, "ref",
587
836M
                                     (const ref_packed *)
588
836M
                                     ((const char *)prp -
589
836M
                                      (*rp & packed_value_mask) + dec));
590
836M
                    break;
591
836M
                }
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
90.5M
                dec += sizeof(ref_packed) * align_packed_per_ref;
598
90.5M
                rp += align_packed_per_ref;
599
90.5M
            } else
600
34.5G
                rp++;
601
34.6G
            continue;
602
35.5G
        }
603
88.2G
        if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
604
            /* reloc is in r_size */
605
3.53G
            rputc(gcst->heap, '\n');
606
3.53G
            rp = print_reloc(prp, "ref",
607
3.53G
                             (const ref_packed *)
608
3.53G
                             (r_size(RP_REF(rp)) == 0 ? prp :
609
3.53G
                              (const ref_packed *)((const char *)prp -
610
3.53G
                                                   r_size(RP_REF(rp)) + dec)));
611
3.53G
            break;
612
3.53G
        }
613
88.2G
        rputc(gcst->heap, 'u');
614
84.7G
        rp += packed_per_ref;
615
84.7G
    }
616
    /* Use a severely deprecated pun to remove the const property. */
617
4.36G
    {
618
4.36G
        union { const ref_packed *r; ref_packed *w; } u;
619
620
4.36G
        u.r = rp;
621
4.36G
        return u.w;
622
4.36G
    }
623
4.36G
#undef RP_REF
624
4.36G
}
625
ref_packed *
626
igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
627
4.36G
{
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
4.36G
    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
4.36G
# define RP_REF(rp) ((const ref *)rp)
638
4.36G
#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
4.36G
    if (r_is_packed(rp)) {
645
1.47G
        if (!r_has_pmark(rp))
646
292
            goto ret_rp;
647
2.89G
    } else {
648
2.89G
        if (!r_has_attr(RP_REF(rp), l_mark))
649
64.8k
            goto ret_rp;
650
2.89G
    }
651
4.36G
    return igc_reloc_ref_ptr_nocheck(prp, gcst);
652
65.1k
ret_rp:
653
    /* Use a severely deprecated pun to remove the const property. */
654
65.1k
    {
655
65.1k
        union { const ref_packed *r; ref_packed *w; } u;
656
657
65.1k
        u.r = rp;
658
65.1k
        return u.w;
659
4.36G
    }
660
4.36G
}
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
368M
{
669
368M
    ref_packed *dest;
670
368M
    ref_packed *src;
671
368M
    ref_packed *end;
672
368M
    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
368M
    src = (ref_packed *) (pre + 1);
681
368M
    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
368M
    if (dpre == pre)    /* Loop while we don't need to copy. */
688
23.3G
        for (;;) {
689
23.3G
            if (r_is_packed(src)) {
690
2.43G
                if (!r_has_pmark(src))
691
56.8M
                    break;
692
2.43G
                if_debug1m('8', mem, "  [8]packed ref "PRI_INTPTR" \"copied\"\n",
693
2.37G
                          (intptr_t)src);
694
2.37G
                *src &= ~lp_mark;
695
2.37G
                src++;
696
20.9G
            } else {   /* full-size ref */
697
20.9G
                ref *const pref = (ref *)src;
698
699
20.9G
                if (!r_has_attr(pref, l_mark))
700
80.8M
                    break;
701
20.9G
                if_debug1m('8', mem, "  [8]ref "PRI_INTPTR" \"copied\"\n", (intptr_t)src);
702
20.8G
                r_clear_attrs(pref, l_mark);
703
20.8G
                src += packed_per_ref;
704
20.8G
            }
705
23.3G
    } else
706
230M
        *dpre = *pre;
707
368M
    dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
708
24.1G
    for (;;) {
709
24.1G
        if (r_is_packed(src)) {
710
14.5G
            if (r_has_pmark(src)) {
711
11.5G
                if_debug2m('8', mem, "  [8]packed ref "PRI_INTPTR" copied to "PRI_INTPTR"\n",
712
11.5G
                          (intptr_t)src, (intptr_t)dest);
713
11.5G
                *dest++ = *src & ~lp_mark;
714
11.5G
            }
715
14.5G
            src++;
716
14.5G
        } else {   /* full-size ref */
717
9.51G
            if (r_has_attr((ref *) src, l_mark)) {
718
8.51G
                ref rtemp;
719
720
8.51G
                if_debug2m('8', mem, "  [8]ref "PRI_INTPTR" copied to "PRI_INTPTR"\n",
721
8.51G
                           (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
8.51G
                ref_assign_inline(&rtemp, (ref *) src);
726
8.51G
                r_clear_attrs(&rtemp, l_mark);
727
8.51G
                ref_assign_inline((ref *) dest, &rtemp);
728
8.51G
                src += packed_per_ref;
729
8.51G
                dest += packed_per_ref;
730
8.51G
            } else {   /* check for end of block */
731
1.00G
                src += packed_per_ref;
732
1.00G
                if (src >= end)
733
368M
                    break;
734
1.00G
            }
735
9.51G
        }
736
24.1G
    }
737
368M
    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
837M
    while (new_size % sizeof(ref))
750
469M
        *dest++ = pt_tag(pt_integer),
751
469M
            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
368M
    if (size - new_size < sizeof(obj_header_t)) { /* Not enough room.  Pad to original size. */
755
250M
        while (new_size < size)
756
0
            *dest++ = pt_tag(pt_integer),
757
0
                new_size += sizeof(ref_packed);
758
250M
    } else {
759
117M
        obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
760
761
117M
        pfree->o_pad = 0;
762
117M
        pfree->o_alone = 0;
763
117M
        pfree->o_size = size - new_size - sizeof(obj_header_t);
764
117M
        pfree->o_type = &st_bytes;
765
117M
    }
766
    /* Re-create the final ref. */
767
368M
    r_set_type((ref *) dest, t_integer);
768
368M
    dpre->o_size = new_size;
769
368M
}