home *** CD-ROM | disk | FTP | other *** search
-
- /* Copyright (C) 1988, 1989 Herve' Touati, Aquarius Project, UC Berkeley */
-
- /* Copyright Herve' Touati, Aquarius Project, UC Berkeley */
-
- #ifdef WITH_GC
- #include <stream.h>
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/resource.h>
- #include "tags.h"
- #include "instr.h"
- #include "hash_table.h"
- #include "string_table.h"
- #include "scan.h"
- #include "inst_args.h"
- #include "inst_table.h"
- #include "memory.h"
- #include "basics.h"
- #include "top_level.h"
- #include "gc.h"
- #include "mark_copy.h"
-
- /* LOCAL DECLARATIONS */
-
- /* choice point that separates copying from marking */
- CellPtr BMIDDLE, HMIDDLE, B2;
-
- /* various stacks used during marking */
- static DownStack MARK_STACK;
- static UpStack REF_STACK;
- static CopyStack COPY_STACK;
-
- /* incremented at each GC. Just a counter */
- int GC_COUNTER;
-
- /* the mark used for marking. Equals to GC_COUNTER modulo 255 + 1 */
- unsigned char MARK;
- #ifdef WITH_VIRTUAL_BACK
- static unsigned char MARK2;
-
- inline void mark2(Cell* p)
- { MKMIN[p - HMIN] = (marked(p)) ? MARK : MARK2; }
-
- inline int marked2(Cell* p)
- { return (marked(p) || MKMIN[p - HMIN] == MARK2); }
-
- inline int unmarked2(Cell* p)
- { return (unmarked(p) && MKMIN[p - HMIN] != MARK2); }
-
- #endif
-
- /* ENVIRONMENTS and REGISTERS */
- /* creates a new environment at the top of the stack, and saves the */
- /* registers in it. Then put yet another one above it, with nothing */
- /* in it. Easier to restore than adding the registers to the current */
- /* environment. */
- static Instr dummy_instr;
- void store_regs_in_env()
- {
- int arity = instr_args[ARG_PROC]->get_arity(P->arg1);
- arity = (NUMBER_OF_REGISTERS < arity) ? NUMBER_OF_REGISTERS : arity;
- dummy_instr.arg2 = arity;
- for (int i = 0; i < arity; i++)
- E[Y1_ENV_OFFSET + i] = X[i];
- Cell* NewE = E + arity + E_TOP_OFFSET;
- NewE[B_ENV_OFFSET] = 0; /* unused */
- NewE[E_ENV_OFFSET] = cell(E);
- NewE[P_ENV_OFFSET] = cell(&dummy_instr);
- E = NewE;
- }
-
- /* restore the top of the stack as before the call to store_regs_in_env */
- void restore_top_env()
- {
- Cell* PreviousE = cellp(E[E_ENV_OFFSET]);
- int arity = instrp(E[P_ENV_OFFSET])->arg2;
- E = PreviousE;
- for (int i = 0; i < arity; i++)
- X[i] = E[Y1_ENV_OFFSET + i];
- }
-
- /* if does not point directly to new space, either it dereferences to */
- /* a pointer to new space that belongs to some living environment, */
- /* that will be traced later on, or to some old environment, which */
- /* modification would then have been trailed. Therefore, there is no */
- /* need to dereference */
- void Env::mark()
- {
- #ifdef WITH_VIRTUAL_BACK
- Cell* y = e + Y1_ENV_OFFSET + already_treated;
- Cell* y0 = e + Y1_ENV_OFFSET + size;
- for (; y < y0; y++) {
- Cell* ptr = y;
- Cell val = *ptr;
- while (get_tag(val) == TAGREF && addr(val) >= E2 && addr(val) != ptr) {
- ptr = addr(val);
- val = *ptr;
- }
- if (get_tag(val) == TAGCONST) continue;
- if (to_new_space(addr(val)))
- mark_from_base(ptr);
- }
- #else
- Cell* y = e + Y1_ENV_OFFSET + already_treated;
- Cell* y0 = e + Y1_ENV_OFFSET + size;
- for (; y < y0; y++) {
- if (get_tag(*y) == TAGCONST) continue;
- if (to_new_space(addr(*y)))
- mark_from_base(y);
- }
- #endif
- }
-
- void Env::update()
- {
- Cell* y = e + Y1_ENV_OFFSET + already_treated;
- Cell* y0 = e + Y1_ENV_OFFSET + size;
- for (; y < y0; y++)
- *y = check_and_relocate(*y);
- }
-
- /* CHOICE POINTS */
-
- ChoiceRecord SAVED_CP;
-
- /* if less than a threshold, use mark_compact instead */
- const float COPY_THRESHOLD = 0.2;
-
- int deterministic()
- {
- return (cellp(B[H_CP_OFFSET]) <= HMIN);
- }
-
- int enough_to_copy()
- {
- Cell* H_THRESHOLD = &HMIN[(int) ((float) (H-HMIN)*COPY_THRESHOLD)];
- Cell* b = B;
- while (cellp(b[H_CP_OFFSET]) > H_THRESHOLD)
- b += FIXED_CP_SIZE + b[SIZE_CP_OFFSET];
- return (cellp(b[H_CP_OFFSET]) <= HMIN);
- }
-
- /* creates a choice point at the top that is above everything else. */
- /* It is easier to code gctrail that way: don't have to worry about */
- /* boundary conditions any more. */
- void setup_cps_pass1()
- {
- /* creates a topmost choice point */
- B -= FIXED_CP_SIZE;
- B[E_CP_OFFSET] = cell(E);
- B[H_CP_OFFSET] = cell(H);
- B[TR_CP_OFFSET] = cell(TR);
- B[P_CP_OFFSET] = 0; /* unused */
- B[SIZE_CP_OFFSET] = 0;
-
- /* find BMIDDLE and B2 */
- BMIDDLE = B;
- Cell* b = B;
- while (cellp(b[H_CP_OFFSET]) > HMIN) {
- BMIDDLE = b;
- b += FIXED_CP_SIZE + b[SIZE_CP_OFFSET];
- }
- B2 = b;
-
- /* treat the case of the cps under B2 such that B.h == HMIN now */
- while (cellp(b[H_CP_OFFSET]) == HMIN) {
- b[H_CP_OFFSET] = cell(H2);
- b += FIXED_CP_SIZE + b[SIZE_CP_OFFSET];
- }
-
- /* set B2 to be above TR2 as well; save previous contents */
- SAVED_CP.tr = cellp(B2[TR_CP_OFFSET]);
- SAVED_CP.e = cellp(B2[E_CP_OFFSET]);
- SAVED_CP.h = cellp(B2[H_CP_OFFSET]);
- TR2 = min(TR2, SAVED_CP.tr);
- E2 = max(E2, SAVED_CP.e);
- B2[TR_CP_OFFSET] = cell(TR2);
- B2[E_CP_OFFSET] = cell(E2);
- B2[H_CP_OFFSET] = cell(HMIN);
-
- /* cache the H entry of BMIDDLE in a global variable */
- HMIDDLE = cellp(BMIDDLE[H_CP_OFFSET]);
- }
-
- void setup_cps_pass2()
- {
- /* restore B2 to its initial contents */
- B2[TR_CP_OFFSET] = cell(SAVED_CP.tr);
- B2[E_CP_OFFSET] = cell(SAVED_CP.e);
- B2[H_CP_OFFSET] = cell(SAVED_CP.h);
-
- /* take BMIDDLE as B2: copied stuff appears as old form now on */
- B2 = BMIDDLE;
- H2 = COPY_STACK.top();
- B2[H_CP_OFFSET] = cell(H2);
-
- /* set B2 to be above TR2 as well; save previous contents */
- SAVED_CP.tr = cellp(B2[TR_CP_OFFSET]);
- SAVED_CP.e = cellp(B2[E_CP_OFFSET]);
- TR2 = min(TR2, SAVED_CP.tr);
- E2 = max(E2, SAVED_CP.e);
- B2[TR_CP_OFFSET] = cell(TR2);
- B2[E_CP_OFFSET] = cell(E2);
- }
-
- void restore_cps()
- {
- /* restore B2 to its initial contents */
- B2[TR_CP_OFFSET] = cell(SAVED_CP.tr);
- B2[E_CP_OFFSET] = cell(SAVED_CP.e);
-
- /* relocate the H entries to their correct, final position */
- Cell* b = B;
- while (b < B2) {
- b[H_CP_OFFSET] = cell(reloc_addr(cellp(b[H_CP_OFFSET])));
- b += FIXED_CP_SIZE + b[SIZE_CP_OFFSET];
- }
-
- /* compute the new values of H, H2, TR, TR2, E2 */
- H = HMIN;
- H2 = cellp(B[H_CP_OFFSET]);
- TR = TR2 = cellp(B[TR_CP_OFFSET]);
- E2 = E;
-
- /* remove the dummy topmost choice point */
- B += FIXED_CP_SIZE;
- }
-
- /* hard to get all the benefit from this. The main problem is that we */
- /* cannot mark env variables as easily. Since this is applied only */
- /* after gctrail and gcenv, if an env variable is found to be */
- /* pointing to a location that is unmarked2 in new space, we know we */
- /* can reset it. We could extend that by dereferencing the var. If */
- /* the first entry to new space is not marked, we can reset the var */
- void Choice::virtual_backtrack()
- {
- #ifdef WITH_VIRTUAL_BACK
- Cell* var0 = tr;
- Cell* var = cellp(b[TR_CP_OFFSET]);
- tr = var;
- for (; var > var0; var--) {
- Cell* ptr = addr(*var);
- if (ptr >= E0) {
- Cell val = *ptr;
- while (get_tag(val) == TAGREF && addr(val) >= E0 && addr(val) != ptr) {
- ptr = addr(val);
- val = *ptr;
- }
- if (pointer_to_new(*ptr) && unmarked2(addr(*ptr)))
- *addr(*var) = *var;
- } else if (ptr >= HMIN) {
- if (unmarked2(ptr))
- *ptr = *var;
- }
- }
- #endif
- }
-
- /* This stack exactly simulates what would happen on backtracking */
- /* supposing we encounter an infinite sequence of fails. This is */
- /* really virtual backtracking! The problem is really the difference */
- /* in sizes of the environments, depending on the point of view! That */
- /* is the only reason why we need a stack (or marking bits). Stacks */
- /* are preferable in general because they are faster and cleaner. */
- /* the cost is on choice points only */
-
- Choice::Choice(Cell* E, Cell* B)
- {
- b = B;
- tr = cellp(B[TR_CP_OFFSET]);
- preserved.init(cellp(b[E_CP_OFFSET]));
- already_done.init(E);
- }
-
- void Choice::mark()
- {
- #ifdef WITH_VIRTUAL_BACK
- virtual_backtrack();
- Cell* x = b + X1_CP_OFFSET;
- Cell* x0 = x + b[SIZE_CP_OFFSET];
- for (; x < x0; x++) {
- Cell* ptr = x;
- Cell val = *ptr;
- while (get_tag(val) == TAGREF && addr(val) >= E2 && addr(val) != ptr) {
- ptr = addr(val);
- val = *ptr;
- }
- if (get_tag(val) == TAGCONST) continue;
- if (to_new_space(addr(val)))
- mark_from_base(ptr);
- }
- #else
- Cell* x = b + X1_CP_OFFSET;
- Cell* x0 = x + b[SIZE_CP_OFFSET];
- for (; x < x0; x++) {
- if (get_tag(*x) == TAGCONST) continue;
- if (to_new_space(addr(*x)))
- mark_from_base(x);
- }
- #endif
- }
-
- #define use(ACTION,PROC_NAME)\
- void Choice::PROC_NAME()\
- {\
- while (already_done.e > preserved.e)\
- already_done.next();\
- Env e_limit = already_done;\
- already_done = preserved;\
- while (preserved.e >= E2) {\
- if (preserved.e > e_limit.e) {\
- preserved.ACTION();\
- preserved.next();\
- } else if (preserved.e == e_limit.e) {\
- preserved.treated(e_limit.size);\
- preserved.ACTION();\
- break;\
- } else {\
- top_level_error("Inconsistent Path thru Env Stack");\
- }\
- }\
- }
- use(mark,mark_preserved_envs)
- use(mark_sweep,mark_preserved_envs_sweep)
- use(update,update_preserved_envs)
- #undef use
-
- void Choice::update()
- {
- Cell* x = b + X1_CP_OFFSET;
- Cell* x0 = x + b[SIZE_CP_OFFSET];
- for (; x < x0; x++)
- *x = check_and_relocate(*x);
- }
-
- /* rotates the size fields of the choice points [B2,B] down, putting */
- /* the one for B2 in B[SIZE_CP_OFFSET] */
- void cp_to_cp_forward()
- {
- int b2_size = B2[SIZE_CP_OFFSET];
- Cell* b = B;
- int size = b[SIZE_CP_OFFSET];
- while (b < B2) {
- b += FIXED_CP_SIZE + size;
- int temp = size;
- size = b[SIZE_CP_OFFSET];
- b[SIZE_CP_OFFSET] = temp;
- }
- B[SIZE_CP_OFFSET] = b2_size;
- }
-
- /* do the opposite. composing those two should be a noop */
- void cp_to_cp_backward()
- {
- int b_size = B[SIZE_CP_OFFSET];
- Cell* b = B2;
- int size = b[SIZE_CP_OFFSET];
- while (b > B) {
- b -= FIXED_CP_SIZE + size;
- int temp = size;
- size = b[SIZE_CP_OFFSET];
- b[SIZE_CP_OFFSET] = temp;
- }
- B2[SIZE_CP_OFFSET] = b_size;
- }
-
- /* THE TRAIL STACK */
-
- /* OLD VERSION
- void gctrail_pass1()
- {
- TrailCP cp(B2, B);
- register Cell* tr0 = cp.tr;
- register Cell* tr = cp.tr;
- register Cell* copy_tr = cp.tr;
- while (cp.nonempty()) {
- tr = tr0;
- tr0 = cp.next_tr;
- for (; tr > tr0; tr--) {
- register Cell* ptr = addr(*tr);
- switch (cp.pass1_action(ptr)) {
- case TRAIL_MARK:
- mark_from_base(ptr);
- *copy_tr-- = *tr;
- break;
- case TRAIL_KEEP:
- *copy_tr-- = *tr;
- break;
- case TRAIL_SKIP:
- break;
- }
- }
- cp.update_tr(copy_tr);
- cp.next();
- }
- }
- */
-
- /* takes advantage of the fact that the tag bit is in the lower bits */
- void gctrail_pass11()
- {
- TrailCP cp(B2, B);
- register Cell* tr0 = cp.tr;
- register Cell* tr = cp.tr;
- register Cell* copy_tr = cp.tr;
- while (cp.nonempty()) {
- tr = tr0;
- tr0 = cp.next_tr;
- Cell* e = cp.e;
- Cell* h = cp.h;
- for (; tr > tr0; tr--) {
- if (cellp(*tr) < h || (cellp(*tr) < e && cellp(*tr) >= E0))
- *copy_tr-- = *tr;
- }
- cp.update_tr(copy_tr);
- cp.next();
- }
- }
-
- void gctrail_pass12()
- {
- register Cell* tr0 = cellp(B[TR_CP_OFFSET]);
- register Cell* tr = cellp(B2[TR_CP_OFFSET]);
- for (; tr > tr0; tr--) {
- register Cell* ptr = addr(*tr);
- if (ptr >= E2 || (ptr < E0 && ptr >= HMIN))
- continue;
- if (pointer_to_new(*ptr))
- mark_from_base(ptr);
- }
- }
-
- void gctrail_pass1()
- {
- gctrail_pass11();
- gctrail_pass12();
- }
-
- /* B2 has been set to BMIDDLE meanwhile; only look at the top part of */
- /* the trail above BMIDDLE now. */
- /* Also, there is the special case of trail entries pointing to the */
- /* part that has been copied. Some of those need relocation */
- void gctrail_pass2()
- {
- TrailCP cp(B2, B);
- Cell* tr0 = cp.tr;
- Cell* tr = cp.tr;
- Cell* copy_tr = cp.tr;
- while (cp.nonempty()) {
- tr = tr0;
- tr0 = cp.next_tr;
- for (; tr > tr0; --tr) {
- Cell* ptr = addr(*tr);
- switch (cp.pass2_action(ptr)) {
- case TRAIL_SKIP:
- break;
- case TRAIL_RELOC:
- *copy_tr-- = relocate(TAGREF, ptr);
- break;
- case TRAIL_COPY_RELOC:
- *copy_tr-- = relocate(TAGREF, ptr);
- ptr = reloc_addr(ptr);
- *ptr = check_and_relocate(*ptr);
- break;
- case TRAIL_IND_RELOC:
- *ptr = check_and_relocate(*ptr);
- *copy_tr-- = *tr;
- break;
- }
- }
- cp.update_tr(copy_tr);
- cp.next();
- }
- }
-
- /* control stacks */
-
- /* we do the traversal of the environment stack and the choice point */
- /* stack together. that way we can avoid having to traverse the */
- /* records twice, and we do not have to use marking nor any extra */
- /* space: just two extra structures. */
- /* will be quite easy to add virtual backtracking inside this routine */
- /* it works as follows: first visit all envs above the topmost choice */
- /* point. then visit all envs that are above the next living env. two */
- /* loops alternating, one visiting next living envs, one visiting the */
- /* next preserved envs. if a given env is shared, its living part is */
- /* first entirely marked, then we wait until the last choice point */
- /* that preserved that env and mark the part that is preserved. */
- /* the update is simple macro substitution from the mark */
-
- #define use(ACTION,PRESERVED_ACTION,PROC_NAME)\
- void PROC_NAME()\
- {\
- /* first, take care of living cells */\
- Env env(E);\
- for (;;) {\
- if (env.e <= E2) {\
- if (env.e == E2)\
- env.ACTION();\
- break;\
- }\
- env.ACTION();\
- env.next();\
- }\
- /* now, take care of preserved cells */\
- Choice cp(E, B);\
- for (;;) {\
- if (cp.last()) break;\
- cp.ACTION();\
- cp.PRESERVED_ACTION();\
- cp.next();\
- }\
- }
- use(mark,mark_preserved_envs,gccontrol_pass1)
- use(update,update_preserved_envs,gccontrol_pass2)
- #undef use
-
- /* new space itself: compaction phase */
-
- /* not too hard. just go thru new area and the marking area in */
- /* parallel. each time i encounter something marked, copy it down */
- /* in copy space. leave behind in each location the relocation */
- /* address (untagged). */
- /* needs a second scan to compute the final addresses. proportional */
- /* to m+n in total */
- /* Also, for being able to restore global stack pointers uniformly, */
- /* we add one entry at the top to relocate the topmost choice point */
- /* entry correctly */
- /* This is also the place to gather statistics about the efficiency */
- /* of the garbage collector */
- static Cell* H2_copy_value;
- static Cell* H_copy_value;
- void global_sweep()
- {
- register Cell* p = HMIDDLE; /* from lowest cp segment */
- register Cell* p0 = H;
- register unsigned char* m = &MKMIN[HMIDDLE - HMIN];
- register Cell* h = H2;
- H_copy_value = HMIDDLE;
-
- /* sweep pass. Should always write relocation addresses */
- for (; p < p0; p++, m++) {
- if (*m == MARK) {
- *h = *p;
- *p = cell(h);
- h++;
- } else {
- *p = cell(h);
- }
- }
-
- /* relocation info for the topmost choice point */
- *p = cell(h);
-
- /* relocate pointers to new space */
- p = H2_copy_value = H2;
- H2 = p0 = h;
- for (; p < p0; p++) {
- if (pointer_to_new(*p))
- *p = relocate(*p);
- }
- }
-
- /* the REF stack: delayed copying of variables in copy space */
- /* objects in the stack should be pointers to locations containing ref */
- /* pointers to cp_down */
- /* if virtual backtracking, we cannot guarantee visiting only once */
- void gcref_pass1()
- {
- while (REF_STACK.nonempty()) {
- Cell* var = REF_STACK.pop();
- Cell* ptr = addr(*var);
- #ifdef WITH_VIRTUAL_BACK
- if (! to_new_space(ptr)) continue;
- #endif
- if (unmarked(ptr)) {
- mark(ptr);
- Cell val = *ptr;
- set_reloc_addr(ptr, COPY_STACK.top());
- COPY_STACK.push(val);
- if (get_tag(val) == TAGREF && addr(val) >= HMIN)
- REF_STACK.push(reloc_addr(ptr));
- }
- *var = make_ptr(TAGREF, reloc_addr(ptr));
- }
- }
-
- /* marking */
-
- /* we pass a pointer to the cell containing the pointer to the object */
- /* to mark. not necessary for marking, but necessary for copying. */
- /* we use the space at the top of the choice point stack (between */
- /* choice point stack and the environment stack) as the marking stack. */
- /* we need to initialize the marking area at each gc. here, since we */
- /* use one byte per mark, we can rotate the mark, and reduce the cost */
- /* of initialization by 255. */
-
- /* when copying, don't mark ref pointers nor what they point to. we */
- /* will do it later. also trail pointers from copy area to new area */
- /* to speed up relocation. */
-
- /* suppose p is a global stack pointer; can't point to env stack */
- /* should be recoded to use a table lookup instead of all those tests */
-
- /* OLD VERSION
- inline int copy_or_mark(Cell* p)
- {
- if (p < HMIN)
- return SHOULD_NEITHER;
- else if (p < HMIDDLE)
- return (marked(p)) ? SHOULD_RELOC : SHOULD_COPY;
- else
- return (marked(p)) ? SHOULD_CHECK_MARK : SHOULD_MARK;
- }
- */
-
- int copy_or_mark_table[2][2] = {
- {SHOULD_MARK, SHOULD_CHECK_MARK},
- {SHOULD_COPY, SHOULD_RELOC}
- };
-
- inline int copy_or_mark(Cell* p)
- {
- if (p >= HMIN)
- return copy_or_mark_table[(p < HMIDDLE)][marked(p)];
- else
- return SHOULD_NEITHER;
- }
-
- /* In the copy part, a list or a structure is marked iff any of its */
- /* elements is. */
- void mark_from_base(Cell* p)
- {
- MARK_STACK.init(B);
- MARK_STACK.push(p);
- for (;;) {
- Cell* var;
- if (COPY_STACK.nonempty())
- var = COPY_STACK.pop();
- else if (MARK_STACK.nonempty())
- var = MARK_STACK.pop();
- else
- break;
-
- switch (get_tag(*var)) {
- case TAGCONST:
- break;
- case TAGREF:
- {
- Cell* ptr = addr(*var);
- switch (copy_or_mark(ptr)) {
- case SHOULD_MARK:
- mark(ptr);
- MARK_STACK.push(ptr);
- break;
- case SHOULD_RELOC: /* ptr to marked copied location */
- *var = make_ptr(TAGREF, reloc_addr(ptr));
- break;
- case SHOULD_COPY:
- REF_STACK.push(var);
- for (;; var = ptr, ptr = addr(*ptr)) {
- /* here, ptr is always a pointer to low cp segment */
- #ifdef WITH_VIRTUAL_BACK
- if (get_tag(*ptr) != TAGREF) {
- MARK_STACK.push(ptr);
- mark2(ptr);
- break;
- }
- if (ptr < HMIN || *var == *ptr || marked2(ptr))
- break;
- mark2(ptr);
- #else
- if (get_tag(*ptr) != TAGREF) {
- MARK_STACK.push(ptr);
- break;
- }
- if (ptr < HMIN || marked(ptr) || *var == *ptr)
- break;
- #endif
- }
- break;
- case SHOULD_CHECK_MARK:
- case SHOULD_NEITHER:
- break;
- }
- }
- break;
- case TAGLIST:
- {
- Cell* list = addr(*var);
- switch (copy_or_mark(list)) {
- case SHOULD_CHECK_MARK: /* marked(car) && unmarked(cdr) */
- if (unmarked(list + 1)) {
- mark(list + 1);
- MARK_STACK.push(list + 1);
- }
- break;
- case SHOULD_MARK:
- for (int i = 0; i < 2; i++) {
- mark(list + i);
- MARK_STACK.push(list + i);
- }
- break;
- case SHOULD_COPY:
- *var = make_ptr(TAGLIST, COPY_STACK.top());
- for (i = 0; i < 2; i++) {
- mark(list + i);
- Cell* dest = COPY_STACK.top();
- COPY_STACK.push(list[i]);
- set_reloc_addr(list + i, dest);
- }
- break;
- case SHOULD_RELOC:
- *var = make_ptr(TAGLIST, reloc_addr(list));
- break;
- case SHOULD_NEITHER:
- break;
- }
- }
- break;
- case TAGSTRUCT:
- {
- Cell* str = addr(*var);
- switch (copy_or_mark(str)) {
- case SHOULD_MARK:
- int i0 = get_int(str[1]) + 2;
- for (int i = 0; i < 2; i++)
- mark(str + i);
- for (i = 2; i < i0; i++) {
- mark(str + i);
- MARK_STACK.push(str + i);
- }
- break;
- case SHOULD_COPY:
- *var = make_ptr(TAGSTRUCT, COPY_STACK.top());
- i0 = get_int(str[1]) + 2;
- for (i = 0; i < i0; i++) {
- mark(str + i);
- Cell* dest = COPY_STACK.top();
- COPY_STACK.push(str[i]);
- set_reloc_addr(str + i, dest);
- }
- break;
- case SHOULD_RELOC:
- *var = make_ptr(TAGSTRUCT, reloc_addr(str));
- break;
- case SHOULD_CHECK_MARK:
- case SHOULD_NEITHER:
- break;
- }
- }
- break;
- }
- }
- }
-
- /* should allocate a fixed size region, just under new area. Needs */
- /* only be initialized once with 0s. For the rest, We can just flip */
- /* and use a global variable, say MARK. MARK is initialized to the */
- /* current gc number modulo 255. When it overflows, the area is */
- /* cleared again. During marking, only MARK is written in the byte */
- /* corresponding to the word to be written. To be marked just means */
- /* that this mark is being written. Only called when MARK is null */
-
- void init_marking_table()
- {
- #ifdef WITH_VIRTUAL_BACK
- if (MARK != 2) return;
- #else
- if (MARK != 1) return;
- #endif
- register int* p = (int*) MKMIN;
- register int* p0 = HMIN;
- while (p < p0)
- *p++ = 0;
- }
-
- /* basic initializations */
-
- void gc_init()
- {
- #ifdef WITH_VIRTUAL_BACK
- MARK = 2 * ((GC_COUNTER % 127) + 1); /* values from 2 to 254 */
- MARK2 = MARK + 1; /* values from 3 to 255 */
- #else
- MARK = (GC_COUNTER % 255) + 1; /* values from 1 to 255 */
- #endif
- GC_COUNTER++;
- REF_STACK.init(E);
- COPY_STACK.init(H2);
- }
-
- /* some basic data: mark(scan,recovered), copy(scan,recovered), cputime */
- /* the data are given in number of cells, milliseconds. */
- struct rusage gc_rusage;
- Cell* H2_entry_value;
- Cell* H_entry_value;
- Cell* TR_entry_value;
- Cell* TR2_entry_value;
- void init_stats()
- {
- getrusage(RUSAGE_SELF, &gc_rusage);
- H2_entry_value = H2;
- H_entry_value = H;
- TR_entry_value = TR;
- TR2_entry_value = TR2;
- }
-
- void display_stat1(char* legend, int before, int after)
- {
- float percent = (before) ? ((float) after/before) * 100 : 0;
- printf("%s(%d,%d,%2.1f),", legend, before, after, percent);
- }
-
- void display_stat2(char* legend, int tb, int cb, int ta, int ca)
- {
- float percentb = (tb) ? ((float) cb/tb) * 100 : 0;
- float percenta = (ta) ? ((float) ca/ta) * 100 : 0;
- printf("%s(%2.1f,%2.1f),", legend, percentb, percenta);
- }
-
- int gc_scanned;
- int gc_copy_scanned;
- int gc_survivors;
- int tr_scanned;
- int tr_survivors;
- float gc_time;
-
- void compute_stats()
- {
- struct timeval from = gc_rusage.ru_utime;
- getrusage(RUSAGE_SELF, &gc_rusage);
- struct timeval to = gc_rusage.ru_utime;
- float mstime = (float) to.tv_usec / 1000000 + to.tv_sec;
- mstime -= (float) from.tv_usec / 1000000 + from.tv_sec;
- gc_time += mstime;
- if (DISPLAY_GC)
- printf("time(%.3f)).\n", mstime);
- if (trace_heap_flag)
- heap_usage.gc_enter(H_entry_value, H2_entry_value);
- }
-
- void mark_copy_stats()
- {
- gc_scanned += H_entry_value - HMIN;
- gc_copy_scanned += H_copy_value - HMIN;
- gc_survivors += H2 - H2_entry_value;
- tr_scanned += TR2_entry_value - TR_entry_value;
- tr_survivors += TR2_entry_value - TR;
- if (DISPLAY_GC) {
- cout << "gc(";
- display_stat1("global", H_entry_value - HMIN, H2 - H2_entry_value);
- display_stat2("copy",
- H_entry_value - HMIN, H_copy_value - HMIN,
- H2 - H2_entry_value, H2_copy_value - H2_entry_value);
- display_stat1("tr", TR2_entry_value-TR_entry_value, TR2_entry_value-TR);
- }
- }
-
- /* top level */
- /* assumes that GC_DOES_COPY. Should also work if everything is above */
- /* the topmost choice point, though slower than the special purpose */
- /* fast_copy garbage collector */
-
- int DISPLAY_GC;
-
- void mark_copy()
- {
- init_stats();
- store_regs_in_env();
- setup_cps_pass1();
- gc_init();
- init_marking_table();
- cp_to_cp_forward();
- gctrail_pass1();
- cp_to_cp_backward();
- gccontrol_pass1();
- gcref_pass1();
- setup_cps_pass2();
- global_sweep();
- gccontrol_pass2();
- cp_to_cp_forward();
- gctrail_pass2();
- cp_to_cp_backward();
- restore_top_env();
- restore_cps();
- mark_copy_stats();
- compute_stats();
- }
-
- int WHICH_GC = MARK_COPY;
- int CHECK_GC_LIMIT;
- int GC_COUNT_LIMIT;
-
- /* we optimize the mark_copy case. Clearly, if there is nothing to */
- /* copy, we should rather use mark_compact. It is faster! Around 7% */
- /* faster in the case of gccomp. */
- void garbage_collector()
- {
- if (CHECK_GC_LIMIT && GC_COUNTER >= GC_COUNT_LIMIT) {
- cerr << "GC Limit passed\n";
- }
- switch (WHICH_GC) {
- case MARK_COPY:
- mark_copy();
- break;
- case MARK_COPY_FAST_COPY:
- if (deterministic())
- fast_copy();
- else
- mark_copy();
- break;
- case MARK_THRESHOLD:
- if (enough_to_copy())
- mark_copy();
- else
- mark_compact();
- break;
- case MARK_COMPACT:
- mark_compact();
- break;
- case MARK_COMPACT_FAST_COPY:
- if (deterministic())
- fast_copy();
- else
- mark_compact();
- break;
- default:
- top_level_error("Select GC algorithm first\n");
- break;
- }
- if (TR - H2 <= HMAXHARD - HMIN) {
- top_level_error("Global Stack Overflow\n");
- }
- }
-
- void find_pointer(Cell val)
- {
- Cell* p;
- Cell* p0;
-
- #define use(FROM,TO,NAME)\
- for (p = FROM, p0 = TO; p < p0; p++) {\
- if (*p == val)\
- cerr << NAME << "[" << (p - FROM) << "]\n";\
- }
- use(H0,H2,"H0")
- use(HMIN,HMAXSOFT,"HMIN")
- use(E0,E,"E0")
- use(B,B0,"B")
- use(TR,TR0,"TR")
- #undef use
- }
-
- #endif
-