# define SILENT
/* 
 * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
 * Copyright (c) 1991, 1992 by Xerox Corporation.  All rights reserved.
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to copy this garbage collector for any purpose,
 * provided the above notices are retained on all copies.
 */
 
/* Machine specific parts contributed by various people.  See README file. */

# ifndef GC←PRIVATE←H
# define GC←PRIVATE←H

# ifndef GC←H
#   include "gc.h"
# endif

# ifndef HEADERS←H
#   include "gc←headers.h"
# endif

typedef int bool;
# define TRUE 1
# define FALSE 0

typedef char * ptr←t;	/* A generic pointer to which we can add	*/
			/* byte displacments.				*/
			
#ifdef ←←STDC←←
#   include <stddef.h>
    typedef void * extern←ptr←t;
#else
    typedef char * extern←ptr←t;
#endif

/*********************************/
/*                               */
/* Definitions for conservative  */
/* collector                     */
/*                               */
/*********************************/

/*********************************/
/*                               */
/* Easily changeable parameters  */
/*                               */
/*********************************/

# if defined(sun) && defined(mc68000)
#    define M68K←SUN
#    define mach←type←known
# endif
# if defined(hp9000s300)
#    define M68K←HP
#    define mach←type←known
# endif
# if defined(vax)
#    define VAX
#    ifdef ultrix
#	define ULTRIX
#    else
#	define BSD
#    endif
#    define mach←type←known
# endif
# if defined(mips)
#    define MIPS
#    ifdef ultrix
#	define ULTRIX
#    else
#	define RISCOS
#    endif
#    define mach←type←known
# endif
# if defined(sequent) && defined(i386)
#    define I386
#    define mach←type←known
# endif
# if defined(ibm032)
#   define RT
#   define mach←type←known
# endif
# if defined(sun) && defined(sparc)
#   define SPARC
#   define mach←type←known
# endif
# if defined(←IBMR2)
#   define IBMRS6000
#   define mach←type←known
# endif
# if defined(SCO)
/*  define SCO */
#   define mach←type←known
/*	--> incompletely implemented */
# endif
# if defined(←AUX←SOURCE)
#   define M68K←SYSV
#   define mach←type←known
# endif
# if defined(←PA←RISC1←0) || defined(←PA←RISC1←1)
#   define HP←PA
#   define mach←type←known
/*	--> incompletely implemented */
# endif


/* Feel free to add more clauses here */

/* Or manually define the machine type here.  A machine type is 	*/
/* characterized by the architecture and assembler syntax.  Some	*/
/* machine types are further subdivided by OS.  In that case, we use	*/
/* the macros ULTRIX, RISCOS, and BSD to distinguish.			*/
/* Note that SGI IRIX is treated identically to RISCOS.			*/
/* The distinction in these cases is usually the stack starting address */
# ifndef mach←type←known
#   define M68K←SUN /* Guess "Sun" */
		    /* Mapping is: M68K←SUN   ==> Sun3 assembler,      */
		    /*             M68K←HP    ==> HP9000/300,          */
		    /*		   M68K←SYSV  ==> A/UX, maybe others   */
		    /*             I386       ==> Sequent Symmetry,    */
                    /*             NS32K      ==> Encore Multimax,     */
                    /*             MIPS       ==> R2000 or R3000       */
                    /*			(RISCOS, ULTRIX variants)      */
                    /*		   VAX	      ==> DEC VAX	       */
                    /*			(BSD, ULTRIX variants)	       */
                    /*		   SCO	      ==> SCO Unix 3.2         */
# endif

#define PRINTSTATS  /* Print garbage collection statistics          	*/
		    /* For less verbose output, undefine in reclaim.c 	*/

#define PRINTTIMES  /* Print the amount of time consumed by each garbage   */
		    /* collection.                                         */

#define PRINTBLOCKS /* Print object sizes associated with heap blocks,     */
		    /* whether the objects are atomic or composite, and    */
		    /* whether or not the block was found to be empty      */
		    /* duing the reclaim phase.  Typically generates       */
		    /* about one screenful per garbage collection.         */
#undef PRINTBLOCKS

#define PRINTBLACKLIST 	/* Print black listed blocks, i.e. values that     */
			/* cause the allocator to avoid allocating certain */
			/* blocks in order to avoid introducing "false	   */
			/* hits".					   */
#undef PRINTBLACKLIST

#ifdef SILENT
#  ifdef PRINTSTATS
#    undef PRINTSTATS
#  endif
#  ifdef PRINTTIMES
#    undef PRINTTIMES
#  endif
#  ifdef PRINTNBLOCKS
#    undef PRINTNBLOCKS
#  endif
#endif

