diff options
Diffstat (limited to 'limbo')
| -rw-r--r-- | limbo/NOTICE | 25 | ||||
| -rw-r--r-- | limbo/asm.c | 289 | ||||
| -rw-r--r-- | limbo/com.c | 1489 | ||||
| -rw-r--r-- | limbo/decls.c | 1367 | ||||
| -rw-r--r-- | limbo/dis.c | 638 | ||||
| -rw-r--r-- | limbo/dtocanon.c | 35 | ||||
| -rw-r--r-- | limbo/ecom.c | 2560 | ||||
| -rw-r--r-- | limbo/fns.h | 391 | ||||
| -rw-r--r-- | limbo/gen.c | 1097 | ||||
| -rw-r--r-- | limbo/lex.c | 1429 | ||||
| -rw-r--r-- | limbo/limbo.h | 701 | ||||
| -rw-r--r-- | limbo/limbo.y | 2032 | ||||
| -rw-r--r-- | limbo/mkfile | 41 | ||||
| -rw-r--r-- | limbo/nodes.c | 1538 | ||||
| -rw-r--r-- | limbo/optab.c | 658 | ||||
| -rw-r--r-- | limbo/optim.c | 1803 | ||||
| -rw-r--r-- | limbo/runt.h | 2012 | ||||
| -rw-r--r-- | limbo/sbl.c | 351 | ||||
| -rw-r--r-- | limbo/stubs.c | 590 | ||||
| -rw-r--r-- | limbo/typecheck.c | 3635 | ||||
| -rw-r--r-- | limbo/types.c | 4745 |
21 files changed, 27426 insertions, 0 deletions
diff --git a/limbo/NOTICE b/limbo/NOTICE new file mode 100644 index 00000000..84818206 --- /dev/null +++ b/limbo/NOTICE @@ -0,0 +1,25 @@ +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + +Copyright © 1995-1999 Lucent Technologies Inc. +Portions Copyright © 1997-2000 Vita Nuova Limited +Portions Copyright © 2000-2006 Vita Nuova Holdings Limited + +This program 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 2 of the License, or +(at your option) any later version. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. diff --git a/limbo/asm.c b/limbo/asm.c new file mode 100644 index 00000000..cf9b2d5f --- /dev/null +++ b/limbo/asm.c @@ -0,0 +1,289 @@ +#include "limbo.h" + +void +asmentry(Decl *e) +{ + if(e == nil) + return; + Bprint(bout, "\tentry\t%ld, %d\n", e->pc->pc, e->desc->id); +} + +void +asmmod(Decl *m) +{ + Bprint(bout, "\tmodule\t"); + Bprint(bout, "%s\n", m->sym->name); + for(m = m->ty->tof->ids; m != nil; m = m->next){ + switch(m->store){ + case Dglobal: + Bprint(bout, "\tlink\t-1,-1,0x%lux,\".mp\"\n", sign(m)); + break; + case Dfn: + Bprint(bout, "\tlink\t%d,%ld,0x%lux,\"", + m->desc->id, m->pc->pc, sign(m)); + if(m->dot->ty->kind == Tadt) + Bprint(bout, "%s.", m->dot->sym->name); + Bprint(bout, "%s\"\n", m->sym->name); + break; + } + } +} + +#define NAMELEN 64 + +void +asmpath(void) +{ + char name[8*NAMELEN], *sp; + + sp = srcpath(name, 8*NAMELEN); + Bprint(bout, "\tsource\t\"%s\"\n", sp); +} + +void +asmdesc(Desc *d) +{ + uchar *m, *e; + + for(; d != nil; d = d->next){ + Bprint(bout, "\tdesc\t$%d,%lud,\"", d->id, d->size); + e = d->map + d->nmap; + for(m = d->map; m < e; m++) + Bprint(bout, "%.2x", *m); + Bprint(bout, "\"\n"); + } +} + +void +asmvar(long size, Decl *d) +{ + Bprint(bout, "\tvar\t@mp,%ld\n", size); + + for(; d != nil; d = d->next) + if(d->store == Dglobal && d->init != nil) + asminitializer(d->offset, d->init); +} + +void +asmldt(long size, Decl *d) +{ + Bprint(bout, "\tldts\t@ldt,%ld\n", size); + + for(; d != nil; d = d->next) + if(d->store == Dglobal && d->init != nil) + asminitializer(d->offset, d->init); +} + +void +asminitializer(long offset, Node *n) +{ + Node *elem, *wild; + Case *c; + Label *lab; + Decl *id; + ulong dv[2]; + long e, last, esz, dotlen, idlen; + int i; + + switch(n->ty->kind){ + case Tbyte: + Bprint(bout, "\tbyte\t@mp+%ld,%ld\n", offset, (long)n->val & 0xff); + break; + case Tint: + case Tfix: + Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, (long)n->val); + break; + case Tbig: + Bprint(bout, "\tlong\t@mp+%ld,%lld # %.16llux\n", offset, n->val, n->val); + break; + case Tstring: + asmstring(offset, n->decl->sym); + break; + case Treal: + dtocanon(n->rval, dv); + Bprint(bout, "\treal\t@mp+%ld,%g # %.8lux%.8lux\n", offset, n->rval, dv[0], dv[1]); + break; + case Tadt: + case Tadtpick: + case Ttuple: + id = n->ty->ids; + for(n = n->left; n != nil; n = n->right){ + asminitializer(offset + id->offset, n->left); + id = id->next; + } + break; + case Tcase: + c = n->ty->cse; + Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab); + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + Bprint(bout, ",%ld,%ld,%ld", (long)lab->start->val, (long)lab->stop->val+1, lab->inst->pc); + } + Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1); + break; + case Tcasel: + c = n->ty->cse; + Bprint(bout, "\tword\t@mp+%ld,%d", offset, c->nlab); + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + Bprint(bout, ",%lld,%lld,%ld", lab->start->val, lab->stop->val+1, lab->inst->pc); + } + Bprint(bout, ",%ld\n", c->iwild ? c->iwild->pc : -1); + break; + case Tcasec: + c = n->ty->cse; + Bprint(bout, "\tword\t@mp+%ld,%d\n", offset, c->nlab); + offset += IBY2WD; + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + asmstring(offset, lab->start->decl->sym); + offset += IBY2WD; + if(lab->stop != lab->start) + asmstring(offset, lab->stop->decl->sym); + offset += IBY2WD; + Bprint(bout, "\tword\t@mp+%ld,%ld\n", offset, lab->inst->pc); + offset += IBY2WD; + } + Bprint(bout, "\tword\t@mp+%ld,%d\n", offset, c->iwild ? c->iwild->pc : -1); + break; + case Tgoto: + c = n->ty->cse; + Bprint(bout, "\tword\t@mp+%ld", offset); + Bprint(bout, ",%ld", n->ty->size/IBY2WD-1); + for(i = 0; i < c->nlab; i++) + Bprint(bout, ",%ld", c->labs[i].inst->pc); + if(c->iwild != nil) + Bprint(bout, ",%ld", c->iwild->pc); + Bprint(bout, "\n"); + break; + case Tany: + break; + case Tarray: + Bprint(bout, "\tarray\t@mp+%ld,$%d,%ld\n", offset, n->ty->tof->decl->desc->id, (long)n->left->val); + if(n->right == nil) + break; + Bprint(bout, "\tindir\t@mp+%ld,0\n", offset); + c = n->right->ty->cse; + wild = nil; + if(c->wild != nil) + wild = c->wild->right; + last = 0; + esz = n->ty->tof->size; + for(i = 0; i < c->nlab; i++){ + e = c->labs[i].start->val; + if(wild != nil){ + for(; last < e; last++) + asminitializer(esz * last, wild); + } + last = e; + e = c->labs[i].stop->val; + elem = c->labs[i].node->right; + for(; last <= e; last++) + asminitializer(esz * last, elem); + } + if(wild != nil) + for(e = n->left->val; last < e; last++) + asminitializer(esz * last, wild); + Bprint(bout, "\tapop\n"); + break; + case Tiface: + if(LDT) + Bprint(bout, "\tword\t@ldt+%d,%d\n", offset, (long)n->val); + else + Bprint(bout, "\tword\t@mp+%d,%d\n", offset, (long)n->val); + offset += IBY2WD; + for(id = n->decl->ty->ids; id != nil; id = id->next){ + offset = align(offset, IBY2WD); + if(LDT) + Bprint(bout, "\text\t@ldt+%d,0x%lux,\"", offset, sign(id)); + else + Bprint(bout, "\text\t@mp+%d,0x%lux,\"", offset, sign(id)); + dotlen = 0; + idlen = id->sym->len + 1; + if(id->dot->ty->kind == Tadt){ + dotlen = id->dot->sym->len + 1; + Bprint(bout, "%s.", id->dot->sym->name); + } + Bprint(bout, "%s\"\n", id->sym->name); + offset += idlen + dotlen + IBY2WD; + } + break; + default: + nerror(n, "can't asm global %n", n); + break; + } +} + +void +asmexc(Except *es) +{ + int i, o, n, id; + Decl *d; + Except *e; + Case *c; + Label *lab; + + n = 0; + for(e = es; e != nil; e = e->next) + n++; + Bprint(bout, "\texceptions\t%d\n", n); + for(e = es; e != nil; e = e->next){ + if(!e->p1->reach && !e->p2->reach) + continue; + c = e->c; + o = e->d->offset; + if(e->desc != nil) + id = e->desc->id; + else + id = -1; + Bprint(bout, "\texception\t%d, %d, %d, %d, %d, %d\n", getpc(e->p1), getpc(e->p2), o, id, c->nlab, e->ne); + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + d = lab->start->decl; + if(lab->start->ty->kind == Texception) + d = d->init->decl; + Bprint(bout, "\texctab\t\"%s\", %d\n", d->sym->name, lab->inst->pc); + } + if(c->iwild == nil) + Bprint(bout, "\texctab\t*, %d\n", -1); + else + Bprint(bout, "\texctab\t*, %d\n", c->iwild->pc); + } +} + +void +asmstring(long offset, Sym *sym) +{ + char *s, *se; + int c; + + Bprint(bout, "\tstring\t@mp+%ld,\"", offset); + s = sym->name; + se = s + sym->len; + for(; s < se; s++){ + c = *s; + if(c == '\n') + Bwrite(bout, "\\n", 2); + else if(c == '\0') + Bwrite(bout, "\\z", 2); + else if(c == '"') + Bwrite(bout, "\\\"", 2); + else if(c == '\\') + Bwrite(bout, "\\\\", 2); + else + Bputc(bout, c); + } + Bprint(bout, "\"\n"); +} + +void +asminst(Inst *in) +{ + for(; in != nil; in = in->next){ + if(in->op == INOOP) + continue; + if(in->pc % 10 == 0) + Bprint(bout, "#%d\n", in->pc); + Bprint(bout, "%I\n", in); + } +} diff --git a/limbo/com.c b/limbo/com.c new file mode 100644 index 00000000..d10f3775 --- /dev/null +++ b/limbo/com.c @@ -0,0 +1,1489 @@ +#include "limbo.h" + +static Inst **breaks; +static Inst **conts; +static Decl **labels; +static Node **bcscps; +static int labdep; +static Inst nocont; + +static int scp; +static Node *scps[MaxScope]; + +static int trcom(Node*, Node*, int); + +static void +pushscp(Node *n) +{ + if (scp >= MaxScope) + fatal("scope too deep"); + scps[scp++] = n; +} + +static void +popscp(void) +{ + scp--; +} + +static Node * +curscp(void) +{ + if (scp == 0) + return nil; + return scps[scp-1]; +} + +static void +zeroscopes(Node *stop) +{ + int i; + Node *cs; + + for (i = scp-1; i >= 0; i--) { + cs = scps[i]; + if (cs == stop) + break; + zcom(cs->left, nil); + } +} + +static void +zeroallscopes(Node *n, Node **nn) +{ + if(n == nil) + return; + for(; n != nil; n = n->right){ + switch(n->op){ + case Oscope: + zeroallscopes(n->right, nn); + zcom(n->left, nn); + return; + case Olabel: + case Odo: + zeroallscopes(n->right, nn); + return; + case Oif: + case Ofor: + zeroallscopes(n->right->left, nn); + zeroallscopes(n->right->right, nn); + return; + case Oalt: + case Ocase: + case Opick: + case Oexcept: + for(n = n->right; n != nil; n = n->right) + zeroallscopes(n->left->right, nn); + return; + case Oseq: + zeroallscopes(n->left, nn); + break; + case Oexstmt: + zeroallscopes(n->left, nn); + zeroallscopes(n->right, nn); + return; + default: + return; + } + } +} + +static Except *excs; + +static void +installexc(Node *en, Inst *p1, Inst *p2, Node *zn) +{ + int i, ne; + Except *e; + Case *c; + Label *lab; + + e = allocmem(sizeof(Except)); + e->p1 = p1; + e->p2 = p2; + e->c = en->ty->cse; + e->d = en->left->decl; + e->zn = zn; + e->desc = nil; + e->next = excs; + excs = e; + + ne = 0; + c = e->c; + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + if(lab->start->ty->kind == Texception) + ne++; + } + e->ne = ne; +} + +static int +inlist(Decl *d, Decl *dd) +{ + for( ; dd != nil; dd = dd->next) + if(d == dd) + return 1; + return 0; +} + +static void +excdesc(void) +{ + ulong o, maxo; + Except *e; + Node *n; + Decl *d, *dd, *nd; + + for(e = excs; e != nil; e = e->next){ + if(e->zn != nil){ + /* set up a decl list for gendesc */ + dd = nil; + maxo = 0; + for(n = e->zn ; n != nil; n = n->right){ + d = n->decl; + d->locals = d->next; + if(!inlist(d, dd)){ + d->next = dd; + dd = d; + o = d->offset+d->ty->size; + if(o > maxo) + maxo = o; + } + } + e->desc = gendesc(e->d, align(maxo, MaxAlign), dd); + for(d = dd; d != nil; d = nd){ + nd = d->next; + d->next = d->locals; + d->locals = nil; + } + e->zn = nil; + } + } +} + +static Except* +reve(Except *e) +{ + Except *l, *n; + + l = nil; + for( ; e != nil; e = n){ + n = e->next; + e->next = l; + l = e; + } + return l; +} + +static int +ckinline0(Node *n, Decl *d) +{ + Decl *dd; + + if(n == nil) + return 1; + if(n->op == Oname){ + dd = n->decl; + if(d == dd) + return 0; + if(dd->caninline == 1) + return ckinline0(dd->init->right, d); + return 1; + } + return ckinline0(n->left, d) && ckinline0(n->right, d); +} + +static void +ckinline(Decl *d) +{ + d->caninline = ckinline0(d->init->right, d); +} + +void +modcom(Decl *entry) +{ + Decl *globals, *m, *nils, *d, *ldts; + long ninst, ndata, ndesc, nlink, offset, ldtoff; + int ok, i, hints; + Dlist *dl; + + if(errors) + return; + + if(emitcode || emitstub || emittab != nil){ + emit(curscope()); + popscope(); + return; + } + + /* + * scom introduces global variables for case statements + * and unaddressable constants, so it must be done before + * popping the global scope + */ + nlabel = 0; + maxstack = MaxTemp; + genstart(); + + for(i = 0; i < nfns; i++) + if(fns[i]->caninline == 1) + ckinline(fns[i]); + + ok = 0; + for(i = 0; i < nfns; i++){ + d = fns[i]; +if(debug['v']) print("fncom: %s %d %p\n", d->sym->name, d->refs, d); + if(d->refs > 1 && !(d->caninline == 1 && local(d) && d->iface == nil)){ + fns[ok++] = d; + fncom(d); + } + } + nfns = ok; + if(blocks != -1) + fatal("blocks not nested correctly"); + firstinst = firstinst->next; + if(errors) + return; + + globals = popscope(); + checkrefs(globals); + if(errors) + return; + globals = vars(globals); + moddataref(); + + nils = popscope(); + m = nil; + for(d = nils; d != nil; d = d->next){ + if(debug['n']) + print("nil '%s' ref %d\n", d->sym->name, d->refs); + if(d->refs && m == nil) + m = dupdecl(d); + d->offset = 0; + } + globals = appdecls(m, globals); + globals = namesort(globals); + globals = modglobals(impdecls->d, globals); + vcom(globals); + narrowmods(); + ldts = nil; + if(LDT) + globals = resolveldts(globals, &ldts); + offset = idoffsets(globals, 0, IBY2WD); + if(LDT) + ldtoff = idindices(ldts); /* ldtoff = idoffsets(ldts, 0, IBY2WD); */ + for(d = nils; d != nil; d = d->next){ + if(debug['n']) + print("nil '%s' ref %d\n", d->sym->name, d->refs); + if(d->refs) + d->offset = m->offset; + } + + if(debug['g']){ + print("globals:\n"); + printdecls(globals); + } + + ndata = 0; + for(d = globals; d != nil; d = d->next) + ndata++; + ndesc = resolvedesc(impdecls->d, offset, globals); + ninst = resolvepcs(firstinst); + modresolve(); + if(impdecls->next != nil) + for(dl = impdecls; dl != nil; dl = dl->next) + resolvemod(dl->d); + nlink = resolvemod(impdecl); + + maxstack *= 10; + if(fixss != 0) + maxstack = fixss; + + if(debug['s']) + print("%ld instructions\n%ld data elements\n%ld type descriptors\n%ld functions exported\n%ld stack size\n", + ninst, ndata, ndesc, nlink, maxstack); + + excs = reve(excs); + + if(gendis){ + discon(XMAGIC); + hints = 0; + if(mustcompile) + hints |= MUSTCOMPILE; + if(dontcompile) + hints |= DONTCOMPILE; + if(LDT) + hints |= HASLDT; + if(excs != nil) + hints |= HASEXCEPT; + discon(hints); /* runtime hints */ + discon(maxstack); /* minimum stack extent size */ + discon(ninst); + discon(offset); + discon(ndesc); + discon(nlink); + disentry(entry); + disinst(firstinst); + disdesc(descriptors); + disvar(offset, globals); + dismod(impdecl); + if(LDT) + disldt(ldtoff, ldts); + if(excs != nil) + disexc(excs); + dispath(); + }else{ + asminst(firstinst); + asmentry(entry); + asmdesc(descriptors); + asmvar(offset, globals); + asmmod(impdecl); + if(LDT) + asmldt(ldtoff, ldts); + if(excs != nil) + asmexc(excs); + asmpath(); + } + if(bsym != nil){ + sblmod(impdecl); + + sblfiles(); + sblinst(firstinst, ninst); + sblty(adts, nadts); + sblfn(fns, nfns); + sblvar(globals); + } + + firstinst = nil; + lastinst = nil; + + excs = nil; +} + +void +fncom(Decl *decl) +{ + Src src; + Node *n; + Decl *loc, *last; + Inst *in; + + curfn = decl; + if(ispoly(decl)) + addfnptrs(decl, 1); + + /* + * pick up the function body and compile it + * this code tries to clean up the parse nodes as fast as possible + * function is Ofunc(name, body) + */ + decl->pc = nextinst(); + tinit(); + labdep = 0; + scp = 0; + breaks = allocmem(maxlabdep * sizeof breaks[0]); + conts = allocmem(maxlabdep * sizeof conts[0]); + labels = allocmem(maxlabdep * sizeof labels[0]); + bcscps = allocmem(maxlabdep * sizeof bcscps[0]); + n = decl->init; + if(decl->caninline == 1) + decl->init = dupn(0, nil, n); + else + decl->init = n->left; + src = n->right->src; + src.start.line = src.stop.line; + src.start.pos = src.stop.pos - 1; + for(n = n->right; n != nil; n = n->right){ + if(n->op != Oseq){ + if(n->op == Ocall && trcom(n, nil, 1)) + break; + scom(n); + break; + } + if(n->left->op == Ocall && trcom(n->left, n->right, 1)){ + n = n->right; + if(n == nil || n->op != Oseq) + break; + } + else + scom(n->left); + } + pushblock(); + in = genrawop(&src, IRET, nil, nil, nil); + popblock(); + reach(decl->pc); + if(in->reach && decl->ty->tof != tnone) + error(src.start, "no return at end of function %D", decl); + /* decl->endpc = lastinst; */ + if(labdep != 0) + fatal("unbalanced label stack"); + free(breaks); + free(conts); + free(labels); + free(bcscps); + + loc = declsort(appdecls(vars(decl->locals), tdecls())); + decl->offset = idoffsets(loc, decl->offset, MaxAlign); + for(last = decl->ty->ids; last != nil && last->next != nil; last = last->next) + ; + if(last != nil) + last->next = loc; + else + decl->ty->ids = loc; + + if(debug['f']){ + print("fn: %s\n", decl->sym->name); + printdecls(decl->ty->ids); + } + + decl->desc = gendesc(decl, decl->offset, decl->ty->ids); + decl->locals = loc; + excdesc(); + if(decl->offset > maxstack) + maxstack = decl->offset; + if(optims) + optim(decl->pc, decl); + if(last != nil) + last->next = nil; + else + decl->ty->ids = nil; +} + +/* + * statement compiler + */ +void +scom(Node *n) +{ + Inst *p, *pp, *p1, *p2, *p3; + Node tret, *left, *zn; + + for(; n != nil; n = n->right){ + switch(n->op){ + case Ocondecl: + case Otypedecl: + case Ovardecl: + case Oimport: + case Oexdecl: + return; + case Ovardecli: + break; + case Oscope: + pushscp(n); + scom(n->right); + popscp(); + zcom(n->left, nil); + return; + case Olabel: + scom(n->right); + return; + case Oif: + pushblock(); + left = simplify(n->left); + if(left->op == Oconst && left->ty == tint){ + if(left->val != 0) + scom(n->right->left); + else + scom(n->right->right); + popblock(); + return; + } + sumark(left); + pushblock(); + p = bcom(left, 1, nil); + tfreenow(); + popblock(); + scom(n->right->left); + if(n->right->right != nil){ + pp = p; + p = genrawop(&lastinst->src, IJMP, nil, nil, nil); + patch(pp, nextinst()); + scom(n->right->right); + } + patch(p, nextinst()); + popblock(); + return; + case Ofor: + n->left = left = simplify(n->left); + if(left->op == Oconst && left->ty == tint){ + if(left->val == 0) + return; + left->op = Onothing; + left->ty = tnone; + left->decl = nil; + } + pp = nextinst(); + pushblock(); + /* b = pushblock(); */ + sumark(left); + p = bcom(left, 1, nil); + tfreenow(); + popblock(); + + if(labdep >= maxlabdep) + fatal("label stack overflow"); + breaks[labdep] = nil; + conts[labdep] = nil; + labels[labdep] = n->decl; + bcscps[labdep] = curscp(); + labdep++; + scom(n->right->left); + labdep--; + + patch(conts[labdep], nextinst()); + if(n->right->right != nil){ + pushblock(); + scom(n->right->right); + popblock(); + } + repushblock(lastinst->block); /* was b */ + patch(genrawop(&lastinst->src, IJMP, nil, nil, nil), pp); /* for cprof: was &left->src */ + popblock(); + patch(p, nextinst()); + patch(breaks[labdep], nextinst()); + return; + case Odo: + pp = nextinst(); + + if(labdep >= maxlabdep) + fatal("label stack overflow"); + breaks[labdep] = nil; + conts[labdep] = nil; + labels[labdep] = n->decl; + bcscps[labdep] = curscp(); + labdep++; + scom(n->right); + labdep--; + + patch(conts[labdep], nextinst()); + + left = simplify(n->left); + if(left->op == Onothing + || left->op == Oconst && left->ty == tint){ + if(left->op == Onothing || left->val != 0){ + pushblock(); + p = genrawop(&left->src, IJMP, nil, nil, nil); + popblock(); + }else + p = nil; + }else{ + pushblock(); + p = bcom(sumark(left), 0, nil); + tfreenow(); + popblock(); + } + patch(p, pp); + patch(breaks[labdep], nextinst()); + return; + case Oalt: + case Ocase: + case Opick: + case Oexcept: +/* need push/pop blocks for alt guards */ + pushblock(); + if(labdep >= maxlabdep) + fatal("label stack overflow"); + breaks[labdep] = nil; + conts[labdep] = &nocont; + labels[labdep] = n->decl; + bcscps[labdep] = curscp(); + labdep++; + switch(n->op){ + case Oalt: + altcom(n); + break; + case Ocase: + case Opick: + casecom(n); + break; + case Oexcept: + excom(n); + break; + } + labdep--; + patch(breaks[labdep], nextinst()); + popblock(); + return; + case Obreak: + pushblock(); + bccom(n, breaks); + popblock(); + break; + case Ocont: + pushblock(); + bccom(n, conts); + popblock(); + break; + case Oseq: + if(n->left->op == Ocall && trcom(n->left, n->right, 0)){ + n = n->right; + if(n == nil || n->op != Oseq) + return; + } + else + scom(n->left); + break; + case Oret: + if(n->left != nil && n->left->op == Ocall && trcom(n->left, nil, 1)) + return; + pushblock(); + if(n->left != nil){ + n->left = simplify(n->left); + sumark(n->left); + ecom(&n->left->src, retalloc(&tret, n->left), n->left); + tfreenow(); + } + genrawop(&n->src, IRET, nil, nil, nil); + popblock(); + return; + case Oexit: + pushblock(); + genrawop(&n->src, IEXIT, nil, nil, nil); + popblock(); + return; + case Onothing: + return; + case Ofunc: + fatal("Ofunc"); + return; + case Oexstmt: + pushblock(); + pp = genrawop(&n->right->src, IEXC0, nil, nil, nil); /* marker */ + p1 = nextinst(); + scom(n->left); + p2 = nextinst(); + p3 = genrawop(&n->right->src, IJMP, nil, nil, nil); + p = genrawop(&n->right->src, IEXC, nil, nil, nil); /* marker */ + p->d.decl = mkdecl(&n->src, 0, n->right->ty); + zn = nil; + zeroallscopes(n->left, &zn); + scom(n->right); + patch(p3, nextinst()); + installexc(n->right, p1, p2, zn); + patch(pp, p); + popblock(); + return; + default: + pushblock(); + n = simplify(n); + sumark(n); + ecom(&n->src, nil, n); + tfreenow(); + popblock(); + return; + } + } +} + +/* + * compile a break, continue + */ +void +bccom(Node *n, Inst **bs) +{ + Sym *s; + Inst *p; + int i, ok; + + s = nil; + if(n->decl != nil) + s = n->decl->sym; + ok = -1; + for(i = 0; i < labdep; i++){ + if(bs[i] == &nocont) + continue; + if(s == nil || labels[i] != nil && labels[i]->sym == s) + ok = i; + } + if(ok < 0){ + nerror(n, "no appropriate target for %V", n); + return; + } + zeroscopes(bcscps[ok]); + p = genrawop(&n->src, IJMP, nil, nil, nil); + p->branch = bs[ok]; + bs[ok] = p; +} + +static int +dogoto(Case *c) +{ + int i, j, k, n, r, q, v; + Label *l, *nl; + Src *src; + + l = c->labs; + n = c->nlab; + if(n == 0) + return 0; + r = l[n-1].stop->val - l[0].start->val+1; + if(r >= 3 && r <= 3*n){ + if(r != n){ + /* remove ranges, fill in gaps */ + c->nlab = r; + nl = c->labs = allocmem(r*sizeof(*nl)); + k = 0; + v = l[0].start->val-1; + for(i = 0; i < n; i++){ + /* p = l[i].start->val; */ + q = l[i].stop->val; + src = &l[i].start->src; + for(j = v+1; j <= q; j++){ + nl[k] = l[i]; + nl[k].start = nl[k].stop = mkconst(src, j); + k++; + } + v = q; + } + if(k != r) + fatal("bad case expansion"); + } + l = c->labs; + for(i = 0; i < r; i++) + l[i].inst = nil; + return 1; + } + return 0; +} + +static void +fillrange(Case *c, Node *nn, Inst *in) +{ + int i, j, n, p, q; + Label *l; + + l = c->labs; + n = c->nlab; + p = nn->left->val; + q = nn->right->val; + for(i = 0; i < n; i++) + if(l[i].start->val == p) + break; + if(i == n) + fatal("fillrange fails"); + for(j = p; j <= q; j++) + l[i++].inst = in; +} + +void +casecom(Node *cn) +{ + Src *src; + Case *c; + Decl *d; + Type *ctype; + Inst *j, *jmps, *wild, *k, *j1, *j2; + Node *n, *p, *left, tmp, nto, tmpc; + Label *labs; + char buf[32]; + int i, nlab, op, needwild, igoto; + + c = cn->ty->cse; + + needwild = cn->op != Opick || c->nlab != cn->left->right->ty->tof->decl->tag; + igoto = cn->left->ty == tint && dogoto(c); + j1 = j2 = nil; + + /* + * generate global which has case labels + */ + if(igoto){ + seprint(buf, buf+sizeof(buf), ".g%d", nlabel++); + cn->ty->kind = Tgoto; + } + else + seprint(buf, buf+sizeof(buf), ".c%d", nlabel++); + d = mkids(&cn->src, enter(buf, 0), cn->ty, nil); + d->init = mkdeclname(&cn->src, d); + + nto.addable = Rmreg; + nto.left = nil; + nto.right = nil; + nto.op = Oname; + nto.ty = d->ty; + nto.decl = d; + + tmp.decl = tmpc.decl = nil; + left = cn->left; + left = simplify(left); + cn->left = left; + sumark(left); + if(debug['c']) + print("case %n\n", left); + ctype = cn->left->ty; + if(left->addable >= Rcant){ + if(cn->op == Opick){ + ecom(&left->src, nil, left); + tfreenow(); + left = mkunary(Oind, dupn(1, &left->src, left->left)); + left->ty = tint; + sumark(left); + ctype = tint; + }else{ + left = eacom(left, &tmp, nil); + tfreenow(); + } + } + + labs = c->labs; + nlab = c->nlab; + + if(igoto){ + if(labs[0].start->val != 0){ + talloc(&tmpc, left->ty, nil); + if(left->addable == Radr || left->addable == Rmadr){ + genrawop(&left->src, IMOVW, left, nil, &tmpc); + left = &tmpc; + } + genrawop(&left->src, ISUBW, sumark(labs[0].start), left, &tmpc); + left = &tmpc; + } + if(needwild){ + j1 = genrawop(&left->src, IBLTW, left, sumark(mkconst(&left->src, 0)), nil); + j2 = genrawop(&left->src, IBGTW, left, sumark(mkconst(&left->src, labs[nlab-1].start->val-labs[0].start->val)), nil); + } + j = nextinst(); + genrawop(&left->src, IGOTO, left, nil, &nto); + j->d.reg = IBY2WD; + } + else{ + op = ICASE; + if(ctype == tbig) + op = ICASEL; + else if(ctype == tstring) + op = ICASEC; + genrawop(&left->src, op, left, nil, &nto); + } + tfree(&tmp); + tfree(&tmpc); + + jmps = nil; + wild = nil; + for(n = cn->right; n != nil; n = n->right){ + j = nextinst(); + for(p = n->left->left; p != nil; p = p->right){ + if(debug['c']) + print("case qualifier %n\n", p->left); + switch(p->left->op){ + case Oconst: + labs[findlab(ctype, p->left, labs, nlab)].inst = j; + break; + case Orange: + labs[findlab(ctype, p->left->left, labs, nlab)].inst = j; + if(igoto) + fillrange(c, p->left, j); + break; + case Owild: + if(needwild) + wild = j; +/* + else + nwarn(p->left, "default case redundant"); +*/ + break; + } + } + + if(debug['c']) + print("case body for %V: %n\n", n->left->left, n->left->right); + + k = nextinst(); + scom(n->left->right); + + src = &lastinst->src; + // if(n->left->right == nil || n->left->right->op == Onothing) + if(k == nextinst()) + src = &n->left->left->src; + j = genrawop(src, IJMP, nil, nil, nil); + j->branch = jmps; + jmps = j; + } + patch(jmps, nextinst()); + if(wild == nil && needwild) + wild = nextinst(); + + if(igoto){ + if(needwild){ + patch(j1, wild); + patch(j2, wild); + } + for(i = 0; i < nlab; i++) + if(labs[i].inst == nil) + labs[i].inst = wild; + } + + c->iwild = wild; + + d->ty->cse = c; + usetype(d->ty); + installids(Dglobal, d); +} + +void +altcom(Node *nalt) +{ + Src altsrc; + Case *c; + Decl *d; + Type *talt; + Node *n, *p, *left, tab, slot, off, add, which, nto, adr; + Node **comm, *op, *tmps; + Inst *j, *tj, *jmps, *me, *wild; + Label *labs; + char buf[32]; + int i, is, ir, nlab, nsnd, altop, isptr; + Inst *pp; + + talt = nalt->ty; + c = talt->cse; + nlab = c->nlab; + nsnd = c->nsnd; + comm = allocmem(nlab * sizeof *comm); + labs = allocmem(nlab * sizeof *labs); + tmps = allocmem(nlab * sizeof *tmps); + c->labs = labs; + + /* + * built the type of the alt channel table + * note that we lie to the garbage collector + * if we know that another reference exists for the channel + */ + is = 0; + ir = nsnd; + i = 0; + for(n = nalt->left; n != nil; n = n->right){ + for(p = n->left->right->left; p != nil; p = p->right){ + left = simplify(p->left); + p->left = left; + if(left->op == Owild) + continue; + comm[i] = hascomm(left); + left = comm[i]->left; + sumark(left); + isptr = left->addable >= Rcant; + if(comm[i]->op == Osnd) + labs[is++].isptr = isptr; + else + labs[ir++].isptr = isptr; + i++; + } + } + + talloc(&which, tint, nil); + talloc(&tab, talt, nil); + + /* + * build the node for the address of each channel, + * the values to send, and the storage fro values received + */ + off = znode; + off.op = Oconst; + off.ty = tint; + off.addable = Rconst; + adr = znode; + adr.op = Oadr; + adr.left = &tab; + adr.ty = tint; + add = znode; + add.op = Oadd; + add.left = &adr; + add.right = &off; + add.ty = tint; + slot = znode; + slot.op = Oind; + slot.left = &add; + sumark(&slot); + + /* + * compile the sending and receiving channels and values + */ + is = 2*IBY2WD; + ir = is + nsnd*2*IBY2WD; + i = 0; + for(n = nalt->left; n != nil; n = n->right){ + for(p = n->left->right->left; p != nil; p = p->right){ + if(p->left->op == Owild) + continue; + + /* + * gen channel + */ + op = comm[i]; + if(op->op == Osnd){ + off.val = is; + is += 2*IBY2WD; + }else{ + off.val = ir; + ir += 2*IBY2WD; + } + left = op->left; + + /* + * this sleaze is lying to the garbage collector + */ + if(left->addable < Rcant) + genmove(&left->src, Mas, tint, left, &slot); + else{ + slot.ty = left->ty; + ecom(&left->src, &slot, left); + tfreenow(); + slot.ty = nil; + } + + /* + * gen value + */ + off.val += IBY2WD; + tmps[i].decl = nil; + p->left = rewritecomm(p->left, comm[i], &tmps[i], &slot); + + i++; + } + } + + /* + * stuff the number of send & receive channels into the table + */ + altsrc = nalt->src; + altsrc.stop.pos += 3; + off.val = 0; + genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nsnd)), &slot); + off.val += IBY2WD; + genmove(&altsrc, Mas, tint, sumark(mkconst(&altsrc, nlab-nsnd)), &slot); + off.val += IBY2WD; + + altop = IALT; + if(c->wild != nil) + altop = INBALT; + pp = genrawop(&altsrc, altop, &tab, nil, &which); + pp->m.offset = talt->size; /* for optimizer */ + + seprint(buf, buf+sizeof(buf), ".g%d", nlabel++); + d = mkids(&nalt->src, enter(buf, 0), mktype(&nalt->src.start, &nalt->src.stop, Tgoto, nil, nil), nil); + d->ty->cse = c; + d->init = mkdeclname(&nalt->src, d); + + nto.addable = Rmreg; + nto.left = nil; + nto.right = nil; + nto.op = Oname; + nto.decl = d; + nto.ty = d->ty; + + me = nextinst(); + genrawop(&altsrc, IGOTO, &which, nil, &nto); + me->d.reg = IBY2WD; /* skip the number of cases field */ + tfree(&tab); + tfree(&which); + + /* + * compile the guard expressions and bodies + */ + i = 0; + is = 0; + ir = nsnd; + jmps = nil; + wild = nil; + for(n = nalt->left; n != nil; n = n->right){ + j = nil; + for(p = n->left->right->left; p != nil; p = p->right){ + tj = nextinst(); + if(p->left->op == Owild){ + wild = nextinst(); + }else{ + if(comm[i]->op == Osnd) + labs[is++].inst = tj; + else{ + labs[ir++].inst = tj; + tacquire(&tmps[i]); + } + sumark(p->left); + if(debug['a']) + print("alt guard %n\n", p->left); + ecom(&p->left->src, nil, p->left); + tfree(&tmps[i]); + tfreenow(); + i++; + } + if(p->right != nil){ + tj = genrawop(&lastinst->src, IJMP, nil, nil, nil); + tj->branch = j; + j = tj; + } + } + + patch(j, nextinst()); + if(debug['a']) + print("alt body %n\n", n->left->right); + scom(n->left); + + j = genrawop(&lastinst->src, IJMP, nil, nil, nil); + j->branch = jmps; + jmps = j; + } + patch(jmps, nextinst()); + free(comm); + + c->iwild = wild; + + usetype(d->ty); + installids(Dglobal, d); +} + +void +excom(Node *en) +{ + Src *src; + Decl *ed; + Type *qt; + Case *c; + Inst *j, *jmps, *wild, *k; + Node *n, *p; + Label *labs; + int nlab; + + ed = en->left->decl; + ed->ty = rtexception; + c = en->ty->cse; + labs = c->labs; + nlab = c->nlab; + jmps = nil; + wild = nil; + for(n = en->right; n != nil; n = n->right){ + qt = nil; + j = nextinst(); + for(p = n->left->left; p != nil; p = p->right){ + switch(p->left->op){ + case Oconst: + labs[findlab(texception, p->left, labs, nlab)].inst = j; + break; + case Owild: + wild = j; + break; + } + if(qt == nil) + qt = p->left->ty; + else if(!tequal(qt, p->left->ty)) + qt = texception; + } + if(qt != nil) + ed->ty = qt; + k = nextinst(); + scom(n->left->right); + src = &lastinst->src; + if(k == nextinst()) + src = &n->left->left->src; + j = genrawop(src, IJMP, nil, nil, nil); + j->branch = jmps; + jmps = j; + } + ed->ty = rtexception; + patch(jmps, nextinst()); + c->iwild = wild; +} + +/* + * rewrite the communication operand + * allocate any temps needed for holding value to send or receive + */ +Node* +rewritecomm(Node *n, Node *comm, Node *tmp, Node *slot) +{ + Node *adr; + Inst *p; + + if(n == nil) + return nil; + adr = nil; + if(n == comm){ + if(comm->op == Osnd && sumark(n->right)->addable < Rcant) + adr = n->right; + else{ + adr = talloc(tmp, n->ty, nil); + tmp->src = n->src; + if(comm->op == Osnd){ + ecom(&n->right->src, tmp, n->right); + tfreenow(); + } + else + trelease(tmp); + } + } + if(n->right == comm && n->op == Oas && comm->op == Orcv + && sumark(n->left)->addable < Rcant) + adr = n->left; + if(adr != nil){ + p = genrawop(&comm->left->src, ILEA, adr, nil, slot); + p->m.offset = adr->ty->size; /* for optimizer */ + if(comm->op == Osnd) + p->m.reg = 1; /* for optimizer */ + return adr; + } + n->left = rewritecomm(n->left, comm, tmp, slot); + n->right = rewritecomm(n->right, comm, tmp, slot); + return n; +} + +/* + * merge together two sorted lists, yielding a sorted list + */ +static Decl* +declmerge(Decl *e, Decl *f) +{ + Decl rock, *d; + int es, fs, v; + + d = &rock; + while(e != nil && f != nil){ + fs = f->ty->size; + es = e->ty->size; + /* v = 0; */ + v = (e->link == nil) - (f->link == nil); + if(v == 0 && (es <= IBY2WD || fs <= IBY2WD)) + v = fs - es; + if(v == 0) + v = e->refs - f->refs; + if(v == 0) + v = fs - es; + if(v == 0) + v = -strcmp(e->sym->name, f->sym->name); + if(v >= 0){ + d->next = e; + d = e; + e = e->next; + while(e != nil && e->nid == 0){ + d = e; + e = e->next; + } + }else{ + d->next = f; + d = f; + f = f->next; + while(f != nil && f->nid == 0){ + d = f; + f = f->next; + } + } + /* d = d->next; */ + } + if(e != nil) + d->next = e; + else + d->next = f; + return rock.next; +} + +/* + * recursively split lists and remerge them after they are sorted + */ +static Decl* +recdeclsort(Decl *d, int n) +{ + Decl *r, *dd; + int i, m; + + if(n <= 1) + return d; + m = n / 2 - 1; + dd = d; + for(i = 0; i < m; i++){ + dd = dd->next; + while(dd->nid == 0) + dd = dd->next; + } + r = dd->next; + while(r->nid == 0){ + dd = r; + r = r->next; + } + dd->next = nil; + return declmerge(recdeclsort(d, n / 2), + recdeclsort(r, (n + 1) / 2)); +} + +/* + * sort the ids by size and number of references + */ +Decl* +declsort(Decl *d) +{ + Decl *dd; + int n; + + n = 0; + for(dd = d; dd != nil; dd = dd->next) + if(dd->nid > 0) + n++; + return recdeclsort(d, n); +} + +Src nilsrc; + +/* Do we finally + * (a) pick off pointers as in the code below + * (b) generate a block move from zeroed memory as in tfree() in gen.b in limbo version + * (c) add a new block zero instruction to dis + * (d) reorganize the locals/temps in a frame + */ +void +zcom1(Node *n, Node **nn) +{ + Type *ty; + Decl *d; + Node *e, *dn; + Src src; + + ty = n->ty; + if (!tmustzero(ty)) + return; + if (n->op == Oname && n->decl->refs == 0) + return; + if (nn) { + if(n->op != Oname) + nerror(n, "fatal: bad op in zcom1 map"); + n->right = *nn; + *nn = n; + return; + } + if (debug['Z']) + print("zcom1 : %n\n", n); + if (ty->kind == Tadtpick) + ty = ty->tof; + if (ty->kind == Ttuple || ty->kind == Tadt) { + for (d = ty->ids; d != nil; d = d->next) { + if (tmustzero(d->ty)) { + if (d->next != nil) + dn = dupn(0, nil, n); + else + dn = n; + e = mkbin(Odot, dn, mkname(&nilsrc, d->sym)); + e->right->decl = d; + e->ty = e->right->ty = d->ty; + zcom1(e, nn); + } + } + } + else { + src = n->src; + n->src = nilsrc; + e = mkbin(Oas, n, mknil(&nilsrc)); + e->ty = e->right->ty = ty; +/* + if (debug['Z']) + print("ecom %n\n", e); +*/ + pushblock(); + e = simplify(e); + sumark(e); + ecom(&e->src, nil, e); + popblock(); + n->src = src; + } +} + +void +zcom0(Decl *id, Node **nn) +{ + Node *e; + + e = mkname(&nilsrc, id->sym); + e->decl = id; + e->ty = id->ty; + zcom1(e, nn); +} + +/* end of scope */ +void +zcom(Node *n, Node **nn) +{ + Decl *ids, *last; + Node *r, *nt; + + for ( ; n != nil; n = r) { + r = n->right; + n->right = nil; + switch (n->op) { + case Ovardecl : + last = n->left->decl; + for (ids = n->decl; ids != last->next; ids = ids->next) + zcom0(ids, nn); + break; + case Oname : + if (n->decl != nildecl) + zcom1(dupn(0, nil, n), nn); + break; + case Otuple : + for (nt = n->left; nt != nil; nt = nt->right) + zcom(nt->left, nn); + break; + default : + fatal("bad node in zcom()"); + break; + } + n->right = r; + } +} + +static int +ret(Node *n, int nilret) +{ + if(n == nil) + return nilret; + if(n->op == Oseq) + n = n->left; + return n->op == Oret && n->left == nil; +} + +static int +trcom(Node *e, Node *ne, int nilret) +{ + Decl *d, *id; + Node *as, *a, *f, *n; + Inst *p; + +return 0; // TBS + if(e->op != Ocall || e->left->op != Oname) + return 0; + d = e->left->decl; + if(d != curfn || d->handler || ispoly(d)) + return 0; + if(!ret(ne, nilret)) + return 0; + pushblock(); + id = d->ty->ids; + /* evaluate args in same order as normal calls */ + for(as = e->right; as != nil; as = as->right){ + a = as->left; + if(!(a->op == Oname && id == a->decl)){ + if(occurs(id, as->right)){ + f = talloc(mkn(0, nil, nil), id->ty, nil); + f->flags |= TEMP; + } + else + f = mkdeclname(&as->src, id); + n = mkbin(Oas, f, a); + n->ty = id->ty; + scom(n); + if(f->flags&TEMP) + as->left = f; + } + id = id->next; + } + id = d->ty->ids; + for(as = e->right; as != nil; as = as->right){ + a = as->left; + if(a->flags&TEMP){ + f = mkdeclname(&as->src, id); + n = mkbin(Oas, f, a); + n->ty = id->ty; + scom(n); + tfree(a); + } + id = id->next; + } + p = genrawop(&e->src, IJMP, nil, nil, nil); + patch(p, d->pc); + popblock(); + return 1; +} diff --git a/limbo/decls.c b/limbo/decls.c new file mode 100644 index 00000000..4edab461 --- /dev/null +++ b/limbo/decls.c @@ -0,0 +1,1367 @@ +#include "limbo.h" + +char *storename[Dend]= +{ + /* Dtype */ "type", + /* Dfn */ "function", + /* Dglobal */ "global", + /* Darg */ "argument", + /* Dlocal */ "local", + /* Dconst */ "con", + /* Dfield */ "field", + /* Dtag */ "pick tag", + /* Dimport */ "import", + /* Dunbound */ "unbound", + /* Dundef */ "undefined", + /* Dwundef */ "undefined", +}; + +char *storeart[Dend] = +{ + /* Dtype */ "a ", + /* Dfn */ "a ", + /* Dglobal */ "a ", + /* Darg */ "an ", + /* Dlocal */ "a ", + /* Dconst */ "a ", + /* Dfield */ "a ", + /* Dtag */ "a", + /* Dimport */ "an ", + /* Dunbound */ "", + /* Dundef */ "", + /* Dwundef */ "", +}; + +int storespace[Dend] = +{ + /* Dtype */ 0, + /* Dfn */ 0, + /* Dglobal */ 1, + /* Darg */ 1, + /* Dlocal */ 1, + /* Dconst */ 0, + /* Dfield */ 1, + /* Dtag */ 0, + /* Dimport */ 0, + /* Dunbound */ 0, + /* Dundef */ 0, + /* Dwundef */ 0, +}; + +static Decl *scopes[MaxScope]; +static Decl *tails[MaxScope]; +static Node *scopenode[MaxScope]; +static uchar scopekind[MaxScope]; +static Decl zdecl; + +static void freeloc(Decl*); + +void +popscopes(void) +{ + Decl *d; + Dlist *id; + + /* + * clear out any decls left in syms + */ + while(scope >= ScopeBuiltin){ + for(d = scopes[scope--]; d != nil; d = d->next){ + if(d->sym != nil){ + d->sym->decl = d->old; + d->old = nil; + } + } + } + + for(id = impdecls; id != nil; id = id->next){ + for(d = id->d->ty->ids; d != nil; d = d->next){ + d->sym->decl = nil; + d->old = nil; + } + } + impdecls = nil; + + scope = ScopeBuiltin; + scopes[ScopeBuiltin] = nil; + tails[ScopeBuiltin] = nil; +} + +void +declstart(void) +{ + Decl *d; + + iota = mkids(&nosrc, enter("iota", 0), tint, nil); + iota->init = mkconst(&nosrc, 0); + + scope = ScopeNils; + scopes[ScopeNils] = nil; + tails[ScopeNils] = nil; + + nildecl = mkdecl(&nosrc, Dglobal, tany); + nildecl->sym = enter("nil", 0); + installids(Dglobal, nildecl); + d = mkdecl(&nosrc, Dglobal, tstring); + d->sym = enter("", 0); + installids(Dglobal, d); + + scope = ScopeGlobal; + scopes[ScopeGlobal] = nil; + tails[ScopeGlobal] = nil; +} + +void +redecl(Decl *d) +{ + Decl *old; + + old = d->sym->decl; + if(old->store == Dwundef) + return; + error(d->src.start, "redeclaration of %K, previously declared as %k on line %L", + d, old, old->src.start); +} + +void +checkrefs(Decl *d) +{ + Decl *id, *m; + long refs; + + for(; d != nil; d = d->next){ + if(d->das) + d->refs--; + switch(d->store){ + case Dtype: + refs = d->refs; + if(d->ty->kind == Tadt){ + for(id = d->ty->ids; id != nil; id = id->next){ + d->refs += id->refs; + if(id->store != Dfn) + continue; + if(id->init == nil && id->link == nil && d->importid == nil) + error(d->src.start, "function %s.%s not defined", d->sym->name, id->sym->name); + if(superwarn && !id->refs && d->importid == nil) + warn(d->src.start, "function %s.%s not referenced", d->sym->name, id->sym->name); + } + } + if(d->ty->kind == Tmodule){ + for(id = d->ty->ids; id != nil; id = id->next){ + refs += id->refs; + if(id->iface != nil) + id->iface->refs += id->refs; + if(id->store == Dtype){ + for(m = id->ty->ids; m != nil; m = m->next){ + refs += m->refs; + if(m->iface != nil) + m->iface->refs += m->refs; + } + } + } + d->refs = refs; + } + if(superwarn && !refs && d->importid == nil) + warn(d->src.start, "%K not referenced", d); + break; + case Dglobal: + if(!superwarn) + break; + case Dlocal: + case Darg: + if(!d->refs && d->sym != nil + && d->sym->name != nil && d->sym->name[0] != '.') + warn(d->src.start, "%K not referenced", d); + break; + case Dconst: + if(superwarn && !d->refs && d->sym != nil) + warn(d->src.start, "%K not referenced", d); + if(d->ty == tstring && d->init != nil) + d->init->decl->refs += d->refs; + break; + case Dfn: + if(d->init == nil && d->importid == nil) + error(d->src.start, "%K not defined", d); + if(superwarn && !d->refs) + warn(d->src.start, "%K not referenced", d); + break; + case Dimport: + if(superwarn && !d->refs) + warn(d->src.start, "%K not referenced", d); + break; + } + if(d->das) + d->refs++; + } +} + +Node* +vardecl(Decl *ids, Type *t) +{ + Node *n; + + n = mkn(Ovardecl, mkn(Oseq, nil, nil), nil); + n->decl = ids; + n->ty = t; + return n; +} + +void +vardecled(Node *n) +{ + Decl *ids, *last; + Type *t; + int store; + + store = Dlocal; + if(scope == ScopeGlobal) + store = Dglobal; + if(n->ty->kind == Texception && n->ty->cons){ + store = Dconst; + fatal("Texception in vardecled"); + } + ids = n->decl; + installids(store, ids); + t = n->ty; + for(last = ids; ids != nil; ids = ids->next){ + ids->ty = t; + last = ids; + } + n->left->decl = last; +} + +Node* +condecl(Decl *ids, Node *init) +{ + Node *n; + + n = mkn(Ocondecl, mkn(Oseq, nil, nil), init); + n->decl = ids; + return n; +} + +void +condecled(Node *n) +{ + Decl *ids, *last; + + ids = n->decl; + installids(Dconst, ids); + for(last = ids; ids != nil; ids = ids->next){ + ids->ty = tunknown; + last = ids; + } + n->left->decl = last; +} + +Node* +exdecl(Decl *ids, Decl *tids) +{ + Node *n; + Type *t; + + t = mktype(&ids->src.start, &ids->src.stop, Texception, nil, tids); + t->cons = 1; + n = mkn(Oexdecl, mkn(Oseq, nil, nil), nil); + n->decl = ids; + n->ty = t; + return n; +} + +void +exdecled(Node *n) +{ + Decl *ids, *last; + Type *t; + + ids = n->decl; + installids(Dconst, ids); + t = n->ty; + for(last = ids; ids != nil; ids = ids->next){ + ids->ty = t; + last = ids; + } + n->left->decl = last; +} + +Node* +importdecl(Node *m, Decl *ids) +{ + Node *n; + + n = mkn(Oimport, mkn(Oseq, nil, nil), m); + n->decl = ids; + return n; +} + +void +importdecled(Node *n) +{ + Decl *ids, *last; + + ids = n->decl; + installids(Dimport, ids); + for(last = ids; ids != nil; ids = ids->next){ + ids->ty = tunknown; + last = ids; + } + n->left->decl = last; +} + +Node* +mkscope(Node *body) +{ + Node *n; + + n = mkn(Oscope, nil, body); + if(body != nil) + n->src = body->src; + return n; +} + +Node* +fndecl(Node *n, Type *t, Node *body) +{ + n = mkbin(Ofunc, n, body); + n->ty = t; + return n; +} + +void +fndecled(Node *n) +{ + Decl *d; + Node *left; + + left = n->left; + if(left->op == Oname){ + d = left->decl->sym->decl; + if(d == nil || d->store == Dimport){ + d = mkids(&left->src, left->decl->sym, n->ty, nil); + installids(Dfn, d); + } + left->decl = d; + d->refs++; + } + if(left->op == Odot) + pushscope(nil, Sother); + if(n->ty->polys != nil){ + pushscope(nil, Sother); + installids(Dtype, n->ty->polys); + } + pushscope(nil, Sother); + installids(Darg, n->ty->ids); + n->ty->ids = popscope(); + if(n->ty->val != nil) + mergepolydecs(n->ty); + if(n->ty->polys != nil) + n->ty->polys = popscope(); + if(left->op == Odot) + popscope(); +} + +/* + * check the function declaration only + * the body will be type checked later by fncheck + */ +Decl * +fnchk(Node *n) +{ + int bad; + Decl *d, *inadt, *adtp; + Type *t; + + bad = 0; + d = n->left->decl; + if(n->left->op == Odot) + d = n->left->right->decl; + if(d == nil) + fatal("decl() fnchk nil"); + n->left->decl = d; + if(d->store == Dglobal || d->store == Dfield) + d->store = Dfn; + if(d->store != Dfn || d->init != nil){ + nerror(n, "redeclaration of function %D, previously declared as %k on line %L", + d, d, d->src.start); + if(d->store == Dfn && d->init != nil) + bad = 1; + } + d->init = n; + + t = n->ty; + inadt = d->dot; + if(inadt != nil && (inadt->store != Dtype || inadt->ty->kind != Tadt)) + inadt = nil; + if(n->left->op == Odot){ + pushscope(nil, Sother); + adtp = outerpolys(n->left); + if(adtp != nil) + installids(Dtype, adtp); + if(!polyequal(adtp, n->decl)) + nerror(n, "adt polymorphic type mismatch"); + n->decl = nil; + } + t = validtype(t, inadt); + if(n->left->op == Odot) + popscope(); + if(debug['d']) + print("declare function %D ty %T newty %T\n", d, d->ty, t); + t = usetype(t); + + if(!polyequal(d->ty->polys, t->polys)) + nerror(n, "function polymorphic type mismatch"); + if(!tcompat(d->ty, t, 0)) + nerror(n, "type mismatch: %D defined as %T declared as %T on line %L", + d, t, d->ty, d->src.start); + else if(!raisescompat(d->ty->u.eraises, t->u.eraises)) + nerror(n, "raises mismatch: %D", d); + if(t->varargs != 0) + nerror(n, "cannot define functions with a '*' argument, such as %D", d); + + t->u.eraises = d->ty->u.eraises; + + d->ty = t; + d->offset = idoffsets(t->ids, MaxTemp, IBY2WD); + d->src = n->src; + + d->locals = nil; + + n->ty = t; + + return bad ? nil: d; +} + +Node* +globalas(Node *dst, Node *v, int valok) +{ + Node *tv; + + if(v == nil) + return nil; + if(v->op == Oas || v->op == Odas){ + v = globalas(v->left, v->right, valok); + if(v == nil) + return nil; + }else if(valok && !initable(dst, v, 0)) + return nil; + switch(dst->op){ + case Oname: + if(dst->decl->init != nil) + nerror(dst, "duplicate assignment to %V, previously assigned on line %L", + dst, dst->decl->init->src.start); + if(valok) + dst->decl->init = v; + return v; + case Otuple: + if(valok && v->op != Otuple) + fatal("can't deal with %n in tuple case of globalas", v); + tv = v->left; + for(dst = dst->left; dst != nil; dst = dst->right){ + globalas(dst->left, tv->left, valok); + if(valok) + tv = tv->right; + } + return v; + } + fatal("can't deal with %n in globalas", dst); + return nil; +} + +int +needsstore(Decl *d) +{ + if(!d->refs) + return 0; + if(d->importid != nil) + return 0; + if(storespace[d->store]) + return 1; + return 0; +} + +/* + * return the list of all referenced storage variables + */ +Decl* +vars(Decl *d) +{ + Decl *v, *n; + + while(d != nil && !needsstore(d)) + d = d->next; + for(v = d; v != nil; v = v->next){ + while(v->next != nil){ + n = v->next; + if(needsstore(n)) + break; + v->next = n->next; + } + } + return d; +} + +/* + * declare variables from the left side of a := statement + */ +static int +recdasdecl(Node *n, int store, int *nid) +{ + Decl *d, *old; + int ok; + + switch(n->op){ + case Otuple: + ok = 1; + for(n = n->left; n != nil; n = n->right) + ok &= recdasdecl(n->left, store, nid); + return ok; + case Oname: + if(n->decl == nildecl){ + *nid = -1; + return 1; + } + d = mkids(&n->src, n->decl->sym, nil, nil); + installids(store, d); + old = d->old; + if(old != nil + && old->store != Dfn + && old->store != Dwundef + && old->store != Dundef) + warn(d->src.start, "redeclaration of %K, previously declared as %k on line %L", + d, old, old->src.start); + n->decl = d; + d->refs++; + d->das = 1; + if(*nid >= 0) + (*nid)++; + return 1; + } + return 0; +} + +static int +recmark(Node *n, int nid) +{ + switch(n->op){ + case Otuple: + for(n = n->left; n != nil; n = n->right) + nid = recmark(n->left, nid); + break; + case Oname: + n->decl->nid = nid; + nid = 0; + break; + } + return nid; +} + +int +dasdecl(Node *n) +{ + int store, ok, nid; + + nid = 0; + if(scope == ScopeGlobal) + store = Dglobal; + else + store = Dlocal; + + ok = recdasdecl(n, store, &nid); + if(!ok) + nerror(n, "illegal declaration expression %V", n); + if(ok && store == Dlocal && nid > 1) + recmark(n, nid); + return ok; +} + +/* + * declare global variables in nested := expressions + */ +void +gdasdecl(Node *n) +{ + if(n == nil) + return; + + if(n->op == Odas){ + gdasdecl(n->right); + dasdecl(n->left); + }else{ + gdasdecl(n->left); + gdasdecl(n->right); + } +} + +Decl* +undefed(Src *src, Sym *s) +{ + Decl *d; + + d = mkids(src, s, tnone, nil); + error(src->start, "%s is not declared", s->name); + installids(Dwundef, d); + return d; +} + +/* +int +inloop() +{ + int i; + + for (i = scope; i > 0; i--) + if (scopekind[i] == Sloop) + return 1; + return 0; +} +*/ + +int +nested() +{ + int i; + + for (i = scope; i > 0; i--) + if (scopekind[i] == Sscope || scopekind[i] == Sloop) + return 1; + return 0; +} + +void +decltozero(Node *n) +{ + Node *scp; + + if ((scp = scopenode[scope]) != nil) { + /* can happen if we do + * x[i] := ...... + * which is an error + */ + if (n->right != nil && errors == 0) + fatal("Ovardecl/Oname/Otuple has right field\n"); + n->right = scp->left; + scp->left = n; + } +} + +void +pushscope(Node *scp, int kind) +{ + if(scope >= MaxScope) + fatal("scope too deep"); + scope++; + scopes[scope] = nil; + tails[scope] = nil; + scopenode[scope] = scp; + scopekind[scope] = kind; +} + +Decl* +curscope(void) +{ + return scopes[scope]; +} + +/* + * revert to old declarations for each symbol in the currect scope. + * remove the effects of any imported adt types + * whenever the adt is imported from a module, + * we record in the type's decl the module to use + * when calling members. the process is reversed here. + */ +Decl* +popscope(void) +{ + Decl *id; + Type *t; + +if (debug['X']) + print("popscope\n"); + for(id = scopes[scope]; id != nil; id = id->next){ + if(id->sym != nil){ +if (debug['X']) + print("%s : %s %d\n", id->sym->name, kindname[id->ty->kind], id->init != nil ? id->init->op : 0); + id->sym->decl = id->old; + id->old = nil; + } + if(id->importid != nil) + id->importid->refs += id->refs; + t = id->ty; + if(id->store == Dtype + && t->decl != nil + && t->decl->timport == id) + t->decl->timport = id->timport; + if(id->store == Dlocal) + freeloc(id); + } + return scopes[scope--]; +} + +/* + * make a new scope, + * preinstalled with some previously installed identifiers + * don't add the identifiers to the scope chain, + * so they remain separate from any newly installed ids + * + * these routines assume no ids are imports + */ +void +repushids(Decl *ids) +{ + Sym *s; + + if(scope >= MaxScope) + fatal("scope too deep"); + scope++; + scopes[scope] = nil; + tails[scope] = nil; + scopenode[scope] = nil; + scopekind[scope] = Sother; + + for(; ids != nil; ids = ids->next){ + if(ids->scope != scope + && (ids->dot == nil || !isimpmod(ids->dot->sym) + || ids->scope != ScopeGlobal || scope != ScopeGlobal + 1)) + fatal("repushids scope mismatch"); + s = ids->sym; + if(s != nil && ids->store != Dtag){ + if(s->decl != nil && s->decl->scope >= scope) + ids->old = s->decl->old; + else + ids->old = s->decl; + s->decl = ids; + } + } +} + +/* + * pop a scope which was started with repushids + * return any newly installed ids + */ +Decl* +popids(Decl *ids) +{ + for(; ids != nil; ids = ids->next){ + if(ids->sym != nil && ids->store != Dtag){ + ids->sym->decl = ids->old; + ids->old = nil; + } + } + return popscope(); +} + +void +installids(int store, Decl *ids) +{ + Decl *d, *last; + Sym *s; + + last = nil; + for(d = ids; d != nil; d = d->next){ + d->scope = scope; + if(d->store == Dundef) + d->store = store; + s = d->sym; + if(s != nil){ + if(s->decl != nil && s->decl->scope >= scope){ + redecl(d); + d->old = s->decl->old; + }else + d->old = s->decl; + s->decl = d; + } + last = d; + } + if(ids != nil){ + d = tails[scope]; + if(d == nil) + scopes[scope] = ids; + else + d->next = ids; + tails[scope] = last; + } +} + +Decl* +lookup(Sym *sym) +{ + int s; + Decl *d; + + for(s = scope; s >= ScopeBuiltin; s--){ + for(d = scopes[s]; d != nil; d = d->next){ + if(d->sym == sym) + return d; + } + } + return nil; +} + +Decl* +mkids(Src *src, Sym *s, Type *t, Decl *next) +{ + Decl *d; + + d = mkdecl(src, Dundef, t); + d->next = next; + d->sym = s; + return d; +} + +Decl* +mkdecl(Src *src, int store, Type *t) +{ + Decl *d; + static Decl z; + + d = allocmem(sizeof *d); + *d = z; + d->src = *src; + d->store = store; + d->ty = t; + d->nid = 1; + return d; +} + +Decl* +dupdecl(Decl *old) +{ + Decl *d; + + d = allocmem(sizeof *d); + *d = *old; + d->next = nil; + return d; +} + +Decl* +dupdecls(Decl *old) +{ + Decl *d, *nd, *first, *last; + + first = last = nil; + for(d = old; d != nil; d = d->next){ + nd = dupdecl(d); + if(first == nil) + first = nd; + else + last->next = nd; + last = nd; + } + return first; +} + +Decl* +appdecls(Decl *d, Decl *dd) +{ + Decl *t; + + if(d == nil) + return dd; + for(t = d; t->next != nil; t = t->next) + ; + t->next = dd; + return d; +} + +Decl* +revids(Decl *id) +{ + Decl *d, *next; + + d = nil; + for(; id != nil; id = next){ + next = id->next; + id->next = d; + d = id; + } + return d; +} + +long +idoffsets(Decl *id, long offset, int al) +{ + int a, algn; + Decl *d; + + algn = 1; + for(; id != nil; id = id->next){ + if(storespace[id->store]){ +usedty(id->ty); + if(id->store == Dlocal && id->link != nil){ + /* id->nid always 1 */ + id->offset = id->link->offset; + continue; + } + a = id->ty->align; + if(id->nid > 1){ + for(d = id->next; d != nil && d->nid == 0; d = d->next) + if(d->ty->align > a) + a = d->ty->align; + algn = a; + } + offset = align(offset, a); + id->offset = offset; + offset += id->ty->size; + if(id->nid == 0 && (id->next == nil || id->next->nid != 0)) + offset = align(offset, algn); + } + } + return align(offset, al); +} + +long +idindices(Decl *id) +{ + int i; + + i = 0; + for(; id != nil; id = id->next){ + if(storespace[id->store]){ + usedty(id->ty); + id->offset = i++; + } + } + return i; +} + +int +declconv(Fmt *f) +{ + Decl *d; + char buf[4096], *s; + + d = va_arg(f->args, Decl*); + if(d->sym == nil) + s = "<???>"; + else + s = d->sym->name; + seprint(buf, buf+sizeof(buf), "%s %s", storename[d->store], s); + return fmtstrcpy(f, buf); +} + +int +storeconv(Fmt *f) +{ + Decl *d; + char buf[4096]; + + d = va_arg(f->args, Decl*); + seprint(buf, buf+sizeof(buf), "%s%s", storeart[d->store], storename[d->store]); + return fmtstrcpy(f, buf); +} + +int +dotconv(Fmt *f) +{ + Decl *d; + char buf[4096], *p, *s; + + d = va_arg(f->args, Decl*); + buf[0] = 0; + p = buf; + if(d->dot != nil && !isimpmod(d->dot->sym)){ + s = "."; + if(d->dot->ty != nil && d->dot->ty->kind == Tmodule) + s = "->"; + p = seprint(buf, buf+sizeof(buf), "%D%s", d->dot, s); + } + seprint(p, buf+sizeof(buf), "%s", d->sym->name); + return fmtstrcpy(f, buf); +} + +/* + * merge together two sorted lists, yielding a sorted list + */ +static Decl* +namemerge(Decl *e, Decl *f) +{ + Decl rock, *d; + + d = &rock; + while(e != nil && f != nil){ + if(strcmp(e->sym->name, f->sym->name) <= 0){ + d->next = e; + e = e->next; + }else{ + d->next = f; + f = f->next; + } + d = d->next; + } + if(e != nil) + d->next = e; + else + d->next = f; + return rock.next; +} + +/* + * recursively split lists and remerge them after they are sorted + */ +static Decl* +recnamesort(Decl *d, int n) +{ + Decl *r, *dd; + int i, m; + + if(n <= 1) + return d; + m = n / 2 - 1; + dd = d; + for(i = 0; i < m; i++) + dd = dd->next; + r = dd->next; + dd->next = nil; + return namemerge(recnamesort(d, n / 2), + recnamesort(r, (n + 1) / 2)); +} + +/* + * sort the ids by name + */ +Decl* +namesort(Decl *d) +{ + Decl *dd; + int n; + + n = 0; + for(dd = d; dd != nil; dd = dd->next) + n++; + return recnamesort(d, n); +} + +void +printdecls(Decl *d) +{ + for(; d != nil; d = d->next) + print("%ld: %K %T ref %d\n", d->offset, d, d->ty, d->refs); +} + +void +mergepolydecs(Type *t) +{ + Node *n, *nn; + Decl *id, *ids, *ids1; + + for(n = t->val; n != nil; n = n->right){ + nn = n->left; + for(ids = nn->decl; ids != nil; ids = ids->next){ + id = ids->sym->decl; + if(id == nil){ + undefed(&ids->src, ids->sym); + break; + } + if(id->store != Dtype){ + error(ids->src.start, "%K is not a type", id); + break; + } + if(id->ty->kind != Tpoly){ + error(ids->src.start, "%K is not a polymorphic type", id); + break; + } + if(id->ty->ids != nil) + error(ids->src.start, "%K redefined", id); + pushscope(nil, Sother); + fielddecled(nn->left); + id->ty->ids = popscope(); + for(ids1 = id->ty->ids; ids1 != nil; ids1 = ids1->next){ + ids1->dot = id; + bindtypes(ids1->ty); + if(ids1->ty->kind != Tfn){ + error(ids1->src.start, "only function types expected"); + id->ty->ids = nil; + } + } + } + } + t->val = nil; +} + +static void +adjfnptrs(Decl *d, Decl *polys1, Decl *polys2) +{ + int n; + Decl *id, *idt, *idf, *arg; + + if(debug['U']) + print("adjnptrs %s\n", d->sym->name); + n = 0; + for(id = d->ty->ids; id != nil; id = id->next) + n++; + for(idt = polys1; idt != nil; idt = idt->next) + for(idf = idt->ty->ids; idf != nil; idf = idf->next) + n -= 2; + for(idt = polys2; idt != nil; idt = idt->next) + for(idf = idt->ty->ids; idf != nil; idf = idf->next) + n -= 2; + for(arg = d->ty->ids; --n >= 0; arg = arg->next) + ; + for(idt = polys1; idt != nil; idt = idt->next){ + for(idf = idt->ty->ids; idf != nil; idf = idf->next){ + idf->link = arg; + arg = arg->next->next; + } + } + for(idt = polys2; idt != nil; idt = idt->next){ + for(idf = idt->ty->ids; idf != nil; idf = idf->next){ + idf->link = arg; + arg = arg->next->next; + } + } +} + +static void +addptrs(Decl *polys, Decl** fps, Decl **last, int link, Src *src) +{ + Decl *idt, *idf, *fp; + + if(debug['U']) + print("addptrs\n"); + for(idt = polys; idt != nil; idt = idt->next){ + for(idf = idt->ty->ids; idf != nil; idf = idf->next){ + fp = mkdecl(src, Darg, tany); + fp->sym = idf->sym; + if(link) + idf->link = fp; + if(*fps == nil) + *fps = fp; + else + (*last)->next = fp; + *last = fp; + fp = mkdecl(src, Darg, tint); + fp->sym = idf->sym; + (*last)->next = fp; + *last = fp; + } + } +} + +void +addfnptrs(Decl *d, int link) +{ + Decl *fps, *last, *polys; + + if(debug['U']) + print("addfnptrs %s %d\n", d->sym->name, link); + polys = encpolys(d); + if(d->ty->flags&FULLARGS){ + if(link) + adjfnptrs(d, d->ty->polys, polys); + if(0 && debug['U']){ + for(d = d->ty->ids; d != nil; d = d->next) + print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr); + print("\n"); + } + return; + } + d->ty->flags |= FULLARGS; + fps = last = nil; + addptrs(d->ty->polys, &fps, &last, link, &d->src); + addptrs(polys, &fps, &last, link, &d->src); + for(last = d->ty->ids; last != nil && last->next != nil; last = last->next) + ; + if(last != nil) + last->next = fps; + else + d->ty->ids = fps; + d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD); + if(0 && debug['U']){ + for(d = d->ty->ids; d != nil; d = d->next) + print("%s=%ld(%d) ", d->sym->name, d->offset, tattr[d->ty->kind].isptr); + print("\n"); + } +} + +void +rmfnptrs(Decl *d) +{ + int n; + Decl *id, *idt, *idf; + + if(debug['U']) + print("rmfnptrs %s\n", d->sym->name); + if(!(d->ty->flags&FULLARGS)) + return; + d->ty->flags &= ~FULLARGS; + n = 0; + for(id = d->ty->ids; id != nil; id = id->next) + n++; + for(idt = d->ty->polys; idt != nil; idt = idt->next) + for(idf = idt->ty->ids; idf != nil; idf = idf->next) + n -= 2; + for(idt = encpolys(d); idt != nil; idt = idt->next) + for(idf = idt->ty->ids; idf != nil; idf = idf->next) + n -= 2; + if(n == 0){ + d->ty->ids = nil; + return; + } + for(id = d->ty->ids; --n > 0; id = id->next) + ; + id->next = nil; + d->offset = idoffsets(d->ty->ids, MaxTemp, IBY2WD); +} + +int +local(Decl *d) +{ + for(d = d->dot; d != nil; d = d->dot) + if(d->store == Dtype && d->ty->kind == Tmodule) + return 0; + return 1; +} + +Decl* +module(Decl *d) +{ + for(d = d->dot; d != nil; d = d->dot) + if(d->store == Dtype && d->ty->kind == Tmodule) + return d; + return nil; +} + +Decl* +outerpolys(Node *n) +{ + Decl *d; + + if(n->op == Odot){ + d = n->right->decl; + if(d == nil) + fatal("decl() outeradt nil"); + d = d->dot; + if(d != nil && d->store == Dtype && d->ty->kind == Tadt) + return d->ty->polys; + } + return nil; +} + +Decl* +encpolys(Decl *d) +{ + if((d = d->dot) == nil) + return nil; + return d->ty->polys; +} + +Decl* +fnlookup(Sym *s, Type *t, Node **m) +{ + Decl *id; + Node *mod; + + id = nil; + mod = nil; + if(t->kind == Tpoly || t->kind == Tmodule) + id = namedot(t->ids, s); + else if(t->kind == Tref){ + t = t->tof; + if(t->kind == Tadt){ + id = namedot(t->ids, s); + if(t->decl != nil && t->decl->timport != nil) + mod = t->decl->timport->eimport; + } + else if(t->kind == Tadtpick){ + id = namedot(t->ids, s); + if(t->decl != nil && t->decl->timport != nil) + mod = t->decl->timport->eimport; + t = t->decl->dot->ty; + if(id == nil) + id = namedot(t->ids, s); + if(t->decl != nil && t->decl->timport != nil) + mod = t->decl->timport->eimport; + } + } + if(id == nil){ + id = lookup(s); + if(id != nil) + mod = id->eimport; + } + if(m != nil) + *m = mod; + return id; +} + +int +isimpmod(Sym *s) +{ + Decl *d; + + for(d = impmods; d != nil; d = d->next) + if(d->sym == s) + return 1; + return 0; +} + +int +dequal(Decl *d1, Decl *d2, int full) +{ + return d1->sym == d2->sym && + d1->store == d2->store && + d1->implicit == d2->implicit && + d1->cyc == d2->cyc && + (!full || tequal(d1->ty, d2->ty)) && + (!full || d1->store == Dfn || sametree(d1->init, d2->init)); +} + +static int +tzero(Type *t) +{ + return t->kind == Texception || tmustzero(t); +} + +static int +isptr(Type *t) +{ + return t->kind == Texception || tattr[t->kind].isptr; +} + +/* can d share the same stack location as another local ? */ +void +shareloc(Decl *d) +{ + int z; + Type *t, *tt; + Decl *dd, *res; + + if(d->store != Dlocal || d->nid != 1) + return; + t = d->ty; + res = nil; + for(dd = fndecls; dd != nil; dd = dd->next){ + if(d == dd) + fatal("d==dd in shareloc"); + if(dd->store != Dlocal || dd->nid != 1 || dd->link != nil || dd->tref != 0) + continue; + tt = dd->ty; + if(t->size != tt->size || t->align != tt->align) + continue; + z = tzero(t)+tzero(tt); + if(z > 0) + continue; /* for now */ + if(t == tt || tequal(t, tt)) + res = dd; + else{ + if(z == 1) + continue; + if(z == 0 || isptr(t) || isptr(tt) || mktdesc(t) == mktdesc(tt)) + res = dd; + } + if(res != nil){ + /* print("%L %K share %L %K\n", d->src.start, d, res->src.start, res); */ + d->link = res; + res->tref = 1; + return; + } + } + return; +} + +static void +freeloc(Decl *d) +{ + if(d->link != nil) + d->link->tref = 0; +} diff --git a/limbo/dis.c b/limbo/dis.c new file mode 100644 index 00000000..84f708ba --- /dev/null +++ b/limbo/dis.c @@ -0,0 +1,638 @@ +#include "limbo.h" + +static void disbig(long, Long); +static void disbyte(long, int); +static void disbytes(long, void*, int); +static void disdatum(long, Node*); +static void disflush(int, long, long); +static void disint(long, long); +static void disreal(long, Real); +static void disstring(long, Sym*); + +static uchar *cache; +static int ncached; +static int ndatum; +static int startoff; +static int lastoff; +static int lastkind; +static int lencache; + +void +discon(long val) +{ + if(val >= -64 && val <= 63) { + Bputc(bout, val & ~0x80); + return; + } + if(val >= -8192 && val <= 8191) { + Bputc(bout, ((val>>8) & ~0xC0) | 0x80); + Bputc(bout, val); + return; + } + if(val < 0 && ((val >> 29) & 0x7) != 7 + || val > 0 && (val >> 29) != 0) + fatal("overflow in constant 0x%lux\n", val); + Bputc(bout, (val>>24) | 0xC0); + Bputc(bout, val>>16); + Bputc(bout, val>>8); + Bputc(bout, val); +} + +void +disword(long w) +{ + Bputc(bout, w >> 24); + Bputc(bout, w >> 16); + Bputc(bout, w >> 8); + Bputc(bout, w); +} + +void +disdata(int kind, long n) +{ + if(n < DMAX && n != 0) + Bputc(bout, DBYTE(kind, n)); + else{ + Bputc(bout, DBYTE(kind, 0)); + discon(n); + } +} + +#define NAMELEN 64 + +void +dismod(Decl *m) +{ + char name[8*NAMELEN]; + ulong fileoff; + + fileoff = Boffset(bout); + strncpy(name, m->sym->name, NAMELEN); + name[NAMELEN-1] = '\0'; + Bwrite(bout, name, strlen(name)+1); + for(m = m->ty->tof->ids; m != nil; m = m->next){ + switch(m->store){ + case Dglobal: + discon(-1); + discon(-1); + disword(sign(m)); + Bprint(bout, ".mp"); + Bputc(bout, '\0'); + break; + case Dfn: +if(debug['v']) print("Dfn: %s %d %p\n", m->sym->name, m->refs, m); + discon(m->pc->pc); + discon(m->desc->id); + disword(sign(m)); + if(m->dot->ty->kind == Tadt) + Bprint(bout, "%s.", m->dot->sym->name); + Bprint(bout, "%s", m->sym->name); + Bputc(bout, '\0'); + break; + default: + fatal("unknown kind %K in dismod", m); + break; + } + } + if(debug['s']) + print("%ld linkage bytes start %ld\n", Boffset(bout) - fileoff, fileoff); +} + +void +dispath(void) +{ + char name[8*NAMELEN], *sp; + + sp = srcpath(name, 8*NAMELEN); + Bwrite(bout, sp, strlen(sp)+1); +} + +void +disentry(Decl *e) +{ + if(e == nil){ + discon(-1); + discon(-1); + return; + } + discon(e->pc->pc); + discon(e->desc->id); +} + +void +disdesc(Desc *d) +{ + ulong fileoff; + + fileoff = Boffset(bout); + for(; d != nil; d = d->next){ + discon(d->id); + discon(d->size); + discon(d->nmap); + Bwrite(bout, d->map, d->nmap); + } + if(debug['s']) + print("%ld type descriptor bytes start %ld\n", Boffset(bout) - fileoff, fileoff); +} + +void +disvar(long size, Decl *d) +{ + ulong fileoff; + + fileoff = Boffset(bout); + USED(size); + + lastkind = -1; + ncached = 0; + ndatum = 0; + + for(; d != nil; d = d->next) + if(d->store == Dglobal && d->init != nil) + disdatum(d->offset, d->init); + + disflush(-1, -1, 0); + + Bputc(bout, 0); + + if(debug['s']) + print("%ld data bytes start %ld\n", Boffset(bout) - fileoff, fileoff); +} + +void +disldt(long size, Decl *ds) +{ + int m; + Decl *d, *id; + Sym *s; + Node *n; + + if(0){ + discon(size); + disvar(size, ds); + return; + } + + m = 0; + for(d = ds; d != nil; d = d->next) + if(d->store == Dglobal && d->init != nil) + m++; + discon(m); + for(d = ds; d != nil; d = d->next){ + if(d->store == Dglobal && d->init != nil){ + n = d->init; + if(n->ty->kind != Tiface) + nerror(n, "disldt: not Tiface"); + discon(n->val); + for(id = n->decl->ty->ids; id != nil; id = id->next){ + disword(sign(id)); + if(id->dot->ty->kind == Tadt){ + s = id->dot->sym; + Bprint(bout, "%s", s->name); + Bputc(bout, '.'); + } + s = id->sym; + Bprint(bout, "%s", s->name); + Bputc(bout, 0); + } + } + } + discon(0); +} + +static void +disdatum(long offset, Node *n) +{ + Node *elem, *wild; + Case *c; + Label *lab; + Decl *id; + Sym *s; + long e, last, esz; + int i; + + switch(n->ty->kind){ + case Tbyte: + disbyte(offset, n->val); + break; + case Tint: + case Tfix: + disint(offset, n->val); + break; + case Tbig: + disbig(offset, n->val); + break; + case Tstring: + disstring(offset, n->decl->sym); + break; + case Treal: + disreal(offset, n->rval); + break; + case Tadt: + case Tadtpick: + case Ttuple: + id = n->ty->ids; + for(n = n->left; n != nil; n = n->right){ + disdatum(offset + id->offset, n->left); + id = id->next; + } + break; + case Tany: + break; + case Tcase: + c = n->ty->cse; + disint(offset, c->nlab); + offset += IBY2WD; + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + disint(offset, lab->start->val); + offset += IBY2WD; + disint(offset, lab->stop->val+1); + offset += IBY2WD; + disint(offset, lab->inst->pc); + offset += IBY2WD; + } + disint(offset, c->iwild ? c->iwild->pc : -1); + break; + case Tcasel: + c = n->ty->cse; + disint(offset, c->nlab); + offset += 2*IBY2WD; + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + disbig(offset, lab->start->val); + offset += IBY2LG; + disbig(offset, lab->stop->val+1); + offset += IBY2LG; + disint(offset, lab->inst->pc); + offset += 2*IBY2WD; + } + disint(offset, c->iwild ? c->iwild->pc : -1); + break; + case Tcasec: + c = n->ty->cse; + disint(offset, c->nlab); + offset += IBY2WD; + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + disstring(offset, lab->start->decl->sym); + offset += IBY2WD; + if(lab->stop != lab->start) + disstring(offset, lab->stop->decl->sym); + offset += IBY2WD; + disint(offset, lab->inst->pc); + offset += IBY2WD; + } + disint(offset, c->iwild ? c->iwild->pc : -1); + break; + case Tgoto: + c = n->ty->cse; + disint(offset, n->ty->size/IBY2WD-1); + offset += IBY2WD; + for(i = 0; i < c->nlab; i++){ + disint(offset, c->labs[i].inst->pc); + offset += IBY2WD; + } + if(c->iwild != nil) + disint(offset, c->iwild->pc); + break; + case Tarray: + disflush(-1, -1, 0); + disdata(DEFA, 1); /* 1 is ignored */ + discon(offset); + disword(n->ty->tof->decl->desc->id); + disword(n->left->val); + + if(n->right == nil) + break; + + disdata(DIND, 1); /* 1 is ignored */ + discon(offset); + disword(0); + + c = n->right->ty->cse; + wild = nil; + if(c->wild != nil) + wild = c->wild->right; + last = 0; + esz = n->ty->tof->size; + for(i = 0; i < c->nlab; i++){ + e = c->labs[i].start->val; + if(wild != nil){ + for(; last < e; last++) + disdatum(esz * last, wild); + } + last = e; + e = c->labs[i].stop->val; + elem = c->labs[i].node->right; + for(; last <= e; last++) + disdatum(esz * last, elem); + } + if(wild != nil) + for(e = n->left->val; last < e; last++) + disdatum(esz * last, wild); + + disflush(-1, -1, 0); + disdata(DAPOP, 1); /* 1 is ignored */ + discon(0); + + break; + case Tiface: + disint(offset, n->val); + offset += IBY2WD; + for(id = n->decl->ty->ids; id != nil; id = id->next){ + offset = align(offset, IBY2WD); + disint(offset, sign(id)); + offset += IBY2WD; + + if(id->dot->ty->kind == Tadt){ + s = id->dot->sym; + disbytes(offset, s->name, s->len); + offset += s->len; + disbyte(offset, '.'); + offset++; + } + s = id->sym; + disbytes(offset, s->name, s->len); + offset += s->len; + disbyte(offset, 0); + offset++; + } + break; + default: + nerror(n, "can't dis global %n", n); + break; + } +} + +void +disexc(Except *es) +{ + int i, n; + Decl *d; + Except *e; + Case *c; + Label *lab; + + n = 0; + for(e = es; e != nil; e = e->next) + if(e->p1->reach || e->p2->reach) + n++; + discon(n); + for(e = es; e != nil; e = e->next){ + if(!e->p1->reach && !e->p2->reach) + continue; + c = e->c; + discon(e->d->offset); + discon(getpc(e->p1)); + discon(getpc(e->p2)); + if(e->desc) + discon(e->desc->id); + else + discon(-1); + discon(c->nlab|(e->ne<<16)); + for(i = 0; i < c->nlab; i++){ + lab = &c->labs[i]; + d = lab->start->decl; + if(lab->start->ty->kind == Texception) + d = d->init->decl; + Bprint(bout, "%s", d->sym->name); + Bputc(bout, '\0'); + discon(lab->inst->pc); + } + if(c->iwild == nil) + discon(-1); + else + discon(c->iwild->pc); + } + discon(0); +} + +static void +disbyte(long off, int v) +{ + disflush(DEFB, off, 1); + cache[ncached++] = v; + ndatum++; +} + +static void +disbytes(long off, void *v, int n) +{ + disflush(DEFB, off, n); + memmove(&cache[ncached], v, n); + ncached += n; + ndatum += n; +} + +static void +disint(long off, long v) +{ + disflush(DEFW, off, IBY2WD); + cache[ncached++] = v >> 24; + cache[ncached++] = v >> 16; + cache[ncached++] = v >> 8; + cache[ncached++] = v; + ndatum++; +} + +static void +disbig(long off, Long v) +{ + ulong iv; + + disflush(DEFL, off, IBY2LG); + iv = v >> 32; + cache[ncached++] = iv >> 24; + cache[ncached++] = iv >> 16; + cache[ncached++] = iv >> 8; + cache[ncached++] = iv; + iv = v; + cache[ncached++] = iv >> 24; + cache[ncached++] = iv >> 16; + cache[ncached++] = iv >> 8; + cache[ncached++] = iv; + ndatum++; +} + +static void +disreal(long off, Real v) +{ + ulong bv[2]; + ulong iv; + + disflush(DEFF, off, IBY2LG); + dtocanon(v, bv); + iv = bv[0]; + cache[ncached++] = iv >> 24; + cache[ncached++] = iv >> 16; + cache[ncached++] = iv >> 8; + cache[ncached++] = iv; + iv = bv[1]; + cache[ncached++] = iv >> 24; + cache[ncached++] = iv >> 16; + cache[ncached++] = iv >> 8; + cache[ncached++] = iv; + ndatum++; +} + +static void +disstring(long offset, Sym *sym) +{ + disflush(-1, -1, 0); + disdata(DEFS, sym->len); + discon(offset); + Bwrite(bout, sym->name, sym->len); +} + +static void +disflush(int kind, long off, long size) +{ + if(kind != lastkind || off != lastoff){ + if(lastkind != -1 && ncached){ + disdata(lastkind, ndatum); + discon(startoff); + Bwrite(bout, cache, ncached); + } + startoff = off; + lastkind = kind; + ncached = 0; + ndatum = 0; + } + lastoff = off + size; + while(kind >= 0 && ncached + size >= lencache){ + lencache = ncached+1024; + cache = reallocmem(cache, lencache); + } +} + +static int dismode[Aend] = { + /* Aimm */ AIMM, + /* Amp */ AMP, + /* Ampind */ AMP|AIND, + /* Afp */ AFP, + /* Afpind */ AFP|AIND, + /* Apc */ AIMM, + /* Adesc */ AIMM, + /* Aoff */ AIMM, + /* Anoff */ AIMM, + /* Aerr */ AXXX, + /* Anone */ AXXX, + /* Aldt */ AIMM, +}; + +static int disregmode[Aend] = { + /* Aimm */ AXIMM, + /* Amp */ AXINM, + /* Ampind */ AXNON, + /* Afp */ AXINF, + /* Afpind */ AXNON, + /* Apc */ AXIMM, + /* Adesc */ AXIMM, + /* Aoff */ AXIMM, + /* Anoff */ AXIMM, + /* Aerr */ AXNON, + /* Anone */ AXNON, + /* Aldt */ AXIMM, +}; + +enum +{ + MAXCON = 4, + MAXADDR = 2*MAXCON, + MAXINST = 3*MAXADDR+2, + NIBUF = 1024 +}; + +static uchar *ibuf; +static int nibuf; + +void +disinst(Inst *in) +{ + ulong fileoff; + + fileoff = Boffset(bout); + ibuf = allocmem(NIBUF); + nibuf = 0; + for(; in != nil; in = in->next){ + if(in->op == INOOP) + continue; + if(nibuf >= NIBUF-MAXINST){ + Bwrite(bout, ibuf, nibuf); + nibuf = 0; + } + ibuf[nibuf++] = in->op; + ibuf[nibuf++] = SRC(dismode[in->sm]) | DST(dismode[in->dm]) | disregmode[in->mm]; + if(in->mm != Anone) + disaddr(in->mm, &in->m); + if(in->sm != Anone) + disaddr(in->sm, &in->s); + if(in->dm != Anone) + disaddr(in->dm, &in->d); + } + if(nibuf > 0) + Bwrite(bout, ibuf, nibuf); + free(ibuf); + ibuf = nil; + + if(debug['s']) + print("%ld instruction bytes start %ld\n", Boffset(bout) - fileoff, fileoff); +} + +void +disaddr(int m, Addr *a) +{ + long val; + + val = 0; + switch(m){ + case Anone: + case Aerr: + default: + break; + case Aimm: + case Apc: + case Adesc: + val = a->offset; + break; + case Aoff: + val = a->decl->iface->offset; + break; + case Anoff: + val = -(a->decl->iface->offset+1); + break; + case Afp: + case Amp: + case Aldt: + val = a->reg; + break; + case Afpind: + case Ampind: + disbcon(a->reg); + val = a->offset; + break; + } + disbcon(val); +} + +void +disbcon(long val) +{ + if(val >= -64 && val <= 63){ + ibuf[nibuf++] = val & ~0x80; + return; + } + if(val >= -8192 && val <= 8191){ + ibuf[nibuf++] = val>>8 & ~0xC0 | 0x80; + ibuf[nibuf++] = val; + return; + } + if(val < 0 && ((val >> 29) & 7) != 7 + || val > 0 && (val >> 29) != 0) + fatal("overflow in constant 16r%lux", val); + ibuf[nibuf++] = val>>24 | 0xC0; + ibuf[nibuf++] = val>>16; + ibuf[nibuf++] = val>>8; + ibuf[nibuf++] = val; +} diff --git a/limbo/dtocanon.c b/limbo/dtocanon.c new file mode 100644 index 00000000..8a23283c --- /dev/null +++ b/limbo/dtocanon.c @@ -0,0 +1,35 @@ +#include "limbo.h" + +void +dtocanon(double f, ulong v[]) +{ + union { double d; ulong ul[2]; } a; + + a.d = 1.; + if(a.ul[0]){ + a.d = f; + v[0] = a.ul[0]; + v[1] = a.ul[1]; + }else{ + a.d = f; + v[0] = a.ul[1]; + v[1] = a.ul[0]; + } +} + +double +canontod(ulong v[2]) +{ + union { double d; unsigned long ul[2]; } a; + + a.d = 1.; + if(a.ul[0]) { + a.ul[0] = v[0]; + a.ul[1] = v[1]; + } + else { + a.ul[1] = v[0]; + a.ul[0] = v[1]; + } + return a.d; +} diff --git a/limbo/ecom.c b/limbo/ecom.c new file mode 100644 index 00000000..e19b51c0 --- /dev/null +++ b/limbo/ecom.c @@ -0,0 +1,2560 @@ +#include "limbo.h" + +static Node* putinline(Node*); +static void fpcall(Src*, int, Node*, Node*); + +void +optabinit(void) +{ + int i; + + for(i = 0; setisbyteinst[i] >= 0; i++) + isbyteinst[setisbyteinst[i]] = 1; + + for(i = 0; setisused[i] >= 0; i++) + isused[setisused[i]] = 1; + + for(i = 0; setsideeffect[i] >= 0; i++) + sideeffect[setsideeffect[i]] = 1; + + opind[Tbyte] = 1; + opind[Tint] = 2; + opind[Tbig] = 3; + opind[Treal] = 4; + opind[Tstring] = 5; + opind[Tfix] = 6; + + opcommute[Oeq] = Oeq; + opcommute[Oneq] = Oneq; + opcommute[Olt] = Ogt; + opcommute[Ogt] = Olt; + opcommute[Ogeq] = Oleq; + opcommute[Oleq] = Ogeq; + opcommute[Oadd] = Oadd; + opcommute[Omul] = Omul; + opcommute[Oxor] = Oxor; + opcommute[Oor] = Oor; + opcommute[Oand] = Oand; + + oprelinvert[Oeq] = Oneq; + oprelinvert[Oneq] = Oeq; + oprelinvert[Olt] = Ogeq; + oprelinvert[Ogt] = Oleq; + oprelinvert[Ogeq] = Olt; + oprelinvert[Oleq] = Ogt; + + isrelop[Oeq] = 1; + isrelop[Oneq] = 1; + isrelop[Olt] = 1; + isrelop[Oleq] = 1; + isrelop[Ogt] = 1; + isrelop[Ogeq] = 1; + isrelop[Oandand] = 1; + isrelop[Ooror] = 1; + isrelop[Onot] = 1; + + precasttab[Tstring][Tbyte] = tint; + precasttab[Tbyte][Tstring] = tint; + precasttab[Treal][Tbyte] = tint; + precasttab[Tbyte][Treal] = tint; + precasttab[Tbig][Tbyte] = tint; + precasttab[Tbyte][Tbig] = tint; + precasttab[Tfix][Tbyte] = tint; + precasttab[Tbyte][Tfix] = tint; + precasttab[Tbig][Tfix] = treal; + precasttab[Tfix][Tbig] = treal; + precasttab[Tstring][Tfix] = treal; + precasttab[Tfix][Tstring] = treal; + + casttab[Tint][Tint] = IMOVW; + casttab[Tbig][Tbig] = IMOVL; + casttab[Treal][Treal] = IMOVF; + casttab[Tbyte][Tbyte] = IMOVB; + casttab[Tstring][Tstring] = IMOVP; + casttab[Tfix][Tfix] = ICVTXX; /* never same type */ + + casttab[Tint][Tbyte] = ICVTWB; + casttab[Tint][Treal] = ICVTWF; + casttab[Tint][Tstring] = ICVTWC; + casttab[Tint][Tfix] = ICVTXX; + casttab[Tbyte][Tint] = ICVTBW; + casttab[Treal][Tint] = ICVTFW; + casttab[Tstring][Tint] = ICVTCW; + casttab[Tfix][Tint] = ICVTXX; + + casttab[Tint][Tbig] = ICVTWL; + casttab[Treal][Tbig] = ICVTFL; + casttab[Tstring][Tbig] = ICVTCL; + casttab[Tbig][Tint] = ICVTLW; + casttab[Tbig][Treal] = ICVTLF; + casttab[Tbig][Tstring] = ICVTLC; + + casttab[Treal][Tstring] = ICVTFC; + casttab[Tstring][Treal] = ICVTCF; + + casttab[Treal][Tfix] = ICVTFX; + casttab[Tfix][Treal] = ICVTXF; + + casttab[Tstring][Tarray] = ICVTCA; + casttab[Tarray][Tstring] = ICVTAC; + + /* + * placeholders; fixed in precasttab + */ + casttab[Tbyte][Tstring] = 0xff; + casttab[Tstring][Tbyte] = 0xff; + casttab[Tbyte][Treal] = 0xff; + casttab[Treal][Tbyte] = 0xff; + casttab[Tbyte][Tbig] = 0xff; + casttab[Tbig][Tbyte] = 0xff; + casttab[Tfix][Tbyte] = 0xff; + casttab[Tbyte][Tfix] = 0xff; + casttab[Tfix][Tbig] = 0xff; + casttab[Tbig][Tfix] = 0xff; + casttab[Tfix][Tstring] = 0xff; + casttab[Tstring][Tfix] = 0xff; +} + +/* + * global variable and constant initialization checking + */ +int +vcom(Decl *ids) +{ + Decl *v; + int ok; + + ok = 1; + for(v = ids; v != nil; v = v->next) + ok &= varcom(v); + for(v = ids; v != nil; v = v->next) + v->init = simplify(v->init); + return ok; +} + +Node* +simplify(Node *n) +{ + if(n == nil) + return nil; + if(debug['F']) + print("simplify %n\n", n); + n = efold(rewrite(n)); + if(debug['F']) + print("simplified %n\n", n); + return n; +} + +static int +isfix(Node *n) +{ + if(n->ty->kind == Tint || n->ty->kind == Tfix){ + if(n->op == Ocast) + return n->left->ty->kind == Tint || n->left->ty->kind == Tfix; + return 1; + } + return 0; +} + +/* + * rewrite an expression to make it easiser to compile, + * or give the correct results + */ +Node* +rewrite(Node *n) +{ + Long v; + Type *t; + Decl *d; + Node *nn, *left, *right; + + if(n == nil) + return nil; + + left = n->left; + right = n->right; + + /* + * rewrites + */ + switch(n->op){ + case Oname: + d = n->decl; + if(d->importid != nil){ + left = mkbin(Omdot, dupn(1, &n->src, d->eimport), mkdeclname(&n->src, d->importid)); + left->ty = n->ty; + return rewrite(left); + } + if((t = n->ty)->kind == Texception){ + if(t->cons) + fatal("cons in rewrite Oname"); + n = mkbin(Oadd, n, mkconst(&n->src, 2*IBY2WD)); + n = mkunary(Oind, n); + n->ty = t; + n->left->ty = n->left->left->ty = tint; + return rewrite(n); + } + break; + case Odas: + n->op = Oas; + return rewrite(n); + case Oneg: + n->left = rewrite(left); + if(n->ty == treal) + break; + left = n->left; + n->right = left; + n->left = mkconst(&n->src, 0); + n->left->ty = n->ty; + n->op = Osub; + break; + case Ocomp: + v = 0; + v = ~v; + n->right = mkconst(&n->src, v); + n->right->ty = n->ty; + n->left = rewrite(left); + n->op = Oxor; + break; + case Oinc: + case Odec: + case Opreinc: + case Opredec: + n->left = rewrite(left); + switch(n->ty->kind){ + case Treal: + n->right = mkrconst(&n->src, 1.0); + break; + case Tint: + case Tbig: + case Tbyte: + case Tfix: + n->right = mkconst(&n->src, 1); + n->right->ty = n->ty; + break; + default: + fatal("can't rewrite inc/dec %n", n); + break; + } + if(n->op == Opreinc) + n->op = Oaddas; + else if(n->op == Opredec) + n->op = Osubas; + break; + case Oslice: + if(right->left->op == Onothing) + right->left = mkconst(&right->left->src, 0); + n->left = rewrite(left); + n->right = rewrite(right); + break; + case Oindex: + n->op = Oindx; + n->left = rewrite(left); + n->right = rewrite(right); + n = mkunary(Oind, n); + n->ty = n->left->ty; + n->left->ty = tint; + break; + case Oload: + n->right = mkn(Oname, nil, nil); + n->right->src = n->left->src; + n->right->decl = n->ty->tof->decl; + n->right->ty = n->ty; + n->left = rewrite(left); + break; + case Ocast: + if(left->ty->kind == Texception){ + n = rewrite(left); + break; + } + n->op = Ocast; + t = precasttab[left->ty->kind][n->ty->kind]; + if(t != nil){ + n->left = mkunary(Ocast, left); + n->left->ty = t; + return rewrite(n); + } + n->left = rewrite(left); + break; + case Oraise: + if(left->ty == tstring) + {} + else if(!left->ty->cons) + break; + else if(left->op != Ocall || left->left->ty->kind == Tfn){ + left = mkunary(Ocall, left); + left->ty = left->left->ty; + } + n->left = rewrite(left); + break; + case Ocall: + t = left->ty; + if(t->kind == Tref) + t = t->tof; + if(t->kind == Tfn){ +if(debug['U']) print("call %n\n", left); + if(left->ty->kind == Tref){ /* call by function reference */ + n->left = mkunary(Oind, left); + n->left->ty = t; + return rewrite(n); + } + d = nil; + if(left->op == Oname) + d = left->decl; + else if(left->op == Omdot && left->right->op == Odot) + d = left->right->right->decl; + else if(left->op == Omdot || left->op == Odot) + d = left->right->decl; + else if(left->op != Oind) + fatal("cannot deal with call %n in rewrite", n); + if(ispoly(d)) + addfnptrs(d, 0); + n->left = rewrite(left); + if(right != nil) + n->right = rewrite(right); + if(d != nil && d->caninline == 1) + n = simplify(putinline(n)); + break; + } + switch(n->ty->kind){ + case Tref: + n = mkunary(Oref, n); + n->ty = n->left->ty; + n->left->ty = n->left->ty->tof; + n->left->left->ty = n->left->ty; + return rewrite(n); + case Tadt: + n->op = Otuple; + n->right = nil; + if(n->ty->tags != nil){ + n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag)); + if(right != nil){ + nn->right = right; + nn->src.stop = right->src.stop; + } + n->ty = left->right->decl->ty->tof; + }else + n->left = right; + return rewrite(n); + case Tadtpick: + n->op = Otuple; + n->right = nil; + n->left = nn = mkunary(Oseq, mkconst(&n->src, left->right->decl->tag)); + if(right != nil){ + nn->right = right; + nn->src.stop = right->src.stop; + } + n->ty = left->right->decl->ty->tof; + return rewrite(n); + case Texception: + if(!n->ty->cons) + return n->left; + if(left->op == Omdot){ + left->right->ty = left->ty; + left = left->right; + } + n->op = Otuple; + n->right = nil; + n->left = nn = mkunary(Oseq, left->decl->init); + nn->right = mkunary(Oseq, mkconst(&n->src, 0)); + nn->right->right = right; + n->ty = mkexbasetype(n->ty); + n = mkunary(Oref, n); + n->ty = internaltype(mktype(&n->src.start, &n->src.stop, Tref, t, nil)); + return rewrite(n); + default: + fatal("can't deal with %n in rewrite/Ocall", n); + break; + } + break; + case Omdot: + /* + * what about side effects from left? + */ + d = right->decl; + switch(d->store){ + case Dfn: + n->left = rewrite(left); + if(right->op == Odot){ + n->right = dupn(1, &left->src, right->right); + n->right->ty = d->ty; + } + break; + case Dconst: + case Dtag: + case Dtype: + /* handled by fold */ + return n; + case Dglobal: + right->op = Oconst; + right->val = d->offset; + right->ty = tint; + + n->left = left = mkunary(Oind, left); + left->ty = tint; + n->op = Oadd; + n = mkunary(Oind, n); + n->ty = n->left->ty; + n->left->ty = tint; + n->left = rewrite(n->left); + return n; + case Darg: + return n; + default: + fatal("can't deal with %n in rewrite/Omdot", n); + break; + } + break; + case Odot: + /* + * what about side effects from left? + */ + d = right->decl; + switch(d->store){ + case Dfn: + if(right->left != nil){ + n = mkbin(Omdot, dupn(1, &left->src, right->left), right); + right->left = nil; + n->ty = d->ty; + return rewrite(n); + } + if(left->ty->kind == Tpoly){ + n = mkbin(Omdot, mkdeclname(&left->src, d->link), mkdeclname(&left->src, d->link->next)); + n->ty = d->ty; + return rewrite(n); + } + n->op = Oname; + n->decl = d; + n->right = nil; + n->left = nil; + return n; + case Dconst: + case Dtag: + case Dtype: + /* handled by fold */ + return n; + } + if(istuple(left)) + return n; /* handled by fold */ + right->op = Oconst; + right->val = d->offset; + right->ty = tint; + + if(left->ty->kind != Tref){ + n->left = mkunary(Oadr, left); + n->left->ty = tint; + } + n->op = Oadd; + n = mkunary(Oind, n); + n->ty = n->left->ty; + n->left->ty = tint; + n->left = rewrite(n->left); + return n; + case Oadr: + left = rewrite(left); + n->left = left; + if(left->op == Oind) + return left->left; + break; + case Otagof: + if(n->decl == nil){ + n->op = Oind; + return rewrite(n); + } + return n; + case Omul: + case Odiv: + left = n->left = rewrite(left); + right = n->right = rewrite(right); + if(n->ty->kind == Tfix && isfix(left) && isfix(right)){ + if(left->op == Ocast && tequal(left->ty, n->ty)) + n->left = left->left; + if(right->op == Ocast && tequal(right->ty, n->ty)) + n->right = right->left; + } + break; + case Oself: + if(newfnptr) + return n; + if(selfdecl == nil){ + d = selfdecl = mkids(&n->src, enter(strdup(".self"), 5), tany, nil); + installids(Dglobal, d); + d->refs++; + } + nn = mkn(Oload, nil, nil); + nn->src = n->src; + nn->left = mksconst(&n->src, enterstring(strdup("$self"), 5)); + nn->ty = impdecl->ty; + usetype(nn->ty); + usetype(nn->ty->tof); + nn = rewrite(nn); + nn->op = Oself; + return nn; + case Ofnptr: + if(n->flags == 0){ + /* module */ + if(left == nil) + left = mkn(Oself, nil, nil); + return rewrite(left); + } + right->flags = n->flags; + n = right; + d = n->decl; + if(n->flags == FNPTR2){ + if(left != nil && left->op != Oname) + fatal("not Oname for addiface"); + if(left == nil){ + addiface(nil, d); + if(newfnptr) + n->flags |= FNPTRN; + } + else + addiface(left->decl, d); /* is this necessary ? */ + n->ty = tint; + return n; + } + if(n->flags == FNPTRA){ + n = mkdeclname(&n->src, d->link); + n->ty = tany; + return n; + } + if(n->flags == (FNPTRA|FNPTR2)){ + n = mkdeclname(&n->src, d->link->next); + n->ty = tint; + return n; + } + break; + case Ochan: + if(left == nil) + left = n->left = mkconst(&n->src, 0); + n->left = rewrite(left); + break; + default: + n->left = rewrite(left); + n->right = rewrite(right); + break; + } + + return n; +} + +/* + * label a node with sethi-ullman numbers and addressablity + * genaddr interprets addable to generate operands, + * so a change here mandates a change there. + * + * addressable: + * const Rconst $value may also be Roff or Rdesc or Rnoff + * Asmall(local) Rreg value(FP) + * Asmall(global) Rmreg value(MP) + * ind(Rareg) Rreg value(FP) + * ind(Ramreg) Rmreg value(MP) + * ind(Rreg) Radr *value(FP) + * ind(Rmreg) Rmadr *value(MP) + * ind(Raadr) Radr value(value(FP)) + * ind(Ramadr) Rmadr value(value(MP)) + * + * almost addressable: + * adr(Rreg) Rareg + * adr(Rmreg) Ramreg + * add(const, Rareg) Rareg + * add(const, Ramreg) Ramreg + * add(const, Rreg) Raadr + * add(const, Rmreg) Ramadr + * add(const, Raadr) Raadr + * add(const, Ramadr) Ramadr + * adr(Radr) Raadr + * adr(Rmadr) Ramadr + * + * strangely addressable: + * fn Rpc + * mdot(module,exp) Rmpc + */ +Node* +sumark(Node *n) +{ + Node *left, *right; + long v; + + if(n == nil) + return nil; + + n->temps = 0; + n->addable = Rcant; + + left = n->left; + right = n->right; + if(left != nil){ + sumark(left); + n->temps = left->temps; + } + if(right != nil){ + sumark(right); + if(right->temps == n->temps) + n->temps++; + else if(right->temps > n->temps) + n->temps = right->temps; + } + + switch(n->op){ + case Oadr: + switch(left->addable){ + case Rreg: + n->addable = Rareg; + break; + case Rmreg: + n->addable = Ramreg; + break; + case Radr: + n->addable = Raadr; + break; + case Rmadr: + n->addable = Ramadr; + break; + } + break; + case Oind: + switch(left->addable){ + case Rreg: + n->addable = Radr; + break; + case Rmreg: + n->addable = Rmadr; + break; + case Rareg: + n->addable = Rreg; + break; + case Ramreg: + n->addable = Rmreg; + break; + case Raadr: + n->addable = Radr; + break; + case Ramadr: + n->addable = Rmadr; + break; + } + break; + case Oname: + switch(n->decl->store){ + case Darg: + case Dlocal: + n->addable = Rreg; + break; + case Dglobal: + n->addable = Rmreg; + if(LDT && n->decl->ty->kind == Tiface) + n->addable = Rldt; + break; + case Dtype: + /* + * check for inferface to load + */ + if(n->decl->ty->kind == Tmodule) + n->addable = Rmreg; + break; + case Dfn: + if(n->flags & FNPTR){ + if(n->flags == FNPTR2) + n->addable = Roff; + else if(n->flags == FNPTR2|FNPTRN) + n->addable = Rnoff; + } + else + n->addable = Rpc; + break; + default: + fatal("cannot deal with %K in Oname in %n", n->decl, n); + break; + } + break; + case Omdot: + n->addable = Rmpc; + break; + case Oconst: + switch(n->ty->kind){ + case Tint: + case Tfix: + v = n->val; + if(v < 0 && ((v >> 29) & 0x7) != 7 + || v > 0 && (v >> 29) != 0){ + n->decl = globalconst(n); + n->addable = Rmreg; + }else + n->addable = Rconst; + break; + case Tbig: + n->decl = globalBconst(n); + n->addable = Rmreg; + break; + case Tbyte: + n->decl = globalbconst(n); + n->addable = Rmreg; + break; + case Treal: + n->decl = globalfconst(n); + n->addable = Rmreg; + break; + case Tstring: + n->decl = globalsconst(n); + n->addable = Rmreg; + break; + default: + fatal("cannot %T const in sumark", n->ty); + break; + } + break; + case Oadd: + if(right->addable == Rconst){ + switch(left->addable){ + case Rareg: + n->addable = Rareg; + break; + case Ramreg: + n->addable = Ramreg; + break; + case Rreg: + case Raadr: + n->addable = Raadr; + break; + case Rmreg: + case Ramadr: + n->addable = Ramadr; + break; + } + } + break; + } + if(n->addable < Rcant) + n->temps = 0; + else if(n->temps == 0) + n->temps = 1; + return n; +} + +Node* +mktn(Type *t) +{ + Node *n; + + n = mkn(Oname, nil, nil); + usedesc(mktdesc(t)); + n->ty = t; + n->decl = t->decl; + if(n->decl == nil) + fatal("mktn t %T nil decl", t); + n->addable = Rdesc; + return n; +} + +/* does a tuple of the form (a, b, ...) form a contiguous block + * of memory on the stack when offsets are assigned later + * - only when (a, b, ...) := rhs and none of the names nil + * can we guarantee this + */ +static int +tupblk0(Node *n, Decl **dd) +{ + Decl *d; + int nid; + + switch(n->op){ + case Otuple: + for(n = n->left; n != nil; n = n->right) + if(!tupblk0(n->left, dd)) + return 0; + return 1; + case Oname: + if(n->decl == nildecl) + return 0; + d = *dd; + if(d != nil && d->next != n->decl) + return 0; + nid = n->decl->nid; + if(d == nil && nid == 1) + return 0; + if(d != nil && nid != 0) + return 0; + *dd = n->decl; + return 1; + } + return 0; +} + +/* could force locals to be next to each other + * - need to shuffle locals list + * - later + */ +static Node* +tupblk(Node *n) +{ + Decl *d; + + if(n->op != Otuple) + return nil; + d = nil; + if(!tupblk0(n, &d)) + return nil; + while(n->op == Otuple) + n = n->left->left; + if(n->op != Oname || n->decl->nid == 1) + fatal("bad tupblk"); + return n; +} + +/* for cprof */ +#define esrc(src, osrc, nto) (src != nil && nto != nil ? src : osrc) + +/* + * compile an expression with an implicit assignment + * note: you are not allowed to use to->src + * + * need to think carefully about the types used in moves + * it particular, it would be nice to gen movp rather than movc sometimes. + */ +Node* +ecom(Src *src, Node *nto, Node *n) +{ + Node *left, *right, *tn; + Node tl, tr, tto, ttn; + Type *t, *tt; + Inst *p, *pp; + int op; + + if(debug['e']){ + print("ecom: %n\n", n); + if(nto != nil) + print("ecom to: %n\n", nto); + } + + if(n->addable < Rcant){ + /* + * think carefully about the type used here + */ + if(nto != nil) + genmove(src, Mas, n->ty, n, nto); + return nto; + } + + tl.decl = nil; + tr.decl = nil; + tto.decl = nil; + ttn.decl = nil; + + left = n->left; + right = n->right; + op = n->op; + switch(op){ + default: + case Oadr: + fatal("can't %n in ecom", n); + return nto; + case Oif: + p = bcom(left, 1, nil); + ecom(&right->left->src, nto, right->left); + if(right->right != nil){ + pp = p; + p = genrawop(&right->left->src, IJMP, nil, nil, nil); + patch(pp, nextinst()); + ecom(&right->right->src, nto, right->right); + } + patch(p, nextinst()); + break; + case Ocomma: + tn = left->left; + ecom(&left->src, nil, left); + ecom(&right->src, nto, right); + tfree(tn); + break; + case Oname: + if(n->addable == Rpc){ + if(nto != nil) + genmove(src, Mas, n->ty, n, nto); + return nto; + } + fatal("can't %n in ecom", n); + break; + case Onothing: + break; + case Oused: + if(nto != nil) + fatal("superfluous used %n to %n", left, nto); + talloc(&tto, left->ty, nil); + ecom(&left->src, &tto, left); + tfree(&tto); + break; + case Oas: + if(right->ty == tany) + right->ty = n->ty; + if(left->op == Oname && left->decl->ty == tany){ + if(nto == nil) + nto = talloc(&tto, right->ty, nil); + left = nto; + nto = nil; + } + if(left->op == Oinds){ + indsascom(src, nto, n); + tfree(&tto); + break; + } + if(left->op == Oslice){ + slicelcom(src, nto, n); + tfree(&tto); + break; + } + + if(left->op == Otuple){ + if(!tupsaliased(right, left)){ + if((tn = tupblk(left)) != nil){ + tn->ty = n->ty; + ecom(&n->right->src, tn, right); + if(nto != nil) + genmove(src, Mas, n->ty, tn, nto); + tfree(&tto); + break; + } + if((tn = tupblk(right)) != nil){ + tn->ty = n->ty; + tuplcom(tn, left); + if(nto != nil) + genmove(src, Mas, n->ty, tn, nto); + tfree(&tto); + break; + } + if(nto == nil && right->op == Otuple && left->ty->kind != Tadtpick){ + tuplrcom(right, left); + tfree(&tto); + break; + } + } + if(right->addable >= Ralways + || right->op != Oname + || tupaliased(right, left)){ + talloc(&tr, n->ty, nil); + ecom(&n->right->src, &tr, right); + right = &tr; + } + tuplcom(right, n->left); + if(nto != nil) + genmove(src, Mas, n->ty, right, nto); + tfree(&tr); + tfree(&tto); + break; + } + + /* + * check for left/right aliasing and build right into temporary + */ + if(right->op == Otuple){ + if(!tupsaliased(left, right) && (tn = tupblk(right)) != nil){ + tn->ty = n->ty; + right = tn; + } + else if(left->op != Oname || tupaliased(left, right)) + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + } + + /* + * think carefully about types here + */ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + ecom(&n->src, left, right); + if(nto != nil) + genmove(src, Mas, nto->ty, left, nto); + tfree(&tl); + tfree(&tr); + tfree(&tto); + break; + case Ochan: + if(left && left->addable >= Rcant) + left = eacom(left, &tl, nto); + genchan(src, left, n->ty->tof, nto); + tfree(&tl); + break; + case Oinds: + if(right->addable < Ralways){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else if(left->temps <= right->temps){ + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nil); + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + } + genop(&n->src, op, left, right, nto); + tfree(&tl); + tfree(&tr); + break; + case Osnd: + if(right->addable < Rcant){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + }else if(left->temps < right->temps){ + right = eacom(right, &tr, nto); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nto); + right = eacom(right, &tr, nil); + } + p = genrawop(&n->src, ISEND, right, nil, left); + p->m.offset = n->ty->size; /* for optimizer */ + if(nto != nil) + genmove(src, Mas, right->ty, right, nto); + tfree(&tl); + tfree(&tr); + break; + case Orcv: + if(nto == nil){ + ecom(&n->src, talloc(&tto, n->ty, nil), n); + tfree(&tto); + return nil; + } + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + if(left->ty->kind == Tchan){ + p = genrawop(src, IRECV, left, nil, nto); + p->m.offset = n->ty->size; /* for optimizer */ + }else{ + recvacom(src, nto, n); + } + tfree(&tl); + break; + case Ocons: + /* + * another temp which can go with analysis + */ + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + if(!sameaddr(right, nto)){ + ecom(&right->src, talloc(&tto, n->ty, nto), right); + genmove(src, Mcons, left->ty, left, &tto); + if(!sameaddr(&tto, nto)) + genmove(src, Mas, nto->ty, &tto, nto); + }else + genmove(src, Mcons, left->ty, left, nto); + tfree(&tl); + tfree(&tto); + break; + case Ohd: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + genmove(src, Mhd, nto->ty, left, nto); + tfree(&tl); + break; + case Otl: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + genmove(src, Mtl, left->ty, left, nto); + tfree(&tl); + break; + case Otuple: + if((tn = tupblk(n)) != nil){ + tn->ty = n->ty; + genmove(src, Mas, n->ty, tn, nto); + break; + } + tupcom(nto, n); + break; + case Oadd: + case Osub: + case Omul: + case Odiv: + case Omod: + case Oand: + case Oor: + case Oxor: + case Olsh: + case Orsh: + case Oexp: + /* + * check for 2 operand forms + */ + if(sameaddr(nto, left)){ + if(right->addable >= Rcant) + right = eacom(right, &tr, nto); + genop(src, op, right, nil, nto); + tfree(&tr); + break; + } + + if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + genop(src, opcommute[op], left, nil, nto); + tfree(&tl); + break; + } + + if(right->addable < left->addable + && opcommute[op] + && n->ty != tstring){ + op = opcommute[op]; + left = right; + right = n->left; + } + if(left->addable < Ralways){ + if(right->addable >= Rcant) + right = eacom(right, &tr, nto); + }else if(right->temps <= left->temps){ + left = ecom(&left->src, talloc(&tl, left->ty, nto), left); + if(right->addable >= Rcant) + right = eacom(right, &tr, nil); + }else{ + right = eacom(right, &tr, nto); + left = ecom(&left->src, talloc(&tl, left->ty, nil), left); + } + + /* + * check for 2 operand forms + */ + if(sameaddr(nto, left)) + genop(src, op, right, nil, nto); + else if(opcommute[op] && sameaddr(nto, right) && n->ty != tstring) + genop(src, opcommute[op], left, nil, nto); + else + genop(src, op, right, left, nto); + tfree(&tl); + tfree(&tr); + break; + case Oaddas: + case Osubas: + case Omulas: + case Odivas: + case Omodas: + case Oexpas: + case Oandas: + case Ooras: + case Oxoras: + case Olshas: + case Orshas: + if(left->op == Oinds){ + indsascom(src, nto, n); + break; + } + if(right->addable < Rcant){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + }else if(left->temps < right->temps){ + right = eacom(right, &tr, nto); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nto); + right = eacom(right, &tr, nil); + } + genop(&n->src, op, right, nil, left); + if(nto != nil) + genmove(src, Mas, left->ty, left, nto); + tfree(&tl); + tfree(&tr); + break; + case Olen: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + op = -1; + t = left->ty; + if(t == tstring) + op = ILENC; + else if(t->kind == Tarray) + op = ILENA; + else if(t->kind == Tlist) + op = ILENL; + else + fatal("can't len %n", n); + genrawop(src, op, left, nil, nto); + tfree(&tl); + break; + case Oneg: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + genop(&n->src, op, left, nil, nto); + tfree(&tl); + break; + case Oinc: + case Odec: + if(left->op == Oinds){ + indsascom(src, nto, n); + break; + } + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + if(nto != nil) + genmove(src, Mas, left->ty, left, nto); + if(right->addable >= Rcant) + fatal("inc/dec amount not addressable: %n", n); + genop(&n->src, op, right, nil, left); + tfree(&tl); + break; + case Ospawn: + if(left->left->op == Oind) + fpcall(&n->src, op, left, nto); + else + callcom(&n->src, op, left, nto); + break; + case Oraise: + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + genrawop(&n->src, IRAISE, left, nil, nil); + tfree(&tl); + break; + case Ocall: + if(left->op == Oind) + fpcall(esrc(src, &n->src, nto), op, n, nto); + else + callcom(esrc(src, &n->src, nto), op, n, nto); + break; + case Oref: + t = left->ty; + if(left->op == Oname && left->decl->store == Dfn || left->op == Omdot && left->right->op == Oname && left->right->decl->store == Dfn){ /* create a function reference */ + Decl *d; + Node *mod, *ind; + + d = left->decl; + if(left->op == Omdot){ + d = left->right->decl; + mod = left->left; + } + else if(d->eimport != nil) + mod = d->eimport; + else{ + mod = rewrite(mkn(Oself, nil, nil)); + addiface(nil, d); + } + sumark(mod); + talloc(&tto, n->ty, nto); + genrawop(src, INEW, mktn(usetype(tfnptr)), nil, &tto); + tr.src = *src; + tr.op = Oind; + tr.left = &tto; + tr.right = nil; + tr.ty = tany; + sumark(&tr); + ecom(src, &tr, mod); + ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, &tto), mkconst(src, IBY2WD))); + ind->ty = ind->left->ty = ind->left->right->ty = tint; + tr.op = Oas; + tr.left = ind; + tr.right = mkdeclname(src, d); + tr.ty = tr.right->ty = tint; + sumark(&tr); + tr.right->addable = mod->op == Oself && newfnptr ? Rnoff : Roff; + ecom(src, nil, &tr); + if(!sameaddr(&tto, nto)) + genmove(src, Mas, n->ty, &tto, nto); + tfree(&tto); + break; + } + if(left->op == Oname && left->decl->store == Dtype){ + genrawop(src, INEW, mktn(t), nil, nto); + break; + } + if(t->kind == Tadt && t->tags != nil){ + pickdupcom(src, nto, left); + break; + } + + tt = t; + if(left->op == Oconst && left->decl->store == Dtag) + t = left->decl->ty->tof; + /* + * could eliminate temp if to does not occur + * in tuple initializer + */ + talloc(&tto, n->ty, nto); + genrawop(src, INEW, mktn(t), nil, &tto); + tr.op = Oind; + tr.left = &tto; + tr.right = nil; + tr.ty = tt; + sumark(&tr); + ecom(src, &tr, left); + if(!sameaddr(&tto, nto)) + genmove(src, Mas, n->ty, &tto, nto); + tfree(&tto); + break; + case Oload: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + talloc(&tr, tint, nil); + if(LDT) + genrawop(src, ILOAD, left, right, nto); + else{ + genrawop(src, ILEA, right, nil, &tr); + genrawop(src, ILOAD, left, &tr, nto); + } + tfree(&tl); + tfree(&tr); + break; + case Ocast: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + t = left->ty; + if(t->kind == Tfix || n->ty->kind == Tfix){ + op = casttab[t->kind][n->ty->kind]; + if(op == ICVTXX) + genfixcastop(src, op, left, nto); + else{ + tn = sumark(mkrconst(src, scale2(t, n->ty))); + genrawop(src, op, left, tn, nto); + } + } + else + genrawop(src, casttab[t->kind][n->ty->kind], left, nil, nto); + tfree(&tl); + break; + case Oarray: + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + genrawop(esrc(src, &left->src, nto), arrayz ? INEWAZ : INEWA, left, mktn(n->ty->tof), nto); + if(right != nil) + arraycom(nto, right); + tfree(&tl); + break; + case Oslice: + tn = right->right; + right = right->left; + + /* + * make the left node of the slice directly addressable + * therefore, if it's len is taken (via tn), + * left's tree won't be rewritten + */ + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + + if(tn->op == Onothing){ + tn = mkn(Olen, left, nil); + tn->src = *src; + tn->ty = tint; + sumark(tn); + } + if(tn->addable < Ralways){ + if(right->addable >= Rcant) + right = eacom(right, &tr, nil); + }else if(right->temps <= tn->temps){ + tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn); + if(right->addable >= Rcant) + right = eacom(right, &tr, nil); + }else{ + right = eacom(right, &tr, nil); + tn = ecom(&tn->src, talloc(&ttn, tn->ty, nil), tn); + } + op = ISLICEA; + if(nto->ty == tstring) + op = ISLICEC; + + /* + * overwrite the destination last, + * since it might be used in computing the slice bounds + */ + if(!sameaddr(left, nto)) + ecom(&left->src, nto, left); + + genrawop(src, op, right, tn, nto); + tfree(&tl); + tfree(&tr); + tfree(&ttn); + break; + case Oindx: + if(right->addable < Rcant){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + }else if(left->temps < right->temps){ + right = eacom(right, &tr, nto); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nto); + right = eacom(right, &tr, nil); + } + if(nto->addable >= Ralways) + nto = ecom(src, talloc(&tto, nto->ty, nil), nto); + op = IINDX; + switch(left->ty->tof->size){ + case IBY2LG: + op = IINDL; + if(left->ty->tof == treal) + op = IINDF; + break; + case IBY2WD: + op = IINDW; + break; + case 1: + op = IINDB; + break; + } + genrawop(src, op, left, nto, right); + // array[] of {....} [index] frees array too early (before index value used) + // function(...) [index] frees array too early (before index value used) + if(tl.decl != nil) + tfreelater(&tl); + else + tfree(&tl); + tfree(&tr); + tfree(&tto); + break; + case Oind: + n = eacom(n, &tl, nto); + genmove(src, Mas, n->ty, n, nto); + tfree(&tl); + break; + case Onot: + case Oandand: + case Ooror: + case Oeq: + case Oneq: + case Olt: + case Oleq: + case Ogt: + case Ogeq: + p = bcom(n, 1, nil); + genmove(src, Mas, tint, sumark(mkconst(src, 1)), nto); + pp = genrawop(src, IJMP, nil, nil, nil); + patch(p, nextinst()); + genmove(src, Mas, tint, sumark(mkconst(src, 0)), nto); + patch(pp, nextinst()); + break; + case Oself: + if(newfnptr){ + if(nto != nil) + genrawop(src, ISELF, nil, nil, nto); + break; + } + tn = sumark(mkdeclname(src, selfdecl)); + p = genbra(src, Oneq, tn, sumark(mkdeclname(src, nildecl))); + n->op = Oload; + ecom(src, tn, n); + patch(p, nextinst()); + genmove(src, Mas, n->ty, tn, nto); + break; + } + return nto; +} + +/* + * compile exp n to yield an addressable expression + * use reg to build a temporary; if t is a temp, it is usable + * if dangle leaves the address dangling, generate into a temporary + * this should only happen with arrays + * + * note that 0adr's are strange as they are only used + * for calculating the addresses of fields within adt's. + * therefore an Oind is the parent or grandparent of the Oadr, + * and we pick off all of the cases where Oadr's argument is not + * addressable by looking from the Oind. + */ +Node* +eacom(Node *n, Node *reg, Node *t) +{ + Node *left, *tn; + + if(n->op == Ocomma){ + tn = n->left->left; + ecom(&n->left->src, nil, n->left); + n = eacom(n->right, reg, t); + tfree(tn); + return n; + } + + if(debug['e'] || debug['E']) + print("eacom: %n\n", n); + + left = n->left; + if(n->op != Oind){ + ecom(&n->src, talloc(reg, n->ty, t), n); + reg->src = n->src; + return reg; + } + + if(left->op == Oadd && left->right->op == Oconst){ + if(left->left->op == Oadr){ + left->left->left = eacom(left->left->left, reg, t); + sumark(n); + if(n->addable >= Rcant) + fatal("eacom can't make node addressable: %n", n); + return n; + } + talloc(reg, left->left->ty, t); + ecom(&left->left->src, reg, left->left); + left->left->decl = reg->decl; + left->left->addable = Rreg; + left->left = reg; + left->addable = Raadr; + n->addable = Radr; + }else if(left->op == Oadr){ + talloc(reg, left->left->ty, t); + ecom(&left->left->src, reg, left->left); + + /* + * sleaze: treat the temp as the type of the field, not the enclosing structure + */ + reg->ty = n->ty; + reg->src = n->src; + return reg; + }else{ + talloc(reg, left->ty, t); + ecom(&left->src, reg, left); + n->left = reg; + n->addable = Radr; + } + return n; +} + +/* + * compile an assignment to an array slice + */ +Node* +slicelcom(Src *src, Node *nto, Node *n) +{ + Node *left, *right, *v; + Node tl, tr, tv, tu; + + tl.decl = nil; + tr.decl = nil; + tv.decl = nil; + tu.decl = nil; + + left = n->left->left; + right = n->left->right->left; + v = n->right; + if(right->addable < Ralways){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + }else if(left->temps <= right->temps){ + right = ecom(&right->src, talloc(&tr, right->ty, nto), right); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nil); /* dangle on right and v */ + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + } + + switch(n->op){ + case Oas: + if(v->addable >= Rcant) + v = eacom(v, &tv, nil); + break; + } + + genrawop(&n->src, ISLICELA, v, right, left); + if(nto != nil) + genmove(src, Mas, n->ty, left, nto); + tfree(&tl); + tfree(&tv); + tfree(&tr); + tfree(&tu); + return nto; +} + +/* + * compile an assignment to a string location + */ +Node* +indsascom(Src *src, Node *nto, Node *n) +{ + Node *left, *right, *u, *v; + Node tl, tr, tv, tu; + + tl.decl = nil; + tr.decl = nil; + tv.decl = nil; + tu.decl = nil; + + left = n->left->left; + right = n->left->right; + v = n->right; + if(right->addable < Ralways){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nto); + }else if(left->temps <= right->temps){ + right = ecom(&right->src, talloc(&tr, right->ty, nto), right); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nil); /* dangle on right and v */ + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + } + + switch(n->op){ + case Oas: + if(v->addable >= Rcant) + v = eacom(v, &tv, nil); + break; + case Oinc: + case Odec: + if(v->addable >= Rcant) + fatal("inc/dec amount not addable"); + u = talloc(&tu, tint, nil); + genop(&n->left->src, Oinds, left, right, u); + if(nto != nil) + genmove(src, Mas, n->ty, u, nto); + nto = nil; + genop(&n->src, n->op, v, nil, u); + v = u; + break; + case Oaddas: + case Osubas: + case Omulas: + case Odivas: + case Omodas: + case Oexpas: + case Oandas: + case Ooras: + case Oxoras: + case Olshas: + case Orshas: + if(v->addable >= Rcant) + v = eacom(v, &tv, nil); + u = talloc(&tu, tint, nil); + genop(&n->left->src, Oinds, left, right, u); + genop(&n->src, n->op, v, nil, u); + v = u; + break; + } + + genrawop(&n->src, IINSC, v, right, left); + tfree(&tl); + tfree(&tv); + tfree(&tr); + tfree(&tu); + if(nto != nil) + genmove(src, Mas, n->ty, v, nto); + return nto; +} + +void +callcom(Src *src, int op, Node *n, Node *ret) +{ + Node frame, tadd, toff, pass, *a, *mod, *ind, *nfn, *args, tmod, tind, *tn; + Inst *in,*p; + Decl *d, *callee; + long off; + int iop; + + args = n->right; + nfn = n->left; + switch(nfn->op){ + case Odot: + callee = nfn->right->decl; + nfn->addable = Rpc; + break; + case Omdot: + callee = nfn->right->decl; + break; + case Oname: + callee = nfn->decl; + break; + default: + callee = nil; + fatal("bad call op in callcom"); + } + if(nfn->addable != Rpc && nfn->addable != Rmpc) + fatal("can't gen call addresses"); + if(nfn->ty->tof != tnone && ret == nil){ + ecom(src, talloc(&tmod, nfn->ty->tof, nil), n); + tfree(&tmod); + return; + } + if(ispoly(callee)) + addfnptrs(callee, 0); + if(nfn->ty->varargs){ + nfn->decl = dupdecl(nfn->right->decl); + nfn->decl->desc = gendesc(nfn->right->decl, idoffsets(nfn->ty->ids, MaxTemp, MaxAlign), nfn->ty->ids); + } + + talloc(&frame, tint, nil); + + mod = nfn->left; + ind = nfn->right; + tmod.decl = tind.decl = nil; + if(nfn->addable == Rmpc){ + if(mod->addable >= Rcant) + mod = eacom(mod, &tmod, nil); /* dangle always */ + if(ind->op != Oname && ind->addable >= Ralways){ + talloc(&tind, ind->ty, nil); + ecom(&ind->src, &tind, ind); + ind = &tind; + } + else if(ind->decl != nil && ind->decl->store != Darg) + ind->addable = Roff; + } + + /* + * stop nested uncalled frames + * otherwise exception handling very complicated + */ + for(a = args; a != nil; a = a->right){ + if(hascall(a->left)){ + tn = mkn(0, nil, nil); + talloc(tn, a->left->ty, nil); + ecom(&a->left->src, tn, a->left); + a->left = tn; + tn->flags |= TEMP; + } + } + + /* + * allocate the frame + */ + if(nfn->addable == Rmpc && !nfn->ty->varargs){ + genrawop(src, IMFRAME, mod, ind, &frame); + }else if(nfn->op == Odot){ + genrawop(src, IFRAME, nfn->left, nil, &frame); + }else{ + in = genrawop(src, IFRAME, nil, nil, &frame); + in->sm = Adesc; + in->s.decl = nfn->decl; + } + + /* + * build a fake node for the argument area + */ + toff = znode; + tadd = znode; + pass = znode; + toff.op = Oconst; + toff.addable = Rconst; + toff.ty = tint; + tadd.op = Oadd; + tadd.addable = Raadr; + tadd.left = &frame; + tadd.right = &toff; + tadd.ty = tint; + pass.op = Oind; + pass.addable = Radr; + pass.left = &tadd; + + /* + * compile all the args + */ + d = nfn->ty->ids; + off = 0; + for(a = args; a != nil; a = a->right){ + off = d->offset; + toff.val = off; + if(d->ty->kind == Tpoly) + pass.ty = a->left->ty; + else + pass.ty = d->ty; + ecom(&a->left->src, &pass, a->left); + d = d->next; + if(a->left->flags & TEMP) + tfree(a->left); + } + if(off > maxstack) + maxstack = off; + + /* + * pass return value + */ + if(ret != nil){ + toff.val = REGRET*IBY2WD; + pass.ty = nfn->ty->tof; + p = genrawop(src, ILEA, ret, nil, &pass); + p->m.offset = ret->ty->size; /* for optimizer */ + } + + /* + * call it + */ + if(nfn->addable == Rmpc){ + iop = IMCALL; + if(op == Ospawn) + iop = IMSPAWN; + genrawop(src, iop, &frame, ind, mod); + tfree(&tmod); + tfree(&tind); + }else if(nfn->op == Odot){ + iop = ICALL; + if(op == Ospawn) + iop = ISPAWN; + genrawop(src, iop, &frame, nil, nfn->right); + }else{ + iop = ICALL; + if(op == Ospawn) + iop = ISPAWN; + in = genrawop(src, iop, &frame, nil, nil); + in->d.decl = nfn->decl; + in->dm = Apc; + } + tfree(&frame); +} + +/* + * initialization code for arrays + * a must be addressable (< Rcant) + */ +void +arraycom(Node *a, Node *elems) +{ + Node tindex, fake, tmp, ri, *e, *n, *q, *body, *wild; + Inst *top, *out; + Case *c; + + if(debug['A']) + print("arraycom: %n %n\n", a, elems); + + /* c = elems->ty->cse; */ + /* don't use c->wild in case we've been inlined */ + wild = nil; + for(e = elems; e != nil; e = e->right) + for(q = e->left->left; q != nil; q = q->right) + if(q->left->op == Owild) + wild = e->left; + if(wild != nil) + arraydefault(a, wild->right); + + tindex = znode; + fake = znode; + talloc(&tmp, tint, nil); + tindex.op = Oindx; + tindex.addable = Rcant; + tindex.left = a; + tindex.right = nil; + tindex.ty = tint; + fake.op = Oind; + fake.addable = Radr; + fake.left = &tmp; + fake.ty = a->ty->tof; + + for(e = elems; e != nil; e = e->right){ + /* + * just duplicate the initializer for Oor + */ + for(q = e->left->left; q != nil; q = q->right){ + if(q->left->op == Owild) + continue; + + body = e->left->right; + if(q->right != nil) + body = dupn(0, &nosrc, body); + top = nil; + out = nil; + ri.decl = nil; + if(q->left->op == Orange){ + /* + * for(i := q.left.left; i <= q.left.right; i++) + */ + talloc(&ri, tint, nil); + ri.src = q->left->src; + ecom(&q->left->src, &ri, q->left->left); + + /* i <= q.left.right; */ + n = mkn(Oleq, &ri, q->left->right); + n->src = q->left->src; + n->ty = tint; + top = nextinst(); + out = bcom(n, 1, nil); + + tindex.right = &ri; + }else{ + tindex.right = q->left; + } + + tindex.addable = Rcant; + tindex.src = q->left->src; + ecom(&tindex.src, &tmp, &tindex); + + ecom(&body->src, &fake, body); + + if(q->left->op == Orange){ + /* i++ */ + n = mkbin(Oinc, &ri, sumark(mkconst(&ri.src, 1))); + n->ty = tint; + n->addable = Rcant; + ecom(&n->src, nil, n); + + /* jump to test */ + patch(genrawop(&q->left->src, IJMP, nil, nil, nil), top); + patch(out, nextinst()); + tfree(&ri); + } + } + } + tfree(&tmp); +} + +/* + * default initialization code for arrays. + * compiles to + * n = len a; + * while(n){ + * n--; + * a[n] = elem; + * } + */ +void +arraydefault(Node *a, Node *elem) +{ + Inst *out, *top; + Node n, e, *t; + + if(debug['A']) + print("arraydefault: %n %n\n", a, elem); + + t = mkn(Olen, a, nil); + t->src = elem->src; + t->ty = tint; + t->addable = Rcant; + talloc(&n, tint, nil); + n.src = elem->src; + ecom(&t->src, &n, t); + + top = nextinst(); + out = bcom(&n, 1, nil); + + t = mkbin(Odec, &n, sumark(mkconst(&elem->src, 1))); + t->ty = tint; + t->addable = Rcant; + ecom(&t->src, nil, t); + + e.decl = nil; + if(elem->addable >= Rcant) + elem = eacom(elem, &e, nil); + + t = mkn(Oindx, a, &n); + t->src = elem->src; + t = mkbin(Oas, mkunary(Oind, t), elem); + t->ty = elem->ty; + t->left->ty = elem->ty; + t->left->left->ty = tint; + sumark(t); + ecom(&t->src, nil, t); + + patch(genrawop(&t->src, IJMP, nil, nil, nil), top); + + tfree(&n); + tfree(&e); + patch(out, nextinst()); +} + +void +tupcom(Node *nto, Node *n) +{ + Node tadr, tadd, toff, fake, *e; + Decl *d; + + if(debug['Y']) + print("tupcom %n\nto %n\n", n, nto); + + /* + * build a fake node for the tuple + */ + toff = znode; + tadd = znode; + fake = znode; + tadr = znode; + toff.op = Oconst; + toff.ty = tint; + tadr.op = Oadr; + tadr.left = nto; + tadr.ty = tint; + tadd.op = Oadd; + tadd.left = &tadr; + tadd.right = &toff; + tadd.ty = tint; + fake.op = Oind; + fake.left = &tadd; + sumark(&fake); + if(fake.addable >= Rcant) + fatal("tupcom: bad value exp %n", &fake); + + /* + * compile all the exps + */ + d = n->ty->ids; + for(e = n->left; e != nil; e = e->right){ + toff.val = d->offset; + fake.ty = d->ty; + ecom(&e->left->src, &fake, e->left); + d = d->next; + } +} + +void +tuplcom(Node *n, Node *nto) +{ + Node tadr, tadd, toff, fake, tas, *e, *as; + Decl *d; + + if(debug['Y']) + print("tuplcom %n\nto %n\n", n, nto); + + /* + * build a fake node for the tuple + */ + toff = znode; + tadd = znode; + fake = znode; + tadr = znode; + toff.op = Oconst; + toff.ty = tint; + tadr.op = Oadr; + tadr.left = n; + tadr.ty = tint; + tadd.op = Oadd; + tadd.left = &tadr; + tadd.right = &toff; + tadd.ty = tint; + fake.op = Oind; + fake.left = &tadd; + sumark(&fake); + if(fake.addable >= Rcant) + fatal("tuplcom: bad value exp for %n", &fake); + + /* + * compile all the exps + */ + d = nto->ty->ids; + if(nto->ty->kind == Tadtpick) + d = nto->ty->tof->ids->next; + for(e = nto->left; e != nil; e = e->right){ + as = e->left; + if(as->op != Oname || as->decl != nildecl){ + toff.val = d->offset; + fake.ty = d->ty; + fake.src = as->src; + if(as->addable < Rcant) + genmove(&as->src, Mas, d->ty, &fake, as); + else{ + tas.op = Oas; + tas.ty = d->ty; + tas.src = as->src; + tas.left = as; + tas.right = &fake; + tas.addable = Rcant; + ecom(&tas.src, nil, &tas); + } + } + d = d->next; + } +} + +void +tuplrcom(Node *n, Node *nto) +{ + Node *s, *d, tas; + Decl *de; + + de = nto->ty->ids; + for(s = n->left, d = nto->left; s != nil && d != nil; s = s->right, d = d->right){ + if(d->left->op != Oname || d->left->decl != nildecl){ + tas.op = Oas; + tas.ty = de->ty; + tas.src = s->left->src; + tas.left = d->left; + tas.right = s->left; + sumark(&tas); + ecom(&tas.src, nil, &tas); + } + de = de->next; + } + if(s != nil || d != nil) + fatal("tuplrcom"); +} + +/* + * boolean compiler + * fall through when condition == true + */ +Inst* +bcom(Node *n, int true, Inst *b) +{ + Inst *bb; + Node tl, tr, *t, *left, *right, *tn; + int op; + + if(n->op == Ocomma){ + tn = n->left->left; + ecom(&n->left->src, nil, n->left); + bb = bcom(n->right, true, b); + tfree(tn); + return bb; + } + + if(debug['b']) + print("bcom %n %d\n", n, true); + + left = n->left; + right = n->right; + op = n->op; + + switch(op){ + case Onothing: + return b; + case Onot: + return bcom(n->left, !true, b); + case Oandand: + if(!true) + return oror(n, true, b); + return andand(n, true, b); + case Ooror: + if(!true) + return andand(n, true, b); + return oror(n, true, b); + case Ogt: + case Ogeq: + case Oneq: + case Oeq: + case Olt: + case Oleq: + break; + default: + if(n->ty->kind == Tint){ + right = mkconst(&n->src, 0); + right->addable = Rconst; + left = n; + op = Oneq; + break; + } + fatal("can't bcom %n", n); + return b; + } + + if(true) + op = oprelinvert[op]; + + if(left->addable < right->addable){ + t = left; + left = right; + right = t; + op = opcommute[op]; + } + + tl.decl = nil; + tr.decl = nil; + if(right->addable < Ralways){ + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else if(left->temps <= right->temps){ + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + if(left->addable >= Rcant) + left = eacom(left, &tl, nil); + }else{ + left = eacom(left, &tl, nil); + right = ecom(&right->src, talloc(&tr, right->ty, nil), right); + } + bb = genbra(&n->src, op, left, right); + bb->branch = b; + tfree(&tl); + tfree(&tr); + return bb; +} + +Inst* +andand(Node *n, int true, Inst *b) +{ + if(debug['b']) + print("andand %n\n", n); + b = bcom(n->left, true, b); + b = bcom(n->right, true, b); + return b; +} + +Inst* +oror(Node *n, int true, Inst *b) +{ + Inst *bb; + + if(debug['b']) + print("oror %n\n", n); + bb = bcom(n->left, !true, nil); + b = bcom(n->right, true, b); + patch(bb, nextinst()); + return b; +} + +/* + * generate code for a recva expression + * this is just a hacked up small alt + */ +void +recvacom(Src *src, Node *nto, Node *n) +{ + Label *labs; + Case *c; + Node which, tab, off, add, adr, slot, *left; + Type *talt; + Inst *p; + + left = n->left; + + labs = allocmem(1 * sizeof *labs); + labs[0].isptr = left->addable >= Rcant; + c = allocmem(sizeof *c); + c->nlab = 1; + c->labs = labs; + talt = mktalt(c); + + talloc(&which, tint, nil); + talloc(&tab, talt, nil); + + /* + * build the node for the address of each channel, + * the values to send, and the storage fro values received + */ + off = znode; + off.op = Oconst; + off.ty = tint; + off.addable = Rconst; + adr = znode; + adr.op = Oadr; + adr.left = &tab; + adr.ty = tint; + add = znode; + add.op = Oadd; + add.left = &adr; + add.right = &off; + add.ty = tint; + slot = znode; + slot.op = Oind; + slot.left = &add; + sumark(&slot); + + /* + * gen the channel + * this sleaze is lying to the garbage collector + */ + off.val = 2*IBY2WD; + if(left->addable < Rcant) + genmove(src, Mas, tint, left, &slot); + else{ + slot.ty = left->ty; + ecom(src, &slot, left); + slot.ty = nil; + } + + /* + * gen the value + */ + off.val += IBY2WD; + p = genrawop(&left->src, ILEA, nto, nil, &slot); + p->m.offset = nto->ty->size; /* for optimizer */ + + /* + * number of senders and receivers + */ + off.val = 0; + genmove(src, Mas, tint, sumark(mkconst(src, 0)), &slot); + off.val += IBY2WD; + genmove(src, Mas, tint, sumark(mkconst(src, 1)), &slot); + off.val += IBY2WD; + + p = genrawop(src, IALT, &tab, nil, &which); + p->m.offset = talt->size; /* for optimizer */ + tfree(&which); + tfree(&tab); +} + +/* + * generate code to duplicate an adt with pick fields + * this is just a hacked up small pick + * n is Oind(exp) + */ +void +pickdupcom(Src *src, Node *nto, Node *n) +{ + Node *start, *stop, *node, *orig, *dest, tmp, clab; + Case *c; + Inst *j, *jmps, *wild; + Label *labs; + Decl *d, *tg, *stg; + Type *t; + int i, nlab; + char buf[32]; + + if(n->op != Oind) + fatal("pickdupcom not Oind: %n" ,n); + + t = n->ty; + nlab = t->decl->tag; + + /* + * generate global which has case labels + */ + seprint(buf, buf+sizeof(buf), ".c%d", nlabel++); + d = mkids(src, enter(buf, 0), mktype(&src->start, &src->stop, Tcase, nil, nil), nil); + d->init = mkdeclname(src, d); + + clab.addable = Rmreg; + clab.left = nil; + clab.right = nil; + clab.op = Oname; + clab.ty = d->ty; + clab.decl = d; + + /* + * generate a temp to hold the real value + * then generate a case on the tag + */ + orig = n->left; + talloc(&tmp, orig->ty, nil); + ecom(src, &tmp, orig); + orig = mkunary(Oind, &tmp); + orig->ty = tint; + sumark(orig); + + dest = mkunary(Oind, nto); + dest->ty = nto->ty->tof; + sumark(dest); + + genrawop(src, ICASE, orig, nil, &clab); + + labs = allocmem(nlab * sizeof *labs); + + i = 0; + jmps = nil; + for(tg = t->tags; tg != nil; tg = tg->next){ + stg = tg; + for(; tg->next != nil; tg = tg->next) + if(stg->ty != tg->next->ty) + break; + start = sumark(simplify(mkdeclname(src, stg))); + stop = start; + node = start; + if(stg != tg){ + stop = sumark(simplify(mkdeclname(src, tg))); + node = mkbin(Orange, start, stop); + } + + labs[i].start = start; + labs[i].stop = stop; + labs[i].node = node; + labs[i++].inst = nextinst(); + + genrawop(src, INEW, mktn(tg->ty->tof), nil, nto); + genmove(src, Mas, tg->ty->tof, orig, dest); + + j = genrawop(src, IJMP, nil, nil, nil); + j->branch = jmps; + jmps = j; + } + + /* + * this should really be a runtime error + */ + wild = genrawop(src, IJMP, nil, nil, nil); + patch(wild, wild); + + patch(jmps, nextinst()); + tfree(&tmp); + + if(i > nlab) + fatal("overflowed label tab for pickdupcom"); + + c = allocmem(sizeof *c); + c->nlab = i; + c->nsnd = 0; + c->labs = labs; + c->iwild = wild; + + d->ty->cse = c; + usetype(d->ty); + installids(Dglobal, d); +} + +/* + * see if name n occurs anywhere in e + */ +int +tupaliased(Node *n, Node *e) +{ + for(;;){ + if(e == nil) + return 0; + if(e->op == Oname && e->decl == n->decl) + return 1; + if(tupaliased(n, e->left)) + return 1; + e = e->right; + } + return 0; +} + +/* + * see if any name in n occurs anywere in e + */ +int +tupsaliased(Node *n, Node *e) +{ + for(;;){ + if(n == nil) + return 0; + if(n->op == Oname && tupaliased(n, e)) + return 1; + if(tupsaliased(n->left, e)) + return 1; + n = n->right; + } + return 0; +} + +/* + * put unaddressable constants in the global data area + */ +Decl* +globalconst(Node *n) +{ + Decl *d; + Sym *s; + char buf[32]; + + seprint(buf, buf+sizeof(buf), ".i.%.8lux", (long)n->val); + s = enter(buf, 0); + d = s->decl; + if(d == nil){ + d = mkids(&n->src, s, tint, nil); + installids(Dglobal, d); + d->init = n; + d->refs++; + } + return d; +} + +Decl* +globalBconst(Node *n) +{ + Decl *d; + Sym *s; + char buf[32]; + + seprint(buf, buf+sizeof(buf), ".B.%.8lux.%8lux", (long)(n->val>>32), (long)n->val); + + s = enter(buf, 0); + d = s->decl; + if(d == nil){ + d = mkids(&n->src, s, tbig, nil); + installids(Dglobal, d); + d->init = n; + d->refs++; + } + return d; +} + +Decl* +globalbconst(Node *n) +{ + Decl *d; + Sym *s; + char buf[32]; + + seprint(buf, buf+sizeof(buf), ".b.%.2lux", (long)n->val & 0xff); + s = enter(buf, 0); + d = s->decl; + if(d == nil){ + d = mkids(&n->src, s, tbyte, nil); + installids(Dglobal, d); + d->init = n; + d->refs++; + } + return d; +} + +Decl* +globalfconst(Node *n) +{ + Decl *d; + Sym *s; + char buf[32]; + ulong dv[2]; + + dtocanon(n->rval, dv); + seprint(buf, buf+sizeof(buf), ".f.%.8lux.%8lux", dv[0], dv[1]); + s = enter(buf, 0); + d = s->decl; + if(d == nil){ + d = mkids(&n->src, s, treal, nil); + installids(Dglobal, d); + d->init = n; + d->refs++; + } + return d; +} + +Decl* +globalsconst(Node *n) +{ + Decl *d; + Sym *s; + + s = n->decl->sym; + d = s->decl; + if(d == nil){ + d = mkids(&n->src, s, tstring, nil); + installids(Dglobal, d); + d->init = n; + } + d->refs++; + return d; +} + +static Node* +subst(Decl *d, Node *e, Node *n) +{ + if(n == nil) + return nil; + if(n->op == Oname){ + if(d == n->decl){ + n = dupn(0, nil, e); + n->ty = d->ty; + } + return n; + } + n->left = subst(d, e, n->left); + n->right = subst(d, e, n->right); + return n; +} + +static Node* +putinline(Node *n) +{ + Node *e, *tn; + Type *t; + Decl *d; + +if(debug['z']) print("inline1: %n\n", n); + if(n->left->op == Oname) + d = n->left->decl; + else + d = n->left->right->decl; + e = d->init; + t = e->ty; + e = dupn(1, &n->src, e->right->left->left); + for(d = t->ids, n = n->right; d != nil && n != nil; d = d->next, n = n->right){ + if(hasside(n->left, 0) && occurs(d, e) != 1){ + tn = talloc(mkn(0, nil, nil), d->ty, nil); + e = mkbin(Ocomma, mkbin(Oas, tn, n->left), subst(d, tn, e)); + e->ty = e->right->ty; + e->left->ty = d->ty; + } + else + e = subst(d, n->left, e); + } + if(d != nil || n != nil) + fatal("bad arg match in putinline()"); +if(debug['z']) print("inline2: %n\n", e); + return e; +} + +static void +fpcall(Src *src, int op, Node *n, Node *ret) +{ + Node tp, *e, *mod, *ind; + + tp.decl = nil; + e = n->left->left; + if(e->addable >= Rcant) + e = eacom(e, &tp, nil); + mod = mkunary(Oind, e); + ind = mkunary(Oind, mkbin(Oadd, dupn(0, src, e), mkconst(src, IBY2WD))); + n->left = mkbin(Omdot, mod, ind); + n->left->ty = e->ty->tof; + mod->ty = ind->ty = ind->left->ty = ind->left->right->ty = tint; + sumark(n); + callcom(src, op, n, ret); + tfree(&tp); +} diff --git a/limbo/fns.h b/limbo/fns.h new file mode 100644 index 00000000..ddaa8a97 --- /dev/null +++ b/limbo/fns.h @@ -0,0 +1,391 @@ +int addfile(File*); +void addfnptrs(Decl*, int); +void addiface(Decl*, Decl*); +void addinclude(char*); +char *addrprint(char*, char*, int, Addr*); +Typelist *addtype(Type*, Typelist*); +Node *adtdecl(Decl *ids, Node *fields); +void adtdecled(Node *n); +void adtdefd(Type*); +Decl *adtmeths(Type*); +void adtstub(Decl*); +long align(long, int); +void *allocmem(ulong); +void altcheck(Node *an, Type *ret); +void altcom(Node*); +Inst *andand(Node*, int, Inst*); +Decl *appdecls(Decl*, Decl*); +int argcompat(Node*, Decl*, Node*); +void arraycom(Node*, Node*); +void arraydefault(Node*, Node*); +Type *arrowtype(Type*, Decl*); +void asmdesc(Desc*); +void asmentry(Decl*); +void asmexc(Except*); +void asminitializer(long, Node*); +void asminst(Inst*); +void asmldt(long, Decl*); +void asmmod(Decl*); +void asmpath(void); +void asmstring(long, Sym*); +void asmvar(long, Decl*); +int assignindices(Node*); +void bccom(Node*, Inst**); +Inst *bcom(Node*, int, Inst*); +void bindnames(Node*); +void bindtypes(Type *t); +Ok callcast(Node*, int, int); +void callcom(Src*, int, Node*, Node*); +Type* calltype(Type*, Node*, Type*); +double canontod(ulong v[2]); +void casecheck(Node *cn, Type *ret); +int casecmp(Type*, Node*, Node*); +void casecom(Node*); +Node *caselist(Node*, Node*); +void casesort(Type*, Label*, Label*, int, int); +Case *checklabels(Node *inits, Type *ctype, int nlab, char *title); +void checkrefs(Decl*); +Node *checkused(Node*); +int circlval(Node*, Node*); +void concheck(Node *n, int isglobal); +Node *condecl(Decl*, Node*); +void condecled(Node *n); +void constub(Decl*); +Type *copytypeids(Type*); +char *ctprint(char*, char*, Type*); +int ctypeconv(Fmt*); +Line curline(void); +Decl *curscope(void); +int cycarc(Type*, Type*); +void cycfield(Type*, Decl*); +void cycsizetype(Type*); +void cyctype(Type*); +int dasdecl(Node *n); +void declaserr(Node*); +int declasinfer(Node*, Type*); +int declconv(Fmt*); +Decl *declsort(Decl*); +void declstart(void); +void decltozero(Node *n); +void deldecl(Decl*); +int dequal(Decl*, Decl*, int); +long descmap(Decl*, uchar*, long); +void disaddr(int, Addr*); +void disbcon(long); +void discon(long); +void disdata(int, long); +void disdesc(Desc*); +void disentry(Decl*); +void disexc(Except*); +void disinst(Inst*); +void disldt(long, Decl*); +void dismod(Decl*); +void dispath(void); +void disvar(long, Decl*); +void disword(long); +int dotconv(Fmt*); +char *dotprint(char*, char*, Decl*, int); +Type *dottype(Type*, Decl*); +void dtocanon(double, ulong[2]); +Decl *dupdecl(Decl*); +Decl *dupdecls(Decl*); +Node *dupn(int, Src*, Node*); +Node *eacom(Node*, Node*, Node*); +Ok echeck(Node *n, int typeok, int isglobal, Node* par); +Node *ecom(Src*, Node*, Node*); +Node *efold(Node *n); +Node *elemsort(Node*); +void emit(Decl*); +Decl *encpolys(Decl*); +Sym *enter(char*, int); +Desc *enterdesc(uchar*, long, long); +Sym *enterstring(char*, int); +char *eprint(char*, char*, Node*); +char *eprintlist(char*, char*, Node*, char*); +void error(Line, char*, ...); +#pragma varargck argpos error 2 +int etconv(Fmt*); +Node *etolist(Node*); +void excheck(Node *n, int isglobal); +void exccheck(Node *cn, Type *ret); +void excom(Node*); +Node *exdecl(Decl*, Decl*); +void exdecled(Node *n); +Type *expandtype(Type*, Type*, Decl*, Tpair**); +Type *expandtypes(Type*, Decl*); +int expconv(Fmt*); +Type *exptotype(Node*); +void fatal(char*, ...); +#pragma varargck argpos fatal 1 +void fielddecled(Node *n); +Node *fielddecl(int store, Decl *ids); +int findlab(Type *ty, Node *v, Label *labs, int nlab); +int fixop(int, Type*, Type*, Type*, int*, int*); +Fline fline(int); +void fmtcheck(Node*, Node*, Node*); +void fncheck(Decl *d); +Decl *fnchk(Node *n); +void fncom(Decl*); +Node *fndecl(Node *n, Type *t, Node *body); +void fndecled(Node *n); +Decl* fnlookup(Sym*, Type*, Node**); +Node *fold(Node*); +void foldbranch(Inst*); +Node *foldc(Node*); +Node *foldcast(Node*, Node*); +Node *foldcasti(Node*, Node*); +Node *foldr(Node*); +Node *foldvc(Node*); +void gbind(Node *n); +int gcheck(Node*, Decl**, int); +void gdasdecl(Node *n); +void gdecl(Node *n); +Addr genaddr(Node*); +Inst *genbra(Src*, int, Node*, Node*); +Inst *genchan(Src*, Node*, Type*, Node*); +Desc *gendesc(Decl*, long, Decl*); +Inst *genfixcastop(Src*, int, Node*, Node*); +Inst *genmove(Src*, int, Type*, Node*, Node*); +Inst *genop(Src*, int, Node*, Node*, Node*); +Inst *genrawop(Src*, int, Node*, Node*, Node*); +void genstart(void); +long getpc(Inst*); +int gfltconv(Fmt*); +Decl *globalBconst(Node*); +Node *globalas(Node*, Node*, int); +Decl *globalbconst(Node*); +Decl *globalconst(Node*); +Decl *globalfconst(Node*); +Decl *globalsconst(Node*); +void gsort(Node*); +int hasasgns(Node*); +int hascall(Node*); +Node *hascomm(Node*); +int hasside(Node*, int); +long idindices(Decl*); +long idoffsets(Decl*, long, int); +Type *idtype(Type*); +void importcheck(Node *n, int isglobal); +void importchk(Node*); +Node *importdecl(Node *m, Decl *ids); +void importdecled(Node *n); +void includef(Sym*); +Node *indsascom(Src*, Node*, Node*); +int initable(Node*, Node*, int); +int inloop(void); +void installids(int store, Decl *ids); +int instconv(Fmt*); +Type *insttype(Type*, Decl*, Tpair**); +Type *internaltype(Type*); +int isimpmod(Sym*); +int islval(Node*); +int ispoly(Decl*); +int ispolyadt(Type*); +int istuple(Node*); +void joiniface(Type*, Type*); +void lexinit(void); +void lexstart(char*); +int lineconv(Fmt*); +int local(Decl*); +Decl *lookdot(Decl*, Sym*); +Decl *lookup(Sym*); +int mapconv(Fmt*); +int marklval(Node*); +int mathchk(Node*, int); +void mergepolydecs(Type*); +Type *mkadtcon(Type*); +Type *mkadtpickcon(Type*, Type*); +Type *mkarrowtype(Line*, Line*, Type*, Sym*); +Node *mkbin(int, Node*, Node*); +Node *mkconst(Src*, Long); +Decl *mkdecl(Src*, int, Type*); +Node *mkdeclname(Src*, Decl*); +Desc *mkdesc(long, Decl*); +Type *mkdottype(Line*, Line*, Type*, Sym*); +Type *mkexbasetype(Type*); +Type *mkextuptype(Type*); +Type *mkextype(Type*); +File *mkfile(char*, int, int, int, char*, int, int); +Decl *mkids(Src*, Sym*, Type*, Decl*); +Type *mkidtype(Src*, Sym*); +Type *mkiface(Decl*); +Inst *mkinst(void); +Type *mkinsttype(Src*, Type*, Typelist*); +Node *mkname(Src*, Sym*); +Node *mknil(Src*); +Node *mkn(int, Node*, Node*); +Node *mkrconst(Src*, Real); +Node *mksconst(Src*, Sym*); +Node *mkscope(Node *body); +Type *mktalt(Case*); +Desc *mktdesc(Type*); +Node *mktn(Type*); +Type *mktype(Line*, Line*, int, Type*, Decl*); +Node *mkunary(int, Node*); +Type *mkvarargs(Node*, Node*); +Teq *modclass(void); +void modcode(Decl*); +void modcom(Decl*); +void moddataref(void); +Node *moddecl(Decl *ids, Node *fields); +void moddecled(Node *n); +Decl *modglobals(Decl*, Decl*); +Decl *modimp(Dlist*, Decl*); +void modrefable(Type*); +void modresolve(void); +void modstub(Decl*); +void modtab(Decl*); +Decl *module(Decl*); +int mustzero(Decl *); +int mpatof(char*, double*); +Decl *namedot(Decl*, Sym*); +Decl *namesort(Decl*); +void narrowmods(void); +void nerror(Node*, char*, ...); +#pragma varargck argpos nerror 2 +int nested(void); +Inst *nextinst(void); +int nodeconv(Fmt*); +int nodes(Node*); +char *nprint(char*, char*, Node*, int); +void nwarn(Node*, char*, ...); +#pragma varargck argpos nwarn 2 +int occurs(Decl*, Node*); +int opconv(Fmt*); +void optabinit(void); +void optim(Inst*, Decl*); +Inst *oror(Node*, int, Inst*); +Decl *outerpolys(Node*); +Node *passfns(Src*, Decl*, Node*, Node*, Type*, Tpair*); +Node *passimplicit(Node*, Node*); +void patch(Inst*, Inst*); +void pickcheck(Node*, Type*); +int pickdecled(Node *n); +Decl *pickdefd(Type*, Decl*); +void pickdupcom(Src*, Node*, Node*); +Decl* polydecl(Decl*); +int polyequal(Decl*, Decl*); +void popblock(void); +Decl *popids(Decl*); +void popscopes(void); +Decl *popscope(void); +void printdecls(Decl*); +int pushblock(void); +void pushlabel(Node*); +void pushscope(Node *, int); +void raisescheck(Type*); +int raisescompat(Node*, Node*); +void reach(Inst*); +void *reallocmem(void*, ulong); +void recvacom(Src*, Node*, Node*); +void redecl(Decl *d); +void reftype(Type*); +void repushblock(int); +void repushids(Decl*); +void resizetype(Type*); +long resolvedesc(Decl*, long, Decl*); +Decl* resolveldts(Decl*, Decl**); +int resolvemod(Decl*); +long resolvepcs(Inst*); +Node *retalloc(Node*, Node*); +Decl *revids(Decl*); +Node *rewrite(Node *n); +Node *rewritecomm(Node*, Node*, Node*, Node*); +Inst *rewritedestreg(Inst*, int, int); +Inst *rewritesrcreg(Inst*, int, int, int); +void rmfnptrs(Decl*); +Node *rotater(Node*); +double rpow(double, int); +int sameaddr(Node*, Node*); +int sametree(Node*, Node*); +void sblfiles(void); +void sblfn(Decl**, int); +void sblinst(Inst*, long); +void sblmod(Decl*); +void sblty(Decl**, int); +void sblvar(Decl*); +double scale(Type*); +double scale2(Type*, Type*); +Node* scheck(Node*, Type*, int); +void scom(Node*); +char *secpy(char*, char*, char*); +char *seprint(char*, char*, char*, ...); +#pragma varargck argpos seprint 3 +void shareloc(Decl*); +int shiftchk(Node*); +ulong sign(Decl*); +Node *simplify(Node*); +Szal sizeids(Decl*, long); +void sizetype(Type*); +Node *slicelcom(Src*, Node*, Node*); +int specific(Type*); +int srcconv(Fmt*); +char* srcpath(char*, int); +int storeconv(Fmt*); +char *stprint(char*, char*, Type*); +Sym *stringcat(Sym*, Sym*); +char *stringpr(char*, char*, Sym*); +Long strtoi(char*, int); +int stypeconv(Fmt*); +Node *sumark(Node*); +int symcmp(Sym*, Sym*); +Node *tacquire(Node*); +Ok tagcast(Node*, Node*, Node*, Decl*, int, int); +Node *talloc(Node*, Type*, Node*); +int tcompat(Type*, Type*, int); +void tcycle(Type*); +Decl *tdecls(void); +long tdescmap(Type*, uchar*, long); +void teqclass(Type*); +int tequal(Type*, Type*); +void tfree(Node*); +void tfreelater(Node*); +void tfreenow(void); +void tinit(void); +int tmustzero(Type *); +Type *toptype(Src*, Type*); +Type *topvartype(Type *t, Decl *id, int tyok, int polyok); +Type* tparent(Type*, Type*); +char *tprint(char*, char*, Type*); +void translate(char*, char*, char*); +void trelease(Node*); +int tunify(Type*, Type*, Tpair**); +int tupaliased(Node*, Node*); +int tupsaliased(Node*, Node*); +void tupcom(Node*, Node*); +void tuplcom(Node*, Node*); +void tuplrcom(Node*, Node*); +Decl *tuplefields(Node*); +void typebuiltin(Decl*, Type*); +Decl *typecheck(int); +int typeconv(Fmt*); +Node *typedecl(Decl *ids, Type *t); +void typedecled(Node *n); +Decl *typeids(Decl*, Type*); +void typeinit(void); +void typestart(void); +Decl *undefed(Src *src, Sym *s); +Desc *usedesc(Desc*); +void usedty(Type*); +Type *usetype(Type*); +Type *validtype(Type*, Decl*); +int valistype(Node*); +Type *valtmap(Type*, Tpair*); +void varcheck(Node *n, int isglobal); +int varcom(Decl*); +Node *vardecl(Decl*, Type*); +void vardecled(Node *n); +Node *varinit(Decl*, Node*); +Decl *varlistdecl(Decl*, Node*); +Decl *vars(Decl*); +int vcom(Decl*); +Type *verifytypes(Type*, Decl*, Decl*); +void warn(Line, char*, ...); +#pragma varargck argpos warn 2 +void yyerror(char*, ...); +#pragma varargck argpos yyerror 1 +int yylex(void); +int yyparse(void); +void zcom(Node *, Node**); +void zcom0(Decl *, Node**); +void zcom1(Node *, Node**); diff --git a/limbo/gen.c b/limbo/gen.c new file mode 100644 index 00000000..feb87d64 --- /dev/null +++ b/limbo/gen.c @@ -0,0 +1,1097 @@ +#include "limbo.h" + +static int addrmode[Rend] = +{ + /* Rreg */ Afp, + /* Rmreg */ Amp, + /* Roff */ Aoff, + /* Rnoff */ Anoff, + /* Rdesc */ Adesc, + /* Rdescp */ Adesc, + /* Rconst */ Aimm, + /* Ralways */ Aerr, + /* Radr */ Afpind, + /* Rmadr */ Ampind, + /* Rcant */ Aerr, + /* Rpc */ Apc, + /* Rmpc */ Aerr, + /* Rareg */ Aerr, + /* Ramreg */ Aerr, + /* Raadr */ Aerr, + /* Ramadr */ Aerr, + /* Rldt */ Aldt, +}; + +static Decl *wtemp; +static Decl *bigtemp; +static int ntemp; +static Node retnode; +static Inst zinst; + + int *blockstack; + int blockdep; + int nblocks; +static int lenblockstack; +static Node *ntoz; + +static Inst* genfixop(Src *src, int op, Node *s, Node *m, Node *d); + +void +genstart(void) +{ + Decl *d; + + d = mkdecl(&nosrc, Dlocal, tint); + d->sym = enter(".ret", 0); + d->offset = IBY2WD * REGRET; + + retnode = znode; + retnode.op = Oname; + retnode.addable = Rreg; + retnode.decl = d; + retnode.ty = tint; + + zinst.op = INOP; + zinst.sm = Anone; + zinst.dm = Anone; + zinst.mm = Anone; + + firstinst = allocmem(sizeof *firstinst); + *firstinst = zinst; + lastinst = firstinst; + + blocks = -1; + blockdep = 0; + nblocks = 0; +} + +/* + * manage nested control flow blocks + */ +int +pushblock(void) +{ + if(blockdep >= lenblockstack){ + lenblockstack = blockdep + 32; + blockstack = reallocmem(blockstack, lenblockstack * sizeof *blockstack); + } + blockstack[blockdep++] = blocks; + return blocks = nblocks++; +} + +void +repushblock(int b) +{ + blockstack[blockdep++] = blocks; + blocks = b; +} + +void +popblock(void) +{ + blocks = blockstack[blockdep -= 1]; +} + +void +tinit(void) +{ + wtemp = nil; + bigtemp = nil; +} + +Decl* +tdecls(void) +{ + Decl *d; + + for(d = wtemp; d != nil; d = d->next){ + if(d->tref != 1) + fatal("temporary %s has %d references", d->sym->name, d->tref-1); + } + + for(d = bigtemp; d != nil; d = d->next){ + if(d->tref != 1) + fatal("temporary %s has %d references", d->sym->name, d->tref-1); + } + + return appdecls(wtemp, bigtemp); +} + +Node* +talloc(Node *n, Type *t, Node *nok) +{ + Decl *d, *ok; + Desc *desc; + char buf[StrSize]; + + ok = nil; + if(nok != nil) + ok = nok->decl; + if(ok == nil || ok->tref == 0 || tattr[ok->ty->kind].big != tattr[t->kind].big || ok->ty->align != t->align) + ok = nil; + *n = znode; + n->op = Oname; + n->addable = Rreg; + n->ty = t; + if(tattr[t->kind].big){ + desc = mktdesc(t); + if(ok != nil && ok->desc == desc){ + ok->tref++; + ok->refs++; + n->decl = ok; + return n; + } + for(d = bigtemp; d != nil; d = d->next){ + if(d->tref == 1 && d->desc == desc && d->ty->align == t->align){ + d->tref++; + d->refs++; + n->decl = d; + return n; + } + } + d = mkdecl(&nosrc, Dlocal, t); + d->desc = desc; + d->tref = 2; + d->refs = 1; + n->decl = d; + seprint(buf, buf+sizeof(buf), ".b%d", ntemp++); + d->sym = enter(buf, 0); + d->next = bigtemp; + bigtemp = d; + return n; + } + if(ok != nil + && tattr[ok->ty->kind].isptr == tattr[t->kind].isptr + && ok->ty->size == t->size){ + ok->tref++; + n->decl = ok; + return n; + } + for(d = wtemp; d != nil; d = d->next){ + if(d->tref == 1 + && tattr[d->ty->kind].isptr == tattr[t->kind].isptr + && d->ty->size == t->size + && d->ty->align == t->align){ + d->tref++; + n->decl = d; + return n; + } + } + d = mkdecl(&nosrc, Dlocal, t); + d->tref = 2; + d->refs = 1; + n->decl = d; + seprint(buf, buf+sizeof(buf), ".t%d", ntemp++); + d->sym = enter(buf, 0); + d->next = wtemp; + wtemp = d; + return n; +} + +void +tfree(Node *n) +{ + if(n == nil || n->decl == nil || n->decl->tref == 0) + return; + if(n->decl->tref == 1) + fatal("double free of temporary %s", n->decl->sym->name); + if (--n->decl->tref == 1) + zcom1(n, nil); +} + +void +tfreelater(Node *n) +{ + if(n == nil || n->decl == nil || n->decl->tref == 0) + return; + if(n->decl->tref == 1) + fatal("double free of temporary %s", n->decl->sym->name); + if(--n->decl->tref == 1){ + Node *nn = mkn(Oname, nil, nil); + + *nn = *n; + nn->left = ntoz; + ntoz = nn; + n->decl->tref++; + } +} + +void +tfreenow() +{ + Node *n, *nn; + + for(n = ntoz; n != nil; n = nn){ + nn = n->left; + n->left = nil; + if(n->decl->tref != 2) + fatal("bad free of temporary %s", n->decl->sym->name); + --n->decl->tref; + zcom1(n, nil); + } + ntoz = nil; +} + +/* + * realloc a temporary after it's been freed + */ +Node* +tacquire(Node *n) +{ + if(n == nil || n->decl == nil || n->decl->tref == 0) + return n; +/* + if(n->decl->tref != 1) + fatal("tacquire ref != 1: %d", n->decl->tref); +*/ + n->decl->tref++; + return n; +} + +void +trelease(Node *n) +{ + if(n == nil || n->decl == nil || n->decl->tref == 0) + return; + if(n->decl->tref == 1) + fatal("double release of temporary %s", n->decl->sym->name); + n->decl->tref--; +} + +Inst* +mkinst(void) +{ + Inst *in; + + in = lastinst->next; + if(in == nil){ + in = allocmem(sizeof *in); + *in = zinst; + lastinst->next = in; + } + lastinst = in; + in->block = blocks; + if(blocks < 0) + fatal("mkinst no block"); + return in; +} + +Inst* +nextinst(void) +{ + Inst *in; + + in = lastinst->next; + if(in != nil) + return in; + in = allocmem(sizeof(*in)); + *in = zinst; + lastinst->next = in; + return in; +} + +/* + * allocate a node for returning + */ +Node* +retalloc(Node *n, Node *nn) +{ + if(nn->ty == tnone) + return nil; + *n = znode; + n->op = Oind; + n->addable = Radr; + n->left = dupn(1, &n->src, &retnode); + n->ty = nn->ty; + return n; +} + +Inst* +genrawop(Src *src, int op, Node *s, Node *m, Node *d) +{ + Inst *in; + + in = mkinst(); + in->op = op; + in->src = *src; +if(in->sm != Anone || in->mm != Anone || in->dm != Anone) +fatal("bogus mkinst in genrawop: %I\n", in); + if(s != nil){ + in->s = genaddr(s); + in->sm = addrmode[s->addable]; + } + if(m != nil){ + in->m = genaddr(m); + in->mm = addrmode[m->addable]; + if(in->mm == Ampind || in->mm == Afpind) + fatal("illegal addressing mode in register %n", m); + } + if(d != nil){ + in->d = genaddr(d); + in->dm = addrmode[d->addable]; + } + return in; +} + +Inst* +genop(Src *src, int op, Node *s, Node *m, Node *d) +{ + Inst *in; + int iop; + + iop = disoptab[op][opind[d->ty->kind]]; + if(iop == 0) + fatal("can't deal with op %s on %n %n %n in genop", opname[op], s, m, d); + if(iop == IMULX || iop == IDIVX) + return genfixop(src, iop, s, m, d); + in = mkinst(); + in->op = iop; + in->src = *src; + if(s != nil){ + in->s = genaddr(s); + in->sm = addrmode[s->addable]; + } + if(m != nil){ + in->m = genaddr(m); + in->mm = addrmode[m->addable]; + if(in->mm == Ampind || in->mm == Afpind) + fatal("illegal addressing mode in register %n", m); + } + if(d != nil){ + in->d = genaddr(d); + in->dm = addrmode[d->addable]; + } + return in; +} + +Inst* +genbra(Src *src, int op, Node *s, Node *m) +{ + Type *t; + Inst *in; + int iop; + + t = s->ty; + if(t == tany) + t = m->ty; + iop = disoptab[op][opind[t->kind]]; + if(iop == 0) + fatal("can't deal with op %s on %n %n in genbra", opname[op], s, m); + in = mkinst(); + in->op = iop; + in->src = *src; + if(s != nil){ + in->s = genaddr(s); + in->sm = addrmode[s->addable]; + } + if(m != nil){ + in->m = genaddr(m); + in->mm = addrmode[m->addable]; + if(in->mm == Ampind || in->mm == Afpind) + fatal("illegal addressing mode in register %n", m); + } + return in; +} + +Inst* +genchan(Src *src, Node *sz, Type *mt, Node *d) +{ + Inst *in; + Desc *td; + Addr reg; + int op, regm; + + regm = Anone; + reg.decl = nil; + reg.reg = 0; + reg.offset = 0; + op = chantab[mt->kind]; + if(op == 0) + fatal("can't deal with op %d in genchan", mt->kind); + + switch(mt->kind){ + case Tadt: + case Tadtpick: + case Ttuple: + td = mktdesc(mt); + if(td->nmap != 0){ + op++; /* sleazy */ + usedesc(td); + regm = Adesc; + reg.decl = mt->decl; + }else{ + regm = Aimm; + reg.offset = mt->size; + } + break; + } + in = mkinst(); + in->op = op; + in->src = *src; + in->s = reg; + in->sm = regm; + if(sz != nil){ + in->m = genaddr(sz); + in->mm = addrmode[sz->addable]; + } + if(d != nil){ + in->d = genaddr(d); + in->dm = addrmode[d->addable]; + } + return in; +} + +Inst* +genmove(Src *src, int how, Type *mt, Node *s, Node *d) +{ + Inst *in; + Desc *td; + Addr reg; + int op, regm; + + regm = Anone; + reg.decl = nil; + reg.reg = 0; + reg.offset = 0; + op = movetab[how][mt->kind]; + if(op == 0) + fatal("can't deal with op %d on %n %n in genmove", how, s, d); + + switch(mt->kind){ + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + if(mt->size == 0 && how == Mas) + return nil; + td = mktdesc(mt); + if(td->nmap != 0){ + op++; /* sleazy */ + usedesc(td); + regm = Adesc; + reg.decl = mt->decl; + }else{ + regm = Aimm; + reg.offset = mt->size; + } + break; + } + in = mkinst(); + in->op = op; + in->src = *src; + if(s != nil){ + in->s = genaddr(s); + in->sm = addrmode[s->addable]; + } + in->m = reg; + in->mm = regm; + if(d != nil){ + in->d = genaddr(d); + in->dm = addrmode[d->addable]; + } + if(s->addable == Rpc) + in->op = IMOVPC; + return in; +} + +void +patch(Inst *b, Inst *dst) +{ + Inst *n; + + for(; b != nil; b = n){ + n = b->branch; + b->branch = dst; + } +} + +long +getpc(Inst *i) +{ + if(i->pc == 0 && i != firstinst && (firstinst->op != INOOP || i != firstinst->next)){ + do + i = i->next; + while(i != nil && i->pc == 0); + if(i == nil || i->pc == 0) + fatal("bad instruction in getpc"); + } + return i->pc; +} + +/* + * follow all possible paths from n, + * marking reached code, compressing branches, and reclaiming unreached insts + */ +void +reach(Inst *in) +{ + Inst *last; + + foldbranch(in); + last = in; + for(in = in->next; in != nil; in = in->next){ + if(!in->reach) + last->next = in->next; + else + last = in; + } + lastinst = last; +} + +/* + * follow all possible paths from n, + * marking reached code, compressing branches, and eliminating tail recursion + */ +void +foldbranch(Inst *in) +{ + Inst *b, *next; + Label *lab; + int i, n; + + while(in != nil && !in->reach){ + in->reach = 1; + if(in->branch != nil) + while(in->branch->op == IJMP){ + if(in == in->branch || in->branch == in->branch->branch) + break; + in->branch = in->branch->branch; + } + switch(in->op){ + case IGOTO: + case ICASE: + case ICASEL: + case ICASEC: + case IEXC: + foldbranch(in->d.decl->ty->cse->iwild); + lab = in->d.decl->ty->cse->labs; + n = in->d.decl->ty->cse->nlab; + for(i = 0; i < n; i++) + foldbranch(lab[i].inst); + if(in->op == IEXC) + in->op = INOOP; + return; + case IEXC0: + foldbranch(in->branch); + in->op = INOOP; + break; + case IRET: + case IEXIT: + case IRAISE: + return; + case IJMP: + b = in->branch; + switch(b->op){ + case ICASE: + case ICASEL: + case ICASEC: + case IRET: + case IEXIT: + next = in->next; + *in = *b; + in->next = next; + /* b->reach = 1; */ /* why ? */ + continue; + } + foldbranch(in->branch); + return; + default: + if(in->branch != nil) + foldbranch(in->branch); + break; + } + + in = in->next; + } +} + +/* + * convert the addressable node into an operand + * see the comment for sumark + */ +Addr +genaddr(Node *n) +{ + Addr a; + + a.reg = 0; + a.offset = 0; + a.decl = nil; + if(n == nil) + return a; + switch(n->addable){ + case Rreg: + if(n->decl != nil) + a.decl = n->decl; + else + a = genaddr(n->left); + break; + case Rmreg: + if(n->decl != nil) + a.decl = n->decl; + else + a = genaddr(n->left); + break; + case Rdesc: + a.decl = n->ty->decl; + break; + case Roff: + case Rnoff: + a.decl = n->decl; + break; + case Rconst: + a.offset = n->val; + break; + case Radr: + a = genaddr(n->left); + break; + case Rmadr: + a = genaddr(n->left); + break; + case Rareg: + case Ramreg: + a = genaddr(n->left); + if(n->op == Oadd) + a.reg += n->right->val; + break; + case Raadr: + case Ramadr: + a = genaddr(n->left); + if(n->op == Oadd) + a.offset += n->right->val; + break; + case Rldt: + a.decl = n->decl; + break; + case Rdescp: + case Rpc: + a.decl = n->decl; + break; + default: + fatal("can't deal with %n in genaddr", n); + break; + } + return a; +} + +int +sameaddr(Node *n, Node *m) +{ + Addr a, b; + + if(n->addable != m->addable) + return 0; + a = genaddr(n); + b = genaddr(m); + return a.offset == b.offset && a.reg == b.reg && a.decl == b.decl; +} + +long +resolvedesc(Decl *mod, long length, Decl *decls) +{ + Desc *g, *d, *last; + int descid; + + g = gendesc(mod, length, decls); + g->used = 0; + last = nil; + for(d = descriptors; d != nil; d = d->next){ + if(!d->used){ + if(last != nil) + last->next = d->next; + else + descriptors = d->next; + continue; + } + last = d; + } + + g->next = descriptors; + descriptors = g; + + descid = 0; + for(d = descriptors; d != nil; d = d->next) + d->id = descid++; + if(g->id != 0) + fatal("bad global descriptor id"); + + return descid; +} + +int +resolvemod(Decl *m) +{ + Decl *id, *d; + + for(id = m->ty->ids; id != nil; id = id->next){ + switch(id->store){ + case Dfn: + id->iface->pc = id->pc; + id->iface->desc = id->desc; +if(debug['v']) print("R1: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc); + break; + case Dtype: + if(id->ty->kind != Tadt) + break; + for(d = id->ty->ids; d != nil; d = d->next){ + if(d->store == Dfn){ + d->iface->pc = d->pc; + d->iface->desc = d->desc; +if(debug['v']) print("R2: %s %p %p %p\n", d->sym->name, d, d->iface, d->pc); + } + } + break; + } + } + /* for addiface */ + for(id = m->ty->tof->ids; id != nil; id = id->next){ + if(id->store == Dfn){ + if(id->pc == nil) + id->pc = id->iface->pc; + if(id->desc == nil) + id->desc = id->iface->desc; +if(debug['v']) print("R3: %s %p %p %p\n", id->sym->name, id, id->iface, id->pc); + } + } + return m->ty->tof->decl->init->val; +} + +/* + * place the Tiface decs in another list + */ +Decl* +resolveldts(Decl *d, Decl **dd) +{ + Decl *d1, *ld1, *d2, *ld2, *n; + + d1 = d2 = nil; + ld1 = ld2 = nil; + for( ; d != nil; d = n){ + n = d->next; + d->next = nil; + if(d->ty->kind == Tiface){ + if(d2 == nil) + d2 = d; + else + ld2->next = d; + ld2 = d; + } + else{ + if(d1 == nil) + d1 = d; + else + ld1->next = d; + ld1 = d; + } + } + *dd = d2; + return d1; +} + +/* + * fix up all pc's + * finalize all data offsets + * fix up instructions with offsets too large + */ +long +resolvepcs(Inst *inst) +{ + Decl *d; + Inst *in; + int op; + ulong r, off; + long v, pc; + + pc = 0; + for(in = inst; in != nil; in = in->next){ + if(!in->reach || in->op == INOP) + fatal("unreachable pc: %I %ld", in, pc); + if(in->op == INOOP){ + in->pc = pc; + continue; + } + d = in->s.decl; + if(d != nil){ + if(in->sm == Adesc){ + if(d->desc != nil) + in->s.offset = d->desc->id; + }else + in->s.reg += d->offset; + } + r = in->s.reg; + off = in->s.offset; + if((in->sm == Afpind || in->sm == Ampind) + && (r >= MaxReg || off >= MaxReg)) + fatal("big offset in %I\n", in); + + d = in->m.decl; + if(d != nil){ + if(in->mm == Adesc){ + if(d->desc != nil) + in->m.offset = d->desc->id; + }else + in->m.reg += d->offset; + } + v = 0; + switch(in->mm){ + case Anone: + break; + case Aimm: + case Apc: + case Adesc: + v = in->m.offset; + break; + case Aoff: + case Anoff: + v = in->m.decl->iface->offset; + break; + case Afp: + case Amp: + case Aldt: + v = in->m.reg; + if(v < 0) + v = 0x8000; + break; + + default: + fatal("can't deal with %I's m mode\n", in); + break; + } + if(v > 0x7fff || v < -0x8000){ + switch(in->op){ + case IALT: + case IINDX: +warn(in->src.start, "possible bug: temp m too big in %I: %ld %ld %d\n", in, in->m.reg, in->m.reg, MaxReg); + rewritedestreg(in, IMOVW, RTemp); + break; + default: + op = IMOVW; + if(isbyteinst[in->op]) + op = IMOVB; + in = rewritesrcreg(in, op, RTemp, pc++); + break; + } + } + + d = in->d.decl; + if(d != nil){ + if(in->dm == Apc) + in->d.offset = d->pc->pc; + else + in->d.reg += d->offset; + } + r = in->d.reg; + off = in->d.offset; + if((in->dm == Afpind || in->dm == Ampind) + && (r >= MaxReg || off >= MaxReg)) + fatal("big offset in %I\n", in); + + in->pc = pc; + pc++; + } + for(in = inst; in != nil; in = in->next){ + d = in->s.decl; + if(d != nil && in->sm == Apc) + in->s.offset = d->pc->pc; + d = in->d.decl; + if(d != nil && in->dm == Apc) + in->d.offset = d->pc->pc; + if(in->branch != nil){ + in->dm = Apc; + in->d.offset = in->branch->pc; + } + } + return pc; +} + +/* + * fixp up a big register constant uses as a source + * ugly: smashes the instruction + */ +Inst* +rewritesrcreg(Inst *in, int op, int treg, int pc) +{ + Inst *new; + Addr a; + int am; + + a = in->m; + am = in->mm; + in->mm = Afp; + in->m.reg = treg; + in->m.decl = nil; + + new = allocmem(sizeof(*in)); + *new = *in; + + *in = zinst; + in->src = new->src; + in->next = new; + in->op = op; + in->s = a; + in->sm = am; + in->dm = Afp; + in->d.reg = treg; + in->pc = pc; + in->reach = 1; + in->block = new->block; + return new; +} + +/* + * fix up a big register constant by moving to the destination + * after the instruction completes + */ +Inst* +rewritedestreg(Inst *in, int op, int treg) +{ + Inst *n; + + n = allocmem(sizeof(*n)); + *n = zinst; + n->next = in->next; + in->next = n; + n->src = in->src; + n->op = op; + n->sm = Afp; + n->s.reg = treg; + n->d = in->m; + n->dm = in->mm; + n->reach = 1; + n->block = in->block; + + in->mm = Afp; + in->m.reg = treg; + in->m.decl = nil; + + return n; +} + +int +instconv(Fmt *f) +{ + Inst *in; + char buf[512], *p; + char *op, *comma; + + in = va_arg(f->args, Inst*); + op = nil; + if(in->op >= 0 && in->op < MAXDIS) + op = instname[in->op]; + if(op == nil) + op = "??"; + buf[0] = '\0'; + if(in->op == INOP) + return fmtstrcpy(f, "\tnop"); + p = seprint(buf, buf + sizeof(buf), "\t%s\t", op); + comma = ""; + if(in->sm != Anone){ + p = addrprint(p, buf + sizeof(buf), in->sm, &in->s); + comma = ","; + } + if(in->mm != Anone){ + p = seprint(p, buf + sizeof(buf), "%s", comma); + p = addrprint(p, buf + sizeof(buf), in->mm, &in->m); + comma = ","; + } + if(in->dm != Anone){ + p = seprint(p, buf + sizeof(buf), "%s", comma); + p = addrprint(p, buf + sizeof(buf), in->dm, &in->d); + } + + if(asmsym && in->s.decl != nil && in->sm == Adesc) + p = seprint(p, buf+sizeof(buf), " #%D", in->s.decl); + if(0 && asmsym && in->m.decl != nil) + p = seprint(p, buf+sizeof(buf), " #%D", in->m.decl); + if(asmsym && in->d.decl != nil && in->dm == Apc) + p = seprint(p, buf+sizeof(buf), " #%D", in->d.decl); + if(asmsym) + p = seprint(p, buf+sizeof(buf), " #%U", in->src); + USED(p); + return fmtstrcpy(f, buf); +} + +char* +addrprint(char *buf, char *end, int am, Addr *a) +{ + switch(am){ + case Anone: + return buf; + case Aimm: + case Apc: + case Adesc: + return seprint(buf, end, "$%ld", a->offset); + case Aoff: + return seprint(buf, end, "$%ld", a->decl->iface->offset); + case Anoff: + return seprint(buf, end, "-$%ld", a->decl->iface->offset); + case Afp: + return seprint(buf, end, "%ld(fp)", a->reg); + case Afpind: + return seprint(buf, end, "%ld(%ld(fp))", a->offset, a->reg); + case Amp: + return seprint(buf, end, "%ld(mp)", a->reg); + case Ampind: + return seprint(buf, end, "%ld(%ld(mp))", a->offset, a->reg); + case Aldt: + return seprint(buf, end, "$%ld", a->reg); + case Aerr: + default: + return seprint(buf, end, "%ld(%ld(?%d?))", a->offset, a->reg, am); + } +} + +static void +genstore(Src *src, Node *n, int offset) +{ + Decl *de; + Node d; + + de = mkdecl(&nosrc, Dlocal, tint); + de->sym = nil; + de->offset = offset; + + d = znode; + d.op = Oname; + d.addable = Rreg; + d.decl = de; + d.ty = tint; + genrawop(src, IMOVW, n, nil, &d); +} + +static Inst* +genfixop(Src *src, int op, Node *s, Node *m, Node *d) +{ + int p, a; + Node *mm; + Inst *i; + + mm = m ? m: d; + op = fixop(op, mm->ty, s->ty, d->ty, &p, &a); + if(op == IMOVW){ /* just zero d */ + s = sumark(mkconst(src, 0)); + return genrawop(src, op, s, nil, d); + } + if(op != IMULX && op != IDIVX) + genstore(src, sumark(mkconst(src, a)), STemp); + genstore(src, sumark(mkconst(src, p)), DTemp); + i = genrawop(src, op, s, m, d); + return i; +} + +Inst* +genfixcastop(Src *src, int op, Node *s, Node *d) +{ + int p, a; + Node *m; + + op = fixop(op, s->ty, tint, d->ty, &p, &a); + if(op == IMOVW){ /* just zero d */ + s = sumark(mkconst(src, 0)); + return genrawop(src, op, s, nil, d); + } + m = sumark(mkconst(src, p)); + if(op != ICVTXX) + genstore(src, sumark(mkconst(src, a)), STemp); + return genrawop(src, op, s, m, d); +} diff --git a/limbo/lex.c b/limbo/lex.c new file mode 100644 index 00000000..bbd6cb69 --- /dev/null +++ b/limbo/lex.c @@ -0,0 +1,1429 @@ +#define Extern +#include "limbo.h" +#include "y.tab.h" + +enum +{ + Leof = -1, + Linestart = 0, + + Mlower = 1, + Mupper = 2, + Munder = 4, + Malpha = Mupper|Mlower|Munder, + Mdigit = 8, + Msign = 16, + Mexp = 32, + Mhex = 64, + Mradix = 128, + + HashSize = 1024, + MaxPath = 4096 +}; + +typedef struct Keywd Keywd; +struct Keywd +{ + char *name; + int token; +}; + + File **files; /* files making up the module, sorted by absolute line */ + int nfiles; +static int lenfiles; +static int lastfile; /* index of last file looked up */ + +static char *incpath[MaxIncPath]; +static Sym *symbols[HashSize]; +static Sym *strings[HashSize]; +static char map[256]; +static Biobuf *bin; +static Line linestack[MaxInclude]; +static int lineno; +static int linepos; +static int bstack; +static int ineof; +static int lasttok; +static YYSTYPE lastyylval; +static char srcdir[MaxPath]; + +static Keywd keywords[] = +{ + "adt", Ladt, + "alt", Lalt, + "array", Larray, + "big", Ltid, + "break", Lbreak, + "byte", Ltid, + "case", Lcase, + "chan", Lchan, + "con", Lcon, + "continue", Lcont, + "cyclic", Lcyclic, + "do", Ldo, + "else", Lelse, + "exception", Lexcept, + "exit", Lexit, + "fixed", Lfix, + "fn", Lfn, + "for", Lfor, + "hd", Lhd, + "if", Lif, + "implement", Limplement, + "import", Limport, + "include", Linclude, + "int", Ltid, + "len", Llen, + "list", Llist, + "load", Lload, + "module", Lmodule, + "nil", Lnil, + "of", Lof, + "or", Lor, + "pick", Lpick, + "raise", Lraise, + "raises", Lraises, + "real", Ltid, + "ref", Lref, + "return", Lreturn, + "self", Lself, + "spawn", Lspawn, + "string", Ltid, + "tagof", Ltagof, + "tl", Ltl, + "to", Lto, + "type", Ltype, + "while", Lwhile, + 0, +}; + +static Keywd tokwords[] = +{ + "&=", Landeq, + "|=", Loreq, + "^=", Lxoreq, + "<<=", Llsheq, + ">>=", Lrsheq, + "+=", Laddeq, + "-=", Lsubeq, + "*=", Lmuleq, + "/=", Ldiveq, + "%=", Lmodeq, + "**=", Lexpeq, + ":=", Ldeclas, + "||", Loror, + "&&", Landand, + "::", Lcons, + "==", Leq, + "!=", Lneq, + "<=", Lleq, + ">=", Lgeq, + "<<", Llsh, + ">>", Lrsh, + "<-", Lcomm, + "++", Linc, + "--", Ldec, + "->", Lmdot, + "=>", Llabs, + "**", Lexp, + "EOF", Leof, + "eof", Beof, + 0, +}; + +void +lexinit(void) +{ + Keywd *k; + int i; + + for(i = 0; i < 256; i++){ + if(i == '_' || i > 0xa0) + map[i] |= Munder; + if(i >= 'A' && i <= 'Z') + map[i] |= Mupper; + if(i >= 'a' && i <= 'z') + map[i] |= Mlower; + if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f') + map[i] |= Mhex; + if(i == 'e' || i == 'E') + map[i] |= Mexp; + if(i == 'r' || i == 'R') + map[i] |= Mradix; + if(i == '-' || i == '+') + map[i] |= Msign; + if(i >= '0' && i <= '9') + map[i] |= Mdigit; + } + + memset(escmap, -1, sizeof(escmap)); + escmap['\''] = '\''; + unescmap['\''] = '\''; + escmap['"'] = '"'; + unescmap['"'] = '"'; + escmap['\\'] = '\\'; + unescmap['\\'] = '\\'; + escmap['a'] = '\a'; + unescmap['\a'] = 'a'; + escmap['b'] = '\b'; + unescmap['\b'] = 'b'; + escmap['f'] = '\f'; + unescmap['\f'] = 'f'; + escmap['n'] = '\n'; + unescmap['\n'] = 'n'; + escmap['r'] = '\r'; + unescmap['\r'] = 'r'; + escmap['t'] = '\t'; + unescmap['\t'] = 't'; + escmap['v'] = '\v'; + unescmap['\v'] = 'v'; + escmap['0'] = '\0'; + unescmap['\0'] = '0'; + + for(k = keywords; k->name != nil; k++) + enter(k->name, k->token); +} + +int +cmap(int c) +{ + if(c<0) + return 0; + if(c<256) + return map[c]; + return Mlower; +} + +void +lexstart(char *in) +{ + char *p; + + ineof = 0; + bstack = 0; + nfiles = 0; + lastfile = 0; + addfile(mkfile(strdup(in), 1, 0, -1, nil, 0, -1)); + bin = bins[bstack]; + lineno = 1; + linepos = Linestart; + + secpy(srcdir, srcdir+MaxPath, in); + p = strrchr(srcdir, '/'); + if(p == nil) + srcdir[0] = '\0'; + else + p[1] = '\0'; +} + +static int +Getc(void) +{ + int c; + + if(ineof) + return Beof; + c = BGETC(bin); + if(c == Beof) + ineof = 1; + linepos++; + return c; +} + +static void +unGetc(void) +{ + if(ineof) + return; + Bungetc(bin); + linepos--; +} + +static int +getrune(void) +{ + int c; + + if(ineof) + return Beof; + c = Bgetrune(bin); + if(c == Beof) + ineof = 1; + linepos++; + return c; +} + +static void +ungetrune(void) +{ + if(ineof) + return; + Bungetrune(bin); + linepos--; +} + +void +addinclude(char *s) +{ + int i; + + for(i = 0; i < MaxIncPath; i++){ + if(incpath[i] == 0){ + incpath[i] = s; + return; + } + } + fatal("out of include path space"); +} + +File* +mkfile(char *name, int abs, int off, int in, char *act, int actoff, int sbl) +{ + File *f; + + f = allocmem(sizeof *f); + f->name = name; + f->abs = abs; + f->off = off; + f->in = in; + f->act = act; + f->actoff = actoff; + f->sbl = sbl; + return f; +} + +int +addfile(File *f) +{ + if(nfiles >= lenfiles){ + lenfiles = nfiles+32; + files = reallocmem(files, lenfiles*sizeof(File*)); + } + files[nfiles] = f; + return nfiles++; +} + +void +includef(Sym *file) +{ + Biobuf *b; + char *p, buf[MaxPath]; + int i; + + linestack[bstack].line = lineno; + linestack[bstack].pos = linepos; + bstack++; + if(bstack >= MaxInclude) + fatal("%L: include file depth too great", curline()); + p = ""; + if(file->name[0] != '/') + p = srcdir; + seprint(buf, buf+sizeof(buf), "%s%s", p, file->name); + b = Bopen(buf, OREAD); + for(i = 0; b == nil && i < MaxIncPath && incpath[i] != nil && file->name[0] != '/'; i++){ + seprint(buf, buf+sizeof(buf), "%s/%s", incpath[i], file->name); + b = Bopen(buf, OREAD); + } + bins[bstack] = b; + if(bins[bstack] == nil){ + yyerror("can't include %s: %r", file->name); + bstack--; + }else{ + addfile(mkfile(strdup(buf), lineno+1, -lineno, lineno, nil, 0, -1)); + lineno++; + linepos = Linestart; + } + bin = bins[bstack]; +} + +/* + * we hit eof in the current file + * revert to the file which included it. + */ +static void +popinclude(void) +{ + Fline fl; + File *f; + int oline, opos, ln; + + ineof = 0; + bstack--; + bin = bins[bstack]; + oline = linestack[bstack].line; + opos = linestack[bstack].pos; + fl = fline(oline); + f = fl.file; + ln = fl.line; + lineno++; + linepos = opos; + addfile(mkfile(f->name, lineno, ln-lineno, f->in, f->act, f->actoff, -1)); +} + +/* + * convert an absolute Line into a file and line within the file + */ +Fline +fline(int absline) +{ + Fline fl; + int l, r, m, s; + + if(absline < files[lastfile]->abs + || lastfile+1 < nfiles && absline >= files[lastfile+1]->abs){ + lastfile = 0; + l = 0; + r = nfiles - 1; + while(l <= r){ + m = (r + l) / 2; + s = files[m]->abs; + if(s <= absline){ + l = m + 1; + lastfile = m; + }else + r = m - 1; + } + } + + fl.file = files[lastfile]; + fl.line = absline + files[lastfile]->off; + return fl; +} + +/* + * read a comment + */ +static int +lexcom(void) +{ + File *f; + char buf[StrSize], *s, *t, *act; + int i, n, c, actline; + + i = 0; + while((c = Getc()) != '\n'){ + if(c == Beof) + return -1; + if(i < sizeof(buf)-1) + buf[i++] = c; + } + buf[i] = 0; + + lineno++; + linepos = Linestart; + + if(strncmp(buf, "line ", 5) != 0 && strncmp(buf, "line\t", 5) != 0) + return 0; + for(s = buf+5; *s == ' ' || *s == '\t'; s++) + ; + if(!(cmap(*s) & Mdigit)) + return 0; + n = 0; + for(; cmap(c = *s) & Mdigit; s++) + n = n * 10 + c - '0'; + for(; *s == ' ' || *s == '\t'; s++) + ; + if(*s != '"') + return 0; + s++; + t = strchr(s, '"'); + if(t == nil || t[1] != '\0') + return 0; + *t = '\0'; + + f = files[nfiles - 1]; + if(n == f->off+lineno && strcmp(s, f->name) == 0) + return 1; + act = f->name; + actline = lineno + f->off; + if(f->act != nil){ + actline += f->actoff; + act = f->act; + } + addfile(mkfile(strdup(s), lineno, n-lineno, f->in, act, actline - n, -1)); + + return 1; +} + +Line +curline(void) +{ + Line line; + + line.line = lineno; + line.pos = linepos; + return line; +} + +int +lineconv(Fmt *f) +{ + Fline fl; + File *file; + Line inl, line; + char buf[StrSize], *s; + + line = va_arg(f->args, Line); + + if(line.line < 0) + return fmtstrcpy(f, "<noline>"); + fl = fline(line.line); + file = fl.file; + + s = seprint(buf, buf+sizeof(buf), "%s:%d", file->name, fl.line); + if(file->act != nil) + s = seprint(s, buf+sizeof(buf), " [ %s:%d ]", file->act, file->actoff+fl.line); + if(file->in >= 0){ + inl.line = file->in; + inl.pos = 0; + seprint(s, buf+sizeof(buf), ": %L", inl); + } + return fmtstrcpy(f, buf); +} + +static char* +posconv(char *s, char *e, Line line) +{ + Fline fl; + + if(line.line < 0) + return secpy(s, e, "nopos"); + + fl = fline(line.line); + return seprint(s, e, "%s:%d.%d", fl.file->name, fl.line, line.pos); +} + +int +srcconv(Fmt *f) +{ + Src src; + char buf[StrSize], *s; + + src = va_arg(f->args, Src); + s = posconv(buf, buf+sizeof(buf), src.start); + s = secpy(s, buf+sizeof(buf), ","); + posconv(s, buf+sizeof(buf), src.stop); + + return fmtstrcpy(f, buf); +} + +int +lexid(int c) +{ + Sym *sym; + char id[StrSize*UTFmax+1], *p; + Rune r; + int i, t; + + p = id; + i = 0; + for(;;){ + if(i < StrSize){ + if(c < Runeself) + *p++ = c; + else{ + r = c; + p += runetochar(p, &r); + } + i++; + } + c = getrune(); + if(c == Beof + || !(cmap(c) & (Malpha|Mdigit))){ + ungetrune(); + break; + } + } + *p = '\0'; + sym = enter(id, Lid); + t = sym->token; + if(t == Lid || t == Ltid) + yylval.tok.v.idval = sym; + return t; +} + +Long +strtoi(char *t, int base) +{ + char *s; + Long v; + int c, neg, ck; + + neg = 0; + if(t[0] == '-'){ + neg = 1; + t++; + }else if(t[0] == '+') + t++; + v = 0; + for(s = t; c = *s; s++){ + ck = cmap(c); + if(ck & Mdigit) + c -= '0'; + else if(ck & Mlower) + c = c - 'a' + 10; + else if(ck & Mupper) + c = c - 'A' + 10; + if(c >= base){ + yyerror("digit '%c' not radix %d", *s, base); + return -1; + } + v = v * base + c; + } + if(neg) + return -v; + return v; +} + +static int +digit(int c, int base) +{ + int cc, ck; + + cc = c; + ck = cmap(c); + if(ck & Mdigit) + c -= '0'; + else if(ck & Mlower) + c = c - 'a' + 10; + else if(ck & Mupper) + c = c - 'A' + 10; + else if(ck & Munder) + {} + else + return -1; + if(c >= base) + yyerror("digit '%c' not radix %d", cc, base); + return c; +} + +double +strtodb(char *t, int base) +{ + double num, dem; + int neg, eneg, dig, exp, c, d; + + num = 0; + neg = 0; + dig = 0; + exp = 0; + eneg = 0; + + c = *t++; + if(c == '-' || c == '+'){ + if(c == '-') + neg = 1; + c = *t++; + } + while((d = digit(c, base)) >= 0){ + num = num*base + d; + c = *t++; + } + if(c == '.') + c = *t++; + while((d = digit(c, base)) >= 0){ + num = num*base + d; + dig++; + c = *t++; + } + if(c == 'e' || c == 'E'){ + c = *t++; + if(c == '-' || c == '+'){ + if(c == '-'){ + dig = -dig; + eneg = 1; + } + c = *t++; + } + while((d = digit(c, base)) >= 0){ + exp = exp*base + d; + c = *t++; + } + } + exp -= dig; + if(exp < 0){ + exp = -exp; + eneg = !eneg; + } + dem = rpow(base, exp); + if(eneg) + num /= dem; + else + num *= dem; + if(neg) + return -num; + return num; +} + +/* + * parse a numeric identifier + * format [0-9]+(r[0-9A-Za-z]+)? + * or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)? + */ +int +lexnum(int c) +{ + char buf[StrSize], *base; + enum { Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp, FracB } state; + double d; + Long v; + int i, ck; + + i = 0; + buf[i++] = c; + state = Int; + if(c == '.') + state = Frac; + base = nil; + for(;;){ + c = Getc(); + if(c == Beof){ + yyerror("end of file in numeric constant"); + return Leof; + } + + ck = cmap(c); + switch(state){ + case Int: + if(ck & Mdigit) + break; + if(ck & Mexp){ + state = ExpSeen; + break; + } + if(ck & Mradix){ + base = &buf[i]; + state = RadixSeen; + break; + } + if(c == '.'){ + state = Frac; + break; + } + goto done; + case RadixSeen: + case Radix: + if(ck & (Mdigit|Malpha)){ + state = Radix; + break; + } + if(c == '.'){ + state = FracB; + break; + } + goto done; + case Frac: + if(ck & Mdigit) + break; + if(ck & Mexp) + state = ExpSeen; + else + goto done; + break; + case FracB: + if(ck & (Mdigit|Malpha)) + break; + goto done; + case ExpSeen: + if(ck & Msign){ + state = ExpSignSeen; + break; + } + /* fall through */ + case ExpSignSeen: + case Exp: + if(ck & Mdigit){ + state = Exp; + break; + } + goto done; + } + if(i < StrSize-1) + buf[i++] = c; + } +done: + buf[i] = 0; + unGetc(); + switch(state){ + default: + yyerror("malformed numerical constant '%s'", buf); + yylval.tok.v.ival = 0; + return Lconst; + case Radix: + *base++ = '\0'; + v = strtoi(buf, 10); + if(v < 0) + break; + if(v < 2 || v > 36){ + yyerror("radix '%s' must be between 2 and 36", buf); + break; + } + v = strtoi(base, v); + break; + case Int: + v = strtoi(buf, 10); + break; + case Frac: + case Exp: + d = strtod(buf, nil); + yylval.tok.v.rval = d; + return Lrconst; + case FracB: + *base++ = '\0'; + v = strtoi(buf, 10); + if(v < 0) + break; + if(v < 2 || v > 36){ + yyerror("radix '%s' must be between 2 and 36", buf); + break; + } + d = strtodb(base, v); + yylval.tok.v.rval = d; + return Lrconst; + } + yylval.tok.v.ival = v; + return Lconst; +} + +int +escchar(void) +{ + char buf[4+1]; + int c, i; + + c = getrune(); + if(c == Beof) + return Beof; + if(c == 'u'){ + for(i = 0; i < 4; i++){ + c = getrune(); + if(c == Beof || !(cmap(c) & (Mdigit|Mhex))){ + yyerror("malformed \\u escape sequence"); + ungetrune(); + break; + } + buf[i] = c; + } + buf[i] = 0; + return strtoul(buf, 0, 16); + } + if(c < 256 && (i = escmap[c]) >= 0) + return i; + yyerror("unrecognized escape \\%C", c); + return c; +} + +void +lexstring(void) +{ + char *str; + int c; + Rune r; + int len, alloc; + + alloc = 32; + len = 0; + str = allocmem(alloc * sizeof(str)); + for(;;){ + c = getrune(); + switch(c){ + case '\\': + c = escchar(); + if(c != Beof) + break; + /* fall through */ + case Beof: + yyerror("end of file in string constant"); + yylval.tok.v.idval = enterstring(str, len); + return; + case '\n': + yyerror("newline in string constant"); + lineno++; + linepos = Linestart; + yylval.tok.v.idval = enterstring(str, len); + return; + case '"': + yylval.tok.v.idval = enterstring(str, len); + return; + } + while(len+UTFmax+1 >= alloc){ + alloc += 32; + str = reallocmem(str, alloc * sizeof(str)); + } + r = c; + len += runetochar(&str[len], &r); + str[len] = '\0'; + } +} + +static int +lex(void) +{ + int c; + +loop: + yylval.tok.src.start.line = lineno; + yylval.tok.src.start.pos = linepos; + c = getrune(); /* ehg: outside switch() to avoid bug in VisualC++5.0 */ + switch(c){ + case Beof: + Bterm(bin); + if(bstack == 0) + return Leof; + popinclude(); + break; + case '#': + if(lexcom() < 0){ + Bterm(bin); + if(bstack == 0) + return Leof; + popinclude(); + } + break; + + case '\n': + lineno++; + linepos = Linestart; + goto loop; + case ' ': + case '\t': + case '\r': + case '\v': + case '\f': + goto loop; + case '"': + lexstring(); + return Lsconst; + case '\'': + c = getrune(); + if(c == '\\') + c = escchar(); + if(c == Beof){ + yyerror("end of file in character constant"); + return Beof; + }else + yylval.tok.v.ival = c; + c = Getc(); + if(c != '\'') { + yyerror("missing closing '"); + unGetc(); + } + return Lconst; + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case ',': + case ';': + case '~': + return c; + + case ':': + c = Getc(); + if(c == ':') + return Lcons; + if(c == '=') + return Ldeclas; + unGetc(); + return ':'; + + case '.': + c = Getc(); + unGetc(); + if(c != Beof && (cmap(c) & Mdigit)) + return lexnum('.'); + return '.'; + + case '|': + c = Getc(); + if(c == '=') + return Loreq; + if(c == '|') + return Loror; + unGetc(); + return '|'; + + case '&': + c = Getc(); + if(c == '=') + return Landeq; + if(c == '&') + return Landand; + unGetc(); + return '&'; + + case '^': + c = Getc(); + if(c == '=') + return Lxoreq; + unGetc(); + return '^'; + + case '*': + c = Getc(); + if(c == '=') + return Lmuleq; + if(c == '*'){ + c = Getc(); + if(c == '=') + return Lexpeq; + unGetc(); + return Lexp; + } + unGetc(); + return '*'; + case '/': + c = Getc(); + if(c == '=') + return Ldiveq; + unGetc(); + return '/'; + case '%': + c = Getc(); + if(c == '=') + return Lmodeq; + unGetc(); + return '%'; + case '=': + c = Getc(); + if(c == '=') + return Leq; + if(c == '>') + return Llabs; + unGetc(); + return '='; + case '!': + c = Getc(); + if(c == '=') + return Lneq; + unGetc(); + return '!'; + case '>': + c = Getc(); + if(c == '=') + return Lgeq; + if(c == '>'){ + c = Getc(); + if(c == '=') + return Lrsheq; + unGetc(); + return Lrsh; + } + unGetc(); + return '>'; + + case '<': + c = Getc(); + if(c == '=') + return Lleq; + if(c == '-') + return Lcomm; + if(c == '<'){ + c = Getc(); + if(c == '=') + return Llsheq; + unGetc(); + return Llsh; + } + unGetc(); + return '<'; + + case '+': + c = Getc(); + if(c == '=') + return Laddeq; + if(c == '+') + return Linc; + unGetc(); + return '+'; + + case '-': + c = Getc(); + if(c == '=') + return Lsubeq; + if(c == '-') + return Ldec; + if(c == '>') + return Lmdot; + unGetc(); + return '-'; + + case '1': case '2': case '3': case '4': case '5': + case '0': case '6': case '7': case '8': case '9': + return lexnum(c); + + default: + if(cmap(c) & Malpha) + return lexid(c); + yyerror("unknown character %c", c); + break; + } + goto loop; +} + +int +yylex(void) +{ + int t; + + t = lex(); + yylval.tok.src.stop.line = lineno; + yylval.tok.src.stop.pos = linepos; + lasttok = t; + lastyylval = yylval; + return t; +} + +static char* +toksp(int t) +{ + Keywd *k; + static char buf[256]; + + switch(t){ + case Lconst: + snprint(buf, sizeof(buf), "%lld", lastyylval.tok.v.ival); + return buf; + case Lrconst: + snprint(buf, sizeof(buf), "%f", lastyylval.tok.v.rval); + return buf; + case Lsconst: + snprint(buf, sizeof(buf), "\"%s\"", lastyylval.tok.v.idval->name); + return buf; + case Ltid: + case Lid: + return lastyylval.tok.v.idval->name; + } + for(k = keywords; k->name != nil; k++) + if(t == k->token) + return k->name; + for(k = tokwords; k->name != nil; k++) + if(t == k->token) + return k->name; + if(t < 0 || t > 255) + fatal("bad token %d in toksp()", t); + buf[0] = t; + buf[1] = '\0'; + return buf; +} + +Sym* +enterstring(char *str, int n) +{ + Sym *s; + char *p, *e; + ulong h; + int c, c0; + + e = str + n; + h = 0; + for(p = str; p < e; p++){ + c = *p; + c ^= c << 6; + h += (c << 11) ^ (c >> 1); + c = *p; + h ^= (c << 14) + (c << 7) + (c << 4) + c; + } + + c0 = str[0]; + h %= HashSize; + for(s = strings[h]; s != nil; s = s->next){ + if(s->name[0] == c0 && s->len == n && memcmp(s->name, str, n) == 0){ + free(str); + return s; + } + } + + if(n == 0) + return enter("", 0); + + s = allocmem(sizeof(Sym)); + memset(s, 0, sizeof(Sym)); + s->name = str; + s->len = n; + s->next = strings[h]; + strings[h] = s; + return s; +} + +int +symcmp(Sym *s, Sym *t) +{ + int n, c; + + n = s->len; + if(n > t->len) + n = t->len; + c = memcmp(s->name, t->name, n); + if(c == 0) + return s->len - t->len; + return c; +} + +Sym* +stringcat(Sym *s, Sym *t) +{ + char *str; + int n; + + n = s->len + t->len; + str = allocmem(n+1); + memmove(str, s->name, s->len); + memmove(str+s->len, t->name, t->len); + str[n] = '\0'; + return enterstring(str, n); +} + +Sym* +enter(char *name, int token) +{ + Sym *s; + char *p; + ulong h; + int c0, c, n; + + c0 = name[0]; + h = 0; + for(p = name; c = *p; p++){ + c ^= c << 6; + h += (c << 11) ^ (c >> 1); + c = *p; + h ^= (c << 14) + (c << 7) + (c << 4) + c; + } + n = p - name; + + h %= HashSize; + for(s = symbols[h]; s != nil; s = s->next) + if(s->name[0] == c0 && strcmp(s->name, name) == 0) + return s; + + s = allocmem(sizeof(Sym)); + memset(s, 0, sizeof(Sym)); + s->hash = h; + s->name = allocmem(n+1); + memmove(s->name, name, n+1); + if(token == 0) + token = Lid; + s->token = token; + s->next = symbols[h]; + s->len = n; + symbols[h] = s; + return s; +} + +char* +stringpr(char *buf, char *end, Sym *sym) +{ + char sb[30], *s, *p; + int i, c, n; + + s = sym->name; + n = sym->len; + if(n > 10) + n = 10; + p = sb; + *p++ = '"'; + for(i = 0; i < n; i++){ + c = s[i]; + switch(c){ + case '\\': + case '"': + case '\n': + case '\r': + case '\t': + case '\b': + case '\a': + case '\v': + case '\0': + *p++ = '\\'; + *p++ = unescmap[c]; + break; + default: + *p++ = c; + break; + } + } + if(n != sym->len){ + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + *p++ = '"'; + *p = 0; + return secpy(buf, end, sb); +} + +void +warn(Line line, char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + if(errors || !dowarn) + return; + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%L: warning: %s\n", line, buf); +} + +void +nwarn(Node *n, char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + if(errors || !dowarn) + return; + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%L: warning: %s\n", n->src.start, buf); +} + +void +error(Line line, char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + errors++; + if(errors >= maxerr){ + if(errors == maxerr) + fprint(2, "too many errors, stopping\n"); + return; + } + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%L: %s\n", line, buf); +} + +void +nerror(Node *n, char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + errors++; + if(errors >= maxerr){ + if(errors == maxerr) + fprint(2, "too many errors, stopping\n"); + return; + } + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "%L: %s\n", n->src.start, buf); +} + +void +yyerror(char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + errors++; + if(errors >= maxerr){ + if(errors == maxerr) + fprint(2, "too many errors, stopping\n"); + return; + } + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + if(lasttok != 0) + fprint(2, "%L: near ` %s ` : %s\n", curline(), toksp(lasttok), buf); + else + fprint(2, "%L: %s\n", curline(), buf); +} + +void +fatal(char *fmt, ...) +{ + char buf[4096]; + va_list arg; + + if(errors == 0 || isfatal){ + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + fprint(2, "fatal limbo compiler error: %s\n", buf); + } + if(bout != nil) + remove(outfile); + if(bsym != nil) + remove(symfile); + if(isfatal) + abort(); + exits(buf); +} + +int +gfltconv(Fmt *f) +{ + double d; + char buf[32]; + + d = va_arg(f->args, double); + g_fmt(buf, d, 'e'); + return fmtstrcpy(f, buf); +} + +char* +secpy(char *p, char *e, char *s) +{ + int c; + + if(p == e){ + p[-1] = '\0'; + return p; + } + for(; c = *s; s++){ + *p++ = c; + if(p == e){ + p[-1] = '\0'; + return p; + } + } + *p = '\0'; + return p; +} + +char* +seprint(char *buf, char *end, char *fmt, ...) +{ + va_list arg; + + if(buf == end) + return buf; + va_start(arg, fmt); + buf = vseprint(buf, end, fmt, arg); + va_end(arg); + return buf; +} + +void* +allocmem(ulong n) +{ + void *p; + + p = malloc(n); + if(p == nil) + fatal("out of memory"); + return p; +} + +void* +reallocmem(void *p, ulong n) +{ + if(p == nil) + p = malloc(n); + else + p = realloc(p, n); + if(p == nil) + fatal("out of memory"); + return p; +} diff --git a/limbo/limbo.h b/limbo/limbo.h new file mode 100644 index 00000000..1fa0d3eb --- /dev/null +++ b/limbo/limbo.h @@ -0,0 +1,701 @@ +#include "lib9.h" +#include "bio.h" +#include "isa.h" +#include "mathi.h" + +/* internal dis ops */ +#define IEXC MAXDIS +#define IEXC0 (MAXDIS+1) +#define INOOP (MAXDIS+2) + +/* temporary */ +#define LDT 1 + +#ifndef Extern +#define Extern extern +#endif + +#define YYMAXDEPTH 200 + +typedef struct Addr Addr; +typedef struct Case Case; +typedef struct Decl Decl; +typedef struct Desc Desc; +typedef struct Dlist Dlist; +typedef struct Except Except; +typedef struct File File; +typedef struct Fline Fline; +typedef struct Inst Inst; +typedef struct Label Label; +typedef struct Line Line; +typedef struct Node Node; +typedef struct Ok Ok; +typedef struct Src Src; +typedef struct Sym Sym; +typedef struct Szal Szal; +typedef struct Tattr Tattr; +typedef struct Teq Teq; +typedef struct Tpair Tpair; +typedef struct Type Type; +typedef struct Typelist Typelist; + +typedef double Real; +typedef vlong Long; + +enum +{ + STemp = NREG * IBY2WD, + RTemp = STemp+IBY2WD, + DTemp = RTemp+IBY2WD, + MaxTemp = DTemp+IBY2WD, + MaxReg = 1<<16, + MaxAlign = IBY2LG, + StrSize = 256, + NumSize = 32, /* max length of printed */ + MaxIncPath = 32, /* max directories in include path */ + MaxScope = 64, /* max nested {} */ + MaxInclude = 32, /* max nested include "" */ + ScopeBuiltin = 0, + ScopeNils = 1, + ScopeGlobal = 2 +}; + +/* + * return tuple from expression type checking + */ +struct Ok +{ + int ok; + int allok; +}; + +/* + * return tuple from type sizing + */ +struct Szal +{ + int size; + int align; +}; + +/* + * return tuple for file/line numbering + */ +struct Fline +{ + File *file; + int line; +}; + +struct File +{ + char *name; + int abs; /* absolute line of start of the part of file */ + int off; /* offset to line in the file */ + int in; /* absolute line where included */ + char *act; /* name of real file with #line fake file */ + int actoff; /* offset from fake line to real line */ + int sbl; /* symbol file number */ +}; + +struct Line +{ + int line; + int pos; /* character within the line */ +}; + +struct Src +{ + Line start; + Line stop; +}; + +enum +{ + Aimm, /* immediate */ + Amp, /* global */ + Ampind, /* global indirect */ + Afp, /* activation frame */ + Afpind, /* frame indirect */ + Apc, /* branch */ + Adesc, /* type descriptor immediate */ + Aoff, /* offset in module description table */ + Anoff, /* above encoded as -ve */ + Aerr, /* error */ + Anone, /* no operand */ + Aldt, /* linkage descriptor table immediate */ + Aend +}; + +struct Addr +{ + long reg; + long offset; + Decl *decl; +}; + +struct Inst +{ + Src src; + ushort op; + long pc; + uchar reach; /* could a control path reach this instruction? */ + uchar sm; /* operand addressing modes */ + uchar mm; + uchar dm; + Addr s; /* operands */ + Addr m; + Addr d; + Inst *branch; /* branch destination */ + Inst *next; + int block; /* blocks nested inside */ +}; + +struct Case +{ + int nlab; + int nsnd; + long offset; /* offset in mp */ + Label *labs; + Node *wild; /* if nothing matches */ + Inst *iwild; +}; + +struct Label +{ + Node *node; + char isptr; /* true if the labelled alt channel is a pointer */ + Node *start; /* value in range [start, stop) => code */ + Node *stop; + Inst *inst; +}; + +enum +{ + Dtype, + Dfn, + Dglobal, + Darg, + Dlocal, + Dconst, + Dfield, + Dtag, /* pick tags */ + Dimport, /* imported identifier */ + Dunbound, /* unbound identified */ + Dundef, + Dwundef, /* undefined, but don't whine */ + + Dend +}; + +struct Decl +{ + Src src; /* where declaration */ + Sym *sym; + uchar store; /* storage class */ + uchar nid; /* block grouping for locals */ + uchar caninline; /* inline function */ + uchar das; /* declared with := */ + Decl *dot; /* parent adt or module */ + Type *ty; + int refs; /* number of references */ + long offset; + int tag; /* union tag */ + + uchar scope; /* in which it was declared */ + uchar handler; /* fn has exception handler in body */ + Decl *next; /* list in same scope, field or argument list, etc. */ + Decl *old; /* declaration of the symbol in enclosing scope */ + + Node *eimport; /* expr from which imported */ + Decl *importid; /* identifier imported */ + Decl *timport; /* stack of identifiers importing a type */ + + Node *init; /* data initialization */ + int tref; /* 1 => is a tmp; >=2 => tmp in use */ + char cycle; /* can create a cycle */ + char cyc; /* so labelled in source */ + char cycerr; /* delivered an error message for cycle? */ + char implicit; /* implicit first argument in an adt? */ + + Decl *iface; /* used external declarations in a module */ + + Decl *locals; /* locals for a function */ + Decl *link; /* pointer to parent function or function argument or local share or parent type dec */ + Inst *pc; /* start of function */ + /* Inst *endpc; */ /* limit of function - unused */ + + Desc *desc; /* heap descriptor */ +}; + +struct Desc +{ + int id; /* dis type identifier */ + uchar used; /* actually used in output? */ + uchar *map; /* byte map of pointers */ + long size; /* length of the object */ + long nmap; /* length of good bytes in map */ + Desc *next; +}; + +struct Dlist +{ + Decl *d; + Dlist *next; +}; + +struct Except +{ + Inst *p1; /* first pc covered */ + Inst *p2; /* last pc not covered */ + Case *c; /* exception case instructions */ + Decl *d; /* exception definition if any */ + Node *zn; /* list of nodes to zero in handler */ + Desc *desc; /* descriptor map for above */ + int ne; /* number of exceptions (ie not strings) in case */ + Except *next; +}; + +struct Sym +{ + ushort token; + char *name; + int len; + int hash; + Sym *next; + Decl *decl; + Decl *unbound; /* place holder for unbound symbols */ +}; + +/* + * ops for nodes + */ +enum +{ + Oadd = 1, + Oaddas, + Oadr, + Oadtdecl, + Oalt, + Oand, + Oandand, + Oandas, + Oarray, + Oas, + Obreak, + Ocall, + Ocase, + Ocast, + Ochan, + Ocomma, + Ocomp, + Ocondecl, + Ocons, + Oconst, + Ocont, + Odas, + Odec, + Odiv, + Odivas, + Odo, + Odot, + Oelem, + Oeq, + Oexcept, + Oexdecl, + Oexit, + Oexp, + Oexpas, + Oexstmt, + Ofielddecl, + Ofnptr, + Ofor, + Ofunc, + Ogeq, + Ogt, + Ohd, + Oif, + Oimport, + Oinc, + Oind, + Oindex, + Oinds, + Oindx, + Oinv, + Ojmp, + Olabel, + Olen, + Oleq, + Oload, + Olsh, + Olshas, + Olt, + Omdot, + Omod, + Omodas, + Omoddecl, + Omul, + Omulas, + Oname, + Oneg, + Oneq, + Onot, + Onothing, + Oor, + Ooras, + Ooror, + Opick, + Opickdecl, + Opredec, + Opreinc, + Oraise, + Orange, + Orcv, + Oref, + Oret, + Orsh, + Orshas, + Oscope, + Oself, + Oseq, + Oslice, + Osnd, + Ospawn, + Osub, + Osubas, + Otagof, + Otl, + Otuple, + Otype, + Otypedecl, + Oused, + Ovardecl, + Ovardecli, + Owild, + Oxor, + Oxoras, + + Oend +}; + +/* + * moves + */ +enum +{ + Mas, + Mcons, + Mhd, + Mtl, + + Mend +}; + +/* + * addressability + */ +enum +{ + Rreg, /* v(fp) */ + Rmreg, /* v(mp) */ + Roff, /* $v */ + Rnoff, /* $v encoded as -ve */ + Rdesc, /* $v */ + Rdescp, /* $v */ + Rconst, /* $v */ + Ralways, /* preceeding are always addressable */ + Radr, /* v(v(fp)) */ + Rmadr, /* v(v(mp)) */ + Rcant, /* following are not quite addressable */ + Rpc, /* branch address */ + Rmpc, /* cross module branch address */ + Rareg, /* $v(fp) */ + Ramreg, /* $v(mp) */ + Raadr, /* $v(v(fp)) */ + Ramadr, /* $v(v(mp)) */ + Rldt, /* $v */ + + Rend +}; + +#define PARENS 1 +#define TEMP 2 +#define FNPTRA 4 /* argument */ +#define FNPTR2 8 /* 2nd parameter */ +#define FNPTRN 16 /* use -ve offset */ +#define FNPTR (FNPTRA|FNPTR2|FNPTRN) + +struct Node +{ + Src src; + uchar op; + uchar addable; + uchar flags; + uchar temps; + Node *left; + Node *right; + Type *ty; + Decl *decl; + Long val; /* for Oconst */ + Real rval; /* for Oconst */ +}; + +enum +{ + /* + * types visible to limbo + */ + Tnone = 0, + Tadt, + Tadtpick, /* pick case of an adt */ + Tarray, + Tbig, /* 64 bit int */ + Tbyte, /* 8 bit unsigned int */ + Tchan, + Treal, + Tfn, + Tint, /* 32 bit int */ + Tlist, + Tmodule, + Tref, + Tstring, + Ttuple, + Texception, + Tfix, + Tpoly, + + /* + * internal use types + */ + Tainit, /* array initializers */ + Talt, /* alt channels */ + Tany, /* type of nil */ + Tarrow, /* unresolved ty->id types */ + Tcase, /* case labels */ + Tcasel, /* case big labels */ + Tcasec, /* case string labels */ + Tdot, /* unresolved ty.id types */ + Terror, + Tgoto, /* goto labels */ + Tid, /* id with unknown type */ + Tiface, /* module interface */ + Texcept, /* exception handler tables */ + Tinst, /* instantiated adt */ + + Tend +}; + +enum +{ + OKbind = 1 << 0, /* type decls are bound */ + OKverify = 1 << 1, /* type looks ok */ + OKsized = 1 << 2, /* started figuring size */ + OKref = 1 << 3, /* recorded use of type */ + OKclass = 1 << 4, /* equivalence class found */ + OKcyc = 1 << 5, /* checked for cycles */ + OKcycsize = 1 << 6, /* checked for cycles and size */ + OKmodref = 1 << 7, /* started checking for a module handle */ + + OKmask = 0xff, + + /* + * recursive marks + */ + TReq = 1 << 0, + TRcom = 1 << 1, + TRcyc = 1 << 2, + TRvis = 1 << 3, +}; + +/* type flags */ +#define FULLARGS 1 /* all hidden args added */ +#define INST 2 /* instantiated adt */ +#define CYCLIC 4 /* cyclic type */ +#define POLY 8 /* polymorphic types inside */ +#define NOPOLY 16 /* no polymorphic types inside */ + +struct Type +{ + Src src; + uchar kind; + uchar varargs; /* if a function, ends with vargs? */ + uchar ok; /* set when type is verified */ + uchar linkall; /* put all iface fns in external linkage? */ + uchar rec; /* in the middle of recursive type */ + uchar cons; /* exception constant */ + uchar align; /* alignment in bytes */ + uchar flags; + int sbl; /* slot in .sbl adt table */ + long sig; /* signature for dynamic type check */ + long size; /* storage required, in bytes */ + Decl *decl; + Type *tof; + Decl *ids; + Decl *tags; /* tagged fields in an adt */ + Decl *polys; /* polymorphic fields in fn or adt */ + Case *cse; /* case or goto labels */ + Type *teq; /* temporary equiv class for equiv checking */ + Type *tcom; /* temporary equiv class for compat checking */ + Teq *eq; /* real equiv class */ + Node *val; /* for Tfix, Tfn, Tadt only */ + union { + Node *eraises; /* for Tfn only */ + Typelist *tlist; /* for Tinst only */ + Tpair *tmap; /* for Tadt only */ + } u; +}; + +/* + * type equivalence classes + */ +struct Teq +{ + int id; /* for signing */ + Type *ty; /* an instance of the class */ + Teq *eq; /* used to link eq sets */ +}; + +struct Tattr +{ + char isptr; + char refable; + char conable; + char big; + char vis; /* type visible to users */ +}; + +enum { + Sother, + Sloop, + Sscope +}; + +struct Tpair +{ + Type *t1; + Type *t2; + Tpair *nxt; +}; + +struct Typelist +{ + Type *t; + Typelist *nxt; +}; + +Extern Decl **adts; +Extern Sym *anontupsym; /* name assigned to all anonymouse tuples */ +Extern int arrayz; +Extern int asmsym; /* generate symbols in assembly language? */ +Extern Biobuf *bins[MaxInclude]; +Extern int blocks; +Extern Biobuf *bout; /* output file */ +Extern Biobuf *bsym; /* symbol output file; nil => no sym out */ +Extern double canonnan; /* standard nan */ +Extern uchar casttab[Tend][Tend]; /* instruction to cast from [1] to [2] */ +Extern long constval; +Extern Decl *curfn; +Extern char debug[256]; +Extern Desc *descriptors; /* list of all possible descriptors */ +Extern int dontcompile; /* dis header flag */ +Extern int dowarn; +Extern char *emitcode; /* emit stub routines for system module functions */ +Extern int emitdyn; /* emit stub routines as above but for dynamic modules */ +Extern int emitstub; /* emit type and call frames for system modules */ +Extern char *emittab; /* emit table of runtime functions for this module */ +Extern int errors; +Extern char escmap[256]; +Extern Inst *firstinst; +Extern long fixss; /* set extent from command line */ +Extern Decl *fndecls; +Extern Decl **fns; +Extern int gendis; /* generate dis or asm? */ +Extern Decl *impdecl; /* id of implementation module or union if many */ +Extern Dlist *impdecls; /* id(s) of implementation module(s) */ +/* Extern Sym *impmod; */ /* name of implementation module */ +Extern Decl *impmods; /* name of implementation module(s) */ +Extern Decl *iota; +Extern uchar isbyteinst[256]; +Extern int isfatal; +Extern int isrelop[Oend]; +Extern uchar isused[Oend]; +Extern Inst *lastinst; +Extern int lenadts; +Extern int maxerr; +Extern int maxlabdep; /* maximum nesting of breakable/continuable statements */ +Extern long maxstack; /* max size of a stack frame called */ +Extern int mustcompile; /* dis header flag */ +Extern int nadts; +Extern int newfnptr; /* ISELF and -ve indices */ +Extern int nfns; +Extern Decl *nildecl; /* declaration for limbo's nil */ +Extern int nlabel; +Extern int dontinline; +Extern Line noline; +Extern Src nosrc; +Extern uchar opcommute[Oend]; +Extern int opind[Tend]; +Extern uchar oprelinvert[Oend]; +Extern int optims; +Extern char *outfile; +Extern Type *precasttab[Tend][Tend]; +Extern int scope; +Extern Decl *selfdecl; /* declaration for limbo's self */ +Extern uchar sideeffect[Oend]; +Extern char *signdump; /* dump sig for this fn */ +Extern int superwarn; +Extern char *symfile; +Extern Type *tany; +Extern Type *tbig; +Extern Type *tbyte; +Extern Type *terror; +Extern Type *tint; +Extern Type *tnone; +Extern Type *treal; +Extern Node *tree; +Extern Type *tstring; +Extern Type *texception; +Extern Type *tunknown; +Extern Type *tfnptr; +Extern Type *rtexception; +Extern char unescmap[256]; +Extern Src unifysrc; +Extern Node znode; + +extern int *blockstack; +extern int blockdep; +extern int nblocks; +extern File **files; +extern int nfiles; +extern uchar chantab[Tend]; +extern uchar disoptab[Oend+1][7]; +extern char *instname[]; +extern char *kindname[Tend]; +extern uchar movetab[Mend][Tend]; +extern char *opname[]; +extern int setisbyteinst[]; +extern int setisused[]; +extern int setsideeffect[]; +extern char *storename[Dend]; +extern int storespace[Dend]; +extern Tattr tattr[Tend]; + +#include "fns.h" + +#pragma varargck type "D" Decl* +#pragma varargck type "I" Inst* +#pragma varargck type "K" Decl* +#pragma varargck type "k" Decl* +#pragma varargck type "L" Line +#pragma varargck type "M" Desc* +#pragma varargck type "n" Node* +#pragma varargck type "O" int +#pragma varargck type "O" uint +#pragma varargck type "g" double +#pragma varargck type "Q" Node* +#pragma varargck type "R" Type* +#pragma varargck type "T" Type* +#pragma varargck type "t" Type* +#pragma varargck type "U" Src +#pragma varargck type "v" Node* +#pragma varargck type "V" Node* diff --git a/limbo/limbo.y b/limbo/limbo.y new file mode 100644 index 00000000..7098cd5d --- /dev/null +++ b/limbo/limbo.y @@ -0,0 +1,2032 @@ +%{ +#include "limbo.h" +%} + +%union +{ + struct{ + Src src; + union{ + Sym *idval; + Long ival; + Real rval; + }v; + }tok; + Decl *ids; + Node *node; + Type *type; + Typelist *types; +} + +%type <type> type fnarg fnargret fnargretp adtk fixtype iditype dotiditype +%type <ids> ids rids nids nrids tuplist forms ftypes ftype + bclab bctarg ptags rptags polydec +%type <node> zexp exp monexp term elist zelist celist + idatom idterms idterm idlist + initlist elemlist elem qual + decl topdecls topdecl fndef fbody stmt stmts qstmts qbodies cqstmts cqbodies + mdecl adtdecl mfield mfields field fields fnname + pstmts pbodies pqual pfields pfbody pdecl dfield dfields + eqstmts eqbodies idexc edecl raises tpoly tpolys texp export exportlist forpoly +%type <types> types + +%right <tok.src> '=' Landeq Loreq Lxoreq Llsheq Lrsheq + Laddeq Lsubeq Lmuleq Ldiveq Lmodeq Lexpeq Ldeclas +%left <tok.src> Lload +%left <tok.src> Loror +%left <tok.src> Landand +%right <tok.src> Lcons +%left <tok.src> '|' +%left <tok.src> '^' +%left <tok.src> '&' +%left <tok.src> Leq Lneq +%left <tok.src> '<' '>' Lleq Lgeq +%left <tok.src> Llsh Lrsh +%left <tok.src> '+' '-' +%left <tok.src> '*' '/' '%' +%right <tok.src> Lexp +%right <tok.src> Lcomm + +%left <tok.src> '(' ')' '[' ']' Linc Ldec Lof Lref +%right <tok.src> Lif Lelse Lfn ':' Lexcept Lraises +%left <tok.src> Lmdot +%left <tok.src> '.' + +%left <tok.src> Lto +%left <tok.src> Lor + + +%nonassoc <tok.v.rval> Lrconst +%nonassoc <tok.v.ival> Lconst +%nonassoc <tok.v.idval> Lid Ltid Lsconst +%nonassoc <tok.src> Llabs Lnil + '!' '~' Llen Lhd Ltl Ltagof + '{' '}' ';' + Limplement Limport Linclude + Lcon Ltype Lmodule Lcyclic + Ladt Larray Llist Lchan Lself + Ldo Lwhile Lfor Lbreak + Lalt Lcase Lpick Lcont + Lreturn Lexit Lspawn Lraise Lfix +%% +prog : Limplement ids ';' + { + impmods = $2; + } topdecls + { + tree = rotater($5); + } + | topdecls + { + impmods = nil; + tree = rotater($1); + } + ; + +topdecls: topdecl + | topdecls topdecl + { + if($1 == nil) + $$ = $2; + else if($2 == nil) + $$ = $1; + else + $$ = mkbin(Oseq, $1, $2); + } + ; + +topdecl : error ';' + { + $$ = nil; + } + | decl + | fndef + | adtdecl ';' + | mdecl ';' + | idatom '=' exp ';' + { + $$ = mkbin(Oas, $1, $3); + } + | idterm '=' exp ';' + { + $$ = mkbin(Oas, $1, $3); + } + | idatom Ldeclas exp ';' + { + $$ = mkbin(Odas, $1, $3); + } + | idterm Ldeclas exp ';' + { + $$ = mkbin(Odas, $1, $3); + } + | idterms ':' type ';' + { + yyerror("illegal declaration"); + $$ = nil; + } + | idterms ':' type '=' exp ';' + { + yyerror("illegal declaration"); + $$ = nil; + } + ; + +idterms : idterm + | idterms ',' idterm + { + $$ = mkbin(Oseq, $1, $3); + } + ; + +decl : Linclude Lsconst ';' + { + includef($2); + $$ = nil; + } + | ids ':' Ltype type ';' + { + $$ = typedecl($1, $4); + } + | ids ':' Limport exp ';' + { + $$ = importdecl($4, $1); + $$->src.start = $1->src.start; + $$->src.stop = $5.stop; + } + | ids ':' type ';' + { + $$ = vardecl($1, $3); + } + | ids ':' type '=' exp ';' + { + $$ = mkbin(Ovardecli, vardecl($1, $3), varinit($1, $5)); + } + | ids ':' Lcon exp ';' + { + $$ = condecl($1, $4); + } + | edecl + ; + +edecl : ids ':' Lexcept ';' + { + $$ = exdecl($1, nil); + } + | ids ':' Lexcept '(' tuplist ')' ';' + { + $$ = exdecl($1, revids($5)); + } + ; + +mdecl : ids ':' Lmodule '{' mfields '}' + { + $1->src.stop = $6.stop; + $$ = moddecl($1, rotater($5)); + } + ; + +mfields : + { + $$ = nil; + } + | mfields mfield + { + if($1 == nil) + $$ = $2; + else if($2 == nil) + $$ = $1; + else + $$ = mkn(Oseq, $1, $2); + } + | error + { + $$ = nil; + } + ; + +mfield : ids ':' type ';' + { + $$ = fielddecl(Dglobal, typeids($1, $3)); + } + | adtdecl ';' + | ids ':' Ltype type ';' + { + $$ = typedecl($1, $4); + } + | ids ':' Lcon exp ';' + { + $$ = condecl($1, $4); + } + | edecl + ; + +adtdecl : ids ':' Ladt polydec '{' fields '}' forpoly + { + $1->src.stop = $7.stop; + $$ = adtdecl($1, rotater($6)); + $$->ty->polys = $4; + $$->ty->val = rotater($8); + } + | ids ':' Ladt polydec Lfor '{' tpolys '}' '{' fields '}' + { + $1->src.stop = $11.stop; + $$ = adtdecl($1, rotater($10)); + $$->ty->polys = $4; + $$->ty->val = rotater($7); + } + ; + +forpoly : + { + $$ = nil; + } + | Lfor '{' tpolys '}' + { + $$ = $3; + } + ; + +fields : + { + $$ = nil; + } + | fields field + { + if($1 == nil) + $$ = $2; + else if($2 == nil) + $$ = $1; + else + $$ = mkn(Oseq, $1, $2); + } + | error + { + $$ = nil; + } + ; + +field : dfield + | pdecl + | ids ':' Lcon exp ';' + { + $$ = condecl($1, $4); + } + ; + +dfields : + { + $$ = nil; + } + | dfields dfield + { + if($1 == nil) + $$ = $2; + else if($2 == nil) + $$ = $1; + else + $$ = mkn(Oseq, $1, $2); + } + ; + +dfield : ids ':' Lcyclic type ';' + { + Decl *d; + + for(d = $1; d != nil; d = d->next) + d->cyc = 1; + $$ = fielddecl(Dfield, typeids($1, $4)); + } + | ids ':' type ';' + { + $$ = fielddecl(Dfield, typeids($1, $3)); + } + ; + +pdecl : Lpick '{' pfields '}' + { + $$ = $3; + } + ; + +pfields : pfbody dfields + { + $1->right->right = $2; + $$ = $1; + } + | pfbody error + { + $$ = nil; + } + | error + { + $$ = nil; + } + ; + +pfbody : ptags Llabs + { + $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $1), nil)); + typeids($1, mktype(&$1->src.start, &$1->src.stop, Tadtpick, nil, nil)); + } + | pfbody dfields ptags Llabs + { + $1->right->right = $2; + $$ = mkn(Opickdecl, $1, mkn(Oseq, fielddecl(Dtag, $3), nil)); + typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil)); + } + | pfbody error ptags Llabs + { + $$ = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, $3), nil)); + typeids($3, mktype(&$3->src.start, &$3->src.stop, Tadtpick, nil, nil)); + } + ; + +ptags : rptags + { + $$ = revids($1); + } + ; + +rptags : Lid + { + $$ = mkids(&$<tok.src>1, $1, nil, nil); + } + | rptags Lor Lid + { + $$ = mkids(&$<tok.src>3, $3, nil, $1); + } + ; + +ids : rids + { + $$ = revids($1); + } + ; + +rids : Lid + { + $$ = mkids(&$<tok.src>1, $1, nil, nil); + } + | rids ',' Lid + { + $$ = mkids(&$<tok.src>3, $3, nil, $1); + } + ; + +fixtype : Lfix '(' exp ',' exp ')' + { + $$ = mktype(&$1.start, &$6.stop, Tfix, nil, nil); + $$->val = mkbin(Oseq, $3, $5); + } + | Lfix '(' exp ')' + { + $$ = mktype(&$1.start, &$4.stop, Tfix, nil, nil); + $$->val = $3; + } + ; + +types : type + { + $$ = addtype($1, nil); + } + | Lcyclic type + { + $$ = addtype($2, nil); + $2->flags |= CYCLIC; + } + | types ',' type + { + $$ = addtype($3, $1); + } + | types ',' Lcyclic type + { + $$ = addtype($4, $1); + $4->flags |= CYCLIC; + } + ; + +type : Ltid + { + $$ = mkidtype(&$<tok.src>1, $1); + } + | iditype + { + $$ = $1; + } + | dotiditype + { + $$ = $1; + } + | type Lmdot Lid + { + $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + } + | type Lmdot Lid '[' types ']' + { + $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + $$ = mkinsttype(&$1->src, $$, $5); + } + | Lref type + { + $$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil); + } + | Lchan Lof type + { + $$ = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil); + } + | '(' tuplist ')' + { + if($2->next == nil) + $$ = $2->ty; + else + $$ = mktype(&$1.start, &$3.stop, Ttuple, nil, revids($2)); + } + | Larray Lof type + { + $$ = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil); + } + | Llist Lof type + { + $$ = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil); + } + | Lfn polydec fnargretp raises + { + $3->src.start = $1.start; + $3->polys = $2; + $3->u.eraises = $4; + $$ = $3; + } + | fixtype +/* + | Lexcept + { + $$ = mktype(&$1.start, &$1.stop, Texception, nil, nil); + $$->cons = 1; + } + | Lexcept '(' tuplist ')' + { + $$ = mktype(&$1.start, &$4.stop, Texception, nil, revids($3)); + $$->cons = 1; + } +*/ + ; + +iditype : Lid + { + $$ = mkidtype(&$<tok.src>1, $1); + } + | Lid '[' types ']' + { + $$ = mkinsttype(&$<tok.src>1, mkidtype(&$<tok.src>1, $1), $3); + } + ; + +dotiditype : type '.' Lid + { + $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + } + | type '.' Lid '[' types ']' + { + $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + $$ = mkinsttype(&$1->src, $$, $5); + } + ; + +tuplist : type + { + $$ = mkids(&$1->src, nil, $1, nil); + } + | tuplist ',' type + { + $$ = mkids(&$1->src, nil, $3, $1); + } + ; + +polydec : + { + $$ = nil; + } + | '[' ids ']' + { + $$ = polydecl($2); + } + ; + +fnarg : '(' forms ')' + { + $$ = mktype(&$1.start, &$3.stop, Tfn, tnone, $2); + } + | '(' '*' ')' + { + $$ = mktype(&$1.start, &$3.stop, Tfn, tnone, nil); + $$->varargs = 1; + } + | '(' ftypes ',' '*' ')' + { + $$ = mktype(&$1.start, &$5.stop, Tfn, tnone, $2); + $$->varargs = 1; + } + ; + +fnargret: fnarg %prec ':' + { + $$ = $1; + } + | fnarg ':' type + { + $1->tof = $3; + $1->src.stop = $3->src.stop; + $$ = $1; + } + ; + +fnargretp: fnargret %prec '=' + { + $$ = $1; + } + | fnargret Lfor '{' tpolys '}' + { + $$ = $1; + $$->val = rotater($4); + } + ; + +forms : + { + $$ = nil; + } + | ftypes + ; + +ftypes : ftype + | ftypes ',' ftype + { + $$ = appdecls($1, $3); + } + ; + +ftype : nids ':' type + { + $$ = typeids($1, $3); + } + | nids ':' adtk + { + Decl *d; + + $$ = typeids($1, $3); + for(d = $$; d != nil; d = d->next) + d->implicit = 1; + } + | idterms ':' type + { + $$ = mkids(&$1->src, enter("junk", 0), $3, nil); + $$->store = Darg; + yyerror("illegal argument declaraion"); + } + | idterms ':' adtk + { + $$ = mkids(&$1->src, enter("junk", 0), $3, nil); + $$->store = Darg; + yyerror("illegal argument declaraion"); + } + ; + +nids : nrids + { + $$ = revids($1); + } + ; + +nrids : Lid + { + $$ = mkids(&$<tok.src>1, $1, nil, nil); + $$->store = Darg; + } + | Lnil + { + $$ = mkids(&$1, nil, nil, nil); + $$->store = Darg; + } + | nrids ',' Lid + { + $$ = mkids(&$<tok.src>3, $3, nil, $1); + $$->store = Darg; + } + | nrids ',' Lnil + { + $$ = mkids(&$3, nil, nil, $1); + $$->store = Darg; + } + ; + +/* +adttype : Lid + { + $$ = mkidtype(&$<tok.src>1, $1); + } + | adttype '.' Lid + { + $$ = mkdottype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + } + | adttype Lmdot Lid + { + $$ = mkarrowtype(&$1->src.start, &$<tok.src>3.stop, $1, $3); + } + | Lref adttype + { + $$ = mktype(&$1.start, &$2->src.stop, Tref, $2, nil); + } + ; + +adtk : Lself adttype + { + $$ = $2; + } + ; +*/ + +adtk : Lself iditype + { + $$ = $2; + } + | Lself Lref iditype + { + $$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil); + } + | Lself dotiditype + { + $$ = $2; + } + | Lself Lref dotiditype + { + $$ = mktype(&$<tok.src>2.start, &$<tok.src>3.stop, Tref, $3, nil); + } + ; + +fndef : fnname fnargretp raises fbody + { + $$ = fndecl($1, $2, $4); + nfns++; + /* patch up polydecs */ + if($1->op == Odot){ + if($1->right->left != nil){ + $2->polys = $1->right->left->decl; + $1->right->left = nil; + } + if($1->left->op == Oname && $1->left->left != nil){ + $$->decl = $1->left->left->decl; + $1->left->left = nil; + } + } + else{ + if($1->left != nil){ + $2->polys = $1->left->decl; + $1->left = nil; + } + } + $2->u.eraises = $3; + $$->src = $1->src; + } + ; + +raises : Lraises '(' idlist ')' + { + $$ = mkn(Otuple, rotater($3), nil); + $$->src.start = $1.start; + $$->src.stop = $4.stop; + } + | Lraises idatom + { + $$ = mkn(Otuple, mkunary(Oseq, $2), nil); + $$->src.start = $1.start; + $$->src.stop = $2->src.stop; + } + | /* empty */ %prec Lraises + { + $$ = nil; + } + ; + +fbody : '{' stmts '}' + { + if($2 == nil){ + $2 = mkn(Onothing, nil, nil); + $2->src.start = curline(); + $2->src.stop = $2->src.start; + } + $$ = rotater($2); + $$->src.start = $1.start; + $$->src.stop = $3.stop; + } + | error '}' + { + $$ = mkn(Onothing, nil, nil); + } + | error '{' stmts '}' + { + $$ = mkn(Onothing, nil, nil); + } + ; + +fnname : Lid polydec + { + $$ = mkname(&$<tok.src>1, $1); + if($2 != nil){ + $$->left = mkn(Onothing, nil ,nil); + $$->left->decl = $2; + } + } + | fnname '.' Lid polydec + { + $$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3)); + if($4 != nil){ + $$->right->left = mkn(Onothing, nil ,nil); + $$->right->left->decl = $4; + } + } + ; + +stmts : + { + $$ = nil; + } + | stmts decl + { + if($1 == nil) + $$ = $2; + else if($2 == nil) + $$ = $1; + else + $$ = mkbin(Oseq, $1, $2); + } + | stmts stmt + { + if($1 == nil) + $$ = $2; + else + $$ = mkbin(Oseq, $1, $2); + } + ; + +elists : '(' elist ')' + | elists ',' '(' elist ')' + ; + +stmt : error ';' + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | error '}' + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | error '{' stmts '}' + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | '{' stmts '}' + { + if($2 == nil){ + $2 = mkn(Onothing, nil, nil); + $2->src.start = curline(); + $2->src.stop = $2->src.start; + } + $$ = mkscope(rotater($2)); + } + | elists ':' type ';' + { + yyerror("illegal declaration"); + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | elists ':' type '=' exp';' + { + yyerror("illegal declaration"); + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | zexp ';' + { + $$ = $1; + } + | Lif '(' exp ')' stmt + { + $$ = mkn(Oif, $3, mkunary(Oseq, $5)); + $$->src.start = $1.start; + $$->src.stop = $5->src.stop; + } + | Lif '(' exp ')' stmt Lelse stmt + { + $$ = mkn(Oif, $3, mkbin(Oseq, $5, $7)); + $$->src.start = $1.start; + $$->src.stop = $7->src.stop; + } + | bclab Lfor '(' zexp ';' zexp ';' zexp ')' stmt + { + $$ = mkunary(Oseq, $10); + if($8->op != Onothing) + $$->right = $8; + $$ = mkbin(Ofor, $6, $$); + $$->decl = $1; + if($4->op != Onothing) + $$ = mkbin(Oseq, $4, $$); + } + | bclab Lwhile '(' zexp ')' stmt + { + $$ = mkn(Ofor, $4, mkunary(Oseq, $6)); + $$->src.start = $2.start; + $$->src.stop = $6->src.stop; + $$->decl = $1; + } + | bclab Ldo stmt Lwhile '(' zexp ')' ';' + { + $$ = mkn(Odo, $6, $3); + $$->src.start = $2.start; + $$->src.stop = $7.stop; + $$->decl = $1; + } + | Lbreak bctarg ';' + { + $$ = mkn(Obreak, nil, nil); + $$->decl = $2; + $$->src = $1; + } + | Lcont bctarg ';' + { + $$ = mkn(Ocont, nil, nil); + $$->decl = $2; + $$->src = $1; + } + | Lreturn zexp ';' + { + $$ = mkn(Oret, $2, nil); + $$->src = $1; + if($2->op == Onothing) + $$->left = nil; + else + $$->src.stop = $2->src.stop; + } + | Lspawn exp ';' + { + $$ = mkn(Ospawn, $2, nil); + $$->src.start = $1.start; + $$->src.stop = $2->src.stop; + } + | Lraise zexp ';' + { + $$ = mkn(Oraise, $2, nil); + $$->src.start = $1.start; + $$->src.stop = $2->src.stop; + } + | bclab Lcase exp '{' cqstmts '}' + { + $$ = mkn(Ocase, $3, caselist($5, nil)); + $$->src = $3->src; + $$->decl = $1; + } + | bclab Lalt '{' qstmts '}' + { + $$ = mkn(Oalt, caselist($4, nil), nil); + $$->src = $2; + $$->decl = $1; + } + | bclab Lpick Lid Ldeclas exp '{' pstmts '}' + { + $$ = mkn(Opick, mkbin(Odas, mkname(&$<tok.src>3, $3), $5), caselist($7, nil)); + $$->src.start = $<tok.src>3.start; + $$->src.stop = $5->src.stop; + $$->decl = $1; + } + | Lexit ';' + { + $$ = mkn(Oexit, nil, nil); + $$->src = $1; + } + | '{' stmts '}' Lexcept idexc '{' eqstmts '}' + { + if($2 == nil){ + $2 = mkn(Onothing, nil, nil); + $2->src.start = curline(); + $2->src.stop = curline(); + } + $2 = mkscope(rotater($2)); + $$ = mkbin(Oexstmt, $2, mkn(Oexcept, $5, caselist($7, nil))); + } +/* + | stmt Lexcept idexc '{' eqstmts '}' + { + $$ = mkbin(Oexstmt, $1, mkn(Oexcept, $3, caselist($5, nil))); + } +*/ + ; + +bclab : + { + $$ = nil; + } + | ids ':' + { + if($1->next != nil) + yyerror("only one identifier allowed in a label"); + $$ = $1; + } + ; + +bctarg : + { + $$ = nil; + } + | Lid + { + $$ = mkids(&$<tok.src>1, $1, nil, nil); + } + ; + +qstmts : qbodies stmts + { + $1->left->right->right = $2; + $$ = $1; + } + ; + +qbodies : qual Llabs + { + $$ = mkunary(Oseq, mkscope(mkunary(Olabel, rotater($1)))); + } + | qbodies stmts qual Llabs + { + $1->left->right->right = $2; + $$ = mkbin(Oseq, mkscope(mkunary(Olabel, rotater($3))), $1); + } + ; + +cqstmts : cqbodies stmts + { + $1->left->right = mkscope($2); + $$ = $1; + } + ; + +cqbodies : qual Llabs + { + $$ = mkunary(Oseq, mkunary(Olabel, rotater($1))); + } + | cqbodies stmts qual Llabs + { + $1->left->right = mkscope($2); + $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1); + } + ; + +eqstmts : eqbodies stmts + { + $1->left->right = mkscope($2); + $$ = $1; + } + ; + +eqbodies : qual Llabs + { + $$ = mkunary(Oseq, mkunary(Olabel, rotater($1))); + } + | eqbodies stmts qual Llabs + { + $1->left->right = mkscope($2); + $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1); + } + ; + +qual : exp + | exp Lto exp + { + $$ = mkbin(Orange, $1, $3); + } + | '*' + { + $$ = mkn(Owild, nil, nil); + $$->src = $1; + } + | qual Lor qual + { + $$ = mkbin(Oseq, $1, $3); + } + | error + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + ; + +pstmts : pbodies stmts + { + $1->left->right = mkscope($2); + $$ = $1; + } + ; + +pbodies : pqual Llabs + { + $$ = mkunary(Oseq, mkunary(Olabel, rotater($1))); + } + | pbodies stmts pqual Llabs + { + $1->left->right = mkscope($2); + $$ = mkbin(Oseq, mkunary(Olabel, rotater($3)), $1); + } + ; + +pqual : Lid + { + $$ = mkname(&$<tok>1.src, $1); + } + | '*' + { + $$ = mkn(Owild, nil, nil); + $$->src = $1; + } + | pqual Lor pqual + { + $$ = mkbin(Oseq, $1, $3); + } + | error + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + ; + +zexp : + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = curline(); + $$->src.stop = $$->src.start; + } + | exp + ; + +exp : monexp + | exp '=' exp + { + $$ = mkbin(Oas, $1, $3); + } + | exp Landeq exp + { + $$ = mkbin(Oandas, $1, $3); + } + | exp Loreq exp + { + $$ = mkbin(Ooras, $1, $3); + } + | exp Lxoreq exp + { + $$ = mkbin(Oxoras, $1, $3); + } + | exp Llsheq exp + { + $$ = mkbin(Olshas, $1, $3); + } + | exp Lrsheq exp + { + $$ = mkbin(Orshas, $1, $3); + } + | exp Laddeq exp + { + $$ = mkbin(Oaddas, $1, $3); + } + | exp Lsubeq exp + { + $$ = mkbin(Osubas, $1, $3); + } + | exp Lmuleq exp + { + $$ = mkbin(Omulas, $1, $3); + } + | exp Ldiveq exp + { + $$ = mkbin(Odivas, $1, $3); + } + | exp Lmodeq exp + { + $$ = mkbin(Omodas, $1, $3); + } + | exp Lexpeq exp + { + $$ = mkbin(Oexpas, $1, $3); + } + | exp Lcomm '=' exp + { + $$ = mkbin(Osnd, $1, $4); + } + | exp Ldeclas exp + { + $$ = mkbin(Odas, $1, $3); + } + | Lload Lid exp %prec Lload + { + $$ = mkn(Oload, $3, nil); + $$->src.start = $<tok.src.start>1; + $$->src.stop = $3->src.stop; + $$->ty = mkidtype(&$<tok.src>2, $2); + } + | exp Lexp exp + { + $$ = mkbin(Oexp, $1, $3); + } + | exp '*' exp + { + $$ = mkbin(Omul, $1, $3); + } + | exp '/' exp + { + $$ = mkbin(Odiv, $1, $3); + } + | exp '%' exp + { + $$ = mkbin(Omod, $1, $3); + } + | exp '+' exp + { + $$ = mkbin(Oadd, $1, $3); + } + | exp '-' exp + { + $$ = mkbin(Osub, $1, $3); + } + | exp Lrsh exp + { + $$ = mkbin(Orsh, $1, $3); + } + | exp Llsh exp + { + $$ = mkbin(Olsh, $1, $3); + } + | exp '<' exp + { + $$ = mkbin(Olt, $1, $3); + } + | exp '>' exp + { + $$ = mkbin(Ogt, $1, $3); + } + | exp Lleq exp + { + $$ = mkbin(Oleq, $1, $3); + } + | exp Lgeq exp + { + $$ = mkbin(Ogeq, $1, $3); + } + | exp Leq exp + { + $$ = mkbin(Oeq, $1, $3); + } + | exp Lneq exp + { + $$ = mkbin(Oneq, $1, $3); + } + | exp '&' exp + { + $$ = mkbin(Oand, $1, $3); + } + | exp '^' exp + { + $$ = mkbin(Oxor, $1, $3); + } + | exp '|' exp + { + $$ = mkbin(Oor, $1, $3); + } + | exp Lcons exp + { + $$ = mkbin(Ocons, $1, $3); + } + | exp Landand exp + { + $$ = mkbin(Oandand, $1, $3); + } + | exp Loror exp + { + $$ = mkbin(Ooror, $1, $3); + } + ; + +monexp : term + | '+' monexp + { + $2->src.start = $1.start; + $$ = $2; + } + | '-' monexp + { + $$ = mkunary(Oneg, $2); + $$->src.start = $1.start; + } + | '!' monexp + { + $$ = mkunary(Onot, $2); + $$->src.start = $1.start; + } + | '~' monexp + { + $$ = mkunary(Ocomp, $2); + $$->src.start = $1.start; + } + | '*' monexp + { + $$ = mkunary(Oind, $2); + $$->src.start = $1.start; + } + | Linc monexp + { + $$ = mkunary(Opreinc, $2); + $$->src.start = $1.start; + } + | Ldec monexp + { + $$ = mkunary(Opredec, $2); + $$->src.start = $1.start; + } + | Lcomm monexp + { + $$ = mkunary(Orcv, $2); + $$->src.start = $1.start; + } + | Lhd monexp + { + $$ = mkunary(Ohd, $2); + $$->src.start = $1.start; + } + | Ltl monexp + { + $$ = mkunary(Otl, $2); + $$->src.start = $1.start; + } + | Llen monexp + { + $$ = mkunary(Olen, $2); + $$->src.start = $1.start; + } + | Lref monexp + { + $$ = mkunary(Oref, $2); + $$->src.start = $1.start; + } + | Ltagof monexp + { + $$ = mkunary(Otagof, $2); + $$->src.start = $1.start; + } + | Larray '[' exp ']' Lof type + { + $$ = mkn(Oarray, $3, nil); + $$->ty = mktype(&$1.start, &$6->src.stop, Tarray, $6, nil); + $$->src = $$->ty->src; + } + | Larray '[' exp ']' Lof '{' initlist '}' + { + $$ = mkn(Oarray, $3, $7); + $$->src.start = $1.start; + $$->src.stop = $8.stop; + } + | Larray '[' ']' Lof '{' initlist '}' + { + $$ = mkn(Onothing, nil, nil); + $$->src.start = $2.start; + $$->src.stop = $3.stop; + $$ = mkn(Oarray, $$, $6); + $$->src.start = $1.start; + $$->src.stop = $7.stop; + } + | Llist Lof '{' celist '}' + { + $$ = etolist($4); + $$->src.start = $1.start; + $$->src.stop = $5.stop; + } + | Lchan Lof type + { + $$ = mkn(Ochan, nil, nil); + $$->ty = mktype(&$1.start, &$3->src.stop, Tchan, $3, nil); + $$->src = $$->ty->src; + } + | Lchan '[' exp ']' Lof type + { + $$ = mkn(Ochan, $3, nil); + $$->ty = mktype(&$1.start, &$6->src.stop, Tchan, $6, nil); + $$->src = $$->ty->src; + } + | Larray Lof Ltid monexp + { + $$ = mkunary(Ocast, $4); + $$->ty = mktype(&$1.start, &$4->src.stop, Tarray, mkidtype(&$<tok.src>3, $3), nil); + $$->src = $$->ty->src; + } + | Ltid monexp + { + $$ = mkunary(Ocast, $2); + $$->src.start = $<tok.src>1.start; + $$->ty = mkidtype(&$$->src, $1); + } + | Lid monexp + { + $$ = mkunary(Ocast, $2); + $$->src.start = $<tok.src>1.start; + $$->ty = mkidtype(&$$->src, $1); + } + | fixtype monexp + { + $$ = mkunary(Ocast, $2); + $$->src.start = $<tok.src>1.start; + $$->ty = $1; + } + ; + +term : idatom + | term '(' zelist ')' + { + $$ = mkn(Ocall, $1, $3); + $$->src.start = $1->src.start; + $$->src.stop = $4.stop; + } + | '(' elist ')' + { + $$ = $2; + if($2->op == Oseq) + $$ = mkn(Otuple, rotater($2), nil); + else + $$->flags |= PARENS; + $$->src.start = $1.start; + $$->src.stop = $3.stop; + } + | term '.' Lid + { + $$ = mkbin(Odot, $1, mkname(&$<tok.src>3, $3)); + } + | term Lmdot term + { + $$ = mkbin(Omdot, $1, $3); + } + | term '[' export ']' + { + $$ = mkbin(Oindex, $1, $3); + $$->src.stop = $4.stop; + } + | term '[' zexp ':' zexp ']' + { + if($3->op == Onothing) + $3->src = $4; + if($5->op == Onothing) + $5->src = $4; + $$ = mkbin(Oslice, $1, mkbin(Oseq, $3, $5)); + $$->src.stop = $6.stop; + } + | term Linc + { + $$ = mkunary(Oinc, $1); + $$->src.stop = $2.stop; + } + | term Ldec + { + $$ = mkunary(Odec, $1); + $$->src.stop = $2.stop; + } + | Lsconst + { + $$ = mksconst(&$<tok.src>1, $1); + } + | Lconst + { + $$ = mkconst(&$<tok.src>1, $1); + if($1 > 0x7fffffff || $1 < -0x7fffffff) + $$->ty = tbig; + $$ = $$; + } + | Lrconst + { + $$ = mkrconst(&$<tok.src>1, $1); + } + | term '[' exportlist ',' export ']' + { + $$ = mkbin(Oindex, $1, rotater(mkbin(Oseq, $3, $5))); + $$->src.stop = $6.stop; + } + ; + +idatom : Lid + { + $$ = mkname(&$<tok.src>1, $1); + } + | Lnil + { + $$ = mknil(&$<tok.src>1); + } + ; + +idterm : '(' idlist ')' + { + $$ = mkn(Otuple, rotater($2), nil); + $$->src.start = $1.start; + $$->src.stop = $3.stop; + } + ; + +exportlist : export + | exportlist ',' export + { + $$ = mkbin(Oseq, $1, $3); + } + ; + +export : exp + | texp + ; + +texp : Ltid + { + $$ = mkn(Otype, nil, nil); + $$->ty = mkidtype(&$<tok.src>1, $1); + $$->src = $$->ty->src; + } + | Larray Lof type + { + $$ = mkn(Otype, nil, nil); + $$->ty = mktype(&$1.start, &$3->src.stop, Tarray, $3, nil); + $$->src = $$->ty->src; + } + | Llist Lof type + { + $$ = mkn(Otype, nil, nil); + $$->ty = mktype(&$1.start, &$3->src.stop, Tlist, $3, nil); + $$->src = $$->ty->src; + } + | Lcyclic type + { + $$ = mkn(Otype, nil ,nil); + $$->ty = $2; + $$->ty->flags |= CYCLIC; + $$->src = $$->ty->src; + } + ; + +idexc : Lid + { + $$ = mkname(&$<tok.src>1, $1); + } + | /* empty */ + { + $$ = nil; + } + ; + +idlist : idterm + | idatom + | idlist ',' idterm + { + $$ = mkbin(Oseq, $1, $3); + } + | idlist ',' idatom + { + $$ = mkbin(Oseq, $1, $3); + } + ; + +zelist : + { + $$ = nil; + } + | elist + { + $$ = rotater($1); + } + ; + +celist : elist + | elist ',' + ; + +elist : exp + | elist ',' exp + { + $$ = mkbin(Oseq, $1, $3); + } + ; + +initlist : elemlist + { + $$ = rotater($1); + } + | elemlist ',' + { + $$ = rotater($1); + } + ; + +elemlist : elem + | elemlist ',' elem + { + $$ = mkbin(Oseq, $1, $3); + } + ; + +elem : exp + { + $$ = mkn(Oelem, nil, $1); + $$->src = $1->src; + } + | qual Llabs exp + { + $$ = mkbin(Oelem, rotater($1), $3); + } + ; + +/* +tpoly : ids Llabs '{' dfields '}' + { + $$ = typedecl($1, mktype(&$1->src.start, &$5.stop, Tpoly, nil, nil)); + $$->left = rotater($4); + } + ; + +tpolys : tpoly + { + $$ = $1; + } + | tpolys tpoly + { + $$ = mkbin(Oseq, $1, $2); + } + ; +*/ + +tpolys : tpoly dfields + { + if($1->op == Oseq) + $1->right->left = rotater($2); + else + $1->left = rotater($2); + $$ = $1; + } + ; + +tpoly : ids Llabs + { + $$ = typedecl($1, mktype(&$1->src.start, &$2.stop, Tpoly, nil, nil)); + } + | tpoly dfields ids Llabs + { + if($1->op == Oseq) + $1->right->left = rotater($2); + else + $1->left = rotater($2); + $$ = mkbin(Oseq, $1, typedecl($3, mktype(&$3->src.start, &$4.stop, Tpoly, nil, nil))); + } + ; + +%% + +static char *mkfileext(char*, char*, char*); +static void usage(void); + +static int dosym; +static int toterrors; +static ulong canonnanbits[] = { 0x7fffffff, 0xffffffff}; +static char* infile; + +#define SLASHMOD "/module" + +static char* +getroot(void) +{ + int n; + char *e, *l, *s; + + if((e = getenv("EMU")) != nil){ + for(s = e; *e != '\0'; e++){ + if(*e == '-' && *(e+1) == 'r' && (e == s || *(e-1) == ' ' || *(e-1) == '\t')){ + e += 2; + l = strchr(e, ' '); + if(l != nil) + *l = '\0'; + if((n = strlen(e)) > 0){ + s = malloc(n+1); + strcpy(s, e); + return s; + } + } + } + } + if((e = getenv("ROOT")) != nil) + return strdup(e); + return nil; +} + +void +main(int argc, char *argv[]) +{ + char *s, *ofile, *ext, *root; + int i; + + FPinit(); + FPcontrol(0, INVAL|ZDIV|OVFL|UNFL|INEX); + canonnan = canontod(canonnanbits); + + fmtinstall('D', dotconv); + fmtinstall('I', instconv); + fmtinstall('K', declconv); + fmtinstall('k', storeconv); + fmtinstall('L', lineconv); + fmtinstall('M', mapconv); + fmtinstall('n', nodeconv); /* exp structure */ + fmtinstall('O', opconv); + fmtinstall('g', gfltconv); + fmtinstall('Q', etconv); /* src expression with type */ + fmtinstall('R', ctypeconv); /* c equivalent type */ + fmtinstall('P', ctypeconv); /* c equivalent type - pointer type */ + fmtinstall('T', typeconv); /* source style types */ + fmtinstall('t', stypeconv); /* structurally descriptive type */ + fmtinstall('U', srcconv); + fmtinstall('v', expconv); /* src expression */ + fmtinstall('V', expconv); /* src expression in '' */ + lexinit(); + typeinit(); + optabinit(); + + gendis = 1; + asmsym = 0; + maxerr = 20; + ofile = nil; + ext = nil; + ARGBEGIN{ + case 'D': + /* + * debug flags: + * + * a alt compilation + * A array constructor compilation + * b boolean and branch compilation + * c case compilation + * d function declaration + * D descriptor generation + * e expression compilation + * E addressable expression compilation + * f print arguments for compiled functions + * F constant folding + * g print out globals + * m module declaration and type checking + * n nil references + * s print sizes of output file sections + * S type signing + * t type checking function bodies + * T timing + * v global var and constant compilation + * x adt verification + * Y tuple compilation + * z Z bug fixes + */ + s = ARGF(); + while(s && *s) + debug[*s++] = 1; + break; + case 'I': + s = ARGF(); + if(s == nil) + usage(); + addinclude(s); + break; + case 'G': + asmsym = 1; + break; + case 'S': + gendis = 0; + break; + case 'a': + emitstub = 1; + break; + case 'A': + emitstub = emitdyn = 1; + break; + case 'c': + mustcompile = 1; + break; + case 'C': + dontcompile = 1; + break; + case 'e': + maxerr = 1000; + break; + case 'f': + isfatal = 1; + break; + case 'F': + newfnptr = 1; + break; + case 'g': + dosym = 1; + break; + case 'i': + dontinline = 1; + break; + case 'o': + ofile = ARGF(); + break; + case 'O': + optims = 1; + break; + case 's': + s = ARGF(); + if(s != nil) + fixss = atoi(s); + break; + case 't': + emittab = ARGF(); + if(emittab == nil) + usage(); + break; + case 'T': + emitcode = ARGF(); + if(emitcode == nil) + usage(); + break; + case 'd': + emitcode = ARGF(); + if(emitcode == nil) + usage(); + emitdyn = 1; + break; + case 'w': + superwarn = dowarn; + dowarn = 1; + break; + case 'x': + ext = ARGF(); + break; + case 'X': + signdump = ARGF(); + break; + case 'z': + arrayz = 1; + break; + default: + usage(); + break; + }ARGEND + + if((root = getroot()) != nil){ + char *r; + + r = malloc(strlen(root)+strlen(SLASHMOD)+1); + strcpy(r, root); + strcat(r, SLASHMOD); + addinclude(r); + free(root); + } + else + addinclude(INCPATH); + + if(argc == 0){ + usage(); + }else if(ofile != nil){ + if(argc != 1) + usage(); + translate(argv[0], ofile, mkfileext(ofile, ".dis", ".sbl")); + }else{ + if(ext == nil){ + ext = ".s"; + if(gendis) + ext = ".dis"; + } + for(i = 0; i < argc; i++){ + s = strrchr(argv[i], '/'); + if(s == nil) + s = argv[i]; + else + s++; + if(argc > 1) + print("%s:\n", argv[i]); + ofile = mkfileext(s, ".b", ext); + translate(argv[i], ofile, mkfileext(ofile, ext, ".sbl")); + } + } + if(toterrors) + exits("errors"); + exits(0); +} + +static void +usage(void) +{ + fprint(2, "usage: limbo [-CGSacgwe] [-I incdir] [-o outfile] [-{T|t|d} module] [-D debug] file ...\n"); + exits("usage"); +} + +static char* +mkfileext(char *file, char *oldext, char *ext) +{ + char *ofile; + int n, n2; + + n = strlen(file); + n2 = strlen(oldext); + if(n >= n2 && strcmp(&file[n-n2], oldext) == 0) + n -= n2; + ofile = malloc(n + strlen(ext) + 1); + memmove(ofile, file, n); + strcpy(ofile+n, ext); + return ofile; +} + +void +translate(char *in, char *out, char *dbg) +{ + Decl *entry; + int doemit; + + infile = in; + outfile = out; + symfile = dbg; + errors = 0; + bins[0] = Bopen(in, OREAD); + if(bins[0] == nil){ + fprint(2, "can't open %s: %r\n", in); + toterrors++; + return; + } + doemit = emitstub || emittab || emitcode; + if(!doemit){ + bout = Bopen(out, OWRITE); + if(bout == nil){ + fprint(2, "can't open %s: %r\n", out); + toterrors++; + Bterm(bins[0]); + return; + } + if(dosym){ + bsym = Bopen(dbg, OWRITE); + if(bsym == nil) + fprint(2, "can't open %s: %r\n", dbg); + } + } + + lexstart(in); + + popscopes(); + typestart(); + declstart(); + + yyparse(); + + entry = typecheck(!doemit); + + modcom(entry); + + fns = nil; + nfns = 0; + descriptors = nil; + + if(bout != nil) + Bterm(bout); + if(bsym != nil) + Bterm(bsym); + toterrors += errors; + if(errors && bout != nil) + remove(out); + if(errors && bsym != nil) + remove(dbg); +} + +void +trapFPE(unsigned exception[5], int value[2]) +{ + /* can't happen; it's just here to keep FPinit happy. */ + USED(exception); + USED(value); +} + +static char * +win2inf(char *s) +{ + int nt = 0; + char *t; + + if(strlen(s) > 1 && s[1] == ':'){ + s[1] = '/'; + s++; + nt = 1; + } + for(t = s; *t != '\0'; t++){ + if(*t == '\\') + *t = '/'; + if(nt) + *t = tolower(*t); + } + return s; +} + +static char *cd; + +/* +static char * +pwd(void) +{ + int ok, qid, l1, l2; + Dir d; + char *p; + char hd[64], buf[128], path[256]; + + if(cd != nil) + return cd; + *hd = *path = '\0'; + qid = -1; + strcpy(buf, "."); + for(;;){ + ok = dirstat(buf, &d); + if(ok < 0) + return ""; + if(d.qid.path == qid && strcmp(d.name, hd) == 0) + break; + l1 = strlen(d.name); + l2 = strlen(path); + memmove(path+l1+1, path, l2+1); + memcpy(path+1, d.name, l1); + path[0] = '/'; + strcpy(hd, d.name); + qid = d.qid.path; + strcat(buf, "/.."); + } + p = win2inf(path); + while(*p == '/' && p[1] == '/') + p++; + cd = malloc(strlen(p)+1); + strcpy(cd, p); + return cd; +} +*/ + +static char * +cleann(char *s) +{ + char *p, *r, *t; + char buf[256]; + + r = t = malloc(strlen(s)+1); + strcpy(t, s); + t = win2inf(t); + if(*t != '/'){ + /* p = pwd(); */ + p = win2inf(getwd(buf, sizeof(buf))); + s = malloc(strlen(p)+strlen(t)+2); + strcpy(s, p); + strcat(s, "/"); + strcat(s, t); + } + else{ + s = malloc(strlen(t)+1); + strcpy(s, t); + } + free(r); + /* print("cleann: %s\n", p); */ + return cleanname(s); +} + +char * +srcpath(char *name, int nlen) +{ + int l1, l2; + char *r, *srcp, *t; + + srcp = cleann(infile); + r = getroot(); + if(r == nil){ + l1 = strlen(INCPATH); + r = malloc(l1+1); + strcpy(r, INCPATH); + if(l1 >= strlen(SLASHMOD) && strcmp(r+l1-strlen(SLASHMOD), SLASHMOD) == 0) + r[l1-strlen(SLASHMOD)] = '\0'; + } + t = cleann(r); + free(r); + r = t; + /* srcp relative to r */ + l1 = strlen(srcp); + l2 = strlen(r); + if(l1 >= l2 && strncmp(srcp, r, l2) == 0){ + /* nothing to do */ + }else + l2 = 0; + strncpy(name, srcp+l2, nlen); + name[nlen-1] = '\0'; + free(r); + free(srcp); + /* print("srcpath: %s\n", name); */ + return name; +} diff --git a/limbo/mkfile b/limbo/mkfile new file mode 100644 index 00000000..2dc20720 --- /dev/null +++ b/limbo/mkfile @@ -0,0 +1,41 @@ +<../mkconfig + +TARG=limbo + +OFILES= asm.$O\ + com.$O\ + decls.$O\ + dis.$O\ + dtocanon.$O\ + ecom.$O\ + gen.$O\ + lex.$O\ + nodes.$O\ + optab.$O\ + optim.$O\ + sbl.$O\ + stubs.$O\ + typecheck.$O\ + types.$O\ + y.tab.$O\ + +HFILES= limbo.h\ + fns.h\ + y.tab.h\ + $ROOT/include/interp.h\ + $ROOT/include/isa.h\ + +LIBS= bio\ + math\ + sec\ + mp\ + 9\ + +YFILES= limbo.y + +BIN=$ROOT/$OBJDIR/bin + +<$ROOT/mkfiles/mkone-$SHELLTYPE + +CFLAGS='-DINCPATH="'$ROOT/module'"' $CFLAGS +YFLAGS=-d diff --git a/limbo/nodes.c b/limbo/nodes.c new file mode 100644 index 00000000..920458d3 --- /dev/null +++ b/limbo/nodes.c @@ -0,0 +1,1538 @@ +#include "limbo.h" + +static vlong +ipow(vlong x, int n) +{ + int inv; + vlong r; + + inv = 0; + if(n < 0){ + n = -n; + inv = 1; + } + r = 1; + for(;;){ + if(n&1) + r *= x; + if((n >>= 1) == 0) + break; + x *= x; + } + if(inv) + r = 1/r; + return r; +} + +double +rpow(double x, int n) +{ + int inv; + double r; + + inv = 0; + if(n < 0){ + n = -n; + inv = 1; + } + r = 1; + for(;;){ + if(n&1) + r *= x; + if((n >>= 1) == 0) + break; + x *= x; + } + if(inv) + r = 1/r; + return r; +} + +Long +real2fix(double v, Type *t) +{ + v /= scale(t); + v = v < 0 ? v-0.5: v+0.5; + return v; +} + +Long +fix2fix(Long v, Type *f, Type *t) +{ + double r; + + r = (double)v * (scale(f)/scale(t)); + r = r < 0 ? r-0.5: r+0.5; + return r; +} + +double +fix2real(Long v, Type *f) +{ + return (double)v * scale(f); +} + +int +istuple(Node *n) +{ + Decl *d; + + switch(n->op){ + case Otuple: + return 1; + case Oname: + d = n->decl; + if(d->importid != nil) + d = d->importid; + return d->store == Dconst && (n->ty->kind == Ttuple || n->ty->kind == Tadt); + case Odot: + return 0; /* istuple(n->left); */ + } + return 0; +} + +static Node* +tuplemem(Node *n, Decl *d) +{ + Type *ty; + Decl *ids; + + ty = n->ty; + n = n->left; + for(ids = ty->ids; ids != nil; ids = ids->next){ + if(ids->sym == d->sym) + break; + else + n = n->right; + } + if(n == nil) + fatal("tuplemem cannot cope !\n"); + return n->left; +} + +int +varcom(Decl *v) +{ + Node *n, tn; + + n = v->init; + n = fold(n); + v->init = n; + if(debug['v']) + print("variable '%D' val %V\n", v, n); + if(n == nil) + return 1; + + tn = znode; + tn.op = Oname; + tn.decl = v; + tn.src = v->src; + tn.ty = v->ty; + return initable(&tn, n, 0); +} + +int +initable(Node *v, Node *n, int allocdep) +{ + Node *e; + + switch(n->ty->kind){ + case Tiface: + case Tgoto: + case Tcase: + case Tcasel: + case Tcasec: + case Talt: + case Texcept: + return 1; + case Tint: + case Tbig: + case Tbyte: + case Treal: + case Tstring: + case Tfix: + if(n->op != Oconst) + break; + return 1; + case Tadt: + case Tadtpick: + case Ttuple: + if(n->op == Otuple) + n = n->left; + else if(n->op == Ocall) + n = n->right; + else + break; + for(; n != nil; n = n->right) + if(!initable(v, n->left, allocdep)) + return 0; + return 1; + case Tarray: + if(n->op != Oarray) + break; + if(allocdep >= DADEPTH){ + nerror(v, "%Vs initializer has arrays nested more than %d deep", v, allocdep); + return 0; + } + allocdep++; + usedesc(mktdesc(n->ty->tof)); + if(n->left->op != Oconst){ + nerror(v, "%Vs size is not a constant", v); + return 0; + } + for(e = n->right; e != nil; e = e->right) + if(!initable(v, e->left->right, allocdep)) + return 0; + return 1; + case Tany: + return 1; + case Tref: + case Tlist: + case Tpoly: + default: + nerror(v, "can't initialize %Q", v); + return 0; + } + nerror(v, "%Vs initializer, %V, is not a constant expression", v, n); + return 0; +} + +/* + * merge together two sorted lists, yielding a sorted list + */ +static Node* +elemmerge(Node *e, Node *f) +{ + Node rock, *r; + + r = &rock; + while(e != nil && f != nil){ + if(e->left->left->val <= f->left->left->val){ + r->right = e; + e = e->right; + }else{ + r->right = f; + f = f->right; + } + r = r->right; + } + if(e != nil) + r->right = e; + else + r->right = f; + return rock.right; +} + +/* + * recursively split lists and remerge them after they are sorted + */ +static Node* +recelemsort(Node *e, int n) +{ + Node *r, *ee; + int i, m; + + if(n <= 1) + return e; + m = n / 2 - 1; + ee = e; + for(i = 0; i < m; i++) + ee = ee->right; + r = ee->right; + ee->right = nil; + return elemmerge(recelemsort(e, n / 2), + recelemsort(r, (n + 1) / 2)); +} + +/* + * sort the elems by index; wild card is first + */ +Node* +elemsort(Node *e) +{ + Node *ee; + int n; + + n = 0; + for(ee = e; ee != nil; ee = ee->right){ + if(ee->left->left->op == Owild) + ee->left->left->val = -1; + n++; + } + return recelemsort(e, n); +} + +int +sametree(Node *n1, Node *n2) +{ + if(n1 == n2) + return 1; + if(n1 == nil || n2 == nil) + return 0; + if(n1->op != n2->op || n1->ty != n2->ty) + return 0; + if(n1->op == Oconst){ + switch(n1->ty->kind){ + case Tbig: + case Tbyte: + case Tint: + return n1->val == n2->val; + case Treal: + return n1->rval == n2->rval; + case Tfix: + return n1->val == n2->val && tequal(n1->ty, n2->ty); + case Tstring: + return n1->decl->sym == n2->decl->sym; + } + return 0; + } + return n1->decl == n2->decl && sametree(n1->left, n2->left) && sametree(n1->right, n2->right); +} + +int +occurs(Decl *d, Node *n) +{ + if(n == nil) + return 0; + if(n->op == Oname){ + if(d == n->decl) + return 1; + return 0; + } + return occurs(d, n->left) + occurs(d, n->right); +} + +/* + * left and right subtrees the same + */ +Node* +folds(Node *n) +{ + if(hasside(n, 1)) + return n; + switch(n->op){ + case Oeq: + case Oleq: + case Ogeq: + n->val = 1; + break; + case Osub: + n->val = 0; + n->rval = 0.0; + break; + case Oxor: + case Oneq: + case Olt: + case Ogt: + n->val = 0; + break; + case Oand: + case Oor: + case Oandand: + case Ooror: + return n->left; + default: + return n; + } + n->op = Oconst; + n->left = n->right = nil; + n->decl = nil; + return n; +} + +/* + * constant folding for typechecked expressions, + */ +Node* +fold(Node *n) +{ + if(n == nil) + return nil; + if(debug['F']) + print("fold %n\n", n); + n = efold(n); + if(debug['F']) + print("folded %n\n", n); + return n; +} + +Node* +efold(Node *n) +{ + Decl *d; + Node *left, *right; + + if(n == nil) + return nil; + + left = n->left; + right = n->right; + switch(n->op){ + case Oname: + d = n->decl; + if(d->importid != nil) + d = d->importid; + if(d->store != Dconst){ + if(d->store == Dtag){ + n->op = Oconst; + n->ty = tint; + n->val = d->tag; + } + break; + } + switch(n->ty->kind){ + case Tbig: + n->op = Oconst; + n->val = d->init->val; + break; + case Tbyte: + n->op = Oconst; + n->val = d->init->val & 0xff; + break; + case Tint: + case Tfix: + n->op = Oconst; + n->val = d->init->val; + break; + case Treal: + n->op = Oconst; + n->rval = d->init->rval; + break; + case Tstring: + n->op = Oconst; + n->decl = d->init->decl; + break; + case Ttuple: + *n = *d->init; + break; + case Tadt: + *n = *d->init; + n = rewrite(n); /* was call */ + break; + case Texception: + if(!n->ty->cons) + fatal("non-const exception type in efold"); + n->op = Oconst; + break; + default: + fatal("unknown const type %T in efold", n->ty); + break; + } + break; + case Oadd: + left = efold(left); + right = efold(right); + n->left = left; + n->right = right; + if(n->ty == tstring && right->op == Oconst){ + if(left->op == Oconst) + n = mksconst(&n->src, stringcat(left->decl->sym, right->decl->sym)); + else if(left->op == Oadd && left->ty == tstring && left->right->op == Oconst){ + left->right = mksconst(&n->src, stringcat(left->right->decl->sym, right->decl->sym)); + n = left; + } + } + break; + case Olen: + left = efold(left); + n->left = left; + if(left->ty == tstring && left->op == Oconst) + n = mkconst(&n->src, utflen(left->decl->sym->name)); + break; + case Oslice: + if(right->left->op == Onothing) + right->left = mkconst(&right->left->src, 0); + n->left = efold(left); + n->right = efold(right); + break; + case Oinds: + n->left = left = efold(left); + n->right = right = efold(right); + if(right->op == Oconst && left->op == Oconst){ + ; + } + break; + case Ocast: + n->op = Ocast; + left = efold(left); + n->left = left; + if(n->ty == left->ty || n->ty->kind == Tfix && tequal(n->ty, left->ty)) + return left; + if(left->op == Oconst) + return foldcast(n, left); + break; + case Odot: + case Omdot: + /* + * what about side effects from left? + */ + d = right->decl; + switch(d->store){ + case Dconst: + case Dtag: + case Dtype: + /* + * set it up as a name and let that case do the hard work + */ + n->op = Oname; + n->decl = d; + n->left = nil; + n->right = nil; + return efold(n); + } + n->left = efold(left); + if(n->left->op == Otuple) + n = tuplemem(n->left, d); + else + n->right = efold(right); + break; + case Otagof: + if(n->decl != nil){ + n->op = Oconst; + n->left = nil; + n->right = nil; + n->val = n->decl->tag; + return efold(n); + } + n->left = efold(left); + break; + case Oif: + n->left = left = efold(left); + n->right = right = efold(right); + if(left->op == Oconst){ + if(left->val) + return right->left; + else + return right->right; + } + break; + default: + n->left = efold(left); + n->right = efold(right); + break; + } + + left = n->left; + right = n->right; + if(left == nil) + return n; + + if(right == nil){ + if(left->op == Oconst){ + if(left->ty == tint || left->ty == tbyte || left->ty == tbig) + return foldc(n); + if(left->ty == treal) + return foldr(n); + } + return n; + } + + if(left->op == Oconst){ + switch(n->op){ + case Olsh: + case Orsh: + if(left->val == 0 && !hasside(right, 1)) + return left; + break; + case Ooror: + if(left->ty == tint || left->ty == tbyte || left->ty == tbig){ + if(left->val == 0){ + n = mkbin(Oneq, right, mkconst(&right->src, 0)); + n->ty = right->ty; + n->left->ty = right->ty; + return efold(n); + } + left->val = 1; + return left; + } + break; + case Oandand: + if(left->ty == tint || left->ty == tbyte || left->ty == tbig){ + if(left->val == 0) + return left; + n = mkbin(Oneq, right, mkconst(&right->src, 0)); + n->ty = right->ty; + n->left->ty = right->ty; + return efold(n); + } + break; + } + } + if(left->op == Oconst && right->op != Oconst + && opcommute[n->op] + && n->ty != tstring){ + n->op = opcommute[n->op]; + n->left = right; + n->right = left; + left = right; + right = n->right; + } + if(right->op == Oconst && left->op == n->op && left->right->op == Oconst + && (n->op == Oadd || n->op == Omul || n->op == Oor || n->op == Oxor || n->op == Oand) + && n->ty != tstring){ + n->left = left->left; + left->left = right; + right = efold(left); + n->right = right; + left = n->left; + } + if(right->op == Oconst){ + if(n->op == Oexp && left->ty == treal){ + if(left->op == Oconst) + return foldr(n); + return n; + } + if(right->ty == tint || right->ty == tbyte || left->ty == tbig){ + if(left->op == Oconst) + return foldc(n); + return foldvc(n); + } + if(right->ty == treal && left->op == Oconst) + return foldr(n); + } + if(sametree(left, right)) + return folds(n); + return n; +} + +/* + * does evaluating the node have any side effects? + */ +int +hasside(Node *n, int strict) +{ + for(; n != nil; n = n->right){ + if(sideeffect[n->op] && (strict || n->op != Oadr && n->op != Oind)) + return 1; + if(hasside(n->left, strict)) + return 1; + } + return 0; +} + +int +hascall(Node *n) +{ + for(; n != nil; n = n->right){ + if(n->op == Ocall || n->op == Ospawn) + return 1; + if(hascall(n->left)) + return 1; + } + return 0; +} + +int +hasasgns(Node *n) +{ + if(n == nil) + return 0; + if(n->op != Ocall && isused[n->op] && n->op != Onothing) + return 1; + return hasasgns(n->left) || hasasgns(n->right); +} + +int +nodes(Node *n) +{ + if(n == nil) + return 0; + return 1+nodes(n->left)+nodes(n->right); +} + +Node* +foldcast(Node *n, Node *left) +{ + Real r; + char *buf, *e; + + switch(left->ty->kind){ + case Tint: + left->val &= 0xffffffff; + if(left->val & 0x80000000) + left->val |= (Long)0xffffffff << 32; + return foldcasti(n, left); + case Tbyte: + left->val &= 0xff; + return foldcasti(n, left); + case Tbig: + return foldcasti(n, left); + case Treal: + switch(n->ty->kind){ + case Tint: + case Tbyte: + case Tbig: + r = left->rval; + left->val = r < 0 ? r - .5 : r + .5; + break; + case Tfix: + left->val = real2fix(left->rval, n->ty); + break; + case Tstring: + buf = allocmem(NumSize); + e = seprint(buf, buf+NumSize, "%g", left->rval); + return mksconst(&n->src, enterstring(buf, e-buf)); + default: + return n; + } + break; + case Tfix: + switch(n->ty->kind){ + case Tint: + case Tbyte: + case Tbig: + left->val = fix2real(left->val, left->ty); + break; + case Treal: + left->rval = fix2real(left->val, left->ty); + break; + case Tfix: + if(tequal(left->ty, n->ty)) + return left; + left->val = fix2fix(left->val, left->ty, n->ty); + break; + case Tstring: + buf = allocmem(NumSize); + e = seprint(buf, buf+NumSize, "%g", fix2real(left->val, left->ty)); + return mksconst(&n->src, enterstring(buf, e-buf)); + default: + return n; + } + break; + case Tstring: + switch(n->ty->kind){ + case Tint: + case Tbyte: + case Tbig: + left->val = strtoi(left->decl->sym->name, 10); + break; + case Treal: + left->rval = strtod(left->decl->sym->name, nil); + break; + case Tfix: + left->val = real2fix(strtod(left->decl->sym->name, nil), n->ty); + break; + default: + return n; + } + break; + default: + return n; + } + left->ty = n->ty; + left->src = n->src; + return left; +} + +/* + * left is some kind of int type + */ +Node* +foldcasti(Node *n, Node *left) +{ + char *buf, *e; + + switch(n->ty->kind){ + case Tint: + left->val &= 0xffffffff; + if(left->val & 0x80000000) + left->val |= (Long)0xffffffff << 32; + break; + case Tbyte: + left->val &= 0xff; + break; + case Tbig: + break; + case Treal: + left->rval = left->val; + break; + case Tfix: + left->val = real2fix(left->val, n->ty); + break; + case Tstring: + buf = allocmem(NumSize); + e = seprint(buf, buf+NumSize, "%lld", left->val); + return mksconst(&n->src, enterstring(buf, e-buf)); + default: + return n; + } + left->ty = n->ty; + left->src = n->src; + return left; +} + +/* + * right is a const int + */ +Node* +foldvc(Node *n) +{ + Node *left, *right; + + left = n->left; + right = n->right; + switch(n->op){ + case Oadd: + case Osub: + case Oor: + case Oxor: + case Olsh: + case Orsh: + case Ooror: + if(right->val == 0) + return left; + if(n->op == Ooror && !hasside(left, 1)) + return right; + break; + case Oand: + if(right->val == 0 && !hasside(left, 1)) + return right; + break; + case Omul: + if(right->val == 1) + return left; + if(right->val == 0 && !hasside(left, 1)) + return right; + break; + case Odiv: + if(right->val == 1) + return left; + break; + case Omod: + if(right->val == 1 && !hasside(left, 1)){ + right->val = 0; + return right; + } + break; + case Oexp: + if(right->val == 0){ + right->val = 1; + return right; + } + if(right->val == 1) + return left; + break; + case Oandand: + if(right->val != 0) + return left; + if(!hasside(left, 1)) + return right; + break; + case Oneq: + if(!isrelop[left->op]) + return n; + if(right->val == 0) + return left; + n->op = Onot; + n->right = nil; + break; + case Oeq: + if(!isrelop[left->op]) + return n; + if(right->val != 0) + return left; + n->op = Onot; + n->right = nil; + break; + } + return n; +} + +/* + * left and right are const ints + */ +Node* +foldc(Node *n) +{ + Node *left, *right; + Long lv, v; + int rv, nb; + + left = n->left; + right = n->right; + switch(n->op){ + case Oadd: + v = left->val + right->val; + break; + case Osub: + v = left->val - right->val; + break; + case Omul: + v = left->val * right->val; + break; + case Odiv: + if(right->val == 0){ + nerror(n, "divide by 0 in constant expression"); + return n; + } + v = left->val / right->val; + break; + case Omod: + if(right->val == 0){ + nerror(n, "mod by 0 in constant expression"); + return n; + } + v = left->val % right->val; + break; + case Oexp: + if(left->val == 0 && right->val < 0){ + nerror(n, "0 to negative power in constant expression"); + return n; + } + v = ipow(left->val, right->val); + break; + case Oand: + v = left->val & right->val; + break; + case Oor: + v = left->val | right->val; + break; + case Oxor: + v = left->val ^ right->val; + break; + case Olsh: + lv = left->val; + rv = right->val; + if(rv < 0 || rv >= n->ty->size * 8){ + nwarn(n, "shift amount %d out of range", rv); + rv = 0; + } + if(rv == 0){ + v = lv; + break; + } + v = lv << rv; + break; + case Orsh: + lv = left->val; + rv = right->val; + nb = n->ty->size * 8; + if(rv < 0 || rv >= nb){ + nwarn(n, "shift amount %d out of range", rv); + rv = 0; + } + if(rv == 0){ + v = lv; + break; + } + v = lv >> rv; + + /* + * properly sign extend c right shifts + */ + if((n->ty == tint || n->ty == tbig) + && rv != 0 + && (lv & (1<<(nb-1)))){ + lv = 0; + lv = ~lv; + v |= lv << (nb - rv); + } + break; + case Oneg: + v = -left->val; + break; + case Ocomp: + v = ~left->val; + break; + case Oeq: + v = left->val == right->val; + break; + case Oneq: + v = left->val != right->val; + break; + case Ogt: + v = left->val > right->val; + break; + case Ogeq: + v = left->val >= right->val; + break; + case Olt: + v = left->val < right->val; + break; + case Oleq: + v = left->val <= right->val; + break; + case Oandand: + v = left->val && right->val; + break; + case Ooror: + v = left->val || right->val; + break; + case Onot: + v = !left->val; + break; + default: + return n; + } + if(n->ty == tint){ + v &= 0xffffffff; + if(v & 0x80000000) + v |= (Long)0xffffffff << 32; + }else if(n->ty == tbyte) + v &= 0xff; + n->left = nil; + n->right = nil; + n->decl = nil; + n->op = Oconst; + n->val = v; + return n; +} + +/* + * left and right are const reals + */ +Node* +foldr(Node *n) +{ + Node *left, *right; + double rv; + Long v; + + rv = 0.; + v = 0; + + left = n->left; + right = n->right; + switch(n->op){ + case Ocast: + return n; + case Oadd: + rv = left->rval + right->rval; + break; + case Osub: + rv = left->rval - right->rval; + break; + case Omul: + rv = left->rval * right->rval; + break; + case Odiv: + rv = left->rval / right->rval; + break; + case Oexp: + rv = rpow(left->rval, right->val); + break; + case Oneg: + rv = -left->rval; + break; + case Oinv: + if(left->rval == 0.0){ + error(n->src.start, "divide by 0 in fixed point type"); + return n; + } + rv = 1/left->rval; + break; + case Oeq: + v = left->rval == right->rval; + break; + case Oneq: + v = left->rval != right->rval; + break; + case Ogt: + v = left->rval > right->rval; + break; + case Ogeq: + v = left->rval >= right->rval; + break; + case Olt: + v = left->rval < right->rval; + break; + case Oleq: + v = left->rval <= right->rval; + break; + default: + return n; + } + n->left = nil; + n->right = nil; + + if(isnan(rv)) + rv = canonnan; + + n->rval = rv; + n->val = v; + + n->op = Oconst; + return n; +} + +Node* +varinit(Decl *d, Node *e) +{ + Node *n; + + n = mkdeclname(&e->src, d); + if(d->next == nil) + return mkbin(Oas, n, e); + return mkbin(Oas, n, varinit(d->next, e)); +} + +/* + * given: an Oseq list with left == next or the last child + * make a list with the right == next + * ie: Oseq(Oseq(a, b),c) ==> Oseq(a, Oseq(b, Oseq(c, nil)))) + */ +Node* +rotater(Node *e) +{ + Node *left; + + if(e == nil) + return e; + if(e->op != Oseq) + return mkunary(Oseq, e); + e->right = mkunary(Oseq, e->right); + while(e->left->op == Oseq){ + left = e->left; + e->left = left->right; + left->right = e; + e = left; + } + return e; +} + +/* + * reverse the case labels list + */ +Node* +caselist(Node *s, Node *nr) +{ + Node *r; + + r = s->right; + s->right = nr; + if(r == nil) + return s; + return caselist(r, s); +} + +/* + * e is a seq of expressions; make into cons's to build a list + */ +Node* +etolist(Node *e) +{ + Node *left, *n; + + if(e == nil) + return nil; + n = mknil(&e->src); + n->src.start = n->src.stop; + if(e->op != Oseq) + return mkbin(Ocons, e, n); + e->right = mkbin(Ocons, e->right, n); + while(e->left->op == Oseq){ + e->op = Ocons; + left = e->left; + e->left = left->right; + left->right = e; + e = left; + } + e->op = Ocons; + return e; +} + +Node* +dupn(int resrc, Src *src, Node *n) +{ + Node *nn; + + nn = allocmem(sizeof *nn); + *nn = *n; + if(resrc) + nn->src = *src; + if(nn->left != nil) + nn->left = dupn(resrc, src, nn->left); + if(nn->right != nil) + nn->right = dupn(resrc, src, nn->right); + return nn; +} + +Node* +mkn(int op, Node *left, Node *right) +{ + Node *n; + + n = allocmem(sizeof *n); + *n = znode; + n->op = op; + n->left = left; + n->right = right; + return n; +} + +Node* +mkunary(int op, Node *left) +{ + Node *n; + + n = mkn(op, left, nil); + n->src = left->src; + return n; +} + +Node* +mkbin(int op, Node *left, Node *right) +{ + Node *n; + + n = mkn(op, left, right); + n->src.start = left->src.start; + n->src.stop = right->src.stop; + return n; +} + +Node* +mkdeclname(Src *src, Decl *d) +{ + Node *n; + + n = mkn(Oname, nil, nil); + n->src = *src; + n->decl = d; + n->ty = d->ty; + d->refs++; + return n; +} + +Node* +mknil(Src *src) +{ + return mkdeclname(src, nildecl); +} + +Node* +mkname(Src *src, Sym *s) +{ + Node *n; + + n = mkn(Oname, nil, nil); + n->src = *src; + if(s->unbound == nil){ + s->unbound = mkdecl(src, Dunbound, nil); + s->unbound->sym = s; + } + n->decl = s->unbound; + return n; +} + +Node* +mkconst(Src *src, Long v) +{ + Node *n; + + n = mkn(Oconst, nil, nil); + n->ty = tint; + n->val = v; + n->src = *src; + return n; +} + +Node* +mkrconst(Src *src, Real v) +{ + Node *n; + + n = mkn(Oconst, nil, nil); + n->ty = treal; + n->rval = v; + n->src = *src; + return n; +} + +Node* +mksconst(Src *src, Sym *s) +{ + Node *n; + + n = mkn(Oconst, nil, nil); + n->ty = tstring; + n->decl = mkdecl(src, Dconst, tstring); + n->decl->sym = s; + n->src = *src; + return n; +} + +int +opconv(Fmt *f) +{ + int op; + char buf[32]; + + op = va_arg(f->args, int); + if(op < 0 || op > Oend) { + seprint(buf, buf+sizeof(buf), "op %d", op); + return fmtstrcpy(f, buf); + } + return fmtstrcpy(f, opname[op]); +} + +int +etconv(Fmt *f) +{ + Node *n; + char buf[1024]; + + n = va_arg(f->args, Node*); + if(n->ty == tany || n->ty == tnone || n->ty == terror) + seprint(buf, buf+sizeof(buf), "%V", n); + else + seprint(buf, buf+sizeof(buf), "%V of type %T", n, n->ty); + return fmtstrcpy(f, buf); +} + +int +expconv(Fmt *f) +{ + Node *n; + char buf[4096], *p; + + n = va_arg(f->args, Node*); + p = buf; + *p = 0; + if(f->r == 'V') + *p++ = '\''; + p = eprint(p, buf+sizeof(buf)-1, n); + if(f->r == 'V') + *p++ = '\''; + *p = 0; + return fmtstrcpy(f, buf); +} + +char* +eprint(char *buf, char *end, Node *n) +{ + if(n == nil) + return buf; + if(n->flags & PARENS) + buf = secpy(buf, end, "("); + switch(n->op){ + case Obreak: + case Ocont: + buf = secpy(buf, end, opname[n->op]); + if(n->decl != nil){ + buf = seprint(buf, end, " %s", n->decl->sym->name); + } + break; + case Oexit: + case Owild: + buf = secpy(buf, end, opname[n->op]); + break; + case Onothing: + break; + case Oadr: + case Oused: + buf = eprint(buf, end, n->left); + break; + case Oseq: + buf = eprintlist(buf, end, n, ", "); + break; + case Oname: + if(n->decl == nil) + buf = secpy(buf, end, "<nil>"); + else + buf = seprint(buf, end, "%s", n->decl->sym->name); + break; + case Oconst: + if(n->ty->kind == Tstring){ + buf = stringpr(buf, end, n->decl->sym); + break; + } + if(n->decl != nil && n->decl->sym != nil){ + buf = seprint(buf, end, "%s", n->decl->sym->name); + break; + } + switch(n->ty->kind){ + case Tint: + case Tbyte: + buf = seprint(buf, end, "%ld", (long)n->val); + break; + case Tbig: + buf = seprint(buf, end, "%lld", n->val); + break; + case Treal: + buf = seprint(buf, end, "%g", n->rval); + break; + case Tfix: + buf = seprint(buf, end, "%ld(%g)", (long)n->val, n->ty->val->rval); + break; + default: + buf = secpy(buf, end, opname[n->op]); + break; + } + break; + case Ocast: + buf = seprint(buf, end, "%T ", n->ty); + buf = eprint(buf, end, n->left); + break; + case Otuple: + if(n->ty != nil && n->ty->kind == Tadt) + buf = seprint(buf, end, "%s", n->ty->decl->sym->name); + buf = seprint(buf, end, "("); + buf = eprintlist(buf, end, n->left, ", "); + buf = secpy(buf, end, ")"); + break; + case Ochan: + if(n->left){ + buf = secpy(buf, end, "chan ["); + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, "] of "); + buf = seprint(buf, end, "%T", n->ty->tof); + }else + buf = seprint(buf, end, "chan of %T", n->ty->tof); + break; + case Oarray: + buf = secpy(buf, end, "array ["); + if(n->left != nil) + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, "] of "); + if(n->right != nil){ + buf = secpy(buf, end, "{"); + buf = eprintlist(buf, end, n->right, ", "); + buf = secpy(buf, end, "}"); + }else{ + buf = seprint(buf, end, "%T", n->ty->tof); + } + break; + case Oelem: + case Olabel: + if(n->left != nil){ + buf = eprintlist(buf, end, n->left, " or "); + buf = secpy(buf, end, " =>"); + } + buf = eprint(buf, end, n->right); + break; + case Orange: + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, " to "); + buf = eprint(buf, end, n->right); + break; + case Ospawn: + buf = secpy(buf, end, "spawn "); + buf = eprint(buf, end, n->left); + break; + case Oraise: + buf = secpy(buf, end, "raise "); + buf = eprint(buf, end, n->left); + break; + case Ocall: + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, "("); + buf = eprintlist(buf, end, n->right, ", "); + buf = secpy(buf, end, ")"); + break; + case Oinc: + case Odec: + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, opname[n->op]); + break; + case Oindex: + case Oindx: + case Oinds: + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, "["); + buf = eprint(buf, end, n->right); + buf = secpy(buf, end, "]"); + break; + case Oslice: + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, "["); + buf = eprint(buf, end, n->right->left); + buf = secpy(buf, end, ":"); + buf = eprint(buf, end, n->right->right); + buf = secpy(buf, end, "]"); + break; + case Oload: + buf = seprint(buf, end, "load %T ", n->ty); + buf = eprint(buf, end, n->left); + break; + case Oref: + case Olen: + case Ohd: + case Otl: + case Otagof: + buf = secpy(buf, end, opname[n->op]); + buf = secpy(buf, end, " "); + buf = eprint(buf, end, n->left); + break; + default: + if(n->right == nil){ + buf = secpy(buf, end, opname[n->op]); + buf = eprint(buf, end, n->left); + }else{ + buf = eprint(buf, end, n->left); + buf = secpy(buf, end, opname[n->op]); + buf = eprint(buf, end, n->right); + } + break; + } + if(n->flags & PARENS) + buf = secpy(buf, end, ")"); + return buf; +} + +char* +eprintlist(char *buf, char *end, Node *elist, char *sep) +{ + if(elist == nil) + return buf; + for(; elist->right != nil; elist = elist->right){ + if(elist->op == Onothing) + continue; + if(elist->left->op == Ofnptr) + return buf; + buf = eprint(buf, end, elist->left); + if(elist->right->left->op != Ofnptr) + buf = secpy(buf, end, sep); + } + buf = eprint(buf, end, elist->left); + return buf; +} + +int +nodeconv(Fmt *f) +{ + Node *n; + char buf[4096]; + + n = va_arg(f->args, Node*); + buf[0] = 0; + nprint(buf, buf+sizeof(buf), n, 0); + return fmtstrcpy(f, buf); +} + +char* +nprint(char *buf, char *end, Node *n, int indent) +{ + int i; + + if(n == nil) + return buf; + buf = seprint(buf, end, "\n"); + for(i = 0; i < indent; i++) + if(buf < end-1) + *buf++ = ' '; + switch(n->op){ + case Oname: + if(n->decl == nil) + buf = secpy(buf, end, "name <nil>"); + else + buf = seprint(buf, end, "name %s", n->decl->sym->name); + break; + case Oconst: + if(n->decl != nil && n->decl->sym != nil) + buf = seprint(buf, end, "const %s", n->decl->sym->name); + else + buf = seprint(buf, end, "%O", n->op); + if(n->ty == tint || n->ty == tbyte || n->ty == tbig) + buf = seprint(buf, end, " (%ld)", (long)n->val); + break; + default: + buf = seprint(buf, end, "%O", n->op); + break; + } + buf = seprint(buf, end, " %T %d %d", n->ty, n->addable, n->temps); + indent += 2; + buf = nprint(buf, end, n->left, indent); + buf = nprint(buf, end, n->right, indent); + return buf; +} diff --git a/limbo/optab.c b/limbo/optab.c new file mode 100644 index 00000000..77b086f4 --- /dev/null +++ b/limbo/optab.c @@ -0,0 +1,658 @@ +#include "limbo.h" + +uchar movetab[Mend][Tend] = +{ + /* Mas */ + { + /* Tnone */ 0, + /* Tadt */ IMOVM, + /* Tadtpick */ IMOVM, + /* Tarray */ IMOVP, + /* Tbig */ IMOVL, + /* Tbyte */ IMOVB, + /* Tchan */ IMOVP, + /* Treal */ IMOVF, + /* Tfn */ 0, + /* Tint */ IMOVW, + /* Tlist */ IMOVP, + /* Tmodule */ IMOVP, + /* Tref */ IMOVP, + /* Tstring */ IMOVP, + /* Ttuple */ IMOVM, + /* Texception */ IMOVM, + /* Tfix */ IMOVW, + /* Tpoly */ IMOVP, + + /* Tainit */ 0, + /* Talt */ 0, + /* Tany */ IMOVP, + /* Tarrow */ 0, + /* Tcase */ 0, + /* Tcasel */ 0, + /* Tcasec */ 0, + /* Tdot */ 0, + /* Terror */ 0, + /* Tgoto */ 0, + /* Tid */ 0, + }, + /* Mcons */ + { + /* Tnone */ 0, + /* Tadt */ ICONSM, + /* Tadtpick */ 0, + /* Tarray */ ICONSP, + /* Tbig */ ICONSL, + /* Tbyte */ ICONSB, + /* Tchan */ ICONSP, + /* Treal */ ICONSF, + /* Tfn */ 0, + /* Tint */ ICONSW, + /* Tlist */ ICONSP, + /* Tmodule */ ICONSP, + /* Tref */ ICONSP, + /* Tstring */ ICONSP, + /* Ttuple */ ICONSM, + /* Texception */ ICONSM, + /* Tfix */ ICONSW, + /* Tpoly */ ICONSP, + + /* Tainit */ 0, + /* Talt */ 0, + /* Tany */ ICONSP, + /* Tarrow */ 0, + /* Tcase */ 0, + /* Tcasel */ 0, + /* Tcasec */ 0, + /* Tdot */ 0, + /* Terror */ 0, + /* Tgoto */ 0, + /* Tid */ 0, + }, + /* Mhd */ + { + /* Tnone */ 0, + /* Tadt */ IHEADM, + /* Tadtpick */ 0, + /* Tarray */ IHEADP, + /* Tbig */ IHEADL, + /* Tbyte */ IHEADB, + /* Tchan */ IHEADP, + /* Treal */ IHEADF, + /* Tfn */ 0, + /* Tint */ IHEADW, + /* Tlist */ IHEADP, + /* Tmodule */ IHEADP, + /* Tref */ IHEADP, + /* Tstring */ IHEADP, + /* Ttuple */ IHEADM, + /* Texception */ IHEADM, + /* Tfix */ IHEADW, + /* Tpoly */ IHEADP, + + /* Tainit */ 0, + /* Talt */ 0, + /* Tany */ IHEADP, + /* Tarrow */ 0, + /* Tcase */ 0, + /* Tcasel */ 0, + /* Tcasec */ 0, + /* Tdot*/ 0, + /* Terror */ 0, + /* Tgoto */ 0, + /* Tid */ 0, + }, + /* Mtl */ + { + /* Tnone */ 0, + /* Tadt */ 0, + /* Tadtpick */ 0, + /* Tarray */ 0, + /* Tbig */ 0, + /* Tbyte */ 0, + /* Tchan */ 0, + /* Treal */ 0, + /* Tfn */ 0, + /* Tint */ 0, + /* Tlist */ ITAIL, + /* Tmodule */ 0, + /* Tref */ 0, + /* Tstring */ 0, + /* Ttuple */ 0, + /* Texception */ 0, + /* Tfix */ 0, + /* Tpoly */ 0, + + /* Tainit */ 0, + /* Talt */ 0, + /* Tany */ 0, + /* Tarrow */ 0, + /* Tcase */ 0, + /* Tcasel */ 0, + /* Tcasec */ 0, + /* Tdot */ 0, + /* Terror */ 0, + /* Tgoto */ 0, + /* Tid */ 0, + }, +}; + +uchar chantab[Tend] = +{ + /* Tnone */ 0, + /* Tadt */ INEWCM, + /* Tadtpick */ 0, + /* Tarray */ INEWCP, + /* Tbig */ INEWCL, + /* Tbyte */ INEWCB, + /* Tchan */ INEWCP, + /* Treal */ INEWCF, + /* Tfn */ 0, + /* Tint */ INEWCW, + /* Tlist */ INEWCP, + /* Tmodule */ INEWCP, + /* Tref */ INEWCP, + /* Tstring */ INEWCP, + /* Ttuple */ INEWCM, + /* Texception */ INEWCM, + /* Tfix */ INEWCW, + /* Tpoly */ INEWCP, + + /* Tainit */ 0, + /* Talt */ 0, + /* Tany */ INEWCP, + /* Tarrow */ 0, + /* Tcase */ 0, + /* Tcasel */ 0, + /* Tcasec */ 0, + /* Tdot */ 0, + /* Terror */ 0, + /* Tgoto */ 0, + /* Tid */ 0, +}; + +uchar disoptab[Oend+1][7] = { + /* opcode default byte word big real string fixed */ + {0}, + /* Oadd */ {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,}, + /* Oaddas */ {0, IADDB, IADDW, IADDL, IADDF, IADDC, IADDW,}, + /* Oadr */ {0}, + /* Oadtdecl */ {0}, + /* Oalt */ {0}, + /* Oand */ {0, IANDB, IANDW, IANDL, 0, 0, 0,}, + /* Oandand */ {0}, + /* Oandas */ {0, IANDB, IANDW, IANDL, 0, 0, 0,}, + /* Oarray */ {0}, + /* Oas */ {0}, + /* Obreak */ {0}, + /* Ocall */ {0}, + /* Ocase */ {0}, + /* Ocast */ {0}, + /* Ochan */ {0}, + /* Ocomma */ {0}, + /* Ocomp */ {0}, + /* Ocondecl */ {0}, + /* Ocons */ {0}, + /* Oconst */ {0}, + /* Ocont */ {0}, + /* Odas */ {0}, + /* Odec */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,}, + /* Odiv */ {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,}, + /* Odivas */ {0, IDIVB, IDIVW, IDIVL, IDIVF, 0, IDIVX,}, + /* Odo */ {0}, + /* Odot */ {0}, + /* Oelem */ {0}, + /* Oeq */ {IBEQW, IBEQB, IBEQW, IBEQL, IBEQF, IBEQC, IBEQW,}, + /* Oexcept */ {0}, + /* Oexdecl */ {0}, + /* Oexit */ {0}, + /* Oexp */ {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,}, + /* Oexpas */ {0, 0, IEXPW, IEXPL, IEXPF, 0, 0,}, + /* Oexstmt */ {0}, + /* Ofielddecl */{0}, + /* Ofnptr */ {0}, + /* Ofor */ {0}, + /* Ofunc */ {0}, + /* Ogeq */ {0, IBGEB, IBGEW, IBGEL, IBGEF, IBGEC, IBGEW,}, + /* Ogt */ {0, IBGTB, IBGTW, IBGTL, IBGTF, IBGTC, IBGTW,}, + /* Ohd */ {0}, + /* Oif */ {0}, + /* Oimport */ {0}, + /* Oinc */ {0, IADDB, IADDW, IADDL, IADDF, 0, IADDW,}, + /* Oind */ {0}, + /* Oindex */ {0,}, + /* Oinds */ {0, 0, IINDC, 0, 0, 0, 0,}, + /* Oindx */ {0, 0, IINDX, 0, 0, 0, 0,}, + /* Oinv */ {0}, + /* Ojmp */ {0}, + /* Olabel */ {0}, + /* Olen */ {ILENA, 0, 0, 0, 0, ILENC, 0,}, + /* Oleq */ {0, IBLEB, IBLEW, IBLEL, IBLEF, IBLEC, IBLEW,}, + /* Oload */ {0}, + /* Olsh */ {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,}, + /* Olshas */ {0, ISHLB, ISHLW, ISHLL, 0, 0, 0,}, + /* Olt */ {0, IBLTB, IBLTW, IBLTL, IBLTF, IBLTC, IBLTW,}, + /* Omdot */ {0}, + /* Omod */ {0, IMODB, IMODW, IMODL, 0, 0, 0,}, + /* Omodas */ {0, IMODB, IMODW, IMODL, 0, 0, 0,}, + /* Omoddecl */ {0}, + /* Omul */ {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,}, + /* Omulas */ {0, IMULB, IMULW, IMULL, IMULF, 0, IMULX,}, + /* Oname */ {0}, + /* Oneg */ {0, 0, 0, 0, INEGF, 0, 0,}, + /* Oneq */ {IBNEW, IBNEB, IBNEW, IBNEL, IBNEF, IBNEC, IBNEW,}, + /* Onot */ {0}, + /* Onothing */ {0}, + /* Oor */ {0, IORB, IORW, IORL, 0, 0, 0,}, + /* Ooras */ {0, IORB, IORW, IORL, 0, 0, 0,}, + /* Ooror */ {0}, + /* Opick */ {0}, + /* Opickdecl */ {0}, + /* Opredec */ {0}, + /* Opreinc */ {0}, + /* Oraise */ {0}, + /* Orange */ {0}, + /* Orcv */ {0}, + /* Oref */ {0}, + /* Oret */ {0}, + /* Orsh */ {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,}, + /* Orshas */ {0, ISHRB, ISHRW, ISHRL, 0, 0, 0,}, + /* Oscope */ {0}, + /* Oself */ {0}, + /* Oseq */ {0}, + /* Oslice */ {ISLICEA,0, 0, 0, 0, ISLICEC, 0,}, + /* Osnd */ {0}, + /* Ospawn */ {0}, + /* Osub */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,}, + /* Osubas */ {0, ISUBB, ISUBW, ISUBL, ISUBF, 0, ISUBW,}, + /* Otagof */ {0}, + /* Otl */ {0}, + /* Otuple */ {0}, + /* Otype */ {0}, + /* Otypedecl */ {0}, + /* Oused */ {0}, + /* Ovardecl */ {0}, + /* Ovardecli */ {0}, + /* Owild */ {0}, + /* Oxor */ {0, IXORB, IXORW, IXORL, 0, 0, 0,}, + /* Oxoras */ {0, IXORB, IXORW, IXORL, 0, 0, 0,}, + + /* Oend */ {0} +}; + +int setisused[] = { + Oas, + Odas, + Oaddas, + Osubas, + Omulas, + Odivas, + Omodas, + Oexpas, + Oandas, + Ooras, + Oxoras, + Olshas, + Onothing, + Orshas, + Oinc, + Odec, + Opreinc, + Opredec, + Ocall, + Oraise, + Ospawn, + Osnd, + Orcv, + + -1 +}; + +int setsideeffect[] = { + Oas, + Odas, + Oaddas, + Osubas, + Omulas, + Odivas, + Omodas, + Oexpas, + Oandas, + Ooras, + Oxoras, + Olshas, + Orshas, + Oinc, + Odec, + Opreinc, + Opredec, + Ocall, + Oraise, + Ospawn, + Osnd, + Orcv, + + Oadr, + Oarray, + Ocast, + Ochan, + Ocons, + Odiv, + Odot, + Oind, + Oindex, + Oinds, + Oindx, + Olen, + Oload, + Omod, + Oref, + + -1 +}; + +char *opname[Oend+1] = { + "unknown", + /* Oadd */ "+", + /* Oaddas */ "+=", + /* Oadr */ "adr", + /* Oadtdecl */ "adtdecl", + /* Oalt */ "alt", + /* Oand */ "&", + /* Oandand */ "&&", + /* Oandas */ "&=", + /* Oarray */ "array", + /* Oas */ "=", + /* Obreak */ "break", + /* Ocall */ "call", + /* Ocase */ "case", + /* Ocast */ "cast", + /* Ochan */ "chan", + /* Ocomma */ ",", + /* Ocomp */ "~", + /* Ocondecl */ "condecl", + /* Ocons */ "::", + /* Oconst */ "const", + /* Ocont */ "continue", + /* Odas */ ":=", + /* Odec */ "--", + /* Odiv */ "/", + /* Odivas */ "/=", + /* Odo */ "do", + /* Odot */ ".", + /* Oelem */ "elem", + /* Oeq */ "==", + /* Oexcept */ "except", + /* Oexdecl */ "exdecl", + /* Oexit */ "exit", + /* Oexp */ "**", + /* Oexpas */ "**=", + /* Oexstmt */ "exstat", + /* Ofielddecl */"fielddecl", + /* Ofnptr */ "fnptr", + /* Ofor */ "for", + /* Ofunc */ "fn(){}", + /* Ogeq */ ">=", + /* Ogt */ ">", + /* Ohd */ "hd", + /* Oif */ "if", + /* Oimport */ "import", + /* Oinc */ "++", + /* Oind */ "*", + /* Oindex */ "index", + /* Oinds */ "inds", + /* Oindx */ "indx", + /* Oinv */ "inv", + /* Ojmp */ "jmp", + /* Olabel */ "label", + /* Olen */ "len", + /* Oleq */ "<=", + /* Oload */ "load", + /* Olsh */ "<<", + /* Olshas */ "<<=", + /* Olt */ "<", + /* Omdot */ "->", + /* Omod */ "%", + /* Omodas */ "%=", + /* Omoddecl */ "moddecl", + /* Omul */ "*", + /* Omulas */ "*=", + /* Oname */ "name", + /* Oneg */ "-", + /* Oneq */ "!=", + /* Onot */ "!", + /* Onothing */ "nothing", + /* Oor */ "|", + /* Ooras */ "|=", + /* Ooror */ "||", + /* Opick */ "pick", + /* Opickdecl */ "pickdecl", + /* Opredec */ "--", + /* Opreinc */ "++", + /* Oraise */ "raise", + /* Orange */ "range", + /* Orcv */ "<-", + /* Oref */ "ref", + /* Oret */ "return", + /* Orsh */ ">>", + /* Orshas */ ">>=", + /* Oscope */ "scope", + /* Oself */ "self", + /* Oseq */ "seq", + /* Oslice */ "slice", + /* Osnd */ "<-=", + /* Ospawn */ "spawn", + /* Osub */ "-", + /* Osubas */ "-=", + /* Otl */ "tagof", + /* Otl */ "tl", + /* Otuple */ "tuple", + /* Otype */ "type", + /* Otypedecl */ "typedecl", + /* Oused */ "used", + /* Ovardecl */ "vardecl", + /* Ovardecli */ "vardecli", + /* Owild */ "*", + /* Oxor */ "^", + /* Oxoras */ "^=", + + /* Oend */ "unknown" +}; + +int setisbyteinst[] = { + IMULB, + ISUBB, + IADDB, + IDIVB, + IORB, + IXORB, + ISHLB, + ISHRB, + IMODB, + IANDB, + IBEQB, + IBNEB, + IBLTB, + IBLEB, + IBGTB, + IBGEB, + + -1 +}; + +char *instname[256] = { + "nop", + "alt", + "nbalt", + "goto", + "call", + "frame", + "spawn", + "runt", + "load", + "mcall", + "mspawn", + "mframe", + "ret", + "jmp", + "case", + "exit", + "new", + "newa", + "newcb", + "newcw", + "newcf", + "newcp", + "newcm", + "newcmp", + "send", + "recv", + "consb", + "consw", + "consp", + "consf", + "consm", + "consmp", + "headb", + "headw", + "headp", + "headf", + "headm", + "headmp", + "tail", + "lea", + "indx", + "movp", + "movm", + "movmp", + "movb", + "movw", + "movf", + "cvtbw", + "cvtwb", + "cvtfw", + "cvtwf", + "cvtca", + "cvtac", + "cvtwc", + "cvtcw", + "cvtfc", + "cvtcf", + "addb", + "addw", + "addf", + "subb", + "subw", + "subf", + "mulb", + "mulw", + "mulf", + "divb", + "divw", + "divf", + "modw", + "modb", + "andb", + "andw", + "orb", + "orw", + "xorb", + "xorw", + "shlb", + "shlw", + "shrb", + "shrw", + "insc", + "indc", + "addc", + "lenc", + "lena", + "lenl", + "beqb", + "bneb", + "bltb", + "bleb", + "bgtb", + "bgeb", + "beqw", + "bnew", + "bltw", + "blew", + "bgtw", + "bgew", + "beqf", + "bnef", + "bltf", + "blef", + "bgtf", + "bgef", + "beqc", + "bnec", + "bltc", + "blec", + "bgtc", + "bgec", + "slicea", + "slicela", + "slicec", + "indw", + "indf", + "indb", + "negf", + "movl", + "addl", + "subl", + "divl", + "modl", + "mull", + "andl", + "orl", + "xorl", + "shll", + "shrl", + "bnel", + "bltl", + "blel", + "bgtl", + "bgel", + "beql", + "cvtlf", + "cvtfl", + "cvtlw", + "cvtwl", + "cvtlc", + "cvtcl", + "headl", + "consl", + "newcl", + "casec", + "indl", + "movpc", + "tcmp", + "mnewz", + "cvtrf", + "cvtfr", + "cvtws", + "cvtsw", + "lsrw", + "lsrl", + "eclr", + "newz", + "newaz", + "raise", + "casel", + "mulx", + "divx", + "cvtxx", + "mulx0", + "divx0", + "cvtxx0", + "mulx1", + "divx1", + "cvtxx1", + "cvtfx", + "cvtxf", + "expw", + "expl", + "expf", + "self", +}; diff --git a/limbo/optim.c b/limbo/optim.c new file mode 100644 index 00000000..a3ccdec3 --- /dev/null +++ b/limbo/optim.c @@ -0,0 +1,1803 @@ +#include "limbo.h" + +#define bzero bbzero /* bsd name space pollution */ +/* + (r, s) := f(); => r, s have def on same pc + s = g(); => this def kills previous r def (and s def) + solution: r has def pc, s has def pc+1 and next instruction has pc pc+2 +*/ + +#define BLEN (8*sizeof(ulong)) +#define BSHIFT 5 /* assumes ulong 4 */ +#define BMASK (BLEN-1) + +#define SIGN(n) (1<<(n-1)) +#define MSK(n) (SIGN(n)|(SIGN(n)-1)) +#define MASK(a, b) (MSK((b)-(a)+1)<<(a)) + +#define isnilsrc(s) ((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0) + +#define limbovar(d) ((d)->sym->name[0] != '.') +#define structure(t) ((t)->kind == Tadt || (t)->kind == Ttuple) + +enum +{ + Bclr, + Band, + Bandinv, + Bstore, + Bandrev, + Bnoop, + Bxor, + Bor, + Bnor, + Bequiv, + Binv, + Bimpby, + Brev, + Bimp, + Bnand, + Bset, +}; + +enum +{ + Suse = 1, + Muse = 2, + Duse = 4, + Sdef = 8, + Mdef = 16, + Ddef = 32, + Tuse1 = 64, /* fixed point temporary */ + Tuse2 = 128, /* fixed point temporary */ + Mduse = 256, /* D used if M nil */ + + None = 0, + Unop = Suse|Ddef, + Cunop = Muse|Ddef, + Threop = Suse|Muse|Ddef, + Binop = Suse|Muse|Ddef|Mduse, + Mbinop = Suse|Mdef|Duse, /* strange */ + Abinop=Suse|Duse|Ddef, + Mabinop = Suse|Muse|Duse|Ddef, + Use1 = Suse, + Use2 = Suse|Duse, + Use3 = Suse|Muse|Duse, +}; + +enum +{ + Sshift = 10, + Mshift = 5, + Dshift = 0, +}; + +#define S(x) ((x)<<Sshift) +#define M(x) ((x)<<Mshift) +#define D(x) ((x)<<Dshift) + +#define SS(x) (((x)>>Sshift)&0x1f) +#define SM(x) (((x)>>Mshift)&0x1f) +#define SD(x) (((x)>>Dshift)&0x1f) + +enum +{ + I = 0, /* ignore */ + B = 1, /* byte */ + W = 4, /* int */ + P = 4, /* pointer */ + A = 4, /* array */ + C = 4, /* string */ + X = 4, /* fixed */ + R = 4, /* float */ + L = 8, /* big */ + F = 8, /* real */ + Sh = 2, /* short */ + Pc = 4, /* pc */ + Mp = 16, /* memory */ + + Bop2 = S(B)|D(B), + Bop = S(B)|M(B)|D(B), + Bopb = S(B)|M(B)|D(Pc), + Wop2 = S(W)|D(W), + Wop = S(W)|M(W)|D(W), + Wopb = S(W)|M(W)|D(Pc), + Lop2 = S(L)|D(L), + Lop = S(L)|M(L)|D(L), + Lopb = S(L)|M(L)|D(Pc), + Cop2 = Wop2, + Cop = Wop, + Copb = Wopb, + Fop2 = Lop2, + Fop = Lop, + Fopb = Lopb, + Xop = Wop, +}; + +typedef struct Array Array; +typedef struct Bits Bits; +typedef struct Blist Blist; +typedef struct Block Block; +typedef struct Idlist Idlist; +typedef struct Optab Optab; + +struct Array +{ + int n; + int m; + Block **a; +}; + +struct Bits +{ + int n; + ulong *b; +}; + +struct Blist +{ + Block *block; + Blist *next; +}; + +struct Block +{ + int dfn; + int flags; + Inst *first; + Inst *last; + Block *prev; + Block *next; + Blist *pred; + Blist *succ; + Bits kill; + Bits gen; + Bits in; + Bits out; +}; + +struct Idlist +{ + int id; + Idlist *next; +}; + +struct Optab +{ + short flags; + short size; +}; + +Block zblock; +Decl *regdecls; +Idlist *frelist; +Idlist *deflist; +Idlist *uselist; + +static void +addlist(Idlist **hd, int id) +{ + Idlist *il; + + if(frelist == nil) + il = (Idlist*)malloc(sizeof(Idlist)); + else{ + il = frelist; + frelist = frelist->next; + } + il->id = id; + il->next = *hd; + *hd = il; +} + +static void +freelist(Idlist **hd) +{ + Idlist *il; + + for(il = *hd; il != nil && il->next != nil; il = il->next) + ; + if(il != nil){ + il->next = frelist; + frelist = *hd; + *hd = nil; + } +} + +Optab opflags[] = { + /* INOP */ None, 0, + /* IALT */ Unop, S(Mp)|D(W), + /* INBALT */ Unop, S(Mp)|D(W), + /* IGOTO */ Use2, S(W)|D(I), + /* ICALL */ Use2, S(P)|D(Pc), + /* IFRAME */ Unop, S(W)|D(P), + /* ISPAWN */ Use2, S(P)|D(Pc), + /* IRUNT */ None, 0, + /* ILOAD */ Threop, S(C)|M(P)|D(P), + /* IMCALL */ Use3, S(P)|M(W)|D(P), + /* IMSPAWN */ Use3, S(P)|M(W)|D(P), + /* IMFRAME */ Threop, S(P)|M(W)|D(P), + /* IRET */ None, 0, + /* IJMP */ Duse, D(Pc), + /* ICASE */ Use2, S(W)|D(I), + /* IEXIT */ None, 0, + /* INEW */ Unop, S(W)|D(P), + /* INEWA */ Threop, S(W)|M(W)|D(P), + /* INEWCB */ Cunop, M(W)|D(P), + /* INEWCW */ Cunop, M(W)|D(P), + /* INEWCF */ Cunop, M(W)|D(P), + /* INEWCP */ Cunop, M(W)|D(P), + /* INEWCM */ Threop, S(W)|M(W)|D(P), + /* INEWCMP */ Threop, S(W)|M(W)|D(P), + /* ISEND */ Use2, S(Mp)|D(P), + /* IRECV */ Unop, S(P)|D(Mp), + /* ICONSB */ Abinop, S(B)|D(P), + /* ICONSW */ Abinop, S(W)|D(P), + /* ICONSP */ Abinop, S(P)|D(P), + /* ICONSF */ Abinop, S(F)|D(P), + /* ICONSM */ Mabinop, S(Mp)|M(W)|D(P), + /* ICONSMP */ Mabinop, S(Mp)|M(W)|D(P), + /* IHEADB */ Unop, S(P)|D(B), + /* IHEADW */ Unop, S(P)|D(W), + /* IHEADP */ Unop, S(P)|D(P), + /* IHEADF */ Unop, S(P)|D(F), + /* IHEADM */ Threop, S(P)|M(W)|D(Mp), + /* IHEADMP */ Threop, S(P)|M(W)|D(Mp), + /* ITAIL */ Unop, S(P)|D(P), + /* ILEA */ Ddef, S(Mp)|D(P), /* S done specially cos of ALT */ + /* IINDX */ Mbinop, S(P)|M(P)|D(W), + /* IMOVP */ Unop, S(P)|D(P), + /* IMOVM */ Threop, S(Mp)|M(W)|D(Mp), + /* IMOVMP */ Threop, S(Mp)|M(W)|D(Mp), + /* IMOVB */ Unop, Bop2, + /* IMOVW */ Unop, Wop2, + /* IMOVF */ Unop, Fop2, + /* ICVTBW */ Unop, S(B)|D(W), + /* ICVTWB */ Unop, S(W)|D(B), + /* ICVTFW */ Unop, S(F)|D(W), + /* ICVTWF */ Unop, S(W)|D(F), + /* ICVTCA */ Unop, S(C)|D(A), + /* ICVTAC */ Unop, S(A)|D(C), + /* ICVTWC */ Unop, S(W)|D(C), + /* ICVTCW */ Unop, S(C)|D(W), + /* ICVTFC */ Unop, S(F)|D(C), + /* ICVTCF */ Unop, S(C)|D(F), + /* IADDB */ Binop, Bop, + /* IADDW */ Binop, Wop, + /* IADDF */ Binop, Fop, + /* ISUBB */ Binop, Bop, + /* ISUBW */ Binop, Wop, + /* ISUBF */ Binop, Fop, + /* IMULB */ Binop, Bop, + /* IMULW */ Binop, Wop, + /* IMULF */ Binop, Fop, + /* IDIVB */ Binop, Bop, + /* IDIVW */ Binop, Wop, + /* IDIVF */ Binop, Fop, + /* IMODW */ Binop, Wop, + /* IMODB */ Binop, Bop, + /* IANDB */ Binop, Bop, + /* IANDW */ Binop, Wop, + /* IORB */ Binop, Bop, + /* IORW */ Binop, Wop, + /* IXORB */ Binop, Bop, + /* IXORW */ Binop, Wop, + /* ISHLB */ Binop, S(W)|M(B)|D(B), + /* ISHLW */ Binop, Wop, + /* ISHRB */ Binop, S(W)|M(B)|D(B), + /* ISHRW */ Binop, Wop, + /* IINSC */ Mabinop, S(W)|M(W)|D(C), + /* IINDC */ Threop, S(C)|M(W)|D(W), + /* IADDC */ Binop, Cop, + /* ILENC */ Unop, S(C)|D(W), + /* ILENA */ Unop, S(A)|D(W), + /* ILENL */ Unop, S(P)|D(W), + /* IBEQB */ Use3, Bopb, + /* IBNEB */ Use3, Bopb, + /* IBLTB */ Use3, Bopb, + /* IBLEB */ Use3, Bopb, + /* IBGTB */ Use3, Bopb, + /* IBGEB */ Use3, Bopb, + /* IBEQW */ Use3, Wopb, + /* IBNEW */ Use3, Wopb, + /* IBLTW */ Use3, Wopb, + /* IBLEW */ Use3, Wopb, + /* IBGTW */ Use3, Wopb, + /* IBGEW */ Use3, Wopb, + /* IBEQF */ Use3, Fopb, + /* IBNEF */ Use3, Fopb, + /* IBLTF */ Use3, Fopb, + /* IBLEF */ Use3, Fopb, + /* IBGTF */ Use3, Fopb, + /* IBGEF */ Use3, Fopb, + /* IBEQC */ Use3, Copb, + /* IBNEC */ Use3, Copb, + /* IBLTC */ Use3, Copb, + /* IBLEC */ Use3, Copb, + /* IBGTC */ Use3, Copb, + /* IBGEC */ Use3, Copb, + /* ISLICEA */ Mabinop, S(W)|M(W)|D(P), + /* ISLICELA */ Use3, S(P)|M(W)|D(P), + /* ISLICEC */ Mabinop, S(W)|M(W)|D(C), + /* IINDW */ Mbinop, S(P)|M(P)|D(W), + /* IINDF */ Mbinop, S(P)|M(P)|D(W), + /* IINDB */ Mbinop, S(P)|M(P)|D(W), + /* INEGF */ Unop, Fop2, + /* IMOVL */ Unop, Lop2, + /* IADDL */ Binop, Lop, + /* ISUBL */ Binop, Lop, + /* IDIVL */ Binop, Lop, + /* IMODL */ Binop, Lop, + /* IMULL */ Binop, Lop, + /* IANDL */ Binop, Lop, + /* IORL */ Binop, Lop, + /* IXORL */ Binop, Lop, + /* ISHLL */ Binop, S(W)|M(L)|D(L), + /* ISHRL */ Binop, S(W)|M(L)|D(L), + /* IBNEL */ Use3, Lopb, + /* IBLTL */ Use3, Lopb, + /* IBLEL */ Use3, Lopb, + /* IBGTL */ Use3, Lopb, + /* IBGEL */ Use3, Lopb, + /* IBEQL */ Use3, Lopb, + /* ICVTLF */ Unop, S(L)|D(F), + /* ICVTFL */ Unop, S(F)|D(L), + /* ICVTLW */ Unop, S(L)|D(W), + /* ICVTWL */ Unop, S(W)|D(L), + /* ICVTLC */ Unop, S(L)|D(C), + /* ICVTCL */ Unop, S(C)|D(L), + /* IHEADL */ Unop, S(P)|D(L), + /* ICONSL */ Abinop, S(L)|D(P), + /* INEWCL */ Cunop, M(W)|D(P), + /* ICASEC */ Use2, S(C)|D(I), + /* IINDL */ Mbinop, S(P)|M(P)|D(W), + /* IMOVPC */ Unop, S(W)|D(P), + /* ITCMP */ Use2, S(P)|D(P), + /* IMNEWZ */ Threop, S(P)|M(W)|D(P), + /* ICVTRF */ Unop, S(R)|D(F), + /* ICVTFR */ Unop, S(F)|D(R), + /* ICVTWS */ Unop, S(W)|D(Sh), + /* ICVTSW */ Unop, S(Sh)|D(W), + /* ILSRW */ Binop, Wop, + /* ILSRL */ Binop, S(W)|M(L)|D(L), + /* IECLR */ None, 0, + /* INEWZ */ Unop, S(W)|D(P), + /* INEWAZ */ Threop, S(W)|M(W)|D(P), + /* IRAISE */ Use1, S(P), + /* ICASEL */ Use2, S(L)|D(I), + /* IMULX */ Binop|Tuse2, Xop, + /* IDIVX */ Binop|Tuse2, Xop, + /* ICVTXX */ Threop, Xop, + /* IMULX0 */ Binop|Tuse1|Tuse2, Xop, + /* IDIVX0 */ Binop|Tuse1|Tuse2, Xop, + /* ICVTXX0 */ Threop|Tuse1, Xop, + /* IMULX1 */ Binop|Tuse1|Tuse2, Xop, + /* IDIVX1 */ Binop|Tuse1|Tuse2, Xop, + /* ICVTXX1 */ Threop|Tuse1, Xop, + /* ICVTFX */ Threop, S(F)|M(F)|D(X), + /* ICVTXF */ Threop, S(X)|M(F)|D(F), + /* IEXPW */ Binop, S(W)|M(W)|D(W), + /* IEXPL */ Binop, S(W)|M(L)|D(L), + /* IEXPF */ Binop, S(W)|M(F)|D(F), + /* ISELF */ Ddef, D(P), + /* IEXC */ None, 0, + /* IEXC0 */ None, 0, + /* INOOP */ None, 0, +}; + +/* +static int +pop(int i) +{ + i = (i & 0x55555555) + ((i>>1) & 0x55555555); + i = (i & 0x33333333) + ((i>>2) & 0x33333333); + i = (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F); + i = (i & 0x00FF00FF) + ((i>>8) & 0x00FF00FF); + i = (i & 0x0000FFFF) + ((i>>16) & 0x0000FFFF); + return i; +} +*/ + +static int +bitc(uint x) +{ + uint n; + + n = (x>>1)&0x77777777; + x -= n; + n = (n>>1)&0x77777777; + x -= n; + n = (n>>1)&0x77777777; + x -= n; + x = (x+(x>>4))&0x0f0f0f0f; + x *= 0x01010101; + return x>>24; +} + +/* +static int +top(uint x) +{ + int i; + + for(i = -1; x; i++) + x >>= 1; + return i; +} +*/ + +static int +topb(uint x) +{ + int i; + + if(x == 0) + return -1; + i = 0; + if(x&0xffff0000){ + i |= 16; + x >>= 16; + } + if(x&0xff00){ + i |= 8; + x >>= 8; + } + if(x&0xf0){ + i |= 4; + x >>= 4; + } + if(x&0xc){ + i |= 2; + x >>= 2; + } + if(x&0x2) + i |= 1; + return i; +} + +/* +static int +lowb(uint x) +{ + int i; + + if(x == 0) + return -1; + for(i = BLEN; x; i--) + x <<= 1; + return i; +} +*/ + +static int +lowb(uint x) +{ + int i; + + if(x == 0) + return -1; + i = 0; + if((x&0xffff) == 0){ + i |= 16; + x >>= 16; + } + if((x&0xff) == 0){ + i |= 8; + x >>= 8; + } + if((x&0xf) == 0){ + i |= 4; + x >>= 4; + } + if((x&0x3) == 0){ + i |= 2; + x >>= 2; + } + return i+1-(x&1); +} + +static void +pbit(int x, int n) +{ + int i, m; + + m = 1; + for(i = 0; i < BLEN; i++){ + if(x&m) + print("%d ", i+n); + m <<= 1; + } +} + +static ulong +bop(int o, ulong s, ulong d) +{ + switch(o){ + case Bclr: return 0; + case Band: return s & d; + case Bandinv: return s & ~d; + case Bstore: return s; + case Bandrev: return ~s & d; + case Bnoop: return d; + case Bxor: return s ^ d; + case Bor: return s | d; + case Bnor: return ~(s | d); + case Bequiv: return ~(s ^ d); + case Binv: return ~d; + case Bimpby: return s | ~d; + case Brev: return ~s; + case Bimp: return ~s | d; + case Bnand: return ~(s & d); + case Bset: return 0xffffffff; + } + return 0; +} + +static Bits +bnew(int n, int bits) +{ + Bits b; + + if(bits) + b.n = (n+BLEN-1)>>BSHIFT; + else + b.n = n; + b.b = allocmem(b.n*sizeof(ulong)); + memset(b.b, 0, b.n*sizeof(ulong)); + return b; +} + +static void +bfree(Bits b) +{ + free(b.b); +} + +static void +bset(Bits b, int n) +{ + b.b[n>>BSHIFT] |= 1<<(n&BMASK); +} + +static void +bclr(Bits b, int n) +{ + b.b[n>>BSHIFT] &= ~(1<<(n&BMASK)); +} + +static int +bmem(Bits b, int n) +{ + return b.b[n>>BSHIFT] & (1<<(n&BMASK)); +} + +static void +bsets(Bits b, int m, int n) +{ + int i, c1, c2; + + c1 = m>>BSHIFT; + c2 = n>>BSHIFT; + m &= BMASK; + n &= BMASK; + if(c1 == c2){ + b.b[c1] |= MASK(m, n); + return; + } + for(i = c1+1; i < c2; i++) + b.b[i] = 0xffffffff; + b.b[c1] |= MASK(m, BLEN-1); + b.b[c2] |= MASK(0, n); +} + +static void +bclrs(Bits b, int m, int n) +{ + int i, c1, c2; + + if(n < 0) + n = (b.n<<BSHIFT)-1; + c1 = m>>BSHIFT; + c2 = n>>BSHIFT; + m &= BMASK; + n &= BMASK; + if(c1 == c2){ + b.b[c1] &= ~MASK(m, n); + return; + } + for(i = c1+1; i < c2; i++) + b.b[i] = 0; + b.b[c1] &= ~MASK(m, BLEN-1); + b.b[c2] &= ~MASK(0, n); +} + +/* b = a op b */ +static Bits +boper(int o, Bits a, Bits b) +{ + int i, n; + + n = a.n; + if(b.n != n) + fatal("boper %d %d %d", o, a.n, b.n); + for(i = 0; i < n; i++) + b.b[i] = bop(o, a.b[i], b.b[i]); + return b; +} + +static int +beq(Bits a, Bits b) +{ + int i, n; + + n = a.n; + for(i = 0; i < n; i++) + if(a.b[i] != b.b[i]) + return 0; + return 1; +} + +static int +bzero(Bits b) +{ + int i, n; + + n = b.n; + for(i = 0; i < n; i++) + if(b.b[i] != 0) + return 0; + return 1; +} + +static int +bitcnt(Bits b) +{ + int i, m, n; + + m = b.n; + n = 0; + for(i = 0; i < m; i++) + n += bitc(b.b[i]); + return n; +} + +static int +topbit(Bits b) +{ + int i, n; + + n = b.n; + for(i = n-1; i >= 0; i--) + if(b.b[i] != 0) + return (i<<BSHIFT)+topb(b.b[i]); + return -1; +} + +static int +lowbit(Bits b) +{ + int i, n; + + n = b.n; + for(i = 0; i < n; i++) + if(b.b[i] != 0) + return (i<<BSHIFT)+lowb(b.b[i]); + return -1; +} + +static void +pbits(Bits b) +{ + int i, n; + + n = b.n; + for(i = 0; i < n; i++) + pbit(b.b[i], i<<BSHIFT); +} + +static char* +decname(Decl *d) +{ + if(d->sym == nil) + return "<??>"; + return d->sym->name; +} + +static void +warning(Inst *i, char *s, Decl *d, Decl *sd) +{ + int n; + char *f; + Decl *ds; + + n = 0; + for(ds = sd; ds != nil; ds = ds->next) + if(ds->link == d) + n += strlen(ds->sym->name)+1; + if(n == 0){ + warn(i->src.start, "%s: %s", d->sym->name, s); + return; + } + n += strlen(d->sym->name); + f = malloc(n+1); + strcpy(f, d->sym->name); + for(ds = sd; ds != nil; ds = ds->next){ + if(ds->link == d){ + strcat(f, "/"); + strcat(f, ds->sym->name); + } + } + warn(i->src.start, "%s: %s", f, s); + free(f); +} + +static int +inspc(Inst *in) +{ + int n; + Inst *i; + + n = 0; + for(i = in; i != nil; i = i->next) + i->pc = n++; + return n; +} + +static Inst* +pc2i(Block *b, int pc) +{ + Inst *i; + + for( ; b != nil; b = b->next){ + if(pc > b->last->pc) + continue; + for(i = b->first; ; i = i->next){ + if(i->pc == pc) + return i; + if(i == b->last) + fatal("pc2i a"); + } + } + fatal("pc2i b"); + return nil; +} + +static void +padr(int am, Addr *a, Inst *br) +{ + long reg; + + if(br != nil){ + print("$%ld", br->pc); + return; + } + reg = a->reg; + if(a->decl != nil && am != Adesc) + reg += a->decl->offset; + switch(am){ + case Anone: + print("-"); + break; + case Aimm: + case Apc: + case Adesc: + print("$%ld", a->offset); + break; + case Aoff: + print("$%ld", a->decl->iface->offset); + break; + case Anoff: + print("-$%ld", a->decl->iface->offset); + break; + case Afp: + print("%ld(fp)", reg); + break; + case Afpind: + print("%ld(%ld(fp))", a->offset, reg); + break; + case Amp: + print("%ld(mp)", reg); + break; + case Ampind: + print("%ld(%ld(mp))", a->offset, reg); + break; + case Aldt: + print("$%ld", reg); + break; + case Aerr: + default: + print("%ld(%ld(?%d?))", a->offset, reg, am); + break; + } +} + +static void +pins(Inst *i) +{ + /* print("%L %ld ", i->src.start, i->pc); */ + print(" %ld ", i->pc); + if(i->op >= 0 && i->op < MAXDIS) + print("%s", instname[i->op]); + else + print("noop"); + print(" "); + padr(i->sm, &i->s, nil); + print(", "); + padr(i->mm, &i->m, nil); + print(", "); + padr(i->dm, &i->d, i->branch); + print("\n"); +} + +static void +blfree(Blist *bl) +{ + Blist *nbl; + + for( ; bl != nil; bl = nbl){ + nbl = bl->next; + free(bl); + } +} + +static void +freebits(Bits *bs, int nv) +{ + int i; + + for(i = 0; i < nv; i++) + bfree(bs[i]); + free(bs); +} + +static void +freeblks(Block *b) +{ + Block *nb; + + for( ; b != nil; b = nb){ + blfree(b->pred); + blfree(b->succ); + bfree(b->kill); + bfree(b->gen); + bfree(b->in); + bfree(b->out); + nb = b->next; + free(b); + } +} + +static int +len(Decl *d) +{ + int n; + + n = 0; + for( ; d != nil; d = d->next) + n++; + return n; +} + +static Bits* +allocbits(int nv, int npc) +{ + int i; + Bits *defs; + + defs = (Bits*)allocmem(nv*sizeof(Bits)); + for(i = 0; i < nv; i++) + defs[i] = bnew(npc, 1); + return defs; +} + +static int +bitcount(Bits *bs, int nv) +{ + int i, n; + + n = 0; + for(i = 0; i < nv; i++) + n += bitcnt(bs[i]); + return n; +} + +static Block* +mkblock(Inst *i) +{ + Block *b; + + b = allocmem(sizeof(Block)); + *b = zblock; + b->first = b->last = i; + return b; +} + +static Blist* +mkblist(Block *b, Blist *nbl) +{ + Blist *bl; + + bl = allocmem(sizeof(Blist)); + bl->block = b; + bl->next = nbl; + return bl; +} + +static void +leader(Inst *i, Array *ab) +{ + int m, n; + Block *b, **a; + + if(i != nil && i->pc == 0){ + if((n = ab->n) == (m = ab->m)){ + a = ab->a; + ab->a = allocmem(2*m*sizeof(Block*)); + memcpy(ab->a, a, m*sizeof(Block*)); + ab->m = 2*m; + free(a); + } + b = mkblock(i); + b->dfn = n; + ab->a[n] = b; + i->pc = ab->n = n+1; + } +} + +static Block* +findb(Inst *i, Array *ab) +{ + if(i == nil) + return nil; + if(i->pc <= 0) + fatal("pc <= 0 in findb"); + return ab->a[i->pc-1]; +} + +static int +memb(Block *b, Blist *bl) +{ + for( ; bl != nil; bl = bl->next) + if(bl->block == b) + return 1; + return 0; +} + +static int +canfallthrough(Inst *i) +{ + if(i == nil) + return 0; + switch(i->op){ + case IGOTO: + case ICASE: + case ICASEL: + case ICASEC: + case IRET: + case IEXIT: + case IRAISE: + case IJMP: + return 0; + case INOOP: + return i->branch != nil; + } + return 1; +} + +static void +predsucc(Block *b1, Block *b2) +{ + if(b1 == nil || b2 == nil) + return; + if(!memb(b1, b2->pred)) + b2->pred = mkblist(b1, b2->pred); + if(!memb(b2, b1->succ)) + b1->succ = mkblist(b2, b1->succ); +} + +static Block* +mkblocks(Inst *in, int *nb) +{ + Inst *i; + Block *b, *firstb, *lastb; + Label *lab; + Array *ab; + int j, n; + + ab = allocmem(sizeof(Array)); + ab->n = 0; + ab->m = 16; + ab->a = allocmem(ab->m*sizeof(Block*)); + leader(in, ab); + for(i = in; i != nil; i = i->next){ + switch(i->op){ + case IGOTO: + case ICASE: + case ICASEL: + case ICASEC: + case INOOP: + if(i->op == INOOP && i->branch != nil){ + leader(i->branch, ab); + leader(i->next, ab); + break; + } + leader(i->d.decl->ty->cse->iwild, ab); + lab = i->d.decl->ty->cse->labs; + n = i->d.decl->ty->cse->nlab; + for(j = 0; j < n; j++) + leader(lab[j].inst, ab); + leader(i->next, ab); + break; + case IRET: + case IEXIT: + case IRAISE: + leader(i->next, ab); + break; + case IJMP: + leader(i->branch, ab); + leader(i->next, ab); + break; + default: + if(i->branch != nil){ + leader(i->branch, ab); + leader(i->next, ab); + } + break; + } + } + firstb = lastb = mkblock(nil); + for(i = in; i != nil; i = i->next){ + if(i->pc != 0){ + b = findb(i, ab); + b->prev = lastb; + lastb->next = b; + if(canfallthrough(lastb->last)) + predsucc(lastb, b); + lastb = b; + } + else + lastb->last = i; + switch(i->op){ + case IGOTO: + case ICASE: + case ICASEL: + case ICASEC: + case INOOP: + if(i->op == INOOP && i->branch != nil){ + b = findb(i->next, ab); + predsucc(lastb, b); + b = findb(i->branch, ab); + predsucc(lastb, b); + break; + } + b = findb(i->d.decl->ty->cse->iwild, ab); + predsucc(lastb, b); + lab = i->d.decl->ty->cse->labs; + n = i->d.decl->ty->cse->nlab; + for(j = 0; j < n; j++){ + b = findb(lab[j].inst, ab); + predsucc(lastb, b); + } + break; + case IRET: + case IEXIT: + case IRAISE: + break; + case IJMP: + b = findb(i->branch, ab); + predsucc(lastb, b); + break; + default: + if(i->branch != nil){ + b = findb(i->next, ab); + predsucc(lastb, b); + b = findb(i->branch, ab); + predsucc(lastb, b); + } + break; + } + } + *nb = ab->n; + free(ab->a); + free(ab); + b = firstb->next; + b->prev = nil; + return b; +} + +static int +back(Block *b1, Block *b2) +{ + return b1->dfn >= b2->dfn; +} + +static void +pblocks(Block *b, int nb) +{ + Inst *i; + Blist *bl; + + print("--------------------%d blocks--------------------\n", nb); + print("------------------------------------------------\n"); + for( ; b != nil; b = b->next){ + print("dfn=%d\n", b->dfn); + print(" pred "); + for(bl = b->pred; bl != nil; bl = bl->next) + print("%d%s ", bl->block->dfn, back(bl->block, b) ? "*" : ""); + print("\n"); + print(" succ "); + for(bl = b->succ; bl != nil; bl = bl->next) + print("%d%s ", bl->block->dfn, back(b, bl->block) ? "*" : ""); + print("\n"); + for(i = b->first; i != nil; i = i->next){ + // print(" %I\n", i); + pins(i); + if(i == b->last) + break; + } + } + print("------------------------------------------------\n"); +} + +static void +ckblocks(Inst *in, Block *b, int nb) +{ + int n; + Block *lastb; + + if(b->first != in) + fatal("A - %d", b->dfn); + n = 0; + lastb = nil; + for( ; b != nil; b = b->next){ + n++; + if(b->prev != lastb) + fatal("a - %d\n", b->dfn); + if(b->prev != nil && b->prev->next != b) + fatal("b - %d\n", b->dfn); + if(b->next != nil && b->next->prev != b) + fatal("c - %d\n", b->dfn); + + if(b->prev != nil && b->prev->last->next != b->first) + fatal("B - %d\n", b->dfn); + if(b->next != nil && b->last->next != b->next->first) + fatal("C - %d\n", b->dfn); + if(b->next == nil && b->last->next != nil) + fatal("D - %d\n", b->dfn); + + if(b->last->branch != nil && b->succ->block->first != b->last->branch) + fatal("0 - %d\n", b->dfn); + + lastb = b; + } + if(n != nb) + fatal("N - %d %d\n", n, nb); +} + +static void +dfs0(Block *b, int *n) +{ + Block *s; + Blist *bl; + + b->flags = 1; + for(bl = b->succ; bl != nil; bl = bl->next){ + s = bl->block; + if(s->flags == 0) + dfs0(s, n); + } + b->dfn = --(*n); +} + +static int +dfs(Block *b, int nb) +{ + int n, u; + Block *b0; + + b0 = b; + n = nb; + dfs0(b0, &n); + u = 0; + for(b = b0; b != nil; b = b->next){ + if(b->flags == 0){ /* unreachable: see foldbranch */ + fatal("found unreachable code"); + u++; + b->prev->next = b->next; + if(b->next){ + b->next->prev = b->prev; + b->prev->last->next = b->next->first; + } + else + b->prev->last->next = nil; + } + b->flags = 0; + } + if(u){ + for(b = b0; b != nil; b = b->next) + b->dfn -= u; + } + return nb-u; +} + +static void +loop0(Block *b) +{ + Block *p; + Blist *bl; + + b->flags = 1; + for(bl = b->pred; bl != nil; bl = bl->next){ + p = bl->block; + if(p->flags == 0) + loop0(p); + } +} + +/* b1->b2 a back edge */ +static void +loop(Block *b, Block *b1, Block *b2) +{ + if(0 && debug['o']) + print("back edge %d->%d\n", b1->dfn, b2->dfn); + b2->flags = 1; + if(b1->flags == 0) + loop0(b1); + if(0 && debug['o']) + print(" loop "); + for( ; b != nil; b = b->next){ + if(b->flags && 0 && debug['o']) + print("%d ", b->dfn); + b->flags = 0; + } + if(0 && debug['o']) + print("\n"); +} + +static void +loops(Block *b) +{ + Block *b0; + Blist *bl; + + b0 = b; + for( ; b != nil; b = b->next){ + for(bl = b->succ; bl != nil; bl = bl->next){ + if(back(b, bl->block)) + loop(b0, b, bl->block); + } + } +} + +static int +imm(int m, Addr *a) +{ + if(m == Aimm) + return a->offset; + fatal("bad immediate value"); + return -1; +} + +static int +desc(int m, Addr *a) +{ + if(m == Adesc) + return a->decl->desc->size; + fatal("bad descriptor value"); + return -1; +} + +static int +fpoff(int m, Addr *a) +{ + int off; + Decl *d; + + if(m == Afp || m == Afpind){ + off = a->reg; + if((d = a->decl) != nil) + off += d->offset; + return off; + } + return -1; +} + +static int +size(Inst *i) +{ + switch(i->op){ + case ISEND: + case IRECV: + case IALT: + case INBALT: + case ILEA: + return i->m.offset; + case IMOVM: + case IHEADM: + case ICONSM: + return imm(i->mm, &i->m); + case IMOVMP: + case IHEADMP: + case ICONSMP: + return desc(i->mm, &i->m); + break; + } + fatal("bad op in size"); + return -1; +} + +static Decl* +mkdec(int o) +{ + Decl *d; + + d = mkdecl(&nosrc, Dlocal, tint); + d->offset = o; + return d; +} + +static void +mkdecls(void) +{ + regdecls = mkdec(REGRET*IBY2WD); + regdecls->next = mkdec(STemp); + regdecls->next->next = mkdec(DTemp); +} + +static Decl* +sharedecls(Decl *d) +{ + Decl *ld; + + ld = d; + for(d = d->next ; d != nil; d = d->next){ + if(d->offset <= ld->offset) + break; + ld = d; + } + return d; +} + +static int +finddec(int o, int s, Decl *vars, int *nv, Inst *i) +{ + int m, n; + Decl *d; + + n = 0; + for(d = vars; d != nil; d = d->next){ + if(o >= d->offset && o < d->offset+d->ty->size){ + m = 1; + while(o+s > d->offset+d->ty->size){ + m++; + d = d->next; + } + *nv = m; + return n; + } + n++; + } + // print("%d %d missing\n", o, s); + pins(i); + fatal("missing decl"); + return -1; +} + +static void +setud(Bits *b, int id, int n, int pc) +{ + if(id < 0) + return; + while(--n >= 0) + bset(b[id++], pc); +} + +static void +ud(Inst *i, Decl *vars, Bits *uses, Bits *defs) +{ + ushort f; + int id, j, nv, pc, sz, s, m, d, ss, sm, sd; + Optab *t; + Idlist *l; + + pc = i->pc; + ss = 0; + t = &opflags[i->op]; + f = t->flags; + sz = t->size; + s = fpoff(i->sm, &i->s); + m = fpoff(i->mm, &i->m); + d = fpoff(i->dm, &i->d); + if(f&Mduse && i->mm == Anone) + f |= Duse; + if(s >= 0){ + if(i->sm == Afp){ + ss = SS(sz); + if(ss == Mp) + ss = size(i); + } + else + ss = IBY2WD; + id = finddec(s, ss, vars, &nv, i); + if(f&Suse) + setud(uses, id, nv, pc); + if(f&Sdef){ + if(i->sm == Afp) + setud(defs, id, nv, pc); + else + setud(uses, id, nv, pc); + } + } + if(m >= 0){ + if(i->mm == Afp){ + sm = SM(sz); + if(sm == Mp) + sm = size(i); + } + else + sm = IBY2WD; + id = finddec(m, sm, vars, &nv, i); + if(f&Muse) + setud(uses, id, nv, pc); + if(f&Mdef){ + if(i->mm == Afp) + setud(defs, id, nv, pc); + else + setud(uses, id, nv, pc); + } + } + if(d >= 0){ + if(i->dm == Afp){ + sd = SD(sz); + if(sd == Mp) + sd = size(i); + } + else + sd = IBY2WD; + id = finddec(d, sd, vars, &nv, i); + if(f&Duse) + setud(uses, id, nv, pc); + if(f&Ddef){ + if(i->dm == Afp) + setud(defs, id, nv, pc); + else + setud(uses, id, nv, pc); + } + } + if(f&Tuse1){ + id = finddec(STemp, IBY2WD, vars, &nv, i); + setud(uses, id, nv, pc); + } + if(f&Tuse2){ + id = finddec(DTemp, IBY2WD, vars, &nv, i); + setud(uses, id, nv, pc); + } + if(i->op == ILEA){ + if(s >= 0){ + id = finddec(s, ss, vars, &nv, i); + if(i->sm == Afp && i->m.reg == 0) + setud(defs, id, nv, pc); + else + setud(uses, id, nv, pc); + } + } + if(0) + switch(i->op){ + case ILEA: + if(s >= 0){ + id = finddec(s, ss, vars, &nv, i); + if(id < 0) + break; + for(j = 0; j < nv; j++){ + if(i->sm == Afp && i->m.reg == 0) + addlist(&deflist, id++); + else + addlist(&uselist, id++); + } + } + break; + case IALT: + case INBALT: + case ICALL: + case IMCALL: + for(l = deflist; l != nil; l = l->next){ + id = l->id; + bset(defs[id], pc); + } + for(l = uselist; l != nil; l = l->next){ + id = l->id; + bset(uses[id], pc); + } + freelist(&deflist); + freelist(&uselist); + break; + } +} + +static void +usedef(Inst *in, Decl *vars, Bits *uses, Bits *defs) +{ + Inst *i; + + for(i = in; i != nil; i = i->next) + ud(i, vars, uses, defs); +} + +static void +pusedef(Bits *ud, int nv, Decl *d, char *s) +{ + int i; + + print("%s\n", s); + for(i = 0; i < nv; i++){ + if(!bzero(ud[i])){ + print("\t%s(%ld): ", decname(d), d->offset); + pbits(ud[i]); + print("\n"); + } + d = d->next; + } +} + +static void +dummydefs(Bits *defs, int nv, int npc) +{ + int i; + + for(i = 0; i < nv; i++) + bset(defs[i], npc++); +} + +static void +dogenkill(Block *b, Bits *defs, int nv) +{ + int i, n, t; + Bits v; + + n = defs[0].n; + v = bnew(n, 0); + for( ; b != nil; b = b->next){ + b->gen = bnew(n, 0); + b->kill = bnew(n, 0); + b->in = bnew(n, 0); + b->out = bnew(n, 0); + for(i = 0; i < nv; i++){ + boper(Bclr, v, v); + bsets(v, b->first->pc, b->last->pc); + boper(Band, defs[i], v); + t = topbit(v); + if(t >= 0) + bset(b->gen, t); + else + continue; + boper(Bclr, v, v); + bsets(v, b->first->pc, b->last->pc); + boper(Binv, v, v); + boper(Band, defs[i], v); + boper(Bor, v, b->kill); + } + } + bfree(v); +} + +static void +udflow(Block *b, int nv, int npc) +{ + int iter; + Block *b0, *p; + Blist *bl; + Bits newin; + + b0 = b; + for(b = b0; b != nil; b = b->next) + boper(Bstore, b->gen, b->out); + newin = bnew(b0->in.n, 0); + iter = 1; + while(iter){ + iter = 0; + for(b = b0; b != nil; b = b->next){ + boper(Bclr, newin, newin); + for(bl = b->pred; bl != nil; bl = bl->next){ + p = bl->block; + boper(Bor, p->out, newin); + } + if(b == b0) + bsets(newin, npc, npc+nv-1); + if(!beq(b->in, newin)) + iter = 1; + boper(Bstore, newin, b->in); + boper(Bstore, b->in, b->out); + boper(Bandrev, b->kill, b->out); + boper(Bor, b->gen, b->out); + } + } + bfree(newin); +} + +static void +pflows(Block *b) +{ + for( ; b != nil; b = b->next){ + print("block %d\n", b->dfn); + print(" gen: "); pbits(b->gen); print("\n"); + print(" kill: "); pbits(b->kill); print("\n"); + print(" in: "); pbits(b->in); print("\n"); + print(" out: "); pbits(b->out); print("\n"); + } +} + +static int +set(Decl *d) +{ + if(d->store == Darg) + return 1; + if(d->sym == nil) /* || d->sym->name[0] == '.') */ + return 1; + if(tattr[d->ty->kind].isptr || d->ty->kind == Texception) + return 1; + return 0; +} + +static int +used(Decl *d) +{ + if(d->sym == nil ) /* || d->sym->name[0] == '.') */ + return 1; + return 0; +} + +static void +udchain(Block *b, Decl *ds, int nv, int npc, Bits *defs, Bits *uses, Decl *sd) +{ + int i, n, p, q; + Bits d, u, dd, ud; + Block *b0; + Inst *in; + + b0 = b; + n = defs[0].n; + u = bnew(n, 0); + d = bnew(n, 0); + dd = bnew(n, 0); + ud = bnew(n, 0); + for(i = 0; i < nv; i++){ + boper(Bstore, defs[i], ud); + bclr(ud, npc+i); + for(b = b0 ; b != nil; b = b->next){ + boper(Bclr, u, u); + bsets(u, b->first->pc, b->last->pc); + boper(Band, uses[i], u); + boper(Bclr, d, d); + bsets(d, b->first->pc, b->last->pc); + boper(Band, defs[i], d); + for(;;){ + p = topbit(u); + if(p < 0) + break; + bclr(u, p); + bclrs(d, p, -1); + q = topbit(d); + if(q >= 0){ + bclr(ud, q); + if(debug['o']) + print("udc b=%d v=%d(%s/%ld) u=%d d=%d\n", b->dfn, i, decname(ds), ds->offset, p, q); + } + else{ + boper(Bstore, defs[i], dd); + boper(Band, b->in, dd); + boper(Bandrev, dd, ud); + if(!bzero(dd)){ + if(debug['o']){ + print("udc b=%d v=%d(%s/%ld) u=%d d=", b->dfn, i, decname(ds), ds->offset, p); + pbits(dd); + print("\n"); + } + if(bmem(dd, npc+i) && !set(ds)) + warning(pc2i(b0, p), "used and not set", ds, sd); + } + else + fatal("no defs in udchain"); + } + } + } + for(;;){ + p = topbit(ud); + if(p < 0) + break; + bclr(ud, p); + if(!used(ds)){ + in = pc2i(b0, p); + if(isnilsrc(&in->src)) /* nilling code */ + in->op = INOOP; /* elim p from bitmaps ? */ + else if(limbovar(ds) && !structure(ds->ty)) + warning(in, "set and not used", ds, sd); + } + } + ds = ds->next; + } + bfree(u); + bfree(d); + bfree(dd); + bfree(ud); +} + +static void +ckflags(void) +{ + int i, j, k, n; + Optab *o; + + n = nelem(opflags); + o = opflags; + for(i = 0; i < n; i++){ + j = (o->flags&(Suse|Sdef)) != 0; + k = SS(o->size) != 0; + if(j != k){ + if(!(j == 0 && k == 1 && i == ILEA)) + fatal("S %ld %s\n", o-opflags, instname[i]); + } + j = (o->flags&(Muse|Mdef)) != 0; + k = SM(o->size) != 0; + if(j != k) + fatal("M %ld %s\n", o-opflags, instname[i]); + j = (o->flags&(Duse|Ddef)) != 0; + k = SD(o->size) != 0; + if(j != k){ + if(!(j == 1 && k == 0 && (i == IGOTO || i == ICASE || i == ICASEC || i == ICASEL))) + fatal("D %ld %s\n", o-opflags, instname[i]); + } + o++; + } +} + +void +optim(Inst *in, Decl *d) +{ + int nb, npc, nv, nd, nu; + Block *b; + Bits *uses, *defs; + Decl *sd; + + ckflags(); + if(debug['o']) + print("************************************************\nfunction %s\n************************************************\n", d->sym->name); + if(in == nil || errors > 0) + return; + d = d->ty->ids; + if(regdecls == nil) + mkdecls(); + regdecls->next->next->next = d; + d = regdecls; + sd = sharedecls(d); + if(debug['o']) + printdecls(d); + b = mkblocks(in, &nb); + ckblocks(in, b, nb); + npc = inspc(in); + nb = dfs(b, nb); + if(debug['o']) + pblocks(b, nb); + loops(b); + nv = len(d); + uses = allocbits(nv, npc+nv); + defs = allocbits(nv, npc+nv); + dummydefs(defs, nv, npc); + usedef(in, d, uses, defs); + if(debug['o']){ + pusedef(uses, nv, d, "uses"); + pusedef(defs, nv, d, "defs"); + } + nu = bitcount(uses, nv); + nd = bitcount(defs, nv); + dogenkill(b, defs, nv); + udflow(b, nv, npc); + if(debug['o']) + pflows(b); + udchain(b, d, nv, npc, defs, uses, sd); + freeblks(b); + freebits(uses, nv); + freebits(defs, nv); + if(debug['o']) + print("nb=%d npc=%d nv=%d nd=%d nu=%d\n", nb, npc, nv, nd, nu); +} + diff --git a/limbo/runt.h b/limbo/runt.h new file mode 100644 index 00000000..1e3a368a --- /dev/null +++ b/limbo/runt.h @@ -0,0 +1,2012 @@ +typedef struct Sys_Qid Sys_Qid; +typedef struct Sys_Dir Sys_Dir; +typedef struct Sys_FD Sys_FD; +typedef struct Sys_Connection Sys_Connection; +typedef struct Sys_FileIO Sys_FileIO; +typedef struct Draw_Point Draw_Point; +typedef struct Draw_Rect Draw_Rect; +typedef struct Draw_Image Draw_Image; +typedef struct Draw_Font Draw_Font; +typedef struct Draw_Display Draw_Display; +typedef struct Draw_Screen Draw_Screen; +typedef struct Draw_Pointer Draw_Pointer; +typedef struct Draw_Context Draw_Context; +typedef struct Prefab_Style Prefab_Style; +typedef struct Prefab_Environ Prefab_Environ; +typedef struct Prefab_Layout Prefab_Layout; +typedef struct Prefab_Element Prefab_Element; +typedef struct Prefab_Compound Prefab_Compound; +typedef struct Tk_Toplevel Tk_Toplevel; +typedef struct Keyring_SigAlg Keyring_SigAlg; +typedef struct Keyring_PK Keyring_PK; +typedef struct Keyring_SK Keyring_SK; +typedef struct Keyring_Certificate Keyring_Certificate; +typedef struct Keyring_DigestState Keyring_DigestState; +struct Sys_Qid +{ + WORD path; + WORD vers; +}; +#define Sys_Qid_size 8 +#define Sys_Qid_map {0} +struct Sys_Dir +{ + String* name; + String* uid; + String* gid; + Sys_Qid qid; + WORD mode; + WORD atime; + WORD mtime; + WORD length; + WORD dtype; + WORD dev; +}; +#define Sys_Dir_size 44 +#define Sys_Dir_map {0xe0,} +struct Sys_FD +{ + WORD fd; +}; +#define Sys_FD_size 4 +#define Sys_FD_map {0} +struct Sys_Connection +{ + Sys_FD* dfd; + Sys_FD* cfd; + String* dir; +}; +#define Sys_Connection_size 12 +#define Sys_Connection_map {0xe0,} +struct Sys_FileIO +{ + Channel* rd_req; + Channel* rd_rep; + Channel* wr_req; + Channel* wr_rep; +}; +typedef struct{ WORD t0; WORD t1; WORD t2; } Sys_FileIO_rd_req; +#define Sys_FileIO_rd_req_size 12 +#define Sys_FileIO_rd_req_map {0} +typedef struct{ Array* t0; String* t1; } Sys_FileIO_rd_rep; +#define Sys_FileIO_rd_rep_size 8 +#define Sys_FileIO_rd_rep_map {0xc0,} +typedef struct{ WORD t0; Array* t1; WORD t2; } Sys_FileIO_wr_req; +#define Sys_FileIO_wr_req_size 12 +#define Sys_FileIO_wr_req_map {0x40,} +typedef struct{ WORD t0; String* t1; } Sys_FileIO_wr_rep; +#define Sys_FileIO_wr_rep_size 8 +#define Sys_FileIO_wr_rep_map {0x40,} +#define Sys_FileIO_size 16 +#define Sys_FileIO_map {0xf0,} +struct Draw_Point +{ + WORD x; + WORD y; +}; +#define Draw_Point_size 8 +#define Draw_Point_map {0} +struct Draw_Rect +{ + Draw_Point min; + Draw_Point max; +}; +#define Draw_Rect_size 16 +#define Draw_Rect_map {0} +struct Draw_Image +{ + Draw_Rect r; + Draw_Rect clipr; + WORD ldepth; + WORD repl; +}; +#define Draw_Image_size 40 +#define Draw_Image_map {0} +struct Draw_Font +{ + String* name; + WORD height; + WORD ascent; +}; +#define Draw_Font_size 12 +#define Draw_Font_map {0x80,} +struct Draw_Display +{ + Draw_Image* image; + Draw_Image* ones; + Draw_Image* zeros; +}; +#define Draw_Display_size 12 +#define Draw_Display_map {0xe0,} +struct Draw_Screen +{ + WORD id; + Draw_Image* image; + Draw_Image* fill; +}; +#define Draw_Screen_size 12 +#define Draw_Screen_map {0x60,} +struct Draw_Pointer +{ + WORD buttons; + Draw_Point xy; +}; +#define Draw_Pointer_size 12 +#define Draw_Pointer_map {0} +struct Draw_Context +{ + Draw_Screen* screen; + Draw_Display* display; + Channel* cir; + Channel* ckbd; + Channel* cptr; + Channel* ctoappl; + Channel* ctomux; +}; +typedef WORD Draw_Context_cir; +#define Draw_Context_cir_size 4 +#define Draw_Context_cir_map {0} +typedef WORD Draw_Context_ckbd; +#define Draw_Context_ckbd_size 4 +#define Draw_Context_ckbd_map {0} +typedef Draw_Pointer* Draw_Context_cptr; +#define Draw_Context_cptr_size 4 +#define Draw_Context_cptr_map {0x80,} +typedef WORD Draw_Context_ctoappl; +#define Draw_Context_ctoappl_size 4 +#define Draw_Context_ctoappl_map {0} +typedef WORD Draw_Context_ctomux; +#define Draw_Context_ctomux_size 4 +#define Draw_Context_ctomux_map {0} +#define Draw_Context_size 28 +#define Draw_Context_map {0xfe,} +struct Prefab_Style +{ + Draw_Font* titlefont; + Draw_Font* textfont; + Draw_Image* screencolor; + Draw_Image* elemcolor; + Draw_Image* edgecolor; + Draw_Image* titlecolor; + Draw_Image* textcolor; + Draw_Image* highlightcolor; +}; +#define Prefab_Style_size 32 +#define Prefab_Style_map {0xff,} +struct Prefab_Environ +{ + Draw_Screen* screen; + Prefab_Style* style; +}; +#define Prefab_Environ_size 8 +#define Prefab_Environ_map {0xc0,} +struct Prefab_Layout +{ + Draw_Font* font; + Draw_Image* color; + String* text; + Draw_Image* icon; + Draw_Image* mask; + String* tag; +}; +#define Prefab_Layout_size 24 +#define Prefab_Layout_map {0xfc,} +struct Prefab_Element +{ + WORD kind; + Draw_Rect r; + Prefab_Environ* environ; + String* tag; + List* kids; + String* str; + Draw_Image* mask; + Draw_Image* image; + Draw_Font* font; +}; +#define Prefab_Element_size 48 +#define Prefab_Element_map {0x7,0xf0,} +struct Prefab_Compound +{ + Draw_Image* image; + Prefab_Environ* environ; + Draw_Rect r; + Prefab_Element* title; + Prefab_Element* contents; +}; +#define Prefab_Compound_size 32 +#define Prefab_Compound_map {0xc3,} +struct Tk_Toplevel +{ + WORD id; +}; +#define Tk_Toplevel_size 4 +#define Tk_Toplevel_map {0} +struct Keyring_SigAlg +{ + String* name; +}; +#define Keyring_SigAlg_size 4 +#define Keyring_SigAlg_map {0x80,} +struct Keyring_PK +{ + Keyring_SigAlg* sa; + String* owner; +}; +#define Keyring_PK_size 8 +#define Keyring_PK_map {0xc0,} +struct Keyring_SK +{ + Keyring_SigAlg* sa; + String* owner; +}; +#define Keyring_SK_size 8 +#define Keyring_SK_map {0xc0,} +struct Keyring_Certificate +{ + Keyring_SigAlg* sa; + String* signer; + WORD exp; +}; +#define Keyring_Certificate_size 12 +#define Keyring_Certificate_map {0xc0,} +struct Keyring_DigestState +{ + WORD x; +}; +#define Keyring_DigestState_size 4 +#define Keyring_DigestState_map {0} +void Sys_announce(void*); +typedef struct F_Sys_announce F_Sys_announce; +struct F_Sys_announce +{ + WORD regs[NREG-1]; + struct{ WORD t0; Sys_Connection t1; }* ret; + uchar temps[12]; + String* addr; +}; +void Sys_bind(void*); +typedef struct F_Sys_bind F_Sys_bind; +struct F_Sys_bind +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* s; + String* on; + WORD flags; +}; +void Sys_byte2char(void*); +typedef struct F_Sys_byte2char F_Sys_byte2char; +struct F_Sys_byte2char +{ + WORD regs[NREG-1]; + struct{ WORD t0; WORD t1; WORD t2; }* ret; + uchar temps[12]; + Array* buf; + WORD n; +}; +void Sys_char2byte(void*); +typedef struct F_Sys_char2byte F_Sys_char2byte; +struct F_Sys_char2byte +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD c; + Array* buf; + WORD n; +}; +void Sys_chdir(void*); +typedef struct F_Sys_chdir F_Sys_chdir; +struct F_Sys_chdir +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* path; +}; +void Sys_create(void*); +typedef struct F_Sys_create F_Sys_create; +struct F_Sys_create +{ + WORD regs[NREG-1]; + Sys_FD** ret; + uchar temps[12]; + String* s; + WORD mode; + WORD perm; +}; +void Sys_dial(void*); +typedef struct F_Sys_dial F_Sys_dial; +struct F_Sys_dial +{ + WORD regs[NREG-1]; + struct{ WORD t0; Sys_Connection t1; }* ret; + uchar temps[12]; + String* addr; + String* local; +}; +void Sys_dirread(void*); +typedef struct F_Sys_dirread F_Sys_dirread; +struct F_Sys_dirread +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + Array* dir; +}; +void Sys_dup(void*); +typedef struct F_Sys_dup F_Sys_dup; +struct F_Sys_dup +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD old; + WORD new; +}; +void Sys_export(void*); +typedef struct F_Sys_export F_Sys_export; +struct F_Sys_export +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* c; + WORD flag; +}; +void Sys_fildes(void*); +typedef struct F_Sys_fildes F_Sys_fildes; +struct F_Sys_fildes +{ + WORD regs[NREG-1]; + Sys_FD** ret; + uchar temps[12]; + WORD fd; +}; +void Sys_file2chan(void*); +typedef struct F_Sys_file2chan F_Sys_file2chan; +struct F_Sys_file2chan +{ + WORD regs[NREG-1]; + Sys_FileIO** ret; + uchar temps[12]; + String* dir; + String* file; + WORD flags; +}; +void Sys_fprint(void*); +typedef struct F_Sys_fprint F_Sys_fprint; +struct F_Sys_fprint +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + String* s; + WORD vargs; +}; +void Sys_fstat(void*); +typedef struct F_Sys_fstat F_Sys_fstat; +struct F_Sys_fstat +{ + WORD regs[NREG-1]; + struct{ WORD t0; Sys_Dir t1; }* ret; + uchar temps[12]; + Sys_FD* fd; +}; +void Sys_fwstat(void*); +typedef struct F_Sys_fwstat F_Sys_fwstat; +struct F_Sys_fwstat +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + Sys_Dir d; +}; +void Sys_listen(void*); +typedef struct F_Sys_listen F_Sys_listen; +struct F_Sys_listen +{ + WORD regs[NREG-1]; + struct{ WORD t0; Sys_Connection t1; }* ret; + uchar temps[12]; + Sys_Connection c; +}; +void Sys_millisec(void*); +typedef struct F_Sys_millisec F_Sys_millisec; +struct F_Sys_millisec +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; +}; +void Sys_mount(void*); +typedef struct F_Sys_mount F_Sys_mount; +struct F_Sys_mount +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + String* on; + WORD flags; + String* spec; +}; +void Sys_open(void*); +typedef struct F_Sys_open F_Sys_open; +struct F_Sys_open +{ + WORD regs[NREG-1]; + Sys_FD** ret; + uchar temps[12]; + String* s; + WORD mode; +}; +void Sys_pctl(void*); +typedef struct F_Sys_pctl F_Sys_pctl; +struct F_Sys_pctl +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD flags; +}; +void Sys_print(void*); +typedef struct F_Sys_print F_Sys_print; +struct F_Sys_print +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* s; + WORD vargs; +}; +void Sys_read(void*); +typedef struct F_Sys_read F_Sys_read; +struct F_Sys_read +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + Array* buf; + WORD n; +}; +void Sys_remove(void*); +typedef struct F_Sys_remove F_Sys_remove; +struct F_Sys_remove +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* s; +}; +void Sys_seek(void*); +typedef struct F_Sys_seek F_Sys_seek; +struct F_Sys_seek +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + WORD off; + WORD start; +}; +void Sys_sleep(void*); +typedef struct F_Sys_sleep F_Sys_sleep; +struct F_Sys_sleep +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD period; +}; +void Sys_sprint(void*); +typedef struct F_Sys_sprint F_Sys_sprint; +struct F_Sys_sprint +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + String* s; + WORD vargs; +}; +void Sys_stat(void*); +typedef struct F_Sys_stat F_Sys_stat; +struct F_Sys_stat +{ + WORD regs[NREG-1]; + struct{ WORD t0; Sys_Dir t1; }* ret; + uchar temps[12]; + String* s; +}; +void Sys_stream(void*); +typedef struct F_Sys_stream F_Sys_stream; +struct F_Sys_stream +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* src; + Sys_FD* dst; + WORD bufsiz; +}; +void Sys_tokenize(void*); +typedef struct F_Sys_tokenize F_Sys_tokenize; +struct F_Sys_tokenize +{ + WORD regs[NREG-1]; + struct{ WORD t0; List* t1; }* ret; + uchar temps[12]; + String* s; + String* delim; +}; +void Sys_unmount(void*); +typedef struct F_Sys_unmount F_Sys_unmount; +struct F_Sys_unmount +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* s1; + String* s2; +}; +void Sys_utfbytes(void*); +typedef struct F_Sys_utfbytes F_Sys_utfbytes; +struct F_Sys_utfbytes +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Array* buf; + WORD n; +}; +void Sys_write(void*); +typedef struct F_Sys_write F_Sys_write; +struct F_Sys_write +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Sys_FD* fd; + Array* buf; + WORD n; +}; +void Sys_wstat(void*); +typedef struct F_Sys_wstat F_Sys_wstat; +struct F_Sys_wstat +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + String* s; + Sys_Dir d; +}; +#define Sys_ERRLEN 64 +#define Sys_WAITLEN 64 +#define Sys_OREAD 0 +#define Sys_OWRITE 1 +#define Sys_ORDWR 2 +#define Sys_CHDIR -2147483648 +#define Sys_MREPL 0 +#define Sys_MBEFORE 1 +#define Sys_MAFTER 2 +#define Sys_MCREATE 4 +#define Sys_NEWFD 1 +#define Sys_FORKFD 2 +#define Sys_NEWNS 4 +#define Sys_FORKNS 8 +#define Sys_NEWPGRP 16 +#define Sys_NODEVS 32 +#define Sys_EXPWAIT 0 +#define Sys_EXPASYNC 1 +#define Sys_UTFmax 3 +#define Sys_UTFerror 128 +void Point_add(void*); +typedef struct F_Point_add F_Point_add; +struct F_Point_add +{ + WORD regs[NREG-1]; + Draw_Point* ret; + uchar temps[12]; + Draw_Point p; + Draw_Point q; +}; +void Point_sub(void*); +typedef struct F_Point_sub F_Point_sub; +struct F_Point_sub +{ + WORD regs[NREG-1]; + Draw_Point* ret; + uchar temps[12]; + Draw_Point p; + Draw_Point q; +}; +void Point_mul(void*); +typedef struct F_Point_mul F_Point_mul; +struct F_Point_mul +{ + WORD regs[NREG-1]; + Draw_Point* ret; + uchar temps[12]; + Draw_Point p; + WORD i; +}; +void Point_div(void*); +typedef struct F_Point_div F_Point_div; +struct F_Point_div +{ + WORD regs[NREG-1]; + Draw_Point* ret; + uchar temps[12]; + Draw_Point p; + WORD i; +}; +void Point_eq(void*); +typedef struct F_Point_eq F_Point_eq; +struct F_Point_eq +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Point p; + Draw_Point q; +}; +void Rect_canon(void*); +typedef struct F_Rect_canon F_Rect_canon; +struct F_Rect_canon +{ + WORD regs[NREG-1]; + Draw_Rect* ret; + uchar temps[12]; + Draw_Rect r; +}; +void Rect_dx(void*); +typedef struct F_Rect_dx F_Rect_dx; +struct F_Rect_dx +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; +}; +void Rect_dy(void*); +typedef struct F_Rect_dy F_Rect_dy; +struct F_Rect_dy +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; +}; +void Rect_eq(void*); +typedef struct F_Rect_eq F_Rect_eq; +struct F_Rect_eq +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Rect s; +}; +void Rect_Xrect(void*); +typedef struct F_Rect_Xrect F_Rect_Xrect; +struct F_Rect_Xrect +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Rect s; +}; +void Rect_inrect(void*); +typedef struct F_Rect_inrect F_Rect_inrect; +struct F_Rect_inrect +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Rect s; +}; +void Rect_clip(void*); +typedef struct F_Rect_clip F_Rect_clip; +struct F_Rect_clip +{ + WORD regs[NREG-1]; + struct{ Draw_Rect t0; WORD t1; }* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Rect s; +}; +void Rect_contains(void*); +typedef struct F_Rect_contains F_Rect_contains; +struct F_Rect_contains +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Point p; +}; +void Rect_addpt(void*); +typedef struct F_Rect_addpt F_Rect_addpt; +struct F_Rect_addpt +{ + WORD regs[NREG-1]; + Draw_Rect* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Point p; +}; +void Rect_subpt(void*); +typedef struct F_Rect_subpt F_Rect_subpt; +struct F_Rect_subpt +{ + WORD regs[NREG-1]; + Draw_Rect* ret; + uchar temps[12]; + Draw_Rect r; + Draw_Point p; +}; +void Rect_inset(void*); +typedef struct F_Rect_inset F_Rect_inset; +struct F_Rect_inset +{ + WORD regs[NREG-1]; + Draw_Rect* ret; + uchar temps[12]; + Draw_Rect r; + WORD n; +}; +void Image_draw(void*); +typedef struct F_Image_draw F_Image_draw; +struct F_Image_draw +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Image* dst; + Draw_Rect r; + Draw_Image* src; + Draw_Image* mask; + Draw_Point p; +}; +void Image_line(void*); +typedef struct F_Image_line F_Image_line; +struct F_Image_line +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Image* dst; + Draw_Point p0; + Draw_Point p1; + WORD radius; + Draw_Image* src; +}; +void Image_text(void*); +typedef struct F_Image_text F_Image_text; +struct F_Image_text +{ + WORD regs[NREG-1]; + Draw_Point* ret; + uchar temps[12]; + Draw_Image* dst; + Draw_Point p; + Draw_Image* src; + Draw_Font* font; + String* str; +}; +void Image_readpixels(void*); +typedef struct F_Image_readpixels F_Image_readpixels; +struct F_Image_readpixels +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Image* src; + Draw_Rect r; + Array* data; +}; +void Image_writepixels(void*); +typedef struct F_Image_writepixels F_Image_writepixels; +struct F_Image_writepixels +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Image* dst; + Draw_Rect r; + Array* data; +}; +void Image_top(void*); +typedef struct F_Image_top F_Image_top; +struct F_Image_top +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Image* win; +}; +void Image_bottom(void*); +typedef struct F_Image_bottom F_Image_bottom; +struct F_Image_bottom +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Image* win; +}; +void Image_setrefresh(void*); +typedef struct F_Image_setrefresh F_Image_setrefresh; +struct F_Image_setrefresh +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Image* win; + WORD func; +}; +void Font_open(void*); +typedef struct F_Font_open F_Font_open; +struct F_Font_open +{ + WORD regs[NREG-1]; + Draw_Font** ret; + uchar temps[12]; + Draw_Display* d; + String* name; +}; +void Font_build(void*); +typedef struct F_Font_build F_Font_build; +struct F_Font_build +{ + WORD regs[NREG-1]; + Draw_Font** ret; + uchar temps[12]; + Draw_Display* d; + String* name; + String* desc; +}; +void Font_width(void*); +typedef struct F_Font_width F_Font_width; +struct F_Font_width +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Draw_Font* f; + String* str; +}; +void Display_allocate(void*); +typedef struct F_Display_allocate F_Display_allocate; +struct F_Display_allocate +{ + WORD regs[NREG-1]; + Draw_Display** ret; + uchar temps[12]; + String* dev; +}; +void Display_startrefresh(void*); +typedef struct F_Display_startrefresh F_Display_startrefresh; +struct F_Display_startrefresh +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Display* d; +}; +void Display_publicscreen(void*); +typedef struct F_Display_publicscreen F_Display_publicscreen; +struct F_Display_publicscreen +{ + WORD regs[NREG-1]; + Draw_Screen** ret; + uchar temps[12]; + Draw_Display* d; + WORD id; +}; +void Display_newimage(void*); +typedef struct F_Display_newimage F_Display_newimage; +struct F_Display_newimage +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Display* d; + Draw_Rect r; + WORD ldepth; + WORD repl; + WORD color; +}; +void Display_readimage(void*); +typedef struct F_Display_readimage F_Display_readimage; +struct F_Display_readimage +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Display* d; + Sys_FD* fd; +}; +void Display_open(void*); +typedef struct F_Display_open F_Display_open; +struct F_Display_open +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Display* d; + String* name; +}; +void Display_color(void*); +typedef struct F_Display_color F_Display_color; +struct F_Display_color +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Display* d; + WORD color; +}; +void Display_rgb(void*); +typedef struct F_Display_rgb F_Display_rgb; +struct F_Display_rgb +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Display* d; + WORD r; + WORD g; + WORD b; +}; +void Screen_allocate(void*); +typedef struct F_Screen_allocate F_Screen_allocate; +struct F_Screen_allocate +{ + WORD regs[NREG-1]; + Draw_Screen** ret; + uchar temps[12]; + Draw_Image* image; + Draw_Image* fill; + WORD public; +}; +void Screen_newwindow(void*); +typedef struct F_Screen_newwindow F_Screen_newwindow; +struct F_Screen_newwindow +{ + WORD regs[NREG-1]; + Draw_Image** ret; + uchar temps[12]; + Draw_Screen* screen; + Draw_Rect r; +}; +void Screen_top(void*); +typedef struct F_Screen_top F_Screen_top; +struct F_Screen_top +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Draw_Screen* screen; + Array* wins; +}; +#define Draw_Black 255 +#define Draw_Blue 201 +#define Draw_Red 15 +#define Draw_Yellow 3 +#define Draw_Green 192 +#define Draw_White 0 +#define Draw_Backup 0 +#define Draw_Prefab 1 +#define Draw_AMexit 10 +#define Draw_AMstartir 11 +#define Draw_AMstartkbd 12 +#define Draw_AMstartptr 13 +#define Draw_AMnewpin 14 +#define Draw_MAtop 20 +void Element_icon(void*); +typedef struct F_Element_icon F_Element_icon; +struct F_Element_icon +{ + WORD regs[NREG-1]; + Prefab_Element** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Rect r; + Draw_Image* icon; + Draw_Image* mask; +}; +void Element_text(void*); +typedef struct F_Element_text F_Element_text; +struct F_Element_text +{ + WORD regs[NREG-1]; + Prefab_Element** ret; + uchar temps[12]; + Prefab_Environ* env; + String* text; + Draw_Rect r; + WORD kind; +}; +void Element_layout(void*); +typedef struct F_Element_layout F_Element_layout; +struct F_Element_layout +{ + WORD regs[NREG-1]; + Prefab_Element** ret; + uchar temps[12]; + Prefab_Environ* env; + List* lay; + Draw_Rect r; + WORD kind; +}; +void Element_elist(void*); +typedef struct F_Element_elist F_Element_elist; +struct F_Element_elist +{ + WORD regs[NREG-1]; + Prefab_Element** ret; + uchar temps[12]; + Prefab_Environ* env; + Prefab_Element* elem; + WORD kind; +}; +void Element_separator(void*); +typedef struct F_Element_separator F_Element_separator; +struct F_Element_separator +{ + WORD regs[NREG-1]; + Prefab_Element** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Rect r; + Draw_Image* icon; + Draw_Image* mask; +}; +void Element_append(void*); +typedef struct F_Element_append F_Element_append; +struct F_Element_append +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Prefab_Element* elist; + Prefab_Element* elem; +}; +void Element_adjust(void*); +typedef struct F_Element_adjust F_Element_adjust; +struct F_Element_adjust +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Element* elem; + WORD equal; + WORD dir; +}; +void Element_clip(void*); +typedef struct F_Element_clip F_Element_clip; +struct F_Element_clip +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Element* elem; + Draw_Rect r; +}; +void Element_scroll(void*); +typedef struct F_Element_scroll F_Element_scroll; +struct F_Element_scroll +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Element* elem; + Draw_Point d; +}; +void Element_translate(void*); +typedef struct F_Element_translate F_Element_translate; +struct F_Element_translate +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Element* elem; + Draw_Point d; +}; +void Element_show(void*); +typedef struct F_Element_show F_Element_show; +struct F_Element_show +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Prefab_Element* elist; + Prefab_Element* elem; +}; +void Compound_iconbox(void*); +typedef struct F_Compound_iconbox F_Compound_iconbox; +struct F_Compound_iconbox +{ + WORD regs[NREG-1]; + Prefab_Compound** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Point p; + String* title; + Draw_Image* icon; + Draw_Image* mask; +}; +void Compound_textbox(void*); +typedef struct F_Compound_textbox F_Compound_textbox; +struct F_Compound_textbox +{ + WORD regs[NREG-1]; + Prefab_Compound** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Rect r; + String* title; + String* text; +}; +void Compound_layoutbox(void*); +typedef struct F_Compound_layoutbox F_Compound_layoutbox; +struct F_Compound_layoutbox +{ + WORD regs[NREG-1]; + Prefab_Compound** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Rect r; + String* title; + List* lay; +}; +void Compound_box(void*); +typedef struct F_Compound_box F_Compound_box; +struct F_Compound_box +{ + WORD regs[NREG-1]; + Prefab_Compound** ret; + uchar temps[12]; + Prefab_Environ* env; + Draw_Point p; + Prefab_Element* title; + Prefab_Element* elist; +}; +void Compound_draw(void*); +typedef struct F_Compound_draw F_Compound_draw; +struct F_Compound_draw +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Compound* comp; +}; +void Compound_scroll(void*); +typedef struct F_Compound_scroll F_Compound_scroll; +struct F_Compound_scroll +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Compound* comp; + String* elem; + Draw_Point d; +}; +void Compound_show(void*); +typedef struct F_Compound_show F_Compound_show; +struct F_Compound_show +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Prefab_Compound* comp; + String* elem; +}; +void Compound_select(void*); +typedef struct F_Compound_select F_Compound_select; +struct F_Compound_select +{ + WORD regs[NREG-1]; + struct{ WORD t0; WORD t1; Prefab_Element* t2; }* ret; + uchar temps[12]; + Prefab_Compound* comp; + String* elem; + WORD i; + Channel* c; +}; +void Compound_tagselect(void*); +typedef struct F_Compound_tagselect F_Compound_tagselect; +struct F_Compound_tagselect +{ + WORD regs[NREG-1]; + struct{ WORD t0; WORD t1; Prefab_Element* t2; }* ret; + uchar temps[12]; + Prefab_Compound* comp; + String* elem; + WORD i; + Channel* c; +}; +void Compound_highlight(void*); +typedef struct F_Compound_highlight F_Compound_highlight; +struct F_Compound_highlight +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + Prefab_Compound* comp; + String* elem; + WORD on; +}; +#define Prefab_EIcon 0 +#define Prefab_EText 1 +#define Prefab_ETitle 2 +#define Prefab_EHorizontal 3 +#define Prefab_EVertical 4 +#define Prefab_ESeparator 5 +#define Prefab_Adjpack 10 +#define Prefab_Adjequal 11 +#define Prefab_Adjfill 12 +#define Prefab_Adjleft 20 +#define Prefab_Adjup 20 +#define Prefab_Adjcenter 21 +#define Prefab_Adjright 22 +#define Prefab_Adjdown 22 +void Tk_toplevel(void*); +typedef struct F_Tk_toplevel F_Tk_toplevel; +struct F_Tk_toplevel +{ + WORD regs[NREG-1]; + Tk_Toplevel** ret; + uchar temps[12]; + Draw_Screen* screen; + String* arg; +}; +void Tk_namechan(void*); +typedef struct F_Tk_namechan F_Tk_namechan; +struct F_Tk_namechan +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + Tk_Toplevel* t; + Channel* c; + String* n; +}; +void Tk_cmd(void*); +typedef struct F_Tk_cmd F_Tk_cmd; +struct F_Tk_cmd +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + Tk_Toplevel* t; + String* arg; +}; +void Tk_mouse(void*); +typedef struct F_Tk_mouse F_Tk_mouse; +struct F_Tk_mouse +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + WORD x; + WORD y; + WORD button; +}; +void Tk_keyboard(void*); +typedef struct F_Tk_keyboard F_Tk_keyboard; +struct F_Tk_keyboard +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + WORD key; +}; +void Real_acos(void*); +typedef struct F_Real_acos F_Real_acos; +struct F_Real_acos +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_acosh(void*); +typedef struct F_Real_acosh F_Real_acosh; +struct F_Real_acosh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_asin(void*); +typedef struct F_Real_asin F_Real_asin; +struct F_Real_asin +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_asinh(void*); +typedef struct F_Real_asinh F_Real_asinh; +struct F_Real_asinh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_atan(void*); +typedef struct F_Real_atan F_Real_atan; +struct F_Real_atan +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_atan2(void*); +typedef struct F_Real_atan2 F_Real_atan2; +struct F_Real_atan2 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL y; + REAL x; +}; +void Real_atanh(void*); +typedef struct F_Real_atanh F_Real_atanh; +struct F_Real_atanh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_cbrt(void*); +typedef struct F_Real_cbrt F_Real_cbrt; +struct F_Real_cbrt +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_ceil(void*); +typedef struct F_Real_ceil F_Real_ceil; +struct F_Real_ceil +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_copysign(void*); +typedef struct F_Real_copysign F_Real_copysign; +struct F_Real_copysign +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL s; +}; +void Real_cos(void*); +typedef struct F_Real_cos F_Real_cos; +struct F_Real_cos +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_cosh(void*); +typedef struct F_Real_cosh F_Real_cosh; +struct F_Real_cosh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_dot(void*); +typedef struct F_Real_dot F_Real_dot; +struct F_Real_dot +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + Array* x; + Array* y; +}; +void Real_erf(void*); +typedef struct F_Real_erf F_Real_erf; +struct F_Real_erf +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_erfc(void*); +typedef struct F_Real_erfc F_Real_erfc; +struct F_Real_erfc +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_exp(void*); +typedef struct F_Real_exp F_Real_exp; +struct F_Real_exp +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_expm1(void*); +typedef struct F_Real_expm1 F_Real_expm1; +struct F_Real_expm1 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_fabs(void*); +typedef struct F_Real_fabs F_Real_fabs; +struct F_Real_fabs +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_fdim(void*); +typedef struct F_Real_fdim F_Real_fdim; +struct F_Real_fdim +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_fmin(void*); +typedef struct F_Real_fmin F_Real_fmin; +struct F_Real_fmin +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_fmax(void*); +typedef struct F_Real_fmax F_Real_fmax; +struct F_Real_fmax +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_finite(void*); +typedef struct F_Real_finite F_Real_finite; +struct F_Real_finite +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + REAL x; +}; +void Real_floor(void*); +typedef struct F_Real_floor F_Real_floor; +struct F_Real_floor +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_fmod(void*); +typedef struct F_Real_fmod F_Real_fmod; +struct F_Real_fmod +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_gemm(void*); +typedef struct F_Real_gemm F_Real_gemm; +struct F_Real_gemm +{ + WORD regs[NREG-1]; + WORD noret; + uchar temps[12]; + WORD transa; + WORD transb; + WORD m; + WORD n; + WORD k; + WORD _pad52; + REAL alpha; + Array* a; + WORD ai0; + WORD aj0; + WORD lda; + Array* b; + WORD bi0; + WORD bj0; + WORD ldb; + REAL beta; + Array* c; + WORD ci0; + WORD cj0; + WORD ldc; +}; +void Real_getFPcontrol(void*); +typedef struct F_Real_getFPcontrol F_Real_getFPcontrol; +struct F_Real_getFPcontrol +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; +}; +void Real_getFPstatus(void*); +typedef struct F_Real_getFPstatus F_Real_getFPstatus; +struct F_Real_getFPstatus +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; +}; +void Real_FPcontrol(void*); +typedef struct F_Real_FPcontrol F_Real_FPcontrol; +struct F_Real_FPcontrol +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD r; + WORD mask; +}; +void Real_FPstatus(void*); +typedef struct F_Real_FPstatus F_Real_FPstatus; +struct F_Real_FPstatus +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + WORD r; + WORD mask; +}; +void Real_hypot(void*); +typedef struct F_Real_hypot F_Real_hypot; +struct F_Real_hypot +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_iamax(void*); +typedef struct F_Real_iamax F_Real_iamax; +struct F_Real_iamax +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Array* x; +}; +void Real_ilogb(void*); +typedef struct F_Real_ilogb F_Real_ilogb; +struct F_Real_ilogb +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + REAL x; +}; +void Real_isnan(void*); +typedef struct F_Real_isnan F_Real_isnan; +struct F_Real_isnan +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + REAL x; +}; +void Real_j0(void*); +typedef struct F_Real_j0 F_Real_j0; +struct F_Real_j0 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_j1(void*); +typedef struct F_Real_j1 F_Real_j1; +struct F_Real_j1 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_jn(void*); +typedef struct F_Real_jn F_Real_jn; +struct F_Real_jn +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + WORD n; + WORD _pad36; + REAL x; +}; +void Real_lgamma(void*); +typedef struct F_Real_lgamma F_Real_lgamma; +struct F_Real_lgamma +{ + WORD regs[NREG-1]; + struct{ WORD t0; REAL t1; }* ret; + uchar temps[12]; + REAL x; +}; +void Real_log(void*); +typedef struct F_Real_log F_Real_log; +struct F_Real_log +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_log10(void*); +typedef struct F_Real_log10 F_Real_log10; +struct F_Real_log10 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_log1p(void*); +typedef struct F_Real_log1p F_Real_log1p; +struct F_Real_log1p +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_modf(void*); +typedef struct F_Real_modf F_Real_modf; +struct F_Real_modf +{ + WORD regs[NREG-1]; + struct{ WORD t0; REAL t1; }* ret; + uchar temps[12]; + REAL x; +}; +void Real_nextafter(void*); +typedef struct F_Real_nextafter F_Real_nextafter; +struct F_Real_nextafter +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_norm1(void*); +typedef struct F_Real_norm1 F_Real_norm1; +struct F_Real_norm1 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + Array* x; +}; +void Real_norm2(void*); +typedef struct F_Real_norm2 F_Real_norm2; +struct F_Real_norm2 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + Array* x; +}; +void Real_pow(void*); +typedef struct F_Real_pow F_Real_pow; +struct F_Real_pow +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL y; +}; +void Real_pow10(void*); +typedef struct F_Real_pow10 F_Real_pow10; +struct F_Real_pow10 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + WORD p; +}; +void Real_remainder(void*); +typedef struct F_Real_remainder F_Real_remainder; +struct F_Real_remainder +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + REAL p; +}; +void Real_rint(void*); +typedef struct F_Real_rint F_Real_rint; +struct F_Real_rint +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_scalbn(void*); +typedef struct F_Real_scalbn F_Real_scalbn; +struct F_Real_scalbn +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; + WORD n; +}; +void Real_sin(void*); +typedef struct F_Real_sin F_Real_sin; +struct F_Real_sin +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_sinh(void*); +typedef struct F_Real_sinh F_Real_sinh; +struct F_Real_sinh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_sqrt(void*); +typedef struct F_Real_sqrt F_Real_sqrt; +struct F_Real_sqrt +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_tan(void*); +typedef struct F_Real_tan F_Real_tan; +struct F_Real_tan +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_tanh(void*); +typedef struct F_Real_tanh F_Real_tanh; +struct F_Real_tanh +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_y0(void*); +typedef struct F_Real_y0 F_Real_y0; +struct F_Real_y0 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_y1(void*); +typedef struct F_Real_y1 F_Real_y1; +struct F_Real_y1 +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + REAL x; +}; +void Real_yn(void*); +typedef struct F_Real_yn F_Real_yn; +struct F_Real_yn +{ + WORD regs[NREG-1]; + REAL* ret; + uchar temps[12]; + WORD n; + WORD _pad36; + REAL x; +}; +#define Real_Infinity Infinity +#define Real_NaN NaN +#define Real_MachEps 2.220446049250313e-16 +#define Real_Pi 3.141592653589793 +#define Real_Degree .017453292519943295 +#define Real_INVAL 1 +#define Real_ZDIV 2 +#define Real_OVFL 4 +#define Real_UNFL 8 +#define Real_INEX 16 +#define Real_RND_NR 0 +#define Real_RND_NINF 256 +#define Real_RND_PINF 512 +#define Real_RND_Z 768 +#define Real_RND_MASK 768 +void Keyring_certtostr(void*); +typedef struct F_Keyring_certtostr F_Keyring_certtostr; +struct F_Keyring_certtostr +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + Keyring_Certificate* c; +}; +void Keyring_pktostr(void*); +typedef struct F_Keyring_pktostr F_Keyring_pktostr; +struct F_Keyring_pktostr +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + Keyring_PK* pk; +}; +void Keyring_sktostr(void*); +typedef struct F_Keyring_sktostr F_Keyring_sktostr; +struct F_Keyring_sktostr +{ + WORD regs[NREG-1]; + String** ret; + uchar temps[12]; + Keyring_SK* sk; +}; +void Keyring_strtocert(void*); +typedef struct F_Keyring_strtocert F_Keyring_strtocert; +struct F_Keyring_strtocert +{ + WORD regs[NREG-1]; + Keyring_Certificate** ret; + uchar temps[12]; + String* s; +}; +void Keyring_strtopk(void*); +typedef struct F_Keyring_strtopk F_Keyring_strtopk; +struct F_Keyring_strtopk +{ + WORD regs[NREG-1]; + Keyring_PK** ret; + uchar temps[12]; + String* s; +}; +void Keyring_strtosk(void*); +typedef struct F_Keyring_strtosk F_Keyring_strtosk; +struct F_Keyring_strtosk +{ + WORD regs[NREG-1]; + Keyring_SK** ret; + uchar temps[12]; + String* s; +}; +void Keyring_sign(void*); +typedef struct F_Keyring_sign F_Keyring_sign; +struct F_Keyring_sign +{ + WORD regs[NREG-1]; + Keyring_Certificate** ret; + uchar temps[12]; + Keyring_SK* sk; + WORD exp; + Keyring_DigestState* state; + WORD alg; +}; +void Keyring_verify(void*); +typedef struct F_Keyring_verify F_Keyring_verify; +struct F_Keyring_verify +{ + WORD regs[NREG-1]; + WORD* ret; + uchar temps[12]; + Keyring_PK* pk; + Keyring_Certificate* cert; + Keyring_DigestState* state; + WORD alg; +}; +void Keyring_genSK(void*); +typedef struct F_Keyring_genSK F_Keyring_genSK; +struct F_Keyring_genSK +{ + WORD regs[NREG-1]; + Keyring_SK** ret; + uchar temps[12]; + String* algname; + String* owner; + WORD length; +}; +void Keyring_genSKfromPK(void*); +typedef struct F_Keyring_genSKfromPK F_Keyring_genSKfromPK; +struct F_Keyring_genSKfromPK +{ + WORD regs[NREG-1]; + Keyring_SK** ret; + uchar temps[12]; + Keyring_PK* pk; + String* owner; +}; +void Keyring_sktopk(void*); +typedef struct F_Keyring_sktopk F_Keyring_sktopk; +struct F_Keyring_sktopk +{ + WORD regs[NREG-1]; + Keyring_PK** ret; + uchar temps[12]; + Keyring_SK* sk; +}; +void Keyring_sha(void*); +typedef struct F_Keyring_sha F_Keyring_sha; +struct F_Keyring_sha +{ + WORD regs[NREG-1]; + Keyring_DigestState** ret; + uchar temps[12]; + Array* buf; + WORD n; + Array* digest; + Keyring_DigestState* state; +}; +void Keyring_md5(void*); +typedef struct F_Keyring_md5 F_Keyring_md5; +struct F_Keyring_md5 +{ + WORD regs[NREG-1]; + Keyring_DigestState** ret; + uchar temps[12]; + Array* buf; + WORD n; + Array* digest; + Keyring_DigestState* state; +}; +#define Keyring_DEScbc 0 +#define Keyring_DESecb 1 +#define Keyring_SHA 2 +#define Keyring_MD5 3 +#define Keyring_SHAdlen 20 +#define Keyring_MD5dlen 16 diff --git a/limbo/sbl.c b/limbo/sbl.c new file mode 100644 index 00000000..fff80392 --- /dev/null +++ b/limbo/sbl.c @@ -0,0 +1,351 @@ +#include "limbo.h" + +static char sbltname[Tend] = +{ + /* Tnone */ 'n', + /* Tadt */ 'a', + /* Tadtpick */ 'a', + /* Tarray */ 'A', + /* Tbig */ 'B', + /* Tbyte */ 'b', + /* Tchan */ 'C', + /* Treal */ 'f', + /* Tfn */ 'F', + /* Tint */ 'i', + /* Tlist */ 'L', + /* Tmodule */ 'm', + /* Tref */ 'R', + /* Tstring */ 's', + /* Ttuple */ 't', + /* Texception */ 't', + /* Tfix */ 'i', + /* Tpoly */ 'P', + + /* Tainit */ '?', + /* Talt */ '?', + /* Tany */ 'N', + /* Tarrow */ '?', + /* Tcase */ '?', + /* Tcasel */ '?', + /* Tcasec */ '?', + /* Tdot */ '?', + /* Terror */ '?', + /* Tgoto */ '?', + /* Tid */ '?', + /* Tiface */ '?', + /* Texcept */ '?', + /* Tinst */ '?', +}; +int sbltadtpick = 'p'; + +static Sym *sfiles; +static Sym *ftail; +static int nsfiles; +static int blockid; +static int lastf; +static int lastline; + +static void sbltype(Type*, int); +static void sbldecl(Decl*, int); +static void sblftype(Type*); +static void sblfdecl(Decl*, int); + +void +sblmod(Decl *m) +{ + Bprint(bsym, "limbo .sbl 2.1\n"); + Bprint(bsym, "%s\n", m->sym->name); + + blockid = 0; + nsfiles = 0; + sfiles = ftail = nil; + lastf = 0; + lastline = 0; +} + +static int +sblfile(char *name) +{ + Sym *s; + int i; + + i = 0; + for(s = sfiles; s != nil; s = s->next){ + if(strcmp(s->name, name) == 0) + return i; + i++; + } + s = allocmem(sizeof(Sym)); + s->name = name; + s->next = nil; + if(sfiles == nil) + sfiles = s; + else + ftail->next = s; + ftail = s; + nsfiles = i + 1; + return i; +} + +static char * +filename(char *s) +{ + char *t; + + t = strrchr(s, '/'); + if(t != nil) + s = t + 1; + t = strrchr(s, '\\'); + if(t != nil) + s = t+1; + t = strrchr(s, ' '); + if(t != nil) + s = t + 1; + return s; +} + +void +sblfiles(void) +{ + Sym *s; + int i; + + for(i = 0; i < nfiles; i++) + files[i]->sbl = sblfile(files[i]->name); + Bprint(bsym, "%d\n", nsfiles); + for(s = sfiles; s != nil; s = s->next) + Bprint(bsym, "%s\n", filename(s->name)); +} + +static char* +sblsrcconv(char *buf, char *end, Src *src) +{ + Fline fl; + File *startf, *stopf; + char *s; + int startl, stopl; + + s = buf; + + fl = fline(src->start.line); + startf = fl.file; + startl = fl.line; + fl = fline(src->stop.line); + stopf = fl.file; + stopl = fl.line; + if(lastf != startf->sbl) + s = seprint(s, end, "%d:", startf->sbl); + if(lastline != startl) + s = seprint(s, end, "%d.", startl); + s = seprint(s, end, "%d,", src->start.pos); + if(startf->sbl != stopf->sbl) + s = seprint(s, end, "%d:", stopf->sbl); + if(startl != stopl) + s = seprint(s, end, "%d.", stopl); + seprint(s, end, "%d ", src->stop.pos); + lastf = stopf->sbl; + lastline = stopl; + return buf; +} + +#define isnilsrc(s) ((s)->start.line == 0 && (s)->stop.line == 0 && (s)->start.pos == 0 && (s)->stop.pos == 0) +#define isnilstopsrc(s) ((s)->stop.line == 0 && (s)->stop.pos == 0) + +void +sblinst(Inst *inst, long ninst) +{ + Inst *in; + char buf[StrSize]; + int *sblblocks, i, b; + Src src; + + Bprint(bsym, "%ld\n", ninst); + sblblocks = allocmem(nblocks * sizeof *sblblocks); + for(i = 0; i < nblocks; i++) + sblblocks[i] = -1; + for(in = inst; in != nil; in = in->next){ + if(in->op == INOOP) + continue; + if(in->src.start.line < 0) + fatal("no file specified for %I", in); + b = sblblocks[in->block]; + if(b < 0) + sblblocks[in->block] = b = blockid++; + if(isnilsrc(&in->src)) + in->src = src; + else if(isnilstopsrc(&in->src)){ /* how does this happen ? */ + in->src.stop = in->src.start; + in->src.stop.pos++; + } + Bprint(bsym, "%s%d\n", sblsrcconv(buf, buf+sizeof(buf), &in->src), b); + src = in->src; + } + free(sblblocks); +} + +void +sblty(Decl **tys, int ntys) +{ + Decl *d; + int i; + + Bprint(bsym, "%d\n", ntys); + for(i = 0; i < ntys; i++){ + d = tys[i]; + d->ty->sbl = i; + } + for(i = 0; i < ntys; i++){ + d = tys[i]; + sbltype(d->ty, 1); + } +} + +void +sblfn(Decl **fns, int nfns) +{ + Decl *f; + int i; + + Bprint(bsym, "%d\n", nfns); + for(i = 0; i < nfns; i++){ + f = fns[i]; + if(ispoly(f)) + rmfnptrs(f); + if(f->dot != nil && f->dot->ty->kind == Tadt) + Bprint(bsym, "%ld:%s.%s\n", f->pc->pc, f->dot->sym->name, f->sym->name); + else + Bprint(bsym, "%ld:%s\n", f->pc->pc, f->sym->name); + sbldecl(f->ty->ids, Darg); + sbldecl(f->locals, Dlocal); + sbltype(f->ty->tof, 0); + } +} + +void +sblvar(Decl *vars) +{ + sbldecl(vars, Dglobal); +} + +static int +isvis(Decl *id) +{ + if(!tattr[id->ty->kind].vis + || id->sym == nil + || id->sym->name == nil /*????*/ + || id->sym->name[0] == '.') + return 0; + if(id->ty == tstring && id->init != nil && id->init->op == Oconst) + return 0; + if(id->src.start.line < 0 || id->src.stop.line < 0) + return 0; + return 1; +} + +static void +sbldecl(Decl *ids, int store) +{ + Decl *id; + char buf[StrSize]; + int n; + + n = 0; + for(id = ids; id != nil; id = id->next){ + if(id->store != store || !isvis(id)) + continue; + n++; + } + Bprint(bsym, "%d\n", n); + for(id = ids; id != nil; id = id->next){ + if(id->store != store || !isvis(id)) + continue; + Bprint(bsym, "%ld:%s:%s", id->offset, id->sym->name, sblsrcconv(buf, buf+sizeof(buf), &id->src)); + sbltype(id->ty, 0); + Bprint(bsym, "\n"); + } +} + +static void +sbltype(Type *t, int force) +{ + Type *lastt; + Decl *tg, *d; + char buf[StrSize]; + + if(t->kind == Tadtpick) + t = t->decl->dot->ty; + + d = t->decl; + if(!force && d != nil && d->ty->sbl >= 0){ + Bprint(bsym, "@%d\n", d->ty->sbl); + return; + } + + switch(t->kind){ + default: + fatal("bad type %T in sbltype", t); + break; + case Tnone: + case Tany: + case Tint: + case Tbig: + case Tbyte: + case Treal: + case Tstring: + case Tfix: + case Tpoly: + Bprint(bsym, "%c", sbltname[t->kind]); + break; + case Tfn: + Bprint(bsym, "%c", sbltname[t->kind]); + sbldecl(t->ids, Darg); + sbltype(t->tof, 0); + break; + case Tarray: + case Tlist: + case Tchan: + case Tref: + Bprint(bsym, "%c", sbltname[t->kind]); + if(t->kind == Tref && t->tof->kind == Tfn){ + tattr[Tany].vis = 1; + sbltype(tfnptr, 0); + tattr[Tany].vis = 0; + } + else + sbltype(t->tof, 0); + break; + case Ttuple: + case Texception: + Bprint(bsym, "%c%d.", sbltname[t->kind], t->size); + sbldecl(t->ids, Dfield); + break; + case Tadt: + if(t->tags != nil) + Bputc(bsym, sbltadtpick); + else + Bputc(bsym, sbltname[t->kind]); + if(d->dot != nil && !isimpmod(d->dot->sym)) + Bprint(bsym, "%s->", d->dot->sym->name); + Bprint(bsym, "%s %s%d\n", d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src), d->ty->size); + sbldecl(t->ids, Dfield); + if(t->tags != nil){ + Bprint(bsym, "%d\n", t->decl->tag); + lastt = nil; + for(tg = t->tags; tg != nil; tg = tg->next){ + Bprint(bsym, "%s:%s", tg->sym->name, sblsrcconv(buf, buf+sizeof(buf), &tg->src)); + if(lastt == tg->ty){ + Bputc(bsym, '\n'); + }else{ + Bprint(bsym, "%d\n", tg->ty->size); + sbldecl(tg->ty->ids, Dfield); + } + lastt = tg->ty; + } + } + break; + case Tmodule: + Bprint(bsym, "%c%s\n%s", sbltname[t->kind], d->sym->name, sblsrcconv(buf, buf+sizeof(buf), &d->src)); + sbldecl(t->ids, Dglobal); + break; + } +} diff --git a/limbo/stubs.c b/limbo/stubs.c new file mode 100644 index 00000000..3c174e4a --- /dev/null +++ b/limbo/stubs.c @@ -0,0 +1,590 @@ +#include "limbo.h" + +static long stubalign(long offset, int a, char** b, char *e); +static void pickadtstub(Type *t); + +void +emit(Decl *globals) +{ + Decl *m, *d, *id; + + for(m = globals; m != nil; m = m->next){ + if(m->store != Dtype || m->ty->kind != Tmodule) + continue; + m->ty = usetype(m->ty); + for(d = m->ty->ids; d != nil; d = d->next){ + d->ty = usetype(d->ty); + if(d->store == Dglobal || d->store == Dfn) + modrefable(d->ty); + if(d->store == Dtype && d->ty->kind == Tadt){ + for(id = d->ty->ids; id != nil; id = id->next){ + id->ty = usetype(id->ty); + modrefable(d->ty); + } + } + } + } + if(emitstub){ + print("#pragma hjdicks x4\n"); + print("#pragma pack x4\n"); + adtstub(globals); + modstub(globals); + print("#pragma pack off\n"); + print("#pragma hjdicks off\n"); + } + if(emittab != nil) + modtab(globals); + if(emitcode) + modcode(globals); +} + +static char* +lowercase(char *f) +{ + char *s = f; + + for( ; *s != 0; s++) + if(*s >= 'A' && *s <= 'Z') + *s += 'a' - 'A'; + return f; +} + +void +modcode(Decl *globals) +{ + Decl *d, *id; + char buf[32]; + + if(emitdyn){ + strcpy(buf, emitcode); + lowercase(buf); + print("#include \"%s.h\"\n", buf); + } + else{ + print("#include <lib9.h>\n"); + print("#include <isa.h>\n"); + print("#include <interp.h>\n"); + print("#include \"%smod.h\"\n", emitcode); + } + print("\n"); + + for(d = globals; d != nil; d = d->next) + if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emitcode) == 0) + break; + + if(d == nil) + return; + + /* + * stub types + */ + for(id = d->ty->ids; id != nil; id = id->next){ + if(id->store == Dtype && id->ty->kind == Tadt){ + id->ty = usetype(id->ty); + print("Type*\tT_%s;\n", id->sym->name); + } + } + + /* + * type maps + */ + if(emitdyn){ + for(id = d->ty->ids; id != nil; id = id->next) + if(id->store == Dtype && id->ty->kind == Tadt) + print("uchar %s_map[] = %s_%s_map;\n", + id->sym->name, emitcode, id->sym->name); + } + + /* + * heap allocation and garbage collection for a type + */ + if(emitdyn){ + for(id = d->ty->ids; id != nil; id = id->next) + if(id->store == Dtype && id->ty->kind == Tadt){ + print("\n%s_%s*\n%salloc%s(void)\n{\n\tHeap *h;\n\n\th = heap(T_%s);\n\treturn H2D(%s_%s*, h);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, emitcode, id->sym->name); + print("\nvoid\n%sfree%s(Heap *h, int swept)\n{\n\t%s_%s *d;\n\n\td = H2D(%s_%s*, h);\n\tfreeheap(h, swept);\n}\n", emitcode, id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name); + } + } + + /* + * initialization function + */ + if(emitdyn) + print("\nvoid\n%sinit(void)\n{\n", emitcode); + else{ + print("\nvoid\n%smodinit(void)\n{\n", emitcode); + print("\tbuiltinmod(\"$%s\", %smodtab);\n", emitcode, emitcode); + } + for(id = d->ty->ids; id != nil; id = id->next){ + if(id->store == Dtype && id->ty->kind == Tadt){ + if(emitdyn) + print("\tT_%s = dtype(%sfree%s, %s_%s_size, %s_map, sizeof(%s_map));\n", + id->sym->name, emitcode, id->sym->name, emitcode, id->sym->name, id->sym->name, id->sym->name); + else + print("\tT_%s = dtype(freeheap, sizeof(%s), %smap, sizeof(%smap));\n", + id->sym->name, id->sym->name, id->sym->name, id->sym->name); + } + } + print("}\n"); + + /* + * end function + */ + if(emitdyn){ + print("\nvoid\n%send(void)\n{\n", emitcode); + for(id = d->ty->ids; id != nil; id = id->next) + if(id->store == Dtype && id->ty->kind == Tadt) + print("\tfreetype(T_%s);\n", id->sym->name); + print("}\n"); + } + + /* + * stub functions + */ + for(id = d->ty->tof->ids; id != nil; id = id->next){ + print("\nvoid\n%s_%s(void *fp)\n{\n\tF_%s_%s *f = fp;\n", + id->dot->sym->name, id->sym->name, + id->dot->sym->name, id->sym->name); + if(id->ty->tof != tnone && tattr[id->ty->tof->kind].isptr) + print("\n\tdestroy(*f->ret);\n\t*f->ret = H;\n"); + print("}\n"); + } + + if(emitdyn) + print("\n#include \"%smod.h\"\n", buf); +} + +void +modtab(Decl *globals) +{ + int n; + Desc *md; + Decl *d, *id; + + print("typedef struct{char *name; long sig; void (*fn)(void*); int size; int np; uchar map[16];} Runtab;\n"); + for(d = globals; d != nil; d = d->next){ + if(d->store == Dtype && d->ty->kind == Tmodule && strcmp(d->sym->name, emittab) == 0){ + n = 0; + print("Runtab %smodtab[]={\n", d->sym->name); + for(id = d->ty->tof->ids; id != nil; id = id->next){ + n++; + print("\t\""); + if(id->dot != d) + print("%s.", id->dot->sym->name); + print("%s\",0x%lux,%s_%s,", id->sym->name, sign(id), + id->dot->sym->name, id->sym->name); + if(id->ty->varargs) + print("0,0,{0},"); + else{ + md = mkdesc(idoffsets(id->ty->ids, MaxTemp, MaxAlign), id->ty->ids); + print("%ld,%ld,%M,", md->size, md->nmap, md); + } + print("\n"); + } + print("\t0\n};\n"); + print("#define %smodlen %d\n", d->sym->name, n); + } + } +} + +/* + * produce activation records for all the functions in modules + */ +void +modstub(Decl *globals) +{ + Type *t; + Decl *d, *id, *m; + char buf[StrSize*2], *p; + long offset; + int arg; + + for(d = globals; d != nil; d = d->next){ + if(d->store != Dtype || d->ty->kind != Tmodule) + continue; + arg = 0; + for(id = d->ty->tof->ids; id != nil; id = id->next){ + if(emitdyn && id->dot->dot != nil) + seprint(buf, buf+sizeof(buf), "%s_%s_%s", id->dot->dot->sym->name, id->dot->sym->name, id->sym->name); + else + seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name); + print("void %s(void*);\ntypedef struct F_%s F_%s;\nstruct F_%s\n{\n", + buf, buf, buf, buf); + print(" WORD regs[NREG-1];\n"); + if(id->ty->tof != tnone) + print(" %R* ret;\n", id->ty->tof); + else + print(" WORD noret;\n"); + print(" uchar temps[%d];\n", MaxTemp-NREG*IBY2WD); + offset = MaxTemp; + for(m = id->ty->ids; m != nil; m = m->next){ + if(m->sym != nil) + p = m->sym->name; + else{ + seprint(buf, buf+sizeof(buf), "arg%d", arg); + p = buf; + } + + /* + * explicit pads for structure alignment + */ + t = m->ty; + offset = stubalign(offset, t->align, nil, nil); + if(offset != m->offset) + yyerror("module stub must not contain data objects"); + // fatal("modstub bad offset"); + print(" %R %s;\n", t, p); + arg++; + offset += t->size; + } + if(id->ty->varargs) + print(" WORD vargs;\n"); + print("};\n"); + } + for(id = d->ty->ids; id != nil; id = id->next) + if(id->store == Dconst) + constub(id); + } +} + +static void +chanstub(char *in, Decl *id) +{ + Desc *desc; + + print("typedef %R %s_%s;\n", id->ty->tof, in, id->sym->name); + desc = mktdesc(id->ty->tof); + print("#define %s_%s_size %ld\n", in, id->sym->name, desc->size); + print("#define %s_%s_map %M\n", in, id->sym->name, desc); +} + +/* + * produce c structs for all adts + */ +void +adtstub(Decl *globals) +{ + Type *t, *tt; + Desc *desc; + Decl *m, *d, *id; + char buf[2*StrSize]; + long offset; + + for(m = globals; m != nil; m = m->next){ + if(m->store != Dtype || m->ty->kind != Tmodule) + continue; + for(d = m->ty->ids; d != nil; d = d->next){ + if(d->store != Dtype) + continue; + t = usetype(d->ty); + d->ty = t; + dotprint(buf, buf+sizeof(buf), d->ty->decl, '_'); + switch(d->ty->kind){ + case Tadt: + print("typedef struct %s %s;\n", buf, buf); + break; + case Tint: + case Tbyte: + case Treal: + case Tbig: + case Tfix: + print("typedef %T %s;\n", t, buf); + break; + } + } + } + for(m = globals; m != nil; m = m->next){ + if(m->store != Dtype || m->ty->kind != Tmodule) + continue; + for(d = m->ty->ids; d != nil; d = d->next){ + if(d->store != Dtype) + continue; + t = d->ty; + if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){ + if(t->tags != nil){ + pickadtstub(t); + continue; + } + dotprint(buf, buf+sizeof(buf), t->decl, '_'); + print("struct %s\n{\n", buf); + offset = 0; + for(id = t->ids; id != nil; id = id->next){ + if(id->store == Dfield){ + tt = id->ty; + offset = stubalign(offset, tt->align, nil, nil); + if(offset != id->offset) + fatal("adtstub bad offset"); + print(" %R %s;\n", tt, id->sym->name); + offset += tt->size; + } + } + if(t->ids == nil){ + print(" char dummy[1];\n"); + offset = 1; + } + offset = stubalign(offset, t->align, nil ,nil); + offset = stubalign(offset, IBY2WD, nil , nil); + if(offset != t->size && t->ids != nil) + fatal("adtstub: bad size"); + print("};\n"); + + for(id = t->ids; id != nil; id = id->next) + if(id->store == Dconst) + constub(id); + + for(id = t->ids; id != nil; id = id->next) + if(id->ty->kind == Tchan) + chanstub(buf, id); + + desc = mktdesc(t); + if(offset != desc->size && t->ids != nil) + fatal("adtstub: bad desc size"); + print("#define %s_size %ld\n", buf, offset); + print("#define %s_map %M\n", buf, desc); +if(0) + print("struct %s_check {int s[2*(sizeof(%s)==%s_size)-1];};\n", buf, buf, buf); + }else if(t->kind == Tchan) + chanstub(m->sym->name, d); + } + } +} + +/* + * emit an expicit pad field for aligning emitted c structs + * according to limbo's definition + */ +static long +stubalign(long offset, int a, char **buf, char *end) +{ + long x; + + x = offset & (a-1); + if(x == 0) + return offset; + x = a - x; + if(buf == nil) + print("\tuchar\t_pad%ld[%ld];\n", offset, x); + else + *buf = seprint(*buf, end, "uchar\t_pad%ld[%ld]; ", offset, x); + offset += x; + if((offset & (a-1)) || x >= a) + fatal("compiler stub misalign"); + return offset; +} + +void +constub(Decl *id) +{ + char buf[StrSize*2]; + + seprint(buf, buf+sizeof(buf), "%s_%s", id->dot->sym->name, id->sym->name); + switch(id->ty->kind){ + case Tbyte: + print("#define %s %d\n", buf, (int)id->init->val & 0xff); + break; + case Tint: + case Tfix: + print("#define %s %ld\n", buf, (long)id->init->val); + break; + case Tbig: + print("#define %s %ld\n", buf, (long)id->init->val); + break; + case Treal: + print("#define %s %g\n", buf, id->init->rval); + break; + case Tstring: + print("#define %s \"%s\"\n", buf, id->init->decl->sym->name); + break; + } +} + +int +mapconv(Fmt *f) +{ + Desc *d; + char *s, *e, buf[1024]; + int i; + + d = va_arg(f->args, Desc*); + e = buf+sizeof(buf); + s = buf; + s = secpy(s, e, "{"); + for(i = 0; i < d->nmap; i++) + s = seprint(s, e, "0x%x,", d->map[i]); + if(i == 0) + s = seprint(s, e, "0"); + seprint(s, e, "}"); + return fmtstrcpy(f, buf); +} + +char* +dotprint(char *buf, char *end, Decl *d, int dot) +{ + if(d->dot != nil){ + buf = dotprint(buf, end, d->dot, dot); + if(buf < end) + *buf++ = dot; + } + if(d->sym == nil) + return buf; + return seprint(buf, end, "%s", d->sym->name); +} + +char *ckindname[Tend] = +{ + /* Tnone */ "void", + /* Tadt */ "struct", + /* Tadtpick */ "?adtpick?", + /* Tarray */ "Array*", + /* Tbig */ "LONG", + /* Tbyte */ "BYTE", + /* Tchan */ "Channel*", + /* Treal */ "REAL", + /* Tfn */ "?fn?", + /* Tint */ "WORD", + /* Tlist */ "List*", + /* Tmodule */ "Modlink*", + /* Tref */ "?ref?", + /* Tstring */ "String*", + /* Ttuple */ "?tuple?", + /* Texception */ "?exception", + /* Tfix */ "WORD", + /* Tpoly */ "void*", + + /* Tainit */ "?ainit?", + /* Talt */ "?alt?", + /* Tany */ "void*", + /* Tarrow */ "?arrow?", + /* Tcase */ "?case?", + /* Tcasel */ "?casel", + /* Tcasec */ "?casec?", + /* Tdot */ "?dot?", + /* Terror */ "?error?", + /* Tgoto */ "?goto?", + /* Tid */ "?id?", + /* Tiface */ "?iface?", + /* Texcept */ "?except?", + /* Tinst */ "?inst?", +}; + +char* +ctprint(char *buf, char *end, Type *t) +{ + Decl *id; + Type *tt; + long offset; + + if(t == nil) + return secpy(buf, end, "void"); + switch(t->kind){ + case Tref: + return seprint(buf, end, "%R*", t->tof); + case Tarray: + case Tlist: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tchan: + case Tmodule: + case Tfix: + case Tpoly: + return seprint(buf, end, "%s", ckindname[t->kind]); + case Tadt: + case Ttuple: + if(t->decl->sym != anontupsym) + return dotprint(buf, end, t->decl, '_'); + offset = 0; + buf = secpy(buf, end, "struct{ "); + for(id = t->ids; id != nil; id = id->next){ + tt = id->ty; + offset = stubalign(offset, tt->align, &buf, end); + if(offset != id->offset) + fatal("ctypeconv tuple bad offset"); + buf = seprint(buf, end, "%R %s; ", tt, id->sym->name); + offset += tt->size; + } + offset = stubalign(offset, t->align, &buf, end); + if(offset != t->size) + fatal("ctypeconv tuple bad t=%T size=%ld offset=%ld", t, t->size, offset); + return secpy(buf, end, "}"); + default: + if(t->kind >= Tend) + yyerror("no C equivalent for type %d", t->kind); + else + yyerror("no C equivalent for type %s", kindname[t->kind]); + break; + } + return buf; +} + +static void +pickadtstub(Type *t) +{ + Type *tt; + Desc *desc; + Decl *id, *tg; + char buf[2*StrSize]; + int ok; + long offset, tgoffset; + + dotprint(buf, buf+sizeof(buf), t->decl, '_'); + offset = 0; + for(tg = t->tags; tg != nil; tg = tg->next) + print("#define %s_%s %ld\n", buf, tg->sym->name, offset++); + print("struct %s\n{\n", buf); + print(" int pick;\n"); + offset = IBY2WD; + for(id = t->ids; id != nil; id = id->next){ + if(id->store == Dfield){ + tt = id->ty; + offset = stubalign(offset, tt->align, nil, nil); + if(offset != id->offset) + fatal("pickadtstub bad offset"); + print(" %R %s;\n", tt, id->sym->name); + offset += tt->size; + } + } + print(" union{\n"); + for(tg = t->tags; tg != nil; tg = tg->next){ + tgoffset = offset; + print(" struct{\n"); + for(id = tg->ty->ids; id != nil; id = id->next){ + if(id->store == Dfield){ + tt = id->ty; + tgoffset = stubalign(tgoffset, tt->align, nil, nil); + if(tgoffset != id->offset) + fatal("pickadtstub bad offset"); + print(" %R %s;\n", tt, id->sym->name); + tgoffset += tt->size; + } + } + if(tg->ty->ids == nil) + print(" char dummy[1];\n"); + print(" } %s;\n", tg->sym->name); + } + print(" } u;\n"); + print("};\n"); + + for(id = t->ids; id != nil; id = id->next) + if(id->store == Dconst) + constub(id); + + for(id = t->ids; id != nil; id = id->next) + if(id->ty->kind == Tchan) + chanstub(buf, id); + + for(tg = t->tags; tg != nil; tg = tg->next){ + ok = tg->ty->tof->ok; + tg->ty->tof->ok = OKverify; + sizetype(tg->ty->tof); + tg->ty->tof->ok = OKmask; + desc = mktdesc(tg->ty->tof); + tg->ty->tof->ok = ok; + print("#define %s_%s_size %ld\n", buf, tg->sym->name, tg->ty->size); + print("#define %s_%s_map %M\n", buf, tg->sym->name, desc); + } +} diff --git a/limbo/typecheck.c b/limbo/typecheck.c new file mode 100644 index 00000000..827aa1e1 --- /dev/null +++ b/limbo/typecheck.c @@ -0,0 +1,3635 @@ +#include "limbo.h" +#include "y.tab.h" + +Node **labstack; +int labdep; +static Node* inexcept; +static Decl* fndec; + +void checkraises(Node *n); + +static void +increfs(Decl *id) +{ + for( ; id != nil; id = id->link) + id->refs++; +} + +static int +fninline(Decl *d) +{ + Node *n, *left, *right; + + left = right = nil; + n = d->init; + if(dontinline || d->caninline < 0 || d->locals != nil || ispoly(d) || n->ty->tof->kind == Tnone || nodes(n) >= 100) + return 0; + n = n->right; + if(n->op == Oseq && n->right == nil) + n = n->left; + /* + * inline + * (a) return e; + * (b) if(c) return e1; else return e2; + * (c) if(c) return e1; return e2; + */ + switch(n->op){ + case Oret: + break; + case Oif: + right = n->right; + if(right->right == nil || right->left->op != Oret || right->right->op != Oret || !tequal(right->left->left->ty, right->right->left->ty)) + return 0; + break; + case Oseq: + left = n->left; + right = n->right; + if(left->op != Oif || left->right->right != nil || left->right->left->op != Oret || right->op != Oseq || right->right != nil || right->left->op != Oret || !tequal(left->right->left->left->ty, right->left->left->ty)) + return 0; + break; + default: + return 0; + } + if(occurs(d, n) || hasasgns(n)) + return 0; + if(n->op == Oseq){ + left->right->right = right->left; + n = left; + right = n->right; + d->init->right->right = nil; + } + if(n->op == Oif){ + n->ty = right->ty = right->left->left->ty; + right->left = right->left->left; + right->right = right->right->left; + d->init->right->left = mkunary(Oret, n); + } + return 1; +} + +static int +isfnrefty(Type *t) +{ + return t->kind == Tref && t->tof->kind == Tfn; +} + +static int +isfnref(Decl *d) +{ + switch(d->store){ + case Dglobal: + case Darg: + case Dlocal: + case Dfield: + case Dimport: + return isfnrefty(d->ty); + } + return 0; +} + +int +argncompat(Node *n, Decl *f, Node *a) +{ + for(; a != nil; a = a->right){ + if(f == nil){ + nerror(n, "%V: too many function arguments", n->left); + return 0; + } + f = f->next; + } + if(f != nil){ + nerror(n, "%V: too few function arguments", n->left); + return 0; + } + return 1; +} + +static void +rewind(Node *n) +{ + Node *r, *nn; + + r = n; + nn = n->left; + for(n = n->right; n != nil; n = n->right){ + if(n->right == nil){ + r->left = nn; + r->right = n->left; + } + else + nn = mkbin(Oindex, nn, n->left); + } +} + +static void +ckmod(Node *n, Decl *id) +{ + Type *t; + Decl *d, *idc; + Node *mod; + + if(id == nil) + fatal("can't find function: %n", n); + idc = nil; + mod = nil; + if(n->op == Oname){ + idc = id; + mod = id->eimport; + } + else if(n->op == Omdot) + mod = n->left; + else if(n->op == Odot){ + idc = id->dot; + t = n->left->ty; + if(t->kind == Tref) + t = t->tof; + if(t->kind == Tadtpick) + t = t->decl->dot->ty; + d = t->decl; + while(d != nil && d->link != nil) + d = d->link; + if(d != nil && d->timport != nil) + mod = d->timport->eimport; + n->right->left = mod; + } + if(mod != nil && mod->ty->kind != Tmodule){ + nerror(n, "cannot use %V as a function reference", n); + return; + } + if(mod != nil){ + if(valistype(mod)){ + nerror(n, "cannot use %V as a function reference because %V is a module interface", n, mod); + return; + } + }else if(idc != nil && idc->dot != nil && !isimpmod(idc->dot->sym)){ + nerror(n, "cannot use %V without importing %s from a variable", n, idc->sym->name); + return; + } + if(mod != nil) + modrefable(n->ty); +} + +static void +addref(Node *n) +{ + Node *nn; + + nn = mkn(0, nil, nil); + *nn = *n; + n->op = Oref; + n->left = nn; + n->right = nil; + n->decl = nil; + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, nn->ty, nil)); +} + +static void +fnref(Node *n, Decl *id) +{ + id->caninline = -1; + ckmod(n, id); + addref(n); + while(id->link != nil) + id = id->link; + if(ispoly(id) && encpolys(id) != nil) + nerror(n, "cannot have a polymorphic adt function reference %s", id->sym->name); +} + +Decl* +typecheck(int checkimp) +{ + Decl *entry, *m, *d; + Sym *s; + int i; + + if(errors) + return nil; + + /* + * generate the set of all functions + * compile one function at a time + */ + gdecl(tree); + gbind(tree); + fns = allocmem(nfns * sizeof(Decl)); + i = gcheck(tree, fns, 0); + if(i != nfns) + fatal("wrong number of functions found in gcheck"); + + maxlabdep = 0; + for(i = 0; i < nfns; i++){ + d = fns[i]; + if(d != nil) + fndec = d; + if(d != nil) + fncheck(d); + fndec = nil; + } + + if(errors) + return nil; + + entry = nil; + if(checkimp){ + Decl *im; + Dlist *dm; + + if(impmods == nil){ + yyerror("no implementation module"); + return nil; + } + for(im = impmods; im != nil; im = im->next){ + for(dm = impdecls; dm != nil; dm = dm->next) + if(dm->d->sym == im->sym) + break; + if(dm == nil || dm->d->ty == nil){ + yyerror("no definition for implementation module %s", im->sym->name); + return nil; + } + } + + /* + * can't check the module spec until all types and imports are determined, + * which happens in scheck + */ + for(dm = impdecls; dm != nil; dm = dm->next){ + im = dm->d; + im->refs++; + im->ty = usetype(im->ty); + if(im->store != Dtype || im->ty->kind != Tmodule){ + error(im->src.start, "cannot implement %K", im); + return nil; + } + } + + /* now check any multiple implementations */ + impdecl = modimp(impdecls, impmods); + + s = enter("init", 0); + entry = nil; + for(dm = impdecls; dm != nil; dm = dm->next){ + im = dm->d; + for(m = im->ty->ids; m != nil; m = m->next){ + m->ty = usetype(m->ty); + m->refs++; + + if(m->sym == s && m->ty->kind == Tfn && entry == nil) + entry = m; + + if(m->store == Dglobal || m->store == Dfn) + modrefable(m->ty); + + if(m->store == Dtype && m->ty->kind == Tadt){ + for(d = m->ty->ids; d != nil; d = d->next){ + d->ty = usetype(d->ty); + modrefable(d->ty); + d->refs++; + } + } + } + checkrefs(im->ty->ids); + } + } + + if(errors) + return nil; + gsort(tree); + tree = nil; + return entry; +} + +/* + * introduce all global declarations + * also adds all fields to adts and modules + * note the complications due to nested Odas expressions + */ +void +gdecl(Node *n) +{ + for(;;){ + if(n == nil) + return; + if(n->op != Oseq) + break; + gdecl(n->left); + n = n->right; + } + switch(n->op){ + case Oimport: + importdecled(n); + gdasdecl(n->right); + break; + case Oadtdecl: + adtdecled(n); + break; + case Ocondecl: + condecled(n); + gdasdecl(n->right); + break; + case Oexdecl: + exdecled(n); + break; + case Omoddecl: + moddecled(n); + break; + case Otypedecl: + typedecled(n); + break; + case Ovardecl: + vardecled(n); + break; + case Ovardecli: + vardecled(n->left); + gdasdecl(n->right); + break; + case Ofunc: + fndecled(n); + break; + case Oas: + case Odas: + case Onothing: + gdasdecl(n); + break; + default: + fatal("can't deal with %O in gdecl", n->op); + } +} + +/* + * bind all global type ids, + * including those nested inside modules + * this needs to be done, since we may use such + * a type later in a nested scope, so if we bound + * the type ids then, the type could get bound + * to a nested declaration + */ +void +gbind(Node *n) +{ + Decl *d, *ids; + + for(;;){ + if(n == nil) + return; + if(n->op != Oseq) + break; + gbind(n->left); + n = n->right; + } + switch(n->op){ + case Oas: + case Ocondecl: + case Odas: + case Oexdecl: + case Ofunc: + case Oimport: + case Onothing: + case Ovardecl: + case Ovardecli: + break; + case Ofielddecl: + bindtypes(n->decl->ty); + break; + case Otypedecl: + bindtypes(n->decl->ty); + if(n->left != nil) + gbind(n->left); + break; + case Opickdecl: + gbind(n->left); + d = n->right->left->decl; + bindtypes(d->ty); + repushids(d->ty->ids); + gbind(n->right->right); + /* get new ids for undefined types; propagate outwards */ + ids = popids(d->ty->ids); + if(ids != nil) + installids(Dundef, ids); + break; + case Oadtdecl: + case Omoddecl: + bindtypes(n->ty); + if(n->ty->polys != nil) + repushids(n->ty->polys); + repushids(n->ty->ids); + gbind(n->left); + /* get new ids for undefined types; propagate outwards */ + ids = popids(n->ty->ids); + if(ids != nil) + installids(Dundef, ids); + if(n->ty->polys != nil) + popids(n->ty->polys); + break; + default: + fatal("can't deal with %O in gbind", n->op); + } +} + +/* + * check all of the > declarations + * bind all type ids referred to within types at the global level + * record decls for defined functions + */ +int +gcheck(Node *n, Decl **fns, int nfns) +{ + Ok rok; + Decl *d; + + for(;;){ + if(n == nil) + return nfns; + if(n->op != Oseq) + break; + nfns = gcheck(n->left, fns, nfns); + n = n->right; + } + + switch(n->op){ + case Ofielddecl: + if(n->decl->ty->u.eraises) + raisescheck(n->decl->ty); + break; + case Onothing: + case Opickdecl: + break; + case Otypedecl: + tcycle(n->ty); + break; + case Oadtdecl: + case Omoddecl: + if(n->ty->polys != nil) + repushids(n->ty->polys); + repushids(n->ty->ids); + if(gcheck(n->left, nil, 0)) + fatal("gcheck fn decls nested in modules or adts"); + if(popids(n->ty->ids) != nil) + fatal("gcheck installs new ids in a module or adt"); + if(n->ty->polys != nil) + popids(n->ty->polys); + break; + case Ovardecl: + varcheck(n, 1); + break; + case Ocondecl: + concheck(n, 1); + break; + case Oexdecl: + excheck(n, 1); + break; + case Oimport: + importcheck(n, 1); + break; + case Ovardecli: + varcheck(n->left, 1); + rok = echeck(n->right, 0, 1, nil); + if(rok.ok){ + if(rok.allok) + n->right = fold(n->right); + globalas(n->right->left, n->right->right, rok.allok); + } + break; + case Oas: + case Odas: + rok = echeck(n, 0, 1, nil); + if(rok.ok){ + if(rok.allok) + n = fold(n); + globalas(n->left, n->right, rok.allok); + } + break; + case Ofunc: + rok = echeck(n->left, 0, 1, n); + if(rok.ok && n->ty->u.eraises) + raisescheck(n->ty); + d = nil; + if(rok.ok) + d = fnchk(n); + fns[nfns++] = d; + break; + default: + fatal("can't deal with %O in gcheck", n->op); + } + return nfns; +} + +/* + * check for unused expression results + * make sure the any calculated expression has + * a destination + */ +Node* +checkused(Node *n) +{ + Type *t; + Node *nn; + + /* + * only nil; and nil = nil; should have type tany + */ + if(n->ty == tany){ + if(n->op == Oname) + return n; + if(n->op == Oas) + return checkused(n->right); + fatal("line %L checkused %n", n->src.start, n); + } + + if(n->op == Ocall && n->left->ty->kind == Tfn && n->left->ty->tof != tnone){ + n = mkunary(Oused, n); + n->ty = n->left->ty; + return n; + } + if(n->op == Ocall && isfnrefty(n->left->ty)){ + if(n->left->ty->tof->tof != tnone){ + n = mkunary(Oused, n); + n->ty = n->left->ty; + } + return n; + } + if(isused[n->op] && (n->op != Ocall || n->left->ty->kind == Tfn)) + return n; + t = n->ty; + if(t->kind == Tfn) + nerror(n, "function %V not called", n); + else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick) + nerror(n, "expressions cannot have type %T", t); + else if(n->op == Otuple){ + for(nn = n->left; nn != nil; nn = nn->right) + checkused(nn->left); + } + else + nwarn(n, "result of expression %V not used", n); + n = mkunary(Oused, n); + n->ty = n->left->ty; + return n; +} + +void +fncheck(Decl *d) +{ + Node *n; + Decl *adtp; + + n = d->init; + if(debug['t']) + print("typecheck tree: %n\n", n); + + fndecls = nil; + adtp = outerpolys(n->left); + if(n->left->op == Odot) + repushids(adtp); + if(d->ty->polys != nil) + repushids(d->ty->polys); + repushids(d->ty->ids); + + labdep = 0; + labstack = allocmem(maxlabdep * sizeof *labstack); + n->right = scheck(n->right, d->ty->tof, Sother); + if(labdep != 0) + fatal("unbalanced label stack in fncheck"); + free(labstack); + + d->locals = appdecls(popids(d->ty->ids), fndecls); + if(d->ty->polys != nil) + popids(d->ty->polys); + if(n->left->op == Odot) + popids(adtp); + fndecls = nil; + + checkrefs(d->ty->ids); + checkrefs(d->ty->polys); + checkrefs(d->locals); + + checkraises(n); + + d->caninline = fninline(d); +} + +Node* +scheck(Node *n, Type *ret, int kind) +{ + Node *left, *right, *last, *top; + Decl *d; + Sym *s; + Ok rok; + int i; + + top = n; + last = nil; + for(; n != nil; n = n->right){ + left = n->left; + right = n->right; + switch(n->op){ + case Ovardecl: + vardecled(n); + varcheck(n, 0); + if (nested() && tmustzero(n->decl->ty)) + decltozero(n); +/* + else if (inloop() && tmustzero(n->decl->ty)) + decltozero(n); +*/ + return top; + case Ovardecli: + vardecled(left); + varcheck(left, 0); + echeck(right, 0, 0, nil); + if (nested() && tmustzero(left->decl->ty)) + decltozero(left); + return top; + case Otypedecl: + typedecled(n); + bindtypes(n->ty); + tcycle(n->ty); + return top; + case Ocondecl: + condecled(n); + concheck(n, 0); + return top; + case Oexdecl: + exdecled(n); + excheck(n, 0); + return top; + case Oimport: + importdecled(n); + importcheck(n, 0); + return top; + case Ofunc: + fatal("scheck func"); + case Oscope: + pushscope(n, kind == Sother ? Sscope : kind); + if (left != nil) + fatal("Oscope has left field"); + echeck(left, 0, 0, nil); + n->right = scheck(right, ret, Sother); + d = popscope(); + fndecls = appdecls(fndecls, d); + return top; + case Olabel: + echeck(left, 0, 0, nil); + n->right = scheck(right, ret, Sother); + return top; + case Oseq: + n->left = scheck(left, ret, Sother); + /* next time will check n->right */ + break; + case Oif: + rok = echeck(left, 0, 0, nil); + if(rok.ok && left->op != Onothing && left->ty != tint) + nerror(n, "if conditional must be an int, not %Q", left); + right->left = scheck(right->left, ret, Sother); + /* next time will check n->right->right */ + n = right; + break; + case Ofor: + rok = echeck(left, 0, 0, nil); + if(rok.ok && left->op != Onothing && left->ty != tint) + nerror(n, "for conditional must be an int, not %Q", left); + /* + * do the continue clause before the body + * this reflects the ordering of declarations + */ + pushlabel(n); + right->right = scheck(right->right, ret, Sother); + right->left = scheck(right->left, ret, Sloop); + labdep--; + if(n->decl != nil && !n->decl->refs) + nwarn(n, "label %s never referenced", n->decl->sym->name); + return top; + case Odo: + rok = echeck(left, 0, 0, nil); + if(rok.ok && left->op != Onothing && left->ty != tint) + nerror(n, "do conditional must be an int, not %Q", left); + pushlabel(n); + n->right = scheck(n->right, ret, Sloop); + labdep--; + if(n->decl != nil && !n->decl->refs) + nwarn(n, "label %s never referenced", n->decl->sym->name); + return top; + case Oalt: + case Ocase: + case Opick: + case Oexcept: + pushlabel(n); + switch(n->op){ + case Oalt: + altcheck(n, ret); + break; + case Ocase: + casecheck(n, ret); + break; + case Opick: + pickcheck(n, ret); + break; + case Oexcept: + exccheck(n, ret); + break; + } + labdep--; + if(n->decl != nil && !n->decl->refs) + nwarn(n, "label %s never referenced", n->decl->sym->name); + return top; + case Oret: + rok = echeck(left, 0, 0, nil); + if(!rok.ok) + return top; + if(left == nil){ + if(ret != tnone) + nerror(n, "return of nothing from a fn of %T", ret); + }else if(ret == tnone){ + if(left->ty != tnone) + nerror(n, "return %Q from a fn with no return type", left); + }else if(!tcompat(ret, left->ty, 0)) + nerror(n, "return %Q from a fn of %T", left, ret); + return top; + case Obreak: + case Ocont: + s = nil; + if(n->decl != nil) + s = n->decl->sym; + for(i = 0; i < labdep; i++){ + if(s == nil || labstack[i]->decl != nil && labstack[i]->decl->sym == s){ + if(n->op == Ocont + && labstack[i]->op != Ofor && labstack[i]->op != Odo) + continue; + if(s != nil) + labstack[i]->decl->refs++; + return top; + } + } + nerror(n, "no appropriate target for %V", n); + return top; + case Oexit: + case Onothing: + return top; + case Oexstmt: + fndec->handler = 1; + n->left = scheck(left, ret, Sother); + n->right = scheck(right, ret, Sother); + return top; + default: + rok = echeck(n, 0, 0, nil); + if(rok.allok) + n = checkused(n); + if(last == nil) + return n; + last->right = n; + return top; + } + last = n; + } + return top; +} + +void +pushlabel(Node *n) +{ + Sym *s; + int i; + + if(labdep >= maxlabdep){ + maxlabdep += MaxScope; + labstack = reallocmem(labstack, maxlabdep * sizeof *labstack); + } + if(n->decl != nil){ + s = n->decl->sym; + n->decl->refs = 0; + for(i = 0; i < labdep; i++) + if(labstack[i]->decl != nil && labstack[i]->decl->sym == s) + nerror(n, "label %s duplicated on line %L", s->name, labstack[i]->decl->src.start); + } + labstack[labdep++] = n; +} + +void +varcheck(Node *n, int isglobal) +{ + Type *t; + Decl *ids, *last; + + t = validtype(n->ty, nil); + t = topvartype(t, n->decl, isglobal, 0); + last = n->left->decl; + for(ids = n->decl; ids != last->next; ids = ids->next){ + ids->ty = t; + shareloc(ids); + } + if(t->u.eraises) + raisescheck(t); +} + +void +concheck(Node *n, int isglobal) +{ + Decl *ids, *last; + Type *t; + Node *init; + Ok rok; + int i; + + pushscope(nil, Sother); + installids(Dconst, iota); + rok = echeck(n->right, 0, isglobal, nil); + popscope(); + + init = n->right; + if(!rok.ok){ + t = terror; + }else{ + t = init->ty; + if(!tattr[t->kind].conable){ + nerror(init, "cannot have a %T constant", t); + rok.allok = 0; + } + } + + last = n->left->decl; + for(ids = n->decl; ids != last->next; ids = ids->next) + ids->ty = t; + + if(!rok.allok) + return; + + i = 0; + for(ids = n->decl; ids != last->next; ids = ids->next){ + if(rok.ok){ + iota->init->val = i; + ids->init = dupn(0, &nosrc, init); + if(!varcom(ids)) + rok.ok = 0; + } + i++; + } +} + +static char* +exname(Decl *d) +{ + int n; + Sym *m; + char *s; + char buf[16]; + + n = 0; + sprint(buf, "%d", scope-ScopeGlobal); + m = impmods->sym; + if(d->dot) + m = d->dot->sym; + if(m) + n += strlen(m->name)+1; + if(fndec) + n += strlen(fndec->sym->name)+1; + n += strlen(buf)+1+strlen(d->sym->name)+1; + s = malloc(n); + strcpy(s, ""); + if(m){ + strcat(s, m->name); + strcat(s, "."); + } + if(fndec){ + strcat(s, fndec->sym->name); + strcat(s, "."); + } + strcat(s, buf); + strcat(s, "."); + strcat(s, d->sym->name); + return s; +} + +void +excheck(Node *n, int isglobal) +{ + char *nm; + Type *t; + Decl *ids, *last; + + t = validtype(n->ty, nil); + t = topvartype(t, n->decl, isglobal, 0); + last = n->left->decl; + for(ids = n->decl; ids != last->next; ids = ids->next){ + ids->ty = t; + nm = exname(ids); + ids->init = mksconst(&n->src, enterstring(nm, strlen(nm))); + /* ids->init = mksconst(&n->src, enterstring(strdup(ids->sym->name), strlen(ids->sym->name))); */ + } +} + +void +importcheck(Node *n, int isglobal) +{ + Node *m; + Decl *id, *last, *v; + Type *t; + Ok rok; + + rok = echeck(n->right, 1, isglobal, nil); + if(!rok.ok) + return; + + m = n->right; + if(m->ty->kind != Tmodule || m->op != Oname){ + nerror(n, "cannot import from %Q", m); + return; + } + + last = n->left->decl; + for(id = n->decl; id != last->next; id = id->next){ + v = namedot(m->ty->ids, id->sym); + if(v == nil){ + error(id->src.start, "%s is not a member of %V", id->sym->name, m); + id->store = Dwundef; + continue; + } + id->store = v->store; + v->ty = validtype(v->ty, nil); + id->ty = t = v->ty; + if(id->store == Dtype && t->decl != nil){ + id->timport = t->decl->timport; + t->decl->timport = id; + } + id->init = v->init; + id->importid = v; + id->eimport = m; + } +} + +static Decl* +rewcall(Node *n, Decl *d) +{ + /* put original function back now we're type checked */ + while(d->link != nil) + d = d->link; + if(n->op == Odot) + n->right->decl = d; + else if(n->op == Omdot){ + n->right->right->decl = d; + n->right->right->ty = d->ty; + } + else + fatal("bad op in Ocall rewcall"); + n->ty = n->right->ty = d->ty; + d->refs++; + usetype(d->ty); + return d; +} + +/* + * annotate the expression with types + */ +Ok +echeck(Node *n, int typeok, int isglobal, Node *par) +{ + Type *t, *tt; + Node *left, *right, *mod, *nn; + Decl *tg, *id, *callee; + Sym *s; + int max, nocheck; + Ok ok, rok, kidsok; + static int tagopt; + + ok.ok = ok.allok = 1; + if(n == nil) + return ok; + + /* avoid deep recursions */ + if(n->op == Oseq){ + for( ; n != nil && n->op == Oseq; n = n->right){ + rok = echeck(n->left, typeok == 2, isglobal, n); + ok.ok &= rok.ok; + ok.allok &= rok.allok; + n->ty = tnone; + } + if(n == nil) + return ok; + } + + left = n->left; + right = n->right; + + nocheck = 0; + if(n->op == Odot || n->op == Omdot || n->op == Ocall || n->op == Oref || n->op == Otagof || n->op == Oindex) + nocheck = 1; + if(n->op != Odas /* special case */ + && n->op != Oload) /* can have better error recovery */ + ok = echeck(left, nocheck, isglobal, n); + if(n->op != Odas /* special case */ + && n->op != Odot /* special check */ + && n->op != Omdot /* special check */ + && n->op != Ocall /* can have better error recovery */ + && n->op != Oindex){ + rok = echeck(right, 0, isglobal, n); + ok.ok &= rok.ok; + ok.allok &= rok.allok; + } + if(!ok.ok){ + n->ty = terror; + ok.allok = 0; + return ok; + } + + switch(n->op){ + case Odas: + kidsok = echeck(right, 0, isglobal, n); + if(!kidsok.ok) + right->ty = terror; + if(!isglobal && !dasdecl(left)){ + kidsok.ok = 0; + }else if(!specific(right->ty) || !declasinfer(left, right->ty)){ + nerror(n, "cannot declare %V from %Q", left, right); + declaserr(left); + kidsok.ok = 0; + } + if(right->ty->kind == Texception) + left->ty = n->ty = mkextuptype(right->ty); + else{ + left->ty = n->ty = right->ty; + usedty(n->ty); + } + kidsok.allok &= kidsok.ok; + if (nested() && tmustzero(left->ty)) + decltozero(left); + return kidsok; + case Oseq: + case Onothing: + n->ty = tnone; + break; + case Owild: + n->ty = tint; + break; + case Ocast: + t = usetype(n->ty); + n->ty = t; + tt = left->ty; + if(tcompat(t, tt, 0)){ + left->ty = t; + break; + } + if(tt->kind == Tarray){ + if(tt->tof == tbyte && t == tstring) + break; + }else if(t->kind == Tarray){ + if(t->tof == tbyte && tt == tstring) + break; + }else if(casttab[tt->kind][t->kind]){ + break; + } + nerror(n, "cannot make a %T from %Q", n->ty, left); + ok.ok = ok.allok = 0; + return ok; + case Ochan: + n->ty = usetype(n->ty); + if(left && left->ty->kind != Tint){ + nerror(n, "channel size %Q is not an int", left); + ok.ok = ok.allok = 0; + return ok; + } + break; + case Oload: + n->ty = usetype(n->ty); + kidsok = echeck(left, 0, isglobal, n); + if(n->ty->kind != Tmodule){ + nerror(n, "cannot load a %T, ", n->ty); + ok.ok = ok.allok = 0; + return ok; + } + if(!kidsok.allok){ + ok.allok = 0; + break; + } + if(left->ty != tstring){ + nerror(n, "cannot load a module from %Q", left); + ok.allok = 0; + break; + } +if(n->ty->tof->decl->refs != 0) +n->ty->tof->decl->refs++; +n->ty->decl->refs++; + usetype(n->ty->tof); + break; + case Oref: + t = left->ty; + if(t->kind != Tadt && t->kind != Tadtpick && t->kind != Tfn && t->kind != Ttuple){ + nerror(n, "cannot make a ref from %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + if(!tagopt && t->kind == Tadt && t->tags != nil && valistype(left)){ + nerror(n, "instances of ref %V must be qualified with a pick tag", left); + ok.ok = ok.allok = 0; + return ok; + } + if(t->kind == Tadtpick) + t->tof = usetype(t->tof); + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tref, t, nil)); + break; + case Oarray: + max = 0; + if(right != nil){ + max = assignindices(n); + if(max < 0){ + ok.ok = ok.allok = 0; + return ok; + } + if(!specific(right->left->ty)){ + nerror(n, "type for array not specific"); + ok.ok = ok.allok = 0; + return ok; + } + n->ty = mktype(&n->src.start, &n->src.stop, Tarray, right->left->ty, nil); + } + n->ty = usetype(n->ty); + + if(left->op == Onothing) + n->left = left = mkconst(&n->left->src, max); + + if(left->ty->kind != Tint){ + nerror(n, "array size %Q is not an int", left); + ok.ok = ok.allok = 0; + return ok; + } + break; + case Oelem: + n->ty = right->ty; + break; + case Orange: + if(left->ty != right->ty + || left->ty != tint && left->ty != tstring){ + nerror(left, "range %Q to %Q is not an int or string range", left, right); + ok.ok = ok.allok = 0; + return ok; + } + n->ty = left->ty; + break; + case Oname: + id = n->decl; + if(id == nil){ + nerror(n, "name with no declaration"); + ok.ok = ok.allok = 0; + return ok; + } + if(id->store == Dunbound){ + s = id->sym; + id = s->decl; + if(id == nil) + id = undefed(&n->src, s); + /* save a little space */ + s->unbound = nil; + n->decl = id; + id->refs++; + } + n->ty = id->ty = usetype(id->ty); + switch(id->store){ + case Dfn: + case Dglobal: + case Darg: + case Dlocal: + case Dimport: + case Dfield: + case Dtag: + break; + case Dundef: + nerror(n, "%s is not declared", id->sym->name); + id->store = Dwundef; + ok.ok = ok.allok = 0; + return ok; + case Dwundef: + ok.ok = ok.allok = 0; + return ok; + case Dconst: + if(id->init == nil){ + nerror(n, "%s's value cannot be determined", id->sym->name); + id->store = Dwundef; + ok.ok = ok.allok = 0; + return ok; + } + break; + case Dtype: + if(typeok) + break; + nerror(n, "%K is not a variable", id); + ok.ok = ok.allok = 0; + return ok; + default: + fatal("echeck: unknown symbol storage"); + } + + if(n->ty == nil){ + nerror(n, "%K's type is not fully defined", id); + id->store = Dwundef; + ok.ok = ok.allok = 0; + return ok; + } + if(id->importid != nil && valistype(id->eimport) + && id->store != Dconst && id->store != Dtype && id->store != Dfn){ + nerror(n, "cannot use %V because %V is a module interface", n, id->eimport); + ok.ok = ok.allok = 0; + return ok; + } + if(n->ty->kind == Texception && !n->ty->cons && par != nil && par->op != Oraise && par->op != Odot){ + nn = mkn(0, nil, nil); + *nn = *n; + n->op = Ocast; + n->left = nn; + n->decl = nil; + n->ty = usetype(mkextuptype(n->ty)); + } + /* function name as function reference */ + if(id->store == Dfn && (par == nil || (par->op != Odot && par->op != Omdot && par->op != Ocall && par->op != Ofunc))) + fnref(n, id); + break; + case Oconst: + if(n->ty == nil){ + nerror(n, "no type in %V", n); + ok.ok = ok.allok = 0; + return ok; + } + break; + case Oas: + t = right->ty; + if(t->kind == Texception) + t = mkextuptype(t); + if(!tcompat(left->ty, t, 1)){ + nerror(n, "type clash in %Q = %Q", left, right); + ok.ok = ok.allok = 0; + return ok; + } + if(t == tany) + t = left->ty; + n->ty = t; + left->ty = t; + if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick) + if(left->ty->kind != Tadtpick || right->ty->kind != Tadtpick) + nerror(n, "expressions cannot have type %T", t); + if(left->ty->kind == Texception){ + nerror(n, "cannot assign to an exception"); + ok.ok = ok.allok = 0; + return ok; + } + if(islval(left)) + break; + ok.ok = ok.allok = 0; + return ok; + case Osnd: + if(left->ty->kind != Tchan){ + nerror(n, "cannot send on %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + if(!tcompat(left->ty->tof, right->ty, 0)){ + nerror(n, "type clash in %Q <-= %Q", left, right); + ok.ok = ok.allok = 0; + return ok; + } + t = right->ty; + if(t == tany) + t = left->ty->tof; + n->ty = t; + break; + case Orcv: + t = left->ty; + if(t->kind == Tarray) + t = t->tof; + if(t->kind != Tchan){ + nerror(n, "cannot receive on %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + if(left->ty->kind == Tarray) + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil, + mkids(&n->src, nil, tint, mkids(&n->src, nil, t->tof, nil)))); + else + n->ty = t->tof; + break; + case Ocons: + if(right->ty->kind != Tlist && right->ty != tany){ + nerror(n, "cannot :: to %Q", right); + ok.ok = ok.allok = 0; + return ok; + } + n->ty = right->ty; + if(right->ty == tany) + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, left->ty, nil)); + else if(!tcompat(right->ty->tof, left->ty, 0)){ + t = tparent(right->ty->tof, left->ty); + if(!tcompat(t, left->ty, 0)){ + nerror(n, "type clash in %Q :: %Q", left, right); + ok.ok = ok.allok = 0; + return ok; + } + else + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Tlist, t, nil)); + } + break; + case Ohd: + case Otl: + if(left->ty->kind != Tlist || left->ty->tof == nil){ + nerror(n, "cannot %O %Q", n->op, left); + ok.ok = ok.allok = 0; + return ok; + } + if(n->op == Ohd) + n->ty = left->ty->tof; + else + n->ty = left->ty; + break; + case Otuple: + n->ty = usetype(mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(left))); + break; + case Ospawn: + if(left->op != Ocall || left->left->ty->kind != Tfn && !isfnrefty(left->left->ty)){ + nerror(left, "cannot spawn %V", left); + ok.ok = ok.allok = 0; + return ok; + } + if(left->ty != tnone){ + nerror(left, "cannot spawn functions which return values, such as %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + break; + case Oraise: + if(left->op == Onothing){ + if(inexcept == nil){ + nerror(n, "%V: empty raise not in exception handler", n); + ok.ok = ok.allok = 0; + return ok; + } + n->left = dupn(1, &n->src, inexcept); + break; + } + if(left->ty != tstring && left->ty->kind != Texception){ + nerror(n, "%V: raise argument %Q is not a string or exception", n, left); + ok.ok = ok.allok = 0; + return ok; + } + if((left->op != Ocall || left->left->ty->kind == Tfn) && left->ty->ids != nil && left->ty->cons){ + nerror(n, "too few exception arguments"); + ok.ok = ok.allok = 0; + return ok; + } + break; + case Ocall:{ + int pure; + + kidsok = echeck(right, 0, isglobal, nil); + t = left->ty; + usedty(t); + pure = 1; + if(t->kind == Tref){ + pure = 0; + t = t->tof; + } + if(t->kind != Tfn) + return callcast(n, kidsok.allok, ok.allok); + n->ty = t->tof; + if(!kidsok.allok){ + ok.allok = 0; + break; + } + + /* + * get the name to call and any associated module + */ + mod = nil; + callee = nil; + id = nil; + tt = nil; + if(left->op == Odot){ + Decl *dd; + Type *ttt; + + callee = left->right->decl; + id = callee->dot; + right = passimplicit(left, right); + n->right = right; + tt = left->left->ty; + if(tt->kind == Tref) + tt = tt->tof; + ttt = tt; + if(tt->kind == Tadtpick) + ttt = tt->decl->dot->ty; + dd = ttt->decl; + while(dd != nil && dd->link != nil) + dd = dd->link; + if(dd != nil && dd->timport != nil) + mod = dd->timport->eimport; + /* + * stash the import module under a rock, + * because we won't be able to get it later + * after scopes are popped + */ + left->right->left = mod; + }else if(left->op == Omdot){ + if(left->right->op == Odot){ + callee = left->right->right->decl; + right = passimplicit(left->right, right); + n->right = right; + tt = left->right->left->ty; + if(tt->kind == Tref) + tt = tt->tof; + }else + callee = left->right->decl; + mod = left->left; + }else if(left->op == Oname){ + callee = left->decl; + id = callee; + mod = id->eimport; + }else if(pure){ + nerror(left, "%V is not a function name", left); + ok.allok = 0; + break; + } + if(pure && callee == nil) + fatal("can't find called function: %n", left); + if(callee != nil && callee->store != Dfn && !isfnref(callee)){ + nerror(left, "%V is not a function or function reference", left); + ok.allok = 0; + break; + } + if(mod != nil && mod->ty->kind != Tmodule){ + nerror(left, "cannot call %V", left); + ok.allok = 0; + break; + } + if(mod != nil){ + if(valistype(mod)){ + nerror(left, "cannot call %V because %V is a module interface", left, mod); + ok.allok = 0; + break; + } + }else if(id != nil && id->dot != nil && !isimpmod(id->dot->sym)){ + nerror(left, "cannot call %V without importing %s from a variable", left, id->sym->name); + ok.allok = 0; + break; + } + if(mod != nil) + modrefable(left->ty); + if(callee != nil && callee->store != Dfn) + callee = nil; + if(t->varargs != 0){ + t = mkvarargs(left, right); + if(left->ty->kind == Tref) + left->ty = usetype(mktype(&t->src.start, &t->src.stop, Tref, t, nil)); + else + left->ty = t; + } + else if(ispoly(callee) || isfnrefty(left->ty) && left->ty->tof->polys != nil){ + Tpair *tp; + + unifysrc = n->src; + if(!argncompat(n, t->ids, right)) + ok.allok = 0; + else if(!tunify(left->ty, calltype(left->ty, right, n->ty), &tp)){ + nerror(n, "function call type mismatch"); + ok.allok = 0; + } + else{ + n->ty = usetype(expandtype(n->ty, nil, nil, &tp)); + if(ispoly(callee) && tt != nil && (tt->kind == Tadt || tt->kind == Tadtpick) && (tt->flags&INST)) + callee = rewcall(left, callee); + n->right = passfns(&n->src, callee, left, right, tt, tp); + } + } + else if(!argcompat(n, t->ids, right)) + ok.allok = 0; + break; + } + case Odot: + t = left->ty; + if(t->kind == Tref) + t = t->tof; + switch(t->kind){ + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + case Tpoly: + id = namedot(t->ids, right->decl->sym); + if(id == nil){ + id = namedot(t->tags, right->decl->sym); + if(id != nil && !valistype(left)){ + nerror(n, "%V is not a type", left); + ok.ok = ok.allok = 0; + return ok; + } + } + if(id == nil){ + id = namedot(t->polys, right->decl->sym); + if(id != nil && !valistype(left)){ + nerror(n, "%V is not a type", left); + ok.ok = ok.allok = 0; + return ok; + } + } + if(id == nil && t->kind == Tadtpick) + id = namedot(t->decl->dot->ty->ids, right->decl->sym); + if(id == nil){ + for(tg = t->tags; tg != nil; tg = tg->next){ + id = namedot(tg->ty->ids, right->decl->sym); + if(id != nil) + break; + } + if(id != nil){ + nerror(n, "cannot yet index field %s of %Q", right->decl->sym->name, left); + ok.ok = ok.allok = 0; + return ok; + } + } + if(id == nil) + break; + if(id->store == Dfield && valistype(left)){ + nerror(n, "%V is not a value", left); + ok.ok = ok.allok = 0; + return ok; + } + id->ty = validtype(id->ty, t->decl); + id->ty = usetype(id->ty); + break; + default: + nerror(left, "%Q cannot be qualified with .", left); + ok.ok = ok.allok = 0; + return ok; + } + if(id == nil){ + nerror(n, "%V is not a member of %Q", right, left); + ok.ok = ok.allok = 0; + return ok; + } + if(id->ty == tunknown){ + nerror(n, "illegal forward reference to %V", n); + ok.ok = ok.allok = 0; + return ok; + } + + increfs(id); + right->decl = id; + n->ty = id->ty; + if((id->store == Dconst || id->store == Dtag) && hasside(left, 1)) + nwarn(left, "result of expression %Q ignored", left); + /* function name as function reference */ + if(id->store == Dfn && (par == nil || (par->op != Omdot && par->op != Ocall && par->op != Ofunc))) + fnref(n, id); + break; + case Omdot: + t = left->ty; + if(t->kind != Tmodule){ + nerror(left, "%Q cannot be qualified with ->", left); + ok.ok = ok.allok = 0; + return ok; + } + id = nil; + if(right->op == Oname){ + id = namedot(t->ids, right->decl->sym); + }else if(right->op == Odot){ + kidsok = echeck(right, 0, isglobal, n); + ok.ok = kidsok.ok; + ok.allok &= kidsok.allok; + if(!ok.ok){ + ok.allok = 0; + return ok; + } + tt = right->left->ty; + if(tt->kind == Tref) + tt = tt->tof; + if(right->ty->kind == Tfn + && tt->kind == Tadt + && tt->decl->dot == t->decl) + id = right->right->decl; + } + if(id == nil){ + nerror(n, "%V is not a member of %Q", right, left); + ok.ok = ok.allok = 0; + return ok; + } + if(id->store != Dconst && id->store != Dtype && id->store != Dtag){ + if(valistype(left)){ + nerror(n, "%V is not a value", left); + ok.ok = ok.allok = 0; + return ok; + } + }else if(hasside(left, 1)) + nwarn(left, "result of expression %Q ignored", left); + if(!typeok && id->store == Dtype){ + nerror(n, "%V is a type, not a value", n); + ok.ok = ok.allok = 0; + return ok; + } + if(id->ty == tunknown){ + nerror(n, "illegal forward reference to %V", n); + ok.ok = ok.allok = 0; + return ok; + } + id->refs++; + right->decl = id; + n->ty = id->ty = usetype(id->ty); + if(id->store == Dglobal) + modrefable(id->ty); + /* function name as function reference */ + if(id->store == Dfn && (par == nil || (par->op != Ocall && par->op != Ofunc))) + fnref(n, id); + break; + case Otagof: + n->ty = tint; + t = left->ty; + if(t->kind == Tref) + t = t->tof; + id = nil; + switch(left->op){ + case Oname: + id = left->decl; + break; + case Odot: + id = left->right->decl; + break; + case Omdot: + if(left->right->op == Odot) + id = left->right->right->decl; + break; + } + if(id != nil && id->store == Dtag + || id != nil && id->store == Dtype && t->kind == Tadt && t->tags != nil) + n->decl = id; + else if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick) + n->decl = nil; + else{ + nerror(n, "cannot get the tag value for %Q", left); + ok.ok = 1; + ok.allok = 0; + return ok; + } + break; + case Oind: + t = left->ty; + if(t->kind != Tref || (t->tof->kind != Tadt && t->tof->kind != Tadtpick && t->tof->kind != Ttuple)){ + nerror(n, "cannot * %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + n->ty = t->tof; + for(tg = t->tof->tags; tg != nil; tg = tg->next) + tg->ty->tof = usetype(tg->ty->tof); + break; + case Oindex: + if(valistype(left)){ + tagopt = 1; + kidsok = echeck(right, 2, isglobal, n); + tagopt = 0; + if(!kidsok.allok){ + ok.ok = ok.allok = 0; + return ok; + } + if((t = exptotype(n)) == nil){ + nerror(n, "%V is not a type list", right); + ok.ok = ok.allok = 0; + return ok; + } + if(!typeok){ + nerror(n, "%Q is not a variable", left); + ok.ok = ok.allok = 0; + return ok; + } + *n = *(n->left); + n->ty = usetype(t); + break; + } + if(0 && right->op == Oseq){ /* a[e1, e2, ...] */ + /* array creation to do before we allow this */ + rewind(n); + return echeck(n, typeok, isglobal, par); + } + t = left->ty; + kidsok = echeck(right, 0, isglobal, n); + if(t->kind != Tarray && t != tstring){ + nerror(n, "cannot index %Q", left); + ok.ok = ok.allok = 0; + return ok; + } + if(t == tstring){ + n->op = Oinds; + n->ty = tint; + }else{ + n->ty = t->tof; + } + if(!kidsok.allok){ + ok.allok = 0; + break; + } + if(right->ty != tint){ + nerror(n, "cannot index %Q with %Q", left, right); + ok.allok = 0; + break; + } + break; + case Oslice: + t = n->ty = left->ty; + if(t->kind != Tarray && t != tstring){ + nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right); + ok.ok = ok.allok = 0; + return ok; + } + if(right->left->ty != tint && right->left->op != Onothing + || right->right->ty != tint && right->right->op != Onothing){ + nerror(n, "cannot slice %Q with '%v:%v'", left, right->left, right->right); + ok.allok = 0; + return ok; + } + break; + case Olen: + t = left->ty; + n->ty = tint; + if(t->kind != Tarray && t->kind != Tlist && t != tstring){ + nerror(n, "len requires an array, string or list in %Q", left); + ok.allok = 0; + return ok; + } + break; + case Ocomp: + case Onot: + case Oneg: + n->ty = left->ty; + usedty(n->ty); + switch(left->ty->kind){ + case Tint: + return ok; + case Treal: + case Tfix: + if(n->op == Oneg) + return ok; + break; + case Tbig: + case Tbyte: + if(n->op == Oneg || n->op == Ocomp) + return ok; + break; + } + nerror(n, "cannot apply %O to %Q", n->op, left); + ok.ok = ok.allok = 0; + return ok; + case Oinc: + case Odec: + case Opreinc: + case Opredec: + n->ty = left->ty; + switch(left->ty->kind){ + case Tint: + case Tbig: + case Tbyte: + case Treal: + break; + default: + nerror(n, "cannot apply %O to %Q", n->op, left); + ok.ok = ok.allok = 0; + return ok; + } + if(islval(left)) + break; + ok.ok = ok.allok = 0; + return ok; + case Oadd: + case Odiv: + case Omul: + case Osub: + if(mathchk(n, 1)) + break; + ok.ok = ok.allok = 0; + return ok; + case Oexp: + case Oexpas: + n->ty = left->ty; + if(n->ty != tint && n->ty != tbig && n->ty != treal){ + nerror(n, "exponend %Q is not int, big or real", left); + ok.ok = ok.allok = 0; + return ok; + } + if(right->ty != tint){ + nerror(n, "exponent %Q is not int", right); + ok.ok = ok.allok = 0; + return ok; + } + if(n->op == Oexpas && !islval(left)){ + ok.ok = ok.allok = 0; + return ok; + } + break; +/* + if(mathchk(n, 0)){ + if(n->ty != tint){ + nerror(n, "exponentiation operands not int"); + ok.ok = ok.allok = 0; + return ok; + } + break; + } + ok.ok = ok.allok = 0; + return ok; +*/ + case Olsh: + case Orsh: + if(shiftchk(n)) + break; + ok.ok = ok.allok = 0; + return ok; + case Oandand: + case Ooror: + if(left->ty != tint){ + nerror(n, "%O's left operand is not an int: %Q", n->op, left); + ok.allok = 0; + } + if(right->ty != tint){ + nerror(n, "%O's right operand is not an int: %Q", n->op, right); + ok.allok = 0; + } + n->ty = tint; + break; + case Oand: + case Omod: + case Oor: + case Oxor: + if(mathchk(n, 0)) + break; + ok.ok = ok.allok = 0; + return ok; + case Oaddas: + case Odivas: + case Omulas: + case Osubas: + if(mathchk(n, 1) && islval(left)) + break; + ok.ok = ok.allok = 0; + return ok; + case Olshas: + case Orshas: + if(shiftchk(n) && islval(left)) + break; + ok.ok = ok.allok = 0; + return ok; + case Oandas: + case Omodas: + case Oxoras: + case Ooras: + if(mathchk(n, 0) && islval(left)) + break; + ok.ok = ok.allok = 0; + return ok; + case Olt: + case Oleq: + case Ogt: + case Ogeq: + if(!mathchk(n, 1)){ + ok.ok = ok.allok = 0; + return ok; + } + n->ty = tint; + break; + case Oeq: + case Oneq: + switch(left->ty->kind){ + case Tint: + case Tbig: + case Tbyte: + case Treal: + case Tstring: + case Tref: + case Tlist: + case Tarray: + case Tchan: + case Tany: + case Tmodule: + case Tfix: + case Tpoly: + if(!tcompat(left->ty, right->ty, 0) && !tcompat(right->ty, left->ty, 0)) + break; + t = left->ty; + if(t == tany) + t = right->ty; + if(t == tany) + t = tint; + if(left->ty == tany) + left->ty = t; + if(right->ty == tany) + right->ty = t; + n->ty = tint; + return ok; + } + nerror(n, "cannot compare %Q to %Q", left, right); + usedty(n->ty); + ok.ok = ok.allok = 0; + return ok; + case Otype: + if(!typeok){ + nerror(n, "%Q is not a variable", n); + ok.ok = ok.allok = 0; + return ok; + } + n->ty = usetype(n->ty); + break; + default: + fatal("unknown op in typecheck: %O", n->op); + } + usedty(n->ty); + return ok; +} + +/* + * n is syntactically a call, but n->left is not a fn + * check if it's the contructor for an adt + */ +Ok +callcast(Node *n, int kidsok, int allok) +{ + Node *left, *right; + Decl *id; + Type *t, *tt; + Ok ok; + + left = n->left; + right = n->right; + id = nil; + switch(left->op){ + case Oname: + id = left->decl; + break; + case Omdot: + if(left->right->op == Odot) + id = left->right->right->decl; + else + id = left->right->decl; + break; + case Odot: + id = left->right->decl; + break; + } +/* + (chan of int)(nil) looks awkward since both sets of brackets needed + if(id == nil && right != nil && right->right == nil && (t = exptotype(left)) != nil){ + n->op = Ocast; + n->left = right->left; + n->right = nil; + n->ty = t; + return echeck(n, 0, 0, nil); + } +*/ + if(id == nil || (id->store != Dtype && id->store != Dtag && id->ty->kind != Texception)){ + nerror(left, "%V is not a function or type name", left); + ok.ok = ok.allok = 0; + return ok; + } + if(id->store == Dtag) + return tagcast(n, left, right, id, kidsok, allok); + t = left->ty; + n->ty = t; + if(!kidsok){ + ok.ok = 1; + ok.allok = 0; + return ok; + } + + if(t->kind == Tref) + t = t->tof; + tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, tuplefields(right)); + if(t->kind == Tadt && tcompat(t, tt, 1)){ + if(right == nil) + *n = *n->left; + ok.ok = 1; + ok.allok = allok; + return ok; + } + + /* try an exception with args */ + tt = mktype(&n->src.start, &n->src.stop, Texception, nil, tuplefields(right)); + tt->cons = 1; + if(t->kind == Texception && t->cons && tcompat(t, tt, 1)){ + if(right == nil) + *n = *n->left; + ok.ok = 1; + ok.allok = allok; + return ok; + } + + /* try a cast */ + if(t->kind != Texception && right != nil && right->right == nil){ /* Oseq but single expression */ + right = right->left; + n->op = Ocast; + n->left = right; + n->right = nil; + n->ty = mkidtype(&n->src, id->sym); + return echeck(n, 0, 0, nil); + } + + nerror(left, "cannot make a %V from '(%v)'", left, right); + ok.ok = ok.allok = 0; + return ok; +} + +Ok +tagcast(Node *n, Node *left, Node *right, Decl *id, int kidsok, int allok) +{ + Type *tt; + Ok ok; + + left->ty = id->ty; + if(left->op == Omdot) + left->right->ty = id->ty; + n->ty = id->ty; + if(!kidsok){ + ok.ok = 1; + ok.allok = 0; + return ok; + } + id->ty->tof = usetype(id->ty->tof); + if(right != nil) + right->ty = id->ty->tof; + tt = mktype(&n->src.start, &n->src.stop, Ttuple, nil, mkids(&nosrc, nil, tint, tuplefields(right))); + tt->ids->store = Dfield; + if(tcompat(id->ty->tof, tt, 1)){ + ok.ok = 1; + ok.allok = allok; + return ok; + } + + nerror(left, "cannot make a %V from '(%v)'", left, right); + ok.ok = ok.allok = 0; + return ok; +} + +int +valistype(Node *n) +{ + switch(n->op){ + case Oname: + if(n->decl->store == Dtype) + return 1; + break; + case Omdot: + return valistype(n->right); + } + return 0; +} + +int +islval(Node *n) +{ + int s; + + s = marklval(n); + if(s == 1) + return 1; + if(s == 0) + nerror(n, "cannot assign to %V", n); + else + circlval(n, n); + return 0; +} + +/* + * check to see if n is an lval + * mark the lval name as set + */ +int +marklval(Node *n) +{ + Decl *id; + Node *nn; + int s; + + if(n == nil) + return 0; + switch(n->op){ + case Oname: + return storespace[n->decl->store] && n->ty->kind != Texception; /*ZZZZ && n->decl->tagged == nil;*/ + case Odot: + if(n->right->decl->store != Dfield) + return 0; + if(n->right->decl->cycle && !n->right->decl->cyc) + return -1; + if(n->left->ty->kind != Tref && marklval(n->left) == 0) + nwarn(n, "assignment to %Q ignored", n); + return 1; + case Omdot: + if(n->right->decl->store == Dglobal) + return 1; + return 0; + case Oind: + for(id = n->ty->ids; id != nil; id = id->next) + if(id->cycle && !id->cyc) + return -1; + return 1; + case Oslice: + if(n->right->right->op != Onothing || n->ty == tstring) + return 0; + return 1; + case Oinds: + /* + * make sure we don't change a string constant + */ + switch(n->left->op){ + case Oconst: + return 0; + case Oname: + return storespace[n->left->decl->store]; + case Odot: + case Omdot: + if(n->left->right->decl != nil) + return storespace[n->left->right->decl->store]; + break; + } + return 1; + case Oindex: + case Oindx: + return 1; + case Otuple: + for(nn = n->left; nn != nil; nn = nn->right){ + s = marklval(nn->left); + if(s != 1) + return s; + } + return 1; + default: + return 0; + } + return 0; +} + +/* + * n has a circular field assignment. + * find it and print an error message. + */ +int +circlval(Node *n, Node *lval) +{ + Decl *id; + Node *nn; + int s; + + if(n == nil) + return 0; + switch(n->op){ + case Oname: + break; + case Odot: + if(n->right->decl->cycle && !n->right->decl->cyc){ + nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V", + lval, n->right->decl->sym->name, n->left, n->left); + return -1; + } + return 1; + case Oind: + for(id = n->ty->ids; id != nil; id = id->next){ + if(id->cycle && !id->cyc){ + nerror(lval, "cannot assign to %V because field '%s' of %V could complete a cycle to %V", + lval, id->sym->name, n, n); + return -1; + } + } + return 1; + case Oslice: + if(n->right->right->op != Onothing || n->ty == tstring) + return 0; + return 1; + case Oindex: + case Oinds: + case Oindx: + return 1; + case Otuple: + for(nn = n->left; nn != nil; nn = nn->right){ + s = circlval(nn->left, lval); + if(s != 1) + return s; + } + return 1; + default: + return 0; + } + return 0; +} + +int +mathchk(Node *n, int realok) +{ + Type *tr, *tl; + + tl = n->left->ty; + tr = n->right->ty; + if(tr != tl && !tequal(tl, tr)){ + nerror(n, "type clash in %Q %O %Q", n->left, n->op, n->right); + return 0; + } + n->ty = tr; + switch(tr->kind){ + case Tint: + case Tbig: + case Tbyte: + return 1; + case Tstring: + switch(n->op){ + case Oadd: + case Oaddas: + case Ogt: + case Ogeq: + case Olt: + case Oleq: + return 1; + } + break; + case Treal: + case Tfix: + if(realok) + return 1; + break; + } + nerror(n, "cannot %O %Q and %Q", n->op, n->left, n->right); + return 0; +} + +int +shiftchk(Node *n) +{ + Node *left, *right; + + right = n->right; + left = n->left; + n->ty = left->ty; + switch(n->ty->kind){ + case Tint: + case Tbyte: + case Tbig: + if(right->ty->kind != Tint){ + nerror(n, "shift %Q is not an int", right); + return 0; + } + return 1; + } + nerror(n, "cannot %Q %O %Q", left, n->op, right); + return 0; +} + +/* + * check for any tany's in t + */ +int +specific(Type *t) +{ + Decl *d; + + if(t == nil) + return 0; + switch(t->kind){ + case Terror: + case Tnone: + case Tint: + case Tbig: + case Tstring: + case Tbyte: + case Treal: + case Tfn: + case Tadt: + case Tadtpick: + case Tmodule: + case Tfix: + return 1; + case Tany: + return 0; + case Tpoly: + return 1; + case Tref: + case Tlist: + case Tarray: + case Tchan: + return specific(t->tof); + case Ttuple: + case Texception: + for(d = t->ids; d != nil; d = d->next) + if(!specific(d->ty)) + return 0; + return 1; + } + fatal("unknown type %T in specific", t); + return 0; +} + +/* + * infer the type of all variable in n from t + * n is the left-hand exp of a := exp + */ +int +declasinfer(Node *n, Type *t) +{ + Decl *ids; + int ok; + + if(t->kind == Texception){ + if(t->cons) + return 0; + t = mkextuptype(t); + } + switch(n->op){ + case Otuple: + if(t->kind != Ttuple && t->kind != Tadt && t->kind != Tadtpick) + return 0; + ok = 1; + n->ty = t; + n = n->left; + ids = t->ids; + if(t->kind == Tadtpick) + ids = t->tof->ids->next; + for(; n != nil && ids != nil; ids = ids->next){ + if(ids->store != Dfield) + continue; + ok &= declasinfer(n->left, ids->ty); + n = n->right; + } + for(; ids != nil; ids = ids->next) + if(ids->store == Dfield) + break; + if(n != nil || ids != nil) + return 0; + return ok; + case Oname: + topvartype(t, n->decl, 0, 0); + if(n->decl == nildecl) + return 1; + n->decl->ty = t; + n->ty = t; + shareloc(n->decl); + return 1; + } + fatal("unknown op %n in declasinfer", n); + return 0; +} + +/* + * an error occured in declaring n; + * set all decl identifiers to Dwundef + * so further errors are squashed. + */ +void +declaserr(Node *n) +{ + switch(n->op){ + case Otuple: + for(n = n->left; n != nil; n = n->right) + declaserr(n->left); + return; + case Oname: + if(n->decl != nildecl) + n->decl->store = Dwundef; + return; + } + fatal("unknown op %n in declaserr", n); +} + +int +argcompat(Node *n, Decl *f, Node *a) +{ + for(; a != nil; a = a->right){ + if(f == nil){ + nerror(n, "%V: too many function arguments", n->left); + return 0; + } + if(!tcompat(f->ty, a->left->ty, 0)){ + nerror(n, "%V: argument type mismatch: expected %T saw %Q", + n->left, f->ty, a->left); + return 0; + } + if(a->left->ty == tany) + a->left->ty = f->ty; + f = f->next; + } + if(f != nil){ + nerror(n, "%V: too few function arguments", n->left); + return 0; + } + return 1; +} + +/* + * fn is Odot(adt, methid) + * pass adt implicitly if needed + * if not, any side effect of adt will be ingored + */ +Node* +passimplicit(Node *fn, Node *args) +{ + Node *n; + Type *t; + + t = fn->ty; + if(t->ids == nil || !t->ids->implicit){ + if(hasside(fn->left, 1)) + nwarn(fn, "result of expression %V ignored", fn->left); + return args; + } + n = fn->left; + if(n->op == Oname && n->decl->store == Dtype){ + nerror(n, "%V is a type and cannot be a self argument", n); + n = mkn(Onothing, nil, nil); + n->src = fn->src; + n->ty = t->ids->ty; + } + args = mkn(Oseq, n, args); + args->src = n->src; + return args; +} + +static int +mem(Type *t, Decl *d) +{ + for( ; d != nil; d = d->next) + if(d->ty == t) /* was if(d->ty == t || tequal(d->ty, t)) */ + return 1; + return 0; +} + +static int +memp(Type *t, Decl *f) +{ + return mem(t, f->ty->polys) || mem(t, encpolys(f)); +} + +static void +passfns0(Src *src, Decl *fn, Node *args0, Node **args, Node **a, Tpair *tp, Decl *polys) +{ + Decl *id, *idt, *idf, *dot; + Type *tt; + Sym *sym; + Node *n, *na, *mod; + Tpair *p; + +if(debug['w']){ + print("polys: "); + for(id=polys; id!=nil; id=id->next) print("%s ", id->sym->name); + print("\nmap: "); + for(p=tp; p!=nil; p=p->nxt) print("%T -> %T ", p->t1, p->t2); + print("\n"); +} + for(idt = polys; idt != nil; idt = idt->next){ + tt = valtmap(idt->ty, tp); + if(tt->kind == Tpoly && fndec != nil && !memp(tt, fndec)) + error(src->start, "cannot determine the instantiated type of %T", tt); + for(idf = idt->ty->ids; idf != nil; idf = idf->next){ + sym = idf->sym; + id = fnlookup(sym, tt, &mod); + while(id != nil && id->link != nil) + id = id->link; +if(debug['v']) print("fnlookup: %p\n", id); + if(id == nil) /* error flagged already */ + continue; + id->refs++; + id->caninline = -1; + if(tt->kind == Tmodule){ /* mod an actual parameter */ + for(;;){ + if(args0 != nil && tequal(tt, args0->left->ty)){ + mod = args0->left; + break; + } + if(args0 != nil) + args0 = args0->right; + } + } + if(mod == nil && (dot = module(id)) != nil && !isimpmod(dot->sym)) + error(src->start, "cannot use %s without importing %s from a variable", id->sym->name, id->dot->sym->name); + +if(debug['U']) print("fp: %s %s %s\n", fn->sym->name, mod ? mod->decl->sym->name : "nil", id->sym->name); + n = mkn(Ofnptr, mod, mkdeclname(src, id)); + n->src = *src; + n->decl = fn; + if(tt->kind == Tpoly) + n->flags = FNPTRA; + else + n->flags = 0; + na = mkn(Oseq, n, nil); + if(*a == nil) + *args = na; + else + (*a)->right = na; + + n = mkn(Ofnptr, mod, mkdeclname(src, id)); + n->src = *src; + n->decl = fn; + if(tt->kind == Tpoly) + n->flags = FNPTRA|FNPTR2; + else + n->flags = FNPTR2; + *a = na->right = mkn(Oseq, n, nil); + } + if(args0 != nil) + args0 = args0->right; + } +} + +Node* +passfns(Src *src, Decl *fn, Node *left, Node *args, Type *adt, Tpair *tp) +{ + Node *a, *args0; + + a = nil; + args0 = args; + if(args != nil) + for(a = args; a->right != nil; a = a->right) + ; + passfns0(src, fn, args0, &args, &a, tp, ispoly(fn) ? fn->ty->polys : left->ty->tof->polys); + if(adt != nil) + passfns0(src, fn, args0, &args, &a, adt->u.tmap, ispoly(fn) ? encpolys(fn) : nil); + return args; +} + +/* + * check the types for a function with a variable number of arguments + * last typed argument must be a constant string, and must use the + * print format for describing arguments. + */ +Type* +mkvarargs(Node *n, Node *args) +{ + Node *s, *a; + Decl *f, *last, *va; + Type *nt; + + nt = copytypeids(n->ty); + n->ty = nt; + f = n->ty->ids; + last = nil; + if(f == nil){ + nerror(n, "%V's type is illegal", n); + return nt; + } + s = args; + for(a = args; a != nil; a = a->right){ + if(f == nil) + break; + if(!tcompat(f->ty, a->left->ty, 0)){ + nerror(n, "%V: argument type mismatch: expected %T saw %Q", + n, f->ty, a->left); + return nt; + } + if(a->left->ty == tany) + a->left->ty = f->ty; + last = f; + f = f->next; + s = a; + } + if(f != nil){ + nerror(n, "%V: too few function arguments", n); + return nt; + } + + s->left = fold(s->left); + s = s->left; + if(s->ty != tstring || s->op != Oconst){ + nerror(args, "%V: format argument %Q is not a string constant", n, s); + return nt; + } + fmtcheck(n, s, a); + va = tuplefields(a); + if(last == nil) + nt->ids = va; + else + last->next = va; + return nt; +} + +/* + * check that a print style format string matches it's arguments + */ +void +fmtcheck(Node *f, Node *fmtarg, Node *va) +{ + Sym *fmt; + Rune r; + char *s, flags[10]; + int i, c, n1, n2, dot, verb, flag, ns, lens, fmtstart; + Type *ty; + + fmt = fmtarg->decl->sym; + s = fmt->name; + lens = fmt->len; + ns = 0; + while(ns < lens){ + c = s[ns++]; + if(c != '%') + continue; + + verb = -1; + n1 = 0; + n2 = 0; + dot = 0; + flag = 0; + fmtstart = ns - 1; + while(ns < lens && verb < 0){ + c = s[ns++]; + switch(c){ + default: + chartorune(&r, &s[ns-1]); + nerror(f, "%V: invalid character %C in format '%.*s'", f, r, ns-fmtstart, &s[fmtstart]); + return; + case '.': + if(dot){ + nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]); + return; + } + n1 = 1; + dot = 1; + continue; + case '*': + if(!n1) + n1 = 1; + else if(!n2 && dot) + n2 = 1; + else{ + nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]); + return; + } + if(va == nil){ + nerror(f, "%V: too few arguments for format '%.*s'", + f, ns-fmtstart, &s[fmtstart]); + return; + } + if(va->left->ty->kind != Tint){ + nerror(f, "%V: format '%.*s' incompatible with argument %Q", + f, ns-fmtstart, &s[fmtstart], va->left); + return; + } + va = va->right; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + while(ns < lens && s[ns] >= '0' && s[ns] <= '9') + ns++; + if(!n1) + n1 = 1; + else if(!n2 && dot) + n2 = 1; + else{ + nerror(f, "%V: invalid format '%.*s'", f, ns-fmtstart, &s[fmtstart]); + return; + } + break; + case '+': + case '-': + case '#': + case ',': + case 'b': + case 'u': + for(i = 0; i < flag; i++){ + if(flags[i] == c){ + nerror(f, "%V: duplicate flag %c in format '%.*s'", + f, c, ns-fmtstart, &s[fmtstart]); + return; + } + } + flags[flag++] = c; + if(flag >= sizeof flags){ + nerror(f, "too many flags in format '%.*s'", ns-fmtstart, &s[fmtstart]); + return; + } + break; + case '%': + case 'r': + verb = Tnone; + break; + case 'H': + verb = Tany; + break; + case 'c': + verb = Tint; + break; + case 'd': + case 'o': + case 'x': + case 'X': + verb = Tint; + for(i = 0; i < flag; i++){ + if(flags[i] == 'b'){ + verb = Tbig; + break; + } + } + break; + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + verb = Treal; + break; + case 's': + case 'q': + verb = Tstring; + break; + } + } + if(verb != Tnone){ + if(verb < 0){ + nerror(f, "%V: incomplete format '%.*s'", f, ns-fmtstart, &s[fmtstart]); + return; + } + if(va == nil){ + nerror(f, "%V: too few arguments for format '%.*s'", f, ns-fmtstart, &s[fmtstart]); + return; + } + ty = va->left->ty; + if(ty->kind == Texception) + ty = mkextuptype(ty); + switch(verb){ + case Tint: + switch(ty->kind){ + case Tstring: + case Tarray: + case Tref: + case Tchan: + case Tlist: + case Tmodule: + if(c == 'x' || c == 'X') + verb = ty->kind; + break; + } + break; + case Tany: + if(tattr[ty->kind].isptr) + verb = ty->kind; + break; + } + if(verb != ty->kind){ + nerror(f, "%V: format '%.*s' incompatible with argument %Q", f, ns-fmtstart, &s[fmtstart], va->left); + return; + } + va = va->right; + } + } + if(va != nil) + nerror(f, "%V: more arguments than formats", f); +} + +Decl* +tuplefields(Node *n) +{ + Decl *d, *h, **last; + + h = nil; + last = &h; + for(; n != nil; n = n->right){ + d = mkdecl(&n->left->src, Dfield, n->left->ty); + *last = d; + last = &d->next; + } + return h; +} + +/* + * make explicit indices for every element in an array initializer + * return the maximum index + * sort the indices and check for duplicates + */ +int +assignindices(Node *ar) +{ + Node *wild, *off, *size, *inits, *n, *q; + Type *t; + Case *c; + int amax, max, last, nlab, ok; + + amax = 0x7fffffff; + size = dupn(0, &nosrc, ar->left); + if(size->ty == tint){ + size = fold(size); + if(size->op == Oconst) + amax = size->val; + } + + inits = ar->right; + max = -1; + last = -1; + t = inits->left->ty; + wild = nil; + nlab = 0; + ok = 1; + for(n = inits; n != nil; n = n->right){ + if(!tcompat(t, n->left->ty, 0)){ + t = tparent(t, n->left->ty); + if(!tcompat(t, n->left->ty, 0)){ + nerror(n->left, "inconsistent types %T and %T and in array initializer", t, n->left->ty); + return -1; + } + else + inits->left->ty = t; + } + if(t == tany) + t = n->left->ty; + + /* + * make up an index if there isn't one + */ + if(n->left->left == nil) + n->left->left = mkn(Oseq, mkconst(&n->left->right->src, last + 1), nil); + + for(q = n->left->left; q != nil; q = q->right){ + off = q->left; + if(off->ty != tint){ + nerror(off, "array index %Q is not an int", off); + ok = 0; + continue; + } + off = fold(off); + switch(off->op){ + case Owild: + if(wild != nil) + nerror(off, "array index * duplicated on line %L", wild->src.start); + wild = off; + continue; + case Orange: + if(off->left->op != Oconst || off->right->op != Oconst){ + nerror(off, "range %V is not constant", off); + off = nil; + }else if(off->left->val < 0 || off->right->val >= amax){ + nerror(off, "array index %V out of bounds", off); + off = nil; + }else + last = off->right->val; + break; + case Oconst: + last = off->val; + if(off->val < 0 || off->val >= amax){ + nerror(off, "array index %V out of bounds", off); + off = nil; + } + break; + case Onothing: + /* get here from a syntax error */ + off = nil; + break; + default: + nerror(off, "array index %V is not constant", off); + off = nil; + break; + } + + nlab++; + if(off == nil){ + off = mkconst(&n->left->right->src, last); + ok = 0; + } + if(last > max) + max = last; + q->left = off; + } + } + + /* + * fix up types of nil elements + */ + for(n = inits; n != nil; n = n->right) + if(n->left->ty == tany) + n->left->ty = t; + + if(!ok) + return -1; + + + c = checklabels(inits, tint, nlab, "array index"); + t = mktype(&inits->src.start, &inits->src.stop, Tainit, nil, nil); + inits->ty = t; + t->cse = c; + + return max + 1; +} + +/* + * check the labels of a case statment + */ +void +casecheck(Node *cn, Type *ret) +{ + Node *n, *q, *wild, *left, *arg; + Type *t; + Case *c; + Ok rok; + int nlab, ok, op; + + rok = echeck(cn->left, 0, 0, nil); + cn->right = scheck(cn->right, ret, Sother); + if(!rok.ok) + return; + arg = cn->left; + + t = arg->ty; + if(t != tint && t != tbig && t != tstring){ + nerror(cn, "case argument %Q is not an int or big or string", arg); + return; + } + + wild = nil; + nlab= 0; + ok = 1; + for(n = cn->right; n != nil; n = n->right){ + q = n->left->left; + if(n->left->right->right == nil) + nwarn(q, "no body for case qualifier %V", q); + for(; q != nil; q = q->right){ + left = fold(q->left); + q->left = left; + switch(left->op){ + case Owild: + if(wild != nil) + nerror(left, "case qualifier * duplicated on line %L", wild->src.start); + wild = left; + break; + case Orange: + if(left->ty != t) + nerror(left, "case qualifier %Q clashes with %Q", left, arg); + else if(left->left->op != Oconst || left->right->op != Oconst){ + nerror(left, "case range %V is not constant", left); + ok = 0; + } + nlab++; + break; + default: + if(left->ty != t){ + nerror(left, "case qualifier %Q clashes with %Q", left, arg); + ok = 0; + }else if(left->op != Oconst){ + nerror(left, "case qualifier %V is not constant", left); + ok = 0; + } + nlab++; + break; + } + } + } + + if(!ok) + return; + + c = checklabels(cn->right, t, nlab, "case qualifier"); + op = Tcase; + if(t == tbig) + op = Tcasel; + else if(t == tstring) + op = Tcasec; + t = mktype(&cn->src.start, &cn->src.stop, op, nil, nil); + cn->ty = t; + t->cse = c; +} + +/* + * check the labels and bodies of a pick statment + */ +void +pickcheck(Node *n, Type *ret) +{ + Node *w, *arg, *qs, *q, *qt, *left, **tags; + Decl *id, *d; + Type *t, *argty; + Case *c; + Ok rok; + int ok, nlab; + + arg = n->left->right; + rok = echeck(arg, 0, 0, nil); + if(!rok.allok) + return; + t = arg->ty; + if(t->kind == Tref) + t = t->tof; + if(arg->ty->kind != Tref || t->kind != Tadt || t->tags == nil){ + nerror(arg, "pick argument %Q is not a ref adt with pick tags", arg); + return; + } + argty = usetype(mktype(&arg->ty->src.start, &arg->ty->src.stop, Tref, t, nil)); + + arg = n->left->left; + pushscope(nil, Sother); + dasdecl(arg); + arg->decl->ty = argty; + arg->ty = argty; + + tags = allocmem(t->decl->tag * sizeof *tags); + memset(tags, 0, t->decl->tag * sizeof *tags); + w = nil; + ok = 1; + nlab = 0; + for(qs = n->right; qs != nil; qs = qs->right){ + qt = nil; + for(q = qs->left->left; q != nil; q = q->right){ + left = q->left; + switch(left->op){ + case Owild: + /* left->ty = tnone; */ + left->ty = t; + if(w != nil) + nerror(left, "pick qualifier * duplicated on line %L", w->src.start); + w = left; + break; + case Oname: + id = namedot(t->tags, left->decl->sym); + if(id == nil){ + nerror(left, "pick qualifier %V is not a member of %Q", left, arg); + ok = 0; + continue; + } + + left->decl = id; + left->ty = id->ty; + + if(tags[id->tag] != nil){ + nerror(left, "pick qualifier %V duplicated on line %L", + left, tags[id->tag]->src.start); + ok = 0; + } + tags[id->tag] = left; + nlab++; + break; + default: + fatal("pickcheck can't handle %n", q); + break; + } + + if(qt == nil) + qt = left; + else if(!tequal(qt->ty, left->ty)) + nerror(left, "type clash in pick qualifiers %Q and %Q", qt, left); + } + + argty->tof = t; + if(qt != nil) + argty->tof = qt->ty; + qs->left->right = scheck(qs->left->right, ret, Sother); + if(qs->left->right == nil) + nwarn(qs->left->left, "no body for pick qualifier %V", qs->left->left); + } + argty->tof = t; + for(qs = n->right; qs != nil; qs = qs->right) + for(q = qs->left->left; q != nil; q = q->right) + q->left = fold(q->left); + + d = popscope(); + d->refs++; + if(d->next != nil) + fatal("pickcheck: installing more than one id"); + fndecls = appdecls(fndecls, d); + + if(!ok) + return; + + c = checklabels(n->right, tint, nlab, "pick qualifier"); + t = mktype(&n->src.start, &n->src.stop, Tcase, nil, nil); + n->ty = t; + t->cse = c; +} + +void +exccheck(Node *en, Type *ret) +{ + Decl *ed; + Node *n, *q, *wild, *left, *oinexcept; + Type *t, *qt; + Case *c; + int nlab, ok; + Ok rok; + char buf[32]; + static int nexc; + + pushscope(nil, Sother); + if(en->left == nil){ + seprint(buf, buf+sizeof(buf), ".ex%d", nexc++); + en->left = mkdeclname(&en->src, mkids(&en->src, enter(buf, 0), texception, nil)); + } + oinexcept = inexcept; + inexcept = en->left; + dasdecl(en->left); + en->left->ty = en->left->decl->ty = texception; + ed = en->left->decl; + /* en->right = scheck(en->right, ret, Sother); */ + t = tstring; + wild = nil; + nlab = 0; + ok = 1; + for(n = en->right; n != nil; n = n->right){ + qt = nil; + for(q = n->left->left; q != nil; q = q->right){ + left = q->left; + switch(left->op){ + case Owild: + left->ty = texception; + if(wild != nil) + nerror(left, "exception qualifier * duplicated on line %L", wild->src.start); + wild = left; + break; + case Orange: + left->ty = tnone; + nerror(left, "exception qualifier %V is illegal", left); + ok = 0; + break; + default: + rok = echeck(left, 0, 0, nil); + if(!rok.ok){ + ok = 0; + break; + } + left = q->left = fold(left); + if(left->ty != t && left->ty->kind != Texception){ + nerror(left, "exception qualifier %Q is not a string or exception", left); + ok = 0; + }else if(left->op != Oconst){ + nerror(left, "exception qualifier %V is not constant", left); + ok = 0; + } + else if(left->ty != t) + left->ty = mkextype(left->ty); + nlab++; + break; + } + + if(qt == nil) + qt = left->ty; + else if(!tequal(qt, left->ty)) + qt = texception; + } + + if(qt != nil) + ed->ty = qt; + n->left->right = scheck(n->left->right, ret, Sother); + if(n->left->right->right == nil) + nwarn(n->left->left, "no body for exception qualifier %V", n->left->left); + } + ed->ty = texception; + inexcept = oinexcept; + if(!ok) + return; + c = checklabels(en->right, texception, nlab, "exception qualifier"); + t = mktype(&en->src.start, &en->src.stop, Texcept, nil, nil); + en->ty = t; + t->cse = c; + ed = popscope(); + fndecls = appdecls(fndecls, ed); +} + +/* + * check array and case labels for validity + */ +Case * +checklabels(Node *inits, Type *ctype, int nlab, char *title) +{ + Node *n, *p, *q, *wild; + Label *labs, *aux; + Case *c; + char buf[256], buf1[256]; + int i, e; + + labs = allocmem(nlab * sizeof *labs); + i = 0; + wild = nil; + for(n = inits; n != nil; n = n->right){ + for(q = n->left->left; q != nil; q = q->right){ + switch(q->left->op){ + case Oconst: + labs[i].start = q->left; + labs[i].stop = q->left; + labs[i++].node = n->left; + break; + case Orange: + labs[i].start = q->left->left; + labs[i].stop = q->left->right; + labs[i++].node = n->left; + break; + case Owild: + wild = n->left; + break; + default: + fatal("bogus index in checklabels"); + break; + } + } + } + + if(i != nlab) + fatal("bad label count: %d then %d", nlab, i); + + aux = allocmem(nlab * sizeof *aux); + casesort(ctype, aux, labs, 0, nlab); + for(i = 0; i < nlab; i++){ + p = labs[i].stop; + if(casecmp(ctype, labs[i].start, p) > 0) + nerror(labs[i].start, "unmatchable %s %V", title, labs[i].node); + for(e = i + 1; e < nlab; e++){ + if(casecmp(ctype, labs[e].start, p) <= 0){ + eprintlist(buf, buf+sizeof(buf), labs[e].node->left, " or "); + eprintlist(buf1, buf1+sizeof(buf1), labs[e-1].node->left, " or "); + nerror(labs[e].start,"%s '%s' overlaps with '%s' on line %L", + title, buf, buf1, p->src.start); + } + + /* + * check for merging case labels + */ + if(ctype != tint + || labs[e].start->val != p->val+1 + || labs[e].node != labs[i].node) + break; + p = labs[e].stop; + } + if(e != i + 1){ + labs[i].stop = p; + memmove(&labs[i+1], &labs[e], (nlab-e) * sizeof *labs); + nlab -= e - (i + 1); + } + } + free(aux); + + c = allocmem(sizeof *c); + c->nlab = nlab; + c->nsnd = 0; + c->labs = labs; + c->wild = wild; + + return c; +} + +static int +matchcmp(Node *na, Node *nb) +{ + Sym *a, *b; + int sa, sb; + + a = na->decl->sym; + b = nb->decl->sym; + sa = a->len > 0 && a->name[a->len-1] == '*'; + sb = b->len > 0 && b->name[b->len-1] == '*'; + if(sa){ + if(sb){ + if(a->len == b->len) + return symcmp(a, b); + return b->len-a->len; + } + else + return 1; + } + else{ + if(sb) + return -1; + else{ + if(na->ty == tstring){ + if(nb->ty == tstring) + return symcmp(a, b); + else + return 1; + } + else{ + if(nb->ty == tstring) + return -1; + else + return symcmp(a, b); + } + } + } +} + +int +casecmp(Type *ty, Node *a, Node *b) +{ + if(ty == tint || ty == tbig){ + if(a->val < b->val) + return -1; + if(a->val > b->val) + return 1; + return 0; + } + if(ty == texception) + return matchcmp(a, b); + return symcmp(a->decl->sym, b->decl->sym); +} + +void +casesort(Type *t, Label *aux, Label *labs, int start, int stop) +{ + int n, top, mid, base; + + n = stop - start; + if(n <= 1) + return; + top = mid = start + n / 2; + + casesort(t, aux, labs, start, top); + casesort(t, aux, labs, mid, stop); + + /* + * merge together two sorted label arrays, yielding a sorted array + */ + n = 0; + base = start; + while(base < top && mid < stop){ + if(casecmp(t, labs[base].start, labs[mid].start) <= 0) + aux[n++] = labs[base++]; + else + aux[n++] = labs[mid++]; + } + if(base < top) + memmove(&aux[n], &labs[base], (top-base) * sizeof *aux); + else if(mid < stop) + memmove(&aux[n], &labs[mid], (stop-mid) * sizeof *aux); + memmove(&labs[start], &aux[0], (stop-start) * sizeof *labs); +} + +/* + * binary search for the label corresponding to a given value + */ +int +findlab(Type *ty, Node *v, Label *labs, int nlab) +{ + int l, r, m; + + if(nlab <= 1) + return 0; + l = 1; + r = nlab - 1; + while(l <= r){ + m = (r + l) / 2; + if(casecmp(ty, labs[m].start, v) <= 0) + l = m + 1; + else + r = m - 1; + } + m = l - 1; + if(casecmp(ty, labs[m].start, v) > 0 + || casecmp(ty, labs[m].stop, v) < 0) + fatal("findlab out of range"); + return m; +} + +void +altcheck(Node *an, Type *ret) +{ + Node *n, *q, *left, *op, *wild; + Case *c; + int ok, nsnd, nrcv; + + an->left = scheck(an->left, ret, Sother); + + ok = 1; + nsnd = 0; + nrcv = 0; + wild = nil; + for(n = an->left; n != nil; n = n->right){ + q = n->left->right->left; + if(n->left->right->right == nil) + nwarn(q, "no body for alt guard %V", q); + for(; q != nil; q = q->right){ + left = q->left; + switch(left->op){ + case Owild: + if(wild != nil) + nerror(left, "alt guard * duplicated on line %L", wild->src.start); + wild = left; + break; + case Orange: + nerror(left, "alt guard %V is illegal", left); + ok = 0; + break; + default: + op = hascomm(left); + if(op == nil){ + nerror(left, "alt guard %V has no communication", left); + ok = 0; + break; + } + if(op->op == Osnd) + nsnd++; + else + nrcv++; + break; + } + } + } + + if(!ok) + return; + + c = allocmem(sizeof *c); + c->nlab = nsnd + nrcv; + c->nsnd = nsnd; + c->wild = wild; + + an->ty = mktalt(c); +} + +Node* +hascomm(Node *n) +{ + Node *r; + + if(n == nil) + return nil; + if(n->op == Osnd || n->op == Orcv) + return n; + r = hascomm(n->left); + if(r != nil) + return r; + return hascomm(n->right); +} + +void +raisescheck(Type *t) +{ + Node *n, *nn; + Ok ok; + + if(t->kind != Tfn) + return; + n = t->u.eraises; + for(nn = n->left; nn != nil; nn = nn->right){ + ok = echeck(nn->left, 0, 0, nil); + if(ok.ok && nn->left->ty->kind != Texception) + nerror(n, "%V: illegal raises expression", nn->left); + } +} + +typedef struct Elist Elist; + +struct Elist{ + Decl *d; + Elist *nxt; +}; + +static Elist* +emerge(Elist *el1, Elist *el2) +{ + int f; + Elist *el, *nxt; + + for( ; el1 != nil; el1 = nxt){ + f = 0; + for(el = el2; el != nil; el = el->nxt){ + if(el1->d == el->d){ + f = 1; + break; + } + } + nxt = el1->nxt; + if(!f){ + el1->nxt = el2; + el2 = el1; + } + } + return el2; +} + +static Elist* +equals(Node *n) +{ + Node *q, *nn; + Elist *e, *el; + + el = nil; + for(q = n->left->left; q != nil; q = q->right){ + nn = q->left; + if(nn->op == Owild) + return nil; + if(nn->ty->kind != Texception) + continue; + e = (Elist*)malloc(sizeof(Elist)); + e->d = nn->decl; + e->nxt = el; + el = e; + } + return el; +} + +static int +caught(Decl *d, Node *n) +{ + Node *q, *nn; + + for(n = n->right; n != nil; n = n->right){ + for(q = n->left->left; q != nil; q = q->right){ + nn = q->left; + if(nn->op == Owild) + return 1; + if(nn->ty->kind != Texception) + continue; + if(d == nn->decl) + return 1; + } + } + return 0; +} + +static Elist* +raisecheck(Node *n, Elist *ql) +{ + int exc; + Node *e; + Elist *el, *nel, *nxt; + + if(n == nil) + return nil; + el = nil; + for(; n != nil; n = n->right){ + switch(n->op){ + case Oscope: + return raisecheck(n->right, ql); + case Olabel: + case Odo: + return raisecheck(n->right, ql); + case Oif: + case Ofor: + return emerge(raisecheck(n->right->left, ql), + raisecheck(n->right->right, ql)); + case Oalt: + case Ocase: + case Opick: + case Oexcept: + exc = n->op == Oexcept; + for(n = n->right; n != nil; n = n->right){ + ql = nil; + if(exc) + ql = equals(n); + el = emerge(raisecheck(n->left->right, ql), el); + } + return el; + case Oseq: + el = emerge(raisecheck(n->left, ql), el); + break; + case Oexstmt: + el = raisecheck(n->left, ql); + nel = nil; + for( ; el != nil; el = nxt){ + nxt = el->nxt; + if(!caught(el->d, n->right)){ + el->nxt = nel; + nel = el; + } + } + return emerge(nel, raisecheck(n->right, ql)); + case Oraise: + e = n->left; + if(e->ty && e->ty->kind == Texception){ + if(!e->ty->cons) + return ql; + if(e->op == Ocall) + e = e->left; + if(e->op == Omdot) + e = e->right; + if(e->op != Oname) + fatal("exception %n not a name", e); + el = (Elist*)malloc(sizeof(Elist)); + el->d = e->decl; + el->nxt = nil; + return el; + } + return nil; + default: + return nil; + } + } + return el; +} + +void +checkraises(Node *n) +{ + int f; + Decl *d; + Elist *e, *el; + Node *es, *nn; + + el = raisecheck(n->right, nil); + es = n->ty->u.eraises; + if(es != nil){ + for(nn = es->left; nn != nil; nn = nn->right){ + d = nn->left->decl; + f = 0; + for(e = el; e != nil; e = e->nxt){ + if(d == e->d){ + f = 1; + e->d = nil; + break; + } + } + if(!f) + nwarn(n, "function %V does not raise %s but declared", n->left, d->sym->name); + } + } + for(e = el; e != nil; e = e->nxt) + if(e->d != nil) + nwarn(n, "function %V raises %s but not declared", n->left, e->d->sym->name); +} + +/* sort all globals in modules now that we've finished with 'last' pointers + * and before any code generation + */ +void +gsort(Node *n) +{ + for(;;){ + if(n == nil) + return; + if(n->op != Oseq) + break; + gsort(n->left); + n = n->right; + } + if(n->op == Omoddecl && n->ty->ok & OKverify){ + n->ty->ids = namesort(n->ty->ids); + sizeids(n->ty->ids, 0); + } +} diff --git a/limbo/types.c b/limbo/types.c new file mode 100644 index 00000000..449b53d7 --- /dev/null +++ b/limbo/types.c @@ -0,0 +1,4745 @@ +#include "limbo.h" +#include "mp.h" +#include "libsec.h" + +char *kindname[Tend] = +{ + /* Tnone */ "no type", + /* Tadt */ "adt", + /* Tadtpick */ "adt", + /* Tarray */ "array", + /* Tbig */ "big", + /* Tbyte */ "byte", + /* Tchan */ "chan", + /* Treal */ "real", + /* Tfn */ "fn", + /* Tint */ "int", + /* Tlist */ "list", + /* Tmodule */ "module", + /* Tref */ "ref", + /* Tstring */ "string", + /* Ttuple */ "tuple", + /* Texception */ "exception", + /* Tfix */ "fixed point", + /* Tpoly */ "polymorphic", + + /* Tainit */ "array initializers", + /* Talt */ "alt channels", + /* Tany */ "polymorphic type", + /* Tarrow */ "->", + /* Tcase */ "case int labels", + /* Tcasel */ "case big labels", + /* Tcasec */ "case string labels", + /* Tdot */ ".", + /* Terror */ "type error", + /* Tgoto */ "goto labels", + /* Tid */ "id", + /* Tiface */ "module interface", + /* Texcept */ "exception handler table", + /* Tinst */ "instantiated type", +}; + +Tattr tattr[Tend] = +{ + /* isptr refable conable big vis */ + /* Tnone */ { 0, 0, 0, 0, 0, }, + /* Tadt */ { 0, 1, 1, 1, 1, }, + /* Tadtpick */ { 0, 1, 0, 1, 1, }, + /* Tarray */ { 1, 0, 0, 0, 1, }, + /* Tbig */ { 0, 0, 1, 1, 1, }, + /* Tbyte */ { 0, 0, 1, 0, 1, }, + /* Tchan */ { 1, 0, 0, 0, 1, }, + /* Treal */ { 0, 0, 1, 1, 1, }, + /* Tfn */ { 0, 1, 0, 0, 1, }, + /* Tint */ { 0, 0, 1, 0, 1, }, + /* Tlist */ { 1, 0, 0, 0, 1, }, + /* Tmodule */ { 1, 0, 0, 0, 1, }, + /* Tref */ { 1, 0, 0, 0, 1, }, + /* Tstring */ { 1, 0, 1, 0, 1, }, + /* Ttuple */ { 0, 1, 1, 1, 1, }, + /* Texception */ { 0, 0, 0, 1, 1, }, + /* Tfix */ { 0, 0, 1, 0, 1, }, + /* Tpoly */ { 1, 0, 0, 0, 1, }, + + /* Tainit */ { 0, 0, 0, 1, 0, }, + /* Talt */ { 0, 0, 0, 1, 0, }, + /* Tany */ { 1, 0, 0, 0, 0, }, + /* Tarrow */ { 0, 0, 0, 0, 1, }, + /* Tcase */ { 0, 0, 0, 1, 0, }, + /* Tcasel */ { 0, 0, 0, 1, 0, }, + /* Tcasec */ { 0, 0, 0, 1, 0, }, + /* Tdot */ { 0, 0, 0, 0, 1, }, + /* Terror */ { 0, 1, 1, 0, 0, }, + /* Tgoto */ { 0, 0, 0, 1, 0, }, + /* Tid */ { 0, 0, 0, 0, 1, }, + /* Tiface */ { 0, 0, 0, 1, 0, }, + /* Texcept */ { 0, 0, 0, 1, 0, }, + /* Tinst */ { 0, 1, 1, 1, 1, }, +}; + +static Teq *eqclass[Tend]; + +static Type ztype; +static int eqrec; +static int eqset; +static int tcomset; + +static int idcompat(Decl*, Decl*, int, int); +static int rtcompat(Type *t1, Type *t2, int any, int); +static int assumeteq(Type *t1, Type *t2); +static int assumetcom(Type *t1, Type *t2); +static int cleartcomrec(Type *t); +static int rtequal(Type*, Type*); +static int cleareqrec(Type*); +static int idequal(Decl*, Decl*, int, int*); +static int pyequal(Type*, Type*); +static int rtsign(Type*, uchar*, int, int); +static int clearrec(Type*); +static int idsign(Decl*, int, uchar*, int, int); +static int idsign1(Decl*, int, uchar*, int, int); +static int raisessign(Node *n, uchar *sig, int lensig, int spos); +static void ckfix(Type*, double); +static int fnunify(Type*, Type*, Tpair**, int); +static int rtunify(Type*, Type*, Tpair**, int); +static int idunify(Decl*, Decl*, Tpair**, int); +static int toccurs(Type*, Tpair**); +static int fncleareqrec(Type*, Type*); +static Type* comtype(Src*, Type*, Decl*); +static Type* duptype(Type*); +static int tpolys(Type*); + +static void +addtmap(Type *t1, Type *t2, Tpair **tpp) +{ + Tpair *tp; + + tp = allocmem(sizeof *tp); + tp->t1 = t1; + tp->t2 = t2; + tp->nxt = *tpp; + *tpp = tp; +} + +Type* +valtmap(Type *t, Tpair *tp) +{ + for( ; tp != nil; tp = tp->nxt) + if(tp->t1 == t) + return tp->t2; + return t; +} + +Typelist* +addtype(Type *t, Typelist *hd) +{ + Typelist *tl, *p; + + tl = allocmem(sizeof(*tl)); + tl->t = t; + tl->nxt = nil; + if(hd == nil) + return tl; + for(p = hd; p->nxt != nil; p = p->nxt) + ; + p->nxt = tl; + return hd; +} + +void +typeinit(void) +{ + Decl *id; + + anontupsym = enter(".tuple", 0); + + ztype.sbl = -1; + ztype.ok = 0; + ztype.rec = 0; + + tbig = mktype(&noline, &noline, Tbig, nil, nil); + tbig->size = IBY2LG; + tbig->align = IBY2LG; + tbig->ok = OKmask; + + tbyte = mktype(&noline, &noline, Tbyte, nil, nil); + tbyte->size = 1; + tbyte->align = 1; + tbyte->ok = OKmask; + + tint = mktype(&noline, &noline, Tint, nil, nil); + tint->size = IBY2WD; + tint->align = IBY2WD; + tint->ok = OKmask; + + treal = mktype(&noline, &noline, Treal, nil, nil); + treal->size = IBY2FT; + treal->align = IBY2FT; + treal->ok = OKmask; + + tstring = mktype(&noline, &noline, Tstring, nil, nil); + tstring->size = IBY2WD; + tstring->align = IBY2WD; + tstring->ok = OKmask; + + texception = mktype(&noline, &noline, Texception, nil, nil); + texception->size = IBY2WD; + texception->align = IBY2WD; + texception->ok = OKmask; + + tany = mktype(&noline, &noline, Tany, nil, nil); + tany->size = IBY2WD; + tany->align = IBY2WD; + tany->ok = OKmask; + + tnone = mktype(&noline, &noline, Tnone, nil, nil); + tnone->size = 0; + tnone->align = 1; + tnone->ok = OKmask; + + terror = mktype(&noline, &noline, Terror, nil, nil); + terror->size = 0; + terror->align = 1; + terror->ok = OKmask; + + tunknown = mktype(&noline, &noline, Terror, nil, nil); + tunknown->size = 0; + tunknown->align = 1; + tunknown->ok = OKmask; + + tfnptr = mktype(&noline, &noline, Ttuple, nil, nil); + id = tfnptr->ids = mkids(&nosrc, nil, tany, nil); + id->store = Dfield; + id->offset = 0; + id->sym = enter("t0", 0); + id->src = nosrc; + id = tfnptr->ids->next = mkids(&nosrc, nil, tint, nil); + id->store = Dfield; + id->offset = IBY2WD; + id->sym = enter("t1", 0); + id->src = nosrc; + + rtexception = mktype(&noline, &noline, Tref, texception, nil); + rtexception->size = IBY2WD; + rtexception->align = IBY2WD; + rtexception->ok = OKmask; +} + +void +typestart(void) +{ + descriptors = nil; + nfns = 0; + nadts = 0; + selfdecl = nil; + if(tfnptr->decl != nil) + tfnptr->decl->desc = nil; + + memset(eqclass, 0, sizeof eqclass); + + typebuiltin(mkids(&nosrc, enter("int", 0), nil, nil), tint); + typebuiltin(mkids(&nosrc, enter("big", 0), nil, nil), tbig); + typebuiltin(mkids(&nosrc, enter("byte", 0), nil, nil), tbyte); + typebuiltin(mkids(&nosrc, enter("string", 0), nil, nil), tstring); + typebuiltin(mkids(&nosrc, enter("real", 0), nil, nil), treal); +} + +Teq* +modclass(void) +{ + return eqclass[Tmodule]; +} + +Type* +mktype(Line *start, Line *stop, int kind, Type *tof, Decl *args) +{ + Type *t; + + t = allocmem(sizeof *t); + *t = ztype; + t->src.start = *start; + t->src.stop = *stop; + t->kind = kind; + t->tof = tof; + t->ids = args; + return t; +} + +Type* +mktalt(Case *c) +{ + Type *t; + char buf[32]; + static int nalt; + + t = mktype(&noline, &noline, Talt, nil, nil); + t->decl = mkdecl(&nosrc, Dtype, t); + seprint(buf, buf+sizeof(buf), ".a%d", nalt++); + t->decl->sym = enter(buf, 0); + t->cse = c; + return usetype(t); +} + +/* + * copy t and the top level of ids + */ +Type* +copytypeids(Type *t) +{ + Type *nt; + Decl *id, *new, *last; + + nt = allocmem(sizeof *nt); + *nt = *t; + last = nil; + for(id = t->ids; id != nil; id = id->next){ + new = allocmem(sizeof *id); + *new = *id; + if(last == nil) + nt->ids = new; + else + last->next = new; + last = new; + } + return nt; +} + +/* + * make each of the ids have type t + */ +Decl* +typeids(Decl *ids, Type *t) +{ + Decl *id; + + if(ids == nil) + return nil; + + ids->ty = t; + for(id = ids->next; id != nil; id = id->next){ + id->ty = t; + } + return ids; +} + +void +typebuiltin(Decl *d, Type *t) +{ + d->ty = t; + t->decl = d; + installids(Dtype, d); +} + +Node * +fielddecl(int store, Decl *ids) +{ + Node *n; + + n = mkn(Ofielddecl, nil, nil); + n->decl = ids; + for(; ids != nil; ids = ids->next) + ids->store = store; + return n; +} + +Node * +typedecl(Decl *ids, Type *t) +{ + Node *n; + + if(t->decl == nil) + t->decl = ids; + n = mkn(Otypedecl, nil, nil); + n->decl = ids; + n->ty = t; + for(; ids != nil; ids = ids->next) + ids->ty = t; + return n; +} + +void +typedecled(Node *n) +{ + installids(Dtype, n->decl); +} + +Node * +adtdecl(Decl *ids, Node *fields) +{ + Node *n; + Type *t; + + n = mkn(Oadtdecl, nil, nil); + t = mktype(&ids->src.start, &ids->src.stop, Tadt, nil, nil); + n->decl = ids; + n->left = fields; + n->ty = t; + t->decl = ids; + for(; ids != nil; ids = ids->next) + ids->ty = t; + return n; +} + +void +adtdecled(Node *n) +{ + Decl *d, *ids; + + d = n->ty->decl; + installids(Dtype, d); + if(n->ty->polys != nil){ + pushscope(nil, Sother); + installids(Dtype, n->ty->polys); + } + pushscope(nil, Sother); + fielddecled(n->left); + n->ty->ids = popscope(); + if(n->ty->polys != nil) + n->ty->polys = popscope(); + for(ids = n->ty->ids; ids != nil; ids = ids->next) + ids->dot = d; +} + +void +fielddecled(Node *n) +{ + for(; n != nil; n = n->right){ + switch(n->op){ + case Oseq: + fielddecled(n->left); + break; + case Oadtdecl: + adtdecled(n); + return; + case Otypedecl: + typedecled(n); + return; + case Ofielddecl: + installids(Dfield, n->decl); + return; + case Ocondecl: + condecled(n); + gdasdecl(n->right); + return; + case Oexdecl: + exdecled(n); + return; + case Opickdecl: + pickdecled(n); + return; + default: + fatal("can't deal with %O in fielddecled", n->op); + } + } +} + +int +pickdecled(Node *n) +{ + Decl *d; + int tag; + + if(n == nil) + return 0; + tag = pickdecled(n->left); + pushscope(nil, Sother); + fielddecled(n->right->right); + d = n->right->left->decl; + d->ty->ids = popscope(); + installids(Dtag, d); + for(; d != nil; d = d->next) + d->tag = tag++; + return tag; +} + +/* + * make the tuple type used to initialize adt t + */ +Type* +mkadtcon(Type *t) +{ + Decl *id, *new, *last; + Type *nt; + + nt = allocmem(sizeof *nt); + *nt = *t; + last = nil; + nt->ids = nil; + nt->kind = Ttuple; + for(id = t->ids; id != nil; id = id->next){ + if(id->store != Dfield) + continue; + new = allocmem(sizeof *id); + *new = *id; + new->cyc = 0; + if(last == nil) + nt->ids = new; + else + last->next = new; + last = new; + } + last->next = nil; + return nt; +} + +/* + * make the tuple type used to initialize t, + * an adt with pick fields tagged by tg + */ +Type* +mkadtpickcon(Type *t, Type *tgt) +{ + Decl *id, *new, *last; + Type *nt; + + last = mkids(&tgt->decl->src, nil, tint, nil); + last->store = Dfield; + nt = mktype(&t->src.start, &t->src.stop, Ttuple, nil, last); + for(id = t->ids; id != nil; id = id->next){ + if(id->store != Dfield) + continue; + new = allocmem(sizeof *id); + *new = *id; + new->cyc = 0; + last->next = new; + last = new; + } + for(id = tgt->ids; id != nil; id = id->next){ + if(id->store != Dfield) + continue; + new = allocmem(sizeof *id); + *new = *id; + new->cyc = 0; + last->next = new; + last = new; + } + last->next = nil; + return nt; +} + +/* + * make an identifier type + */ +Type* +mkidtype(Src *src, Sym *s) +{ + Type *t; + + t = mktype(&src->start, &src->stop, Tid, nil, nil); + if(s->unbound == nil){ + s->unbound = mkdecl(src, Dunbound, nil); + s->unbound->sym = s; + } + t->decl = s->unbound; + return t; +} + +/* + * make a qualified type for t->s + */ +Type* +mkarrowtype(Line *start, Line *stop, Type *t, Sym *s) +{ + Src src; + + src.start = *start; + src.stop = *stop; + t = mktype(start, stop, Tarrow, t, nil); + if(s->unbound == nil){ + s->unbound = mkdecl(&src, Dunbound, nil); + s->unbound->sym = s; + } + t->decl = s->unbound; + return t; +} + +/* + * make a qualified type for t.s + */ +Type* +mkdottype(Line *start, Line *stop, Type *t, Sym *s) +{ + Src src; + + src.start = *start; + src.stop = *stop; + t = mktype(start, stop, Tdot, t, nil); + if(s->unbound == nil){ + s->unbound = mkdecl(&src, Dunbound, nil); + s->unbound->sym = s; + } + t->decl = s->unbound; + return t; +} + +Type* +mkinsttype(Src* src, Type *tt, Typelist *tl) +{ + Type *t; + + t = mktype(&src->start, &src->stop, Tinst, tt, nil); + t->u.tlist = tl; + return t; +} + +/* + * look up the name f in the fields of a module, adt, or tuple + */ +Decl* +namedot(Decl *ids, Sym *s) +{ + for(; ids != nil; ids = ids->next) + if(ids->sym == s) + return ids; + return nil; +} + +/* + * complete the declaration of an adt + * methods frames get sized in module definition or during function definition + * place the methods at the end of the field list + */ +void +adtdefd(Type *t) +{ + Decl *d, *id, *next, *aux, *store, *auxhd, *tagnext; + int seentags; + + if(debug['x']) + print("adt %T defd\n", t); + d = t->decl; + tagnext = nil; + store = nil; + for(id = t->polys; id != nil; id = id->next){ + id->store = Dtype; + id->ty = verifytypes(id->ty, d, nil); + } + for(id = t->ids; id != nil; id = next){ + if(id->store == Dtag){ + if(t->tags != nil) + error(id->src.start, "only one set of pick fields allowed"); + tagnext = pickdefd(t, id); + next = tagnext; + if(store != nil) + store->next = next; + else + t->ids = next; + continue; + }else{ + id->dot = d; + next = id->next; + store = id; + } + } + aux = nil; + store = nil; + auxhd = nil; + seentags = 0; + for(id = t->ids; id != nil; id = next){ + if(id == tagnext) + seentags = 1; + + next = id->next; + id->dot = d; + id->ty = topvartype(verifytypes(id->ty, d, nil), id, 1, 1); + if(id->store == Dfield && id->ty->kind == Tfn) + id->store = Dfn; + if(id->store == Dfn || id->store == Dconst){ + if(store != nil) + store->next = next; + else + t->ids = next; + if(aux != nil) + aux->next = id; + else + auxhd = id; + aux = id; + }else{ + if(seentags) + error(id->src.start, "pick fields must be the last data fields in an adt"); + store = id; + } + } + if(aux != nil) + aux->next = nil; + if(store != nil) + store->next = auxhd; + else + t->ids = auxhd; + + for(id = t->tags; id != nil; id = id->next){ + id->ty = verifytypes(id->ty, d, nil); + if(id->ty->tof == nil) + id->ty->tof = mkadtpickcon(t, id->ty); + } +} + +/* + * assemble the data structure for an adt with a pick clause. + * since the scoping rules for adt pick fields are strange, + * we have a cutomized check for overlapping defitions. + */ +Decl* +pickdefd(Type *t, Decl *tg) +{ + Decl *id, *xid, *lasttg, *d; + Type *tt; + int tag; + + lasttg = nil; + d = t->decl; + t->tags = tg; + tag = 0; + while(tg != nil){ + tt = tg->ty; + if(tt->kind != Tadtpick || tg->tag != tag) + break; + tt->decl = tg; + lasttg = tg; + for(; tg != nil; tg = tg->next){ + if(tg->ty != tt) + break; + tag++; + lasttg = tg; + tg->dot = d; + } + for(id = tt->ids; id != nil; id = id->next){ + xid = namedot(t->ids, id->sym); + if(xid != nil) + error(id->src.start, "redeclaration of %K, previously declared as %k on line %L", + id, xid, xid->src.start); + id->dot = d; + } + } + if(lasttg == nil){ + error(t->src.start, "empty pick field declaration in %T", t); + t->tags = nil; + }else + lasttg->next = nil; + d->tag = tag; + return tg; +} + +Node* +moddecl(Decl *ids, Node *fields) +{ + Node *n; + Type *t; + + n = mkn(Omoddecl, mkn(Oseq, nil, nil), nil); + t = mktype(&ids->src.start, &ids->src.stop, Tmodule, nil, nil); + n->decl = ids; + n->left = fields; + n->ty = t; + return n; +} + +void +moddecled(Node *n) +{ + Decl *d, *ids, *im, *dot; + Type *t; + Sym *s; + char buf[StrSize]; + int isimp; + Dlist *dm, *dl; + + d = n->decl; + installids(Dtype, d); + isimp = 0; + for(ids = d; ids != nil; ids = ids->next){ + for(im = impmods; im != nil; im = im->next){ + if(ids->sym == im->sym){ + isimp = 1; + d = ids; + dm = malloc(sizeof(Dlist)); + dm->d = ids; + dm->next = nil; + if(impdecls == nil) + impdecls = dm; + else{ + for(dl = impdecls; dl->next != nil; dl = dl->next) + ; + dl->next = dm; + } + } + } + ids->ty = n->ty; + } + pushscope(nil, Sother); + fielddecled(n->left); + + d->ty->ids = popscope(); + + /* + * make the current module the -> parent of all contained decls-> + */ + for(ids = d->ty->ids; ids != nil; ids = ids->next) + ids->dot = d; + + t = d->ty; + t->decl = d; + if(debug['m']) + print("declare module %s\n", d->sym->name); + + /* + * add the iface declaration in case it's needed later + */ + seprint(buf, buf+sizeof(buf), ".m.%s", d->sym->name); + installids(Dglobal, mkids(&d->src, enter(buf, 0), tnone, nil)); + + if(isimp){ + for(ids = d->ty->ids; ids != nil; ids = ids->next){ + s = ids->sym; + if(s->decl != nil && s->decl->scope >= scope){ + dot = s->decl->dot; + if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 0)) + continue; + redecl(ids); + ids->old = s->decl->old; + }else + ids->old = s->decl; + s->decl = ids; + ids->scope = scope; + } + } +} + +/* + * for each module in id, + * link by field ext all of the decls for + * functions needed in external linkage table + * collect globals and make a tuple for all of them + */ +Type* +mkiface(Decl *m) +{ + Decl *iface, *last, *globals, *glast, *id, *d; + Type *t; + char buf[StrSize]; + + iface = last = allocmem(sizeof(Decl)); + globals = glast = mkdecl(&m->src, Dglobal, mktype(&m->src.start, &m->src.stop, Tadt, nil, nil)); + for(id = m->ty->ids; id != nil; id = id->next){ + switch(id->store){ + case Dglobal: + glast = glast->next = dupdecl(id); + id->iface = globals; + glast->iface = id; + break; + case Dfn: + id->iface = last = last->next = dupdecl(id); + last->iface = id; + break; + case Dtype: + if(id->ty->kind != Tadt) + break; + for(d = id->ty->ids; d != nil; d = d->next){ + if(d->store == Dfn){ + d->iface = last = last->next = dupdecl(d); + last->iface = d; + } + } + break; + } + } + last->next = nil; + iface = namesort(iface->next); + + if(globals->next != nil){ + glast->next = nil; + globals->ty->ids = namesort(globals->next); + globals->ty->decl = globals; + globals->sym = enter(".mp", 0); + globals->dot = m; + globals->next = iface; + iface = globals; + } + + /* + * make the interface type and install an identifier for it + * the iface has a ref count if it is loaded + */ + t = mktype(&m->src.start, &m->src.stop, Tiface, nil, iface); + seprint(buf, buf+sizeof(buf), ".m.%s", m->sym->name); + id = enter(buf, 0)->decl; + t->decl = id; + id->ty = t; + + /* + * dummy node so the interface is initialized + */ + id->init = mkn(Onothing, nil, nil); + id->init->ty = t; + id->init->decl = id; + return t; +} + +void +joiniface(Type *mt, Type *t) +{ + Decl *id, *d, *iface, *globals; + + iface = t->ids; + globals = iface; + if(iface != nil && iface->store == Dglobal) + iface = iface->next; + for(id = mt->tof->ids; id != nil; id = id->next){ + switch(id->store){ + case Dglobal: + for(d = id->ty->ids; d != nil; d = d->next) + d->iface->iface = globals; + break; + case Dfn: + id->iface->iface = iface; + iface = iface->next; + break; + default: + fatal("unknown store %k in joiniface", id); + break; + } + } + if(iface != nil) + fatal("join iface not matched"); + mt->tof = t; +} + +void +addiface(Decl *m, Decl *d) +{ + Type *t; + Decl *id, *last, *dd, *lastorig; + Dlist *dl; + + if(d == nil || !local(d)) + return; + modrefable(d->ty); + if(m == nil){ + if(impdecls->next != nil) + for(dl = impdecls; dl != nil; dl = dl->next) + if(dl->d->ty->tof != impdecl->ty->tof) /* impdecl last */ + addiface(dl->d, d); + addiface(impdecl, d); + return; + } + t = m->ty->tof; + last = nil; + lastorig = nil; + for(id = t->ids; id != nil; id = id->next){ + if(d == id || d == id->iface) + return; + last = id; + if(id->tag == 0) + lastorig = id; + } + dd = dupdecl(d); + if(d->dot == nil) + d->dot = dd->dot = m; + d->iface = dd; + dd->iface = d; +if(debug['v']) print("addiface %p %p\n", d, dd); + if(last == nil) + t->ids = dd; + else + last->next = dd; + dd->tag = 1; /* mark so not signed */ + if(lastorig == nil) + t->ids = namesort(t->ids); + else + lastorig->next = namesort(lastorig->next); +} + +/* + * eliminate unused declarations from interfaces + * label offset within interface + */ +void +narrowmods(void) +{ + Teq *eq; + Decl *id, *last; + Type *t; + long offset; + + for(eq = modclass(); eq != nil; eq = eq->eq){ + t = eq->ty->tof; + + if(t->linkall == 0){ + last = nil; + for(id = t->ids; id != nil; id = id->next){ + if(id->refs == 0){ + if(last == nil) + t->ids = id->next; + else + last->next = id->next; + }else + last = id; + } + + /* + * need to resize smaller interfaces + */ + resizetype(t); + } + + offset = 0; + for(id = t->ids; id != nil; id = id->next) + id->offset = offset++; + + /* + * rathole to stuff number of entries in interface + */ + t->decl->init->val = offset; + } +} + +/* + * check to see if any data field of module m if referenced. + * if so, mark all data in m + */ +void +moddataref(void) +{ + Teq *eq; + Decl *id; + + for(eq = modclass(); eq != nil; eq = eq->eq){ + id = eq->ty->tof->ids; + if(id != nil && id->store == Dglobal && id->refs) + for(id = eq->ty->ids; id != nil; id = id->next) + if(id->store == Dglobal) + modrefable(id->ty); + } +} + +/* + * move the global declarations in interface to the front + */ +Decl* +modglobals(Decl *mod, Decl *globals) +{ + Decl *id, *head, *last; + + /* + * make a copy of all the global declarations + * used for making a type descriptor for globals ONLY + * note we now have two declarations for the same variables, + * which is apt to cause problems if code changes + * + * here we fix up the offsets for the real declarations + */ + idoffsets(mod->ty->ids, 0, 1); + + last = head = allocmem(sizeof(Decl)); + for(id = mod->ty->ids; id != nil; id = id->next) + if(id->store == Dglobal) + last = last->next = dupdecl(id); + + last->next = globals; + return head->next; +} + +/* + * snap all id type names to the actual type + * check that all types are completely defined + * verify that the types look ok + */ +Type* +validtype(Type *t, Decl *inadt) +{ + if(t == nil) + return t; + bindtypes(t); + t = verifytypes(t, inadt, nil); + cycsizetype(t); + teqclass(t); + return t; +} + +Type* +usetype(Type *t) +{ + if(t == nil) + return t; + t = validtype(t, nil); + reftype(t); + return t; +} + +Type* +internaltype(Type *t) +{ + bindtypes(t); + t->ok = OKverify; + sizetype(t); + t->ok = OKmask; + return t; +} + +/* + * checks that t is a valid top-level type + */ +Type* +topvartype(Type *t, Decl *id, int tyok, int polyok) +{ + if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick) + error(id->src.start, "cannot declare %s with type %T", id->sym->name, t); + if(!tyok && t->kind == Tfn) + error(id->src.start, "cannot declare %s to be a function", id->sym->name); + if(!polyok && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t)) + error(id->src.start, "cannot declare %s of a polymorphic type", id->sym->name); + return t; +} + +Type* +toptype(Src *src, Type *t) +{ + if(t->kind == Tadt && t->tags != nil || t->kind == Tadtpick) + error(src->start, "%T, an adt with pick fields, must be used with ref", t); + if(t->kind == Tfn) + error(src->start, "data cannot have a fn type like %T", t); + return t; +} + +static Type* +comtype(Src *src, Type *t, Decl* adtd) +{ + if(adtd == nil && (t->kind == Tadt || t->kind == Tadtpick) && ispolyadt(t)) + error(src->start, "polymorphic type %T illegal here", t); + return t; +} + +void +usedty(Type *t) +{ + if(t != nil && (t->ok | OKmodref) != OKmask) + fatal("used ty %t %2.2ux", t, t->ok); +} + +void +bindtypes(Type *t) +{ + Decl *id; + Typelist *tl; + + if(t == nil) + return; + if((t->ok & OKbind) == OKbind) + return; + t->ok |= OKbind; + switch(t->kind){ + case Tadt: + if(t->polys != nil){ + pushscope(nil, Sother); + installids(Dtype, t->polys); + } + if(t->val != nil) + mergepolydecs(t); + if(t->polys != nil){ + popscope(); + for(id = t->polys; id != nil; id = id->next) + bindtypes(id->ty); + } + break; + case Tadtpick: + case Tmodule: + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tiface: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Texcept: + case Tfix: + case Tpoly: + break; + case Tarray: + case Tarrow: + case Tchan: + case Tdot: + case Tlist: + case Tref: + bindtypes(t->tof); + break; + case Tid: + id = t->decl->sym->decl; + if(id == nil) + id = undefed(&t->src, t->decl->sym); + /* save a little space */ + id->sym->unbound = nil; + t->decl = id; + break; + case Ttuple: + case Texception: + for(id = t->ids; id != nil; id = id->next) + bindtypes(id->ty); + break; + case Tfn: + if(t->polys != nil){ + pushscope(nil, Sother); + installids(Dtype, t->polys); + } + for(id = t->ids; id != nil; id = id->next) + bindtypes(id->ty); + bindtypes(t->tof); + if(t->val != nil) + mergepolydecs(t); + if(t->polys != nil){ + popscope(); + for(id = t->polys; id != nil; id = id->next) + bindtypes(id->ty); + } + break; + case Tinst: + bindtypes(t->tof); + for(tl = t->u.tlist; tl != nil; tl = tl->nxt) + bindtypes(tl->t); + break; + default: + fatal("bindtypes: unknown type kind %d", t->kind); + } +} + +/* + * walk the type checking for validity + */ +Type* +verifytypes(Type *t, Decl *adtt, Decl *poly) +{ + Node *n; + Decl *id, *id1, *last; + char buf[32]; + int i, cyc; + Ok ok, ok1; + double max; + Typelist *tl; + + if(t == nil) + return nil; + if((t->ok & OKverify) == OKverify) + return t; + t->ok |= OKverify; +if((t->ok & (OKverify|OKbind)) != (OKverify|OKbind)) +fatal("verifytypes bogus ok for %t", t); + cyc = t->flags&CYCLIC; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tiface: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Texcept: + break; + case Tfix: + n = t->val; + max = 0.0; + if(n->op == Oseq){ + ok = echeck(n->left, 0, 0, n); + ok1 = echeck(n->right, 0, 0, n); + if(!ok.ok || !ok1.ok) + return terror; + if(n->left->ty != treal || n->right->ty != treal){ + error(t->src.start, "fixed point scale/maximum not real"); + return terror; + } + n->right = fold(n->right); + if(n->right->op != Oconst){ + error(t->src.start, "fixed point maximum not constant"); + return terror; + } + if((max = n->right->rval) <= 0){ + error(t->src.start, "non-positive fixed point maximum"); + return terror; + } + n = n->left; + } + else{ + ok = echeck(n, 0, 0, nil); + if(!ok.ok) + return terror; + if(n->ty != treal){ + error(t->src.start, "fixed point scale not real"); + return terror; + } + } + n = t->val = fold(n); + if(n->op != Oconst){ + error(t->src.start, "fixed point scale not constant"); + return terror; + } + if(n->rval <= 0){ + error(t->src.start, "non-positive fixed point scale"); + return terror; + } + ckfix(t, max); + break; + case Tref: + t->tof = comtype(&t->src, verifytypes(t->tof, adtt, nil), adtt); + if(t->tof != nil && !tattr[t->tof->kind].refable){ + error(t->src.start, "cannot have a ref %T", t->tof); + return terror; + } + if(0 && t->tof->kind == Tfn && t->tof->ids != nil && t->tof->ids->implicit) + error(t->src.start, "function references cannot have a self argument"); + if(0 && t->tof->kind == Tfn && t->polys != nil) + error(t->src.start, "function references cannot be polymorphic"); + break; + case Tchan: + case Tarray: + case Tlist: + t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt); + break; + case Tid: + t->ok &= ~OKverify; + t = verifytypes(idtype(t), adtt, nil); + break; + case Tarrow: + t->ok &= ~OKverify; + t = verifytypes(arrowtype(t, adtt), adtt, nil); + break; + case Tdot: + /* + * verify the parent adt & lookup the tag fields + */ + t->ok &= ~OKverify; + t = verifytypes(dottype(t, adtt), adtt, nil); + break; + case Tadt: + /* + * this is where Tadt may get tag fields added + */ + adtdefd(t); + break; + case Tadtpick: + for(id = t->ids; id != nil; id = id->next){ + id->ty = topvartype(verifytypes(id->ty, id->dot, nil), id, 0, 1); + if(id->store == Dconst) + error(t->src.start, "pick fields cannot be a con like %s", id->sym->name); + } + verifytypes(t->decl->dot->ty, nil, nil); + break; + case Tmodule: + for(id = t->ids; id != nil; id = id->next){ + id->ty = verifytypes(id->ty, nil, nil); + if(id->store == Dglobal && id->ty->kind == Tfn) + id->store = Dfn; + if(id->store != Dtype && id->store != Dfn) + topvartype(id->ty, id, 0, 0); + } + break; + case Ttuple: + case Texception: + if(t->decl == nil){ + t->decl = mkdecl(&t->src, Dtype, t); + t->decl->sym = enter(".tuple", 0); + } + i = 0; + for(id = t->ids; id != nil; id = id->next){ + id->store = Dfield; + if(id->sym == nil){ + seprint(buf, buf+sizeof(buf), "t%d", i); + id->sym = enter(buf, 0); + } + i++; + id->ty = toptype(&id->src, verifytypes(id->ty, adtt, nil)); + /* id->ty = comtype(&id->src, toptype(&id->src, verifytypes(id->ty, adtt, nil)), adtt); */ + } + break; + case Tfn: + last = nil; + for(id = t->ids; id != nil; id = id->next){ + id->store = Darg; + id->ty = topvartype(verifytypes(id->ty, adtt, nil), id, 0, 1); + if(id->implicit){ + Decl *selfd; + + selfd = poly ? poly : adtt; + if(selfd == nil) + error(t->src.start, "function is not a member of an adt, so can't use self"); + else if(id != t->ids) + error(id->src.start, "only the first argument can use self"); + else if(id->ty != selfd->ty && (id->ty->kind != Tref || id->ty->tof != selfd->ty)) + error(id->src.start, "self argument's type must be %s or ref %s", + selfd->sym->name, selfd->sym->name); + } + last = id; + } + for(id = t->polys; id != nil; id = id->next){ + if(adtt != nil){ + for(id1 = adtt->ty->polys; id1 != nil; id1 = id1->next){ + if(id1->sym == id->sym) + id->ty = id1->ty; + } + } + id->store = Dtype; + id->ty = verifytypes(id->ty, adtt, nil); + } + t->tof = comtype(&t->src, toptype(&t->src, verifytypes(t->tof, adtt, nil)), adtt); + if(t->varargs && (last == nil || last->ty != tstring)) + error(t->src.start, "variable arguments must be preceded by a string"); + if(t->varargs && t->polys != nil) + error(t->src.start, "polymorphic functions must not have variable arguments"); + break; + case Tpoly: + for(id = t->ids; id != nil; id = id->next){ + id->store = Dfn; + id->ty = verifytypes(id->ty, adtt, t->decl); + } + break; + case Tinst: + t->ok &= ~OKverify; + t->tof = verifytypes(t->tof, adtt, nil); + for(tl = t->u.tlist; tl != nil; tl = tl->nxt) + tl->t = verifytypes(tl->t, adtt, nil); + t = verifytypes(insttype(t, adtt, nil), adtt, nil); + break; + default: + fatal("verifytypes: unknown type kind %d", t->kind); + } + if(cyc) + t->flags |= CYCLIC; + return t; +} + +/* + * resolve an id type + */ +Type* +idtype(Type *t) +{ + Decl *id; + Type *tt; + + id = t->decl; + if(id->store == Dunbound) + fatal("idtype: unbound decl"); + tt = id->ty; + if(id->store != Dtype && id->store != Dtag){ + if(id->store == Dundef){ + id->store = Dwundef; + error(t->src.start, "%s is not declared", id->sym->name); + }else if(id->store == Dimport){ + id->store = Dwundef; + error(t->src.start, "%s's type cannot be determined", id->sym->name); + }else if(id->store != Dwundef) + error(t->src.start, "%s is not a type", id->sym->name); + return terror; + } + if(tt == nil){ + error(t->src.start, "%t not fully defined", t); + return terror; + } + return tt; +} + +/* + * resolve a -> qualified type + */ +Type* +arrowtype(Type *t, Decl *adtt) +{ + Type *tt; + Decl *id; + + id = t->decl; + if(id->ty != nil){ + if(id->store == Dunbound) + fatal("arrowtype: unbound decl has a type"); + return id->ty; + } + + /* + * special hack to allow module variables to derive other types + */ + tt = t->tof; + if(tt->kind == Tid){ + id = tt->decl; + if(id->store == Dunbound) + fatal("arrowtype: Tid's decl unbound"); + if(id->store == Dimport){ + id->store = Dwundef; + error(t->src.start, "%s's type cannot be determined", id->sym->name); + return terror; + } + + /* + * forward references to module variables can't be resolved + */ + if(id->store != Dtype && !(id->ty->ok & OKbind)){ + error(t->src.start, "%s's type cannot be determined", id->sym->name); + return terror; + } + + if(id->store == Dwundef) + return terror; + tt = id->ty = verifytypes(id->ty, adtt, nil); + if(tt == nil){ + error(t->tof->src.start, "%T is not a module", t->tof); + return terror; + } + }else + tt = verifytypes(t->tof, adtt, nil); + t->tof = tt; + if(tt == terror) + return terror; + if(tt->kind != Tmodule){ + error(t->src.start, "%T is not a module", tt); + return terror; + } + id = namedot(tt->ids, t->decl->sym); + if(id == nil){ + error(t->src.start, "%s is not a member of %T", t->decl->sym->name, tt); + return terror; + } + if(id->store == Dtype && id->ty != nil){ + t->decl = id; + return id->ty; + } + error(t->src.start, "%T is not a type", t); + return terror; +} + +/* + * resolve a . qualified type + */ +Type* +dottype(Type *t, Decl *adtt) +{ + Type *tt; + Decl *id; + + if(t->decl->ty != nil){ + if(t->decl->store == Dunbound) + fatal("dottype: unbound decl has a type"); + return t->decl->ty; + } + t->tof = tt = verifytypes(t->tof, adtt, nil); + if(tt == terror) + return terror; + if(tt->kind != Tadt){ + error(t->src.start, "%T is not an adt", tt); + return terror; + } + id = namedot(tt->tags, t->decl->sym); + if(id != nil && id->ty != nil){ + t->decl = id; + return id->ty; + } + error(t->src.start, "%s is not a pick tag of %T", t->decl->sym->name, tt); + return terror; +} + +Type* +insttype(Type *t, Decl *adtt, Tpair **tp) +{ + Type *tt; + Typelist *tl; + Decl *ids; + Tpair *tp1, *tp2; + Src src; + + src = t->src; + if(tp == nil){ + tp2 = nil; + tp = &tp2; + } + if(t->tof->kind != Tadt && t->tof->kind != Tadtpick){ + error(src.start, "%T is not an adt", t->tof); + return terror; + } + if(t->tof->kind == Tadt) + ids = t->tof->polys; + else + ids = t->tof->decl->dot->ty->polys; + if(ids == nil){ + error(src.start, "%T is not a polymorphic adt", t->tof); + return terror; + } + for(tl = t->u.tlist; tl != nil && ids != nil; tl = tl->nxt, ids = ids->next){ + tt = tl->t; + if(!tattr[tt->kind].isptr){ + error(src.start, "%T is not a pointer type", tt); + return terror; + } + unifysrc = src; + if(!tunify(ids->ty, tt, &tp1)){ + error(src.start, "type %T does not match %T", tt, ids->ty); + return terror; + } + /* usetype(tt); */ + tt = verifytypes(tt, adtt, nil); + addtmap(ids->ty, tt, tp); + } + if(tl != nil){ + error(src.start, "too many actual types in instantiation"); + return terror; + } + if(ids != nil){ + error(src.start, "too few actual types in instantiation"); + return terror; + } + tp1 = *tp; + tt = t->tof; + t = expandtype(tt, t, adtt, tp); + if(t == tt && adtt == nil) + t = duptype(t); + if(t != tt){ + t->u.tmap = tp1; + if(debug['w']){ + print("tmap for %T: ", t); + for( ; tp1!=nil; tp1=tp1->nxt) + print("%T -> %T ", tp1->t1, tp1->t2); + print("\n"); + } + } + t->src = src; + return t; +} + +/* + * walk a type, putting all adts, modules, and tuples into equivalence classes + */ +void +teqclass(Type *t) +{ + Decl *id, *tg; + Teq *teq; + + if(t == nil || (t->ok & OKclass) == OKclass) + return; + t->ok |= OKclass; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tiface: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Texcept: + case Tfix: + case Tpoly: + return; + case Tref: + teqclass(t->tof); + return; + case Tchan: + case Tarray: + case Tlist: + teqclass(t->tof); + if(!debug['Z']) + return; + break; + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + for(id = t->ids; id != nil; id = id->next) + teqclass(id->ty); + for(tg = t->tags; tg != nil; tg = tg->next) + teqclass(tg->ty); + for(id = t->polys; id != nil; id = id->next) + teqclass(id->ty); + break; + case Tmodule: + t->tof = mkiface(t->decl); + for(id = t->ids; id != nil; id = id->next) + teqclass(id->ty); + break; + case Tfn: + for(id = t->ids; id != nil; id = id->next) + teqclass(id->ty); + for(id = t->polys; id != nil; id = id->next) + teqclass(id->ty); + teqclass(t->tof); + return; + default: + fatal("teqclass: unknown type kind %d", t->kind); + return; + } + + /* + * find an equivalent type + * stupid linear lookup could be made faster + */ + if((t->ok & OKsized) != OKsized) + fatal("eqclass type not sized: %t", t); + + for(teq = eqclass[t->kind]; teq != nil; teq = teq->eq){ + if(t->size == teq->ty->size && tequal(t, teq->ty)){ + t->eq = teq; + if(t->kind == Tmodule) + joiniface(t, t->eq->ty->tof); + return; + } + } + + /* + * if no equiv type, make one + */ + t->eq = allocmem(sizeof(Teq)); + t->eq->id = 0; + t->eq->ty = t; + t->eq->eq = eqclass[t->kind]; + eqclass[t->kind] = t->eq; +} + +/* + * record that we've used the type + * using a type uses all types reachable from that type + */ +void +reftype(Type *t) +{ + Decl *id, *tg; + + if(t == nil || (t->ok & OKref) == OKref) + return; + t->ok |= OKref; + if(t->decl != nil && t->decl->refs == 0) + t->decl->refs++; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tiface: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Texcept: + case Tfix: + case Tpoly: + break; + case Tref: + case Tchan: + case Tarray: + case Tlist: + if(t->decl != nil){ + if(nadts >= lenadts){ + lenadts = nadts + 32; + adts = reallocmem(adts, lenadts * sizeof *adts); + } + adts[nadts++] = t->decl; + } + reftype(t->tof); + break; + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + if(t->kind == Tadt || t->kind == Ttuple && t->decl->sym != anontupsym){ + if(nadts >= lenadts){ + lenadts = nadts + 32; + adts = reallocmem(adts, lenadts * sizeof *adts); + } + adts[nadts++] = t->decl; + } + for(id = t->ids; id != nil; id = id->next) + if(id->store != Dfn) + reftype(id->ty); + for(tg = t->tags; tg != nil; tg = tg->next) + reftype(tg->ty); + for(id = t->polys; id != nil; id = id->next) + reftype(id->ty); + if(t->kind == Tadtpick) + reftype(t->decl->dot->ty); + break; + case Tmodule: + /* + * a module's elements should get used individually + * but do the globals for any sbl file + */ + if(bsym != nil) + for(id = t->ids; id != nil; id = id->next) + if(id->store == Dglobal) + reftype(id->ty); + break; + case Tfn: + for(id = t->ids; id != nil; id = id->next) + reftype(id->ty); + for(id = t->polys; id != nil; id = id->next) + reftype(id->ty); + reftype(t->tof); + break; + default: + fatal("reftype: unknown type kind %d", t->kind); + break; + } +} + +/* + * check all reachable types for cycles and illegal forward references + * find the size of all the types + */ +void +cycsizetype(Type *t) +{ + Decl *id, *tg; + + if(t == nil || (t->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized)) + return; + t->ok |= OKcycsize; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tiface: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Texcept: + case Tfix: + case Tpoly: + t->ok |= OKcyc; + sizetype(t); + break; + case Tref: + case Tchan: + case Tarray: + case Tlist: + cyctype(t); + sizetype(t); + cycsizetype(t->tof); + break; + case Tadt: + case Ttuple: + case Texception: + cyctype(t); + sizetype(t); + for(id = t->ids; id != nil; id = id->next) + cycsizetype(id->ty); + for(tg = t->tags; tg != nil; tg = tg->next){ + if((tg->ty->ok & (OKcycsize|OKcyc|OKsized)) == (OKcycsize|OKcyc|OKsized)) + continue; + tg->ty->ok |= (OKcycsize|OKcyc|OKsized); + for(id = tg->ty->ids; id != nil; id = id->next) + cycsizetype(id->ty); + } + for(id = t->polys; id != nil; id = id->next) + cycsizetype(id->ty); + break; + case Tadtpick: + t->ok &= ~OKcycsize; + cycsizetype(t->decl->dot->ty); + break; + case Tmodule: + cyctype(t); + sizetype(t); + for(id = t->ids; id != nil; id = id->next) + cycsizetype(id->ty); + sizeids(t->ids, 0); + break; + case Tfn: + cyctype(t); + sizetype(t); + for(id = t->ids; id != nil; id = id->next) + cycsizetype(id->ty); + for(id = t->polys; id != nil; id = id->next) + cycsizetype(id->ty); + cycsizetype(t->tof); + sizeids(t->ids, MaxTemp); + break; + default: + fatal("cycsizetype: unknown type kind %d", t->kind); + break; + } +} + +/* check for circularity in type declarations + * - has to be called before verifytypes + */ +void +tcycle(Type *t) +{ + Decl *id; + Type *tt; + Typelist *tl; + + if(t == nil) + return; + switch(t->kind){ + default: + break; + case Tchan: + case Tarray: + case Tref: + case Tlist: + case Tdot: + tcycle(t->tof); + break; + case Tfn: + case Ttuple: + tcycle(t->tof); + for(id = t->ids; id != nil; id = id->next) + tcycle(id->ty); + break; + case Tarrow: + if(t->rec&TRvis){ + error(t->src.start, "circularity in definition of %T", t); + *t = *terror; /* break the cycle */ + return; + } + tt = t->tof; + t->rec |= TRvis; + tcycle(tt); + if(tt->kind == Tid) + tt = tt->decl->ty; + id = namedot(tt->ids, t->decl->sym); + if(id != nil) + tcycle(id->ty); + t->rec &= ~TRvis; + break; + case Tid: + if(t->rec&TRvis){ + error(t->src.start, "circularity in definition of %T", t); + *t = *terror; /* break the cycle */ + return; + } + t->rec |= TRvis; + tcycle(t->decl->ty); + t->rec &= ~TRvis; + break; + case Tinst: + tcycle(t->tof); + for(tl = t->u.tlist; tl != nil; tl = tl->nxt) + tcycle(tl->t); + break; + } +} + +/* + * marks for checking for arcs + */ +enum +{ + ArcValue = 1 << 0, + ArcList = 1 << 1, + ArcArray = 1 << 2, + ArcRef = 1 << 3, + ArcCyc = 1 << 4, /* cycle found */ + ArcPolycyc = 1 << 5, +}; + +void +cyctype(Type *t) +{ + Decl *id, *tg; + + if((t->ok & OKcyc) == OKcyc) + return; + t->ok |= OKcyc; + t->rec |= TRcyc; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tfn: + case Tchan: + case Tarray: + case Tref: + case Tlist: + case Tfix: + case Tpoly: + break; + case Tadt: + case Tmodule: + case Ttuple: + case Texception: + for(id = t->ids; id != nil; id = id->next) + cycfield(t, id); + for(tg = t->tags; tg != nil; tg = tg->next){ + if((tg->ty->ok & OKcyc) == OKcyc) + continue; + tg->ty->ok |= OKcyc; + for(id = tg->ty->ids; id != nil; id = id->next) + cycfield(t, id); + } + break; + default: + fatal("checktype: unknown type kind %d", t->kind); + break; + } + t->rec &= ~TRcyc; +} + +void +cycfield(Type *base, Decl *id) +{ + int arc; + + if(!storespace[id->store]) + return; + arc = cycarc(base, id->ty); + + if((arc & (ArcCyc|ArcValue)) == (ArcCyc|ArcValue)){ + if(id->cycerr == 0) + error(base->src.start, "illegal type cycle without a reference in field %s of %t", + id->sym->name, base); + id->cycerr = 1; + }else if(arc & ArcCyc){ + if((arc & ArcArray) && id->cyc == 0 && !(arc & ArcPolycyc)){ + if(id->cycerr == 0) + error(base->src.start, "illegal circular reference to type %T in field %s of %t", + id->ty, id->sym->name, base); + id->cycerr = 1; + } + id->cycle = 1; + }else if(id->cyc != 0){ + if(id->cycerr == 0) + error(id->src.start, "spurious cyclic qualifier for field %s of %t", id->sym->name, base); + id->cycerr = 1; + } +} + +int +cycarc(Type *base, Type *t) +{ + Decl *id, *tg; + int me, arc; + + if(t == nil) + return 0; + if(t->rec & TRcyc){ + if(tequal(t, base)){ + if(t->kind == Tmodule) + return ArcCyc | ArcRef; + else + return ArcCyc | ArcValue; + } + return 0; + } + t->rec |= TRcyc; + me = 0; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tchan: + case Tfn: + case Tfix: + case Tpoly: + break; + case Tarray: + me = cycarc(base, t->tof) & ~ArcValue | ArcArray; + break; + case Tref: + me = cycarc(base, t->tof) & ~ArcValue | ArcRef; + break; + case Tlist: + me = cycarc(base, t->tof) & ~ArcValue | ArcList; + break; + case Tadt: + case Tadtpick: + case Tmodule: + case Ttuple: + case Texception: + me = 0; + for(id = t->ids; id != nil; id = id->next){ + if(!storespace[id->store]) + continue; + arc = cycarc(base, id->ty); + if((arc & ArcCyc) && id->cycerr == 0) + me |= arc; + } + for(tg = t->tags; tg != nil; tg = tg->next){ + arc = cycarc(base, tg->ty); + if((arc & ArcCyc) && tg->cycerr == 0) + me |= arc; + } + + if(t->kind == Tmodule) + me = me & ArcCyc | ArcRef | ArcPolycyc; + else + me &= ArcCyc | ArcValue | ArcPolycyc; + break; + default: + fatal("cycarc: unknown type kind %d", t->kind); + break; + } + t->rec &= ~TRcyc; + if(t->flags&CYCLIC) + me |= ArcPolycyc; + return me; +} + +/* + * set the sizes and field offsets for t + * look only as deeply as needed to size this type. + * cycsize type will clean up the rest. + */ +void +sizetype(Type *t) +{ + Decl *id, *tg; + Szal szal; + long sz, al, a; + + if(t == nil) + return; + if((t->ok & OKsized) == OKsized) + return; + t->ok |= OKsized; +if((t->ok & (OKverify|OKsized)) != (OKverify|OKsized)) +fatal("sizetype bogus ok for %t", t); + switch(t->kind){ + default: + fatal("sizetype: unknown type kind %d", t->kind); + break; + case Terror: + case Tnone: + case Tbyte: + case Tint: + case Tbig: + case Tstring: + case Tany: + case Treal: + fatal("%T should have a size", t); + break; + case Tref: + case Tchan: + case Tarray: + case Tlist: + case Tmodule: + case Tfix: + case Tpoly: + t->size = t->align = IBY2WD; + break; + case Ttuple: + case Tadt: + case Texception: + if(t->tags == nil){ + if(!debug['z']){ + szal = sizeids(t->ids, 0); + t->size = align(szal.size, szal.align); + t->align = szal.align; + }else{ + szal = sizeids(t->ids, 0); + t->align = IBY2LG; + t->size = align(szal.size, IBY2LG); + } + return; + } + if(!debug['z']){ + szal = sizeids(t->ids, IBY2WD); + sz = szal.size; + al = szal.align; + if(al < IBY2WD) + al = IBY2WD; + }else{ + szal = sizeids(t->ids, IBY2WD); + sz = szal.size; + al = IBY2LG; + } + for(tg = t->tags; tg != nil; tg = tg->next){ + if((tg->ty->ok & OKsized) == OKsized) + continue; + tg->ty->ok |= OKsized; + if(!debug['z']){ + szal = sizeids(tg->ty->ids, sz); + a = szal.align; + if(a < al) + a = al; + tg->ty->size = align(szal.size, a); + tg->ty->align = a; + }else{ + szal = sizeids(tg->ty->ids, sz); + tg->ty->size = align(szal.size, IBY2LG); + tg->ty->align = IBY2LG; + } + } + break; + case Tfn: + t->size = 0; + t->align = 1; + break; + case Tainit: + t->size = 0; + t->align = 1; + break; + case Talt: + t->size = t->cse->nlab * 2*IBY2WD + 2*IBY2WD; + t->align = IBY2WD; + break; + case Tcase: + case Tcasec: + t->size = t->cse->nlab * 3*IBY2WD + 2*IBY2WD; + t->align = IBY2WD; + break; + case Tcasel: + t->size = t->cse->nlab * 6*IBY2WD + 3*IBY2WD; + t->align = IBY2LG; + break; + case Tgoto: + t->size = t->cse->nlab * IBY2WD + IBY2WD; + if(t->cse->iwild != nil) + t->size += IBY2WD; + t->align = IBY2WD; + break; + case Tiface: + sz = IBY2WD; + for(id = t->ids; id != nil; id = id->next){ + sz = align(sz, IBY2WD) + IBY2WD; + sz += id->sym->len + 1; + if(id->dot->ty->kind == Tadt) + sz += id->dot->sym->len + 1; + } + t->size = sz; + t->align = IBY2WD; + break; + case Texcept: + t->size = 0; + t->align = IBY2WD; + break; + } +} + +Szal +sizeids(Decl *id, long off) +{ + Szal szal; + int a, al; + + al = 1; + for(; id != nil; id = id->next){ + if(storespace[id->store]){ + sizetype(id->ty); + /* + * alignment can be 0 if we have + * illegal forward declarations. + * just patch a; other code will flag an error + */ + a = id->ty->align; + if(a == 0) + a = 1; + + if(a > al) + al = a; + + off = align(off, a); + id->offset = off; + off += id->ty->size; + } + } + szal.size = off; + szal.align = al; + return szal; +} + +long +align(long off, int align) +{ + if(align == 0) + fatal("align 0"); + while(off % align) + off++; + return off; +} + +/* + * recalculate a type's size + */ +void +resizetype(Type *t) +{ + if((t->ok & OKsized) == OKsized){ + t->ok &= ~OKsized; + cycsizetype(t); + } +} + +/* + * check if a module is accessable from t + * if so, mark that module interface + */ +void +modrefable(Type *t) +{ + Decl *id, *m, *tg; + + if(t == nil || (t->ok & OKmodref) == OKmodref) + return; + if((t->ok & OKverify) != OKverify) + fatal("modrefable unused type %t", t); + t->ok |= OKmodref; + switch(t->kind){ + case Terror: + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tnone: + case Tany: + case Tfix: + case Tpoly: + break; + case Tchan: + case Tref: + case Tarray: + case Tlist: + modrefable(t->tof); + break; + case Tmodule: + t->tof->linkall = 1; + t->decl->refs++; + for(id = t->ids; id != nil; id = id->next){ + switch(id->store){ + case Dglobal: + case Dfn: + modrefable(id->ty); + break; + case Dtype: + if(id->ty->kind != Tadt) + break; + for(m = id->ty->ids; m != nil; m = m->next) + if(m->store == Dfn) + modrefable(m->ty); + break; + } + } + break; + case Tfn: + case Tadt: + case Ttuple: + case Texception: + for(id = t->ids; id != nil; id = id->next) + if(id->store != Dfn) + modrefable(id->ty); + for(tg = t->tags; tg != nil; tg = tg->next){ +/* + if((tg->ty->ok & OKmodref) == OKmodref) + continue; +*/ + tg->ty->ok |= OKmodref; + for(id = tg->ty->ids; id != nil; id = id->next) + modrefable(id->ty); + } + for(id = t->polys; id != nil; id = id->next) + modrefable(id->ty); + modrefable(t->tof); + break; + case Tadtpick: + modrefable(t->decl->dot->ty); + break; + default: + fatal("unknown type kind %d", t->kind); + break; + } +} + +Desc* +gendesc(Decl *d, long size, Decl *decls) +{ + Desc *desc; + + if(debug['D']) + print("generate desc for %D\n", d); + if(ispoly(d)) + addfnptrs(d, 0); + desc = usedesc(mkdesc(size, decls)); + return desc; +} + +Desc* +mkdesc(long size, Decl *d) +{ + uchar *pmap; + long len, n; + + len = (size+8*IBY2WD-1) / (8*IBY2WD); + pmap = allocmem(len); + memset(pmap, 0, len); + n = descmap(d, pmap, 0); + if(n >= 0) + n = n / (8*IBY2WD) + 1; + else + n = 0; + if(n > len) + fatal("wrote off end of decl map: %ld %ld", n, len); + return enterdesc(pmap, size, n); +} + +Desc* +mktdesc(Type *t) +{ + Desc *d; + uchar *pmap; + long len, n; + +usedty(t); + if(debug['D']) + print("generate desc for %T\n", t); + if(t->decl == nil){ + t->decl = mkdecl(&t->src, Dtype, t); + t->decl->sym = enter("_mktdesc_", 0); + } + if(t->decl->desc != nil) + return t->decl->desc; + len = (t->size+8*IBY2WD-1) / (8*IBY2WD); + pmap = allocmem(len); + memset(pmap, 0, len); + n = tdescmap(t, pmap, 0); + if(n >= 0) + n = n / (8*IBY2WD) + 1; + else + n = 0; + if(n > len) + fatal("wrote off end of type map for %T: %ld %ld 0x%2.2ux", t, n, len, t->ok); + d = enterdesc(pmap, t->size, n); + t->decl->desc = d; + if(debug['j']){ + uchar *m, *e; + + print("generate desc for %T\n", t); + print("\tdesc\t$%d,%lud,\"", d->id, d->size); + e = d->map + d->nmap; + for(m = d->map; m < e; m++) + print("%.2x", *m); + print("\"\n"); + } + return d; +} + +Desc* +enterdesc(uchar *map, long size, long nmap) +{ + Desc *d, *last; + int c; + + last = nil; + for(d = descriptors; d != nil; d = d->next){ + if(d->size > size || d->size == size && d->nmap > nmap) + break; + if(d->size == size && d->nmap == nmap){ + c = memcmp(d->map, map, nmap); + if(c == 0){ + free(map); + return d; + } + if(c > 0) + break; + } + last = d; + } + d = allocmem(sizeof *d); + d->id = -1; + d->used = 0; + d->map = map; + d->size = size; + d->nmap = nmap; + if(last == nil){ + d->next = descriptors; + descriptors = d; + }else{ + d->next = last->next; + last->next = d; + } + return d; +} + +Desc* +usedesc(Desc *d) +{ + d->used = 1; + return d; +} + +/* + * create the pointer description byte map for every type in decls + * each bit corresponds to a word, and is 1 if occupied by a pointer + * the high bit in the byte maps the first word + */ +long +descmap(Decl *decls, uchar *map, long start) +{ + Decl *d; + long last, m; + + if(debug['D']) + print("descmap offset %ld\n", start); + last = -1; + for(d = decls; d != nil; d = d->next){ + if(d->store == Dtype && d->ty->kind == Tmodule + || d->store == Dfn + || d->store == Dconst) + continue; + if(d->store == Dlocal && d->link != nil) + continue; + m = tdescmap(d->ty, map, d->offset + start); + if(debug['D']){ + if(d->sym != nil) + print("descmap %s type %T offset %ld returns %ld\n", + d->sym->name, d->ty, d->offset+start, m); + else + print("descmap type %T offset %ld returns %ld\n", d->ty, d->offset+start, m); + } + if(m >= 0) + last = m; + } + return last; +} + +long +tdescmap(Type *t, uchar *map, long offset) +{ + Label *lab; + long i, e, m; + int bit; + + if(t == nil) + return -1; + + m = -1; + if(t->kind == Talt){ + lab = t->cse->labs; + e = t->cse->nlab; + offset += IBY2WD * 2; + for(i = 0; i < e; i++){ + if(lab[i].isptr){ + bit = offset / IBY2WD % 8; + map[offset / (8*IBY2WD)] |= 1 << (7 - bit); + m = offset; + } + offset += 2*IBY2WD; + } + return m; + } + if(t->kind == Tcasec){ + e = t->cse->nlab; + offset += IBY2WD; + for(i = 0; i < e; i++){ + bit = offset / IBY2WD % 8; + map[offset / (8*IBY2WD)] |= 1 << (7 - bit); + offset += IBY2WD; + bit = offset / IBY2WD % 8; + map[offset / (8*IBY2WD)] |= 1 << (7 - bit); + m = offset; + offset += 2*IBY2WD; + } + return m; + } + + if(tattr[t->kind].isptr){ + bit = offset / IBY2WD % 8; + map[offset / (8*IBY2WD)] |= 1 << (7 - bit); + return offset; + } + if(t->kind == Tadtpick) + t = t->tof; + if(t->kind == Ttuple || t->kind == Tadt || t->kind == Texception){ + if(debug['D']) + print("descmap adt offset %ld\n", offset); + if(t->rec != 0) + fatal("illegal cyclic type %t in tdescmap", t); + t->rec = 1; + offset = descmap(t->ids, map, offset); + t->rec = 0; + return offset; + } + + return -1; +} + +/* + * can a t2 be assigned to a t1? + * any means Tany matches all types, + * not just references + */ +int +tcompat(Type *t1, Type *t2, int any) +{ + int ok, v; + + if(t1 == t2) + return 1; + if(t1 == nil || t2 == nil) + return 0; + if(t2->kind == Texception && t1->kind != Texception) + t2 = mkextuptype(t2); + tcomset = 0; + ok = rtcompat(t1, t2, any, 0); + v = cleartcomrec(t1) + cleartcomrec(t2); + if(v != tcomset) + fatal("recid t1 %t and t2 %t not balanced in tcompat: %d v %d", t1, t2, v, tcomset); + return ok; +} + +static int +rtcompat(Type *t1, Type *t2, int any, int inaorc) +{ + if(t1 == t2) + return 1; + if(t1 == nil || t2 == nil) + return 0; + if(t1->kind == Terror || t2->kind == Terror) + return 1; + if(t2->kind == Texception && t1->kind != Texception) + t2 = mkextuptype(t2); + + if(debug['x']) + print("rtcompat: %t and %t\n", t1, t2); + + t1->rec |= TRcom; + t2->rec |= TRcom; + switch(t1->kind){ + default: + fatal("unknown type %t v %t in rtcompat", t1, t2); + case Tstring: + return t2->kind == Tstring || t2->kind == Tany; + case Texception: + if(t2->kind == Texception && t1->cons == t2->cons){ + if(assumetcom(t1, t2)) + return 1; + return idcompat(t1->ids, t2->ids, 0, inaorc); + } + return 0; + case Tnone: + case Tint: + case Tbig: + case Tbyte: + case Treal: + return t1->kind == t2->kind; + case Tfix: + return t1->kind == t2->kind && sametree(t1->val, t2->val); + case Tany: + if(tattr[t2->kind].isptr) + return 1; + return any; + case Tref: + case Tlist: + case Tarray: + case Tchan: + if(t1->kind != t2->kind){ + if(t2->kind == Tany) + return 1; + return 0; + } + if(t1->kind != Tref && assumetcom(t1, t2)) + return 1; + return rtcompat(t1->tof, t2->tof, 0, t1->kind == Tarray || t1->kind == Tchan || inaorc); + case Tfn: + break; + case Ttuple: + if(t2->kind == Tadt && t2->tags == nil + || t2->kind == Ttuple){ + if(assumetcom(t1, t2)) + return 1; + return idcompat(t1->ids, t2->ids, any, inaorc); + } + if(t2->kind == Tadtpick){ + t2->tof->rec |= TRcom; + if(assumetcom(t1, t2->tof)) + return 1; + return idcompat(t1->ids, t2->tof->ids->next, any, inaorc); + } + return 0; + case Tadt: + if(t2->kind == Ttuple && t1->tags == nil){ + if(assumetcom(t1, t2)) + return 1; + return idcompat(t1->ids, t2->ids, any, inaorc); + } + if(t1->tags != nil && t2->kind == Tadtpick && !inaorc) + t2 = t2->decl->dot->ty; + break; + case Tadtpick: +/* + if(t2->kind == Ttuple) + return idcompat(t1->tof->ids->next, t2->ids, any, inaorc); +*/ + break; + case Tmodule: + if(t2->kind == Tany) + return 1; + break; + case Tpoly: + if(t2->kind == Tany) + return 1; + break; + } + return tequal(t1, t2); +} + +/* + * add the assumption that t1 and t2 are compatable + */ +static int +assumetcom(Type *t1, Type *t2) +{ + Type *r1, *r2; + + if(t1->tcom == nil && t2->tcom == nil){ + tcomset += 2; + t1->tcom = t2->tcom = t1; + }else{ + if(t1->tcom == nil){ + r1 = t1; + t1 = t2; + t2 = r1; + } + for(r1 = t1->tcom; r1 != r1->tcom; r1 = r1->tcom) + ; + for(r2 = t2->tcom; r2 != nil && r2 != r2->tcom; r2 = r2->tcom) + ; + if(r1 == r2) + return 1; + if(r2 == nil) + tcomset++; + t2->tcom = t1; + for(; t2 != r1; t2 = r2){ + r2 = t2->tcom; + t2->tcom = r1; + } + } + return 0; +} + +static int +cleartcomrec(Type *t) +{ + Decl *id; + int n; + + n = 0; + for(; t != nil && (t->rec & TRcom) == TRcom; t = t->tof){ + t->rec &= ~TRcom; + if(t->tcom != nil){ + t->tcom = nil; + n++; + } + if(t->kind == Tadtpick) + n += cleartcomrec(t->tof); + if(t->kind == Tmodule) + t = t->tof; + for(id = t->ids; id != nil; id = id->next) + n += cleartcomrec(id->ty); + for(id = t->tags; id != nil; id = id->next) + n += cleartcomrec(id->ty); + for(id = t->polys; id != nil; id = id->next) + n += cleartcomrec(id->ty); + } + return n; +} + +/* + * id1 and id2 are the fields in an adt or tuple + * simple structural check; ignore names + */ +static int +idcompat(Decl *id1, Decl *id2, int any, int inaorc) +{ + for(; id1 != nil; id1 = id1->next){ + if(id1->store != Dfield) + continue; + while(id2 != nil && id2->store != Dfield) + id2 = id2->next; + if(id2 == nil + || id1->store != id2->store + || !rtcompat(id1->ty, id2->ty, any, inaorc)) + return 0; + id2 = id2->next; + } + while(id2 != nil && id2->store != Dfield) + id2 = id2->next; + return id2 == nil; +} + +int +tequal(Type *t1, Type *t2) +{ + int ok, v; + + eqrec = 0; + eqset = 0; + ok = rtequal(t1, t2); + v = cleareqrec(t1) + cleareqrec(t2); + if(0 && v != eqset) + fatal("recid t1 %t and t2 %t not balanced in tequal: %d %d", t1, t2, v, eqset); + eqset = 0; + return ok; +} + +/* + * structural equality on types + */ +static int +rtequal(Type *t1, Type *t2) +{ + /* + * this is just a shortcut + */ + if(t1 == t2) + return 1; + + if(t1 == nil || t2 == nil) + return 0; + if(t1->kind == Terror || t2->kind == Terror) + return 1; + + if(t1->kind != t2->kind) + return 0; + + if(t1->eq != nil && t2->eq != nil) + return t1->eq == t2->eq; + + if(debug['x']) + print("rtequal: %t and %t\n", t1, t2); + + t1->rec |= TReq; + t2->rec |= TReq; + switch(t1->kind){ + default: + fatal("unknown type %t v %t in rtequal", t1, t2); + case Tnone: + case Tbig: + case Tbyte: + case Treal: + case Tint: + case Tstring: + /* + * this should always be caught by t1 == t2 check + */ + fatal("bogus value type %t vs %t in rtequal", t1, t2); + return 1; + case Tfix: + return sametree(t1->val, t2->val); + case Tref: + case Tlist: + case Tarray: + case Tchan: + if(t1->kind != Tref && assumeteq(t1, t2)) + return 1; + return rtequal(t1->tof, t2->tof); + case Tfn: + if(t1->varargs != t2->varargs) + return 0; + if(!idequal(t1->ids, t2->ids, 0, storespace)) + return 0; + /* if(!idequal(t1->polys, t2->polys, 1, nil)) */ + if(!pyequal(t1, t2)) + return 0; + return rtequal(t1->tof, t2->tof); + case Ttuple: + case Texception: + if(t1->kind != t2->kind || t1->cons != t2->cons) + return 0; + if(assumeteq(t1, t2)) + return 1; + return idequal(t1->ids, t2->ids, 0, storespace); + case Tadt: + case Tadtpick: + case Tmodule: + if(assumeteq(t1, t2)) + return 1; + /* + * compare interfaces when comparing modules + */ + if(t1->kind == Tmodule) + return idequal(t1->tof->ids, t2->tof->ids, 1, nil); + + /* + * picked adts; check parent, + * assuming equiv picked fields, + * then check picked fields are equiv + */ + if(t1->kind == Tadtpick && !rtequal(t1->decl->dot->ty, t2->decl->dot->ty)) + return 0; + + /* + * adts with pick tags: check picked fields for equality + */ + if(!idequal(t1->tags, t2->tags, 1, nil)) + return 0; + + /* if(!idequal(t1->polys, t2->polys, 1, nil)) */ + if(!pyequal(t1, t2)) + return 0; + return idequal(t1->ids, t2->ids, 1, storespace); + case Tpoly: + if(assumeteq(t1, t2)) + return 1; + if(t1->decl->sym != t2->decl->sym) + return 0; + return idequal(t1->ids, t2->ids, 1, nil); + } +} + +static int +assumeteq(Type *t1, Type *t2) +{ + Type *r1, *r2; + + if(t1->teq == nil && t2->teq == nil){ + eqrec++; + eqset += 2; + t1->teq = t2->teq = t1; + }else{ + if(t1->teq == nil){ + r1 = t1; + t1 = t2; + t2 = r1; + } + for(r1 = t1->teq; r1 != r1->teq; r1 = r1->teq) + ; + for(r2 = t2->teq; r2 != nil && r2 != r2->teq; r2 = r2->teq) + ; + if(r1 == r2) + return 1; + if(r2 == nil) + eqset++; + t2->teq = t1; + for(; t2 != r1; t2 = r2){ + r2 = t2->teq; + t2->teq = r1; + } + } + return 0; +} + +/* + * checking structural equality for adts, tuples, and fns + */ +static int +idequal(Decl *id1, Decl *id2, int usenames, int *storeok) +{ + /* + * this is just a shortcut + */ + if(id1 == id2) + return 1; + + for(; id1 != nil; id1 = id1->next){ + if(storeok != nil && !storeok[id1->store]) + continue; + while(id2 != nil && storeok != nil && !storeok[id2->store]) + id2 = id2->next; + if(id2 == nil + || usenames && id1->sym != id2->sym + || id1->store != id2->store + || id1->implicit != id2->implicit + || id1->cyc != id2->cyc + || (id1->dot == nil) != (id2->dot == nil) + || id1->dot != nil && id2->dot != nil && id1->dot->ty->kind != id2->dot->ty->kind + || !rtequal(id1->ty, id2->ty)) + return 0; + id2 = id2->next; + } + while(id2 != nil && storeok != nil && !storeok[id2->store]) + id2 = id2->next; + return id1 == nil && id2 == nil; +} + +static int +pyequal(Type *t1, Type *t2) +{ + Type *pt1, *pt2; + Decl *id1, *id2; + + if(t1 == t2) + return 1; + id1 = t1->polys; + id2 = t2->polys; + for(; id1 != nil; id1 = id1->next){ + if(id2 == nil) + return 0; + pt1 = id1->ty; + pt2 = id2->ty; + if(!rtequal(pt1, pt2)){ + if(t1->u.tmap != nil) + pt1 = valtmap(pt1, t1->u.tmap); + if(t2->u.tmap != nil) + pt2 = valtmap(pt2, t2->u.tmap); + if(!rtequal(pt1, pt2)) + return 0; + } + id2 = id2->next; + } + return id1 == nil && id2 == nil; +} + +static int +cleareqrec(Type *t) +{ + Decl *id; + int n; + + n = 0; + for(; t != nil && (t->rec & TReq) == TReq; t = t->tof){ + t->rec &= ~TReq; + if(t->teq != nil){ + t->teq = nil; + n++; + } + if(t->kind == Tadtpick) + n += cleareqrec(t->decl->dot->ty); + if(t->kind == Tmodule) + t = t->tof; + for(id = t->ids; id != nil; id = id->next) + n += cleareqrec(id->ty); + for(id = t->tags; id != nil; id = id->next) + n += cleareqrec(id->ty); + for(id = t->polys; id != nil; id = id->next) + n += cleareqrec(id->ty); + } + return n; +} + +int +raisescompat(Node *n1, Node *n2) +{ + if(n1 == n2) + return 1; + if(n2 == nil) + return 1; /* no need to repeat in definition if given in declaration */ + if(n1 == nil) + return 0; + for(n1 = n1->left, n2 = n2->left; n1 != nil && n2 != nil; n1 = n1->right, n2 = n2->right){ + if(n1->left->decl != n2->left->decl) + return 0; + } + return n1 == n2; +} + +/* t1 a polymorphic type */ +static int +fnunify(Type *t1, Type *t2, Tpair **tp, int swapped) +{ + Decl *id, *ids; + Sym *sym; + + for(ids = t1->ids; ids != nil; ids = ids->next){ + sym = ids->sym; + id = fnlookup(sym, t2, nil); + if(id != nil) + usetype(id->ty); + if(id == nil){ + if(dowarn) + error(unifysrc.start, "type %T does not have a '%s' function", t2, sym->name); + return 0; + } + else if(id->ty->kind != Tfn){ + if(dowarn) + error(unifysrc.start, "%T is not a function", id->ty); + return 0; + } + else if(!rtunify(ids->ty, id->ty, tp, !swapped)){ + if(dowarn) + error(unifysrc.start, "%T and %T are not compatible wrt %s", ids->ty, id->ty, sym->name); + return 0; + } + } + return 1; +} + +static int +fncleareqrec(Type *t1, Type *t2) +{ + Decl *id, *ids; + int n; + + n = 0; + n += cleareqrec(t1); + n += cleareqrec(t2); + for(ids = t1->ids; ids != nil; ids = ids->next){ + id = fnlookup(ids->sym, t2, nil); + if(id == nil) + continue; + else{ + n += cleareqrec(ids->ty); + n += cleareqrec(id->ty); + } + } + return n; +} +int +tunify(Type *t1, Type *t2, Tpair **tp) +{ + int ok, v; + Tpair *p; + + *tp = nil; + eqrec = 0; + eqset = 0; + ok = rtunify(t1, t2, tp, 0); + v = cleareqrec(t1) + cleareqrec(t2); + for(p = *tp; p != nil; p = p->nxt) + v += fncleareqrec(p->t1, p->t2); + if(0 && v != eqset) + fatal("recid t1 %t and t2 %t not balanced in tunify: %d %d", t1, t2, v, eqset); + return ok; +} + +static int +rtunify(Type *t1, Type *t2, Tpair **tp, int swapped) +{ + Type *tmp; + +if(debug['w']) print("rtunifya - %T %T\n", t1, t2); + t1 = valtmap(t1, *tp); + t2 = valtmap(t2, *tp); +if(debug['w']) print("rtunifyb - %T %T\n", t1, t2); + if(t1 == t2) + return 1; + if(t1 == nil || t2 == nil) + return 0; + if(t1->kind == Terror || t2->kind == Terror) + return 1; + if(t1->kind != Tpoly && t2->kind == Tpoly){ + tmp = t1; + t1 = t2; + t2 = tmp; + swapped = !swapped; + } + if(t1->kind == Tpoly){ +/* + if(typein(t1, t2)) + return 0; +*/ + if(!tattr[t2->kind].isptr) + return 0; + if(t2->kind != Tany) + addtmap(t1, t2, tp); + return fnunify(t1, t2, tp, swapped); + } + if(t1->kind != Tany && t2->kind == Tany){ + tmp = t1; + t1 = t2; + t2 = tmp; + swapped = !swapped; + } + if(t1->kind == Tadt && t1->tags != nil && t2->kind == Tadtpick && !swapped) + t2 = t2->decl->dot->ty; + if(t2->kind == Tadt && t2->tags != nil && t1->kind == Tadtpick && swapped) + t1 = t1->decl->dot->ty; + if(t1->kind != Tany && t1->kind != t2->kind) + return 0; + t1->rec |= TReq; + t2->rec |= TReq; + switch(t1->kind){ + default: + return tequal(t1, t2); + case Tany: + return tattr[t2->kind].isptr; + case Tref: + case Tlist: + case Tarray: + case Tchan: + if(t1->kind != Tref && assumeteq(t1, t2)) + return 1; + return rtunify(t1->tof, t2->tof, tp, swapped); + case Tfn: + if(!idunify(t1->ids, t2->ids, tp, swapped)) + return 0; + if(!idunify(t1->polys, t2->polys, tp, swapped)) + return 0; + return rtunify(t1->tof, t2->tof, tp, swapped); + case Ttuple: + if(assumeteq(t1, t2)) + return 1; + return idunify(t1->ids, t2->ids, tp, swapped); + case Tadt: + case Tadtpick: + if(assumeteq(t1, t2)) + return 1; + if(!idunify(t1->polys, t2->polys, tp, swapped)) + return 0; + if(!idunify(t1->tags, t2->tags, tp, swapped)) + return 0; + return idunify(t1->ids, t2->ids, tp, swapped); + case Tmodule: + if(assumeteq(t1, t2)) + return 1; + return idunify(t1->tof->ids, t2->tof->ids, tp, swapped); + case Tpoly: + return t1 == t2; + } + return 1; +} + +static int +idunify(Decl *id1, Decl *id2, Tpair **tp, int swapped) +{ + if(id1 == id2) + return 1; + for(; id1 != nil; id1 = id1->next){ + if(id2 == nil || !rtunify(id1->ty, id2->ty, tp, swapped)) + return 0; + id2 = id2->next; + } + return id1 == nil && id2 == nil; +} + +int +polyequal(Decl *id1, Decl *id2) +{ + int ck2; + Decl *d; + + /* allow id2 list to have an optional for clause */ + ck2 = 0; + for(d = id2; d != nil; d = d->next) + if(d->ty->ids != nil) + ck2 = 1; + for( ; id1 != nil; id1 = id1->next){ + if(id2 == nil + || id1->sym != id2->sym + || id1->ty->decl != nil && id2->ty->decl != nil && id1->ty->decl->sym != id2->ty->decl->sym) + return 0; + if(ck2 && !idequal(id1->ty->ids, id2->ty->ids, 1, nil)) + return 0; + id2 = id2->next; + } + return id1 == nil && id2 == nil; +} + +Type* +calltype(Type *f, Node *a, Type *rt) +{ + Type *t; + Decl *id, *first, *last; + + first = last = nil; + t = mktype(&f->src.start, &f->src.stop, Tfn, rt, nil); + t->polys = f->kind == Tref ? f->tof->polys : f->polys; + for( ; a != nil; a = a->right){ + id = mkdecl(&f->src, Darg, a->left->ty); + if(last == nil) + first = id; + else + last->next = id; + last = id; + } + t->ids = first; + if(f->kind == Tref) + t = mktype(&f->src.start, &f->src.stop, Tref, t, nil); + return t; +} + +static Type* +duptype(Type *t) +{ + Type *nt; + + nt = allocmem(sizeof(*nt)); + *nt = *t; + nt->ok &= ~(OKverify|OKref|OKclass|OKsized|OKcycsize|OKcyc); + nt->flags |= INST; + nt->eq = nil; + nt->sbl = -1; + if(t->decl != nil && (nt->kind == Tadt || nt->kind == Tadtpick || nt->kind == Ttuple)){ + nt->decl = dupdecl(t->decl); + nt->decl->ty = nt; + nt->decl->link = t->decl; + if(t->decl->dot != nil){ + nt->decl->dot = dupdecl(t->decl->dot); + nt->decl->dot->link = t->decl->dot; + } + } + else + nt->decl = nil; + return nt; +} + +static int +dpolys(Decl *ids) +{ + Decl *p; + + for(p = ids; p != nil; p = p->next) + if(tpolys(p->ty)) + return 1; + return 0; +} + +static int +tpolys(Type *t) +{ + int v; + Typelist *tl; + + if(t == nil) + return 0; + if(t->flags&(POLY|NOPOLY)) + return t->flags&POLY; + switch(t->kind){ + default: + v = 0; + break; + case Tarrow: + case Tdot: + case Tpoly: + v = 1; + break; + case Tref: + case Tlist: + case Tarray: + case Tchan: + v = tpolys(t->tof); + break; + case Tid: + v = tpolys(t->decl->ty); + break; + case Tinst: + for(tl = t->u.tlist; tl != nil; tl = tl->nxt) + if(tpolys(tl->t)){ + v = 1; + break; + } + v = tpolys(t->tof); + break; + case Tfn: + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + if(t->polys != nil){ + v = 1; + break; + } + if(t->rec&TRvis) + return 0; + t->rec |= TRvis; + v = tpolys(t->tof) || dpolys(t->polys) || dpolys(t->ids) || dpolys(t->tags); + t->rec &= ~TRvis; + if(t->kind == Tadtpick && v == 0) + v = tpolys(t->decl->dot->ty); + break; + } + if(v) + t->flags |= POLY; + else + t->flags |= NOPOLY; + return v; +} + +static int +doccurs(Decl *ids, Tpair **tp) +{ + Decl *p; + + for(p = ids; p != nil; p = p->next) + if(toccurs(p->ty, tp)) + return 1; + return 0; +} + +static int +toccurs(Type *t, Tpair **tp) +{ + int o; + Typelist *tl; + + if(t == nil) + return 0; + if(!(t->flags&(POLY|NOPOLY))) + tpolys(t); + if(t->flags&NOPOLY) + return 0; + switch(t->kind){ + default: + fatal("unknown type %t in toccurs", t); + case Tnone: + case Tbig: + case Tbyte: + case Treal: + case Tint: + case Tstring: + case Tfix: + case Tmodule: + case Terror: + return 0; + case Tarrow: + case Tdot: + return 1; + case Tpoly: + return valtmap(t, *tp) != t; + case Tref: + case Tlist: + case Tarray: + case Tchan: + return toccurs(t->tof, tp); + case Tid: + return toccurs(t->decl->ty, tp); + case Tinst: + for(tl = t->u.tlist; tl != nil; tl = tl->nxt) + if(toccurs(tl->t, tp)) + return 1; + return toccurs(t->tof, tp); + case Tfn: + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + if(t->rec&TRvis) + return 0; + t->rec |= TRvis; + o = toccurs(t->tof, tp) || doccurs(t->polys, tp) || doccurs(t->ids, tp) || doccurs(t->tags, tp); + t->rec &= ~TRvis; + if(t->kind == Tadtpick && o == 0) + o = toccurs(t->decl->dot->ty, tp); + return o; + } +} + +static Decl* +expandids(Decl *ids, Decl *adtt, Tpair **tp, int sym) +{ + Decl *p, *q, *nids, *last; + + nids = last = nil; + for(p = ids; p != nil; p = p->next){ + q = dupdecl(p); + q->ty = expandtype(p->ty, nil, adtt, tp); + if(sym && q->ty->decl != nil) + q->sym = q->ty->decl->sym; + if(q->store == Dfn){ +if(debug['v']) print("%p->link = %p\n", q, p); + q->link = p; + } + if(nids == nil) + nids = q; + else + last->next = q; + last = q; + } + return nids; +} + +Type* +expandtype(Type *t, Type *instt, Decl *adtt, Tpair **tp) +{ + Type *nt; + Decl *ids; + + if(t == nil) + return nil; +if(debug['w']) print("expandtype %d %lux %T\n", t->kind, (ulong)t, t); + if(!toccurs(t, tp)) + return t; +if(debug['w']) print("\texpanding\n"); + switch(t->kind){ + default: + fatal("unknown type %t in expandtype", t); + case Tpoly: + return valtmap(t, *tp); + case Tref: + case Tlist: + case Tarray: + case Tchan: + nt = duptype(t); + nt->tof = expandtype(t->tof, nil, adtt, tp); + return nt; + case Tid: + return expandtype(idtype(t), nil, adtt, tp); + case Tdot: + return expandtype(dottype(t, adtt), nil, adtt, tp); + case Tarrow: + return expandtype(arrowtype(t, adtt), nil, adtt, tp); + case Tinst: + if((nt = valtmap(t, *tp)) != t) + return nt; + return expandtype(insttype(t, adtt, tp), nil, adtt, tp); + case Tfn: + case Tadt: + case Tadtpick: + case Ttuple: + case Texception: + if((nt = valtmap(t, *tp)) != t) + return nt; + if(t->kind == Tadt) + adtt = t->decl; + nt = duptype(t); + addtmap(t, nt, tp); + if(instt != nil) + addtmap(instt, nt, tp); + nt->tof = expandtype(t->tof, nil, adtt, tp); + nt->polys = expandids(t->polys, adtt, tp, 1); + nt->ids = expandids(t->ids, adtt, tp, 0); + nt->tags = expandids(t->tags, adtt, tp, 0); + if(t->kind == Tadt){ + for(ids = nt->tags; ids != nil; ids = ids->next) + ids->ty->decl->dot = nt->decl; + } + if(t->kind == Tadtpick){ + nt->decl->dot->ty = expandtype(t->decl->dot->ty, nil, adtt, tp); + } + if((t->kind == Tadt || t->kind == Tadtpick) && t->u.tmap != nil){ + Tpair *p; + + nt->u.tmap = nil; + for(p = t->u.tmap; p != nil; p = p->nxt) + addtmap(valtmap(p->t1, *tp), valtmap(p->t2, *tp), &nt->u.tmap); + if(debug['w']){ + print("new tmap for %T->%T: ", t, nt); + for(p=nt->u.tmap;p!=nil;p=p->nxt)print("%T -> %T ", p->t1, p->t2); + print("\n"); + } + } + return nt; + } +} + +/* + * create type signatures + * sign the same information used + * for testing type equality + */ +ulong +sign(Decl *d) +{ + Type *t; + uchar *sig, md5sig[MD5dlen]; + char buf[StrSize]; + int i, sigend, sigalloc, v; + + t = d->ty; + if(t->sig != 0) + return t->sig; + + if(ispoly(d)) + rmfnptrs(d); + + sig = 0; + sigend = -1; + sigalloc = 1024; + while(sigend < 0 || sigend >= sigalloc){ + sigalloc *= 2; + sig = reallocmem(sig, sigalloc); + eqrec = 0; + sigend = rtsign(t, sig, sigalloc, 0); + v = clearrec(t); + if(v != eqrec) + fatal("recid not balanced in sign: %d %d", v, eqrec); + eqrec = 0; + } + sig[sigend] = '\0'; + + if(signdump != nil){ + seprint(buf, buf+sizeof(buf), "%D", d); + if(strcmp(buf, signdump) == 0){ + print("sign %D len %d\n", d, sigend); + print("%s\n", (char*)sig); + } + } + + md5(sig, sigend, md5sig, nil); + for(i = 0; i < MD5dlen; i += 4) + t->sig ^= md5sig[i+0] | (md5sig[i+1]<<8) | (md5sig[i+2]<<16) | (md5sig[i+3]<<24); + if(debug['S']) + print("signed %D type %T len %d sig %#lux\n", d, t, sigend, t->sig); + free(sig); + return t->sig; +} + +enum +{ + SIGSELF = 'S', + SIGVARARGS = '*', + SIGCYC = 'y', + SIGREC = '@' +}; + +static int sigkind[Tend] = +{ + /* Tnone */ 'n', + /* Tadt */ 'a', + /* Tadtpick */ 'p', + /* Tarray */ 'A', + /* Tbig */ 'B', + /* Tbyte */ 'b', + /* Tchan */ 'C', + /* Treal */ 'r', + /* Tfn */ 'f', + /* Tint */ 'i', + /* Tlist */ 'L', + /* Tmodule */ 'm', + /* Tref */ 'R', + /* Tstring */ 's', + /* Ttuple */ 't', + /* Texception */ 'e', + /* Tfix */ 'x', + /* Tpoly */ 'P', +}; + +static int +rtsign(Type *t, uchar *sig, int lensig, int spos) +{ + Decl *id, *tg; + char name[32]; + int kind, lenname; + + if(t == nil) + return spos; + + if(spos < 0 || spos + 8 >= lensig) + return -1; + + if(t->eq != nil && t->eq->id){ + if(t->eq->id < 0 || t->eq->id > eqrec) + fatal("sign rec %T %d %d", t, t->eq->id, eqrec); + + sig[spos++] = SIGREC; + seprint(name, name+sizeof(name), "%d", t->eq->id); + lenname = strlen(name); + if(spos + lenname > lensig) + return -1; + strcpy((char*)&sig[spos], name); + spos += lenname; + return spos; + } + if(t->eq != nil){ + eqrec++; + t->eq->id = eqrec; + } + + kind = sigkind[t->kind]; + sig[spos++] = kind; + if(kind == 0) + fatal("no sigkind for %t", t); + + t->rec = 1; + switch(t->kind){ + default: + fatal("bogus type %t in rtsign", t); + return -1; + case Tnone: + case Tbig: + case Tbyte: + case Treal: + case Tint: + case Tstring: + case Tpoly: + return spos; + case Tfix: + seprint(name, name+sizeof(name), "%g", t->val->rval); + lenname = strlen(name); + if(spos+lenname-1 >= lensig) + return -1; + strcpy((char*)&sig[spos], name); + spos += lenname; + return spos; + case Tref: + case Tlist: + case Tarray: + case Tchan: + return rtsign(t->tof, sig, lensig, spos); + case Tfn: + if(t->varargs != 0) + sig[spos++] = SIGVARARGS; + if(t->polys != nil) + spos = idsign(t->polys, 0, sig, lensig, spos); + spos = idsign(t->ids, 0, sig, lensig, spos); + if(t->u.eraises) + spos = raisessign(t->u.eraises, sig, lensig, spos); + return rtsign(t->tof, sig, lensig, spos); + case Ttuple: + return idsign(t->ids, 0, sig, lensig, spos); + case Tadt: + /* + * this is a little different than in rtequal, + * since we flatten the adt we used to represent the globals + */ + if(t->eq == nil){ + if(strcmp(t->decl->sym->name, ".mp") != 0) + fatal("no t->eq field for %t", t); + spos--; + for(id = t->ids; id != nil; id = id->next){ + spos = idsign1(id, 1, sig, lensig, spos); + if(spos < 0 || spos >= lensig) + return -1; + sig[spos++] = ';'; + } + return spos; + } + if(t->polys != nil) + spos = idsign(t->polys, 0, sig, lensig, spos); + spos = idsign(t->ids, 1, sig, lensig, spos); + if(spos < 0 || t->tags == nil) + return spos; + + /* + * convert closing ')' to a ',', then sign any tags + */ + sig[spos-1] = ','; + for(tg = t->tags; tg != nil; tg = tg->next){ + lenname = tg->sym->len; + if(spos + lenname + 2 >= lensig) + return -1; + strcpy((char*)&sig[spos], tg->sym->name); + spos += lenname; + sig[spos++] = '='; + sig[spos++] = '>'; + + spos = rtsign(tg->ty, sig, lensig, spos); + if(spos < 0 || spos >= lensig) + return -1; + + if(tg->next != nil) + sig[spos++] = ','; + } + if(spos >= lensig) + return -1; + sig[spos++] = ')'; + return spos; + case Tadtpick: + spos = idsign(t->ids, 1, sig, lensig, spos); + if(spos < 0) + return spos; + return rtsign(t->decl->dot->ty, sig, lensig, spos); + case Tmodule: + if(t->tof->linkall == 0) + fatal("signing a narrowed module"); + + if(spos >= lensig) + return -1; + sig[spos++] = '{'; + for(id = t->tof->ids; id != nil; id = id->next){ + if(id->tag) + continue; + if(strcmp(id->sym->name, ".mp") == 0){ + spos = rtsign(id->ty, sig, lensig, spos); + if(spos < 0) + return -1; + continue; + } + spos = idsign1(id, 1, sig, lensig, spos); + if(spos < 0 || spos >= lensig) + return -1; + sig[spos++] = ';'; + } + if(spos >= lensig) + return -1; + sig[spos++] = '}'; + return spos; + } +} + +static int +idsign(Decl *id, int usenames, uchar *sig, int lensig, int spos) +{ + int first; + + if(spos >= lensig) + return -1; + sig[spos++] = '('; + first = 1; + for(; id != nil; id = id->next){ + if(id->store == Dlocal) + fatal("local %s in idsign", id->sym->name); + + if(!storespace[id->store]) + continue; + + if(!first){ + if(spos >= lensig) + return -1; + sig[spos++] = ','; + } + + spos = idsign1(id, usenames, sig, lensig, spos); + if(spos < 0) + return -1; + first = 0; + } + if(spos >= lensig) + return -1; + sig[spos++] = ')'; + return spos; +} + +static int +idsign1(Decl *id, int usenames, uchar *sig, int lensig, int spos) +{ + char *name; + int lenname; + + if(usenames){ + name = id->sym->name; + lenname = id->sym->len; + if(spos + lenname + 1 >= lensig) + return -1; + strcpy((char*)&sig[spos], name); + spos += lenname; + sig[spos++] = ':'; + } + + if(spos + 2 >= lensig) + return -1; + + if(id->implicit != 0) + sig[spos++] = SIGSELF; + + if(id->cyc != 0) + sig[spos++] = SIGCYC; + + return rtsign(id->ty, sig, lensig, spos); +} + +static int +raisessign(Node *n, uchar *sig, int lensig, int spos) +{ + int m; + char *s; + Node *nn; + + if(spos >= lensig) + return -1; + sig[spos++] = '('; + for(nn = n->left; nn != nil; nn = nn->right){ + s = nn->left->decl->sym->name; + m = nn->left->decl->sym->len; + if(spos+m-1 >= lensig) + return -1; + strcpy((char*)&sig[spos], s); + spos += m; + if(nn->right != nil){ + if(spos >= lensig) + return -1; + sig[spos++] = ','; + } + } + if(spos >= lensig) + return -1; + sig[spos++] = ')'; + return spos; +} + +static int +clearrec(Type *t) +{ + Decl *id; + int n; + + n = 0; + for(; t != nil && t->rec; t = t->tof){ + t->rec = 0; + if(t->eq != nil && t->eq->id != 0){ + t->eq->id = 0; + n++; + } + if(t->kind == Tmodule){ + for(id = t->tof->ids; id != nil; id = id->next) + n += clearrec(id->ty); + return n; + } + if(t->kind == Tadtpick) + n += clearrec(t->decl->dot->ty); + for(id = t->ids; id != nil; id = id->next) + n += clearrec(id->ty); + for(id = t->tags; id != nil; id = id->next) + n += clearrec(id->ty); + for(id = t->polys; id != nil; id = id->next) + n += clearrec(id->ty); + } + return n; +} + +/* must a variable of the given type be zeroed ? (for uninitialized declarations inside loops) */ +int +tmustzero(Type *t) +{ + if(t==nil) + return 0; + if(tattr[t->kind].isptr) + return 1; + if(t->kind == Tadtpick) + t = t->tof; + if(t->kind == Ttuple || t->kind == Tadt) + return mustzero(t->ids); + return 0; +} + +int +mustzero(Decl *decls) +{ + Decl *d; + + for (d = decls; d != nil; d = d->next) + if (tmustzero(d->ty)) + return 1; + return 0; +} + +int +typeconv(Fmt *f) +{ + Type *t; + char *p, buf[1024]; + + t = va_arg(f->args, Type*); + if(t == nil){ + p = "nothing"; + }else{ + p = buf; + buf[0] = 0; + tprint(buf, buf+sizeof(buf), t); + } + return fmtstrcpy(f, p); +} + +int +stypeconv(Fmt *f) +{ + Type *t; + char *p, buf[1024]; + + t = va_arg(f->args, Type*); + if(t == nil){ + p = "nothing"; + }else{ + p = buf; + buf[0] = 0; + stprint(buf, buf+sizeof(buf), t); + } + return fmtstrcpy(f, p); +} + +int +ctypeconv(Fmt *f) +{ + Type *t; + char buf[1024]; + + t = va_arg(f->args, Type*); + buf[0] = 0; + ctprint(buf, buf+sizeof(buf), t); + return fmtstrcpy(f, buf); +} + +char* +tprint(char *buf, char *end, Type *t) +{ + Decl *id; + Typelist *tl; + + if(t == nil) + return buf; + if(t->kind >= Tend) + return seprint(buf, end, "kind %d", t->kind); + switch(t->kind){ + case Tarrow: + buf = seprint(buf, end, "%T->%s", t->tof, t->decl->sym->name); + break; + case Tdot: + buf = seprint(buf, end, "%T.%s", t->tof, t->decl->sym->name); + break; + case Tid: + case Tpoly: + buf = seprint(buf, end, "%s", t->decl->sym->name); + break; + case Tinst: + buf = tprint(buf, end, t->tof); + buf = secpy(buf ,end, "["); + for(tl = t->u.tlist; tl != nil; tl = tl->nxt){ + buf = tprint(buf, end, tl->t); + if(tl->nxt != nil) + buf = secpy(buf, end, ", "); + } + buf = secpy(buf, end, "]"); + break; + case Tint: + case Tbig: + case Tstring: + case Treal: + case Tbyte: + case Tany: + case Tnone: + case Terror: + case Tainit: + case Talt: + case Tcase: + case Tcasel: + case Tcasec: + case Tgoto: + case Tiface: + case Texception: + case Texcept: + buf = secpy(buf, end, kindname[t->kind]); + break; + case Tfix: + buf = seprint(buf, end, "%s(%v)", kindname[t->kind], t->val); + break; + case Tref: + buf = secpy(buf, end, "ref "); + buf = tprint(buf, end, t->tof); + break; + case Tchan: + case Tarray: + case Tlist: + buf = seprint(buf, end, "%s of ", kindname[t->kind]); + buf = tprint(buf, end, t->tof); + break; + case Tadtpick: + buf = seprint(buf, end, "%s.%s", t->decl->dot->sym->name, t->decl->sym->name); + break; + case Tadt: + if(t->decl->dot != nil && !isimpmod(t->decl->dot->sym)) + buf = seprint(buf, end, "%s->%s", t->decl->dot->sym->name, t->decl->sym->name); + else + buf = seprint(buf, end, "%s", t->decl->sym->name); + if(t->polys != nil){ + buf = secpy(buf ,end, "["); + for(id = t->polys; id != nil; id = id->next){ + if(t->u.tmap != nil) + buf = tprint(buf, end, valtmap(id->ty, t->u.tmap)); + else + buf = seprint(buf, end, "%s", id->sym->name); + if(id->next != nil) + buf = secpy(buf, end, ", "); + } + buf = secpy(buf, end, "]"); + } + break; + case Tmodule: + buf = seprint(buf, end, "%s", t->decl->sym->name); + break; + case Ttuple: + buf = secpy(buf, end, "("); + for(id = t->ids; id != nil; id = id->next){ + buf = tprint(buf, end, id->ty); + if(id->next != nil) + buf = secpy(buf, end, ", "); + } + buf = secpy(buf, end, ")"); + break; + case Tfn: + buf = secpy(buf, end, "fn"); + if(t->polys != nil){ + buf = secpy(buf, end, "["); + for(id = t->polys; id != nil; id = id->next){ + buf = seprint(buf, end, "%s", id->sym->name); + if(id->next != nil) + buf = secpy(buf, end, ", "); + } + buf = secpy(buf, end, "]"); + } + buf = secpy(buf, end, "("); + for(id = t->ids; id != nil; id = id->next){ + if(id->sym == nil) + buf = secpy(buf, end, "nil: "); + else + buf = seprint(buf, end, "%s: ", id->sym->name); + if(id->implicit) + buf = secpy(buf, end, "self "); + buf = tprint(buf, end, id->ty); + if(id->next != nil) + buf = secpy(buf, end, ", "); + } + if(t->varargs && t->ids != nil) + buf = secpy(buf, end, ", *"); + else if(t->varargs) + buf = secpy(buf, end, "*"); + if(t->tof != nil && t->tof->kind != Tnone){ + buf = secpy(buf, end, "): "); + buf = tprint(buf, end, t->tof); + break; + } + buf = secpy(buf, end, ")"); + break; + default: + yyerror("tprint: unknown type kind %d", t->kind); + break; + } + return buf; +} + +char* +stprint(char *buf, char *end, Type *t) +{ + if(t == nil) + return buf; + switch(t->kind){ + case Tid: + return seprint(buf, end, "id %s", t->decl->sym->name); + case Tadt: + case Tadtpick: + case Tmodule: + buf = secpy(buf, end, kindname[t->kind]); + buf = secpy(buf, end, " "); + return tprint(buf, end, t); + } + return tprint(buf, end, t); +} + +/* generalize ref P.A, ref P.B to ref P */ + +/* +Type* +tparentx(Type *t1, Type* t2) +{ + if(t1 == nil || t2 == nil || t1->kind != Tref || t2->kind != Tref) + return t1; + t1 = t1->tof; + t2 = t2->tof; + if(t1 == nil || t2 == nil || t1->kind != Tadtpick || t2->kind != Tadtpick) + return t1; + t1 = t1->decl->dot->ty; + t2 = t2->decl->dot->ty; + if(tequal(t1, t2)) + return mktype(&t1->src.start, &t1->src.stop, Tref, t1, nil); + return t1; +} +*/ + +static int +tparent0(Type *t1, Type *t2) +{ + Decl *id1, *id2; + + if(t1 == t2) + return 1; + if(t1 == nil || t2 == nil) + return 0; + if(t1->kind == Tadt && t2->kind == Tadtpick) + t2 = t2->decl->dot->ty; + if(t1->kind == Tadtpick && t2->kind == Tadt) + t1 = t1->decl->dot->ty; + if(t1->kind != t2->kind) + return 0; + switch(t1->kind){ + default: + fatal("unknown type %t v %t in tparent", t1, t2); + break; + case Terror: + case Tstring: + case Tnone: + case Tint: + case Tbig: + case Tbyte: + case Treal: + case Tany: + return 1; + case Texception: + case Tfix: + case Tfn: + case Tadt: + case Tmodule: + case Tpoly: + return tcompat(t1, t2, 0); + case Tref: + case Tlist: + case Tarray: + case Tchan: + return tparent0(t1->tof, t2->tof); + case Ttuple: + for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next) + if(!tparent0(id1->ty, id2->ty)) + return 0; + return id1 == nil && id2 == nil; + case Tadtpick: + return tequal(t1->decl->dot->ty, t2->decl->dot->ty); + } + return 0; +} + +static Type* +tparent1(Type *t1, Type *t2) +{ + Type *t, *nt; + Decl *id, *id1, *id2, *idt; + + if(t1->kind == Tadt && t2->kind == Tadtpick) + t2 = t2->decl->dot->ty; + if(t1->kind == Tadtpick && t2->kind == Tadt) + t1 = t1->decl->dot->ty; + switch(t1->kind){ + default: + return t1; + case Tref: + case Tlist: + case Tarray: + case Tchan: + t = tparent1(t1->tof, t2->tof); + if(t == t1->tof) + return t1; + return mktype(&t1->src.start, &t1->src.stop, t1->kind, t, nil); + case Ttuple: + nt = nil; + id = nil; + for(id1 = t1->ids, id2 = t2->ids; id1 != nil && id2 != nil; id1 = id1->next, id2 = id2->next){ + t = tparent1(id1->ty, id2->ty); + if(t != id1->ty){ + if(nt == nil){ + nt = mktype(&t1->src.start, &t1->src.stop, Ttuple, nil, dupdecls(t1->ids)); + for(id = nt->ids, idt = t1->ids; idt != id1; id = id->next, idt = idt->next) + ; + } + id->ty = t; + } + if(id != nil) + id = id->next; + } + if(nt == nil) + return t1; + return nt; + case Tadtpick: + if(tequal(t1, t2)) + return t1; + return t1->decl->dot->ty; + } + return t1; +} + +Type* +tparent(Type *t1, Type *t2) +{ + if(tparent0(t1, t2)) + return tparent1(t1, t2); + return t1; +} + +/* + * make the tuple type used to initialize an exception type + */ +Type* +mkexbasetype(Type *t) +{ + Decl *id, *new, *last; + Type *nt; + + if(!t->cons) + fatal("mkexbasetype on non-constant"); + last = mkids(&t->decl->src, nil, tstring, nil); + last->store = Dfield; + nt = mktype(&t->src.start, &t->src.stop, Texception, nil, last); + nt->cons = 0; + new = mkids(&t->decl->src, nil, tint, nil); + new->store = Dfield; + last->next = new; + last = new; + for(id = t->ids; id != nil; id = id->next){ + new = allocmem(sizeof *id); + *new = *id; + new->cyc = 0; + last->next = new; + last = new; + } + last->next = nil; + return usetype(nt); +} + +/* + * make an instantiated exception type + */ +Type* +mkextype(Type *t) +{ + Type *nt; + + if(!t->cons) + fatal("mkextype on non-constant"); + if(t->tof != nil) + return t->tof; + nt = copytypeids(t); + nt->cons = 0; + t->tof = usetype(nt); + return t->tof; +} + +/* + * convert an instantiated exception type to it's underlying type + */ +Type* +mkextuptype(Type *t) +{ + Decl *id; + Type *nt; + + if(t->cons) + return t; + if(t->tof != nil) + return t->tof; + id = t->ids; + if(id == nil) + nt = t; + else if(id->next == nil) + nt = id->ty; + else{ + nt = copytypeids(t); + nt->cons = 0; + nt->kind = Ttuple; + } + t->tof = usetype(nt); + return t->tof; +} + +static void +ckfix(Type *t, double max) +{ + int p; + vlong k, x; + double s; + + s = t->val->rval; + if(max == 0.0) + k = ((vlong)1<<32)-1; + else + k = 2*(vlong)(max/s+0.5)+1; + x = 1; + for(p = 0; k > x; p++) + x *= 2; + if(p == 0 || p > 32){ + error(t->src.start, "cannot fit fixed type into an int"); + return; + } + if(p < 32) + t->val->rval /= (double)(1<<(32-p)); +} + +double +scale(Type *t) +{ + Node *n; + + if(t->kind == Tint || t->kind == Treal) + return 1.0; + if(t->kind != Tfix) + fatal("scale() on non fixed point type"); + n = t->val; + if(n->op != Oconst) + fatal("non constant scale"); + if(n->ty != treal) + fatal("non real scale"); + return n->rval; +} + +double +scale2(Type *f, Type *t) +{ + return scale(f)/scale(t); +} + +#define I(x) ((int)(x)) +#define V(x) ((Long)(x)) +#define D(x) ((double)(x)) + +/* put x in normal form */ +static int +nf(double x, int *mant) +{ + int p; + double m; + + p = 0; + m = x; + while(m >= 1){ + p++; + m /= 2; + } + while(m < 0.5){ + p--; + m *= 2; + } + m *= D(1<<16)*D(1<<15); + if(m >= D(0x7fffffff) - 0.5){ + *mant = 0x7fffffff; + return p; + } + *mant = I(m+0.5); + return p; +} + +static int +ispow2(double x) +{ + int m; + + nf(x, &m); + if(m != 1<<30) + return 0; + return 1; +} + +static int +round(double x, int n, int *m) +{ + if(n != 31) + fatal("not 31 in round"); + return nf(x, m); +} + +static int +fixmul2(double sx, double sy, double sr, int *rp, int *ra) +{ + int k, n, a; + double alpha; + + alpha = (sx*sy)/sr; + n = 31; + k = round(1/alpha, n, &a); + *rp = 1-k; + *ra = 0; + return IMULX; +} + +static int +fixdiv2(double sx, double sy, double sr, int *rp, int *ra) +{ + int k, n, b; + double beta; + + beta = sx/(sy*sr); + n = 31; + k = round(beta, n, &b); + *rp = k-1; + *ra = 0; + return IDIVX; +} + +static int +fixmul(double sx, double sy, double sr, int *rp, int *ra) +{ + int k, m, n, a, v; + vlong W; + double alpha, eps; + + alpha = (sx*sy)/sr; + if(ispow2(alpha)) + return fixmul2(sx, sy, sr, rp, ra); + n = 31; + k = round(1/alpha, n, &a); + m = n-k; + if(m < -n-1) + return IMOVW; /* result is zero whatever the values */ + v = 0; + W = 0; + eps = D(1<<m)/(alpha*D(a)) - 1; + if(eps < 0){ + v = a-1; + eps = -eps; + } + if(m < 0 && D(1<<n)*eps*D(a) >= D(a)-1+D(1<<m)) + W = (V(1)<<(-m)) - 1; + if(v != 0 || W != 0) + m = m<<2|(v != 0)<<1|(W != 0); + *rp = m; + *ra = a; + return v == 0 && W == 0 ? IMULX0: IMULX1; +} + +static int +fixdiv(double sx, double sy, double sr, int *rp, int *ra) +{ + int k, m, n, b, v; + vlong W; + double beta, eps; + + beta = sx/(sy*sr); + if(ispow2(beta)) + return fixdiv2(sx, sy, sr, rp, ra); + n = 31; + k = round(beta, n, &b); + m = k-n; + if(m <= -2*n) + return IMOVW; /* result is zero whatever the values */ + v = 0; + W = 0; + eps = (D(1<<m)*D(b))/beta - 1; + if(eps < 0) + v = 1; + if(m < 0) + W = (V(1)<<(-m)) - 1; + if(v != 0 || W != 0) + m = m<<2|(v != 0)<<1|(W != 0); + *rp = m; + *ra = b; + return v == 0 && W == 0 ? IDIVX0: IDIVX1; +} + +static int +fixcast(double sx, double sr, int *rp, int *ra) +{ + int op; + + op = fixmul(sx, 1.0, sr, rp, ra); + return op-IMULX+ICVTXX; +} + +int +fixop(int op, Type *tx, Type *ty, Type *tr, int *rp, int *ra) +{ + double sx, sy, sr; + + sx = scale(tx); + sy = scale(ty); + sr = scale(tr); + if(op == IMULX) + op = fixmul(sx, sy, sr, rp, ra); + else if(op == IDIVX) + op = fixdiv(sx, sy, sr, rp, ra); + else + op = fixcast(sx, sr, rp, ra); + return op; +} + +int +ispoly(Decl *d) +{ + Type *t; + + if(d == nil) + return 0; + t = d->ty; + if(t->kind == Tfn){ + if(t->polys != nil) + return 1; + if((d = d->dot) == nil) + return 0; + t = d->ty; + return t->kind == Tadt && t->polys != nil; + } + return 0; +} + +int +ispolyadt(Type *t) +{ + return (t->kind == Tadt || t->kind == Tadtpick) && t->polys != nil && !(t->flags & INST); +} + +Decl* +polydecl(Decl *ids) +{ + Decl *id; + Type *t; + + for(id = ids; id != nil; id = id->next){ + t = mktype(&id->src.start, &id->src.stop, Tpoly, nil, nil); + id->ty = t; + t->decl = id; + } + return ids; +} + +/* try to convert an expression tree to a type */ +Type* +exptotype(Node *n) +{ + Type *t, *tt; + Decl *d; + Typelist *tl; + Src *src; + + if(n == nil) + return nil; + t = nil; + switch(n->op){ + case Oname: + if((d = n->decl) != nil && d->store == Dtype) + t = d->ty; + break; + case Otype: + case Ochan: + t = n->ty; + break; + case Oref: + t = exptotype(n->left); + if(t != nil) + t = mktype(&n->src.start, &n->src.stop, Tref, t, nil); + break; + case Odot: + t = exptotype(n->left); + if(t != nil){ + d = namedot(t->tags, n->right->decl->sym); + if(d == nil) + t = nil; + else + t = d->ty; + } + if(t == nil) + t = exptotype(n->right); + break; + case Omdot: + t = exptotype(n->right); + break; + case Oindex: + t = exptotype(n->left); + if(t != nil){ + src = &n->src; + tl = nil; + for(n = n->right; n != nil; n = n->right){ + if(n->op == Oseq) + tt = exptotype(n->left); + else + tt = exptotype(n); + if(tt == nil) + return nil; + tl = addtype(tt, tl); + if(n->op != Oseq) + break; + } + t = mkinsttype(src, t, tl); + } + break; + } + return t; +} + +static char* +uname(Decl *im) +{ + Decl *p; + int n; + char *s; + + n = 0; + for(p = im; p != nil; p = p->next) + n += strlen(p->sym->name)+1; + s = allocmem(n); + strcpy(s, ""); + for(p = im; p != nil; p = p->next){ + strcat(s, p->sym->name); + if(p->next != nil) + strcat(s, "+"); + } + return s; +} + +/* check all implementation modules have consistent declarations + * and create their union if needed + */ +Decl* +modimp(Dlist *dl, Decl *im) +{ + Decl *u, *d, *dd, *ids, *dot, *last; + Sym *s; + Dlist *dl0; + long sg, sg0; + char buf[StrSize], *un; + + if(dl->next == nil) + return dl->d; + dl0 = dl; + sg0 = 0; + un = uname(im); + seprint(buf, buf+sizeof(buf), ".m.%s", un); + installids(Dglobal, mkids(&dl->d->src, enter(buf, 0), tnone, nil)); + u = dupdecl(dl->d); + u->sym = enter(un, 0); + u->sym->decl = u; + u->ty = mktype(&u->src.start, &u->src.stop, Tmodule, nil, nil); + u->ty->decl = u; + last = nil; + for( ; dl != nil; dl = dl->next){ + d = dl->d; + ids = d->ty->tof->ids; /* iface */ + if(ids != nil && ids->store == Dglobal) /* .mp */ + sg = sign(ids); + else + sg = 0; + if(dl == dl0) + sg0 = sg; + else if(sg != sg0) + error(d->src.start, "%s's module data not consistent with that of %s\n", d->sym->name, dl0->d->sym->name); + for(ids = d->ty->ids; ids != nil; ids = ids->next){ + s = ids->sym; + if(s->decl != nil && s->decl->scope >= scope){ + if(ids == s->decl){ + dd = dupdecl(ids); + if(u->ty->ids == nil) + u->ty->ids = dd; + else + last->next = dd; + last = dd; + continue; + } + dot = s->decl->dot; + if(s->decl->store != Dwundef && dot != nil && dot != d && isimpmod(dot->sym) && dequal(ids, s->decl, 1)) + ids->refs = s->decl->refs; + else + redecl(ids); + ids->init = s->decl->init; + } + } + } + u->ty = usetype(u->ty); + return u; +} + +static void +modres(Decl *d) +{ + Decl *ids, *id, *n, *i; + Type *t; + + for(ids = d->ty->ids; ids != nil; ids = ids->next){ + id = ids->sym->decl; + if(ids != id){ + n = ids->next; + i = ids->iface; + t = ids->ty; + *ids = *id; + ids->next = n; + ids->iface = i; + ids->ty = t; + } + } +} + +/* update the fields of duplicate declarations in other implementation modules + * and their union + */ +void +modresolve(void) +{ + Dlist *dl; + + dl = impdecls; + if(dl->next == nil) + return; + for( ; dl != nil; dl = dl->next) + modres(dl->d); + modres(impdecl); +} |
