summaryrefslogtreecommitdiff
path: root/src/cmd/gc/subr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/subr.c')
-rw-r--r--src/cmd/gc/subr.c3885
1 files changed, 3885 insertions, 0 deletions
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
new file mode 100644
index 000000000..1a05d43d0
--- /dev/null
+++ b/src/cmd/gc/subr.c
@@ -0,0 +1,3885 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go.h"
+#include "md5.h"
+#include "y.tab.h"
+#include "opnames.h"
+#include "yerr.h"
+
+static void dodump(Node*, int);
+
+typedef struct Error Error;
+struct Error
+{
+ int lineno;
+ int seq;
+ char *msg;
+};
+static Error *err;
+static int nerr;
+static int merr;
+
+void
+errorexit(void)
+{
+ flusherrors();
+ if(outfile)
+ remove(outfile);
+ exit(1);
+}
+
+extern int yychar;
+int
+parserline(void)
+{
+ if(yychar != 0 && yychar != -2) // parser has one symbol lookahead
+ return prevlineno;
+ return lineno;
+}
+
+static void
+adderr(int line, char *fmt, va_list arg)
+{
+ Fmt f;
+ Error *p;
+
+ erroring++;
+ fmtstrinit(&f);
+ fmtprint(&f, "%L: ", line);
+ fmtvprint(&f, fmt, arg);
+ fmtprint(&f, "\n");
+ erroring--;
+
+ if(nerr >= merr) {
+ if(merr == 0)
+ merr = 16;
+ else
+ merr *= 2;
+ p = realloc(err, merr*sizeof err[0]);
+ if(p == nil) {
+ merr = nerr;
+ flusherrors();
+ print("out of memory\n");
+ errorexit();
+ }
+ err = p;
+ }
+ err[nerr].seq = nerr;
+ err[nerr].lineno = line;
+ err[nerr].msg = fmtstrflush(&f);
+ nerr++;
+}
+
+static int
+errcmp(const void *va, const void *vb)
+{
+ Error *a, *b;
+
+ a = (Error*)va;
+ b = (Error*)vb;
+ if(a->lineno != b->lineno)
+ return a->lineno - b->lineno;
+ if(a->seq != b->seq)
+ return a->seq - b->seq;
+ return strcmp(a->msg, b->msg);
+}
+
+void
+flusherrors(void)
+{
+ int i;
+
+ if(nerr == 0)
+ return;
+ qsort(err, nerr, sizeof err[0], errcmp);
+ for(i=0; i<nerr; i++)
+ if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
+ print("%s", err[i].msg);
+ nerr = 0;
+}
+
+static void
+hcrash(void)
+{
+ if(debug['h']) {
+ flusherrors();
+ if(outfile)
+ unlink(outfile);
+ *(volatile int*)0 = 0;
+ }
+}
+
+void
+yyerrorl(int line, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(line, fmt, arg);
+ va_end(arg);
+
+ hcrash();
+ nerrors++;
+ if(nerrors >= 10 && !debug['e']) {
+ flusherrors();
+ print("%L: too many errors\n", line);
+ errorexit();
+ }
+}
+
+extern int yystate, yychar;
+
+void
+yyerror(char *fmt, ...)
+{
+ int i;
+ static int lastsyntax;
+ va_list arg;
+ char buf[512], *p;
+
+ if(strncmp(fmt, "syntax error", 12) == 0) {
+ nsyntaxerrors++;
+
+ if(debug['x'])
+ print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
+
+ // only one syntax error per line
+ if(lastsyntax == lexlineno)
+ return;
+ lastsyntax = lexlineno;
+
+ if(strstr(fmt, "{ or {")) {
+ // The grammar has { and LBRACE but both show up as {.
+ // Rewrite syntax error referring to "{ or {" to say just "{".
+ strecpy(buf, buf+sizeof buf, fmt);
+ p = strstr(buf, "{ or {");
+ if(p)
+ memmove(p+1, p+6, strlen(p+6)+1);
+ fmt = buf;
+ }
+
+ // look for parse state-specific errors in list (see go.errors).
+ for(i=0; i<nelem(yymsg); i++) {
+ if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
+ yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
+ return;
+ }
+ }
+
+ // plain "syntax error" gets "near foo" added
+ if(strcmp(fmt, "syntax error") == 0) {
+ yyerrorl(lexlineno, "syntax error near %s", lexbuf);
+ return;
+ }
+
+ // if bison says "syntax error, more info"; print "syntax error: more info".
+ if(fmt[12] == ',') {
+ yyerrorl(lexlineno, "syntax error:%s", fmt+13);
+ return;
+ }
+
+ yyerrorl(lexlineno, "%s", fmt);
+ return;
+ }
+
+ va_start(arg, fmt);
+ adderr(parserline(), fmt, arg);
+ va_end(arg);
+
+ hcrash();
+ nerrors++;
+ if(nerrors >= 10 && !debug['e']) {
+ flusherrors();
+ print("%L: too many errors\n", parserline());
+ errorexit();
+ }
+}
+
+void
+warn(char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(parserline(), fmt, arg);
+ va_end(arg);
+
+ hcrash();
+}
+
+void
+fatal(char *fmt, ...)
+{
+ va_list arg;
+
+ flusherrors();
+
+ print("%L: internal compiler error: ", lineno);
+ va_start(arg, fmt);
+ vfprint(1, fmt, arg);
+ va_end(arg);
+ print("\n");
+
+ // If this is a released compiler version, ask for a bug report.
+ if(strncmp(getgoversion(), "release", 7) == 0) {
+ print("\n");
+ print("Please file a bug report including a short program that triggers the error.\n");
+ print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
+ }
+ hcrash();
+ errorexit();
+}
+
+void
+linehist(char *file, int32 off, int relative)
+{
+ Hist *h;
+ char *cp;
+
+ if(debug['i']) {
+ if(file != nil) {
+ if(off < 0)
+ print("pragma %s", file);
+ else
+ if(off > 0)
+ print("line %s", file);
+ else
+ print("import %s", file);
+ } else
+ print("end of import");
+ print(" at line %L\n", lexlineno);
+ }
+
+ if(off < 0 && file[0] != '/' && !relative) {
+ cp = mal(strlen(file) + strlen(pathname) + 2);
+ sprint(cp, "%s/%s", pathname, file);
+ file = cp;
+ }
+
+ h = mal(sizeof(Hist));
+ h->name = file;
+ h->line = lexlineno;
+ h->offset = off;
+ h->link = H;
+ if(ehist == H) {
+ hist = h;
+ ehist = h;
+ return;
+ }
+ ehist->link = h;
+ ehist = h;
+}
+
+int32
+setlineno(Node *n)
+{
+ int32 lno;
+
+ lno = lineno;
+ if(n != N)
+ switch(n->op) {
+ case ONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ break;
+ default:
+ lineno = n->lineno;
+ if(lineno == 0) {
+ if(debug['K'])
+ warn("setlineno: line 0");
+ lineno = lno;
+ }
+ }
+ return lno;
+}
+
+uint32
+stringhash(char *p)
+{
+ int32 h;
+ int c;
+
+ h = 0;
+ for(;;) {
+ c = *p++;
+ if(c == 0)
+ break;
+ h = h*PRIME1 + c;
+ }
+
+ if(h < 0) {
+ h = -h;
+ if(h < 0)
+ h = 0;
+ }
+ return h;
+}
+
+Sym*
+lookup(char *name)
+{
+ return pkglookup(name, localpkg);
+}
+
+Sym*
+pkglookup(char *name, Pkg *pkg)
+{
+ Sym *s;
+ uint32 h;
+ int c;
+
+ h = stringhash(name) % NHASH;
+ c = name[0];
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->name[0] != c || s->pkg != pkg)
+ continue;
+ if(strcmp(s->name, name) == 0)
+ return s;
+ }
+
+ s = mal(sizeof(*s));
+ s->name = mal(strlen(name)+1);
+ strcpy(s->name, name);
+
+ s->pkg = pkg;
+
+ s->link = hash[h];
+ hash[h] = s;
+ s->lexical = LNAME;
+
+ return s;
+}
+
+Sym*
+restrictlookup(char *name, Pkg *pkg)
+{
+ if(!exportname(name) && pkg != localpkg)
+ yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
+ return pkglookup(name, pkg);
+}
+
+
+// find all the exported symbols in package opkg
+// and make them available in the current package
+void
+importdot(Pkg *opkg, Node *pack)
+{
+ Sym *s, *s1;
+ uint32 h;
+ int n;
+
+ n = 0;
+ for(h=0; h<NHASH; h++) {
+ for(s = hash[h]; s != S; s = s->link) {
+ if(s->pkg != opkg)
+ continue;
+ if(s->def == N)
+ continue;
+ if(!exportname(s->name) || utfrune(s->name, 0xb7)) // 0xb7 = center dot
+ continue;
+ s1 = lookup(s->name);
+ if(s1->def != N) {
+ redeclare(s1, "during import");
+ continue;
+ }
+ s1->def = s->def;
+ s1->block = s->block;
+ s1->def->pack = pack;
+ n++;
+ }
+ }
+ if(n == 0) {
+ // can't possibly be used - there were no symbols
+ yyerrorl(pack->lineno, "imported and not used: %Z", opkg->path);
+ }
+}
+
+static void
+gethunk(void)
+{
+ char *h;
+ int32 nh;
+
+ nh = NHUNK;
+ if(thunk >= 10L*NHUNK)
+ nh = 10L*NHUNK;
+ h = (char*)malloc(nh);
+ if(h == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ hunk = h;
+ nhunk = nh;
+ thunk += nh;
+}
+
+void*
+mal(int32 n)
+{
+ void *p;
+
+ if(n >= NHUNK) {
+ p = malloc(n);
+ if(p == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ memset(p, 0, n);
+ return p;
+ }
+
+ while((uintptr)hunk & MAXALIGN) {
+ hunk++;
+ nhunk--;
+ }
+ if(nhunk < n)
+ gethunk();
+
+ p = hunk;
+ nhunk -= n;
+ hunk += n;
+ memset(p, 0, n);
+ return p;
+}
+
+void*
+remal(void *p, int32 on, int32 n)
+{
+ void *q;
+
+ q = (uchar*)p + on;
+ if(q != hunk || nhunk < n) {
+ if(on+n >= NHUNK) {
+ q = mal(on+n);
+ memmove(q, p, on);
+ return q;
+ }
+ if(nhunk < on+n)
+ gethunk();
+ memmove(hunk, p, on);
+ p = hunk;
+ hunk += on;
+ nhunk -= on;
+ }
+ hunk += n;
+ nhunk -= n;
+ return p;
+}
+
+Node*
+nod(int op, Node *nleft, Node *nright)
+{
+ Node *n;
+
+ n = mal(sizeof(*n));
+ n->op = op;
+ n->left = nleft;
+ n->right = nright;
+ n->lineno = parserline();
+ n->xoffset = BADWIDTH;
+ n->orig = n;
+ return n;
+}
+
+int
+algtype(Type *t)
+{
+ int a;
+
+ if(issimple[t->etype] || isptr[t->etype] ||
+ t->etype == TCHAN || t->etype == TFUNC || t->etype == TMAP) {
+ if(t->width == 1)
+ a = AMEM8;
+ else if(t->width == 2)
+ a = AMEM16;
+ else if(t->width == 4)
+ a = AMEM32;
+ else if(t->width == 8)
+ a = AMEM64;
+ else if(t->width == 16)
+ a = AMEM128;
+ else
+ a = AMEM; // just bytes (int, ptr, etc)
+ } else if(t->etype == TSTRING)
+ a = ASTRING; // string
+ else if(isnilinter(t))
+ a = ANILINTER; // nil interface
+ else if(t->etype == TINTER)
+ a = AINTER; // interface
+ else if(isslice(t))
+ a = ASLICE; // slice
+ else {
+ if(t->width == 1)
+ a = ANOEQ8;
+ else if(t->width == 2)
+ a = ANOEQ16;
+ else if(t->width == 4)
+ a = ANOEQ32;
+ else if(t->width == 8)
+ a = ANOEQ64;
+ else if(t->width == 16)
+ a = ANOEQ128;
+ else
+ a = ANOEQ; // just bytes, but no hash/eq
+ }
+ return a;
+}
+
+Type*
+maptype(Type *key, Type *val)
+{
+ Type *t;
+
+
+ if(key != nil && key->etype != TANY && algtype(key) == ANOEQ) {
+ if(key->etype == TFORW) {
+ // map[key] used during definition of key.
+ // postpone check until key is fully defined.
+ // if there are multiple uses of map[key]
+ // before key is fully defined, the error
+ // will only be printed for the first one.
+ // good enough.
+ if(key->maplineno == 0)
+ key->maplineno = lineno;
+ } else
+ yyerror("invalid map key type %T", key);
+ }
+ t = typ(TMAP);
+ t->down = key;
+ t->type = val;
+ return t;
+}
+
+Type*
+typ(int et)
+{
+ Type *t;
+
+ t = mal(sizeof(*t));
+ t->etype = et;
+ t->width = BADWIDTH;
+ t->lineno = lineno;
+ t->orig = t;
+ return t;
+}
+
+static int
+methcmp(const void *va, const void *vb)
+{
+ Type *a, *b;
+ int i;
+
+ a = *(Type**)va;
+ b = *(Type**)vb;
+ i = strcmp(a->sym->name, b->sym->name);
+ if(i != 0)
+ return i;
+ if(!exportname(a->sym->name)) {
+ i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
+ if(i != 0)
+ return i;
+ }
+ return 0;
+}
+
+Type*
+sortinter(Type *t)
+{
+ Type *f;
+ int i;
+ Type **a;
+
+ if(t->type == nil || t->type->down == nil)
+ return t;
+
+ i=0;
+ for(f=t->type; f; f=f->down)
+ i++;
+ a = mal(i*sizeof f);
+ i = 0;
+ for(f=t->type; f; f=f->down)
+ a[i++] = f;
+ qsort(a, i, sizeof a[0], methcmp);
+ while(i-- > 0) {
+ a[i]->down = f;
+ f = a[i];
+ }
+ t->type = f;
+ return t;
+}
+
+Node*
+nodintconst(int64 v)
+{
+ Node *c;
+
+ c = nod(OLITERAL, N, N);
+ c->addable = 1;
+ c->val.u.xval = mal(sizeof(*c->val.u.xval));
+ mpmovecfix(c->val.u.xval, v);
+ c->val.ctype = CTINT;
+ c->type = types[TIDEAL];
+ ullmancalc(c);
+ return c;
+}
+
+Node*
+nodfltconst(Mpflt* v)
+{
+ Node *c;
+
+ c = nod(OLITERAL, N, N);
+ c->addable = 1;
+ c->val.u.fval = mal(sizeof(*c->val.u.fval));
+ mpmovefltflt(c->val.u.fval, v);
+ c->val.ctype = CTFLT;
+ c->type = types[TIDEAL];
+ ullmancalc(c);
+ return c;
+}
+
+void
+nodconst(Node *n, Type *t, int64 v)
+{
+ memset(n, 0, sizeof(*n));
+ n->op = OLITERAL;
+ n->addable = 1;
+ ullmancalc(n);
+ n->val.u.xval = mal(sizeof(*n->val.u.xval));
+ mpmovecfix(n->val.u.xval, v);
+ n->val.ctype = CTINT;
+ n->type = t;
+
+ if(isfloat[t->etype])
+ fatal("nodconst: bad type %T", t);
+}
+
+Node*
+nodnil(void)
+{
+ Node *c;
+
+ c = nodintconst(0);
+ c->val.ctype = CTNIL;
+ c->type = types[TNIL];
+ return c;
+}
+
+Node*
+nodbool(int b)
+{
+ Node *c;
+
+ c = nodintconst(0);
+ c->val.ctype = CTBOOL;
+ c->val.u.bval = b;
+ c->type = idealbool;
+ return c;
+}
+
+Type*
+aindex(Node *b, Type *t)
+{
+ Type *r;
+ int bound;
+
+ bound = -1; // open bound
+ typecheck(&b, Erv);
+ if(b != nil) {
+ switch(consttype(b)) {
+ default:
+ yyerror("array bound must be an integer expression");
+ break;
+ case CTINT:
+ bound = mpgetfix(b->val.u.xval);
+ if(bound < 0)
+ yyerror("array bound must be non negative");
+ break;
+ }
+ }
+
+ // fixed array
+ r = typ(TARRAY);
+ r->type = t;
+ r->bound = bound;
+ return r;
+}
+
+static void
+indent(int dep)
+{
+ int i;
+
+ for(i=0; i<dep; i++)
+ print(". ");
+}
+
+static void
+dodumplist(NodeList *l, int dep)
+{
+ for(; l; l=l->next)
+ dodump(l->n, dep);
+}
+
+static void
+dodump(Node *n, int dep)
+{
+ if(n == N)
+ return;
+
+ indent(dep);
+ if(dep > 10) {
+ print("...\n");
+ return;
+ }
+
+ if(n->ninit != nil) {
+ print("%O-init\n", n->op);
+ dodumplist(n->ninit, dep+1);
+ indent(dep);
+ }
+
+ switch(n->op) {
+ default:
+ print("%N\n", n);
+ dodump(n->left, dep+1);
+ dodump(n->right, dep+1);
+ break;
+
+ case OTYPE:
+ print("%O %S type=%T\n", n->op, n->sym, n->type);
+ if(n->type == T && n->ntype) {
+ indent(dep);
+ print("%O-ntype\n", n->op);
+ dodump(n->ntype, dep+1);
+ }
+ break;
+
+ case OIF:
+ print("%O%J\n", n->op, n);
+ dodump(n->ntest, dep+1);
+ if(n->nbody != nil) {
+ indent(dep);
+ print("%O-then\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+ if(n->nelse != nil) {
+ indent(dep);
+ print("%O-else\n", n->op);
+ dodumplist(n->nelse, dep+1);
+ }
+ break;
+
+ case OSELECT:
+ print("%O%J\n", n->op, n);
+ dodumplist(n->nbody, dep+1);
+ break;
+
+ case OSWITCH:
+ case OFOR:
+ print("%O%J\n", n->op, n);
+ dodump(n->ntest, dep+1);
+
+ if(n->nbody != nil) {
+ indent(dep);
+ print("%O-body\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+
+ if(n->nincr != N) {
+ indent(dep);
+ print("%O-incr\n", n->op);
+ dodump(n->nincr, dep+1);
+ }
+ break;
+
+ case OCASE:
+ // the right side points to label of the body
+ if(n->right != N && n->right->op == OGOTO && n->right->left->op == ONAME)
+ print("%O%J GOTO %N\n", n->op, n, n->right->left);
+ else
+ print("%O%J\n", n->op, n);
+ dodump(n->left, dep+1);
+ break;
+
+ case OXCASE:
+ print("%N\n", n);
+ dodump(n->left, dep+1);
+ dodump(n->right, dep+1);
+ indent(dep);
+ print("%O-nbody\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ break;
+ }
+
+ if(0 && n->ntype != nil) {
+ indent(dep);
+ print("%O-ntype\n", n->op);
+ dodump(n->ntype, dep+1);
+ }
+ if(n->list != nil) {
+ indent(dep);
+ print("%O-list\n", n->op);
+ dodumplist(n->list, dep+1);
+ }
+ if(n->rlist != nil) {
+ indent(dep);
+ print("%O-rlist\n", n->op);
+ dodumplist(n->rlist, dep+1);
+ }
+ if(n->op != OIF && n->nbody != nil) {
+ indent(dep);
+ print("%O-nbody\n", n->op);
+ dodumplist(n->nbody, dep+1);
+ }
+}
+
+void
+dumplist(char *s, NodeList *l)
+{
+ print("%s\n", s);
+ dodumplist(l, 1);
+}
+
+void
+dump(char *s, Node *n)
+{
+ print("%s [%p]\n", s, n);
+ dodump(n, 1);
+}
+
+static char*
+goopnames[] =
+{
+ [OADDR] = "&",
+ [OADD] = "+",
+ [OANDAND] = "&&",
+ [OANDNOT] = "&^",
+ [OAND] = "&",
+ [OAPPEND] = "append",
+ [OAS] = "=",
+ [OAS2] = "=",
+ [OBREAK] = "break",
+ [OCALL] = "function call",
+ [OCAP] = "cap",
+ [OCASE] = "case",
+ [OCLOSE] = "close",
+ [OCOMPLEX] = "complex",
+ [OCOM] = "^",
+ [OCONTINUE] = "continue",
+ [OCOPY] = "copy",
+ [ODEC] = "--",
+ [ODEFER] = "defer",
+ [ODIV] = "/",
+ [OEQ] = "==",
+ [OFALL] = "fallthrough",
+ [OFOR] = "for",
+ [OGE] = ">=",
+ [OGOTO] = "goto",
+ [OGT] = ">",
+ [OIF] = "if",
+ [OIMAG] = "imag",
+ [OINC] = "++",
+ [OIND] = "*",
+ [OLEN] = "len",
+ [OLE] = "<=",
+ [OLSH] = "<<",
+ [OLT] = "<",
+ [OMAKE] = "make",
+ [OMINUS] = "-",
+ [OMOD] = "%",
+ [OMUL] = "*",
+ [ONEW] = "new",
+ [ONE] = "!=",
+ [ONOT] = "!",
+ [OOROR] = "||",
+ [OOR] = "|",
+ [OPANIC] = "panic",
+ [OPLUS] = "+",
+ [OPRINTN] = "println",
+ [OPRINT] = "print",
+ [ORANGE] = "range",
+ [OREAL] = "real",
+ [ORECV] = "<-",
+ [ORETURN] = "return",
+ [ORSH] = ">>",
+ [OSELECT] = "select",
+ [OSEND] = "<-",
+ [OSUB] = "-",
+ [OSWITCH] = "switch",
+ [OXOR] = "^",
+};
+
+int
+Oconv(Fmt *fp)
+{
+ int o;
+
+ o = va_arg(fp->args, int);
+ if((fp->flags & FmtSharp) && o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
+ return fmtstrcpy(fp, goopnames[o]);
+ if(o < 0 || o >= nelem(opnames) || opnames[o] == nil)
+ return fmtprint(fp, "O-%d", o);
+ return fmtstrcpy(fp, opnames[o]);
+}
+
+int
+Lconv(Fmt *fp)
+{
+ struct
+ {
+ Hist* incl; /* start of this include file */
+ int32 idel; /* delta line number to apply to include */
+ Hist* line; /* start of this #line directive */
+ int32 ldel; /* delta line number to apply to #line */
+ } a[HISTSZ];
+ int32 lno, d;
+ int i, n;
+ Hist *h;
+
+ lno = va_arg(fp->args, int32);
+
+ n = 0;
+ for(h=hist; h!=H; h=h->link) {
+ if(h->offset < 0)
+ continue;
+ if(lno < h->line)
+ break;
+ if(h->name) {
+ if(h->offset > 0) {
+ // #line directive
+ if(n > 0 && n < HISTSZ) {
+ a[n-1].line = h;
+ a[n-1].ldel = h->line - h->offset + 1;
+ }
+ } else {
+ // beginning of file
+ if(n < HISTSZ) {
+ a[n].incl = h;
+ a[n].idel = h->line;
+ a[n].line = 0;
+ }
+ n++;
+ }
+ continue;
+ }
+ n--;
+ if(n > 0 && n < HISTSZ) {
+ d = h->line - a[n].incl->line;
+ a[n-1].ldel += d;
+ a[n-1].idel += d;
+ }
+ }
+
+ if(n > HISTSZ)
+ n = HISTSZ;
+
+ for(i=n-1; i>=0; i--) {
+ if(i != n-1) {
+ if(fp->flags & ~(FmtWidth|FmtPrec))
+ break;
+ fmtprint(fp, " ");
+ }
+ if(debug['L'])
+ fmtprint(fp, "%s/", pathname);
+ if(a[i].line)
+ fmtprint(fp, "%s:%d[%s:%d]",
+ a[i].line->name, lno-a[i].ldel+1,
+ a[i].incl->name, lno-a[i].idel+1);
+ else
+ fmtprint(fp, "%s:%d",
+ a[i].incl->name, lno-a[i].idel+1);
+ lno = a[i].incl->line - 1; // now print out start of this file
+ }
+ if(n == 0)
+ fmtprint(fp, "<epoch>");
+
+ return 0;
+}
+
+/*
+s%,%,\n%g
+s%\n+%\n%g
+s%^[ ]*T%%g
+s%,.*%%g
+s%.+% [T&] = "&",%g
+s%^ ........*\]%&~%g
+s%~ %%g
+*/
+
+static char*
+etnames[] =
+{
+ [TINT] = "INT",
+ [TUINT] = "UINT",
+ [TINT8] = "INT8",
+ [TUINT8] = "UINT8",
+ [TINT16] = "INT16",
+ [TUINT16] = "UINT16",
+ [TINT32] = "INT32",
+ [TUINT32] = "UINT32",
+ [TINT64] = "INT64",
+ [TUINT64] = "UINT64",
+ [TUINTPTR] = "UINTPTR",
+ [TFLOAT32] = "FLOAT32",
+ [TFLOAT64] = "FLOAT64",
+ [TCOMPLEX64] = "COMPLEX64",
+ [TCOMPLEX128] = "COMPLEX128",
+ [TBOOL] = "BOOL",
+ [TPTR32] = "PTR32",
+ [TPTR64] = "PTR64",
+ [TFUNC] = "FUNC",
+ [TARRAY] = "ARRAY",
+ [TSTRUCT] = "STRUCT",
+ [TCHAN] = "CHAN",
+ [TMAP] = "MAP",
+ [TINTER] = "INTER",
+ [TFORW] = "FORW",
+ [TFIELD] = "FIELD",
+ [TSTRING] = "STRING",
+ [TANY] = "ANY",
+};
+
+int
+Econv(Fmt *fp)
+{
+ int et;
+
+ et = va_arg(fp->args, int);
+ if(et < 0 || et >= nelem(etnames) || etnames[et] == nil)
+ return fmtprint(fp, "E-%d", et);
+ return fmtstrcpy(fp, etnames[et]);
+}
+
+static const char* classnames[] = {
+ "Pxxx",
+ "PEXTERN",
+ "PAUTO",
+ "PPARAM",
+ "PPARAMOUT",
+ "PPARAMREF",
+ "PFUNC",
+};
+
+int
+Jconv(Fmt *fp)
+{
+ Node *n;
+ char *s;
+ int c;
+
+ n = va_arg(fp->args, Node*);
+
+ c = fp->flags&FmtShort;
+
+ if(!c && n->ullman != 0)
+ fmtprint(fp, " u(%d)", n->ullman);
+
+ if(!c && n->addable != 0)
+ fmtprint(fp, " a(%d)", n->addable);
+
+ if(!c && n->vargen != 0)
+ fmtprint(fp, " g(%d)", n->vargen);
+
+ if(n->lineno != 0)
+ fmtprint(fp, " l(%d)", n->lineno);
+
+ if(!c && n->xoffset != BADWIDTH)
+ fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta);
+
+ if(n->class != 0) {
+ s = "";
+ if (n->class & PHEAP) s = ",heap";
+ if ((n->class & ~PHEAP) < nelem(classnames))
+ fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
+ else
+ fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
+ }
+
+ if(n->colas != 0)
+ fmtprint(fp, " colas(%d)", n->colas);
+
+ if(n->funcdepth != 0)
+ fmtprint(fp, " f(%d)", n->funcdepth);
+
+ if(n->noescape != 0)
+ fmtprint(fp, " ne(%d)", n->noescape);
+
+ if(!c && n->typecheck != 0)
+ fmtprint(fp, " tc(%d)", n->typecheck);
+
+ if(!c && n->dodata != 0)
+ fmtprint(fp, " dd(%d)", n->dodata);
+
+ if(n->isddd != 0)
+ fmtprint(fp, " isddd(%d)", n->isddd);
+
+ if(n->implicit != 0)
+ fmtprint(fp, " implicit(%d)", n->implicit);
+
+ if(!c && n->pun != 0)
+ fmtprint(fp, " pun(%d)", n->pun);
+
+ if(!c && n->used != 0)
+ fmtprint(fp, " used(%d)", n->used);
+ return 0;
+}
+
+int
+Sconv(Fmt *fp)
+{
+ Sym *s;
+
+ s = va_arg(fp->args, Sym*);
+ if(s == S) {
+ fmtstrcpy(fp, "<S>");
+ return 0;
+ }
+
+ if(fp->flags & FmtShort)
+ goto shrt;
+
+ if(exporting || (fp->flags & FmtSharp)) {
+ if(packagequotes)
+ fmtprint(fp, "\"%Z\"", s->pkg->path);
+ else
+ fmtprint(fp, "%s", s->pkg->prefix);
+ fmtprint(fp, ".%s", s->name);
+ return 0;
+ }
+
+ if(s->pkg && s->pkg != localpkg || longsymnames || (fp->flags & FmtLong)) {
+ // This one is for the user. If the package name
+ // was used by multiple packages, give the full
+ // import path to disambiguate.
+ if(erroring && pkglookup(s->pkg->name, nil)->npkg > 1) {
+ fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+ return 0;
+ }
+ fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ return 0;
+ }
+
+shrt:
+ fmtstrcpy(fp, s->name);
+ return 0;
+}
+
+static char*
+basicnames[] =
+{
+ [TINT] = "int",
+ [TUINT] = "uint",
+ [TINT8] = "int8",
+ [TUINT8] = "uint8",
+ [TINT16] = "int16",
+ [TUINT16] = "uint16",
+ [TINT32] = "int32",
+ [TUINT32] = "uint32",
+ [TINT64] = "int64",
+ [TUINT64] = "uint64",
+ [TUINTPTR] = "uintptr",
+ [TFLOAT32] = "float32",
+ [TFLOAT64] = "float64",
+ [TCOMPLEX64] = "complex64",
+ [TCOMPLEX128] = "complex128",
+ [TBOOL] = "bool",
+ [TANY] = "any",
+ [TSTRING] = "string",
+ [TNIL] = "nil",
+ [TIDEAL] = "ideal",
+ [TBLANK] = "blank",
+};
+
+int
+Tpretty(Fmt *fp, Type *t)
+{
+ Type *t1;
+ Sym *s;
+
+ if(0 && debug['r']) {
+ debug['r'] = 0;
+ fmtprint(fp, "%T (orig=%T)", t, t->orig);
+ debug['r'] = 1;
+ return 0;
+ }
+
+ if(t->etype != TFIELD
+ && t->sym != S
+ && !(fp->flags&FmtLong)) {
+ s = t->sym;
+ if(t == types[t->etype] && t->etype != TUNSAFEPTR)
+ return fmtprint(fp, "%s", s->name);
+ if(exporting) {
+ if(fp->flags & FmtShort)
+ fmtprint(fp, "%hS", s);
+ else
+ fmtprint(fp, "%S", s);
+ if(s->pkg != localpkg)
+ return 0;
+ if(t->vargen)
+ fmtprint(fp, "ยท%d", t->vargen);
+ return 0;
+ }
+ return fmtprint(fp, "%S", s);
+ }
+
+ if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
+ if(isideal(t) && t->etype != TIDEAL && t->etype != TNIL)
+ fmtprint(fp, "ideal ");
+ return fmtprint(fp, "%s", basicnames[t->etype]);
+ }
+
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ if(fp->flags&FmtShort) // pass flag thru for methodsym
+ return fmtprint(fp, "*%hT", t->type);
+ return fmtprint(fp, "*%T", t->type);
+
+ case TCHAN:
+ switch(t->chan) {
+ case Crecv:
+ return fmtprint(fp, "<-chan %T", t->type);
+ case Csend:
+ return fmtprint(fp, "chan<- %T", t->type);
+ }
+ if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
+ return fmtprint(fp, "chan (%T)", t->type);
+ return fmtprint(fp, "chan %T", t->type);
+
+ case TMAP:
+ return fmtprint(fp, "map[%T] %T", t->down, t->type);
+
+ case TFUNC:
+ // t->type is method struct
+ // t->type->down is result struct
+ // t->type->down->down is arg struct
+ if(t->thistuple && !(fp->flags&FmtSharp) && !(fp->flags&FmtShort)) {
+ fmtprint(fp, "method(");
+ for(t1=getthisx(t)->type; t1; t1=t1->down) {
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ }
+
+ if(!(fp->flags&FmtByte))
+ fmtprint(fp, "func");
+ fmtprint(fp, "(");
+ for(t1=getinargx(t)->type; t1; t1=t1->down) {
+ if(noargnames && t1->etype == TFIELD) {
+ if(t1->isddd)
+ fmtprint(fp, "...%T", t1->type->type);
+ else
+ fmtprint(fp, "%T", t1->type);
+ } else
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ switch(t->outtuple) {
+ case 0:
+ break;
+ case 1:
+ t1 = getoutargx(t)->type;
+ if(t1 == T) {
+ // failure to typecheck earlier; don't know the type
+ fmtprint(fp, " ?unknown-type?");
+ break;
+ }
+ if(t1->etype == TFIELD)
+ t1 = t1->type;
+ fmtprint(fp, " %T", t1);
+ break;
+ default:
+ t1 = getoutargx(t)->type;
+ fmtprint(fp, " (");
+ for(; t1; t1=t1->down) {
+ if(noargnames && t1->etype == TFIELD)
+ fmtprint(fp, "%T", t1->type);
+ else
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ fmtprint(fp, ")");
+ break;
+ }
+ return 0;
+
+ case TARRAY:
+ if(t->bound >= 0)
+ return fmtprint(fp, "[%d]%T", (int)t->bound, t->type);
+ if(t->bound == -100)
+ return fmtprint(fp, "[...]%T", t->type);
+ return fmtprint(fp, "[]%T", t->type);
+
+ case TINTER:
+ fmtprint(fp, "interface {");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, " ");
+ if(exportname(t1->sym->name))
+ fmtprint(fp, "%hS", t1->sym);
+ else
+ fmtprint(fp, "%S", t1->sym);
+ fmtprint(fp, "%hhT", t1->type);
+ if(t1->down)
+ fmtprint(fp, ";");
+ }
+ return fmtprint(fp, " }");
+
+ case TSTRUCT:
+ if(t->funarg) {
+ fmtprint(fp, "(");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, "%T", t1);
+ if(t1->down)
+ fmtprint(fp, ", ");
+ }
+ return fmtprint(fp, ")");
+ }
+ fmtprint(fp, "struct {");
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ fmtprint(fp, " %T", t1);
+ if(t1->down)
+ fmtprint(fp, ";");
+ }
+ return fmtprint(fp, " }");
+
+ case TFIELD:
+ if(t->sym == S || t->embedded) {
+ if(exporting)
+ fmtprint(fp, "? ");
+ } else
+ fmtprint(fp, "%hS ", t->sym);
+ if(t->isddd)
+ fmtprint(fp, "...%T", t->type->type);
+ else
+ fmtprint(fp, "%T", t->type);
+ if(t->note) {
+ fmtprint(fp, " ");
+ if(exporting)
+ fmtprint(fp, ":");
+ fmtprint(fp, "\"%Z\"", t->note);
+ }
+ return 0;
+
+ case TFORW:
+ if(exporting)
+ yyerror("undefined type %S", t->sym);
+ if(t->sym)
+ return fmtprint(fp, "undefined %S", t->sym);
+ return fmtprint(fp, "undefined");
+
+ case TUNSAFEPTR:
+ if(exporting)
+ return fmtprint(fp, "\"unsafe\".Pointer");
+ return fmtprint(fp, "unsafe.Pointer");
+ }
+
+ // Don't know how to handle - fall back to detailed prints.
+ return -1;
+}
+
+int
+Tconv(Fmt *fp)
+{
+ Type *t, *t1;
+ int r, et, sharp, minus;
+
+ sharp = (fp->flags & FmtSharp);
+ minus = (fp->flags & FmtLeft);
+ fp->flags &= ~(FmtSharp|FmtLeft);
+
+ t = va_arg(fp->args, Type*);
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ t->trecur++;
+ if(t->trecur > 5) {
+ fmtprint(fp, "...");
+ goto out;
+ }
+
+ if(!debug['t']) {
+ if(sharp)
+ exporting++;
+ if(minus)
+ noargnames++;
+ r = Tpretty(fp, t);
+ if(sharp)
+ exporting--;
+ if(minus)
+ noargnames--;
+ if(r >= 0) {
+ t->trecur--;
+ return 0;
+ }
+ }
+
+ if(sharp || exporting)
+ fatal("missing %E case during export", t->etype);
+
+ et = t->etype;
+ fmtprint(fp, "%E ", et);
+ if(t->sym != S)
+ fmtprint(fp, "<%S>", t->sym);
+
+ switch(et) {
+ default:
+ if(t->type != T)
+ fmtprint(fp, " %T", t->type);
+ break;
+
+ case TFIELD:
+ fmtprint(fp, "%T", t->type);
+ break;
+
+ case TFUNC:
+ if(fp->flags & FmtLong)
+ fmtprint(fp, "%d%d%d(%lT,%lT)%lT",
+ t->thistuple, t->intuple, t->outtuple,
+ t->type, t->type->down->down, t->type->down);
+ else
+ fmtprint(fp, "%d%d%d(%T,%T)%T",
+ t->thistuple, t->intuple, t->outtuple,
+ t->type, t->type->down->down, t->type->down);
+ break;
+
+ case TINTER:
+ fmtprint(fp, "{");
+ if(fp->flags & FmtLong)
+ for(t1=t->type; t1!=T; t1=t1->down)
+ fmtprint(fp, "%lT;", t1);
+ fmtprint(fp, "}");
+ break;
+
+ case TSTRUCT:
+ fmtprint(fp, "{");
+ if(fp->flags & FmtLong)
+ for(t1=t->type; t1!=T; t1=t1->down)
+ fmtprint(fp, "%lT;", t1);
+ fmtprint(fp, "}");
+ break;
+
+ case TMAP:
+ fmtprint(fp, "[%T]%T", t->down, t->type);
+ break;
+
+ case TARRAY:
+ if(t->bound >= 0)
+ fmtprint(fp, "[%d]%T", t->bound, t->type);
+ else
+ fmtprint(fp, "[]%T", t->type);
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ fmtprint(fp, "%T", t->type);
+ break;
+ }
+
+out:
+ t->trecur--;
+ return 0;
+}
+
+int
+Nconv(Fmt *fp)
+{
+ char buf1[500];
+ Node *n;
+
+ n = va_arg(fp->args, Node*);
+ if(n == N) {
+ fmtprint(fp, "<N>");
+ goto out;
+ }
+
+ if(fp->flags & FmtSign) {
+ if(n->type == T)
+ fmtprint(fp, "%#N", n);
+ else if(n->type->etype == TNIL)
+ fmtprint(fp, "nil");
+ else
+ fmtprint(fp, "%#N (type %T)", n, n->type);
+ goto out;
+ }
+
+ if(fp->flags & FmtSharp) {
+ if(n->orig != N)
+ n = n->orig;
+ exprfmt(fp, n, 0);
+ goto out;
+ }
+
+ switch(n->op) {
+ default:
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O%hJ", n->op, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+
+ case ONAME:
+ case ONONAME:
+ if(n->sym == S) {
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O%hJ", n->op, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+ }
+ if (fp->flags & FmtShort)
+ fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
+ else
+ fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
+ goto ptyp;
+
+ case OREGISTER:
+ fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
+ break;
+
+ case OLITERAL:
+ switch(n->val.ctype) {
+ default:
+ snprint(buf1, sizeof(buf1), "LITERAL-ctype=%d", n->val.ctype);
+ break;
+ case CTINT:
+ snprint(buf1, sizeof(buf1), "I%B", n->val.u.xval);
+ break;
+ case CTFLT:
+ snprint(buf1, sizeof(buf1), "F%g", mpgetflt(n->val.u.fval));
+ break;
+ case CTCPLX:
+ snprint(buf1, sizeof(buf1), "(F%g+F%gi)",
+ mpgetflt(&n->val.u.cval->real),
+ mpgetflt(&n->val.u.cval->imag));
+ break;
+ case CTSTR:
+ snprint(buf1, sizeof(buf1), "S\"%Z\"", n->val.u.sval);
+ break;
+ case CTBOOL:
+ snprint(buf1, sizeof(buf1), "B%d", n->val.u.bval);
+ break;
+ case CTNIL:
+ snprint(buf1, sizeof(buf1), "N");
+ break;
+ }
+ fmtprint(fp, "%O-%s%J", n->op, buf1, n);
+ break;
+
+ case OASOP:
+ fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
+ break;
+
+ case OTYPE:
+ fmtprint(fp, "%O %T", n->op, n->type);
+ break;
+ }
+ if(n->sym != S)
+ fmtprint(fp, " %S G%d", n->sym, n->vargen);
+
+ptyp:
+ if(n->type != T)
+ fmtprint(fp, " %T", n->type);
+
+out:
+ return 0;
+}
+
+Node*
+treecopy(Node *n)
+{
+ Node *m;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ default:
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->left = treecopy(n->left);
+ m->right = treecopy(n->right);
+ m->list = listtreecopy(n->list);
+ if(m->defn)
+ abort();
+ break;
+
+ case ONONAME:
+ if(n->sym == lookup("iota")) {
+ // Not sure yet whether this is the real iota,
+ // but make a copy of the Node* just in case,
+ // so that all the copies of this const definition
+ // don't have the same iota value.
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->iota = iota;
+ break;
+ }
+ // fall through
+ case ONAME:
+ case OLITERAL:
+ case OTYPE:
+ m = n;
+ break;
+ }
+ return m;
+}
+
+int
+Zconv(Fmt *fp)
+{
+ Rune r;
+ Strlit *sp;
+ char *s, *se;
+ int n;
+
+ sp = va_arg(fp->args, Strlit*);
+ if(sp == nil)
+ return fmtstrcpy(fp, "<nil>");
+
+ s = sp->s;
+ se = s + sp->len;
+ while(s < se) {
+ n = chartorune(&r, s);
+ s += n;
+ switch(r) {
+ case Runeerror:
+ if(n == 1) {
+ fmtprint(fp, "\\x%02x", (uchar)*(s-1));
+ break;
+ }
+ // fall through
+ default:
+ if(r < ' ') {
+ fmtprint(fp, "\\x%02x", r);
+ break;
+ }
+ fmtrune(fp, r);
+ break;
+ case '\t':
+ fmtstrcpy(fp, "\\t");
+ break;
+ case '\n':
+ fmtstrcpy(fp, "\\n");
+ break;
+ case '\"':
+ case '\\':
+ fmtrune(fp, '\\');
+ fmtrune(fp, r);
+ break;
+ }
+ }
+ return 0;
+}
+
+int
+isnil(Node *n)
+{
+ if(n == N)
+ return 0;
+ if(n->op != OLITERAL)
+ return 0;
+ if(n->val.ctype != CTNIL)
+ return 0;
+ return 1;
+}
+
+int
+isptrto(Type *t, int et)
+{
+ if(t == T)
+ return 0;
+ if(!isptr[t->etype])
+ return 0;
+ t = t->type;
+ if(t == T)
+ return 0;
+ if(t->etype != et)
+ return 0;
+ return 1;
+}
+
+int
+istype(Type *t, int et)
+{
+ return t != T && t->etype == et;
+}
+
+int
+isfixedarray(Type *t)
+{
+ return t != T && t->etype == TARRAY && t->bound >= 0;
+}
+
+int
+isslice(Type *t)
+{
+ return t != T && t->etype == TARRAY && t->bound < 0;
+}
+
+int
+isblank(Node *n)
+{
+ char *p;
+
+ if(n == N || n->sym == S)
+ return 0;
+ p = n->sym->name;
+ if(p == nil)
+ return 0;
+ return p[0] == '_' && p[1] == '\0';
+}
+
+int
+isinter(Type *t)
+{
+ return t != T && t->etype == TINTER;
+}
+
+int
+isnilinter(Type *t)
+{
+ if(!isinter(t))
+ return 0;
+ if(t->type != T)
+ return 0;
+ return 1;
+}
+
+int
+isideal(Type *t)
+{
+ if(t == T)
+ return 0;
+ if(t == idealstring || t == idealbool)
+ return 1;
+ switch(t->etype) {
+ case TNIL:
+ case TIDEAL:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * given receiver of type t (t == r or t == *r)
+ * return type to hang methods off (r).
+ */
+Type*
+methtype(Type *t)
+{
+ if(t == T)
+ return T;
+
+ // strip away pointer if it's there
+ if(isptr[t->etype]) {
+ if(t->sym != S)
+ return T;
+ t = t->type;
+ if(t == T)
+ return T;
+ }
+
+ // need a type name
+ if(t->sym == S)
+ return T;
+
+ // check types
+ if(!issimple[t->etype])
+ switch(t->etype) {
+ default:
+ return T;
+ case TSTRUCT:
+ case TARRAY:
+ case TMAP:
+ case TCHAN:
+ case TSTRING:
+ case TFUNC:
+ break;
+ }
+
+ return t;
+}
+
+int
+cplxsubtype(int et)
+{
+ switch(et) {
+ case TCOMPLEX64:
+ return TFLOAT32;
+ case TCOMPLEX128:
+ return TFLOAT64;
+ }
+ fatal("cplxsubtype: %E\n", et);
+ return 0;
+}
+
+static int
+eqnote(Strlit *a, Strlit *b)
+{
+ if(a == b)
+ return 1;
+ if(a == nil || b == nil)
+ return 0;
+ if(a->len != b->len)
+ return 0;
+ return memcmp(a->s, b->s, a->len) == 0;
+}
+
+// Return 1 if t1 and t2 are identical, following the spec rules.
+//
+// Any cyclic type must go through a named type, and if one is
+// named, it is only identical to the other if they are the same
+// pointer (t1 == t2), so there's no chance of chasing cycles
+// ad infinitum, so no need for a depth counter.
+int
+eqtype(Type *t1, Type *t2)
+{
+ if(t1 == t2)
+ return 1;
+ if(t1 == T || t2 == T || t1->etype != t2->etype || t1->sym || t2->sym)
+ return 0;
+
+ switch(t1->etype) {
+ case TINTER:
+ case TSTRUCT:
+ for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+ if(t1->etype != TFIELD || t2->etype != TFIELD)
+ fatal("struct/interface missing field: %T %T", t1, t2);
+ if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype(t1->type, t2->type) || !eqnote(t1->note, t2->note))
+ return 0;
+ }
+ return t1 == T && t2 == T;
+
+ case TFUNC:
+ // Loop over structs: receiver, in, out.
+ for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+ Type *ta, *tb;
+
+ if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+ fatal("func missing struct: %T %T", t1, t2);
+
+ // Loop over fields in structs, ignoring argument names.
+ for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
+ if(ta->etype != TFIELD || tb->etype != TFIELD)
+ fatal("func struct missing field: %T %T", ta, tb);
+ if(ta->isddd != tb->isddd || !eqtype(ta->type, tb->type))
+ return 0;
+ }
+ if(ta != T || tb != T)
+ return 0;
+ }
+ return t1 == T && t2 == T;
+
+ case TARRAY:
+ if(t1->bound != t2->bound)
+ return 0;
+ break;
+
+ case TCHAN:
+ if(t1->chan != t2->chan)
+ return 0;
+ break;
+ }
+
+ return eqtype(t1->down, t2->down) && eqtype(t1->type, t2->type);
+}
+
+// Are t1 and t2 equal struct types when field names are ignored?
+// For deciding whether the result struct from g can be copied
+// directly when compiling f(g()).
+int
+eqtypenoname(Type *t1, Type *t2)
+{
+ if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+ return 0;
+
+ t1 = t1->type;
+ t2 = t2->type;
+ for(;;) {
+ if(!eqtype(t1, t2))
+ return 0;
+ if(t1 == T)
+ return 1;
+ t1 = t1->down;
+ t2 = t2->down;
+ }
+}
+
+// Is type src assignment compatible to type dst?
+// If so, return op code to use in conversion.
+// If not, return 0.
+//
+// It is the caller's responsibility to call exportassignok
+// to check for assignments to other packages' unexported fields,
+int
+assignop(Type *src, Type *dst, char **why)
+{
+ Type *missing, *have;
+ int ptr;
+
+ if(why != nil)
+ *why = "";
+
+ if(safemode && src != T && src->etype == TUNSAFEPTR) {
+ yyerror("cannot use unsafe.Pointer");
+ errorexit();
+ }
+
+ if(src == dst)
+ return OCONVNOP;
+ if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
+ return 0;
+
+ // 1. src type is identical to dst.
+ if(eqtype(src, dst))
+ return OCONVNOP;
+
+ // 2. src and dst have identical underlying types
+ // and either src or dst is not a named type or
+ // both are interface types.
+ if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER))
+ return OCONVNOP;
+
+ // 3. dst is an interface type and src implements dst.
+ if(dst->etype == TINTER && src->etype != TNIL) {
+ if(implements(src, dst, &missing, &have, &ptr))
+ return OCONVIFACE;
+ if(why != nil) {
+ if(isptrto(src, TINTER))
+ *why = smprint(":\n\t%T is pointer to interface, not interface", src);
+ else if(have && have->sym == missing->sym)
+ *why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else if(ptr)
+ *why = smprint(":\n\t%T does not implement %T (%S method requires pointer receiver)",
+ src, dst, missing->sym);
+ else if(have)
+ *why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else
+ *why = smprint(":\n\t%T does not implement %T (missing %S method)",
+ src, dst, missing->sym);
+ }
+ return 0;
+ }
+ if(isptrto(dst, TINTER)) {
+ if(why != nil)
+ *why = smprint(":\n\t%T is pointer to interface, not interface", dst);
+ return 0;
+ }
+ if(src->etype == TINTER && dst->etype != TBLANK) {
+ if(why != nil)
+ *why = ": need type assertion";
+ return 0;
+ }
+
+ // 4. src is a bidirectional channel value, dst is a channel type,
+ // src and dst have identical element types, and
+ // either src or dst is not a named type.
+ if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
+ if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
+ return OCONVNOP;
+
+ // 5. src is the predeclared identifier nil and dst is a nillable type.
+ if(src->etype == TNIL) {
+ switch(dst->etype) {
+ case TARRAY:
+ if(dst->bound != -100) // not slice
+ break;
+ case TPTR32:
+ case TPTR64:
+ case TFUNC:
+ case TMAP:
+ case TCHAN:
+ case TINTER:
+ return OCONVNOP;
+ }
+ }
+
+ // 6. rule about untyped constants - already converted by defaultlit.
+
+ // 7. Any typed value can be assigned to the blank identifier.
+ if(dst->etype == TBLANK)
+ return OCONVNOP;
+
+ return 0;
+}
+
+// Can we convert a value of type src to a value of type dst?
+// If so, return op code to use in conversion (maybe OCONVNOP).
+// If not, return 0.
+int
+convertop(Type *src, Type *dst, char **why)
+{
+ int op;
+
+ if(why != nil)
+ *why = "";
+
+ if(src == dst)
+ return OCONVNOP;
+ if(src == T || dst == T)
+ return 0;
+
+ // 1. src can be assigned to dst.
+ if((op = assignop(src, dst, why)) != 0)
+ return op;
+
+ // The rules for interfaces are no different in conversions
+ // than assignments. If interfaces are involved, stop now
+ // with the good message from assignop.
+ // Otherwise clear the error.
+ if(src->etype == TINTER || dst->etype == TINTER)
+ return 0;
+ if(why != nil)
+ *why = "";
+
+ // 2. src and dst have identical underlying types.
+ if(eqtype(src->orig, dst->orig))
+ return OCONVNOP;
+
+ // 3. src and dst are unnamed pointer types
+ // and their base types have identical underlying types.
+ if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
+ if(eqtype(src->type->orig, dst->type->orig))
+ return OCONVNOP;
+
+ // 4. src and dst are both integer or floating point types.
+ if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
+ if(simtype[src->etype] == simtype[dst->etype])
+ return OCONVNOP;
+ return OCONV;
+ }
+
+ // 5. src and dst are both complex types.
+ if(iscomplex[src->etype] && iscomplex[dst->etype]) {
+ if(simtype[src->etype] == simtype[dst->etype])
+ return OCONVNOP;
+ return OCONV;
+ }
+
+ // 6. src is an integer or has type []byte or []int
+ // and dst is a string type.
+ if(isint[src->etype] && dst->etype == TSTRING)
+ return ORUNESTR;
+
+ if(isslice(src) && src->sym == nil && src->type == types[src->type->etype] && dst->etype == TSTRING) {
+ switch(src->type->etype) {
+ case TUINT8:
+ return OARRAYBYTESTR;
+ case TINT:
+ return OARRAYRUNESTR;
+ }
+ }
+
+ // 7. src is a string and dst is []byte or []int.
+ // String to slice.
+ if(src->etype == TSTRING && isslice(dst) && dst->sym == nil && dst->type == types[dst->type->etype]) {
+ switch(dst->type->etype) {
+ case TUINT8:
+ return OSTRARRAYBYTE;
+ case TINT:
+ return OSTRARRAYRUNE;
+ }
+ }
+
+ // 8. src is a pointer or uintptr and dst is unsafe.Pointer.
+ if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
+ return OCONVNOP;
+
+ // 9. src is unsafe.Pointer and dst is a pointer or uintptr.
+ if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
+ return OCONVNOP;
+
+ return 0;
+}
+
+// Convert node n for assignment to type t.
+Node*
+assignconv(Node *n, Type *t, char *context)
+{
+ int op;
+ Node *r, *old;
+ char *why;
+
+ if(n == N || n->type == T)
+ return n;
+
+ old = n;
+ old->diag++; // silence errors about n; we'll issue one below
+ defaultlit(&n, t);
+ old->diag--;
+ if(t->etype == TBLANK)
+ return n;
+
+ exportassignok(n->type, context);
+ if(eqtype(n->type, t))
+ return n;
+
+ op = assignop(n->type, t, &why);
+ if(op == 0) {
+ yyerror("cannot use %+N as type %T in %s%s", n, t, context, why);
+ op = OCONV;
+ }
+
+ r = nod(op, n, N);
+ r->type = t;
+ r->typecheck = 1;
+ r->implicit = 1;
+ return r;
+}
+
+static int
+subtype(Type **stp, Type *t, int d)
+{
+ Type *st;
+
+loop:
+ st = *stp;
+ if(st == T)
+ return 0;
+
+ d++;
+ if(d >= 10)
+ return 0;
+
+ switch(st->etype) {
+ default:
+ return 0;
+
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TARRAY:
+ stp = &st->type;
+ goto loop;
+
+ case TANY:
+ if(!st->copyany)
+ return 0;
+ *stp = t;
+ break;
+
+ case TMAP:
+ if(subtype(&st->down, t, d))
+ break;
+ stp = &st->type;
+ goto loop;
+
+ case TFUNC:
+ for(;;) {
+ if(subtype(&st->type, t, d))
+ break;
+ if(subtype(&st->type->down->down, t, d))
+ break;
+ if(subtype(&st->type->down, t, d))
+ break;
+ return 0;
+ }
+ break;
+
+ case TSTRUCT:
+ for(st=st->type; st!=T; st=st->down)
+ if(subtype(&st->type, t, d))
+ return 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Is this a 64-bit type?
+ */
+int
+is64(Type *t)
+{
+ if(t == T)
+ return 0;
+ switch(simtype[t->etype]) {
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Is a conversion between t1 and t2 a no-op?
+ */
+int
+noconv(Type *t1, Type *t2)
+{
+ int e1, e2;
+
+ e1 = simtype[t1->etype];
+ e2 = simtype[t2->etype];
+
+ switch(e1) {
+ case TINT8:
+ case TUINT8:
+ return e2 == TINT8 || e2 == TUINT8;
+
+ case TINT16:
+ case TUINT16:
+ return e2 == TINT16 || e2 == TUINT16;
+
+ case TINT32:
+ case TUINT32:
+ case TPTR32:
+ return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32;
+
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64;
+
+ case TFLOAT32:
+ return e2 == TFLOAT32;
+
+ case TFLOAT64:
+ return e2 == TFLOAT64;
+ }
+ return 0;
+}
+
+void
+argtype(Node *on, Type *t)
+{
+ dowidth(t);
+ if(!subtype(&on->type, t, 0))
+ fatal("argtype: failed %N %T\n", on, t);
+}
+
+Type*
+shallow(Type *t)
+{
+ Type *nt;
+
+ if(t == T)
+ return T;
+ nt = typ(0);
+ *nt = *t;
+ if(t->orig == t)
+ nt->orig = nt;
+ return nt;
+}
+
+static Type*
+deep(Type *t)
+{
+ Type *nt, *xt;
+
+ if(t == T)
+ return T;
+
+ switch(t->etype) {
+ default:
+ nt = t; // share from here down
+ break;
+
+ case TANY:
+ nt = shallow(t);
+ nt->copyany = 1;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TARRAY:
+ nt = shallow(t);
+ nt->type = deep(t->type);
+ break;
+
+ case TMAP:
+ nt = shallow(t);
+ nt->down = deep(t->down);
+ nt->type = deep(t->type);
+ break;
+
+ case TFUNC:
+ nt = shallow(t);
+ nt->type = deep(t->type);
+ nt->type->down = deep(t->type->down);
+ nt->type->down->down = deep(t->type->down->down);
+ break;
+
+ case TSTRUCT:
+ nt = shallow(t);
+ nt->type = shallow(t->type);
+ xt = nt->type;
+
+ for(t=t->type; t!=T; t=t->down) {
+ xt->type = deep(t->type);
+ xt->down = shallow(t->down);
+ xt = xt->down;
+ }
+ break;
+ }
+ return nt;
+}
+
+Node*
+syslook(char *name, int copy)
+{
+ Sym *s;
+ Node *n;
+
+ s = pkglookup(name, runtimepkg);
+ if(s == S || s->def == N)
+ fatal("syslook: can't find runtime.%s", name);
+
+ if(!copy)
+ return s->def;
+
+ n = nod(0, N, N);
+ *n = *s->def;
+ n->type = deep(s->def->type);
+
+ return n;
+}
+
+/*
+ * compute a hash value for type t.
+ * if t is a method type, ignore the receiver
+ * so that the hash can be used in interface checks.
+ * %-T (which calls Tpretty, above) already contains
+ * all the necessary logic to generate a representation
+ * of the type that completely describes it.
+ * using smprint here avoids duplicating that code.
+ * using md5 here is overkill, but i got tired of
+ * accidental collisions making the runtime think
+ * two types are equal when they really aren't.
+ */
+uint32
+typehash(Type *t)
+{
+ char *p;
+ MD5 d;
+
+ longsymnames = 1;
+ if(t->thistuple) {
+ // hide method receiver from Tpretty
+ t->thistuple = 0;
+ p = smprint("%-T", t);
+ t->thistuple = 1;
+ }else
+ p = smprint("%-T", t);
+ longsymnames = 0;
+ md5reset(&d);
+ md5write(&d, (uchar*)p, strlen(p));
+ free(p);
+ return md5sum(&d);
+}
+
+Type*
+ptrto(Type *t)
+{
+ Type *t1;
+
+ if(tptr == 0)
+ fatal("ptrto: nil");
+ t1 = typ(tptr);
+ t1->type = t;
+ t1->width = widthptr;
+ t1->align = widthptr;
+ return t1;
+}
+
+void
+frame(int context)
+{
+ char *p;
+ NodeList *l;
+ Node *n;
+ int flag;
+
+ p = "stack";
+ l = nil;
+ if(curfn)
+ l = curfn->dcl;
+ if(context) {
+ p = "external";
+ l = externdcl;
+ }
+
+ flag = 1;
+ for(; l; l=l->next) {
+ n = l->n;
+ switch(n->op) {
+ case ONAME:
+ if(flag)
+ print("--- %s frame ---\n", p);
+ print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type);
+ flag = 0;
+ break;
+
+ case OTYPE:
+ if(flag)
+ print("--- %s frame ---\n", p);
+ print("%O %T\n", n->op, n->type);
+ flag = 0;
+ break;
+ }
+ }
+}
+
+/*
+ * calculate sethi/ullman number
+ * roughly how many registers needed to
+ * compile a node. used to compile the
+ * hardest side first to minimize registers.
+ */
+void
+ullmancalc(Node *n)
+{
+ int ul, ur;
+
+ if(n == N)
+ return;
+
+ switch(n->op) {
+ case OREGISTER:
+ case OLITERAL:
+ case ONAME:
+ ul = 1;
+ if(n->class == PPARAMREF || (n->class & PHEAP))
+ ul++;
+ goto out;
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ ul = UINF;
+ goto out;
+ }
+ ul = 1;
+ if(n->left != N)
+ ul = n->left->ullman;
+ ur = 1;
+ if(n->right != N)
+ ur = n->right->ullman;
+ if(ul == ur)
+ ul += 1;
+ if(ur > ul)
+ ul = ur;
+
+out:
+ n->ullman = ul;
+}
+
+void
+badtype(int o, Type *tl, Type *tr)
+{
+ Fmt fmt;
+ char *s;
+
+ fmtstrinit(&fmt);
+ if(tl != T)
+ fmtprint(&fmt, "\n %T", tl);
+ if(tr != T)
+ fmtprint(&fmt, "\n %T", tr);
+
+ // common mistake: *struct and *interface.
+ if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
+ if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
+ fmtprint(&fmt, "\n (*struct vs *interface)");
+ else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
+ fmtprint(&fmt, "\n (*interface vs *struct)");
+ }
+ s = fmtstrflush(&fmt);
+ yyerror("illegal types for operand: %O%s", o, s);
+}
+
+/*
+ * iterator to walk a structure declaration
+ */
+Type*
+structfirst(Iter *s, Type **nn)
+{
+ Type *n, *t;
+
+ n = *nn;
+ if(n == T)
+ goto bad;
+
+ switch(n->etype) {
+ default:
+ goto bad;
+
+ case TSTRUCT:
+ case TINTER:
+ case TFUNC:
+ break;
+ }
+
+ t = n->type;
+ if(t == T)
+ goto rnil;
+
+ if(t->etype != TFIELD)
+ fatal("structfirst: not field %T", t);
+
+ s->t = t;
+ return t;
+
+bad:
+ fatal("structfirst: not struct %T", n);
+
+rnil:
+ return T;
+}
+
+Type*
+structnext(Iter *s)
+{
+ Type *n, *t;
+
+ n = s->t;
+ t = n->down;
+ if(t == T)
+ goto rnil;
+
+ if(t->etype != TFIELD)
+ goto bad;
+
+ s->t = t;
+ return t;
+
+bad:
+ fatal("structnext: not struct %T", n);
+
+rnil:
+ return T;
+}
+
+/*
+ * iterator to this and inargs in a function
+ */
+Type*
+funcfirst(Iter *s, Type *t)
+{
+ Type *fp;
+
+ if(t == T)
+ goto bad;
+
+ if(t->etype != TFUNC)
+ goto bad;
+
+ s->tfunc = t;
+ s->done = 0;
+ fp = structfirst(s, getthis(t));
+ if(fp == T) {
+ s->done = 1;
+ fp = structfirst(s, getinarg(t));
+ }
+ return fp;
+
+bad:
+ fatal("funcfirst: not func %T", t);
+ return T;
+}
+
+Type*
+funcnext(Iter *s)
+{
+ Type *fp;
+
+ fp = structnext(s);
+ if(fp == T && !s->done) {
+ s->done = 1;
+ fp = structfirst(s, getinarg(s->tfunc));
+ }
+ return fp;
+}
+
+Type**
+getthis(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getthis: not a func %T", t);
+ return &t->type;
+}
+
+Type**
+getoutarg(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getoutarg: not a func %T", t);
+ return &t->type->down;
+}
+
+Type**
+getinarg(Type *t)
+{
+ if(t->etype != TFUNC)
+ fatal("getinarg: not a func %T", t);
+ return &t->type->down->down;
+}
+
+Type*
+getthisx(Type *t)
+{
+ return *getthis(t);
+}
+
+Type*
+getoutargx(Type *t)
+{
+ return *getoutarg(t);
+}
+
+Type*
+getinargx(Type *t)
+{
+ return *getinarg(t);
+}
+
+/*
+ * return !(op)
+ * eg == <=> !=
+ */
+int
+brcom(int a)
+{
+ switch(a) {
+ case OEQ: return ONE;
+ case ONE: return OEQ;
+ case OLT: return OGE;
+ case OGT: return OLE;
+ case OLE: return OGT;
+ case OGE: return OLT;
+ }
+ fatal("brcom: no com for %A\n", a);
+ return a;
+}
+
+/*
+ * return reverse(op)
+ * eg a op b <=> b r(op) a
+ */
+int
+brrev(int a)
+{
+ switch(a) {
+ case OEQ: return OEQ;
+ case ONE: return ONE;
+ case OLT: return OGT;
+ case OGT: return OLT;
+ case OLE: return OGE;
+ case OGE: return OLE;
+ }
+ fatal("brcom: no rev for %A\n", a);
+ return a;
+}
+
+/*
+ * return side effect-free n, appending side effects to init.
+ * result is assignable if n is.
+ */
+Node*
+safeexpr(Node *n, NodeList **init)
+{
+ Node *l;
+ Node *r;
+ Node *a;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ return n;
+
+ case ODOT:
+ l = safeexpr(n->left, init);
+ if(l == n->left)
+ return n;
+ r = nod(OXXX, N, N);
+ *r = *n;
+ r->left = l;
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ return r;
+
+ case ODOTPTR:
+ case OIND:
+ l = safeexpr(n->left, init);
+ if(l == n->left)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->left = l;
+ walkexpr(&a, init);
+ return a;
+
+ case OINDEX:
+ case OINDEXMAP:
+ l = safeexpr(n->left, init);
+ r = safeexpr(n->right, init);
+ if(l == n->left && r == n->right)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->left = l;
+ a->right = r;
+ walkexpr(&a, init);
+ return a;
+ }
+
+ // make a copy; must not be used as an lvalue
+ if(islvalue(n))
+ fatal("missing lvalue case in safeexpr: %N", n);
+ return cheapexpr(n, init);
+}
+
+static Node*
+copyexpr(Node *n, Type *t, NodeList **init)
+{
+ Node *a, *l;
+
+ l = nod(OXXX, N, N);
+ tempname(l, t);
+ a = nod(OAS, l, n);
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *init = list(*init, a);
+ return l;
+}
+
+/*
+ * return side-effect free and cheap n, appending side effects to init.
+ * result may not be assignable.
+ */
+Node*
+cheapexpr(Node *n, NodeList **init)
+{
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ return n;
+ }
+
+ return copyexpr(n, n->type, init);
+}
+
+/*
+ * return n in a local variable of type t if it is not already.
+ */
+Node*
+localexpr(Node *n, Type *t, NodeList **init)
+{
+ if(n->op == ONAME &&
+ (n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
+ convertop(n->type, t, nil) == OCONVNOP)
+ return n;
+
+ return copyexpr(n, t, init);
+}
+
+void
+setmaxarg(Type *t)
+{
+ int32 w;
+
+ dowidth(t);
+ w = t->argwid;
+ if(t->argwid >= MAXWIDTH)
+ fatal("bad argwid %T", t);
+ if(w > maxarg)
+ maxarg = w;
+}
+
+/* unicode-aware case-insensitive strcmp */
+
+static int
+cistrcmp(char *p, char *q)
+{
+ Rune rp, rq;
+
+ while(*p || *q) {
+ if(*p == 0)
+ return +1;
+ if(*q == 0)
+ return -1;
+ p += chartorune(&rp, p);
+ q += chartorune(&rq, q);
+ rp = tolowerrune(rp);
+ rq = tolowerrune(rq);
+ if(rp < rq)
+ return -1;
+ if(rp > rq)
+ return +1;
+ }
+ return 0;
+}
+
+/*
+ * code to resolve elided DOTs
+ * in embedded types
+ */
+
+// search depth 0 --
+// return count of fields+methods
+// found with a given name
+static int
+lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
+{
+ Type *f, *u;
+ int c;
+
+ u = t;
+ if(isptr[u->etype])
+ u = u->type;
+
+ c = 0;
+ if(u->etype == TSTRUCT || u->etype == TINTER) {
+ for(f=u->type; f!=T; f=f->down)
+ if(f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0)) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ u = methtype(t);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down)
+ if(f->embedded == 0 && (f->sym == s || (ignorecase && cistrcmp(f->sym->name, s->name) == 0))) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ return c;
+}
+
+// search depth d --
+// return count of fields+methods
+// found at search depth.
+// answer is in dotlist array and
+// count of number of ways is returned.
+int
+adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase)
+{
+ Type *f, *u;
+ int c, a;
+
+ if(t->trecur)
+ return 0;
+ t->trecur = 1;
+
+ if(d == 0) {
+ c = lookdot0(s, t, save, ignorecase);
+ goto out;
+ }
+
+ c = 0;
+ u = t;
+ if(isptr[u->etype])
+ u = u->type;
+ if(u->etype != TSTRUCT && u->etype != TINTER)
+ goto out;
+
+ d--;
+ for(f=u->type; f!=T; f=f->down) {
+ if(!f->embedded)
+ continue;
+ if(f->sym == S)
+ continue;
+ a = adddot1(s, f->type, d, save, ignorecase);
+ if(a != 0 && c == 0)
+ dotlist[d].field = f;
+ c += a;
+ }
+
+out:
+ t->trecur = 0;
+ return c;
+}
+
+// in T.field
+// find missing fields that
+// will give shortest unique addressing.
+// modify the tree with missing type names.
+Node*
+adddot(Node *n)
+{
+ Type *t;
+ Sym *s;
+ int c, d;
+
+ typecheck(&n->left, Etype|Erv);
+ t = n->left->type;
+ if(t == T)
+ goto ret;
+
+ if(n->left->op == OTYPE)
+ goto ret;
+
+ if(n->right->op != ONAME)
+ goto ret;
+ s = n->right->sym;
+ if(s == S)
+ goto ret;
+
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(s, t, d, nil, 0);
+ if(c > 0)
+ goto out;
+ }
+ goto ret;
+
+out:
+ if(c > 1)
+ yyerror("ambiguous DOT reference %T.%S", t, s);
+
+ // rebuild elided dots
+ for(c=d-1; c>=0; c--)
+ n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym));
+ret:
+ return n;
+}
+
+
+/*
+ * code to help generate trampoline
+ * functions for methods on embedded
+ * subtypes.
+ * these are approx the same as
+ * the corresponding adddot routines
+ * except that they expect to be called
+ * with unique tasks and they return
+ * the actual methods.
+ */
+
+typedef struct Symlink Symlink;
+struct Symlink
+{
+ Type* field;
+ uchar good;
+ uchar followptr;
+ Symlink* link;
+};
+static Symlink* slist;
+
+static void
+expand0(Type *t, int followptr)
+{
+ Type *f, *u;
+ Symlink *sl;
+
+ u = t;
+ if(isptr[u->etype]) {
+ followptr = 1;
+ u = u->type;
+ }
+
+ if(u->etype == TINTER) {
+ for(f=u->type; f!=T; f=f->down) {
+ if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
+ continue;
+ if(f->sym->flags & SymUniq)
+ continue;
+ f->sym->flags |= SymUniq;
+ sl = mal(sizeof(*sl));
+ sl->field = f;
+ sl->link = slist;
+ sl->followptr = followptr;
+ slist = sl;
+ }
+ return;
+ }
+
+ u = methtype(t);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down) {
+ if(!exportname(f->sym->name) && f->sym->pkg != localpkg)
+ continue;
+ if(f->sym->flags & SymUniq)
+ continue;
+ f->sym->flags |= SymUniq;
+ sl = mal(sizeof(*sl));
+ sl->field = f;
+ sl->link = slist;
+ sl->followptr = followptr;
+ slist = sl;
+ }
+ }
+}
+
+static void
+expand1(Type *t, int d, int followptr)
+{
+ Type *f, *u;
+
+ if(t->trecur)
+ return;
+ if(d == 0)
+ return;
+ t->trecur = 1;
+
+ if(d != nelem(dotlist)-1)
+ expand0(t, followptr);
+
+ u = t;
+ if(isptr[u->etype]) {
+ followptr = 1;
+ u = u->type;
+ }
+ if(u->etype != TSTRUCT && u->etype != TINTER)
+ goto out;
+
+ for(f=u->type; f!=T; f=f->down) {
+ if(!f->embedded)
+ continue;
+ if(f->sym == S)
+ continue;
+ expand1(f->type, d-1, followptr);
+ }
+
+out:
+ t->trecur = 0;
+}
+
+void
+expandmeth(Sym *s, Type *t)
+{
+ Symlink *sl;
+ Type *f;
+ int c, d;
+
+ if(s == S)
+ return;
+ if(t == T || t->xmethod != nil)
+ return;
+
+ // mark top-level method symbols
+ // so that expand1 doesn't consider them.
+ for(f=t->method; f != nil; f=f->down)
+ f->sym->flags |= SymUniq;
+
+ // generate all reachable methods
+ slist = nil;
+ expand1(t, nelem(dotlist)-1, 0);
+
+ // check each method to be uniquely reachable
+ for(sl=slist; sl!=nil; sl=sl->link) {
+ sl->field->sym->flags &= ~SymUniq;
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(sl->field->sym, t, d, &f, 0);
+ if(c == 0)
+ continue;
+ if(c == 1) {
+ sl->good = 1;
+ sl->field = f;
+ }
+ break;
+ }
+ }
+
+ for(f=t->method; f != nil; f=f->down)
+ f->sym->flags &= ~SymUniq;
+
+ t->xmethod = t->method;
+ for(sl=slist; sl!=nil; sl=sl->link) {
+ if(sl->good) {
+ // add it to the base type method list
+ f = typ(TFIELD);
+ *f = *sl->field;
+ f->embedded = 1; // needs a trampoline
+ if(sl->followptr)
+ f->embedded = 2;
+ f->down = t->xmethod;
+ t->xmethod = f;
+ }
+ }
+}
+
+/*
+ * Given funarg struct list, return list of ODCLFIELD Node fn args.
+ */
+static NodeList*
+structargs(Type **tl, int mustname)
+{
+ Iter savet;
+ Node *a, *n;
+ NodeList *args;
+ Type *t;
+ char buf[100];
+ int gen;
+
+ args = nil;
+ gen = 0;
+ for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
+ n = N;
+ if(t->sym)
+ n = newname(t->sym);
+ else if(mustname) {
+ // have to give it a name so we can refer to it in trampoline
+ snprint(buf, sizeof buf, ".anon%d", gen++);
+ n = newname(lookup(buf));
+ }
+ a = nod(ODCLFIELD, n, typenod(t->type));
+ a->isddd = t->isddd;
+ if(n != N)
+ n->isddd = t->isddd;
+ args = list(args, a);
+ }
+ return args;
+}
+
+/*
+ * Generate a wrapper function to convert from
+ * a receiver of type T to a receiver of type U.
+ * That is,
+ *
+ * func (t T) M() {
+ * ...
+ * }
+ *
+ * already exists; this function generates
+ *
+ * func (u U) M() {
+ * u.M()
+ * }
+ *
+ * where the types T and U are such that u.M() is valid
+ * and calls the T.M method.
+ * The resulting function is for use in method tables.
+ *
+ * rcvr - U
+ * method - M func (t T)(), a TFIELD type struct
+ * newnam - the eventual mangled name of this function
+ */
+void
+genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
+{
+ Node *this, *fn, *call, *n, *t, *pad;
+ NodeList *l, *args, *in, *out;
+ Type *tpad;
+ int isddd;
+ Val v;
+
+ if(debug['r'])
+ print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
+ rcvr, method, newnam);
+
+ lineno = 1; // less confusing than end of input
+
+ dclcontext = PEXTERN;
+ markdcl();
+
+ this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr));
+ this->left->ntype = this->right;
+ in = structargs(getinarg(method->type), 1);
+ out = structargs(getoutarg(method->type), 0);
+
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(newnam);
+ t = nod(OTFUNC, N, N);
+ l = list1(this);
+ if(iface && rcvr->width < types[tptr]->width) {
+ // Building method for interface table and receiver
+ // is smaller than the single pointer-sized word
+ // that the interface call will pass in.
+ // Add a dummy padding argument after the
+ // receiver to make up the difference.
+ tpad = typ(TARRAY);
+ tpad->type = types[TUINT8];
+ tpad->bound = types[tptr]->width - rcvr->width;
+ pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
+ l = list(l, pad);
+ }
+ t->list = concat(l, in);
+ t->rlist = out;
+ fn->nname->ntype = t;
+ funchdr(fn);
+
+ // arg list
+ args = nil;
+ isddd = 0;
+ for(l=in; l; l=l->next) {
+ args = list(args, l->n->left);
+ isddd = l->n->left->isddd;
+ }
+
+ // generate nil pointer check for better error
+ if(isptr[rcvr->etype] && rcvr->type == getthisx(method->type)->type->type) {
+ // generating wrapper from *T to T.
+ n = nod(OIF, N, N);
+ n->ntest = nod(OEQ, this->left, nodnil());
+ // these strings are already in the reflect tables,
+ // so no space cost to use them here.
+ l = nil;
+ v.ctype = CTSTR;
+ v.u.sval = strlit(rcvr->type->sym->pkg->name); // package name
+ l = list(l, nodlit(v));
+ v.u.sval = strlit(rcvr->type->sym->name); // type name
+ l = list(l, nodlit(v));
+ v.u.sval = strlit(method->sym->name);
+ l = list(l, nodlit(v)); // method name
+ call = nod(OCALL, syslook("panicwrap", 0), N);
+ call->list = l;
+ n->nbody = list1(call);
+ fn->nbody = list(fn->nbody, n);
+ }
+
+ // generate call
+ call = nod(OCALL, adddot(nod(OXDOT, this->left, newname(method->sym))), N);
+ call->list = args;
+ call->isddd = isddd;
+ if(method->type->outtuple > 0) {
+ n = nod(ORETURN, N, N);
+ n->list = list1(call);
+ call = n;
+ }
+ fn->nbody = list(fn->nbody, call);
+
+ if(0 && debug['r'])
+ dumplist("genwrapper body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+ funccompile(fn, 0);
+}
+
+static Type*
+ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
+{
+ int i, c, d;
+ Type *m;
+
+ *followptr = 0;
+
+ if(t == T)
+ return T;
+
+ for(d=0; d<nelem(dotlist); d++) {
+ c = adddot1(s, t, d, &m, ignorecase);
+ if(c > 1) {
+ yyerror("%T.%S is ambiguous", t, s);
+ return T;
+ }
+ if(c == 1) {
+ for(i=0; i<d; i++) {
+ if(isptr[dotlist[i].field->type->etype]) {
+ *followptr = 1;
+ break;
+ }
+ }
+ if(m->type->etype != TFUNC || m->type->thistuple == 0) {
+ yyerror("%T.%S is a field, not a method", t, s);
+ return T;
+ }
+ return m;
+ }
+ }
+ return T;
+}
+
+int
+implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
+{
+ Type *t0, *im, *tm, *rcvr, *imtype;
+ int followptr;
+
+ t0 = t;
+ if(t == T)
+ return 0;
+
+ // if this is too slow,
+ // could sort these first
+ // and then do one loop.
+
+ if(t->etype == TINTER) {
+ for(im=iface->type; im; im=im->down) {
+ for(tm=t->type; tm; tm=tm->down) {
+ if(tm->sym == im->sym) {
+ if(eqtype(tm->type, im->type))
+ goto found;
+ *m = im;
+ *samename = tm;
+ *ptr = 0;
+ return 0;
+ }
+ }
+ *m = im;
+ *samename = nil;
+ *ptr = 0;
+ return 0;
+ found:;
+ }
+ return 1;
+ }
+
+ t = methtype(t);
+ if(t != T)
+ expandmeth(t->sym, t);
+ for(im=iface->type; im; im=im->down) {
+ imtype = methodfunc(im->type, 0);
+ tm = ifacelookdot(im->sym, t, &followptr, 0);
+ if(tm == T || !eqtype(methodfunc(tm->type, 0), imtype)) {
+ if(tm == T)
+ tm = ifacelookdot(im->sym, t, &followptr, 1);
+ *m = im;
+ *samename = tm;
+ *ptr = 0;
+ return 0;
+ }
+ // if pointer receiver in method,
+ // the method does not exist for value types.
+ rcvr = getthisx(tm->type)->type->type;
+ if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm->type)) {
+ if(0 && debug['r'])
+ yyerror("interface pointer mismatch");
+
+ *m = im;
+ *samename = nil;
+ *ptr = 1;
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * even simpler simtype; get rid of ptr, bool.
+ * assuming that the front end has rejected
+ * all the invalid conversions (like ptr -> bool)
+ */
+int
+simsimtype(Type *t)
+{
+ int et;
+
+ if(t == 0)
+ return 0;
+
+ et = simtype[t->etype];
+ switch(et) {
+ case TPTR32:
+ et = TUINT32;
+ break;
+ case TPTR64:
+ et = TUINT64;
+ break;
+ case TBOOL:
+ et = TUINT8;
+ break;
+ }
+ return et;
+}
+
+NodeList*
+concat(NodeList *a, NodeList *b)
+{
+ if(a == nil)
+ return b;
+ if(b == nil)
+ return a;
+
+ a->end->next = b;
+ a->end = b->end;
+ b->end = nil;
+ return a;
+}
+
+NodeList*
+list1(Node *n)
+{
+ NodeList *l;
+
+ if(n == nil)
+ return nil;
+ if(n->op == OBLOCK && n->ninit == nil)
+ return n->list;
+ l = mal(sizeof *l);
+ l->n = n;
+ l->end = l;
+ return l;
+}
+
+NodeList*
+list(NodeList *l, Node *n)
+{
+ return concat(l, list1(n));
+}
+
+void
+listsort(NodeList** l, int(*f)(Node*, Node*))
+{
+ NodeList *l1, *l2, *le;
+
+ if(*l == nil || (*l)->next == nil)
+ return;
+
+ l1 = *l;
+ l2 = *l;
+ for(;;) {
+ l2 = l2->next;
+ if(l2 == nil)
+ break;
+ l2 = l2->next;
+ if(l2 == nil)
+ break;
+ l1 = l1->next;
+ }
+
+ l2 = l1->next;
+ l1->next = nil;
+ l2->end = (*l)->end;
+ (*l)->end = l1;
+
+ l1 = *l;
+ listsort(&l1, f);
+ listsort(&l2, f);
+
+ if ((*f)(l1->n, l2->n) < 0) {
+ *l = l1;
+ } else {
+ *l = l2;
+ l2 = l1;
+ l1 = *l;
+ }
+
+ // now l1 == *l; and l1 < l2
+
+ while ((l1 != nil) && (l2 != nil)) {
+ while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
+ l1 = l1->next;
+
+ // l1 is last one from l1 that is < l2
+ le = l1->next; // le is the rest of l1, first one that is >= l2
+ if (le != nil)
+ le->end = (*l)->end;
+
+ (*l)->end = l1; // cut *l at l1
+ *l = concat(*l, l2); // glue l2 to *l's tail
+
+ l1 = l2; // l1 is the first element of *l that is < the new l2
+ l2 = le; // ... because l2 now is the old tail of l1
+ }
+
+ *l = concat(*l, l2); // any remainder
+}
+
+NodeList*
+listtreecopy(NodeList *l)
+{
+ NodeList *out;
+
+ out = nil;
+ for(; l; l=l->next)
+ out = list(out, treecopy(l->n));
+ return out;
+}
+
+Node*
+liststmt(NodeList *l)
+{
+ Node *n;
+
+ n = nod(OBLOCK, N, N);
+ n->list = l;
+ if(l)
+ n->lineno = l->n->lineno;
+ return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+count(NodeList *l)
+{
+ int n;
+
+ n = 0;
+ for(; l; l=l->next)
+ n++;
+ return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+structcount(Type *t)
+{
+ int v;
+ Iter s;
+
+ v = 0;
+ for(t = structfirst(&s, &t); t != T; t = structnext(&s))
+ v++;
+ return v;
+}
+
+/*
+ * return power of 2 of the constant
+ * operand. -1 if it is not a power of 2.
+ * 1000+ if it is a -(power of 2)
+ */
+int
+powtwo(Node *n)
+{
+ uvlong v, b;
+ int i;
+
+ if(n == N || n->op != OLITERAL || n->type == T)
+ goto no;
+ if(!isint[n->type->etype])
+ goto no;
+
+ v = mpgetfix(n->val.u.xval);
+ b = 1ULL;
+ for(i=0; i<64; i++) {
+ if(b == v)
+ return i;
+ b = b<<1;
+ }
+
+ if(!issigned[n->type->etype])
+ goto no;
+
+ v = -v;
+ b = 1ULL;
+ for(i=0; i<64; i++) {
+ if(b == v)
+ return i+1000;
+ b = b<<1;
+ }
+
+no:
+ return -1;
+}
+
+/*
+ * return the unsigned type for
+ * a signed integer type.
+ * returns T if input is not a
+ * signed integer type.
+ */
+Type*
+tounsigned(Type *t)
+{
+
+ // this is types[et+1], but not sure
+ // that this relation is immutable
+ switch(t->etype) {
+ default:
+ print("tounsigned: unknown type %T\n", t);
+ t = T;
+ break;
+ case TINT:
+ t = types[TUINT];
+ break;
+ case TINT8:
+ t = types[TUINT8];
+ break;
+ case TINT16:
+ t = types[TUINT16];
+ break;
+ case TINT32:
+ t = types[TUINT32];
+ break;
+ case TINT64:
+ t = types[TUINT64];
+ break;
+ }
+ return t;
+}
+
+/*
+ * magic number for signed division
+ * see hacker's delight chapter 10
+ */
+void
+smagic(Magic *m)
+{
+ int p;
+ uint64 ad, anc, delta, q1, r1, q2, r2, t;
+ uint64 mask, two31;
+
+ m->bad = 0;
+ switch(m->w) {
+ default:
+ m->bad = 1;
+ return;
+ case 8:
+ mask = 0xffLL;
+ break;
+ case 16:
+ mask = 0xffffLL;
+ break;
+ case 32:
+ mask = 0xffffffffLL;
+ break;
+ case 64:
+ mask = 0xffffffffffffffffLL;
+ break;
+ }
+ two31 = mask ^ (mask>>1);
+
+ p = m->w-1;
+ ad = m->sd;
+ if(m->sd < 0)
+ ad = -m->sd;
+
+ // bad denominators
+ if(ad == 0 || ad == 1 || ad == two31) {
+ m->bad = 1;
+ return;
+ }
+
+ t = two31;
+ ad &= mask;
+
+ anc = t - 1 - t%ad;
+ anc &= mask;
+
+ q1 = two31/anc;
+ r1 = two31 - q1*anc;
+ q1 &= mask;
+ r1 &= mask;
+
+ q2 = two31/ad;
+ r2 = two31 - q2*ad;
+ q2 &= mask;
+ r2 &= mask;
+
+ for(;;) {
+ p++;
+ q1 <<= 1;
+ r1 <<= 1;
+ q1 &= mask;
+ r1 &= mask;
+ if(r1 >= anc) {
+ q1++;
+ r1 -= anc;
+ q1 &= mask;
+ r1 &= mask;
+ }
+
+ q2 <<= 1;
+ r2 <<= 1;
+ q2 &= mask;
+ r2 &= mask;
+ if(r2 >= ad) {
+ q2++;
+ r2 -= ad;
+ q2 &= mask;
+ r2 &= mask;
+ }
+
+ delta = ad - r2;
+ delta &= mask;
+ if(q1 < delta || (q1 == delta && r1 == 0)) {
+ continue;
+ }
+ break;
+ }
+
+ m->sm = q2+1;
+ if(m->sm & two31)
+ m->sm |= ~mask;
+ m->s = p-m->w;
+}
+
+/*
+ * magic number for unsigned division
+ * see hacker's delight chapter 10
+ */
+void
+umagic(Magic *m)
+{
+ int p;
+ uint64 nc, delta, q1, r1, q2, r2;
+ uint64 mask, two31;
+
+ m->bad = 0;
+ m->ua = 0;
+
+ switch(m->w) {
+ default:
+ m->bad = 1;
+ return;
+ case 8:
+ mask = 0xffLL;
+ break;
+ case 16:
+ mask = 0xffffLL;
+ break;
+ case 32:
+ mask = 0xffffffffLL;
+ break;
+ case 64:
+ mask = 0xffffffffffffffffLL;
+ break;
+ }
+ two31 = mask ^ (mask>>1);
+
+ m->ud &= mask;
+ if(m->ud == 0 || m->ud == two31) {
+ m->bad = 1;
+ return;
+ }
+ nc = mask - (-m->ud&mask)%m->ud;
+ p = m->w-1;
+
+ q1 = two31/nc;
+ r1 = two31 - q1*nc;
+ q1 &= mask;
+ r1 &= mask;
+
+ q2 = (two31-1) / m->ud;
+ r2 = (two31-1) - q2*m->ud;
+ q2 &= mask;
+ r2 &= mask;
+
+ for(;;) {
+ p++;
+ if(r1 >= nc-r1) {
+ q1 <<= 1;
+ q1++;
+ r1 <<= 1;
+ r1 -= nc;
+ } else {
+ q1 <<= 1;
+ r1 <<= 1;
+ }
+ q1 &= mask;
+ r1 &= mask;
+ if(r2+1 >= m->ud-r2) {
+ if(q2 >= two31-1) {
+ m->ua = 1;
+ }
+ q2 <<= 1;
+ q2++;
+ r2 <<= 1;
+ r2++;
+ r2 -= m->ud;
+ } else {
+ if(q2 >= two31) {
+ m->ua = 1;
+ }
+ q2 <<= 1;
+ r2 <<= 1;
+ r2++;
+ }
+ q2 &= mask;
+ r2 &= mask;
+
+ delta = m->ud - 1 - r2;
+ delta &= mask;
+
+ if(p < m->w+m->w)
+ if(q1 < delta || (q1 == delta && r1 == 0)) {
+ continue;
+ }
+ break;
+ }
+ m->um = q2+1;
+ m->s = p-m->w;
+}
+
+Sym*
+ngotype(Node *n)
+{
+ if(n->sym != S && n->realtype != T)
+ if(strncmp(n->sym->name, "autotmp_", 8) != 0)
+ if(strncmp(n->sym->name, "statictmp_", 8) != 0)
+ return typename(n->realtype)->left->sym;
+
+ return S;
+}
+
+/*
+ * Convert raw string to the prefix that will be used in the symbol table.
+ * Invalid bytes turn into %xx. Right now the only bytes that need
+ * escaping are %, ., and ", but we escape all control characters too.
+ */
+static char*
+pathtoprefix(char *s)
+{
+ static char hex[] = "0123456789abcdef";
+ char *p, *r, *w;
+ int n;
+
+ // check for chars that need escaping
+ n = 0;
+ for(r=s; *r; r++)
+ if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"')
+ n++;
+
+ // quick exit
+ if(n == 0)
+ return s;
+
+ // escape
+ p = mal((r-s)+1+2*n);
+ for(r=s, w=p; *r; r++) {
+ if(*r <= ' ' || *r == '.' || *r == '%' || *r == '"') {
+ *w++ = '%';
+ *w++ = hex[(*r>>4)&0xF];
+ *w++ = hex[*r&0xF];
+ } else
+ *w++ = *r;
+ }
+ *w = '\0';
+ return p;
+}
+
+Pkg*
+mkpkg(Strlit *path)
+{
+ Pkg *p;
+ int h;
+
+ if(strlen(path->s) != path->len) {
+ yyerror("import path contains NUL byte");
+ errorexit();
+ }
+
+ h = stringhash(path->s) & (nelem(phash)-1);
+ for(p=phash[h]; p; p=p->link)
+ if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
+ return p;
+
+ p = mal(sizeof *p);
+ p->path = path;
+ p->prefix = pathtoprefix(path->s);
+ p->link = phash[h];
+ phash[h] = p;
+ return p;
+}
+
+Strlit*
+strlit(char *s)
+{
+ Strlit *t;
+
+ t = mal(sizeof *t + strlen(s));
+ strcpy(t->s, s);
+ t->len = strlen(s);
+ return t;
+}