#if defined(PRINTSTATS) && !defined(GATHERSTATS)
#   define GATHERSTATS
#endif


#ifdef SPARC
#   define ALIGN←DOUBLE  /* Align objects of size > 1 word on 2 word   */
			 /* boundaries.  Wasteful of memory, but       */
			 /* apparently required by SPARC architecture. */

#endif

#define MERGE←SIZES /* Round up some object sizes, so that fewer distinct */
		    /* free lists are actually maintained.  This applies  */
		    /* only to the top level routines in misc.c, not to   */
		    /* user generated code that calls GC←allocobj and     */
		    /* GC←allocaobj directly.                             */
		    /* Slows down average programs slightly.  May however */
		    /* substantially reduce fragmentation if allocation   */
		    /* request sizes are widely scattered.                */
		    /* May save significant amounts of space for obj←map  */
		    /* entries.						  */

/* ALIGN←DOUBLE requires MERGE←SIZES at present. */
# if defined(ALIGN←DOUBLE) && !defined(MERGE←SIZES)
#   define MERGE←SIZES
# endif


/* For PRINTTIMES to tell the truth, we need to know the value of HZ for
   this system. */

#include <time.h>
#if !defined(CLOCKS←PER←SEC) && !defined(PCR)
/* This is technically a bug in the implementation.  ANSI requires that
 * CLOCKS←PER←SEC be defined.  But at least under SunOS4.1.2, it isn't. */
#  if defined(M68K←HP)||defined(M68K←SUN)||defined M68K←SYSV||defined(SPARC)
#    include <sys/param.h>
#    define FLOAT←HZ (double)HZ
#  else
#    define FLOAT←HZ 60.0
#  endif
#else
#  ifdef CLOCKS←PER←SEC
#    define FLOAT←HZ (double)CLOCKS←PER←SEC
#  else
#    define FLOAT←HZ 60.0
#  endif
#endif

#if defined(M68K←SUN) || defined(M68K←SYSV)
#  define ALIGNMENT   2   /* Pointers are aligned on 2 byte boundaries */
			  /* by the Sun C compiler.                    */
#else
#  ifdef VAX
#    define ALIGNMENT 4   /* Pointers are longword aligned by 4.2 C compiler */
#  else
#    ifdef RT
#      define ALIGNMENT 4
#    else
#      ifdef SPARC
#        define ALIGNMENT 4
#      else
#        ifdef I386
#           define ALIGNMENT 4      /* Sequent compiler aligns pointers */
#        else
#          ifdef NS32K
#            define ALIGNMENT 4     /* Pointers are aligned on NS32K */
#          else
#            ifdef MIPS
#              define ALIGNMENT 4   /* MIPS hardware requires pointer */
				    /* alignment                      */
#            else
#              ifdef M68K←HP
#                define ALIGNMENT 2 /* 2 byte alignment inside struct/union, */
				    /* 4 bytes elsewhere 		     */
#              else
#		 ifdef IBMRS6000
#		   define ALIGNMENT 4
#		 else
#		   ifdef HP←PA
#		     define ALIGNMENT 4
#		   else
		     --> specify alignment <--
#		   endif
#		 endif
#              endif
#            endif
#          endif
#        endif
#      endif
#    endif
#  endif
# endif

/* STACKBOTTOM is the cool end of the stack, which is usually the	*/
/* highest address in the stack.					*/
/* Under PCR, we have other ways of finding thread stacks.		*/
#ifndef PCR
# ifdef RT
#   define STACKBOTTOM ((ptr←t) 0x1fffd800)
# else
#   ifdef I386
#     define STACKBOTTOM ((ptr←t) 0x3ffff000)  /* For Sequent */
#   else
#     ifdef NS32K
#       define STACKBOTTOM ((ptr←t) 0xfffff000) /* for Encore */
#     else
#       ifdef MIPS
#	  ifdef ULTRIX
#           define STACKBOTTOM ((ptr←t) 0x7fffc000)
#	  else
#	    ifdef RISCOS
#             define STACKBOTTOM ((ptr←t) 0x7ffff000)
			      /* Could probably be slightly lower since  */
			      /* startup code allocates lots of junk     */
#	    else
		--> fix it
#	    endif
#	  endif
#       else
#         ifdef M68K←HP
#           define STACKBOTTOM ((ptr←t) 0xffeffffc)
			      /* empirically determined.  seems to work. */
