[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