// 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 "y.tab.h" void errorexit(void) { if(outfile) remove(outfile); myexit(1); } void yyerror(char *fmt, ...) { va_list arg; print("%L: ", lineno); va_start(arg, fmt); vfprint(1, fmt, arg); va_end(arg); if(strcmp(fmt, "syntax error") == 0) print(" near %s", namebuf); print("\n"); if(debug['h']) *(int*)0 = 0; nerrors++; if(nerrors >= 10) fatal("too many errors"); } void warn(char *fmt, ...) { va_list arg; print("%L: ", lineno); va_start(arg, fmt); vfprint(1, fmt, arg); va_end(arg); print("\n"); if(debug['h']) *(int*)0 = 0; } void fatal(char *fmt, ...) { va_list arg; print("%L: fatal error: ", lineno); va_start(arg, fmt); vfprint(1, fmt, arg); va_end(arg); print("\n"); if(debug['h']) *(int*)0 = 0; myexit(1); } void linehist(char *file, int32 off) { Hist *h; char *cp; if(debug['i']) { if(file != nil) { if(off < 0) print("pragma %s at line %L\n", file, lineno); else print("import %s at line %L\n", file, lineno); } else print("end of import at line %L\n", lineno); } if(off < 0 && file[0] != '/') { cp = mal(strlen(file) + strlen(pathname) + 2); sprint(cp, "%s/%s", pathname, file); file = cp; } h = alloc(sizeof(Hist)); h->name = file; h->line = lineno; 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 && n->op != ONAME) { 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 *p) { Sym *s; uint32 h; int c; h = stringhash(p) % NHASH; c = p[0]; for(s = hash[h]; s != S; s = s->link) { if(s->name[0] != c) continue; if(strcmp(s->name, p) == 0) if(strcmp(s->package, package) == 0) return s; } s = mal(sizeof(*s)); s->lexical = LNAME; s->name = mal(strlen(p)+1); s->opackage = package; s->package = package; strcpy(s->name, p); s->link = hash[h]; hash[h] = s; return s; } Sym* pkglookup(char *p, char *k) { Sym *s; uint32 h; int c; h = stringhash(p) % NHASH; c = p[0]; for(s = hash[h]; s != S; s = s->link) { if(s->name[0] != c) continue; if(strcmp(s->name, p) == 0) if(strcmp(s->package, k) == 0) return s; } s = mal(sizeof(*s)); s->lexical = LNAME; s->name = mal(strlen(p)+1); strcpy(s->name, p); // botch - should probably try to reuse the pkg string s->package = mal(strlen(k)+1); s->opackage = s->package; strcpy(s->package, k); s->link = hash[h]; hash[h] = s; return s; } void gethunk(void) { char *h; int32 nh; nh = NHUNK; if(thunk >= 10L*NHUNK) nh = 10L*NHUNK; h = (char*)malloc(nh); if(h == (char*)-1) { yyerror("out of memory"); errorexit(); } hunk = h; nhunk = nh; thunk += nh; } void* mal(int32 n) { void *p; while((uintptr)hunk & MAXALIGN) { hunk++; nhunk--; } while(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) { while(nhunk < on+n) gethunk(); memmove(hunk, p, on); p = hunk; hunk += on; nhunk -= on; } hunk += n; nhunk -= n; return p; } Dcl* dcl(void) { Dcl *d; d = mal(sizeof(*d)); d->lineno = lineno; return d; } 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 = lineno; return n; } int algtype(Type *t) { int a; a = AUNK; if(issimple[t->etype]) a = ASIMP; // simple mem else if(isptrto(t, TSTRING)) a = ASTRING; // string else if(isptr[t->etype]) a = APTR; // pointer else if(isinter(t)) a = AINTER; // interface // else // fatal("algtype: cant find type %T", t); return a; } Node* list(Node *a, Node *b) { if(a == N) return b; if(b == N) return a; return nod(OLIST, a, b); } Type* typ(int et) { Type *t; t = mal(sizeof(*t)); t->etype = et; return t; } Node* dobad(void) { return nod(OBAD, N, N); } Node* nodintconst(int32 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[TINT]; ullmancalc(c); return c; } Node* rev(Node *na) { Node *i, *n; /* * since yacc wants to build lists * stacked down on the left - * this routine converts them to * stack down on the right - * in memory without recursion */ if(na == N || na->op != OLIST) return na; i = na; for(n = na->left; n != N; n = n->left) { if(n->op != OLIST) break; i->left = n->right; n->right = i; i = n; } i->left = n; return i; } Node* unrev(Node *na) { Node *i, *n; /* * this restores a reverse list */ if(na == N || na->op != OLIST) return na; i = na; for(n = na->right; n != N; n = n->right) { if(n->op != OLIST) break; i->right = n->left; n->left = i; i = n; } i->right = n; return i; } /* * na and nb are reversed lists. * append them into one big reversed list. */ Node* appendr(Node *na, Node *nb) { Node **l, *n; for(l=&nb; (n=*l)->op == OLIST; l=&n->left) ; *l = nod(OLIST, na, *l); return nb; } Type* aindex(Node *b, Type *t) { Type *r; int bound; bound = -1; // open bound walktype(b, Erv); switch(whatis(b)) { default: // variable bound yyerror("array bound must be an integer expression"); break; case Wnil: // open bound break; case Wlitint: // fixed bound 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; } void indent(int dep) { int i; for(i=0; iop) { case OLIST: if(n->left != N && n->left->op == OLIST) dodump(n->left, dep+1); else dodump(n->left, dep); n = n->right; goto loop; } indent(dep); if(dep > 10) { print("...\n"); return; } if(n->ninit != N) { print("%O-init\n", n->op); dodump(n->ninit, dep+1); indent(dep); } switch(n->op) { default: print("%N\n", n); break; case OTYPE: print("%O-%E %lT\n", n->op, n->etype, n); break; case OIF: print("%O%J\n", n->op, n); dodump(n->ntest, dep+1); if(n->nbody != N) { indent(dep); print("%O-then\n", n->op); dodump(n->nbody, dep+1); } if(n->nelse != N) { indent(dep); print("%O-else\n", n->op); dodump(n->nelse, dep+1); } return; case OSELECT: print("%O%J\n", n->op, n); dodump(n->nbody, dep+1); return; case OSWITCH: case OFOR: print("%O%J\n", n->op, n); dodump(n->ntest, dep+1); if(n->nbody != N) { indent(dep); print("%O-body\n", n->op); dodump(n->nbody, dep+1); } if(n->nincr != N) { indent(dep); print("%O-incr\n", n->op); dodump(n->nincr, dep+1); } return; case OCASE: // the right side points to the next case print("%O%J\n", n->op, n); dodump(n->left, dep+1); return; } dodump(n->left, dep+1); n = n->right; dep++; goto loop; } void dump(char *s, Node *n) { print("%s\n", s); dodump(n, 1); } int whatis(Node *n) { Type *t; if(n == N) return Wnil; if(n->op == OLITERAL) { switch(n->val.ctype) { default: break; case CTINT: case CTSINT: case CTUINT: return Wlitint; case CTFLT: return Wlitfloat; case CTBOOL: return Wlitbool; case CTSTR: return Wlitstr; case CTNIL: return Wlitnil; // not used } return Wtunkn; } t = n->type; if(t == T) return Wtnil; switch(t->etype) { case TINT: case TINT8: case TINT16: case TINT32: case TINT64: case TUINT: case TUINT8: case TUINT16: case TUINT32: case TUINT64: case TUINTPTR: return Wtint; case TFLOAT: case TFLOAT32: case TFLOAT64: case TFLOAT80: return Wtfloat; case TBOOL: return Wtbool; case TPTR32: case TPTR64: if(isptrto(t, TSTRING)) return Wtstr; break; } return Wtunkn; } /* s%,%,\n%g s%\n+%\n%g s%^[ ]*O%%g s%,.*%%g s%.+% [O&] = "&",%g s%^ ........*\]%&~%g s%~ %%g */ static char* opnames[] = { [OADDR] = "ADDR", [OADD] = "ADD", [OANDAND] = "ANDAND", [OAND] = "AND", [OARRAY] = "ARRAY", [OASOP] = "ASOP", [OAS] = "AS", [OBAD] = "BAD", [OBREAK] = "BREAK", [OCALL] = "CALL", [OCALLMETH] = "CALLMETH", [OCALLINTER] = "CALLINTER", [OCASE] = "CASE", [OXCASE] = "XCASE", [OCMP] = "CMP", [OFALL] = "FALL", [OCONV] = "CONV", [OCOM] = "COM", [OCONST] = "CONST", [OCONTINUE] = "CONTINUE", [ODCLARG] = "DCLARG", [ODCLFIELD] = "DCLFIELD", [ODCLFUNC] = "DCLFUNC", [ODIV] = "DIV", [ODOT] = "DOT", [ODOTPTR] = "DOTPTR", [ODOTMETH] = "DOTMETH", [ODOTINTER] = "DOTINTER", [OEMPTY] = "EMPTY", [OEND] = "END", [OEQ] = "EQ", [OFOR] = "FOR", [OFUNC] = "FUNC", [OGE] = "GE", [OPROC] = "PROC", [OGOTO] = "GOTO", [OGT] = "GT", [OIF] = "IF", [OINDEX] = "INDEX", [OINDEXPTR] = "INDEXPTR", [OIND] = "IND", [OKEY] = "KEY", [OLABEL] = "LABEL", [OLE] = "LE", [OLEN] = "LEN", [OCAP] = "CAP", [OLIST] = "LIST", [OLITERAL] = "LITERAL", [OLSH] = "LSH", [OLT] = "LT", [OMINUS] = "MINUS", [OMOD] = "MOD", [OMUL] = "MUL", [ONAME] = "NAME", [ONONAME] = "NONAME", [ONE] = "NE", [ONOT] = "NOT", [OOROR] = "OROR", [OOR] = "OR", [OPLUS] = "PLUS", [OREGISTER] = "REGISTER", [OINDREG] = "INDREG", [OSEND] = "SEND", [ORECV] = "RECV", [OPTR] = "PTR", [ORETURN] = "RETURN", [ORSH] = "RSH", [OSLICE] = "SLICE", [OSUB] = "SUB", [OSELECT] = "SELECT", [OSWITCH] = "SWITCH", [OTYPE] = "TYPE", [OVAR] = "VAR", [OIMPORT] = "IMPORT", [OXOR] = "XOR", [ONEW] = "NEW", [OFALL] = "FALL", [OXFALL] = "XFALL", [OPANIC] = "PANIC", [OPANICN] = "PANICN", [OPRINT] = "PRINT", [OPRINTN] = "PRINTN", [OXXX] = "XXX", }; int Oconv(Fmt *fp) { char buf[500]; int o; o = va_arg(fp->args, int); if(o < 0 || o >= nelem(opnames) || opnames[o] == nil) { snprint(buf, sizeof(buf), "O-%d", o); return fmtstrcpy(fp, buf); } return fmtstrcpy(fp, opnames[o]); } int Lconv(Fmt *fp) { char str[STRINGSZ], s[STRINGSZ]; 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(n < HISTSZ) { /* beginning of file */ 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; str[0] = 0; for(i=n-1; i>=0; i--) { if(i != n-1) { if(fp->flags & ~(FmtWidth|FmtPrec)) break; strcat(str, " "); } if(a[i].line) snprint(s, STRINGSZ, "%s:%ld[%s:%ld]", a[i].line->name, lno-a[i].ldel+1, a[i].incl->name, lno-a[i].idel+1); else snprint(s, STRINGSZ, "%s:%ld", a[i].incl->name, lno-a[i].idel+1); if(strlen(s)+strlen(str) >= STRINGSZ-10) break; strcat(str, s); lno = a[i].incl->line - 1; /* now print out start of this file */ } if(n == 0) strcat(str, ""); return fmtstrcpy(fp, str); } /* 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", [TFLOAT] = "FLOAT", [TFLOAT32] = "FLOAT32", [TFLOAT64] = "FLOAT64", [TFLOAT80] = "FLOAT80", [TBOOL] = "BOOL", [TPTR32] = "PTR32", [TPTR64] = "PTR64", [TFUNC] = "FUNC", [TARRAY] = "ARRAY", // [TDARRAY] = "DARRAY", [TSTRUCT] = "STRUCT", [TCHAN] = "CHAN", [TMAP] = "MAP", [TINTER] = "INTER", [TFORW] = "FORW", [TFIELD] = "FIELD", [TSTRING] = "STRING", [TCHAN] = "CHAN", [TANY] = "ANY", [TFORWINTER] = "FORWINTER", [TFORWSTRUCT] = "FORWSTRUCT", }; int Econv(Fmt *fp) { char buf[500]; int et; et = va_arg(fp->args, int); if(et < 0 || et >= nelem(etnames) || etnames[et] == nil) { snprint(buf, sizeof(buf), "E-%d", et); return fmtstrcpy(fp, buf); } return fmtstrcpy(fp, etnames[et]); } int Jconv(Fmt *fp) { char buf[500], buf1[100]; Node *n; n = va_arg(fp->args, Node*); strcpy(buf, ""); if(n->ullman != 0) { snprint(buf1, sizeof(buf1), " u(%d)", n->ullman); strncat(buf, buf1, sizeof(buf)); } if(n->addable != 0) { snprint(buf1, sizeof(buf1), " a(%d)", n->addable); strncat(buf, buf1, sizeof(buf)); } if(n->vargen != 0) { snprint(buf1, sizeof(buf1), " g(%ld)", n->vargen); strncat(buf, buf1, sizeof(buf)); } if(n->lineno != 0) { snprint(buf1, sizeof(buf1), " l(%ld)", n->lineno); strncat(buf, buf1, sizeof(buf)); } return fmtstrcpy(fp, buf); } int Gconv(Fmt *fp) { char buf[100]; Type *t; t = va_arg(fp->args, Type*); if(t->etype == TFUNC) { if(t->vargen != 0) { snprint(buf, sizeof(buf), "-%d%d%d g(%ld)", t->thistuple, t->outtuple, t->intuple, t->vargen); goto out; } snprint(buf, sizeof(buf), "-%d%d%d", t->thistuple, t->outtuple, t->intuple); goto out; } if(t->vargen != 0) { snprint(buf, sizeof(buf), " g(%ld)", t->vargen); goto out; } strcpy(buf, ""); out: return fmtstrcpy(fp, buf); } int Sconv(Fmt *fp) { char buf[500]; Sym *s; char *opk, *pkg, *nam; s = va_arg(fp->args, Sym*); if(s == S) { snprint(buf, sizeof(buf), ""); goto out; } pkg = ""; nam = pkg; opk = pkg; if(s->opackage != nil) opk = s->opackage; if(s->package != nil) pkg = s->package; if(s->name != nil) nam = s->name; if(!(fp->flags & FmtShort)) if(strcmp(pkg, package) || strcmp(opk, package) || (fp->flags & FmtLong)) { if(strcmp(opk, pkg) == 0) { snprint(buf, sizeof(buf), "%s.%s", pkg, nam); goto out; } snprint(buf, sizeof(buf), "(%s)%s.%s", opk, pkg, nam); goto out; } snprint(buf, sizeof(buf), "%s", nam); out: return fmtstrcpy(fp, buf); } 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", [TFLOAT] = "float", [TFLOAT32] = "float32", [TFLOAT64] = "float64", [TFLOAT80] = "float80", [TBOOL] = "bool", [TANY] = "any", }; int Tpretty(Fmt *fp, Type *t) { Type *t1; Sym *s; if(t->etype != TFIELD && t->sym != S && !(fp->flags&FmtLong)) { s = t->sym; if(t == types[t->etype] || t == types[TSTRING]) return fmtprint(fp, "%s", s->name); if(exporting) { if(t->xsym != S) s = t->xsym; if(strcmp(s->opackage, package) == 0) if(s->otype != t || !s->export) return fmtprint(fp, "%lS_%s", s, filename); return fmtprint(fp, "%lS", s); } return fmtprint(fp, "%S", s); } if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) return fmtprint(fp, "%s", basicnames[t->etype]); switch(t->etype) { case TPTR32: case TPTR64: if(t->type && t->type->etype == TSTRING) return fmtprint(fp, "string"); return fmtprint(fp, "*%T", 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, ")"); } fmtprint(fp, "("); for(t1=getinargx(t)->type; t1; t1=t1->down) { 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->etype != TFIELD) { fmtprint(fp, " %T", t1); break; } default: t1 = getoutargx(t)->type; fmtprint(fp, " ("); for(; t1; t1=t1->down) { 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); return fmtprint(fp, "[]%T", t->type); case TCHAN: return fmtprint(fp, "chan %T", t->type); case TMAP: return fmtprint(fp, "map[%T] %T", t->down, t->type); case TINTER: fmtprint(fp, "interface {"); for(t1=t->type; t1!=T; t1=t1->down) { fmtprint(fp, " %hS %hT", t1->sym, t1->type); if(t1->down) fmtprint(fp, ";"); } return fmtprint(fp, " }"); case TSTRUCT: 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, "? "); return fmtprint(fp, "%T", t->type); } return fmtprint(fp, "%hS %T", t->sym, t->type); case TFORW: if(exporting) yyerror("undefined type %S", t->sym); if(t->sym) return fmtprint(fp, "undefined %S", t->sym); return fmtprint(fp, "undefined"); } // Don't know how to handle - fall back to detailed prints. return -1; } int Tconv(Fmt *fp) { char buf[500], buf1[500]; Type *t, *t1; int et, exp; t = va_arg(fp->args, Type*); if(t == T) return fmtstrcpy(fp, ""); t->trecur++; if(t->trecur > 5) { strncat(buf, "...", sizeof(buf)); goto out; } if(!debug['t']) { exp = (fp->flags & FmtSharp); if(exp) exporting++; if(Tpretty(fp, t) >= 0) { t->trecur--; if(exp) exporting--; return 0; } if(exp) exporting--; } et = t->etype; strcpy(buf, ""); if(t->sym != S) snprint(buf, sizeof(buf), "<%S>", t->sym); switch(et) { default: snprint(buf1, sizeof(buf1), "%E", et); strncat(buf, buf1, sizeof(buf)); if(t->type != T) { snprint(buf1, sizeof(buf1), " %T", t->type); strncat(buf, buf1, sizeof(buf)); } break; case TFIELD: snprint(buf1, sizeof(buf1), "%T", t->type); strncat(buf, buf1, sizeof(buf)); break; case TFUNC: if(fp->flags & FmtLong) snprint(buf1, sizeof(buf1), "%d%d%d(%lT,%lT)%lT", t->thistuple, t->intuple, t->outtuple, t->type, t->type->down->down, t->type->down); else snprint(buf1, sizeof(buf1), "%d%d%d(%T,%T)%T", t->thistuple, t->intuple, t->outtuple, t->type, t->type->down->down, t->type->down); strncat(buf, buf1, sizeof(buf)); break; case TINTER: strncat(buf, "I{", sizeof(buf)); if(fp->flags & FmtLong) { for(t1=t->type; t1!=T; t1=t1->down) { snprint(buf1, sizeof(buf1), "%lT;", t1); strncat(buf, buf1, sizeof(buf)); } } strncat(buf, "}", sizeof(buf)); break; case TSTRUCT: strncat(buf, "{", sizeof(buf)); if(fp->flags & FmtLong) { for(t1=t->type; t1!=T; t1=t1->down) { snprint(buf1, sizeof(buf1), "%lT;", t1); strncat(buf, buf1, sizeof(buf)); } } strncat(buf, "}", sizeof(buf)); break; case TMAP: snprint(buf, sizeof(buf), "MAP[%T]%T", t->down, t->type); break; case TARRAY: if(t->bound >= 0) snprint(buf1, sizeof(buf1), "[%ld]%T", t->bound, t->type); else snprint(buf1, sizeof(buf1), "[]%T", t->type); strncat(buf, buf1, sizeof(buf)); break; case TPTR32: case TPTR64: snprint(buf1, sizeof(buf1), "*%T", t->type); strncat(buf, buf1, sizeof(buf)); break; } out: t->trecur--; return fmtstrcpy(fp, buf); } int Nconv(Fmt *fp) { char buf[500], buf1[500]; Node *n; n = va_arg(fp->args, Node*); if(n == N) { snprint(buf, sizeof(buf), ""); goto out; } switch(n->op) { default: snprint(buf, sizeof(buf), "%O%J", n->op, n); break; case ONAME: case ONONAME: if(n->sym == S) { snprint(buf, sizeof(buf), "%O%J", n->op, n); break; } snprint(buf, sizeof(buf), "%O-%S G%ld%J", n->op, n->sym, n->sym->vargen, n); goto ptyp; case OREGISTER: snprint(buf, sizeof(buf), "%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: case CTSINT: case CTUINT: 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 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; } snprint(buf, sizeof(buf), "%O-%s%J", n->op, buf1, n); break; case OASOP: snprint(buf, sizeof(buf), "%O-%O%J", n->op, n->etype, n); break; case OTYPE: snprint(buf, sizeof(buf), "%O-%E%J", n->op, n->etype, n); break; } if(n->sym != S) { snprint(buf1, sizeof(buf1), " %S G%ld", n->sym, n->sym->vargen); strncat(buf, buf1, sizeof(buf)); } ptyp: if(n->type != T) { snprint(buf1, sizeof(buf1), " %T", n->type); strncat(buf, buf1, sizeof(buf)); } out: return fmtstrcpy(fp, buf); } 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); break; case OLITERAL: if(n->iota) { m = literal(iota); break; } m = nod(OXXX, N, N); *m = *n; break; case ONAME: m = nod(OXXX, N, N); *m = *n; break; } return m; } int Zconv(Fmt *fp) { char *s, *se; char *p; char buf[500]; int c; String *sp; sp = va_arg(fp->args, String*); if(sp == nil) { snprint(buf, sizeof(buf), ""); goto out; } s = sp->s; se = s + sp->len; p = buf; loop: c = *s++; if(s > se) c = 0; switch(c) { default: *p++ = c; break; case 0: *p = 0; goto out; case '\t': *p++ = '\\'; *p++ = 't'; break; case '\n': *p++ = '\\'; *p++ = 'n'; break; } goto loop; out: return fmtstrcpy(fp, buf); } static char* wnames[] = { [Wnil] = "Wnil", [Wtnil] = "Wtnil", [Wtfloat] = "Wtfloat", [Wtint] = "Wtint", [Wtbool] = "Wtbool", [Wtstr] = "Wtstr", [Wlitfloat] = "float constant", [Wlitint] = "int constant", [Wlitbool] = "bool", [Wlitstr] = "string", [Wlitnil] = "nil", }; int Wconv(Fmt *fp) { int w; w = va_arg(fp->args, int); if(w < 0 || w >= nelem(wnames) || wnames[w] == nil) return fmtprint(fp, "W-%d", w); return fmtstrcpy(fp, wnames[w]); } 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 isptrarray(Type *t) { if(isptrto(t, TARRAY)) if(t->type->bound >= 0) return 1; return 0; } int isptrdarray(Type *t) { if(isptrto(t, TARRAY)) if(t->type->bound < 0) return 1; return 0; } int isselect(Node *n) { Sym *s; if(n == N) return 0; n = n->left; s = pkglookup("selectsend", "sys"); if(s == n->sym) return 1; s = pkglookup("selectrecv", "sys"); if(s == n->sym) return 1; return 0; } int isinter(Type *t) { if(t != T && t->etype == TINTER) return 1; return 0; } int isnilinter(Type *t) { if(!isinter(t)) return 0; if(t->type != T) return 0; return 1; } Type* ismethod(Type *t) { int a; Sym *s; if(t == T) return T; // no interfaces if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER)) return T; a = algtype(t); // direct receiver s = t->sym; if(s != S) { if(t->methptr == 2) goto both; t->methptr |= 1; goto out; } // pointer receiver if(!isptr[t->etype]) return T; t = t->type; if(t == T) return T; s = t->sym; if(s != S) { if(t->methptr == 1) goto both; t->methptr |= 2; goto out; } return T; both: yyerror("type %T used as both direct and indirect method", t); t->methptr = 3; out: switch(a) { default: yyerror("type %T cannot be used as a method", t); case ASIMP: case APTR: case ASTRING: break; } return t; } /* * this is ismethod() without side effects */ Type* methtype(Type *t) { Sym *s; if(t == T) return T; if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER)) return T; s = t->sym; if(s != S) return t; if(!isptr[t->etype]) return T; t = t->type; if(t == T) return T; s = t->sym; if(s != S) return t; return T; } /* * this is another ismethod() * returns 1 if t=T and method wants *T */ int needaddr(Type *t) { Sym *s; if(t == T) return 0; if(t->etype == TINTER || (t->etype == tptr && t->type->etype == TINTER)) return 0; s = t->sym; if(s != S && t->methptr == 2) return 1; return 0; } int iscomposite(Type *t) { if(t == T) return 0; switch(t->etype) { case TMAP: case TARRAY: case TSTRUCT: return 1; } return 0; } Sym* globalsig(Type *t) { int et; Sym *s; char buf[NSYMB]; char *glob; if(t == T) return S; glob = "sys"; et = t->etype; switch(et) { default: return S; case TINTER: if(isnilinter(t)) { snprint(buf, sizeof(buf), "%s_%s", "sigi", "inter"); goto out; } return S; case TPTR32: case TPTR64: if(isptrto(t, TSTRING)) { et = TSTRING; break; } return S; case TINT: case TINT8: case TINT16: case TINT32: case TINT64: case TUINT: case TUINT8: case TUINT16: case TUINT32: case TUINT64: case TUINTPTR: case TFLOAT: case TFLOAT32: case TFLOAT64: case TFLOAT80: case TBOOL: break; } if(t->sym == S) return S; if(t->method != T) return S; if(strcmp(t->sym->name, types[et]->sym->name) != 0) return S; snprint(buf, sizeof(buf), "%s_%S", "sigt", t->sym); out: s = pkglookup(buf, glob); if(s->oname == N) { s->oname = newname(s); s->oname->type = types[TUINT8]; s->oname->class = PEXTERN; s->local = s->local; } //print("*** %lT %lS\n", t, s); return s; } Sym* signame(Type *t, int block) { Sym *s, *ss; char *e; Dcl *x; char buf[NSYMB]; if(t == T) goto bad; s = t->sym; if(s == S) { if(isptr[t->etype]) { t = t->type; if(t == T) goto bad; } s = t->sym; if(s == S) goto bad; } ss = globalsig(t); if(ss != S) return ss; e = "sigt"; if(t->etype == TINTER) e = "sigi"; if(block == 0) block = s->tblock; if(block > 1) { snprint(buf, sizeof(buf), "%s_%d%s", e, block, s->name); // record internal type for signature generation x = mal(sizeof(*x)); x->op = OTYPE; x->dsym = s; x->dtype = s->otype; x->forw = signatlist; x->block = block; signatlist = x; } else snprint(buf, sizeof(buf), "%s_%s", e, s->name); ss = pkglookup(buf, s->opackage); if(ss->oname == N) { ss->oname = newname(ss); ss->oname->type = types[TUINT8]; ss->oname->class = PEXTERN; ss->local = s->local; //print("signame: %d %lS\n", ss->local, ss); } return ss; bad: return S; } int bytearraysz(Type *t) { if(t == T) return -2; if(isptr[t->etype]) { t = t->type; if(t == T) return -2; } if(t->etype != TARRAY) return -2; if(!eqtype(t->type, types[TUINT8], 0)) return -2; return t->bound; // -1 is dyn, >=0 is fixed } int eqtype(Type *t1, Type *t2, int d) { if(d >= 10) return 1; if(t1 == t2) return 1; if(t1 == T || t2 == T) return 0; if(t1->etype != t2->etype) return 0; switch(t1->etype) { case TINTER: case TSTRUCT: t1 = t1->type; t2 = t2->type; for(;;) { if(!eqtype(t1, t2, d+1)) return 0; if(t1 == T) return 1; if(t1->nname != N && t1->nname->sym != S) { if(t2->nname == N || t2->nname->sym == S) return 0; if(strcmp(t1->nname->sym->name, t2->nname->sym->name) != 0) { // assigned names dont count if(t1->nname->sym->name[0] != '_' || t2->nname->sym->name[0] != '_') return 0; } } t1 = t1->down; t2 = t2->down; } return 1; case TFUNC: // Loop over structs: receiver, in, out. t1 = t1->type; t2 = t2->type; for(;;) { Type *ta, *tb; if(t1 == t2) break; if(t1 == T || t2 == T) return 0; if(t1->etype != TSTRUCT || t2->etype != TSTRUCT) return 0; // Loop over fields in structs, checking type only. ta = t1->type; tb = t2->type; while(ta != tb) { if(ta == T || tb == T) return 0; if(ta->etype != TFIELD || tb->etype != TFIELD) return 0; if(!eqtype(ta->type, tb->type, 0)) return 0; ta = ta->down; tb = tb->down; } t1 = t1->down; t2 = t2->down; } return 1; case TARRAY: if(t1->bound == t2->bound) break; return 0; } return eqtype(t1->type, t2->type, d+1); } 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: *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; } void argtype(Node *on, Type *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; return nt; } Type* deep(Type *t) { Type *nt, *xt; if(t == T) return T; switch(t->etype) { default: nt = t; // share from here down 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, "sys"); if(s == S || s->oname == N) fatal("looksys: cant find sys.%s", name); if(!copy) return s->oname; n = nod(0, N, N); *n = *s->oname; n->type = deep(s->oname->type); return n; } /* * are the arg names of two * functions the same. we know * that eqtype has been called * and has returned true. */ int eqargs(Type *t1, Type *t2) { if(t1 == t2) return 1; if(t1 == T || t2 == T) return 0; if(t1->etype != t2->etype) return 0; if(t1->etype != TFUNC) fatal("eqargs: oops %E", t1->etype); t1 = t1->type; t2 = t2->type; for(;;) { if(t1 == t2) break; if(!eqtype(t1, t2, 0)) return 0; t1 = t1->down; t2 = t2->down; } return 1; } uint32 typehash(Type *at, int d) { uint32 h; Type *t; if(at == T) return PRIME2; if(d >= 5) return PRIME3; if(at->recur) return 0; at->recur = 1; h = at->etype*PRIME4; switch(at->etype) { default: h += PRIME5 * typehash(at->type, d+1); break; case TINTER: // botch -- should be sorted? for(t=at->type; t!=T; t=t->down) h += PRIME6 * typehash(t, d+1); break; case TSTRUCT: for(t=at->type; t!=T; t=t->down) h += PRIME7 * typehash(t, d+1); break; case TFUNC: t = at->type; // skip this argument if(t != T) t = t->down; for(; t!=T; t=t->down) h += PRIME7 * typehash(t, d+1); break; } at->recur = 0; return h; } Type* ptrto(Type *t) { Type *t1; if(tptr == 0) fatal("ptrto: nil"); t1 = typ(tptr); t1->type = t; t1->width = types[tptr]->width; return t1; } Node* literal(int32 v) { Node *n; n = nod(OLITERAL, N, N); n->val.u.xval = mal(sizeof(*n->val.u.xval)); n->val.ctype = CTINT; mpmovecfix(n->val.u.xval, v); return n; } void frame(int context) { char *p; Dcl *d; int flag; p = "stack"; d = autodcl; if(context) { p = "external"; d = externdcl; } flag = 1; for(; d!=D; d=d->forw) { switch(d->op) { case ONAME: if(flag) print("--- %s frame ---\n", p); print("%O %S G%ld T\n", d->op, d->dsym, d->dnode->vargen, d->dnode->type); flag = 0; break; case OTYPE: if(flag) print("--- %s frame ---\n", p); print("%O %lT\n", d->op, d->dnode); 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 OLITERAL: case ONAME: ul = 0; goto out; case OCALL: case OCALLMETH: case OCALLINTER: ul = UINF; goto out; } ul = 0; if(n->left != N) ul = n->left->ullman; ur = 0; 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) { loop: switch(o) { case OCALL: if(tl == T || tr == T) break; if(isptr[tl->etype] && isptr[tr->etype]) { tl = tl->type; tr = tr->type; goto loop; } if(tl->etype != TFUNC || tr->etype != TFUNC) break; // if(eqtype(t1, t2, 0)) } yyerror("illegal types for operand: %O", o); if(tl != T) print(" %lT\n", tl); if(tr != T) print(" %lT\n", tr); } /* * this routine gets called to propagate the type * of the last decl up to the arguments before it. * (a,b,c int) comes out (a int, b int, c int). */ Node* cleanidlist(Node *na) { Node *last, *n; if(na->op != OLIST) return na; for(last=na; last->op == OLIST; last=last->right) ; if(last->op != ODCLFIELD) fatal("cleanidlist: %O", last->op); if(last->type == T) fatal("cleanidlist: no type"); for(n=na; n->op == OLIST; n=n->right) n->left->type = last->type; return na; } /* * 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; } /* * iterator to walk a list */ Node* listfirst(Iter *s, Node **nn) { Node *n; n = *nn; if(n == N) { s->done = 1; s->an = &s->n; s->n = N; return N; } if(n->op == OLIST) { s->done = 0; s->n = n; s->an = &n->left; return n->left; } s->done = 1; s->an = nn; return n; } Node* listnext(Iter *s) { Node *n, *r; if(s->done) { s->an = &s->n; s->n = N; return N; } n = s->n; r = n->right; if(r == N) { s->an = &s->n; s->n = N; return N; } if(r->op == OLIST) { s->n = r; s->an = &r->left; return r->left; } s->done = 1; s->an = &n->right; return n->right; } Type** getthis(Type *t) { if(t->etype != TFUNC) fatal("getthis: not a func %N", t); return &t->type; } Type** getoutarg(Type *t) { if(t->etype != TFUNC) fatal("getoutarg: not a func %N", t); return &t->type->down; } Type** getinarg(Type *t) { if(t->etype != TFUNC) fatal("getinarg: not a func %N", 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); } /* * code to resolve elided DOTs * in embedded types */ // search depth 0 -- // return count of fields+methods // found with a given name int lookdot0(Sym *s, Type *t) { 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) c++; } u = methtype(t); if(u != T) { for(f=u->method; f!=T; f=f->down) if(f->sym == s && f->embedded == 0) 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 *f, *u; int c, a; if(t->trecur) return 0; t->trecur = 1; if(d == 0) { c = lookdot0(s, t); 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); 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; Node *l; int c, d; walktype(n->left, Erv); t = n->left->type; if(t == T) return n; if(n->right->op != ONAME) return n; s = n->right->sym; if(s == S) return n; for(d=0; d 0) goto out; } return n; out: if(c > 1) yyerror("ambiguous DOT reference %S", s); // rebuild elided dots for(c=d-1; c>=0; c--) { n = nod(ODOT, n, n->right); n->left->right = newname(dotlist[c].field->sym); } 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; Symlink* link; }; static Symlink* slist; void expand0(Type *t) { Type *f, *u; Symlink *sl; u = t; if(isptr[u->etype]) u = u->type; u = methtype(t); if(u != T) { for(f=u->method; f!=T; f=f->down) { if(f->sym->uniq) continue; f->sym->uniq = 1; sl = mal(sizeof(*sl)); sl->field = f; sl->link = slist; slist = sl; } } } void expand1(Type *t, int d) { Type *f, *u; if(t->trecur) return; if(d == 0) return; t->trecur = 1; if(d != nelem(dotlist)-1) expand0(t); u = t; if(isptr[u->etype]) 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); } out: t->trecur = 0; } void expandmeth(Sym *s, Type *t) { Symlink *sl; Type *f; int c, d; if(s == S) return; if(t == T) return; //print("s=%S t=%lT\n", s, t); // generate all reachable methods slist = nil; expand1(t, nelem(dotlist)-1); // check each method to be uniquely reachable for(sl=slist; sl!=nil; sl=sl->link) { sl->field->sym->uniq = 0; for(d=0; dfield->sym, t, d); if(c == 0) continue; if(c == 1) sl->good = 1; break; } } for(sl=slist; sl!=nil; sl=sl->link) { if(sl->good) { //print(" %lT\n", sl->field); // add it to the base type method list f = typ(TFIELD); *f = *sl->field; f->embedded = 1; // needs a trampoline f->down = t->method; t->method = f; } } }