#	  else
#	    ifdef IBMRS6000
#	      define STACKBOTTOM ((ptr←t) 0x2ff80000)
#           else
#	      if defined(VAX) && defined(ULTRIX)
#		define STACKBOTTOM ((ptr←t) 0x7fffc800)
#	      else
#		ifdef SCO
#		  define STACKBOTTOM ((ptr←t) 0x7ffffffc)
#		else
#		  ifdef M68K←SYSV
#		    define STACKBOTTOM ((ptr←t)0xFFFFFFFC)
			/* The stack starts at the top of memory,	*/
			/* near as I can figure. --Parag		*/
#		  else
#		    ifdef HP←PA
#		      include <sys/param.h>
#		      include <sys/user.h>
#		      define STACKBOTTOM  ((ptr←t) (USRSTACK))
#		      define STACK←GROWS←UP
#		    else
	 /* other VAXes, SPARC, and various flavors of Sun 2s and Sun 3s use */
	 /* the default heuristic, which is to take the address of a local   */
	 /* variable in gc←init, and round it up to the next multiple        */
	 /* of 16 Meg.  This is crucial on Suns, since various models        */
	 /* that are supposed to be able to share executables, do not        */
	 /* use the same stack base.  In particular, Sun 3/80s are           */
	 /* different from other Sun 3s.                                     */
	 /* This probably works on some more of the above machines.          */
#		    endif
#		  endif
#		endif
#	      endif
#	    endif
#         endif
#       endif
#     endif
#   endif
# endif
#endif /* PCR */


# ifndef STACK←GROWS←UP
#   define STACK←GROWS←DOWN
# endif

/* Start of data segment for each of the above systems.  Note that the */
/* default case works only for contiguous text and data, such as on a  */
/* Vax.                                                                */
# ifdef M68K←SUN
    extern char etext;
#   define DATASTART ((ptr←t)((((word) (&etext)) + 0x1ffff) & ~0x1ffff))
# else
#   ifdef RT
#     define DATASTART ((ptr←t) 0x10000000)
#   else
#     if defined(I386) || defined(SPARC)
	extern int etext;
#       define DATASTART ((ptr←t)((((word) (&etext)) + 0xfff) & ~0xfff))
		/* On very old SPARCs this is too conservative. */
#     else
#       ifdef NS32K
	  extern char **environ;
#         define DATASTART ((ptr←t)(&environ))
			      /* hideous kludge: environ is the first   */
			      /* word in crt0.o, and delimits the start */
			      /* of the data segment, no matter which   */
			      /* ld options were passed through.        */
#       else
#         ifdef MIPS
#           define DATASTART 0x10000000
			      /* Could probably be slightly higher since */
			      /* startup code allocates lots of junk     */
#         else
#           ifdef M68K←HP
	      extern char etext;
#             define DATASTART ((ptr←t)((((word) (&etext)) + 0xfff) & ~0xfff))
#	    else
#             ifdef IBMRS6000
#		define DATASTART ((ptr←t)0x20000000)
#	      else
#		ifdef SCO
#		  define DATASTART ((ptr←t)(0x00400000
					     +((long)&etext & 0xFFC00000)
					     +((long)&etext & 0xFFC00FFF)))
#		else
#		  ifdef M68K←SYSV
	/* This only works for shared-text binaries with magic number 0413.
	   The other sorts of SysV binaries put the data at the end of the text,
	   in which case the default of &etext would work.  Unfortunately,
	   handling both would require having the magic-number available.
	   	   		-- Parag
	   */
#		    define DATASTART ((ptr←t)(0x4000000))    
#                 else
#		    ifdef HP←PA
			extern int ←←data←start;
#			define DATASTART ((ptr←t)(&←←data←start))
#		    else
		    	extern char etext;
#                   	define DATASTART ((ptr←t)(&etext))
#		    endif
#		  endif
#		endif
#	      endif
#           endif
#         endif
#       endif
#     endif
#   endif
# endif

# define HINCR 16          /* Initial heap increment, in blocks of 4K        */
# define MAXHINCR 512      /* Maximum heap increment, in blocks              */
# define HINCR←MULT 3      /* After each new allocation, GC←hincr is multiplied */
# define HINCR←DIV 2       /* by HINCR←MULT/HINCR←DIV                        */
# define GC←MULT 3         /* Don't collect if the fraction of   */
			   /* non-collectable memory in the heap */
			   /* exceeds GC←MUL/GC←DIV              */
# define GC←DIV  4

# define NON←GC←HINCR ((word)8)
			   /* Heap increment if most of heap if collection */
			   /* was suppressed because most of heap is not   */
			   /* collectable                                  */

