# include <stdio.h>
# include "gc←private.h"

# ifdef PCR
#   define MAX←ROOT←SETS 1024
#   include "pcr/il/PCR←IL.h"
#   include "pcr/th/PCR←ThCtl.h"
#   include "pcr/mm/PCR←MM.h"
# else
#   define MAX←ROOT←SETS 64
# endif

/* Data structure for list of root sets.				*/
/* We keep a hash table, so that we can filter out duplicate additions.	*/
struct roots {
	ptr←t r←start;
	ptr←t r←end;
	struct roots * r←next;
};

static struct roots static←roots[MAX←ROOT←SETS];

static n←root←sets = 0;

	/* static←roots[0..n←root←sets) contains the valid root sets. */

#define RT←SIZE 64  /* Power of 2, may be != MAX←ROOT←SETS */
#define LOG←RT←SIZE 6

static struct roots * root←index[RT←SIZE];
	/* Hash table header.  Used only to check whether a range is 	*/
	/* already present.						*/

static int rt←hash(addr)
char * addr;
{
    word result = (word) addr;
#   if CPP←WORDSZ > 8*LOG←RT←SIZE
	result ↑= result >> 8*LOG←RT←SIZE;
#   endif
#   if CPP←WORDSZ > 4*LOG←RT←SIZE
	result ↑= result >> 4*LOG←RT←SIZE;
#   endif
    result ↑= result >> 2*LOG←RT←SIZE;
    result ↑= result >> LOG←RT←SIZE;
    result &= (RT←SIZE-1);
    return(result);
}

/* Is a range starting at addr already in the table? */
static bool roots←present(b, e)
char *b, *e;
{
    register int h = rt←hash(b);
    register struct roots *p = root←index[h];
    
    while (p != 0) {
        if (p -> r←start == (ptr←t)b && p -> r←end >= (ptr←t)e) return(TRUE);
        p = p -> r←next;
    }
    return(FALSE);
}

/* Add the given root structure to the index. */
static void add←roots←to←index(p)
struct roots *p;
{
    register int h = rt←hash(p -> r←start);
    
    p -> r←next = root←index[h];
    root←index[h] = p;
}


word GC←root←size = 0;

void GC←add←roots(b, e)
char * b; char * e;
{
    DCL←LOCK←STATE;
    
    DISABLE←SIGNALS();
    LOCK();
    GC←add←roots←inner(b, e);
    UNLOCK();
    ENABLE←SIGNALS();
}


/* Add [b,e) to the root set.  Adding the same interval a second time	*/
/* is a moderately fast noop, and hence benign.  We do not handle	*/
/* different but overlapping intervals efficiently.  (We do handle	*/
/* them correctly.)							*/
void GC←add←roots←inner(b, e)
char * b; char * e;
{
    /* We exclude GC data structures from root sets.  It's usually safe	*/
    /* to mark from those, but it is a waste of time.			*/
    if ( (ptr←t)b < beginGC←arrays && (ptr←t)e > beginGC←arrays) {
        if ((ptr←t)e <= endGC←arrays) {
            e = (char *)beginGC←arrays;
        } else {
            GC←add←roots←inner(b, (char *)beginGC←arrays);
            GC←add←roots←inner((char *)endGC←arrays, e);
            return;
        }
    } else if ((ptr←t)b < endGC←arrays && (ptr←t)e > endGC←arrays) {
        b = (char *)endGC←arrays;
    }
    if (roots←present(b,e)) return;
    if (n←root←sets == MAX←ROOT←SETS) {
        ABORT("Too many root sets\n");
    }
    static←roots[n←root←sets].r←start = (ptr←t)b;
    static←roots[n←root←sets].r←end = (ptr←t)e;
    static←roots[n←root←sets].r←next = 0;
    add←roots←to←index(static←roots + n←root←sets);
    GC←root←size += (ptr←t)e - (ptr←t)b;
    n←root←sets++;
}

void GC←clear←roots()
{
    DCL←LOCK←STATE;
    
    DISABLE←SIGNALS();
    LOCK();
    n←root←sets = 0;
    GC←root←size = 0;
    UNLOCK();
    ENABLE←SIGNALS();
}

