diff --git a/es.h b/es.h index 83ab221..3ef04f7 100644 --- a/es.h +++ b/es.h @@ -136,7 +136,7 @@ extern List *sortlist(List *list); /* tree.c */ -extern Tree *mk(NodeKind VARARGS); +extern Tree *gcmk(NodeKind VARARGS); /* gcalloc a tree node */ /* closure.c */ @@ -404,6 +404,12 @@ extern void gcenable(void); /* enable collections */ extern void gcdisable(void); /* disable collections */ extern Boolean gcisblocked(void); /* is collection disabled? */ +/* operations with pspace, the explicitly-collected gc space for parse tree building */ +extern void *palloc(size_t n, Tag *t); /* allocate n with collection tag t, but in pspace */ +extern void *pseal(void *p); /* collect pspace into gcspace with root p */ +extern char *pdup(const char *s); /* copy a 0-terminated string into pspace */ +extern char *pndup(const char *s, size_t n); /* copy a counted string into pspace */ + /* * garbage collector tags diff --git a/gc.c b/gc.c index 946ee31..d489e7c 100644 --- a/gc.c +++ b/gc.c @@ -19,9 +19,11 @@ struct Space { #define INSPACE(p, sp) ((sp)->bot <= (char *) (p) && (char *) (p) < (sp)->top) #define MIN_minspace 10000 +#define MIN_minpspace 1000 #if GCPROTECT -#define NSPACES 10 +#define NSPACES 12 +#define FIRSTSPACE 1 #endif #if HAVE_SYSCONF @@ -38,12 +40,13 @@ int gcblocked = 0; Tag StringTag; /* own variables */ -static Space *new, *old; +static Space *new, *old, *pspace; #if GCPROTECT static Space *spaces; #endif static Root *globalrootlist, *exceptionrootlist; static size_t minspace = MIN_minspace; /* minimum number of bytes in a new space */ +static size_t minpspace = MIN_minpspace; /* @@ -136,14 +139,15 @@ static void initmmu(void) { #if GCPROTECT /* mkspace -- create a new ``half'' space in debugging mode */ -static Space *mkspace(Space *space, Space *next) { +static Space *mkspace(Space *space, Space *next, size_t size) { assert(space == NULL || (&spaces[0] <= space && space < &spaces[NSPACES])); + /* find and clear out any existing/next spaces */ if (space != NULL) { Space *sp; if (space->bot == NULL) sp = NULL; - else if ((size_t) SPACESIZE(space) < minspace) + else if ((size_t) SPACESIZE(space) < size) sp = space; else { sp = space->next; @@ -160,12 +164,13 @@ static Space *mkspace(Space *space, Space *next) { } } + /* build new space (or set up existing &space[n]) */ if (space == NULL) { space = ealloc(sizeof (Space)); memzero(space, sizeof (Space)); } if (space->bot == NULL) { - size_t n = PAGEROUND(minspace); + size_t n = PAGEROUND(size); space->bot = take(n); space->top = space->bot + n / (sizeof (*space->bot)); } @@ -175,13 +180,14 @@ static Space *mkspace(Space *space, Space *next) { return space; } -#define newspace(next) mkspace(NULL, next) +#define newspace(next) mkspace(NULL, next, minspace) +#define newpspace(next) mkspace(NULL, next, minpspace) #else /* !GCPROTECT */ /* newspace -- create a new ``half'' space */ -static Space *newspace(Space *next) { - size_t n = ALIGN(minspace); +static Space *newspacesz(Space *next, size_t size) { + size_t n = ALIGN(size); Space *space = ealloc(sizeof (Space) + n); space->bot = (void *) &space[1]; space->top = (void *) (((char *) space->bot) + n); @@ -189,6 +195,8 @@ static Space *newspace(Space *next) { space->next = next; return space; } +#define newspace(next) newspacesz(next, minspace) +#define newpspace(next) newspacesz(next, minpspace) #endif /* !GCPROTECT */ @@ -273,12 +281,20 @@ extern void exceptionunroot(void) { #define FOLLOWTO(p) ((Tag *) (((char *) p) + 1)) #define FOLLOW(tagp) ((void *) (((char *) tagp) - 1)) +/* TODO: remove pmode: it's the Wrong Thing */ +static Boolean pmode = FALSE; + /* forward -- forward an individual pointer from old space */ extern void *forward(void *p) { Tag *tag; void *np; - if (!isinspace(old, p)) { + if (pmode && !isinspace(pspace, p)) { + VERBOSE(("GC %8ux : <>\n", p)); + return p; + } + + if (!pmode && !isinspace(old, p)) { VERBOSE(("GC %8ux : <>\n", p)); return p; } @@ -297,6 +313,12 @@ extern void *forward(void *p) { VERBOSE(("%s -> %8ux (forwarded)\n", tag->typename, np)); TAG(p) = FOLLOWTO(np); } + + if (pmode) { + tag = TAG(np); + (*tag->scan)(np); + } + return np; } @@ -395,8 +417,8 @@ extern void gc(void) { for (; new->next != NULL; new = new->next) ; if (++new >= &spaces[NSPACES]) - new = &spaces[0]; - new = mkspace(new, NULL); + new = &spaces[FIRSTSPACE]; + new = mkspace(new, NULL, minspace); #else new = newspace(NULL); #endif @@ -425,7 +447,7 @@ extern void gc(void) { #if GCINFO if (gcinfo) eprint( - "[GC: old %8d live %8d min %8d (pid %5d)]\n", + "[ GC: old %8d live %8d min %8d (pid %5d)]\n", olddata, livedata, minspace, getpid() ); #endif @@ -439,15 +461,91 @@ extern void gc(void) { } while (new->next != NULL); } +/* pseal -- collect pspace to new with p as its only root, and return the collected p */ +extern void *pseal(void *p) { + size_t psize = 0; + Space *sp; +#if GCINFO + size_t newdata = 0, livedata = 0; +#endif +#if GCPROTECT + Space *base; +#endif + + for (sp = pspace; sp != NULL; sp = sp->next) + psize += SPACEUSED(sp); + + if (psize == 0) + return p; + + /* TODO: this is an overestimate since it counts garbage */ + gcreserve(psize); + VERBOSE(("Reserved %d for pspace copy\n", psize)); + +#if GCINFO + if (gcinfo) + for (sp = new; sp != NULL; sp = sp->next) + newdata += SPACEUSED(sp); +#endif + + assert (gcblocked >= 0); + ++gcblocked; + +#if GCVERBOSE + for (sp = pspace; sp != NULL; sp = sp->next) + VERBOSE(("GC pspace = %ux ... %ux\n", sp->bot, sp->current)); +#endif + if (p != NULL) { + VERBOSE(("GC new space = %ux ... %ux\n", new->bot, new->top)); + + pmode = TRUE; + p = forward(p); + (*(TAG(p))->scan)(p); + pmode = FALSE; + } + +#if GCINFO + if (gcinfo) { + for (sp = new; sp != NULL; sp = sp->next) + livedata += SPACEUSED(sp); + eprint( + "[pseal: old %8d live %8d min %8d diff %5d (pid %5d)]\n", + psize, livedata, minpspace, (livedata - newdata), getpid() + ); + } +#endif + + if (psize > minpspace) + minpspace = psize * 2; + else if (psize < minpspace / 2 && MIN_minpspace <= minpspace / 2) + minpspace /= 2; + +#if GCPROTECT + for (base = pspace; base->next != NULL; base = base->next) + ; +#endif + deprecate(pspace); +#if GCPROTECT + pspace = mkspace(base, NULL, minpspace); +#else + pspace = newpspace(NULL); +#endif + + --gcblocked; + return p; +} + /* initgc -- initialize the garbage collector */ extern void initgc(void) { #if GCPROTECT initmmu(); spaces = ealloc(NSPACES * sizeof (Space)); memzero(spaces, NSPACES * sizeof (Space)); - new = mkspace(&spaces[0], NULL); + new = mkspace(&spaces[FIRSTSPACE], NULL, minspace); + pspace = mkspace(&spaces[0], NULL, minpspace); #else new = newspace(NULL); + pspace = newpspace(NULL); #endif old = NULL; } @@ -481,6 +579,24 @@ extern void *gcalloc(size_t nbytes, Tag *tag) { } } +/* palloc -- allocate an object in pspace */ +extern void *palloc(size_t nbytes, Tag *tag) { + size_t n = ALIGN(nbytes + sizeof (Tag *)); + assert(tag == NULL || tag->magic == TAGMAGIC); + for (;;) { + Tag **p = (void *) pspace->current; + char *q = ((char *) p) + n; + if (q <= pspace->top) { + pspace->current = q; + *p++ = tag; + return p; + } + if (minpspace < nbytes) + minpspace = nbytes + sizeof (Tag *); + pspace = newpspace(pspace); + } +} + /* * strings @@ -503,10 +619,25 @@ extern char *gcndup(const char *s, size_t n) { RefReturn(result); } +extern char *pndup(const char *s, size_t n) { + char *ns; + + ns = palloc((n + 1) * sizeof (char), &StringTag); + memcpy(ns, s, n); + ns[n] = '\0'; + assert(strlen(ns) == n); + + return ns; +} + extern char *gcdup(const char *s) { return gcndup(s, strlen(s)); } +extern char *pdup(const char *s) { + return pndup(s, strlen(s)); +} + static void *StringCopy(void *op) { size_t n = strlen(op) + 1; char *np = gcalloc(n, &StringTag); @@ -547,12 +678,24 @@ extern char *sealbuffer(Buffer *buf) { return s; } +extern char *psealbuffer(Buffer *buf) { + char *s = pdup(buf->str); + efree(buf); + return s; +} + extern char *sealcountedbuffer(Buffer *buf) { char *s = gcndup(buf->str, buf->current); efree(buf); return s; } +extern char *psealcountedbuffer(Buffer *buf) { + char *s = pndup(buf->str, buf->current); + efree(buf); + return s; +} + extern Buffer *bufncat(Buffer *buf, const char *s, size_t len) { while (buf->current + len >= buf->len) buf = expandbuffer(buf, buf->current + len - buf->len); diff --git a/gc.h b/gc.h index 683b5be..dd781c5 100644 --- a/gc.h +++ b/gc.h @@ -50,6 +50,8 @@ extern Buffer *bufcat(Buffer *buf, const char *s); extern Buffer *bufputc(Buffer *buf, char c); extern char *sealbuffer(Buffer *buf); extern char *sealcountedbuffer(Buffer *buf); +extern char *psealbuffer(Buffer *buf); /* pspace variant of sealbuffer */ +extern char *psealcountedbuffer(Buffer *buf); /* pspace variant of sealcountedbuffer */ extern void freebuffer(Buffer *buf); extern void *forward(void *p); diff --git a/heredoc.c b/heredoc.c index 60ad0b1..d9de728 100644 --- a/heredoc.c +++ b/heredoc.c @@ -22,7 +22,7 @@ extern Tree *getherevar(void) { while ((c = GETC()) != EOF && !dnw[c]) buf = bufputc(buf, c); len = buf->len; - s = sealcountedbuffer(buf); + s = psealcountedbuffer(buf); if (len == 0) { yyerror("null variable name in here document"); return NULL; @@ -54,7 +54,7 @@ extern Tree *snarfheredoc(const char *eof, Boolean quoted) { if (buf->current == 0 && tree != NULL) freebuffer(buf); else - *tailp = treecons(mk(nQword, sealcountedbuffer(buf)), NULL); + *tailp = treecons(mk(nQword, psealcountedbuffer(buf)), NULL); break; } if (s != (unsigned char *) eof) @@ -72,7 +72,7 @@ extern Tree *snarfheredoc(const char *eof, Boolean quoted) { if (buf->current == 0) freebuffer(buf); else { - *tailp = treecons(mk(nQword, sealcountedbuffer(buf)), NULL); + *tailp = treecons(mk(nQword, psealcountedbuffer(buf)), NULL); tailp = &(*tailp)->CDR; } var = getherevar(); @@ -133,7 +133,7 @@ extern Boolean queueheredoc(Tree *t) { return FALSE; } - here = gcalloc(sizeof (Here), NULL); + here = palloc(sizeof (Here), NULL); here->next = hereq; here->marker = eof; hereq = here; diff --git a/input.c b/input.c index c07b116..430f9f6 100644 --- a/input.c +++ b/input.c @@ -139,8 +139,8 @@ static int eoffill(Input UNUSED *in) { #if HAVE_READLINE /* callreadline -- readline wrapper */ static char *callreadline(char *prompt0) { - char *volatile prompt = prompt0; char *r; + Ref(char *volatile, prompt, prompt0); if (prompt == NULL) prompt = ""; /* bug fix for readline 2.0 */ checkreloadhistory(); @@ -162,6 +162,7 @@ static char *callreadline(char *prompt0) { } slow = FALSE; SIGCHK(); + RefEnd(prompt); return r; } #endif @@ -240,24 +241,26 @@ extern Tree *parse(char *pr1, char *pr2) { #endif prompt2 = pr2; - gcreserve(300 * sizeof (Tree)); - gcdisable(); result = yyparse(); - gcenable(); if (result || error != NULL) { - const char *e; assert(error != NULL); - e = error; + Ref(const char *, e, error); error = NULL; + pseal(NULL); fail("$&parse", "%s", e); + RefEnd(e); } #if LISPTREES + Ref(Tree *, pt, pseal(parsetree)); if (input->runflags & run_lisptrees) - eprint("%B\n", parsetree); + eprint("%B\n", pt); + RefReturn(pt); +#else + return pseal(parsetree); #endif - return parsetree; + } /* resetparser -- clear parser errors in the signal handler */ @@ -568,9 +571,6 @@ extern void initinput(void) { globalroot(&prompt); /* main prompt */ globalroot(&prompt2); /* secondary prompt */ - /* call the parser's initialization */ - initparse(); - #if HAVE_READLINE rl_readline_name = "es"; diff --git a/input.h b/input.h index 7766759..3048756 100644 --- a/input.h +++ b/input.h @@ -43,7 +43,6 @@ extern void print_prompt2(void); extern Tree *parsetree; extern int yyparse(void); -extern void initparse(void); /* heredoc.c */ diff --git a/prim-etc.c b/prim-etc.c index 1003e5d..c4152fe 100644 --- a/prim-etc.c +++ b/prim-etc.c @@ -193,7 +193,7 @@ PRIM(parse) { loginput(dumphistbuffer()); result = (tree == NULL) ? NULL - : mklist(mkterm(NULL, mkclosure(mk(nThunk, tree), NULL)), + : mklist(mkterm(NULL, mkclosure(gcmk(nThunk, tree), NULL)), NULL); RefEnd3(tree, prompt2, prompt1); return result; diff --git a/str.c b/str.c index 031dc6d..17e8574 100644 --- a/str.c +++ b/str.c @@ -15,7 +15,7 @@ static int str_grow(Format *f, size_t more) { } /* strv -- print a formatted string into gc space */ -extern char *strv(const char *fmt, va_list args) { +static char *sstrv(char *(*seal)(Buffer *), const char *fmt, va_list args) { Buffer *buf; Format format; @@ -37,7 +37,11 @@ extern char *strv(const char *fmt, va_list args) { fmtputc(&format, '\0'); gcenable(); - return sealbuffer(format.u.p); + return seal(format.u.p); +} + +extern char *strv(const char *fmt, va_list args) { + return sstrv(sealbuffer, fmt, args); } /* str -- create a string (in garbage collection space) by printing to it */ @@ -50,6 +54,16 @@ extern char *str VARARGS1(const char *, fmt) { return s; } +/* pstr -- create a string (in pspace) by printing to it */ +extern char *pstr VARARGS1(const char *, fmt) { + char *s; + va_list args; + VA_START(args, fmt); + s = sstrv(psealbuffer, fmt, args); + va_end(args); + return s; +} + #define PRINT_ALLOCSIZE 64 diff --git a/syntax.c b/syntax.c index fddb745..7ca627e 100644 --- a/syntax.c +++ b/syntax.c @@ -8,11 +8,6 @@ Tree errornode; Tree *parsetree; -/* initparse -- called at the dawn of time */ -extern void initparse(void) { - globalroot(&parsetree); -} - /* treecons -- create new tree list cell */ extern Tree *treecons(Tree *car, Tree *cdr) { assert(cdr == NULL || cdr->kind == nList); @@ -116,8 +111,8 @@ extern Tree *mkpipe(Tree *t1, int outfd, int infd, Tree *t2) { Boolean pipetail; pipetail = firstis(t2, "%pipe"); - tail = prefix(str("%d", outfd), - prefix(str("%d", infd), + tail = prefix(pstr("%d", outfd), + prefix(pstr("%d", infd), pipetail ? t2->CDR : treecons(thunkify(t2), NULL))); if (firstis(t1, "%pipe")) return treeappend(t1, tail); @@ -158,7 +153,7 @@ extern Tree *redirect(Tree *t) { } extern Tree *mkredircmd(char *cmd, int fd) { - return prefix(cmd, prefix(str("%d", fd), NULL)); + return prefix(cmd, prefix(pstr("%d", fd), NULL)); } extern Tree *mkredir(Tree *cmd, Tree *file) { @@ -175,7 +170,7 @@ extern Tree *mkredir(Tree *cmd, Tree *file) { yyerror("bad /dev/fd redirection"); op = ""; } - var = mk(nWord, str("_devfd%d", id++)); + var = mk(nWord, pstr("_devfd%d", id++)); cmd = treecons( mk(nWord, op), treecons(var, NULL) @@ -197,14 +192,14 @@ extern Tree *mkredir(Tree *cmd, Tree *file) { /* mkclose -- make a %close node with a placeholder */ extern Tree *mkclose(int fd) { - return prefix("%close", prefix(str("%d", fd), treecons(&placeholder, NULL))); + return prefix("%close", prefix(pstr("%d", fd), treecons(&placeholder, NULL))); } /* mkdup -- make a %dup node with a placeholder */ extern Tree *mkdup(int fd0, int fd1) { return prefix("%dup", - prefix(str("%d", fd0), - prefix(str("%d", fd1), + prefix(pstr("%d", fd0), + prefix(pstr("%d", fd1), treecons(&placeholder, NULL)))); } diff --git a/syntax.h b/syntax.h index 83e3da3..aa26c67 100644 --- a/syntax.h +++ b/syntax.h @@ -4,6 +4,11 @@ #define CDR u[1].p +/* tree.c */ + +extern Tree *mk(NodeKind VARARGS); /* palloc a tree node */ + + /* syntax.c */ extern Tree errornode; @@ -31,6 +36,10 @@ extern Tree *firstprepend(Tree *first, Tree *args); extern Tree *mkmatch(Tree *subj, Tree *cases); +/* str.c */ + +extern char *pstr(const char *fmt VARARGS); + /* heredoc.c */ extern Boolean readheredocs(Boolean endfile); diff --git a/token.c b/token.c index 3ac0b48..57f95b1 100644 --- a/token.c +++ b/token.c @@ -195,7 +195,7 @@ top: while ((c = GETC()) == ' ' || c == '\t') else if (streq(buf, "match")) return MATCH; w = RW; - y->str = gcdup(buf); + y->str = pdup(buf); return WORD; } if (c == '`' || c == '!' || c == '$' || c == '\'' || c == '=') { @@ -244,7 +244,7 @@ top: while ((c = GETC()) == ' ' || c == '\t') } UNGETC(c); buf[i] = '\0'; - y->str = gcdup(buf); + y->str = pdup(buf); return QWORD; case '\\': if ((c = GETC()) == '\n') { @@ -306,7 +306,7 @@ top: while ((c = GETC()) == ' ' || c == '\t') break; } buf[1] = 0; - y->str = gcdup(buf); + y->str = pdup(buf); return QWORD; case '#': while ((c = GETC()) != '\n') /* skip comment until newline */ diff --git a/tree.c b/tree.c index 0a158b6..442328f 100644 --- a/tree.c +++ b/tree.c @@ -6,50 +6,65 @@ DefineTag(Tree1, static); DefineTag(Tree2, static); -/* mk -- make a new node; used to generate the parse tree */ -extern Tree *mk VARARGS1(NodeKind, t) { - va_list ap; +/* gmk -- make a new node; used to generate the parse tree */ +static Tree *gmk(void *(*alloc)(size_t, Tag *), NodeKind t, va_list ap) { Tree *n; gcdisable(); - VA_START(ap, t); switch (t) { default: panic("mk: bad node kind %d", t); case nWord: case nQword: case nPrim: - n = gcalloc(offsetof(Tree, u[1]), &Tree1Tag); + n = alloc(offsetof(Tree, u[1]), &Tree1Tag); n->u[0].s = va_arg(ap, char *); break; case nCall: case nThunk: case nVar: - n = gcalloc(offsetof(Tree, u[1]), &Tree1Tag); + n = alloc(offsetof(Tree, u[1]), &Tree1Tag); n->u[0].p = va_arg(ap, Tree *); break; case nAssign: case nConcat: case nClosure: case nFor: case nLambda: case nLet: case nList: case nLocal: case nVarsub: case nMatch: case nExtract: - n = gcalloc(offsetof(Tree, u[2]), &Tree2Tag); + n = alloc(offsetof(Tree, u[2]), &Tree2Tag); n->u[0].p = va_arg(ap, Tree *); n->u[1].p = va_arg(ap, Tree *); break; case nRedir: - n = gcalloc(offsetof(Tree, u[2]), NULL); + n = alloc(offsetof(Tree, u[2]), NULL); n->u[0].p = va_arg(ap, Tree *); n->u[1].p = va_arg(ap, Tree *); break; case nPipe: - n = gcalloc(offsetof(Tree, u[2]), NULL); + n = alloc(offsetof(Tree, u[2]), NULL); n->u[0].i = va_arg(ap, int); n->u[1].i = va_arg(ap, int); break; } n->kind = t; - va_end(ap); Ref(Tree *, tree, n); gcenable(); RefReturn(tree); } +extern Tree *mk VARARGS1(NodeKind, t) { + va_list ap; + Tree *tree = NULL; + VA_START(ap, t); + tree = gmk(palloc, t, ap); + va_end(ap); + return tree; +} + +extern Tree *gcmk VARARGS1(NodeKind, t) { + va_list ap; + Ref(Tree *, tree, NULL); + VA_START(ap, t); + tree = gmk(gcalloc, t, ap); + va_end(ap); + RefReturn(tree); +} + /* * garbage collection functions