/*********************************/
/*                               */
/* OS interface routines	 */
/*                               */
/*********************************/

/* HBLKSIZE aligned allocation.  0 is taken to mean failure 	*/
/* space is assumed to be cleared.				*/
# ifdef PCR
#   ifdef ←←STDC←←
        void * real←malloc(size←t size);
#   else 
        char * real←malloc();
#   endif
#   define GET←MEM(bytes) HBLKPTR(real←malloc((size←t)bytes + HBLKSIZE) \
				  + HBLKSIZE-1);
#   define THREADS
# else
    caddr←t sbrk();
#   ifdef ←←STDC←←
#     define GET←MEM(bytes) HBLKPTR(sbrk((size←t)(bytes + HBLKSIZE)) \
				    + HBLKSIZE-1);
#   else
#     define GET←MEM(bytes) HBLKPTR(sbrk((int)(bytes + HBLKSIZE)) + HBLKSIZE-1);
#   endif
# endif

/*
 * Mutual exclusion between allocator/collector routines.
 * Needed if there is more than one allocator thread.
 * FASTLOCK() is assumed to try to acquire the lock in a cheap and
 * dirty way that is acceptable for a few instructions, e.g. by
 * inhibiting preemption.  This is assumed to have succeeded only
 * if a subsequent call to FASTLOCK←SUCCEEDED() returns TRUE.
 * If signals cannot be tolerated with the FASTLOCK held, then
 * FASTLOCK should disable signals.  The code executed under
 * FASTLOCK is otherwise immune to interruption, provided it is
 * not restarted.
 * DCL←LOCK←STATE declares any local variables needed by LOCK and UNLOCK
 * and/or DISABLE←SIGNALS and ENABLE←SIGNALS and/or FASTLOCK.
 * (There is currently no equivalent for FASTLOCK.)
 */  
# ifdef PCR
#    include  "pcr/th/PCR←Th.h"
#    include  "pcr/th/PCR←ThCrSec.h"
     extern struct PCR←Th←MLRep GC←allocate←ml;
#    define DCL←LOCK←STATE  PCR←sigset←t GC←old←sig←mask
#    define LOCK() PCR←Th←ML←Acquire(&GC←allocate←ml) 
#    define UNLOCK() PCR←Th←ML←Release(&GC←allocate←ml)
#    define FASTLOCK() PCR←ThCrSec←EnterSys()
     /* Here we cheat (a lot): */
#        define FASTLOCK←SUCCEEDED() (*(int *)(&GC←allocate←ml) == 0)
		/* TRUE if nobody currently holds the lock */
#    define FASTUNLOCK() PCR←ThCrSec←ExitSys()
# else
#    define DCL←LOCK←STATE
#    define LOCK()
#    define UNLOCK()
#    define FASTLOCK() LOCK()
#    define FASTLOCK←SUCCEEDED() TRUE
#    define FASTUNLOCK() UNLOCK()
# endif

/* Delay any interrupts or signals that may abort this thread.  Data	*/
/* structures are in a consistent state outside this pair of calls.	*/
/* ANSI C allows both to be empty (though the standard isn't very	*/
/* clear on that point).  Standard malloc implementations are usually	*/
/* neither interruptable nor thread-safe, and thus correspond to	*/
/* empty definitions.							*/
# ifdef PCR
#   define DISABLE←SIGNALS() \
		 PCR←Th←SetSigMask(PCR←allSigsBlocked,&GC←old←sig←mask)
#   define ENABLE←SIGNALS() \
		PCR←Th←SetSigMask(&GC←old←sig←mask, NIL)
# else
#   if 0 /* debugging */
#     define DISABLE←SIGNALS()
#     define ENABLE←SIGNALS()
#   else
#     define DISABLE←SIGNALS() GC←disable←signals()
	void GC←disable←signals();
#     define ENABLE←SIGNALS() GC←enable←signals()
	void GC←enable←signals();
#   endif
# endif

/*
 * Stop and restart mutator threads.
 */
# ifdef PCR
#     include "pcr/th/PCR←ThCtl.h"
#     define STOP←WORLD() \
 	PCR←ThCtl←SetExclusiveMode(PCR←ThCtl←ExclusiveMode←stopNormal, \
 				   PCR←allSigsBlocked, \
 				   PCR←waitForever)
#     define START←WORLD() \
	PCR←ThCtl←SetExclusiveMode(PCR←ThCtl←ExclusiveMode←null, \
 				   PCR←allSigsBlocked, \
 				   PCR←waitForever);
# else
#     define STOP←WORLD()
#     define START←WORLD()
# endif