# ifdef PCR
PCR←ERes GC←mark←thread←stack(PCR←Th←T *t, PCR←Any dummy)
{
    struct PCR←ThCtl←TInfoRep info;
    PCR←ERes result;
    
    info.ti←stkLow = info.ti←stkHi = 0;
    result = PCR←ThCtl←GetInfo(t, &info);
    GC←mark←all←stack((ptr←t)(info.ti←stkLow), (ptr←t)(info.ti←stkHi));
    return(result);
}

PCR←ERes GC←mark←old←obj(void *p, size←t size, PCR←Any data)
{
    GC←mark←all((ptr←t)p, (ptr←t)p + size);
    return(PCR←ERes←okay);
}

# else
ptr←t GC←approx←sp()
{
    word dummy;
    
    return((ptr←t)(&dummy));
}
# endif
/*
 * Call the mark routines (GC←tl←mark for a single pointer, GC←mark←all
 * on groups of pointers) on every top level accessible pointer.
 * This is source language specific.  The following works for C.
 */

GC←mark←roots()
{
    register int i;

    /*
     * mark from registers - i.e., call GC←tl←mark(i) for each
     * register i
     */
        GC←mark←regs(); /* usually defined in machine←dep.c */


#   ifdef PCR
	/* Traverse data allocated by previous memory managers.		*/
	{
	  extern struct PCR←MM←ProcsRep * GC←old←allocator;
	  
	  if ((*(GC←old←allocator->mmp←enumerate))(PCR←Bool←false,
	  					   GC←mark←old←obj, 0)
	      != PCR←ERes←okay) {
	      ABORT("Old object enumeration failed");
	  }
	}
	
        /* Add new static data areas of dynamically loaded modules.	*/
        {
          PCR←IL←LoadedFile * p = PCR←IL←GetLastLoadedFile();
          PCR←IL←LoadedSegment * q;
          
          /* Skip uncommited files */
          while (p != NIL && !(p -> lf←commitPoint)) {
              /* The loading of this file has not yet been committed	*/
              /* Hence its description could be inconsistent.  		*/
              /* Furthermore, it hasn't yet been run.  Hence its data	*/
              /* segments can't possibly reference heap allocated	*/
              /* objects.						*/
              p = p -> lf←prev;
          }
          for (; p != NIL; p = p -> lf←prev) {
            for (q = p -> lf←ls; q != NIL; q = q -> ls←next) {
              if ((q -> ls←flags & PCR←IL←SegFlags←Traced←MASK)
                  == PCR←IL←SegFlags←Traced←on) {
                GC←add←roots←inner
                	((char *)(q -> ls←addr), 
                	 (char *)(q -> ls←addr) + q -> ls←bytes);
              }
            }
          }
        }
        
        
        /* Traverse all thread stacks. */
          if (PCR←ERes←IsErr(
                PCR←ThCtl←ApplyToAllOtherThreads(GC←mark←thread←stack,0))
              || PCR←ERes←IsErr(GC←mark←thread←stack(PCR←Th←CurrThread(), 0))) {
              ABORT("Thread stack marking failed\n");
          }
#   else
        /* Mark everything on the stack.           */
#   	  ifdef STACK←GROWS←DOWN
	    GC←mark←all←stack( GC←approx←sp(), GC←stackbottom );
#	  else
	    GC←mark←all←stack( GC←stackbottom, GC←approx←sp() );
#	  endif

#   endif

    /* Reregister dynamic libraries, in case one got added.	*/
       GC←register←dynamic←libraries();
    /* Mark everything in static data areas                             */
      for (i = 0; i < n←root←sets; i++) {
        GC←mark←all(static←roots[i].r←start, static←roots[i].r←end);
      }
}

/*
 * Top level GC←mark routine. Mark from the object pointed to by p.
 * GC←tl←mark is normally called by GC←mark←regs, and thus must be defined.
 */
void GC←tl←mark(p)
word p;
{
    word q;

    q = p;
    GC←mark←all←stack((ptr←t)(&q), (ptr←t)((&q)+1));
}