/* 
 * 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.
 */
# include "gc←private.h"

/*
 * We maintain several hash tables of hblks that have had false hits.
 * Each contains one bit per hash bucket;  If any page in the bucket
 * has had a false hit, we assume that all of them have.
 * False hits from the stack(s) are much more dangerous than false hits
 * from elsewhere, since the former can pin a large object that spans the
 * block, eventhough it does not start on the dangerous block.
 */
 
/*
 * Externally callable routines are:
 
 * GC←add←to←black←list←normal
 * GC←add←to←black←list←stack
 * GC←promote←black←lists
 * GC←is←black←listed
 *
 * All require that the allocator lock is held.
 */

# define LOG←HT←ENTRIES  14	/* Collisions are likely if heap grows	*/
				/* to more than 16K hblks = 64MB.	*/
				/* Each hash table occupies 2K bytes.   */
# define HT←ENTRIES ((word)1 << LOG←HT←ENTRIES)
# define HT←SIZE (HT←ENTRIES >> LOGWL)
typedef word black←list←t[HT←SIZE];

# define HASH(addr) (((addr) >> LOG←HBLKSIZE) & (HT←ENTRIES - 1))

/* Pointers to individual tables.  We replace one table by another by 	*/
/* switching these pointers.  GC←black←lists is not used directly.	*/
word * GC←new←normal←bl;
		/* Nonstack false references seen at last complete	*/
		/* collection.						*/
word * GC←old←normal←bl;
		/* Nonstack false references seen at preceding		*/
		/* collection.						*/
word * GC←incomplete←normal←bl;
		/* Nonstack false references seen at current,		*/
		/* not yet completed collection.			*/
word * GC←new←stack←bl;
word * GC←old←stack←bl;
word * GC←incomplete←stack←bl;

# define get←bl←entry←from←index(bl, index) \
		(((bl)[divWORDSZ(index)] >> modWORDSZ(index)) & 1)
# define set←bl←entry←from←index(bl, index) \
		(bl)[divWORDSZ(index)] |= 1 << modWORDSZ(index)
# define clear←bl←entry←from←index(bl, index) \
		(bl)[divWORDSZ(index)] &= ~(1 << modWORDSZ(index))
		
GC←bl←init()
{
# ifndef ALL←INTERIOR←POINTERS
    GC←new←normal←bl = (word *)GC←scratch←alloc((word)(sizeof(black←list←t)));
    GC←old←normal←bl = (word *)GC←scratch←alloc((word)(sizeof (black←list←t)));
    GC←incomplete←normal←bl = (word *)GC←scratch←alloc
    					((word)(sizeof(black←list←t)));
# endif
    GC←new←stack←bl = (word *)GC←scratch←alloc((word)(sizeof(black←list←t)));
    GC←old←stack←bl = (word *)GC←scratch←alloc((word)(sizeof(black←list←t)));
    GC←incomplete←stack←bl = (word *)GC←scratch←alloc
    					((word)(sizeof(black←list←t)));
}
		
void GC←clear←bl(doomed)
word *doomed;
{
    bzero((char *)doomed, (int)HT←SIZE*sizeof(word));
}

/* Signal the completion of a collection.  Turn the incomplete black	*/
/* lists into new black lists, etc.					*/			 
void GC←promote←black←lists()
{
    word * very←old←normal←bl = GC←old←normal←bl;
    word * very←old←stack←bl = GC←old←stack←bl;
    
    GC←old←normal←bl = GC←new←normal←bl;
    GC←new←normal←bl = GC←incomplete←normal←bl;
    GC←old←stack←bl = GC←new←stack←bl;
    GC←new←stack←bl = GC←incomplete←stack←bl;
#   ifndef ALL←INTERIOR←POINTERS
      GC←clear←bl(very←old←normal←bl);
#   endif
    GC←clear←bl(very←old←stack←bl);
    GC←incomplete←normal←bl = very←old←normal←bl;
    GC←incomplete←stack←bl = very←old←stack←bl;
}

# ifndef ALL←INTERIOR←POINTERS
/* P is not a valid pointer reference, but it falls inside	*/
/* the plausible heap bounds.					*/
/* Add it to the normal incomplete black list if appropriate.	*/
void GC←add←to←black←list←normal(p)
word p;
{
    if (!(GC←modws←valid←offsets[p & (sizeof(word)-1)])) return;
    {
        register int index = HASH(p);
        
        if (HDR(p) == 0 || get←bl←entry←from←index(GC←new←normal←bl, index)) {
#   	    ifdef PRINTBLACKLIST
		if (!get←bl←entry←from←index(GC←incomplete←normal←bl, index)) {
	    	  GC←printf1("Black listing (normal) 0x%lx\n",
	    	  	     (unsigned long) p);
	    	}
#           endif
            set←bl←entry←from←index(GC←incomplete←normal←bl, index);
        } /* else this is probably just an interior pointer to an allocated */
          /* object, and isn't worth black listing.			    */
    }
}
# endif

/* And the same for false pointers from the stack. */
void GC←add←to←black←list←stack(p)
word p;
{
    register int index = HASH(p);
        
    if (HDR(p) == 0 || get←bl←entry←from←index(GC←new←stack←bl, index)) {
#   	ifdef PRINTBLACKLIST
	    if (!get←bl←entry←from←index(GC←incomplete←stack←bl, index)) {
	    	  GC←printf1("Black listing (stack) 0x%lx\n",
	    	             (unsigned long)p);
	    }
#       endif
	set←bl←entry←from←index(GC←incomplete←stack←bl, index);
    }
}

/*
 * Is the block starting at h of size len bytes black listed?   If so,
 * return the address of the next plausible r such that (r, len) might not
 * be black listed.  (R may not actually be in the heap.  We guarantee only
 * that every smaller value of r after h is also black listed.)
 * If (h,len) is not black listed, return 0.
 * Knows about the structure of the black list hash tables.
 */
struct hblk * GC←is←black←listed(h, len)
struct hblk * h;
word len;
{
    register int index = HASH((word)h);
    register word i;
    word nblocks = divHBLKSZ(len);

#   ifndef ALL←INTERIOR←POINTERS
      if (get←bl←entry←from←index(GC←new←normal←bl, index)
        && get←bl←entry←from←index(GC←old←normal←bl, index)) {
        return(h+1);
      }
#   endif
    
    for (i = 0; ; ) {
        if (GC←new←stack←bl[divWORDSZ(index)] == 0) {
            /* An easy case */
            i += WORDSZ - modWORDSZ(index);
        } else {
          if (get←bl←entry←from←index(GC←new←stack←bl, index)
            && get←bl←entry←from←index(GC←old←stack←bl, index)) {
            return(h+i+1);
          }
          i++;
        }
        if (i >= nblocks) break;
        index = HASH((word)(h+i));
    }
    return(0);
}