/* Abandon ship */
# ifdef PCR
    void PCR←Base←Panic(const char *fmt, ...);
#   define ABORT(s) PCR←Base←Panic(s)
# else
#   define ABORT(s) abort(s)
# endif

/* Exit abnormally, but without making a mess (e.g. out of memory) */
# ifdef PCR
    void PCR←Base←Exit(int status);
#   define EXIT() PCR←Base←Exit(1)
# else
#   ifndef ←←STDC←←
      void exit();
#   endif
#   define EXIT() exit(1)
# endif


/*********************************/
/*                               */
/* Word-size-dependent defines   */
/*                               */
/*********************************/

#define WORDS←TO←BYTES(x)   ((x)<<2)
#define BYTES←TO←WORDS(x)   ((x)>>2)

#define CPP←WORDSZ              32
#define WORDSZ ((word)CPP←WORDSZ)
#define LOGWL               ((word)5)    /* log[2] of above */
#define BYTES←PER←WORD      ((word)(sizeof (word)))
#define ONES                0xffffffff
#define MSBYTE              0xff000000
#define SIGNB               0x80000000
#define MAXSHORT            0x7fff
#define modHALFWORDSZ(n) ((n) & 0xf)    /* mod n by size of half word    */
#define divHALFWORDSZ(n) ((n) >> 4)	/* divide n by size of half word */
#define modWORDSZ(n) ((n) & 0x1f)       /* mod n by size of word         */
#define divWORDSZ(n) ((n) >> 5)         /* divide n by size of word      */
#define twice(n) ((n) << 1)             /* double n                      */

/*********************/
/*                   */
/*  Size Parameters  */
/*                   */
/*********************/

/*  heap block size, bytes. Should be power of 2 */

#define CPP←LOG←HBLKSIZE 12
#define LOG←HBLKSIZE   ((word)CPP←LOG←HBLKSIZE)
#define CPP←HBLKSIZE (1 << CPP←LOG←HBLKSIZE)
#define HBLKSIZE ((word)CPP←HBLKSIZE)


/*  max size objects supported by freelist (larger objects may be   */
/*  allocated, but less efficiently)                                */

#define CPP←MAXOBJSZ    BYTES←TO←WORDS(CPP←HBLKSIZE/2)
#define MAXOBJSZ ((word)CPP←MAXOBJSZ)
		
# define divHBLKSZ(n) ((n) >> LOG←HBLKSIZE)
 
# define modHBLKSZ(n) ((n) & (HBLKSIZE-1))
 
# define HBLKPTR(objptr) ((struct hblk *)(((word) (objptr)) & ~(HBLKSIZE-1)))

# define HBLKDISPL(objptr) (((word) (objptr)) & (HBLKSIZE-1))


/********************************************/
/*                                          */
/*    H e a p   B l o c k s                 */
/*                                          */
/********************************************/

/*  heap block header */
#define HBLKMASK   (HBLKSIZE-1)

#define BITS←PER←HBLK (HBLKSIZE * 8)

#define MARK←BITS←PER←HBLK (BITS←PER←HBLK/CPP←WORDSZ)
	   /* upper bound                                    */
	   /* We allocate 1 bit/word.  Only the first word   */
	   /* in each object is actually marked.             */

# ifdef ALIGN←DOUBLE
#   define MARK←BITS←SZ (((MARK←BITS←PER←HBLK + 2*CPP←WORDSZ - 1) \
			  / (2*CPP←WORDSZ))*2)
# else
#   define MARK←BITS←SZ ((MARK←BITS←PER←HBLK + CPP←WORDSZ - 1)/CPP←WORDSZ)
# endif
	   /* Upper bound on number of mark words per heap block  */
	   
/* Mark stack entries. */
typedef struct ms←entry {
    word * mse←start;  /* inclusive */
    word * mse←end;	/* exclusive */
} mse;

typedef mse * (*mark←proc)(/* word * addr, hdr * hhdr, mse * msp, mse * msl */);
	  /* Procedure to arrange for the descendents of the object at	*/
	  /* addr to be marked.  Msp points at the top entry on the	*/
	  /* mark stack.  Msl delimits the hot end of the mark stack.	*/
	  /* hhdr is the hdr structure corresponding to addr.		*/
	  /* Returns the new mark stack pointer.			*/

