[MLton-devel] cvs commit: MAIL

Stephen Weeks sweeks@users.sourceforge.net
Thu, 11 Jul 2002 10:43:01 -0700


sweeks      02/07/11 10:43:01

  Modified:    runtime  Makefile gc.c gc.h
  Removed:     runtime  GC_world.c
  Log:
  Turned on the mark-compact GC into the runtime.  The collector now decides on a
  per-collection basis whether to use mark-compact or cheney copying (see doGC in
  gc.c).  It uses copying if it believes the copy can be performed in memory,
  otherwise it uses mark-compact.
  
  Added heap resizing code that works with both copying and mark-compact.  There
  are lots of hardwired constants that surely need to be tweaked.
  
  Reorganized the code in gc.c quite a bit and cleaned it up.
  
  Moved loadWorld and saveWorld from GC_world.c back to gc.c, which meant that a
  lot of GC internals didn't need to be exported in gc.h anymore.

Revision  Changes    Path
1.30      +0 -2      mlton/runtime/Makefile

Index: Makefile
===================================================================
RCS file: /cvsroot/mlton/mlton/runtime/Makefile,v
retrieving revision 1.29
retrieving revision 1.30
diff -u -r1.29 -r1.30
--- Makefile	6 Jul 2002 17:22:08 -0000	1.29
+++ Makefile	11 Jul 2002 17:43:00 -0000	1.30
@@ -155,7 +155,6 @@
 	Posix/TTY/getpgrp.o			\
 	Posix/TTY/sendbreak.o			\
 	Posix/TTY/setpgrp.o			\
-	GC_world.o				\
 	bcopy.o					\
 	gc.o					\
 	libmlton.o				\
@@ -304,7 +303,6 @@
 	Posix/TTY/getpgrp-gdb.o			\
 	Posix/TTY/sendbreak-gdb.o		\
 	Posix/TTY/setpgrp-gdb.o			\
-	GC_world-gdb.o				\
 	bcopy.o					\
 	gc-gdb.o				\
 	libmlton-gdb.o				\



1.54      +1772 -1911mlton/runtime/gc.c

Index: gc.c
===================================================================
RCS file: /cvsroot/mlton/mlton/runtime/gc.c,v
retrieving revision 1.53
retrieving revision 1.54
diff -u -r1.53 -r1.54
--- gc.c	8 Jul 2002 01:00:21 -0000	1.53
+++ gc.c	11 Jul 2002 17:43:00 -0000	1.54
@@ -46,26 +46,25 @@
 	BOGUS_POINTER = 0x1,
 	DEBUG = FALSE,
 	DEBUG_DETAILED = FALSE,
-	DEBUG_MARK = FALSE,
-	DEBUG_MARK_SIZE = FALSE,
+	DEBUG_MARK_COMPACT = FALSE,
 	DEBUG_MEM = FALSE,
+	DEBUG_RESIZING = TRUE,
 	DEBUG_SIGNALS = FALSE,
 	DEBUG_THREADS = FALSE,
 	FORWARDED = 0xFFFFFFFF,
 	HEADER_SIZE = WORD_SIZE,
+	LIVE_RATIO = 8,	/* The desired live ratio. */
 	STACK_HEADER_SIZE = WORD_SIZE,
-	VERIFY_MARK = FALSE,
 };
 
+#define LIVE_RATIO_MIN 1.25
+
 typedef enum {
 	MARK_MODE,
 	UNMARK_MODE,
 } MarkMode;
 
-W32 mark (GC_state s, pointer root, MarkMode mode);
-
 #define BOGUS_THREAD (GC_thread)BOGUS_POINTER
-
 #define STACK_HEADER GC_objectHeader (STACK_TYPE_INDEX)
 #define STRING_HEADER GC_objectHeader (STRING_TYPE_INDEX)
 #define THREAD_HEADER GC_objectHeader (THREAD_TYPE_INDEX)
@@ -115,11 +114,29 @@
 	return ((x < y) ? x : y);
 }
 
+static inline W64 min64 (W64 x, W64 y) {
+	return ((x < y) ? x : y);
+}
+
 static inline uint max(uint x, uint y) {
 	return ((x > y) ? x : y);
 }
+
+static inline W64 max64 (W64 x, W64 y) {
+	return ((x > y) ? x : y);
+}
 #endif
 
+
+/*
+ * Round size up to a multiple of the size of a page.
+ */
+static inline size_t roundPage (GC_state s, size_t size) {
+	size += s->pageSize - 1;
+	size -= size % s->pageSize;
+	return (size);
+}
+
 #if (defined (__linux__) || defined (__FreeBSD__))
 /* A super-safe mmap.
  *  Allocates a region of memory with dead zones at the high and low ends.
@@ -266,170 +283,176 @@
 
 #endif
 
-static inline void releaseFromSpace (GC_state s) {
-	if (s->messages)
-		fprintf (stderr, "Releasing from space.\n");
-	release (s->base, s->fromSize);
-	s->base = NULL;
-	s->fromSize = 0;
-}
+static inline void copy (pointer src, pointer dst, uint size) {
+	uint	*to,
+		*from,
+		*limit;
 
-static inline void releaseToSpace (GC_state s) {
-	if (s->messages)
-		fprintf (stderr, "Releasing to space.\n");
-	release (s->toBase, s->toSize);
-	s->toBase = NULL;
-	s->toSize = 0;
+	if (DEBUG_DETAILED)
+		fprintf (stderr, "copy (0x%08x, 0x%08x, %u)\n",
+				(uint)src, (uint)dst, size);
+	assert (isWordAligned((uint)src));
+	assert (isWordAligned((uint)dst));
+	assert (isWordAligned(size));
+	assert (dst <= src or src + size <= dst);
+	if (src == dst)
+		return;
+	from = (uint*)src;
+	to = (uint*)dst;
+	limit = (uint*)(src + size);
+	until (from == limit)
+		*to++ = *from++;
 }
 
-/* ------------------------------------------------- */
-/*                     roundPage                     */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                              rusage                              */
+/* ---------------------------------------------------------------- */
 
-/*
- * Round size up to a multiple of the size of a page.
- */
-static inline size_t
-roundPage(GC_state s, size_t size)
-{
-	size += s->pageSize - 1;
-	size -= size % s->pageSize;
-	return (size);
-}
+int fixedGetrusage (int who, struct rusage *rup) {
+	struct tms	tbuff;
+	int		res;
+	clock_t		user,
+			sys;
+	static bool	first = TRUE;
+	static long	hz;
 
-/* ------------------------------------------------- */
-/*                      display                      */
-/* ------------------------------------------------- */
+	if (first) {
+		first = FALSE;
+		hz = sysconf(_SC_CLK_TCK);
+	}
+	res = getrusage(who, rup);
+	unless (res == 0)
+		return (res);
+	if (times(&tbuff) == -1)
+		diee("Impossible: times() failed");
+	switch (who) {
+	case RUSAGE_SELF:
+		user = tbuff.tms_utime;
+		sys = tbuff.tms_stime;
+		break;
+	case RUSAGE_CHILDREN:
+		user = tbuff.tms_cutime;
+		sys = tbuff.tms_cstime;
+		break;
+	default:
+		die("getrusage() accepted unknown who: %d", who);
+		exit(1);  /* needed to keep gcc from whining. */
+	}
+	rup->ru_utime.tv_sec = user / hz;
+	rup->ru_utime.tv_usec = (user % hz) * (1000000 / hz);
+	rup->ru_stime.tv_sec = sys / hz;
+	rup->ru_stime.tv_usec = (sys % hz) * (1000000 / hz);
+	return (0);
+}
 
