summaryrefslogtreecommitdiff
path: root/limbo
diff options
context:
space:
mode:
Diffstat (limited to 'limbo')
-rw-r--r--limbo/NOTICE25
-rw-r--r--limbo/asm.c289
-rw-r--r--limbo/com.c1489
-rw-r--r--limbo/decls.c1367
-rw-r--r--limbo/dis.c638
-rw-r--r--limbo/dtocanon.c35
-rw-r--r--limbo/ecom.c2560
-rw-r--r--limbo/fns.h391
-rw-r--r--limbo/gen.c1097
-rw-r--r--limbo/lex.c1429
-rw-r--r--limbo/limbo.h701
-rw-r--r--limbo/limbo.y2032
-rw-r--r--limbo/mkfile41
-rw-r--r--limbo/nodes.c1538
-rw-r--r--limbo/optab.c658
-rw-r--r--limbo/optim.c1803
-rw-r--r--limbo/runt.h2012
-rw-r--r--limbo/sbl.c351
-rw-r--r--limbo/stubs.c590
-rw-r--r--limbo/typecheck.c3635
-rw-r--r--limbo/types.c4745
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);
+}