struct hblkhdr {
    signed←word hb←sz;  /* If in use, size in words, of objects in the block. */
		        /* if free, the size in bytes of the whole block      */
    struct hblk * hb←next; 	/* Link field for hblk free list	 */
    				/* and for lists of chunks waiting to be */
    				/* reclaimed.				 */
    mark←proc hb←mark←proc;   /* Procedure to mark objects.  Can	 */
    				/* also be retrived through obj←kind.	 */
    				/* But one level of indirection matters  */
    				/* here.				 */
    char* hb←map;    /* A pointer to a pointer validity map of the block.  */
    		      /* See GC←obj←map.				    */
    		      /* Valid for all blocks with headers.		    */
    int hb←obj←kind;   /* Kind of objects in the block.  Each kind 	*/
    			/* identifies a mark procedure and a set of 	*/
    			/* list headers.  sometimes called regions.	*/
      	
    word hb←marks[MARK←BITS←SZ];
			    /* Bit i in the array refers to the             */
			    /* object starting at the ith word (header      */
			    /* INCLUDED) in the heap block.                 */
};

/*  heap block body */

# define DISCARD←WORDS 0
	/* Number of words to be dropped at the beginning of each block	*/
	/* Must be a multiple of 32.  May reasonably be nonzero		*/
	/* on mcachines that don't guarantee longword alignment of	*/
	/* pointers, so that the number of false hits is minimized.	*/
	/* 0 and 32 are probably the only reasonable values.		*/

# define BODY←SZ ((HBLKSIZE-WORDS←TO←BYTES(DISCARD←WORDS))/sizeof(word))

struct hblk {
#   if (DISCARD←WORDS != 0)
        word garbage[DISCARD←WORDS];
#   endif
    word hb←body[BODY←SZ];
};

# define HDR←WORDS ((word)DISCARD←WORDS)
# define HDR←BYTES ((word)WORDS←TO←BYTES(DISCARD←WORDS))

/* Object free list link */
# define obj←link(p) (*(ptr←t *)(p))

/*  lists of all heap blocks and free lists  */
/* These are grouped together in a struct    */
/* so that they can be easily skipped by the */
/* GC←mark routine.                          */
/* The ordering is weird to make GC←malloc   */
/* faster by keeping the important fields    */
/* sufficiently close together that a        */
/* single load of a base register will do.   */

struct ←GC←arrays {
  word ←heapsize;
  ptr←t ←last←heap←addr;
  ptr←t ←prev←heap←addr;
  word ←words←allocd;
  	/* Number of words allocated during this collection cycle */
  ptr←t ←objfreelist[MAXOBJSZ+1];
			  /* free list for objects */
# ifdef MERGE←SIZES
    unsigned ←size←map[WORDS←TO←BYTES(MAXOBJSZ+1)];
    	/* Number of words to allocate for a given allocation request in */
    	/* bytes.							 */
# endif 
  ptr←t ←aobjfreelist[MAXOBJSZ+1];
			  /* free list for atomic objs*/
  ptr←t ←obj←map[MAXOBJSZ+1];
                       /* If not NIL, then a pointer to a map of valid  */
    		       /* object addresses. hbh←map[sz][i] is j if the  */
    		       /* address block←start+i is a valid pointer      */
    		       /* to an object at block←start+i&~3 - 4*j.  It   */
    		       /* is OBJ←INVALID if block←start+4*i is not      */
    		       /* valid as a pointer to an object.              */
    		       /* We assume that all values of j <= OBJ←INVALID */
    		       /* The zeroth entry corresponds to large objects.*/
#       define OBJ←INVALID  0x7f
#	define CPP←MAX←OFFSET (WORDS←TO←BYTES(OBJ←INVALID) - 1)	
#	define MAX←OFFSET ((word)CPP←MAX←OFFSET)
  struct hblk * ←reclaim←list[MAXOBJSZ+1];
  struct hblk * ←areclaim←list[MAXOBJSZ+1];
};

# define VALID←OFFSET←SZ \
	(CPP←MAX←OFFSET > WORDS←TO←BYTES(CPP←MAXOBJSZ)? \
	 CPP←MAX←OFFSET+1 \
	 : WORDS←TO←BYTES(CPP←MAXOBJSZ)+1)
extern char GC←valid←offsets[VALID←OFFSET←SZ];

				/* GC←valid←offsets[i] == TRUE ==> i 	*/
				/* is registered as a displacement.	*/
    
extern char GC←modws←valid←offsets[sizeof(word)];
				/* GC←valid←offsets[i] ==>		  */
				/* GC←modws←valid←offsets[i%sizeof(word)] */

extern struct ←GC←arrays GC←arrays; 