-void GC_display (GC_state s, FILE *stream) {
-	fprintf (stream, "GC state\n\tbase = 0x%x\n\tfrontier - base = %u\n\tlimit - base = %u\n\tlimit - frontier = %d\n",
-			(uint) s->base, 
-			s->frontier - s->base,
-			s->limit - s->base,
-			s->limit - s->frontier);
-	fprintf (stream, "\tcanHandle = %d\n", s->canHandle);
-	fprintf (stream, "\texnStack = %u  bytesNeeded = %u  reserved = %u  used = %u\n",
-			s->currentThread->exnStack,
-			s->currentThread->bytesNeeded,
-			s->currentThread->stack->reserved,
-			s->currentThread->stack->used);
-	fprintf (stream, "\tstackBottom = %x\nstackTop - stackBottom = %u\nstackLimit - stackTop = %u\n",
-			(uint)s->stackBottom,
-			s->stackTop - s->stackBottom,
-			(s->stackLimit - s->stackTop));
+static inline void rusageZero (struct rusage *ru) {
+	memset(ru, 0, sizeof(*ru));
 }
 
-/* ------------------------------------------------- */
-/*                      object                       */
-/* ------------------------------------------------- */
+static void rusagePlusMax (struct rusage *ru1,
+			      struct rusage *ru2,
+			      struct rusage *ru) {
+	const int	million = 1000000;
+	time_t		sec,
+			usec;
 
-static inline pointer
-object (GC_state s, uint header, uint bytesRequested)
-{
-	pointer result;
+	sec = ru1->ru_utime.tv_sec + ru2->ru_utime.tv_sec;
+	usec = ru1->ru_utime.tv_usec + ru2->ru_utime.tv_usec;
+	sec += (usec / million);
+	usec %= million;
+	ru->ru_utime.tv_sec = sec;
+	ru->ru_utime.tv_usec = usec;
 
-	assert(s->frontier + bytesRequested <= s->limit);
-	assert(isWordAligned(bytesRequested));
-	*(uint*)s->frontier = header;
-	result = s->frontier + HEADER_SIZE;
-	s->frontier += bytesRequested;
-	return result;
-}
+	sec = ru1->ru_stime.tv_sec + ru2->ru_stime.tv_sec;
+	usec = ru1->ru_stime.tv_usec + ru2->ru_stime.tv_usec;
+	sec += (usec / million);
+	usec %= million;
+	ru->ru_stime.tv_sec = sec;
+	ru->ru_stime.tv_usec = usec;
 
-static inline W64 w64align (W64 w) {
- 	return ((w + 3) & ~ 3);
+	ru->ru_maxrss = max(ru1->ru_maxrss, ru2->ru_maxrss);
+	ru->ru_ixrss = max(ru1->ru_ixrss, ru2->ru_ixrss);
+	ru->ru_idrss = max(ru1->ru_idrss, ru2->ru_idrss);
+	ru->ru_isrss = max(ru1->ru_isrss, ru2->ru_isrss);
+	ru->ru_minflt = ru1->ru_minflt + ru2->ru_minflt;
+	ru->ru_majflt = ru1->ru_majflt + ru2->ru_majflt;
+	ru->ru_nswap = ru1->ru_nswap + ru2->ru_nswap;
+	ru->ru_inblock = ru1->ru_inblock + ru2->ru_inblock;
+	ru->ru_oublock = ru1->ru_oublock + ru2->ru_oublock;
+	ru->ru_msgsnd = ru1->ru_msgsnd + ru2->ru_msgsnd;
+	ru->ru_msgrcv = ru1->ru_msgrcv + ru2->ru_msgrcv;
+	ru->ru_nsignals = ru1->ru_nsignals + ru2->ru_nsignals;
+	ru->ru_nvcsw = ru1->ru_nvcsw + ru2->ru_nvcsw;
+	ru->ru_nivcsw = ru1->ru_nivcsw + ru2->ru_nivcsw;
 }
 
-pointer GC_arrayAllocate (GC_state s, W32 ensureBytesFree, W32 numElts, 
-				W32 header) {
-	uint numPointers;
-	uint numNonPointers;
-	uint tag;
-	uint eltSize;
-	W64 arraySize64;
-	W32 arraySize;
-	W32 *frontier;
-	W32 *last;
-	pointer res;
-	W32 require;
-	W64 require64;
-
-	SPLIT_HEADER();
-	assert ((numPointers == 1 and numNonPointers == 0)
-			or (numPointers == 0 and numNonPointers > 0));
-	eltSize = numPointers * POINTER_SIZE + numNonPointers;
-	arraySize64 = 
-		w64align((W64)eltSize * (W64)numElts + GC_ARRAY_HEADER_SIZE);
-	require64 = arraySize64 + (W64)ensureBytesFree;
-	if (require64 >= 0x100000000llu)
-		die ("Out of memory: cannot allocate %llu bytes.\n",
-			require64);
-	require = (W32)require64;
-	arraySize = (W32)arraySize64;
-	if (DEBUG)
-		fprintf (stderr, "array with %u elts of size %u and total size %u.  ensureBytesFree = %u\n",
-			(uint)numElts, (uint)eltSize, (uint)arraySize,
-			(uint)ensureBytesFree);
-	if (require > s->limitPlusSlop - s->frontier) {
-		GC_enter (s);
-		GC_doGC (s, require, 0);
-		GC_leave (s);
-	}
-	frontier = (W32*)s->frontier;
-	last = (W32*)((pointer)frontier + arraySize);
-	*frontier++ = 0; /* counter word */
-	*frontier++ = numElts;
-	*frontier++ = header;
-	res = (pointer)frontier;
-	if (1 == numPointers)
-		for ( ; frontier < last; frontier++)
-			*frontier = 0x1;
-	s->frontier = (pointer)last;
-	/* Unfortunately, the invariant isn't quite true here, because unless we
- 	 * did the GC, we never set s->currentThread->stack->used to reflect
-	 * what the mutator did with stackTop.
- 	 */
-	/*	assert(GC_mutatorInvariant(s)); */
-	if (DEBUG) {
-		fprintf (stderr, "GC_arrayAllocate done.  res = 0x%x  frontier = 0x%x\n",
-				(uint)res, (uint)s->frontier);
-		GC_display (s, stderr);
-	}
-	assert (ensureBytesFree <= s->limitPlusSlop - s->frontier);
-	return res;
-}	
+static void rusageMinusMax (struct rusage *ru1,
+				struct rusage *ru2,
+				struct rusage *ru) {
+	const int	million = 1000000;
+	time_t		sec,
+			usec;
 
-/* ------------------------------------------------- */
-/*                  getFrameLayout                   */
-/* ------------------------------------------------- */
+	sec = (ru1->ru_utime.tv_sec - ru2->ru_utime.tv_sec) - 1;
+	usec = ru1->ru_utime.tv_usec + million - ru2->ru_utime.tv_usec;
+	sec += (usec / million);
+	usec %= million;
+	ru->ru_utime.tv_sec = sec;
+	ru->ru_utime.tv_usec = usec;
 
-static inline GC_frameLayout	*
-getFrameLayout (GC_state s, word returnAddress)
-{
-	GC_frameLayout *layout;
-	uint index;
+	sec = (ru1->ru_stime.tv_sec - ru2->ru_stime.tv_sec) - 1;
+	usec = ru1->ru_stime.tv_usec + million - ru2->ru_stime.tv_usec;
+	sec += (usec / million);
+	usec %= million;
+	ru->ru_stime.tv_sec = sec;
+	ru->ru_stime.tv_usec = usec;
 
-	if (s->native)
-		index = *((uint*)(returnAddress - 4));
-	else
-		index = (uint)returnAddress;
-	assert (0 <= index and index <= s->maxFrameIndex);
-	layout = &(s->frameLayouts[index]);
-	assert (layout->numBytes > 0);
-	return layout;
+	ru->ru_maxrss = max(ru1->ru_maxrss, ru2->ru_maxrss);
+	ru->ru_ixrss = max(ru1->ru_ixrss, ru2->ru_ixrss);
+	ru->ru_idrss = max(ru1->ru_idrss, ru2->ru_idrss);
+	ru->ru_isrss = max(ru1->ru_isrss, ru2->ru_isrss);
+	ru->ru_minflt = ru1->ru_minflt - ru2->ru_minflt;
+	ru->ru_majflt = ru1->ru_majflt - ru2->ru_majflt;
+	ru->ru_nswap = ru1->ru_nswap - ru2->ru_nswap;
+	ru->ru_inblock = ru1->ru_inblock - ru2->ru_inblock;
+	ru->ru_oublock = ru1->ru_oublock - ru2->ru_oublock;
+	ru->ru_msgsnd = ru1->ru_msgsnd - ru2->ru_msgsnd;
+	ru->ru_msgrcv = ru1->ru_msgrcv - ru2->ru_msgrcv;
+	ru->ru_nsignals = ru1->ru_nsignals - ru2->ru_nsignals;
+	ru->ru_nvcsw = ru1->ru_nvcsw - ru2->ru_nvcsw;
+	ru->ru_nivcsw = ru1->ru_nivcsw - ru2->ru_nivcsw;
 }
 
-/* ------------------------------------------------- */
-/*                      Stacks                       */
-/* ------------------------------------------------- */
+static uint rusageTime(struct rusage *ru) {
+	uint	result;
 
-/* stackSlop returns the amount of "slop" space needed between the top of 
- * the stack and the end of the stack space.
- * If you change this, make sure and change Thread_switchTo in ccodegen.h
- *   and thread_switchTo in x86-generate-transfers.sml.
- */
-static inline uint stackSlop (GC_state s) {
-	return 2 * s->maxFrameSize;
+	result = 0;
+	result += 1000 * ru->ru_utime.tv_sec;
+	result += 1000 * ru->ru_stime.tv_sec;
+	result += ru->ru_utime.tv_usec / 1000;
+	result += ru->ru_stime.tv_usec / 1000;
+	return result;
+}
+
+/* Return time as number of milliseconds. */
+static inline uint currentTime () {
+	struct rusage	ru;
+
+	fixedGetrusage(RUSAGE_SELF, &ru);
+	return (rusageTime(&ru));
+}
+
+/* ---------------------------------------------------------------- */
+/*                              Stacks                              */
+/* ---------------------------------------------------------------- */
+
+/* stackSlop returns the amount of "slop" space needed between the top of 
+ * the stack and the end of the stack space.
+ * If you change this, make sure and change Thread_switchTo in ccodegen.h
+ *   and thread_switchTo in x86-generate-transfers.sml.
+ */
+static inline uint stackSlop (GC_state s) {
+	return 2 * s->maxFrameSize;
 }
 
 static inline uint initialStackSize (GC_state s) {
@@ -445,9 +468,7 @@
 /* If you change this, make sure and change Thread_switchTo in ccodegen.h
  *   and thread_switchTo in x86-generate-transfers.sml.
  */
-static inline pointer
-stackBottom (GC_stack stack)
-{
+static inline pointer stackBottom (GC_stack stack) {
 	return ((pointer)stack) + sizeof (struct GC_stack);
 }
 
@@ -455,9 +476,7 @@
 /* If you change this, make sure and change Thread_switchTo in ccodegen.h
  *   and thread_switchTo in x86-generate-transfers.sml.
  */
-static inline pointer
-stackTop(GC_stack stack)
-{
+static inline pointer stackTop (GC_stack stack) {
 	return stackBottom(stack) + stack->used;
 }
 
@@ -465,52 +484,61 @@
 /* If you change this, make sure and change Thread_switchTo in ccodegen.h
  *   and thread_switchTo in x86-generate-transfers.sml.
  */
-static inline pointer
-stackLimit(GC_state s, GC_stack stack)
-{
-	return stackBottom(stack) + stack->reserved - stackSlop(s);
+static inline pointer stackLimit (GC_state s, GC_stack stack) {
+	return stackBottom (stack) + stack->reserved - stackSlop (s);
 }
 
-/* Number of bytes used by the stack. */
-/* If you change this, make sure and change Thread_switchTo in ccodegen.h
- *   and thread_switchTo in x86-generate-transfers.sml.
- */
-static inline uint
-currentStackUsed (GC_state s)
-{
-	return s->stackTop - s->stackBottom;
+static inline bool stackIsEmpty (GC_stack stack) {
+	return 0 == stack->used;
 }
 
-static inline bool
-stackIsEmpty (GC_stack stack)
-{
-	return 0 == stack->used;
+static inline GC_frameLayout * getFrameLayout (GC_state s, word returnAddress) {
+	GC_frameLayout *layout;
+	uint index;
+
+	if (s->native)
+		index = *((uint*)(returnAddress - 4));
+	else
+		index = (uint)returnAddress;
+	assert (0 <= index and index <= s->maxFrameIndex);
+	layout = &(s->frameLayouts[index]);
+	assert (layout->numBytes > 0);
+	return layout;
 }
 
-static inline uint
-topFrameSize (GC_state s, GC_stack stack)
-{
+static inline uint topFrameSize (GC_state s, GC_stack stack) {
 	GC_frameLayout *layout;
 	
 	assert (not (stackIsEmpty (stack)));
-	layout = getFrameLayout(s, *(word*)(stackTop (stack) - WORD_SIZE));
+	layout = getFrameLayout (s, *(word*)(stackTop (stack) - WORD_SIZE));
 	return layout->numBytes;
 }
 
+static inline uint stackNeedsReserved (GC_state s, GC_stack stack) {
+	return stack->used + stackSlop (s) - topFrameSize (s, stack);
+}
+
 /* stackTopIsOk ensures that when this stack becomes current that 
  * the stackTop is less than the stackLimit.
  */
-static inline bool
-stackTopIsOk (GC_state s, GC_stack stack)
-{
+static inline bool stackTopIsOk (GC_state s, GC_stack stack) {
 	return stackTop (stack) 
 		       	<= stackLimit (s, stack) 
 			+ (stackIsEmpty (stack) ? 0 : topFrameSize (s, stack));
 }
 
-static inline GC_stack
-newStack (GC_state s, uint size)
-{
+static inline pointer object (GC_state s, uint header, uint bytesRequested) {
+	pointer result;
+
+	assert (s->frontier + bytesRequested <= s->limit);
+	assert (isWordAligned (bytesRequested));
+	*(uint*)s->frontier = header;
+	result = s->frontier + HEADER_SIZE;
+	s->frontier += bytesRequested;
+	return result;
+}
+
+static inline GC_stack newStack (GC_state s, uint size) {
 	GC_stack stack;
 
 	stack = (GC_stack) object (s, STACK_HEADER, stackBytes (size));
@@ -521,9 +549,7 @@
 	return stack;
 }
 
-inline void
-GC_setStack (GC_state s)
-{
+inline void setStack (GC_state s) {
 	GC_stack stack;
 
 	stack = s->currentThread->stack;
@@ -532,73 +558,50 @@
 	s->stackLimit = stackLimit (s, stack);
 }
 
-static inline void
-stackCopy (GC_stack from, GC_stack to)
-{
+static inline void switchToThread (GC_state s, GC_thread t) {
+	s->currentThread = t;
+	setStack(s);
+}
+
+static inline void stackCopy (GC_stack from, GC_stack to) {
 	assert (from->used <= to->reserved);
 	to->used = from->used;
 	memcpy (stackBottom (to), stackBottom (from), from->used);
 }
 
-/* ------------------------------------------------- */
-/*                 computeSemiSize                   */
-/* ------------------------------------------------- */
+/* Number of bytes used by the stack. */
+/* If you change this, make sure and change Thread_switchTo in ccodegen.h
+ *   and thread_switchTo in x86-generate-transfers.sml.
+ */
+static inline uint
+currentStackUsed (GC_state s)
+{
+	return s->stackTop - s->stackBottom;
+}
 
-#define LIVE_RATIO_MIN 1.25
+/* ---------------------------------------------------------------- */
+/*                          foreachGlobal                           */
+/* ---------------------------------------------------------------- */
 
-enum {
-	GROW_RATIO = 3,
-	LIVE_RATIO = 8,	/* The desired live ratio. */
-	SHRINK_RATIO = 20,
-};
+typedef void (*GC_pointerFun) (GC_state s, pointer *p);
 
-/*
- * For computing the semispace size (y) based on the live amount (x), there are
- * three possibilities, depending on x.  
- *
- * Let R = s->ramSlop * s->totalRam
- * 
- * Case 1:  x * (1 + LIVE_RATIO) <= R
- * 	The semispace will easily fit in memory with the live ratio and
- * 	there will be enough space to do a GC in memory, which requires having
- * 	the entire old space (of size y = LIVE_RATIO * x) plus the amount of
- *      live data in memory (x) in new space.
- *   In this case, set y = x * LIVE_RATIO.
- * 
- * Case 2: R < x * (1 + LIVE_RATIO) and x * (1 + LIVE_RATIO_MIN) <= R
- * 	The semispace will not fit into memory with the live ratio, but there
- * 	is still enough space that we can hope to do the GC in memory if
- * 	we use a smaller live ratio.
- *   In this case set y = R - x.   Thus, x + y = R, and we can still do the
- *   GC in memory.
- * 
- * Case 3: R < x * (1 + LIVE_RATIO_MIN)
- * 	Trying to do the GC in memory would require too small of a live ratio,
- * 	so we're gonna page.  Use a small live ratio to keep the working set
- * 	small. 
- *    In this case, set y = LIVE_RATIO_MIN * x.
- */
+static inline void maybeCall (GC_pointerFun f, GC_state s, pointer *pp) {
+	if (GC_isPointer (*pp))
+		f (s, pp);
+}
 
-static W32 computeSemiSize (GC_state s, W64 live) {
-	W32 res;
+/* Apply f to each global pointer into the heap. */
+static inline void foreachGlobal (GC_state s, GC_pointerFun f)
+{
+	int i;
 
-	if (live <= s->liveThresh1)
-		res = min (live * LIVE_RATIO, s->halfMem);
-	else if (live <= s->liveThresh2)
-	        res = min (s->ramSlop * s->totalRam - live, s->halfMem);
-	else if (live <= s->liveThresh3)
-		res = live * LIVE_RATIO_MIN;
-	else
-		res = s->totalRam + s->totalSwap;
-	if (s->maxHeap > 0 and res > s->maxHeap / 2)
-		res = s->maxHeap / 2;
-	return roundPage (s, res);
+ 	for (i = 0; i < s->numGlobals; ++i)
+		maybeCall (f, s, &s->globals [i]);
+	maybeCall (f, s, (pointer*)&s->currentThread);
+	maybeCall (f, s, (pointer*)&s->savedThread);
+	maybeCall (f, s, (pointer*)&s->signalHandler);
 }
 
-/* ------------------------------------------------- */
-/*                    arrayNumBytes                  */
-/* ------------------------------------------------- */
-
 /* The number of bytes in an array, not including the header. */
 static inline uint
 arrayNumBytes (pointer p, 
@@ -617,41 +620,15 @@
 	return result;
 }
 
-static inline void
-maybeCall (GC_pointerFun f, GC_state s, pointer *pp)
-{
-	if (GC_isPointer (*pp))
-		f (s, pp);
-}
-
-/* ------------------------------------------------- */
-/*                 GC_foreachGlobal                  */
-/* ------------------------------------------------- */
-
-/* Apply f to each global pointer into the heap. */
-inline void
-GC_foreachGlobal (GC_state s, GC_pointerFun f)
-{
-	int i;
-
- 	for (i = 0; i < s->numGlobals; ++i)
-		maybeCall (f, s, &s->globals [i]);
-	maybeCall (f, s, (pointer*)&s->currentThread);
-	maybeCall (f, s, (pointer*)&s->savedThread);
-	maybeCall (f, s, (pointer*)&s->signalHandler);
-}
-
-/* ------------------------------------------------- */
-/*             GC_foreachPointerInObject             */
-/* ------------------------------------------------- */
-/*
- * Apply f to each pointer in the object p, where p points at the first
- * data word in the object.
+/* ---------------------------------------------------------------- */
+/*                      foreachPointerInObject                      */
+/* ---------------------------------------------------------------- */
+/* foreachPointerInObject (s, f, p) applies f to each pointer in the object
+ * pointer to by p.
  * Returns pointer to the end of object, i.e. just past object.
  */
-inline pointer
-GC_foreachPointerInObject (GC_state s, GC_pointerFun f, pointer p)
-{
+
+inline pointer foreachPointerInObject (GC_state s, GC_pointerFun f, pointer p) {
 	word header;
 	uint numPointers;
 	uint numNonPointers;
@@ -663,8 +640,19 @@
 		fprintf(stderr, "foreachPointerInObject p = 0x%x  header = 0x%x  tag = %s  numNonPointers = %d  numPointers = %d\n", 
 			(uint)p, header, tagToString (tag), 
 			numNonPointers, numPointers);
-	switch (tag) {
-	case ARRAY_TAG: { 
+	if (NORMAL_TAG == tag) {
+		pointer max;
+
+		p += toBytes (numNonPointers);
+		max = p + toBytes (numPointers);
+		/* Apply f to all internal pointers. */
+		for ( ; p < max; p += POINTER_SIZE) {
+			if (DEBUG_DETAILED)
+				fprintf(stderr, "p = 0x%08x  *p = 0x%08x\n",
+						(uint)p, (uint)*p);
+			maybeCall(f, s, (pointer*)p);
+		}
+	} else if (ARRAY_TAG == tag) {
 		uint numBytes;
 		pointer max;
 
@@ -697,25 +685,7 @@
 			}
 		}
 		assert(p == max);
-	}
-		break;
-	case NORMAL_TAG: {
-		pointer max;
-
-		p += toBytes (numNonPointers);
-		max = p + toBytes (numPointers);
-		/* Apply f to all internal pointers. */
-		for ( ; p < max; p += POINTER_SIZE) {
-			if (DEBUG_DETAILED)
-				fprintf(stderr, "p = 0x%08x  *p = 0x%08x\n",
-						(uint)p, (uint)*p);
-			maybeCall(f, s, (pointer*)p);
-		}
-	}
-		break;
-	default:
-		assert (STACK_TAG == tag);
-	{
+	} else {
 		GC_stack stack;
 		pointer top, bottom;
 		int i;
@@ -723,9 +693,10 @@
 		GC_frameLayout *layout;
 		GC_offsets frameOffsets;
 
+		assert (STACK_TAG == tag);
 		stack = (GC_stack)p;
-		bottom = stackBottom(stack);
-		top = stackTop(stack);
+		bottom = stackBottom (stack);
+		top = stackTop (stack);
 		assert(stack->used <= stack->reserved);
 		while (top > bottom) {
 			/* Invariant: top points just past a "return address". */
@@ -752,20 +723,17 @@
 		assert(top == bottom);
 		p += sizeof(struct GC_stack) + stack->reserved;
 	}
-	}
 	return p;
 }
 
-/* ------------------------------------------------- */
-/*                      toData                       */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                              toData                              */
+/* ---------------------------------------------------------------- */
 
 /* If p points at the beginning of an object, then toData p returns a pointer 
  * to the start of the object data.
  */
-static inline pointer
-toData (pointer p)
-{
+static inline pointer toData (pointer p) {
 	word header;	
 
 	header = *(word*)p;
@@ -777,24 +745,24 @@
 		return p + GC_NORMAL_HEADER_SIZE;
 }
 
-/* ------------------------------------------------- */
-/*             GC_foreachPointerInRange              */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                      foreachPointerInRange                       */
+/* ---------------------------------------------------------------- */
 
-/* Apply f to each pointer between front and *back, which should be a 
+/* foreachPointerInRange (s, front, back, f)
+ * Apply f to each pointer between front and *back, which should be a 
  * contiguous sequence of objects, where front points at the beginning of
  * the first object and *back points just past the end of the last object.
  * f may increase *back (for example, this is done by forward).
  */
 
-static inline void
-GC_foreachPointerInRange (GC_state s, pointer front, pointer *back,
-				GC_pointerFun f)
-{
+static inline void foreachPointerInRange (GC_state s, pointer front, 
+						pointer *back,
+						GC_pointerFun f) {
 	pointer b;
 
 	if (DEBUG_DETAILED)
-		fprintf (stderr, "GC_foreachPointerInRange  front = 0x%08x  *back = 0x%08x\n",
+		fprintf (stderr, "foreachPointerInRange  front = 0x%08x  *back = 0x%08x\n",
 				(uint)front, (uint)*back);
 	b = *back;
 	assert (front <= b);
@@ -804,20 +772,20 @@
 	       		if (DEBUG_DETAILED)
 				fprintf (stderr, "front = 0x%08x  *back = 0x%08x\n",
 						(uint)front, (uint)*back);
-			front = GC_foreachPointerInObject(s, f, toData (front));
+			front = foreachPointerInObject(s, f, toData (front));
 		}
 		b = *back;
 	}
 	assert(front == *back);
 }
 
-/* ------------------------------------------------- */
-/*                     invariant                     */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                            invariant                             */
+/* ---------------------------------------------------------------- */
 
 #ifndef NODEBUG
 
-static inline bool GC_isInFromSpace (GC_state s, pointer p) {
+static inline bool isInFromSpace (GC_state s, pointer p) {
  	return (s->base <= p and p < s->frontier);
 }
 
@@ -825,7 +793,7 @@
 assertIsInFromSpace (GC_state s, pointer *p) 
 {
 #ifndef NODEBUG
-	unless (GC_isInFromSpace (s, *p))
+	unless (isInFromSpace (s, *p))
 		die ("gc.c: assertIsInFromSpace (0x%x);\n", (uint)*p);
 #endif
 }
@@ -869,8 +837,8 @@
 			and s->limit == s->base + s->fromSize - LIMIT_SLOP));
 	assert(s->toBase == NULL or s->toSize == s->fromSize);
 	/* Check that all pointers are into from space. */
-	GC_foreachGlobal(s, assertIsInFromSpace);
-	GC_foreachPointerInRange(s, s->base, &s->frontier, assertIsInFromSpace);
+	foreachGlobal(s, assertIsInFromSpace);
+	foreachPointerInRange(s, s->base, &s->frontier, assertIsInFromSpace);
 	/* Current thread. */
 	{
 /*		uint offset; */
@@ -899,186 +867,112 @@
 	return TRUE;
 }
 
-bool
-GC_mutatorInvariant(GC_state s)
-{
+bool mutatorInvariant (GC_state s) {
 	if (DEBUG)
-		GC_display(s, stderr);
-	assert(stackTopIsOk(s, s->currentThread->stack));
-	assert(invariant(s));
+		GC_display (s, stderr);
+	assert (stackTopIsOk (s, s->currentThread->stack));
+	assert (invariant (s));
 	return TRUE;
 }
 #endif /* #ifndef NODEBUG */
 
-/* ------------------------------------------------- */
-/*                      Threads                      */
-/* ------------------------------------------------- */
-
-static inline uint
-threadBytes()
-{
-	return wordAlign(HEADER_SIZE + sizeof(struct GC_thread));
-}
-
-static inline uint
-initialThreadBytes(GC_state s)
-{
-	return threadBytes() + stackBytes(initialStackSize(s));
-}
-
-static inline void
-ensureFree(GC_state s, uint bytesRequested)
-{
-	if (bytesRequested > s->limit - s->frontier) {
-		GC_doGC(s, bytesRequested, 0);
-	}
-}
-
-static inline GC_thread
-newThreadOfSize (GC_state s, uint stackSize)
-{
-	GC_stack stack;
-	GC_thread t;
-
-	ensureFree (s, stackBytes (stackSize) + threadBytes ());
-	stack = newStack (s, stackSize);
-	t = (GC_thread) object (s, THREAD_HEADER, threadBytes ());
-	t->exnStack = BOGUS_EXN_STACK;
-	t->stack = stack;
-	if (DEBUG_DETAILED)
-		fprintf (stderr, "0x%x = newThreadOfSize (%u)\n",
-				(uint)t, stackSize);;
-	return t;
-}
-
-static inline void
-switchToThread(GC_state s, GC_thread t)
-{
-	s->currentThread = t;
-	GC_setStack(s);
-}
-
-static inline GC_thread
-copyThread (GC_state s, GC_thread from, uint size)
-{
-	GC_thread to;
-
-	/* newThreadOfSize may do a GC, which invalidates from.  
-	 * Hence we need to stash from where the GC can find it.
-	 */
-	s->savedThread = from;
-	to = newThreadOfSize (s, size);
-	if (DEBUG_THREADS)
-		fprintf (stderr, "0x%08x = copyThread (0x%08x)\n", 
-				(uint)to, (uint)from);
-	from = s->savedThread;
-	stackCopy (from->stack, to->stack);
-	to->exnStack = from->exnStack;
-	return to;
-}
-
-/* ------------------------------------------------- */
-/*                fromSpace, toSpace                 */
-/* ------------------------------------------------- */
-
-static inline void setLimit(GC_state s) {
+static inline void setLimit (GC_state s) {
 	s->limitPlusSlop = s->base + s->fromSize;
 	s->limit = s->limitPlusSlop - LIMIT_SLOP;
 }
 
-/* ------------------------------------------------- */
-/*                      Signals                      */
-/* ------------------------------------------------- */
-
-static inline void
-blockSignals(GC_state s)
-{
-	sigprocmask(SIG_BLOCK, &s->signalsHandled, NULL);
+static inline void blockSignals (GC_state s) {
+	sigprocmask (SIG_BLOCK, &s->signalsHandled, NULL);
 }
 
-static inline void
-unblockSignals(GC_state s)
-{
-	sigprocmask(SIG_UNBLOCK, &s->signalsHandled, NULL);
+static inline void unblockSignals (GC_state s) {
+	sigprocmask (SIG_UNBLOCK, &s->signalsHandled, NULL);
 }
 
+/* ---------------------------------------------------------------- */
+/*                         enter and leave                          */
+/* ---------------------------------------------------------------- */
+
 /* enter and leave should be called at the start and end of every GC function
  * that is exported to the outside world.  They make sure that signals are
  * blocked for the duration of the function and check the GC invariant
  * They are a bit tricky because of the case when the runtime system is invoked
  * from within an ML signal handler.
  */
-void
-GC_enter(GC_state s)
-{
+void enter (GC_state s) {
 	/* used needs to be set because the mutator has changed s->stackTop. */
-	s->currentThread->stack->used = currentStackUsed(s);
+	s->currentThread->stack->used = currentStackUsed (s);
 	if (DEBUG) 
-		GC_display(s, stderr);
+		GC_display (s, stderr);
 	unless (s->inSignalHandler) {
-		blockSignals(s);
+		blockSignals (s);
 		if (s->limit == 0)
-			setLimit(s);
+			setLimit (s);
 	}
-	assert(invariant(s));
+	assert (invariant (s));
 }
 
-void GC_leave (GC_state s)
+void leave (GC_state s)
 {
-	assert (GC_mutatorInvariant (s));
+	assert (mutatorInvariant (s));
 	if (s->signalIsPending and 0 == s->canHandle)
 		s->limit = 0;
 	unless (s->inSignalHandler)
 		unblockSignals (s);
 }
 
-pointer
-GC_copyCurrentThread (GC_state s)
-{
-	GC_thread t;
-	GC_thread res;
-	
-	if (DEBUG_THREADS)
-		fprintf (stderr, "GC_copyCurrentThread\n");
-	GC_enter (s);
-	t = s->currentThread;
-	res = copyThread (s, t, t->stack->used);
-	assert (res->stack->reserved == res->stack->used);
-	GC_leave (s);
-	if (DEBUG_THREADS)
-		fprintf (stderr, "0x%08x = GC_copyCurrentThread\n", (uint)res);
-	return (pointer)res;
-}
-
-static inline uint
-stackNeedsReserved (GC_state s, GC_stack stack)
-{
-	return stack->used + stackSlop(s) - topFrameSize(s, stack);
+static inline void releaseFromSpace (GC_state s) {
+	if (s->messages)
+		fprintf (stderr, "Releasing from space.\n");
+	release (s->base, s->fromSize);
+	s->base = NULL;
+	s->fromSize = 0;
 }
 
-pointer
-GC_copyThread (GC_state s, GC_thread t)
-{
-	GC_thread res;
-
-	if (DEBUG_THREADS)
-		fprintf (stderr, "GC_copyThread (0x%08x)\n", (uint)t);
-	GC_enter (s);
-	assert (t->stack->reserved == t->stack->used);
-	res = copyThread (s, t, stackNeedsReserved (s, t->stack));
-	GC_leave (s);
-	return (pointer)res;
+static inline void releaseToSpace (GC_state s) {
+	if (0 == s->toSize)
+		return;
+	if (s->messages)
+		fprintf (stderr, "Releasing to space.\n");
+	release (s->toBase, s->toSize);
+	s->toBase = NULL;
+	s->toSize = 0;
 }
 
-extern struct GC_state gcState;
+/* ---------------------------------------------------------------- */
+/*                            GC_display                            */
+/* ---------------------------------------------------------------- */
 
-inline void
-GC_fromSpace(GC_state s)
-{
-	s->base = smmap(s->fromSize);
-	if (s->fromSize > s->maxHeapSizeSeen)
-		s->maxHeapSizeSeen = s->fromSize;
-	setLimit(s);
+void GC_display (GC_state s, FILE *stream) {
+	fprintf (stream, "GC state\n\tbase = 0x%x\n\tfrontier - base = %u\n\tlimit - base = %u\n\tlimit - frontier = %d\n",
+			(uint) s->base, 
+			s->frontier - s->base,
+			s->limit - s->base,
+			s->limit - s->frontier);
+	fprintf (stream, "\tcanHandle = %d\n", s->canHandle);
+	fprintf (stream, "\texnStack = %u  bytesNeeded = %u  reserved = %u  used = %u\n",
+			s->currentThread->exnStack,
+			s->currentThread->bytesNeeded,
+			s->currentThread->stack->reserved,
+			s->currentThread->stack->used);
+	fprintf (stream, "\tstackBottom = %x\nstackTop - stackBottom = %u\nstackLimit - stackTop = %u\n",
+			(uint)s->stackBottom,
+			s->stackTop - s->stackBottom,
+			(s->stackLimit - s->stackTop));
+}
+
+/* ---------------------------------------------------------------- */
+/*                   Semispace memory management                    */
+/* ---------------------------------------------------------------- */
+
+static W32 computeSemiSize (GC_state s, W64 live) {
+	W32 res;
+
+	res = min64 (s->totalRam + s->totalSwap,
+			max64 (live * LIVE_RATIO_MIN, 
+				min64 (s->ramSlop * s->totalRam,
+					live * LIVE_RATIO)));
+	return roundPage (s, res);
 }
 
 /* This toggles back and forth between high and low addresses to decrease
@@ -1121,519 +1015,702 @@
 	return result;
 }
 
-void GC_toSpace (GC_state s) {
-	s->toBase = allocateSemi (s, s->toSize);
- 	if (s->toBase == (void*)-1) 
-		diee("Out of swap space");
+/* prepareToSpace (s, need, minSize) allocates a space of the size necessary to
+ * work with need live data, and ensures that at least minSize is available.
+ * It returns TRUE if it is able to allocate the space, and returns FALSE if it
+ * is unable.  If a reasonable size to space is already there, then
+ * prepareToSpace leaves it.
+ */
+static inline bool prepareToSpace (GC_state s, W64 need, W32 minSize) {
+	W32 backoff, requested;
+	int i;
+
+	requested = computeSemiSize (s, need);
+	if (requested < minSize)
+		requested = minSize;
+	if (s->toSize >= minSize and s->toSize >= requested / 2)
+		/* Tospace is big enough.  Keep it. */
+		return TRUE;
+	else
+		releaseToSpace (s);
+	assert (0 == s->toSize and NULL == s->toBase);
+	s->toSize = requested;
+	backoff = roundPage (s, (requested - minSize) / BACKOFF_TRIES);
+	for (i = 0; i < BACKOFF_TRIES; ++i) {
+		s->toBase = allocateSemi (s, s->toSize);
+		unless ((void*)-1 == s->toBase)
+			return TRUE;
+		s->toBase = (void*)NULL;
+		if (s->messages)
+			fprintf(stderr, "[Requested %luM cannot be satisfied, backing off by %luM (need = %luM).\n",
+				meg (s->toSize), meg (backoff), meg (need));
+		s->toSize -= backoff;
+	}
+	s->toSize = 0;
+	return FALSE;
 }
 
-/* ------------------------------------------------- */
-/*                    getrusage                      */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                    Cheney Copying Collection                     */
+/* ---------------------------------------------------------------- */
 
-int
-fixedGetrusage(int who, struct rusage *rup)
-{
-	struct tms	tbuff;
-	int		res;
-	clock_t		user,
-			sys;
-	static bool	first = TRUE;
-	static long	hz;
+#if METER
+int sizes[25600];
+#endif
 
-	if (first) {
-		first = FALSE;
-		hz = sysconf(_SC_CLK_TCK);
-	}
-	res = getrusage(who, rup);
-	unless (res == 0)
-		return (res);
-	if (times(&tbuff) == -1)
-		diee("Impossible: times() failed");
-	switch (who) {
-	case RUSAGE_SELF:
-		user = tbuff.tms_utime;
-		sys = tbuff.tms_stime;
-		break;
-	case RUSAGE_CHILDREN:
-		user = tbuff.tms_cutime;
-		sys = tbuff.tms_cstime;
-		break;
-	default:
-		die("getrusage() accepted unknown who: %d", who);
-		exit(1);  /* needed to keep gcc from whining. */
-	}
-	rup->ru_utime.tv_sec = user / hz;
-	rup->ru_utime.tv_usec = (user % hz) * (1000000 / hz);
-	rup->ru_stime.tv_sec = sys / hz;
-	rup->ru_stime.tv_usec = (sys % hz) * (1000000 / hz);
-	return (0);
-}
+/* forward (s, pp) forwards the object pointed to by *pp and updates *pp to 
+ * point to the new object. 
+ */
+static inline void forward (GC_state s, pointer *pp) {
+	pointer p;
+	word header;
+	word tag;
 
-static inline void
-rusageZero(struct rusage *ru)
-{
-	memset(ru, 0, sizeof(*ru));
-}
+	if (DEBUG_DETAILED)
+		fprintf(stderr, "forward  pp = 0x%x  *pp = 0x%x\n", (uint)pp, (uint)*pp);
+	assert (isInFromSpace (s, *pp));
+	p = *pp;
+	header = GC_getHeader(p);
+	if (header != FORWARDED) { /* forward the object */
+		uint headerBytes, objectBytes, size, skip;
+		uint numPointers, numNonPointers;
 
-static void
-rusagePlusMax(struct rusage *ru1,
-	      struct rusage *ru2,
-	      struct rusage *ru)
-{
-	const int	million = 1000000;
-	time_t		sec,
-			usec;
+		/* Compute the space taken by the header and object body. */
+		SPLIT_HEADER();
+		if (NORMAL_TAG == tag) { /* Fixed size object. */
+			headerBytes = GC_NORMAL_HEADER_SIZE;
+			objectBytes = toBytes (numPointers + numNonPointers);
+			skip = 0;
+		} else if (ARRAY_TAG == tag) {
+			assert (ARRAY_TAG == tag);
+			headerBytes = GC_ARRAY_HEADER_SIZE;
+			objectBytes = arrayNumBytes (p, numPointers,
+								numNonPointers);
+			skip = 0;
+		} else { /* Stack. */
+			GC_stack stack;
 
-	sec = ru1->ru_utime.tv_sec + ru2->ru_utime.tv_sec;
-	usec = ru1->ru_utime.tv_usec + ru2->ru_utime.tv_usec;
-	sec += (usec / million);
-	usec %= million;
-	ru->ru_utime.tv_sec = sec;
-	ru->ru_utime.tv_usec = usec;
+			headerBytes = STACK_HEADER_SIZE;
+			/* Shrink stacks that don't use a lot of their reserved
+		 	 * space.
+			 */
+			stack = (GC_stack)p;
+			if (stack->used <= stack->reserved / 4)
+				stack->reserved = 
+					wordAlign (max (stack->reserved / 2, 
+							stackNeedsReserved (s, stack)));
+			objectBytes = sizeof (struct GC_stack) + stack->used;
+			skip = stack->reserved - stack->used;
+		}
+		size = headerBytes + objectBytes;
+		assert (s->back + size + skip <= s->toLimit);
+  		/* Copy the object. */
+		if (DEBUG_DETAILED)
+			fprintf (stderr, "copying from 0x%08x to 0x%08x\n",
+					(uint)p, (uint)s->back);
+		copy (p - headerBytes, s->back, size);
+#if METER
+		if (size < sizeof(sizes)/sizeof(sizes[0])) sizes[size]++;
+#endif
+ 		/* Store the forwarding pointer in the old object. */
+		*(word*)(p - WORD_SIZE) = FORWARDED;
+		*(pointer*)p = s->back + headerBytes;
+		/* Update the back of the queue. */
+		s->back += size + skip;
+		assert(isWordAligned((uint)s->back));
+	}
+	*pp = *(pointer*)p;
+	assert(isInToSpace(s, *pp));
+}
 
-	sec = ru1->ru_stime.tv_sec + ru2->ru_stime.tv_sec;
-	usec = ru1->ru_stime.tv_usec + ru2->ru_stime.tv_usec;
-	sec += (usec / million);
-	usec %= million;
-	ru->ru_stime.tv_sec = sec;
-	ru->ru_stime.tv_usec = usec;
+static inline void forwardEachPointerInRange (GC_state s, pointer front,
+						pointer *back) {
+	pointer b;
 
-	ru->ru_maxrss = max(ru1->ru_maxrss, ru2->ru_maxrss);
-	ru->ru_ixrss = max(ru1->ru_ixrss, ru2->ru_ixrss);
-	ru->ru_idrss = max(ru1->ru_idrss, ru2->ru_idrss);
-	ru->ru_isrss = max(ru1->ru_isrss, ru2->ru_isrss);
-	ru->ru_minflt = ru1->ru_minflt + ru2->ru_minflt;
-	ru->ru_majflt = ru1->ru_majflt + ru2->ru_majflt;
-	ru->ru_nswap = ru1->ru_nswap + ru2->ru_nswap;
-	ru->ru_inblock = ru1->ru_inblock + ru2->ru_inblock;
-	ru->ru_oublock = ru1->ru_oublock + ru2->ru_oublock;
-	ru->ru_msgsnd = ru1->ru_msgsnd + ru2->ru_msgsnd;
-	ru->ru_msgrcv = ru1->ru_msgrcv + ru2->ru_msgrcv;
-	ru->ru_nsignals = ru1->ru_nsignals + ru2->ru_nsignals;
-	ru->ru_nvcsw = ru1->ru_nvcsw + ru2->ru_nvcsw;
-	ru->ru_nivcsw = ru1->ru_nivcsw + ru2->ru_nivcsw;
+	b = *back;
+	assert(front <= b);
+	while (front < b) {
+		while (front < b) {
+			assert(isWordAligned((uint)front));
+			front = foreachPointerInObject(s, forward, toData(front));
+		}
+		b = *back;
+	}
+	assert(front == *back);
 }
 
-static void
-rusageMinusMax (struct rusage *ru1,
-		struct rusage *ru2,
-		struct rusage *ru)
-{
-	const int	million = 1000000;
-	time_t		sec,
-			usec;
+static void swapSemis (GC_state s) {
+	pointer p;
+	uint tmp;
 
-	sec = (ru1->ru_utime.tv_sec - ru2->ru_utime.tv_sec) - 1;
-	usec = ru1->ru_utime.tv_usec + million - ru2->ru_utime.tv_usec;
-	sec += (usec / million);
-	usec %= million;
-	ru->ru_utime.tv_sec = sec;
-	ru->ru_utime.tv_usec = usec;
+	p = s->base;
+	s->base = s->toBase;
+	s->toBase = p;
+	tmp = s->fromSize;
+	s->fromSize = s->toSize;
+	s->toSize = tmp;
+}
 
-	sec = (ru1->ru_stime.tv_sec - ru2->ru_stime.tv_sec) - 1;
-	usec = ru1->ru_stime.tv_usec + million - ru2->ru_stime.tv_usec;
-	sec += (usec / million);
-	usec %= million;
-	ru->ru_stime.tv_sec = sec;
-	ru->ru_stime.tv_usec = usec;
+static inline void cheneyCopy (GC_state s) {
+	pointer front;
 
-	ru->ru_maxrss = max(ru1->ru_maxrss, ru2->ru_maxrss);
-	ru->ru_ixrss = max(ru1->ru_ixrss, ru2->ru_ixrss);
-	ru->ru_idrss = max(ru1->ru_idrss, ru2->ru_idrss);
-	ru->ru_isrss = max(ru1->ru_isrss, ru2->ru_isrss);
-	ru->ru_minflt = ru1->ru_minflt - ru2->ru_minflt;
-	ru->ru_majflt = ru1->ru_majflt - ru2->ru_majflt;
-	ru->ru_nswap = ru1->ru_nswap - ru2->ru_nswap;
-	ru->ru_inblock = ru1->ru_inblock - ru2->ru_inblock;
-	ru->ru_oublock = ru1->ru_oublock - ru2->ru_oublock;
-	ru->ru_msgsnd = ru1->ru_msgsnd - ru2->ru_msgsnd;
-	ru->ru_msgrcv = ru1->ru_msgrcv - ru2->ru_msgrcv;
-	ru->ru_nsignals = ru1->ru_nsignals - ru2->ru_nsignals;
-	ru->ru_nvcsw = ru1->ru_nvcsw - ru2->ru_nvcsw;
-	ru->ru_nivcsw = ru1->ru_nivcsw - ru2->ru_nivcsw;
+	s->numCopyingGCs++;
+ 	if (DEBUG or s->messages) {
+		fprintf (stderr, "Copying GC.\n");
+		fprintf (stderr, "fromSpace = %x  toSpace = %x\n",
+				(uint)s->base, (uint)s->toBase);
+	 	fprintf (stderr, "fromSpace size = %s", 
+				uintToCommaString (s->fromSize));
+		fprintf (stderr, "  toSpace size = %s\n",
+				uintToCommaString (s->toSize));
+	}
+	assert (s->toBase != (void*)NULL);
+	assert (s->toSize >= s->fromSize);
+	s->back = s->toBase;
+	s->toLimit = s->toBase + s->toSize;
+	front = s->back;
+	foreachGlobal (s, forward);
+	forwardEachPointerInRange (s, front, &s->back);
+	swapSemis (s);
+	s->frontier = s->back;
 }
 
-static uint
-rusageTime(struct rusage *ru)
-{
-	uint	result;
+/* ---------------------------------------------------------------- */
+/*                       Depth-first Marking                        */
+/* ---------------------------------------------------------------- */
 
-	result = 0;
-	result += 1000 * ru->ru_utime.tv_sec;
-	result += 1000 * ru->ru_stime.tv_sec;
-	result += ru->ru_utime.tv_usec / 1000;
-	result += ru->ru_stime.tv_usec / 1000;
-	return result;
+static inline uint *arrayCounterp (pointer a) {
+	return ((uint*)a - 3);
 }
 
-/* Return time as number of milliseconds. */
-static inline uint
-currentTime()
-{
-	struct rusage	ru;
+static inline uint arrayCounter (pointer a) {
+	return *(arrayCounterp (a));
+}
 
-	fixedGetrusage(RUSAGE_SELF, &ru);
-	return (rusageTime(&ru));
+static inline bool isMarked (pointer p) {
+	return MARK_MASK & GC_getHeader (p);
 }
 
-/* ------------------------------------------------- */
-/*                   initSignalStack                 */
-/* ------------------------------------------------- */
+static bool modeEqMark (MarkMode m, pointer p) {
+	return (((MARK_MODE == m) and isMarked (p))
+		or ((UNMARK_MODE == m) and not isMarked (p)));
+}
 
-static inline void
-initSignalStack(GC_state s)
-{
-#if (defined (__linux__) || defined (__FreeBSD__))
-        static stack_t altstack;
-	size_t ss_size = roundPage(s, SIGSTKSZ);
-	size_t psize = s->pageSize;
-	void *ss_sp = ssmmap(2 * ss_size, psize, psize);
-	altstack.ss_sp = ss_sp + ss_size;
-	altstack.ss_size = ss_size;
-	altstack.ss_flags = 0;
-	sigaltstack(&altstack, NULL);
-#endif
-}
-
-/* ------------------------------------------------- */
-/*                  Initialization                   */
-/* ------------------------------------------------- */
-
-/* set fromSize.
- * size must not be an approximation, because setHeapParams will die if it
- * can't set fromSize big enough.
+/* mark (s, p) sets all the mark bits in the object graph pointed to by p. 
+ * If the mode is MARK, it sets the bits to 1.
+ * If the mode is UNMARK, it sets the bits to 0.
+ * It returns the amount marked.
  */
-inline void
-GC_setHeapParams(GC_state s, uint size)
-{
-	if (s->useFixedHeap) {
-		if (0 == s->fromSize)
-			s->fromSize = roundPage(s, s->ramSlop * s->totalRam);
-	        s->fromSize = roundPage(s, s->fromSize / 2);
+W32 mark (GC_state s, pointer root, MarkMode mode) {
+	pointer cur;  /* The current object being marked. */
+	GC_offsets frameOffsets;
+	Header* headerp;
+	Header header;
+	uint index;
+	GC_frameLayout *layout;
+	pointer max; /* The end of the pointers in an object. */
+	pointer next; /* The next object to mark. */
+	Header *nextHeaderp;
+	Header nextHeader;
+	W32 numBytes;
+	uint numNonPointers;
+	uint numPointers;
+	pointer prev; /* The previous object on the mark stack. */
+	W32 size;
+	uint tag;
+	pointer todo; /* A pointer to the pointer in cur to next. */
+	pointer top; /* The top of the next stack frame to mark. */
+
+	if (modeEqMark (mode, root))
+		/* Object has already been marked. */
+		return 0;
+	size = 0;
+	cur = root;
+	prev = NULL;
+	headerp = GC_getHeaderp (cur);
+	header = *(Header*)headerp;
+	goto mark;	
+markNext:
+	/* cur is the object that was being marked.
+	 * prev is the mark stack.
+	 * next is the unmarked object to be marked.
+	 * todo is a pointer to the pointer inside cur that points to next.
+	 * headerp points to the header of next.
+	 * header is the header of next.
+	 */
+	if (DEBUG_MARK_COMPACT)
+		fprintf (stderr, "markNext  cur = 0x%08x  next = 0x%08x  prev = 0x%08x  todo = 0x%08x\n",
+				(uint)cur, (uint)next, (uint)prev, (uint)todo);
+	assert (not modeEqMark (mode, next));
+	assert (header == GC_getHeader (next));
+	assert (headerp == GC_getHeaderp (next));
+	assert (*(pointer*) todo == next);
+	*(pointer*)todo = prev;
+	prev = cur;
+	cur = next;
+mark:
+	if (DEBUG_MARK_COMPACT)
+		fprintf (stderr, "mark  cur = 0x%08x  prev = 0x%08x  mode = %s\n",
+				(uint)cur, (uint)prev,
+				(mode == MARK_MODE) ? "mark" : "unmark");
+	/* cur is the object to mark. 
+	 * prev is the mark stack.
+	 * headerp points to the header of cur.
+	 * header is the header of cur.
+	 */
+	assert (not modeEqMark (mode, cur));
+	assert (header == GC_getHeader (cur));
+	assert (headerp == GC_getHeaderp (cur));
+	header = (MARK_MODE == mode)
+			? header | MARK_MASK
+			: header & ~MARK_MASK;
+	SPLIT_HEADER();
+	if (NORMAL_TAG == tag) {
+		todo = cur + toBytes (numNonPointers);
+		max = todo + toBytes (numPointers);
+		size += GC_NORMAL_HEADER_SIZE + (max - cur);
+		index = 0;
+markInNormal:
+		assert (todo <= max);
+		if (DEBUG_MARK_COMPACT)
+			fprintf (stderr, "markInNormal  index = %d\n", index);
+		if (todo == max) {
+			*headerp = header & ~COUNTER_MASK;
+			goto ret;
+		}
+		next = *(pointer*)todo;
+		if (not GC_isPointer (next)) {
+markNextInNormal:
+			todo += POINTER_SIZE;
+			index++;
+			goto markInNormal;
+		}
+		nextHeaderp = GC_getHeaderp (next);
+		nextHeader = *nextHeaderp;
+		if ((nextHeader & MARK_MASK)
+			== (MARK_MODE == mode ? MARK_MASK : 0))
+			goto markNextInNormal;
+		*headerp = (header & ~COUNTER_MASK) |
+				(index << COUNTER_SHIFT);
+		headerp = nextHeaderp;
+		header = nextHeader;
+		goto markNext;
+	} else if (ARRAY_TAG == tag) {
+		assert (0 == GC_arrayNumElements (cur)
+				? 0 == numPointers
+				: TRUE);
+		numBytes = arrayNumBytes (cur, numPointers, numNonPointers);
+		size += GC_ARRAY_HEADER_SIZE + numBytes;
+		*headerp = header;
+		if (0 == numBytes or 0 == numPointers)
+			goto ret;
+		assert (0 == numNonPointers);
+		max = cur + numBytes;
+		todo = cur;
+		index = 0;
+markInArray:
+		if (DEBUG_MARK_COMPACT)
+			fprintf (stderr, "markInArray index = %d\n", index);
+		if (todo == max) {
+			*arrayCounterp (cur) = 0;
+			goto ret;
+		}
+		next = *(pointer*)todo;
+		if (not GC_isPointer (next)) {
+markNextInArray:
+			todo += POINTER_SIZE;
+			index++;
+			goto markInArray;
+		}
+		nextHeaderp = GC_getHeaderp (next);
+		nextHeader = *nextHeaderp;
+		if ((nextHeader & MARK_MASK)
+			== (MARK_MODE == mode ? MARK_MASK : 0))
+			goto markNextInArray;
+		*arrayCounterp (cur) = index;
+		headerp = nextHeaderp;
+		header = nextHeader;
+		goto markNext;
 	} else {
-		s->fromSize = computeSemiSize (s, size);
+		assert (STACK_TAG == tag);
+		*headerp = header;
+		size += stackBytes (((GC_stack)cur)->reserved);
+		top = stackTop ((GC_stack)cur);
+		assert (((GC_stack)cur)->used <= ((GC_stack)cur)->reserved);
+markInStack:
+		/* Invariant: top points just past the return address of the
+		 * frame to be marked.
+		 */
+		assert (stackBottom ((GC_stack)cur) <= top);
+		if (DEBUG_MARK_COMPACT)
+			fprintf (stderr, "markInStack  top = %d\n",
+					top - stackBottom ((GC_stack)cur));
+					
+		if (top == stackBottom ((GC_stack)(cur)))
+			goto ret;
+		index = 0;
+		layout = getFrameLayout (s, *(word*) (top - WORD_SIZE));
+		frameOffsets = layout->offsets;
+		((GC_stack)cur)->markTop = top;
+markInFrame:
+		if (index == frameOffsets [0]) {
+			top -= layout->numBytes;
+			goto markInStack;
+		}
+		todo = top - layout->numBytes + frameOffsets [index + 1];
+		next = *(pointer*)todo;
+		if (DEBUG_MARK_COMPACT)
+			fprintf (stderr, 
+				"    offset %u  todo 0x%08x  next = 0x%08x\n", 
+				frameOffsets [index + 1], 
+				(uint)todo, (uint)next);
+		if (not GC_isPointer (next)) {
+			index++;
+			goto markInFrame;
+		}
+		nextHeaderp = GC_getHeaderp (next);
+		nextHeader = *nextHeaderp;
+		if ((nextHeader & MARK_MASK)
+			== (MARK_MODE == mode ? MARK_MASK : 0)) {
+			index++;
+			goto markInFrame;
+		}
+		((GC_stack)cur)->markIndex = index;		
+		headerp = nextHeaderp;
+		header = nextHeader;
+		goto markNext;
+	}
+	assert (FALSE);
+ret:
+	/* Done marking cur, continue with prev.
+	 * Need to set the pointer in the prev object that pointed to cur 
+	 * to point back to prev, and restore prev.
+ 	 */
+	if (DEBUG_MARK_COMPACT)
+		fprintf (stderr, "return  cur = 0x%08x  prev = 0x%08x\n",
+				(uint)cur, (uint)prev);
+	assert (modeEqMark (mode, cur));
+	if (NULL == prev)
+		return size;
+	headerp = GC_getHeaderp (prev);
+	header = *headerp;
+	SPLIT_HEADER();
+	if (NORMAL_TAG == tag) {
+		todo = prev + toBytes (numNonPointers);
+		max = todo + toBytes (numPointers);
+		index = (header & COUNTER_MASK) >> COUNTER_SHIFT;
+		todo += index * POINTER_SIZE;
+		next = cur;
+		cur = prev;
+		prev = *(pointer*)todo;
+		*(pointer*)todo = next;
+		todo += POINTER_SIZE;
+		index++;
+		goto markInNormal;
+	} else if (ARRAY_TAG == tag) {
+		max = prev + arrayNumBytes (prev, numPointers, numNonPointers);
+		index = arrayCounter (prev);
+		todo = prev + index * POINTER_SIZE;
+		next = cur;
+		cur = prev;
+		prev = *(pointer*)todo;
+		*(pointer*)todo = next;
+		todo += POINTER_SIZE;
+		index++;
+		goto markInArray;
+	} else {
+		assert (STACK_TAG == tag);
+		next = cur;
+		cur = prev;
+		index = ((GC_stack)cur)->markIndex;
+		top = ((GC_stack)cur)->markTop;
+		layout = getFrameLayout (s, *(word*) (top - WORD_SIZE));
+		frameOffsets = layout->offsets;
+		todo = top - layout->numBytes + frameOffsets [index + 1];
+		prev = *(pointer*)todo;
+		*(pointer*)todo = next;
+		index++;
+		goto markInFrame;
 	}
-	if (size + LIMIT_SLOP > s->fromSize)
-		die("Out of memory (setHeapParams).");
+	assert (FALSE);
 }
 
-static int processor_has_sse2=0;
+/* ---------------------------------------------------------------- */
+/*                 Jonkers Mark-compact Collection                  */
+/* ---------------------------------------------------------------- */
 
-static void readProcessor() {
-#if 0
-	int status = system("/bin/cat /proc/cpuinfo | /bin/egrep -q '^flags.*:.* mmx .*xmm'");
-  
-	if (status==0)
-		processor_has_sse2=1;
-	else
-		processor_has_sse2=0;
-#endif
-	processor_has_sse2=0;
+static inline void markGlobal (GC_state s, pointer *pp) {
+	mark (s, *pp, MARK_MODE);
 }
 
-/*
- * Set RAM and SWAP size.
- * Note the total amount of RAM is multiplied by ramSlop so that we don't
- * use all of memory or start swapping.
- *
- * Ensure that s->totalRam + s->totalSwap < 4G.
- */
-
-#if (defined (__linux__))
-#include <sys/sysinfo.h>
-/* struct sysinfo copied from /usr/include/linux/kernel.h on a 2.4 kernel
- * because we need mem_unit.
- * On older kernels, it will be guaranteed to be zero, and we test for that
- * below.
- */
-struct Msysinfo {
-	long uptime;			/* Seconds since boot */
-	unsigned long loads[3];		/* 1, 5, and 15 minute load averages */
-	unsigned long totalram;		/* Total usable main memory size */
-	unsigned long freeram;		/* Available memory size */
-	unsigned long sharedram;	/* Amount of shared memory */
-	unsigned long bufferram;	/* Memory used by buffers */
-	unsigned long totalswap;	/* Total swap space size */
-	unsigned long freeswap;		/* swap space still available */
-	unsigned short procs;		/* Number of current processes */
-	unsigned long totalhigh;	/* Total high memory size */
-	unsigned long freehigh;		/* Available high memory size */
-	unsigned int mem_unit;		/* Memory unit size in bytes */
-	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
-};
-static inline void
-setMemInfo(GC_state s)
-{
-	struct Msysinfo	sbuf;
-	W32 maxMem;
-	W64 tmp;
-	uint memUnit;
-
-	maxMem = 0x100000000llu - s->pageSize;
-	unless (0 == sysinfo((struct sysinfo*)&sbuf))
-		diee("sysinfo failed");
-	memUnit = sbuf.mem_unit;
-	/* On 2.2 kernels, mem_unit is not defined, but will be zero, so go
-	 * ahead and pretend it is one.
-	 */
-	if (0 == memUnit)
-		memUnit = 1;
-	tmp = memUnit * (W64)sbuf.totalram;
-	s->totalRam = (tmp > (W64)maxMem) ? maxMem : (W32)tmp;
-	maxMem = maxMem - s->totalRam;
-	tmp = memUnit * (W64)sbuf.totalswap;
-	s->totalSwap = (tmp > (W64)maxMem) ? maxMem : (W32)tmp;
-}
-#elif (defined (__CYGWIN__))
-#include <windows.h>
-static inline void
-setMemInfo(GC_state s)
-{
-	MEMORYSTATUS ms; 
-
-	GlobalMemoryStatus(&ms); 
-	s->totalRam = ms.dwTotalPhys;
-	s->totalSwap = ms.dwTotalPageFile;
+static inline void unmarkGlobal (GC_state s, pointer *pp) {
+       	mark (s, *pp, UNMARK_MODE);
 }
-#elif (defined (__FreeBSD__))
-
-/* returns total amount of swap available */
-static int 
-get_total_swap() 
-{
-        static char buffer[256];
-        FILE *file;
-        int total_size = 0;
-
-        file = popen("/usr/sbin/swapinfo -k | awk '{ print $4; }'\n", "r");
-        if (file == NULL) 
-                diee("swapinfo failed");
-
-        /* skip header */
-        fgets(buffer, 255, file);
-
-        while (fgets(buffer, 255, file) != NULL) { 
-                total_size += atoi(buffer);
-        }
 
-        pclose(file);
+static inline void threadInternal (GC_state s, pointer *pp) {
+	Header *headerp;
 
-        return total_size * 1024;
+	if (FALSE)
+		fprintf (stderr, "threadInternal pp = 0x%08x  *pp = 0x%08x  header = 0x%08x\n",
+				(uint)pp, *(uint*)pp, (uint)GC_getHeader (*pp));
+	headerp = GC_getHeaderp (*pp);
+	*(Header*)pp = *headerp;
+	*headerp = (Header)pp;
 }
 
-/* returns total amount of memory available */
-static int 
-get_total_mem() 
+static inline uint objectSize (GC_state s, pointer p)
 {
-        static char buffer[256];
-        FILE *file;
-        int total_size = 0;
-
-        file = popen("/sbin/sysctl hw.physmem | awk '{ print $2; }'\n", "r");
-        if (file == NULL) 
-                diee("sysctl failed");
-
-
-        fgets(buffer, 255, file);
-
-        pclose(file);
-
-        return atoi(buffer);
-}
+	uint headerBytes, objectBytes;
+       	word header;
+	uint tag, numPointers, numNonPointers;
 
-static inline void
-setMemInfo(GC_state s)
-{
-	s->totalRam = get_total_mem();
-	s->totalSwap = get_total_swap();
+	header = GC_getHeader(p);
+	SPLIT_HEADER();
+	if (NORMAL_TAG == tag) { /* Fixed size object. */
+		headerBytes = GC_NORMAL_HEADER_SIZE;
+		objectBytes = toBytes (numPointers + numNonPointers);
+	} else if (STACK_TAG == tag) { /* Stack. */
+		headerBytes = STACK_HEADER_SIZE;
+		objectBytes = sizeof(struct GC_stack) + ((GC_stack)p)->reserved;
+	} else { /* Array. */
+		assert(ARRAY_TAG == tag);
+		headerBytes = GC_ARRAY_HEADER_SIZE;
+		objectBytes = arrayNumBytes(p, numPointers, numNonPointers);
+	}
+	return headerBytes + objectBytes;
 }
 
-#endif /* definition of setMemInfo */
-
-static void newWorld(GC_state s)
-{
-	int i;
-
-	assert (isWordAligned (sizeof (struct GC_thread)));
-	for (i = 0; i < s->numGlobals; ++i)
-		s->globals[i] = (pointer)BOGUS_POINTER;
-	GC_setHeapParams (s, s->bytesLive + initialThreadBytes (s));
-	assert (s->bytesLive + initialThreadBytes (s) + LIMIT_SLOP 
-			<= s->fromSize);
-	GC_fromSpace (s);
-	s->frontier = s->base;
-	s->toSize = s->fromSize;
-	GC_toSpace (s); /* FIXME: Why does toSpace need to be allocated? */
-	switchToThread (s, newThreadOfSize (s, initialStackSize (s)));
-	assert (initialThreadBytes (s) == s->frontier - s->base);
-	assert (s->frontier + s->bytesLive <= s->limit);
-	assert (GC_mutatorInvariant (s));
-}
+static inline void updateForwardPointers (GC_state s) {
+	pointer back;
+	pointer front;
+	uint gap;
+	pointer endOfLastMarked;
+	Header header;
+	Header *headerp;
+	pointer p;
+	uint size;
 
-static void usage(string s) {
-	die("Usage: %s [@MLton [fixed-heap n[{k|m}]] [gc-messages] [gc-summary] [load-world file] [ram-slop x] --] args", 
-		s);
-}
+	if (DEBUG_MARK_COMPACT)
+		fprintf (stderr, "updateForwardPointers\n");
+	back = s->frontier;
+	front = s->base;
+	endOfLastMarked = front;
+	gap = 0;
+updateObject:
+	if (front == back)
+		goto done;
+	headerp = (Header*)front;
+	header = *headerp;
+	if (0 == header) {
+		/* We're looking at an array.  Move to the header. */
+		p = front + 3 * WORD_SIZE;
+		headerp = (Header*)(p - WORD_SIZE);
+		header = *headerp;
+	} else 
+		p = front + WORD_SIZE;
+	if (1 == (1 & header)) {
+		/* It's a header */
+		if (MARK_MASK & header) {
+			/* It is marked, but has no forward pointers. 
+			 * Thread internal pointers.
+			 */
+thread:
+			size = objectSize (s, p);
+			if (DEBUG_MARK_COMPACT)
+	       			fprintf (stderr, "threading 0x%08x of size %u\n", 
+						(uint)p, size);
+			if (front - endOfLastMarked >= 4 * WORD_SIZE) {
+				/* Compress all of the unmarked into one string.
+				 * We require 4 * WORD_SIZE space to be available
+				 * because that is the smallest possible array.
+				 * You cannot use 3 * WORD_SIZE because even
+				 * zero-length arrays require an extra word for
+				 * the forwarding pointer.  If you did use
+				 * 3 * WORD_SIZE, updateBackwardPointersAndSlide
+				 * would skip the extra word and be completely
+				 * busted.
+				 */
+				if (DEBUG_MARK_COMPACT)
+					fprintf (stderr, "compressing from 0x%08x to 0x%08x (length = %u)\n",
+							(uint)endOfLastMarked,
+							(uint)front,
+							front - endOfLastMarked);
+				*(uint*)endOfLastMarked = 0;
+				*(uint*)(endOfLastMarked + WORD_SIZE) = 
+					front - endOfLastMarked - 3 * WORD_SIZE;
+				*(uint*)(endOfLastMarked + 2 * WORD_SIZE) =
+					GC_objectHeader (STRING_TYPE_INDEX);
+			}
+			front += size;
+			endOfLastMarked = front;
+			foreachPointerInObject (s, threadInternal, p);
+			goto updateObject;
+		} else {
+			/* It's not marked. */
+			size = objectSize (s, p);
+			gap += size;
+			front += size;
+			goto updateObject;
+		}
+	} else {
+		pointer new;
 
-static float stringToFloat(string s) {
-	float f;
+		assert (0 == (3 & header));
+		/* It's a pointer.  This object must be live.  Fix all the
+		 * forward pointers to it, store its header, then thread
+                 * its internal pointers.
+		 */
+		new = p - gap;
+		do {
+			pointer cur;
 
-	sscanf(s, "%f", &f);
-	return f;
+			cur = (pointer)header;
+			header = *(word*)cur;
+			*(word*)cur = (word)new;
+		} while (0 == (1 & header));
+		*headerp = header;
+		goto thread;
+	}
+	assert (FALSE);
+done:
+	return;
 }
 
-static uint stringToBytes(string s) {
-	char c;
-	uint result;
-	int i, m;
-	
-	result = 0;
-	i = 0;
+static inline void updateBackwardPointersAndSlide (GC_state s) {
+	pointer back;
+	pointer front;
+	uint gap;
+	Header header;
+	pointer p;
+	uint size;
+	uint totalSize;
 
-	while ((c = s[i++]) != '\000') {
-		switch (c) {
-		case 'm':
-			if (s[i] == '\000') 
-				result = result * 1048576;
-			else return 0;
-			break;
-		case 'k':
-			if (s[i] == '\000') 
-				result = result * 1024;
-			else return 0;
-			break;
-		default:
-			m = (int)(c - '0');
-			if (0 <= m and m <= 9)
-				result = result * 10 + m;
-			else return 0;
+	if (DEBUG_MARK_COMPACT)
+		fprintf (stderr, "updateBackwardPointersAndSlide\n");
+	back = s->frontier;
+	front = s->base;
+	gap = 0;
+	totalSize = 0;
+updateObject:
+	if (front == back)
+		goto done;
+	header = *(word*)front;
+	if (0 == header) {
+		/* We're looking at an array.  Move to the header. */
+		p = front + 3 * WORD_SIZE;
+		header = *(Header*)(p - WORD_SIZE);
+	} else 
+		p = front + WORD_SIZE;
+	if (1 == (1 & header)) {
+		/* It's a header */
+		if (MARK_MASK & header) {
+			/* It is marked, but has no backward pointers to it.
+			 * Unmark it.
+			 */
+unmark:
+			*GC_getHeaderp (p) = header & ~MARK_MASK;
+			size = objectSize (s, p);
+			if (DEBUG_MARK_COMPACT)
+				fprintf (stderr, "unmarking 0x%08x of size %u\n", 
+						(uint)p, size);
+			/* slide */
+			unless (0 == gap)
+				if (DEBUG_MARK_COMPACT)
+					fprintf (stderr, "sliding 0x%08x down %u\n",
+							(uint)front, gap);
+			copy (front, front - gap, size);
+			totalSize += size;
+			front += size;
+			goto updateObject;
+		} else {
+			/* It's not marked. */
+			size = objectSize (s, p);
+			if (DEBUG_MARK_COMPACT)
+				fprintf (stderr, "skipping 0x%08x of size %u\n",
+						(uint)p, size);
+			gap += size;
+			front += size;
+			goto updateObject;
 		}
+	} else {
+		pointer new;
+
+		/* It's a pointer.  This object must be live.  Fix all the
+		 * forward pointers to it.  Then unmark it.
+		 */
+		new = p - gap;
+		do {
+			pointer cur;
+
+			assert (0 == (3 & header));
+			cur = (pointer)header;
+			header = *(word*)cur;
+			*(word*)cur = (word)new;
+		} while (0 == (1 & header));
+		/* The header will be stored by umark. */
+		goto unmark;
 	}
-	
-	return result;
+	assert (FALSE);
+done:
+	s->frontier = s->base + totalSize;
+	return;
 }
 
-int
-GC_init(GC_state s, int argc, char **argv,
-			void (*loadGlobals)(FILE *file)) {
-	char *worldFile;
-	int i;
-
-	s->pageSize = getpagesize();
-	initSignalStack(s);
-	s->bytesAllocated = 0;
-	s->bytesCopied = 0;
-	s->canHandle = 0;
-	s->currentThread = BOGUS_THREAD;
-	rusageZero(&s->ru_gc);
-	s->inSignalHandler = FALSE;
-	s->isOriginal = TRUE;
-	s->maxBytesLive = 0;
-	s->maxHeap = 0;
-	s->maxHeapSizeSeen = 0;
-	s->maxPause = 0;
-	s->maxStackSizeSeen = 0;
-	s->messages = FALSE;
-	s->numGCs = 0;
-	s->numLCs = 0;
-	s->ramSlop = 0.80;
-	s->savedThread = BOGUS_THREAD;
-	s->signalHandler = BOGUS_THREAD;
-	sigemptyset(&s->signalsHandled);
-	s->signalIsPending = FALSE;
-	sigemptyset(&s->signalsPending);
-	s->startTime = currentTime();
-	s->summary = FALSE;
- 	readProcessor();
-	worldFile = NULL;
-	i = 1;
-	if (argc > 1 and (0 == strcmp (argv [1], "@MLton"))) {
-		bool done;
+static inline void markCompact (GC_state s) {
+	if (s->messages)
+		fprintf (stderr, "Mark-compact GC.\n");
+	s->numMarkCompactGCs++;
+	foreachGlobal (s, markGlobal);
+	foreachGlobal (s, threadInternal);
+	updateForwardPointers (s);
+	updateBackwardPointersAndSlide (s);
+	if (s->messages)
+		fprintf (stderr, "Mark-compact GC done.\n");
+}
 
-		/* process @MLton args */
-		i = 2;
-		done = FALSE;
-		while (!done) {
-			if (i == argc)
-				usage(argv[0]);
-			else {
-				string arg;
+/* ---------------------------------------------------------------- */
+/*                          Heap Resizing                           */
+/* ---------------------------------------------------------------- */
 
-				arg = argv[i];
-				if (0 == strcmp(arg, "fixed-heap")) {
-					++i;
-					if (i == argc)
-						usage(argv[0]);
-					s->useFixedHeap = TRUE;
-					s->fromSize =
-						stringToBytes(argv[i++]);
-				} else if (0 == strcmp(arg, "gc-messages")) {
-					++i;
-					s->messages = TRUE;
-				} else if (0 == strcmp(arg, "gc-summary")) {
-					++i;
-					s->summary = TRUE;
-				} else if (0 == strcmp(arg, "load-world")) {
-					++i;
-					s->isOriginal = FALSE;
-					if (i == argc) 
-						usage(argv[0]);
-					worldFile = argv[i++];
-				} else if (0 == strcmp(arg, "max-heap")) {
-					++i;
-					if (i == argc) 
-						usage(argv[0]);
-					s->useFixedHeap = FALSE;
-					s->maxHeap = stringToBytes(argv[i++]);
-				} else if (0 == strcmp(arg, "ram-slop")) {
-					++i;
-					if (i == argc)
-						usage(argv[0]);
-					s->ramSlop =
-						stringToFloat(argv[i++]);
-				} else if (0 == strcmp(arg, "--")) {
-					++i;
-					done = TRUE;
-				} else if (i > 1)
-					usage(argv[0]);
-			        else done = TRUE;
-			}
-		}
+static inline void shrinkFromSpace (GC_state s, W32 keep) {
+	assert (keep <= s->fromSize);
+	if (0 == keep)
+		releaseFromSpace (s);
+	else if (keep < s->fromSize) {
+		if (DEBUG or s->messages)
+			fprintf (stderr, 
+				"Shrinking from space at %x to %u bytes.\n",
+				(uint)s->base , (uint)keep);
+		decommit (s->base + keep, s->fromSize - keep);
+		s->fromSize = keep;
 	}
-	setMemInfo(s);
-	s->halfMem = 
-		roundPage (s, s->ramSlop * (s->totalRam + s->totalSwap) / 2);
-	s->halfRam = roundPage (s, s->ramSlop * s->totalRam / 2);
-	s->liveThresh1 = s->ramSlop * s->totalRam / (1 + LIVE_RATIO);
-	s->liveThresh2 = s->ramSlop * s->totalRam / (1 + LIVE_RATIO_MIN);
-	s->liveThresh3 = (s->totalRam + s->totalSwap) / LIVE_RATIO_MIN;
-	if (DEBUG)
-		fprintf(stderr, "totalRam = %u  totalSwap = %u\n",
-			s->totalRam, s->totalSwap);
-	if (s->isOriginal)
-		newWorld(s);
-	else
-		GC_loadWorld(s, worldFile, loadGlobals);
-	return i;
 }
 
-#if METER
-int sizes[25600];
-#endif
-
-/* ------------------------------------------------- */
-/*                   translateHeap                   */
-/* ------------------------------------------------- */
+static inline void shrinkToSpace (GC_state s, W32 keep) {
+	assert (keep <= s->toSize);
+	if (0 == keep)
+		releaseToSpace (s);
+	else if (keep < s->toSize) {
+		if (DEBUG or s->messages)
+			fprintf (stderr, 
+				"Shrinking to space at %x to %u bytes.\n",
+				(uint)s->toBase , (uint)keep);
+		decommit (s->toBase + keep, s->toSize - keep);
+		s->toSize = keep;
+	}
+}
 
-static void translatePointer(GC_state s, pointer *p) {
+static void translatePointer (GC_state s, pointer *p) {
 	if (s->translateUp)
 		*p += s->translateDiff;
 	else
 		*p -= s->translateDiff;
 }
 
-void GC_translateHeap (GC_state s, pointer from, pointer to, uint size) {
+/* Translate all pointers to the heap from within the stack and the heap for
+ * a heap that has moved from s->base == old to s->base.
+ */
+static void translateHeap (GC_state s, pointer from, pointer to, uint size) {
 	pointer limit;
 
 	if (s->messages)
@@ -1650,440 +1727,175 @@
 		s->translateUp = FALSE;
 	}
 	/* Translate globals and heap. */
-	GC_foreachGlobal (s, translatePointer);
+	foreachGlobal (s, translatePointer);
 	limit = to + size;
-	GC_foreachPointerInRange (s, to, &limit, translatePointer);
+	foreachPointerInRange (s, to, &limit, translatePointer);
 }
 
-static inline void copy (pointer src, pointer dst, uint size) {
-	uint	*to,
-		*from,
-		*limit;
+/* Resize from space and to space, guaranteeing that at least need bytes are
+ * available in from space and that to space is either the same size as from
+ * space or is unmapped.
+ */
+static inline void resizeHeap (GC_state s, W64 need) {
+	bool grow;
+	W32 keep;
+
+	grow = FALSE;
+	keep = 0;
+	if (need >= s->fromSize)
+		grow = TRUE;
+	else if (need * LIVE_RATIO_MIN >= s->ramSlop * s->totalRam) {
+		/* Paging matters.  Change the heap size if the 
+		 * desired size (LIVE_RATIO * needed) is very different
+		 * from fromSize.
+		 */
+		if (need * 1.5 <= s->fromSize)
+			keep = need * LIVE_RATIO_MIN;
+		else if (need * 1.1 >= s->fromSize)
+			grow = TRUE;
+		else
+			keep = s->fromSize;
+	} else if (need * 10 >= s->ramSlop * s->totalRam) {
+		/* Go ahead and use all of memory. */
+		if (s->fromSize >= s->ramSlop * s->totalRam)
+			keep = s->ramSlop * s->totalRam;
+		else
+			grow = TRUE;
+	} else {
+		if (need * 20 <= s->fromSize)
+			keep = need * 8;
+		else if (need * 3 >= s->fromSize)
+			grow = TRUE;
+		else
+			keep = s->fromSize;
+	}
+	if (DEBUG_RESIZING);
+		fprintf (stderr, "need = %u  keep = %u\n",
+				(uint)need, (uint)keep);
+	/* Shrink or grow the heap. */
+	if (not grow)
+		shrinkFromSpace (s, roundPage (s, keep));
+	else {
+		pointer old;
 
-	if (DEBUG_DETAILED)
-		fprintf (stderr, "copy (0x%08x, 0x%08x, %u)\n",
-				(uint)src, (uint)dst, size);
-	assert (isWordAligned((uint)src));
-	assert (isWordAligned((uint)dst));
-	assert (isWordAligned(size));
-	assert (dst <= src or src + size <= dst);
-	if (src == dst)
-		return;
-	from = (uint*)src;
-	to = (uint*)dst;
-	limit = (uint*)(src + size);
-	until (from == limit)
-		*to++ = *from++;
+		if (DEBUG_RESIZING)
+			fprintf (stderr, "Growing from space.\n");
+		releaseToSpace (s);
+		old = s->base;
+		shrinkFromSpace (s, roundPage (s, s->bytesLive));
+		/* Allocate a space of the desired size. */
+		if (prepareToSpace (s, need, need)) {
+			copy (s->base, s->toBase, s->bytesLive);
+			releaseFromSpace (s);
+		} else {
+			/* Write the heap to a file and try again. */
+			FILE *stream;
+			char template[80] = "/tmp/FromSpaceXXXXXX";
+			int fd;
+	
+			fd = smkstemp (template);
+			sclose (fd);
+			if (s->messages)
+				fprintf (stderr, "Paging fromSpace to %s.\n", 
+						template);
+			stream = sfopen (template, "wb");
+			sfwrite (s->base, 1, s->bytesLive, stream);
+			sfclose (stream);
+			releaseFromSpace (s);
+			if (prepareToSpace (s, need, need)) {
+				stream = sfopen (template, "rb");
+				sfread (s->toBase, 1, s->bytesLive, stream);
+				sfclose (stream);
+				sunlink (template);
+			} else {
+				sunlink (template);
+				if (s->messages)
+					showMem ();
+				die ("Out of memory.  Need %llu bytes.\n", need);
+			}
+		}
+		translateHeap (s, old, s->toBase, s->bytesLive);
+		swapSemis (s);
+		setStack (s);
+		s->frontier = s->base + s->bytesLive;
+	}
+	setLimit (s);
+	/* Resize to space. */
+	if (0 == s->toSize)
+		/* nothing */ ;
+	else if (/* toSpace is smaller than fromSpace, so we won't be
+             	  * able to use it for the next GC anyways. 
+ 		  */
+ 		s->toSize < s->fromSize
+		or /* Holding on to toSpace may cause paging. */
+		s->fromSize + s->toSize > s->ramSlop * s->totalRam)
+		releaseToSpace (s);
+	else
+		shrinkToSpace (s, s->fromSize);
+	assert (s->fromSize >= need);
+	assert (0 == s->toSize or s->fromSize == s->toSize);
 }
 
-/* ------------------------------------------------- */
-/*                      forward                      */
-/* ------------------------------------------------- */
-/*
- * Forward the object pointed to by *pp.
- * Update *pp to point to the new object. 
- */
-static inline void
-forward(GC_state s, pointer *pp)
-{
-	pointer p;
-	word header;
-	word tag;
+static void growStack (GC_state s) {
+	uint size;
+	GC_stack stack;
 
-	if (DEBUG_DETAILED)
-		fprintf(stderr, "forward  pp = 0x%x  *pp = 0x%x\n", (uint)pp, (uint)*pp);
-	assert (GC_isInFromSpace (s, *pp));
-	p = *pp;
-	header = GC_getHeader(p);
-	if (header != FORWARDED) { /* forward the object */
-		uint headerBytes, objectBytes, size, skip;
-		uint numPointers, numNonPointers;
+	size = 2 * s->currentThread->stack->reserved;
+	assert (stackBytes (size) <= s->limitPlusSlop - s->frontier);
+	if (DEBUG or s->messages)
+		fprintf (stderr, "Growing stack to size %u.\n", size);
+	if (size > s->maxStackSizeSeen)
+		s->maxStackSizeSeen = size;
+	stack = newStack (s, size);
+	stackCopy (s->currentThread->stack, stack);
+	s->currentThread->stack = stack;
+	setStack (s);
+}
 
-		/* Compute the space taken by the header and object body. */
-		SPLIT_HEADER();
-		if (NORMAL_TAG == tag) { /* Fixed size object. */
-			headerBytes = GC_NORMAL_HEADER_SIZE;
-			objectBytes = toBytes(numPointers + numNonPointers);
-			if (VERIFY_MARK)
-				s->forwardSize += headerBytes + objectBytes;
-			if (DEBUG_MARK_SIZE)
-				fprintf (stderr, "0x%08x normal of size %u\n",
-						(uint)p, 
-						headerBytes + objectBytes);
-			skip = 0;
-		} else if (STACK_TAG == tag) { /* Stack. */
-			GC_stack stack;
+uint getStackBytesRequested (GC_state s) {
+	return (stackTopIsOk (s, s->currentThread->stack))
+		? 0 
+		: stackBytes (2 * s->currentThread->stack->reserved);
+}
 
-			headerBytes = STACK_HEADER_SIZE;
-			/* Resize stacks not being used as continuations. */
-			stack = (GC_stack)p;
-			if (VERIFY_MARK)
-				s->forwardSize += stackBytes (stack->reserved);
-			if (DEBUG_MARK_SIZE)
-				fprintf (stderr, "0x%08x stack of size %u\n",
-						(uint)p,
-						stackBytes (stack->reserved));
-			if (stack->used != stack->reserved) {
-				if (4 * stack->used <= stack->reserved)
-					stack->reserved = stack->reserved / 2;
-				else if (4 * stack->used > 3 * stack->reserved)
-					stack->reserved = stack->reserved * 2;
-				stack->reserved = 
-					wordAlign(max(stack->reserved, 
-							stackNeedsReserved(s, stack)));
-				if (stack->reserved > s->maxStackSizeSeen)
-					s->maxStackSizeSeen = stack->reserved;
-				assert(stackTopIsOk(s, stack));
-			}
-			objectBytes = sizeof (struct GC_stack) + stack->used;
-			skip = stack->reserved - stack->used;
-		} else { /* Array. */
-			assert (ARRAY_TAG == tag);
-			headerBytes = GC_ARRAY_HEADER_SIZE;
-			objectBytes = arrayNumBytes (p, numPointers,
-								numNonPointers);
-			if (VERIFY_MARK)
-				s->forwardSize += headerBytes + objectBytes;
-			if (DEBUG_MARK_SIZE)
-				fprintf (stderr, "0x%08x array of size %u\n",
-						(uint)p,
-						headerBytes + objectBytes);
-			skip = 0;
-		} 
-		size = headerBytes + objectBytes;
-		/* This check is necessary, because toSpace may be smaller
-		 * than fromSpace, and so the copy may fail.
-		 */
-  		if (s->back + size + skip > s->toLimit) {
-			if (s->messages) {
-				showMem ();
-				fprintf (stderr, "size=%u skip=%u remaining=%u s->fromSize=%u s->toSize=%u headerBytes=%d objectBytes=%u header=%x\n",
-						size, skip, s->toLimit - s->back, s->fromSize, s->toSize, headerBytes, objectBytes, header);
-			}
-			die ("Out of memory (forward).\nDiagnostic: probably a RAM problem.");
-		}
-  		/* Copy the object. */
-		if (DEBUG_DETAILED)
-			fprintf (stderr, "copying from 0x%08x to 0x%08x\n",
-					(uint)p, (uint)s->back);
-		copy (p - headerBytes, s->back, size);
-#if METER
-		if (size < sizeof(sizes)/sizeof(sizes[0])) sizes[size]++;
-#endif
- 		/* Store the forwarding pointer in the old object. */
-		*(word*)(p - WORD_SIZE) = FORWARDED;
-		*(pointer*)p = s->back + headerBytes;
-		/* Update the back of the queue. */
-		s->back += size + skip;
-		assert(isWordAligned((uint)s->back));
-	}
-	*pp = *(pointer*)p;
-	assert(isInToSpace(s, *pp));
-}
-
-static inline void forwardEachPointerInRange(GC_state s, pointer front,
-						pointer *back) {
-	pointer b;
-
-	b = *back;
-	assert(front <= b);
-	while (front < b) {
-		while (front < b) {
-			assert(isWordAligned((uint)front));
-			front = GC_foreachPointerInObject(s, forward, toData(front));
-		}
-		b = *back;
-	}
-	assert(front == *back);
-}
-
-static inline uint objectSize (GC_state s, pointer p)
-{
-	uint headerBytes, objectBytes;
-       	word header;
-	uint tag, numPointers, numNonPointers;
-
-	header = GC_getHeader(p);
-	SPLIT_HEADER();
-	if (NORMAL_TAG == tag) { /* Fixed size object. */
-		headerBytes = GC_NORMAL_HEADER_SIZE;
-		objectBytes = toBytes (numPointers + numNonPointers);
-	} else if (STACK_TAG == tag) { /* Stack. */
-		headerBytes = STACK_HEADER_SIZE;
-		objectBytes = sizeof(struct GC_stack) + ((GC_stack)p)->reserved;
-	} else { /* Array. */
-		assert(ARRAY_TAG == tag);
-		headerBytes = GC_ARRAY_HEADER_SIZE;
-		objectBytes = arrayNumBytes(p, numPointers, numNonPointers);
-	}
-	return headerBytes + objectBytes;
-}
-
-/* ------------------------------------------------- */
-/*                       doGC                        */
-/* ------------------------------------------------- */
-
-static inline void prepareToSpace (GC_state s, uint bytesRequested, 
-					uint stackBytesRequested) {
-	W64 needed;
-	W32 backoff, requested;
-	int i;
-
-	if (s->useFixedHeap)
-		return;
-	needed = (W64)s->bytesLive + (W64)bytesRequested 
-			+ (W64)stackBytesRequested;
-	requested = computeSemiSize (s, needed);
-	if (0 != s->toSize) {
-		assert (s->fromSize == s->toSize);
-		if (s->toSize < requested / 2)
-			/* The heap needs to grow. */
-			releaseToSpace (s);
-		else
-			/* The heap is fine. */
-			return;
-	}
-	assert (0 == s->toSize and NULL == s->toBase);
-	if (requested < s->fromSize)
-		requested = s->fromSize;
-	s->toSize = requested;
-	backoff = roundPage (s, requested / BACKOFF_TRIES);
-	for (i = 0; i < BACKOFF_TRIES; ++i) {
-		s->toBase = allocateSemi (s, s->toSize);
-		unless ((void*)-1 == s->toBase)
-			return;
-		s->toBase = (void*)NULL;
-		if (s->messages)
-			fprintf(stderr, "[Requested %luM cannot be satisfied, backing off by %luM (need = %luM).\n",
-				meg(s->toSize), meg(backoff), meg(needed));
-		s->toSize -= backoff;
-	}
-	if (s->messages)
-		showMem ();
-	die ("Out of swap space: cannot obtain %u bytes.", s->toSize);
-}
-
-static inline void resizeHeap (GC_state s, uint bytesRequested) {
-	W64 needed;
-	uint keep;
-
-	if (s->useFixedHeap)
-		return;
-	needed = (W64)s->bytesLive + bytesRequested;
-	/* Determine the size of new space (now fromSpace). */
-	if (needed <= s->liveThresh1)
-		/* If the ratio of live data to semispace size is too low,
-		 * shrink new space.
-		 */
-		keep = needed * SHRINK_RATIO < (W64)s->fromSize
-			? roundPage (s, needed * LIVE_RATIO)
-			: s->fromSize;
-	else 
-		/* We're in the region where paging is relevant, so shrink
-		 * the heap to whatever we think is the optimal size.
-		 */
-		keep = computeSemiSize (s, needed);
-	if (keep < s->fromSize) {
-		if (DEBUG or s->messages)
-			fprintf(stderr, 
-				"Shrinking new space at %x to %u bytes.\n",
-				(uint)s->base , keep);
-		decommit (s->base + keep, s->fromSize - keep);
-		s->fromSize = keep;
-	}
-	/* Determine the size of old space, and possibly unmap it. */
-	if (s->toSize < s->fromSize)
-		/* toSpace is smaller than fromSpace, so we won't be
-		 * able to use it for the next GC anyways.
-		 */
-		keep = 0;
-	else if (s->fromSize > s->halfRam)
-		/* Holding on to toSpace may cause swapping. */
-		keep = 0;
-        /* s->fromSize <= s->toSize and s->fromSize < s->halfRam */
-	else if (GROW_RATIO * needed > (W64)s->toSize)
-		/* toSpace is too small */
-		keep = 0;
-	else
-		/* toSpace is about right, so make it the same size as
-		 * fromSpace.
-		 */
-		keep = s->fromSize;
-	assert (keep <= s->toSize);
-	if (keep < s->toSize) {
-		if (DEBUG or s->messages)
-			fprintf(stderr, 
-				"Shrinking old space at %x to %u bytes.\n",
-				(uint)s->toBase , keep);
-		assert(keep <= s->toSize);
-		if (0 == keep)
-			releaseToSpace (s);
-		else {
-			decommit(s->toBase + keep, s->toSize - keep);
-			s->toSize = keep;
-		}
-	}
-}
-
-#if (defined (__CYGWIN__))
-static inline void shrinkFromSpace (GC_state s, W32 keep) {
-
-}
-#elif (defined (__linux__) || defined (__FreeBSD__))
-static inline void shrinkFromSpace (GC_state s, W32 keep) {
-	if (keep < s->fromSize) {
-		decommit (s->base + keep, s->fromSize - keep);
-		s->fromSize = keep;
-	}
-}
-#endif
-
-static void swapSemis (GC_state s) {
-	pointer p;
-	uint tmp;
-
-	p = s->base;
-	s->base = s->toBase;
-	s->toBase = p;
-	tmp = s->fromSize;
-	s->fromSize = s->toSize;
-	s->toSize = tmp;
-}
-
-static void pageFromSpace (GC_state s, uint bytesRequested) {
-	FILE *stream;
-	char template[80] = "/tmp/FromSpaceXXXXXX";
-	int fd;
-	pointer old;
-
-	fd = smkstemp (template);
-	sclose (fd);
-	if (s->messages)
-		fprintf (stderr, "Paging fromSpace to %s.\n", template);
-	stream = sfopen (template, "wb");
-	old = s->base;
-	sfwrite (s->base, 1, s->bytesLive, stream);
-	sfclose (stream);
-	releaseFromSpace (s);
-	prepareToSpace (s, bytesRequested, 0);
-	if (s->bytesLive + bytesRequested > s->toSize) {
-		sunlink (template);
-		if (s->messages)
-			showMem ();
-		die ("Out of memory.  Unable to allocate semispace.  bytesRequested = %s.", uintToCommaString (bytesRequested));
-	}
-	swapSemis (s);
-	stream = sfopen (template, "rb");
-	sfread (s->base, 1, s->bytesLive, stream);
-	sfclose (stream);
-	sunlink (template);
-	GC_translateHeap (s, old, s->base, s->bytesLive);
-}
-
-/* If the GC didn't create enough space, then release toSpace, shrink 
- * fromSpace as much as possible, shifting it to a new location.  Then
- * try to allocate the semispace again.
- */
-static void shiftFromSpace (GC_state s, uint bytesRequested) {
-	pointer old, semi;
-	W32 keep, size;
-
-	if (s->messages)
-		fprintf (stderr, "Shifting.\n");
-	unless (0 == s->toSize)
-		releaseToSpace (s);
-	old = s->base;
-	size = s->fromSize;
-	/* Allocate a new from space that is just large enough. */
-	keep = roundPage (s, s->bytesLive);
-	s->fromSize = keep;
-	semi = allocateSemi (s, keep);
-	if ((void*)-1 == semi)
-		pageFromSpace (s, bytesRequested);
-	else {
-		s->base = semi;
-		memcpy (s->base, old, keep);
-		GC_translateHeap (s, old, s->base, s->bytesLive);
-		release (old, size);
-		/* Allocate a new toSpace and copy to it. */
-		prepareToSpace (s, bytesRequested, 0);
-		if (s->bytesLive + bytesRequested > s->toSize) {
-			releaseToSpace (s);
-			pageFromSpace (s, bytesRequested);
-		} else {
-			old = s->base;
-			memcpy (s->toBase, s->base, keep);
-			GC_translateHeap (s, old, s->toBase, s->bytesLive);
-			release (s->base, keep);
-			s->base = s->toBase;
-			s->fromSize = s->toSize;
-			s->toBase = NULL;
-			s->toSize = 0;
-		}
-	}
-	GC_setStack (s);
-	s->frontier = s->base + s->bytesLive;
-	setLimit (s);
-	assert (bytesRequested <= s->limit - s->frontier);
-}
+/* ---------------------------------------------------------------- */
+/*                        Garbage Collection                        */
+/* ---------------------------------------------------------------- */
 
-static inline void markCompact (GC_state s);
-
-void GC_doGC(GC_state s, uint bytesRequested, uint stackBytesRequested) {
+void doGC (GC_state s, uint bytesRequested) {
 	uint gcTime;
+	W64 need;
 	uint size;
-	pointer front;
+	uint stackBytesRequested;
 	struct rusage ru_start, ru_finish, ru_total;
 	
-	assert(invariant(s));
+	assert (invariant (s));
 	if (DEBUG or s->messages)
-		fprintf(stderr, "Starting gc.  bytesRequested = %u\n",
-				bytesRequested);
+		fprintf (stderr, "Starting gc.  bytesRequested = %u\n",
+					bytesRequested);
 	fixedGetrusage (RUSAGE_SELF, &ru_start);
-	prepareToSpace (s, bytesRequested, stackBytesRequested);
-	assert (s->toBase != (void*)NULL);
- 	if (DEBUG or s->messages) {
-		fprintf (stderr, "fromSpace = %x  toSpace = %x\n",
-				(uint)s->base, (uint)s->toBase);
-	 	fprintf (stderr, "fromSpace size = %s", 
-				uintToCommaString(s->fromSize));
-		fprintf (stderr, "  toSpace size = %s\n",
-				uintToCommaString(s->toSize));
-	}
- 	s->numGCs++;
- 	s->bytesAllocated += s->frontier - s->base - s->bytesLive;
-	/* The actual GC. */
-	if (VERIFY_MARK)
-		markCompact (s);
-	s->back = s->toBase;
-	s->toLimit = s->toBase + s->toSize;
-	front = s->back;
-	if (VERIFY_MARK)
-		s->forwardSize = 0;
-	GC_foreachGlobal (s, forward);
-	forwardEachPointerInRange (s, front, &s->back);
-	if (VERIFY_MARK and s->markSize != s->forwardSize) {
-		fprintf (stderr, "markSize = %u  forwardSize = %u\n",
-				s->markSize, s->forwardSize);
-		die ("bug");
-	}
+ 	s->bytesAllocated += s->frontier - (s->base + s->bytesLive);
 	size = s->fromSize;
-	swapSemis (s);
-	GC_setStack(s);
-	s->frontier = s->back;
+	stackBytesRequested = getStackBytesRequested (s);
+	need = (W64)s->bytesLive + (W64)bytesRequested 
+			+ (W64)stackBytesRequested;
+        if (not s->useFixedHeap
+		and (W64)s->bytesLive + (W64)s->fromSize 
+			<= s->ramSlop * s->totalRam
+		and prepareToSpace (s, need, s->fromSize))
+		cheneyCopy (s);
+	else
+		markCompact (s);
+	setStack (s);
+	setLimit (s);
 	s->bytesLive = s->frontier - s->base;
 	if (s->bytesLive > s->maxBytesLive)
 		s->maxBytesLive = s->bytesLive;
-	s->bytesCopied += s->bytesLive;
-	setLimit(s);
-	if (bytesRequested > s->limit - s->frontier) {
-		shiftFromSpace (s, bytesRequested);
-	} else {
-		resizeHeap (s, bytesRequested);
-		setLimit (s);
-	}
-	fixedGetrusage(RUSAGE_SELF, &ru_finish);
-	rusageMinusMax(&ru_finish, &ru_start, &ru_total);
+	resizeHeap (s, need);
+	if (stackBytesRequested > 0)
+		growStack (s);
+	fixedGetrusage (RUSAGE_SELF, &ru_finish);
+	rusageMinusMax (&ru_finish, &ru_start, &ru_total);
 	rusagePlusMax (&s->ru_gc, &ru_total, &s->ru_gc);
 	gcTime = rusageTime (&ru_total);
 	s->maxPause = max (s->maxPause, gcTime);
@@ -2095,25 +1907,18 @@
 			100.0 * ((double) s->bytesLive) / size);
 	}
 	if (DEBUG) 
-		GC_display(s, stderr);
-	assert(invariant(s));
+		GC_display (s, stderr);
+	assert (invariant (s));
 }
 
-/* ------------------------------------------------- */
-/*                       GC_gc                       */
-/* ------------------------------------------------- */
-
 void GC_gc (GC_state s, uint bytesRequested, bool force,
 		string file, int line) {
 	uint stackBytesRequested;
 
-	GC_enter (s);
+	enter (s);
 	s->currentThread->bytesNeeded = bytesRequested;
 start:
-	stackBytesRequested =
-		(stackTopIsOk (s, s->currentThread->stack))
-		? 0 
-		: stackBytes (2 * s->currentThread->stack->reserved);
+	stackBytesRequested = getStackBytesRequested (s);
 	if (DEBUG) {
 		fprintf (stderr, "%s %d: ", file, line);
 		fprintf (stderr, "bytesRequested = %u  stackBytesRequested = %u\n",
@@ -2124,26 +1929,12 @@
 		(W64)(W32)s->frontier + (W64)bytesRequested 
 			+ (W64)stackBytesRequested > (W64)(W32)s->limit) {
 		if (s->messages)
-			fprintf(stderr, "%s %d: GC_doGC\n", file, line);
+			fprintf(stderr, "%s %d: doGC\n", file, line);
 		/* This GC will grow the stack, if necessary. */
-		GC_doGC (s, bytesRequested, s->currentThread->stack->reserved);
-	} else if (not (stackTopIsOk (s, s->currentThread->stack))) {
-		uint size;
-		GC_stack stack;
-
-		size = 2 * s->currentThread->stack->reserved;
-		if (DEBUG)
-			fprintf (stderr, "Growing stack to size %u.\n", size);
-		if (size > s->maxStackSizeSeen)
-			s->maxStackSizeSeen = size;
-		/* The newStack can't cause a GC, because we checked above to 
-		 * make sure there was enough space. 
-		 */
-		stack = newStack (s, size);
-		stackCopy (s->currentThread->stack, stack);
-		s->currentThread->stack = stack;
-		GC_setStack (s);
-	} else {
+		doGC (s, bytesRequested);
+	} else if (not (stackTopIsOk (s, s->currentThread->stack)))
+		growStack (s);
+	else {
 		/* Switch to the signal handler thread. */
 		assert (0 == s->canHandle);
 		if (DEBUG_SIGNALS) {
@@ -2167,607 +1958,677 @@
 			goto start;
 	}
 	assert (s->currentThread->bytesNeeded <= s->limit - s->frontier);
-	/* The GC_enter and GC_leave must be outside the start loop.  If they
+	/* The enter and leave must be outside the start loop.  If they
          * were inside and force == TRUE, a signal handler could intervene just
-         * before the GC_enter or just after the GC_leave, which would set 
+         * before the enter or just after the leave, which would set 
          * limit to 0 and cause the while loop to go forever, performing a GC 
          * at each iteration and never switching to the signal handler.
          */
-	GC_leave(s);
+	leave(s);
 }
 
-/* ------------------------------------------------- */
-/*                 GC_createStrings                  */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                         GC_arrayAllocate                         */
+/* ---------------------------------------------------------------- */
 
-void GC_createStrings (GC_state s, struct GC_stringInit inits[]) {
-	pointer frontier;
-	int i;
+static inline W64 w64align (W64 w) {
+ 	return ((w + 3) & ~ 3);
+}
 
-	assert (invariant (s));
-	frontier = s->frontier;
-	for(i = 0; inits[i].str != NULL; ++i) {
-		uint numElements, numBytes;
+pointer GC_arrayAllocate (GC_state s, W32 ensureBytesFree, W32 numElts, 
+				W32 header) {
+	uint numPointers;
+	uint numNonPointers;
+	uint tag;
+	uint eltSize;
+	W64 arraySize64;
+	W32 arraySize;
+	W32 *frontier;
+	W32 *last;
+	pointer res;
+	W32 require;
+	W64 require64;
 
-		numElements = inits[i].size;
-		numBytes = GC_ARRAY_HEADER_SIZE
-			+ ((0 == numElements) 
-				? POINTER_SIZE 
-				: wordAlign(numElements));
-		if (frontier + numBytes >= s->limit)
-			die("Unable to allocate string constant \"%s\".", 
-				inits[i].str);
-		*(word*)frontier = 0; /* counter word */
-		*(word*)(frontier + WORD_SIZE) = numElements;
-		*(word*)(frontier + 2 * WORD_SIZE) = STRING_HEADER;
-		s->globals[inits[i].globalIndex] = 
-			frontier + GC_ARRAY_HEADER_SIZE;
-		if (DEBUG_DETAILED)
-			fprintf (stderr, "allocated string at 0x%x\n",
-					(uint)s->globals[inits[i].globalIndex]);
-		{
-			int j;
+	SPLIT_HEADER();
+	assert ((numPointers == 1 and numNonPointers == 0)
+			or (numPointers == 0 and numNonPointers > 0));
+	eltSize = numPointers * POINTER_SIZE + numNonPointers;
+	arraySize64 = 
+		w64align((W64)eltSize * (W64)numElts + GC_ARRAY_HEADER_SIZE);
+	require64 = arraySize64 + (W64)ensureBytesFree;
+	if (require64 >= 0x100000000llu)
+		die ("Out of memory: cannot allocate %llu bytes.\n",
+			require64);
+	require = (W32)require64;
+	arraySize = (W32)arraySize64;
+	if (DEBUG)
+		fprintf (stderr, "array with %u elts of size %u and total size %u.  ensureBytesFree = %u\n",
+			(uint)numElts, (uint)eltSize, (uint)arraySize,
+			(uint)ensureBytesFree);
+	if (require > s->limitPlusSlop - s->frontier) {
+		enter (s);
+		doGC (s, require);
+		leave (s);
+	}
+	frontier = (W32*)s->frontier;
+	last = (W32*)((pointer)frontier + arraySize);
+	*frontier++ = 0; /* counter word */
+	*frontier++ = numElts;
+	*frontier++ = header;
+	res = (pointer)frontier;
+	if (1 == numPointers)
+		for ( ; frontier < last; frontier++)
+			*frontier = 0x1;
+	s->frontier = (pointer)last;
+	/* Unfortunately, the invariant isn't quite true here, because unless we
+ 	 * did the GC, we never set s->currentThread->stack->used to reflect
+	 * what the mutator did with stackTop.
+ 	 */
+	/*	assert(mutatorInvariant(s)); */
+	if (DEBUG) {
+		fprintf (stderr, "GC_arrayAllocate done.  res = 0x%x  frontier = 0x%x\n",
+				(uint)res, (uint)s->frontier);
+		GC_display (s, stderr);
+	}
+	assert (ensureBytesFree <= s->limitPlusSlop - s->frontier);
+	return res;
+}	
 
-			for (j = 0; j < numElements; ++j)
-				*(frontier + GC_ARRAY_HEADER_SIZE + j) 
-					= inits[i].str[j];
-		}
-		frontier += numBytes;
+static inline void ensureFree (GC_state s, uint bytesRequested) {
+	if (bytesRequested > s->limit - s->frontier) {
+		doGC (s, bytesRequested);
 	}
-	s->frontier = frontier;
-	assert(GC_mutatorInvariant(s));
 }
 
-/* ------------------------------------------------- */
-/*                      GC_done                      */
-/* ------------------------------------------------- */
+/* ---------------------------------------------------------------- */
+/*                             Threads                              */
+/* ---------------------------------------------------------------- */
 
-static void displayUint (string name, uint n) {
-	fprintf (stderr, "%s: %s\n", name, uintToCommaString(n));
+static inline uint threadBytes () {
+	return wordAlign(HEADER_SIZE + sizeof(struct GC_thread));
 }
 
-static void displayUllong (string name, ullong n) {
-	fprintf (stderr, "%s: %s\n", name, ullongToCommaString(n));
+static inline uint initialThreadBytes (GC_state s) {
+	return threadBytes() + stackBytes(initialStackSize(s));
 }
 
-inline void
-GC_done (GC_state s)
-{
-	GC_enter(s);
-	release(s->base, s->fromSize);
-	unless (0 == s->toSize)
-		releaseToSpace (s);
-	if (s->summary) {
-		double time;
-		uint gcTime = rusageTime(&s->ru_gc);
+static inline GC_thread newThreadOfSize (GC_state s, uint stackSize) {
+	GC_stack stack;
+	GC_thread t;
 
-		displayUint("max semispace size(bytes)", s->maxHeapSizeSeen);
-		displayUint("max stack size(bytes)", s->maxStackSizeSeen);
-		time = (double)(currentTime() - s->startTime);
-		fprintf(stderr, "GC time(ms): %s (%.1f%%)\n",
-			intToCommaString(gcTime), 
-			(0.0 == time) ? 0.0 
-			: 100.0 * ((double) gcTime) / time);
-		displayUint("maxPause(ms)", s->maxPause);
-		displayUint("number of GCs", s->numGCs);
-		displayUllong("number of LCs", s->numLCs);
-		displayUllong("bytes allocated",
-	 			s->bytesAllocated 
-				+ (s->frontier - s->base - s->bytesLive));
-		displayUllong("bytes copied", s->bytesCopied);
-		displayUint("max bytes live", s->maxBytesLive);
-#if METER
-		{
-			int i;
-			for(i = 0; i < cardof(sizes); ++i) {
-				if (0 != sizes[i])
-					fprintf(stderr, "COUNT[%d]=%d\n", i, sizes[i]);
-		  	}
-		}
+	ensureFree (s, stackBytes (stackSize) + threadBytes ());
+	stack = newStack (s, stackSize);
+	t = (GC_thread) object (s, THREAD_HEADER, threadBytes ());
+	t->exnStack = BOGUS_EXN_STACK;
+	t->stack = stack;
+	if (DEBUG_DETAILED)
+		fprintf (stderr, "0x%x = newThreadOfSize (%u)\n",
+				(uint)t, stackSize);;
+	return t;
+}
+
+static inline GC_thread copyThread (GC_state s, GC_thread from, uint size) {
+	GC_thread to;
+
+	/* newThreadOfSize may do a GC, which invalidates from.  
+	 * Hence we need to stash from where the GC can find it.
+	 */
+	s->savedThread = from;
+	to = newThreadOfSize (s, size);
+	if (DEBUG_THREADS)
+		fprintf (stderr, "0x%08x = copyThread (0x%08x)\n", 
+				(uint)to, (uint)from);
+	from = s->savedThread;
+	stackCopy (from->stack, to->stack);
+	to->exnStack = from->exnStack;
+	return to;
+}
+
+pointer GC_copyCurrentThread (GC_state s) {
+	GC_thread t;
+	GC_thread res;
+	
+	if (DEBUG_THREADS)
+		fprintf (stderr, "GC_copyCurrentThread\n");
+	enter (s);
+	t = s->currentThread;
+	res = copyThread (s, t, t->stack->used);
+	assert (res->stack->reserved == res->stack->used);
+	leave (s);
+	if (DEBUG_THREADS)
+		fprintf (stderr, "0x%08x = GC_copyCurrentThread\n", (uint)res);
+	return (pointer)res;
+}
+
+pointer GC_copyThread (GC_state s, GC_thread t) {
+	GC_thread res;
+
+	if (DEBUG_THREADS)
+		fprintf (stderr, "GC_copyThread (0x%08x)\n", (uint)t);
+	enter (s);
+	assert (t->stack->reserved == t->stack->used);
+	res = copyThread (s, t, stackNeedsReserved (s, t->stack));
+	leave (s);
+	return (pointer)res;
+}
+
+/* ---------------------------------------------------------------- */
+/*                          Initialization                          */
+/* ---------------------------------------------------------------- */
+
+static inline void initSignalStack(GC_state s) {
+#if (defined (__linux__) || defined (__FreeBSD__))
+        static stack_t altstack;
+	size_t ss_size = roundPage(s, SIGSTKSZ);
+	size_t psize = s->pageSize;
+	void *ss_sp = ssmmap(2 * ss_size, psize, psize);
+	altstack.ss_sp = ss_sp + ss_size;
+	altstack.ss_size = ss_size;
+	altstack.ss_flags = 0;
+	sigaltstack(&altstack, NULL);
 #endif
+}
 
-	}	
+/* set fromSize.
+ * size must not be an approximation, because setHeapParams will die if it
+ * can't set fromSize big enough.
+ */
+inline void setHeapParams (GC_state s, uint size) {
+	if (s->useFixedHeap) {
+		if (0 == s->fromSize)
+			s->fromSize = roundPage (s, s->ramSlop * s->totalRam);
+		else
+		        s->fromSize = roundPage (s, s->fromSize);
+	} else {
+		s->fromSize = computeSemiSize (s, size);
+	}
+	if (size > s->fromSize)
+		die ("Out of memory (setHeapParams).");
 }
 
-/* GC_handler sets s->limit = 0 so that the next limit check will fail. 
- * Signals need to be blocked during the handler (i.e. it should run atomically)
- * because sigaddset does both a read and a write of s->signalsPending.
- * The signals are blocked by Posix_Signal_handle (see Posix/Signal/Signal.c).
+static int processor_has_sse2=0;
+
+static void readProcessor() {
+#if 0
+	int status = system("/bin/cat /proc/cpuinfo | /bin/egrep -q '^flags.*:.* mmx .*xmm'");
+  
+	if (status==0)
+		processor_has_sse2=1;
+	else
+		processor_has_sse2=0;
+#endif
+	processor_has_sse2=0;
+}
+
+/*
+ * Set RAM and SWAP size.
+ * Note the total amount of RAM is multiplied by ramSlop so that we don't
+ * use all of memory or start paging.
+ *
+ * Ensure that s->totalRam + s->totalSwap < 4G.
+ */
+
+#if (defined (__linux__))
+#include <sys/sysinfo.h>
+/* struct sysinfo copied from /usr/include/linux/kernel.h on a 2.4 kernel
+ * because we need mem_unit.
+ * On older kernels, it will be guaranteed to be zero, and we test for that
+ * below.
  */
-inline void
-GC_handler(GC_state s, int signum)
+struct Msysinfo {
+	long uptime;			/* Seconds since boot */
+	unsigned long loads[3];		/* 1, 5, and 15 minute load averages */
+	unsigned long totalram;		/* Total usable main memory size */
+	unsigned long freeram;		/* Available memory size */
+	unsigned long sharedram;	/* Amount of shared memory */
+	unsigned long bufferram;	/* Memory used by buffers */
+	unsigned long totalswap;	/* Total swap space size */
+	unsigned long freeswap;		/* swap space still available */
+	unsigned short procs;		/* Number of current processes */
+	unsigned long totalhigh;	/* Total high memory size */
+	unsigned long freehigh;		/* Available high memory size */
+	unsigned int mem_unit;		/* Memory unit size in bytes */
+	char _f[20-2*sizeof(long)-sizeof(int)];	/* Padding: libc5 uses this.. */
+};
+static inline void
+setMemInfo (GC_state s)
 {
-	if (DEBUG_SIGNALS)
-		fprintf(stderr, "GC_handler  signum = %d\n", signum);
-	if (0 == s->canHandle) {
-		if (DEBUG_SIGNALS)
-			fprintf(stderr, "setting limit = 0\n");
-		s->limit = 0;
-	}
-	sigaddset(&s->signalsPending, signum);
-	s->signalIsPending = TRUE;
-	if (DEBUG_SIGNALS)
-		fprintf(stderr, "GC_handler done\n");
+	struct Msysinfo	sbuf;
+	W32 maxMem;
+	W64 tmp;
+	uint memUnit;
+
+	maxMem = 0x100000000llu - s->pageSize;
+	unless (0 == sysinfo((struct sysinfo*)&sbuf))
+		diee("sysinfo failed");
+	memUnit = sbuf.mem_unit;
+	/* On 2.2 kernels, mem_unit is not defined, but will be zero, so go
+	 * ahead and pretend it is one.
+	 */
+	if (0 == memUnit)
+		memUnit = 1;
+	tmp = memUnit * (W64)sbuf.totalram;
+	s->totalRam = (tmp > (W64)maxMem) ? maxMem : (W32)tmp;
+	maxMem = maxMem - s->totalRam;
+	tmp = memUnit * (W64)sbuf.totalswap;
+	s->totalSwap = (tmp > (W64)maxMem) ? maxMem : (W32)tmp;
 }
+#elif (defined (__CYGWIN__))
+#include <windows.h>
+static inline void
+setMemInfo(GC_state s)
+{
+	MEMORYSTATUS ms; 
 
-void GC_finishHandler (GC_state s) {
-	assert(s->canHandle == 1);
-	s->inSignalHandler = FALSE;	
-	sigemptyset(&s->signalsPending);
-	unblockSignals(s);
+	GlobalMemoryStatus(&ms); 
+	s->totalRam = ms.dwTotalPhys;
+	s->totalSwap = ms.dwTotalPageFile;
 }
+#elif (defined (__FreeBSD__))
 
-/* ------------------------------------------------- */
-/*                       mark                        */
-/* ------------------------------------------------- */
+/* returns total amount of swap available */
+static int 
+get_total_swap() 
+{
+        static char buffer[256];
+        FILE *file;
+        int total_size = 0;
 
-static inline uint *arrayCounterp (pointer a) {
-	return ((uint*)a - 3);
+        file = popen("/usr/sbin/swapinfo -k | awk '{ print $4; }'\n", "r");
+        if (file == NULL) 
+                diee("swapinfo failed");
+
+        /* skip header */
+        fgets(buffer, 255, file);
+
+        while (fgets(buffer, 255, file) != NULL) { 
+                total_size += atoi(buffer);
+        }
+
+        pclose(file);
+
+        return total_size * 1024;
 }
 
-static inline uint arrayCounter (pointer a) {
-	return *(arrayCounterp (a));
+/* returns total amount of memory available */
+static int 
+get_total_mem() 
+{
+        static char buffer[256];
+        FILE *file;
+        int total_size = 0;
+
+        file = popen("/sbin/sysctl hw.physmem | awk '{ print $2; }'\n", "r");
+        if (file == NULL) 
+                diee("sysctl failed");
+
+
+        fgets(buffer, 255, file);
+
+        pclose(file);
+
+        return atoi(buffer);
 }
 
-static inline bool isMarked (pointer p) {
-	return MARK_MASK & GC_getHeader (p);
+static inline void
+setMemInfo(GC_state s)
+{
+	s->totalRam = get_total_mem();
+	s->totalSwap = get_total_swap();
 }
 
-static bool modeEqMark (MarkMode m, pointer p) {
-	return (((MARK_MODE == m) and isMarked (p))
-		or ((UNMARK_MODE == m) and not isMarked (p)));
+#endif /* definition of setMemInfo */
+
+inline void fromSpace (GC_state s)
+{
+	s->base = smmap (s->fromSize);
+	if (s->fromSize > s->maxHeapSizeSeen)
+		s->maxHeapSizeSeen = s->fromSize;
+	setLimit (s);
 }
 
-/* GC_mark (s, p) sets all the mark bits in the object graph pointed to by p. 
- * If the mode is MARK, it sets the bits to 1.
- * If the mode is UNMARK, it sets the bits to 0.
- * It returns the amount marked.
- */
-W32 mark (GC_state s, pointer root, MarkMode mode) {
-	pointer cur;  /* The current object being marked. */
-	GC_offsets frameOffsets;
-	Header* headerp;
-	Header header;
-	uint index;
-	GC_frameLayout *layout;
-	pointer max; /* The end of the pointers in an object. */
-	pointer next; /* The next object to mark. */
-	Header *nextHeaderp;
-	Header nextHeader;
-	W32 numBytes;
-	uint numNonPointers;
-	uint numPointers;
-	pointer prev; /* The previous object on the mark stack. */
-	W32 size;
-	uint tag;
-	pointer todo; /* A pointer to the pointer in cur to next. */
-	pointer top; /* The top of the next stack frame to mark. */
+static void usage(string s) {
+	die("Usage: %s [@MLton [fixed-heap n[{k|m}]] [gc-messages] [gc-summary] [load-world file] [ram-slop x] --] args", 
+		s);
+}
 
-	if (modeEqMark (mode, root))
-		/* Object has already been marked. */
-		return 0;
-	size = 0;
-	cur = root;
-	prev = NULL;
-	headerp = GC_getHeaderp (cur);
-	header = *(Header*)headerp;
-	goto mark;	
-markNext:
-	/* cur is the object that was being marked.
-	 * prev is the mark stack.
-	 * next is the unmarked object to be marked.
-	 * todo is a pointer to the pointer inside cur that points to next.
-	 * headerp points to the header of next.
-	 * header is the header of next.
-	 */
-	if (DEBUG_MARK)
-		fprintf (stderr, "markNext  cur = 0x%08x  next = 0x%08x  prev = 0x%08x  todo = 0x%08x\n",
-				(uint)cur, (uint)next, (uint)prev, (uint)todo);
-	assert (not modeEqMark (mode, next));
-	assert (header == GC_getHeader (next));
-	assert (headerp == GC_getHeaderp (next));
-	assert (*(pointer*) todo == next);
-	*(pointer*)todo = prev;
-	prev = cur;
-	cur = next;
-mark:
-	if (DEBUG_MARK)
-		fprintf (stderr, "mark  cur = 0x%08x  prev = 0x%08x  mode = %s\n",
-				(uint)cur, (uint)prev,
-				(mode == MARK_MODE) ? "mark" : "unmark");
-	/* cur is the object to mark. 
-	 * prev is the mark stack.
-	 * headerp points to the header of cur.
-	 * header is the header of cur.
-	 */
-	assert (not modeEqMark (mode, cur));
-	assert (header == GC_getHeader (cur));
-	assert (headerp == GC_getHeaderp (cur));
-	header = (MARK_MODE == mode)
-			? header | MARK_MASK
-			: header & ~MARK_MASK;
-	SPLIT_HEADER();
-	switch (tag) {
-	case ARRAY_TAG:
-		assert (0 == GC_arrayNumElements (cur)
-				? 0 == numPointers
-				: TRUE);
-		numBytes = arrayNumBytes (cur, numPointers, numNonPointers);
-		if (DEBUG_MARK_SIZE)
-			fprintf (stderr, "0x%08x array of size %u\n",
-					(uint)cur,
-					GC_ARRAY_HEADER_SIZE + (uint)numBytes);
-		size += GC_ARRAY_HEADER_SIZE + numBytes;
-		*headerp = header;
-		if (0 == numBytes or 0 == numPointers)
-			goto ret;
-		assert (0 == numNonPointers);
-		max = cur + numBytes;
-		todo = cur;
-		index = 0;
-markInArray:
-		if (DEBUG_MARK)
-			fprintf (stderr, "markInArray index = %d\n", index);
-		if (todo == max) {
-			*arrayCounterp (cur) = 0;
-			goto ret;
-		}
-		next = *(pointer*)todo;
-		if (not GC_isPointer (next)) {
-markNextInArray:
-			todo += POINTER_SIZE;
-			index++;
-			goto markInArray;
-		}
-		nextHeaderp = GC_getHeaderp (next);
-		nextHeader = *nextHeaderp;
-		if ((nextHeader & MARK_MASK)
-			== (MARK_MODE == mode ? MARK_MASK : 0))
-			goto markNextInArray;
-		*arrayCounterp (cur) = index;
-		headerp = nextHeaderp;
-		header = nextHeader;
-		goto markNext;
-	case NORMAL_TAG:
-		todo = cur + toBytes (numNonPointers);
-		max = todo + toBytes (numPointers);
-		if (DEBUG_MARK_SIZE)
-			fprintf (stderr, "0x%08x normal of size %u\n",
-					(uint)cur,
-					GC_NORMAL_HEADER_SIZE + (max - cur));
-		size += GC_NORMAL_HEADER_SIZE + (max - cur);
-		index = 0;
-markInNormal:
-		assert (todo <= max);
-		if (DEBUG_MARK)
-			fprintf (stderr, "markInNormal  index = %d\n", index);
-		if (todo == max) {
-			*headerp = header & ~COUNTER_MASK;
-			goto ret;
-		}
-		next = *(pointer*)todo;
-		if (not GC_isPointer (next)) {
-markNextInNormal:
-			todo += POINTER_SIZE;
-			index++;
-			goto markInNormal;
-		}
-		nextHeaderp = GC_getHeaderp (next);
-		nextHeader = *nextHeaderp;
-		if ((nextHeader & MARK_MASK)
-			== (MARK_MODE == mode ? MARK_MASK : 0))
-			goto markNextInNormal;
-		*headerp = (header & ~COUNTER_MASK) |
-				(index << COUNTER_SHIFT);
-		headerp = nextHeaderp;
-		header = nextHeader;
-		goto markNext;
-	default:
-		assert (STACK_TAG == tag);
-		*headerp = header;
-		if (DEBUG_MARK_SIZE)
-			fprintf (stderr, "0x%08x stack of size %u\n",
-					(uint)cur,
-					stackBytes (((GC_stack)cur)->reserved));
-		size += stackBytes (((GC_stack)cur)->reserved);
-		top = stackTop ((GC_stack)cur);
-		assert (((GC_stack)cur)->used <= ((GC_stack)cur)->reserved);
-markInStack:
-		/* Invariant: top points just past the return address of the
-		 * frame to be marked.
-		 */
-		assert (stackBottom ((GC_stack)cur) <= top);
-		if (DEBUG_MARK)
-			fprintf (stderr, "markInStack  top = %d\n",
-					top - stackBottom ((GC_stack)cur));
-					
-		if (top == stackBottom ((GC_stack)(cur)))
-			goto ret;
-		index = 0;
-		layout = getFrameLayout (s, *(word*) (top - WORD_SIZE));
-		frameOffsets = layout->offsets;
-		((GC_stack)cur)->markTop = top;
-markInFrame:
-		if (index == frameOffsets [0]) {
-			top -= layout->numBytes;
-			goto markInStack;
-		}
-		todo = top - layout->numBytes + frameOffsets [index + 1];
-		next = *(pointer*)todo;
-		if (DEBUG_MARK)
-			fprintf (stderr, 
-				"    offset %u  todo 0x%08x  next = 0x%08x\n", 
-				frameOffsets [index + 1], 
-				(uint)todo, (uint)next);
-		if (not GC_isPointer (next)) {
-			index++;
-			goto markInFrame;
-		}
-		nextHeaderp = GC_getHeaderp (next);
-		nextHeader = *nextHeaderp;
-		if ((nextHeader & MARK_MASK)
-			== (MARK_MODE == mode ? MARK_MASK : 0)) {
-			index++;
-			goto markInFrame;
+static float stringToFloat(string s) {
+	float f;
+
+	sscanf(s, "%f", &f);
+	return f;
+}
+
+static uint stringToBytes(string s) {
+	char c;
+	uint result;
+	int i, m;
+	
+	result = 0;
+	i = 0;
+
+	while ((c = s[i++]) != '\000') {
+		switch (c) {
+		case 'm':
+			if (s[i] == '\000') 
+				result = result * 1048576;
+			else return 0;
+			break;
+		case 'k':
+			if (s[i] == '\000') 
+				result = result * 1024;
+			else return 0;
+			break;
+		default:
+			m = (int)(c - '0');
+			if (0 <= m and m <= 9)
+				result = result * 10 + m;
+			else return 0;
 		}
-		((GC_stack)cur)->markIndex = index;		
-		headerp = nextHeaderp;
-		header = nextHeader;
-		goto markNext;
 	}
-	assert (FALSE);
-ret:
-	/* Done marking cur, continue with prev.
-	 * Need to set the pointer in the prev object that pointed to cur 
-	 * to point back to prev, and restore prev.
- 	 */
-	if (DEBUG_MARK)
-		fprintf (stderr, "return  cur = 0x%08x  prev = 0x%08x\n",
-				(uint)cur, (uint)prev);
-	assert (modeEqMark (mode, cur));
-	if (NULL == prev)
-		return size;
-	headerp = GC_getHeaderp (prev);
-	header = *headerp;
-	SPLIT_HEADER();
-	switch (tag) {
-	case ARRAY_TAG:
-		max = prev + arrayNumBytes (prev, numPointers, numNonPointers);
-		index = arrayCounter (prev);
-		todo = prev + index * POINTER_SIZE;
-		next = cur;
-		cur = prev;
-		prev = *(pointer*)todo;
-		*(pointer*)todo = next;
-		todo += POINTER_SIZE;
-		index++;
-		goto markInArray;
-	case NORMAL_TAG:
-		todo = prev + toBytes (numNonPointers);
-		max = todo + toBytes (numPointers);
-		index = (header & COUNTER_MASK) >> COUNTER_SHIFT;
-		todo += index * POINTER_SIZE;
-		next = cur;
-		cur = prev;
-		prev = *(pointer*)todo;
-		*(pointer*)todo = next;
-		todo += POINTER_SIZE;
-		index++;
-		goto markInNormal;
-	default:
-		assert (STACK_TAG == tag);
-		next = cur;
-		cur = prev;
-		index = ((GC_stack)cur)->markIndex;
-		top = ((GC_stack)cur)->markTop;
-		layout = getFrameLayout (s, *(word*) (top - WORD_SIZE));
-		frameOffsets = layout->offsets;
-		todo = top - layout->numBytes + frameOffsets [index + 1];
-		prev = *(pointer*)todo;
-		*(pointer*)todo = next;
-		index++;
-		goto markInFrame;
+	
+	return result;
+}
+
+static void newWorld (GC_state s)
+{
+	int i;
+
+	assert (isWordAligned (sizeof (struct GC_thread)));
+	for (i = 0; i < s->numGlobals; ++i)
+		s->globals[i] = (pointer)BOGUS_POINTER;
+	setHeapParams (s, s->bytesLive + initialThreadBytes (s));
+	assert (s->bytesLive + initialThreadBytes (s) + LIMIT_SLOP 
+			<= s->fromSize);
+	fromSpace (s);
+	s->frontier = s->base;
+	s->toSize = 0;
+	s->toBase = NULL;
+	switchToThread (s, newThreadOfSize (s, initialStackSize (s)));
+	assert (initialThreadBytes (s) == s->frontier - s->base);
+	assert (s->frontier + s->bytesLive <= s->limit);
+	assert (mutatorInvariant (s));
+}
+
+int GC_init (GC_state s, int argc, char **argv, 
+		void (*loadGlobals)(FILE *file)) {
+	char *worldFile;
+	int i;
+
+	s->pageSize = getpagesize();
+	initSignalStack(s);
+	s->bytesAllocated = 0;
+	s->bytesCopied = 0;
+	s->canHandle = 0;
+	s->currentThread = BOGUS_THREAD;
+	rusageZero(&s->ru_gc);
+	s->inSignalHandler = FALSE;
+	s->isOriginal = TRUE;
+	s->maxBytesLive = 0;
+	s->maxHeap = 0;
+	s->maxHeapSizeSeen = 0;
+	s->maxPause = 0;
+	s->maxStackSizeSeen = 0;
+	s->messages = FALSE;
+	s->numCopyingGCs = 0;
+	s->numMarkCompactGCs = 0;
+	s->numLCs = 0;
+	s->ramSlop = 0.80;
+	s->savedThread = BOGUS_THREAD;
+	s->signalHandler = BOGUS_THREAD;
+	sigemptyset(&s->signalsHandled);
+	s->signalIsPending = FALSE;
+	sigemptyset(&s->signalsPending);
+	s->startTime = currentTime();
+	s->summary = FALSE;
+ 	readProcessor();
+	worldFile = NULL;
+	i = 1;
+	if (argc > 1 and (0 == strcmp (argv [1], "@MLton"))) {
+		bool done;
+
+		/* process @MLton args */
+		i = 2;
+		done = FALSE;
+		while (!done) {
+			if (i == argc)
+				usage(argv[0]);
+			else {
+				string arg;
+
+				arg = argv[i];
+				if (0 == strcmp(arg, "fixed-heap")) {
+					++i;
+					if (i == argc)
+						usage(argv[0]);
+					s->useFixedHeap = TRUE;
+					s->fromSize =
+						stringToBytes (argv[i++]);
+				} else if (0 == strcmp(arg, "gc-messages")) {
+					++i;
+					s->messages = TRUE;
+				} else if (0 == strcmp(arg, "gc-summary")) {
+					++i;
+					s->summary = TRUE;
+				} else if (0 == strcmp(arg, "load-world")) {
+					++i;
+					s->isOriginal = FALSE;
+					if (i == argc) 
+						usage(argv[0]);
+					worldFile = argv[i++];
+				} else if (0 == strcmp(arg, "max-heap")) {
+					++i;
+					if (i == argc) 
+						usage(argv[0]);
+					s->useFixedHeap = FALSE;
+					s->maxHeap = stringToBytes(argv[i++]);
+				} else if (0 == strcmp(arg, "ram-slop")) {
+					++i;
+					if (i == argc)
+						usage(argv[0]);
+					s->ramSlop =
+						stringToFloat(argv[i++]);
+				} else if (0 == strcmp(arg, "--")) {
+					++i;
+					done = TRUE;
+				} else if (i > 1)
+					usage(argv[0]);
+			        else done = TRUE;
+			}
+		}
 	}
-	assert (FALSE);
-}
-
-static inline void markGlobal (GC_state s, pointer *pp) {
-	s->markSize += mark (s, *pp, MARK_MODE);
-}
-
-static inline void unmarkGlobal (GC_state s, pointer *pp) {
-       	mark (s, *pp, UNMARK_MODE);
+	setMemInfo(s);
+	if (DEBUG)
+		fprintf(stderr, "totalRam = %u  totalSwap = %u\n",
+			s->totalRam, s->totalSwap);
+	if (s->isOriginal)
+		newWorld(s);
+	else
+		GC_loadWorld (s, worldFile, loadGlobals);
+	return i;
 }
 
-static inline void threadInternal (GC_state s, pointer *pp) {
-	Header *headerp;
+void GC_createStrings (GC_state s, struct GC_stringInit inits[]) {
+	pointer frontier;
+	int i;
 
-	headerp = GC_getHeaderp(*pp);
-	*(Header*)pp = *headerp;
-	*headerp = (Header)pp;
-}
+	assert (invariant (s));
+	frontier = s->frontier;
+	for(i = 0; inits[i].str != NULL; ++i) {
+		uint numElements, numBytes;
 
-static inline void updateForwardPointers (GC_state s) {
-	pointer back;
-	pointer front;
-	uint gap;
-	pointer endOfLastMarked;
-	Header header;
-	Header *headerp;
-	pointer p;
-	uint size;
-	uint totalSize;
+		numElements = inits[i].size;
+		numBytes = GC_ARRAY_HEADER_SIZE
+			+ ((0 == numElements) 
+				? POINTER_SIZE 
+				: wordAlign(numElements));
+		if (frontier + numBytes >= s->limit)
+			die("Unable to allocate string constant \"%s\".", 
+				inits[i].str);
+		*(word*)frontier = 0; /* counter word */
+		*(word*)(frontier + WORD_SIZE) = numElements;
+		*(word*)(frontier + 2 * WORD_SIZE) = STRING_HEADER;
+		s->globals[inits[i].globalIndex] = 
+			frontier + GC_ARRAY_HEADER_SIZE;
+		if (DEBUG_DETAILED)
+			fprintf (stderr, "allocated string at 0x%x\n",
+					(uint)s->globals[inits[i].globalIndex]);
+		{
+			int j;
 
-	if (DEBUG_MARK)
-		fprintf (stderr, "updateForwardPointers\n");
-	back = s->frontier;
-	front = s->base;
-	endOfLastMarked = front;
-	gap = 0;
-	totalSize = 0;
-updateObject:
-	if (front == back)
-		goto done;
-	headerp = (Header*)front;
-	header = *headerp;
-	if (0 == header) {
-		/* We're looking at an array.  Move to the header. */
-		p = front + 3 * WORD_SIZE;
-		headerp = (Header*)(p - WORD_SIZE);
-		header = *headerp;
-	} else 
-		p = front + WORD_SIZE;
-	if (1 == (1 & header)) {
-		/* It's a header */
-		if (MARK_MASK & header) {
-			/* It is marked, but has no forward pointers. 
-			 * Thread internal pointers.
-			 */
-thread:
-			size = objectSize (s, p);
-			if (DEBUG_MARK)
-	       			fprintf (stderr, "threading 0x%08x of size %u\n", 
-						(uint)p, size);
-			if (front - endOfLastMarked >= 4 * WORD_SIZE) {
-				/* Compress all of the unmarked into one string.
-				 */
-				if (DEBUG_MARK)
-					fprintf (stderr, "compressing from 0x%08x to 0x%08x (length = %u)\n",
-							(uint)endOfLastMarked,
-							(uint)front,
-							front - endOfLastMarked);
-				*(uint*)endOfLastMarked = 0;
-				*(uint*)(endOfLastMarked + WORD_SIZE) = 
-					front - endOfLastMarked - 3 * WORD_SIZE;
-				*(uint*)(endOfLastMarked + 2 * WORD_SIZE) =
-					GC_objectHeader (STRING_TYPE_INDEX);
-			}
-			totalSize += size;
-			front += size;
-			endOfLastMarked = front;
-			GC_foreachPointerInObject (s, threadInternal, p);
-			goto updateObject;
-		} else {
-				/* It's not marked. */
-			size = objectSize (s, p);
-			gap += size;
-			front += size;
-			goto updateObject;
+			for (j = 0; j < numElements; ++j)
+				*(frontier + GC_ARRAY_HEADER_SIZE + j) 
+					= inits[i].str[j];
 		}
-	} else {
-		pointer new;
+		frontier += numBytes;
+	}
+	s->frontier = frontier;
+	assert(mutatorInvariant(s));
+}
 
-		assert (0 == (3 & header));
-		/* It's a pointer.  This object must be live.  Fix all the
-		 * forward pointers to it, store its header, then thread
-                 * its internal pointers.
-		 */
-		new = p - gap;
-		do {
-			pointer cur;
+static void displayUint (string name, uint n) {
+	fprintf (stderr, "%s: %s\n", name, uintToCommaString(n));
+}
 
-			cur = (pointer)header;
-			header = *(word*)cur;
-			*(word*)cur = (word)new;
-		} while (0 == (1 & header));
-		*headerp = header;
-		goto thread;
-	}
-done:
-	s->markSize = totalSize;
-	return;
+static void displayUllong (string name, ullong n) {
+	fprintf (stderr, "%s: %s\n", name, ullongToCommaString(n));
 }
 
-static inline void updateBackwardPointersAndSlide (GC_state s) {
-	pointer back;
-	pointer front;
-	uint gap;
-	Header header;
-	pointer p;
-	uint size;
-	uint totalSize;
+inline void GC_done (GC_state s) {
+	enter (s);
+	release (s->base, s->fromSize);
+	releaseToSpace (s);
+	if (s->summary) {
+		double time;
+		uint gcTime = rusageTime (&s->ru_gc);
 
-	if (DEBUG_MARK)
-		fprintf (stderr, "updateBackwardPointersAndSlide\n");
-	back = s->frontier;
-	front = s->base;
-	gap = 0;
-	totalSize = 0;
-updateObject:
-	if (front == back)
-		goto done;
-	header = *(word*)front;
-	if (0 == header) {
-		/* We're looking at an array.  Move to the header. */
-		p = front + 3 * WORD_SIZE;
-		header = *(Header*)(p - WORD_SIZE);
-	} else 
-		p = front + WORD_SIZE;
-	if (1 == (1 & header)) {
-		/* It's a header */
-		if (MARK_MASK & header) {
-			/* It is marked, but has no backward pointers to it.
-			 * Unmark it.
-			 */
-unmark:
-			*GC_getHeaderp (p) = header & ~MARK_MASK;
-			size = objectSize (s, p);
-			if (DEBUG_MARK)
-				fprintf (stderr, "unmarking 0x%08x of size %u\n", 
-						(uint)p, size);
-			/* slide */
-			unless (0 == gap)
-				if (DEBUG_MARK)
-					fprintf (stderr, "sliding 0x%08x down %u\n",
-							(uint)front, gap);
-			copy (front, front - gap, size);
-			totalSize += size;
-			front += size;
-			goto updateObject;
-		} else {
-			size = objectSize (s, p);
-			/* It's not marked. */
-			gap += size;
-			front += size;
-			goto updateObject;
+		displayUint("max semispace size(bytes)", s->maxHeapSizeSeen);
+		displayUint("max stack size(bytes)", s->maxStackSizeSeen);
+		time = (double)(currentTime() - s->startTime);
+		fprintf(stderr, "GC time(ms): %s (%.1f%%)\n",
+			intToCommaString(gcTime), 
+			(0.0 == time) ? 0.0 
+			: 100.0 * ((double) gcTime) / time);
+		displayUint ("maxPause(ms)", s->maxPause);
+		displayUint ("number of copying GCs", s->numCopyingGCs);
+		displayUint ("number of mark compact GCs", s->numMarkCompactGCs);
+		displayUllong ("number of LCs", s->numLCs);
+		displayUllong ("bytes allocated",
+	 			s->bytesAllocated 
+				+ (s->frontier - s->base - s->bytesLive));
+		displayUllong ("bytes copied", s->bytesCopied);
+		displayUint ("max bytes live", s->maxBytesLive);
+#if METER
+		{
+			int i;
+			for(i = 0; i < cardof(sizes); ++i) {
+				if (0 != sizes[i])
+					fprintf(stderr, "COUNT[%d]=%d\n", i, sizes[i]);
+		  	}
 		}
-	} else {
-		pointer new;
+#endif
 
-		assert (0 == (3 & header));
-		/* It's a pointer.  This object must be live.  Fix all the
-		 * forward pointers to it.  Then unmark it.
-		 */
-		new = p - gap;
-		do {
-			pointer cur;
+	}	
+}
 
-			cur = (pointer)header;
-			header = *(word*)cur;
-			*(word*)cur = (word)new;
-		} while (0 == (1 & header));
-		/* The header will be stored by umark. */
-		goto unmark;
+void GC_finishHandler (GC_state s) {
+	assert (s->canHandle == 1);
+	s->inSignalHandler = FALSE;	
+	sigemptyset (&s->signalsPending);
+	unblockSignals (s);
+}
+
+/* GC_handler sets s->limit = 0 so that the next limit check will fail. 
+ * Signals need to be blocked during the handler (i.e. it should run atomically)
+ * because sigaddset does both a read and a write of s->signalsPending.
+ * The signals are blocked by Posix_Signal_handle (see Posix/Signal/Signal.c).
+ */
+inline void GC_handler (GC_state s, int signum) {
+	if (DEBUG_SIGNALS)
+		fprintf (stderr, "GC_handler  signum = %d\n", signum);
+	if (0 == s->canHandle) {
+		if (DEBUG_SIGNALS)
+			fprintf (stderr, "setting limit = 0\n");
+		s->limit = 0;
 	}
-done:
-	return;
+	sigaddset (&s->signalsPending, signum);
+	s->signalIsPending = TRUE;
+	if (DEBUG_SIGNALS)
+		fprintf (stderr, "GC_handler done\n");
 }
 
-static inline void markCompact (GC_state s) {
-	GC_foreachGlobal (s, markGlobal);
-	GC_foreachGlobal (s, threadInternal);
-	updateForwardPointers (s);
-	updateBackwardPointersAndSlide (s);
+/* worldTerminator is used to separate the human readable messages at the 
+ * beginning of the world file from the machine readable data.
+ */
+static const char worldTerminator = '\000';
+
+void GC_loadWorld (GC_state s, 
+			char *fileName,
+			void (*loadGlobals)(FILE *file)) {
+	FILE *file;
+	uint heapSize, magic;
+	pointer base, frontier;
+	char c;
+	
+	file = sfopen(fileName, "rb");
+	until ((c = fgetc(file)) == worldTerminator or EOF == c);
+	if (EOF == c) die("Invalid world.");
+	magic = sfreadUint(file);
+	unless (s->magic == magic)
+		die("Invalid world: wrong magic number.");
+	base = (pointer)sfreadUint(file);
+	frontier = (pointer)sfreadUint(file);
+	s->currentThread = (GC_thread)sfreadUint(file);
+	s->signalHandler = (GC_thread)sfreadUint(file);
+	heapSize = frontier - base;
+	s->bytesLive = heapSize;
+       	setHeapParams (s, heapSize);
+	fromSpace (s);
+	sfread (s->base, 1, heapSize, file);
+	s->frontier = s->base + heapSize;
+	(*loadGlobals)(file);
+	unless (EOF == fgetc (file))
+		die("Invalid world: junk at end of file.");
+	fclose(file);
+	/* translateHeap must occur after loading the heap and globals, since it
+	 * changes pointers in all of them.
+	 */
+	translateHeap (s, base, s->base, heapSize);
+	setStack (s);
+	s->toSize = 0;
+	s->toBase = NULL;
+	assert (mutatorInvariant (s));
 }
 
 uint GC_size (GC_state s, pointer root) {
 	uint res;
 
-	if (DEBUG_MARK)
+	if (DEBUG_MARK_COMPACT)
 		fprintf (stderr, "GC_size marking\n");
 	res = mark (s, root, MARK_MODE);
-	if (DEBUG_MARK)
+	if (DEBUG_MARK_COMPACT)
 		fprintf (stderr, "GC_size unmarking\n");
 	mark (s, root, UNMARK_MODE);
 	return res;
+}
+
+void GC_saveWorld (GC_state s, int fd) {
+	char buf[80];
+
+	enter (s);
+	/* Compact the heap into fromSpace */
+	doGC (s, 0);
+	sprintf(buf,
+		"Heap file created by MLton.\nbase = %x\nfrontier = %x\n",
+		(uint)s->base,
+		(uint)s->frontier);
+	swrite(fd, buf, 1 + strlen(buf)); /* +1 to get the '\000' */
+	swriteUint(fd, s->magic);
+	swriteUint(fd, (uint)s->base);
+	swriteUint(fd, (uint)s->frontier);
+	swriteUint(fd, (uint)s->currentThread);
+	swriteUint(fd, (uint)s->signalHandler);
+ 	swrite(fd, s->base, s->frontier - s->base);
+	(*s->saveGlobals)(fd);
+	leave (s);
 }



1.27      +13 -46    mlton/runtime/gc.h

Index: gc.h
===================================================================
RCS file: /cvsroot/mlton/mlton/runtime/gc.h,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -r1.26 -r1.27
--- gc.h	7 Jul 2002 21:41:51 -0000	1.26
+++ gc.h	11 Jul 2002 17:43:00 -0000	1.27
@@ -82,6 +82,16 @@
 
 #define TWOPOWER(n) (1 << (n))
 
+/* The GC can either always use copying, always use mark-compact, or
+ * automatically switch between the two, using copying for small heaps and
+ * mark-compact for large heaps.
+ */
+typedef enum {
+	GC_METHOD_AUTO_SWITCH,
+	GC_METHOD_COPY,
+	CC_METHOD_MARK_COMPACT,
+} GCMethod;
+
 /* ------------------------------------------------- */
 /*                    object type                    */
 /* ------------------------------------------------- */
@@ -195,8 +205,6 @@
 	GC_frameLayout *frameLayouts;
 	uint fromSize; /* Size (bytes) of from space. */
 	pointer *globals; /* An array of size numGlobals. */
-	uint halfMem; /* bytes */
-	uint halfRam; /* bytes */
 	bool inSignalHandler; 	/* TRUE iff a signal handler is running. */
 	/* canHandle == 0 iff GC may switch to the signal handler
  	 * thread.  This is used to implement critical sections.
@@ -204,11 +212,7 @@
 	volatile int canHandle;
 	bool isOriginal;
 	pointer limitPlusSlop; /* limit + LIMIT_SLOP */
-	uint liveThresh1;
-	uint liveThresh2;
-	uint liveThresh3;
 	uint magic; /* The magic number required for a valid world file. */
-	uint markSize;
 	uint maxBytesLive;
 	uint maxFrameIndex; /* 0 <= frameIndex < maxFrameIndex */
 	uint maxFrameSize;
@@ -218,14 +222,16 @@
 	uint maxPause; /* max time spent in any gc in milliseconds. */
 	uint maxStackSizeSeen;
 	bool messages; /* Print out a message at the start and end of each gc. */
+	GCMethod method;
 	/* native is true iff the native codegen was used.
 	 * The GC needs to know this because it affects how it finds the
 	 * layout of stack frames.
  	 */
 	bool native;
- 	uint numGCs; /* Total number of GCs done. */
+	uint numCopyingGCs;
 	uint numGlobals; /* Number of pointers in globals array. */
  	ullong numLCs;
+ 	uint numMarkCompactGCs;
 	GC_ObjectType *objectTypes; /* Array of object types. */
 	uint pageSize; /* bytes */
 	float ramSlop;
@@ -303,10 +309,6 @@
 	return *(GC_arrayNumElementsp (a));
 }
 
-static inline void GC_arrayShrink (pointer array, uint numElements) {
-	*GC_arrayNumElementsp (array) = numElements;
-}
-
 /* GC_copyThread (s, t) returns a copy of the thread pointed to by t.
  */
 pointer GC_copyThread (GC_state s, GC_thread t);
@@ -333,35 +335,17 @@
 /* GC_display (s, str) prints out the state s to stream str. */
 void GC_display (GC_state s, FILE *stream);
 
-/* GC_doGC is for use by GC related functions only.  External callers should
- * use GC_gc.
- */
-void GC_doGC (GC_state s, uint bytesRequested, uint stackBytesRequested);
-
 /* GC_done should be called after the program is done.
  * munmaps heap and stack.
  * Prints out gc statistics if s->summary is set.
  */
 void GC_done (GC_state s);
 
-/* GC_enter is fo use by GC functions only.
- * It is called when transitioning from the mutator to the GC.
- */
-void GC_enter (GC_state s);
-
 /* GC_finishHandler should be called by the mutator signal handler thread when
  * it is done handling the signal.
  */
 void GC_finishHandler (GC_state s);
 
-/* GC_foreachPointerInObject (s, f, p) applies f to each pointer in the object
- * pointer to by p.
- */
-typedef void (*GC_pointerFun)(GC_state s, pointer *p);
-pointer GC_foreachPointerInObject(GC_state s, GC_pointerFun f, pointer p);
-
-void GC_fromSpace (GC_state s);
-
 /* GC_gc does a gc.
  * This will also resize the stack if necessary.
  * It will also switch to the signal handler thread if there is a pending signal.
@@ -422,16 +406,10 @@
 		and slot < s->stackBottom + s->currentThread->stack->reserved;
 }
 
-/* GC_leave is for use by GC functions only. 
- * It is called when transition from the GC to the mutator.
- */
-void GC_leave (GC_state s);
-
 void GC_loadWorld (GC_state s, 
 			char *fileName,
 			void (*loadGlobals)(FILE *file));
 
-bool GC_mutatorInvariant (GC_state s);
 
 /*
  * Build the header for an object, given the index to its type info.
@@ -447,18 +425,7 @@
 /* Return a serialized version of the object rooted at root. */
 /* pointer GC_serialize(GC_state s, pointer root); */
 
-void GC_setHeapParams (GC_state s, uint size);
-
-void GC_setStack (GC_state s);
-
 /* Return the amount of heap space taken by the object pointed to by root. */
 uint GC_size (GC_state s, pointer root);
-
-void GC_toSpace (GC_state s);
-
-/* Translate all pointers to the heap from within the stack and the heap for
- * a heap that has moved from s->base == old to s->base.
- */
-void GC_translateHeap(GC_state s, pointer from, pointer to, uint size);
 
 #endif /* #ifndef _MLTON_GC_H */





-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
PC Mods, Computing goodies, cases & more
http://thinkgeek.com/sf
_______________________________________________
MLton-devel mailing list
MLton-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mlton-devel