aboutsummaryrefslogtreecommitdiff
path: root/limbo
diff options
context:
space:
mode:
authorbhgv <bhgv.empire@gmail.com>2018-03-01 16:54:45 +0200
committerbhgv <bhgv.empire@gmail.com>2018-03-01 16:54:45 +0200
commitb786f20bbab5a59046aa78a2c6c2a11536497202 (patch)
tree0851ecdec889eb9b7ba3751cc04d4f0b474e4a9e /limbo
inferno-os tree was separated from the inferno-os-android (separated from the Android driver)
Diffstat (limited to 'limbo')
-rw-r--r--limbo/NOTICE25
-rw-r--r--limbo/asm.c308
-rw-r--r--limbo/asm.obin0 -> 33636 bytes
-rw-r--r--limbo/com.c1510
-rw-r--r--limbo/com.obin0 -> 90292 bytes
-rw-r--r--limbo/decls.c1366
-rw-r--r--limbo/decls.obin0 -> 96528 bytes
-rw-r--r--limbo/dis.c638
-rw-r--r--limbo/dis.obin0 -> 52496 bytes
-rw-r--r--limbo/dtocanon.c35
-rw-r--r--limbo/dtocanon.obin0 -> 4252 bytes
-rw-r--r--limbo/ecom.c2558
-rw-r--r--limbo/ecom.obin0 -> 127764 bytes
-rw-r--r--limbo/fns.h391
-rw-r--r--limbo/gen.c1096
-rw-r--r--limbo/gen.obin0 -> 78192 bytes
-rw-r--r--limbo/lex.c1453
-rw-r--r--limbo/lex.obin0 -> 94416 bytes
-rw-r--r--limbo/limbo.h702
-rw-r--r--limbo/limbo.y1995
-rw-r--r--limbo/mkfile41
-rw-r--r--limbo/nodes.c1538
-rw-r--r--limbo/nodes.obin0 -> 94316 bytes
-rw-r--r--limbo/optab.c658
-rw-r--r--limbo/optab.obin0 -> 18432 bytes
-rw-r--r--limbo/optim.c1803
-rw-r--r--limbo/optim.obin0 -> 94656 bytes
-rw-r--r--limbo/sbl.c352
-rw-r--r--limbo/sbl.obin0 -> 36820 bytes
-rw-r--r--limbo/stubs.c590
-rw-r--r--limbo/stubs.obin0 -> 50736 bytes
-rw-r--r--limbo/typecheck.c3631
-rw-r--r--limbo/typecheck.obin0 -> 193724 bytes
-rw-r--r--limbo/types.c4745
-rw-r--r--limbo/types.obin0 -> 253212 bytes
-rw-r--r--limbo/ut0
-rw-r--r--limbo/y.tab.c3052
-rw-r--r--limbo/y.tab.h90
-rw-r--r--limbo/y.tab.obin0 -> 103156 bytes
39 files changed, 28577 insertions, 0 deletions
diff --git a/limbo/NOTICE b/limbo/NOTICE
new file mode 100644
index 0000000..4d026aa
--- /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-2009 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 0000000..32de4aa
--- /dev/null
+++ b/limbo/asm.c
@@ -0,0 +1,308 @@
+#include "limbo.h"
+
+#ifdef ANDROID
+#undef malloc
+#undef free
+#undef realloc
+
+void* malloc_(size_t l){
+ return malloc(l);
+}
+
+void* realloc_(void*p, size_t l){
+ return realloc(p, l);
+}
+
+void free_(void* p) {
+ free(p);
+}
+#endif
+
+
+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,%ld\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+%ld,%ld\n", offset, (long)n->val);
+ else
+ Bprint(bout, "\tword\t@mp+%ld,%ld\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+%ld,0x%lux,\"", offset, sign(id));
+ else
+ Bprint(bout, "\text\t@mp+%ld,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%ld, %ld, %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\", %ld\n", d->sym->name, lab->inst->pc);
+ }
+ if(c->iwild == nil)
+ Bprint(bout, "\texctab\t*, %d\n", -1);
+ else
+ Bprint(bout, "\texctab\t*, %ld\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, "#%ld\n", in->pc);
+ Bprint(bout, "%I\n", in);
+ }
+}
diff --git a/limbo/asm.o b/limbo/asm.o
new file mode 100644
index 0000000..9bfdd42
--- /dev/null
+++ b/limbo/asm.o
Binary files differ
diff --git a/limbo/com.c b/limbo/com.c
new file mode 100644
index 0000000..1571045
--- /dev/null
+++ b/limbo/com.c
@@ -0,0 +1,1510 @@
+#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;
+ int valued;
+
+ 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();
+ valued = decl->ty->tof != tnone;
+ in = genrawop(&src, valued? IRAISE: IRET, nil, nil, nil);
+ popblock();
+ reach(decl->pc);
+ if(valued && in->reach)
+ 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;
+}
+
+static int
+nconstqual(Node *s1)
+{
+ Node *s2;
+ int n;
+
+ n = 0;
+ for(; s1 != nil; s1 = s1->right){
+ for(s2 = s1->left->left; s2 != nil; s2 = s2->right)
+ if(s2->left->op == Oconst)
+ n++;
+ }
+ return n;
+}
+
+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 || nconstqual(cn->right) != 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 && (n->left->op != Oname || n->left->decl != nildecl))
+ 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;
+}
+
+/*
+ * tail-recursive call
+ */
+static int
+trcom(Node *e, Node *ne, int nilret)
+{
+ Decl *d, *id;
+ Node *as, *a, *f, *n;
+ Inst *p;
+
+ if(1)
+ return 0; /* TO DO: should we enable this? */
+ 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/com.o b/limbo/com.o
new file mode 100644
index 0000000..5ddb0be
--- /dev/null
+++ b/limbo/com.o
Binary files differ
diff --git a/limbo/decls.c b/limbo/decls.c
new file mode 100644
index 0000000..3581ba2
--- /dev/null
+++ b/limbo/decls.c
@@ -0,0 +1,1366 @@
+#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 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 = "<nil>";
+ 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/decls.o b/limbo/decls.o
new file mode 100644
index 0000000..2d83d71
--- /dev/null
+++ b/limbo/decls.o
Binary files differ
diff --git a/limbo/dis.c b/limbo/dis.c
new file mode 100644
index 0000000..a42d0ad
--- /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];
+ vlong 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("%lld linkage bytes start %lld\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)
+{
+ vlong 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("%lld type descriptor bytes start %lld\n", Boffset(bout) - fileoff, fileoff);
+}
+
+void
+disvar(long size, Decl *d)
+{
+ vlong 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("%lld data bytes start %lld\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)
+{
+ vlong 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("%lld instruction bytes start %lld\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/dis.o b/limbo/dis.o
new file mode 100644
index 0000000..b0971c2
--- /dev/null
+++ b/limbo/dis.o
Binary files differ
diff --git a/limbo/dtocanon.c b/limbo/dtocanon.c
new file mode 100644
index 0000000..8a23283
--- /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/dtocanon.o b/limbo/dtocanon.o
new file mode 100644
index 0000000..2911383
--- /dev/null
+++ b/limbo/dtocanon.o
Binary files differ
diff --git a/limbo/ecom.c b/limbo/ecom.c
new file mode 100644
index 0000000..ab5d975
--- /dev/null
+++ b/limbo/ecom.c
@@ -0,0 +1,2558 @@
+#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 iftrue, 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, iftrue, b);
+ tfree(tn);
+ return bb;
+ }
+
+ if(debug['b'])
+ print("bcom %n %d\n", n, iftrue);
+
+ left = n->left;
+ right = n->right;
+ op = n->op;
+
+ switch(op){
+ case Onothing:
+ return b;
+ case Onot:
+ return bcom(n->left, !iftrue, b);
+ case Oandand:
+ if(!iftrue)
+ return oror(n, iftrue, b);
+ return andand(n, iftrue, b);
+ case Ooror:
+ if(!iftrue)
+ return andand(n, iftrue, b);
+ return oror(n, iftrue, 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(iftrue)
+ 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 iftrue, Inst *b)
+{
+ if(debug['b'])
+ print("andand %n\n", n);
+ b = bcom(n->left, iftrue, b);
+ b = bcom(n->right, iftrue, b);
+ return b;
+}
+
+Inst*
+oror(Node *n, int iftrue, Inst *b)
+{
+ Inst *bb;
+
+ if(debug['b'])
+ print("oror %n\n", n);
+ bb = bcom(n->left, !iftrue, nil);
+ b = bcom(n->right, iftrue, 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;
+ }
+}
+
+/*
+ * 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;
+ }
+}
+
+/*
+ * 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/ecom.o b/limbo/ecom.o
new file mode 100644
index 0000000..7eb06b7
--- /dev/null
+++ b/limbo/ecom.o
Binary files differ
diff --git a/limbo/fns.h b/limbo/fns.h
new file mode 100644
index 0000000..ddaa8a9
--- /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 0000000..fe77dc8
--- /dev/null
+++ b/limbo/gen.c
@@ -0,0 +1,1096 @@
+#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;
+ continue;
+ }
+ foldbranch(b);
+ 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 < 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/gen.o b/limbo/gen.o
new file mode 100644
index 0000000..3688ec9
--- /dev/null
+++ b/limbo/gen.o
Binary files differ
diff --git a/limbo/lex.c b/limbo/lex.c
new file mode 100644
index 0000000..d75e28f
--- /dev/null
+++ b/limbo/lex.c
@@ -0,0 +1,1453 @@
+#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,
+ "dynamic", Ldynamic,
+ "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(int israw)
+{
+ char *str;
+ int c, t, startlno;
+ Rune r;
+ int len, alloc;
+
+ alloc = 32;
+ len = 0;
+ str = allocmem(alloc * sizeof(str));
+ startlno = lineno;
+ for(;;){
+ c = getrune();
+ if(israw){
+ switch(c){
+ case '`':
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ case '\n':
+ lineno++;
+ linepos = Linestart;
+ break;
+ case Beof:
+ t = lineno;
+ lineno = startlno;
+ yyerror("end of file in raw string constant");
+ lineno = t;
+ yylval.tok.v.idval = enterstring(str, len);
+ return;
+ }
+ }else{
+ 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(0);
+ return Lsconst;
+ case '`':
+ lexstring(1);
+ 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/lex.o b/limbo/lex.o
new file mode 100644
index 0000000..f7dc984
--- /dev/null
+++ b/limbo/lex.o
Binary files differ
diff --git a/limbo/limbo.h b/limbo/limbo.h
new file mode 100644
index 0000000..fa3fd0d
--- /dev/null
+++ b/limbo/limbo.h
@@ -0,0 +1,702 @@
+#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 */
+ schar 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 oldcycles;
+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 0000000..a6ccab9
--- /dev/null
+++ b/limbo/limbo.y
@@ -0,0 +1,1995 @@
+%{
+#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
+ Ldynamic
+%%
+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 'y':
+ oldcycles = 1;
+ 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 *
+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 = 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 0000000..2dc2072
--- /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 0000000..27ca779
--- /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/nodes.o b/limbo/nodes.o
new file mode 100644
index 0000000..064582a
--- /dev/null
+++ b/limbo/nodes.o
Binary files differ
diff --git a/limbo/optab.c b/limbo/optab.c
new file mode 100644
index 0000000..77b086f
--- /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/optab.o b/limbo/optab.o
new file mode 100644
index 0000000..bd67633
--- /dev/null
+++ b/limbo/optab.o
Binary files differ
diff --git a/limbo/optim.c b/limbo/optim.c
new file mode 100644
index 0000000..324bed6
--- /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 "<nil>";
+ 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 < 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/optim.o b/limbo/optim.o
new file mode 100644
index 0000000..580ead3
--- /dev/null
+++ b/limbo/optim.o
Binary files differ
diff --git a/limbo/sbl.c b/limbo/sbl.c
new file mode 100644
index 0000000..5ffce98
--- /dev/null
+++ b/limbo/sbl.c
@@ -0,0 +1,352 @@
+#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;
+ src = nosrc;
+ 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%ld.", 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%ld\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, "%ld\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/sbl.o b/limbo/sbl.o
new file mode 100644
index 0000000..2d9627e
--- /dev/null
+++ b/limbo/sbl.o
Binary files differ
diff --git a/limbo/stubs.c b/limbo/stubs.c
new file mode 100644
index 0000000..14bd13d
--- /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){
+ adtstub(globals);
+ modstub(globals);
+ }
+ 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, %smodlen);\n", emitcode, 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("\tvoid *r;\n");
+ print("\n\tr = *f->ret;\n\t*f->ret = H;\n\tdestroy(r);\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 Tadtpick:
+ return ctprint(buf, end, t->decl->dot->ty);
+ 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/stubs.o b/limbo/stubs.o
new file mode 100644
index 0000000..3fb53ff
--- /dev/null
+++ b/limbo/stubs.o
Binary files differ
diff --git a/limbo/typecheck.c b/limbo/typecheck.c
new file mode 100644
index 0000000..f310926
--- /dev/null
+++ b/limbo/typecheck.c
@@ -0,0 +1,3631 @@
+#include "limbo.h"
+#include "y.tab.h"
+
+Node **labstack;
+int labdep;
+static Node* inexcept;
+static Decl* fndec;
+
+void checkraises(Node *n);
+
+
+#define rewind rewind_
+
+
+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 = nil;
+ if(d->dot)
+ m = d->dot->sym;
+ else if(impmods)
+ m = impmods->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;
+ 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;
+ }
+}
+
+/*
+ * 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(oldcycles && 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:
+ if(!oldcycles)
+ return 1;
+ 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 ignored
+ */
+Node*
+passimplicit(Node *fn, Node *args)
+{
+ Node *n;
+ Type *t;
+
+ t = fn->ty;
+ n = fn->left;
+ if(t->ids == nil || !t->ids->implicit){
+ if(!isfnrefty(t) && hasside(n, 1))
+ nwarn(fn, "result of expression %V ignored", n);
+ return args;
+ }
+ 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)
+ break;
+ 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 its 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/typecheck.o b/limbo/typecheck.o
new file mode 100644
index 0000000..76d6027
--- /dev/null
+++ b/limbo/typecheck.o
Binary files differ
diff --git a/limbo/types.c b/limbo/types.c
new file mode 100644
index 0000000..6d23111
--- /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 customized check for overlapping definitions.
+ */
+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(v != eqset && 0)
+ 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;
+ }
+}
+
+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:
+ v = 0;
+ for(tl = t->u.tlist; tl != nil; tl = tl->nxt)
+ if(tpolys(tl->t)){
+ v = 1;
+ break;
+ }
+ if(v == 0)
+ 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;
+ }
+}
+
+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 its 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
+fround(double x, int n, int *m)
+{
+ if(n != 31)
+ fatal("not 31 in fround");
+ 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 = fround(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 = fround(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 = fround(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 = fround(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);
+}
diff --git a/limbo/types.o b/limbo/types.o
new file mode 100644
index 0000000..05071fc
--- /dev/null
+++ b/limbo/types.o
Binary files differ
diff --git a/limbo/ut b/limbo/ut
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/limbo/ut
diff --git a/limbo/y.tab.c b/limbo/y.tab.c
new file mode 100644
index 0000000..95a915e
--- /dev/null
+++ b/limbo/y.tab.c
@@ -0,0 +1,3052 @@
+
+#line 2 "limbo.y"
+#include "limbo.h"
+
+#line 5 "limbo.y"
+typedef union
+{
+ struct{
+ Src src;
+ union{
+ Sym *idval;
+ Long ival;
+ Real rval;
+ }v;
+ }tok;
+ Decl *ids;
+ Node *node;
+ Type *type;
+ Typelist *types;
+} YYSTYPE;
+extern int yyerrflag;
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 150
+#endif
+YYSTYPE yylval;
+YYSTYPE yyval;
+#define Landeq 57346
+#define Loreq 57347
+#define Lxoreq 57348
+#define Llsheq 57349
+#define Lrsheq 57350
+#define Laddeq 57351
+#define Lsubeq 57352
+#define Lmuleq 57353
+#define Ldiveq 57354
+#define Lmodeq 57355
+#define Lexpeq 57356
+#define Ldeclas 57357
+#define Lload 57358
+#define Loror 57359
+#define Landand 57360
+#define Lcons 57361
+#define Leq 57362
+#define Lneq 57363
+#define Lleq 57364
+#define Lgeq 57365
+#define Llsh 57366
+#define Lrsh 57367
+#define Lexp 57368
+#define Lcomm 57369
+#define Linc 57370
+#define Ldec 57371
+#define Lof 57372
+#define Lref 57373
+#define Lif 57374
+#define Lelse 57375
+#define Lfn 57376
+#define Lexcept 57377
+#define Lraises 57378
+#define Lmdot 57379
+#define Lto 57380
+#define Lor 57381
+#define Lrconst 57382
+#define Lconst 57383
+#define Lid 57384
+#define Ltid 57385
+#define Lsconst 57386
+#define Llabs 57387
+#define Lnil 57388
+#define Llen 57389
+#define Lhd 57390
+#define Ltl 57391
+#define Ltagof 57392
+#define Limplement 57393
+#define Limport 57394
+#define Linclude 57395
+#define Lcon 57396
+#define Ltype 57397
+#define Lmodule 57398
+#define Lcyclic 57399
+#define Ladt 57400
+#define Larray 57401
+#define Llist 57402
+#define Lchan 57403
+#define Lself 57404
+#define Ldo 57405
+#define Lwhile 57406
+#define Lfor 57407
+#define Lbreak 57408
+#define Lalt 57409
+#define Lcase 57410
+#define Lpick 57411
+#define Lcont 57412
+#define Lreturn 57413
+#define Lexit 57414
+#define Lspawn 57415
+#define Lraise 57416
+#define Lfix 57417
+#define Ldynamic 57418
+#define YYEOFCODE 1
+#define YYERRCODE 2
+
+#line 1596 "limbo.y"
+
+
+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 'y':
+ oldcycles = 1;
+ 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 *
+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 = 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;
+}
+short yyexca[] =
+{-1, 1,
+ 1, -1,
+ -2, 0,
+-1, 3,
+ 1, 3,
+ -2, 0,
+-1, 17,
+ 39, 88,
+ 50, 62,
+ 54, 88,
+ 99, 62,
+ -2, 250,
+-1, 209,
+ 59, 29,
+ 71, 29,
+ -2, 0,
+-1, 228,
+ 1, 2,
+ -2, 0,
+-1, 271,
+ 50, 176,
+ -2, 255,
+-1, 305,
+ 59, 41,
+ 71, 41,
+ 91, 41,
+ -2, 0,
+-1, 307,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 376,
+ 50, 62,
+ 99, 62,
+ -2, 250,
+-1, 377,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 383,
+ 53, 71,
+ 54, 71,
+ -2, 110,
+-1, 385,
+ 53, 72,
+ 54, 72,
+ -2, 112,
+-1, 417,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 424,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 439,
+ 53, 71,
+ 54, 71,
+ -2, 111,
+-1, 440,
+ 53, 72,
+ 54, 72,
+ -2, 113,
+-1, 448,
+ 71, 277,
+ 99, 277,
+ -2, 163,
+-1, 465,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 482,
+ 50, 126,
+ 99, 126,
+ -2, 239,
+-1, 487,
+ 71, 274,
+ -2, 0,
+-1, 499,
+ 59, 47,
+ 71, 47,
+ -2, 0,
+-1, 504,
+ 59, 41,
+ 71, 41,
+ 91, 41,
+ -2, 0,
+-1, 510,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 544,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 550,
+ 71, 154,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 558,
+ 56, 59,
+ 62, 59,
+ -2, 62,
+-1, 564,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 569,
+ 71, 157,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 573,
+ 72, 176,
+ -2, 163,
+-1, 592,
+ 71, 160,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 598,
+ 71, 168,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 602,
+ 72, 175,
+ 85, 150,
+ 86, 150,
+ 87, 150,
+ 89, 150,
+ 90, 150,
+ 91, 150,
+ -2, 0,
+-1, 605,
+ 50, 62,
+ 56, 171,
+ 62, 171,
+ 99, 62,
+ -2, 250,
+};
+#define YYNPROD 282
+#define YYPRIVATE 57344
+#define YYLAST 2674
+short yyact[] =
+{
+ 375, 587, 449, 360, 501, 380, 408, 307, 365, 311,
+ 355, 447, 428, 295, 445, 184, 268, 15, 83, 8,
+ 101, 49, 82, 4, 317, 12, 42, 23, 48, 77,
+ 78, 79, 109, 35, 196, 51, 540, 487, 359, 6,
+ 482, 3, 6, 396, 455, 454, 361, 14, 346, 21,
+ 14, 378, 349, 290, 419, 283, 117, 223, 327, 31,
+ 284, 224, 221, 46, 461, 111, 104, 11, 513, 595,
+ 40, 208, 597, 185, 163, 164, 165, 166, 167, 168,
+ 169, 170, 171, 172, 173, 174, 175, 43, 116, 581,
+ 181, 182, 183, 71, 10, 284, 203, 10, 206, 284,
+ 92, 345, 345, 418, 207, 345, 32, 113, 32, 543,
+ 118, 291, 291, 44, 284, 118, 424, 423, 422, 481,
+ 426, 425, 427, 229, 230, 231, 232, 233, 234, 235,
+ 236, 237, 238, 239, 240, 509, 242, 243, 244, 245,
+ 246, 247, 248, 249, 250, 251, 252, 253, 254, 255,
+ 256, 257, 258, 259, 260, 261, 262, 263, 185, 6,
+ 200, 271, 228, 479, 478, 305, 22, 14, 22, 269,
+ 477, 210, 434, 265, 216, 192, 193, 277, 561, 22,
+ 406, 282, 306, 420, 192, 193, 226, 285, 405, 205,
+ 403, 350, 286, 93, 555, 267, 215, 47, 411, 26,
+ 411, 25, 24, 508, 309, 308, 300, 591, 568, 63,
+ 553, 549, 227, 5, 10, 416, 415, 414, 192, 193,
+ 75, 74, 69, 68, 73, 532, 18, 54, 55, 62,
+ 60, 61, 64, 521, 294, 486, 213, 531, 192, 193,
+ 464, 310, 323, 302, 65, 66, 67, 111, 320, 395,
+ 19, 379, 23, 562, 315, 576, 86, 204, 296, 504,
+ 76, 191, 494, 84, 313, 475, 89, 6, 463, 394,
+ 17, 357, 192, 193, 18, 14, 91, 81, 192, 193,
+ 280, 185, 43, 217, 2, 337, 13, 444, 209, 113,
+ 80, 526, 19, 96, 575, 181, 335, 491, 87, 88,
+ 85, 333, 314, 600, 212, 192, 193, 19, 44, 610,
+ 192, 193, 45, 107, 76, 344, 18, 377, 19, 489,
+ 483, 354, 10, 353, 298, 608, 86, 114, 389, 190,
+ 33, 115, 484, 84, 214, 387, 89, 385, 114, 398,
+ 535, 383, 115, 194, 388, 197, 91, 81, 321, 560,
+ 352, 279, 322, 202, 45, 399, 400, 5, 18, 600,
+ 381, 503, 536, 495, 218, 599, 496, 412, 87, 88,
+ 85, 417, 432, 558, 489, 185, 539, 489, 500, 431,
+ 596, 433, 430, 593, 76, 489, 489, 437, 102, 86,
+ 435, 584, 570, 22, 19, 448, 84, 337, 182, 89,
+ 489, 72, 410, 440, 313, 489, 551, 439, 590, 91,
+ 81, 490, 333, 476, 17, 429, 70, 288, 18, 75,
+ 74, 45, 469, 73, 465, 18, 474, 438, 472, 496,
+ 13, 87, 88, 85, 287, 503, 266, 156, 281, 119,
+ 589, 103, 86, 192, 193, 448, 39, 76, 90, 84,
+ 158, 90, 89, 506, 289, 483, 564, 292, 293, 485,
+ 493, 36, 91, 81, 514, 588, 462, 197, 185, 413,
+ 404, 473, 167, 280, 326, 222, 519, 105, 518, 511,
+ 512, 316, 319, 507, 87, 88, 85, 86, 448, 523,
+ 519, 525, 524, 34, 84, 397, 529, 89, 176, 522,
+ 76, 90, 328, 90, 528, 533, 393, 91, 81, 180,
+ 541, 90, 332, 179, 542, 537, 519, 329, 548, 176,
+ 199, 552, 90, 177, 198, 550, 195, 402, 178, 87,
+ 88, 85, 157, 442, 160, 330, 161, 162, 556, 559,
+ 437, 313, 441, 325, 225, 76, 159, 158, 566, 342,
+ 343, 573, 565, 571, 567, 569, 347, 473, 137, 138,
+ 139, 136, 134, 519, 341, 579, 41, 201, 580, 602,
+ 573, 582, 583, 544, 382, 324, 220, 386, 219, 545,
+ 557, 471, 470, 467, 410, 421, 592, 391, 392, 39,
+ 594, 473, 186, 573, 598, 601, 90, 136, 134, 520,
+ 603, 19, 607, 241, 401, 29, 609, 27, 356, 534,
+ 304, 473, 90, 364, 120, 90, 90, 30, 90, 28,
+ 1, 460, 270, 272, 86, 90, 312, 182, 167, 538,
+ 578, 84, 577, 409, 89, 499, 498, 586, 585, 90,
+ 90, 16, 407, 194, 91, 81, 303, 351, 9, 473,
+ 547, 546, 443, 517, 50, 516, 452, 211, 7, 446,
+ 334, 456, 264, 457, 297, 502, 87, 88, 85, 52,
+ 53, 56, 96, 367, 108, 59, 72, 106, 468, 112,
+ 57, 58, 76, 63, 143, 142, 140, 141, 137, 138,
+ 139, 136, 134, 90, 75, 74, 69, 273, 73, 110,
+ 18, 54, 55, 62, 60, 61, 64, 20, 90, 37,
+ 38, 0, 0, 0, 90, 0, 276, 492, 274, 275,
+ 67, 497, 140, 141, 137, 138, 139, 136, 134, 0,
+ 0, 0, 0, 0, 76, 90, 0, 0, 0, 0,
+ 0, 0, 497, 0, 0, 90, 90, 0, 0, 0,
+ 0, 0, 0, 0, 0, 530, 0, 0, 90, 90,
+ 0, 0, 90, 122, 123, 124, 125, 126, 127, 128,
+ 129, 130, 131, 132, 133, 135, 0, 155, 154, 153,
+ 152, 151, 150, 148, 149, 144, 145, 146, 147, 143,
+ 142, 140, 141, 137, 138, 139, 136, 134, 0, 339,
+ 0, 90, 0, 0, 0, 0, 86, 0, 0, 0,
+ 90, 0, 0, 84, 90, 0, 89, 0, 98, 90,
+ 0, 90, 0, 0, 0, 362, 91, 81, 0, 0,
+ 90, 0, 0, 0, 0, 0, 90, 0, 0, 0,
+ 50, 95, 0, 97, 94, 99, 0, 100, 87, 88,
+ 85, 0, 0, 0, 0, 52, 53, 56, 338, 0,
+ 0, 59, 374, 0, 76, 0, 57, 58, 0, 63,
+ 366, 0, 0, 0, 0, 90, 0, 0, 0, 90,
+ 75, 74, 376, 68, 73, 0, 18, 54, 55, 62,
+ 60, 61, 64, 363, 505, 362, 0, 0, 13, 0,
+ 90, 0, 0, 0, 65, 66, 67, 0, 0, 0,
+ 50, 368, 0, 90, 0, 369, 370, 373, 371, 372,
+ 76, 0, 0, 0, 0, 52, 53, 56, 0, 0,
+ 0, 59, 374, 0, 0, 0, 57, 58, 0, 63,
+ 366, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 74, 376, 68, 73, 0, 18, 54, 55, 62,
+ 60, 61, 64, 363, 466, 362, 0, 0, 13, 0,
+ 0, 0, 0, 0, 65, 66, 67, 0, 0, 0,
+ 50, 368, 0, 0, 0, 369, 370, 373, 371, 372,
+ 76, 0, 0, 0, 0, 52, 53, 56, 0, 0,
+ 0, 59, 374, 0, 0, 0, 57, 58, 0, 63,
+ 366, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 74, 376, 68, 73, 0, 18, 54, 55, 62,
+ 60, 61, 64, 363, 436, 362, 0, 0, 13, 0,
+ 0, 0, 0, 0, 65, 66, 67, 0, 0, 0,
+ 50, 368, 0, 0, 0, 369, 370, 373, 371, 372,
+ 76, 0, 0, 0, 0, 52, 53, 56, 0, 0,
+ 0, 59, 374, 0, 0, 0, 57, 58, 0, 63,
+ 366, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 74, 376, 68, 73, 0, 18, 54, 55, 62,
+ 60, 61, 64, 363, 358, 604, 0, 0, 13, 0,
+ 0, 0, 0, 0, 65, 66, 67, 0, 0, 0,
+ 50, 368, 0, 0, 0, 369, 370, 373, 371, 372,
+ 76, 0, 0, 0, 0, 52, 53, 606, 0, 0,
+ 0, 59, 374, 0, 0, 0, 57, 58, 0, 63,
+ 366, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 75, 74, 605, 68, 73, 0, 18, 54, 55, 62,
+ 60, 61, 64, 363, 572, 0, 0, 0, 13, 0,
+ 0, 0, 0, 0, 65, 66, 67, 0, 0, 50,
+ 0, 368, 0, 0, 0, 369, 370, 373, 371, 372,
+ 76, 0, 0, 0, 52, 53, 450, 0, 0, 0,
+ 59, 374, 0, 0, 0, 57, 58, 0, 63, 366,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 75,
+ 74, 376, 68, 73, 0, 18, 54, 55, 62, 60,
+ 61, 64, 363, 362, 0, 0, 0, 13, 0, 0,
+ 0, 0, 0, 65, 66, 67, 0, 0, 50, 0,
+ 368, 0, 0, 0, 369, 370, 373, 371, 372, 76,
+ 0, 0, 0, 52, 53, 56, 0, 0, 0, 59,
+ 374, 0, 0, 0, 57, 58, 0, 63, 366, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 75, 74,
+ 376, 68, 73, 0, 18, 54, 55, 62, 60, 61,
+ 64, 363, 451, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 65, 66, 67, 0, 0, 50, 0, 368,
+ 0, 0, 0, 369, 370, 373, 371, 372, 76, 0,
+ 0, 0, 52, 53, 450, 86, 0, 0, 59, 72,
+ 0, 0, 84, 57, 58, 89, 63, 0, 0, 0,
+ 0, 0, 0, 0, 0, 91, 81, 75, 74, 69,
+ 68, 73, 0, 18, 54, 55, 62, 60, 61, 64,
+ 0, 0, 0, 50, 0, 0, 0, 87, 88, 85,
+ 318, 65, 66, 67, 0, 0, 0, 0, 52, 53,
+ 56, 0, 0, 76, 59, 72, 0, 76, 278, 57,
+ 58, 0, 63, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 75, 74, 69, 68, 73, 0, 18,
+ 54, 55, 62, 60, 61, 64, 0, 0, 50, 0,
+ 0, 0, 0, 0, 0, 0, 0, 65, 66, 67,
+ 0, 0, 0, 52, 53, 56, 0, 0, 0, 59,
+ 72, 0, 0, 76, 57, 58, 0, 63, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 75, 74,
+ 69, 68, 73, 0, 18, 54, 55, 62, 60, 61,
+ 64, 0, 0, 0, 52, 53, 56, 0, 0, 0,
+ 59, 72, 65, 66, 67, 57, 58, 0, 63, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 76, 75,
+ 74, 69, 68, 73, 0, 18, 54, 55, 62, 60,
+ 61, 64, 0, 0, 0, 0, 0, 0, 0, 0,
+ 86, 0, 0, 65, 66, 67, 0, 84, 0, 0,
+ 89, 0, 98, 0, 0, 0, 0, 0, 0, 76,
+ 91, 81, 0, 0, 0, 0, 0, 0, 0, 86,
+ 0, 0, 0, 0, 0, 95, 84, 97, 94, 89,
+ 0, 98, 87, 88, 85, 0, 0, 0, 0, 91,
+ 81, 0, 0, 0, 0, 0, 0, 0, 76, 0,
+ 0, 0, 0, 0, 0, 0, 459, 458, 0, 0,
+ 100, 87, 88, 85, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 76, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 563, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 153, 152, 151, 150, 148, 149,
+ 144, 145, 146, 147, 143, 142, 140, 141, 137, 138,
+ 139, 136, 134, 0, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 554, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 151,
+ 150, 148, 149, 144, 145, 146, 147, 143, 142, 140,
+ 141, 137, 138, 139, 136, 134, 0, 0, 0, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 527, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 150, 148, 149, 144, 145, 146,
+ 147, 143, 142, 140, 141, 137, 138, 139, 136, 134,
+ 0, 0, 0, 0, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 480, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 0, 0, 0, 0, 0, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 348, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 144, 145, 146, 147, 143, 142,
+ 140, 141, 137, 138, 139, 136, 134, 0, 0, 0,
+ 0, 0, 0, 0, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 340, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 301, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 299, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 189, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 188, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 122,
+ 123, 124, 125, 126, 127, 128, 129, 130, 131, 132,
+ 133, 135, 187, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134, 0, 86, 0, 0, 0, 86,
+ 0, 0, 84, 86, 0, 89, 84, 0, 0, 89,
+ 384, 0, 0, 89, 0, 91, 81, 0, 0, 91,
+ 390, 0, 0, 91, 81, 0, 0, 121, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 87, 88, 85,
+ 0, 87, 88, 85, 0, 87, 88, 85, 0, 0,
+ 0, 0, 0, 76, 0, 0, 0, 76, 0, 0,
+ 0, 76, 122, 123, 124, 125, 126, 127, 128, 129,
+ 130, 131, 132, 133, 135, 0, 155, 154, 153, 152,
+ 151, 150, 148, 149, 144, 145, 146, 147, 143, 142,
+ 140, 141, 137, 138, 139, 136, 134, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 135, 574, 155,
+ 154, 153, 152, 151, 150, 148, 149, 144, 145, 146,
+ 147, 143, 142, 140, 141, 137, 138, 139, 136, 134,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ 135, 515, 155, 154, 153, 152, 151, 150, 148, 149,
+ 144, 145, 146, 147, 143, 142, 140, 141, 137, 138,
+ 139, 136, 134, 0, 0, 0, 122, 123, 124, 125,
+ 126, 127, 128, 129, 130, 131, 132, 133, 135, 488,
+ 155, 154, 153, 152, 151, 150, 148, 149, 144, 145,
+ 146, 147, 143, 142, 140, 141, 137, 138, 139, 136,
+ 134, 0, 0, 0, 336, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 135, 0, 155,
+ 154, 153, 152, 151, 150, 148, 149, 144, 145, 146,
+ 147, 143, 142, 140, 141, 137, 138, 139, 136, 134,
+ 0, 0, 0, 331, 122, 123, 124, 125, 126, 127,
+ 128, 129, 130, 131, 132, 133, 135, 0, 155, 154,
+ 153, 152, 151, 150, 148, 149, 144, 145, 146, 147,
+ 143, 142, 140, 141, 137, 138, 139, 136, 134, 0,
+ 510, 122, 123, 124, 125, 126, 127, 128, 129, 130,
+ 131, 132, 133, 135, 0, 155, 154, 153, 152, 151,
+ 150, 148, 149, 144, 145, 146, 147, 143, 142, 140,
+ 141, 137, 138, 139, 136, 134, 0, 453, 122, 123,
+ 124, 125, 126, 127, 128, 129, 130, 131, 132, 133,
+ 135, 0, 155, 154, 153, 152, 151, 150, 148, 149,
+ 144, 145, 146, 147, 143, 142, 140, 141, 137, 138,
+ 139, 136, 134, 155, 154, 153, 152, 151, 150, 148,
+ 149, 144, 145, 146, 147, 143, 142, 140, 141, 137,
+ 138, 139, 136, 134
+};
+short yypact[] =
+{
+ 211,-1000, 334, 355,-1000, 130,-1000,-1000, 129, 127,
+ 603, 601, 9, 269, 443,-1000, 407, 525,-1000, 253,
+ -36, 125,-1000,-1000,-1000,-1000,-1000,1421,1421,1421,
+1421,2216, 562, 121, 767, 336, 382, -21, 427, 279,
+-1000, 334, 16,-1000,-1000,-1000, 380,-1000,2215,-1000,
+ 378, 493,1462,1462,1462,1462,1462,1462,1462,1462,
+1462,1462,1462,1462,1462, 478, 483, 468,1462, 163,
+1462,-1000,1421,-1000,-1000,-1000, 553,2160,2105,2050,
+ 257,-1000,-1000,-1000,2216, 481,2216, 479, 475, 525,
+-1000, 526,-1000,-1000,2216,1421, 185,1421, 32, 218,
+ 525, 234, 295, 525, 213,2216, 538, 536, -37,-1000,
+ 425, 7, -38,-1000,-1000,-1000, 502,-1000, 253,-1000,
+ 355,-1000,1421,1421,1421,1421,1421,1421,1421,1421,
+1421,1421,1421,1421, 599,1421,1421,1421,1421,1421,
+1421,1421,1421,1421,1421,1421,1421,1421,1421,1421,
+1421,1421,1421,1421,1421,1421,1421,1421, 377, 362,
+ 637,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,-1000,
+-1000,-1000,-1000,-1000,-1000,-1000,1366, 291, 210,2216,
+1421,-1000,-1000,-1000, 15,2614,1421,-1000,-1000,-1000,
+-1000,1421, 375, 358, 390,2216, 13, 390,2216,2216,
+ 550, 585, 252,1995,-1000,1421,1940,-1000,2216, 608,
+ 95,-1000,-1000, 134, 253,-1000,-1000, 334, 390,-1000,
+-1000, 268,1306,1306, 289,-1000,-1000,-1000, 355,2614,
+2614,2614,2614,2614,2614,2614,2614,2614,2614,2614,
+2614,1421,2614, 560, 560, 560, 560, 524, 524, 690,
+ 690, 654, 654, 654, 654,1898,1898,1845,1791,1737,
+1684,1684,1630,2635, 535, -39,-1000, 396, 501, 424,
+ -41,2614,-1000,1462, 457, 472,2216,2501, 467,1462,
+1421, 390,2462,-1000,1421, 759,1885, 523, 508, 390,
+-1000,2216, 390, 390, 336, 6, 390,2216,-1000,-1000,
+1830,-1000, 12, 120,-1000, 606, 201,1033,-1000,-1000,
+ 11, 180,-1000, 298, 534,-1000, 390,-1000,2224, 390,
+-1000,-1000,-1000,2614,-1000,-1000,1421, 637,2220, 403,
+ 390, 461, 199,-1000, 178, -56, 450,2614,1421,-1000,
+-1000, 585, 585, 390,-1000, 448,-1000, 390,-1000, 118,
+-1000,-1000, 420, 116,-1000, 109,-1000, 334,-1000,-1000,
+-1000, 419, 145,-1000, 4, 111, 546, 31, 356, 356,
+1421,1421,1421, 100,1421,2614, 163, 963,-1000,-1000,
+ 334,-1000,-1000,-1000,2216,-1000, 390, 500, 491,2614,
+1462, 390, 390, 217,1310,-1000,1421,2216,2577, 3,
+ 2, 390,2216,-1000,1530,-1000, -23,-1000,-1000,-1000,
+ 416, 198, 169,1501,-1000,-1000,-1000, 893, 544,2216,
+-1000,1421, 543, 542,1241,1421, 195, 354, 98,-1000,
+ 92, 91,1775, 47,-1000, 0,-1000,-1000, 270,-1000,
+-1000,-1000,-1000, 390,1310, 164, -62,-1000,2424, 349,
+1462,-1000, 390,-1000,-1000,-1000, 390, 225,2216,1421,
+-1000, 192, 287, 376, 189, 823, 402,1421, 131,2540,
+1421,1421, -18, 414,2371,1310, 583,-1000,-1000,-1000,
+-1000,-1000,-1000, 350,-1000, 162,-1000,1310,1421,1310,
+1421,-1000, 219,1720, 334,1421,2216, 165, 154, 607,
+-1000, 278, 306,-1000, 606,-1000, 317, -4,-1000,1421,
+1241, 37, 533, 540,-1000,1310, 140,-1000, 344,2424,
+1421,-1000,-1000,2614,-1000,2614,-1000,-1000, 139,1665,
+ 122,-1000,-1000, 314, 302,-1000, 290, 107, 183,-1000,
+-1000,1610, 408,1421,1241,1421, 137,-1000, 330,-1000,
+1172,-1000,2318,-1000,-1000,-1000, 232, 405,-1000, 193,
+-1000,-1000,1310,-1000,1241, 17,-1000, 531,-1000,1172,
+-1000, 329, 145,2424, 406,-1000,-1000, 136,-1000, 321,
+-1000,1421, -3, 318,-1000, 1,-1000, 303,-1000,-1000,
+-1000,-1000,1172,-1000, 529,-1000,-1000,-1000,1103,-1000,
+ 406, 263,1241, 247, 145, 163,1462,-1000,-1000,-1000,
+-1000
+};
+short yypgo[] =
+{
+ 0, 258, 710, 709, 33, 24, 416, 22, 18, 46,
+ 707, 699, 679, 34, 677, 674, 32, 673, 12, 4,
+ 665, 70, 8, 0, 21, 35, 15, 662, 660, 93,
+ 25, 67, 26, 14, 659, 11, 2, 38, 41, 23,
+ 658, 657, 3, 7, 655, 653, 651, 650, 648, 19,
+ 647, 646, 642, 10, 641, 638, 637, 1, 636, 635,
+ 633, 6, 5, 632, 630, 629, 17, 20, 626, 9,
+ 623, 16, 622, 621, 13, 620, 614, 613
+};
+short yyr1[] =
+{
+ 0, 76, 75, 75, 38, 38, 39, 39, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39, 30, 30, 37,
+ 37, 37, 37, 37, 37, 37, 66, 66, 48, 51,
+ 51, 51, 50, 50, 50, 50, 50, 49, 49, 73,
+ 73, 53, 53, 53, 52, 52, 52, 62, 62, 61,
+ 61, 60, 58, 58, 58, 59, 59, 59, 19, 20,
+ 20, 9, 10, 10, 6, 6, 74, 74, 74, 74,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 7, 7, 8, 8, 13, 13, 21, 21,
+ 2, 2, 2, 3, 3, 4, 4, 14, 14, 15,
+ 15, 16, 16, 16, 16, 11, 12, 12, 12, 12,
+ 5, 5, 5, 5, 40, 67, 67, 67, 41, 41,
+ 41, 54, 54, 43, 43, 43, 77, 77, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
+ 17, 17, 18, 18, 44, 45, 45, 46, 47, 47,
+ 63, 64, 64, 36, 36, 36, 36, 36, 55, 56,
+ 56, 57, 57, 57, 57, 22, 22, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,
+ 23, 23, 23, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 25, 25, 25,
+ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+ 29, 29, 31, 72, 72, 71, 71, 70, 70, 70,
+ 70, 65, 65, 32, 32, 32, 32, 27, 27, 28,
+ 28, 26, 26, 33, 33, 34, 34, 35, 35, 69,
+ 68, 68
+};
+short yyr2[] =
+{
+ 0, 0, 5, 1, 1, 2, 2, 1, 1, 2,
+ 2, 4, 4, 4, 4, 4, 6, 1, 3, 3,
+ 5, 5, 4, 6, 5, 1, 4, 7, 6, 0,
+ 2, 1, 4, 2, 5, 5, 1, 8, 11, 0,
+ 4, 0, 2, 1, 1, 1, 5, 0, 2, 5,
+ 4, 4, 2, 2, 1, 2, 4, 4, 1, 1,
+ 3, 1, 1, 3, 6, 4, 1, 2, 3, 4,
+ 1, 1, 1, 3, 6, 2, 3, 3, 3, 3,
+ 4, 1, 1, 4, 3, 6, 1, 3, 0, 3,
+ 3, 3, 5, 1, 3, 1, 5, 0, 1, 1,
+ 3, 3, 3, 3, 3, 1, 1, 1, 3, 3,
+ 2, 3, 2, 3, 4, 4, 2, 0, 3, 2,
+ 4, 2, 4, 0, 2, 2, 3, 5, 2, 2,
+ 4, 3, 4, 6, 2, 5, 7, 10, 6, 8,
+ 3, 3, 3, 3, 3, 6, 5, 8, 2, 8,
+ 0, 2, 0, 1, 2, 2, 4, 2, 2, 4,
+ 2, 2, 4, 1, 3, 1, 3, 1, 2, 2,
+ 4, 1, 1, 3, 1, 0, 1, 1, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 6, 8, 7,
+ 5, 3, 6, 4, 2, 2, 2, 1, 4, 3,
+ 3, 3, 4, 6, 2, 2, 1, 1, 1, 6,
+ 1, 1, 3, 1, 3, 1, 1, 1, 3, 3,
+ 2, 1, 0, 1, 1, 3, 3, 0, 1, 1,
+ 2, 1, 3, 1, 2, 1, 3, 1, 3, 2,
+ 2, 4
+};
+short yychk[] =
+{
+-1000, -75, 73, -38, -39, 2, -37, -40, -49, -48,
+ -29, -31, -30, 75, -9, -66, -54, 59, 63, 39,
+ -10, -9, 59, -39, 72, 72, 72, 4, 16, 4,
+ 16, 50, 99, 61, 50, -4, 54, -3, -2, 39,
+ -21, 41, -32, -31, -29, 59, 99, 72, -23, -24,
+ 17, -25, 32, 33, 64, 65, 34, 43, 44, 38,
+ 67, 68, 66, 46, 69, 81, 82, 83, 60, 59,
+ -6, -29, 39, 61, 58, 57, 97, -23, -23, -23,
+ -1, 60, -7, -8, 46, 83, 39, 81, 82, 49,
+ -6, 59, -31, 72, 77, 74, -1, 76, 51, 78,
+ 80, -67, 52, 59, 87, 50, -14, 34, -15, -16,
+ -11, -30, -12, -31, 59, 63, -9, 40, 99, 59,
+ -76, 72, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 38, 16, 37, 34, 35, 36,
+ 32, 33, 31, 30, 26, 27, 28, 29, 24, 25,
+ 23, 22, 21, 20, 19, 18, 59, 39, 54, 53,
+ 41, 43, 44, -24, -24, -24, -24, -24, -24, -24,
+ -24, -24, -24, -24, -24, -24, 41, 45, 45, 45,
+ 41, -24, -24, -24, -26, -23, 39, 72, 72, 72,
+ 72, 4, 53, 54, -1, 45, -13, -1, 45, 45,
+ -21, 41, -1, -23, 72, 4, -23, 72, 39, 70,
+ -21, -41, 70, 2, 39, -29, -21, 70, -1, 40,
+ 40, 99, 50, 50, 99, 42, -31, -29, -38, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, 4, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -23, -23, -23, -23, -23, -23,
+ -23, -23, -23, -23, -27, -26, 59, -25, -71, -22,
+ -72, -23, -70, 60, 81, 82, 79, -23, 42, 60,
+ 70, -1, -23, 40, 99, -23, -23, 59, 59, -1,
+ 40, 99, -1, -1, -4, -74, -1, 79, 72, 72,
+ -23, 72, -13, -51, 2, 70, 87, -43, 71, 70,
+ -32, -69, -68, -9, 34, -16, -1, -5, 84, -1,
+ -5, 59, 63, -23, 40, 42, 50, 99, 45, 45,
+ -1, 42, 45, -24, -28, -26, 42, -23, 99, 40,
+ 72, 41, 41, -1, -67, 99, 42, -1, 72, 40,
+ 71, -50, -9, -49, -66, -53, 2, 70, 71, -37,
+ -42, -9, 2, 70, -77, -22, 47, -17, 88, 92,
+ 93, 95, 96, 94, 39, -23, 59, -43, 40, 71,
+ -62, 62, 40, -7, 46, -8, -1, -22, -71, -23,
+ 60, -1, -1, 45, 70, 71, 99, 45, -23, -74,
+ -74, -1, 79, 72, 50, 72, 71, -52, -61, -60,
+ -9, 91, -69, 50, 72, 71, 70, -43, 99, 50,
+ 72, 39, 87, 86, 85, 90, 89, 91, -18, 59,
+ -18, -22, -23, -22, 72, -26, 71, -61, -9, -7,
+ -8, 42, 42, -1, 70, -33, -34, -35, -23, -36,
+ 34, 2, -1, 40, 42, 42, -1, -1, 77, 76,
+ -73, 87, 50, 70, 71, -43, 71, 39, -1, -23,
+ 39, 39, -42, -9, -23, 70, 59, 72, 72, 72,
+ 72, 72, 40, 50, 62, -33, 71, 99, 55, 56,
+ 62, 72, -1, -23, 70, 76, 79, -1, -58, -59,
+ 2, -19, -20, 59, 70, 71, 51, -26, 72, 4,
+ 40, -22, -22, 86, 50, 70, -44, -45, -36, -23,
+ 16, 71, -35, -23, -36, -23, 72, 72, -69, -23,
+ -1, 72, 71, -62, 2, 62, 56, -53, -65, 59,
+ 40, -23, -42, 72, 40, 39, -46, -47, -36, 71,
+ -43, 62, -23, 71, 72, 72, -19, -9, 59, -19,
+ 59, 71, 70, 72, 48, -22, -42, -22, 71, -43,
+ 62, -36, 2, -23, 70, 62, 62, -63, -64, -36,
+ -42, 72, 40, -36, 62, -55, -56, -57, 59, 34,
+ 2, 71, -43, 62, -22, 72, 62, 71, -43, 62,
+ 56, -36, 40, -57, 2, 59, 34, -57, 62, -42,
+ 62
+};
+short yydef[] =
+{
+ 0, -2, 0, -2, 4, 0, 7, 8, 0, 0,
+ 0, 17, 0, 0, 0, 25, 0, -2, 251, 0,
+ 61, 0, 62, 5, 6, 9, 10, 0, 0, 0,
+ 0, 0, 0, 0, 0, 117, 0, 95, 93, 97,
+ 121, 0, 0, 263, 264, 250, 0, 1, 0, 177,
+ 0, 213, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 250,
+ 0, 237, 0, 246, 247, 248, 0, 0, 0, 0,
+ 0, 70, 71, 72, 0, 0, 0, 0, 0, 88,
+ 81, 82, 18, 19, 0, 0, 0, 0, 0, 0,
+ 88, 0, 0, 88, 0, 0, 0, 0, 98, 99,
+ 0, 0, 105, 17, 106, 107, 0, 252, 0, 63,
+ 0, 11, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 267, 0, 0,
+ 175, 244, 245, 214, 215, 216, 217, 218, 219, 220,
+ 221, 222, 223, 224, 225, 226, 0, 0, 0, 0,
+ 0, 234, 235, 236, 0, 271, 0, 13, 12, 14,
+ 15, 0, 0, 0, 75, 0, 0, 86, 0, 0,
+ 0, 0, 0, 0, 22, 0, 0, 26, 0, -2,
+ 0, 114, 123, 0, 0, 116, 122, 0, 94, 90,
+ 91, 0, 0, 0, 0, 89, 265, 266, -2, 178,
+ 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
+ 189, 0, 191, 193, 194, 195, 196, 197, 198, 199,
+ 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+ 210, 211, 212, 192, 0, 268, 240, 241, 253, 0,
+ 0, -2, 256, 257, 0, 0, 0, 0, 0, 0,
+ 0, 231, 0, 239, 0, 0, 0, 73, 84, 76,
+ 77, 0, 78, 79, 117, 0, 66, 0, 20, 21,
+ 0, 24, 0, 0, 31, -2, 0, -2, 119, 123,
+ 0, 0, 47, 0, 0, 100, 101, 102, 0, 103,
+ 104, 108, 109, 190, 238, 242, 175, 0, 0, 0,
+ 260, 0, 0, 233, 0, 269, 0, 272, 0, 65,
+ 16, 0, 0, 87, 80, 0, 83, 67, 23, 0,
+ 28, 30, 0, 0, 36, 0, 43, 0, 118, 124,
+ 125, 0, 0, 123, 0, 0, 0, 0, 152, 152,
+ 175, 0, 175, 0, 0, 176, -2, -2, 115, 96,
+ 279, 280, 92, -2, 0, -2, 0, 0, 254, 255,
+ 70, 258, 259, 0, 0, 230, 270, 0, 0, 0,
+ 0, 68, 0, 27, 0, 33, 39, 42, 44, 45,
+ 0, 0, 0, 151, 128, 129, 123, -2, 0, 0,
+ 134, 0, 0, 0, -2, 0, 0, 0, 0, 153,
+ 0, 0, 0, 0, 148, 0, 120, 48, 0, -2,
+ -2, 243, 249, 227, 0, 0, 273, 275, -2, 0,
+ 165, 167, 232, 64, 74, 85, 69, 0, 0, 0,
+ 37, 0, 0, 0, 0, -2, 131, 0, 0, 0,
+ 175, 175, 0, 0, 0, 0, 0, 140, 141, 142,
+ 143, 144, -2, 0, 281, 0, 229, -2, 0, 0,
+ 0, 32, 0, 0, 0, 0, 0, 0, 0, -2,
+ 54, 0, 58, 59, -2, 130, 262, 0, 132, 0,
+ -2, 0, 0, 0, 151, 0, 0, 123, 0, 163,
+ 0, 228, 276, 164, 166, 278, 34, 35, 0, 0,
+ 0, 50, 51, 52, 53, 55, 0, 0, 0, 261,
+ 127, 0, 135, 175, -2, 175, 0, 123, 0, 146,
+ -2, 155, 0, 40, 46, 49, 0, 0, -2, 0,
+ 60, 38, 0, 133, -2, 0, 138, 0, 145, -2,
+ 158, 0, 167, -2, 0, 56, 57, 0, 123, 0,
+ 136, 175, 0, 0, 156, 0, 123, 0, 171, 172,
+ 174, 149, -2, 161, 0, 139, 159, 147, -2, 169,
+ 0, 0, -2, 0, 174, -2, 172, 173, 162, 137,
+ 170
+};
+short yytok1[] =
+{
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 64, 0, 0, 0, 36, 23, 0,
+ 39, 40, 34, 32, 99, 33, 54, 35, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 50, 72,
+ 26, 4, 27, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 41, 0, 42, 22, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 70, 21, 71, 65
+};
+short yytok2[] =
+{
+ 2, 3, 5, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 16, 17, 18, 19, 20, 24, 25,
+ 28, 29, 30, 31, 37, 38, 43, 44, 45, 46,
+ 47, 48, 49, 51, 52, 53, 55, 56, 57, 58,
+ 59, 60, 61, 62, 63, 66, 67, 68, 69, 73,
+ 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
+ 84, 85, 86, 87, 88, 89, 90, 91, 92, 93,
+ 94, 95, 96, 97, 98
+};
+long yytok3[] =
+{
+ 0
+};
+#define YYFLAG -1000
+#define yyclearin yychar = -1
+#define yyerrok yyerrflag = 0
+
+#ifdef yydebug
+#include "y.debug"
+#else
+#define yydebug 0
+char* yytoknames[1]; /* for debugging */
+char* yystates[1]; /* for debugging */
+#endif
+
+/* parser for yacc output */
+
+int yynerrs = 0; /* number of errors */
+int yyerrflag = 0; /* error recovery flag */
+
+extern int fprint(int, char*, ...);
+extern int sprint(char*, char*, ...);
+
+char*
+yytokname(int yyc)
+{
+ static char x[16];
+
+ if(yyc > 0 && yyc <= sizeof(yytoknames)/sizeof(yytoknames[0]))
+ if(yytoknames[yyc-1])
+ return yytoknames[yyc-1];
+ sprint(x, "<%d>", yyc);
+ return x;
+}
+
+char*
+yystatname(int yys)
+{
+ static char x[16];
+
+ if(yys >= 0 && yys < sizeof(yystates)/sizeof(yystates[0]))
+ if(yystates[yys])
+ return yystates[yys];
+ sprint(x, "<%d>\n", yys);
+ return x;
+}
+
+long
+yylex1(void)
+{
+ long yychar;
+ long *t3p;
+ int c;
+
+ yychar = yylex();
+ if(yychar <= 0) {
+ c = yytok1[0];
+ goto out;
+ }
+ if(yychar < sizeof(yytok1)/sizeof(yytok1[0])) {
+ c = yytok1[yychar];
+ goto out;
+ }
+ if(yychar >= YYPRIVATE)
+ if(yychar < YYPRIVATE+sizeof(yytok2)/sizeof(yytok2[0])) {
+ c = yytok2[yychar-YYPRIVATE];
+ goto out;
+ }
+ for(t3p=yytok3;; t3p+=2) {
+ c = t3p[0];
+ if(c == yychar) {
+ c = t3p[1];
+ goto out;
+ }
+ if(c == 0)
+ break;
+ }
+ c = 0;
+
+out:
+ if(c == 0)
+ c = yytok2[1]; /* unknown char */
+ if(yydebug >= 3)
+ fprint(2, "lex %.4lux %s\n", yychar, yytokname(c));
+ return c;
+}
+
+int
+yyparse(void)
+{
+ struct
+ {
+ YYSTYPE yyv;
+ int yys;
+ } yys[YYMAXDEPTH], *yyp, *yypt;
+ short *yyxi;
+ int yyj, yym, yystate, yyn, yyg;
+ long yychar;
+ YYSTYPE save1, save2;
+ int save3, save4;
+
+ save1 = yylval;
+ save2 = yyval;
+ save3 = yynerrs;
+ save4 = yyerrflag;
+
+ yystate = 0;
+ yychar = -1;
+ yynerrs = 0;
+ yyerrflag = 0;
+ yyp = &yys[-1];
+ goto yystack;
+
+ret0:
+ yyn = 0;
+ goto ret;
+
+ret1:
+ yyn = 1;
+ goto ret;
+
+ret:
+ yylval = save1;
+ yyval = save2;
+ yynerrs = save3;
+ yyerrflag = save4;
+ return yyn;
+
+yystack:
+ /* put a state and value onto the stack */
+ if(yydebug >= 4)
+ fprint(2, "char %s in %s", yytokname(yychar), yystatname(yystate));
+
+ yyp++;
+ if(yyp >= &yys[YYMAXDEPTH]) {
+ yyerror("yacc stack overflow");
+ goto ret1;
+ }
+ yyp->yys = yystate;
+ yyp->yyv = yyval;
+
+yynewstate:
+ yyn = yypact[yystate];
+ if(yyn <= YYFLAG)
+ goto yydefault; /* simple state */
+ if(yychar < 0)
+ yychar = yylex1();
+ yyn += yychar;
+ if(yyn < 0 || yyn >= YYLAST)
+ goto yydefault;
+ yyn = yyact[yyn];
+ if(yychk[yyn] == yychar) { /* valid shift */
+ yychar = -1;
+ yyval = yylval;
+ yystate = yyn;
+ if(yyerrflag > 0)
+ yyerrflag--;
+ goto yystack;
+ }
+
+yydefault:
+ /* default state action */
+ yyn = yydef[yystate];
+ if(yyn == -2) {
+ if(yychar < 0)
+ yychar = yylex1();
+
+ /* look through exception table */
+ for(yyxi=yyexca;; yyxi+=2)
+ if(yyxi[0] == -1 && yyxi[1] == yystate)
+ break;
+ for(yyxi += 2;; yyxi += 2) {
+ yyn = yyxi[0];
+ if(yyn < 0 || yyn == yychar)
+ break;
+ }
+ yyn = yyxi[1];
+ if(yyn < 0)
+ goto ret0;
+ }
+ if(yyn == 0) {
+ /* error ... attempt to resume parsing */
+ switch(yyerrflag) {
+ case 0: /* brand new error */
+ yyerror("syntax error");
+ yynerrs++;
+ if(yydebug >= 1) {
+ fprint(2, "%s", yystatname(yystate));
+ fprint(2, "saw %s\n", yytokname(yychar));
+ }
+
+ case 1:
+ case 2: /* incompletely recovered error ... try again */
+ yyerrflag = 3;
+
+ /* find a state where "error" is a legal shift action */
+ while(yyp >= yys) {
+ yyn = yypact[yyp->yys] + YYERRCODE;
+ if(yyn >= 0 && yyn < YYLAST) {
+ yystate = yyact[yyn]; /* simulate a shift of "error" */
+ if(yychk[yystate] == YYERRCODE)
+ goto yystack;
+ }
+
+ /* the current yyp has no shift onn "error", pop stack */
+ if(yydebug >= 2)
+ fprint(2, "error recovery pops state %d, uncovers %d\n",
+ yyp->yys, (yyp-1)->yys );
+ yyp--;
+ }
+ /* there is no state on the stack with an error shift ... abort */
+ goto ret1;
+
+ case 3: /* no shift yet; clobber input char */
+ if(yydebug >= 2)
+ fprint(2, "error recovery discards %s\n", yytokname(yychar));
+ if(yychar == YYEOFCODE)
+ goto ret1;
+ yychar = -1;
+ goto yynewstate; /* try again in the same state */
+ }
+ }
+
+ /* reduction by production yyn */
+ if(yydebug >= 2)
+ fprint(2, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
+
+ yypt = yyp;
+ yyp -= yyr2[yyn];
+ yyval = (yyp+1)->yyv;
+ yym = yyn;
+
+ /* consult goto table to find next state */
+ yyn = yyr1[yyn];
+ yyg = yypgo[yyn];
+ yyj = yyg + yyp->yys + 1;
+
+ if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
+ yystate = yyact[yyg];
+ switch(yym) {
+
+case 1:
+#line 74 "limbo.y"
+{
+ impmods = yypt[-1].yyv.ids;
+ } break;
+case 2:
+#line 77 "limbo.y"
+{
+ tree = rotater(yypt[-0].yyv.node);
+ } break;
+case 3:
+#line 81 "limbo.y"
+{
+ impmods = nil;
+ tree = rotater(yypt[-0].yyv.node);
+ } break;
+case 5:
+#line 89 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else if(yypt[-0].yyv.node == nil)
+ yyval.node = yypt[-1].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 6:
+#line 100 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 11:
+#line 108 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ } break;
+case 12:
+#line 112 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ } break;
+case 13:
+#line 116 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ } break;
+case 14:
+#line 120 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ } break;
+case 15:
+#line 124 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = nil;
+ } break;
+case 16:
+#line 129 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = nil;
+ } break;
+case 18:
+#line 137 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 19:
+#line 143 "limbo.y"
+{
+ includef(yypt[-1].yyv.tok.v.idval);
+ yyval.node = nil;
+ } break;
+case 20:
+#line 148 "limbo.y"
+{
+ yyval.node = typedecl(yypt[-4].yyv.ids, yypt[-1].yyv.type);
+ } break;
+case 21:
+#line 152 "limbo.y"
+{
+ yyval.node = importdecl(yypt[-1].yyv.node, yypt[-4].yyv.ids);
+ yyval.node->src.start = yypt[-4].yyv.ids->src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 22:
+#line 158 "limbo.y"
+{
+ yyval.node = vardecl(yypt[-3].yyv.ids, yypt[-1].yyv.type);
+ } break;
+case 23:
+#line 162 "limbo.y"
+{
+ yyval.node = mkbin(Ovardecli, vardecl(yypt[-5].yyv.ids, yypt[-3].yyv.type), varinit(yypt[-5].yyv.ids, yypt[-1].yyv.node));
+ } break;
+case 24:
+#line 166 "limbo.y"
+{
+ yyval.node = condecl(yypt[-4].yyv.ids, yypt[-1].yyv.node);
+ } break;
+case 26:
+#line 173 "limbo.y"
+{
+ yyval.node = exdecl(yypt[-3].yyv.ids, nil);
+ } break;
+case 27:
+#line 177 "limbo.y"
+{
+ yyval.node = exdecl(yypt[-6].yyv.ids, revids(yypt[-2].yyv.ids));
+ } break;
+case 28:
+#line 183 "limbo.y"
+{
+ yypt[-5].yyv.ids->src.stop = yypt[-0].yyv.tok.src.stop;
+ yyval.node = moddecl(yypt[-5].yyv.ids, rotater(yypt[-1].yyv.node));
+ } break;
+case 29:
+#line 190 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 30:
+#line 194 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else if(yypt[-0].yyv.node == nil)
+ yyval.node = yypt[-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 31:
+#line 203 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 32:
+#line 209 "limbo.y"
+{
+ yyval.node = fielddecl(Dglobal, typeids(yypt[-3].yyv.ids, yypt[-1].yyv.type));
+ } break;
+case 34:
+#line 214 "limbo.y"
+{
+ yyval.node = typedecl(yypt[-4].yyv.ids, yypt[-1].yyv.type);
+ } break;
+case 35:
+#line 218 "limbo.y"
+{
+ yyval.node = condecl(yypt[-4].yyv.ids, yypt[-1].yyv.node);
+ } break;
+case 37:
+#line 225 "limbo.y"
+{
+ yypt[-7].yyv.ids->src.stop = yypt[-1].yyv.tok.src.stop;
+ yyval.node = adtdecl(yypt[-7].yyv.ids, rotater(yypt[-2].yyv.node));
+ yyval.node->ty->polys = yypt[-4].yyv.ids;
+ yyval.node->ty->val = rotater(yypt[-0].yyv.node);
+ } break;
+case 38:
+#line 232 "limbo.y"
+{
+ yypt[-10].yyv.ids->src.stop = yypt[-0].yyv.tok.src.stop;
+ yyval.node = adtdecl(yypt[-10].yyv.ids, rotater(yypt[-1].yyv.node));
+ yyval.node->ty->polys = yypt[-7].yyv.ids;
+ yyval.node->ty->val = rotater(yypt[-4].yyv.node);
+ } break;
+case 39:
+#line 241 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 40:
+#line 245 "limbo.y"
+{
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 41:
+#line 251 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 42:
+#line 255 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else if(yypt[-0].yyv.node == nil)
+ yyval.node = yypt[-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 43:
+#line 264 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 46:
+#line 272 "limbo.y"
+{
+ yyval.node = condecl(yypt[-4].yyv.ids, yypt[-1].yyv.node);
+ } break;
+case 47:
+#line 278 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 48:
+#line 282 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else if(yypt[-0].yyv.node == nil)
+ yyval.node = yypt[-1].yyv.node;
+ else
+ yyval.node = mkn(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 49:
+#line 293 "limbo.y"
+{
+ Decl *d;
+
+ for(d = yypt[-4].yyv.ids; d != nil; d = d->next)
+ d->cyc = 1;
+ yyval.node = fielddecl(Dfield, typeids(yypt[-4].yyv.ids, yypt[-1].yyv.type));
+ } break;
+case 50:
+#line 301 "limbo.y"
+{
+ yyval.node = fielddecl(Dfield, typeids(yypt[-3].yyv.ids, yypt[-1].yyv.type));
+ } break;
+case 51:
+#line 307 "limbo.y"
+{
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 52:
+#line 313 "limbo.y"
+{
+ yypt[-1].yyv.node->right->right = yypt[-0].yyv.node;
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 53:
+#line 318 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 54:
+#line 322 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 55:
+#line 328 "limbo.y"
+{
+ yyval.node = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, yypt[-1].yyv.ids), nil));
+ typeids(yypt[-1].yyv.ids, mktype(&yypt[-1].yyv.ids->src.start, &yypt[-1].yyv.ids->src.stop, Tadtpick, nil, nil));
+ } break;
+case 56:
+#line 333 "limbo.y"
+{
+ yypt[-3].yyv.node->right->right = yypt[-2].yyv.node;
+ yyval.node = mkn(Opickdecl, yypt[-3].yyv.node, mkn(Oseq, fielddecl(Dtag, yypt[-1].yyv.ids), nil));
+ typeids(yypt[-1].yyv.ids, mktype(&yypt[-1].yyv.ids->src.start, &yypt[-1].yyv.ids->src.stop, Tadtpick, nil, nil));
+ } break;
+case 57:
+#line 339 "limbo.y"
+{
+ yyval.node = mkn(Opickdecl, nil, mkn(Oseq, fielddecl(Dtag, yypt[-1].yyv.ids), nil));
+ typeids(yypt[-1].yyv.ids, mktype(&yypt[-1].yyv.ids->src.start, &yypt[-1].yyv.ids->src.stop, Tadtpick, nil, nil));
+ } break;
+case 58:
+#line 346 "limbo.y"
+{
+ yyval.ids = revids(yypt[-0].yyv.ids);
+ } break;
+case 59:
+#line 352 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, nil);
+ } break;
+case 60:
+#line 356 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, yypt[-2].yyv.ids);
+ } break;
+case 61:
+#line 362 "limbo.y"
+{
+ yyval.ids = revids(yypt[-0].yyv.ids);
+ } break;
+case 62:
+#line 368 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, nil);
+ } break;
+case 63:
+#line 372 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, yypt[-2].yyv.ids);
+ } break;
+case 64:
+#line 378 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-5].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tfix, nil, nil);
+ yyval.type->val = mkbin(Oseq, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ } break;
+case 65:
+#line 383 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-3].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tfix, nil, nil);
+ yyval.type->val = yypt[-1].yyv.node;
+ } break;
+case 66:
+#line 390 "limbo.y"
+{
+ yyval.types = addtype(yypt[-0].yyv.type, nil);
+ } break;
+case 67:
+#line 394 "limbo.y"
+{
+ yyval.types = addtype(yypt[-0].yyv.type, nil);
+ yypt[-0].yyv.type->flags |= CYCLIC;
+ } break;
+case 68:
+#line 399 "limbo.y"
+{
+ yyval.types = addtype(yypt[-0].yyv.type, yypt[-2].yyv.types);
+ } break;
+case 69:
+#line 403 "limbo.y"
+{
+ yyval.types = addtype(yypt[-0].yyv.type, yypt[-3].yyv.types);
+ yypt[-0].yyv.type->flags |= CYCLIC;
+ } break;
+case 70:
+#line 410 "limbo.y"
+{
+ yyval.type = mkidtype(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 71:
+#line 414 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 72:
+#line 418 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 73:
+#line 422 "limbo.y"
+{
+ yyval.type = mkarrowtype(&yypt[-2].yyv.type->src.start, &yypt[-0].yyv.tok.src.stop, yypt[-2].yyv.type, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 74:
+#line 426 "limbo.y"
+{
+ yyval.type = mkarrowtype(&yypt[-5].yyv.type->src.start, &yypt[-3].yyv.tok.src.stop, yypt[-5].yyv.type, yypt[-3].yyv.tok.v.idval);
+ yyval.type = mkinsttype(&yypt[-5].yyv.type->src, yyval.type, yypt[-1].yyv.types);
+ } break;
+case 75:
+#line 431 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-1].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tref, yypt[-0].yyv.type, nil);
+ } break;
+case 76:
+#line 435 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tchan, yypt[-0].yyv.type, nil);
+ } break;
+case 77:
+#line 439 "limbo.y"
+{
+ if(yypt[-1].yyv.ids->next == nil)
+ yyval.type = yypt[-1].yyv.ids->ty;
+ else
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Ttuple, nil, revids(yypt[-1].yyv.ids));
+ } break;
+case 78:
+#line 446 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tarray, yypt[-0].yyv.type, nil);
+ } break;
+case 79:
+#line 450 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tlist, yypt[-0].yyv.type, nil);
+ } break;
+case 80:
+#line 454 "limbo.y"
+{
+ yypt[-1].yyv.type->src.start = yypt[-3].yyv.tok.src.start;
+ yypt[-1].yyv.type->polys = yypt[-2].yyv.ids;
+ yypt[-1].yyv.type->u.eraises = yypt[-0].yyv.node;
+ yyval.type = yypt[-1].yyv.type;
+ } break;
+case 82:
+#line 476 "limbo.y"
+{
+ yyval.type = mkidtype(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 83:
+#line 480 "limbo.y"
+{
+ yyval.type = mkinsttype(&yypt[-3].yyv.tok.src, mkidtype(&yypt[-3].yyv.tok.src, yypt[-3].yyv.tok.v.idval), yypt[-1].yyv.types);
+ } break;
+case 84:
+#line 486 "limbo.y"
+{
+ yyval.type = mkdottype(&yypt[-2].yyv.type->src.start, &yypt[-0].yyv.tok.src.stop, yypt[-2].yyv.type, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 85:
+#line 490 "limbo.y"
+{
+ yyval.type = mkdottype(&yypt[-5].yyv.type->src.start, &yypt[-3].yyv.tok.src.stop, yypt[-5].yyv.type, yypt[-3].yyv.tok.v.idval);
+ yyval.type = mkinsttype(&yypt[-5].yyv.type->src, yyval.type, yypt[-1].yyv.types);
+ } break;
+case 86:
+#line 497 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.type->src, nil, yypt[-0].yyv.type, nil);
+ } break;
+case 87:
+#line 501 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-2].yyv.ids->src, nil, yypt[-0].yyv.type, yypt[-2].yyv.ids);
+ } break;
+case 88:
+#line 507 "limbo.y"
+{
+ yyval.ids = nil;
+ } break;
+case 89:
+#line 511 "limbo.y"
+{
+ yyval.ids = polydecl(yypt[-1].yyv.ids);
+ } break;
+case 90:
+#line 517 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tfn, tnone, yypt[-1].yyv.ids);
+ } break;
+case 91:
+#line 521 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tfn, tnone, nil);
+ yyval.type->varargs = 1;
+ } break;
+case 92:
+#line 526 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-4].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tfn, tnone, yypt[-3].yyv.ids);
+ yyval.type->varargs = 1;
+ } break;
+case 93:
+#line 533 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 94:
+#line 537 "limbo.y"
+{
+ yypt[-2].yyv.type->tof = yypt[-0].yyv.type;
+ yypt[-2].yyv.type->src.stop = yypt[-0].yyv.type->src.stop;
+ yyval.type = yypt[-2].yyv.type;
+ } break;
+case 95:
+#line 545 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 96:
+#line 549 "limbo.y"
+{
+ yyval.type = yypt[-4].yyv.type;
+ yyval.type->val = rotater(yypt[-1].yyv.node);
+ } break;
+case 97:
+#line 556 "limbo.y"
+{
+ yyval.ids = nil;
+ } break;
+case 100:
+#line 564 "limbo.y"
+{
+ yyval.ids = appdecls(yypt[-2].yyv.ids, yypt[-0].yyv.ids);
+ } break;
+case 101:
+#line 570 "limbo.y"
+{
+ yyval.ids = typeids(yypt[-2].yyv.ids, yypt[-0].yyv.type);
+ } break;
+case 102:
+#line 574 "limbo.y"
+{
+ Decl *d;
+
+ yyval.ids = typeids(yypt[-2].yyv.ids, yypt[-0].yyv.type);
+ for(d = yyval.ids; d != nil; d = d->next)
+ d->implicit = 1;
+ } break;
+case 103:
+#line 582 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-2].yyv.node->src, enter("junk", 0), yypt[-0].yyv.type, nil);
+ yyval.ids->store = Darg;
+ yyerror("illegal argument declaraion");
+ } break;
+case 104:
+#line 588 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-2].yyv.node->src, enter("junk", 0), yypt[-0].yyv.type, nil);
+ yyval.ids->store = Darg;
+ yyerror("illegal argument declaraion");
+ } break;
+case 105:
+#line 596 "limbo.y"
+{
+ yyval.ids = revids(yypt[-0].yyv.ids);
+ } break;
+case 106:
+#line 602 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, nil);
+ yyval.ids->store = Darg;
+ } break;
+case 107:
+#line 607 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, nil, nil, nil);
+ yyval.ids->store = Darg;
+ } break;
+case 108:
+#line 612 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, yypt[-2].yyv.ids);
+ yyval.ids->store = Darg;
+ } break;
+case 109:
+#line 617 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, nil, nil, yypt[-2].yyv.ids);
+ yyval.ids->store = Darg;
+ } break;
+case 110:
+#line 650 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 111:
+#line 654 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-1].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tref, yypt[-0].yyv.type, nil);
+ } break;
+case 112:
+#line 658 "limbo.y"
+{
+ yyval.type = yypt[-0].yyv.type;
+ } break;
+case 113:
+#line 662 "limbo.y"
+{
+ yyval.type = mktype(&yypt[-1].yyv.tok.src.start, &yypt[-0].yyv.tok.src.stop, Tref, yypt[-0].yyv.type, nil);
+ } break;
+case 114:
+#line 668 "limbo.y"
+{
+ yyval.node = fndecl(yypt[-3].yyv.node, yypt[-2].yyv.type, yypt[-0].yyv.node);
+ nfns++;
+ /* patch up polydecs */
+ if(yypt[-3].yyv.node->op == Odot){
+ if(yypt[-3].yyv.node->right->left != nil){
+ yypt[-2].yyv.type->polys = yypt[-3].yyv.node->right->left->decl;
+ yypt[-3].yyv.node->right->left = nil;
+ }
+ if(yypt[-3].yyv.node->left->op == Oname && yypt[-3].yyv.node->left->left != nil){
+ yyval.node->decl = yypt[-3].yyv.node->left->left->decl;
+ yypt[-3].yyv.node->left->left = nil;
+ }
+ }
+ else{
+ if(yypt[-3].yyv.node->left != nil){
+ yypt[-2].yyv.type->polys = yypt[-3].yyv.node->left->decl;
+ yypt[-3].yyv.node->left = nil;
+ }
+ }
+ yypt[-2].yyv.type->u.eraises = yypt[-1].yyv.node;
+ yyval.node->src = yypt[-3].yyv.node->src;
+ } break;
+case 115:
+#line 694 "limbo.y"
+{
+ yyval.node = mkn(Otuple, rotater(yypt[-1].yyv.node), nil);
+ yyval.node->src.start = yypt[-3].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 116:
+#line 700 "limbo.y"
+{
+ yyval.node = mkn(Otuple, mkunary(Oseq, yypt[-0].yyv.node), nil);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.node->src.stop;
+ } break;
+case 117:
+#line 706 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 118:
+#line 712 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil){
+ yypt[-1].yyv.node = mkn(Onothing, nil, nil);
+ yypt[-1].yyv.node->src.start = curline();
+ yypt[-1].yyv.node->src.stop = yypt[-1].yyv.node->src.start;
+ }
+ yyval.node = rotater(yypt[-1].yyv.node);
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 119:
+#line 723 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ } break;
+case 120:
+#line 727 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ } break;
+case 121:
+#line 733 "limbo.y"
+{
+ yyval.node = mkname(&yypt[-1].yyv.tok.src, yypt[-1].yyv.tok.v.idval);
+ if(yypt[-0].yyv.ids != nil){
+ yyval.node->left = mkn(Onothing, nil ,nil);
+ yyval.node->left->decl = yypt[-0].yyv.ids;
+ }
+ } break;
+case 122:
+#line 741 "limbo.y"
+{
+ yyval.node = mkbin(Odot, yypt[-3].yyv.node, mkname(&yypt[-1].yyv.tok.src, yypt[-1].yyv.tok.v.idval));
+ if(yypt[-0].yyv.ids != nil){
+ yyval.node->right->left = mkn(Onothing, nil ,nil);
+ yyval.node->right->left->decl = yypt[-0].yyv.ids;
+ }
+ } break;
+case 123:
+#line 751 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 124:
+#line 755 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else if(yypt[-0].yyv.node == nil)
+ yyval.node = yypt[-1].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 125:
+#line 764 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil)
+ yyval.node = yypt[-0].yyv.node;
+ else
+ yyval.node = mkbin(Oseq, yypt[-1].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 128:
+#line 777 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 129:
+#line 783 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 130:
+#line 789 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 131:
+#line 795 "limbo.y"
+{
+ if(yypt[-1].yyv.node == nil){
+ yypt[-1].yyv.node = mkn(Onothing, nil, nil);
+ yypt[-1].yyv.node->src.start = curline();
+ yypt[-1].yyv.node->src.stop = yypt[-1].yyv.node->src.start;
+ }
+ yyval.node = mkscope(rotater(yypt[-1].yyv.node));
+ } break;
+case 132:
+#line 804 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 133:
+#line 811 "limbo.y"
+{
+ yyerror("illegal declaration");
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 134:
+#line 818 "limbo.y"
+{
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 135:
+#line 822 "limbo.y"
+{
+ yyval.node = mkn(Oif, yypt[-2].yyv.node, mkunary(Oseq, yypt[-0].yyv.node));
+ yyval.node->src.start = yypt[-4].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.node->src.stop;
+ } break;
+case 136:
+#line 828 "limbo.y"
+{
+ yyval.node = mkn(Oif, yypt[-4].yyv.node, mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node));
+ yyval.node->src.start = yypt[-6].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.node->src.stop;
+ } break;
+case 137:
+#line 834 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, yypt[-0].yyv.node);
+ if(yypt[-2].yyv.node->op != Onothing)
+ yyval.node->right = yypt[-2].yyv.node;
+ yyval.node = mkbin(Ofor, yypt[-4].yyv.node, yyval.node);
+ yyval.node->decl = yypt[-9].yyv.ids;
+ if(yypt[-6].yyv.node->op != Onothing)
+ yyval.node = mkbin(Oseq, yypt[-6].yyv.node, yyval.node);
+ } break;
+case 138:
+#line 844 "limbo.y"
+{
+ yyval.node = mkn(Ofor, yypt[-2].yyv.node, mkunary(Oseq, yypt[-0].yyv.node));
+ yyval.node->src.start = yypt[-4].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.node->src.stop;
+ yyval.node->decl = yypt[-5].yyv.ids;
+ } break;
+case 139:
+#line 851 "limbo.y"
+{
+ yyval.node = mkn(Odo, yypt[-2].yyv.node, yypt[-5].yyv.node);
+ yyval.node->src.start = yypt[-6].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-1].yyv.tok.src.stop;
+ yyval.node->decl = yypt[-7].yyv.ids;
+ } break;
+case 140:
+#line 858 "limbo.y"
+{
+ yyval.node = mkn(Obreak, nil, nil);
+ yyval.node->decl = yypt[-1].yyv.ids;
+ yyval.node->src = yypt[-2].yyv.tok.src;
+ } break;
+case 141:
+#line 864 "limbo.y"
+{
+ yyval.node = mkn(Ocont, nil, nil);
+ yyval.node->decl = yypt[-1].yyv.ids;
+ yyval.node->src = yypt[-2].yyv.tok.src;
+ } break;
+case 142:
+#line 870 "limbo.y"
+{
+ yyval.node = mkn(Oret, yypt[-1].yyv.node, nil);
+ yyval.node->src = yypt[-2].yyv.tok.src;
+ if(yypt[-1].yyv.node->op == Onothing)
+ yyval.node->left = nil;
+ else
+ yyval.node->src.stop = yypt[-1].yyv.node->src.stop;
+ } break;
+case 143:
+#line 879 "limbo.y"
+{
+ yyval.node = mkn(Ospawn, yypt[-1].yyv.node, nil);
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-1].yyv.node->src.stop;
+ } break;
+case 144:
+#line 885 "limbo.y"
+{
+ yyval.node = mkn(Oraise, yypt[-1].yyv.node, nil);
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-1].yyv.node->src.stop;
+ } break;
+case 145:
+#line 891 "limbo.y"
+{
+ yyval.node = mkn(Ocase, yypt[-3].yyv.node, caselist(yypt[-1].yyv.node, nil));
+ yyval.node->src = yypt[-3].yyv.node->src;
+ yyval.node->decl = yypt[-5].yyv.ids;
+ } break;
+case 146:
+#line 897 "limbo.y"
+{
+ yyval.node = mkn(Oalt, caselist(yypt[-1].yyv.node, nil), nil);
+ yyval.node->src = yypt[-3].yyv.tok.src;
+ yyval.node->decl = yypt[-4].yyv.ids;
+ } break;
+case 147:
+#line 903 "limbo.y"
+{
+ yyval.node = mkn(Opick, mkbin(Odas, mkname(&yypt[-5].yyv.tok.src, yypt[-5].yyv.tok.v.idval), yypt[-3].yyv.node), caselist(yypt[-1].yyv.node, nil));
+ yyval.node->src.start = yypt[-5].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-3].yyv.node->src.stop;
+ yyval.node->decl = yypt[-7].yyv.ids;
+ } break;
+case 148:
+#line 910 "limbo.y"
+{
+ yyval.node = mkn(Oexit, nil, nil);
+ yyval.node->src = yypt[-1].yyv.tok.src;
+ } break;
+case 149:
+#line 915 "limbo.y"
+{
+ if(yypt[-6].yyv.node == nil){
+ yypt[-6].yyv.node = mkn(Onothing, nil, nil);
+ yypt[-6].yyv.node->src.start = curline();
+ yypt[-6].yyv.node->src.stop = curline();
+ }
+ yypt[-6].yyv.node = mkscope(rotater(yypt[-6].yyv.node));
+ yyval.node = mkbin(Oexstmt, yypt[-6].yyv.node, mkn(Oexcept, yypt[-3].yyv.node, caselist(yypt[-1].yyv.node, nil)));
+ } break;
+case 150:
+#line 933 "limbo.y"
+{
+ yyval.ids = nil;
+ } break;
+case 151:
+#line 937 "limbo.y"
+{
+ if(yypt[-1].yyv.ids->next != nil)
+ yyerror("only one identifier allowed in a label");
+ yyval.ids = yypt[-1].yyv.ids;
+ } break;
+case 152:
+#line 945 "limbo.y"
+{
+ yyval.ids = nil;
+ } break;
+case 153:
+#line 949 "limbo.y"
+{
+ yyval.ids = mkids(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval, nil, nil);
+ } break;
+case 154:
+#line 955 "limbo.y"
+{
+ yypt[-1].yyv.node->left->right->right = yypt[-0].yyv.node;
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 155:
+#line 962 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkscope(mkunary(Olabel, rotater(yypt[-1].yyv.node))));
+ } break;
+case 156:
+#line 966 "limbo.y"
+{
+ yypt[-3].yyv.node->left->right->right = yypt[-2].yyv.node;
+ yyval.node = mkbin(Oseq, mkscope(mkunary(Olabel, rotater(yypt[-1].yyv.node))), yypt[-3].yyv.node);
+ } break;
+case 157:
+#line 973 "limbo.y"
+{
+ yypt[-1].yyv.node->left->right = mkscope(yypt[-0].yyv.node);
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 158:
+#line 980 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)));
+ } break;
+case 159:
+#line 984 "limbo.y"
+{
+ yypt[-3].yyv.node->left->right = mkscope(yypt[-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)), yypt[-3].yyv.node);
+ } break;
+case 160:
+#line 991 "limbo.y"
+{
+ yypt[-1].yyv.node->left->right = mkscope(yypt[-0].yyv.node);
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 161:
+#line 998 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)));
+ } break;
+case 162:
+#line 1002 "limbo.y"
+{
+ yypt[-3].yyv.node->left->right = mkscope(yypt[-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)), yypt[-3].yyv.node);
+ } break;
+case 164:
+#line 1010 "limbo.y"
+{
+ yyval.node = mkbin(Orange, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 165:
+#line 1014 "limbo.y"
+{
+ yyval.node = mkn(Owild, nil, nil);
+ yyval.node->src = yypt[-0].yyv.tok.src;
+ } break;
+case 166:
+#line 1019 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 167:
+#line 1023 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 168:
+#line 1031 "limbo.y"
+{
+ yypt[-1].yyv.node->left->right = mkscope(yypt[-0].yyv.node);
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 169:
+#line 1038 "limbo.y"
+{
+ yyval.node = mkunary(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)));
+ } break;
+case 170:
+#line 1042 "limbo.y"
+{
+ yypt[-3].yyv.node->left->right = mkscope(yypt[-2].yyv.node);
+ yyval.node = mkbin(Oseq, mkunary(Olabel, rotater(yypt[-1].yyv.node)), yypt[-3].yyv.node);
+ } break;
+case 171:
+#line 1049 "limbo.y"
+{
+ yyval.node = mkname(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 172:
+#line 1053 "limbo.y"
+{
+ yyval.node = mkn(Owild, nil, nil);
+ yyval.node->src = yypt[-0].yyv.tok.src;
+ } break;
+case 173:
+#line 1058 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 174:
+#line 1062 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 175:
+#line 1070 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = curline();
+ yyval.node->src.stop = yyval.node->src.start;
+ } break;
+case 178:
+#line 1080 "limbo.y"
+{
+ yyval.node = mkbin(Oas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 179:
+#line 1084 "limbo.y"
+{
+ yyval.node = mkbin(Oandas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 180:
+#line 1088 "limbo.y"
+{
+ yyval.node = mkbin(Ooras, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 181:
+#line 1092 "limbo.y"
+{
+ yyval.node = mkbin(Oxoras, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 182:
+#line 1096 "limbo.y"
+{
+ yyval.node = mkbin(Olshas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 183:
+#line 1100 "limbo.y"
+{
+ yyval.node = mkbin(Orshas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 184:
+#line 1104 "limbo.y"
+{
+ yyval.node = mkbin(Oaddas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 185:
+#line 1108 "limbo.y"
+{
+ yyval.node = mkbin(Osubas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 186:
+#line 1112 "limbo.y"
+{
+ yyval.node = mkbin(Omulas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 187:
+#line 1116 "limbo.y"
+{
+ yyval.node = mkbin(Odivas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 188:
+#line 1120 "limbo.y"
+{
+ yyval.node = mkbin(Omodas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 189:
+#line 1124 "limbo.y"
+{
+ yyval.node = mkbin(Oexpas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 190:
+#line 1128 "limbo.y"
+{
+ yyval.node = mkbin(Osnd, yypt[-3].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 191:
+#line 1132 "limbo.y"
+{
+ yyval.node = mkbin(Odas, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 192:
+#line 1136 "limbo.y"
+{
+ yyval.node = mkn(Oload, yypt[-0].yyv.node, nil);
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.node->src.stop;
+ yyval.node->ty = mkidtype(&yypt[-1].yyv.tok.src, yypt[-1].yyv.tok.v.idval);
+ } break;
+case 193:
+#line 1143 "limbo.y"
+{
+ yyval.node = mkbin(Oexp, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 194:
+#line 1147 "limbo.y"
+{
+ yyval.node = mkbin(Omul, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 195:
+#line 1151 "limbo.y"
+{
+ yyval.node = mkbin(Odiv, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 196:
+#line 1155 "limbo.y"
+{
+ yyval.node = mkbin(Omod, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 197:
+#line 1159 "limbo.y"
+{
+ yyval.node = mkbin(Oadd, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 198:
+#line 1163 "limbo.y"
+{
+ yyval.node = mkbin(Osub, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 199:
+#line 1167 "limbo.y"
+{
+ yyval.node = mkbin(Orsh, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 200:
+#line 1171 "limbo.y"
+{
+ yyval.node = mkbin(Olsh, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 201:
+#line 1175 "limbo.y"
+{
+ yyval.node = mkbin(Olt, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 202:
+#line 1179 "limbo.y"
+{
+ yyval.node = mkbin(Ogt, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 203:
+#line 1183 "limbo.y"
+{
+ yyval.node = mkbin(Oleq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 204:
+#line 1187 "limbo.y"
+{
+ yyval.node = mkbin(Ogeq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 205:
+#line 1191 "limbo.y"
+{
+ yyval.node = mkbin(Oeq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 206:
+#line 1195 "limbo.y"
+{
+ yyval.node = mkbin(Oneq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 207:
+#line 1199 "limbo.y"
+{
+ yyval.node = mkbin(Oand, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 208:
+#line 1203 "limbo.y"
+{
+ yyval.node = mkbin(Oxor, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 209:
+#line 1207 "limbo.y"
+{
+ yyval.node = mkbin(Oor, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 210:
+#line 1211 "limbo.y"
+{
+ yyval.node = mkbin(Ocons, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 211:
+#line 1215 "limbo.y"
+{
+ yyval.node = mkbin(Oandand, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 212:
+#line 1219 "limbo.y"
+{
+ yyval.node = mkbin(Ooror, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 214:
+#line 1226 "limbo.y"
+{
+ yypt[-0].yyv.node->src.start = yypt[-1].yyv.tok.src.start;
+ yyval.node = yypt[-0].yyv.node;
+ } break;
+case 215:
+#line 1231 "limbo.y"
+{
+ yyval.node = mkunary(Oneg, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 216:
+#line 1236 "limbo.y"
+{
+ yyval.node = mkunary(Onot, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 217:
+#line 1241 "limbo.y"
+{
+ yyval.node = mkunary(Ocomp, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 218:
+#line 1246 "limbo.y"
+{
+ yyval.node = mkunary(Oind, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 219:
+#line 1251 "limbo.y"
+{
+ yyval.node = mkunary(Opreinc, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 220:
+#line 1256 "limbo.y"
+{
+ yyval.node = mkunary(Opredec, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 221:
+#line 1261 "limbo.y"
+{
+ yyval.node = mkunary(Orcv, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 222:
+#line 1266 "limbo.y"
+{
+ yyval.node = mkunary(Ohd, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 223:
+#line 1271 "limbo.y"
+{
+ yyval.node = mkunary(Otl, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 224:
+#line 1276 "limbo.y"
+{
+ yyval.node = mkunary(Olen, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 225:
+#line 1281 "limbo.y"
+{
+ yyval.node = mkunary(Oref, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 226:
+#line 1286 "limbo.y"
+{
+ yyval.node = mkunary(Otagof, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ } break;
+case 227:
+#line 1291 "limbo.y"
+{
+ yyval.node = mkn(Oarray, yypt[-3].yyv.node, nil);
+ yyval.node->ty = mktype(&yypt[-5].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tarray, yypt[-0].yyv.type, nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 228:
+#line 1297 "limbo.y"
+{
+ yyval.node = mkn(Oarray, yypt[-5].yyv.node, yypt[-1].yyv.node);
+ yyval.node->src.start = yypt[-7].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 229:
+#line 1303 "limbo.y"
+{
+ yyval.node = mkn(Onothing, nil, nil);
+ yyval.node->src.start = yypt[-5].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-4].yyv.tok.src.stop;
+ yyval.node = mkn(Oarray, yyval.node, yypt[-1].yyv.node);
+ yyval.node->src.start = yypt[-6].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 230:
+#line 1312 "limbo.y"
+{
+ yyval.node = etolist(yypt[-1].yyv.node);
+ yyval.node->src.start = yypt[-4].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 231:
+#line 1318 "limbo.y"
+{
+ yyval.node = mkn(Ochan, nil, nil);
+ yyval.node->ty = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tchan, yypt[-0].yyv.type, nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 232:
+#line 1324 "limbo.y"
+{
+ yyval.node = mkn(Ochan, yypt[-3].yyv.node, nil);
+ yyval.node->ty = mktype(&yypt[-5].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tchan, yypt[-0].yyv.type, nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 233:
+#line 1330 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yypt[-0].yyv.node);
+ yyval.node->ty = mktype(&yypt[-3].yyv.tok.src.start, &yypt[-0].yyv.node->src.stop, Tarray, mkidtype(&yypt[-1].yyv.tok.src, yypt[-1].yyv.tok.v.idval), nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 234:
+#line 1336 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ yyval.node->ty = mkidtype(&yyval.node->src, yypt[-1].yyv.tok.v.idval);
+ } break;
+case 235:
+#line 1342 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ yyval.node->ty = mkidtype(&yyval.node->src, yypt[-1].yyv.tok.v.idval);
+ } break;
+case 236:
+#line 1348 "limbo.y"
+{
+ yyval.node = mkunary(Ocast, yypt[-0].yyv.node);
+ yyval.node->src.start = yypt[-1].yyv.tok.src.start;
+ yyval.node->ty = yypt[-1].yyv.type;
+ } break;
+case 238:
+#line 1357 "limbo.y"
+{
+ yyval.node = mkn(Ocall, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ yyval.node->src.start = yypt[-3].yyv.node->src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 239:
+#line 1363 "limbo.y"
+{
+ yyval.node = yypt[-1].yyv.node;
+ if(yypt[-1].yyv.node->op == Oseq)
+ yyval.node = mkn(Otuple, rotater(yypt[-1].yyv.node), nil);
+ else
+ yyval.node->flags |= PARENS;
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 240:
+#line 1373 "limbo.y"
+{
+ yyval.node = mkbin(Odot, yypt[-2].yyv.node, mkname(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval));
+ } break;
+case 241:
+#line 1377 "limbo.y"
+{
+ yyval.node = mkbin(Omdot, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 242:
+#line 1381 "limbo.y"
+{
+ yyval.node = mkbin(Oindex, yypt[-3].yyv.node, yypt[-1].yyv.node);
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 243:
+#line 1386 "limbo.y"
+{
+ if(yypt[-3].yyv.node->op == Onothing)
+ yypt[-3].yyv.node->src = yypt[-2].yyv.tok.src;
+ if(yypt[-1].yyv.node->op == Onothing)
+ yypt[-1].yyv.node->src = yypt[-2].yyv.tok.src;
+ yyval.node = mkbin(Oslice, yypt[-5].yyv.node, mkbin(Oseq, yypt[-3].yyv.node, yypt[-1].yyv.node));
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 244:
+#line 1395 "limbo.y"
+{
+ yyval.node = mkunary(Oinc, yypt[-1].yyv.node);
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 245:
+#line 1400 "limbo.y"
+{
+ yyval.node = mkunary(Odec, yypt[-1].yyv.node);
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 246:
+#line 1405 "limbo.y"
+{
+ yyval.node = mksconst(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 247:
+#line 1409 "limbo.y"
+{
+ yyval.node = mkconst(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.ival);
+ if(yypt[-0].yyv.tok.v.ival > 0x7fffffff || yypt[-0].yyv.tok.v.ival < -0x7fffffff)
+ yyval.node->ty = tbig;
+ yyval.node = yyval.node;
+ } break;
+case 248:
+#line 1416 "limbo.y"
+{
+ yyval.node = mkrconst(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.rval);
+ } break;
+case 249:
+#line 1420 "limbo.y"
+{
+ yyval.node = mkbin(Oindex, yypt[-5].yyv.node, rotater(mkbin(Oseq, yypt[-3].yyv.node, yypt[-1].yyv.node)));
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 250:
+#line 1427 "limbo.y"
+{
+ yyval.node = mkname(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 251:
+#line 1431 "limbo.y"
+{
+ yyval.node = mknil(&yypt[-0].yyv.tok.src);
+ } break;
+case 252:
+#line 1437 "limbo.y"
+{
+ yyval.node = mkn(Otuple, rotater(yypt[-1].yyv.node), nil);
+ yyval.node->src.start = yypt[-2].yyv.tok.src.start;
+ yyval.node->src.stop = yypt[-0].yyv.tok.src.stop;
+ } break;
+case 254:
+#line 1446 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 257:
+#line 1456 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node->ty = mkidtype(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 258:
+#line 1462 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node->ty = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tarray, yypt[-0].yyv.type, nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 259:
+#line 1468 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil, nil);
+ yyval.node->ty = mktype(&yypt[-2].yyv.tok.src.start, &yypt[-0].yyv.type->src.stop, Tlist, yypt[-0].yyv.type, nil);
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 260:
+#line 1474 "limbo.y"
+{
+ yyval.node = mkn(Otype, nil ,nil);
+ yyval.node->ty = yypt[-0].yyv.type;
+ yyval.node->ty->flags |= CYCLIC;
+ yyval.node->src = yyval.node->ty->src;
+ } break;
+case 261:
+#line 1483 "limbo.y"
+{
+ yyval.node = mkname(&yypt[-0].yyv.tok.src, yypt[-0].yyv.tok.v.idval);
+ } break;
+case 262:
+#line 1487 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 265:
+#line 1495 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 266:
+#line 1499 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 267:
+#line 1505 "limbo.y"
+{
+ yyval.node = nil;
+ } break;
+case 268:
+#line 1509 "limbo.y"
+{
+ yyval.node = rotater(yypt[-0].yyv.node);
+ } break;
+case 272:
+#line 1520 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 273:
+#line 1526 "limbo.y"
+{
+ yyval.node = rotater(yypt[-0].yyv.node);
+ } break;
+case 274:
+#line 1530 "limbo.y"
+{
+ yyval.node = rotater(yypt[-1].yyv.node);
+ } break;
+case 276:
+#line 1537 "limbo.y"
+{
+ yyval.node = mkbin(Oseq, yypt[-2].yyv.node, yypt[-0].yyv.node);
+ } break;
+case 277:
+#line 1543 "limbo.y"
+{
+ yyval.node = mkn(Oelem, nil, yypt[-0].yyv.node);
+ yyval.node->src = yypt[-0].yyv.node->src;
+ } break;
+case 278:
+#line 1548 "limbo.y"
+{
+ yyval.node = mkbin(Oelem, rotater(yypt[-2].yyv.node), yypt[-0].yyv.node);
+ } break;
+case 279:
+#line 1573 "limbo.y"
+{
+ if(yypt[-1].yyv.node->op == Oseq)
+ yypt[-1].yyv.node->right->left = rotater(yypt[-0].yyv.node);
+ else
+ yypt[-1].yyv.node->left = rotater(yypt[-0].yyv.node);
+ yyval.node = yypt[-1].yyv.node;
+ } break;
+case 280:
+#line 1583 "limbo.y"
+{
+ yyval.node = typedecl(yypt[-1].yyv.ids, mktype(&yypt[-1].yyv.ids->src.start, &yypt[-0].yyv.tok.src.stop, Tpoly, nil, nil));
+ } break;
+case 281:
+#line 1587 "limbo.y"
+{
+ if(yypt[-3].yyv.node->op == Oseq)
+ yypt[-3].yyv.node->right->left = rotater(yypt[-2].yyv.node);
+ else
+ yypt[-3].yyv.node->left = rotater(yypt[-2].yyv.node);
+ yyval.node = mkbin(Oseq, yypt[-3].yyv.node, typedecl(yypt[-1].yyv.ids, mktype(&yypt[-1].yyv.ids->src.start, &yypt[-0].yyv.tok.src.stop, Tpoly, nil, nil)));
+ } break;
+ }
+ goto yystack; /* stack new state and value */
+}
diff --git a/limbo/y.tab.h b/limbo/y.tab.h
new file mode 100644
index 0000000..bad2cec
--- /dev/null
+++ b/limbo/y.tab.h
@@ -0,0 +1,90 @@
+
+typedef union
+{
+ struct{
+ Src src;
+ union{
+ Sym *idval;
+ Long ival;
+ Real rval;
+ }v;
+ }tok;
+ Decl *ids;
+ Node *node;
+ Type *type;
+ Typelist *types;
+} YYSTYPE;
+extern YYSTYPE yylval;
+#define Landeq 57346
+#define Loreq 57347
+#define Lxoreq 57348
+#define Llsheq 57349
+#define Lrsheq 57350
+#define Laddeq 57351
+#define Lsubeq 57352
+#define Lmuleq 57353
+#define Ldiveq 57354
+#define Lmodeq 57355
+#define Lexpeq 57356
+#define Ldeclas 57357
+#define Lload 57358
+#define Loror 57359
+#define Landand 57360
+#define Lcons 57361
+#define Leq 57362
+#define Lneq 57363
+#define Lleq 57364
+#define Lgeq 57365
+#define Llsh 57366
+#define Lrsh 57367
+#define Lexp 57368
+#define Lcomm 57369
+#define Linc 57370
+#define Ldec 57371
+#define Lof 57372
+#define Lref 57373
+#define Lif 57374
+#define Lelse 57375
+#define Lfn 57376
+#define Lexcept 57377
+#define Lraises 57378
+#define Lmdot 57379
+#define Lto 57380
+#define Lor 57381
+#define Lrconst 57382
+#define Lconst 57383
+#define Lid 57384
+#define Ltid 57385
+#define Lsconst 57386
+#define Llabs 57387
+#define Lnil 57388
+#define Llen 57389
+#define Lhd 57390
+#define Ltl 57391
+#define Ltagof 57392
+#define Limplement 57393
+#define Limport 57394
+#define Linclude 57395
+#define Lcon 57396
+#define Ltype 57397
+#define Lmodule 57398
+#define Lcyclic 57399
+#define Ladt 57400
+#define Larray 57401
+#define Llist 57402
+#define Lchan 57403
+#define Lself 57404
+#define Ldo 57405
+#define Lwhile 57406
+#define Lfor 57407
+#define Lbreak 57408
+#define Lalt 57409
+#define Lcase 57410
+#define Lpick 57411
+#define Lcont 57412
+#define Lreturn 57413
+#define Lexit 57414
+#define Lspawn 57415
+#define Lraise 57416
+#define Lfix 57417
+#define Ldynamic 57418
diff --git a/limbo/y.tab.o b/limbo/y.tab.o
new file mode 100644
index 0000000..1bfcd54
--- /dev/null
+++ b/limbo/y.tab.o
Binary files differ