# define GC←objfreelist GC←arrays.←objfreelist
# define GC←aobjfreelist GC←arrays.←aobjfreelist
# define GC←reclaim←list GC←arrays.←reclaim←list
# define GC←areclaim←list GC←arrays.←areclaim←list
# define GC←obj←map GC←arrays.←obj←map
# define GC←last←heap←addr GC←arrays.←last←heap←addr
# define GC←prev←heap←addr GC←arrays.←prev←heap←addr
# define GC←words←allocd GC←arrays.←words←allocd
# define GC←heapsize GC←arrays.←heapsize
# ifdef MERGE←SIZES
#   define GC←size←map GC←arrays.←size←map
# endif

# define beginGC←arrays ((ptr←t)(&GC←arrays))
# define endGC←arrays (((ptr←t)(&GC←arrays)) + (sizeof GC←arrays))


# define MAXOBJKINDS 16

/* Object kinds: */
extern struct obj←kind {
   ptr←t *ok←freelist;	/* Array of free listheaders for this kind of object */
   			/* Point either to GC←arrays or to storage allocated */
   			/* with GC←scratch←alloc.			     */
   struct hblk **ok←reclaim←list;
   			/* List headers for lists of blocks waiting to be */
   			/* swept.					  */
   mark←proc ok←mark←proc; /* Procedure to either mark referenced objects,  */
   			   /* or push them on the mark stack.		    */
   bool ok←init;     /* Clear objects before putting them on the free list. */
} GC←obj←kinds[MAXOBJKINDS];
/* Predefined kinds: */
# define PTRFREE 0
# define NORMAL  1

extern int GC←n←kinds;

extern char * GC←invalid←map;
			/* Pointer to the nowhere valid hblk map */
			/* Blocks pointing to this map are free. */

extern struct hblk * GC←hblkfreelist;
				/* List of completely empty heap blocks	*/
				/* Linked through hb←next field of 	*/
				/* header structure associated with	*/
				/* block.				*/

extern bool GC←is←initialized;		/* GC←init() has been run.	*/

			
extern word GC←words←allocd←before←gc;
			/* Number of words allocated before this	*/
			/* collection cycle.				*/

# ifdef GATHERSTATS
   extern word GC←composite←in←use;
   		/* Number of words in accessible composite	*/
		/* objects.					*/
   extern word GC←atomic←in←use;
   		/* Number of words in accessible atomic		*/
		/* objects.					*/
# endif

# ifndef PCR
    extern ptr←t GC←stackbottom;	/* Cool end of user stack	*/
# endif

extern word GC←hincr;      	/* current heap increment, in blocks	*/

extern ptr←t GC←least←plausible←heap←addr;
extern ptr←t GC←greatest←plausible←heap←addr;
			/* Bounds on the heap.  Guaranteed valid	*/
			/* Likely to include future heap expansion.	*/
			
/* Operations */
# define update←GC←hincr  GC←hincr = (GC←hincr * HINCR←MULT)/HINCR←DIV; \
		       if (GC←hincr > MAXHINCR) {GC←hincr = MAXHINCR;}
# define abs(x)  ((x) < 0? (-(x)) : (x))

/****************************/
/*                          */
/*   Objects                */
/*                          */
/****************************/


/*  Marks are in a reserved area in                          */
/*  each heap block.  Each word has one mark bit associated  */
/*  with it. Only those corresponding to the beginning of an */
/*  object are used.                                         */


/* Operations */

/*
 * Retrieve, set, clear the mark bit corresponding
 * to the nth word in a given heap block.
 *
 * (Recall that bit n corresponds to object beginning at word n
 * relative to the beginning of the block, including unused words)
 */

# define mark←bit←from←hdr(hhdr,n) (((hhdr)->hb←marks[divWORDSZ(n)] \
			    >> (modWORDSZ(n))) & 1)
# define set←mark←bit←from←hdr(hhdr,n) (hhdr)->hb←marks[divWORDSZ(n)] \
				|= 1 << modWORDSZ(n)

# define clear←mark←bit←from←hdr(hhdr,n) (hhdr)->hb←marks[divWORDSZ(n)] \
				&= ~(1 << modWORDSZ(n))

/* Important internal collector routines */

void GC←apply←to←all←blocks(/*fn, client←data*/);
			/* Invoke fn(hbp, client←data) for each 	*/
			/* allocated heap block.			*/
mse * GC←no←mark←proc(/*addr,hhdr,msp,msl*/);
			/* Mark procedure for PTRFREE objects.	*/
mse * GC←normal←mark←proc(/*addr,hhdr,msp,msl*/);
			/* Mark procedure for NORMAL objects.	*/
