home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************************
- *
- * Object Table maintenance module.
- *
- ***********************************************************************/
-
- /***********************************************************************
- *
- * Copyright (C) 1990, 1991 Free Software Foundation, Inc.
- * Written by Steve Byrne.
- *
- * This file is part of GNU Smalltalk.
- *
- * GNU Smalltalk is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 1, or (at your option) any later
- * version.
- *
- * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * GNU Smalltalk; see the file COPYING. If not, write to the Free Software
- * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- ***********************************************************************/
-
-
- /*
- * Change Log
- * ============================================================================
- * Author Date Change
- * sbyrne 8 Apr 90 Changed oopFree to oopValid to fix the bug with
- * someInstance losing after GC's due to objects that
- * have non-free OOP table entries, but point to freed
- * objects.
- *
- * sbyrne 7 Apr 90 Increased mem space size to 4M. This can be
- * decreased as necessary.
- *
- * sbyrne 24 Feb 90 Update to change log: there are no longer any
- * explicitly allocated OOPs due to the new symbol table
- * structure; the comment below is now a noop.
- *
- * sbyrne 20 Sep 89 Added oop table slot GC'ing. I'm not dealing with
- * oop table slots that are explictly allocated; I
- * believe that most OOP slots are not explicitly chosen
- * and so not running the incremental reclaimer for that
- * case shouldn't hurt us.
- *
- * sbyrne 12 Sep 89 Much of the garbage collector's operation depends on
- * the fact that only 1 flip will occur between any two
- * operations (such as a compilation, or a byte-code).
- * The code would be much more complex if this were not
- * the case, and I'm not sure that things would even be
- * possible if this were not the case. Anyway, there is
- * code in this routine to check for that eventuality
- * and to halt the system if it occurs.
- *
- * sbyrne 6 Sep 89 started implementing the garbage collector (YAY!!!)
- *
- * sbyrne 13 Jan 89 Created.
- *
- */
-
-
- #include <stdio.h>
- #include "mst.h"
- #include "mstoop.h"
- #include "mstdict.h"
- #include "mstsave.h"
- #include "mstcomp.h"
-
- /* Size of the object semi spaces, in bytes */
- #define K 1024
-
- #ifndef atarist
- #define MEM_SPACE_SIZE (4 * K * K)
- #else
- #define MEM_SPACE_SIZE (1152 * K)
- #endif
-
- #define oopReclaimFactor 4 /* how many oops to reclaim per oop alloc */
- /* #define INLINE_MACROS */
-
-
- /* Define this flag to turn on debugging code for OOP table management */
- /* #define OOP_DEBUGGING */
-
-
-
- #define alignSize(size) \
- ( ((size) + DOUBLE_ALIGNMENT - 1) & ~(DOUBLE_ALIGNMENT - 1) )
-
- #define objSpace(obj) \
- ((char *)(obj) >= spaces[1].space)
-
-
- typedef struct CompiledMethodStruct *Method;
-
- extern Boolean regressionTesting;
-
- /* These are the real OOPS for nil, true, and false */
- OOP nilOOP, trueOOP, falseOOP;
-
- /* The OOP table. This contains a pointer to the object, and some flag
- bits indicating which semispace the pointed-to object lives in. Some
- of the bits indicate the difference between the allocated length (stored
- in the object itself, and the real length, for things like byte strings
- that may not be an even multiple of 4 (== sizeof(void *)). */
- struct OOPStruct oopTable[TOTAL_OOP_TABLE_SLOTS];
-
- /* This is the head of the free list. The free list is maintained in the
- oop table. Each OOP on the free list has a bit indicating that it's free,
- a pointer to the next free OOP, and a pointer to the previous free OOP.
- when this points at NIL, we're out of space */
- OOP freeOOPs;
- int numFreeOOPs;
-
- /* The indices of which is the current new space (toSpace) and which is
- the current old space (fromSpace). At GC flip time, these two are
- interchanged. */
- char/*int*/ fromSpace, toSpace;
-
- Boolean gcFlipped, gcState, gcMessage;
- int gcFlipCounter;
-
-
- /* This vector holds the storage for all the Character objects in the system.
- Since all character objects are unique, we pre-allocate space for 256 of
- them, and treat them as special built-ins when doing garbage collection.*/
- CharObject charObjectTable[NUM_CHAR_OBJECTS];
-
- /* This is "nil" object in the system. That is, the single instance of the
- UndefinedObject class, which is called "nil". */
- struct NilObjectStruct nilObject;
-
- /* These represent the two boolean objects in the system, true and false.
- This is the object storage for those two objects.
- false == &booleanObjects[0], true == &booleanObjects[1] */
- struct BooleanObjectStruct booleanObjects[2];
-
- struct memorySpaceStruct {
- char *space;
- char *allocPtr;
- char *copyPtr;
- char *scanPtr;
- unsigned long size;
- };
-
- /* These two variables represent information about the semispaces. spaces
- holds the information for each semispace (basically the pointer to the
- base of the space, and the pointers into it for allocation, copying, and
- scanning. curSpace holds the address of one of the two semispace data
- structures, and is used by the garbage collector */
- static struct memorySpaceStruct spaces[2];
- static struct memorySpaceStruct *curSpace;
-
- static Object curObject;
- static long curObjectSize, copyQuota;
- static double copyRate;
- static double copyRateAdjustment = 1.50; /* copy 50% more than last time */
- static long oopTableIndex;
-
- static Object moveObject();
- static void initCharObject(), moveRootOOPs(),
- initSpace(), markBuiltinOOPs(),
- copyReferencedObjects(), finishOOPScan(), clearOldOOPs();
-
- #ifdef remove_soon /* Tue May 15 01:23:08 1990 */
- /**/static char marks[TOTAL_OOP_TABLE_SLOTS];
- /**/static void zeroMarks();
- /**/static int traceOOPStuff = 0;
- #endif /* remove_soon Tue May 15 01:23:08 1990 */
- /*
- * void initOOPTable()
- *
- * Description
- *
- * Initialize the OOP table. Initially, all the OOPs are on the OOP free
- * list so that's just how we initialize them. We do as much
- * initialization as we can, but we're called before classses are
- * defined, so things that have definite classes must wait until
- * the classes are defined.
- *
- */
- void initOOPTable()
- {
- int i;
-
- #ifdef remove_soon /* Mon May 14 23:41:50 1990 */
- /**/zeroMarks();
- #endif /* remove_soon Mon May 14 23:41:50 1990 */
-
- numFreeOOPs = OOP_TABLE_SIZE;
-
- for (i = 0; i < OOP_TABLE_SIZE; i++) {
- oopTable[i].object = (Object)&oopTable[i+1];
- oopTable[i].isFree = 1; /* setting this free bit */
- }
- oopTable[i-1].object = nil;
-
- freeOOPs = oopTable;
-
- nilOOP = &oopTable[nilOOPIndex];
- trueOOP = &oopTable[trueOOPIndex];
- falseOOP = &oopTable[falseOOPIndex];
-
- nilOOP->isFree = trueOOP->isFree = falseOOP->isFree = 0;
- nilOOP->emptyBytes = trueOOP->emptyBytes = falseOOP->emptyBytes = 0;
-
- nilOOP->object = (Object)&nilObject;
- nilObject.objSize = ROUNDED_WORDS(sizeof(struct NilObjectStruct));
-
- trueOOP->object = (Object)&booleanObjects[0];
- falseOOP->object = (Object)&booleanObjects[1];
- booleanObjects[0].objSize = ROUNDED_WORDS(sizeof(struct BooleanObjectStruct));
- booleanObjects[1].objSize = ROUNDED_WORDS(sizeof(struct BooleanObjectStruct));
- booleanObjects[0].booleanValue= trueOOP;
- booleanObjects[1].booleanValue= falseOOP;
- }
-
-
- /*
- * void initNil()
- *
- * Description
- *
- * Initialize the "nil" object.
- *
- */
- void initNil()
- {
- nilObject.objClass = undefinedObjectClass;
- }
-
- /*
- * void initBooleans()
- *
- * Description
- *
- * Initialize the two boolean objects, after their respective classes have
- * been created.
- *
- */
- void initBooleans()
- {
- booleanObjects[0].objClass = trueClass;
- booleanObjects[1].objClass = falseClass;
- }
-
- /*
- * void initCharTable()
- *
- * Description
- *
- * Initialize the instances of the class Character, after that class has
- * been created.
- *
- */
- void initCharTable()
- {
- int i;
-
- for (i = 0; i < NUM_CHAR_OBJECTS; i++) {
- initCharObject(i);
- oopTable[i + CHAR_OBJECT_BASE].object = (Object)&charObjectTable[i];
- oopTable[i + CHAR_OBJECT_BASE].isFree = 0;
- oopTable[i + CHAR_OBJECT_BASE].emptyBytes = 0;
- }
- }
-
- /*
- * static void initCharObject(i)
- *
- * Description
- *
- * Initialize a single character object.
- *
- * Inputs
- *
- * i : The index of the character object, in the range 0..255.
- *
- */
- static void initCharObject(i)
- int i;
- {
- charObjectTable[i].objSize = ROUNDED_WORDS(sizeof(CharObject));
- charObjectTable[i].objClass = charClass;
- charObjectTable[i].charVal = i;
- }
-
- /*
- * void fixupMetaclassObjects()
- *
- * Description
- *
- * Called after the fundamental class hierarchy has been defined, this
- * function goes through and fixes up all the objects in the oop table
- * that don't have a objClass (objClass == nilOOP). It's a
- * chicken-and-egg problem: the metaclassClass doesn't yet exist when the
- * hierarchy is put together, so after it's created, we have to go back
- * and fix all the metaclasses that we created.
- *
- */
- void fixupMetaclassObjects()
- {
- int i;
-
- for (i = 0; i < OOP_TABLE_SIZE; i++) {
- if (!oopTable[i].isFree && isNil(oopTable[i].object->objClass)) {
- oopTable[i].object->objClass = metaclassClass;
- }
- }
- }
-
- /*
- * OOP findAnInstance(classOOP)
- *
- * Description
- *
- * Finds and returns an instance of the class CLASSOOP. Returns "nil" if
- * there are no instances present.
- *
- * Inputs
- *
- * classOOP:
- * OOP for a class for which to find an instance
- *
- * Outputs
- *
- * The first instance of the given class in the OOP table.
- */
- OOP findAnInstance(classOOP)
- OOP classOOP;
- {
- int i;
-
- for (i = 0; i < OOP_TABLE_SIZE; i++) {
- if (!oopTable[i].isFree && oopTable[i].object->objClass == classOOP) {
- return (&oopTable[i]);
- }
- }
-
- return (nilOOP);
- }
-
- /*
- * long oopIndex(oop)
- *
- * Description
- *
- * Returns the index within the OOP table of the given OOP.
- *
- * Inputs
- *
- * oop : OOP to return index of
- *
- * Outputs
- *
- * Returned index in the OOP table, in range 0..TOTAL_OOP_TABLE_SLOTS.
- */
- long oopIndex(oop)
- OOP oop;
- {
- return (oop - oopTable);
- }
-
- /*
- * Boolean oopIndexValid(index)
- *
- * Description
- *
- * Checks to see if index represents a valid OOP.
- *
- * Inputs
- *
- * index : a long index into the OOP table, apparently 1 based due to
- * being called from Smalltalk via a primitive.
- *
- * Outputs
- *
- * True if the index represents a valid OOP table element, false
- * otherwise.
- */
- Boolean oopIndexValid(index)
- long index;
- {
- return (index >= 1 && index <= TOTAL_OOP_TABLE_SLOTS);
- }
-
- #ifndef INLINE_MACROS
-
- OOP oopAt(index)
- long index;
- {
- return (oopAtMac(index));
- }
-
- #endif /* INLINE_MACROS */
-
- void swapObjects(oop1, oop2)
- OOP oop1, oop2;
- {
- struct OOPStruct tempOOP;
-
- tempOOP = *oop2;
- *oop2 = *oop1;
- *oop1 = tempOOP;
- }
-
- OOP charOOPAt(c)
- Byte c;
- {
- return (&oopTable[c + CHAR_OBJECT_BASE]);
- }
-
- Byte charOOPValue(charOOP)
- OOP charOOP;
- {
- return (charOOP - &oopTable[CHAR_OBJECT_BASE]);
- }
-
- void printObject(oop)
- OOP oop;
- {
- if (isInt(oop)) {
- printf("%d", toInt(oop));
- } else if (isNil(oop)) {
- printf("nil");
- } else if (oop == trueOOP) {
- printf("true");
- } else if (oop == falseOOP) {
- printf("false");
- } else if (oopClass(oop) == charClass) {
- printf("$%c", charOOPValue(oop));
- } else if (oopClass(oop) == floatClass) {
- printf("%#g", floatOOPValue(oop));
- } else if (oopClass(oop) == symbolClass) {
- printf("#"); printSymbol(oop);
- } else if (oopClass(oop) == stringClass) {
- /* ### have to quote embedded quote chars */
- printf("'");
- printString(oop);
- printf("'");
- } else {
- printOOPConstructor(oop);
- }
- }
-
- Boolean oopValid(oop)
- OOP oop;
- {
- return (!oop->isFree && (oop->oddMark || oop->evenMark));
- }
-
- #ifndef INLINE_MACROS
-
- Boolean oopAvailable(index)
- long index;
- {
- return (oopAvailableMac(index);
- }
-
- #endif /* INLINE_MACROS */
-
- /*
- * OOP allocOOP(obj)
- *
- * Description
- *
- * Given an object OBJ, this routine allocates an OOP table slot for it
- * and returns it. It marks the OOP so that it indicates the object is in
- * new space, and that the oop has been referenced on this pass (to keep
- * the OOP table reaper from reclaiming this OOP).
- *
- * Inputs
- *
- * obj : Object that the new OOP should point to.
- *
- * Outputs
- *
- * An OOP, which is the address of an element in the OOP table.
- */
- OOP allocOOP(obj)
- Object obj;
- {
- OOP oop;
- int i;
-
- for (i = 0; i < oopReclaimFactor; i++) {
- if (oopTableIndex >= OOP_TABLE_SIZE) {
- break;
- }
- oop = &oopTable[oopTableIndex++];
- if (!oop->isFree) {
- if (!oop->evenMark && !oop->oddMark) {
- #ifdef remove_soon /* Mon May 14 23:42:47 1990 */
- /**/if (traceOOPStuff) {
- /**/printf("freeing %d\n", oopTableIndex - 1);
- /**/}
- #endif /* remove_soon Mon May 14 23:42:47 1990 */
- #ifdef remove_soon /* Tue May 15 02:23:58 1990 */
- /**/ if (marks[oopTableIndex - 1]) {
- /**/ printf("freeling link %d\n", oopTableIndex -1 );
- /**/ }
- #endif /* remove_soon Tue May 15 02:23:58 1990 */
- /* we've found a dead one...add it to the free list */
- numFreeOOPs++;
- oop->object = (Object)freeOOPs;
- freeOOPs = oop;
- freeOOPs->isFree = true;
- } else if (toSpace) {
- #ifdef remove_soon /* Mon May 14 23:42:54 1990 */
- /**/if (oop == &oopTable[100]) { /* ### */
- /**/printf("clearing 101 even\n");
- /**/}
- #endif /* remove_soon Mon May 14 23:42:54 1990 */
- oop->evenMark = 0;
- #ifdef remove_soon /* Tue May 15 02:23:47 1990 */
- /**/ if (marks[oopTableIndex - 1]) {
- /**/ printf("even marking %d\n", oopTableIndex -1 );
- /**/ }
- #endif /* remove_soon Tue May 15 02:23:47 1990 */
- } else {
- #ifdef remove_soon /* Mon May 14 23:42:59 1990 */
- /**/if (oop == &oopTable[100]) { /* ### */
- /**/printf("clearing 101 odd\n");
- /**/}
- #endif /* remove_soon Mon May 14 23:42:59 1990 */
- oop->oddMark = 0;
- #ifdef remove_soon /* Tue May 15 02:23:31 1990 */
- /**/ if (marks[oopTableIndex - 1]) {
- /**/ printf("odd marking %d\n", oopTableIndex -1);
- /**/ }
- #endif /* remove_soon Tue May 15 02:23:31 1990 */
- }
- #ifdef remove_soon /* Mon May 14 23:42:19 1990 */
- /**/if (traceOOPStuff)
- /**/{ int i;
- /**/
- /**/ i = oop - oopTable;
- /**/if (marks[i] != 0) {
- /**/ printf("AllocOOP: oop %d value %d even %d odd %d\n", i, marks[i],
- /**/ oop->evenMark, oop->oddMark);
- /**/}
- /**/marks[i] = 2; /* mark as freed */
- /**/}
- #endif /* remove_soon Mon May 14 23:42:19 1990 */
- }
- }
-
- oop = freeOOPs;
-
- numFreeOOPs--;
-
- if (oop == nil) {
- errorf("Ran out of OOP Table slots!!!");
- exit(1);
- /* ### this needs to be fixed */
- }
-
- if (!oop->isFree) {
- errorf("Allocating allocated OOP!!!");
- exit(0);
- }
-
- freeOOPs = (OOP)oop->object;
-
- if (objSpace(obj) != toSpace) {
- obj = moveObject(obj);
- }
-
- #ifdef remove_soon /* Mon May 14 23:43:09 1990 */
- /**/if (oop == &oopTable[100] || oop == &oopTable[123]) { /* ### */
- /**/printf("allocating %d\n", oop - oopTable);
- /**/debug();
- /**/}
- #endif /* remove_soon Mon May 14 23:43:09 1990 */
-
- oop->object = obj;
- oop->isFree = false;
- oop->emptyBytes = 0;
- oop->inSpace = toSpace;
- oop->oddMark = toSpace;
- oop->evenMark = !toSpace;
-
- #ifdef remove_soon /* Mon May 14 23:42:32 1990 */
- /**/{ extern int okToPrint;
- /**/if (traceOOPStuff && okToPrint) {
- /**/ static Boolean dumped = false;
- /**/ if (!dumped) {
- /**/ OOP o;
- /**/ dumped = true;
- /**/ for (o = oopTable; o < oop; o++) {
- /**/ printf("Object %d is ", o - oopTable); printObject(o);
- /**/ printf("\n");
- /**/ }
- /**/ }
- /**/if (oop - oopTable > 23000) {
- /**/ printf("Allocated %d = ", oop - oopTable); printObject(oop);
- /**/ printf("\n");
- /**/}
- /**/}
- /**/}
- #endif /* remove_soon Mon May 14 23:42:32 1990 */
- #ifdef remove_soon /* Tue May 15 02:23:14 1990 */
- /**/
- /**/ if (oopClass(oop) == symLinkClass) {
- /**/ marks[oop - oopTable] = 1;
- /**/ printf("Allocating link %d: ", oop-oopTable);
- /**/ printSymLink(oop);
- /**/ printf("\n");
- /**/ }
- #endif /* remove_soon Tue May 15 02:23:14 1990 */
-
- return (oop);
- }
-
-
- /*
- * void setOOPObject(oop, object)
- *
- * Description
- *
- * Sets the object of OOP to be OBJECT. Makes sure that the object is in
- * new space before it assigns it to OOP.
- *
- * Inputs
- *
- * oop : an OOP table entry to be assigned into
- * object: an object that the OOP should point to.
- *
- */
- void setOOPObject(oop, object)
- OOP oop;
- Object object;
- {
- if (objSpace(object) != toSpace) {
- object = moveObject(object);
- }
- oop->object = object;
- oop->inSpace = toSpace;
- }
-
- /*
- * void initMem()
- *
- * Description
- *
- * Initialize the memory allocator. Both semispaces are allocated, and
- * the various garbage collection flags are set to their initial values.
- *
- */
- void initMem()
- {
- int i;
-
- for (i = 0; i < 2; i++) {
- spaces[i].space = (char *)malloc(MEM_SPACE_SIZE);
- if (spaces[i].space == NULL) {
- printf("Malloc failure; you're out of paging/swapping space\n");
- exit(1);
- }
- initSpace(&spaces[i]);
- }
-
- curSpace = &spaces[0];
- toSpace = 0;
- fromSpace = !toSpace;
- gcFlipped = false;
- gcState = false;
- gcMessage = true;
- copyRate = 0.0; /* don't copy anything until first flip */
- copyQuota = 0;
- oopTableIndex = 0;
- markBuiltinOOPs();
- clearGCFlipFlags();
- }
-
- Object curSpaceAddr()
- {
- return ((Object)curSpace->space);
- }
-
- void setSpaceInfo(size)
- long size;
- {
- curSpace->copyPtr = curSpace->scanPtr = curSpace->space + size;
- curSpace->size -= size;
- }
-
- #ifndef INLINE_MACROS
-
- void clearGCFlipFlags()
- {
- clearGCFlipFlagsMac();
- }
-
- #endif /* INLINE_MACROS */
-
- /*
- * Object allocObj(size)
- *
- * Description
- *
- * Allocate and return space for an object of SIZE bytes. This basically
- * means moving the allocation pointer for the current space down by SIZE
- * bytes, and, if there isn't enough space left, flipping the garbage
- * collector to switch semispaces. The space is merely allocated; it is
- * not initialized.
- *
- * Inputs
- *
- * size : size in bytes of the object to allocate. This will be rounded
- * by this routine up to a suitable boundary, typically to a 4
- * byte boundary.
- *
- * Outputs
- *
- * Address of the newly allocated object.
- */
- Object allocObj(size)
- long size;
- {
- size = alignSize(size);
-
- copyReferencedObjects((long)(size * copyRate));
- curSpace->allocPtr -= size;
- if (curSpace->allocPtr <= curSpace->copyPtr) {
- gcFlip();
- curSpace->allocPtr -= size;
- }
-
- return ((Object)curSpace->allocPtr);
- }
-
-
- /*
- * static void copyReferencedObjects(numBytes)
- *
- * Description
- *
- * This is the heart of the garbage collector. It is told that NUMBYTES
- * have been allocated, and it sweeps through a proportional number of
- * objects that have been copied into new space, looking for references to
- * objects that are still in old space. It has to special case
- * CompiledMethod objects due to their unusual structure. It uses global
- * variables to preserve its state so that it can sweep through only as
- * many objects as it wants, and resume sweeping on the next allocation
- * where it left off.
- *
- * Inputs
- *
- * numBytes:
- * The number of bytes that have been allocated. Used as a
- * parameter that controls how much space to sweep on this
- * iteration.
- *
- */
- static void copyReferencedObjects(numBytes)
- long numBytes;
- {
- Object result, object;
- OOP curClass;
- InstanceSpec instanceSpec;
- int stepSize, i;
- Method method;
-
- copyQuota += numBytes;
- while (curSpace->scanPtr < curSpace->copyPtr
- && copyQuota > 0) { /* there's more to scan */
- if (curObject == nil) {
- /* if there is no current object, start off with the object's class */
- object = (Object)curSpace->scanPtr;
- curClass = object->objClass;
- maybeMoveOOP(curClass);
-
- if (curClass == compiledMethodClass) {
- /* Compiled methods have to be dealt with specially since they
- * have a structure that's unlike a regular Smalltalk object:
- * it has two fixed instance variables (description and
- * methodHeader), a variable number of literals, and then
- * a bunch of bytecodes, which must be skipped over */
- stepSize = object->objSize * sizeof(OOP);
- method = (Method)object;
- maybeMoveOOP(method->descriptor);
- if (method->header.headerFlag == 0 || method->header.headerFlag == 3) {
- for (i = 0; i < method->header.numLiterals; i++) {
- maybeMoveOOP(method->literals[i]);
- }
- }
-
- curSpace->scanPtr += stepSize;
- copyQuota -= stepSize;
- } else if (!classIsPointers(curClass)) {
- /* nothing to scan, just skip over it */
- stepSize = object->objSize * sizeof(OOP);
- curSpace->scanPtr += stepSize;
- copyQuota -= stepSize;
- } else {
- /* we've got an object with sub structure, so we set the object
- * size pointer to 2 words less than the object size (ignore
- * the header; we've already copied the class) and set the
- * scan pointer to the first word of the object. We then continue
- * to go through the normal scanning procedure (fall out the
- * bottom of the if and go back to the top of the loop again)
- */
- curObject = object;
- curObjectSize = numOOPs(curObject) * sizeof(OOP);
- curSpace->scanPtr = (char *)curObject->data;
- }
-
- } else {
- /* we're part way through scanning an object, continue to scan... */
- if (curObjectSize <= 0) {
- curObject = nil;
- } else {
- maybeMoveOOP(*(OOP *)curSpace->scanPtr);
- stepSize = sizeof(OOP);
- curSpace->scanPtr += stepSize;
- curObjectSize -= stepSize;
- copyQuota -= stepSize;
- }
- }
- }
-
- if (copyQuota < 0) {
- copyQuota = 0;
- }
- }
-
- /*
- * Boolean gcOff()
- *
- * Description
- *
- * Turns off the garbage collector. Returns the previous on/off state.
- *
- * Outputs
- *
- * Previous state of the garbage collector (on or off).
- */
- Boolean gcOff()
- {
- Boolean oldGCState;
-
- oldGCState = gcState;
- gcState = false;
- return (oldGCState);
- }
-
- /*
- * void gcOn()
- *
- * Description
- *
- * Turns on the garbage collector.
- *
- */
- void gcOn()
- {
- gcState = true;
- }
-
- /*
- * void setGCState(state)
- *
- * Description
- *
- * Set the garbage collector flag to the specified state (either on or
- * off).
- *
- * Inputs
- *
- * state : Boolean, true => gc on.
- *
- */
- void setGCState(state)
- Boolean state;
- {
- gcState = state;
- }
-
-
- /*
- * void fullGC()
- *
- * Description
- *
- * Perform a complete garbage collection of the system. This basically
- * copies all the remaining referenced objects to new space, flips old and
- * new space, copies the root set to the now new space, and then copies
- * all the referenced objects to the now new space.
- *
- */
- void fullGC()
- {
- if (!gcState) {
- /* can't do a full GC with gc turned off! */
- return;
- }
-
-
- copyReferencedObjects(0x7FFFFFFF); /* copy what we can */
- gcFlip();
- copyReferencedObjects(0x7FFFFFFF);
- clearOldOOPs();
- }
-
-
-
-
-
- /*
- * static void finishOOPScan()
- *
- * Description
- *
- * Scans through the OOP table, finishing the incremental OOP table
- * scanning that goes on as OOPs are allocated.
- *
- */
- static void finishOOPScan()
- {
- OOP oop;
- int i;
-
- for (i = oopTableIndex; i < OOP_TABLE_SIZE; i++) {
- oop = &oopTable[i];
- if (!oop->isFree) {
- if (!oop->evenMark && !oop->oddMark) {
- /* we've found a dead one...add it to the free list */
- numFreeOOPs++;
- oop->object = (Object)freeOOPs;
- freeOOPs = oop;
- freeOOPs->isFree = true;
- /* printf("Freed %d\n", i); */
- } else if (toSpace) {
- oop->evenMark = 0;
- } else {
- oop->oddMark = 0;
- }
- }
- }
- }
-
- /*
- * static void clearOldOOPs()
- *
- * Description
- *
- * Scans through the OOP table, removing OOPS that have died. Only
- * called at the end of a full GC to remove any stragglers.
- *
- */
- static void clearOldOOPs()
- {
- OOP oop;
- int i;
-
- for (i = oopTableIndex; i < OOP_TABLE_SIZE; i++) {
- oop = &oopTable[i];
- if (!oop->isFree) {
- if ((toSpace && !oop->oddMark) || (!toSpace && !oop->evenMark)) {
- /* we've found a dead one...add it to the free list */
- numFreeOOPs++;
- oop->object = (Object)freeOOPs;
- freeOOPs = oop;
- freeOOPs->isFree = true;
- /* printf("Freed %d\n", i); */
- } else if (toSpace) {
- oop->evenMark = 0;
- } else {
- oop->oddMark = 0;
- }
- }
- }
- }
-
- /*
- * void gcFlip()
- *
- * Description
- *
- * Switches the garbage collector's notion of which space is "new" space
- * and which is "old" space. Readjusts the garbage collection parameters
- * based on things like the allocation to copying ratio. Copies the root
- * set to new space.
- *
- */
- void gcFlip()
- {
- long oldCopySize, oldNewSize;
-
- if (!gcState) {
- errorf("Attempted to do a gcFlip with garbage collector off!");
- exit(1);
- }
-
- if (gcFlipCounter >= 1) {
- errorf("Attempted to do a gcFlip too soon after a gcFlip!");
- exit(1);
- }
-
-
- #ifdef OOP_DEBUGGING
- printf("%d free oops = %.2f%%, scanner was at %d/%d\n", numFreeOOPs,
- 100.0 * numFreeOOPs / OOP_TABLE_SIZE, oopTableIndex, OOP_TABLE_SIZE);
- #endif
-
- if (gcMessage && !regressionTesting) {
- /* print the first part of this message before we finish scanning
- * oop table for live ones, so that the delay caused by this scanning
- * is apparent.
- */
- printf("\"GC flipping "); fflush(stdout);
- }
-
- finishOOPScan(); /* make sure we're done */
-
- oldCopySize = curSpace->copyPtr - curSpace->space;
- oldNewSize = curSpace->space + MEM_SPACE_SIZE - curSpace->allocPtr;
-
- if (oldCopySize == 0) { /* ### Experimental */
- oldCopySize = oldNewSize;
- }
-
-
- copyRate = ((double)oldCopySize) / oldNewSize;
- copyRate *= copyRateAdjustment;
-
- toSpace = !toSpace;
- fromSpace = !fromSpace;
- curSpace = &spaces[toSpace];
- curObject = nil;
- copyQuota = 0;
- oopTableIndex = 0;
- initSpace(curSpace);
-
- #ifdef remove_soon /* Mon May 14 23:41:55 1990 */
- /**/zeroMarks();
- #endif /* remove_soon Mon May 14 23:41:55 1990 */
-
- /* note the use of quotation marks around the printed message. The
- idea here was to make them appear as Smalltalk comments, so that
- generated output could be fed to another Smalltalk without harm. */
- if (gcMessage && !regressionTesting) {
- printf("to space %d...", toSpace); fflush(stdout);
- printf("copied space = %.1f%%...", oldCopySize * 100.0 / MEM_SPACE_SIZE);
- }
- moveRootOOPs();
- if (gcMessage && !regressionTesting) {
- printf("done\"\n");
- }
- gcFlipped = true;
- }
-
- /*
- * static void moveRootOOPs()
- *
- * Description
- *
- * Copies the root objects from old space to new space. All of the root
- * objects are those that are mentioned in the set of objects that are
- * known specially by the interpreter, those that are being used by the
- * interpreter, and some information about compilation state. Also, the
- * built-in oops (Characters, nil, true, false) are marked as being in new
- * space so that they won't ever be moved.
- *
- */
- static void moveRootOOPs()
- {
- OOP **oopPtr, oop;
- int i;
-
- markBuiltinOOPs();
-
- /* copy objects that have global pointers */
- for (oopPtr = globalOOPs; *oopPtr; oopPtr++) {
- oop = **oopPtr;
- maybeMoveOOP(oop); /* use the maybe form here so that we don't
- * accidentally move builtins, which have
- * already been marked as being in the new
- * space
- */
- }
-
- moveProcessorRegisters();
- copyCompileContext();
- }
-
- /*
- * static void markBuiltinOOPs()
- *
- * Description
- *
- * Marks all of the builtin OOPS (nil, true, false, and the Characters) as
- * being in the current new space.
- *
- */
- static void markBuiltinOOPs()
- {
- int i;
-
- for (i = OOP_TABLE_SIZE; i < TOTAL_OOP_TABLE_SLOTS; i++) {
- oopTable[i].inSpace = toSpace;
- oopTable[i].evenMark = oopTable[i].oddMark = 1;
- }
- }
-
- /*
- * static void initSpace(space)
- *
- * Description
- *
- * Initializes the allocation and copying pointers for semispace SPACE.
- *
- * Inputs
- *
- * space : Semispace index number.
- *
- */
- static void initSpace(space)
- struct memorySpaceStruct *space;
- {
- space->copyPtr = space->scanPtr = space->space;
- space->size = MEM_SPACE_SIZE;
- space->allocPtr = space->space + space->size;
- }
-
-
- #ifndef INLINE_MACROS
-
- /*
- * void maybeMoveOOP(oop)
- *
- * Description
- *
- * Move OOP to new space if it's not already there.
- *
- * Inputs
- *
- * oop : OOP to be examined, and, if it's in old space, moved to new
- * space.
- *
- */
- void maybeMoveOOP(oop)
- OOP oop;
- {
- maybeMoveOOPMac(oop);
- }
-
- #endif /* INLINE_MACROS */
-
-
- /*
- * void moveOOP(oop)
- *
- * Description
- *
- * Moves an OOP from old space to new space unconditionally. Basically
- * marks the OOP as being in the current new space, copies the object that
- * the oop points to to new space, and sets the even/odd flags to keep the
- * OOP table garbage collector from reaping this OOP.
- *
- * Inputs
- *
- * oop : OOP to be moved. Should always be in OLD space.
- *
- */
- void moveOOP(oop)
- OOP oop;
- {
- Object object;
-
- object = oopToObj(oop);
- oop->inSpace = toSpace;
- oop->object = moveObject(object);
- #ifdef remove_soon /* Mon May 14 23:42:39 1990 */
- /**/if (traceOOPStuff)
- /**/{ int i;
- /**/
- /**/ i = oop - oopTable;
- /**/if (marks[i] != 0) {
- /**/ printf("moveOOP: oop %d has a mark value of %d\n", i, marks[i]);
- /**/}
- /**/marks[i] = 1;
- /**/}
- #endif /* remove_soon Mon May 14 23:42:39 1990 */
- if (toSpace) {
- oop->oddMark = 1;
- } else {
- oop->evenMark = 1;
- }
- }
-
- /*
- * static Object moveObject(object)
- *
- * Description
- *
- * Copies OBJECT from old space to new space. Adjusts the garbage
- * collectors pointers to indicate that the object has been added to new
- * space so that the scanner will see it.
- *
- * Inputs
- *
- * object: Object to be moved to new space.
- *
- */
- static Object moveObject(object)
- Object object;
- {
- int size;
-
- size = object->objSize * sizeof(OOP);
- memcpy(curSpace->copyPtr, object, size);
- object = (Object)curSpace->copyPtr;
- curSpace->copyPtr += size;
- curSpace->size -= size;
- if (curSpace->copyPtr >= curSpace->allocPtr) {
- errorf("Garbage collector failed...ran out of room while copying!!!");
- exit(0);
- }
-
- return (object);
- }
-
- /*
- * void printFreeList()
- *
- * Description
- *
- * Debugging support routine. Prints the free list. Meant only to be
- * called from a debugger.
- *
- */
- void printFreeList()
- {
- OOP oop;
- for (oop = freeOOPs; oop != nil; oop = (OOP)oop->object) {
- printf("oop %x\n", oop);
- printf("oop isfree %d\n", oop->isFree);
- }
- }
-
- /*
- * debug()
- *
- * Description
- *
- * Used for debugging. You set a breakpoint in the debug routine in the
- * debugger, and have code call it when you want it to stop. Performs no
- * action normally.
- *
- */
- debug()
- {
- }
-
-
- #ifdef remove_soon /* Mon May 14 23:42:01 1990 */
- /**/static void zeroMarks()
- /**/{
- /**/ char *p;
- /**/
- /**/ for (p = marks; p < &marks[TOTAL_OOP_TABLE_SLOTS]; ) {
- /**/ *p++ = 0;
- /**/ }
- /**/}
- #endif /* remove_soon Mon May 14 23:42:01 1990 */
-