xref: /plan9-contrib/sys/src/cmd/gs/src/igcref.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
17dd7cddfSDavid du Colombier /* Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
27dd7cddfSDavid du Colombier 
3*593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4*593dc095SDavid du Colombier   implied.
57dd7cddfSDavid du Colombier 
6*593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7*593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8*593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
97dd7cddfSDavid du Colombier 
10*593dc095SDavid du Colombier   For more information about licensing, please refer to
11*593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12*593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13*593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14*593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
157dd7cddfSDavid du Colombier */
167dd7cddfSDavid du Colombier 
17*593dc095SDavid du Colombier /* $Id: igcref.c,v 1.6 2004/08/04 19:36:12 stefan Exp $ */
187dd7cddfSDavid du Colombier /* ref garbage collector for Ghostscript */
197dd7cddfSDavid du Colombier #include "memory_.h"
207dd7cddfSDavid du Colombier #include "ghost.h"
217dd7cddfSDavid du Colombier #include "gsexit.h"
227dd7cddfSDavid du Colombier #include "gsstruct.h"		/* for gxalloc.h included by iastate.h */
237dd7cddfSDavid du Colombier #include "iname.h"
247dd7cddfSDavid du Colombier #include "iastate.h"
257dd7cddfSDavid du Colombier #include "idebug.h"
267dd7cddfSDavid du Colombier #include "igc.h"
277dd7cddfSDavid du Colombier #include "ipacked.h"
287dd7cddfSDavid du Colombier #include "store.h"		/* for ref_assign_inline */
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier /* Define whether to trace every step of relocating ref pointers. */
317dd7cddfSDavid du Colombier #if 0
327dd7cddfSDavid du Colombier #  define rputc(c) dputc(c)
337dd7cddfSDavid du Colombier #else
347dd7cddfSDavid du Colombier #  define rputc(c) DO_NOTHING
357dd7cddfSDavid du Colombier #endif
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier /* Forward references */
387dd7cddfSDavid du Colombier ptr_proc_reloc(igc_reloc_ref_ptr, ref_packed);
397dd7cddfSDavid du Colombier refs_proc_reloc(igc_reloc_refs);
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier /*
427dd7cddfSDavid du Colombier  * Define the 'structure' type descriptor for refs.
437dd7cddfSDavid du Colombier  * This is special because it has different shared procs.
447dd7cddfSDavid du Colombier  */
457dd7cddfSDavid du Colombier private gc_proc_clear_reloc(refs_clear_reloc);
467dd7cddfSDavid du Colombier private gc_proc_set_reloc(refs_set_reloc);
477dd7cddfSDavid du Colombier private gc_proc_compact(refs_compact);
487dd7cddfSDavid du Colombier private const struct_shared_procs_t refs_shared_procs =
497dd7cddfSDavid du Colombier {refs_clear_reloc, refs_set_reloc, refs_compact};
507dd7cddfSDavid du Colombier private struct_proc_clear_marks(refs_clear_marks);
517dd7cddfSDavid du Colombier private struct_proc_reloc_ptrs(refs_do_reloc);
527dd7cddfSDavid du Colombier const gs_memory_struct_type_t st_refs =
537dd7cddfSDavid du Colombier {sizeof(ref), "refs", &refs_shared_procs, refs_clear_marks, 0, refs_do_reloc};
547dd7cddfSDavid du Colombier 
557dd7cddfSDavid du Colombier /*
567dd7cddfSDavid du Colombier  * Define the GC procedures for structs that actually contain refs.
577dd7cddfSDavid du Colombier  * These are special because the shared refs_* procedures
587dd7cddfSDavid du Colombier  * are never called.  Instead, we unmark the individual refs in clear_marks,
597dd7cddfSDavid du Colombier  * disregard refs_*_reloc (because we will never relocate a ptr_ref_type
607dd7cddfSDavid du Colombier  * pointer pointing into the structure), disregard refs_compact (because
617dd7cddfSDavid du Colombier  * compaction is never required), and remove the marks in reloc_ptrs.
627dd7cddfSDavid du Colombier  * See also the comment about ptr_ref_type in imemory.h.
637dd7cddfSDavid du Colombier  */
CLEAR_MARKS_PROC(ref_struct_clear_marks)647dd7cddfSDavid du Colombier CLEAR_MARKS_PROC(ref_struct_clear_marks)
657dd7cddfSDavid du Colombier {
667dd7cddfSDavid du Colombier     ref *pref = (ref *) vptr;
677dd7cddfSDavid du Colombier     ref *end = (ref *) ((char *)vptr + size);
687dd7cddfSDavid du Colombier 
697dd7cddfSDavid du Colombier     for (; pref < end; pref++)
707dd7cddfSDavid du Colombier 	r_clear_attrs(pref, l_mark);
717dd7cddfSDavid du Colombier }
ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)727dd7cddfSDavid du Colombier ENUM_PTRS_BEGIN_PROC(ref_struct_enum_ptrs)
737dd7cddfSDavid du Colombier {
747dd7cddfSDavid du Colombier     if (index >= size / sizeof(ref))
757dd7cddfSDavid du Colombier 	return 0;
767dd7cddfSDavid du Colombier     pep->ptr = (const ref *)vptr + index;
777dd7cddfSDavid du Colombier     return ptr_ref_type;
787dd7cddfSDavid du Colombier     ENUM_PTRS_END_PROC
797dd7cddfSDavid du Colombier }
RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)807dd7cddfSDavid du Colombier RELOC_PTRS_BEGIN(ref_struct_reloc_ptrs)
817dd7cddfSDavid du Colombier {
82*593dc095SDavid du Colombier     vm_spaces spaces = gcst->spaces;
83*593dc095SDavid du Colombier     const gs_memory_t *cmem = space_system->stable_memory;
84*593dc095SDavid du Colombier 
857dd7cddfSDavid du Colombier     ref *beg = vptr;
867dd7cddfSDavid du Colombier     ref *end = (ref *) ((char *)vptr + size);
877dd7cddfSDavid du Colombier 
887dd7cddfSDavid du Colombier     igc_reloc_refs((ref_packed *) beg, (ref_packed *) end, gcst);
89*593dc095SDavid du Colombier     ref_struct_clear_marks(cmem, vptr, size, pstype);
907dd7cddfSDavid du Colombier } RELOC_PTRS_END
917dd7cddfSDavid du Colombier 
927dd7cddfSDavid du Colombier /* ------ Unmarking phase ------ */
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier /* Unmark a single ref. */
957dd7cddfSDavid du Colombier void
ptr_ref_unmark(enum_ptr_t * pep,gc_state_t * ignored)967dd7cddfSDavid du Colombier ptr_ref_unmark(enum_ptr_t *pep, gc_state_t * ignored)
977dd7cddfSDavid du Colombier {
987dd7cddfSDavid du Colombier     ref_packed *rpp = (ref_packed *)pep->ptr;
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier     if (r_is_packed(rpp))
1017dd7cddfSDavid du Colombier 	r_clear_pmark(rpp);
1027dd7cddfSDavid du Colombier     else
1037dd7cddfSDavid du Colombier 	r_clear_attrs((ref *)rpp, l_mark);
1047dd7cddfSDavid du Colombier }
1057dd7cddfSDavid du Colombier 
1067dd7cddfSDavid du Colombier /* Unmarking routine for ref objects. */
1077dd7cddfSDavid du Colombier private void
refs_clear_marks(const gs_memory_t * cmem,void * vptr,uint size,const gs_memory_struct_type_t * pstype)108*593dc095SDavid du Colombier refs_clear_marks(const gs_memory_t *cmem,
109*593dc095SDavid du Colombier 		 void /*obj_header_t */ *vptr, uint size,
1107dd7cddfSDavid du Colombier 		 const gs_memory_struct_type_t * pstype)
1117dd7cddfSDavid du Colombier {
1127dd7cddfSDavid du Colombier     ref_packed *rp = (ref_packed *) vptr;
1137dd7cddfSDavid du Colombier     ref_packed *end = (ref_packed *) ((byte *) vptr + size);
1147dd7cddfSDavid du Colombier 
1157dd7cddfSDavid du Colombier     /* Since the last ref is full-size, we only need to check for */
1167dd7cddfSDavid du Colombier     /* the end of the block when we see one of those. */
1177dd7cddfSDavid du Colombier     for (;;) {
1187dd7cddfSDavid du Colombier 	if (r_is_packed(rp)) {
1197dd7cddfSDavid du Colombier #ifdef DEBUG
1207dd7cddfSDavid du Colombier 	    if (gs_debug_c('8')) {
1217dd7cddfSDavid du Colombier 		dlprintf1("  [8]unmark packed 0x%lx ", (ulong) rp);
122*593dc095SDavid du Colombier 		debug_print_ref(cmem, (const ref *)rp);
1237dd7cddfSDavid du Colombier 		dputs("\n");
1247dd7cddfSDavid du Colombier 	    }
1257dd7cddfSDavid du Colombier #endif
1267dd7cddfSDavid du Colombier 	    r_clear_pmark(rp);
1277dd7cddfSDavid du Colombier 	    rp++;
1287dd7cddfSDavid du Colombier 	} else {		/* full-size ref */
1297dd7cddfSDavid du Colombier 	    ref *const pref = (ref *)rp;
1307dd7cddfSDavid du Colombier 
1317dd7cddfSDavid du Colombier #ifdef DEBUG
1327dd7cddfSDavid du Colombier 	    if (gs_debug_c('8')) {
1337dd7cddfSDavid du Colombier 		dlprintf1("  [8]unmark ref 0x%lx ", (ulong) rp);
134*593dc095SDavid du Colombier 		debug_print_ref(cmem, pref);
1357dd7cddfSDavid du Colombier 		dputs("\n");
1367dd7cddfSDavid du Colombier 	    }
1377dd7cddfSDavid du Colombier #endif
1387dd7cddfSDavid du Colombier 	    r_clear_attrs(pref, l_mark);
1397dd7cddfSDavid du Colombier 	    rp += packed_per_ref;
1407dd7cddfSDavid du Colombier 	    if (rp >= (ref_packed *) end)
1417dd7cddfSDavid du Colombier 		break;
1427dd7cddfSDavid du Colombier 	}
1437dd7cddfSDavid du Colombier     }
1447dd7cddfSDavid du Colombier }
1457dd7cddfSDavid du Colombier 
1467dd7cddfSDavid du Colombier /* ------ Marking phase ------ */
1477dd7cddfSDavid du Colombier 
1487dd7cddfSDavid du Colombier /* Mark a ref.  Return true if new mark. */
1497dd7cddfSDavid du Colombier bool
ptr_ref_mark(enum_ptr_t * pep,gc_state_t * ignored)1507dd7cddfSDavid du Colombier ptr_ref_mark(enum_ptr_t *pep, gc_state_t * ignored)
1517dd7cddfSDavid du Colombier {
1527dd7cddfSDavid du Colombier     ref_packed *rpp = (void *)pep->ptr;
1537dd7cddfSDavid du Colombier 
1547dd7cddfSDavid du Colombier     if (r_is_packed(rpp)) {
1557dd7cddfSDavid du Colombier 	if (r_has_pmark(rpp))
1567dd7cddfSDavid du Colombier 	    return false;
1577dd7cddfSDavid du Colombier 	r_set_pmark(rpp);
1587dd7cddfSDavid du Colombier     } else {
1597dd7cddfSDavid du Colombier 	ref *const pref = (ref *)rpp;
1607dd7cddfSDavid du Colombier 
1617dd7cddfSDavid du Colombier 	if (r_has_attr(pref, l_mark))
1627dd7cddfSDavid du Colombier 	    return false;
1637dd7cddfSDavid du Colombier 	r_set_attrs(pref, l_mark);
1647dd7cddfSDavid du Colombier     }
1657dd7cddfSDavid du Colombier     return true;
1667dd7cddfSDavid du Colombier }
1677dd7cddfSDavid du Colombier 
1687dd7cddfSDavid du Colombier /* ------ Relocation planning phase ------ */
1697dd7cddfSDavid du Colombier 
1707dd7cddfSDavid du Colombier /*
1717dd7cddfSDavid du Colombier  * We store relocation in the size field of refs that don't use it,
1727dd7cddfSDavid du Colombier  * so that we don't have to scan all the way to an unmarked object.
1737dd7cddfSDavid du Colombier  * We must avoid nulls, which sometimes have useful information
1747dd7cddfSDavid du Colombier  * in their size fields, and the types above t_next_index, which are
1757dd7cddfSDavid du Colombier  * actually operators in disguise and also use the size field.
1767dd7cddfSDavid du Colombier  */
1777dd7cddfSDavid du Colombier 
1787dd7cddfSDavid du Colombier /* Clear the relocation for a ref object. */
1797dd7cddfSDavid du Colombier private void
refs_clear_reloc(obj_header_t * hdr,uint size)1807dd7cddfSDavid du Colombier refs_clear_reloc(obj_header_t *hdr, uint size)
1817dd7cddfSDavid du Colombier {
1827dd7cddfSDavid du Colombier     ref_packed *rp = (ref_packed *) (hdr + 1);
1837dd7cddfSDavid du Colombier     ref_packed *end = (ref_packed *) ((byte *) rp + size);
1847dd7cddfSDavid du Colombier 
1857dd7cddfSDavid du Colombier     while (rp < end) {
1867dd7cddfSDavid du Colombier 	if (r_is_packed(rp))
1877dd7cddfSDavid du Colombier 	    rp++;
1887dd7cddfSDavid du Colombier 	else {
1897dd7cddfSDavid du Colombier 	    /* Full-size ref.  Store the relocation here if possible. */
1907dd7cddfSDavid du Colombier 	    ref *const pref = (ref *)rp;
1917dd7cddfSDavid du Colombier 
1927dd7cddfSDavid du Colombier 	    if (!ref_type_uses_size_or_null(r_type(pref))) {
1937dd7cddfSDavid du Colombier 		if_debug1('8', "  [8]clearing reloc at 0x%lx\n", (ulong) rp);
1947dd7cddfSDavid du Colombier 		r_set_size(pref, 0);
1957dd7cddfSDavid du Colombier 	    }
1967dd7cddfSDavid du Colombier 	    rp += packed_per_ref;
1977dd7cddfSDavid du Colombier 	}
1987dd7cddfSDavid du Colombier     }
1997dd7cddfSDavid du Colombier }
2007dd7cddfSDavid du Colombier 
2017dd7cddfSDavid du Colombier /* Set the relocation for a ref object. */
2027dd7cddfSDavid du Colombier private bool
refs_set_reloc(obj_header_t * hdr,uint reloc,uint size)2037dd7cddfSDavid du Colombier refs_set_reloc(obj_header_t * hdr, uint reloc, uint size)
2047dd7cddfSDavid du Colombier {
2057dd7cddfSDavid du Colombier     ref_packed *rp = (ref_packed *) (hdr + 1);
2067dd7cddfSDavid du Colombier     ref_packed *end = (ref_packed *) ((byte *) rp + size);
2077dd7cddfSDavid du Colombier     uint freed = 0;
2087dd7cddfSDavid du Colombier 
2097dd7cddfSDavid du Colombier     /*
2107dd7cddfSDavid du Colombier      * We have to be careful to keep refs aligned properly.
2117dd7cddfSDavid du Colombier      * For the moment, we do this by either keeping or discarding
2127dd7cddfSDavid du Colombier      * an entire (aligned) block of align_packed_per_ref packed elements
2137dd7cddfSDavid du Colombier      * as a unit.  We know that align_packed_per_ref <= packed_per_ref,
2147dd7cddfSDavid du Colombier      * and we also know that packed refs are always allocated in blocks
2157dd7cddfSDavid du Colombier      * of align_packed_per_ref, so this makes things relatively easy.
2167dd7cddfSDavid du Colombier      */
2177dd7cddfSDavid du Colombier     while (rp < end) {
2187dd7cddfSDavid du Colombier 	if (r_is_packed(rp)) {
2197dd7cddfSDavid du Colombier #if align_packed_per_ref == 1
2207dd7cddfSDavid du Colombier 	    if (r_has_pmark(rp)) {
2217dd7cddfSDavid du Colombier 		if_debug1('8',
2227dd7cddfSDavid du Colombier 			  "  [8]packed ref 0x%lx is marked\n",
2237dd7cddfSDavid du Colombier 			  (ulong) rp);
2247dd7cddfSDavid du Colombier 		rp++;
2257dd7cddfSDavid du Colombier 	    } else {
2267dd7cddfSDavid du Colombier #else
2277dd7cddfSDavid du Colombier 	    int i;
2287dd7cddfSDavid du Colombier 
2297dd7cddfSDavid du Colombier 	    /*
2307dd7cddfSDavid du Colombier 	     * Note: align_packed_per_ref is typically
2317dd7cddfSDavid du Colombier 	     * 2 or 4 for 32-bit processors.
2327dd7cddfSDavid du Colombier 	     */
2337dd7cddfSDavid du Colombier #define all_marked (align_packed_per_ref * lp_mark)
2347dd7cddfSDavid du Colombier # if align_packed_per_ref == 2
2357dd7cddfSDavid du Colombier #  if arch_sizeof_int == arch_sizeof_short * 2
2367dd7cddfSDavid du Colombier #    undef all_marked
2377dd7cddfSDavid du Colombier #    define all_marked ( (lp_mark << (sizeof(short) * 8)) + lp_mark )
2387dd7cddfSDavid du Colombier #    define marked (*(int *)rp & all_marked)
2397dd7cddfSDavid du Colombier #  else
2407dd7cddfSDavid du Colombier #    define marked ((*rp & lp_mark) + (rp[1] & lp_mark))
2417dd7cddfSDavid du Colombier #  endif
2427dd7cddfSDavid du Colombier # else
2437dd7cddfSDavid du Colombier #  if align_packed_per_ref == 4
2447dd7cddfSDavid du Colombier #    define marked ((*rp & lp_mark) + (rp[1] & lp_mark) +\
2457dd7cddfSDavid du Colombier 		    (rp[2] & lp_mark) + (rp[3] & lp_mark))
2467dd7cddfSDavid du Colombier #  else
2477dd7cddfSDavid du Colombier 	    /*
2487dd7cddfSDavid du Colombier 	     * The value of marked is logically a uint, not an int:
2497dd7cddfSDavid du Colombier 	     * we declare it as int only to avoid a compiler warning
2507dd7cddfSDavid du Colombier 	     * message about using a non-int value in a switch statement.
2517dd7cddfSDavid du Colombier 	     */
2527dd7cddfSDavid du Colombier 	    int marked = *rp & lp_mark;
2537dd7cddfSDavid du Colombier 
2547dd7cddfSDavid du Colombier 	    for (i = 1; i < align_packed_per_ref; i++)
2557dd7cddfSDavid du Colombier 		marked += rp[i] & lp_mark;
2567dd7cddfSDavid du Colombier #  endif
2577dd7cddfSDavid du Colombier # endif
2587dd7cddfSDavid du Colombier 	    /*
2597dd7cddfSDavid du Colombier 	     * Now marked is lp_mark * the number of marked
2607dd7cddfSDavid du Colombier 	     * packed refs in the aligned block, except for
2617dd7cddfSDavid du Colombier 	     * a couple of special cases above.
2627dd7cddfSDavid du Colombier 	     */
2637dd7cddfSDavid du Colombier 	    switch (marked) {
2647dd7cddfSDavid du Colombier 		case all_marked:
2657dd7cddfSDavid du Colombier 		    if_debug2('8',
2667dd7cddfSDavid du Colombier 			      "  [8]packed refs 0x%lx..0x%lx are marked\n",
2677dd7cddfSDavid du Colombier 			      (ulong) rp,
2687dd7cddfSDavid du Colombier 			      (ulong) (rp + (align_packed_per_ref - 1)));
2697dd7cddfSDavid du Colombier 		    rp += align_packed_per_ref;
2707dd7cddfSDavid du Colombier 		    break;
2717dd7cddfSDavid du Colombier 		default:
2727dd7cddfSDavid du Colombier 		    /* At least one packed ref in the block */
2737dd7cddfSDavid du Colombier 		    /* is marked: Keep the whole block. */
2747dd7cddfSDavid du Colombier 		    for (i = align_packed_per_ref; i--; rp++) {
2757dd7cddfSDavid du Colombier 			r_set_pmark(rp);
2767dd7cddfSDavid du Colombier 			if_debug1('8',
2777dd7cddfSDavid du Colombier 				  "  [8]packed ref 0x%lx is marked\n",
2787dd7cddfSDavid du Colombier 				  (ulong) rp);
2797dd7cddfSDavid du Colombier 		    }
2807dd7cddfSDavid du Colombier 		    break;
2817dd7cddfSDavid du Colombier 		case 0:
2827dd7cddfSDavid du Colombier #endif
2837dd7cddfSDavid du Colombier 		    if_debug2('8', "  [8]%d packed ref(s) at 0x%lx are unmarked\n",
2847dd7cddfSDavid du Colombier 			      align_packed_per_ref, (ulong) rp);
2857dd7cddfSDavid du Colombier 		    {
2867dd7cddfSDavid du Colombier 			uint rel = reloc + freed;
2877dd7cddfSDavid du Colombier 
2887dd7cddfSDavid du Colombier 			/* Change this to an integer so we can */
2897dd7cddfSDavid du Colombier 			/* store the relocation here. */
2907dd7cddfSDavid du Colombier 			*rp = pt_tag(pt_integer) +
2917dd7cddfSDavid du Colombier 			    min(rel, packed_max_value);
2927dd7cddfSDavid du Colombier 		    }
2937dd7cddfSDavid du Colombier 		    rp += align_packed_per_ref;
2947dd7cddfSDavid du Colombier 		    freed += sizeof(ref_packed) * align_packed_per_ref;
2957dd7cddfSDavid du Colombier 	    }
2967dd7cddfSDavid du Colombier 	} else {		/* full-size ref */
2977dd7cddfSDavid du Colombier 	    uint rel = reloc + freed;
2987dd7cddfSDavid du Colombier 
2997dd7cddfSDavid du Colombier 	    /* The following assignment is logically */
3007dd7cddfSDavid du Colombier 	    /* unnecessary; we do it only for convenience */
3017dd7cddfSDavid du Colombier 	    /* in debugging. */
3027dd7cddfSDavid du Colombier 	    ref *pref = (ref *) rp;
3037dd7cddfSDavid du Colombier 
3047dd7cddfSDavid du Colombier 	    if (!r_has_attr(pref, l_mark)) {
3057dd7cddfSDavid du Colombier 		if_debug1('8', "  [8]ref 0x%lx is unmarked\n",
3067dd7cddfSDavid du Colombier 			  (ulong) pref);
3077dd7cddfSDavid du Colombier 		/* Change this to a mark so we can */
3087dd7cddfSDavid du Colombier 		/* store the relocation. */
3097dd7cddfSDavid du Colombier 		r_set_type(pref, t_mark);
3107dd7cddfSDavid du Colombier 		r_set_size(pref, rel);
3117dd7cddfSDavid du Colombier 		freed += sizeof(ref);
3127dd7cddfSDavid du Colombier 	    } else {
3137dd7cddfSDavid du Colombier 		if_debug1('8', "  [8]ref 0x%lx is marked\n",
3147dd7cddfSDavid du Colombier 			  (ulong) pref);
3157dd7cddfSDavid du Colombier 		/* Store the relocation here if possible. */
3167dd7cddfSDavid du Colombier 		if (!ref_type_uses_size_or_null(r_type(pref))) {
3177dd7cddfSDavid du Colombier 		    if_debug2('8', "  [8]storing reloc %u at 0x%lx\n",
3187dd7cddfSDavid du Colombier 			      rel, (ulong) pref);
3197dd7cddfSDavid du Colombier 		    r_set_size(pref, rel);
3207dd7cddfSDavid du Colombier 		}
3217dd7cddfSDavid du Colombier 	    }
3227dd7cddfSDavid du Colombier 	    rp += packed_per_ref;
3237dd7cddfSDavid du Colombier 	}
3247dd7cddfSDavid du Colombier     }
3257dd7cddfSDavid du Colombier     if_debug3('7', " [7]at end of refs 0x%lx, size = %u, freed = %u\n",
3267dd7cddfSDavid du Colombier 	      (ulong) (hdr + 1), size, freed);
3277dd7cddfSDavid du Colombier     if (freed == size)
3287dd7cddfSDavid du Colombier 	return false;
3297dd7cddfSDavid du Colombier #if arch_sizeof_int > arch_sizeof_short
3307dd7cddfSDavid du Colombier     /*
3317dd7cddfSDavid du Colombier      * If the final relocation can't fit in the r_size field
3327dd7cddfSDavid du Colombier      * (which can't happen if the object shares a chunk with
3337dd7cddfSDavid du Colombier      * any other objects, so we know reloc = 0 in this case),
3347dd7cddfSDavid du Colombier      * we have to keep the entire object unless there are no
3357dd7cddfSDavid du Colombier      * references to any ref in it.
3367dd7cddfSDavid du Colombier      */
3377dd7cddfSDavid du Colombier     if (freed <= max_ushort)
3387dd7cddfSDavid du Colombier 	return true;
3397dd7cddfSDavid du Colombier     /*
3407dd7cddfSDavid du Colombier      * We have to mark all surviving refs, but we also must
3417dd7cddfSDavid du Colombier      * overwrite any non-surviving refs with something that
3427dd7cddfSDavid du Colombier      * doesn't contain any pointers.
3437dd7cddfSDavid du Colombier      */
3447dd7cddfSDavid du Colombier     rp = (ref_packed *) (hdr + 1);
3457dd7cddfSDavid du Colombier     while (rp < end) {
3467dd7cddfSDavid du Colombier 	if (r_is_packed(rp)) {
3477dd7cddfSDavid du Colombier 	    if (!r_has_pmark(rp))
3487dd7cddfSDavid du Colombier 		*rp = pt_tag(pt_integer) | lp_mark;
3497dd7cddfSDavid du Colombier 	    ++rp;
3507dd7cddfSDavid du Colombier 	} else {		/* The following assignment is logically */
3517dd7cddfSDavid du Colombier 	    /* unnecessary; we do it only for convenience */
3527dd7cddfSDavid du Colombier 	    /* in debugging. */
3537dd7cddfSDavid du Colombier 	    ref *pref = (ref *) rp;
3547dd7cddfSDavid du Colombier 
3557dd7cddfSDavid du Colombier 	    if (!r_has_attr(pref, l_mark)) {
3567dd7cddfSDavid du Colombier 		r_set_type_attrs(pref, t_mark, l_mark);
3577dd7cddfSDavid du Colombier 		r_set_size(pref, reloc);
3587dd7cddfSDavid du Colombier 	    } else {
3597dd7cddfSDavid du Colombier 		if (!ref_type_uses_size_or_null(r_type(pref)))
3607dd7cddfSDavid du Colombier 		    r_set_size(pref, reloc);
3617dd7cddfSDavid du Colombier 	    }
3627dd7cddfSDavid du Colombier 	    rp += packed_per_ref;
3637dd7cddfSDavid du Colombier 	}
3647dd7cddfSDavid du Colombier     }
3657dd7cddfSDavid du Colombier     /* The last ref has to remain unmarked. */
3667dd7cddfSDavid du Colombier     r_clear_attrs((ref *) rp - 1, l_mark);
3677dd7cddfSDavid du Colombier #endif
3687dd7cddfSDavid du Colombier     return true;
3697dd7cddfSDavid du Colombier }
3707dd7cddfSDavid du Colombier 
3717dd7cddfSDavid du Colombier /* ------ Relocation phase ------ */
3727dd7cddfSDavid du Colombier 
3737dd7cddfSDavid du Colombier /* Relocate all the pointers in a block of refs. */
3747dd7cddfSDavid du Colombier private void
3757dd7cddfSDavid du Colombier refs_do_reloc(void /*obj_header_t */ *vptr, uint size,
3767dd7cddfSDavid du Colombier 	      const gs_memory_struct_type_t * pstype, gc_state_t * gcst)
3777dd7cddfSDavid du Colombier {
3787dd7cddfSDavid du Colombier     igc_reloc_refs((ref_packed *) vptr,
3797dd7cddfSDavid du Colombier 		   (ref_packed *) ((char *)vptr + size),
3807dd7cddfSDavid du Colombier 		   gcst);
3817dd7cddfSDavid du Colombier }
3827dd7cddfSDavid du Colombier /* Relocate the contents of a block of refs. */
3837dd7cddfSDavid du Colombier /* If gcst->relocating_untraced is true, we are relocating pointers from an */
3847dd7cddfSDavid du Colombier /* untraced space, so relocate all refs, not just marked ones. */
3857dd7cddfSDavid du Colombier void
3867dd7cddfSDavid du Colombier igc_reloc_refs(ref_packed * from, ref_packed * to, gc_state_t * gcst)
3877dd7cddfSDavid du Colombier {
3887dd7cddfSDavid du Colombier     int min_trace = gcst->min_collect;
3897dd7cddfSDavid du Colombier     ref_packed *rp = from;
3907dd7cddfSDavid du Colombier     bool do_all = gcst->relocating_untraced;
3917dd7cddfSDavid du Colombier 
392*593dc095SDavid du Colombier     vm_spaces spaces = gcst->spaces;
393*593dc095SDavid du Colombier     const gs_memory_t *cmem = space_system->stable_memory;
394*593dc095SDavid du Colombier 
3957dd7cddfSDavid du Colombier     while (rp < to) {
3967dd7cddfSDavid du Colombier 	ref *pref;
3977dd7cddfSDavid du Colombier #ifdef DEBUG
3987dd7cddfSDavid du Colombier 	const void *before = 0;
3997dd7cddfSDavid du Colombier 	const void *after = 0;
4007dd7cddfSDavid du Colombier # define DO_RELOC(var, stat)\
4017dd7cddfSDavid du Colombier     BEGIN before = (var); stat; after = (var); END
4027dd7cddfSDavid du Colombier # define SET_RELOC(var, expr)\
4037dd7cddfSDavid du Colombier     BEGIN before = (var); after = (var) = (expr); END
4047dd7cddfSDavid du Colombier #else
4057dd7cddfSDavid du Colombier # define DO_RELOC(var, stat) stat
4067dd7cddfSDavid du Colombier # define SET_RELOC(var, expr) var = expr
4077dd7cddfSDavid du Colombier #endif
4087dd7cddfSDavid du Colombier 
4097dd7cddfSDavid du Colombier 	if (r_is_packed(rp)) {
4107dd7cddfSDavid du Colombier 	    rp++;
4117dd7cddfSDavid du Colombier 	    continue;
4127dd7cddfSDavid du Colombier 	}
4137dd7cddfSDavid du Colombier 	/* The following assignment is logically unnecessary; */
4147dd7cddfSDavid du Colombier 	/* we do it only for convenience in debugging. */
4157dd7cddfSDavid du Colombier 	pref = (ref *) rp;
4167dd7cddfSDavid du Colombier 	if_debug3('8', "  [8]relocating %s %d ref at 0x%lx",
4177dd7cddfSDavid du Colombier 		  (r_has_attr(pref, l_mark) ? "marked" : "unmarked"),
4187dd7cddfSDavid du Colombier 		  r_btype(pref), (ulong) pref);
4197dd7cddfSDavid du Colombier 	if ((r_has_attr(pref, l_mark) || do_all) &&
4207dd7cddfSDavid du Colombier 	    r_space(pref) >= min_trace
4217dd7cddfSDavid du Colombier 	    ) {
4227dd7cddfSDavid du Colombier 	    switch (r_type(pref)) {
4237dd7cddfSDavid du Colombier 		    /* Struct cases */
4247dd7cddfSDavid du Colombier 		case t_file:
4257dd7cddfSDavid du Colombier 		    DO_RELOC(pref->value.pfile, RELOC_VAR(pref->value.pfile));
4267dd7cddfSDavid du Colombier 		    break;
4277dd7cddfSDavid du Colombier 		case t_device:
4287dd7cddfSDavid du Colombier 		    DO_RELOC(pref->value.pdevice,
4297dd7cddfSDavid du Colombier 			     RELOC_VAR(pref->value.pdevice));
4307dd7cddfSDavid du Colombier 		    break;
4317dd7cddfSDavid du Colombier 		case t_fontID:
4327dd7cddfSDavid du Colombier 		case t_struct:
4337dd7cddfSDavid du Colombier 		case t_astruct:
4347dd7cddfSDavid du Colombier 		    DO_RELOC(pref->value.pstruct,
4357dd7cddfSDavid du Colombier 			     RELOC_VAR(pref->value.pstruct));
4367dd7cddfSDavid du Colombier 		    break;
4377dd7cddfSDavid du Colombier 		    /* Non-trivial non-struct cases */
4387dd7cddfSDavid du Colombier 		case t_dictionary:
4397dd7cddfSDavid du Colombier 		    rputc('d');
4407dd7cddfSDavid du Colombier 		    SET_RELOC(pref->value.pdict,
4417dd7cddfSDavid du Colombier 			      (dict *)igc_reloc_ref_ptr((ref_packed *)pref->value.pdict, gcst));
4427dd7cddfSDavid du Colombier 		    break;
4437dd7cddfSDavid du Colombier 		case t_array:
4447dd7cddfSDavid du Colombier 		    {
4457dd7cddfSDavid du Colombier 			uint size = r_size(pref);
4467dd7cddfSDavid du Colombier 
4477dd7cddfSDavid du Colombier 			if (size != 0) {	/* value.refs might be NULL */
4487dd7cddfSDavid du Colombier 
4497dd7cddfSDavid du Colombier 			    /*
4507dd7cddfSDavid du Colombier 			     * If the array is large, we allocated it in its
4517dd7cddfSDavid du Colombier 			     * own object (at least originally -- this might
4527dd7cddfSDavid du Colombier 			     * be a pointer to a subarray.)  In this case,
4537dd7cddfSDavid du Colombier 			     * we know it is the only object in its
4547dd7cddfSDavid du Colombier 			     * containing st_refs object, so we know that
4557dd7cddfSDavid du Colombier 			     * the mark containing the relocation appears
4567dd7cddfSDavid du Colombier 			     * just after it.
4577dd7cddfSDavid du Colombier 			     */
4587dd7cddfSDavid du Colombier 			    if (size < max_size_st_refs / sizeof(ref)) {
4597dd7cddfSDavid du Colombier 				rputc('a');
4607dd7cddfSDavid du Colombier 				SET_RELOC(pref->value.refs,
4617dd7cddfSDavid du Colombier 				    (ref *) igc_reloc_ref_ptr(
4627dd7cddfSDavid du Colombier 				     (ref_packed *) pref->value.refs, gcst));
4637dd7cddfSDavid du Colombier 			    } else {
4647dd7cddfSDavid du Colombier 				rputc('A');
4657dd7cddfSDavid du Colombier 				/*
4667dd7cddfSDavid du Colombier 				 * See the t_shortarray case below for why we
4677dd7cddfSDavid du Colombier 				 * decrement size.
4687dd7cddfSDavid du Colombier 				 */
4697dd7cddfSDavid du Colombier 				--size;
4707dd7cddfSDavid du Colombier 				SET_RELOC(pref->value.refs,
4717dd7cddfSDavid du Colombier 				    (ref *) igc_reloc_ref_ptr(
4727dd7cddfSDavid du Colombier 				   (ref_packed *) (pref->value.refs + size),
4737dd7cddfSDavid du Colombier 							       gcst) - size);
4747dd7cddfSDavid du Colombier 			    }
4757dd7cddfSDavid du Colombier 			}
4767dd7cddfSDavid du Colombier 		    }
4777dd7cddfSDavid du Colombier 		    break;
4787dd7cddfSDavid du Colombier 		case t_mixedarray:
4797dd7cddfSDavid du Colombier 		    if (r_size(pref) != 0) {	/* value.refs might be NULL */
4807dd7cddfSDavid du Colombier 			rputc('m');
4817dd7cddfSDavid du Colombier 			SET_RELOC(pref->value.packed,
4827dd7cddfSDavid du Colombier 				  igc_reloc_ref_ptr(pref->value.packed, gcst));
4837dd7cddfSDavid du Colombier 		    }
4847dd7cddfSDavid du Colombier 		    break;
4857dd7cddfSDavid du Colombier 		case t_shortarray:
4867dd7cddfSDavid du Colombier 		    {
4877dd7cddfSDavid du Colombier 			uint size = r_size(pref);
4887dd7cddfSDavid du Colombier 
4897dd7cddfSDavid du Colombier 			/*
4907dd7cddfSDavid du Colombier 			 * Since we know that igc_reloc_ref_ptr works by
4917dd7cddfSDavid du Colombier 			 * scanning forward, and we know that all the
4927dd7cddfSDavid du Colombier 			 * elements of this array itself are marked, we can
4937dd7cddfSDavid du Colombier 			 * save some scanning time by relocating the pointer
4947dd7cddfSDavid du Colombier 			 * to the end of the array rather than the
4957dd7cddfSDavid du Colombier 			 * beginning.
4967dd7cddfSDavid du Colombier 			 */
4977dd7cddfSDavid du Colombier 			if (size != 0) {	/* value.refs might be NULL */
4987dd7cddfSDavid du Colombier 			    rputc('s');
4997dd7cddfSDavid du Colombier 			    /*
5007dd7cddfSDavid du Colombier 			     * igc_reloc_ref_ptr has to be able to determine
5017dd7cddfSDavid du Colombier 			     * whether the pointer points into a space that
5027dd7cddfSDavid du Colombier 			     * isn't being collected.  It does this by
5037dd7cddfSDavid du Colombier 			     * checking whether the referent of the pointer
5047dd7cddfSDavid du Colombier 			     * is marked.  For this reason, we have to pass
5057dd7cddfSDavid du Colombier 			     * a pointer to the last real element of the
5067dd7cddfSDavid du Colombier 			     * array, rather than just beyond it.
5077dd7cddfSDavid du Colombier 			     */
5087dd7cddfSDavid du Colombier 			    --size;
5097dd7cddfSDavid du Colombier 			    SET_RELOC(pref->value.packed,
5107dd7cddfSDavid du Colombier 				igc_reloc_ref_ptr(pref->value.packed + size,
5117dd7cddfSDavid du Colombier 						  gcst) - size);
5127dd7cddfSDavid du Colombier 			}
5137dd7cddfSDavid du Colombier 		    }
5147dd7cddfSDavid du Colombier 		    break;
5157dd7cddfSDavid du Colombier 		case t_name:
5167dd7cddfSDavid du Colombier 		    {
517*593dc095SDavid du Colombier 			void *psub = name_ref_sub_table(cmem, pref);
5187dd7cddfSDavid du Colombier 			void *rsub = RELOC_OBJ(psub); /* gcst implicit */
5197dd7cddfSDavid du Colombier 
5207dd7cddfSDavid du Colombier 			SET_RELOC(pref->value.pname,
5217dd7cddfSDavid du Colombier 				  (name *)
5227dd7cddfSDavid du Colombier 				  ((char *)rsub + ((char *)pref->value.pname -
5237dd7cddfSDavid du Colombier 						   (char *)psub)));
5247dd7cddfSDavid du Colombier 		    } break;
5257dd7cddfSDavid du Colombier 		case t_string:
5267dd7cddfSDavid du Colombier 		    {
5277dd7cddfSDavid du Colombier 			gs_string str;
5287dd7cddfSDavid du Colombier 
5297dd7cddfSDavid du Colombier 			str.data = pref->value.bytes;
5307dd7cddfSDavid du Colombier 			str.size = r_size(pref);
5317dd7cddfSDavid du Colombier 
5327dd7cddfSDavid du Colombier 			DO_RELOC(str.data, RELOC_STRING_VAR(str));
5337dd7cddfSDavid du Colombier 			pref->value.bytes = str.data;
5347dd7cddfSDavid du Colombier 		    }
5357dd7cddfSDavid du Colombier 		    break;
5367dd7cddfSDavid du Colombier 		case t_oparray:
5377dd7cddfSDavid du Colombier 		    rputc('o');
5387dd7cddfSDavid du Colombier 		    SET_RELOC(pref->value.const_refs,
5397dd7cddfSDavid du Colombier 			(const ref *)igc_reloc_ref_ptr((const ref_packed *)pref->value.const_refs, gcst));
5407dd7cddfSDavid du Colombier 		    break;
5417dd7cddfSDavid du Colombier 		default:
5427dd7cddfSDavid du Colombier 		    goto no_reloc; /* don't print trace message */
5437dd7cddfSDavid du Colombier 	    }
5447dd7cddfSDavid du Colombier 	    if_debug2('8', ", 0x%lx => 0x%lx", (ulong)before, (ulong)after);
5457dd7cddfSDavid du Colombier 	}
5467dd7cddfSDavid du Colombier no_reloc:
5477dd7cddfSDavid du Colombier 	if_debug0('8', "\n");
5487dd7cddfSDavid du Colombier 	rp += packed_per_ref;
5497dd7cddfSDavid du Colombier     }
5507dd7cddfSDavid du Colombier }
5517dd7cddfSDavid du Colombier 
5527dd7cddfSDavid du Colombier /* Relocate a pointer to a ref. */
5537dd7cddfSDavid du Colombier /* See gsmemory.h for why the argument is const and the result is not. */
5547dd7cddfSDavid du Colombier ref_packed *
555*593dc095SDavid du Colombier igc_reloc_ref_ptr(const ref_packed * prp, gc_state_t *gcst)
5567dd7cddfSDavid du Colombier {
5577dd7cddfSDavid du Colombier     /*
5587dd7cddfSDavid du Colombier      * Search forward for relocation.  This algorithm is intrinsically very
5597dd7cddfSDavid du Colombier      * inefficient; we hope eventually to replace it with a better one.
5607dd7cddfSDavid du Colombier      */
5617dd7cddfSDavid du Colombier     const ref_packed *rp = prp;
5627dd7cddfSDavid du Colombier     uint dec = 0;
5637dd7cddfSDavid du Colombier #ifdef ALIGNMENT_ALIASING_BUG
5647dd7cddfSDavid du Colombier     const ref *rpref;
5657dd7cddfSDavid du Colombier # define RP_REF(rp) (rpref = (const ref *)rp, rpref)
5667dd7cddfSDavid du Colombier #else
5677dd7cddfSDavid du Colombier # define RP_REF(rp) ((const ref *)rp)
5687dd7cddfSDavid du Colombier #endif
5697dd7cddfSDavid du Colombier     /*
5707dd7cddfSDavid du Colombier      * Iff this pointer points into a space that wasn't traced,
5717dd7cddfSDavid du Colombier      * the referent won't be marked.  In this case, we shouldn't
5727dd7cddfSDavid du Colombier      * do any relocation.  Check for this first.
5737dd7cddfSDavid du Colombier      */
5747dd7cddfSDavid du Colombier     if (r_is_packed(rp)) {
5757dd7cddfSDavid du Colombier 	if (!r_has_pmark(rp))
5767dd7cddfSDavid du Colombier 	    goto ret_rp;
5777dd7cddfSDavid du Colombier     } else {
5787dd7cddfSDavid du Colombier 	if (!r_has_attr(RP_REF(rp), l_mark))
5797dd7cddfSDavid du Colombier 	    goto ret_rp;
5807dd7cddfSDavid du Colombier     }
5817dd7cddfSDavid du Colombier     for (;;) {
5827dd7cddfSDavid du Colombier 
5837dd7cddfSDavid du Colombier 	if (r_is_packed(rp)) {
5847dd7cddfSDavid du Colombier 	    /*
5857dd7cddfSDavid du Colombier 	     * Normally, an unmarked packed ref will be an
5867dd7cddfSDavid du Colombier 	     * integer whose value is the amount of relocation.
5877dd7cddfSDavid du Colombier 	     * However, the relocation value might have been
5887dd7cddfSDavid du Colombier 	     * too large to fit.  If this is the case, for
5897dd7cddfSDavid du Colombier 	     * each such unmarked packed ref we pass over,
5907dd7cddfSDavid du Colombier 	     * we have to decrement the final relocation.
5917dd7cddfSDavid du Colombier 	     */
5927dd7cddfSDavid du Colombier 	    rputc((*rp & lp_mark ? '1' : '0'));
5937dd7cddfSDavid du Colombier 	    if (!(*rp & lp_mark)) {
5947dd7cddfSDavid du Colombier 		if (*rp != pt_tag(pt_integer) + packed_max_value) {
5957dd7cddfSDavid du Colombier 		    /* This is a stored relocation value. */
5967dd7cddfSDavid du Colombier 		    rputc('\n');
5977dd7cddfSDavid du Colombier 		    rp = print_reloc(prp, "ref",
5987dd7cddfSDavid du Colombier 				     (const ref_packed *)
5997dd7cddfSDavid du Colombier 				     ((const char *)prp -
6007dd7cddfSDavid du Colombier 				      (*rp & packed_value_mask) + dec));
6017dd7cddfSDavid du Colombier 		    break;
6027dd7cddfSDavid du Colombier 		}
6037dd7cddfSDavid du Colombier 		/*
6047dd7cddfSDavid du Colombier 		 * We know this is the first of an aligned block
6057dd7cddfSDavid du Colombier 		 * of packed refs.  Skip over the entire block,
6067dd7cddfSDavid du Colombier 		 * decrementing the final relocation.
6077dd7cddfSDavid du Colombier 		 */
6087dd7cddfSDavid du Colombier 		dec += sizeof(ref_packed) * align_packed_per_ref;
6097dd7cddfSDavid du Colombier 		rp += align_packed_per_ref;
6107dd7cddfSDavid du Colombier 	    } else
6117dd7cddfSDavid du Colombier 		rp++;
6127dd7cddfSDavid du Colombier 	    continue;
6137dd7cddfSDavid du Colombier 	}
6147dd7cddfSDavid du Colombier 	if (!ref_type_uses_size_or_null(r_type(RP_REF(rp)))) {
6157dd7cddfSDavid du Colombier 	    /* reloc is in r_size */
6167dd7cddfSDavid du Colombier 	    rputc('\n');
6177dd7cddfSDavid du Colombier 	    rp = print_reloc(prp, "ref",
6187dd7cddfSDavid du Colombier 			     (const ref_packed *)
6197dd7cddfSDavid du Colombier 			     (r_size(RP_REF(rp)) == 0 ? prp :
6207dd7cddfSDavid du Colombier 			      (const ref_packed *)((const char *)prp -
6217dd7cddfSDavid du Colombier 						   r_size(RP_REF(rp)) + dec)));
6227dd7cddfSDavid du Colombier 	    break;
6237dd7cddfSDavid du Colombier 	}
6247dd7cddfSDavid du Colombier 	rputc('u');
6257dd7cddfSDavid du Colombier 	rp += packed_per_ref;
6267dd7cddfSDavid du Colombier     }
6277dd7cddfSDavid du Colombier ret_rp:
6287dd7cddfSDavid du Colombier     /* Use a severely deprecated pun to remove the const property. */
6297dd7cddfSDavid du Colombier     {
6307dd7cddfSDavid du Colombier 	union { const ref_packed *r; ref_packed *w; } u;
6317dd7cddfSDavid du Colombier 
6327dd7cddfSDavid du Colombier 	u.r = rp;
6337dd7cddfSDavid du Colombier 	return u.w;
6347dd7cddfSDavid du Colombier     }
6357dd7cddfSDavid du Colombier }
6367dd7cddfSDavid du Colombier 
6377dd7cddfSDavid du Colombier /* ------ Compaction phase ------ */
6387dd7cddfSDavid du Colombier 
6397dd7cddfSDavid du Colombier /* Compact a ref object. */
6407dd7cddfSDavid du Colombier /* Remove the marks at the same time. */
6417dd7cddfSDavid du Colombier private void
642*593dc095SDavid du Colombier refs_compact(const gs_memory_t *mem, obj_header_t * pre, obj_header_t * dpre, uint size)
6437dd7cddfSDavid du Colombier {
6447dd7cddfSDavid du Colombier     ref_packed *dest;
6457dd7cddfSDavid du Colombier     ref_packed *src;
6467dd7cddfSDavid du Colombier     ref_packed *end;
6477dd7cddfSDavid du Colombier     uint new_size;
6487dd7cddfSDavid du Colombier 
6497dd7cddfSDavid du Colombier     src = (ref_packed *) (pre + 1);
6507dd7cddfSDavid du Colombier     end = (ref_packed *) ((byte *) src + size);
6517dd7cddfSDavid du Colombier     /*
6527dd7cddfSDavid du Colombier      * We know that a block of refs always ends with an unmarked
6537dd7cddfSDavid du Colombier      * full-size ref, so we only need to check for reaching the end
6547dd7cddfSDavid du Colombier      * of the block when we see one of those.
6557dd7cddfSDavid du Colombier      */
6567dd7cddfSDavid du Colombier     if (dpre == pre)		/* Loop while we don't need to copy. */
6577dd7cddfSDavid du Colombier 	for (;;) {
6587dd7cddfSDavid du Colombier 	    if (r_is_packed(src)) {
6597dd7cddfSDavid du Colombier 		if (!r_has_pmark(src))
6607dd7cddfSDavid du Colombier 		    break;
6617dd7cddfSDavid du Colombier 		if_debug1('8', "  [8]packed ref 0x%lx \"copied\"\n",
6627dd7cddfSDavid du Colombier 			  (ulong) src);
6637dd7cddfSDavid du Colombier 		*src &= ~lp_mark;
6647dd7cddfSDavid du Colombier 		src++;
6657dd7cddfSDavid du Colombier 	    } else {		/* full-size ref */
6667dd7cddfSDavid du Colombier 		ref *const pref = (ref *)src;
6677dd7cddfSDavid du Colombier 
6687dd7cddfSDavid du Colombier 		if (!r_has_attr(pref, l_mark))
6697dd7cddfSDavid du Colombier 		    break;
6707dd7cddfSDavid du Colombier 		if_debug1('8', "  [8]ref 0x%lx \"copied\"\n", (ulong) src);
6717dd7cddfSDavid du Colombier 		r_clear_attrs(pref, l_mark);
6727dd7cddfSDavid du Colombier 		src += packed_per_ref;
6737dd7cddfSDavid du Colombier 	    }
6747dd7cddfSDavid du Colombier     } else
6757dd7cddfSDavid du Colombier 	*dpre = *pre;
6767dd7cddfSDavid du Colombier     dest = (ref_packed *) ((char *)dpre + ((char *)src - (char *)pre));
6777dd7cddfSDavid du Colombier     for (;;) {
6787dd7cddfSDavid du Colombier 	if (r_is_packed(src)) {
6797dd7cddfSDavid du Colombier 	    if (r_has_pmark(src)) {
6807dd7cddfSDavid du Colombier 		if_debug2('8', "  [8]packed ref 0x%lx copied to 0x%lx\n",
6817dd7cddfSDavid du Colombier 			  (ulong) src, (ulong) dest);
6827dd7cddfSDavid du Colombier 		*dest++ = *src & ~lp_mark;
6837dd7cddfSDavid du Colombier 	    }
6847dd7cddfSDavid du Colombier 	    src++;
6857dd7cddfSDavid du Colombier 	} else {		/* full-size ref */
6867dd7cddfSDavid du Colombier 	    if (r_has_attr((ref *) src, l_mark)) {
6877dd7cddfSDavid du Colombier 		ref rtemp;
6887dd7cddfSDavid du Colombier 
6897dd7cddfSDavid du Colombier 		if_debug2('8', "  [8]ref 0x%lx copied to 0x%lx\n",
6907dd7cddfSDavid du Colombier 			  (ulong) src, (ulong) dest);
6917dd7cddfSDavid du Colombier 		/* We can't just use ref_assign_inline, */
6927dd7cddfSDavid du Colombier 		/* because the source and destination */
6937dd7cddfSDavid du Colombier 		/* might overlap! */
6947dd7cddfSDavid du Colombier 		ref_assign_inline(&rtemp, (ref *) src);
6957dd7cddfSDavid du Colombier 		r_clear_attrs(&rtemp, l_mark);
6967dd7cddfSDavid du Colombier 		ref_assign_inline((ref *) dest, &rtemp);
6977dd7cddfSDavid du Colombier 		dest += packed_per_ref;
6987dd7cddfSDavid du Colombier 		src += packed_per_ref;
6997dd7cddfSDavid du Colombier 	    } else {		/* check for end of block */
7007dd7cddfSDavid du Colombier 		src += packed_per_ref;
7017dd7cddfSDavid du Colombier 		if (src >= end)
7027dd7cddfSDavid du Colombier 		    break;
7037dd7cddfSDavid du Colombier 	    }
7047dd7cddfSDavid du Colombier 	}
7057dd7cddfSDavid du Colombier     }
7067dd7cddfSDavid du Colombier     new_size = (byte *) dest - (byte *) (dpre + 1) + sizeof(ref);
7077dd7cddfSDavid du Colombier #ifdef DEBUG
7087dd7cddfSDavid du Colombier     /* Check that the relocation came out OK. */
7097dd7cddfSDavid du Colombier     /* NOTE: this check only works within a single chunk. */
7107dd7cddfSDavid du Colombier     if ((byte *) src - (byte *) dest != r_size((ref *) src - 1) + sizeof(ref)) {
7117dd7cddfSDavid du Colombier 	lprintf3("Reloc error for refs 0x%lx: reloc = %lu, stored = %u\n",
7127dd7cddfSDavid du Colombier 		 (ulong) dpre, (ulong) ((byte *) src - (byte *) dest),
7137dd7cddfSDavid du Colombier 		 (uint) r_size((ref *) src - 1));
714*593dc095SDavid du Colombier 	gs_abort(mem);
7157dd7cddfSDavid du Colombier     }
7167dd7cddfSDavid du Colombier #endif
7177dd7cddfSDavid du Colombier     /* Pad to a multiple of sizeof(ref). */
7187dd7cddfSDavid du Colombier     while (new_size & (sizeof(ref) - 1))
7197dd7cddfSDavid du Colombier 	*dest++ = pt_tag(pt_integer),
7207dd7cddfSDavid du Colombier 	    new_size += sizeof(ref_packed);
7217dd7cddfSDavid du Colombier     /* We want to make the newly freed space into a free block, */
7227dd7cddfSDavid du Colombier     /* but we can only do this if we have enough room. */
7237dd7cddfSDavid du Colombier     if (size - new_size < sizeof(obj_header_t)) {	/* Not enough room.  Pad to original size. */
7247dd7cddfSDavid du Colombier 	while (new_size < size)
7257dd7cddfSDavid du Colombier 	    *dest++ = pt_tag(pt_integer),
7267dd7cddfSDavid du Colombier 		new_size += sizeof(ref_packed);
7277dd7cddfSDavid du Colombier     } else {
7287dd7cddfSDavid du Colombier 	obj_header_t *pfree = (obj_header_t *) ((ref *) dest + 1);
7297dd7cddfSDavid du Colombier 
7307dd7cddfSDavid du Colombier 	pfree->o_alone = 0;
7317dd7cddfSDavid du Colombier 	pfree->o_size = size - new_size - sizeof(obj_header_t);
7327dd7cddfSDavid du Colombier 	pfree->o_type = &st_bytes;
7337dd7cddfSDavid du Colombier     }
7347dd7cddfSDavid du Colombier     /* Re-create the final ref. */
7357dd7cddfSDavid du Colombier     r_set_type((ref *) dest, t_integer);
7367dd7cddfSDavid du Colombier     dpre->o_size = new_size;
7377dd7cddfSDavid du Colombier }
738