void GC←mark←init();
void GC←mark();  		/* Mark from everything on the mark stack. */
void GC←mark←reliable();	/* as above, but fix things up after	*/
				/* a mark stack overflow.		*/
void GC←mark←all(/*b,t*/);	/* Mark from everything in a range.	*/
void GC←mark←all←stack(/*b,t*/);    /* Mark from everything in a range,	    */
				    /* consider interior pointers as valid  */
void GC←remark();	/* Mark from all marked objects.  Used	*/
		 	/* only if we had to drop something.	*/
void GC←tl←mark(/*p*/);	/* Mark from a single root.		*/
		 	
void GC←add←to←black←list←normal(/* bits */);
			/* Register bits as a possible future false	*/
			/* reference from the heap or static data	*/
void GC←add←to←black←list←stack(/* bits */);
struct hblk * GC←is←black←listed(/* h, len */);
			/* If there are likely to be false references	*/
			/* to a block starting at h of the indicated    */
			/* length, then return the next plausible	*/
			/* starting location for h that might avoid	*/
			/* these false references.			*/
void GC←promote←black←lists();
			/* Declare an end to a black listing phase.	*/
		 	
ptr←t GC←scratch←alloc(/*bytes*/);
				/* GC internal memory allocation for	*/
				/* small objects.  Deallocation is not  */
				/* possible.				*/
				
void GC←invalidate←map(/* hdr */);
				/* Remove the object map associated	*/
				/* with the block.  This identifies	*/
				/* the block as invalid to the mark	*/
				/* routines.				*/
void GC←add←map←entry(/*sz*/);
				/* Add a heap block map for objects of	*/
				/* size sz to obj←map.			*/
void GC←register←displacement←inner(/*offset*/);
				/* Version of GC←register←displacement	*/
				/* that assumes lock is already held	*/
				/* and signals are already disabled.	*/
				
void GC←init←inner();
				
void GC←new←hblk(/*size←in←words, kind*/);
				/* Allocate a new heap block, and build */
				/* a free list in it.			*/				
struct hblk * GC←allochblk(/*size←in←words, kind*/);
				/* Allocate a heap block, clear it if	*/
				/* for composite objects, inform	*/
				/* the marker that block is valid	*/
				/* for objects of indicated size.	*/
				/* sz < 0 ==> atomic.			*/ 
void GC←freehblk();		/* Deallocate a heap block and mark it  */
				/* as invalid.				*/
				
void GC←start←reclaim(/*abort←if←found*/);
				/* Restore unmarked objects to free	*/
				/* lists, or (if abort←if←found is	*/
				/* TRUE) report them.			*/
				/* Sweeping of small object pages is	*/
				/* largely deferred.			*/
void GC←continue←reclaim(/*size, kind*/);
				/* Sweep pages of the given size and	*/
				/* kind, as long as possible, and	*/
				/* as long as the corr. free list is    */
				/* empty.				*/
void GC←gcollect←inner();	/* Collect; caller must have acquired	*/
				/* lock and disabled signals.		*/
void GC←init();			/* Initialize collector.		*/

ptr←t GC←generic←malloc(/* bytes, kind */);
				/* Allocate an object of the given	*/
				/* kind.  By default, there are only	*/
				/* two kinds: composite, and atomic.	*/
				/* We claim it's possible for clever	*/
				/* client code that understands GC	*/
				/* internals to add more, e.g. to	*/
				/* communicate object layout info	*/
				/* to the collector.			*/
ptr←t GC←generic←malloc←words←small(/*words, kind*/);
				/* As above, but size in units of words */
				/* Bypasses MERGE←SIZES.  Assumes	*/
				/* words <= MAXOBJSZ.			*/
ptr←t GC←allocobj(/* sz←inn←words, kind */);
				/* Make the indicated 			  */
				/* free list nonempty, and return its	*/
				/* head.				*/
				
void GC←install←header(/*h*/);
				/* Install a header for block h.	*/
void GC←install←counts(/*h, sz*/);
				/* Set up forwarding counts for block	*/
				/* h of size sz.			*/
void GC←remove←header(/*h*/);
				/* Remove the header for block h.	*/
void GC←remove←counts(/*h, sz*/);
				/* Remove forwarding counts for h.	*/
hdr * GC←find←header(/*p*/);	/* Debugging only.			*/

void GC←finalize();	/* Perform all indicated finalization actions	*/
			/* on unmarked objects.				*/
			
void GC←add←to←heap(/*p, bytes*/);
			/* Add a HBLKSIZE aligned chunk to the heap.	*/
# endif /* GC←PRIVATE←H */