diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/gc | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-upstream/60.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/gc')
39 files changed, 26646 insertions, 0 deletions
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile new file mode 100644 index 000000000..0af7659e4 --- /dev/null +++ b/src/cmd/gc/Makefile @@ -0,0 +1,71 @@ +# 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 ../../Make.inc +O:=$(HOST_O) + +LIB=gc.a + +HFILES=\ + go.h\ + y.tab.h\ + md5.h\ + +YFILES=\ + go.y\ + +OFILES=\ + align.$O\ + bits.$O\ + builtin.$O\ + closure.$O\ + const.$O\ + dcl.$O\ + export.$O\ + gen.$O\ + init.$O\ + lex.$O\ + md5.$O\ + mparith1.$O\ + mparith2.$O\ + mparith3.$O\ + obj.$O\ + print.$O\ + range.$O\ + reflect.$O\ + select.$O\ + sinit.$O\ + subr.$O\ + swt.$O\ + typecheck.$O\ + unsafe.$O\ + walk.$O\ + y1.tab.$O\ + +NOINSTALL=1 +include ../../Make.clib + +install: $(LIB) + +y1.tab.c: y.tab.c # make yystate global, yytname mutable + cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/; s/char const \*yymsgp/char *yymsgp/' >y1.tab.c + +yerr.h: bisonerrors go.errors y.tab.h # y.tab.h rule generates y.output too + awk -f bisonerrors y.output go.errors >yerr.h + +subr.$O: yerr.h + +builtin.c: builtin.c.boot + cp builtin.c.boot builtin.c + +subr.$O: opnames.h + +opnames.h: mkopnames go.h + ./mkopnames go.h >opnames.h + +CLEANFILES+=*.[568] [568].out y1.tab.c yerr.h mkbuiltin1 builtin.c _builtin.c opnames.h + +mkbuiltin1: mkbuiltin1.$O + $(HOST_LD) -o $@ mkbuiltin1.$O -L"$(GOROOT)"/lib -lbio -l9 -lm $(HOST_LDFLAGS) + diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c new file mode 100644 index 000000000..14c1c4a8d --- /dev/null +++ b/src/cmd/gc/align.c @@ -0,0 +1,653 @@ +// 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" + +/* + * machine size and rounding + * alignment is dictated around + * the size of a pointer, set in betypeinit + * (see ../6g/galign.c). + */ + +static int defercalc; + +uint32 +rnd(uint32 o, uint32 r) +{ + if(r < 1 || r > 8 || (r&(r-1)) != 0) + fatal("rnd"); + return (o+r-1)&~(r-1); +} + +static void +offmod(Type *t) +{ + Type *f; + int32 o; + + o = 0; + for(f=t->type; f!=T; f=f->down) { + if(f->etype != TFIELD) + fatal("offmod: not TFIELD: %lT", f); + f->width = o; + o += widthptr; + if(o >= MAXWIDTH) { + yyerror("interface too large"); + o = widthptr; + } + } +} + +static vlong +widstruct(Type *errtype, Type *t, vlong o, int flag) +{ + Type *f; + int32 w, maxalign; + + maxalign = flag; + if(maxalign < 1) + maxalign = 1; + for(f=t->type; f!=T; f=f->down) { + if(f->etype != TFIELD) + fatal("widstruct: not TFIELD: %lT", f); + dowidth(f->type); + if(f->type->align > maxalign) + maxalign = f->type->align; + if(f->type->width < 0) + fatal("invalid width %lld", f->type->width); + w = f->type->width; + if(f->type->align > 0) + o = rnd(o, f->type->align); + f->width = o; // really offset for TFIELD + if(f->nname != N) { + // this same stackparam logic is in addrescapes + // in typecheck.c. usually addrescapes runs after + // widstruct, in which case we could drop this, + // but function closure functions are the exception. + if(f->nname->stackparam) { + f->nname->stackparam->xoffset = o; + f->nname->xoffset = 0; + } else + f->nname->xoffset = o; + } + o += w; + if(o >= MAXWIDTH) { + yyerror("type %lT too large", errtype); + o = 8; // small but nonzero + } + } + // final width is rounded + if(flag) + o = rnd(o, maxalign); + t->align = maxalign; + + // type width only includes back to first field's offset + if(t->type == T) + t->width = 0; + else + t->width = o - t->type->width; + return o; +} + +void +dowidth(Type *t) +{ + int32 et; + int64 w; + int lno; + Type *t1; + + if(widthptr == 0) + fatal("dowidth without betypeinit"); + + if(t == T) + return; + + if(t->width > 0) + return; + + if(t->width == -2) { + lno = lineno; + lineno = t->lineno; + yyerror("invalid recursive type %T", t); + t->width = 0; + lineno = lno; + return; + } + + // defer checkwidth calls until after we're done + defercalc++; + + lno = lineno; + lineno = t->lineno; + t->width = -2; + t->align = 0; + + et = t->etype; + switch(et) { + case TFUNC: + case TCHAN: + case TMAP: + case TSTRING: + break; + + default: + /* simtype == 0 during bootstrap */ + if(simtype[t->etype] != 0) + et = simtype[t->etype]; + break; + } + + w = 0; + switch(et) { + default: + fatal("dowidth: unknown type: %T", t); + break; + + /* compiler-specific stuff */ + case TINT8: + case TUINT8: + case TBOOL: // bool is int8 + w = 1; + break; + case TINT16: + case TUINT16: + w = 2; + break; + case TINT32: + case TUINT32: + case TFLOAT32: + w = 4; + break; + case TINT64: + case TUINT64: + case TFLOAT64: + case TCOMPLEX64: + w = 8; + t->align = widthptr; + break; + case TCOMPLEX128: + w = 16; + t->align = widthptr; + break; + case TPTR32: + w = 4; + checkwidth(t->type); + break; + case TPTR64: + w = 8; + checkwidth(t->type); + break; + case TUNSAFEPTR: + w = widthptr; + break; + case TINTER: // implemented as 2 pointers + w = 2*widthptr; + t->align = widthptr; + offmod(t); + break; + case TCHAN: // implemented as pointer + w = widthptr; + checkwidth(t->type); + + // make fake type to check later to + // trigger channel argument check. + t1 = typ(TCHANARGS); + t1->type = t; + checkwidth(t1); + break; + case TCHANARGS: + t1 = t->type; + dowidth(t->type); // just in case + if(t1->type->width >= (1<<16)) + yyerror("channel element type too large (>64kB)"); + t->width = 1; + break; + case TMAP: // implemented as pointer + w = widthptr; + checkwidth(t->type); + checkwidth(t->down); + break; + case TFORW: // should have been filled in + yyerror("invalid recursive type %T", t); + w = 1; // anything will do + break; + case TANY: + // dummy type; should be replaced before use. + if(!debug['A']) + fatal("dowidth any"); + w = 1; // anything will do + break; + case TSTRING: + if(sizeof_String == 0) + fatal("early dowidth string"); + w = sizeof_String; + t->align = widthptr; + break; + case TARRAY: + if(t->type == T) + break; + if(t->bound >= 0) { + uint64 cap; + + dowidth(t->type); + if(t->type->width != 0) { + cap = (MAXWIDTH-1) / t->type->width; + if(t->bound > cap) + yyerror("type %lT larger than address space", t); + } + w = t->bound * t->type->width; + t->align = t->type->align; + } + else if(t->bound == -1) { + w = sizeof_Array; + checkwidth(t->type); + t->align = widthptr; + } + else if(t->bound == -100) + yyerror("use of [...] array outside of array literal"); + else + fatal("dowidth %T", t); // probably [...]T + break; + + case TSTRUCT: + if(t->funarg) + fatal("dowidth fn struct %T", t); + w = widstruct(t, t, 0, 1); + break; + + case TFUNC: + // make fake type to check later to + // trigger function argument computation. + t1 = typ(TFUNCARGS); + t1->type = t; + checkwidth(t1); + + // width of func type is pointer + w = widthptr; + break; + + case TFUNCARGS: + // function is 3 cated structures; + // compute their widths as side-effect. + t1 = t->type; + w = widstruct(t->type, *getthis(t1), 0, 0); + w = widstruct(t->type, *getinarg(t1), w, widthptr); + w = widstruct(t->type, *getoutarg(t1), w, widthptr); + t1->argwid = w; + if(w%widthptr) + warn("bad type %T %d\n", t1, w); + t->align = 1; + break; + } + + t->width = w; + if(t->align == 0) { + if(w > 8 || (w&(w-1)) != 0) + fatal("invalid alignment for %T", t); + t->align = w; + } + lineno = lno; + + if(defercalc == 1) + resumecheckwidth(); + else + --defercalc; +} + +/* + * when a type's width should be known, we call checkwidth + * to compute it. during a declaration like + * + * type T *struct { next T } + * + * it is necessary to defer the calculation of the struct width + * until after T has been initialized to be a pointer to that struct. + * similarly, during import processing structs may be used + * before their definition. in those situations, calling + * defercheckwidth() stops width calculations until + * resumecheckwidth() is called, at which point all the + * checkwidths that were deferred are executed. + * dowidth should only be called when the type's size + * is needed immediately. checkwidth makes sure the + * size is evaluated eventually. + */ +typedef struct TypeList TypeList; +struct TypeList { + Type *t; + TypeList *next; +}; + +static TypeList *tlfree; +static TypeList *tlq; + +void +checkwidth(Type *t) +{ + TypeList *l; + + if(t == T) + return; + + // function arg structs should not be checked + // outside of the enclosing function. + if(t->funarg) + fatal("checkwidth %T", t); + + if(!defercalc) { + dowidth(t); + return; + } + if(t->deferwidth) + return; + t->deferwidth = 1; + + l = tlfree; + if(l != nil) + tlfree = l->next; + else + l = mal(sizeof *l); + + l->t = t; + l->next = tlq; + tlq = l; +} + +void +defercheckwidth(void) +{ + // we get out of sync on syntax errors, so don't be pedantic. + if(defercalc && nerrors == 0) + fatal("defercheckwidth"); + defercalc = 1; +} + +void +resumecheckwidth(void) +{ + TypeList *l; + + if(!defercalc) + fatal("resumecheckwidth"); + for(l = tlq; l != nil; l = tlq) { + l->t->deferwidth = 0; + tlq = l->next; + dowidth(l->t); + l->next = tlfree; + tlfree = l; + } + defercalc = 0; +} + +void +typeinit(void) +{ + int i, etype, sameas; + Type *t; + Sym *s, *s1; + + if(widthptr == 0) + fatal("typeinit before betypeinit"); + + for(i=0; i<NTYPE; i++) + simtype[i] = i; + + types[TPTR32] = typ(TPTR32); + dowidth(types[TPTR32]); + + types[TPTR64] = typ(TPTR64); + dowidth(types[TPTR64]); + + t = typ(TUNSAFEPTR); + types[TUNSAFEPTR] = t; + t->sym = pkglookup("Pointer", unsafepkg); + t->sym->def = typenod(t); + + dowidth(types[TUNSAFEPTR]); + + tptr = TPTR32; + if(widthptr == 8) + tptr = TPTR64; + + for(i=TINT8; i<=TUINT64; i++) + isint[i] = 1; + isint[TINT] = 1; + isint[TUINT] = 1; + isint[TUINTPTR] = 1; + + isfloat[TFLOAT32] = 1; + isfloat[TFLOAT64] = 1; + + iscomplex[TCOMPLEX64] = 1; + iscomplex[TCOMPLEX128] = 1; + + isptr[TPTR32] = 1; + isptr[TPTR64] = 1; + + isforw[TFORW] = 1; + + issigned[TINT] = 1; + issigned[TINT8] = 1; + issigned[TINT16] = 1; + issigned[TINT32] = 1; + issigned[TINT64] = 1; + + /* + * initialize okfor + */ + for(i=0; i<NTYPE; i++) { + if(isint[i] || i == TIDEAL) { + okforeq[i] = 1; + okforcmp[i] = 1; + okforarith[i] = 1; + okforadd[i] = 1; + okforand[i] = 1; + okforconst[i] = 1; + issimple[i] = 1; + minintval[i] = mal(sizeof(*minintval[i])); + maxintval[i] = mal(sizeof(*maxintval[i])); + } + if(isfloat[i]) { + okforeq[i] = 1; + okforcmp[i] = 1; + okforadd[i] = 1; + okforarith[i] = 1; + okforconst[i] = 1; + issimple[i] = 1; + minfltval[i] = mal(sizeof(*minfltval[i])); + maxfltval[i] = mal(sizeof(*maxfltval[i])); + } + if(iscomplex[i]) { + okforeq[i] = 1; + okforadd[i] = 1; + okforarith[i] = 1; + okforconst[i] = 1; + issimple[i] = 1; + } + } + + issimple[TBOOL] = 1; + + okforadd[TSTRING] = 1; + + okforbool[TBOOL] = 1; + + okforcap[TARRAY] = 1; + okforcap[TCHAN] = 1; + + okforconst[TBOOL] = 1; + okforconst[TSTRING] = 1; + + okforlen[TARRAY] = 1; + okforlen[TCHAN] = 1; + okforlen[TMAP] = 1; + okforlen[TSTRING] = 1; + + okforeq[TPTR32] = 1; + okforeq[TPTR64] = 1; + okforeq[TUNSAFEPTR] = 1; + okforeq[TINTER] = 1; + okforeq[TMAP] = 1; + okforeq[TCHAN] = 1; + okforeq[TFUNC] = 1; + okforeq[TSTRING] = 1; + okforeq[TBOOL] = 1; + okforeq[TARRAY] = 1; // refined in typecheck + + okforcmp[TSTRING] = 1; + + for(i=0; i<nelem(okfor); i++) + okfor[i] = okfornone; + + // binary + okfor[OADD] = okforadd; + okfor[OAND] = okforand; + okfor[OANDAND] = okforbool; + okfor[OANDNOT] = okforand; + okfor[ODIV] = okforarith; + okfor[OEQ] = okforeq; + okfor[OGE] = okforcmp; + okfor[OGT] = okforcmp; + okfor[OLE] = okforcmp; + okfor[OLT] = okforcmp; + okfor[OMOD] = okforand; + okfor[OMUL] = okforarith; + okfor[ONE] = okforeq; + okfor[OOR] = okforand; + okfor[OOROR] = okforbool; + okfor[OSUB] = okforarith; + okfor[OXOR] = okforand; + okfor[OLSH] = okforand; + okfor[ORSH] = okforand; + + // unary + okfor[OCOM] = okforand; + okfor[OMINUS] = okforarith; + okfor[ONOT] = okforbool; + okfor[OPLUS] = okforarith; + + // special + okfor[OCAP] = okforcap; + okfor[OLEN] = okforlen; + + // comparison + iscmp[OLT] = 1; + iscmp[OGT] = 1; + iscmp[OGE] = 1; + iscmp[OLE] = 1; + iscmp[OEQ] = 1; + iscmp[ONE] = 1; + + mpatofix(maxintval[TINT8], "0x7f"); + mpatofix(minintval[TINT8], "-0x80"); + mpatofix(maxintval[TINT16], "0x7fff"); + mpatofix(minintval[TINT16], "-0x8000"); + mpatofix(maxintval[TINT32], "0x7fffffff"); + mpatofix(minintval[TINT32], "-0x80000000"); + mpatofix(maxintval[TINT64], "0x7fffffffffffffff"); + mpatofix(minintval[TINT64], "-0x8000000000000000"); + + mpatofix(maxintval[TUINT8], "0xff"); + mpatofix(maxintval[TUINT16], "0xffff"); + mpatofix(maxintval[TUINT32], "0xffffffff"); + mpatofix(maxintval[TUINT64], "0xffffffffffffffff"); + + /* f is valid float if min < f < max. (min and max are not themselves valid.) */ + mpatoflt(maxfltval[TFLOAT32], "33554431p103"); /* 2^24-1 p (127-23) + 1/2 ulp*/ + mpatoflt(minfltval[TFLOAT32], "-33554431p103"); + mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970"); /* 2^53-1 p (1023-52) + 1/2 ulp */ + mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970"); + + maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32]; + minfltval[TCOMPLEX64] = minfltval[TFLOAT32]; + maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64]; + minfltval[TCOMPLEX128] = minfltval[TFLOAT64]; + + /* for walk to use in error messages */ + types[TFUNC] = functype(N, nil, nil); + + /* types used in front end */ + // types[TNIL] got set early in lexinit + types[TIDEAL] = typ(TIDEAL); + types[TINTER] = typ(TINTER); + + /* simple aliases */ + simtype[TMAP] = tptr; + simtype[TCHAN] = tptr; + simtype[TFUNC] = tptr; + simtype[TUNSAFEPTR] = tptr; + + /* pick up the backend typedefs */ + for(i=0; typedefs[i].name; i++) { + s = lookup(typedefs[i].name); + s1 = pkglookup(typedefs[i].name, builtinpkg); + + etype = typedefs[i].etype; + if(etype < 0 || etype >= nelem(types)) + fatal("typeinit: %s bad etype", s->name); + sameas = typedefs[i].sameas; + if(sameas < 0 || sameas >= nelem(types)) + fatal("typeinit: %s bad sameas", s->name); + simtype[etype] = sameas; + minfltval[etype] = minfltval[sameas]; + maxfltval[etype] = maxfltval[sameas]; + minintval[etype] = minintval[sameas]; + maxintval[etype] = maxintval[sameas]; + + t = types[etype]; + if(t != T) + fatal("typeinit: %s already defined", s->name); + + t = typ(etype); + t->sym = s; + + dowidth(t); + types[etype] = t; + s1->def = typenod(t); + } + + Array_array = rnd(0, widthptr); + Array_nel = rnd(Array_array+widthptr, types[TUINT32]->width); + Array_cap = rnd(Array_nel+types[TUINT32]->width, types[TUINT32]->width); + sizeof_Array = rnd(Array_cap+types[TUINT32]->width, widthptr); + + // string is same as slice wo the cap + sizeof_String = rnd(Array_nel+types[TUINT32]->width, widthptr); + + dowidth(types[TSTRING]); + dowidth(idealstring); +} + +/* + * compute total size of f's in/out arguments. + */ +int +argsize(Type *t) +{ + Iter save; + Type *fp; + int w, x; + + w = 0; + + fp = structfirst(&save, getoutarg(t)); + while(fp != T) { + x = fp->width + fp->type->width; + if(x > w) + w = x; + fp = structnext(&save); + } + + fp = funcfirst(&save, t); + while(fp != T) { + x = fp->width + fp->type->width; + if(x > w) + w = x; + fp = funcnext(&save); + } + + w = (w+widthptr-1) & ~(widthptr-1); + return w; +} diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors new file mode 100755 index 000000000..5110f5350 --- /dev/null +++ b/src/cmd/gc/bisonerrors @@ -0,0 +1,124 @@ +#!/usr/bin/awk -f +# Copyright 2010 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. + +# This program implements the core idea from +# +# Clinton L. Jeffery, Generating LR syntax error messages from examples, +# ACM TOPLAS 25(5) (September 2003). http://doi.acm.org/10.1145/937563.937566 +# +# It reads Bison's summary of a grammar followed by a file +# like go.errors, replacing lines beginning with % by the +# yystate and yychar that will be active when an error happens +# while parsing that line. +# +# Unlike the system described in the paper, the lines in go.errors +# give grammar symbol name lists, not actual program fragments. +# This is a little less programmer-friendly but doesn't require being +# able to run the text through lex.c. + +BEGIN{ + bison = 1 + grammar = 0 + states = 0 +} + +# In Grammar section of y.output, +# record lhs and length of rhs for each rule. +bison && /^Grammar/ { grammar = 1 } +bison && /^(Terminals|state 0)/ { grammar = 0 } +grammar && NF>0 { + if($2 != "|") { + r = $2 + sub(/:$/, "", r) + } + rulelhs[$1] = r + rulesize[$1] = NF-2 + if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") { + rulesize[$1] = 0 + } +} + +# In state dumps, record shift/reduce actions. +bison && /^state 0/ { grammar = 0; states = 1 } + +states && /^state / { state = $2 } +states { statetext[state] = statetext[state] $0 "\n" } + +states && / shift, and go to state/ { + n = nshift[state]++ + shift[state,n] = $7 + shifttoken[state,n] = $1 + next +} +states && / go to state/ { + n = nshift[state]++ + shift[state,n] = $5 + shifttoken[state,n] = $1 + next +} +states && / reduce using rule/ { + n = nreduce[state]++ + reduce[state,n] = $5 + reducetoken[state,n] = $1 + next +} + +# First // comment marks the beginning of the pattern file. +/^\/\// { bison = 0; grammar = 0; state = 0 } +bison { next } + +# Treat % as first field on line as introducing a pattern (token sequence). +# Run it through the LR machine and print the induced "yystate, yychar," +# at the point where the error happens. +$1 == "%" { + nstack = 0 + state = 0 + f = 2 + tok = "" + for(;;) { + if(tok == "" && f <= NF) { + tok = $f + f++ + } + found = 0 + for(j=0; j<nshift[state]; j++) { + if(shifttoken[state,j] == tok) { + # print "SHIFT " tok " " state " -> " shift[state,j] + stack[nstack++] = state + state = shift[state,j] + found = 1 + tok = "" + break + } + } + if(found) + continue + for(j=0; j<nreduce[state]; j++) { + if(reducetoken[state,j] == tok || reducetoken[state,j] == "$default") { + stack[nstack++] = state + rule = reduce[state,j] + nstack -= rulesize[rule] + state = stack[--nstack] + lhs = rulelhs[rule] + if(tok != "") + --f + tok = rulelhs[rule] + # print "REDUCE " nstack " " state " " tok " rule " rule " size " rulesize[rule] + found = 1 + break + } + } + if(found) + continue + + # No shift or reduce applied - found the error. + printf("\t%s, %s,\n", state, tok); + break + } + next +} + +# Print other lines verbatim. +{print} diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c new file mode 100644 index 000000000..7188ac411 --- /dev/null +++ b/src/cmd/gc/bits.c @@ -0,0 +1,161 @@ +// Inferno utils/cc/bits.c +// http://code.google.com/p/inferno-os/source/browse/utils/cc/bits.c +// +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "go.h" + +/* +Bits +bor(Bits a, Bits b) +{ + Bits c; + int i; + + for(i=0; i<BITS; i++) + c.b[i] = a.b[i] | b.b[i]; + return c; +} + +Bits +band(Bits a, Bits b) +{ + Bits c; + int i; + + for(i=0; i<BITS; i++) + c.b[i] = a.b[i] & b.b[i]; + return c; +} + +Bits +bnot(Bits a) +{ + Bits c; + int i; + + for(i=0; i<BITS; i++) + c.b[i] = ~a.b[i]; + return c; +} +*/ + +int +bany(Bits *a) +{ + int i; + + for(i=0; i<BITS; i++) + if(a->b[i]) + return 1; + return 0; +} + +/* +int +beq(Bits a, Bits b) +{ + int i; + + for(i=0; i<BITS; i++) + if(a.b[i] != b.b[i]) + return 0; + return 1; +} +*/ + +int +bnum(Bits a) +{ + int i; + int32 b; + + for(i=0; i<BITS; i++) + if(b = a.b[i]) + return 32*i + bitno(b); + fatal("bad in bnum"); + return 0; +} + +Bits +blsh(uint n) +{ + Bits c; + + c = zbits; + c.b[n/32] = 1L << (n%32); + return c; +} + +/* +int +bset(Bits a, uint n) +{ + if(a.b[n/32] & (1L << (n%32))) + return 1; + return 0; +} +*/ + +int +bitno(int32 b) +{ + int i; + + for(i=0; i<32; i++) + if(b & (1L<<i)) + return i; + fatal("bad in bitno"); + return 0; +} + +int +Qconv(Fmt *fp) +{ + Bits bits; + int i, first; + + first = 1; + bits = va_arg(fp->args, Bits); + while(bany(&bits)) { + i = bnum(bits); + if(first) + first = 0; + else + fmtprint(fp, " "); + if(var[i].sym == S) + fmtprint(fp, "$%lld", var[i].offset); + else { + fmtprint(fp, var[i].sym->name); + if(var[i].offset != 0) + fmtprint(fp, "%+d", var[i].offset); + } + bits.b[i/32] &= ~(1L << (i%32)); + } + return 0; +} diff --git a/src/cmd/gc/builtin.c.boot b/src/cmd/gc/builtin.c.boot new file mode 100644 index 000000000..190c56008 --- /dev/null +++ b/src/cmd/gc/builtin.c.boot @@ -0,0 +1,114 @@ +char *runtimeimport = + "package runtime\n" + "import runtime \"runtime\"\n" + "func \"\".new (? int32) *any\n" + "func \"\".panicindex ()\n" + "func \"\".panicslice ()\n" + "func \"\".throwreturn ()\n" + "func \"\".throwinit ()\n" + "func \"\".panicwrap (? string, ? string, ? string)\n" + "func \"\".panic (? interface { })\n" + "func \"\".recover (? *int32) interface { }\n" + "func \"\".printbool (? bool)\n" + "func \"\".printfloat (? float64)\n" + "func \"\".printint (? int64)\n" + "func \"\".printuint (? uint64)\n" + "func \"\".printcomplex (? complex128)\n" + "func \"\".printstring (? string)\n" + "func \"\".printpointer (? any)\n" + "func \"\".printiface (? any)\n" + "func \"\".printeface (? any)\n" + "func \"\".printslice (? any)\n" + "func \"\".printnl ()\n" + "func \"\".printsp ()\n" + "func \"\".goprintf ()\n" + "func \"\".concatstring ()\n" + "func \"\".append ()\n" + "func \"\".appendslice (typ *uint8, x any, y []any) any\n" + "func \"\".cmpstring (? string, ? string) int\n" + "func \"\".slicestring (? string, ? int, ? int) string\n" + "func \"\".slicestring1 (? string, ? int) string\n" + "func \"\".intstring (? int64) string\n" + "func \"\".slicebytetostring (? []uint8) string\n" + "func \"\".sliceinttostring (? []int) string\n" + "func \"\".stringtoslicebyte (? string) []uint8\n" + "func \"\".stringtosliceint (? string) []int\n" + "func \"\".stringiter (? string, ? int) int\n" + "func \"\".stringiter2 (? string, ? int) (retk int, retv int)\n" + "func \"\".slicecopy (to any, fr any, wid uint32) int\n" + "func \"\".slicestringcopy (to any, fr any) int\n" + "func \"\".convI2E (elem any) any\n" + "func \"\".convI2I (typ *uint8, elem any) any\n" + "func \"\".convT2E (typ *uint8, elem any) any\n" + "func \"\".convT2I (typ *uint8, typ2 *uint8, elem any) any\n" + "func \"\".assertE2E (typ *uint8, iface any) any\n" + "func \"\".assertE2E2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".assertE2I (typ *uint8, iface any) any\n" + "func \"\".assertE2I2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".assertE2T (typ *uint8, iface any) any\n" + "func \"\".assertE2T2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".assertI2E (typ *uint8, iface any) any\n" + "func \"\".assertI2E2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".assertI2I (typ *uint8, iface any) any\n" + "func \"\".assertI2I2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".assertI2T (typ *uint8, iface any) any\n" + "func \"\".assertI2T2 (typ *uint8, iface any) (ret any, ok bool)\n" + "func \"\".ifaceeq (i1 any, i2 any) bool\n" + "func \"\".efaceeq (i1 any, i2 any) bool\n" + "func \"\".ifacethash (i1 any) uint32\n" + "func \"\".efacethash (i1 any) uint32\n" + "func \"\".makemap (mapType *uint8, hint int64) map[any] any\n" + "func \"\".mapaccess1 (mapType *uint8, hmap map[any] any, key any) any\n" + "func \"\".mapaccess2 (mapType *uint8, hmap map[any] any, key any) (val any, pres bool)\n" + "func \"\".mapassign1 (mapType *uint8, hmap map[any] any, key any, val any)\n" + "func \"\".mapassign2 (mapType *uint8, hmap map[any] any, key any, val any, pres bool)\n" + "func \"\".mapiterinit (mapType *uint8, hmap map[any] any, hiter *any)\n" + "func \"\".mapiternext (hiter *any)\n" + "func \"\".mapiter1 (hiter *any) any\n" + "func \"\".mapiter2 (hiter *any) (key any, val any)\n" + "func \"\".makechan (chanType *uint8, hint int64) chan any\n" + "func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n" + "func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n" + "func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n" + "func \"\".closechan (hchan any)\n" + "func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n" + "func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n" + "func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n" + "func \"\".newselect (size int) *uint8\n" + "func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n" + "func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n" + "func \"\".selectrecv2 (sel *uint8, hchan <-chan any, elem *any, received *bool) bool\n" + "func \"\".selectdefault (sel *uint8) bool\n" + "func \"\".selectgo (sel *uint8)\n" + "func \"\".block ()\n" + "func \"\".makeslice (typ *uint8, nel int64, cap int64) []any\n" + "func \"\".growslice (typ *uint8, old []any, n int64) []any\n" + "func \"\".sliceslice1 (old []any, lb uint64, width uint64) []any\n" + "func \"\".sliceslice (old []any, lb uint64, hb uint64, width uint64) []any\n" + "func \"\".slicearray (old *any, nel uint64, lb uint64, hb uint64, width uint64) []any\n" + "func \"\".closure ()\n" + "func \"\".int64div (? int64, ? int64) int64\n" + "func \"\".uint64div (? uint64, ? uint64) uint64\n" + "func \"\".int64mod (? int64, ? int64) int64\n" + "func \"\".uint64mod (? uint64, ? uint64) uint64\n" + "func \"\".float64toint64 (? float64) int64\n" + "func \"\".float64touint64 (? float64) uint64\n" + "func \"\".int64tofloat64 (? int64) float64\n" + "func \"\".uint64tofloat64 (? uint64) float64\n" + "func \"\".complex128div (num complex128, den complex128) complex128\n" + "\n" + "$$\n"; +char *unsafeimport = + "package unsafe\n" + "import runtime \"runtime\"\n" + "type \"\".Pointer uintptr\n" + "func \"\".Offsetof (? any) uintptr\n" + "func \"\".Sizeof (? any) uintptr\n" + "func \"\".Alignof (? any) uintptr\n" + "func \"\".Typeof (i interface { }) interface { }\n" + "func \"\".Reflect (i interface { }) (typ interface { }, addr \"\".Pointer)\n" + "func \"\".Unreflect (typ interface { }, addr \"\".Pointer) interface { }\n" + "func \"\".New (typ interface { }) \"\".Pointer\n" + "func \"\".NewArray (typ interface { }, n int) \"\".Pointer\n" + "\n" + "$$\n"; diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c new file mode 100644 index 000000000..1261eefb7 --- /dev/null +++ b/src/cmd/gc/closure.c @@ -0,0 +1,252 @@ +// 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. + +/* + * function literals aka closures + */ + +#include "go.h" + +void +closurehdr(Node *ntype) +{ + Node *n, *name, *a; + NodeList *l; + + n = nod(OCLOSURE, N, N); + n->ntype = ntype; + n->funcdepth = funcdepth; + + funchdr(n); + + // steal ntype's argument names and + // leave a fresh copy in their place. + // references to these variables need to + // refer to the variables in the external + // function declared below; see walkclosure. + n->list = ntype->list; + n->rlist = ntype->rlist; + ntype->list = nil; + ntype->rlist = nil; + for(l=n->list; l; l=l->next) { + name = l->n->left; + if(name) + name = newname(name->sym); + a = nod(ODCLFIELD, name, l->n->right); + a->isddd = l->n->isddd; + if(name) + name->isddd = a->isddd; + ntype->list = list(ntype->list, a); + } + for(l=n->rlist; l; l=l->next) { + name = l->n->left; + if(name) + name = newname(name->sym); + ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right)); + } +} + +Node* +closurebody(NodeList *body) +{ + Node *func, *v; + NodeList *l; + + if(body == nil) + body = list1(nod(OEMPTY, N, N)); + + func = curfn; + l = func->dcl; + func->nbody = body; + funcbody(func); + + // closure-specific variables are hanging off the + // ordinary ones in the symbol table; see oldname. + // unhook them. + // make the list of pointers for the closure call. + for(l=func->cvars; l; l=l->next) { + v = l->n; + v->closure->closure = v->outer; + v->heapaddr = nod(OADDR, oldname(v->sym), N); + } + + return func; +} + +void +typecheckclosure(Node *func, int top) +{ + Node *oldfn; + NodeList *l; + Node *v; + + oldfn = curfn; + typecheck(&func->ntype, Etype); + func->type = func->ntype->type; + if(curfn == nil) { + xtop = list(xtop, func); + return; + } + + if(func->type != T) { + curfn = func; + typechecklist(func->nbody, Etop); + curfn = oldfn; + } + + // type check the & of closed variables outside the closure, + // so that the outer frame also grabs them and knows they + // escape. + func->enter = nil; + for(l=func->cvars; l; l=l->next) { + v = l->n; + if(v->type == T) { + // if v->type is nil, it means v looked like it was + // going to be used in the closure but wasn't. + // this happens because when parsing a, b, c := f() + // the a, b, c gets parsed as references to older + // a, b, c before the parser figures out this is a + // declaration. + v->op = 0; + continue; + } + // For a closure that is called in place, but not + // inside a go statement, avoid moving variables to the heap. + if ((top & (Ecall|Eproc)) == Ecall) + v->heapaddr->etype = 1; + typecheck(&v->heapaddr, Erv); + func->enter = list(func->enter, v->heapaddr); + v->heapaddr = N; + } +} + +static Node* +makeclosure(Node *func, NodeList **init, int nowrap) +{ + Node *xtype, *v, *addr, *xfunc; + NodeList *l; + static int closgen; + char *p; + + /* + * wrap body in external function + * with extra closure parameters. + */ + xtype = nod(OTFUNC, N, N); + + // each closure variable has a corresponding + // address parameter. + for(l=func->cvars; l; l=l->next) { + v = l->n; + if(v->op == 0) + continue; + addr = nod(ONAME, N, N); + p = smprint("&%s", v->sym->name); + addr->sym = lookup(p); + free(p); + addr->ntype = nod(OIND, typenod(v->type), N); + addr->class = PPARAM; + addr->addable = 1; + addr->ullman = 1; + + v->heapaddr = addr; + + xtype->list = list(xtype->list, nod(ODCLFIELD, addr, addr->ntype)); + } + + // then a dummy arg where the closure's caller pc sits + if (!nowrap) + xtype->list = list(xtype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + + // then the function arguments + xtype->list = concat(xtype->list, func->list); + xtype->rlist = concat(xtype->rlist, func->rlist); + + // create the function + xfunc = nod(ODCLFUNC, N, N); + snprint(namebuf, sizeof namebuf, "_func_%.3d", ++closgen); + xfunc->nname = newname(lookup(namebuf)); + xfunc->nname->ntype = xtype; + xfunc->nname->defn = xfunc; + declare(xfunc->nname, PFUNC); + xfunc->nname->funcdepth = func->funcdepth; + xfunc->funcdepth = func->funcdepth; + xfunc->nbody = func->nbody; + xfunc->dcl = func->dcl; + if(xfunc->nbody == nil) + fatal("empty body - won't generate any code"); + typecheck(&xfunc, Etop); + closures = list(closures, xfunc); + + return xfunc; +} + +Node* +walkclosure(Node *func, NodeList **init) +{ + int narg; + Node *xtype, *xfunc, *call, *clos; + NodeList *l, *in; + + /* + * wrap body in external function + * with extra closure parameters. + */ + + // create the function + xfunc = makeclosure(func, init, 0); + xtype = xfunc->nname->ntype; + + // prepare call of sys.closure that turns external func into func literal value. + clos = syslook("closure", 1); + clos->type = T; + clos->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // siz + in = list(in, nod(ODCLFIELD, N, xtype)); + narg = 0; + for(l=func->cvars; l; l=l->next) { + if(l->n->op == 0) + continue; + narg++; + in = list(in, nod(ODCLFIELD, N, l->n->heapaddr->ntype)); + } + clos->ntype->list = in; + clos->ntype->rlist = list1(nod(ODCLFIELD, N, typenod(func->type))); + typecheck(&clos, Erv); + + call = nod(OCALL, clos, N); + if(narg*widthptr > 100) + yyerror("closure needs too many variables; runtime will reject it"); + in = list1(nodintconst(narg*widthptr)); + in = list(in, xfunc->nname); + in = concat(in, func->enter); + call->list = in; + + typecheck(&call, Erv); + walkexpr(&call, init); + return call; +} + +// Special case for closures that get called in place. +// Optimize runtime.closure(X, __func__xxxx_, .... ) away +// to __func__xxxx_(Y ....). +// On entry, expect n->op == OCALL, n->left->op == OCLOSURE. +void +walkcallclosure(Node *n, NodeList **init) +{ + if (n->op != OCALLFUNC || n->left->op != OCLOSURE) { + dump("walkcallclosure", n); + fatal("abuse of walkcallclosure"); + } + + // New arg list for n. First the closure-args + // and then the original parameter list. + n->list = concat(n->left->enter, n->list); + n->left = makeclosure(n->left, init, 1)->nname; + dowidth(n->left->type); + n->type = getoutargx(n->left->type); + // for a single valued function, pull the field type out of the struct + if (n->type && n->type->type && !n->type->type->down) + n->type = n->type->type->type; +} diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c new file mode 100644 index 000000000..36a64cb97 --- /dev/null +++ b/src/cmd/gc/const.c @@ -0,0 +1,1299 @@ +// 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" +#define TUP(x,y) (((x)<<16)|(y)) + +static Val tocplx(Val); +static Val toflt(Val); +static Val tostr(Val); +static Val copyval(Val); +static void cmplxmpy(Mpcplx*, Mpcplx*); +static void cmplxdiv(Mpcplx*, Mpcplx*); + +/* + * truncate float literal fv to 32-bit or 64-bit precision + * according to type; return truncated value. + */ +Mpflt* +truncfltlit(Mpflt *oldv, Type *t) +{ + double d; + float f; + Mpflt *fv; + + if(t == T) + return oldv; + + fv = mal(sizeof *fv); + *fv = *oldv; + + // convert large precision literal floating + // into limited precision (float64 or float32) + // botch -- this assumes that compiler fp + // has same precision as runtime fp + switch(t->etype) { + case TFLOAT64: + d = mpgetflt(fv); + mpmovecflt(fv, d); + break; + + case TFLOAT32: + d = mpgetflt(fv); + f = d; + d = f; + mpmovecflt(fv, d); + break; + } + return fv; +} + +/* + * convert n, if literal, to type t. + * implicit conversion. + */ +void +convlit(Node **np, Type *t) +{ + convlit1(np, t, 0); +} + +/* + * convert n, if literal, to type t. + * return a new node if necessary + * (if n is a named constant, can't edit n->type directly). + */ +void +convlit1(Node **np, Type *t, int explicit) +{ + int ct, et; + Node *n, *nn; + + n = *np; + if(n == N || t == T || n->type == T || isideal(t) || n->type == t) + return; + if(!explicit && !isideal(n->type)) + return; + + if(n->op == OLITERAL) { + nn = nod(OXXX, N, N); + *nn = *n; + n = nn; + *np = n; + } + + switch(n->op) { + default: + if(n->type->etype == TIDEAL) { + convlit(&n->left, t); + convlit(&n->right, t); + n->type = t; + } + return; + case OLITERAL: + // target is invalid type for a constant? leave alone. + if(!okforconst[t->etype] && n->type->etype != TNIL) { + defaultlit(&n, T); + *np = n; + return; + } + break; + case OLSH: + case ORSH: + convlit1(&n->left, t, explicit && isideal(n->left->type)); + t = n->left->type; + if(t != T && t->etype == TIDEAL && n->val.ctype != CTINT) + n->val = toint(n->val); + if(t != T && !isint[t->etype]) { + yyerror("invalid operation: %#N (shift of type %T)", n, t); + t = T; + } + n->type = t; + return; + } + + // avoided repeated calculations, errors + if(eqtype(n->type, t)) + return; + + ct = consttype(n); + if(ct < 0) + goto bad; + + et = t->etype; + if(et == TINTER) { + if(ct == CTNIL && n->type == types[TNIL]) { + n->type = t; + return; + } + defaultlit(np, T); + return; + } + + switch(ct) { + default: + goto bad; + + case CTNIL: + switch(et) { + default: + n->type = T; + goto bad; + + case TSTRING: + // let normal conversion code handle it + return; + + case TARRAY: + if(!isslice(t)) + goto bad; + break; + + case TPTR32: + case TPTR64: + case TINTER: + case TMAP: + case TCHAN: + case TFUNC: + case TUNSAFEPTR: + break; + } + break; + + case CTSTR: + case CTBOOL: + if(et != n->type->etype) + goto bad; + break; + + case CTINT: + case CTFLT: + case CTCPLX: + ct = n->val.ctype; + if(isint[et]) { + switch(ct) { + default: + goto bad; + case CTCPLX: + case CTFLT: + n->val = toint(n->val); + // flowthrough + case CTINT: + overflow(n->val, t); + break; + } + } else + if(isfloat[et]) { + switch(ct) { + default: + goto bad; + case CTCPLX: + case CTINT: + n->val = toflt(n->val); + // flowthrough + case CTFLT: + overflow(n->val, t); + n->val.u.fval = truncfltlit(n->val.u.fval, t); + break; + } + } else + if(iscomplex[et]) { + switch(ct) { + default: + goto bad; + case CTFLT: + case CTINT: + n->val = tocplx(n->val); + break; + case CTCPLX: + overflow(n->val, t); + break; + } + } else + if(et == TSTRING && ct == CTINT && explicit) + n->val = tostr(n->val); + else + goto bad; + break; + } + n->type = t; + return; + +bad: + if(!n->diag) { + yyerror("cannot convert %#N to type %T", n, t); + n->diag = 1; + } + if(isideal(n->type)) { + defaultlit(&n, T); + *np = n; + } + return; +} + +static Val +copyval(Val v) +{ + Mpint *i; + Mpflt *f; + Mpcplx *c; + + switch(v.ctype) { + case CTINT: + i = mal(sizeof(*i)); + mpmovefixfix(i, v.u.xval); + v.u.xval = i; + break; + case CTFLT: + f = mal(sizeof(*f)); + mpmovefltflt(f, v.u.fval); + v.u.fval = f; + break; + case CTCPLX: + c = mal(sizeof(*c)); + mpmovefltflt(&c->real, &v.u.cval->real); + mpmovefltflt(&c->imag, &v.u.cval->imag); + v.u.cval = c; + break; + } + return v; +} + +static Val +tocplx(Val v) +{ + Mpcplx *c; + + switch(v.ctype) { + case CTINT: + c = mal(sizeof(*c)); + mpmovefixflt(&c->real, v.u.xval); + mpmovecflt(&c->imag, 0.0); + v.ctype = CTCPLX; + v.u.cval = c; + break; + case CTFLT: + c = mal(sizeof(*c)); + mpmovefltflt(&c->real, v.u.fval); + mpmovecflt(&c->imag, 0.0); + v.ctype = CTCPLX; + v.u.cval = c; + break; + } + return v; +} + +static Val +toflt(Val v) +{ + Mpflt *f; + + switch(v.ctype) { + case CTINT: + f = mal(sizeof(*f)); + mpmovefixflt(f, v.u.xval); + v.ctype = CTFLT; + v.u.fval = f; + break; + case CTCPLX: + f = mal(sizeof(*f)); + mpmovefltflt(f, &v.u.cval->real); + if(mpcmpfltc(&v.u.cval->imag, 0) != 0) + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); + v.ctype = CTFLT; + v.u.fval = f; + break; + } + return v; +} + +Val +toint(Val v) +{ + Mpint *i; + + switch(v.ctype) { + case CTFLT: + i = mal(sizeof(*i)); + if(mpmovefltfix(i, v.u.fval) < 0) + yyerror("constant %#F truncated to integer", v.u.fval); + v.ctype = CTINT; + v.u.xval = i; + break; + case CTCPLX: + i = mal(sizeof(*i)); + if(mpmovefltfix(i, &v.u.cval->real) < 0) + yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag); + if(mpcmpfltc(&v.u.cval->imag, 0) != 0) + yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag); + v.ctype = CTINT; + v.u.xval = i; + break; + } + return v; +} + +void +overflow(Val v, Type *t) +{ + // v has already been converted + // to appropriate form for t. + if(t == T || t->etype == TIDEAL) + return; + switch(v.ctype) { + case CTINT: + if(!isint[t->etype]) + fatal("overflow: %T integer constant", t); + if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 || + mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0) + yyerror("constant %B overflows %T", v.u.xval, t); + break; + case CTFLT: + if(!isfloat[t->etype]) + fatal("overflow: %T floating-point constant", t); + if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 || + mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0) + yyerror("constant %#F overflows %T", v.u.fval, t); + break; + case CTCPLX: + if(!iscomplex[t->etype]) + fatal("overflow: %T complex constant", t); + if(mpcmpfltflt(&v.u.cval->real, minfltval[t->etype]) <= 0 || + mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 || + mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 || + mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0) + yyerror("constant %#F overflows %T", v.u.fval, t); + break; + } +} + +static Val +tostr(Val v) +{ + Rune rune; + int l; + Strlit *s; + + switch(v.ctype) { + case CTINT: + if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 || + mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0) + yyerror("overflow in int -> string"); + rune = mpgetfix(v.u.xval); + l = runelen(rune); + s = mal(sizeof(*s)+l); + s->len = l; + runetochar((char*)s->s, &rune); + memset(&v, 0, sizeof v); + v.ctype = CTSTR; + v.u.sval = s; + break; + + case CTFLT: + yyerror("no float -> string"); + + case CTNIL: + memset(&v, 0, sizeof v); + v.ctype = CTSTR; + v.u.sval = mal(sizeof *s); + break; + } + return v; +} + +int +consttype(Node *n) +{ + if(n == N || n->op != OLITERAL) + return -1; + return n->val.ctype; +} + +int +isconst(Node *n, int ct) +{ + return consttype(n) == ct; +} + +/* + * if n is constant, rewrite as OLITERAL node. + */ +void +evconst(Node *n) +{ + Node *nl, *nr; + int32 len; + Strlit *str; + int wl, wr, lno, et; + Val v, rv; + Mpint b; + + // pick off just the opcodes that can be + // constant evaluated. + switch(n->op) { + default: + return; + case OADD: + case OADDSTR: + case OAND: + case OANDAND: + case OANDNOT: + case OARRAYBYTESTR: + case OCOM: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLSH: + case OLT: + case OMINUS: + case OMOD: + case OMUL: + case ONE: + case ONOT: + case OOR: + case OOROR: + case OPLUS: + case ORSH: + case OSUB: + case OXOR: + break; + case OCONV: + if(n->type == T) + return; + if(!okforconst[n->type->etype] && n->type->etype != TNIL) + return; + break; + } + + nl = n->left; + if(nl == N || nl->type == T) + return; + if(consttype(nl) < 0) + return; + wl = nl->type->etype; + if(isint[wl] || isfloat[wl] || iscomplex[wl]) + wl = TIDEAL; + + nr = n->right; + if(nr == N) + goto unary; + if(nr->type == T) + return; + if(consttype(nr) < 0) + return; + wr = nr->type->etype; + if(isint[wr] || isfloat[wr] || iscomplex[wr]) + wr = TIDEAL; + + // check for compatible general types (numeric, string, etc) + if(wl != wr) + goto illegal; + + // check for compatible types. + switch(n->op) { + default: + // ideal const mixes with anything but otherwise must match. + if(nl->type->etype != TIDEAL) { + defaultlit(&nr, nl->type); + n->right = nr; + } + if(nr->type->etype != TIDEAL) { + defaultlit(&nl, nr->type); + n->left = nl; + } + if(nl->type->etype != nr->type->etype) + goto illegal; + break; + + case OLSH: + case ORSH: + // right must be unsigned. + // left can be ideal. + defaultlit(&nr, types[TUINT]); + n->right = nr; + if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype])) + goto illegal; + nl->val = toint(nl->val); + nr->val = toint(nr->val); + break; + } + + // copy numeric value to avoid modifying + // n->left, in case someone still refers to it (e.g. iota). + v = nl->val; + if(wl == TIDEAL) + v = copyval(v); + + rv = nr->val; + + // convert to common ideal + if(v.ctype == CTCPLX || rv.ctype == CTCPLX) { + v = tocplx(v); + rv = tocplx(rv); + } + if(v.ctype == CTFLT || rv.ctype == CTFLT) { + v = toflt(v); + rv = toflt(rv); + } + if(v.ctype != rv.ctype) { + // Use of undefined name as constant? + if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0) + return; + fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype); + } + + // run op + switch(TUP(n->op, v.ctype)) { + default: + illegal: + if(!n->diag) { + yyerror("illegal constant expression: %T %O %T", + nl->type, n->op, nr->type); + n->diag = 1; + } + return; + + case TUP(OADD, CTINT): + mpaddfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OSUB, CTINT): + mpsubfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OMUL, CTINT): + mpmulfixfix(v.u.xval, rv.u.xval); + break; + case TUP(ODIV, CTINT): + if(mpcmpfixc(rv.u.xval, 0) == 0) { + yyerror("division by zero"); + mpmovecfix(v.u.xval, 1); + break; + } + mpdivfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OMOD, CTINT): + if(mpcmpfixc(rv.u.xval, 0) == 0) { + yyerror("division by zero"); + mpmovecfix(v.u.xval, 1); + break; + } + mpmodfixfix(v.u.xval, rv.u.xval); + break; + + case TUP(OLSH, CTINT): + mplshfixfix(v.u.xval, rv.u.xval); + break; + case TUP(ORSH, CTINT): + mprshfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OOR, CTINT): + mporfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OAND, CTINT): + mpandfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OANDNOT, CTINT): + mpandnotfixfix(v.u.xval, rv.u.xval); + break; + case TUP(OXOR, CTINT): + mpxorfixfix(v.u.xval, rv.u.xval); + break; + + case TUP(OADD, CTFLT): + mpaddfltflt(v.u.fval, rv.u.fval); + break; + case TUP(OSUB, CTFLT): + mpsubfltflt(v.u.fval, rv.u.fval); + break; + case TUP(OMUL, CTFLT): + mpmulfltflt(v.u.fval, rv.u.fval); + break; + case TUP(ODIV, CTFLT): + if(mpcmpfltc(rv.u.fval, 0) == 0) { + yyerror("division by zero"); + mpmovecflt(v.u.fval, 1.0); + break; + } + mpdivfltflt(v.u.fval, rv.u.fval); + break; + + case TUP(OADD, CTCPLX): + mpaddfltflt(&v.u.cval->real, &rv.u.cval->real); + mpaddfltflt(&v.u.cval->imag, &rv.u.cval->imag); + break; + case TUP(OSUB, CTCPLX): + mpsubfltflt(&v.u.cval->real, &rv.u.cval->real); + mpsubfltflt(&v.u.cval->imag, &rv.u.cval->imag); + break; + case TUP(OMUL, CTCPLX): + cmplxmpy(v.u.cval, rv.u.cval); + break; + case TUP(ODIV, CTCPLX): + if(mpcmpfltc(&rv.u.cval->real, 0) == 0 && + mpcmpfltc(&rv.u.cval->imag, 0) == 0) { + yyerror("complex division by zero"); + mpmovecflt(&rv.u.cval->real, 1.0); + mpmovecflt(&rv.u.cval->imag, 0.0); + break; + } + cmplxdiv(v.u.cval, rv.u.cval); + break; + + case TUP(OEQ, CTNIL): + goto settrue; + case TUP(ONE, CTNIL): + goto setfalse; + + case TUP(OEQ, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0) + goto settrue; + goto setfalse; + case TUP(ONE, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0) + goto settrue; + goto setfalse; + case TUP(OLT, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0) + goto settrue; + goto setfalse; + case TUP(OLE, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0) + goto settrue; + goto setfalse; + case TUP(OGE, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0) + goto settrue; + goto setfalse; + case TUP(OGT, CTINT): + if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0) + goto settrue; + goto setfalse; + + case TUP(OEQ, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) == 0) + goto settrue; + goto setfalse; + case TUP(ONE, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) != 0) + goto settrue; + goto setfalse; + case TUP(OLT, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) < 0) + goto settrue; + goto setfalse; + case TUP(OLE, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) <= 0) + goto settrue; + goto setfalse; + case TUP(OGE, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) >= 0) + goto settrue; + goto setfalse; + case TUP(OGT, CTFLT): + if(mpcmpfltflt(v.u.fval, rv.u.fval) > 0) + goto settrue; + goto setfalse; + + case TUP(OEQ, CTCPLX): + if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) == 0 && + mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) == 0) + goto settrue; + goto setfalse; + case TUP(ONE, CTCPLX): + if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) != 0 || + mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) != 0) + goto settrue; + goto setfalse; + + case TUP(OEQ, CTSTR): + if(cmpslit(nl, nr) == 0) + goto settrue; + goto setfalse; + case TUP(ONE, CTSTR): + if(cmpslit(nl, nr) != 0) + goto settrue; + goto setfalse; + case TUP(OLT, CTSTR): + if(cmpslit(nl, nr) < 0) + goto settrue; + goto setfalse; + case TUP(OLE, CTSTR): + if(cmpslit(nl, nr) <= 0) + goto settrue; + goto setfalse; + case TUP(OGE, CTSTR): + if(cmpslit(nl, nr) >= 0l) + goto settrue; + goto setfalse; + case TUP(OGT, CTSTR): + if(cmpslit(nl, nr) > 0) + goto settrue; + goto setfalse; + case TUP(OADDSTR, CTSTR): + len = v.u.sval->len + rv.u.sval->len; + str = mal(sizeof(*str) + len); + str->len = len; + memcpy(str->s, v.u.sval->s, v.u.sval->len); + memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len); + str->len = len; + v.u.sval = str; + break; + + case TUP(OOROR, CTBOOL): + if(v.u.bval || rv.u.bval) + goto settrue; + goto setfalse; + case TUP(OANDAND, CTBOOL): + if(v.u.bval && rv.u.bval) + goto settrue; + goto setfalse; + case TUP(OEQ, CTBOOL): + if(v.u.bval == rv.u.bval) + goto settrue; + goto setfalse; + case TUP(ONE, CTBOOL): + if(v.u.bval != rv.u.bval) + goto settrue; + goto setfalse; + } + goto ret; + +unary: + // copy numeric value to avoid modifying + // nl, in case someone still refers to it (e.g. iota). + v = nl->val; + if(wl == TIDEAL) + v = copyval(v); + + switch(TUP(n->op, v.ctype)) { + default: + if(!n->diag) { + yyerror("illegal constant expression %O %T", n->op, nl->type); + n->diag = 1; + } + return; + + case TUP(OCONV, CTNIL): + case TUP(OARRAYBYTESTR, CTNIL): + if(n->type->etype == TSTRING) { + v = tostr(v); + nl->type = n->type; + break; + } + // fall through + case TUP(OCONV, CTINT): + case TUP(OCONV, CTFLT): + case TUP(OCONV, CTSTR): + convlit1(&nl, n->type, 1); + break; + + case TUP(OPLUS, CTINT): + break; + case TUP(OMINUS, CTINT): + mpnegfix(v.u.xval); + break; + case TUP(OCOM, CTINT): + et = Txxx; + if(nl->type != T) + et = nl->type->etype; + + // calculate the mask in b + // result will be (a ^ mask) + switch(et) { + default: + // signed guys change sign + mpmovecfix(&b, -1); + break; + + case TUINT8: + case TUINT16: + case TUINT32: + case TUINT64: + case TUINT: + case TUINTPTR: + // unsigned guys invert their bits + mpmovefixfix(&b, maxintval[et]); + break; + } + mpxorfixfix(v.u.xval, &b); + break; + + case TUP(OPLUS, CTFLT): + break; + case TUP(OMINUS, CTFLT): + mpnegflt(v.u.fval); + break; + + case TUP(OPLUS, CTCPLX): + break; + case TUP(OMINUS, CTCPLX): + mpnegflt(&v.u.cval->real); + mpnegflt(&v.u.cval->imag); + break; + + case TUP(ONOT, CTBOOL): + if(!v.u.bval) + goto settrue; + goto setfalse; + } + +ret: + // rewrite n in place. + *n = *nl; + n->val = v; + + // check range. + lno = setlineno(n); + overflow(v, n->type); + lineno = lno; + + // truncate precision for non-ideal float. + if(v.ctype == CTFLT && n->type->etype != TIDEAL) + n->val.u.fval = truncfltlit(v.u.fval, n->type); + return; + +settrue: + *n = *nodbool(1); + return; + +setfalse: + *n = *nodbool(0); + return; +} + +Node* +nodlit(Val v) +{ + Node *n; + + n = nod(OLITERAL, N, N); + n->val = v; + switch(v.ctype) { + default: + fatal("nodlit ctype %d", v.ctype); + case CTSTR: + n->type = idealstring; + break; + case CTBOOL: + n->type = idealbool; + break; + case CTINT: + case CTFLT: + case CTCPLX: + n->type = types[TIDEAL]; + break; + case CTNIL: + n->type = types[TNIL]; + break; + } + return n; +} + +Node* +nodcplxlit(Val r, Val i) +{ + Node *n; + Mpcplx *c; + + r = toflt(r); + i = toflt(i); + + c = mal(sizeof(*c)); + n = nod(OLITERAL, N, N); + n->type = types[TIDEAL]; + n->val.u.cval = c; + n->val.ctype = CTCPLX; + + if(r.ctype != CTFLT || i.ctype != CTFLT) + fatal("nodcplxlit ctype %d/%d", r.ctype, i.ctype); + + mpmovefltflt(&c->real, r.u.fval); + mpmovefltflt(&c->imag, i.u.fval); + return n; +} + +// TODO(rsc): combine with convlit +void +defaultlit(Node **np, Type *t) +{ + int lno; + Node *n, *nn; + + n = *np; + if(n == N || !isideal(n->type)) + return; + + switch(n->op) { + case OLITERAL: + nn = nod(OXXX, N, N); + *nn = *n; + n = nn; + *np = n; + break; + case OLSH: + case ORSH: + defaultlit(&n->left, t); + t = n->left->type; + if(t != T && !isint[t->etype]) { + yyerror("invalid operation: %#N (shift of type %T)", n, t); + t = T; + } + n->type = t; + return; + default: + if(n->left == N) { + dump("defaultlit", n); + fatal("defaultlit"); + } + // n is ideal, so left and right must both be ideal. + // n has not been computed as a constant value, + // so either left or right must not be constant. + // The only 'ideal' non-constant expressions are shifts. Ugh. + // If one of these is a shift and the other is not, use that type. + // When compiling x := 1<<i + 3.14, this means we try to push + // the float64 down into the 1<<i, producing the correct error + // (cannot shift float64). + if(t == T && (n->right->op == OLSH || n->right->op == ORSH)) { + defaultlit(&n->left, T); + defaultlit(&n->right, n->left->type); + } else if(t == T && (n->left->op == OLSH || n->left->op == ORSH)) { + defaultlit(&n->right, T); + defaultlit(&n->left, n->right->type); + } else { + defaultlit(&n->left, t); + defaultlit(&n->right, t); + } + if(n->type == idealbool || n->type == idealstring) + n->type = types[n->type->etype]; + else + n->type = n->left->type; + return; + } + + lno = setlineno(n); + switch(n->val.ctype) { + default: + if(t != T) { + convlit(np, t); + break; + } + if(n->val.ctype == CTNIL) { + lineno = lno; + yyerror("use of untyped nil"); + n->type = T; + break; + } + if(n->val.ctype == CTSTR) { + n->type = types[TSTRING]; + break; + } + yyerror("defaultlit: unknown literal: %#N", n); + break; + case CTBOOL: + n->type = types[TBOOL]; + if(t != T && t->etype == TBOOL) + n->type = t; + break; + case CTINT: + n->type = types[TINT]; + goto num; + case CTFLT: + n->type = types[TFLOAT64]; + goto num; + case CTCPLX: + n->type = types[TCOMPLEX128]; + goto num; + num: + if(t != T) { + if(isint[t->etype]) { + n->type = t; + n->val = toint(n->val); + } + else + if(isfloat[t->etype]) { + n->type = t; + n->val = toflt(n->val); + } + else + if(iscomplex[t->etype]) { + n->type = t; + n->val = tocplx(n->val); + } + } + overflow(n->val, n->type); + break; + } + lineno = lno; +} + +/* + * defaultlit on both nodes simultaneously; + * if they're both ideal going in they better + * get the same type going out. + * force means must assign concrete (non-ideal) type. + */ +void +defaultlit2(Node **lp, Node **rp, int force) +{ + Node *l, *r; + + l = *lp; + r = *rp; + if(l->type == T || r->type == T) + return; + if(!isideal(l->type)) { + convlit(rp, l->type); + return; + } + if(!isideal(r->type)) { + convlit(lp, r->type); + return; + } + if(!force) + return; + if(isconst(l, CTCPLX) || isconst(r, CTCPLX)) { + convlit(lp, types[TCOMPLEX128]); + convlit(rp, types[TCOMPLEX128]); + return; + } + if(isconst(l, CTFLT) || isconst(r, CTFLT)) { + convlit(lp, types[TFLOAT64]); + convlit(rp, types[TFLOAT64]); + return; + } + convlit(lp, types[TINT]); + convlit(rp, types[TINT]); +} + +int +cmpslit(Node *l, Node *r) +{ + int32 l1, l2, i, m; + uchar *s1, *s2; + + l1 = l->val.u.sval->len; + l2 = r->val.u.sval->len; + s1 = (uchar*)l->val.u.sval->s; + s2 = (uchar*)r->val.u.sval->s; + + m = l1; + if(l2 < m) + m = l2; + + for(i=0; i<m; i++) { + if(s1[i] == s2[i]) + continue; + if(s1[i] > s2[i]) + return +1; + return -1; + } + if(l1 == l2) + return 0; + if(l1 > l2) + return +1; + return -1; +} + +int +smallintconst(Node *n) +{ + if(n->op == OLITERAL && n->type != T) + switch(simtype[n->type->etype]) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TBOOL: + case TPTR32: + return 1; + case TINT64: + case TUINT64: + if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0 + || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) + break; + return 1; + } + return 0; +} + +long +nonnegconst(Node *n) +{ + if(n->op == OLITERAL && n->type != T) + switch(simtype[n->type->etype]) { + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TIDEAL: + // check negative and 2^31 + if(mpcmpfixfix(n->val.u.xval, minintval[TUINT32]) < 0 + || mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0) + break; + return mpgetfix(n->val.u.xval); + } + return -1; +} + +/* + * convert x to type et and back to int64 + * for sign extension and truncation. + */ +static int64 +iconv(int64 x, int et) +{ + switch(et) { + case TINT8: + x = (int8)x; + break; + case TUINT8: + x = (uint8)x; + break; + case TINT16: + x = (int16)x; + break; + case TUINT16: + x = (uint64)x; + break; + case TINT32: + x = (int32)x; + break; + case TUINT32: + x = (uint32)x; + break; + case TINT64: + case TUINT64: + break; + } + return x; +} + +/* + * convert constant val to type t; leave in con. + * for back end. + */ +void +convconst(Node *con, Type *t, Val *val) +{ + int64 i; + int tt; + + tt = simsimtype(t); + + // copy the constant for conversion + nodconst(con, types[TINT8], 0); + con->type = t; + con->val = *val; + + if(isint[tt]) { + con->val.ctype = CTINT; + con->val.u.xval = mal(sizeof *con->val.u.xval); + switch(val->ctype) { + default: + fatal("convconst ctype=%d %lT", val->ctype, t); + case CTINT: + i = mpgetfix(val->u.xval); + break; + case CTBOOL: + i = val->u.bval; + break; + case CTNIL: + i = 0; + break; + } + i = iconv(i, tt); + mpmovecfix(con->val.u.xval, i); + return; + } + + if(isfloat[tt]) { + con->val = toflt(con->val); + if(con->val.ctype != CTFLT) + fatal("convconst ctype=%d %T", con->val.ctype, t); + if(tt == TFLOAT32) + con->val.u.fval = truncfltlit(con->val.u.fval, t); + return; + } + + if(iscomplex[tt]) { + con->val = tocplx(con->val); + if(tt == TCOMPLEX64) { + con->val.u.cval->real = *truncfltlit(&con->val.u.cval->real, types[TFLOAT32]); + con->val.u.cval->imag = *truncfltlit(&con->val.u.cval->imag, types[TFLOAT32]); + } + return; + } + + fatal("convconst %lT constant", t); + +} + +// complex multiply v *= rv +// (a, b) * (c, d) = (a*c - b*d, b*c + a*d) +static void +cmplxmpy(Mpcplx *v, Mpcplx *rv) +{ + Mpflt ac, bd, bc, ad; + + mpmovefltflt(&ac, &v->real); + mpmulfltflt(&ac, &rv->real); // ac + + mpmovefltflt(&bd, &v->imag); + mpmulfltflt(&bd, &rv->imag); // bd + + mpmovefltflt(&bc, &v->imag); + mpmulfltflt(&bc, &rv->real); // bc + + mpmovefltflt(&ad, &v->real); + mpmulfltflt(&ad, &rv->imag); // ad + + mpmovefltflt(&v->real, &ac); + mpsubfltflt(&v->real, &bd); // ac-bd + + mpmovefltflt(&v->imag, &bc); + mpaddfltflt(&v->imag, &ad); // bc+ad +} + +// complex divide v /= rv +// (a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d) +static void +cmplxdiv(Mpcplx *v, Mpcplx *rv) +{ + Mpflt ac, bd, bc, ad, cc_plus_dd; + + mpmovefltflt(&cc_plus_dd, &rv->real); + mpmulfltflt(&cc_plus_dd, &rv->real); // cc + + mpmovefltflt(&ac, &rv->imag); + mpmulfltflt(&ac, &rv->imag); // dd + + mpaddfltflt(&cc_plus_dd, &ac); // cc+dd + + mpmovefltflt(&ac, &v->real); + mpmulfltflt(&ac, &rv->real); // ac + + mpmovefltflt(&bd, &v->imag); + mpmulfltflt(&bd, &rv->imag); // bd + + mpmovefltflt(&bc, &v->imag); + mpmulfltflt(&bc, &rv->real); // bc + + mpmovefltflt(&ad, &v->real); + mpmulfltflt(&ad, &rv->imag); // ad + + mpmovefltflt(&v->real, &ac); + mpaddfltflt(&v->real, &bd); // ac+bd + mpdivfltflt(&v->real, &cc_plus_dd); // (ac+bd)/(cc+dd) + + mpmovefltflt(&v->imag, &bc); + mpsubfltflt(&v->imag, &ad); // bc-ad + mpdivfltflt(&v->imag, &cc_plus_dd); // (bc+ad)/(cc+dd) +} diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c new file mode 100644 index 000000000..890cf7f10 --- /dev/null +++ b/src/cmd/gc/cplx.c @@ -0,0 +1,479 @@ +// 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 "gg.h" + +static void subnode(Node *nr, Node *ni, Node *nc); +static void minus(Node *nl, Node *res); + void complexminus(Node*, Node*); + void complexadd(int op, Node*, Node*, Node*); + void complexmul(Node*, Node*, Node*); + +#define CASE(a,b) (((a)<<16)|((b)<<0)) + +static int +overlap(Node *f, Node *t) +{ + // check whether f and t could be overlapping stack references. + // not exact, because it's hard to check for the stack register + // in portable code. close enough: worst case we will allocate + // an extra temporary and the registerizer will clean it up. + return f->op == OINDREG && + t->op == OINDREG && + f->xoffset+f->type->width >= t->xoffset && + t->xoffset+t->type->width >= f->xoffset; +} + +/* + * generate: + * res = n; + * simplifies and calls gmove. + */ +void +complexmove(Node *f, Node *t) +{ + int ft, tt; + Node n1, n2, n3, n4; + + if(debug['g']) { + dump("\ncomplexmove-f", f); + dump("complexmove-t", t); + } + + if(!t->addable) + fatal("complexmove: to not addable"); + + ft = simsimtype(f->type); + tt = simsimtype(t->type); + switch(CASE(ft,tt)) { + + default: + fatal("complexmove: unknown conversion: %T -> %T\n", + f->type, t->type); + + case CASE(TCOMPLEX64,TCOMPLEX64): + case CASE(TCOMPLEX64,TCOMPLEX128): + case CASE(TCOMPLEX128,TCOMPLEX64): + case CASE(TCOMPLEX128,TCOMPLEX128): + // complex to complex move/convert. + // make f addable. + // also use temporary if possible stack overlap. + if(!f->addable || overlap(f, t)) { + tempname(&n1, f->type); + complexmove(f, &n1); + f = &n1; + } + + subnode(&n1, &n2, f); + subnode(&n3, &n4, t); + + cgen(&n1, &n3); + cgen(&n2, &n4); + break; + } +} + +int +complexop(Node *n, Node *res) +{ + if(n != N && n->type != T) + if(iscomplex[n->type->etype]) { + goto maybe; + } + if(res != N && res->type != T) + if(iscomplex[res->type->etype]) { + goto maybe; + } + + if(n->op == OREAL || n->op == OIMAG) + goto yes; + + goto no; + +maybe: + switch(n->op) { + case OCONV: // implemented ops + case OADD: + case OSUB: + case OMUL: + case OMINUS: + case OCOMPLEX: + case OREAL: + case OIMAG: + goto yes; + + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: + goto yes; + } + +no: +//dump("\ncomplex-no", n); + return 0; +yes: +//dump("\ncomplex-yes", n); + return 1; +} + +void +complexgen(Node *n, Node *res) +{ + Node *nl, *nr; + Node tnl, tnr; + Node n1, n2, tmp; + int tl, tr; + + if(debug['g']) { + dump("\ncomplexgen-n", n); + dump("complexgen-res", res); + } + + // pick off float/complex opcodes + switch(n->op) { + case OCOMPLEX: + if(res->addable) { + subnode(&n1, &n2, res); + tempname(&tmp, n1.type); + cgen(n->left, &tmp); + cgen(n->right, &n2); + cgen(&tmp, &n1); + return; + } + break; + + case OREAL: + case OIMAG: + nl = n->left; + if(!nl->addable) { + tempname(&tmp, nl->type); + complexgen(nl, &tmp); + nl = &tmp; + } + subnode(&n1, &n2, nl); + if(n->op == OREAL) { + cgen(&n1, res); + return; + } + cgen(&n2, res); + return; + } + + // perform conversion from n to res + tl = simsimtype(res->type); + tl = cplxsubtype(tl); + tr = simsimtype(n->type); + tr = cplxsubtype(tr); + if(tl != tr) { + if(!n->addable) { + tempname(&n1, n->type); + complexmove(n, &n1); + n = &n1; + } + complexmove(n, res); + return; + } + + if(!res->addable) { + igen(res, &n1, N); + cgen(n, &n1); + regfree(&n1); + return; + } + if(n->addable) { + complexmove(n, res); + return; + } + + switch(n->op) { + default: + dump("complexgen: unknown op", n); + fatal("complexgen: unknown op %O", n->op); + + case ODOT: + case ODOTPTR: + case OINDEX: + case OIND: + case ONAME: // PHEAP or PPARAMREF var + case OCALLFUNC: + igen(n, &n1, res); + complexmove(&n1, res); + regfree(&n1); + return; + + case OCONV: + case OADD: + case OSUB: + case OMUL: + case OMINUS: + case OCOMPLEX: + case OREAL: + case OIMAG: + break; + } + + nl = n->left; + if(nl == N) + return; + nr = n->right; + + // make both sides addable in ullman order + if(nr != N) { + if(nl->ullman > nr->ullman && !nl->addable) { + tempname(&tnl, nl->type); + cgen(nl, &tnl); + nl = &tnl; + } + if(!nr->addable) { + tempname(&tnr, nr->type); + cgen(nr, &tnr); + nr = &tnr; + } + } + if(!nl->addable) { + tempname(&tnl, nl->type); + cgen(nl, &tnl); + nl = &tnl; + } + + switch(n->op) { + default: + fatal("complexgen: unknown op %O", n->op); + break; + + case OCONV: + complexmove(nl, res); + break; + + case OMINUS: + complexminus(nl, res); + break; + + case OADD: + case OSUB: + complexadd(n->op, nl, nr, res); + break; + + case OMUL: + complexmul(nl, nr, res); + break; + } +} + +void +complexbool(int op, Node *nl, Node *nr, int true, Prog *to) +{ + Node tnl, tnr; + Node n1, n2, n3, n4; + Node na, nb, nc; + + // make both sides addable in ullman order + if(nr != N) { + if(nl->ullman > nr->ullman && !nl->addable) { + tempname(&tnl, nl->type); + cgen(nl, &tnl); + nl = &tnl; + } + if(!nr->addable) { + tempname(&tnr, nr->type); + cgen(nr, &tnr); + nr = &tnr; + } + } + if(!nl->addable) { + tempname(&tnl, nl->type); + cgen(nl, &tnl); + nl = &tnl; + } + + // build tree + // real(l) == real(r) && imag(l) == imag(r) + + subnode(&n1, &n2, nl); + subnode(&n3, &n4, nr); + + memset(&na, 0, sizeof(na)); + na.op = OANDAND; + na.left = &nb; + na.right = &nc; + na.type = types[TBOOL]; + + memset(&nb, 0, sizeof(na)); + nb.op = OEQ; + nb.left = &n1; + nb.right = &n3; + nb.type = types[TBOOL]; + + memset(&nc, 0, sizeof(na)); + nc.op = OEQ; + nc.left = &n2; + nc.right = &n4; + nc.type = types[TBOOL]; + + if(op == ONE) + true = !true; + + bgen(&na, true, to); +} + +void +nodfconst(Node *n, Type *t, Mpflt* fval) +{ + memset(n, 0, sizeof(*n)); + n->op = OLITERAL; + n->addable = 1; + ullmancalc(n); + n->val.u.fval = fval; + n->val.ctype = CTFLT; + n->type = t; + + if(!isfloat[t->etype]) + fatal("nodfconst: bad type %T", t); +} + +// break addable nc-complex into nr-real and ni-imaginary +static void +subnode(Node *nr, Node *ni, Node *nc) +{ + int tc; + Type *t; + + if(!nc->addable) + fatal("subnode not addable"); + + tc = simsimtype(nc->type); + tc = cplxsubtype(tc); + t = types[tc]; + + if(nc->op == OLITERAL) { + nodfconst(nr, t, &nc->val.u.cval->real); + nodfconst(ni, t, &nc->val.u.cval->imag); + return; + } + + *nr = *nc; + nr->type = t; + + *ni = *nc; + ni->type = t; + ni->xoffset += t->width; +} + +// generate code res = -nl +static void +minus(Node *nl, Node *res) +{ + Node ra; + + memset(&ra, 0, sizeof(ra)); + ra.op = OMINUS; + ra.left = nl; + ra.type = nl->type; + cgen(&ra, res); +} + +// build and execute tree +// real(res) = -real(nl) +// imag(res) = -imag(nl) +void +complexminus(Node *nl, Node *res) +{ + Node n1, n2, n5, n6; + + subnode(&n1, &n2, nl); + subnode(&n5, &n6, res); + + minus(&n1, &n5); + minus(&n2, &n6); +} + + +// build and execute tree +// real(res) = real(nl) op real(nr) +// imag(res) = imag(nl) op imag(nr) +void +complexadd(int op, Node *nl, Node *nr, Node *res) +{ + Node n1, n2, n3, n4, n5, n6; + Node ra; + + subnode(&n1, &n2, nl); + subnode(&n3, &n4, nr); + subnode(&n5, &n6, res); + + memset(&ra, 0, sizeof(ra)); + ra.op = op; + ra.left = &n1; + ra.right = &n3; + ra.type = n1.type; + cgen(&ra, &n5); + + memset(&ra, 0, sizeof(ra)); + ra.op = op; + ra.left = &n2; + ra.right = &n4; + ra.type = n2.type; + cgen(&ra, &n6); +} + +// build and execute tree +// tmp = real(nl)*real(nr) - imag(nl)*imag(nr) +// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr) +// real(res) = tmp +void +complexmul(Node *nl, Node *nr, Node *res) +{ + Node n1, n2, n3, n4, n5, n6; + Node rm1, rm2, ra, tmp; + + subnode(&n1, &n2, nl); + subnode(&n3, &n4, nr); + subnode(&n5, &n6, res); + tempname(&tmp, n5.type); + + // real part -> tmp + memset(&rm1, 0, sizeof(ra)); + rm1.op = OMUL; + rm1.left = &n1; + rm1.right = &n3; + rm1.type = n1.type; + + memset(&rm2, 0, sizeof(ra)); + rm2.op = OMUL; + rm2.left = &n2; + rm2.right = &n4; + rm2.type = n2.type; + + memset(&ra, 0, sizeof(ra)); + ra.op = OSUB; + ra.left = &rm1; + ra.right = &rm2; + ra.type = rm1.type; + cgen(&ra, &tmp); + + // imag part + memset(&rm1, 0, sizeof(ra)); + rm1.op = OMUL; + rm1.left = &n1; + rm1.right = &n4; + rm1.type = n1.type; + + memset(&rm2, 0, sizeof(ra)); + rm2.op = OMUL; + rm2.left = &n2; + rm2.right = &n3; + rm2.type = n2.type; + + memset(&ra, 0, sizeof(ra)); + ra.op = OADD; + ra.left = &rm1; + ra.right = &rm2; + ra.type = rm1.type; + cgen(&ra, &n6); + + // tmp ->real part + cgen(&tmp, &n5); +} diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c new file mode 100644 index 000000000..5bfeeb97a --- /dev/null +++ b/src/cmd/gc/dcl.c @@ -0,0 +1,1254 @@ +// 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" + +static void funcargs(Node*); + +static int +dflag(void) +{ + if(!debug['d']) + return 0; + if(debug['y']) + return 1; + if(incannedimport) + return 0; + return 1; +} + +/* + * declaration stack & operations + */ + +static void +dcopy(Sym *a, Sym *b) +{ + a->pkg = b->pkg; + a->name = b->name; + a->def = b->def; + a->block = b->block; + a->lastlineno = b->lastlineno; +} + +static Sym* +push(void) +{ + Sym *d; + + d = mal(sizeof(*d)); + d->lastlineno = lineno; + d->link = dclstack; + dclstack = d; + return d; +} + +static Sym* +pushdcl(Sym *s) +{ + Sym *d; + + d = push(); + dcopy(d, s); + if(dflag()) + print("\t%L push %S %p\n", lineno, s, s->def); + return d; +} + +void +popdcl(void) +{ + Sym *d, *s; + int lno; + +// if(dflag()) +// print("revert\n"); + + for(d=dclstack; d!=S; d=d->link) { + if(d->name == nil) + break; + s = pkglookup(d->name, d->pkg); + lno = s->lastlineno; + dcopy(s, d); + d->lastlineno = lno; + if(dflag()) + print("\t%L pop %S %p\n", lineno, s, s->def); + } + if(d == S) + fatal("popdcl: no mark"); + dclstack = d->link; + block = d->block; +} + +void +poptodcl(void) +{ + // pop the old marker and push a new one + // (cannot reuse the existing one) + // because we use the markers to identify blocks + // for the goto restriction checks. + popdcl(); + markdcl(); +} + +void +markdcl(void) +{ + Sym *d; + + d = push(); + d->name = nil; // used as a mark in fifo + d->block = block; + + blockgen++; + block = blockgen; + +// if(dflag()) +// print("markdcl\n"); +} + +void +dumpdcl(char *st) +{ + Sym *s, *d; + int i; + + i = 0; + for(d=dclstack; d!=S; d=d->link) { + i++; + print(" %.2d %p", i, d); + if(d->name == nil) { + print("\n"); + continue; + } + print(" '%s'", d->name); + s = pkglookup(d->name, d->pkg); + print(" %lS\n", s); + } +} + +void +testdclstack(void) +{ + Sym *d; + + for(d=dclstack; d!=S; d=d->link) { + if(d->name == nil) { + yyerror("mark left on the stack"); + continue; + } + } +} + +void +redeclare(Sym *s, char *where) +{ + if(s->lastlineno == 0) + yyerror("%S redeclared %s\n" + "\tprevious declaration during import", + s, where); + else + yyerror("%S redeclared %s\n" + "\tprevious declaration at %L", + s, where, s->lastlineno); +} + +/* + * declare individual names - var, typ, const + */ +void +declare(Node *n, int ctxt) +{ + Sym *s; + int gen; + static int typegen, vargen; + + if(isblank(n)) + return; + + n->lineno = parserline(); + s = n->sym; + gen = 0; + if(ctxt == PEXTERN) { + externdcl = list(externdcl, n); + if(dflag()) + print("\t%L global decl %S %p\n", lineno, s, n); + } else { + if(curfn == nil && ctxt == PAUTO) + fatal("automatic outside function"); + if(curfn != nil) + curfn->dcl = list(curfn->dcl, n); + if(n->op == OTYPE) + gen = ++typegen; + else if(n->op == ONAME) + gen = ++vargen; + pushdcl(s); + n->curfn = curfn; + } + if(ctxt == PAUTO) + n->xoffset = BADWIDTH; + + if(s->block == block) + redeclare(s, "in this block"); + + s->block = block; + s->lastlineno = parserline(); + s->def = n; + n->vargen = gen; + n->funcdepth = funcdepth; + n->class = ctxt; + + autoexport(n, ctxt); +} + +void +addvar(Node *n, Type *t, int ctxt) +{ + if(n==N || n->sym == S || (n->op != ONAME && n->op != ONONAME) || t == T) + fatal("addvar: n=%N t=%T nil", n, t); + + n->op = ONAME; + declare(n, ctxt); + n->type = t; +} + +/* + * declare variables from grammar + * new_name_list (type | [type] = expr_list) + */ +NodeList* +variter(NodeList *vl, Node *t, NodeList *el) +{ + int doexpr; + Node *v, *e, *as2; + NodeList *init; + + init = nil; + doexpr = el != nil; + + if(count(el) == 1 && count(vl) > 1) { + e = el->n; + as2 = nod(OAS2, N, N); + as2->list = vl; + as2->rlist = list1(e); + for(; vl; vl=vl->next) { + v = vl->n; + v->op = ONAME; + declare(v, dclcontext); + v->ntype = t; + v->defn = as2; + if(funcdepth > 0) + init = list(init, nod(ODCL, v, N)); + } + return list(init, as2); + } + + for(; vl; vl=vl->next) { + if(doexpr) { + if(el == nil) { + yyerror("missing expr in var dcl"); + break; + } + e = el->n; + el = el->next; + } else + e = N; + + v = vl->n; + v->op = ONAME; + declare(v, dclcontext); + v->ntype = t; + + if(e != N || funcdepth > 0 || isblank(v)) { + if(funcdepth > 0) + init = list(init, nod(ODCL, v, N)); + e = nod(OAS, v, e); + init = list(init, e); + if(e->right != N) + v->defn = e; + } + } + if(el != nil) + yyerror("extra expr in var dcl"); + return init; +} + +/* + * declare constants from grammar + * new_name_list [[type] = expr_list] + */ +NodeList* +constiter(NodeList *vl, Node *t, NodeList *cl) +{ + Node *v, *c; + NodeList *vv; + + vv = nil; + if(cl == nil) { + if(t != N) + yyerror("constdcl cannot have type without expr"); + cl = lastconst; + t = lasttype; + } else { + lastconst = cl; + lasttype = t; + } + cl = listtreecopy(cl); + + for(; vl; vl=vl->next) { + if(cl == nil) { + yyerror("missing expr in const dcl"); + break; + } + c = cl->n; + cl = cl->next; + + v = vl->n; + v->op = OLITERAL; + declare(v, dclcontext); + + v->ntype = t; + v->defn = c; + + vv = list(vv, nod(ODCLCONST, v, N)); + } + if(cl != nil) + yyerror("extra expr in const dcl"); + iota += 1; + return vv; +} + +/* + * this generates a new name node, + * typically for labels or other one-off names. + */ +Node* +newname(Sym *s) +{ + Node *n; + + if(s == S) + fatal("newname nil"); + + n = nod(ONAME, N, N); + n->sym = s; + n->type = T; + n->addable = 1; + n->ullman = 1; + n->xoffset = 0; + return n; +} + +/* + * this generates a new name node for a name + * being declared. + */ +Node* +dclname(Sym *s) +{ + Node *n; + + n = newname(s); + n->op = ONONAME; // caller will correct it + return n; +} + +Node* +typenod(Type *t) +{ + // if we copied another type with *t = *u + // then t->nod might be out of date, so + // check t->nod->type too + if(t->nod == N || t->nod->type != t) { + t->nod = nod(OTYPE, N, N); + t->nod->type = t; + t->nod->sym = t->sym; + } + return t->nod; +} + + +/* + * this will return an old name + * that has already been pushed on the + * declaration list. a diagnostic is + * generated if no name has been defined. + */ +Node* +oldname(Sym *s) +{ + Node *n; + Node *c; + + n = s->def; + if(n == N) { + // maybe a top-level name will come along + // to give this a definition later. + // walkdef will check s->def again once + // all the input source has been processed. + n = newname(s); + n->op = ONONAME; + n->iota = iota; // save current iota value in const declarations + } + if(curfn != nil && n->funcdepth > 0 && n->funcdepth != funcdepth && n->op == ONAME) { + // inner func is referring to var in outer func. + // + // TODO(rsc): If there is an outer variable x and we + // are parsing x := 5 inside the closure, until we get to + // the := it looks like a reference to the outer x so we'll + // make x a closure variable unnecessarily. + if(n->closure == N || n->closure->funcdepth != funcdepth) { + // create new closure var. + c = nod(ONAME, N, N); + c->sym = s; + c->class = PPARAMREF; + c->isddd = n->isddd; + c->defn = n; + c->addable = 0; + c->ullman = 2; + c->funcdepth = funcdepth; + c->outer = n->closure; + n->closure = c; + c->closure = n; + c->xoffset = 0; + curfn->cvars = list(curfn->cvars, c); + } + // return ref to closure var, not original + return n->closure; + } + return n; +} + +/* + * same for types + */ +Type* +newtype(Sym *s) +{ + Type *t; + + t = typ(TFORW); + t->sym = s; + t->type = T; + return t; +} + + +/* + * := declarations + */ + +static int +colasname(Node *n) +{ + switch(n->op) { + case ONAME: + case ONONAME: + case OPACK: + case OTYPE: + case OLITERAL: + return n->sym != S; + } + return 0; +} + +void +colasdefn(NodeList *left, Node *defn) +{ + int nnew, nerr; + NodeList *l; + Node *n; + + nnew = 0; + nerr = 0; + for(l=left; l; l=l->next) { + n = l->n; + if(isblank(n)) + continue; + if(!colasname(n)) { + yyerror("non-name %#N on left side of :=", n); + nerr++; + continue; + } + if(n->sym->block == block) + continue; + + nnew++; + n = newname(n->sym); + declare(n, dclcontext); + n->defn = defn; + defn->ninit = list(defn->ninit, nod(ODCL, n, N)); + l->n = n; + } + if(nnew == 0 && nerr == 0) + yyerror("no new variables on left side of :="); +} + +Node* +colas(NodeList *left, NodeList *right) +{ + Node *as; + + as = nod(OAS2, N, N); + as->list = left; + as->rlist = right; + as->colas = 1; + colasdefn(left, as); + + // make the tree prettier; not necessary + if(count(left) == 1 && count(right) == 1) { + as->left = as->list->n; + as->right = as->rlist->n; + as->list = nil; + as->rlist = nil; + as->op = OAS; + } + + return as; +} + +/* + * declare the arguments in an + * interface field declaration. + */ +void +ifacedcl(Node *n) +{ + if(n->op != ODCLFIELD || n->right == N) + fatal("ifacedcl"); + + dclcontext = PAUTO; + markdcl(); + funcdepth++; + n->outer = curfn; + curfn = n; + funcargs(n->right); + + // funcbody is normally called after the parser has + // seen the body of a function but since an interface + // field declaration does not have a body, we must + // call it now to pop the current declaration context. + funcbody(n); +} + +/* + * declare the function proper + * and declare the arguments. + * called in extern-declaration context + * returns in auto-declaration context. + */ +void +funchdr(Node *n) +{ + + if(n->nname != N) { + n->nname->op = ONAME; + declare(n->nname, PFUNC); + n->nname->defn = n; + } + + // change the declaration context from extern to auto + if(funcdepth == 0 && dclcontext != PEXTERN) + fatal("funchdr: dclcontext"); + + dclcontext = PAUTO; + markdcl(); + funcdepth++; + + n->outer = curfn; + curfn = n; + if(n->nname) + funcargs(n->nname->ntype); + else + funcargs(n->ntype); +} + +static void +funcargs(Node *nt) +{ + Node *n; + NodeList *l; + int gen; + + if(nt->op != OTFUNC) + fatal("funcargs %O", nt->op); + + // declare the receiver and in arguments. + // no n->defn because type checking of func header + // will fill in the types before we can demand them. + if(nt->left != N) { + n = nt->left; + if(n->op != ODCLFIELD) + fatal("funcargs1 %O", n->op); + if(n->left != N) { + n->left->op = ONAME; + n->left->ntype = n->right; + declare(n->left, PPARAM); + } + } + for(l=nt->list; l; l=l->next) { + n = l->n; + if(n->op != ODCLFIELD) + fatal("funcargs2 %O", n->op); + if(n->left != N) { + n->left->op = ONAME; + n->left->ntype = n->right; + declare(n->left, PPARAM); + } + } + + // declare the out arguments. + gen = 0; + for(l=nt->rlist; l; l=l->next) { + n = l->n; + if(n->op != ODCLFIELD) + fatal("funcargs3 %O", n->op); + if(n->left != N) { + n->left->op = ONAME; + n->left->ntype = n->right; + if(isblank(n->left)) { + // Give it a name so we can assign to it during return. + snprint(namebuf, sizeof(namebuf), ".anon%d", gen++); + n->left->sym = lookup(namebuf); + } + declare(n->left, PPARAMOUT); + } + } +} + +/* + * finish the body. + * called in auto-declaration context. + * returns in extern-declaration context. + */ +void +funcbody(Node *n) +{ + // change the declaration context from auto to extern + if(dclcontext != PAUTO) + fatal("funcbody: dclcontext"); + popdcl(); + funcdepth--; + curfn = n->outer; + n->outer = N; + if(funcdepth == 0) + dclcontext = PEXTERN; +} + +/* + * new type being defined with name s. + */ +Node* +typedcl0(Sym *s) +{ + Node *n; + + n = dclname(s); + n->op = OTYPE; + declare(n, dclcontext); + return n; +} + +/* + * node n, which was returned by typedcl0 + * is being declared to have uncompiled type t. + * return the ODCLTYPE node to use. + */ +Node* +typedcl1(Node *n, Node *t, int local) +{ + n->ntype = t; + n->local = local; + return nod(ODCLTYPE, n, N); +} + +/* + * typedcl1 but during imports + */ +void +typedcl2(Type *pt, Type *t) +{ + Node *n; + + // override declaration in unsafe.go for Pointer. + // there is no way in Go code to define unsafe.Pointer + // so we have to supply it. + if(incannedimport && + strcmp(importpkg->name, "unsafe") == 0 && + strcmp(pt->nod->sym->name, "Pointer") == 0) { + t = types[TUNSAFEPTR]; + } + + if(pt->etype == TFORW) + goto ok; + if(!eqtype(pt->orig, t)) + yyerror("inconsistent definition for type %S during import\n\t%lT\n\t%lT", pt->sym, pt->orig, t); + return; + +ok: + n = pt->nod; + copytype(pt->nod, t); + // unzero nod + pt->nod = n; + + pt->sym->lastlineno = parserline(); + declare(n, PEXTERN); + + checkwidth(pt); +} + +/* + * structs, functions, and methods. + * they don't belong here, but where do they belong? + */ + + +/* + * turn a parsed struct into a type + */ +static Type** +stotype(NodeList *l, int et, Type **t, int funarg) +{ + Type *f, *t1, *t2, **t0; + Strlit *note; + int lno; + Node *n, *left; + char *what; + + t0 = t; + lno = lineno; + what = "field"; + if(et == TINTER) + what = "method"; + + for(; l; l=l->next) { + n = l->n; + lineno = n->lineno; + note = nil; + + if(n->op != ODCLFIELD) + fatal("stotype: oops %N\n", n); + left = n->left; + if(funarg && isblank(left)) + left = N; + if(n->right != N) { + if(et == TINTER && left != N) { + // queue resolution of method type for later. + // right now all we need is the name list. + // avoids cycles for recursive interface types. + n->type = typ(TINTERMETH); + n->type->nname = n->right; + n->right = N; + left->type = n->type; + queuemethod(n); + } else { + typecheck(&n->right, Etype); + n->type = n->right->type; + if(n->type == T) + continue; + if(left != N) + left->type = n->type; + n->right = N; + if(n->embedded && n->type != T) { + t1 = n->type; + if(t1->sym == S && isptr[t1->etype]) { + t1 = t1->type; + if(t1->etype == TINTER) + yyerror("embedded type cannot be a pointer to interface"); + } + if(isptr[t1->etype]) + yyerror("embedded type cannot be a pointer"); + else if(t1->etype == TFORW && t1->embedlineno == 0) + t1->embedlineno = lineno; + } + } + } + + if(n->type == T) { + // assume error already printed + continue; + } + + switch(n->val.ctype) { + case CTSTR: + if(et != TSTRUCT) + yyerror("interface method cannot have annotation"); + note = n->val.u.sval; + break; + default: + if(et != TSTRUCT) + yyerror("interface method cannot have annotation"); + else + yyerror("field annotation must be string"); + case CTxxx: + note = nil; + break; + } + + if(et == TINTER && left == N) { + // embedded interface - inline the methods + if(n->type->etype != TINTER) { + if(n->type->etype == TFORW) + yyerror("interface type loop involving %T", n->type); + else + yyerror("interface contains embedded non-interface %T", n->type); + continue; + } + for(t1=n->type->type; t1!=T; t1=t1->down) { + f = typ(TFIELD); + f->type = t1->type; + f->width = BADWIDTH; + f->nname = newname(t1->sym); + f->sym = t1->sym; + for(t2=*t0; t2!=T; t2=t2->down) { + if(t2->sym == f->sym) { + yyerror("duplicate method %s", t2->sym->name); + break; + } + } + *t = f; + t = &f->down; + } + continue; + } + + f = typ(TFIELD); + f->type = n->type; + f->note = note; + f->width = BADWIDTH; + f->isddd = n->isddd; + + if(left != N && left->op == ONAME) { + f->nname = left; + f->embedded = n->embedded; + f->sym = f->nname->sym; + if(importpkg && !exportname(f->sym->name)) + f->sym = pkglookup(f->sym->name, structpkg); + if(f->sym && !isblank(f->nname)) { + for(t1=*t0; t1!=T; t1=t1->down) { + if(t1->sym == f->sym) { + yyerror("duplicate %s %s", what, t1->sym->name); + break; + } + } + } + } + + *t = f; + t = &f->down; + } + + *t = T; + lineno = lno; + return t; +} + +Type* +dostruct(NodeList *l, int et) +{ + Type *t; + int funarg; + + /* + * convert a parsed id/type list into + * a type for struct/interface/arglist + */ + + funarg = 0; + if(et == TFUNC) { + funarg = 1; + et = TSTRUCT; + } + t = typ(et); + t->funarg = funarg; + stotype(l, et, &t->type, funarg); + if(t->type == T && l != nil) { + t->broke = 1; + return t; + } + if(et == TINTER) + t = sortinter(t); + if(!funarg) + checkwidth(t); + return t; +} + + +Node* +embedded(Sym *s) +{ + Node *n; + char *name; + + // Names sometimes have disambiguation junk + // appended after a center dot. Discard it when + // making the name for the embedded struct field. + enum { CenterDot = 0xB7 }; + name = s->name; + if(utfrune(s->name, CenterDot)) { + name = strdup(s->name); + *utfrune(name, CenterDot) = 0; + } + + n = newname(lookup(name)); + n = nod(ODCLFIELD, n, oldname(s)); + n->embedded = 1; + return n; +} + +/* + * check that the list of declarations is either all anonymous or all named + */ + +static Node* +findtype(NodeList *l) +{ + for(; l; l=l->next) + if(l->n->op == OKEY) + return l->n->right; + return N; +} + +NodeList* +checkarglist(NodeList *all, int input) +{ + int named; + Node *n, *t, *nextt; + NodeList *l; + + named = 0; + for(l=all; l; l=l->next) { + if(l->n->op == OKEY) { + named = 1; + break; + } + } + if(named) { + n = N; + for(l=all; l; l=l->next) { + n = l->n; + if(n->op != OKEY && n->sym == S) { + yyerror("mixed named and unnamed function parameters"); + break; + } + } + if(l == nil && n != N && n->op != OKEY) + yyerror("final function parameter must have type"); + } + + nextt = nil; + for(l=all; l; l=l->next) { + // can cache result from findtype to avoid + // quadratic behavior here, but unlikely to matter. + n = l->n; + if(named) { + if(n->op == OKEY) { + t = n->right; + n = n->left; + nextt = nil; + } else { + if(nextt == nil) + nextt = findtype(l); + t = nextt; + } + } else { + t = n; + n = N; + } + if(n != N && n->sym == S) { + t = n; + n = N; + } + if(n != N) + n = newname(n->sym); + n = nod(ODCLFIELD, n, t); + if(n->right != N && n->right->op == ODDD) { + if(!input) + yyerror("cannot use ... in output argument list"); + else if(l->next != nil) + yyerror("can only use ... as final argument in list"); + n->right->op = OTARRAY; + n->right->right = n->right->left; + n->right->left = N; + n->isddd = 1; + if(n->left != N) + n->left->isddd = 1; + } + l->n = n; + } + return all; +} + + +Node* +fakethis(void) +{ + Node *n; + + n = nod(ODCLFIELD, N, typenod(ptrto(typ(TSTRUCT)))); + return n; +} + +/* + * Is this field a method on an interface? + * Those methods have an anonymous + * *struct{} as the receiver. + * (See fakethis above.) + */ +int +isifacemethod(Type *f) +{ + Type *rcvr; + Type *t; + + rcvr = getthisx(f)->type; + if(rcvr->sym != S) + return 0; + t = rcvr->type; + if(!isptr[t->etype]) + return 0; + t = t->type; + if(t->sym != S || t->etype != TSTRUCT || t->type != T) + return 0; + return 1; +} + +/* + * turn a parsed function declaration + * into a type + */ +Type* +functype(Node *this, NodeList *in, NodeList *out) +{ + Type *t; + NodeList *rcvr; + + t = typ(TFUNC); + + rcvr = nil; + if(this) + rcvr = list1(this); + t->type = dostruct(rcvr, TFUNC); + t->type->down = dostruct(out, TFUNC); + t->type->down->down = dostruct(in, TFUNC); + + if(this) + t->thistuple = 1; + t->outtuple = count(out); + t->intuple = count(in); + t->outnamed = t->outtuple > 0 && out->n->left != N; + + return t; +} + +Sym* +methodsym(Sym *nsym, Type *t0, int iface) +{ + Sym *s; + char *p; + Type *t; + char *suffix; + + t = t0; + if(t == T) + goto bad; + s = t->sym; + if(s == S) { + if(!isptr[t->etype]) + goto bad; + t = t->type; + if(t == T) + goto bad; + s = t->sym; + if(s == S) + goto bad; + } + + // if t0 == *t and t0 has a sym, + // we want to see *t, not t0, in the method name. + if(t != t0 && t0->sym) + t0 = ptrto(t); + + suffix = ""; + if(iface) { + dowidth(t0); + if(t0->width < types[tptr]->width) + suffix = "·i"; + } + if(t0->sym == S && isptr[t0->etype]) + p = smprint("(%#hT).%s%s", t0, nsym->name, suffix); + else + p = smprint("%#hT.%s%s", t0, nsym->name, suffix); + s = pkglookup(p, s->pkg); + free(p); + return s; + +bad: + yyerror("illegal receiver type: %T", t0); + return S; +} + +Node* +methodname(Node *n, Type *t) +{ + Sym *s; + + s = methodsym(n->sym, t, 0); + if(s == S) + return n; + return newname(s); +} + +Node* +methodname1(Node *n, Node *t) +{ + char *star; + char *p; + + star = nil; + if(t->op == OIND) { + star = "*"; + t = t->left; + } + if(t->sym == S || isblank(n)) + return newname(n->sym); + if(star) + p = smprint("(%s%S).%S", star, t->sym, n->sym); + else + p = smprint("%S.%S", t->sym, n->sym); + n = newname(pkglookup(p, t->sym->pkg)); + free(p); + return n; +} + +/* + * add a method, declared as a function, + * n is fieldname, pa is base type, t is function type + */ +void +addmethod(Sym *sf, Type *t, int local) +{ + Type *f, *d, *pa; + Node *n; + + pa = nil; + + // get field sym + if(sf == S) + fatal("no method symbol"); + + // get parent type sym + pa = getthisx(t)->type; // ptr to this structure + if(pa == T) { + yyerror("missing receiver"); + return; + } + + pa = pa->type; + f = methtype(pa); + if(f == T) { + t = pa; + if(t != T) { + if(isptr[t->etype]) { + if(t->sym != S) { + yyerror("invalid receiver type %T (%T is a pointer type)", pa, t); + return; + } + t = t->type; + } + } + if(t != T) { + if(t->sym == S) { + yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t); + return; + } + if(isptr[t->etype]) { + yyerror("invalid receiver type %T (%T is a pointer type)", pa, t); + return; + } + if(t->etype == TINTER) { + yyerror("invalid receiver type %T (%T is an interface type)", pa, t); + return; + } + } + // Should have picked off all the reasons above, + // but just in case, fall back to generic error. + yyerror("invalid receiver type %T", pa); + return; + } + + pa = f; + if(importpkg && !exportname(sf->name)) + sf = pkglookup(sf->name, importpkg); + + n = nod(ODCLFIELD, newname(sf), N); + n->type = t; + + d = T; // last found + for(f=pa->method; f!=T; f=f->down) { + d = f; + if(f->etype != TFIELD) + fatal("addmethod: not TFIELD: %N", f); + if(strcmp(sf->name, f->sym->name) != 0) + continue; + if(!eqtype(t, f->type)) + yyerror("method redeclared: %T.%S\n\t%T\n\t%T", pa, sf, f->type, t); + return; + } + + if(local && !pa->local) { + // defining method on non-local type. + yyerror("cannot define new methods on non-local type %T", pa); + return; + } + + if(d == T) + stotype(list1(n), 0, &pa->method, 0); + else + stotype(list1(n), 0, &d->down, 0); + return; +} + +void +funccompile(Node *n, int isclosure) +{ + stksize = BADWIDTH; + maxarg = 0; + + if(n->type == T) { + if(nerrors == 0) + fatal("funccompile missing type"); + return; + } + + // assign parameter offsets + checkwidth(n->type); + + // record offset to actual frame pointer. + // for closure, have to skip over leading pointers and PC slot. + nodfp->xoffset = 0; + if(isclosure) { + NodeList *l; + for(l=n->nname->ntype->list; l; l=l->next) { + nodfp->xoffset += widthptr; + if(l->n->left == N) // found slot for PC + break; + } + } + + if(curfn) + fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym); + + stksize = 0; + dclcontext = PAUTO; + funcdepth = n->funcdepth + 1; + compile(n); + curfn = nil; + funcdepth = 0; + dclcontext = PEXTERN; +} + + + diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go new file mode 100644 index 000000000..3fe7fafdd --- /dev/null +++ b/src/cmd/gc/doc.go @@ -0,0 +1,55 @@ +// 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. + +/* + +Gc is the generic label for the family of Go compilers +that function as part of the (modified) Plan 9 tool chain. The C compiler +documentation at + + http://plan9.bell-labs.com/sys/doc/comp.pdf (Tools overview) + http://plan9.bell-labs.com/sys/doc/compiler.pdf (C compiler architecture) + +gives the overall design of the tool chain. Aside from a few adapted pieces, +such as the optimizer, the Go compilers are wholly new programs. + +The compiler reads in a set of Go files, typically suffixed ".go". They +must all be part of one package. The output is a single intermediate file +representing the "binary assembly" of the compiled package, ready as input +for the linker (6l, etc.). + +The generated files contain type information about the symbols exported by +the package and about types used by symbols imported by the package from +other packages. It is therefore not necessary when compiling client C of +package P to read the files of P's dependencies, only the compiled output +of P. + +Usage: + 6g [flags] file... +The specified files must be Go source files and all part of the same package. +Substitute 6g with 8g or 5g where appropriate. + +Flags: + -o file + output file, default file.6 for 6g, etc. + -e + normally the compiler quits after 10 errors; -e prints all errors + -L + show entire file path when printing line numbers in errors + -I dir1 -I dir2 + add dir1 and dir2 to the list of paths to check for imported packages + -N + disable optimization + -S + write assembly language text to standard output + -u + disallow importing packages not marked as safe + -V + print the compiler version + +There are also a number of debugging flags; run the command with no arguments +to get a usage message. + +*/ +package documentation diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c new file mode 100644 index 000000000..014f0c5f0 --- /dev/null +++ b/src/cmd/gc/export.c @@ -0,0 +1,428 @@ +// 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" + +static void dumpsym(Sym*); +static void dumpexporttype(Sym*); +static void dumpexportvar(Sym*); +static void dumpexportconst(Sym*); + +void +exportsym(Node *n) +{ + if(n == N || n->sym == S) + return; + if(n->sym->flags & (SymExport|SymPackage)) { + if(n->sym->flags & SymPackage) + yyerror("export/package mismatch: %S", n->sym); + return; + } + n->sym->flags |= SymExport; + + exportlist = list(exportlist, n); +} + +static void +packagesym(Node *n) +{ + if(n == N || n->sym == S) + return; + if(n->sym->flags & (SymExport|SymPackage)) { + if(n->sym->flags & SymExport) + yyerror("export/package mismatch: %S", n->sym); + return; + } + n->sym->flags |= SymPackage; + + exportlist = list(exportlist, n); +} + +int +exportname(char *s) +{ + Rune r; + + if((uchar)s[0] < Runeself) + return 'A' <= s[0] && s[0] <= 'Z'; + chartorune(&r, s); + return isupperrune(r); +} + +static int +initname(char *s) +{ + return strcmp(s, "init") == 0; +} + +void +autoexport(Node *n, int ctxt) +{ + if(n == N || n->sym == S) + return; + if((ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN) + return; + if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left) // method + return; + if(exportname(n->sym->name) || initname(n->sym->name)) + exportsym(n); + else + packagesym(n); +} + +static void +dumppkg(Pkg *p) +{ + char *suffix; + + if(p == nil || p == localpkg || p->exported) + return; + p->exported = 1; + suffix = ""; + if(!p->direct) + suffix = " // indirect"; + Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix); +} + +static void +dumpprereq(Type *t) +{ + if(t == T) + return; + + if(t->printed || t == types[t->etype]) + return; + t->printed = 1; + + if(t->sym != S) { + dumppkg(t->sym->pkg); + if(t->etype != TFIELD) + dumpsym(t->sym); + } + dumpprereq(t->type); + dumpprereq(t->down); +} + +static void +dumpexportconst(Sym *s) +{ + Node *n; + Type *t; + + n = s->def; + typecheck(&n, Erv); + if(n == N || n->op != OLITERAL) + fatal("dumpexportconst: oconst nil: %S", s); + + t = n->type; // may or may not be specified + if(t != T) + dumpprereq(t); + + Bprint(bout, "\t"); + Bprint(bout, "const %#S", s); + if(t != T && !isideal(t)) + Bprint(bout, " %#T", t); + Bprint(bout, " = "); + + switch(n->val.ctype) { + default: + fatal("dumpexportconst: unknown ctype: %S %d", s, n->val.ctype); + case CTINT: + Bprint(bout, "%B\n", n->val.u.xval); + break; + case CTBOOL: + if(n->val.u.bval) + Bprint(bout, "true\n"); + else + Bprint(bout, "false\n"); + break; + case CTFLT: + Bprint(bout, "%F\n", n->val.u.fval); + break; + case CTCPLX: + Bprint(bout, "(%F+%F)\n", &n->val.u.cval->real, &n->val.u.cval->imag); + break; + case CTSTR: + Bprint(bout, "\"%Z\"\n", n->val.u.sval); + break; + } +} + +static void +dumpexportvar(Sym *s) +{ + Node *n; + Type *t; + + n = s->def; + typecheck(&n, Erv); + if(n == N || n->type == T) { + yyerror("variable exported but not defined: %S", s); + return; + } + + t = n->type; + dumpprereq(t); + + Bprint(bout, "\t"); + if(t->etype == TFUNC && n->class == PFUNC) + Bprint(bout, "func %#S %#hhT", s, t); + else + Bprint(bout, "var %#S %#T", s, t); + Bprint(bout, "\n"); +} + +static void +dumpexporttype(Sym *s) +{ + Type *t; + + t = s->def->type; + dumpprereq(t); + Bprint(bout, "\t"); + switch (t->etype) { + case TFORW: + yyerror("export of incomplete type %T", t); + return; + } + if(Bprint(bout, "type %#T %l#T\n", t, t) < 0) + fatal("Bprint failed for %T", t); +} + +static int +methcmp(const void *va, const void *vb) +{ + Type *a, *b; + + a = *(Type**)va; + b = *(Type**)vb; + return strcmp(a->sym->name, b->sym->name); +} + +static void +dumpsym(Sym *s) +{ + Type *f, *t; + Type **m; + int i, n; + + if(s->flags & SymExported) + return; + s->flags |= SymExported; + + if(s->def == N) { + yyerror("unknown export symbol: %S", s); + return; + } + + dumppkg(s->pkg); + + switch(s->def->op) { + default: + yyerror("unexpected export symbol: %O %S", s->def->op, s); + break; + case OLITERAL: + dumpexportconst(s); + break; + case OTYPE: + t = s->def->type; + n = 0; + for(f=t->method; f!=T; f=f->down) { + dumpprereq(f); + n++; + } + m = mal(n*sizeof m[0]); + i = 0; + for(f=t->method; f!=T; f=f->down) + m[i++] = f; + qsort(m, n, sizeof m[0], methcmp); + + dumpexporttype(s); + for(i=0; i<n; i++) { + f = m[i]; + Bprint(bout, "\tfunc (%#T) %hS %#hhT\n", + f->type->type->type, f->sym, f->type); + } + break; + case ONAME: + dumpexportvar(s); + break; + } +} + +static void +dumptype(Type *t) +{ + // no need to re-dump type if already exported + if(t->printed) + return; + + // no need to dump type if it's not ours (was imported) + if(t->sym != S && t->sym->def == typenod(t) && !t->local) + return; + + Bprint(bout, "type %#T %l#T\n", t, t); +} + +void +dumpexport(void) +{ + NodeList *l; + int32 i, lno; + Pkg *p; + + lno = lineno; + + packagequotes = 1; + Bprint(bout, "\n$$ // exports\n"); + + Bprint(bout, " package %s", localpkg->name); + if(safemode) + Bprint(bout, " safe"); + Bprint(bout, "\n"); + + for(i=0; i<nelem(phash); i++) + for(p=phash[i]; p; p=p->link) + if(p->direct) + dumppkg(p); + + for(l=exportlist; l; l=l->next) { + lineno = l->n->lineno; + dumpsym(l->n->sym); + } + + Bprint(bout, "\n$$ // local types\n"); + + for(l=typelist; l; l=l->next) { + lineno = l->n->lineno; + dumptype(l->n->type); + } + + Bprint(bout, "\n$$\n"); + packagequotes = 0; + + lineno = lno; +} + +/* + * import + */ + +/* + * return the sym for ss, which should match lexical + */ +Sym* +importsym(Sym *s, int op) +{ + if(s->def != N && s->def->op != op) + redeclare(s, "during import"); + + // mark the symbol so it is not reexported + if(s->def == N) { + if(exportname(s->name) || initname(s->name)) + s->flags |= SymExport; + else + s->flags |= SymPackage; // package scope + } + return s; +} + +/* + * return the type pkg.name, forward declaring if needed + */ +Type* +pkgtype(Sym *s) +{ + Type *t; + + importsym(s, OTYPE); + if(s->def == N || s->def->op != OTYPE) { + t = typ(TFORW); + t->sym = s; + s->def = typenod(t); + } + if(s->def->type == T) + yyerror("pkgtype %lS", s); + return s->def->type; +} + +static int +mypackage(Sym *s) +{ + // we import all definitions for runtime. + // lowercase ones can only be used by the compiler. + return s->pkg == localpkg || s->pkg == runtimepkg; +} + +void +importconst(Sym *s, Type *t, Node *n) +{ + Node *n1; + + if(!exportname(s->name) && !mypackage(s)) + return; + importsym(s, OLITERAL); + convlit(&n, t); + if(s->def != N) { + // TODO: check if already the same. + return; + } + + if(n->op != OLITERAL) { + yyerror("expression must be a constant"); + return; + } + if(n->sym != S) { + n1 = nod(OXXX, N, N); + *n1 = *n; + n = n1; + } + n->sym = s; + declare(n, PEXTERN); + + if(debug['E']) + print("import const %S\n", s); +} + +void +importvar(Sym *s, Type *t, int ctxt) +{ + Node *n; + + if(!exportname(s->name) && !initname(s->name) && !mypackage(s)) + return; + + importsym(s, ONAME); + if(s->def != N && s->def->op == ONAME) { + if(eqtype(t, s->def->type)) + return; + yyerror("inconsistent definition for var %S during import\n\t%T\n\t%T", + s, s->def->type, t); + } + n = newname(s); + n->type = t; + declare(n, ctxt); + + if(debug['E']) + print("import var %S %lT\n", s, t); +} + +void +importtype(Type *pt, Type *t) +{ + if(pt != T && t != T) + typedcl2(pt, t); + + if(debug['E']) + print("import type %T %lT\n", pt, t); +} + +void +importmethod(Sym *s, Type *t) +{ + checkwidth(t); + addmethod(s, t, 0); +} + diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c new file mode 100644 index 000000000..cb66921ba --- /dev/null +++ b/src/cmd/gc/gen.c @@ -0,0 +1,790 @@ +// 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. + +/* + * portable half of code generator. + * mainly statements and control flow. + */ + +#include "go.h" + +static void cgen_dcl(Node *n); +static void cgen_proc(Node *n, int proc); +static void checkgoto(Node*, Node*); + +static Label *labellist; +static Label *lastlabel; + +Node* +sysfunc(char *name) +{ + Node *n; + + n = newname(pkglookup(name, runtimepkg)); + n->class = PFUNC; + return n; +} + +void +allocparams(void) +{ + NodeList *l; + Node *n; + uint32 w; + Sym *s; + int lno; + + if(stksize < 0) + fatal("allocparams not during code generation"); + + /* + * allocate (set xoffset) the stack + * slots for all automatics. + * allocated starting at -w down. + */ + lno = lineno; + for(l=curfn->dcl; l; l=l->next) { + n = l->n; + if(n->op == ONAME && n->class == PHEAP-1) { + // heap address variable; finish the job + // started in addrescapes. + s = n->sym; + tempname(n, n->type); + n->sym = s; + } + if(n->op != ONAME || n->class != PAUTO) + continue; + if (n->xoffset != BADWIDTH) + continue; + if(n->type == T) + continue; + dowidth(n->type); + w = n->type->width; + if(w >= MAXWIDTH) + fatal("bad width"); + stksize += w; + stksize = rnd(stksize, n->type->align); + if(thechar == '5') + stksize = rnd(stksize, widthptr); + n->xoffset = -stksize; + } + lineno = lno; +} + +void +clearlabels(void) +{ + Label *l; + + for(l=labellist; l!=L; l=l->link) + l->sym->label = L; + + labellist = L; + lastlabel = L; +} + +static Label* +newlab(Node *n) +{ + Sym *s; + Label *lab; + + s = n->left->sym; + if((lab = s->label) == L) { + lab = mal(sizeof(*lab)); + if(lastlabel == nil) + labellist = lab; + else + lastlabel->link = lab; + lastlabel = lab; + lab->sym = s; + s->label = lab; + } + + if(n->op == OLABEL) { + if(lab->def != N) + yyerror("label %S already defined at %L", s, lab->def->lineno); + else + lab->def = n; + } else + lab->use = list(lab->use, n); + + return lab; +} + +void +checklabels(void) +{ + Label *lab; + NodeList *l; + + for(lab=labellist; lab!=L; lab=lab->link) { + if(lab->def == N) { + for(l=lab->use; l; l=l->next) + yyerrorl(l->n->lineno, "label %S not defined", lab->sym); + continue; + } + if(lab->use == nil && !lab->used) { + yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym); + continue; + } + if(lab->gotopc != P) + fatal("label %S never resolved", lab->sym); + for(l=lab->use; l; l=l->next) + checkgoto(l->n, lab->def); + } +} + +static void +checkgoto(Node *from, Node *to) +{ + int nf, nt; + Sym *block, *dcl, *fs, *ts; + int lno; + + if(from->sym == to->sym) + return; + + nf = 0; + for(fs=from->sym; fs; fs=fs->link) + nf++; + nt = 0; + for(fs=to->sym; fs; fs=fs->link) + nt++; + fs = from->sym; + for(; nf > nt; nf--) + fs = fs->link; + if(fs != to->sym) { + lno = lineno; + setlineno(from); + + // decide what to complain about. + // prefer to complain about 'into block' over declarations, + // so scan backward to find most recent block or else dcl. + block = S; + dcl = S; + ts = to->sym; + for(; nt > nf; nt--) { + if(ts->pkg == nil) + block = ts; + else + dcl = ts; + ts = ts->link; + } + while(ts != fs) { + if(ts->pkg == nil) + block = ts; + else + dcl = ts; + ts = ts->link; + fs = fs->link; + } + + if(block) + yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno); + else + yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno); + lineno = lno; + } +} + +static Label* +stmtlabel(Node *n) +{ + Label *lab; + + if(n->sym != S) + if((lab = n->sym->label) != L) + if(lab->def != N) + if(lab->def->right == n) + return lab; + return L; +} + +/* + * compile statements + */ +void +genlist(NodeList *l) +{ + for(; l; l=l->next) + gen(l->n); +} + +void +gen(Node *n) +{ + int32 lno; + Prog *scontin, *sbreak; + Prog *p1, *p2, *p3; + Label *lab; + int32 wasregalloc; + + lno = setlineno(n); + wasregalloc = anyregalloc(); + + if(n == N) + goto ret; + + p3 = pc; // save pc for loop labels + if(n->ninit) + genlist(n->ninit); + + setlineno(n); + + switch(n->op) { + default: + fatal("gen: unknown op %N", n); + break; + + case OCASE: + case OFALL: + case OXCASE: + case OXFALL: + case ODCLCONST: + case ODCLFUNC: + case ODCLTYPE: + break; + + case OEMPTY: + break; + + case OBLOCK: + genlist(n->list); + break; + + case OLABEL: + lab = newlab(n); + + // if there are pending gotos, resolve them all to the current pc. + for(p1=lab->gotopc; p1; p1=p2) { + p2 = unpatch(p1); + patch(p1, pc); + } + lab->gotopc = P; + if(lab->labelpc == P) + lab->labelpc = pc; + + if(n->right) { + switch(n->right->op) { + case OFOR: + case OSWITCH: + case OSELECT: + // so stmtlabel can find the label + n->right->sym = lab->sym; + } + } + break; + + case OGOTO: + // if label is defined, emit jump to it. + // otherwise save list of pending gotos in lab->gotopc. + // the list is linked through the normal jump target field + // to avoid a second list. (the jumps are actually still + // valid code, since they're just going to another goto + // to the same label. we'll unwind it when we learn the pc + // of the label in the OLABEL case above.) + lab = newlab(n); + if(lab->labelpc != P) + gjmp(lab->labelpc); + else + lab->gotopc = gjmp(lab->gotopc); + break; + + case OBREAK: + if(n->left != N) { + lab = n->left->sym->label; + if(lab == L) { + yyerror("break label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->breakpc == P) { + yyerror("invalid break label %S", n->left->sym); + break; + } + gjmp(lab->breakpc); + break; + } + if(breakpc == P) { + yyerror("break is not in a loop"); + break; + } + gjmp(breakpc); + break; + + case OCONTINUE: + if(n->left != N) { + lab = n->left->sym->label; + if(lab == L) { + yyerror("continue label not defined: %S", n->left->sym); + break; + } + lab->used = 1; + if(lab->continpc == P) { + yyerror("invalid continue label %S", n->left->sym); + break; + } + gjmp(lab->continpc); + break; + } + if(continpc == P) { + yyerror("continue is not in a loop"); + break; + } + gjmp(continpc); + break; + + case OFOR: + sbreak = breakpc; + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done + scontin = continpc; + continpc = pc; + + // define break and continue labels + if((lab = stmtlabel(n)) != L) { + lab->breakpc = breakpc; + lab->continpc = continpc; + } + gen(n->nincr); // contin: incr + patch(p1, pc); // test: + bgen(n->ntest, 0, breakpc); // if(!test) goto break + genlist(n->nbody); // body + gjmp(continpc); + patch(breakpc, pc); // done: + continpc = scontin; + breakpc = sbreak; + if(lab) { + lab->breakpc = P; + lab->continpc = P; + } + break; + + case OIF: + p1 = gjmp(P); // goto test + p2 = gjmp(P); // p2: goto else + patch(p1, pc); // test: + bgen(n->ntest, 0, p2); // if(!test) goto p2 + genlist(n->nbody); // then + p3 = gjmp(P); // goto done + patch(p2, pc); // else: + genlist(n->nelse); // else + patch(p3, pc); // done: + break; + + case OSWITCH: + sbreak = breakpc; + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done + + // define break label + if((lab = stmtlabel(n)) != L) + lab->breakpc = breakpc; + + patch(p1, pc); // test: + genlist(n->nbody); // switch(test) body + patch(breakpc, pc); // done: + breakpc = sbreak; + if(lab != L) + lab->breakpc = P; + break; + + case OSELECT: + sbreak = breakpc; + p1 = gjmp(P); // goto test + breakpc = gjmp(P); // break: goto done + + // define break label + if((lab = stmtlabel(n)) != L) + lab->breakpc = breakpc; + + patch(p1, pc); // test: + genlist(n->nbody); // select() body + patch(breakpc, pc); // done: + breakpc = sbreak; + if(lab != L) + lab->breakpc = P; + break; + + case OASOP: + cgen_asop(n); + break; + + case ODCL: + cgen_dcl(n->left); + break; + + case OAS: + if(gen_as_init(n)) + break; + cgen_as(n->left, n->right); + break; + + case OCALLMETH: + cgen_callmeth(n, 0); + break; + + case OCALLINTER: + cgen_callinter(n, N, 0); + break; + + case OCALLFUNC: + cgen_call(n, 0); + break; + + case OPROC: + cgen_proc(n, 1); + break; + + case ODEFER: + cgen_proc(n, 2); + break; + + case ORETURN: + cgen_ret(n); + break; + } + +ret: + if(anyregalloc() != wasregalloc) { + dump("node", n); + fatal("registers left allocated"); + } + + lineno = lno; +} + +/* + * generate call to non-interface method + * proc=0 normal call + * proc=1 goroutine run in new proc + * proc=2 defer call save away stack + */ +void +cgen_callmeth(Node *n, int proc) +{ + Node *l; + + // generate a rewrite for method call + // (p.f)(...) goes to (f)(p,...) + + l = n->left; + if(l->op != ODOTMETH) + fatal("cgen_callmeth: not dotmethod: %N"); + + n->op = OCALLFUNC; + n->left = n->left->right; + n->left->type = l->type; + + if(n->left->op == ONAME) + n->left->class = PFUNC; + cgen_call(n, proc); +} + +/* + * generate code to start new proc running call n. + */ +void +cgen_proc(Node *n, int proc) +{ + switch(n->left->op) { + default: + fatal("cgen_proc: unknown call %O", n->left->op); + + case OCALLMETH: + cgen_callmeth(n->left, proc); + break; + + case OCALLINTER: + cgen_callinter(n->left, N, proc); + break; + + case OCALLFUNC: + cgen_call(n->left, proc); + break; + } + +} + +/* + * generate declaration. + * nothing to do for on-stack automatics, + * but might have to allocate heap copy + * for escaped variables. + */ +static void +cgen_dcl(Node *n) +{ + if(debug['g']) + dump("\ncgen-dcl", n); + if(n->op != ONAME) { + dump("cgen_dcl", n); + fatal("cgen_dcl"); + } + if(!(n->class & PHEAP)) + return; + if(n->alloc == nil) + n->alloc = callnew(n->type); + cgen_as(n->heapaddr, n->alloc); +} + +/* + * generate discard of value + */ +static void +cgen_discard(Node *nr) +{ + Node tmp; + + if(nr == N) + return; + + switch(nr->op) { + case ONAME: + if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF) + gused(nr); + break; + + // unary + case OADD: + case OAND: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLSH: + case OLT: + case OMOD: + case OMUL: + case ONE: + case OOR: + case ORSH: + case OSUB: + case OXOR: + cgen_discard(nr->left); + cgen_discard(nr->right); + break; + + // binary + case OCAP: + case OCOM: + case OLEN: + case OMINUS: + case ONOT: + case OPLUS: + cgen_discard(nr->left); + break; + + // special enough to just evaluate + default: + tempname(&tmp, nr->type); + cgen_as(&tmp, nr); + gused(&tmp); + } +} + +/* + * generate assignment: + * nl = nr + * nr == N means zero nl. + */ +void +cgen_as(Node *nl, Node *nr) +{ + Node nc; + Type *tl; + int iszer; + + if(nl == N) + return; + + if(debug['g']) { + dump("cgen_as", nl); + dump("cgen_as = ", nr); + } + + if(isblank(nl)) { + cgen_discard(nr); + return; + } + + iszer = 0; + if(nr == N || isnil(nr)) { + // externals and heaps should already be clear + if(nr == N) { + if(nl->class == PEXTERN) + return; + if(nl->class & PHEAP) + return; + } + + tl = nl->type; + if(tl == T) + return; + if(isfat(tl)) { + clearfat(nl); + goto ret; + } + + /* invent a "zero" for the rhs */ + iszer = 1; + nr = &nc; + memset(nr, 0, sizeof(*nr)); + switch(simtype[tl->etype]) { + default: + fatal("cgen_as: tl %T", tl); + break; + + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + nr->val.u.xval = mal(sizeof(*nr->val.u.xval)); + mpmovecfix(nr->val.u.xval, 0); + nr->val.ctype = CTINT; + break; + + case TFLOAT32: + case TFLOAT64: + nr->val.u.fval = mal(sizeof(*nr->val.u.fval)); + mpmovecflt(nr->val.u.fval, 0.0); + nr->val.ctype = CTFLT; + break; + + case TBOOL: + nr->val.u.bval = 0; + nr->val.ctype = CTBOOL; + break; + + case TPTR32: + case TPTR64: + nr->val.ctype = CTNIL; + break; + + case TCOMPLEX64: + case TCOMPLEX128: + nr->val.u.cval = mal(sizeof(*nr->val.u.cval)); + mpmovecflt(&nr->val.u.cval->real, 0.0); + mpmovecflt(&nr->val.u.cval->imag, 0.0); + break; + } + nr->op = OLITERAL; + nr->type = tl; + nr->addable = 1; + ullmancalc(nr); + } + + tl = nl->type; + if(tl == T) + return; + + cgen(nr, nl); + if(iszer && nl->addable) + gused(nl); + +ret: + ; +} + +/* + * gather series of offsets + * >=0 is direct addressed field + * <0 is pointer to next field (+1) + */ +int +dotoffset(Node *n, int *oary, Node **nn) +{ + int i; + + switch(n->op) { + case ODOT: + if(n->xoffset == BADWIDTH) { + dump("bad width in dotoffset", n); + fatal("bad width in dotoffset"); + } + i = dotoffset(n->left, oary, nn); + if(i > 0) { + if(oary[i-1] >= 0) + oary[i-1] += n->xoffset; + else + oary[i-1] -= n->xoffset; + break; + } + if(i < 10) + oary[i++] = n->xoffset; + break; + + case ODOTPTR: + if(n->xoffset == BADWIDTH) { + dump("bad width in dotoffset", n); + fatal("bad width in dotoffset"); + } + i = dotoffset(n->left, oary, nn); + if(i < 10) + oary[i++] = -(n->xoffset+1); + break; + + default: + *nn = n; + return 0; + } + if(i >= 10) + *nn = N; + return i; +} + +/* + * make a new off the books + */ +void +tempname(Node *nn, Type *t) +{ + Node *n; + Sym *s; + uint32 w; + + if(stksize < 0) + fatal("tempname not during code generation"); + + if (curfn == N) + fatal("no curfn for tempname"); + + if(t == T) { + yyerror("tempname called with nil type"); + t = types[TINT32]; + } + + // give each tmp a different name so that there + // a chance to registerizer them + snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen); + statuniqgen++; + s = lookup(namebuf); + n = nod(ONAME, N, N); + n->sym = s; + n->type = t; + n->class = PAUTO; + n->addable = 1; + n->ullman = 1; + n->noescape = 1; + n->curfn = curfn; + curfn->dcl = list(curfn->dcl, n); + + dowidth(t); + w = t->width; + stksize += w; + stksize = rnd(stksize, t->align); + if(thechar == '5') + stksize = rnd(stksize, widthptr); + n->xoffset = -stksize; + + // print("\ttmpname (%d): %N\n", stksize, n); + + *nn = *n; +} diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors new file mode 100644 index 000000000..b5af4678c --- /dev/null +++ b/src/cmd/gc/go.errors @@ -0,0 +1,70 @@ +// Copyright 2010 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. + +// Example-based syntax error messages. +// See bisonerrors, Makefile, go.y. + +static struct { + int yystate; + int yychar; + char *msg; +} yymsg[] = { + // Each line of the form % token list + // is converted by bisonerrors into the yystate and yychar caused + // by that token list. + + % loadsys package LIMPORT '(' LLITERAL import_package import_there ',' + "unexpected comma during import block", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';' + "unexpected semicolon or newline before {", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';' + "unexpected semicolon or newline before {", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';' + "unexpected semicolon or newline before {", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY + "unexpected semicolon or newline before {", + + % loadsys package imports LFUNC LNAME '(' ')' ';' '{' + "unexpected semicolon or newline before {", + + % loadsys package imports LTYPE LNAME ';' + "unexpected semicolon or newline in type declaration", + + % loadsys package imports LCHAN '}' + "unexpected } in channel type", + + % loadsys package imports LCHAN ')' + "unexpected ) in channel type", + + % loadsys package imports LCHAN ',' + "unexpected comma in channel type", + + % loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE + "unexpected semicolon or newline before else", + + % loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME + "name list not allowed in interface type", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME + "var declaration not allowed in for initializer", + + % loadsys package imports LVAR LNAME '[' ']' LNAME '{' + "unexpected { at end of statement", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{' + "unexpected { at end of statement", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';' + "argument to go/defer must be function call", + + % loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';' + "need trailing comma before newline in composite literal", + + % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME + "nested func not allowed", +}; diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h new file mode 100644 index 000000000..da0fb5146 --- /dev/null +++ b/src/cmd/gc/go.h @@ -0,0 +1,1279 @@ +// 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 <u.h> +#include <libc.h> +#include <bio.h> + +#undef OAPPEND + +// avoid <ctype.h> +#undef isblank +#define isblank goisblank + +#ifndef EXTERN +#define EXTERN extern +#endif + +#undef BUFSIZ + +enum +{ + NHUNK = 50000, + BUFSIZ = 8192, + NSYMB = 500, + NHASH = 1024, + STRINGSZ = 200, + YYMAXDEPTH = 500, + MAXALIGN = 7, + UINF = 100, + HISTSZ = 10, + + PRIME1 = 3, + + AUNK = 100, + + // these values are known by runtime + AMEM = 0, + ANOEQ, + ASTRING, + AINTER, + ANILINTER, + ASLICE, + AMEM8, + AMEM16, + AMEM32, + AMEM64, + AMEM128, + ANOEQ8, + ANOEQ16, + ANOEQ32, + ANOEQ64, + ANOEQ128, + + BADWIDTH = -1000000000, +}; + +extern vlong MAXWIDTH; + +/* + * note this is the representation + * of the compilers string literals, + * it is not the runtime representation + */ +typedef struct Strlit Strlit; +struct Strlit +{ + int32 len; + char s[3]; // variable +}; + +/* + * note this is the runtime representation + * of hashmap iterator. it is probably + * insafe to use it this way, but it puts + * all the changes in one place. + * only flag is referenced from go. + * actual placement does not matter as long + * as the size is >= actual size. + */ +typedef struct Hiter Hiter; +struct Hiter +{ + uchar data[8]; // return val from next + int32 elemsize; // size of elements in table */ + int32 changes; // number of changes observed last time */ + int32 i; // stack pointer in subtable_state */ + uchar last[8]; // last hash value returned */ + uchar h[8]; // the hash table */ + struct + { + uchar sub[8]; // pointer into subtable */ + uchar start[8]; // pointer into start of subtable */ + uchar end[8]; // pointer into end of subtable */ + uchar pad[8]; + } sub[4]; +}; + +enum +{ + Mpscale = 29, // safely smaller than bits in a long + Mpprec = 16, // Mpscale*Mpprec is max number of bits + Mpnorm = Mpprec - 1, // significant words in a normalized float + Mpbase = 1L << Mpscale, + Mpsign = Mpbase >> 1, + Mpmask = Mpbase - 1, + Mpdebug = 0, +}; + +typedef struct Mpint Mpint; +struct Mpint +{ + long a[Mpprec]; + uchar neg; + uchar ovf; +}; + +typedef struct Mpflt Mpflt; +struct Mpflt +{ + Mpint val; + short exp; +}; + +typedef struct Mpcplx Mpcplx; +struct Mpcplx +{ + Mpflt real; + Mpflt imag; +}; + +typedef struct Val Val; +struct Val +{ + short ctype; + union + { + short reg; // OREGISTER + short bval; // bool value CTBOOL + Mpint* xval; // int CTINT + Mpflt* fval; // float CTFLT + Mpcplx* cval; // float CTCPLX + Strlit* sval; // string CTSTR + } u; +}; + +typedef struct Pkg Pkg; +typedef struct Sym Sym; +typedef struct Node Node; +typedef struct NodeList NodeList; +typedef struct Type Type; +typedef struct Label Label; + +struct Type +{ + uchar etype; + uchar chan; + uchar recur; // to detect loops + uchar trecur; // to detect loops + uchar printed; + uchar embedded; // TFIELD embedded type + uchar siggen; + uchar funarg; + uchar copyany; + uchar local; // created in this file + uchar deferwidth; + uchar broke; + uchar isddd; // TFIELD is ... argument + uchar align; + + Node* nod; // canonical OTYPE node + Type* orig; // original type (type literal or predefined type) + int lineno; + + // TFUNCT + uchar thistuple; + uchar outtuple; + uchar intuple; + uchar outnamed; + + Type* method; + Type* xmethod; + + Sym* sym; + int32 vargen; // unique name for OTYPE/ONAME + + Node* nname; + vlong argwid; + + // most nodes + Type* type; + vlong width; // offset in TFIELD, width in all others + + // TFIELD + Type* down; // also used in TMAP + Strlit* note; // literal string annotation + + // TARRAY + int32 bound; // negative is dynamic array + + int32 maplineno; // first use of TFORW as map key + int32 embedlineno; // first use of TFORW as embedded type +}; +#define T ((Type*)0) + +struct Node +{ + uchar op; + uchar ullman; // sethi/ullman number + uchar addable; // type of addressability - 0 is not addressable + uchar trecur; // to detect loops + uchar etype; // op for OASOP, etype for OTYPE, exclam for export + uchar class; // PPARAM, PAUTO, PEXTERN, etc + uchar method; // OCALLMETH name + uchar embedded; // ODCLFIELD embedded type + uchar colas; // OAS resulting from := + uchar diag; // already printed error about this + uchar noescape; // ONAME never move to heap + uchar funcdepth; + uchar builtin; // built-in name, like len or close + uchar walkdef; + uchar typecheck; + uchar local; + uchar initorder; + uchar dodata; // compile literal assignment as data statement + uchar used; + uchar isddd; + uchar pun; // don't registerize variable ONAME + uchar readonly; + uchar implicit; // don't show in printout + + // most nodes + Node* left; + Node* right; + Type* type; + Type* realtype; // as determined by typecheck + NodeList* list; + NodeList* rlist; + Node* orig; // original form, for printing, and tracking copies of ONAMEs + + // for-body + NodeList* ninit; + Node* ntest; + Node* nincr; + NodeList* nbody; + + // if-body + NodeList* nelse; + + // cases + Node* ncase; + + // func + Node* nname; + Node* shortname; + NodeList* enter; + NodeList* exit; + NodeList* cvars; // closure params + NodeList* dcl; // autodcl for this func/closure + + // OLITERAL/OREGISTER + Val val; + + // ONAME + Node* ntype; + Node* defn; + Node* pack; // real package for import . names + Node* curfn; // function for local variables + + // ONAME func param with PHEAP + Node* heapaddr; // temp holding heap address of param + Node* stackparam; // OPARAM node referring to stack copy of param + Node* alloc; // allocation call + + // ONAME closure param with PPARAMREF + Node* outer; // outer PPARAMREF in nested closure + Node* closure; // ONAME/PHEAP <-> ONAME/PPARAMREF + + // OPACK + Pkg* pkg; + + Sym* sym; // various + int32 vargen; // unique name for OTYPE/ONAME + int32 lineno; + int32 endlineno; + vlong xoffset; + int32 stkdelta; // offset added by stack frame compaction phase. + int32 ostk; + int32 iota; +}; +#define N ((Node*)0) +EXTERN int32 walkgen; + +struct NodeList +{ + Node* n; + NodeList* next; + NodeList* end; +}; + +enum +{ + SymExport = 1<<0, + SymPackage = 1<<1, + SymExported = 1<<2, + SymUniq = 1<<3, + SymSiggen = 1<<4, +}; + +struct Sym +{ + ushort lexical; + uchar flags; + uchar sym; // huffman encoding in object file + Sym* link; + int32 npkg; // number of imported packages with this name + + // saved and restored by dcopy + Pkg* pkg; + char* name; // variable name + Node* def; // definition: ONAME OTYPE OPACK or OLITERAL + Label* label; // corresponding label (ephemeral) + int32 block; // blocknumber to catch redeclaration + int32 lastlineno; // last declaration for diagnostic +}; +#define S ((Sym*)0) + +EXTERN Sym* dclstack; + +struct Pkg +{ + char* name; + Strlit* path; + Sym* pathsym; + char* prefix; + Pkg* link; + char exported; // import line written in export data + char direct; // imported directly +}; + +typedef struct Iter Iter; +struct Iter +{ + int done; + Type* tfunc; + Type* t; + Node** an; + Node* n; +}; + +typedef struct Hist Hist; +struct Hist +{ + Hist* link; + char* name; + int32 line; + int32 offset; +}; +#define H ((Hist*)0) + +enum +{ + OXXX, + + // names + ONAME, + ONONAME, + OTYPE, + OPACK, + OLITERAL, + + // exprs + OADD, OSUB, OOR, OXOR, OADDSTR, + OADDR, + OANDAND, + OAPPEND, + OARRAY, + OARRAYBYTESTR, OARRAYRUNESTR, + OSTRARRAYBYTE, OSTRARRAYRUNE, + OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP, + OBAD, + OCALL, OCALLFUNC, OCALLMETH, OCALLINTER, + OCAP, + OCLOSE, + OCLOSURE, + OCMPIFACE, OCMPSTR, + OCOMPLIT, OMAPLIT, OSTRUCTLIT, OARRAYLIT, + OCONV, OCONVIFACE, OCONVNOP, + OCOPY, + ODCL, ODCLFUNC, ODCLFIELD, ODCLCONST, ODCLTYPE, + ODOT, ODOTPTR, ODOTMETH, ODOTINTER, OXDOT, + ODOTTYPE, + ODOTTYPE2, + OEQ, ONE, OLT, OLE, OGE, OGT, + OIND, + OINDEX, OINDEXMAP, + OKEY, OPARAM, + OLEN, + OMAKE, OMAKECHAN, OMAKEMAP, OMAKESLICE, + OHMUL, ORRC, OLRC, // high-mul and rotate-carry + OMUL, ODIV, OMOD, OLSH, ORSH, OAND, OANDNOT, + ONEW, + ONOT, OCOM, OPLUS, OMINUS, + OOROR, + OPANIC, OPRINT, OPRINTN, + OPAREN, + OSEND, + OSLICE, OSLICEARR, OSLICESTR, + ORECOVER, + ORECV, + ORUNESTR, + OSELRECV, + OSELRECV2, + OIOTA, + OREAL, OIMAG, OCOMPLEX, + + // stmts + OBLOCK, + OBREAK, + OCASE, OXCASE, + OCONTINUE, + ODEFER, + OEMPTY, + OFALL, OXFALL, + OFOR, + OGOTO, + OIF, + OLABEL, + OPROC, + ORANGE, + ORETURN, + OSELECT, + OSWITCH, + OTYPESW, // l = r.(type) + + // types + OTCHAN, + OTMAP, + OTSTRUCT, + OTINTER, + OTFUNC, + OTARRAY, + OTPAREN, + + // misc + ODDD, + + // for back ends + OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG, + + OEND, +}; +enum +{ + Txxx, // 0 + + TINT8, TUINT8, // 1 + TINT16, TUINT16, + TINT32, TUINT32, + TINT64, TUINT64, + TINT, TUINT, TUINTPTR, + + TCOMPLEX64, // 12 + TCOMPLEX128, + + TFLOAT32, // 14 + TFLOAT64, + + TBOOL, // 16 + + TPTR32, TPTR64, // 17 + + TFUNC, // 19 + TARRAY, + T_old_DARRAY, + TSTRUCT, // 22 + TCHAN, + TMAP, + TINTER, // 25 + TFORW, + TFIELD, + TANY, + TSTRING, + TUNSAFEPTR, + + // pseudo-types for literals + TIDEAL, // 31 + TNIL, + TBLANK, + + // pseudo-type for frame layout + TFUNCARGS, + TCHANARGS, + TINTERMETH, + + NTYPE, +}; +enum +{ + CTxxx, + + CTINT, + CTFLT, + CTCPLX, + CTSTR, + CTBOOL, + CTNIL, +}; + +enum +{ + /* types of channel */ + /* must match ../../pkg/nreflect/type.go:/Chandir */ + Cxxx, + Crecv = 1<<0, + Csend = 1<<1, + Cboth = Crecv | Csend, +}; + +enum +{ + Pxxx, + + PEXTERN, // declaration context + PAUTO, + PPARAM, + PPARAMOUT, + PPARAMREF, // param passed by reference + PFUNC, + + PHEAP = 1<<7, +}; + +enum +{ + Etop = 1<<1, // evaluated at statement level + Erv = 1<<2, // evaluated in value context + Etype = 1<<3, + Ecall = 1<<4, // call-only expressions are ok + Efnstruct = 1<<5, // multivalue function returns are ok + Eiota = 1<<6, // iota is ok + Easgn = 1<<7, // assigning to expression + Eindir = 1<<8, // indirecting through expression + Eaddr = 1<<9, // taking address of expression + Eproc = 1<<10, // inside a go statement + Ecomplit = 1<<11, // type in composite literal +}; + +#define BITS 5 +#define NVAR (BITS*sizeof(uint32)*8) + +typedef struct Bits Bits; +struct Bits +{ + uint32 b[BITS]; +}; + +EXTERN Bits zbits; + +typedef struct Var Var; +struct Var +{ + vlong offset; + Sym* sym; + Sym* gotype; + Node* node; + int width; + char name; + char etype; + char addr; +}; + +EXTERN Var var[NVAR]; + +typedef struct Typedef Typedef; +struct Typedef +{ + char* name; + int etype; + int sameas; +}; + +extern Typedef typedefs[]; + +typedef struct Sig Sig; +struct Sig +{ + char* name; + Pkg* pkg; + Sym* isym; + Sym* tsym; + Type* type; + Type* mtype; + int32 offset; + Sig* link; +}; + +typedef struct Io Io; +struct Io +{ + char* infile; + Biobuf* bin; + int32 ilineno; + int nlsemi; + int eofnl; + int peekc; + int peekc1; // second peekc for ... + char* cp; // used for content when bin==nil + int importsafe; +}; + +typedef struct Dlist Dlist; +struct Dlist +{ + Type* field; +}; + +typedef struct Idir Idir; +struct Idir +{ + Idir* link; + char* dir; +}; + +/* + * argument passing to/from + * smagic and umagic + */ +typedef struct Magic Magic; +struct Magic +{ + int w; // input for both - width + int s; // output for both - shift + int bad; // output for both - unexpected failure + + // magic multiplier for signed literal divisors + int64 sd; // input - literal divisor + int64 sm; // output - multiplier + + // magic multiplier for unsigned literal divisors + uint64 ud; // input - literal divisor + uint64 um; // output - multiplier + int ua; // output - adder +}; + +typedef struct Prog Prog; + +struct Label +{ + uchar used; + Sym* sym; + Node* def; + NodeList* use; + Label* link; + + // for use during gen + Prog* gotopc; // pointer to unresolved gotos + Prog* labelpc; // pointer to code + Prog* breakpc; // pointer to code + Prog* continpc; // pointer to code +}; +#define L ((Label*)0) + +/* + * note this is the runtime representation + * of the compilers arrays. + * + * typedef struct + * { // must not move anything + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * uchar cap[4]; // allocated number of elements + * } Array; + */ +EXTERN int Array_array; // runtime offsetof(Array,array) - same for String +EXTERN int Array_nel; // runtime offsetof(Array,nel) - same for String +EXTERN int Array_cap; // runtime offsetof(Array,cap) +EXTERN int sizeof_Array; // runtime sizeof(Array) + + +/* + * note this is the runtime representation + * of the compilers strings. + * + * typedef struct + * { // must not move anything + * uchar array[8]; // pointer to data + * uchar nel[4]; // number of elements + * } String; + */ +EXTERN int sizeof_String; // runtime sizeof(String) + +EXTERN Dlist dotlist[10]; // size is max depth of embeddeds + +EXTERN Io curio; +EXTERN Io pushedio; +EXTERN int32 lexlineno; +EXTERN int32 lineno; +EXTERN int32 prevlineno; +EXTERN char* pathname; +EXTERN Hist* hist; +EXTERN Hist* ehist; + +EXTERN char* infile; +EXTERN char* outfile; +EXTERN Biobuf* bout; +EXTERN int nerrors; +EXTERN int nsavederrors; +EXTERN int nsyntaxerrors; +EXTERN int safemode; +EXTERN char namebuf[NSYMB]; +EXTERN char lexbuf[NSYMB]; +EXTERN char litbuf[NSYMB]; +EXTERN char debug[256]; +EXTERN Sym* hash[NHASH]; +EXTERN Sym* importmyname; // my name for package +EXTERN Pkg* localpkg; // package being compiled +EXTERN Pkg* importpkg; // package being imported +EXTERN Pkg* structpkg; // package that declared struct, during import +EXTERN Pkg* builtinpkg; // fake package for builtins +EXTERN Pkg* gostringpkg; // fake pkg for Go strings +EXTERN Pkg* runtimepkg; // package runtime +EXTERN Pkg* stringpkg; // fake package for C strings +EXTERN Pkg* typepkg; // fake package for runtime type info +EXTERN Pkg* unsafepkg; // package unsafe +EXTERN Pkg* phash[128]; +EXTERN int tptr; // either TPTR32 or TPTR64 +extern char* runtimeimport; +extern char* unsafeimport; +EXTERN Idir* idirs; + +EXTERN Type* types[NTYPE]; +EXTERN Type* idealstring; +EXTERN Type* idealbool; +EXTERN uchar simtype[NTYPE]; +EXTERN uchar isptr[NTYPE]; +EXTERN uchar isforw[NTYPE]; +EXTERN uchar isint[NTYPE]; +EXTERN uchar isfloat[NTYPE]; +EXTERN uchar iscomplex[NTYPE]; +EXTERN uchar issigned[NTYPE]; +EXTERN uchar issimple[NTYPE]; + +EXTERN uchar okforeq[NTYPE]; +EXTERN uchar okforadd[NTYPE]; +EXTERN uchar okforand[NTYPE]; +EXTERN uchar okfornone[NTYPE]; +EXTERN uchar okforcmp[NTYPE]; +EXTERN uchar okforbool[NTYPE]; +EXTERN uchar okforcap[NTYPE]; +EXTERN uchar okforlen[NTYPE]; +EXTERN uchar okforarith[NTYPE]; +EXTERN uchar okforconst[NTYPE]; +EXTERN uchar* okfor[OEND]; +EXTERN uchar iscmp[OEND]; + +EXTERN Mpint* minintval[NTYPE]; +EXTERN Mpint* maxintval[NTYPE]; +EXTERN Mpflt* minfltval[NTYPE]; +EXTERN Mpflt* maxfltval[NTYPE]; + +EXTERN NodeList* xtop; +EXTERN NodeList* externdcl; +EXTERN NodeList* closures; +EXTERN NodeList* exportlist; +EXTERN NodeList* typelist; +EXTERN int dclcontext; // PEXTERN/PAUTO +EXTERN int incannedimport; +EXTERN int statuniqgen; // name generator for static temps +EXTERN int loophack; + +EXTERN int32 iota; +EXTERN NodeList* lastconst; +EXTERN Node* lasttype; +EXTERN int32 maxarg; +EXTERN int32 stksize; // stack size for current frame +EXTERN int32 blockgen; // max block number +EXTERN int32 block; // current block number +EXTERN int hasdefer; // flag that curfn has defer statetment + +EXTERN Node* curfn; + +EXTERN int widthptr; + +EXTERN Node* typesw; +EXTERN Node* nblank; + +extern int thechar; +extern char* thestring; +EXTERN char* hunk; +EXTERN int32 nhunk; +EXTERN int32 thunk; + +EXTERN int exporting; +EXTERN int erroring; +EXTERN int noargnames; + +EXTERN int funcdepth; +EXTERN int typecheckok; +EXTERN int packagequotes; +EXTERN int longsymnames; +EXTERN int compiling_runtime; + +/* + * y.tab.c + */ +int yyparse(void); + +/* + * align.c + */ +int argsize(Type *t); +void checkwidth(Type *t); +void defercheckwidth(void); +void dowidth(Type *t); +void resumecheckwidth(void); +uint32 rnd(uint32 o, uint32 r); +void typeinit(void); + +/* + * bits.c + */ +int Qconv(Fmt *fp); +Bits band(Bits a, Bits b); +int bany(Bits *a); +int beq(Bits a, Bits b); +int bitno(int32 b); +Bits blsh(uint n); +Bits bnot(Bits a); +int bnum(Bits a); +Bits bor(Bits a, Bits b); +int bset(Bits a, uint n); + +/* + * closure.c + */ +Node* closurebody(NodeList *body); +void closurehdr(Node *ntype); +void typecheckclosure(Node *func, int top); +Node* walkclosure(Node *func, NodeList **init); +void walkcallclosure(Node *n, NodeList **init); + +/* + * const.c + */ +int cmpslit(Node *l, Node *r); +int consttype(Node *n); +void convconst(Node *con, Type *t, Val *val); +void convlit(Node **np, Type *t); +void convlit1(Node **np, Type *t, int explicit); +void defaultlit(Node **np, Type *t); +void defaultlit2(Node **lp, Node **rp, int force); +void evconst(Node *n); +int isconst(Node *n, int ct); +Node* nodcplxlit(Val r, Val i); +Node* nodlit(Val v); +long nonnegconst(Node *n); +void overflow(Val v, Type *t); +int smallintconst(Node *n); +Val toint(Val v); +Mpflt* truncfltlit(Mpflt *oldv, Type *t); + +/* + * cplx.c + */ +void complexadd(int op, Node *nl, Node *nr, Node *res); +void complexbool(int op, Node *nl, Node *nr, int true, Prog *to); +void complexgen(Node *n, Node *res); +void complexminus(Node *nl, Node *res); +void complexmove(Node *f, Node *t); +void complexmul(Node *nl, Node *nr, Node *res); +int complexop(Node *n, Node *res); +void nodfconst(Node *n, Type *t, Mpflt* fval); + +/* + * dcl.c + */ +void addmethod(Sym *sf, Type *t, int local); +void addvar(Node *n, Type *t, int ctxt); +NodeList* checkarglist(NodeList *all, int input); +Node* colas(NodeList *left, NodeList *right); +void colasdefn(NodeList *left, Node *defn); +NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); +Node* dclname(Sym *s); +void declare(Node *n, int ctxt); +Type* dostruct(NodeList *l, int et); +void dumpdcl(char *st); +Node* embedded(Sym *s); +Node* fakethis(void); +void funcbody(Node *n); +void funccompile(Node *n, int isclosure); +void funchdr(Node *n); +Type* functype(Node *this, NodeList *in, NodeList *out); +void ifacedcl(Node *n); +int isifacemethod(Type *f); +void markdcl(void); +Node* methodname(Node *n, Type *t); +Node* methodname1(Node *n, Node *t); +Sym* methodsym(Sym *nsym, Type *t0, int iface); +Node* newname(Sym *s); +Type* newtype(Sym *s); +Node* oldname(Sym *s); +void popdcl(void); +void poptodcl(void); +void redeclare(Sym *s, char *where); +void testdclstack(void); +Node* typedcl0(Sym *s); +Node* typedcl1(Node *n, Node *t, int local); +void typedcl2(Type *pt, Type *t); +Node* typenod(Type *t); +NodeList* variter(NodeList *vl, Node *t, NodeList *el); + +/* + * export.c + */ +void autoexport(Node *n, int ctxt); +void dumpexport(void); +int exportname(char *s); +void exportsym(Node *n); +void importconst(Sym *s, Type *t, Node *n); +void importmethod(Sym *s, Type *t); +Sym* importsym(Sym *s, int op); +void importtype(Type *pt, Type *t); +void importvar(Sym *s, Type *t, int ctxt); +Type* pkgtype(Sym *s); + +/* + * gen.c + */ +void allocparams(void); +void cgen_as(Node *nl, Node *nr); +void cgen_callmeth(Node *n, int proc); +void clearlabels(void); +void checklabels(void); +int dotoffset(Node *n, int *oary, Node **nn); +void gen(Node *n); +void genlist(NodeList *l); +Node* sysfunc(char *name); +void tempname(Node *n, Type *t); + +/* + * init.c + */ +void fninit(NodeList *n); +Node* renameinit(Node *n); + +/* + * lex.c + */ +void cannedimports(char *file, char *cp); +void importfile(Val *f, int line); +char* lexname(int lex); +void mkpackage(char* pkgname); +void unimportfile(void); +int32 yylex(void); +extern int windows; +extern int yylast; +extern int yyprev; + +/* + * mparith1.c + */ +int Bconv(Fmt *fp); +int Fconv(Fmt *fp); +void mpaddcfix(Mpint *a, vlong c); +void mpaddcflt(Mpflt *a, double c); +void mpatofix(Mpint *a, char *as); +void mpatoflt(Mpflt *a, char *as); +int mpcmpfixc(Mpint *b, vlong c); +int mpcmpfixfix(Mpint *a, Mpint *b); +int mpcmpfixflt(Mpint *a, Mpflt *b); +int mpcmpfltc(Mpflt *b, double c); +int mpcmpfltfix(Mpflt *a, Mpint *b); +int mpcmpfltflt(Mpflt *a, Mpflt *b); +void mpcomfix(Mpint *a); +void mpdivfixfix(Mpint *a, Mpint *b); +void mpmodfixfix(Mpint *a, Mpint *b); +void mpmovefixfix(Mpint *a, Mpint *b); +void mpmovefixflt(Mpflt *a, Mpint *b); +int mpmovefltfix(Mpint *a, Mpflt *b); +void mpmovefltflt(Mpflt *a, Mpflt *b); +void mpmulcfix(Mpint *a, vlong c); +void mpmulcflt(Mpflt *a, double c); +void mpsubfixfix(Mpint *a, Mpint *b); +void mpsubfltflt(Mpflt *a, Mpflt *b); + +/* + * mparith2.c + */ +void mpaddfixfix(Mpint *a, Mpint *b); +void mpandfixfix(Mpint *a, Mpint *b); +void mpandnotfixfix(Mpint *a, Mpint *b); +void mpdivfract(Mpint *a, Mpint *b); +void mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d); +vlong mpgetfix(Mpint *a); +void mplshfixfix(Mpint *a, Mpint *b); +void mpmovecfix(Mpint *a, vlong c); +void mpmulfixfix(Mpint *a, Mpint *b); +void mpmulfract(Mpint *a, Mpint *b); +void mpnegfix(Mpint *a); +void mporfixfix(Mpint *a, Mpint *b); +void mprshfixfix(Mpint *a, Mpint *b); +void mpshiftfix(Mpint *a, int s); +int mptestfix(Mpint *a); +void mpxorfixfix(Mpint *a, Mpint *b); + +/* + * mparith3.c + */ +void mpaddfltflt(Mpflt *a, Mpflt *b); +void mpdivfltflt(Mpflt *a, Mpflt *b); +double mpgetflt(Mpflt *a); +void mpmovecflt(Mpflt *a, double c); +void mpmulfltflt(Mpflt *a, Mpflt *b); +void mpnegflt(Mpflt *a); +void mpnorm(Mpflt *a); +int mptestflt(Mpflt *a); +int sigfig(Mpflt *a); + +/* + * obj.c + */ +void Bputname(Biobuf *b, Sym *s); +int duint16(Sym *s, int off, uint16 v); +int duint32(Sym *s, int off, uint32 v); +int duint64(Sym *s, int off, uint64 v); +int duint8(Sym *s, int off, uint8 v); +int duintptr(Sym *s, int off, uint64 v); +int dsname(Sym *s, int off, char *dat, int ndat); +void dumpobj(void); +void ieeedtod(uint64 *ieee, double native); +Sym* stringsym(char*, int); + +/* + * print.c + */ +void exprfmt(Fmt *f, Node *n, int prec); +void exprlistfmt(Fmt *f, NodeList *l); + +/* + * range.c + */ +void typecheckrange(Node *n); +void walkrange(Node *n); + +/* + * reflect.c + */ +void dumptypestructs(void); +Type* methodfunc(Type *f, Type*); +Node* typename(Type *t); +Sym* typesym(Type *t); + +/* + * select.c + */ +void typecheckselect(Node *sel); +void walkselect(Node *sel); + +/* + * sinit.c + */ +void anylit(int, Node *n, Node *var, NodeList **init); +int gen_as_init(Node *n); +NodeList* initfix(NodeList *l); +int oaslit(Node *n, NodeList **init); +int stataddr(Node *nam, Node *n); + +/* + * subr.c + */ +int Econv(Fmt *fp); +int Jconv(Fmt *fp); +int Lconv(Fmt *fp); +int Nconv(Fmt *fp); +int Oconv(Fmt *fp); +int Sconv(Fmt *fp); +int Tconv(Fmt *fp); +int Tpretty(Fmt *fp, Type *t); +int Zconv(Fmt *fp); +Node* adddot(Node *n); +int adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase); +Type* aindex(Node *b, Type *t); +int algtype(Type *t); +void argtype(Node *on, Type *t); +Node* assignconv(Node *n, Type *t, char *context); +int assignop(Type *src, Type *dst, char **why); +void badtype(int o, Type *tl, Type *tr); +int brcom(int a); +int brrev(int a); +NodeList* concat(NodeList *a, NodeList *b); +int convertop(Type *src, Type *dst, char **why); +int count(NodeList *l); +int cplxsubtype(int et); +void dump(char *s, Node *n); +void dumplist(char *s, NodeList *l); +int eqtype(Type *t1, Type *t2); +int eqtypenoname(Type *t1, Type *t2); +void errorexit(void); +void expandmeth(Sym *s, Type *t); +void fatal(char *fmt, ...); +void flusherrors(void); +void frame(int context); +Type* funcfirst(Iter *s, Type *t); +Type* funcnext(Iter *s); +void genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface); +Type** getinarg(Type *t); +Type* getinargx(Type *t); +Type** getoutarg(Type *t); +Type* getoutargx(Type *t); +Type** getthis(Type *t); +Type* getthisx(Type *t); +int implements(Type *t, Type *iface, Type **missing, Type **have, int *ptr); +void importdot(Pkg *opkg, Node *pack); +int is64(Type *t); +int isblank(Node *n); +int isfixedarray(Type *t); +int isideal(Type *t); +int isinter(Type *t); +int isnil(Node *n); +int isnilinter(Type *t); +int isptrto(Type *t, int et); +int isslice(Type *t); +int istype(Type *t, int et); +void linehist(char *file, int32 off, int relative); +NodeList* list(NodeList *l, Node *n); +NodeList* list1(Node *n); +void listsort(NodeList**, int(*f)(Node*, Node*)); +Node* liststmt(NodeList *l); +NodeList* listtreecopy(NodeList *l); +Sym* lookup(char *name); +void* mal(int32 n); +Type* maptype(Type *key, Type *val); +Type* methtype(Type *t); +Pkg* mkpkg(Strlit *path); +Sym* ngotype(Node *n); +int noconv(Type *t1, Type *t2); +Node* nod(int op, Node *nleft, Node *nright); +Node* nodbool(int b); +void nodconst(Node *n, Type *t, int64 v); +Node* nodintconst(int64 v); +Node* nodfltconst(Mpflt *v); +Node* nodnil(void); +int parserline(void); +Sym* pkglookup(char *name, Pkg *pkg); +int powtwo(Node *n); +Type* ptrto(Type *t); +void* remal(void *p, int32 on, int32 n); +Sym* restrictlookup(char *name, Pkg *pkg); +Node* safeexpr(Node *n, NodeList **init); +void saveerrors(void); +Node* cheapexpr(Node *n, NodeList **init); +Node* localexpr(Node *n, Type *t, NodeList **init); +int32 setlineno(Node *n); +void setmaxarg(Type *t); +Type* shallow(Type *t); +int simsimtype(Type *t); +void smagic(Magic *m); +Type* sortinter(Type *t); +uint32 stringhash(char *p); +Strlit* strlit(char *s); +int structcount(Type *t); +Type* structfirst(Iter *s, Type **nn); +Type* structnext(Iter *s); +Node* syslook(char *name, int copy); +Type* tounsigned(Type *t); +Node* treecopy(Node *n); +Type* typ(int et); +uint32 typehash(Type *t); +void ullmancalc(Node *n); +void umagic(Magic *m); +void warn(char *fmt, ...); +void yyerror(char *fmt, ...); +void yyerrorl(int line, char *fmt, ...); + +/* + * swt.c + */ +void typecheckswitch(Node *n); +void walkswitch(Node *sw); + +/* + * typecheck.c + */ +int exportassignok(Type *t, char *desc); +int islvalue(Node *n); +Node* typecheck(Node **np, int top); +void typechecklist(NodeList *l, int top); +Node* typecheckdef(Node *n); +void resumetypecopy(void); +void copytype(Node *n, Type *t); +void defertypecopy(Node *n, Type *t); +void queuemethod(Node *n); + +/* + * unsafe.c + */ +Node* unsafenmagic(Node *n); + +/* + * walk.c + */ +Node* callnew(Type *t); +Node* chanfn(char *name, int n, Type *t); +Node* mkcall(char *name, Type *t, NodeList **init, ...); +Node* mkcall1(Node *fn, Type *t, NodeList **init, ...); +int vmatch1(Node *l, Node *r); +void walk(Node *fn); +void walkexpr(Node **np, NodeList **init); +void walkexprlist(NodeList *l, NodeList **init); +void walkexprlistsafe(NodeList *l, NodeList **init); +void walkstmt(Node **np); +void walkstmtlist(NodeList *l); + +/* + * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c + */ +#define P ((Prog*)0) + +typedef struct Plist Plist; +struct Plist +{ + Node* name; + Prog* firstpc; + int recur; + Plist* link; +}; + +EXTERN Plist* plist; +EXTERN Plist* plast; + +EXTERN Prog* continpc; +EXTERN Prog* breakpc; +EXTERN Prog* pc; +EXTERN Prog* firstpc; +EXTERN Prog* retpc; + +EXTERN Node* nodfp; + +int anyregalloc(void); +void betypeinit(void); +void bgen(Node *n, int true, Prog *to); +void cgen(Node*, Node*); +void cgen_asop(Node *n); +void cgen_call(Node *n, int proc); +void cgen_callinter(Node *n, Node *res, int proc); +void cgen_ret(Node *n); +void clearfat(Node *n); +void compile(Node*); +void defframe(Prog*); +int dgostringptr(Sym*, int off, char *str); +int dgostrlitptr(Sym*, int off, Strlit*); +int dstringptr(Sym *s, int off, char *str); +int dsymptr(Sym *s, int off, Sym *x, int xoff); +int duintxx(Sym *s, int off, uint64 v, int wid); +void dumpdata(void); +void dumpfuncs(void); +void fixautoused(Prog*); +void gdata(Node*, Node*, int); +void gdatacomplex(Node*, Mpcplx*); +void gdatastring(Node*, Strlit*); +void genembedtramp(Type*, Type*, Sym*, int iface); +void ggloblnod(Node *nam, int32 width); +void ggloblsym(Sym *s, int32 width, int dupok); +Prog* gjmp(Prog*); +void gused(Node*); +int isfat(Type*); +void markautoused(Prog*); +Plist* newplist(void); +Node* nodarg(Type*, int); +void nopout(Prog*); +void patch(Prog*, Prog*); +Prog* unpatch(Prog*); +void zfile(Biobuf *b, char *p, int n); +void zhist(Biobuf *b, int line, vlong offset); +void zname(Biobuf *b, Sym *s, int t); +void data(void); +void text(void); + diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y new file mode 100644 index 000000000..4c7fe6068 --- /dev/null +++ b/src/cmd/gc/go.y @@ -0,0 +1,1985 @@ +// 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. + +/* + * Go language grammar. + * + * The Go semicolon rules are: + * + * 1. all statements and declarations are terminated by semicolons. + * 2. semicolons can be omitted before a closing ) or }. + * 3. semicolons are inserted by the lexer before a newline + * following a specific list of tokens. + * + * Rules #1 and #2 are accomplished by writing the lists as + * semicolon-separated lists with an optional trailing semicolon. + * Rule #3 is implemented in yylex. + */ + +%{ +#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */ +#include "go.h" + +static void fixlbrace(int); +%} +%union { + Node* node; + NodeList* list; + Type* type; + Sym* sym; + struct Val val; + int lint; +} + +// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /' + +%token <val> LLITERAL +%token <lint> LASOP +%token <sym> LBREAK LCASE LCHAN LCOLAS LCONST LCONTINUE LDDD +%token <sym> LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO +%token <sym> LIF LIMPORT LINTERFACE LMAP LNAME +%token <sym> LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH +%token <sym> LTYPE LVAR + +%token LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT +%token LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH + +%type <lint> lbrace import_here +%type <sym> sym packname +%type <val> oliteral + +%type <node> stmt ntype +%type <node> arg_type +%type <node> case caseblock +%type <node> compound_stmt dotname embed expr complitexpr +%type <node> expr_or_type +%type <node> fndcl fnliteral +%type <node> for_body for_header for_stmt if_header if_stmt non_dcl_stmt +%type <node> interfacedcl keyval labelname name +%type <node> name_or_type non_expr_type +%type <node> new_name dcl_name oexpr typedclname +%type <node> onew_name +%type <node> osimple_stmt pexpr pexpr_no_paren +%type <node> pseudocall range_stmt select_stmt +%type <node> simple_stmt +%type <node> switch_stmt uexpr +%type <node> xfndcl typedcl + +%type <list> xdcl fnbody fnres loop_body dcl_name_list +%type <list> new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list +%type <list> oexpr_list caseblock_list stmt_list oarg_type_list_ocomma arg_type_list +%type <list> interfacedcl_list vardcl vardcl_list structdcl structdcl_list +%type <list> common_dcl constdcl constdcl1 constdcl_list typedcl_list + +%type <node> convtype comptype dotdotdot +%type <node> indcl interfacetype structtype ptrtype +%type <node> recvchantype non_recvchantype othertype fnret_type fntype + +%type <val> hidden_tag + +%type <sym> hidden_importsym hidden_pkg_importsym + +%type <node> hidden_constant hidden_literal hidden_dcl +%type <node> hidden_interfacedcl hidden_structdcl hidden_opt_sym + +%type <list> hidden_funres +%type <list> ohidden_funres +%type <list> hidden_funarg_list ohidden_funarg_list +%type <list> hidden_interfacedcl_list ohidden_interfacedcl_list +%type <list> hidden_structdcl_list ohidden_structdcl_list + +%type <type> hidden_type hidden_type_misc hidden_pkgtype +%type <type> hidden_type_func +%type <type> hidden_type_recv_chan hidden_type_non_recv_chan + +%left LCOMM /* outside the usual hierarchy; here for good error messages */ + +%left LOROR +%left LANDAND +%left LEQ LNE LLE LGE LLT LGT +%left '+' '-' '|' '^' +%left '*' '/' '%' '&' LLSH LRSH LANDNOT + +/* + * manual override of shift/reduce conflicts. + * the general form is that we assign a precedence + * to the token being shifted and then introduce + * NotToken with lower precedence or PreferToToken with higher + * and annotate the reducing rule accordingly. + */ +%left NotPackage +%left LPACKAGE + +%left NotParen +%left '(' + +%left ')' +%left PreferToRightParen + +%error-verbose + +%% +file: + loadsys + package + imports + xdcl_list + { + xtop = concat(xtop, $4); + } + +package: + %prec NotPackage + { + prevlineno = lineno; + yyerror("package statement must be first"); + flusherrors(); + mkpackage("main"); + } +| LPACKAGE sym ';' + { + mkpackage($2->name); + } + +/* + * this loads the definitions for the low-level runtime functions, + * so that the compiler can generate calls to them, + * but does not make the name "runtime" visible as a package. + */ +loadsys: + { + importpkg = runtimepkg; + + if(debug['A']) + cannedimports("runtime.builtin", "package runtime\n\n$$\n\n"); + else + cannedimports("runtime.builtin", runtimeimport); + curio.importsafe = 1; + } + import_package + import_there + { + importpkg = nil; + } + +imports: +| imports import ';' + +import: + LIMPORT import_stmt +| LIMPORT '(' import_stmt_list osemi ')' +| LIMPORT '(' ')' + +import_stmt: + import_here import_package import_there + { + Pkg *ipkg; + Sym *my; + Node *pack; + + ipkg = importpkg; + my = importmyname; + importpkg = nil; + importmyname = S; + + if(my == nil) + my = lookup(ipkg->name); + + pack = nod(OPACK, N, N); + pack->sym = my; + pack->pkg = ipkg; + pack->lineno = $1; + + if(my->name[0] == '.') { + importdot(ipkg, pack); + break; + } + if(my->name[0] == '_' && my->name[1] == '\0') + break; + if(my->def) { + lineno = $1; + redeclare(my, "as imported package name"); + } + my->def = pack; + my->lastlineno = $1; + my->block = 1; // at top level + } + + +import_stmt_list: + import_stmt +| import_stmt_list ';' import_stmt + +import_here: + LLITERAL + { + // import with original name + $$ = parserline(); + importmyname = S; + importfile(&$1, $$); + } +| sym LLITERAL + { + // import with given name + $$ = parserline(); + importmyname = $1; + importfile(&$2, $$); + } +| '.' LLITERAL + { + // import into my name space + $$ = parserline(); + importmyname = lookup("."); + importfile(&$2, $$); + } + +import_package: + LPACKAGE sym import_safety ';' + { + if(importpkg->name == nil) { + importpkg->name = $2->name; + pkglookup($2->name, nil)->npkg++; + } else if(strcmp(importpkg->name, $2->name) != 0) + yyerror("conflicting names %s and %s for package %Z", importpkg->name, $2->name, importpkg->path); + importpkg->direct = 1; + + if(safemode && !curio.importsafe) + yyerror("cannot import unsafe package %Z", importpkg->path); + } + +import_safety: +| LNAME + { + if(strcmp($1->name, "safe") == 0) + curio.importsafe = 1; + } + +import_there: + { + defercheckwidth(); + } + hidden_import_list '$' '$' + { + resumecheckwidth(); + unimportfile(); + } + +/* + * declarations + */ +xdcl: + { + yyerror("empty top-level declaration"); + $$ = nil; + } +| common_dcl +| xfndcl + { + $$ = list1($1); + } +| non_dcl_stmt + { + yyerror("non-declaration statement outside function body"); + $$ = nil; + } +| error + { + $$ = nil; + } + +common_dcl: + LVAR vardcl + { + $$ = $2; + } +| LVAR '(' vardcl_list osemi ')' + { + $$ = $3; + } +| LVAR '(' ')' + { + $$ = nil; + } +| lconst constdcl + { + $$ = $2; + iota = -100000; + lastconst = nil; + } +| lconst '(' constdcl osemi ')' + { + $$ = $3; + iota = -100000; + lastconst = nil; + } +| lconst '(' constdcl ';' constdcl_list osemi ')' + { + $$ = concat($3, $5); + iota = -100000; + lastconst = nil; + } +| lconst '(' ')' + { + $$ = nil; + iota = -100000; + } +| LTYPE typedcl + { + $$ = list1($2); + } +| LTYPE '(' typedcl_list osemi ')' + { + $$ = $3; + } +| LTYPE '(' ')' + { + $$ = nil; + } + +lconst: + LCONST + { + iota = 0; + } + +vardcl: + dcl_name_list ntype + { + $$ = variter($1, $2, nil); + } +| dcl_name_list ntype '=' expr_list + { + $$ = variter($1, $2, $4); + } +| dcl_name_list '=' expr_list + { + $$ = variter($1, nil, $3); + } + +constdcl: + dcl_name_list ntype '=' expr_list + { + $$ = constiter($1, $2, $4); + } +| dcl_name_list '=' expr_list + { + $$ = constiter($1, N, $3); + } + +constdcl1: + constdcl +| dcl_name_list ntype + { + $$ = constiter($1, $2, nil); + } +| dcl_name_list + { + $$ = constiter($1, N, nil); + } + +typedclname: + sym + { + // different from dclname because the name + // becomes visible right here, not at the end + // of the declaration. + $$ = typedcl0($1); + } + +typedcl: + typedclname ntype + { + $$ = typedcl1($1, $2, 1); + } + +simple_stmt: + expr + { + $$ = $1; + } +| expr LASOP expr + { + $$ = nod(OASOP, $1, $3); + $$->etype = $2; // rathole to pass opcode + } +| expr_list '=' expr_list + { + if($1->next == nil && $3->next == nil) { + // simple + $$ = nod(OAS, $1->n, $3->n); + break; + } + // multiple + $$ = nod(OAS2, N, N); + $$->list = $1; + $$->rlist = $3; + } +| expr_list LCOLAS expr_list + { + if($3->n->op == OTYPESW) { + Node *n; + + n = N; + if($3->next != nil) + yyerror("expr.(type) must be alone in list"); + if($1->next != nil) + yyerror("argument count mismatch: %d = %d", count($1), 1); + else if($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME) + yyerror("invalid variable name %#N in type switch", $1->n); + else + n = $1->n; + $$ = nod(OTYPESW, n, $3->n->right); + break; + } + $$ = colas($1, $3); + } +| expr LINC + { + $$ = nod(OASOP, $1, nodintconst(1)); + $$->etype = OADD; + } +| expr LDEC + { + $$ = nod(OASOP, $1, nodintconst(1)); + $$->etype = OSUB; + } + +case: + LCASE expr_or_type_list ':' + { + Node *n; + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = nod(OXCASE, N, N); + $$->list = $2; + if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { + // type switch - declare variable + n = newname(n->sym); + n->used = 1; // TODO(rsc): better job here + declare(n, dclcontext); + $$->nname = n; + } + break; + } +| LCASE expr_or_type_list '=' expr ':' + { + Node *n; + + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = nod(OXCASE, N, N); + if($2->next == nil) + n = nod(OAS, $2->n, $4); + else { + n = nod(OAS2, N, N); + n->list = $2; + n->rlist = list1($4); + } + $$->list = list1(n); + } +| LCASE expr_or_type_list LCOLAS expr ':' + { + // will be converted to OCASE + // right will point to next case + // done in casebody() + markdcl(); + $$ = nod(OXCASE, N, N); + $$->list = list1(colas($2, list1($4))); + } +| LDEFAULT ':' + { + Node *n; + + markdcl(); + $$ = nod(OXCASE, N, N); + if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) { + // type switch - declare variable + n = newname(n->sym); + n->used = 1; // TODO(rsc): better job here + declare(n, dclcontext); + $$->nname = n; + } + } + +compound_stmt: + '{' + { + markdcl(); + } + stmt_list '}' + { + $$ = liststmt($3); + popdcl(); + } + +caseblock: + case + { + // If the last token read by the lexer was consumed + // as part of the case, clear it (parser has cleared yychar). + // If the last token read by the lexer was the lookahead + // leave it alone (parser has it cached in yychar). + // This is so that the stmt_list action doesn't look at + // the case tokens if the stmt_list is empty. + yylast = yychar; + } + stmt_list + { + int last; + + // This is the only place in the language where a statement + // list is not allowed to drop the final semicolon, because + // it's the only place where a statement list is not followed + // by a closing brace. Handle the error for pedantry. + + // Find the final token of the statement list. + // yylast is lookahead; yyprev is last of stmt_list + last = yyprev; + + if(last > 0 && last != ';' && yychar != '}') + yyerror("missing statement after label"); + $$ = $1; + $$->nbody = $3; + popdcl(); + } + +caseblock_list: + { + $$ = nil; + } +| caseblock_list caseblock + { + $$ = list($1, $2); + } + +loop_body: + LBODY + { + markdcl(); + } + stmt_list '}' + { + $$ = $3; + popdcl(); + } + +range_stmt: + expr_list '=' LRANGE expr + { + $$ = nod(ORANGE, N, $4); + $$->list = $1; + $$->etype = 0; // := flag + } +| expr_list LCOLAS LRANGE expr + { + $$ = nod(ORANGE, N, $4); + $$->list = $1; + $$->colas = 1; + colasdefn($1, $$); + } + +for_header: + osimple_stmt ';' osimple_stmt ';' osimple_stmt + { + // init ; test ; incr + if($5 != N && $5->colas != 0) + yyerror("cannot declare in the for-increment"); + $$ = nod(OFOR, N, N); + if($1 != N) + $$->ninit = list1($1); + $$->ntest = $3; + $$->nincr = $5; + } +| osimple_stmt + { + // normal test + $$ = nod(OFOR, N, N); + $$->ntest = $1; + } +| range_stmt + +for_body: + for_header loop_body + { + $$ = $1; + $$->nbody = concat($$->nbody, $2); + } + +for_stmt: + LFOR + { + markdcl(); + } + for_body + { + $$ = $3; + popdcl(); + } + +if_header: + osimple_stmt + { + // test + $$ = nod(OIF, N, N); + $$->ntest = $1; + } +| osimple_stmt ';' osimple_stmt + { + // init ; test + $$ = nod(OIF, N, N); + if($1 != N) + $$->ninit = list1($1); + $$->ntest = $3; + } + +if_stmt: + LIF + { + markdcl(); + } + if_header + { + if($3->ntest == N) + yyerror("missing condition in if statement"); + } + loop_body + { + $$ = $3; + $$->nbody = $5; + // no popdcl; maybe there's an LELSE + } + +switch_stmt: + LSWITCH + { + markdcl(); + } + if_header + { + Node *n; + n = $3->ntest; + if(n != N && n->op != OTYPESW) + n = N; + typesw = nod(OXXX, typesw, n); + } + LBODY caseblock_list '}' + { + $$ = $3; + $$->op = OSWITCH; + $$->list = $6; + typesw = typesw->left; + popdcl(); + } + +select_stmt: + LSELECT + { + typesw = nod(OXXX, typesw, N); + } + LBODY caseblock_list '}' + { + $$ = nod(OSELECT, N, N); + $$->lineno = typesw->lineno; + $$->list = $4; + typesw = typesw->left; + } + +/* + * expressions + */ +expr: + uexpr +| expr LOROR expr + { + $$ = nod(OOROR, $1, $3); + } +| expr LANDAND expr + { + $$ = nod(OANDAND, $1, $3); + } +| expr LEQ expr + { + $$ = nod(OEQ, $1, $3); + } +| expr LNE expr + { + $$ = nod(ONE, $1, $3); + } +| expr LLT expr + { + $$ = nod(OLT, $1, $3); + } +| expr LLE expr + { + $$ = nod(OLE, $1, $3); + } +| expr LGE expr + { + $$ = nod(OGE, $1, $3); + } +| expr LGT expr + { + $$ = nod(OGT, $1, $3); + } +| expr '+' expr + { + $$ = nod(OADD, $1, $3); + } +| expr '-' expr + { + $$ = nod(OSUB, $1, $3); + } +| expr '|' expr + { + $$ = nod(OOR, $1, $3); + } +| expr '^' expr + { + $$ = nod(OXOR, $1, $3); + } +| expr '*' expr + { + $$ = nod(OMUL, $1, $3); + } +| expr '/' expr + { + $$ = nod(ODIV, $1, $3); + } +| expr '%' expr + { + $$ = nod(OMOD, $1, $3); + } +| expr '&' expr + { + $$ = nod(OAND, $1, $3); + } +| expr LANDNOT expr + { + $$ = nod(OANDNOT, $1, $3); + } +| expr LLSH expr + { + $$ = nod(OLSH, $1, $3); + } +| expr LRSH expr + { + $$ = nod(ORSH, $1, $3); + } + /* not an expression anymore, but left in so we can give a good error */ +| expr LCOMM expr + { + $$ = nod(OSEND, $1, $3); + } + +uexpr: + pexpr +| '*' uexpr + { + $$ = nod(OIND, $2, N); + } +| '&' uexpr + { + $$ = nod(OADDR, $2, N); + } +| '+' uexpr + { + $$ = nod(OPLUS, $2, N); + } +| '-' uexpr + { + $$ = nod(OMINUS, $2, N); + } +| '!' uexpr + { + $$ = nod(ONOT, $2, N); + } +| '~' uexpr + { + yyerror("the bitwise complement operator is ^"); + $$ = nod(OCOM, $2, N); + } +| '^' uexpr + { + $$ = nod(OCOM, $2, N); + } +| LCOMM uexpr + { + $$ = nod(ORECV, $2, N); + } + +/* + * call-like statements that + * can be preceded by 'defer' and 'go' + */ +pseudocall: + pexpr '(' ')' + { + $$ = nod(OCALL, $1, N); + } +| pexpr '(' expr_or_type_list ocomma ')' + { + $$ = nod(OCALL, $1, N); + $$->list = $3; + } +| pexpr '(' expr_or_type_list LDDD ocomma ')' + { + $$ = nod(OCALL, $1, N); + $$->list = $3; + $$->isddd = 1; + } + +pexpr_no_paren: + LLITERAL + { + $$ = nodlit($1); + } +| name +| pexpr '.' sym + { + if($1->op == OPACK) { + Sym *s; + s = restrictlookup($3->name, $1->pkg); + $1->used = 1; + $$ = oldname(s); + break; + } + $$ = nod(OXDOT, $1, newname($3)); + } +| pexpr '.' '(' expr_or_type ')' + { + $$ = nod(ODOTTYPE, $1, $4); + } +| pexpr '.' '(' LTYPE ')' + { + $$ = nod(OTYPESW, N, $1); + } +| pexpr '[' expr ']' + { + $$ = nod(OINDEX, $1, $3); + } +| pexpr '[' oexpr ':' oexpr ']' + { + $$ = nod(OSLICE, $1, nod(OKEY, $3, $5)); + } +| pseudocall +| convtype '(' expr ')' + { + // conversion + $$ = nod(OCALL, $1, N); + $$->list = list1($3); + } +| comptype lbrace braced_keyval_list '}' + { + // composite expression + $$ = nod(OCOMPLIT, N, $1); + $$->list = $3; + + fixlbrace($2); + } +| pexpr_no_paren '{' braced_keyval_list '}' + { + // composite expression + $$ = nod(OCOMPLIT, N, $1); + $$->list = $3; + } +| '(' expr_or_type ')' '{' braced_keyval_list '}' + { + yyerror("cannot parenthesize type in composite literal"); + // composite expression + $$ = nod(OCOMPLIT, N, $2); + $$->list = $5; + } +| fnliteral + +keyval: + expr ':' complitexpr + { + $$ = nod(OKEY, $1, $3); + } + +complitexpr: + expr +| '{' braced_keyval_list '}' + { + $$ = nod(OCOMPLIT, N, N); + $$->list = $2; + } + +pexpr: + pexpr_no_paren +| '(' expr_or_type ')' + { + $$ = $2; + + // Need to know on lhs of := whether there are ( ). + // Don't bother with the OPAREN in other cases: + // it's just a waste of memory and time. + switch($$->op) { + case ONAME: + case ONONAME: + case OPACK: + case OTYPE: + case OLITERAL: + $$ = nod(OPAREN, $$, N); + } + } + +expr_or_type: + expr +| non_expr_type %prec PreferToRightParen + +name_or_type: + ntype + +lbrace: + LBODY + { + $$ = LBODY; + } +| '{' + { + $$ = '{'; + } + +/* + * names and types + * newname is used before declared + * oldname is used after declared + */ +new_name: + sym + { + $$ = newname($1); + } + +dcl_name: + sym + { + $$ = dclname($1); + } + +onew_name: + { + $$ = N; + } +| new_name + +sym: + LNAME + +name: + sym %prec NotParen + { + $$ = oldname($1); + if($$->pack != N) + $$->pack->used = 1; + } + +labelname: + new_name + +/* + * to avoid parsing conflicts, type is split into + * channel types + * function types + * parenthesized types + * any other type + * the type system makes additional restrictions, + * but those are not implemented in the grammar. + */ +dotdotdot: + LDDD + { + yyerror("final argument in variadic function missing type"); + $$ = nod(ODDD, typenod(typ(TINTER)), N); + } +| LDDD ntype + { + $$ = nod(ODDD, $2, N); + } + +ntype: + recvchantype +| fntype +| othertype +| ptrtype +| dotname +| '(' ntype ')' + { + $$ = nod(OTPAREN, $2, N); + } + +non_expr_type: + recvchantype +| fntype +| othertype +| '*' non_expr_type + { + $$ = nod(OIND, $2, N); + } + +non_recvchantype: + fntype +| othertype +| ptrtype +| dotname +| '(' ntype ')' + { + $$ = nod(OTPAREN, $2, N); + } + +convtype: + fntype +| othertype + +comptype: + othertype + +fnret_type: + recvchantype +| fntype +| othertype +| ptrtype +| dotname + +dotname: + name +| name '.' sym + { + if($1->op == OPACK) { + Sym *s; + s = restrictlookup($3->name, $1->pkg); + $1->used = 1; + $$ = oldname(s); + break; + } + $$ = nod(OXDOT, $1, newname($3)); + } + +othertype: + '[' oexpr ']' ntype + { + $$ = nod(OTARRAY, $2, $4); + } +| '[' LDDD ']' ntype + { + // array literal of nelem + $$ = nod(OTARRAY, nod(ODDD, N, N), $4); + } +| LCHAN non_recvchantype + { + $$ = nod(OTCHAN, $2, N); + $$->etype = Cboth; + } +| LCHAN LCOMM ntype + { + $$ = nod(OTCHAN, $3, N); + $$->etype = Csend; + } +| LMAP '[' ntype ']' ntype + { + $$ = nod(OTMAP, $3, $5); + } +| structtype +| interfacetype + +ptrtype: + '*' ntype + { + $$ = nod(OIND, $2, N); + } + +recvchantype: + LCOMM LCHAN ntype + { + $$ = nod(OTCHAN, $3, N); + $$->etype = Crecv; + } + +structtype: + LSTRUCT lbrace structdcl_list osemi '}' + { + $$ = nod(OTSTRUCT, N, N); + $$->list = $3; + fixlbrace($2); + } +| LSTRUCT lbrace '}' + { + $$ = nod(OTSTRUCT, N, N); + fixlbrace($2); + } + +interfacetype: + LINTERFACE lbrace interfacedcl_list osemi '}' + { + $$ = nod(OTINTER, N, N); + $$->list = $3; + fixlbrace($2); + } +| LINTERFACE lbrace '}' + { + $$ = nod(OTINTER, N, N); + fixlbrace($2); + } + +/* + * function stuff + * all in one place to show how crappy it all is + */ +xfndcl: + LFUNC fndcl fnbody + { + $$ = $2; + if($$ == N) + break; + $$->nbody = $3; + $$->endlineno = lineno; + funcbody($$); + } + +fndcl: + dcl_name '(' oarg_type_list_ocomma ')' fnres + { + Node *n; + + $3 = checkarglist($3, 1); + $$ = nod(ODCLFUNC, N, N); + $$->nname = $1; + n = nod(OTFUNC, N, N); + n->list = $3; + n->rlist = $5; + if(strcmp($1->sym->name, "init") == 0) { + $$->nname = renameinit($1); + if($3 != nil || $5 != nil) + yyerror("func init must have no arguments and no return values"); + } + if(strcmp(localpkg->name, "main") == 0 && strcmp($1->sym->name, "main") == 0) { + if($3 != nil || $5 != nil) + yyerror("func main must have no arguments and no return values"); + } + // TODO: check if nname already has an ntype + $$->nname->ntype = n; + funchdr($$); + } +| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres + { + Node *rcvr, *t; + Node *name; + + name = newname($4); + $2 = checkarglist($2, 0); + $6 = checkarglist($6, 1); + $$ = N; + if($2 == nil) { + yyerror("method has no receiver"); + break; + } + if($2->next != nil) { + yyerror("method has multiple receivers"); + break; + } + rcvr = $2->n; + if(rcvr->op != ODCLFIELD) { + yyerror("bad receiver in method"); + break; + } + if(rcvr->right->op == OTPAREN || (rcvr->right->op == OIND && rcvr->right->left->op == OTPAREN)) + yyerror("cannot parenthesize receiver type"); + + $$ = nod(ODCLFUNC, N, N); + $$->nname = methodname1(name, rcvr->right); + t = nod(OTFUNC, rcvr, N); + t->list = $6; + t->rlist = $8; + $$->nname->ntype = t; + $$->shortname = name; + funchdr($$); + } + +fntype: + LFUNC '(' oarg_type_list_ocomma ')' fnres + { + $3 = checkarglist($3, 1); + $$ = nod(OTFUNC, N, N); + $$->list = $3; + $$->rlist = $5; + } + +fnbody: + { + $$ = nil; + } +| '{' stmt_list '}' + { + $$ = $2; + if($$ == nil) + $$ = list1(nod(OEMPTY, N, N)); + } + +fnres: + %prec NotParen + { + $$ = nil; + } +| fnret_type + { + $$ = list1(nod(ODCLFIELD, N, $1)); + } +| '(' oarg_type_list_ocomma ')' + { + $2 = checkarglist($2, 0); + $$ = $2; + } + +fnlitdcl: + fntype + { + closurehdr($1); + } + +fnliteral: + fnlitdcl lbrace stmt_list '}' + { + $$ = closurebody($3); + fixlbrace($2); + } +| fnlitdcl error + { + $$ = closurebody(nil); + } + +/* + * lists of things + * note that they are left recursive + * to conserve yacc stack. they need to + * be reversed to interpret correctly + */ +xdcl_list: + { + $$ = nil; + } +| xdcl_list xdcl ';' + { + $$ = concat($1, $2); + if(nsyntaxerrors == 0) + testdclstack(); + } + +vardcl_list: + vardcl +| vardcl_list ';' vardcl + { + $$ = concat($1, $3); + } + +constdcl_list: + constdcl1 +| constdcl_list ';' constdcl1 + { + $$ = concat($1, $3); + } + +typedcl_list: + typedcl + { + $$ = list1($1); + } +| typedcl_list ';' typedcl + { + $$ = list($1, $3); + } + +structdcl_list: + structdcl +| structdcl_list ';' structdcl + { + $$ = concat($1, $3); + } + +interfacedcl_list: + interfacedcl + { + $$ = list1($1); + } +| interfacedcl_list ';' interfacedcl + { + $$ = list($1, $3); + } + +structdcl: + new_name_list ntype oliteral + { + NodeList *l; + + for(l=$1; l; l=l->next) { + l->n = nod(ODCLFIELD, l->n, $2); + l->n->val = $3; + } + } +| embed oliteral + { + $1->val = $2; + $$ = list1($1); + } +| '(' embed ')' oliteral + { + $2->val = $4; + $$ = list1($2); + yyerror("cannot parenthesize embedded type"); + } +| '*' embed oliteral + { + $2->right = nod(OIND, $2->right, N); + $2->val = $3; + $$ = list1($2); + } +| '(' '*' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } +| '*' '(' embed ')' oliteral + { + $3->right = nod(OIND, $3->right, N); + $3->val = $5; + $$ = list1($3); + yyerror("cannot parenthesize embedded type"); + } + +packname: + LNAME + { + Node *n; + + $$ = $1; + n = oldname($1); + if(n->pack != N) + n->pack->used = 1; + } +| LNAME '.' sym + { + Pkg *pkg; + + if($1->def == N || $1->def->op != OPACK) { + yyerror("%S is not a package", $1); + pkg = localpkg; + } else { + $1->def->used = 1; + pkg = $1->def->pkg; + } + $$ = restrictlookup($3->name, pkg); + } + +embed: + packname + { + $$ = embedded($1); + } + +interfacedcl: + new_name indcl + { + $$ = nod(ODCLFIELD, $1, $2); + ifacedcl($$); + } +| packname + { + $$ = nod(ODCLFIELD, N, oldname($1)); + } +| '(' packname ')' + { + $$ = nod(ODCLFIELD, N, oldname($2)); + yyerror("cannot parenthesize embedded type"); + } + +indcl: + '(' oarg_type_list_ocomma ')' fnres + { + // without func keyword + $2 = checkarglist($2, 1); + $$ = nod(OTFUNC, fakethis(), N); + $$->list = $2; + $$->rlist = $4; + } + +/* + * function arguments. + */ +arg_type: + name_or_type +| sym name_or_type + { + $$ = nod(ONONAME, N, N); + $$->sym = $1; + $$ = nod(OKEY, $$, $2); + } +| sym dotdotdot + { + $$ = nod(ONONAME, N, N); + $$->sym = $1; + $$ = nod(OKEY, $$, $2); + } +| dotdotdot + +arg_type_list: + arg_type + { + $$ = list1($1); + } +| arg_type_list ',' arg_type + { + $$ = list($1, $3); + } + +oarg_type_list_ocomma: + { + $$ = nil; + } +| arg_type_list ocomma + { + $$ = $1; + } + +/* + * statement + */ +stmt: + { + $$ = N; + } +| compound_stmt +| common_dcl + { + $$ = liststmt($1); + } +| non_dcl_stmt +| error + { + $$ = N; + } + +non_dcl_stmt: + simple_stmt +| for_stmt +| switch_stmt +| select_stmt +| if_stmt + { + popdcl(); + $$ = $1; + } +| if_stmt LELSE stmt + { + if($3->op != OIF && $3->op != OBLOCK) + yyerror("missing { } after else"); + + popdcl(); + $$ = $1; + $$->nelse = list1($3); + } +| labelname ':' + { + $1 = nod(OLABEL, $1, N); + $1->sym = dclstack; // context, for goto restrictions + } + stmt + { + NodeList *l; + + $1->right = $4; + l = list1($1); + if($4) + l = list(l, $4); + $$ = liststmt(l); + } +| LFALL + { + // will be converted to OFALL + $$ = nod(OXFALL, N, N); + } +| LBREAK onew_name + { + $$ = nod(OBREAK, $2, N); + } +| LCONTINUE onew_name + { + $$ = nod(OCONTINUE, $2, N); + } +| LGO pseudocall + { + $$ = nod(OPROC, $2, N); + } +| LDEFER pseudocall + { + $$ = nod(ODEFER, $2, N); + } +| LGOTO new_name + { + $$ = nod(OGOTO, $2, N); + $$->sym = dclstack; // context, for goto restrictions + } +| LRETURN oexpr_list + { + $$ = nod(ORETURN, N, N); + $$->list = $2; + } + +stmt_list: + stmt + { + $$ = nil; + if($1 != N) + $$ = list1($1); + } +| stmt_list ';' stmt + { + $$ = $1; + if($3 != N) + $$ = list($$, $3); + } + +new_name_list: + new_name + { + $$ = list1($1); + } +| new_name_list ',' new_name + { + $$ = list($1, $3); + } + +dcl_name_list: + dcl_name + { + $$ = list1($1); + } +| dcl_name_list ',' dcl_name + { + $$ = list($1, $3); + } + +expr_list: + expr + { + $$ = list1($1); + } +| expr_list ',' expr + { + $$ = list($1, $3); + } + +expr_or_type_list: + expr_or_type + { + $$ = list1($1); + } +| expr_or_type_list ',' expr_or_type + { + $$ = list($1, $3); + } + +/* + * list of combo of keyval and val + */ +keyval_list: + keyval + { + $$ = list1($1); + } +| complitexpr + { + $$ = list1($1); + } +| keyval_list ',' keyval + { + $$ = list($1, $3); + } +| keyval_list ',' complitexpr + { + $$ = list($1, $3); + } + +braced_keyval_list: + { + $$ = nil; + } +| keyval_list ocomma + { + $$ = $1; + } + +/* + * optional things + */ +osemi: +| ';' + +ocomma: +| ',' + +oexpr: + { + $$ = N; + } +| expr + +oexpr_list: + { + $$ = nil; + } +| expr_list + +osimple_stmt: + { + $$ = N; + } +| simple_stmt + +ohidden_funarg_list: + { + $$ = nil; + } +| hidden_funarg_list + +ohidden_structdcl_list: + { + $$ = nil; + } +| hidden_structdcl_list + +ohidden_interfacedcl_list: + { + $$ = nil; + } +| hidden_interfacedcl_list + +oliteral: + { + $$.ctype = CTxxx; + } +| LLITERAL + +/* + * import syntax from header of + * an output package + */ +hidden_import: + LIMPORT sym LLITERAL ';' + { + // Informational: record package name + // associated with import path, for use in + // human-readable messages. + Pkg *p; + + p = mkpkg($3.u.sval); + if(p->name == nil) { + p->name = $2->name; + pkglookup($2->name, nil)->npkg++; + } else if(strcmp(p->name, $2->name) != 0) + yyerror("conflicting names %s and %s for package %Z", p->name, $2->name, p->path); + } +| LVAR hidden_pkg_importsym hidden_type ';' + { + importvar($2, $3, PEXTERN); + } +| LCONST hidden_pkg_importsym '=' hidden_constant ';' + { + importconst($2, types[TIDEAL], $4); + } +| LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';' + { + importconst($2, $3, $5); + } +| LTYPE hidden_pkgtype hidden_type ';' + { + importtype($2, $3); + } +| LFUNC hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres ';' + { + importvar($2, functype(N, $4, $6), PFUNC); + } +| LFUNC '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres ';' + { + if($3->next != nil || $3->n->op != ODCLFIELD) { + yyerror("bad receiver in method"); + YYERROR; + } + importmethod($5, functype($3->n, $7, $9)); + } + +hidden_pkgtype: + hidden_pkg_importsym + { + $$ = pkgtype($1); + importsym($1, OTYPE); + } + +hidden_type: + hidden_type_misc +| hidden_type_recv_chan +| hidden_type_func + +hidden_type_non_recv_chan: + hidden_type_misc +| hidden_type_func + +hidden_type_misc: + hidden_importsym + { + $$ = pkgtype($1); + } +| LNAME + { + // predefined name like uint8 + $1 = pkglookup($1->name, builtinpkg); + if($1->def == N || $1->def->op != OTYPE) { + yyerror("%s is not a type", $1->name); + $$ = T; + } else + $$ = $1->def->type; + } +| '[' ']' hidden_type + { + $$ = aindex(N, $3); + } +| '[' LLITERAL ']' hidden_type + { + $$ = aindex(nodlit($2), $4); + } +| LMAP '[' hidden_type ']' hidden_type + { + $$ = maptype($3, $5); + } +| LSTRUCT '{' ohidden_structdcl_list '}' + { + $$ = dostruct($3, TSTRUCT); + } +| LINTERFACE '{' ohidden_interfacedcl_list '}' + { + $$ = dostruct($3, TINTER); + } +| '*' hidden_type + { + $$ = ptrto($2); + } +| LCHAN hidden_type_non_recv_chan + { + $$ = typ(TCHAN); + $$->type = $2; + $$->chan = Cboth; + } +| LCHAN '(' hidden_type_recv_chan ')' + { + $$ = typ(TCHAN); + $$->type = $3; + $$->chan = Cboth; + } +| LCHAN LCOMM hidden_type + { + $$ = typ(TCHAN); + $$->type = $3; + $$->chan = Csend; + } + +hidden_type_recv_chan: + LCOMM LCHAN hidden_type + { + $$ = typ(TCHAN); + $$->type = $3; + $$->chan = Crecv; + } + +hidden_type_func: + LFUNC '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = functype(nil, $3, $5); + } + +hidden_opt_sym: + sym + { + $$ = newname($1); + } +| '?' + { + $$ = N; + } + +hidden_dcl: + hidden_opt_sym hidden_type hidden_tag + { + $$ = nod(ODCLFIELD, $1, typenod($2)); + $$->val = $3; + } +| hidden_opt_sym LDDD hidden_type hidden_tag + { + Type *t; + + t = typ(TARRAY); + t->bound = -1; + t->type = $3; + $$ = nod(ODCLFIELD, $1, typenod(t)); + $$->isddd = 1; + $$->val = $4; + } + +hidden_structdcl: + sym hidden_type hidden_tag + { + $$ = nod(ODCLFIELD, newname($1), typenod($2)); + $$->val = $3; + } +| '?' hidden_type hidden_tag + { + Sym *s; + + s = $2->sym; + if(s == S && isptr[$2->etype]) + s = $2->type->sym; + if(s && s->pkg == builtinpkg) + s = lookup(s->name); + $$ = embedded(s); + $$->right = typenod($2); + $$->val = $3; + } + +hidden_tag: + { + $$.ctype = CTxxx; + } +| ':' LLITERAL // extra colon avoids conflict with "" looking like beginning of "".typename + { + $$ = $2; + } + +hidden_interfacedcl: + sym '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); + } +| hidden_importsym '(' ohidden_funarg_list ')' ohidden_funres + { + $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5))); + } + +ohidden_funres: + { + $$ = nil; + } +| hidden_funres + +hidden_funres: + '(' ohidden_funarg_list ')' + { + $$ = $2; + } +| hidden_type + { + $$ = list1(nod(ODCLFIELD, N, typenod($1))); + } + +hidden_literal: + LLITERAL + { + $$ = nodlit($1); + } +| '-' LLITERAL + { + $$ = nodlit($2); + switch($$->val.ctype){ + case CTINT: + mpnegfix($$->val.u.xval); + break; + case CTFLT: + mpnegflt($$->val.u.fval); + break; + default: + yyerror("bad negated constant"); + } + } +| sym + { + $$ = oldname(pkglookup($1->name, builtinpkg)); + if($$->op != OLITERAL) + yyerror("bad constant %S", $$->sym); + } + +hidden_constant: + hidden_literal +| '(' hidden_literal '+' hidden_literal ')' + { + $$ = nodcplxlit($2->val, $4->val); + } + +hidden_importsym: + LLITERAL '.' sym + { + Pkg *p; + + if($1.u.sval->len == 0) + p = importpkg; + else + p = mkpkg($1.u.sval); + $$ = pkglookup($3->name, p); + } + +hidden_pkg_importsym: + hidden_importsym + { + $$ = $1; + structpkg = $$->pkg; + } + +hidden_import_list: +| hidden_import_list hidden_import + +hidden_funarg_list: + hidden_dcl + { + $$ = list1($1); + } +| hidden_funarg_list ',' hidden_dcl + { + $$ = list($1, $3); + } + +hidden_structdcl_list: + hidden_structdcl + { + $$ = list1($1); + } +| hidden_structdcl_list ';' hidden_structdcl + { + $$ = list($1, $3); + } + +hidden_interfacedcl_list: + hidden_interfacedcl + { + $$ = list1($1); + } +| hidden_interfacedcl_list ';' hidden_interfacedcl + { + $$ = list($1, $3); + } + +%% + +static void +fixlbrace(int lbr) +{ + // If the opening brace was an LBODY, + // set up for another one now that we're done. + // See comment in lex.c about loophack. + if(lbr == LBODY) + loophack = 1; +} + diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c new file mode 100644 index 000000000..8818db08c --- /dev/null +++ b/src/cmd/gc/init.c @@ -0,0 +1,195 @@ +// 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" + +/* + * a function named init is a special case. + * it is called by the initialization before + * main is run. to make it unique within a + * package and also uncallable, the name, + * normally "pkg.init", is altered to "pkg.init·1". + */ +Node* +renameinit(Node *n) +{ + Sym *s; + static int initgen; + + s = n->sym; + if(s == S) + return n; + if(strcmp(s->name, "init") != 0) + return n; + + snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen); + s = lookup(namebuf); + return newname(s); +} + +/* + * hand-craft the following initialization code + * var initdone· uint8 (1) + * func init() (2) + * if initdone· != 0 { (3) + * if initdone· == 2 (4) + * return + * throw(); (5) + * } + * initdone· = 1; (6) + * // over all matching imported symbols + * <pkg>.init() (7) + * { <init stmts> } (8) + * init·<n>() // if any (9) + * initdone· = 2; (10) + * return (11) + * } + */ +static int +anyinit(NodeList *n) +{ + uint32 h; + Sym *s; + NodeList *l; + + // are there any interesting init statements + for(l=n; l; l=l->next) { + switch(l->n->op) { + case ODCLFUNC: + case ODCLCONST: + case ODCLTYPE: + case OEMPTY: + break; + default: + return 1; + } + } + + // is this main + if(strcmp(localpkg->name, "main") == 0) + return 1; + + // is there an explicit init function + snprint(namebuf, sizeof(namebuf), "init·1"); + s = lookup(namebuf); + if(s->def != N) + return 1; + + // are there any imported init functions + for(h=0; h<NHASH; h++) + for(s = hash[h]; s != S; s = s->link) { + if(s->name[0] != 'i' || strcmp(s->name, "init") != 0) + continue; + if(s->def == N) + continue; + return 1; + } + + // then none + return 0; +} + +void +fninit(NodeList *n) +{ + int i; + Node *gatevar; + Node *a, *b, *fn; + NodeList *r; + uint32 h; + Sym *s, *initsym; + + if(debug['A']) { + // sys.go or unsafe.go during compiler build + return; + } + + n = initfix(n); + if(!anyinit(n)) + return; + + r = nil; + + // (1) + snprint(namebuf, sizeof(namebuf), "initdone·"); + gatevar = newname(lookup(namebuf)); + addvar(gatevar, types[TUINT8], PEXTERN); + + // (2) + maxarg = 0; + snprint(namebuf, sizeof(namebuf), "init"); + + fn = nod(ODCLFUNC, N, N); + initsym = lookup(namebuf); + fn->nname = newname(initsym); + fn->nname->ntype = nod(OTFUNC, N, N); + funchdr(fn); + + // (3) + a = nod(OIF, N, N); + a->ntest = nod(ONE, gatevar, nodintconst(0)); + r = list(r, a); + + // (4) + b = nod(OIF, N, N); + b->ntest = nod(OEQ, gatevar, nodintconst(2)); + b->nbody = list1(nod(ORETURN, N, N)); + a->nbody = list1(b); + + // (5) + b = syslook("throwinit", 0); + b = nod(OCALL, b, N); + a->nbody = list(a->nbody, b); + + // (6) + a = nod(OAS, gatevar, nodintconst(1)); + r = list(r, a); + + // (7) + for(h=0; h<NHASH; h++) + for(s = hash[h]; s != S; s = s->link) { + if(s->name[0] != 'i' || strcmp(s->name, "init") != 0) + continue; + if(s->def == N) + continue; + if(s == initsym) + continue; + + // could check that it is fn of no args/returns + a = nod(OCALL, s->def, N); + r = list(r, a); + } + + // (8) + r = concat(r, n); + + // (9) + // could check that it is fn of no args/returns + for(i=1;; i++) { + snprint(namebuf, sizeof(namebuf), "init·%d", i); + s = lookup(namebuf); + if(s->def == N) + break; + a = nod(OCALL, s->def, N); + r = list(r, a); + } + + // (10) + a = nod(OAS, gatevar, nodintconst(2)); + r = list(r, a); + + // (11) + a = nod(ORETURN, N, N); + r = list(r, a); + exportsym(fn->nname); + + fn->nbody = r; + funcbody(fn); + + curfn = fn; + typecheck(&fn, Etop); + typechecklist(r, Etop); + curfn = nil; + funccompile(fn, 0); +} diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c new file mode 100644 index 000000000..29b6d27ff --- /dev/null +++ b/src/cmd/gc/lex.c @@ -0,0 +1,1956 @@ +// 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. + +#define EXTERN +#include "go.h" +#include "y.tab.h" +#include <ar.h> + +#undef getc +#undef ungetc +#define getc ccgetc +#define ungetc ccungetc + +extern int yychar; +int windows; +int yyprev; +int yylast; + +static void lexinit(void); +static void lexfini(void); +static void yytinit(void); +static int getc(void); +static void ungetc(int); +static int32 getr(void); +static int escchar(int, int*, vlong*); +static void addidir(char*); +static int getlinepragma(void); +static char *goos, *goarch, *goroot; + +// Our own isdigit, isspace, isalpha, isalnum that take care +// of EOF and other out of range arguments. +static int +yy_isdigit(int c) +{ + return c >= 0 && c <= 0xFF && isdigit(c); +} + +static int +yy_isspace(int c) +{ + return c >= 0 && c <= 0xFF && isspace(c); +} + +static int +yy_isalpha(int c) +{ + return c >= 0 && c <= 0xFF && isalpha(c); +} + +static int +yy_isalnum(int c) +{ + return c >= 0 && c <= 0xFF && isalnum(c); +} + +// Disallow use of isdigit etc. +#undef isdigit +#undef isspace +#undef isalpha +#undef isalnum +#define isdigit use_yy_isdigit_instead_of_isdigit +#define isspace use_yy_isspace_instead_of_isspace +#define isalpha use_yy_isalpha_instead_of_isalpha +#define isalnum use_yy_isalnum_instead_of_isalnum + +#define DBG if(!debug['x']);else print +enum +{ + EOF = -1, +}; + +void +usage(void) +{ + print("gc: usage: %cg [flags] file.go...\n", thechar); + print("flags:\n"); + // -A is allow use of "any" type, for bootstrapping + print(" -I DIR search for packages in DIR\n"); + print(" -d print declarations\n"); + print(" -e no limit on number of errors printed\n"); + print(" -f print stack frame structure\n"); + print(" -h panic on an error\n"); + print(" -o file specify output file\n"); + print(" -S print the assembly language\n"); + print(" -V print the compiler version\n"); + print(" -u disable package unsafe\n"); + print(" -w print the parse tree after typing\n"); + print(" -x print lex tokens\n"); + exit(0); +} + +void +fault(int s) +{ + // If we've already complained about things + // in the program, don't bother complaining + // about the seg fault too; let the user clean up + // the code and try again. + if(nsavederrors + nerrors > 0) + errorexit(); + fatal("fault"); +} + +int +main(int argc, char *argv[]) +{ + int i, c; + NodeList *l; + char *p; + + signal(SIGBUS, fault); + signal(SIGSEGV, fault); + + localpkg = mkpkg(strlit("")); + localpkg->prefix = "\"\""; + + builtinpkg = mkpkg(strlit("go.builtin")); + + gostringpkg = mkpkg(strlit("go.string")); + gostringpkg->name = "go.string"; + gostringpkg->prefix = "go.string"; // not go%2estring + + runtimepkg = mkpkg(strlit("runtime")); + runtimepkg->name = "runtime"; + + typepkg = mkpkg(strlit("type")); + typepkg->name = "type"; + + unsafepkg = mkpkg(strlit("unsafe")); + unsafepkg->name = "unsafe"; + + goroot = getgoroot(); + goos = getgoos(); + goarch = thestring; + + outfile = nil; + ARGBEGIN { + default: + c = ARGC(); + if(c >= 0 && c < sizeof(debug)) + debug[c]++; + break; + + case 'o': + outfile = EARGF(usage()); + break; + + case 'I': + addidir(EARGF(usage())); + break; + + case 'u': + safemode = 1; + break; + + case 'V': + print("%cg version %s\n", thechar, getgoversion()); + exit(0); + } ARGEND + + if(argc < 1) + usage(); + + // special flag to detect compilation of package runtime + compiling_runtime = debug['+']; + + pathname = mal(1000); + if(getwd(pathname, 999) == 0) + strcpy(pathname, "/???"); + + if(yy_isalpha(pathname[0]) && pathname[1] == ':') { + // On Windows. + windows = 1; + + // Canonicalize path by converting \ to / (Windows accepts both). + for(p=pathname; *p; p++) + if(*p == '\\') + *p = '/'; + } + + fmtinstall('O', Oconv); // node opcodes + fmtinstall('E', Econv); // etype opcodes + fmtinstall('J', Jconv); // all the node flags + fmtinstall('S', Sconv); // sym pointer + fmtinstall('T', Tconv); // type pointer + fmtinstall('N', Nconv); // node pointer + fmtinstall('Z', Zconv); // escaped string + fmtinstall('L', Lconv); // line number + fmtinstall('B', Bconv); // big numbers + fmtinstall('F', Fconv); // big float numbers + + betypeinit(); + if(widthptr == 0) + fatal("betypeinit failed"); + + lexinit(); + typeinit(); + yytinit(); + + blockgen = 1; + dclcontext = PEXTERN; + nerrors = 0; + lexlineno = 1; + + for(i=0; i<argc; i++) { + infile = argv[i]; + linehist(infile, 0, 0); + + curio.infile = infile; + curio.bin = Bopen(infile, OREAD); + if(curio.bin == nil) { + print("open %s: %r\n", infile); + errorexit(); + } + curio.peekc = 0; + curio.peekc1 = 0; + curio.nlsemi = 0; + + block = 1; + iota = -1000000; + + yyparse(); + if(nsyntaxerrors != 0) + errorexit(); + + linehist(nil, 0, 0); + if(curio.bin != nil) + Bterm(curio.bin); + } + testdclstack(); + mkpackage(localpkg->name); // final import not used checks + lexfini(); + + typecheckok = 1; + if(debug['f']) + frame(1); + + // Process top-level declarations in four phases. + // Phase 1: const, type, and names and types of funcs. + // This will gather all the information about types + // and methods but doesn't depend on any of it. + // Phase 2: Variable assignments. + // To check interface assignments, depends on phase 1. + // Phase 3: Type check function bodies. + // Phase 4: Compile function bodies. + defercheckwidth(); + for(l=xtop; l; l=l->next) + if(l->n->op != ODCL && l->n->op != OAS) + typecheck(&l->n, Etop); + for(l=xtop; l; l=l->next) + if(l->n->op == ODCL || l->n->op == OAS) + typecheck(&l->n, Etop); + resumetypecopy(); + resumecheckwidth(); + + for(l=xtop; l; l=l->next) { + if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) { + curfn = l->n; + saveerrors(); + typechecklist(l->n->nbody, Etop); + if(nerrors != 0) + l->n->nbody = nil; // type errors; do not compile + } + } + + curfn = nil; + + if(nsavederrors+nerrors) + errorexit(); + + for(l=xtop; l; l=l->next) + if(l->n->op == ODCLFUNC) + funccompile(l->n, 0); + + if(nsavederrors+nerrors == 0) + fninit(xtop); + + while(closures) { + l = closures; + closures = nil; + for(; l; l=l->next) { + funccompile(l->n, 1); + } + } + + for(l=externdcl; l; l=l->next) + if(l->n->op == ONAME) + typecheck(&l->n, Erv); + + if(nerrors+nsavederrors) + errorexit(); + + dumpobj(); + + if(nerrors+nsavederrors) + errorexit(); + + flusherrors(); + exit(0); + return 0; +} + +void +saveerrors(void) +{ + nsavederrors += nerrors; + nerrors = 0; +} + +static int +arsize(Biobuf *b, char *name) +{ + struct ar_hdr *a; + + if((a = Brdline(b, '\n')) == nil) + return -1; + if(Blinelen(b) != sizeof(struct ar_hdr)) + return -1; + if(strncmp(a->name, name, strlen(name)) != 0) + return -1; + return atoi(a->size); +} + +static int +skiptopkgdef(Biobuf *b) +{ + char *p; + int sz; + + /* archive header */ + if((p = Brdline(b, '\n')) == nil) + return 0; + if(Blinelen(b) != 8) + return 0; + if(memcmp(p, "!<arch>\n", 8) != 0) + return 0; + /* symbol table is first; skip it */ + sz = arsize(b, "__.SYMDEF"); + if(sz < 0) + return 0; + Bseek(b, sz, 1); + /* package export block is second */ + sz = arsize(b, "__.PKGDEF"); + if(sz <= 0) + return 0; + return 1; +} + +static void +addidir(char* dir) +{ + Idir** pp; + + if(dir == nil) + return; + + for(pp = &idirs; *pp != nil; pp = &(*pp)->link) + ; + *pp = mal(sizeof(Idir)); + (*pp)->link = nil; + (*pp)->dir = dir; +} + +// is this path a local name? begins with ./ or ../ or / +static int +islocalname(Strlit *name) +{ + if(!windows && name->len >= 1 && name->s[0] == '/') + return 1; + if(windows && name->len >= 3 && + yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/') + return 1; + if(name->len >= 2 && strncmp(name->s, "./", 2) == 0) + return 1; + if(name->len >= 3 && strncmp(name->s, "../", 3) == 0) + return 1; + return 0; +} + +static int +findpkg(Strlit *name) +{ + Idir *p; + char *q; + + if(islocalname(name)) { + if(safemode) + return 0; + // try .a before .6. important for building libraries: + // if there is an array.6 in the array.a library, + // want to find all of array.a, not just array.6. + snprint(namebuf, sizeof(namebuf), "%Z.a", name); + if(access(namebuf, 0) >= 0) + return 1; + snprint(namebuf, sizeof(namebuf), "%Z.%c", name, thechar); + if(access(namebuf, 0) >= 0) + return 1; + return 0; + } + + // local imports should be canonicalized already. + // don't want to see "container/../container/vector" + // as different from "container/vector". + q = mal(name->len+1); + memmove(q, name->s, name->len); + q[name->len] = '\0'; + cleanname(q); + if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) { + yyerror("non-canonical import path %Z (should be %s)", name, q); + return 0; + } + + for(p = idirs; p != nil; p = p->link) { + snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name); + if(access(namebuf, 0) >= 0) + return 1; + snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, thechar); + if(access(namebuf, 0) >= 0) + return 1; + } + if(goroot != nil) { + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.a", goroot, goos, goarch, name); + if(access(namebuf, 0) >= 0) + return 1; + snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s/%Z.%c", goroot, goos, goarch, name, thechar); + if(access(namebuf, 0) >= 0) + return 1; + } + return 0; +} + +void +importfile(Val *f, int line) +{ + Biobuf *imp; + char *file, *p, *q; + int32 c; + int len; + Strlit *path; + char *cleanbuf; + + // TODO(rsc): don't bother reloading imports more than once? + + if(f->ctype != CTSTR) { + yyerror("import statement not a string"); + return; + } + + if(strlen(f->u.sval->s) != f->u.sval->len) { + yyerror("import path contains NUL"); + errorexit(); + } + + // The package name main is no longer reserved, + // but we reserve the import path "main" to identify + // the main package, just as we reserve the import + // path "math" to identify the standard math package. + if(strcmp(f->u.sval->s, "main") == 0) { + yyerror("cannot import \"main\""); + errorexit(); + } + + if(strcmp(f->u.sval->s, "unsafe") == 0) { + if(safemode) { + yyerror("cannot import package unsafe"); + errorexit(); + } + importpkg = mkpkg(f->u.sval); + cannedimports("unsafe.6", unsafeimport); + return; + } + + path = f->u.sval; + if(islocalname(path)) { + cleanbuf = mal(strlen(pathname) + strlen(path->s) + 2); + strcpy(cleanbuf, pathname); + strcat(cleanbuf, "/"); + strcat(cleanbuf, path->s); + cleanname(cleanbuf); + path = strlit(cleanbuf); + } + + if(!findpkg(path)) { + yyerror("can't find import: %Z", f->u.sval); + errorexit(); + } + importpkg = mkpkg(path); + + imp = Bopen(namebuf, OREAD); + if(imp == nil) { + yyerror("can't open import: %Z: %r", f->u.sval); + errorexit(); + } + file = strdup(namebuf); + + len = strlen(namebuf); + if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') { + if(!skiptopkgdef(imp)) { + yyerror("import %s: not a package file", file); + errorexit(); + } + } + + // check object header + p = Brdstr(imp, '\n', 1); + if(strcmp(p, "empty archive") != 0) { + if(strncmp(p, "go object ", 10) != 0) { + yyerror("import %s: not a go object file", file); + errorexit(); + } + q = smprint("%s %s %s", getgoos(), thestring, getgoversion()); + if(strcmp(p+10, q) != 0) { + yyerror("import %s: object is [%s] expected [%s]", file, p+10, q); + errorexit(); + } + free(q); + } + + // assume files move (get installed) + // so don't record the full path. + linehist(file + len - path->len - 2, -1, 1); // acts as #pragma lib + + /* + * position the input right + * after $$ and return + */ + pushedio = curio; + curio.bin = imp; + curio.peekc = 0; + curio.peekc1 = 0; + curio.infile = file; + curio.nlsemi = 0; + typecheckok = 1; + + for(;;) { + c = getc(); + if(c == EOF) + break; + if(c != '$') + continue; + c = getc(); + if(c == EOF) + break; + if(c != '$') + continue; + return; + } + yyerror("no import in: %Z", f->u.sval); + unimportfile(); +} + +void +unimportfile(void) +{ + if(curio.bin != nil) { + Bterm(curio.bin); + curio.bin = nil; + } else + lexlineno--; // re correct sys.6 line number + + curio = pushedio; + pushedio.bin = nil; + incannedimport = 0; + typecheckok = 0; +} + +void +cannedimports(char *file, char *cp) +{ + lexlineno++; // if sys.6 is included on line 1, + + pushedio = curio; + curio.bin = nil; + curio.peekc = 0; + curio.peekc1 = 0; + curio.infile = file; + curio.cp = cp; + curio.nlsemi = 0; + curio.importsafe = 0; + + typecheckok = 1; + incannedimport = 1; +} + +static int +isfrog(int c) +{ + // complain about possibly invisible control characters + if(c < 0) + return 1; + if(c < ' ') { + if(c == '\n' || c== '\r' || c == '\t') // good white space + return 0; + return 1; + } + if(0x7f <= c && c <= 0xa0) // DEL, unicode block including unbreakable space. + return 1; + return 0; +} + +typedef struct Loophack Loophack; +struct Loophack { + int v; + Loophack *next; +}; + +static int32 +_yylex(void) +{ + int c, c1, clen, escflag, ncp; + vlong v; + char *cp, *ep; + Rune rune; + Sym *s; + static Loophack *lstk; + Loophack *h; + + prevlineno = lineno; + +l0: + c = getc(); + if(yy_isspace(c)) { + if(c == '\n' && curio.nlsemi) { + ungetc(c); + DBG("lex: implicit semi\n"); + return ';'; + } + goto l0; + } + + lineno = lexlineno; /* start of token */ + + if(c >= Runeself) { + /* all multibyte runes are alpha */ + cp = lexbuf; + ep = lexbuf+sizeof lexbuf; + goto talph; + } + + if(yy_isalpha(c)) { + cp = lexbuf; + ep = lexbuf+sizeof lexbuf; + goto talph; + } + + if(yy_isdigit(c)) + goto tnum; + + switch(c) { + case EOF: + lineno = prevlineno; + ungetc(EOF); + return -1; + + case '_': + cp = lexbuf; + ep = lexbuf+sizeof lexbuf; + goto talph; + + case '.': + c1 = getc(); + if(yy_isdigit(c1)) { + cp = lexbuf; + ep = lexbuf+sizeof lexbuf; + *cp++ = c; + c = c1; + c1 = 0; + goto casedot; + } + if(c1 == '.') { + c1 = getc(); + if(c1 == '.') { + c = LDDD; + goto lx; + } + ungetc(c1); + c1 = '.'; + } + break; + + case '"': + /* "..." */ + strcpy(lexbuf, "\"<string>\""); + cp = mal(8); + clen = sizeof(int32); + ncp = 8; + + for(;;) { + if(clen+UTFmax > ncp) { + cp = remal(cp, ncp, ncp); + ncp += ncp; + } + if(escchar('"', &escflag, &v)) + break; + if(v < Runeself || escflag) { + cp[clen++] = v; + } else { + rune = v; + c = runelen(rune); + runetochar(cp+clen, &rune); + clen += c; + } + } + goto strlit; + + case '`': + /* `...` */ + strcpy(lexbuf, "`<string>`"); + cp = mal(8); + clen = sizeof(int32); + ncp = 8; + + for(;;) { + if(clen+UTFmax > ncp) { + cp = remal(cp, ncp, ncp); + ncp += ncp; + } + c = getr(); + if(c == EOF) { + yyerror("eof in string"); + break; + } + if(c == '`') + break; + rune = c; + clen += runetochar(cp+clen, &rune); + } + + strlit: + *(int32*)cp = clen-sizeof(int32); // length + do { + cp[clen++] = 0; + } while(clen & MAXALIGN); + yylval.val.u.sval = (Strlit*)cp; + yylval.val.ctype = CTSTR; + DBG("lex: string literal\n"); + strcpy(litbuf, "string literal"); + return LLITERAL; + + case '\'': + /* '.' */ + if(escchar('\'', &escflag, &v)) { + yyerror("empty character literal or unescaped ' in character literal"); + v = '\''; + } + if(!escchar('\'', &escflag, &v)) { + yyerror("missing '"); + ungetc(v); + } + yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); + mpmovecfix(yylval.val.u.xval, v); + yylval.val.ctype = CTINT; + DBG("lex: codepoint literal\n"); + strcpy(litbuf, "string literal"); + return LLITERAL; + + case '/': + c1 = getc(); + if(c1 == '*') { + int nl; + + nl = 0; + for(;;) { + c = getr(); + if(c == '\n') + nl = 1; + while(c == '*') { + c = getr(); + if(c == '/') { + if(nl) + ungetc('\n'); + goto l0; + } + if(c == '\n') + nl = 1; + } + if(c == EOF) { + yyerror("eof in comment"); + errorexit(); + } + } + } + if(c1 == '/') { + c = getlinepragma(); + for(;;) { + if(c == '\n' || c == EOF) { + ungetc(c); + goto l0; + } + c = getr(); + } + } + if(c1 == '=') { + c = ODIV; + goto asop; + } + break; + + case ':': + c1 = getc(); + if(c1 == '=') { + c = LCOLAS; + goto lx; + } + break; + + case '*': + c1 = getc(); + if(c1 == '=') { + c = OMUL; + goto asop; + } + break; + + case '%': + c1 = getc(); + if(c1 == '=') { + c = OMOD; + goto asop; + } + break; + + case '+': + c1 = getc(); + if(c1 == '+') { + c = LINC; + goto lx; + } + if(c1 == '=') { + c = OADD; + goto asop; + } + break; + + case '-': + c1 = getc(); + if(c1 == '-') { + c = LDEC; + goto lx; + } + if(c1 == '=') { + c = OSUB; + goto asop; + } + break; + + case '>': + c1 = getc(); + if(c1 == '>') { + c = LRSH; + c1 = getc(); + if(c1 == '=') { + c = ORSH; + goto asop; + } + break; + } + if(c1 == '=') { + c = LGE; + goto lx; + } + c = LGT; + break; + + case '<': + c1 = getc(); + if(c1 == '<') { + c = LLSH; + c1 = getc(); + if(c1 == '=') { + c = OLSH; + goto asop; + } + break; + } + if(c1 == '=') { + c = LLE; + goto lx; + } + if(c1 == '-') { + c = LCOMM; + goto lx; + } + c = LLT; + break; + + case '=': + c1 = getc(); + if(c1 == '=') { + c = LEQ; + goto lx; + } + break; + + case '!': + c1 = getc(); + if(c1 == '=') { + c = LNE; + goto lx; + } + break; + + case '&': + c1 = getc(); + if(c1 == '&') { + c = LANDAND; + goto lx; + } + if(c1 == '^') { + c = LANDNOT; + c1 = getc(); + if(c1 == '=') { + c = OANDNOT; + goto asop; + } + break; + } + if(c1 == '=') { + c = OAND; + goto asop; + } + break; + + case '|': + c1 = getc(); + if(c1 == '|') { + c = LOROR; + goto lx; + } + if(c1 == '=') { + c = OOR; + goto asop; + } + break; + + case '^': + c1 = getc(); + if(c1 == '=') { + c = OXOR; + goto asop; + } + break; + + /* + * clumsy dance: + * to implement rule that disallows + * if T{1}[0] { ... } + * but allows + * if (T{1}[0]) { ... } + * the block bodies for if/for/switch/select + * begin with an LBODY token, not '{'. + * + * when we see the keyword, the next + * non-parenthesized '{' becomes an LBODY. + * loophack is normally 0. + * a keyword makes it go up to 1. + * parens push loophack onto a stack and go back to 0. + * a '{' with loophack == 1 becomes LBODY and disables loophack. + * + * i said it was clumsy. + */ + case '(': + case '[': + if(loophack || lstk != nil) { + h = malloc(sizeof *h); + h->v = loophack; + h->next = lstk; + lstk = h; + loophack = 0; + } + goto lx; + case ')': + case ']': + if(lstk != nil) { + h = lstk; + loophack = h->v; + lstk = h->next; + free(h); + } + goto lx; + case '{': + if(loophack == 1) { + DBG("%L lex: LBODY\n", lexlineno); + loophack = 0; + return LBODY; + } + goto lx; + + default: + goto lx; + } + ungetc(c1); + +lx: + if(c > 0xff) + DBG("%L lex: TOKEN %s\n", lexlineno, lexname(c)); + else + DBG("%L lex: TOKEN '%c'\n", lexlineno, c); + if(isfrog(c)) { + yyerror("illegal character 0x%ux", c); + goto l0; + } + if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) { + yyerror("%s: unexpected %c", "syntax error", c); + goto l0; + } + return c; + +asop: + yylval.lint = c; // rathole to hold which asop + DBG("lex: TOKEN ASOP %c\n", c); + return LASOP; + +talph: + /* + * cp is set to lexbuf and some + * prefix has been stored + */ + for(;;) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + if(c >= Runeself) { + ungetc(c); + rune = getr(); + // 0xb7 · is used for internal names + if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7)) + yyerror("invalid identifier character 0x%ux", rune); + cp += runetochar(cp, &rune); + } else if(!yy_isalnum(c) && c != '_') + break; + else + *cp++ = c; + c = getc(); + } + *cp = 0; + ungetc(c); + + s = lookup(lexbuf); + switch(s->lexical) { + case LIGNORE: + goto l0; + + case LFOR: + case LIF: + case LSWITCH: + case LSELECT: + loophack = 1; // see comment about loophack above + break; + } + + DBG("lex: %S %s\n", s, lexname(s->lexical)); + yylval.sym = s; + return s->lexical; + +tnum: + c1 = 0; + cp = lexbuf; + ep = lexbuf+sizeof lexbuf; + if(c != '0') { + for(;;) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + *cp++ = c; + c = getc(); + if(yy_isdigit(c)) + continue; + goto dc; + } + } + *cp++ = c; + c = getc(); + if(c == 'x' || c == 'X') { + for(;;) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + *cp++ = c; + c = getc(); + if(yy_isdigit(c)) + continue; + if(c >= 'a' && c <= 'f') + continue; + if(c >= 'A' && c <= 'F') + continue; + if(cp == lexbuf+2) + yyerror("malformed hex constant"); + goto ncu; + } + } + + if(c == 'p') // 0p begins floating point zero + goto casep; + + c1 = 0; + for(;;) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + if(!yy_isdigit(c)) + break; + if(c < '0' || c > '7') + c1 = 1; // not octal + *cp++ = c; + c = getc(); + } + if(c == '.') + goto casedot; + if(c == 'e' || c == 'E') + goto casee; + if(c == 'i') + goto casei; + if(c1) + yyerror("malformed octal constant"); + goto ncu; + +dc: + if(c == '.') + goto casedot; + if(c == 'e' || c == 'E') + goto casee; + if(c == 'p' || c == 'P') + goto casep; + if(c == 'i') + goto casei; + +ncu: + *cp = 0; + ungetc(c); + + yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval)); + mpatofix(yylval.val.u.xval, lexbuf); + if(yylval.val.u.xval->ovf) { + yyerror("overflow in constant"); + mpmovecfix(yylval.val.u.xval, 0); + } + yylval.val.ctype = CTINT; + DBG("lex: integer literal\n"); + strcpy(litbuf, "literal "); + strcat(litbuf, lexbuf); + return LLITERAL; + +casedot: + for(;;) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + *cp++ = c; + c = getc(); + if(!yy_isdigit(c)) + break; + } + if(c == 'i') + goto casei; + if(c != 'e' && c != 'E') + goto caseout; + +casee: + *cp++ = 'e'; + c = getc(); + if(c == '+' || c == '-') { + *cp++ = c; + c = getc(); + } + if(!yy_isdigit(c)) + yyerror("malformed fp constant exponent"); + while(yy_isdigit(c)) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + *cp++ = c; + c = getc(); + } + if(c == 'i') + goto casei; + goto caseout; + +casep: + *cp++ = 'p'; + c = getc(); + if(c == '+' || c == '-') { + *cp++ = c; + c = getc(); + } + if(!yy_isdigit(c)) + yyerror("malformed fp constant exponent"); + while(yy_isdigit(c)) { + if(cp+10 >= ep) { + yyerror("identifier too long"); + errorexit(); + } + *cp++ = c; + c = getc(); + } + if(c == 'i') + goto casei; + goto caseout; + +casei: + // imaginary constant + *cp = 0; + yylval.val.u.cval = mal(sizeof(*yylval.val.u.cval)); + mpmovecflt(&yylval.val.u.cval->real, 0.0); + mpatoflt(&yylval.val.u.cval->imag, lexbuf); + if(yylval.val.u.cval->imag.val.ovf) { + yyerror("overflow in imaginary constant"); + mpmovecflt(&yylval.val.u.cval->real, 0.0); + } + yylval.val.ctype = CTCPLX; + DBG("lex: imaginary literal\n"); + strcpy(litbuf, "literal "); + strcat(litbuf, lexbuf); + return LLITERAL; + +caseout: + *cp = 0; + ungetc(c); + + yylval.val.u.fval = mal(sizeof(*yylval.val.u.fval)); + mpatoflt(yylval.val.u.fval, lexbuf); + if(yylval.val.u.fval->val.ovf) { + yyerror("overflow in float constant"); + mpmovecflt(yylval.val.u.fval, 0.0); + } + yylval.val.ctype = CTFLT; + DBG("lex: floating literal\n"); + strcpy(litbuf, "literal "); + strcat(litbuf, lexbuf); + return LLITERAL; +} + +/* + * read and interpret syntax that looks like + * //line parse.y:15 + * as a discontinuity in sequential line numbers. + * the next line of input comes from parse.y:15 + */ +static int +getlinepragma(void) +{ + int i, c, n; + char *cp, *ep; + Hist *h; + + for(i=0; i<5; i++) { + c = getr(); + if(c != "line "[i]) + goto out; + } + + cp = lexbuf; + ep = lexbuf+sizeof(lexbuf)-5; + for(;;) { + c = getr(); + if(c == '\n' || c == EOF) + goto out; + if(c == ' ') + continue; + if(c == ':') + break; + if(cp < ep) + *cp++ = c; + } + *cp = 0; + + n = 0; + for(;;) { + c = getr(); + if(!yy_isdigit(c)) + break; + n = n*10 + (c-'0'); + if(n > 1e8) { + yyerror("line number out of range"); + errorexit(); + } + } + + if(c != '\n' || n <= 0) + goto out; + + // try to avoid allocating file name over and over + for(h=hist; h!=H; h=h->link) { + if(h->name != nil && strcmp(h->name, lexbuf) == 0) { + linehist(h->name, n, 0); + goto out; + } + } + linehist(strdup(lexbuf), n, 0); + +out: + return c; +} + +int32 +yylex(void) +{ + int lx; + + lx = _yylex(); + + if(curio.nlsemi && lx == EOF) { + // Treat EOF as "end of line" for the purposes + // of inserting a semicolon. + lx = ';'; + } + + switch(lx) { + case LNAME: + case LLITERAL: + case LBREAK: + case LCONTINUE: + case LFALL: + case LRETURN: + case LINC: + case LDEC: + case ')': + case '}': + case ']': + curio.nlsemi = 1; + break; + default: + curio.nlsemi = 0; + break; + } + + // Track last two tokens returned by yylex. + yyprev = yylast; + yylast = lx; + return lx; +} + +static int +getc(void) +{ + int c; + + c = curio.peekc; + if(c != 0) { + curio.peekc = curio.peekc1; + curio.peekc1 = 0; + if(c == '\n' && pushedio.bin == nil) + lexlineno++; + return c; + } + + if(curio.bin == nil) { + c = *curio.cp & 0xff; + if(c != 0) + curio.cp++; + } else + c = Bgetc(curio.bin); + + switch(c) { + case 0: + if(curio.bin != nil) { + yyerror("illegal NUL byte"); + break; + } + case EOF: + // insert \n at EOF + if(curio.eofnl) + return EOF; + curio.eofnl = 1; + c = '\n'; + case '\n': + if(pushedio.bin == nil) + lexlineno++; + break; + } + return c; +} + +static void +ungetc(int c) +{ + curio.peekc1 = curio.peekc; + curio.peekc = c; + if(c == '\n' && pushedio.bin == nil) + lexlineno--; +} + +static int32 +getr(void) +{ + int c, i; + char str[UTFmax+1]; + Rune rune; + + c = getc(); + if(c < Runeself) + return c; + i = 0; + str[i++] = c; + +loop: + c = getc(); + str[i++] = c; + if(!fullrune(str, i)) + goto loop; + c = chartorune(&rune, str); + if(rune == Runeerror && c == 1) { + lineno = lexlineno; + yyerror("illegal UTF-8 sequence"); + flusherrors(); + print("\t"); + for(c=0; c<i; c++) + print("%s%.2x", c > 0 ? " " : "", *(uchar*)(str+c)); + print("\n"); + } + return rune; +} + +static int +escchar(int e, int *escflg, vlong *val) +{ + int i, u, c; + vlong l; + + *escflg = 0; + + c = getr(); + switch(c) { + case EOF: + yyerror("eof in string"); + return 1; + case '\n': + yyerror("newline in string"); + return 1; + case '\\': + break; + default: + if(c == e) + return 1; + *val = c; + return 0; + } + + u = 0; + c = getr(); + switch(c) { + case 'x': + *escflg = 1; // it's a byte + i = 2; + goto hex; + + case 'u': + i = 4; + u = 1; + goto hex; + + case 'U': + i = 8; + u = 1; + goto hex; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + *escflg = 1; // it's a byte + goto oct; + + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': c = '\\'; break; + + default: + if(c != e) + yyerror("unknown escape sequence: %c", c); + } + *val = c; + return 0; + +hex: + l = 0; + for(; i>0; i--) { + c = getc(); + if(c >= '0' && c <= '9') { + l = l*16 + c-'0'; + continue; + } + if(c >= 'a' && c <= 'f') { + l = l*16 + c-'a' + 10; + continue; + } + if(c >= 'A' && c <= 'F') { + l = l*16 + c-'A' + 10; + continue; + } + yyerror("non-hex character in escape sequence: %c", c); + ungetc(c); + break; + } + if(u && (l > Runemax || (0xd800 <= l && l < 0xe000))) { + yyerror("invalid Unicode code point in escape sequence: %#llx", l); + l = Runeerror; + } + *val = l; + return 0; + +oct: + l = c - '0'; + for(i=2; i>0; i--) { + c = getc(); + if(c >= '0' && c <= '7') { + l = l*8 + c-'0'; + continue; + } + yyerror("non-octal character in escape sequence: %c", c); + ungetc(c); + } + if(l > 255) + yyerror("octal escape value > 255: %d", l); + + *val = l; + return 0; +} + +static struct +{ + char* name; + int lexical; + int etype; + int op; +} syms[] = +{ +/* name lexical etype op + */ +/* basic types */ + "int8", LNAME, TINT8, OXXX, + "int16", LNAME, TINT16, OXXX, + "int32", LNAME, TINT32, OXXX, + "int64", LNAME, TINT64, OXXX, + + "uint8", LNAME, TUINT8, OXXX, + "uint16", LNAME, TUINT16, OXXX, + "uint32", LNAME, TUINT32, OXXX, + "uint64", LNAME, TUINT64, OXXX, + + "float32", LNAME, TFLOAT32, OXXX, + "float64", LNAME, TFLOAT64, OXXX, + + "complex64", LNAME, TCOMPLEX64, OXXX, + "complex128", LNAME, TCOMPLEX128, OXXX, + + "bool", LNAME, TBOOL, OXXX, + "byte", LNAME, TUINT8, OXXX, + "string", LNAME, TSTRING, OXXX, + + "any", LNAME, TANY, OXXX, + + "break", LBREAK, Txxx, OXXX, + "case", LCASE, Txxx, OXXX, + "chan", LCHAN, Txxx, OXXX, + "const", LCONST, Txxx, OXXX, + "continue", LCONTINUE, Txxx, OXXX, + "default", LDEFAULT, Txxx, OXXX, + "else", LELSE, Txxx, OXXX, + "defer", LDEFER, Txxx, OXXX, + "fallthrough", LFALL, Txxx, OXXX, + "for", LFOR, Txxx, OXXX, + "func", LFUNC, Txxx, OXXX, + "go", LGO, Txxx, OXXX, + "goto", LGOTO, Txxx, OXXX, + "if", LIF, Txxx, OXXX, + "import", LIMPORT, Txxx, OXXX, + "interface", LINTERFACE, Txxx, OXXX, + "map", LMAP, Txxx, OXXX, + "package", LPACKAGE, Txxx, OXXX, + "range", LRANGE, Txxx, OXXX, + "return", LRETURN, Txxx, OXXX, + "select", LSELECT, Txxx, OXXX, + "struct", LSTRUCT, Txxx, OXXX, + "switch", LSWITCH, Txxx, OXXX, + "type", LTYPE, Txxx, OXXX, + "var", LVAR, Txxx, OXXX, + + "append", LNAME, Txxx, OAPPEND, + "cap", LNAME, Txxx, OCAP, + "close", LNAME, Txxx, OCLOSE, + "complex", LNAME, Txxx, OCOMPLEX, + "copy", LNAME, Txxx, OCOPY, + "imag", LNAME, Txxx, OIMAG, + "len", LNAME, Txxx, OLEN, + "make", LNAME, Txxx, OMAKE, + "new", LNAME, Txxx, ONEW, + "panic", LNAME, Txxx, OPANIC, + "print", LNAME, Txxx, OPRINT, + "println", LNAME, Txxx, OPRINTN, + "real", LNAME, Txxx, OREAL, + "recover", LNAME, Txxx, ORECOVER, + + "notwithstanding", LIGNORE, Txxx, OXXX, + "thetruthofthematter", LIGNORE, Txxx, OXXX, + "despiteallobjections", LIGNORE, Txxx, OXXX, + "whereas", LIGNORE, Txxx, OXXX, + "insofaras", LIGNORE, Txxx, OXXX, +}; + +static void +lexinit(void) +{ + int i, lex; + Sym *s, *s1; + Type *t; + int etype; + + /* + * initialize basic types array + * initialize known symbols + */ + for(i=0; i<nelem(syms); i++) { + lex = syms[i].lexical; + s = lookup(syms[i].name); + s->lexical = lex; + + etype = syms[i].etype; + if(etype != Txxx) { + if(etype < 0 || etype >= nelem(types)) + fatal("lexinit: %s bad etype", s->name); + t = types[etype]; + if(t == T) { + t = typ(etype); + t->sym = s; + + if(etype != TANY && etype != TSTRING) + dowidth(t); + types[etype] = t; + } + s1 = pkglookup(syms[i].name, builtinpkg); + s1->lexical = LNAME; + s1->def = typenod(t); + continue; + } + } + + // logically, the type of a string literal. + // types[TSTRING] is the named type string + // (the type of x in var x string or var x = "hello"). + // this is the ideal form + // (the type of x in const x = "hello"). + idealstring = typ(TSTRING); + idealbool = typ(TBOOL); + + s = pkglookup("true", builtinpkg); + s->def = nodbool(1); + s->def->sym = lookup("true"); + s->def->type = idealbool; + + s = pkglookup("false", builtinpkg); + s->def = nodbool(0); + s->def->sym = lookup("false"); + s->def->type = idealbool; + + s = lookup("_"); + s->block = -100; + s->def = nod(ONAME, N, N); + s->def->sym = s; + types[TBLANK] = typ(TBLANK); + s->def->type = types[TBLANK]; + nblank = s->def; +} + +static void +lexfini(void) +{ + Sym *s; + int lex, etype, i; + Val v; + + for(i=0; i<nelem(syms); i++) { + lex = syms[i].lexical; + if(lex != LNAME) + continue; + s = lookup(syms[i].name); + s->lexical = lex; + + etype = syms[i].etype; + if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) + s->def = typenod(types[etype]); + + etype = syms[i].op; + if(etype != OXXX && s->def == N) { + s->def = nod(ONAME, N, N); + s->def->sym = s; + s->def->etype = etype; + s->def->builtin = 1; + } + } + + for(i=0; typedefs[i].name; i++) { + s = lookup(typedefs[i].name); + if(s->def == N) + s->def = typenod(types[typedefs[i].etype]); + } + + // there's only so much table-driven we can handle. + // these are special cases. + types[TNIL] = typ(TNIL); + s = lookup("nil"); + if(s->def == N) { + v.ctype = CTNIL; + s->def = nodlit(v); + s->def->sym = s; + } + + s = lookup("iota"); + if(s->def == N) { + s->def = nod(OIOTA, N, N); + s->def->sym = s; + } + + s = lookup("true"); + if(s->def == N) { + s->def = nodbool(1); + s->def->sym = s; + } + + s = lookup("false"); + if(s->def == N) { + s->def = nodbool(0); + s->def->sym = s; + } + + nodfp = nod(ONAME, N, N); + nodfp->noescape = 1; + nodfp->type = types[TINT32]; + nodfp->xoffset = 0; + nodfp->class = PPARAM; + nodfp->sym = lookup(".fp"); +} + +struct +{ + int lex; + char* name; +} lexn[] = +{ + LANDAND, "ANDAND", + LASOP, "ASOP", + LBREAK, "BREAK", + LCASE, "CASE", + LCHAN, "CHAN", + LCOLAS, "COLAS", + LCONST, "CONST", + LCONTINUE, "CONTINUE", + LDEC, "DEC", + LDEFER, "DEFER", + LELSE, "ELSE", + LEQ, "EQ", + LFALL, "FALL", + LFOR, "FOR", + LFUNC, "FUNC", + LGE, "GE", + LGO, "GO", + LGOTO, "GOTO", + LGT, "GT", + LIF, "IF", + LIMPORT, "IMPORT", + LINC, "INC", + LINTERFACE, "INTERFACE", + LLE, "LE", + LLITERAL, "LITERAL", + LLSH, "LSH", + LLT, "LT", + LMAP, "MAP", + LNAME, "NAME", + LNE, "NE", + LOROR, "OROR", + LPACKAGE, "PACKAGE", + LRANGE, "RANGE", + LRETURN, "RETURN", + LRSH, "RSH", + LSTRUCT, "STRUCT", + LSWITCH, "SWITCH", + LTYPE, "TYPE", + LVAR, "VAR", +}; + +char* +lexname(int lex) +{ + int i; + static char buf[100]; + + for(i=0; i<nelem(lexn); i++) + if(lexn[i].lex == lex) + return lexn[i].name; + snprint(buf, sizeof(buf), "LEX-%d", lex); + return buf; +} + +struct +{ + char *have; + char *want; +} yytfix[] = +{ + "$end", "EOF", + "LLITERAL", "literal", + "LASOP", "op=", + "LBREAK", "break", + "LCASE", "case", + "LCOLAS", ":=", + "LCONST", "const", + "LCONTINUE", "continue", + "LDDD", "...", + "LDEFAULT", "default", + "LDEFER", "defer", + "LELSE", "else", + "LFALL", "fallthrough", + "LFOR", "for", + "LFUNC", "func", + "LGO", "go", + "LGOTO", "goto", + "LIF", "if", + "LIMPORT", "import", + "LINTERFACE", "interface", + "LMAP", "map", + "LNAME", "name", + "LPACKAGE", "package", + "LRANGE", "range", + "LRETURN", "return", + "LSELECT", "select", + "LSTRUCT", "struct", + "LSWITCH", "switch", + "LTYPE", "type", + "LVAR", "var", + "LANDAND", "&&", + "LANDNOT", "&^", + "LBODY", "{", + "LCOMM", "<-", + "LDEC", "--", + "LINC", "++", + "LEQ", "==", + "LGE", ">=", + "LGT", ">", + "LLE", "<=", + "LLT", "<", + "LLSH", "<<", + "LRSH", ">>", + "LOROR", "||", + "LNE", "!=", + + // spell out to avoid confusion with punctuation in error messages + "';'", "semicolon or newline", + "','", "comma", +}; + +static void +yytinit(void) +{ + int i, j; + extern char *yytname[]; + char *s, *t; + + for(i=0; yytname[i] != nil; i++) { + s = yytname[i]; + + if(strcmp(s, "LLITERAL") == 0) { + strcpy(litbuf, "literal"); + yytname[i] = litbuf; + goto loop; + } + + // apply yytfix if possible + for(j=0; j<nelem(yytfix); j++) { + if(strcmp(s, yytfix[j].have) == 0) { + yytname[i] = yytfix[j].want; + goto loop; + } + } + + // turn 'x' into x. + if(s[0] == '\'') { + t = strdup(s+1); + t[strlen(t)-1] = '\0'; + yytname[i] = t; + } + loop:; + } +} + +void +mkpackage(char* pkgname) +{ + Sym *s; + int32 h; + char *p; + + if(localpkg->name == nil) { + if(strcmp(pkgname, "_") == 0) + yyerror("invalid package name _"); + localpkg->name = pkgname; + } else { + if(strcmp(pkgname, localpkg->name) != 0) + yyerror("package %s; expected %s", pkgname, localpkg->name); + for(h=0; h<NHASH; h++) { + for(s = hash[h]; s != S; s = s->link) { + if(s->def == N || s->pkg != localpkg) + continue; + if(s->def->op == OPACK) { + // throw away top-level package name leftover + // from previous file. + // leave s->block set to cause redeclaration + // errors if a conflicting top-level name is + // introduced by a different file. + if(!s->def->used && !nsyntaxerrors) + yyerrorl(s->def->lineno, "imported and not used: %Z", s->def->pkg->path); + s->def = N; + continue; + } + if(s->def->sym != s) { + // throw away top-level name left over + // from previous import . "x" + if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) { + yyerrorl(s->def->pack->lineno, "imported and not used: %Z", s->def->pack->pkg->path); + s->def->pack->used = 1; + } + s->def = N; + continue; + } + } + } + } + + if(outfile == nil) { + p = strrchr(infile, '/'); + if(p == nil) + p = infile; + else + p = p+1; + snprint(namebuf, sizeof(namebuf), "%s", p); + p = strrchr(namebuf, '.'); + if(p != nil) + *p = 0; + outfile = smprint("%s.%c", namebuf, thechar); + } +} diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c new file mode 100644 index 000000000..7cea1a6cf --- /dev/null +++ b/src/cmd/gc/md5.c @@ -0,0 +1,290 @@ +// 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. + +// 64-bit MD5 (does full MD5 but returns 64 bits only). +// Translation of ../../pkg/crypto/md5/md5*.go. + +#include "go.h" +#include "md5.h" + +static int md5block(MD5 *dig, uchar *p, int nn); + +enum { + _Chunk = 64 +}; + +#define _Init0 0x67452301 +#define _Init1 0xEFCDAB89 +#define _Init2 0x98BADCFE +#define _Init3 0x10325476 + +void +md5reset(MD5 *d) +{ + d->s[0] = _Init0; + d->s[1] = _Init1; + d->s[2] = _Init2; + d->s[3] = _Init3; + d->nx = 0; + d->len = 0; +} + +void +md5write(MD5 *d, uchar *p, int nn) +{ + int i, n; + + d->len += nn; + if(d->nx > 0) { + n = nn; + if(n > _Chunk - d->nx) + n = _Chunk - d->nx; + for(i=0; i<n; i++) + d->x[d->nx+i] = p[i]; + d->nx += n; + if(d->nx == _Chunk) { + md5block(d, d->x, _Chunk); + d->nx = 0; + } + p += n; + nn -= n; + } + n = md5block(d, p, nn); + p += n; + nn -= n; + if(nn > 0) { + for(i=0; i<nn; i++) + d->x[i] = p[i]; + d->nx = nn; + } +} + +uint64 +md5sum(MD5 *d) +{ + uchar tmp[64]; + int i; + uint64 len; + + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + len = d->len; + memset(tmp, 0, sizeof tmp); + tmp[0] = 0x80; + if(len%64 < 56) + md5write(d, tmp, 56-len%64); + else + md5write(d, tmp, 64+56-len%64); + + // Length in bits. + len <<= 3; + for(i=0; i<8; i++) + tmp[i] = len>>(8*i); + md5write(d, tmp, 8); + + if(d->nx != 0) + fatal("md5sum"); + + return d->s[0] | ((uint64)d->s[1]<<32); +} + + +// MD5 block step. +// In its own file so that a faster assembly or C version +// can be substituted easily. + +// table[i] = int((1<<32) * abs(sin(i+1 radians))). +static uint32 table[64] = { + // round 1 + 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821, + + // round 2 + 0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + 0xd62f105d, + 0x2441453, + 0xd8a1e681, + 0xe7d3fbc8, + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a, + + // round3 + 0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x4881d05, + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665, + + // round 4 + 0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391, +}; + +static uint32 shift1[] = { 7, 12, 17, 22 }; +static uint32 shift2[] = { 5, 9, 14, 20 }; +static uint32 shift3[] = { 4, 11, 16, 23 }; +static uint32 shift4[] = { 6, 10, 15, 21 }; + +static int +md5block(MD5 *dig, uchar *p, int nn) +{ + uint32 a, b, c, d, aa, bb, cc, dd; + int i, j, n; + uint32 X[16]; + + a = dig->s[0]; + b = dig->s[1]; + c = dig->s[2]; + d = dig->s[3]; + n = 0; + + while(nn >= _Chunk) { + aa = a; + bb = b; + cc = c; + dd = d; + + for(i=0; i<16; i++) { + j = i*4; + X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | (p[j+3]<<24); + } + + // Round 1. + for(i=0; i<16; i++) { + uint32 x, t, s, f; + x = i; + t = i; + s = shift1[i%4]; + f = ((c ^ d) & b) ^ d; + a += f + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + + t = d; + d = c; + c = b; + b = a; + a = t; + } + + // Round 2. + for(i=0; i<16; i++) { + uint32 x, t, s, g; + + x = (1+5*i)%16; + t = 16+i; + s = shift2[i%4]; + g = ((b ^ c) & d) ^ c; + a += g + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + + t = d; + d = c; + c = b; + b = a; + a = t; + } + + // Round 3. + for(i=0; i<16; i++) { + uint32 x, t, s, h; + + x = (5+3*i)%16; + t = 32+i; + s = shift3[i%4]; + h = b ^ c ^ d; + a += h + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + + t = d; + d = c; + c = b; + b = a; + a = t; + } + + // Round 4. + for(i=0; i<16; i++) { + uint32 x, s, t, ii; + + x = (7*i)%16; + s = shift4[i%4]; + t = 48+i; + ii = c ^ (b | ~d); + a += ii + X[x] + table[t]; + a = a<<s | a>>(32-s); + a += b; + + t = d; + d = c; + c = b; + b = a; + a = t; + } + + a += aa; + b += bb; + c += cc; + d += dd; + + p += _Chunk; + n += _Chunk; + nn -= _Chunk; + } + + dig->s[0] = a; + dig->s[1] = b; + dig->s[2] = c; + dig->s[3] = d; + return n; +} diff --git a/src/cmd/gc/md5.h b/src/cmd/gc/md5.h new file mode 100644 index 000000000..f153e30f2 --- /dev/null +++ b/src/cmd/gc/md5.h @@ -0,0 +1,16 @@ +// 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. + +typedef struct MD5 MD5; +struct MD5 +{ + uint32 s[4]; + uchar x[64]; + int nx; + uint64 len; +}; + +void md5reset(MD5*); +void md5write(MD5*, uchar*, int); +uint64 md5sum(MD5*); diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin new file mode 100755 index 000000000..cfd6e59c1 --- /dev/null +++ b/src/cmd/gc/mkbuiltin @@ -0,0 +1,31 @@ +#!/bin/sh +# 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. + +# Generate builtin.c and builtin.c.boot from $* (runtime.go and unsafe.go). +# Run this after changing runtime.go and unsafe.go +# or after changing the export metadata format in the compiler. +# Either way, you need to have a working compiler binary first. + +set -e + +eval $(gomake --no-print-directory -f ../../Make.inc go-env) +if [ -z "$GC" ]; then + echo 'missing $GC - gomake failed?' 1>&2 + exit 1 +fi + +gomake mkbuiltin1 +rm -f _builtin.c +for i in runtime unsafe +do + $GC -A $i.go + O=$O ./mkbuiltin1 $i >>_builtin.c +done + +# If _builtin.c has changed vs builtin.c.boot, +# check in the new change. +cmp -s _builtin.c builtin.c.boot || cp _builtin.c builtin.c.boot + +mv _builtin.c builtin.c diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c new file mode 100644 index 000000000..ad83c0346 --- /dev/null +++ b/src/cmd/gc/mkbuiltin1.c @@ -0,0 +1,84 @@ +// 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. + +// Compile .go file, import data from .6 file, and generate C string version. + +#include <u.h> +#include <libc.h> +#include <stdio.h> + +void esc(char*); + +void +main(int argc, char **argv) +{ + char *name; + FILE *fin; + char buf[1024], initfunc[1024], *p, *q; + + if(argc != 2) { + fprintf(stderr, "usage: mkbuiltin1 sys\n"); + sysfatal("in file $1.6 s/PACKAGE/$1/\n"); + } + + name = argv[1]; + snprintf(initfunc, sizeof(initfunc), "init_%s_function", name); + + snprintf(buf, sizeof(buf), "%s.%s", name, getenv("O")); + if((fin = fopen(buf, "r")) == NULL) { + sysfatal("open %s: %r\n", buf); + } + + // look for $$ that introduces imports + while(fgets(buf, sizeof buf, fin) != NULL) + if(strstr(buf, "$$")) + goto begin; + sysfatal("did not find beginning of imports\n"); + +begin: + printf("char *%simport =\n", name); + + // process imports, stopping at $$ that closes them + while(fgets(buf, sizeof buf, fin) != NULL) { + buf[strlen(buf)-1] = 0; // chop \n + if(strstr(buf, "$$")) + goto end; + + // chop leading white space + for(p=buf; *p==' ' || *p == '\t'; p++) + ; + + // cut out decl of init_$1_function - it doesn't exist + if(strstr(buf, initfunc)) + continue; + + // sys.go claims to be in package PACKAGE to avoid + // conflicts during "6g sys.go". rename PACKAGE to $2. + printf("\t\""); + while(q = strstr(p, "PACKAGE")) { + *q = 0; + esc(p); // up to the substitution + printf("%s", name); // the sub name + p = q+7; // continue with rest + } + + esc(p); + printf("\\n\"\n"); + } + sysfatal("did not find end of imports\n"); + +end: + printf("\t\"$$\\n\";\n"); + exits(0); +} + +void +esc(char *p) +{ + for(; *p; p++) { + if(*p == '\\' || *p == '\"') + printf("\\"); + putchar(*p); + } +} diff --git a/src/cmd/gc/mkopnames b/src/cmd/gc/mkopnames new file mode 100755 index 000000000..fb2ceec81 --- /dev/null +++ b/src/cmd/gc/mkopnames @@ -0,0 +1,24 @@ +#!/bin/sh +# 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. + +# Disable colored grep if user has it set to --color=always. +# (Arguably user error.) +export GREP_OPTIONS="" + +echo '// auto generated by mkopnames' +echo 'static char*' +echo 'opnames[] = ' +echo '{' +sed -n '/OXXX/,/OEND/p' go.h | + cpp | + sed 's!//.*!!; /^#/d' | + tr ' ' '\n' | + tr -d ' \t,' | + grep . | + sort | + grep -v '^OEND$' | + sed 's/O//; s/.*/ [O&] = "&",/' +echo '};' + diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c new file mode 100644 index 000000000..6cd4e2500 --- /dev/null +++ b/src/cmd/gc/mparith1.c @@ -0,0 +1,509 @@ +// 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" + +/// uses arithmetic + +int +mpcmpfixflt(Mpint *a, Mpflt *b) +{ + char buf[500]; + Mpflt c; + + snprint(buf, sizeof(buf), "%B", a); + mpatoflt(&c, buf); + return mpcmpfltflt(&c, b); +} + +int +mpcmpfltfix(Mpflt *a, Mpint *b) +{ + char buf[500]; + Mpflt c; + + snprint(buf, sizeof(buf), "%B", b); + mpatoflt(&c, buf); + return mpcmpfltflt(a, &c); +} + +int +mpcmpfixfix(Mpint *a, Mpint *b) +{ + Mpint c; + + mpmovefixfix(&c, a); + mpsubfixfix(&c, b); + return mptestfix(&c); +} + +int +mpcmpfixc(Mpint *b, vlong c) +{ + Mpint a; + + mpmovecfix(&a, c); + return mpcmpfixfix(&a, b); +} + +int +mpcmpfltflt(Mpflt *a, Mpflt *b) +{ + Mpflt c; + + mpmovefltflt(&c, a); + mpsubfltflt(&c, b); + return mptestflt(&c); +} + +int +mpcmpfltc(Mpflt *b, double c) +{ + Mpflt a; + + mpmovecflt(&a, c); + return mpcmpfltflt(&a, b); +} + +void +mpsubfixfix(Mpint *a, Mpint *b) +{ + mpnegfix(a); + mpaddfixfix(a, b); + mpnegfix(a); +} + +void +mpsubfltflt(Mpflt *a, Mpflt *b) +{ + mpnegflt(a); + mpaddfltflt(a, b); + mpnegflt(a); +} + +void +mpaddcfix(Mpint *a, vlong c) +{ + Mpint b; + + mpmovecfix(&b, c); + mpaddfixfix(a, &b); +} + +void +mpaddcflt(Mpflt *a, double c) +{ + Mpflt b; + + mpmovecflt(&b, c); + mpaddfltflt(a, &b); +} + +void +mpmulcfix(Mpint *a, vlong c) +{ + Mpint b; + + mpmovecfix(&b, c); + mpmulfixfix(a, &b); +} + +void +mpmulcflt(Mpflt *a, double c) +{ + Mpflt b; + + mpmovecflt(&b, c); + mpmulfltflt(a, &b); +} + +void +mpdivfixfix(Mpint *a, Mpint *b) +{ + Mpint q, r; + + mpdivmodfixfix(&q, &r, a, b); + mpmovefixfix(a, &q); +} + +void +mpmodfixfix(Mpint *a, Mpint *b) +{ + Mpint q, r; + + mpdivmodfixfix(&q, &r, a, b); + mpmovefixfix(a, &r); +} + +void +mpcomfix(Mpint *a) +{ + Mpint b; + + mpmovecfix(&b, 1); + mpnegfix(a); + mpsubfixfix(a, &b); +} + +void +mpmovefixflt(Mpflt *a, Mpint *b) +{ + a->val = *b; + a->exp = 0; + mpnorm(a); +} + +// convert (truncate) b to a. +// return -1 (but still convert) if b was non-integer. +static int +mpexactfltfix(Mpint *a, Mpflt *b) +{ + Mpflt f; + + *a = b->val; + mpshiftfix(a, b->exp); + if(b->exp < 0) { + f.val = *a; + f.exp = 0; + mpnorm(&f); + if(mpcmpfltflt(b, &f) != 0) + return -1; + } + return 0; +} + +int +mpmovefltfix(Mpint *a, Mpflt *b) +{ + Mpflt f; + int i; + + if(mpexactfltfix(a, b) == 0) + return 0; + + // try rounding down a little + f = *b; + f.val.a[0] = 0; + if(mpexactfltfix(a, &f) == 0) + return 0; + + // try rounding up a little + for(i=1; i<Mpprec; i++) { + f.val.a[i]++; + if(f.val.a[i] != Mpbase) + break; + f.val.a[i] = 0; + } + mpnorm(&f); + if(mpexactfltfix(a, &f) == 0) + return 0; + + return -1; +} + +void +mpmovefixfix(Mpint *a, Mpint *b) +{ + *a = *b; +} + +void +mpmovefltflt(Mpflt *a, Mpflt *b) +{ + *a = *b; +} + +static double tab[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7 }; +static void +mppow10flt(Mpflt *a, int p) +{ + if(p < 0) + abort(); + if(p < nelem(tab)) { + mpmovecflt(a, tab[p]); + return; + } + mppow10flt(a, p>>1); + mpmulfltflt(a, a); + if(p & 1) + mpmulcflt(a, 10); +} + +// +// floating point input +// required syntax is [+-]d*[.]d*[e[+-]d*] +// +void +mpatoflt(Mpflt *a, char *as) +{ + Mpflt b; + int dp, c, f, ef, ex, eb; + char *s; + + s = as; + dp = 0; /* digits after decimal point */ + f = 0; /* sign */ + ex = 0; /* exponent */ + eb = 0; /* binary point */ + + mpmovecflt(a, 0.0); + for(;;) { + switch(c = *s++) { + default: + goto bad; + + case '-': + f = 1; + + case ' ': + case '\t': + case '+': + continue; + + case '.': + dp = 1; + continue; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '0': + mpmulcflt(a, 10); + mpaddcflt(a, c-'0'); + if(dp) + dp++; + continue; + + case 'P': + case 'p': + eb = 1; + + case 'E': + case 'e': + ex = 0; + ef = 0; + for(;;) { + c = *s++; + if(c == '+' || c == ' ' || c == '\t') + continue; + if(c == '-') { + ef = 1; + continue; + } + if(c >= '0' && c <= '9') { + ex = ex*10 + (c-'0'); + if(ex > 1e8) { + yyerror("exponent out of range"); + errorexit(); + } + continue; + } + break; + } + if(ef) + ex = -ex; + + case 0: + break; + } + break; + } + + if(eb) { + if(dp) + goto bad; + a->exp += ex; + goto out; + } + + if(dp) + dp--; + if(mpcmpfltc(a, 0.0) != 0) { + if(ex >= dp) { + mppow10flt(&b, ex-dp); + mpmulfltflt(a, &b); + } else { + mppow10flt(&b, dp-ex); + mpdivfltflt(a, &b); + } + } + +out: + if(f) + mpnegflt(a); + return; + +bad: + yyerror("set ovf in mpatof"); + mpmovecflt(a, 0.0); +} + +// +// fixed point input +// required syntax is [+-][0[x]]d* +// +void +mpatofix(Mpint *a, char *as) +{ + int c, f; + char *s; + + s = as; + f = 0; + mpmovecfix(a, 0); + + c = *s++; + switch(c) { + case '-': + f = 1; + + case '+': + c = *s++; + if(c != '0') + break; + + case '0': + goto oct; + } + + while(c) { + if(c >= '0' && c <= '9') { + mpmulcfix(a, 10); + mpaddcfix(a, c-'0'); + c = *s++; + continue; + } + goto bad; + } + goto out; + +oct: + c = *s++; + if(c == 'x' || c == 'X') + goto hex; + while(c) { + if(c >= '0' && c <= '7') { + mpmulcfix(a, 8); + mpaddcfix(a, c-'0'); + c = *s++; + continue; + } + goto bad; + } + goto out; + +hex: + c = *s++; + while(c) { + if(c >= '0' && c <= '9') { + mpmulcfix(a, 16); + mpaddcfix(a, c-'0'); + c = *s++; + continue; + } + if(c >= 'a' && c <= 'f') { + mpmulcfix(a, 16); + mpaddcfix(a, c+10-'a'); + c = *s++; + continue; + } + if(c >= 'A' && c <= 'F') { + mpmulcfix(a, 16); + mpaddcfix(a, c+10-'A'); + c = *s++; + continue; + } + goto bad; + } + +out: + if(f) + mpnegfix(a); + return; + +bad: + yyerror("set ovf in mpatov: %s", as); + mpmovecfix(a, 0); +} + +int +Bconv(Fmt *fp) +{ + char buf[500], *p; + Mpint *xval, q, r, ten; + int f; + + xval = va_arg(fp->args, Mpint*); + mpmovefixfix(&q, xval); + f = 0; + if(mptestfix(&q) < 0) { + f = 1; + mpnegfix(&q); + } + mpmovecfix(&ten, 10); + + p = &buf[sizeof(buf)]; + *--p = 0; + for(;;) { + mpdivmodfixfix(&q, &r, &q, &ten); + *--p = mpgetfix(&r) + '0'; + if(mptestfix(&q) <= 0) + break; + } + if(f) + *--p = '-'; + return fmtstrcpy(fp, p); +} + +int +Fconv(Fmt *fp) +{ + char buf[500]; + Mpflt *fvp, fv; + double d; + + fvp = va_arg(fp->args, Mpflt*); + if(fp->flags & FmtSharp) { + // alternate form - decimal for error messages. + // for well in range, convert to double and use print's %g + if(-900 < fvp->exp && fvp->exp < 900) { + d = mpgetflt(fvp); + if(d >= 0 && (fp->flags & FmtSign)) + fmtprint(fp, "+"); + return fmtprint(fp, "%g", d); + } + // TODO(rsc): for well out of range, print + // an approximation like 1.234e1000 + } + + if(sigfig(fvp) == 0) { + snprint(buf, sizeof(buf), "0p+0"); + goto out; + } + fv = *fvp; + + while(fv.val.a[0] == 0) { + mpshiftfix(&fv.val, -Mpscale); + fv.exp += Mpscale; + } + while((fv.val.a[0]&1) == 0) { + mpshiftfix(&fv.val, -1); + fv.exp += 1; + } + + if(fv.exp >= 0) { + snprint(buf, sizeof(buf), "%Bp+%d", &fv.val, fv.exp); + goto out; + } + snprint(buf, sizeof(buf), "%Bp-%d", &fv.val, -fv.exp); + +out: + return fmtstrcpy(fp, buf); +} diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c new file mode 100644 index 000000000..403255005 --- /dev/null +++ b/src/cmd/gc/mparith2.c @@ -0,0 +1,683 @@ +// 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" + +// +// return the significant +// words of the argument +// +static int +mplen(Mpint *a) +{ + int i, n; + long *a1; + + n = -1; + a1 = &a->a[0]; + for(i=0; i<Mpprec; i++) { + if(*a1++ != 0) + n = i; + } + return n+1; +} + +// +// left shift mpint by one +// ignores sign and overflow +// +static void +mplsh(Mpint *a) +{ + long *a1, x; + int i, c; + + c = 0; + a1 = &a->a[0]; + for(i=0; i<Mpprec; i++) { + x = (*a1 << 1) + c; + c = 0; + if(x >= Mpbase) { + x -= Mpbase; + c = 1; + } + *a1++ = x; + } +} + +// +// left shift mpint by Mpscale +// ignores sign and overflow +// +static void +mplshw(Mpint *a) +{ + long *a1; + int i; + + a1 = &a->a[Mpprec-1]; + for(i=1; i<Mpprec; i++) { + a1[0] = a1[-1]; + a1--; + } + a1[0] = 0; +} + +// +// right shift mpint by one +// ignores sign and overflow +// +static void +mprsh(Mpint *a) +{ + long *a1, x, lo; + int i, c; + + c = 0; + lo = a->a[0] & 1; + a1 = &a->a[Mpprec]; + for(i=0; i<Mpprec; i++) { + x = *--a1; + *a1 = (x + c) >> 1; + c = 0; + if(x & 1) + c = Mpbase; + } + if(a->neg && lo != 0) + mpaddcfix(a, -1); +} + +// +// right shift mpint by Mpscale +// ignores sign and overflow +// +static void +mprshw(Mpint *a) +{ + long *a1, lo; + int i; + + lo = a->a[0]; + a1 = &a->a[0]; + for(i=1; i<Mpprec; i++) { + a1[0] = a1[1]; + a1++; + } + a1[0] = 0; + if(a->neg && lo != 0) + mpaddcfix(a, -1); +} + +// +// return the sign of (abs(a)-abs(b)) +// +static int +mpcmp(Mpint *a, Mpint *b) +{ + long x, *a1, *b1; + int i; + + if(a->ovf || b->ovf) { + yyerror("ovf in cmp"); + return 0; + } + + a1 = &a->a[0] + Mpprec; + b1 = &b->a[0] + Mpprec; + + for(i=0; i<Mpprec; i++) { + x = *--a1 - *--b1; + if(x > 0) + return +1; + if(x < 0) + return -1; + } + return 0; +} + +// +// negate a +// ignore sign and ovf +// +static void +mpneg(Mpint *a) +{ + long x, *a1; + int i, c; + + a1 = &a->a[0]; + c = 0; + for(i=0; i<Mpprec; i++) { + x = -*a1 -c; + c = 0; + if(x < 0) { + x += Mpbase; + c = 1; + } + *a1++ = x; + } +} + +// shift left by s (or right by -s) +void +mpshiftfix(Mpint *a, int s) +{ + if(s >= 0) { + while(s >= Mpscale) { + mplshw(a); + s -= Mpscale; + } + while(s > 0) { + mplsh(a); + s--; + } + } else { + s = -s; + while(s >= Mpscale) { + mprshw(a); + s -= Mpscale; + } + while(s > 0) { + mprsh(a); + s--; + } + } +} + +/// implements fix arihmetic + +void +mpaddfixfix(Mpint *a, Mpint *b) +{ + int i, c; + long x, *a1, *b1; + + if(a->ovf || b->ovf) { + yyerror("ovf in mpaddxx"); + a->ovf = 1; + return; + } + + c = 0; + a1 = &a->a[0]; + b1 = &b->a[0]; + if(a->neg != b->neg) + goto sub; + + // perform a+b + for(i=0; i<Mpprec; i++) { + x = *a1 + *b1++ + c; + c = 0; + if(x >= Mpbase) { + x -= Mpbase; + c = 1; + } + *a1++ = x; + } + a->ovf = c; + if(a->ovf) + yyerror("set ovf in mpaddxx"); + + return; + +sub: + // perform a-b + switch(mpcmp(a, b)) { + case 0: + mpmovecfix(a, 0); + break; + + case 1: + for(i=0; i<Mpprec; i++) { + x = *a1 - *b1++ - c; + c = 0; + if(x < 0) { + x += Mpbase; + c = 1; + } + *a1++ = x; + } + break; + + case -1: + a->neg ^= 1; + for(i=0; i<Mpprec; i++) { + x = *b1++ - *a1 - c; + c = 0; + if(x < 0) { + x += Mpbase; + c = 1; + } + *a1++ = x; + } + break; + } +} + +void +mpmulfixfix(Mpint *a, Mpint *b) +{ + + int i, j, na, nb; + long *a1, x; + Mpint s, q; + + if(a->ovf || b->ovf) { + yyerror("ovf in mpmulfixfix"); + a->ovf = 1; + return; + } + + // pick the smaller + // to test for bits + na = mplen(a); + nb = mplen(b); + if(na > nb) { + mpmovefixfix(&s, a); + a1 = &b->a[0]; + na = nb; + } else { + mpmovefixfix(&s, b); + a1 = &a->a[0]; + } + s.neg = 0; + + mpmovecfix(&q, 0); + for(i=0; i<na; i++) { + x = *a1++; + for(j=0; j<Mpscale; j++) { + if(x & 1) + mpaddfixfix(&q, &s); + mplsh(&s); + x >>= 1; + } + } + + q.neg = a->neg ^ b->neg; + mpmovefixfix(a, &q); + if(a->ovf) + yyerror("set ovf in mpmulfixfix"); +} + +void +mpmulfract(Mpint *a, Mpint *b) +{ + + int i, j; + long *a1, x; + Mpint s, q; + + if(a->ovf || b->ovf) { + yyerror("ovf in mpmulflt"); + a->ovf = 1; + return; + } + + mpmovefixfix(&s, b); + a1 = &a->a[Mpprec]; + s.neg = 0; + mpmovecfix(&q, 0); + + x = *--a1; + if(x != 0) + yyerror("mpmulfract not normal"); + + for(i=0; i<Mpprec-1; i++) { + x = *--a1; + if(x == 0) { + mprshw(&s); + continue; + } + for(j=0; j<Mpscale; j++) { + x <<= 1; + if(x & Mpbase) + mpaddfixfix(&q, &s); + mprsh(&s); + } + } + + q.neg = a->neg ^ b->neg; + mpmovefixfix(a, &q); + if(a->ovf) + yyerror("set ovf in mpmulflt"); +} + +void +mporfixfix(Mpint *a, Mpint *b) +{ + int i; + long x, *a1, *b1; + + if(a->ovf || b->ovf) { + yyerror("ovf in mporfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + if(a->neg) { + a->neg = 0; + mpneg(a); + } + if(b->neg) + mpneg(b); + + a1 = &a->a[0]; + b1 = &b->a[0]; + for(i=0; i<Mpprec; i++) { + x = *a1 | *b1++; + *a1++ = x; + } + + if(b->neg) + mpneg(b); + if(x & Mpsign) { + a->neg = 1; + mpneg(a); + } +} + +void +mpandfixfix(Mpint *a, Mpint *b) +{ + int i; + long x, *a1, *b1; + + if(a->ovf || b->ovf) { + yyerror("ovf in mpandfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + if(a->neg) { + a->neg = 0; + mpneg(a); + } + if(b->neg) + mpneg(b); + + a1 = &a->a[0]; + b1 = &b->a[0]; + for(i=0; i<Mpprec; i++) { + x = *a1 & *b1++; + *a1++ = x; + } + + if(b->neg) + mpneg(b); + if(x & Mpsign) { + a->neg = 1; + mpneg(a); + } +} + +void +mpandnotfixfix(Mpint *a, Mpint *b) +{ + int i; + long x, *a1, *b1; + + if(a->ovf || b->ovf) { + yyerror("ovf in mpandnotfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + if(a->neg) { + a->neg = 0; + mpneg(a); + } + if(b->neg) + mpneg(b); + + a1 = &a->a[0]; + b1 = &b->a[0]; + for(i=0; i<Mpprec; i++) { + x = *a1 & ~*b1++; + *a1++ = x; + } + + if(b->neg) + mpneg(b); + if(x & Mpsign) { + a->neg = 1; + mpneg(a); + } +} + +void +mpxorfixfix(Mpint *a, Mpint *b) +{ + int i; + long x, *a1, *b1; + + if(a->ovf || b->ovf) { + yyerror("ovf in mporfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + if(a->neg) { + a->neg = 0; + mpneg(a); + } + if(b->neg) + mpneg(b); + + a1 = &a->a[0]; + b1 = &b->a[0]; + for(i=0; i<Mpprec; i++) { + x = *a1 ^ *b1++; + *a1++ = x; + } + + if(b->neg) + mpneg(b); + if(x & Mpsign) { + a->neg = 1; + mpneg(a); + } +} + +void +mplshfixfix(Mpint *a, Mpint *b) +{ + vlong s; + + if(a->ovf || b->ovf) { + yyerror("ovf in mporfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + s = mpgetfix(b); + if(s < 0 || s >= Mpprec*Mpscale) { + yyerror("stupid shift: %lld", s); + mpmovecfix(a, 0); + return; + } + + mpshiftfix(a, s); +} + +void +mprshfixfix(Mpint *a, Mpint *b) +{ + vlong s; + + if(a->ovf || b->ovf) { + yyerror("ovf in mprshfixfix"); + mpmovecfix(a, 0); + a->ovf = 1; + return; + } + s = mpgetfix(b); + if(s < 0 || s >= Mpprec*Mpscale) { + yyerror("stupid shift: %lld", s); + if(a->neg) + mpmovecfix(a, -1); + else + mpmovecfix(a, 0); + return; + } + + mpshiftfix(a, -s); +} + +void +mpnegfix(Mpint *a) +{ + a->neg ^= 1; +} + +vlong +mpgetfix(Mpint *a) +{ + vlong v; + + if(a->ovf) { + yyerror("constant overflow"); + return 0; + } + + v = (vlong)a->a[0]; + v |= (vlong)a->a[1] << Mpscale; + v |= (vlong)a->a[2] << (Mpscale+Mpscale); + if(a->neg) + v = -v; + return v; +} + +void +mpmovecfix(Mpint *a, vlong c) +{ + int i; + long *a1; + vlong x; + + a->neg = 0; + a->ovf = 0; + + x = c; + if(x < 0) { + a->neg = 1; + x = -x; + } + + a1 = &a->a[0]; + for(i=0; i<Mpprec; i++) { + *a1++ = x&Mpmask; + x >>= Mpscale; + } +} + +void +mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d) +{ + int i, ns, ds; + + ns = n->neg; + ds = d->neg; + n->neg = 0; + d->neg = 0; + + mpmovefixfix(r, n); + mpmovecfix(q, 0); + + // shift denominator until it + // is larger than numerator + for(i=0; i<Mpprec*Mpscale; i++) { + if(mpcmp(d, r) > 0) + break; + mplsh(d); + } + + // if it never happens + // denominator is probably zero + if(i >= Mpprec*Mpscale) { + q->ovf = 1; + r->ovf = 1; + n->neg = ns; + d->neg = ds; + yyerror("set ovf in mpdivmodfixfix"); + return; + } + + // shift denominator back creating + // quotient a bit at a time + // when done the remaining numerator + // will be the remainder + for(; i>0; i--) { + mplsh(q); + mprsh(d); + if(mpcmp(d, r) <= 0) { + mpaddcfix(q, 1); + mpsubfixfix(r, d); + } + } + + n->neg = ns; + d->neg = ds; + r->neg = ns; + q->neg = ns^ds; +} + +static int +iszero(Mpint *a) +{ + long *a1; + int i; + a1 = &a->a[0] + Mpprec; + for(i=0; i<Mpprec; i++) { + if(*--a1 != 0) + return 0; + } + return 1; +} + +void +mpdivfract(Mpint *a, Mpint *b) +{ + Mpint n, d; + int i, j, neg; + long *a1, x; + + mpmovefixfix(&n, a); // numerator + mpmovefixfix(&d, b); // denominator + a1 = &a->a[Mpprec]; // quotient + + neg = n.neg ^ d.neg; + n.neg = 0; + d.neg = 0; + for(i=0; i<Mpprec; i++) { + x = 0; + for(j=0; j<Mpscale; j++) { + x <<= 1; + if(mpcmp(&d, &n) <= 0) { + if(!iszero(&d)) + x |= 1; + mpsubfixfix(&n, &d); + } + mprsh(&d); + } + *--a1 = x; + } + a->neg = neg; +} + +int +mptestfix(Mpint *a) +{ + Mpint b; + int r; + + mpmovecfix(&b, 0); + r = mpcmp(a, &b); + if(a->neg) { + if(r > 0) + return -1; + if(r < 0) + return +1; + } + return r; +} diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c new file mode 100644 index 000000000..b11a4f5f1 --- /dev/null +++ b/src/cmd/gc/mparith3.c @@ -0,0 +1,313 @@ +// 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" + +/* + * returns the leading non-zero + * word of the number + */ +int +sigfig(Mpflt *a) +{ + int i; + + for(i=Mpprec-1; i>=0; i--) + if(a->val.a[i] != 0) + break; +//print("sigfig %d %d\n", i-z+1, z); + return i+1; +} + +/* + * shifts the leading non-zero + * word of the number to Mpnorm + */ +void +mpnorm(Mpflt *a) +{ + int s, os; + long x; + + os = sigfig(a); + if(os == 0) { + // zero + a->exp = 0; + a->val.neg = 0; + return; + } + + // this will normalize to the nearest word + x = a->val.a[os-1]; + s = (Mpnorm-os) * Mpscale; + + // further normalize to the nearest bit + for(;;) { + x <<= 1; + if(x & Mpbase) + break; + s++; + if(x == 0) { + // this error comes from trying to + // convert an Inf or something + // where the initial x=0x80000000 + s = (Mpnorm-os) * Mpscale; + break; + } + } + + mpshiftfix(&a->val, s); + a->exp -= s; +} + +/// implements float arihmetic + +void +mpaddfltflt(Mpflt *a, Mpflt *b) +{ + int sa, sb, s; + Mpflt c; + + if(Mpdebug) + print("\n%F + %F", a, b); + + sa = sigfig(a); + if(sa == 0) { + mpmovefltflt(a, b); + goto out; + } + + sb = sigfig(b); + if(sb == 0) + goto out; + + s = a->exp - b->exp; + if(s > 0) { + // a is larger, shift b right + mpmovefltflt(&c, b); + mpshiftfix(&c.val, -s); + mpaddfixfix(&a->val, &c.val); + goto out; + } + if(s < 0) { + // b is larger, shift a right + mpshiftfix(&a->val, s); + a->exp -= s; + mpaddfixfix(&a->val, &b->val); + goto out; + } + mpaddfixfix(&a->val, &b->val); + +out: + mpnorm(a); + if(Mpdebug) + print(" = %F\n\n", a); +} + +void +mpmulfltflt(Mpflt *a, Mpflt *b) +{ + int sa, sb; + + if(Mpdebug) + print("%F\n * %F\n", a, b); + + sa = sigfig(a); + if(sa == 0) { + // zero + a->exp = 0; + a->val.neg = 0; + return; + } + + sb = sigfig(b); + if(sb == 0) { + // zero + mpmovefltflt(a, b); + return; + } + + mpmulfract(&a->val, &b->val); + a->exp = (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1; + + mpnorm(a); + if(Mpdebug) + print(" = %F\n\n", a); +} + +void +mpdivfltflt(Mpflt *a, Mpflt *b) +{ + int sa, sb; + Mpflt c; + + if(Mpdebug) + print("%F\n / %F\n", a, b); + + sb = sigfig(b); + if(sb == 0) { + // zero and ovfl + a->exp = 0; + a->val.neg = 0; + a->val.ovf = 1; + yyerror("mpdivfltflt divide by zero"); + return; + } + + sa = sigfig(a); + if(sa == 0) { + // zero + a->exp = 0; + a->val.neg = 0; + return; + } + + // adjust b to top + mpmovefltflt(&c, b); + mpshiftfix(&c.val, Mpscale); + + // divide + mpdivfract(&a->val, &c.val); + a->exp = (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1; + + mpnorm(a); + if(Mpdebug) + print(" = %F\n\n", a); +} + +double +mpgetflt(Mpflt *a) +{ + int s, i, e; + uvlong v, vm; + double f; + + if(a->val.ovf) + yyerror("mpgetflt ovf"); + + s = sigfig(a); + if(s == 0) + return 0; + + if(s != Mpnorm) { + yyerror("mpgetflt norm"); + mpnorm(a); + } + + while((a->val.a[Mpnorm-1] & Mpsign) == 0) { + mpshiftfix(&a->val, 1); + a->exp -= 1; + } + + // the magic numbers (64, 63, 53, 10, -1074) are + // IEEE specific. this should be done machine + // independently or in the 6g half of the compiler + + // pick up the mantissa and a rounding bit in a uvlong + s = 53+1; + v = 0; + for(i=Mpnorm-1; s>=Mpscale; i--) { + v = (v<<Mpscale) | a->val.a[i]; + s -= Mpscale; + } + vm = v; + if(s > 0) + vm = (vm<<s) | (a->val.a[i]>>(Mpscale-s)); + + // continue with 64 more bits + s += 64; + for(; s>=Mpscale; i--) { + v = (v<<Mpscale) | a->val.a[i]; + s -= Mpscale; + } + if(s > 0) + v = (v<<s) | (a->val.a[i]>>(Mpscale-s)); + + // gradual underflow + e = Mpnorm*Mpscale + a->exp - 53; + if(e < -1074) { + s = -e - 1074; + if(s > 54) + s = 54; + v |= vm & ((1ULL<<s) - 1); + vm >>= s; + e = -1074; + } + +//print("vm=%.16llux v=%.16llux\n", vm, v); + // round toward even + if(v != 0 || (vm&2ULL) != 0) + vm = (vm>>1) + (vm&1ULL); + else + vm >>= 1; + + f = (double)(vm); + f = ldexp(f, e); + + if(a->val.neg) + f = -f; + return f; +} + +void +mpmovecflt(Mpflt *a, double c) +{ + int i; + double f; + long l; + + if(Mpdebug) + print("\nconst %g", c); + mpmovecfix(&a->val, 0); + a->exp = 0; + if(c == 0) + goto out; + if(c < 0) { + a->val.neg = 1; + c = -c; + } + + f = frexp(c, &i); + a->exp = i; + + for(i=0; i<10; i++) { + f = f*Mpbase; + l = floor(f); + f = f - l; + a->exp -= Mpscale; + a->val.a[0] = l; + if(f == 0) + break; + mpshiftfix(&a->val, Mpscale); + } + +out: + mpnorm(a); + if(Mpdebug) + print(" = %F\n", a); +} + +void +mpnegflt(Mpflt *a) +{ + a->val.neg ^= 1; +} + +int +mptestflt(Mpflt *a) +{ + int s; + + if(Mpdebug) + print("\n%F?", a); + s = sigfig(a); + if(s != 0) { + s = +1; + if(a->val.neg) + s = -1; + } + if(Mpdebug) + print(" = %d\n", s); + return s; +} diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c new file mode 100644 index 000000000..456aabb88 --- /dev/null +++ b/src/cmd/gc/obj.c @@ -0,0 +1,301 @@ +// 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" + +/* + * architecture-independent object file output + */ + +static void outhist(Biobuf *b); +static void dumpglobls(void); + +void +dumpobj(void) +{ + bout = Bopen(outfile, OWRITE); + if(bout == nil) { + flusherrors(); + print("can't create %s: %r\n", outfile); + errorexit(); + } + + Bprint(bout, "go object %s %s %s\n", getgoos(), thestring, getgoversion()); + Bprint(bout, " exports automatically generated from\n"); + Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name); + dumpexport(); + Bprint(bout, "\n!\n"); + + outhist(bout); + + // add nil plist w AEND to catch + // auto-generated trampolines, data + newplist(); + + dumpglobls(); + dumptypestructs(); + dumpdata(); + dumpfuncs(); + + Bterm(bout); +} + +static void +dumpglobls(void) +{ + Node *n; + NodeList *l; + + // add globals + for(l=externdcl; l; l=l->next) { + n = l->n; + if(n->op != ONAME) + continue; + + if(n->type == T) + fatal("external %#N nil type\n", n); + if(n->class == PFUNC) + continue; + if(n->sym->pkg != localpkg) + continue; + dowidth(n->type); + + ggloblnod(n, n->type->width); + } +} + +void +Bputname(Biobuf *b, Sym *s) +{ + Bprint(b, "%s", s->pkg->prefix); + Bputc(b, '.'); + Bwrite(b, s->name, strlen(s->name)+1); +} + +static void +outzfile(Biobuf *b, char *p) +{ + char *q, *q2; + + while(p) { + q = utfrune(p, '/'); + if(windows) { + q2 = utfrune(p, '\\'); + if(q2 && (!q || q2 < q)) + q = q2; + } + if(!q) { + zfile(b, p, strlen(p)); + return; + } + if(q > p) + zfile(b, p, q-p); + p = q + 1; + } +} + +#define isdelim(c) (c == '/' || c == '\\') + +static void +outwinname(Biobuf *b, Hist *h, char *ds, char *p) +{ + if(isdelim(p[0])) { + // full rooted name + zfile(b, ds, 3); // leading "c:/" + outzfile(b, p+1); + } else { + // relative name + if(h->offset == 0 && pathname && pathname[1] == ':') { + if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) { + // using current drive + zfile(b, pathname, 3); // leading "c:/" + outzfile(b, pathname+3); + } else { + // using drive other then current, + // we don't have any simple way to + // determine current working directory + // there, therefore will output name as is + zfile(b, ds, 2); // leading "c:" + } + } + outzfile(b, p); + } +} + +static void +outhist(Biobuf *b) +{ + Hist *h; + int i, depth = 0; + char *p, ds[] = {'c', ':', '/', 0}; + + for(h = hist; h != H; h = h->link) { + p = h->name; + if(p) { + if(windows) { + // if windows variable is set, then, we know already, + // pathname is started with windows drive specifier + // and all '\' were replaced with '/' (see lex.c) + if(isdelim(p[0]) && isdelim(p[1])) { + // file name has network name in it, + // like \\server\share\dir\file.go + zfile(b, "//", 2); // leading "//" + outzfile(b, p+2); + } else if(p[1] == ':') { + // file name has drive letter in it + ds[0] = p[0]; + outwinname(b, h, ds, p+2); + } else { + // no drive letter in file name + outwinname(b, h, pathname, p); + } + } else { + if(p[0] == '/') { + // full rooted name, like /home/rsc/dir/file.go + zfile(b, "/", 1); // leading "/" + outzfile(b, p+1); + } else { + // relative name, like dir/file.go + if(h->offset >= 0 && pathname && pathname[0] == '/') { + zfile(b, "/", 1); // leading "/" + outzfile(b, pathname+1); + } + outzfile(b, p); + } + } + if(h->offset > 0) { + //line directive + depth++; + } + } else if(depth > 0) { + for(i = 0; i < depth; i++) + zhist(b, h->line, h->offset); + depth = 0; + } + zhist(b, h->line, h->offset); + } +} + +void +ieeedtod(uint64 *ieee, double native) +{ + double fr, ho, f; + int exp; + uint32 h, l; + uint64 bits; + + if(native < 0) { + ieeedtod(ieee, -native); + *ieee |= 1ULL<<63; + return; + } + if(native == 0) { + *ieee = 0; + return; + } + fr = frexp(native, &exp); + f = 2097152L; /* shouldnt use fp constants here */ + fr = modf(fr*f, &ho); + h = ho; + h &= 0xfffffL; + f = 65536L; + fr = modf(fr*f, &ho); + l = ho; + l <<= 16; + l |= (int32)(fr*f); + bits = ((uint64)h<<32) | l; + if(exp < -1021) { + // gradual underflow + bits |= 1LL<<52; + bits >>= -1021 - exp; + exp = -1022; + } + bits |= (uint64)(exp+1022L) << 52; + *ieee = bits; +} + +int +duint8(Sym *s, int off, uint8 v) +{ + return duintxx(s, off, v, 1); +} + +int +duint16(Sym *s, int off, uint16 v) +{ + return duintxx(s, off, v, 2); +} + +int +duint32(Sym *s, int off, uint32 v) +{ + return duintxx(s, off, v, 4); +} + +int +duint64(Sym *s, int off, uint64 v) +{ + return duintxx(s, off, v, 8); +} + +int +duintptr(Sym *s, int off, uint64 v) +{ + return duintxx(s, off, v, widthptr); +} + +Sym* +stringsym(char *s, int len) +{ + static int gen; + Sym *sym; + int off, n, m; + struct { + Strlit lit; + char buf[110]; + } tmp; + Pkg *pkg; + + if(len > 100) { + // huge strings are made static to avoid long names + snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen); + pkg = localpkg; + } else { + // small strings get named by their contents, + // so that multiple modules using the same string + // can share it. + tmp.lit.len = len; + memmove(tmp.lit.s, s, len); + tmp.lit.s[len] = '\0'; + snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp); + pkg = gostringpkg; + } + sym = pkglookup(namebuf, pkg); + + // SymUniq flag indicates that data is generated already + if(sym->flags & SymUniq) + return sym; + sym->flags |= SymUniq; + + data(); + off = 0; + + // string header + off = dsymptr(sym, off, sym, widthptr+4); + off = duint32(sym, off, len); + + // string data + for(n=0; n<len; n+=m) { + m = 8; + if(m > len-n) + m = len-n; + off = dsname(sym, off, s+n, m); + } + off = duint8(sym, off, 0); // terminating NUL for runtime + off = (off+widthptr-1)&~(widthptr-1); // round to pointer alignment + ggloblsym(sym, off, 1); + text(); + + return sym; +} diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c new file mode 100644 index 000000000..abe8ea892 --- /dev/null +++ b/src/cmd/gc/pgen.c @@ -0,0 +1,210 @@ +// Copyright 2011 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 "gg.h" +#include "opt.h" + +static void compactframe(Prog* p); + +void +compile(Node *fn) +{ + Plist *pl; + Node nod1, *n; + Prog *ptxt; + int32 lno; + Type *t; + Iter save; + vlong oldstksize; + + if(newproc == N) { + newproc = sysfunc("newproc"); + deferproc = sysfunc("deferproc"); + deferreturn = sysfunc("deferreturn"); + panicindex = sysfunc("panicindex"); + panicslice = sysfunc("panicslice"); + throwreturn = sysfunc("throwreturn"); + } + + if(fn->nbody == nil) + return; + + saveerrors(); + + // set up domain for labels + clearlabels(); + + lno = setlineno(fn); + + curfn = fn; + dowidth(curfn->type); + + if(curfn->type->outnamed) { + // add clearing of the output parameters + t = structfirst(&save, getoutarg(curfn->type)); + while(t != T) { + if(t->nname != N) { + n = nod(OAS, t->nname, N); + typecheck(&n, Etop); + curfn->nbody = concat(list1(n), curfn->nbody); + } + t = structnext(&save); + } + } + + hasdefer = 0; + walk(curfn); + if(nerrors != 0) + goto ret; + + allocparams(); + + continpc = P; + breakpc = P; + + pl = newplist(); + pl->name = curfn->nname; + + setlineno(curfn); + + nodconst(&nod1, types[TINT32], 0); + ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1); + afunclit(&ptxt->from); + + ginit(); + genlist(curfn->enter); + + retpc = nil; + if(hasdefer || curfn->exit) { + Prog *p1; + + p1 = gjmp(nil); + retpc = gjmp(nil); + patch(p1, pc); + } + + genlist(curfn->nbody); + gclean(); + checklabels(); + if(nerrors != 0) + goto ret; + if(curfn->endlineno) + lineno = curfn->endlineno; + + if(curfn->type->outtuple != 0) + ginscall(throwreturn, 0); + + if(retpc) + patch(retpc, pc); + ginit(); + if(hasdefer) + ginscall(deferreturn, 0); + if(curfn->exit) + genlist(curfn->exit); + gclean(); + if(nerrors != 0) + goto ret; + pc->as = ARET; // overwrite AEND + pc->lineno = lineno; + + if(!debug['N'] || debug['R'] || debug['P']) { + regopt(ptxt); + } + + oldstksize = stksize; + compactframe(ptxt); + if(0) + print("compactframe: %ld to %ld\n", oldstksize, stksize); + + defframe(ptxt); + + if(0) + frame(0); + +ret: + lineno = lno; +} + + +// Sort the list of stack variables. autos after anything else, +// within autos, unused after used, and within used on reverse alignment. +// non-autos sort on offset. +static int +cmpstackvar(Node *a, Node *b) +{ + if (a->class != b->class) + return (a->class == PAUTO) ? 1 : -1; + if (a->class != PAUTO) + return a->xoffset - b->xoffset; + if ((a->used == 0) != (b->used == 0)) + return b->used - a->used; + return b->type->align - a->type->align; + +} + +// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. +static void +compactframe(Prog* ptxt) +{ + NodeList *ll; + Node* n; + vlong w; + + if (stksize == 0) + return; + + // Mark the PAUTO's unused. + for(ll=curfn->dcl; ll != nil; ll=ll->next) + if (ll->n->class == PAUTO) + ll->n->used = 0; + + markautoused(ptxt); + + listsort(&curfn->dcl, cmpstackvar); + + // Unused autos are at the end, chop 'em off. + ll = curfn->dcl; + n = ll->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + curfn->dcl = nil; + stksize = 0; + return; + } + + for(ll = curfn->dcl; ll->next != nil; ll=ll->next) { + n = ll->next->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + ll->next = nil; + curfn->dcl->end = ll; + break; + } + } + + // Reassign stack offsets of the locals that are still there. + stksize = 0; + for(ll = curfn->dcl; ll != nil; ll=ll->next) { + n = ll->n; + if (n->class != PAUTO || n->op != ONAME) + continue; + + w = n->type->width; + if(w >= MAXWIDTH || w < 0) + fatal("bad width"); + stksize += w; + stksize = rnd(stksize, n->type->align); + if(thechar == '5') + stksize = rnd(stksize, widthptr); + n->stkdelta = -stksize - n->xoffset; + } + + fixautoused(ptxt); + + // The debug information needs accurate offsets on the symbols. + for(ll = curfn->dcl ;ll != nil; ll=ll->next) { + if (ll->n->class != PAUTO || ll->n->op != ONAME) + continue; + ll->n->xoffset += ll->n->stkdelta; + ll->n->stkdelta = 0; + } +} diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c new file mode 100644 index 000000000..5913e848a --- /dev/null +++ b/src/cmd/gc/print.c @@ -0,0 +1,480 @@ +// 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" + +enum +{ + PFIXME = 0, +}; + +void +exprlistfmt(Fmt *f, NodeList *l) +{ + for(; l; l=l->next) { + exprfmt(f, l->n, 0); + if(l->next) + fmtprint(f, ", "); + } +} + +void +exprfmt(Fmt *f, Node *n, int prec) +{ + int nprec; + char *p; + + nprec = 0; + if(n == nil) { + fmtprint(f, "<nil>"); + return; + } + + if(n->implicit) { + exprfmt(f, n->left, prec); + return; + } + + switch(n->op) { + case OAPPEND: + case ONAME: + case ONONAME: + case OPACK: + case OLITERAL: + case ODOT: + case ODOTPTR: + case ODOTINTER: + case ODOTMETH: + case ODOTTYPE: + case ODOTTYPE2: + case OXDOT: + case OARRAYBYTESTR: + case OCAP: + case OCLOSE: + case OCOPY: + case OLEN: + case OMAKE: + case ONEW: + case OPANIC: + case OPRINT: + case OPRINTN: + case OCALL: + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: + case OCONV: + case OCONVNOP: + case OMAKESLICE: + case ORUNESTR: + case OADDR: + case OCOM: + case OIND: + case OMINUS: + case ONOT: + case OPLUS: + case ORECV: + case OCONVIFACE: + case OTPAREN: + case OINDEX: + case OINDEXMAP: + case OPAREN: + nprec = 7; + break; + + case OMUL: + case ODIV: + case OMOD: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + nprec = 6; + break; + + case OADD: + case OSUB: + case OOR: + case OXOR: + nprec = 5; + break; + + case OEQ: + case OLT: + case OLE: + case OGE: + case OGT: + case ONE: + nprec = 4; + break; + + case OSEND: + nprec = 3; + break; + + case OANDAND: + nprec = 2; + break; + + case OOROR: + nprec = 1; + break; + + case OTYPE: + if(n->sym != S) + nprec = 7; + break; + } + + if(prec > nprec) + fmtprint(f, "("); + + switch(n->op) { + default: + bad: + fmtprint(f, "(node %O)", n->op); + break; + + case OPAREN: + fmtprint(f, "(%#N)", n->left); + break; + + case OREGISTER: + fmtprint(f, "%R", n->val.u.reg); + break; + + case OLITERAL: + if(n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } + switch(n->val.ctype) { + default: + goto bad; + case CTINT: + fmtprint(f, "%B", n->val.u.xval); + break; + case CTBOOL: + if(n->val.u.bval) + fmtprint(f, "true"); + else + fmtprint(f, "false"); + break; + case CTCPLX: + fmtprint(f, "%.17g+%.17gi", + mpgetflt(&n->val.u.cval->real), + mpgetflt(&n->val.u.cval->imag)); + break; + case CTFLT: + fmtprint(f, "%.17g", mpgetflt(n->val.u.fval)); + break; + case CTSTR: + fmtprint(f, "\"%Z\"", n->val.u.sval); + break; + case CTNIL: + fmtprint(f, "nil"); + break; + } + break; + + case ONAME: + case OPACK: + case ONONAME: + fmtprint(f, "%S", n->sym); + break; + + case OTYPE: + if(n->type == T && n->sym != S) { + fmtprint(f, "%S", n->sym); + break; + } + fmtprint(f, "%T", n->type); + break; + + case OTARRAY: + fmtprint(f, "[]"); + exprfmt(f, n->left, PFIXME); + break; + + case OTPAREN: + fmtprint(f, "("); + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + break; + + case OTMAP: + fmtprint(f, "map["); + exprfmt(f, n->left, 0); + fmtprint(f, "] "); + exprfmt(f, n->right, 0); + break; + + case OTCHAN: + if(n->etype == Crecv) + fmtprint(f, "<-"); + fmtprint(f, "chan"); + if(n->etype == Csend) { + fmtprint(f, "<- "); + exprfmt(f, n->left, 0); + } else { + fmtprint(f, " "); + if(n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) { + fmtprint(f, "("); + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + } else + exprfmt(f, n->left, 0); + } + break; + + case OTSTRUCT: + fmtprint(f, "<struct>"); + break; + + case OTINTER: + fmtprint(f, "<inter>"); + break; + + case OTFUNC: + fmtprint(f, "<func>"); + break; + + case OAS: + exprfmt(f, n->left, 0); + fmtprint(f, " = "); + exprfmt(f, n->right, 0); + break; + + case OASOP: + exprfmt(f, n->left, 0); + fmtprint(f, " %#O= ", n->etype); + exprfmt(f, n->right, 0); + break; + + case OAS2: + case OAS2DOTTYPE: + case OAS2FUNC: + case OAS2MAPR: + case OAS2MAPW: + case OAS2RECV: + exprlistfmt(f, n->list); + fmtprint(f, " = "); + exprlistfmt(f, n->rlist); + break; + + case OADD: + case OANDAND: + case OANDNOT: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLT: + case OLSH: + case OMOD: + case OMUL: + case ONE: + case OOR: + case OOROR: + case ORSH: + case OSEND: + case OSUB: + case OXOR: + exprfmt(f, n->left, nprec); + fmtprint(f, " %#O ", n->op); + exprfmt(f, n->right, nprec+1); + break; + + case OADDR: + case OCOM: + case OIND: + case OMINUS: + case ONOT: + case OPLUS: + case ORECV: + fmtprint(f, "%#O", n->op); + if((n->op == OMINUS || n->op == OPLUS) && n->left->op == n->op) + fmtprint(f, " "); + exprfmt(f, n->left, 0); + break; + + case OCLOSURE: + fmtprint(f, "func literal"); + break; + + case OCOMPLIT: + fmtprint(f, "composite literal"); + break; + + case OARRAYLIT: + if(isslice(n->type)) + fmtprint(f, "slice literal"); + else + fmtprint(f, "array literal"); + break; + + case OMAPLIT: + fmtprint(f, "map literal"); + break; + + case OSTRUCTLIT: + fmtprint(f, "struct literal"); + break; + + case OXDOT: + case ODOT: + case ODOTPTR: + case ODOTINTER: + case ODOTMETH: + exprfmt(f, n->left, 7); + if(n->right == N || n->right->sym == S) + fmtprint(f, ".<nil>"); + else { + // skip leading type· in method name + p = utfrrune(n->right->sym->name, 0xb7); + if(p) + p+=2; + else + p = n->right->sym->name; + fmtprint(f, ".%s", p); + } + break; + + case ODOTTYPE: + case ODOTTYPE2: + exprfmt(f, n->left, 7); + fmtprint(f, ".("); + if(n->right != N) + exprfmt(f, n->right, 0); + else + fmtprint(f, "%T", n->type); + fmtprint(f, ")"); + break; + + case OINDEX: + case OINDEXMAP: + exprfmt(f, n->left, 7); + fmtprint(f, "["); + exprfmt(f, n->right, 0); + fmtprint(f, "]"); + break; + + case OSLICE: + case OSLICESTR: + case OSLICEARR: + exprfmt(f, n->left, 7); + fmtprint(f, "["); + if(n->right->left != N) + exprfmt(f, n->right->left, 0); + fmtprint(f, ":"); + if(n->right->right != N) + exprfmt(f, n->right->right, 0); + fmtprint(f, "]"); + break; + + case OCALL: + case OCALLFUNC: + case OCALLINTER: + case OCALLMETH: + exprfmt(f, n->left, 7); + fmtprint(f, "("); + exprlistfmt(f, n->list); + if(n->isddd) + fmtprint(f, "..."); + fmtprint(f, ")"); + break; + + case OCOMPLEX: + fmtprint(f, "complex("); + exprfmt(f, n->left, 0); + fmtprint(f, ", "); + exprfmt(f, n->right, 0); + fmtprint(f, ")"); + break; + + case OREAL: + fmtprint(f, "real("); + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + break; + + case OIMAG: + fmtprint(f, "imag("); + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + break; + + case OCONV: + case OCONVIFACE: + case OCONVNOP: + case OARRAYBYTESTR: + case OSTRARRAYBYTE: + case ORUNESTR: + if(n->type == T || n->type->sym == S) + fmtprint(f, "(%T)(", n->type); + else + fmtprint(f, "%T(", n->type); + if(n->left == N) + exprlistfmt(f, n->list); + else + exprfmt(f, n->left, 0); + fmtprint(f, ")"); + break; + + case OAPPEND: + case OCAP: + case OCLOSE: + case OLEN: + case OCOPY: + case OMAKE: + case ONEW: + case OPANIC: + case OPRINT: + case OPRINTN: + fmtprint(f, "%#O(", n->op); + if(n->left) + exprfmt(f, n->left, 0); + else + exprlistfmt(f, n->list); + fmtprint(f, ")"); + break; + + case OMAKESLICE: + fmtprint(f, "make(%#T, ", n->type); + exprfmt(f, n->left, 0); + if(count(n->list) > 2) { + fmtprint(f, ", "); + exprfmt(f, n->right, 0); + } + fmtprint(f, ")"); + break; + + case OMAKEMAP: + case OMAKECHAN: + fmtprint(f, "make(%#T)", n->type); + break; + + // Some statements + + case ODCL: + fmtprint(f, "var %S %#T", n->left->sym, n->left->type); + break; + + case ORETURN: + fmtprint(f, "return "); + exprlistfmt(f, n->list); + break; + + case OPROC: + fmtprint(f, "go %#N", n->left); + break; + + case ODEFER: + fmtprint(f, "defer %#N", n->left); + break; + } + + if(prec > nprec) + fmtprint(f, ")"); +} diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c new file mode 100644 index 000000000..5ce693ae3 --- /dev/null +++ b/src/cmd/gc/range.c @@ -0,0 +1,256 @@ +// 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. + +/* + * range + */ + +#include "go.h" + +void +typecheckrange(Node *n) +{ + char *why; + Type *t, *t1, *t2; + Node *v1, *v2; + NodeList *ll; + + // delicate little dance. see typecheckas2 + for(ll=n->list; ll; ll=ll->next) + if(ll->n->defn != n) + typecheck(&ll->n, Erv | Easgn); + + typecheck(&n->right, Erv); + if((t = n->right->type) == T) + goto out; + if(isptr[t->etype] && isfixedarray(t->type)) + t = t->type; + n->type = t; + + switch(t->etype) { + default: + yyerror("cannot range over %+N", n->right); + goto out; + + case TARRAY: + t1 = types[TINT]; + t2 = t->type; + break; + + case TMAP: + t1 = t->down; + t2 = t->type; + break; + + case TCHAN: + t1 = t->type; + t2 = nil; + if(count(n->list) == 2) + goto toomany; + break; + + case TSTRING: + t1 = types[TINT]; + t2 = types[TINT]; + break; + } + + if(count(n->list) > 2) { + toomany: + yyerror("too many variables in range"); + } + + v1 = n->list->n; + v2 = N; + if(n->list->next) + v2 = n->list->next->n; + + if(v1->defn == n) + v1->type = t1; + else if(v1->type != T && assignop(t1, v1->type, &why) == 0) + yyerror("cannot assign type %T to %+N in range%s", t1, v1, why); + if(v2) { + if(v2->defn == n) + v2->type = t2; + else if(v2->type != T && assignop(t2, v2->type, &why) == 0) + yyerror("cannot assign type %T to %+N in range%s", t2, v2, why); + } + +out: + typechecklist(n->nbody, Etop); + + // second half of dance + n->typecheck = 1; + for(ll=n->list; ll; ll=ll->next) + if(ll->n->typecheck == 0) + typecheck(&ll->n, Erv | Easgn); +} + +void +walkrange(Node *n) +{ + Node *ohv1, *hv1, *hv2; // hidden (old) val 1, 2 + Node *ha, *hit; // hidden aggregate, iterator + Node *hn, *hp; // hidden len, pointer + Node *hb; // hidden bool + Node *a, *v1, *v2; // not hidden aggregate, val 1, 2 + Node *fn, *tmp; + NodeList *body, *init; + Type *th, *t; + int lno; + + t = n->type; + init = nil; + + a = n->right; + lno = setlineno(a); + if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) { + a = nod(OCONV, n->right, N); + a->type = types[TSTRING]; + } + + v1 = n->list->n; + hv1 = N; + + v2 = N; + if(n->list->next) + v2 = n->list->next->n; + hv2 = N; + + if(v2 == N && t->etype == TARRAY) { + // will have just one reference to argument. + // no need to make a potentially expensive copy. + ha = a; + } else { + ha = nod(OXXX, N, N); + tempname(ha, a->type); + init = list(init, nod(OAS, ha, a)); + } + + switch(t->etype) { + default: + fatal("walkrange"); + + case TARRAY: + hv1 = nod(OXXX, N, n); + tempname(hv1, types[TINT]); + hn = nod(OXXX, N, N); + tempname(hn, types[TINT]); + hp = nil; + + init = list(init, nod(OAS, hv1, N)); + init = list(init, nod(OAS, hn, nod(OLEN, ha, N))); + if(v2) { + hp = nod(OXXX, N, N); + tempname(hp, ptrto(n->type->type)); + tmp = nod(OINDEX, ha, nodintconst(0)); + tmp->etype = 1; // no bounds check + init = list(init, nod(OAS, hp, nod(OADDR, tmp, N))); + } + + n->ntest = nod(OLT, hv1, hn); + n->nincr = nod(OASOP, hv1, nodintconst(1)); + n->nincr->etype = OADD; + body = list1(nod(OAS, v1, hv1)); + if(v2) { + body = list(body, nod(OAS, v2, nod(OIND, hp, N))); + tmp = nod(OADD, hp, nodintconst(t->type->width)); + tmp->type = hp->type; + tmp->typecheck = 1; + tmp->right->type = types[tptr]; + tmp->right->typecheck = 1; + body = list(body, nod(OAS, hp, tmp)); + } + break; + + case TMAP: + th = typ(TARRAY); + th->type = ptrto(types[TUINT8]); + th->bound = (sizeof(struct Hiter) + widthptr - 1) / widthptr; + hit = nod(OXXX, N, N); + tempname(hit, th); + + fn = syslook("mapiterinit", 1); + argtype(fn, t->down); + argtype(fn, t->type); + argtype(fn, th); + init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N))); + n->ntest = nod(ONE, nod(OINDEX, hit, nodintconst(0)), nodnil()); + + fn = syslook("mapiternext", 1); + argtype(fn, th); + n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N)); + + if(v2 == N) { + fn = syslook("mapiter1", 1); + argtype(fn, th); + argtype(fn, t->down); + a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N))); + } else { + fn = syslook("mapiter2", 1); + argtype(fn, th); + argtype(fn, t->down); + argtype(fn, t->type); + a = nod(OAS2, N, N); + a->list = list(list1(v1), v2); + a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N))); + } + body = list1(a); + break; + + case TCHAN: + hv1 = nod(OXXX, N, n); + tempname(hv1, t->type); + hb = nod(OXXX, N, N); + tempname(hb, types[TBOOL]); + + n->ntest = nod(ONE, hb, nodbool(0)); + a = nod(OAS2RECV, N, N); + a->typecheck = 1; + a->list = list(list1(hv1), hb); + a->rlist = list1(nod(ORECV, ha, N)); + n->ntest->ninit = list1(a); + body = list1(nod(OAS, v1, hv1)); + break; + + case TSTRING: + ohv1 = nod(OXXX, N, N); + tempname(ohv1, types[TINT]); + + hv1 = nod(OXXX, N, N); + tempname(hv1, types[TINT]); + init = list(init, nod(OAS, hv1, N)); + + if(v2 == N) + a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1)); + else { + hv2 = nod(OXXX, N, N); + tempname(hv2, types[TINT]); + a = nod(OAS2, N, N); + a->list = list(list1(hv1), hv2); + fn = syslook("stringiter2", 0); + a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1)); + } + n->ntest = nod(ONE, hv1, nodintconst(0)); + n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a); + + body = list1(nod(OAS, v1, ohv1)); + if(v2 != N) + body = list(body, nod(OAS, v2, hv2)); + break; + } + + n->op = OFOR; + typechecklist(init, Etop); + n->ninit = concat(n->ninit, init); + typechecklist(n->ntest->ninit, Etop); + typecheck(&n->ntest, Erv); + typecheck(&n->nincr, Etop); + typechecklist(body, Etop); + n->nbody = concat(body, n->nbody); + walkstmt(&n); + + lineno = lno; +} + diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c new file mode 100644 index 000000000..810787d30 --- /dev/null +++ b/src/cmd/gc/reflect.c @@ -0,0 +1,939 @@ +// 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" + +/* + * runtime interface and reflection data structures + */ + +static NodeList* signatlist; +static Sym* dtypesym(Type*); +static Sym* weaktypesym(Type*); + +static int +sigcmp(Sig *a, Sig *b) +{ + int i; + + i = strcmp(a->name, b->name); + if(i != 0) + return i; + if(a->pkg == b->pkg) + return 0; + if(a->pkg == nil) + return -1; + if(b->pkg == nil) + return +1; + return strcmp(a->pkg->path->s, b->pkg->path->s); +} + +static Sig* +lsort(Sig *l, int(*f)(Sig*, Sig*)) +{ + Sig *l1, *l2, *le; + + if(l == 0 || l->link == 0) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->link; + if(l2 == 0) + break; + l2 = l2->link; + if(l2 == 0) + break; + l1 = l1->link; + } + + l2 = l1->link; + l1->link = 0; + l1 = lsort(l, f); + l2 = lsort(l2, f); + + /* set up lead element */ + if((*f)(l1, l2) < 0) { + l = l1; + l1 = l1->link; + } else { + l = l2; + l2 = l2->link; + } + le = l; + + for(;;) { + if(l1 == 0) { + while(l2) { + le->link = l2; + le = l2; + l2 = l2->link; + } + le->link = 0; + break; + } + if(l2 == 0) { + while(l1) { + le->link = l1; + le = l1; + l1 = l1->link; + } + break; + } + if((*f)(l1, l2) < 0) { + le->link = l1; + le = l1; + l1 = l1->link; + } else { + le->link = l2; + le = l2; + l2 = l2->link; + } + } + le->link = 0; + return l; +} + +/* + * f is method type, with receiver. + * return function type, receiver as first argument (or not). + */ +Type* +methodfunc(Type *f, Type *receiver) +{ + NodeList *in, *out; + Node *d; + Type *t; + + in = nil; + if(receiver) { + d = nod(ODCLFIELD, N, N); + d->type = receiver; + in = list(in, d); + } + for(t=getinargx(f)->type; t; t=t->down) { + d = nod(ODCLFIELD, N, N); + d->type = t->type; + d->isddd = t->isddd; + in = list(in, d); + } + + out = nil; + for(t=getoutargx(f)->type; t; t=t->down) { + d = nod(ODCLFIELD, N, N); + d->type = t->type; + out = list(out, d); + } + + return functype(N, in, out); +} + +/* + * return methods of non-interface type t, sorted by name. + * generates stub functions as needed. + */ +static Sig* +methods(Type *t) +{ + Type *f, *mt, *it, *this; + Sig *a, *b; + Sym *method; + Prog *oldlist; + + // named method type + mt = methtype(t); + if(mt == T) + return nil; + expandmeth(mt->sym, mt); + + // type stored in interface word + it = t; + if(it->width > widthptr) + it = ptrto(t); + + // make list of methods for t, + // generating code if necessary. + a = nil; + oldlist = nil; + for(f=mt->xmethod; f; f=f->down) { + if(f->type->etype != TFUNC) + continue; + if(f->etype != TFIELD) + fatal("methods: not field"); + method = f->sym; + if(method == nil) + continue; + + // get receiver type for this particular method. + // if pointer receiver but non-pointer t and + // this is not an embedded pointer inside a struct, + // method does not apply. + this = getthisx(f->type)->type->type; + if(isptr[this->etype] && this->type == t) + continue; + if(isptr[this->etype] && !isptr[t->etype] + && f->embedded != 2 && !isifacemethod(f->type)) + continue; + + b = mal(sizeof(*b)); + b->link = a; + a = b; + + a->name = method->name; + if(!exportname(method->name)) { + if(method->pkg == nil) + fatal("methods: missing package"); + a->pkg = method->pkg; + } + a->isym = methodsym(method, it, 1); + a->tsym = methodsym(method, t, 0); + a->type = methodfunc(f->type, t); + a->mtype = methodfunc(f->type, nil); + + if(!(a->isym->flags & SymSiggen)) { + a->isym->flags |= SymSiggen; + if(!eqtype(this, it) || this->width < types[tptr]->width) { + if(oldlist == nil) + oldlist = pc; + // Is okay to call genwrapper here always, + // but we can generate more efficient code + // using genembedtramp if all that is necessary + // is a pointer adjustment and a JMP. + if(isptr[it->etype] && isptr[this->etype] + && f->embedded && !isifacemethod(f->type)) + genembedtramp(it, f, a->isym, 1); + else + genwrapper(it, f, a->isym, 1); + } + } + + if(!(a->tsym->flags & SymSiggen)) { + a->tsym->flags |= SymSiggen; + if(!eqtype(this, t)) { + if(oldlist == nil) + oldlist = pc; + if(isptr[t->etype] && isptr[this->etype] + && f->embedded && !isifacemethod(f->type)) + genembedtramp(t, f, a->tsym, 0); + else + genwrapper(t, f, a->tsym, 0); + } + } + } + + // restore data output + if(oldlist) { + // old list ended with AEND; change to ANOP + // so that the trampolines that follow can be found. + nopout(oldlist); + + // start new data list + newplist(); + } + + return lsort(a, sigcmp); +} + +/* + * return methods of interface type t, sorted by name. + */ +static Sig* +imethods(Type *t) +{ + Sig *a, *all, *last; + Type *f; + Sym *method, *isym; + Prog *oldlist; + + all = nil; + last = nil; + oldlist = nil; + for(f=t->type; f; f=f->down) { + if(f->etype != TFIELD) + fatal("imethods: not field"); + if(f->type->etype != TFUNC || f->sym == nil) + continue; + method = f->sym; + a = mal(sizeof(*a)); + a->name = method->name; + if(!exportname(method->name)) { + if(method->pkg == nil) + fatal("imethods: missing package"); + a->pkg = method->pkg; + } + a->mtype = f->type; + a->offset = 0; + a->type = methodfunc(f->type, nil); + + if(last && sigcmp(last, a) >= 0) + fatal("sigcmp vs sortinter %s %s", last->name, a->name); + if(last == nil) + all = a; + else + last->link = a; + last = a; + + // Compiler can only refer to wrappers for + // named interface types. + if(t->sym == S) + continue; + + // NOTE(rsc): Perhaps an oversight that + // IfaceType.Method is not in the reflect data. + // Generate the method body, so that compiled + // code can refer to it. + isym = methodsym(method, t, 0); + if(!(isym->flags & SymSiggen)) { + isym->flags |= SymSiggen; + if(oldlist == nil) + oldlist = pc; + genwrapper(t, f, isym, 0); + } + } + + if(oldlist) { + // old list ended with AEND; change to ANOP + // so that the trampolines that follow can be found. + nopout(oldlist); + + // start new data list + newplist(); + } + + return all; +} + +static void +dimportpath(Pkg *p) +{ + static Pkg *gopkg; + char *nam; + Node *n; + + if(p->pathsym != S) + return; + + if(gopkg == nil) { + gopkg = mkpkg(strlit("go")); + gopkg->name = "go"; + } + nam = smprint("importpath.%s.", p->prefix); + + n = nod(ONAME, N, N); + n->sym = pkglookup(nam, gopkg); + free(nam); + n->class = PEXTERN; + n->xoffset = 0; + p->pathsym = n->sym; + + gdatastring(n, p->path); + ggloblsym(n->sym, types[TSTRING]->width, 1); +} + +static int +dgopkgpath(Sym *s, int ot, Pkg *pkg) +{ + if(pkg == nil) + return dgostringptr(s, ot, nil); + + // Emit reference to go.importpath.""., which 6l will + // rewrite using the correct import path. Every package + // that imports this one directly defines the symbol. + if(pkg == localpkg) { + static Sym *ns; + + if(ns == nil) + ns = pkglookup("importpath.\"\".", mkpkg(strlit("go"))); + return dsymptr(s, ot, ns, 0); + } + + dimportpath(pkg); + return dsymptr(s, ot, pkg->pathsym, 0); +} + +/* + * uncommonType + * ../../pkg/runtime/type.go:/uncommonType + */ +static int +dextratype(Sym *sym, int off, Type *t, int ptroff) +{ + int ot, n; + Sym *s; + Sig *a, *m; + + m = methods(t); + if(t->sym == nil && m == nil) + return off; + + // fill in *extraType pointer in header + dsymptr(sym, ptroff, sym, off); + + n = 0; + for(a=m; a; a=a->link) { + dtypesym(a->type); + n++; + } + + ot = off; + s = sym; + if(t->sym) { + ot = dgostringptr(s, ot, t->sym->name); + if(t != types[t->etype]) + ot = dgopkgpath(s, ot, t->sym->pkg); + else + ot = dgostringptr(s, ot, nil); + } else { + ot = dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); + } + + // slice header + ot = dsymptr(s, ot, s, ot + widthptr + 2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + + // methods + for(a=m; a; a=a->link) { + // method + // ../../pkg/runtime/type.go:/method + ot = dgostringptr(s, ot, a->name); + ot = dgopkgpath(s, ot, a->pkg); + ot = dsymptr(s, ot, dtypesym(a->mtype), 0); + ot = dsymptr(s, ot, dtypesym(a->type), 0); + if(a->isym) + ot = dsymptr(s, ot, a->isym, 0); + else + ot = duintptr(s, ot, 0); + if(a->tsym) + ot = dsymptr(s, ot, a->tsym, 0); + else + ot = duintptr(s, ot, 0); + } + + return ot; +} + +enum { + KindBool = 1, + KindInt, + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindUint, + KindUint8, + KindUint16, + KindUint32, + KindUint64, + KindUintptr, + KindFloat32, + KindFloat64, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +static int +kinds[] = +{ + [TINT] = KindInt, + [TUINT] = KindUint, + [TINT8] = KindInt8, + [TUINT8] = KindUint8, + [TINT16] = KindInt16, + [TUINT16] = KindUint16, + [TINT32] = KindInt32, + [TUINT32] = KindUint32, + [TINT64] = KindInt64, + [TUINT64] = KindUint64, + [TUINTPTR] = KindUintptr, + [TFLOAT32] = KindFloat32, + [TFLOAT64] = KindFloat64, + [TBOOL] = KindBool, + [TSTRING] = KindString, + [TPTR32] = KindPtr, + [TPTR64] = KindPtr, + [TSTRUCT] = KindStruct, + [TINTER] = KindInterface, + [TCHAN] = KindChan, + [TMAP] = KindMap, + [TARRAY] = KindArray, + [TFUNC] = KindFunc, + [TCOMPLEX64] = KindComplex64, + [TCOMPLEX128] = KindComplex128, + [TUNSAFEPTR] = KindUnsafePointer, +}; + +static char* +structnames[] = +{ + [TINT] = "*runtime.IntType", + [TUINT] = "*runtime.UintType", + [TINT8] = "*runtime.IntType", + [TUINT8] = "*runtime.UintType", + [TINT16] = "*runtime.IntType", + [TUINT16] = "*runtime.UintType", + [TINT32] = "*runtime.IntType", + [TUINT32] = "*runtime.UintType", + [TINT64] = "*runtime.IntType", + [TUINT64] = "*runtime.UintType", + [TUINTPTR] = "*runtime.UintType", + [TCOMPLEX64] = "*runtime.ComplexType", + [TCOMPLEX128] = "*runtime.ComplexType", + [TFLOAT32] = "*runtime.FloatType", + [TFLOAT64] = "*runtime.FloatType", + [TBOOL] = "*runtime.BoolType", + [TSTRING] = "*runtime.StringType", + [TUNSAFEPTR] = "*runtime.UnsafePointerType", + + [TPTR32] = "*runtime.PtrType", + [TPTR64] = "*runtime.PtrType", + [TSTRUCT] = "*runtime.StructType", + [TINTER] = "*runtime.InterfaceType", + [TCHAN] = "*runtime.ChanType", + [TMAP] = "*runtime.MapType", + [TARRAY] = "*runtime.ArrayType", + [TFUNC] = "*runtime.FuncType", +}; + +static Sym* +typestruct(Type *t) +{ + char *name; + int et; + + et = t->etype; + if(et < 0 || et >= nelem(structnames) || (name = structnames[et]) == nil) { + fatal("typestruct %lT", t); + return nil; // silence gcc + } + + if(isslice(t)) + name = "*runtime.SliceType"; + + return pkglookup(name, typepkg); +} + +static int +haspointers(Type *t) +{ + Type *t1; + + switch(t->etype) { + case TINT: + case TUINT: + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TUINTPTR: + case TFLOAT32: + case TFLOAT64: + case TBOOL: + return 0; + case TARRAY: + if(t->bound < 0) // slice + return 1; + return haspointers(t->type); + case TSTRUCT: + for(t1=t->type; t1!=T; t1=t1->down) + if(haspointers(t1->type)) + return 1; + return 0; + case TSTRING: + case TPTR32: + case TPTR64: + case TUNSAFEPTR: + case TINTER: + case TCHAN: + case TMAP: + case TFUNC: + default: + return 1; + } +} + +/* + * commonType + * ../../pkg/runtime/type.go:/commonType + */ +static int +dcommontype(Sym *s, int ot, Type *t) +{ + int i; + Sym *sptr; + char *p; + + dowidth(t); + + sptr = nil; + if(t->sym != nil && !isptr[t->etype]) + sptr = dtypesym(ptrto(t)); + else + sptr = weaktypesym(ptrto(t)); + + // empty interface pointing at this type. + // all the references that we emit are *interface{}; + // they point here. + ot = rnd(ot, widthptr); + ot = dsymptr(s, ot, typestruct(t), 0); + ot = dsymptr(s, ot, s, 2*widthptr); + + // ../../pkg/runtime/type.go:/commonType + // actual type structure + // type commonType struct { + // size uintptr; + // hash uint32; + // alg uint8; + // align uint8; + // fieldAlign uint8; + // kind uint8; + // string *string; + // *extraType; + // ptrToThis *Type + // } + ot = duintptr(s, ot, t->width); + ot = duint32(s, ot, typehash(t)); + ot = duint8(s, ot, algtype(t)); + ot = duint8(s, ot, t->align); // align + ot = duint8(s, ot, t->align); // fieldAlign + i = kinds[t->etype]; + if(t->etype == TARRAY && t->bound < 0) + i = KindSlice; + if(!haspointers(t)) + i |= KindNoPointers; + ot = duint8(s, ot, i); // kind + longsymnames = 1; + p = smprint("%-T", t); + longsymnames = 0; + ot = dgostringptr(s, ot, p); // string + free(p); + + // skip pointer to extraType, + // which follows the rest of this type structure. + // caller will fill in if needed. + // otherwise linker will assume 0. + ot += widthptr; + + ot = dsymptr(s, ot, sptr, 0); // ptrto type + return ot; +} + +Sym* +typesym(Type *t) +{ + char *p; + Sym *s; + + p = smprint("%#-T", t); + s = pkglookup(p, typepkg); + free(p); + return s; +} + +Node* +typename(Type *t) +{ + Sym *s; + Node *n; + + if(t == T || (isptr[t->etype] && t->type == T) || isideal(t)) + fatal("typename %T", t); + s = typesym(t); + if(s->def == N) { + n = nod(ONAME, N, N); + n->sym = s; + n->type = types[TUINT8]; + n->addable = 1; + n->ullman = 1; + n->class = PEXTERN; + n->xoffset = 0; + s->def = n; + + signatlist = list(signatlist, typenod(t)); + } + + n = nod(OADDR, s->def, N); + n->type = ptrto(s->def->type); + n->addable = 1; + n->ullman = 2; + return n; +} + +static Sym* +weaktypesym(Type *t) +{ + char *p; + Sym *s; + static Pkg *weak; + + if(weak == nil) { + weak = mkpkg(strlit("weak.type")); + weak->name = "weak.type"; + weak->prefix = "weak.type"; // not weak%2etype + } + + p = smprint("%#-T", t); + s = pkglookup(p, weak); + free(p); + return s; +} + +static Sym* +dtypesym(Type *t) +{ + int ot, xt, n, isddd, dupok; + Sym *s, *s1, *s2; + Sig *a, *m; + Type *t1, *tbase, *t2; + + if(isideal(t)) + fatal("dtypesym %T", t); + + s = typesym(t); + if(s->flags & SymSiggen) + return s; + s->flags |= SymSiggen; + + // special case (look for runtime below): + // when compiling package runtime, + // emit the type structures for int, float, etc. + tbase = t; + if(isptr[t->etype] && t->sym == S && t->type->sym != S) + tbase = t->type; + dupok = tbase->sym == S; + + if(compiling_runtime && tbase == types[tbase->etype]) // int, float, etc + goto ok; + + // named types from other files are defined only by those files + if(tbase->sym && !tbase->local) + return s; + if(isforw[tbase->etype]) + return s; + +ok: + ot = 0; + xt = 0; + switch(t->etype) { + default: + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + break; + + case TARRAY: + if(t->bound >= 0) { + // ../../pkg/runtime/type.go:/ArrayType + s1 = dtypesym(t->type); + t2 = typ(TARRAY); + t2->type = t->type; + t2->bound = -1; // slice + s2 = dtypesym(t2); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); + ot = duintptr(s, ot, t->bound); + } else { + // ../../pkg/runtime/type.go:/SliceType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + } + break; + + case TCHAN: + // ../../pkg/runtime/type.go:/ChanType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = duintptr(s, ot, t->chan); + break; + + case TFUNC: + for(t1=getthisx(t)->type; t1; t1=t1->down) + dtypesym(t1->type); + isddd = 0; + for(t1=getinargx(t)->type; t1; t1=t1->down) { + isddd = t1->isddd; + dtypesym(t1->type); + } + for(t1=getoutargx(t)->type; t1; t1=t1->down) + dtypesym(t1->type); + + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = duint8(s, ot, isddd); + + // two slice headers: in and out. + ot = rnd(ot, widthptr); + ot = dsymptr(s, ot, s, ot+2*(widthptr+2*4)); + n = t->thistuple + t->intuple; + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + ot = dsymptr(s, ot, s, ot+1*(widthptr+2*4)+n*widthptr); + ot = duint32(s, ot, t->outtuple); + ot = duint32(s, ot, t->outtuple); + + // slice data + for(t1=getthisx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + for(t1=getinargx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + for(t1=getoutargx(t)->type; t1; t1=t1->down, n++) + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + break; + + case TINTER: + m = imethods(t); + n = 0; + for(a=m; a; a=a->link) { + dtypesym(a->type); + n++; + } + + // ../../pkg/runtime/type.go:/InterfaceType + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s, ot+widthptr+2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + for(a=m; a; a=a->link) { + // ../../pkg/runtime/type.go:/imethod + ot = dgostringptr(s, ot, a->name); + ot = dgopkgpath(s, ot, a->pkg); + ot = dsymptr(s, ot, dtypesym(a->type), 0); + } + break; + + case TMAP: + // ../../pkg/runtime/type.go:/MapType + s1 = dtypesym(t->down); + s2 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + ot = dsymptr(s, ot, s2, 0); + break; + + case TPTR32: + case TPTR64: + if(t->type->etype == TANY) { + // ../../pkg/runtime/type.go:/UnsafePointerType + ot = dcommontype(s, ot, t); + break; + } + // ../../pkg/runtime/type.go:/PtrType + s1 = dtypesym(t->type); + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s1, 0); + break; + + case TSTRUCT: + // ../../pkg/runtime/type.go:/StructType + // for security, only the exported fields. + n = 0; + for(t1=t->type; t1!=T; t1=t1->down) { + dtypesym(t1->type); + n++; + } + ot = dcommontype(s, ot, t); + xt = ot - 2*widthptr; + ot = dsymptr(s, ot, s, ot+widthptr+2*4); + ot = duint32(s, ot, n); + ot = duint32(s, ot, n); + for(t1=t->type; t1!=T; t1=t1->down) { + // ../../pkg/runtime/type.go:/structField + if(t1->sym && !t1->embedded) { + ot = dgostringptr(s, ot, t1->sym->name); + if(exportname(t1->sym->name)) + ot = dgostringptr(s, ot, nil); + else + ot = dgopkgpath(s, ot, t1->sym->pkg); + } else { + ot = dgostringptr(s, ot, nil); + ot = dgostringptr(s, ot, nil); + } + ot = dsymptr(s, ot, dtypesym(t1->type), 0); + ot = dgostrlitptr(s, ot, t1->note); + ot = duintptr(s, ot, t1->width); // field offset + } + break; + } + ot = dextratype(s, ot, t, xt); + ggloblsym(s, ot, dupok); + return s; +} + +void +dumptypestructs(void) +{ + int i; + NodeList *l; + Node *n; + Type *t; + Pkg *p; + + // copy types from externdcl list to signatlist + for(l=externdcl; l; l=l->next) { + n = l->n; + if(n->op != OTYPE) + continue; + signatlist = list(signatlist, n); + } + + // process signatlist + for(l=signatlist; l; l=l->next) { + n = l->n; + if(n->op != OTYPE) + continue; + t = n->type; + dtypesym(t); + if(t->sym) + dtypesym(ptrto(t)); + } + + // generate import strings for imported packages + for(i=0; i<nelem(phash); i++) + for(p=phash[i]; p; p=p->link) + if(p->direct) + dimportpath(p); + + // do basic types if compiling package runtime. + // they have to be in at least one package, + // and runtime is always loaded implicitly, + // so this is as good as any. + // another possible choice would be package main, + // but using runtime means fewer copies in .6 files. + if(compiling_runtime) { + for(i=1; i<=TBOOL; i++) + dtypesym(ptrto(types[i])); + dtypesym(ptrto(types[TSTRING])); + dtypesym(ptrto(types[TUNSAFEPTR])); + + // add paths for runtime and main, which 6l imports implicitly. + dimportpath(runtimepkg); + dimportpath(mkpkg(strlit("main"))); + } +} diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go new file mode 100644 index 000000000..549f7abe3 --- /dev/null +++ b/src/cmd/gc/runtime.go @@ -0,0 +1,130 @@ +// 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. + +// NOTE: If you change this file you must run "./mkbuiltin" +// to update builtin.c.boot. This is not done automatically +// to avoid depending on having a working compiler binary. + +package PACKAGE + +// emitted by compiler, not referred to by go programs + +func new(int32) *any +func panicindex() +func panicslice() +func throwreturn() +func throwinit() +func panicwrap(string, string, string) + +func panic(interface{}) +func recover(*int32) interface{} + +func printbool(bool) +func printfloat(float64) +func printint(int64) +func printuint(uint64) +func printcomplex(complex128) +func printstring(string) +func printpointer(any) +func printiface(any) +func printeface(any) +func printslice(any) +func printnl() +func printsp() +func goprintf() + +// filled in by compiler: int n, string, string, ... +func concatstring() + +// filled in by compiler: Type*, int n, Slice, ... +func append() +func appendslice(typ *byte, x any, y []any) any + +func cmpstring(string, string) int +func slicestring(string, int, int) string +func slicestring1(string, int) string +func intstring(int64) string +func slicebytetostring([]byte) string +func sliceinttostring([]int) string +func stringtoslicebyte(string) []byte +func stringtosliceint(string) []int +func stringiter(string, int) int +func stringiter2(string, int) (retk int, retv int) +func slicecopy(to any, fr any, wid uint32) int +func slicestringcopy(to any, fr any) int + +// interface conversions +func convI2E(elem any) (ret any) +func convI2I(typ *byte, elem any) (ret any) +func convT2E(typ *byte, elem any) (ret any) +func convT2I(typ *byte, typ2 *byte, elem any) (ret any) + +// interface type assertions x.(T) +func assertE2E(typ *byte, iface any) (ret any) +func assertE2E2(typ *byte, iface any) (ret any, ok bool) +func assertE2I(typ *byte, iface any) (ret any) +func assertE2I2(typ *byte, iface any) (ret any, ok bool) +func assertE2T(typ *byte, iface any) (ret any) +func assertE2T2(typ *byte, iface any) (ret any, ok bool) +func assertI2E(typ *byte, iface any) (ret any) +func assertI2E2(typ *byte, iface any) (ret any, ok bool) +func assertI2I(typ *byte, iface any) (ret any) +func assertI2I2(typ *byte, iface any) (ret any, ok bool) +func assertI2T(typ *byte, iface any) (ret any) +func assertI2T2(typ *byte, iface any) (ret any, ok bool) + +func ifaceeq(i1 any, i2 any) (ret bool) +func efaceeq(i1 any, i2 any) (ret bool) +func ifacethash(i1 any) (ret uint32) +func efacethash(i1 any) (ret uint32) + +// *byte is really *runtime.Type +func makemap(mapType *byte, hint int64) (hmap map[any]any) +func mapaccess1(mapType *byte, hmap map[any]any, key any) (val any) +func mapaccess2(mapType *byte, hmap map[any]any, key any) (val any, pres bool) +func mapassign1(mapType *byte, hmap map[any]any, key any, val any) +func mapassign2(mapType *byte, hmap map[any]any, key any, val any, pres bool) +func mapiterinit(mapType *byte, hmap map[any]any, hiter *any) +func mapiternext(hiter *any) +func mapiter1(hiter *any) (key any) +func mapiter2(hiter *any) (key any, val any) + +// *byte is really *runtime.Type +func makechan(chanType *byte, hint int64) (hchan chan any) +func chanrecv1(chanType *byte, hchan <-chan any) (elem any) +func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool) +func chansend1(chanType *byte, hchan chan<- any, elem any) +func closechan(hchan any) + +func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool +func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool +func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool + +func newselect(size int) (sel *byte) +func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) +func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool) +func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool) +func selectdefault(sel *byte) (selected bool) +func selectgo(sel *byte) +func block() + +func makeslice(typ *byte, nel int64, cap int64) (ary []any) +func growslice(typ *byte, old []any, n int64) (ary []any) +func sliceslice1(old []any, lb uint64, width uint64) (ary []any) +func sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) +func slicearray(old *any, nel uint64, lb uint64, hb uint64, width uint64) (ary []any) + +func closure() // has args, but compiler fills in + +// only used on 32-bit +func int64div(int64, int64) int64 +func uint64div(uint64, uint64) uint64 +func int64mod(int64, int64) int64 +func uint64mod(uint64, uint64) uint64 +func float64toint64(float64) int64 +func float64touint64(float64) uint64 +func int64tofloat64(int64) float64 +func uint64tofloat64(uint64) float64 + +func complex128div(num complex128, den complex128) (quo complex128) diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c new file mode 100644 index 000000000..909ad3aa4 --- /dev/null +++ b/src/cmd/gc/select.c @@ -0,0 +1,351 @@ +// 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. + +/* + * select + */ + +#include "go.h" + +void +typecheckselect(Node *sel) +{ + Node *ncase, *n, *def; + NodeList *l; + int lno, count; + + def = nil; + lno = setlineno(sel); + count = 0; + typechecklist(sel->ninit, Etop); + for(l=sel->list; l; l=l->next) { + count++; + ncase = l->n; + setlineno(ncase); + if(ncase->op != OXCASE) + fatal("typecheckselect %O", ncase->op); + + if(ncase->list == nil) { + // default + if(def != N) + yyerror("multiple defaults in select (first at %L)", def->lineno); + else + def = ncase; + } else if(ncase->list->next) { + yyerror("select cases cannot be lists"); + } else { + n = typecheck(&ncase->list->n, Etop); + ncase->left = n; + ncase->list = nil; + setlineno(n); + switch(n->op) { + default: + yyerror("select case must be receive, send or assign recv"); + break; + + case OAS: + // convert x = <-c into OSELRECV(x, <-c). + // remove implicit conversions; the eventual assignment + // will reintroduce them. + if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit) + n->right = n->right->left; + + if(n->right->op != ORECV) { + yyerror("select assignment must have receive on right hand side"); + break; + } + n->op = OSELRECV; + break; + + case OAS2RECV: + // convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok + if(n->right->op != ORECV) { + yyerror("select assignment must have receive on right hand side"); + break; + } + n->op = OSELRECV2; + n->left = n->list->n; + n->ntest = n->list->next->n; + n->right = n->rlist->n; + break; + + case ORECV: + // convert <-c into OSELRECV(N, <-c) + n = nod(OSELRECV, N, n); + ncase->left = n; + break; + + case OSEND: + break; + } + } + typechecklist(ncase->nbody, Etop); + } + sel->xoffset = count; + lineno = lno; +} + +void +walkselect(Node *sel) +{ + int lno, i; + Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch; + NodeList *l, *init; + + if(sel->list == nil && sel->xoffset != 0) + fatal("double walkselect"); // already rewrote + + lno = setlineno(sel); + i = count(sel->list); + + // optimization: zero-case select + if(i == 0) { + sel->nbody = list1(mkcall("block", nil, nil)); + goto out; + } + + // optimization: one-case select: single op. + if(i == 1) { + cas = sel->list->n; + setlineno(cas); + l = cas->ninit; + if(cas->left != N) { // not default: + n = cas->left; + l = concat(l, n->ninit); + n->ninit = nil; + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + ch = cheapexpr(n->left, &l); + n->left = ch; + break; + + case OSELRECV: + r = n->right; + ch = cheapexpr(r->left, &l); + r->left = ch; + + if(n->left == N) + n = r; + else { + n = nod(OAS, n->left, r); + typecheck(&n, Etop); + } + break; + + case OSELRECV2: + r = n->right; + ch = cheapexpr(r->left, &l); + r->left = ch; + + a = nod(OAS2, N, N); + a->list = n->list; + a->rlist = n->rlist; + n = a; + typecheck(&n, Etop); + break; + } + + // if ch == nil { block() }; n; + a = nod(OIF, N, N); + a->ntest = nod(OEQ, ch, nodnil()); + a->nbody = list1(mkcall("block", nil, &l)); + typecheck(&a, Etop); + l = list(l, a); + l = list(l, n); + } + l = concat(l, cas->nbody); + sel->nbody = l; + goto out; + } + + // introduce temporary variables for OSELRECV where needed. + // this rewrite is used by both the general code and the next optimization. + for(l=sel->list; l; l=l->next) { + cas = l->n; + setlineno(cas); + n = cas->left; + if(n == N) + continue; + switch(n->op) { + case OSELRECV: + case OSELRECV2: + ch = n->right->left; + + // If we can use the address of the target without + // violating addressability or order of operations, do so. + // Otherwise introduce a temporary. + // Also introduce a temporary for := variables that escape, + // so that we can delay the heap allocation until the case + // is selected. + if(n->op == OSELRECV2) { + if(n->ntest == N || isblank(n->ntest)) + n->ntest = nodnil(); + else if(n->ntest->op == ONAME && + (!n->colas || (n->ntest->class&PHEAP) == 0) && + convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) { + n->ntest = nod(OADDR, n->ntest, N); + n->ntest->etype = 1; // pointer does not escape + typecheck(&n->ntest, Erv); + } else { + tmp = nod(OXXX, N, N); + tempname(tmp, types[TBOOL]); + a = nod(OADDR, tmp, N); + a->etype = 1; // pointer does not escape + typecheck(&a, Erv); + r = nod(OAS, n->ntest, tmp); + typecheck(&r, Etop); + cas->nbody = concat(list1(r), cas->nbody); + n->ntest = a; + } + } + + if(n->left == N || isblank(n->left)) + n->left = nodnil(); + else if(n->left->op == ONAME && + (!n->colas || (n->left->class&PHEAP) == 0) && + convertop(ch->type->type, n->left->type, nil) == OCONVNOP) { + n->left = nod(OADDR, n->left, N); + n->left->etype = 1; // pointer does not escape + typecheck(&n->left, Erv); + } else { + tmp = nod(OXXX, N, N); + tempname(tmp, ch->type->type); + a = nod(OADDR, tmp, N); + a->etype = 1; // pointer does not escape + typecheck(&a, Erv); + r = nod(OAS, n->left, tmp); + typecheck(&r, Etop); + cas->nbody = concat(list1(r), cas->nbody); + n->left = a; + } + + cas->nbody = concat(n->ninit, cas->nbody); + n->ninit = nil; + break; + } + } + + // optimization: two-case select but one is default: single non-blocking op. + if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) { + if(sel->list->n->left == nil) { + cas = sel->list->next->n; + dflt = sel->list->n; + } else { + dflt = sel->list->next->n; + cas = sel->list->n; + } + + n = cas->left; + setlineno(n); + r = nod(OIF, N, N); + r->ninit = cas->ninit; + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + // if c != nil && selectnbsend(c, v) { body } else { default body } + ch = cheapexpr(n->left, &r->ninit); + r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), ch, n->right); + break; + + case OSELRECV: + // if c != nil && selectnbrecv(&v, c) { body } else { default body } + r = nod(OIF, N, N); + r->ninit = cas->ninit; + ch = cheapexpr(n->right->left, &r->ninit); + r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); + break; + + case OSELRECV2: + // if c != nil && selectnbrecv2(&v, c) { body } else { default body } + r = nod(OIF, N, N); + r->ninit = cas->ninit; + ch = cheapexpr(n->right->left, &r->ninit); + r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type), + types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch); + break; + } + typecheck(&r->ntest, Erv); + r->nbody = cas->nbody; + r->nelse = concat(dflt->ninit, dflt->nbody); + sel->nbody = list1(r); + goto out; + } + + init = sel->ninit; + sel->ninit = nil; + + // generate sel-struct + setlineno(sel); + var = nod(OXXX, N, N); + tempname(var, ptrto(types[TUINT8])); + r = nod(OAS, var, mkcall("newselect", var->type, nil, nodintconst(sel->xoffset))); + typecheck(&r, Etop); + init = list(init, r); + + // register cases + for(l=sel->list; l; l=l->next) { + cas = l->n; + setlineno(cas); + n = cas->left; + r = nod(OIF, N, N); + r->nbody = cas->ninit; + cas->ninit = nil; + if(n != nil) { + r->nbody = concat(r->nbody, n->ninit); + n->ninit = nil; + } + if(n == nil) { + // selectdefault(sel *byte); + r->ntest = mkcall("selectdefault", types[TBOOL], &init, var); + } else { + switch(n->op) { + default: + fatal("select %O", n->op); + + case OSEND: + // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); + n->left = safeexpr(n->left, &r->ninit); + n->right = localexpr(n->right, n->left->type->type, &r->ninit); + n->right = nod(OADDR, n->right, N); + n->right->etype = 1; // pointer does not escape + typecheck(&n->right, Erv); + r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], + &init, var, n->left, n->right); + break; + + case OSELRECV: + // selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool); + r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL], + &init, var, n->right->left, n->left); + break; + + case OSELRECV2: + // selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool); + r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL], + &init, var, n->right->left, n->left, n->ntest); + break; + } + } + r->nbody = concat(r->nbody, cas->nbody); + r->nbody = list(r->nbody, nod(OBREAK, N, N)); + init = list(init, r); + } + + // run the select + setlineno(sel); + init = list(init, mkcall("selectgo", T, nil, var)); + sel->nbody = init; + +out: + sel->list = nil; + walkstmtlist(sel->nbody); + lineno = lno; +} diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c new file mode 100644 index 000000000..917e2ae6d --- /dev/null +++ b/src/cmd/gc/sinit.c @@ -0,0 +1,971 @@ +// 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. + +/* + * static initialization + */ + +#include "go.h" + +static NodeList *initlist; +static void init2(Node*, NodeList**); +static void init2list(NodeList*, NodeList**); + +static void +init1(Node *n, NodeList **out) +{ + NodeList *l; + + if(n == N) + return; + init1(n->left, out); + init1(n->right, out); + for(l=n->list; l; l=l->next) + init1(l->n, out); + + if(n->op != ONAME) + return; + switch(n->class) { + case PEXTERN: + case PFUNC: + break; + default: + if(isblank(n) && n->defn != N && !n->defn->initorder) { + n->defn->initorder = 1; + *out = list(*out, n->defn); + } + return; + } + + if(n->initorder == 1) + return; + if(n->initorder == 2) { + if(n->class == PFUNC) + return; + + // if there have already been errors printed, + // those errors probably confused us and + // there might not be a loop. let the user + // fix those first. + flusherrors(); + if(nerrors > 0) + errorexit(); + + print("initialization loop:\n"); + for(l=initlist;; l=l->next) { + if(l->next == nil) + break; + l->next->end = l; + } + for(; l; l=l->end) + print("\t%L %S refers to\n", l->n->lineno, l->n->sym); + print("\t%L %S\n", n->lineno, n->sym); + errorexit(); + } + n->initorder = 2; + l = malloc(sizeof *l); + l->next = initlist; + l->n = n; + l->end = nil; + initlist = l; + + // make sure that everything n depends on is initialized. + // n->defn is an assignment to n + if(n->defn != N) { + switch(n->defn->op) { + default: + goto bad; + + case ODCLFUNC: + init2list(n->defn->nbody, out); + break; + + case OAS: + if(n->defn->left != n) + goto bad; + n->defn->dodata = 1; + init1(n->defn->right, out); + if(debug['j']) + print("%S\n", n->sym); + *out = list(*out, n->defn); + break; + + case OAS2FUNC: + case OAS2MAPR: + case OAS2DOTTYPE: + case OAS2RECV: + if(n->defn->initorder) + break; + n->defn->initorder = 1; + for(l=n->defn->rlist; l; l=l->next) + init1(l->n, out); + *out = list(*out, n->defn); + break; + } + } + l = initlist; + initlist = l->next; + if(l->n != n) + fatal("bad initlist"); + free(l); + n->initorder = 1; + return; + +bad: + dump("defn", n->defn); + fatal("init1: bad defn"); +} + +// recurse over n, doing init1 everywhere. +static void +init2(Node *n, NodeList **out) +{ + if(n == N || n->initorder == 1) + return; + init1(n, out); + init2(n->left, out); + init2(n->right, out); + init2(n->ntest, out); + init2list(n->ninit, out); + init2list(n->list, out); + init2list(n->rlist, out); + init2list(n->nbody, out); + init2list(n->nelse, out); +} + +static void +init2list(NodeList *l, NodeList **out) +{ + for(; l; l=l->next) + init2(l->n, out); +} + + +static void +initreorder(NodeList *l, NodeList **out) +{ + Node *n; + + for(; l; l=l->next) { + n = l->n; + switch(n->op) { + case ODCLFUNC: + case ODCLCONST: + case ODCLTYPE: + continue; + } + initreorder(n->ninit, out); + n->ninit = nil; + init1(n, out); + } +} + +NodeList* +initfix(NodeList *l) +{ + NodeList *lout; + + lout = nil; + initreorder(l, &lout); + return lout; +} + +/* + * from here down is the walk analysis + * of composite literals. + * most of the work is to generate + * data statements for the constant + * part of the composite literal. + */ + +static void structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init); +static void arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init); +static void slicelit(int ctxt, Node *n, Node *var, NodeList **init); +static void maplit(int ctxt, Node *n, Node *var, NodeList **init); + +static Node* +staticname(Type *t, int ctxt) +{ + Node *n; + + snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen); + statuniqgen++; + n = newname(lookup(namebuf)); + if(!ctxt) + n->readonly = 1; + addvar(n, t, PEXTERN); + return n; +} + +static int +isliteral(Node *n) +{ + if(n->op == OLITERAL) + if(n->val.ctype != CTNIL) + return 1; + return 0; +} + +static int +simplename(Node *n) +{ + if(n->op != ONAME) + goto no; + if(!n->addable) + goto no; + if(n->class & PHEAP) + goto no; + if(n->class == PPARAMREF) + goto no; + return 1; + +no: + return 0; +} + +static void +litas(Node *l, Node *r, NodeList **init) +{ + Node *a; + + a = nod(OAS, l, r); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); +} + +enum +{ + MODEDYNAM = 1, + MODECONST = 2, +}; + +static int +getdyn(Node *n, int top) +{ + NodeList *nl; + Node *value; + int mode; + + mode = 0; + switch(n->op) { + default: + if(isliteral(n)) + return MODECONST; + return MODEDYNAM; + case OARRAYLIT: + if(!top && n->type->bound < 0) + return MODEDYNAM; + case OSTRUCTLIT: + break; + } + + for(nl=n->list; nl; nl=nl->next) { + value = nl->n->right; + mode |= getdyn(value, 0); + if(mode == (MODEDYNAM|MODECONST)) + break; + } + return mode; +} + +static void +structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init) +{ + Node *r, *a; + NodeList *nl; + Node *index, *value; + + for(nl=n->list; nl; nl=nl->next) { + r = nl->n; + if(r->op != OKEY) + fatal("structlit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + + switch(value->op) { + case OARRAYLIT: + if(value->type->bound < 0) { + if(pass == 1 && ctxt != 0) { + a = nod(ODOT, var, newname(index->sym)); + slicelit(ctxt, value, a, init); + } else + if(pass == 2 && ctxt == 0) { + a = nod(ODOT, var, newname(index->sym)); + slicelit(ctxt, value, a, init); + } else + if(pass == 3) + break; + continue; + } + a = nod(ODOT, var, newname(index->sym)); + arraylit(ctxt, pass, value, a, init); + continue; + + case OSTRUCTLIT: + a = nod(ODOT, var, newname(index->sym)); + structlit(ctxt, pass, value, a, init); + continue; + } + + if(isliteral(value)) { + if(pass == 2) + continue; + } else + if(pass == 1) + continue; + + // build list of var.field = expr + a = nod(ODOT, var, newname(index->sym)); + a = nod(OAS, a, value); + typecheck(&a, Etop); + walkexpr(&a, init); + if(pass == 1) { + if(a->op != OAS) + fatal("structlit: not as"); + a->dodata = 2; + } + *init = list(*init, a); + } +} + +static void +arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init) +{ + Node *r, *a; + NodeList *l; + Node *index, *value; + + for(l=n->list; l; l=l->next) { + r = l->n; + if(r->op != OKEY) + fatal("arraylit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + + switch(value->op) { + case OARRAYLIT: + if(value->type->bound < 0) { + if(pass == 1 && ctxt != 0) { + a = nod(OINDEX, var, index); + slicelit(ctxt, value, a, init); + } else + if(pass == 2 && ctxt == 0) { + a = nod(OINDEX, var, index); + slicelit(ctxt, value, a, init); + } else + if(pass == 3) + break; + continue; + } + a = nod(OINDEX, var, index); + arraylit(ctxt, pass, value, a, init); + continue; + + case OSTRUCTLIT: + a = nod(OINDEX, var, index); + structlit(ctxt, pass, value, a, init); + continue; + } + + if(isliteral(index) && isliteral(value)) { + if(pass == 2) + continue; + } else + if(pass == 1) + continue; + + // build list of var[index] = value + a = nod(OINDEX, var, index); + a = nod(OAS, a, value); + typecheck(&a, Etop); + walkexpr(&a, init); // add any assignments in r to top + if(pass == 1) { + if(a->op != OAS) + fatal("structlit: not as"); + a->dodata = 2; + } + *init = list(*init, a); + } +} + +static void +slicelit(int ctxt, Node *n, Node *var, NodeList **init) +{ + Node *r, *a; + NodeList *l; + Type *t; + Node *vstat, *vauto; + Node *index, *value; + int mode; + + // make an array type + t = shallow(n->type); + t->bound = mpgetfix(n->right->val.u.xval); + t->width = 0; + t->sym = nil; + dowidth(t); + + if(ctxt != 0) { + + // put everything into static array + vstat = staticname(t, ctxt); + arraylit(ctxt, 1, n, vstat, init); + arraylit(ctxt, 2, n, vstat, init); + + // copy static to slice + a = nod(OSLICE, vstat, nod(OKEY, N, N)); + a = nod(OAS, var, a); + typecheck(&a, Etop); + a->dodata = 2; + *init = list(*init, a); + return; + } + + // recipe for var = []t{...} + // 1. make a static array + // var vstat [...]t + // 2. assign (data statements) the constant part + // vstat = constpart{} + // 3. make an auto pointer to array and allocate heap to it + // var vauto *[...]t = new([...]t) + // 4. copy the static array to the auto array + // *vauto = vstat + // 5. assign slice of allocated heap to var + // var = [0:]*auto + // 6. for each dynamic part assign to the slice + // var[i] = dynamic part + // + // an optimization is done if there is no constant part + // 3. var vauto *[...]t = new([...]t) + // 5. var = [0:]*auto + // 6. var[i] = dynamic part + + // if the literal contains constants, + // make static initialized array (1),(2) + vstat = N; + mode = getdyn(n, 1); + if(mode & MODECONST) { + vstat = staticname(t, ctxt); + arraylit(ctxt, 1, n, vstat, init); + } + + // make new auto *array (3 declare) + vauto = nod(OXXX, N, N); + tempname(vauto, ptrto(t)); + + // set auto to point at new heap (3 assign) + a = nod(ONEW, N, N); + a->list = list1(typenod(t)); + a = nod(OAS, vauto, a); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + + if(vstat != N) { + // copy static to heap (4) + a = nod(OIND, vauto, N); + a = nod(OAS, a, vstat); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + } + + // make slice out of heap (5) + a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N))); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + + // put dynamics into slice (6) + for(l=n->list; l; l=l->next) { + r = l->n; + if(r->op != OKEY) + fatal("slicelit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + a = nod(OINDEX, var, index); + a->etype = 1; // no bounds checking + // TODO need to check bounds? + + switch(value->op) { + case OARRAYLIT: + if(value->type->bound < 0) + break; + arraylit(ctxt, 2, value, a, init); + continue; + + case OSTRUCTLIT: + structlit(ctxt, 2, value, a, init); + continue; + } + + if(isliteral(index) && isliteral(value)) + continue; + + // build list of var[c] = expr + a = nod(OAS, a, value); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + } +} + +static void +maplit(int ctxt, Node *n, Node *var, NodeList **init) +{ + Node *r, *a; + NodeList *l; + int nerr, b; + Type *t, *tk, *tv, *t1; + Node *vstat, *index, *value; + Sym *syma, *symb; + +ctxt = 0; + + // make the map var + nerr = nerrors; + + a = nod(OMAKE, N, N); + a->list = list1(typenod(n->type)); + litas(var, a, init); + + // count the initializers + b = 0; + for(l=n->list; l; l=l->next) { + r = l->n; + + if(r->op != OKEY) + fatal("slicelit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + + if(isliteral(index) && isliteral(value)) + b++; + } + + t = T; + if(b != 0) { + // build type [count]struct { a Tindex, b Tvalue } + t = n->type; + tk = t->down; + tv = t->type; + + symb = lookup("b"); + t = typ(TFIELD); + t->type = tv; + t->sym = symb; + + syma = lookup("a"); + t1 = t; + t = typ(TFIELD); + t->type = tk; + t->sym = syma; + t->down = t1; + + t1 = t; + t = typ(TSTRUCT); + t->type = t1; + + t1 = t; + t = typ(TARRAY); + t->bound = b; + t->type = t1; + + dowidth(t); + + // make and initialize static array + vstat = staticname(t, ctxt); + b = 0; + for(l=n->list; l; l=l->next) { + r = l->n; + + if(r->op != OKEY) + fatal("slicelit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + + if(isliteral(index) && isliteral(value)) { + // build vstat[b].a = key; + a = nodintconst(b); + a = nod(OINDEX, vstat, a); + a = nod(ODOT, a, newname(syma)); + a = nod(OAS, a, index); + typecheck(&a, Etop); + walkexpr(&a, init); + a->dodata = 2; + *init = list(*init, a); + + // build vstat[b].b = value; + a = nodintconst(b); + a = nod(OINDEX, vstat, a); + a = nod(ODOT, a, newname(symb)); + a = nod(OAS, a, value); + typecheck(&a, Etop); + walkexpr(&a, init); + a->dodata = 2; + *init = list(*init, a); + + b++; + } + } + + // loop adding structure elements to map + // for i = 0; i < len(vstat); i++ { + // map[vstat[i].a] = vstat[i].b + // } + index = nod(OXXX, N, N); + tempname(index, types[TINT]); + + a = nod(OINDEX, vstat, index); + a->etype = 1; // no bounds checking + a = nod(ODOT, a, newname(symb)); + + r = nod(OINDEX, vstat, index); + r->etype = 1; // no bounds checking + r = nod(ODOT, r, newname(syma)); + r = nod(OINDEX, var, r); + + r = nod(OAS, r, a); + + a = nod(OFOR, N, N); + a->nbody = list1(r); + + a->ninit = list1(nod(OAS, index, nodintconst(0))); + a->ntest = nod(OLT, index, nodintconst(t->bound)); + a->nincr = nod(OASOP, index, nodintconst(1)); + a->nincr->etype = OADD; + + typecheck(&a, Etop); + walkstmt(&a); + *init = list(*init, a); + } + + // put in dynamic entries one-at-a-time + for(l=n->list; l; l=l->next) { + r = l->n; + + if(r->op != OKEY) + fatal("slicelit: rhs not OKEY: %N", r); + index = r->left; + value = r->right; + + if(isliteral(index) && isliteral(value)) + continue; + + // build list of var[c] = expr + a = nod(OINDEX, var, r->left); + a = nod(OAS, a, r->right); + typecheck(&a, Etop); + walkexpr(&a, init); + if(nerr != nerrors) + break; + + *init = list(*init, a); + } +} + +void +anylit(int ctxt, Node *n, Node *var, NodeList **init) +{ + Type *t; + Node *a, *vstat; + + t = n->type; + switch(n->op) { + default: + fatal("anylit: not lit"); + + case OSTRUCTLIT: + if(t->etype != TSTRUCT) + fatal("anylit: not struct"); + + if(simplename(var)) { + + if(ctxt == 0) { + // lay out static data + vstat = staticname(t, ctxt); + structlit(ctxt, 1, n, vstat, init); + + // copy static to var + a = nod(OAS, var, vstat); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + + // add expressions to automatic + structlit(ctxt, 2, n, var, init); + break; + } + structlit(ctxt, 1, n, var, init); + structlit(ctxt, 2, n, var, init); + break; + } + + // initialize of not completely specified + if(count(n->list) < structcount(t)) { + a = nod(OAS, var, N); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + } + structlit(ctxt, 3, n, var, init); + break; + + case OARRAYLIT: + if(t->etype != TARRAY) + fatal("anylit: not array"); + if(t->bound < 0) { + slicelit(ctxt, n, var, init); + break; + } + + if(simplename(var)) { + + if(ctxt == 0) { + // lay out static data + vstat = staticname(t, ctxt); + arraylit(1, 1, n, vstat, init); + + // copy static to automatic + a = nod(OAS, var, vstat); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + + // add expressions to automatic + arraylit(ctxt, 2, n, var, init); + break; + } + arraylit(ctxt, 1, n, var, init); + arraylit(ctxt, 2, n, var, init); + break; + } + + // initialize of not completely specified + if(count(n->list) < t->bound) { + a = nod(OAS, var, N); + typecheck(&a, Etop); + walkexpr(&a, init); + *init = list(*init, a); + } + arraylit(ctxt, 3, n, var, init); + break; + + case OMAPLIT: + if(t->etype != TMAP) + fatal("anylit: not map"); + maplit(ctxt, n, var, init); + break; + } +} + +int +oaslit(Node *n, NodeList **init) +{ + int ctxt; + + if(n->left == N || n->right == N) + goto no; + if(n->left->type == T || n->right->type == T) + goto no; + if(!simplename(n->left)) + goto no; + if(!eqtype(n->left->type, n->right->type)) + goto no; + + // context is init() function. + // implies generated data executed + // exactly once and not subject to races. + ctxt = 0; +// if(n->dodata == 1) +// ctxt = 1; + + switch(n->right->op) { + default: + goto no; + + case OSTRUCTLIT: + case OARRAYLIT: + case OMAPLIT: + if(vmatch1(n->left, n->right)) + goto no; + anylit(ctxt, n->right, n->left, init); + break; + } + n->op = OEMPTY; + return 1; + +no: + // not a special composit literal assignment + return 0; +} + +static int +getlit(Node *lit) +{ + if(smallintconst(lit)) + return mpgetfix(lit->val.u.xval); + return -1; +} + +int +stataddr(Node *nam, Node *n) +{ + int l; + + if(n == N) + goto no; + + switch(n->op) { + + case ONAME: + *nam = *n; + return n->addable; + + case ODOT: + if(!stataddr(nam, n->left)) + break; + nam->xoffset += n->xoffset; + nam->type = n->type; + return 1; + + case OINDEX: + if(n->left->type->bound < 0) + break; + if(!stataddr(nam, n->left)) + break; + l = getlit(n->right); + if(l < 0) + break; + nam->xoffset += l*n->type->width; + nam->type = n->type; + return 1; + } + +no: + return 0; +} + +int +gen_as_init(Node *n) +{ + Node *nr, *nl; + Node nam, nod1; + + if(n->dodata == 0) + goto no; + + nr = n->right; + nl = n->left; + if(nr == N) { + if(!stataddr(&nam, nl)) + goto no; + if(nam.class != PEXTERN) + goto no; + goto yes; + } + + if(nr->type == T || !eqtype(nl->type, nr->type)) + goto no; + + if(!stataddr(&nam, nl)) + goto no; + + if(nam.class != PEXTERN) + goto no; + + switch(nr->op) { + default: + goto no; + + case OCONVNOP: + nr = nr->left; + if(nr == N || nr->op != OSLICEARR) + goto no; + // fall through + + case OSLICEARR: + if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) { + nr = nr->left; + goto slice; + } + goto no; + + case OLITERAL: + break; + } + + switch(nr->type->etype) { + default: + goto no; + + case TBOOL: + case TINT8: + case TUINT8: + case TINT16: + case TUINT16: + case TINT32: + case TUINT32: + case TINT64: + case TUINT64: + case TINT: + case TUINT: + case TUINTPTR: + case TPTR32: + case TPTR64: + case TFLOAT32: + case TFLOAT64: + gused(N); // in case the data is the dest of a goto + gdata(&nam, nr, nr->type->width); + break; + + case TCOMPLEX64: + case TCOMPLEX128: + gused(N); // in case the data is the dest of a goto + gdatacomplex(&nam, nr->val.u.cval); + break; + + case TSTRING: + gused(N); // in case the data is the dest of a goto + gdatastring(&nam, nr->val.u.sval); + break; + } + +yes: + return 1; + +slice: + gused(N); // in case the data is the dest of a goto + nl = nr; + if(nr == N || nr->op != OADDR) + goto no; + nr = nr->left; + if(nr == N || nr->op != ONAME) + goto no; + + // nr is the array being converted to a slice + if(nr->type == T || nr->type->etype != TARRAY || nr->type->bound < 0) + goto no; + + nam.xoffset += Array_array; + gdata(&nam, nl, types[tptr]->width); + + nam.xoffset += Array_nel-Array_array; + nodconst(&nod1, types[TINT32], nr->type->bound); + gdata(&nam, &nod1, types[TINT32]->width); + + nam.xoffset += Array_cap-Array_nel; + gdata(&nam, &nod1, types[TINT32]->width); + + goto yes; + +no: + if(n->dodata == 2) { + dump("\ngen_as_init", n); + fatal("gen_as_init couldnt make data statement"); + } + return 0; +} + 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; +} diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c new file mode 100644 index 000000000..c2968c44b --- /dev/null +++ b/src/cmd/gc/swt.c @@ -0,0 +1,896 @@ +// 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" + +enum +{ + Snorm = 0, + Strue, + Sfalse, + Stype, + + Tdefault, // default case + Texprconst, // normal constant case + Texprvar, // normal variable case + Ttypenil, // case nil + Ttypeconst, // type hashes + Ttypevar, // interface type + + Ncase = 4, // count needed to split +}; + +typedef struct Case Case; +struct Case +{ + Node* node; // points at case statement + uint32 hash; // hash of a type switch + uint8 type; // type of case + uint8 diag; // suppress multiple diagnostics + uint16 ordinal; // position in switch + Case* link; // linked list to link +}; +#define C ((Case*)nil) + +void +dumpcase(Case *c0) +{ + Case *c; + + for(c=c0; c!=C; c=c->link) { + switch(c->type) { + case Tdefault: + print("case-default\n"); + print(" ord=%d\n", c->ordinal); + break; + case Texprconst: + print("case-exprconst\n"); + print(" ord=%d\n", c->ordinal); + break; + case Texprvar: + print("case-exprvar\n"); + print(" ord=%d\n", c->ordinal); + print(" op=%O\n", c->node->left->op); + break; + case Ttypenil: + print("case-typenil\n"); + print(" ord=%d\n", c->ordinal); + break; + case Ttypeconst: + print("case-typeconst\n"); + print(" ord=%d\n", c->ordinal); + print(" hash=%ux\n", c->hash); + break; + case Ttypevar: + print("case-typevar\n"); + print(" ord=%d\n", c->ordinal); + break; + default: + print("case-???\n"); + print(" ord=%d\n", c->ordinal); + print(" op=%O\n", c->node->left->op); + print(" hash=%ux\n", c->hash); + break; + } + } + print("\n"); +} + +static int +ordlcmp(Case *c1, Case *c2) +{ + // sort default first + if(c1->type == Tdefault) + return -1; + if(c2->type == Tdefault) + return +1; + + // sort nil second + if(c1->type == Ttypenil) + return -1; + if(c2->type == Ttypenil) + return +1; + + // sort by ordinal + if(c1->ordinal > c2->ordinal) + return +1; + if(c1->ordinal < c2->ordinal) + return -1; + return 0; +} + +static int +exprcmp(Case *c1, Case *c2) +{ + int ct, n; + Node *n1, *n2; + + // sort non-constants last + if(c1->type != Texprconst) + return +1; + if(c2->type != Texprconst) + return -1; + + n1 = c1->node->left; + n2 = c2->node->left; + + ct = n1->val.ctype; + if(ct != n2->val.ctype) { + // invalid program, but return a sort + // order so that we can give a better + // error later. + return ct - n2->val.ctype; + } + + // sort by constant value + n = 0; + switch(ct) { + case CTFLT: + n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval); + break; + case CTINT: + n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval); + break; + case CTSTR: + n = cmpslit(n1, n2); + break; + } + + return n; +} + +static int +typecmp(Case *c1, Case *c2) +{ + + // sort non-constants last + if(c1->type != Ttypeconst) + return +1; + if(c2->type != Ttypeconst) + return -1; + + // sort by hash code + if(c1->hash > c2->hash) + return +1; + if(c1->hash < c2->hash) + return -1; + + // sort by ordinal so duplicate error + // happens on later case. + if(c1->ordinal > c2->ordinal) + return +1; + if(c1->ordinal < c2->ordinal) + return -1; + return 0; +} + +static Case* +csort(Case *l, int(*f)(Case*, Case*)) +{ + Case *l1, *l2, *le; + + if(l == C || l->link == C) + return l; + + l1 = l; + l2 = l; + for(;;) { + l2 = l2->link; + if(l2 == C) + break; + l2 = l2->link; + if(l2 == C) + break; + l1 = l1->link; + } + + l2 = l1->link; + l1->link = C; + l1 = csort(l, f); + l2 = csort(l2, f); + + /* set up lead element */ + if((*f)(l1, l2) < 0) { + l = l1; + l1 = l1->link; + } else { + l = l2; + l2 = l2->link; + } + le = l; + + for(;;) { + if(l1 == C) { + while(l2) { + le->link = l2; + le = l2; + l2 = l2->link; + } + le->link = C; + break; + } + if(l2 == C) { + while(l1) { + le->link = l1; + le = l1; + l1 = l1->link; + } + break; + } + if((*f)(l1, l2) < 0) { + le->link = l1; + le = l1; + l1 = l1->link; + } else { + le->link = l2; + le = l2; + l2 = l2->link; + } + } + le->link = C; + return l; +} + +static Node* +newlabel(void) +{ + static int label; + + label++; + snprint(namebuf, sizeof(namebuf), "%.6d", label); + return newname(lookup(namebuf)); +} + +/* + * build separate list of statements and cases + * make labels between cases and statements + * deal with fallthrough, break, unreachable statements + */ +static void +casebody(Node *sw, Node *typeswvar) +{ + Node *n, *c, *last; + Node *def; + NodeList *cas, *stat, *l, *lc; + Node *go, *br; + int32 lno, needvar; + + lno = setlineno(sw); + if(sw->list == nil) + return; + + cas = nil; // cases + stat = nil; // statements + def = N; // defaults + br = nod(OBREAK, N, N); + + for(l=sw->list; l; l=l->next) { + n = l->n; + lno = setlineno(n); + if(n->op != OXCASE) + fatal("casebody %O", n->op); + n->op = OCASE; + needvar = count(n->list) != 1 || n->list->n->op == OLITERAL; + + go = nod(OGOTO, newlabel(), N); + if(n->list == nil) { + if(def != N) + yyerror("more than one default case"); + // reuse original default case + n->right = go; + def = n; + } + + if(n->list != nil && n->list->next == nil) { + // one case - reuse OCASE node. + c = n->list->n; + n->left = c; + n->right = go; + n->list = nil; + cas = list(cas, n); + } else { + // expand multi-valued cases + for(lc=n->list; lc; lc=lc->next) { + c = lc->n; + cas = list(cas, nod(OCASE, c, go)); + } + } + + stat = list(stat, nod(OLABEL, go->left, N)); + if(typeswvar && needvar && n->nname != N) { + NodeList *l; + + l = list1(nod(ODCL, n->nname, N)); + l = list(l, nod(OAS, n->nname, typeswvar)); + typechecklist(l, Etop); + stat = concat(stat, l); + } + stat = concat(stat, n->nbody); + + // botch - shouldnt fall thru declaration + last = stat->end->n; + if(last->op == OXFALL) { + if(typeswvar) { + setlineno(last); + yyerror("cannot fallthrough in type switch"); + } + last->op = OFALL; + } else + stat = list(stat, br); + } + + stat = list(stat, br); + if(def) + cas = list(cas, def); + + sw->list = cas; + sw->nbody = stat; + lineno = lno; +} + +static Case* +mkcaselist(Node *sw, int arg) +{ + Node *n; + Case *c, *c1, *c2; + NodeList *l; + int ord; + + c = C; + ord = 0; + + for(l=sw->list; l; l=l->next) { + n = l->n; + c1 = mal(sizeof(*c1)); + c1->link = c; + c = c1; + + ord++; + c->ordinal = ord; + c->node = n; + + if(n->left == N) { + c->type = Tdefault; + continue; + } + + switch(arg) { + case Stype: + c->hash = 0; + if(n->left->op == OLITERAL) { + c->type = Ttypenil; + continue; + } + if(istype(n->left->type, TINTER)) { + c->type = Ttypevar; + continue; + } + + c->hash = typehash(n->left->type); + c->type = Ttypeconst; + continue; + + case Snorm: + case Strue: + case Sfalse: + c->type = Texprvar; + switch(consttype(n->left)) { + case CTFLT: + case CTINT: + case CTSTR: + c->type = Texprconst; + } + continue; + } + } + + if(c == C) + return C; + + // sort by value and diagnose duplicate cases + switch(arg) { + case Stype: + c = csort(c, typecmp); + for(c1=c; c1!=C; c1=c1->link) { + for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) { + if(c1->type == Ttypenil || c1->type == Tdefault) + break; + if(c2->type == Ttypenil || c2->type == Tdefault) + break; + if(!eqtype(c1->node->left->type, c2->node->left->type)) + continue; + yyerrorl(c2->node->lineno, "duplicate case in switch\n\tprevious case at %L", c1->node->lineno); + } + } + break; + case Snorm: + case Strue: + case Sfalse: + c = csort(c, exprcmp); + for(c1=c; c1->link!=C; c1=c1->link) { + if(exprcmp(c1, c1->link) != 0) + continue; + setlineno(c1->link->node); + yyerror("duplicate case in switch\n\tprevious case at %L", c1->node->lineno); + } + break; + } + + // put list back in processing order + c = csort(c, ordlcmp); + return c; +} + +static Node* exprname; + +static Node* +exprbsw(Case *c0, int ncase, int arg) +{ + NodeList *cas; + Node *a, *n; + Case *c; + int i, half, lno; + + cas = nil; + if(ncase < Ncase) { + for(i=0; i<ncase; i++) { + n = c0->node; + lno = setlineno(n); + + switch(arg) { + case Strue: + a = nod(OIF, N, N); + a->ntest = n->left; // if val + a->nbody = list1(n->right); // then goto l + break; + + case Sfalse: + a = nod(OIF, N, N); + a->ntest = nod(ONOT, n->left, N); // if !val + typecheck(&a->ntest, Erv); + a->nbody = list1(n->right); // then goto l + break; + + default: + a = nod(OIF, N, N); + a->ntest = nod(OEQ, exprname, n->left); // if name == val + typecheck(&a->ntest, Erv); + a->nbody = list1(n->right); // then goto l + break; + } + + cas = list(cas, a); + c0 = c0->link; + lineno = lno; + } + return liststmt(cas); + } + + // find the middle and recur + c = c0; + half = ncase>>1; + for(i=1; i<half; i++) + c = c->link; + a = nod(OIF, N, N); + a->ntest = nod(OLE, exprname, c->node->left); + typecheck(&a->ntest, Erv); + a->nbody = list1(exprbsw(c0, half, arg)); + a->nelse = list1(exprbsw(c->link, ncase-half, arg)); + return a; +} + +/* + * normal (expression) switch. + * rebulid case statements into if .. goto + */ +static void +exprswitch(Node *sw) +{ + Node *def; + NodeList *cas; + Node *a; + Case *c0, *c, *c1; + Type *t; + int arg, ncase; + + casebody(sw, N); + + arg = Snorm; + if(isconst(sw->ntest, CTBOOL)) { + arg = Strue; + if(sw->ntest->val.u.bval == 0) + arg = Sfalse; + } + walkexpr(&sw->ntest, &sw->ninit); + t = sw->type; + if(t == T) + return; + + /* + * convert the switch into OIF statements + */ + exprname = N; + cas = nil; + if(arg != Strue && arg != Sfalse) { + exprname = nod(OXXX, N, N); + tempname(exprname, sw->ntest->type); + cas = list1(nod(OAS, exprname, sw->ntest)); + typechecklist(cas, Etop); + } + + c0 = mkcaselist(sw, arg); + if(c0 != C && c0->type == Tdefault) { + def = c0->node->right; + c0 = c0->link; + } else { + def = nod(OBREAK, N, N); + } + +loop: + if(c0 == C) { + cas = list(cas, def); + sw->nbody = concat(cas, sw->nbody); + sw->list = nil; + walkstmtlist(sw->nbody); + return; + } + + // deal with the variables one-at-a-time + if(c0->type != Texprconst) { + a = exprbsw(c0, 1, arg); + cas = list(cas, a); + c0 = c0->link; + goto loop; + } + + // do binary search on run of constants + ncase = 1; + for(c=c0; c->link!=C; c=c->link) { + if(c->link->type != Texprconst) + break; + ncase++; + } + + // break the chain at the count + c1 = c->link; + c->link = C; + + // sort and compile constants + c0 = csort(c0, exprcmp); + a = exprbsw(c0, ncase, arg); + cas = list(cas, a); + + c0 = c1; + goto loop; + +} + +static Node* hashname; +static Node* facename; +static Node* boolname; + +static Node* +typeone(Node *t) +{ + NodeList *init; + Node *a, *b, *var; + + var = t->nname; + init = nil; + if(var == N) { + typecheck(&nblank, Erv | Easgn); + var = nblank; + } else + init = list1(nod(ODCL, var, N)); + + a = nod(OAS2, N, N); + a->list = list(list1(var), boolname); // var,bool = + b = nod(ODOTTYPE, facename, N); + b->type = t->left->type; // interface.(type) + a->rlist = list1(b); + typecheck(&a, Etop); + init = list(init, a); + + b = nod(OIF, N, N); + b->ntest = boolname; + b->nbody = list1(t->right); // if bool { goto l } + a = liststmt(list(init, b)); + return a; +} + +static Node* +typebsw(Case *c0, int ncase) +{ + NodeList *cas; + Node *a, *n; + Case *c; + int i, half; + + cas = nil; + + if(ncase < Ncase) { + for(i=0; i<ncase; i++) { + n = c0->node; + if(c0->type != Ttypeconst) + fatal("typebsw"); + a = nod(OIF, N, N); + a->ntest = nod(OEQ, hashname, nodintconst(c0->hash)); + typecheck(&a->ntest, Erv); + a->nbody = list1(n->right); + cas = list(cas, a); + c0 = c0->link; + } + return liststmt(cas); + } + + // find the middle and recur + c = c0; + half = ncase>>1; + for(i=1; i<half; i++) + c = c->link; + a = nod(OIF, N, N); + a->ntest = nod(OLE, hashname, nodintconst(c->hash)); + typecheck(&a->ntest, Erv); + a->nbody = list1(typebsw(c0, half)); + a->nelse = list1(typebsw(c->link, ncase-half)); + return a; +} + +/* + * convert switch of the form + * switch v := i.(type) { case t1: ..; case t2: ..; } + * into if statements + */ +static void +typeswitch(Node *sw) +{ + Node *def; + NodeList *cas, *hash; + Node *a, *n; + Case *c, *c0, *c1; + int ncase; + Type *t; + Val v; + + if(sw->ntest == nil) + return; + if(sw->ntest->right == nil) { + setlineno(sw); + yyerror("type switch must have an assignment"); + return; + } + walkexpr(&sw->ntest->right, &sw->ninit); + if(!istype(sw->ntest->right->type, TINTER)) { + yyerror("type switch must be on an interface"); + return; + } + cas = nil; + + /* + * predeclare temporary variables + * and the boolean var + */ + facename = nod(OXXX, N, N); + tempname(facename, sw->ntest->right->type); + a = nod(OAS, facename, sw->ntest->right); + typecheck(&a, Etop); + cas = list(cas, a); + + casebody(sw, facename); + + boolname = nod(OXXX, N, N); + tempname(boolname, types[TBOOL]); + typecheck(&boolname, Erv); + + hashname = nod(OXXX, N, N); + tempname(hashname, types[TUINT32]); + typecheck(&hashname, Erv); + + t = sw->ntest->right->type; + if(isnilinter(t)) + a = syslook("efacethash", 1); + else + a = syslook("ifacethash", 1); + argtype(a, t); + a = nod(OCALL, a, N); + a->list = list1(facename); + a = nod(OAS, hashname, a); + typecheck(&a, Etop); + cas = list(cas, a); + + c0 = mkcaselist(sw, Stype); + if(c0 != C && c0->type == Tdefault) { + def = c0->node->right; + c0 = c0->link; + } else { + def = nod(OBREAK, N, N); + } + + /* + * insert if statement into each case block + */ + for(c=c0; c!=C; c=c->link) { + n = c->node; + switch(c->type) { + + case Ttypenil: + v.ctype = CTNIL; + a = nod(OIF, N, N); + a->ntest = nod(OEQ, facename, nodlit(v)); + typecheck(&a->ntest, Erv); + a->nbody = list1(n->right); // if i==nil { goto l } + n->right = a; + break; + + case Ttypevar: + case Ttypeconst: + n->right = typeone(n); + break; + } + } + + /* + * generate list of if statements, binary search for constant sequences + */ + while(c0 != C) { + if(c0->type != Ttypeconst) { + n = c0->node; + cas = list(cas, n->right); + c0=c0->link; + continue; + } + + // identify run of constants + c1 = c = c0; + while(c->link!=C && c->link->type==Ttypeconst) + c = c->link; + c0 = c->link; + c->link = nil; + + // sort by hash + c1 = csort(c1, typecmp); + + // for debugging: linear search + if(0) { + for(c=c1; c!=C; c=c->link) { + n = c->node; + cas = list(cas, n->right); + } + continue; + } + + // combine adjacent cases with the same hash + ncase = 0; + for(c=c1; c!=C; c=c->link) { + ncase++; + hash = list1(c->node->right); + while(c->link != C && c->link->hash == c->hash) { + hash = list(hash, c->link->node->right); + c->link = c->link->link; + } + c->node->right = liststmt(hash); + } + + // binary search among cases to narrow by hash + cas = list(cas, typebsw(c1, ncase)); + } + if(nerrors == 0) { + cas = list(cas, def); + sw->nbody = concat(cas, sw->nbody); + sw->list = nil; + walkstmtlist(sw->nbody); + } +} + +void +walkswitch(Node *sw) +{ + + /* + * reorder the body into (OLIST, cases, statements) + * cases have OGOTO into statements. + * both have inserted OBREAK statements + */ + walkstmtlist(sw->ninit); + if(sw->ntest == N) { + sw->ntest = nodbool(1); + typecheck(&sw->ntest, Erv); + } + + if(sw->ntest->op == OTYPESW) { + typeswitch(sw); +//dump("sw", sw); + return; + } + exprswitch(sw); +} + +/* + * type check switch statement + */ +void +typecheckswitch(Node *n) +{ + int top, lno; + Type *t; + NodeList *l, *ll; + Node *ncase, *nvar; + Node *def; + + lno = lineno; + typechecklist(n->ninit, Etop); + + if(n->ntest != N && n->ntest->op == OTYPESW) { + // type switch + top = Etype; + typecheck(&n->ntest->right, Erv); + t = n->ntest->right->type; + if(t != T && t->etype != TINTER) + yyerror("cannot type switch on non-interface value %+N", n->ntest->right); + } else { + // value switch + top = Erv; + if(n->ntest) { + typecheck(&n->ntest, Erv); + defaultlit(&n->ntest, T); + t = n->ntest->type; + } else + t = types[TBOOL]; + } + n->type = t; + + def = N; + for(l=n->list; l; l=l->next) { + ncase = l->n; + setlineno(n); + if(ncase->list == nil) { + // default + if(def != N) + yyerror("multiple defaults in switch (first at %L)", def->lineno); + else + def = ncase; + } else { + for(ll=ncase->list; ll; ll=ll->next) { + setlineno(ll->n); + typecheck(&ll->n, Erv | Etype); + if(ll->n->type == T || t == T) + continue; + switch(top) { + case Erv: // expression switch + defaultlit(&ll->n, t); + if(ll->n->op == OTYPE) + yyerror("type %T is not an expression", ll->n->type); + else if(ll->n->type != T && !eqtype(ll->n->type, t)) + yyerror("case %+N in %T switch", ll->n, t); + break; + case Etype: // type switch + if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) + ; + else if(ll->n->op != OTYPE && ll->n->type != T) { + yyerror("%#N is not a type", ll->n); + // reset to original type + ll->n = n->ntest->right; + } + break; + } + } + } + if(top == Etype && n->type != T) { + ll = ncase->list; + nvar = ncase->nname; + if(nvar != N) { + if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) { + // single entry type switch + nvar->ntype = typenod(ll->n->type); + } else { + // multiple entry type switch or default + nvar->ntype = typenod(n->type); + } + } + } + typechecklist(ncase->nbody, Etop); + } + + lineno = lno; +} diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c new file mode 100644 index 000000000..78cdb5bf2 --- /dev/null +++ b/src/cmd/gc/typecheck.c @@ -0,0 +1,2827 @@ +// 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. + +/* + * type check the whole tree of an expression. + * calculates expression types. + * evaluates compile time constants. + * marks variables that escape the local frame. + * rewrites n->op to be more specific in some cases. + */ + +#include "go.h" + +static void implicitstar(Node**); +static int onearg(Node*, char*, ...); +static int twoarg(Node*); +static int lookdot(Node*, Type*, int); +static int looktypedot(Node*, Type*, int); +static void typecheckaste(int, Node*, int, Type*, NodeList*, char*); +static Type* lookdot1(Sym *s, Type *t, Type *f, int); +static int nokeys(NodeList*); +static void typecheckcomplit(Node**); +static void addrescapes(Node*); +static void typecheckas2(Node*); +static void typecheckas(Node*); +static void typecheckfunc(Node*); +static void checklvalue(Node*, char*); +static void checkassign(Node*); +static void checkassignlist(NodeList*); +static void stringtoarraylit(Node**); +static Node* resolve(Node*); +static Type* getforwtype(Node*); + +static NodeList* typecheckdefstack; + +/* + * resolve ONONAME to definition, if any. + */ +Node* +resolve(Node *n) +{ + Node *r; + + if(n != N && n->op == ONONAME && (r = n->sym->def) != N) { + if(r->op != OIOTA) + n = r; + else if(n->iota >= 0) + n = nodintconst(n->iota); + } + return n; +} + +void +typechecklist(NodeList *l, int top) +{ + for(; l; l=l->next) + typecheck(&l->n, top); +} + +static char* _typekind[] = { + [TINT] = "int", + [TUINT] = "uint", + [TINT8] = "int8", + [TUINT8] = "uint8", + [TINT16] = "int16", + [TUINT16] = "uint16", + [TINT32] = "int32", + [TUINT32] = "uint32", + [TINT64] = "int64", + [TUINT64] = "uint64", + [TUINTPTR] = "uintptr", + [TCOMPLEX64] = "complex64", + [TCOMPLEX128] = "complex128", + [TFLOAT32] = "float32", + [TFLOAT64] = "float64", + [TBOOL] = "bool", + [TSTRING] = "string", + [TPTR32] = "pointer", + [TPTR64] = "pointer", + [TSTRUCT] = "struct", + [TINTER] = "interface", + [TCHAN] = "chan", + [TMAP] = "map", + [TARRAY] = "array", + [TFUNC] = "func", + [TNIL] = "nil", + [TIDEAL] = "ideal number", +}; + +static char* +typekind(int et) +{ + static char buf[50]; + char *s; + + if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil) + return s; + snprint(buf, sizeof buf, "etype=%d", et); + return buf; +} + +/* + * type check node *np. + * replaces *np with a new pointer in some cases. + * returns the final value of *np as a convenience. + */ +Node* +typecheck(Node **np, int top) +{ + int et, aop, op, ptr; + Node *n, *l, *r; + NodeList *args; + int lno, ok, ntop; + Type *t, *tp, *ft, *missing, *have; + Sym *sym; + Val v; + char *why; + + // cannot type check until all the source has been parsed + if(!typecheckok) + fatal("early typecheck"); + + n = *np; + if(n == N) + return N; + + lno = setlineno(n); + + // Skip over parens. + while(n->op == OPAREN) + n = n->left; + + // Resolve definition of name and value of iota lazily. + n = resolve(n); + + *np = n; + + // Skip typecheck if already done. + // But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed. + if(n->typecheck == 1) { + switch(n->op) { + case ONAME: + case OTYPE: + case OLITERAL: + case OPACK: + break; + default: + lineno = lno; + return n; + } + } + + if(n->typecheck == 2) { + yyerror("typechecking loop"); + lineno = lno; + return n; + } + n->typecheck = 2; + + if(n->sym) { + if(n->op == ONAME && n->etype != 0 && !(top & Ecall)) { + yyerror("use of builtin %S not in function call", n->sym); + goto error; + } + + // a dance to handle forward-declared recursive pointer types. + if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T) + defertypecopy(n, ft); + + typecheckdef(n); + n->realtype = n->type; + if(n->op == ONONAME) + goto error; + } + *np = n; + +reswitch: + ok = 0; + switch(n->op) { + default: + // until typecheck is complete, do nothing. + dump("typecheck", n); + fatal("typecheck %O", n->op); + + /* + * names + */ + case OLITERAL: + ok |= Erv; + if(n->type == T && n->val.ctype == CTSTR) + n->type = idealstring; + goto ret; + + case ONONAME: + ok |= Erv; + goto ret; + + case ONAME: + if(n->etype != 0) { + ok |= Ecall; + goto ret; + } + if(!(top & Easgn)) { + // not a write to the variable + if(isblank(n)) { + yyerror("cannot use _ as value"); + goto error; + } + n->used = 1; + } + ok |= Erv; + goto ret; + + case OPACK: + yyerror("use of package %S not in selector", n->sym); + goto error; + + case ODDD: + break; + + /* + * types (OIND is with exprs) + */ + case OTYPE: + ok |= Etype; + if(n->type == T) + goto error; + break; + + case OTPAREN: + ok |= Etype; + l = typecheck(&n->left, Etype); + if(l->type == T) + goto error; + n->op = OTYPE; + n->type = l->type; + n->left = N; + break; + + case OTARRAY: + ok |= Etype; + t = typ(TARRAY); + l = n->left; + r = n->right; + if(l == nil) { + t->bound = -1; // slice + } else if(l->op == ODDD) { + t->bound = -100; // to be filled in + if(!(top&Ecomplit)) + yyerror("use of [...] array outside of array literal"); + } else { + l = typecheck(&n->left, Erv); + switch(consttype(l)) { + case CTINT: + v = l->val; + break; + case CTFLT: + v = toint(l->val); + break; + default: + yyerror("invalid array bound %#N", l); + goto error; + } + t->bound = mpgetfix(v.u.xval); + if(t->bound < 0) { + yyerror("array bound must be non-negative"); + goto error; + } else + overflow(v, types[TINT]); + } + typecheck(&r, Etype); + if(r->type == T) + goto error; + t->type = r->type; + n->op = OTYPE; + n->type = t; + n->left = N; + n->right = N; + if(t->bound != -100) + checkwidth(t); + break; + + case OTMAP: + ok |= Etype; + l = typecheck(&n->left, Etype); + r = typecheck(&n->right, Etype); + if(l->type == T || r->type == T) + goto error; + n->op = OTYPE; + n->type = maptype(l->type, r->type); + n->left = N; + n->right = N; + break; + + case OTCHAN: + ok |= Etype; + l = typecheck(&n->left, Etype); + if(l->type == T) + goto error; + t = typ(TCHAN); + t->type = l->type; + t->chan = n->etype; + n->op = OTYPE; + n->type = t; + n->left = N; + n->etype = 0; + break; + + case OTSTRUCT: + ok |= Etype; + n->op = OTYPE; + n->type = dostruct(n->list, TSTRUCT); + if(n->type == T) + goto error; + n->list = nil; + break; + + case OTINTER: + ok |= Etype; + n->op = OTYPE; + n->type = dostruct(n->list, TINTER); + if(n->type == T) + goto error; + break; + + case OTFUNC: + ok |= Etype; + n->op = OTYPE; + n->type = functype(n->left, n->list, n->rlist); + if(n->type == T) + goto error; + break; + + /* + * type or expr + */ + case OIND: + ntop = Erv | Etype; + if(!(top & Eaddr)) + ntop |= Eindir; + l = typecheck(&n->left, ntop); + if((t = l->type) == T) + goto error; + if(l->op == OTYPE) { + ok |= Etype; + n->op = OTYPE; + n->type = ptrto(l->type); + n->left = N; + goto ret; + } + if(!isptr[t->etype]) { + yyerror("invalid indirect of %+N", n->left); + goto error; + } + ok |= Erv; + n->type = t->type; + goto ret; + + /* + * arithmetic exprs + */ + case OASOP: + ok |= Etop; + l = typecheck(&n->left, Erv); + checkassign(n->left); + r = typecheck(&n->right, Erv); + if(l->type == T || r->type == T) + goto error; + op = n->etype; + goto arith; + + case OADD: + case OAND: + case OANDAND: + case OANDNOT: + case ODIV: + case OEQ: + case OGE: + case OGT: + case OLE: + case OLT: + case OLSH: + case ORSH: + case OMOD: + case OMUL: + case ONE: + case OOR: + case OOROR: + case OSUB: + case OXOR: + ok |= Erv; + l = typecheck(&n->left, Erv | (top & Eiota)); + r = typecheck(&n->right, Erv | (top & Eiota)); + if(l->type == T || r->type == T) + goto error; + op = n->op; + arith: + if(op == OLSH || op == ORSH) + goto shift; + // ideal mixed with non-ideal + defaultlit2(&l, &r, 0); + n->left = l; + n->right = r; + if(l->type == T || r->type == T) + goto error; + t = l->type; + if(t->etype == TIDEAL) + t = r->type; + et = t->etype; + if(et == TIDEAL) + et = TINT; + if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) { + // comparison is okay as long as one side is + // assignable to the other. convert so they have + // the same type. (the only conversion that isn't + // a no-op is concrete == interface.) + if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) { + l = nod(aop, l, N); + l->type = r->type; + l->typecheck = 1; + n->left = l; + t = l->type; + } else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) { + r = nod(aop, r, N); + r->type = l->type; + r->typecheck = 1; + n->right = r; + t = r->type; + } + et = t->etype; + } + if(t->etype != TIDEAL && !eqtype(l->type, r->type)) { + defaultlit2(&l, &r, 1); + yyerror("invalid operation: %#N (mismatched types %T and %T)", n, l->type, r->type); + goto error; + } + if(!okfor[op][et]) { + notokfor: + yyerror("invalid operation: %#N (operator %#O not defined on %s)", n, op, typekind(et)); + goto error; + } + // okfor allows any array == array; + // restrict to slice == nil and nil == slice. + if(l->type->etype == TARRAY && !isslice(l->type)) + goto notokfor; + if(r->type->etype == TARRAY && !isslice(r->type)) + goto notokfor; + if(isslice(l->type) && !isnil(l) && !isnil(r)) { + yyerror("invalid operation: %#N (slice can only be compared to nil)", n); + goto error; + } + t = l->type; + if(iscmp[n->op]) { + evconst(n); + t = types[TBOOL]; + if(n->op != OLITERAL) { + defaultlit2(&l, &r, 1); + n->left = l; + n->right = r; + } + } + if(et == TSTRING) { + if(iscmp[n->op]) { + n->etype = n->op; + n->op = OCMPSTR; + } else if(n->op == OADD) + n->op = OADDSTR; + } + if(et == TINTER) { + if(l->op == OLITERAL && l->val.ctype == CTNIL) { + // swap for back end + n->left = r; + n->right = l; + } else if(r->op == OLITERAL && r->val.ctype == CTNIL) { + // leave alone for back end + } else { + n->etype = n->op; + n->op = OCMPIFACE; + } + } + n->type = t; + goto ret; + + shift: + defaultlit(&r, types[TUINT]); + n->right = r; + t = r->type; + if(!isint[t->etype] || issigned[t->etype]) { + yyerror("invalid operation: %#N (shift count type %T, must be unsigned integer)", n, r->type); + goto error; + } + t = l->type; + if(t != T && t->etype != TIDEAL && !isint[t->etype]) { + yyerror("invalid operation: %#N (shift of type %T)", n, t); + goto error; + } + // no defaultlit for left + // the outer context gives the type + n->type = l->type; + goto ret; + + case OCOM: + case OMINUS: + case ONOT: + case OPLUS: + ok |= Erv; + l = typecheck(&n->left, Erv | (top & Eiota)); + if((t = l->type) == T) + goto error; + if(!okfor[n->op][t->etype]) { + yyerror("invalid operation: %#O %T", n->op, t); + goto error; + } + n->type = t; + goto ret; + + /* + * exprs + */ + case OADDR: + ok |= Erv; + typecheck(&n->left, Erv | Eaddr); + if(n->left->type == T) + goto error; + switch(n->left->op) { + case OMAPLIT: + case OSTRUCTLIT: + case OARRAYLIT: + break; + default: + checklvalue(n->left, "take the address of"); + } + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(!(top & Eindir) && !n->etype) + addrescapes(n->left); + n->type = ptrto(t); + goto ret; + + case OCOMPLIT: + ok |= Erv; + typecheckcomplit(&n); + if(n->type == T) + goto error; + goto ret; + + case OXDOT: + n = adddot(n); + n->op = ODOT; + // fall through + case ODOT: + typecheck(&n->left, Erv|Etype); + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(n->right->op != ONAME) { + yyerror("rhs of . must be a name"); // impossible + goto error; + } + sym = n->right->sym; + if(l->op == OTYPE) { + if(!looktypedot(n, t, 0)) { + if(looktypedot(n, t, 1)) + yyerror("%#N undefined (cannot refer to unexported method %S)", n, n->right->sym); + else + yyerror("%#N undefined (type %T has no method %S)", n, t, n->right->sym); + goto error; + } + if(n->type->etype != TFUNC || n->type->thistuple != 1) { + yyerror("type %T has no method %hS", n->left->type, sym); + n->type = T; + goto error; + } + n->op = ONAME; + n->sym = methodsym(sym, l->type, 0); + n->type = methodfunc(n->type, l->type); + n->xoffset = 0; + n->class = PFUNC; + ok = Erv; + goto ret; + } + tp = t; + if(isptr[t->etype] && t->type->etype != TINTER) { + t = t->type; + if(t == T) + goto error; + n->op = ODOTPTR; + checkwidth(t); + } + if(!lookdot(n, t, 0)) { + if(lookdot(n, t, 1)) + yyerror("%#N undefined (cannot refer to unexported field or method %S)", n, n->right->sym); + else + yyerror("%#N undefined (type %T has no field or method %S)", n, tp, n->right->sym); + goto error; + } + switch(n->op) { + case ODOTINTER: + case ODOTMETH: + ok |= Ecall; + break; + default: + ok |= Erv; + break; + } + goto ret; + + case ODOTTYPE: + ok |= Erv; + typecheck(&n->left, Erv); + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(!isinter(t)) { + yyerror("invalid type assertion: %#N (non-interface type %T on left)", n, t); + goto error; + } + if(n->right != N) { + typecheck(&n->right, Etype); + n->type = n->right->type; + n->right = N; + if(n->type == T) + goto error; + } + if(n->type != T && n->type->etype != TINTER) + if(!implements(n->type, t, &missing, &have, &ptr)) { + if(have) + yyerror("impossible type assertion: %+N cannot have dynamic type %T" + " (wrong type for %S method)\n\thave %S%hhT\n\twant %S%hhT", + l, n->type, missing->sym, have->sym, have->type, + missing->sym, missing->type); + else + yyerror("impossible type assertion: %+N cannot have dynamic type %T" + " (missing %S method)", l, n->type, missing->sym); + goto error; + } + goto ret; + + case OINDEX: + ok |= Erv; + typecheck(&n->left, Erv); + defaultlit(&n->left, T); + implicitstar(&n->left); + l = n->left; + typecheck(&n->right, Erv); + r = n->right; + if((t = l->type) == T || r->type == T) + goto error; + switch(t->etype) { + default: + yyerror("invalid operation: %#N (index of type %T)", n, t); + goto error; + + case TARRAY: + defaultlit(&n->right, T); + if(n->right->type != T && !isint[n->right->type->etype]) + yyerror("non-integer array index %#N", n->right); + n->type = t->type; + break; + + case TMAP: + n->etype = 0; + defaultlit(&n->right, t->down); + if(n->right->type != T) + n->right = assignconv(n->right, t->down, "map index"); + n->type = t->type; + n->op = OINDEXMAP; + break; + + case TSTRING: + defaultlit(&n->right, types[TUINT]); + if(n->right->type != T && !isint[n->right->type->etype]) + yyerror("non-integer string index %#N", n->right); + n->type = types[TUINT8]; + break; + } + goto ret; + + case ORECV: + ok |= Etop | Erv; + typecheck(&n->left, Erv); + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(t->etype != TCHAN) { + yyerror("invalid operation: %#N (receive from non-chan type %T)", n, t); + goto error; + } + if(!(t->chan & Crecv)) { + yyerror("invalid operation: %#N (receive from send-only type %T)", n, t); + goto error; + } + n->type = t->type; + goto ret; + + case OSEND: + if(top & Erv) { + yyerror("send statement %#N used as value; use select for non-blocking send", n); + goto error; + } + ok |= Etop | Erv; + l = typecheck(&n->left, Erv); + typecheck(&n->right, Erv); + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(t->etype != TCHAN) { + yyerror("invalid operation: %#N (send to non-chan type %T)", n, t); + goto error; + } + if(!(t->chan & Csend)) { + yyerror("invalid operation: %#N (send to receive-only type %T)", n, t); + goto error; + } + defaultlit(&n->right, t->type); + r = n->right; + if((t = r->type) == T) + goto error; + r = assignconv(r, l->type->type, "send"); + // TODO: more aggressive + n->etype = 0; + n->type = T; + goto ret; + + case OSLICE: + ok |= Erv; + typecheck(&n->left, top); + typecheck(&n->right->left, Erv); + typecheck(&n->right->right, Erv); + defaultlit(&n->left, T); + defaultlit(&n->right->left, T); + defaultlit(&n->right->right, T); + if(isfixedarray(n->left->type)) { + n->left = nod(OADDR, n->left, N); + typecheck(&n->left, top); + } + if(n->right->left != N) { + if((t = n->right->left->type) == T) + goto error; + if(!isint[t->etype]) { + yyerror("invalid slice index %#N (type %T)", n->right->left, t); + goto error; + } + } + if(n->right->right != N) { + if((t = n->right->right->type) == T) + goto error; + if(!isint[t->etype]) { + yyerror("invalid slice index %#N (type %T)", n->right->right, t); + goto error; + } + } + l = n->left; + if((t = l->type) == T) + goto error; + if(istype(t, TSTRING)) { + n->type = t; + n->op = OSLICESTR; + goto ret; + } + if(isptr[t->etype] && isfixedarray(t->type)) { + n->type = typ(TARRAY); + n->type->type = t->type->type; + n->type->bound = -1; + dowidth(n->type); + n->op = OSLICEARR; + goto ret; + } + if(isslice(t)) { + n->type = t; + goto ret; + } + yyerror("cannot slice %#N (type %T)", l, t); + goto error; + + /* + * call and call like + */ + case OCALL: + l = n->left; + if(l->op == ONAME && (r = unsafenmagic(n)) != N) { + if(n->isddd) + yyerror("invalid use of ... with builtin %#N", l); + n = r; + goto reswitch; + } + typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc)); + l = n->left; + if(l->op == ONAME && l->etype != 0) { + if(n->isddd && l->etype != OAPPEND) + yyerror("invalid use of ... with builtin %#N", l); + // builtin: OLEN, OCAP, etc. + n->op = l->etype; + n->left = n->right; + n->right = N; + goto reswitch; + } + defaultlit(&n->left, T); + l = n->left; + if(l->op == OTYPE) { + if(n->isddd || l->type->bound == -100) + yyerror("invalid use of ... in type conversion", l); + // pick off before type-checking arguments + ok |= Erv; + // turn CALL(type, arg) into CONV(arg) w/ type + n->left = N; + n->op = OCONV; + n->type = l->type; + if(onearg(n, "conversion to %T", l->type) < 0) + goto error; + goto doconv; + } + + if(count(n->list) == 1) + typecheck(&n->list->n, Erv | Efnstruct); + else + typechecklist(n->list, Erv); + if((t = l->type) == T) + goto error; + checkwidth(t); + + switch(l->op) { + case ODOTINTER: + n->op = OCALLINTER; + break; + + case ODOTMETH: + n->op = OCALLMETH; + // typecheckaste was used here but there wasn't enough + // information further down the call chain to know if we + // were testing a method receiver for unexported fields. + // It isn't necessary, so just do a sanity check. + tp = getthisx(t)->type->type; + if(l->left == N || !eqtype(l->left->type, tp)) + fatal("method receiver"); + break; + + default: + n->op = OCALLFUNC; + if(t->etype != TFUNC) { + yyerror("cannot call non-function %#N (type %T)", l, t); + goto error; + } + break; + } + typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, "function argument"); + ok |= Etop; + if(t->outtuple == 0) + goto ret; + ok |= Erv; + if(t->outtuple == 1) { + t = getoutargx(l->type)->type; + if(t == T) + goto error; + if(t->etype == TFIELD) + t = t->type; + n->type = t; + goto ret; + } + // multiple return + if(!(top & (Efnstruct | Etop))) { + yyerror("multiple-value %#N() in single-value context", l); + goto ret; + } + n->type = getoutargx(l->type); + goto ret; + + case OCAP: + case OLEN: + case OREAL: + case OIMAG: + ok |= Erv; + if(onearg(n, "%#O", n->op) < 0) + goto error; + typecheck(&n->left, Erv); + defaultlit(&n->left, T); + implicitstar(&n->left); + l = n->left; + t = l->type; + if(t == T) + goto error; + switch(n->op) { + case OCAP: + if(!okforcap[t->etype]) + goto badcall1; + break; + case OLEN: + if(!okforlen[t->etype]) + goto badcall1; + break; + case OREAL: + case OIMAG: + if(!iscomplex[t->etype]) + goto badcall1; + if(isconst(l, CTCPLX)){ + if(n->op == OREAL) + n = nodfltconst(&l->val.u.cval->real); + else + n = nodfltconst(&l->val.u.cval->imag); + } + n->type = types[cplxsubtype(t->etype)]; + goto ret; + } + // might be constant + switch(t->etype) { + case TSTRING: + if(isconst(l, CTSTR)) { + r = nod(OXXX, N, N); + nodconst(r, types[TINT], l->val.u.sval->len); + r->orig = n; + n = r; + } + break; + case TARRAY: + if(t->bound >= 0 && l->op == ONAME) { + r = nod(OXXX, N, N); + nodconst(r, types[TINT], t->bound); + r->orig = n; + n = r; + } + break; + } + n->type = types[TINT]; + goto ret; + + case OCOMPLEX: + ok |= Erv; + if(twoarg(n) < 0) + goto error; + l = typecheck(&n->left, Erv | (top & Eiota)); + r = typecheck(&n->right, Erv | (top & Eiota)); + if(l->type == T || r->type == T) + goto error; + defaultlit2(&l, &r, 0); + n->left = l; + n->right = r; + if(l->type->etype != r->type->etype) { + badcmplx: + yyerror("invalid operation: %#N (complex of types %T, %T)", n, l->type, r->type); + goto error; + } + switch(l->type->etype) { + default: + goto badcmplx; + case TIDEAL: + t = types[TIDEAL]; + break; + case TFLOAT32: + t = types[TCOMPLEX64]; + break; + case TFLOAT64: + t = types[TCOMPLEX128]; + break; + } + if(l->op == OLITERAL && r->op == OLITERAL) { + // make it a complex literal + n = nodcplxlit(l->val, r->val); + } + n->type = t; + goto ret; + + case OCLOSE: + if(onearg(n, "%#O", n->op) < 0) + goto error; + typecheck(&n->left, Erv); + defaultlit(&n->left, T); + l = n->left; + if((t = l->type) == T) + goto error; + if(t->etype != TCHAN) { + yyerror("invalid operation: %#N (non-chan type %T)", n, t); + goto error; + } + ok |= Etop; + goto ret; + + case OAPPEND: + ok |= Erv; + args = n->list; + if(args == nil) { + yyerror("missing arguments to append"); + goto error; + } + typechecklist(args, Erv); + if((t = args->n->type) == T) + goto error; + n->type = t; + if(!isslice(t)) { + yyerror("first argument to append must be slice; have %lT", t); + goto error; + } + if(n->isddd) { + if(args->next == nil) { + yyerror("cannot use ... on first argument to append"); + goto error; + } + if(args->next->next != nil) { + yyerror("too many arguments to append"); + goto error; + } + args->next->n = assignconv(args->next->n, t->orig, "append"); + goto ret; + } + for(args=args->next; args != nil; args=args->next) { + if(args->n->type == T) + continue; + args->n = assignconv(args->n, t->type, "append"); + } + goto ret; + + case OCOPY: + ok |= Etop|Erv; + args = n->list; + if(args == nil || args->next == nil) { + yyerror("missing arguments to copy"); + goto error; + } + if(args->next->next != nil) { + yyerror("too many arguments to copy"); + goto error; + } + n->left = args->n; + n->right = args->next->n; + n->type = types[TINT]; + typecheck(&n->left, Erv); + typecheck(&n->right, Erv); + if(n->left->type == T || n->right->type == T) + goto error; + defaultlit(&n->left, T); + defaultlit(&n->right, T); + + // copy([]byte, string) + if(isslice(n->left->type) && n->right->type->etype == TSTRING) { + if (n->left->type->type == types[TUINT8]) + goto ret; + yyerror("arguments to copy have different element types: %lT and string", n->left->type); + goto error; + } + + if(!isslice(n->left->type) || !isslice(n->right->type)) { + if(!isslice(n->left->type) && !isslice(n->right->type)) + yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type); + else if(!isslice(n->left->type)) + yyerror("first argument to copy should be slice; have %lT", n->left->type); + else + yyerror("second argument to copy should be slice or string; have %lT", n->right->type); + goto error; + } + if(!eqtype(n->left->type->type, n->right->type->type)) { + yyerror("arguments to copy have different element types: %lT and %lT", n->left->type, n->right->type); + goto error; + } + goto ret; + + case OCONV: + doconv: + ok |= Erv; + typecheck(&n->left, Erv | (top & (Eindir | Eiota))); + convlit1(&n->left, n->type, 1); + if((t = n->left->type) == T || n->type == T) + goto error; + if((n->op = convertop(t, n->type, &why)) == 0) { + yyerror("cannot convert %+N to type %T%s", n->left, n->type, why); + op = OCONV; + } + switch(n->op) { + case OCONVNOP: + if(n->left->op == OLITERAL) { + n->op = OLITERAL; + n->val = n->left->val; + } + break; + case OSTRARRAYBYTE: + case OSTRARRAYRUNE: + if(n->left->op == OLITERAL) + stringtoarraylit(&n); + break; + } + goto ret; + + case OMAKE: + ok |= Erv; + args = n->list; + if(args == nil) { + yyerror("missing argument to make"); + goto error; + } + l = args->n; + args = args->next; + typecheck(&l, Etype); + if((t = l->type) == T) + goto error; + + switch(t->etype) { + default: + badmake: + yyerror("cannot make type %T", t); + goto error; + + case TARRAY: + if(!isslice(t)) + goto badmake; + if(args == nil) { + yyerror("missing len argument to make(%T)", t); + goto error; + } + l = args->n; + args = args->next; + typecheck(&l, Erv); + defaultlit(&l, types[TINT]); + r = N; + if(args != nil) { + r = args->n; + args = args->next; + typecheck(&r, Erv); + defaultlit(&r, types[TINT]); + } + if(l->type == T || (r && r->type == T)) + goto error; + if(!isint[l->type->etype]) { + yyerror("non-integer len argument to make(%T)", t); + goto error; + } + if(r && !isint[r->type->etype]) { + yyerror("non-integer cap argument to make(%T)", t); + goto error; + } + n->left = l; + n->right = r; + n->op = OMAKESLICE; + break; + + case TMAP: + if(args != nil) { + l = args->n; + args = args->next; + typecheck(&l, Erv); + defaultlit(&l, types[TINT]); + if(l->type == T) + goto error; + if(!isint[l->type->etype]) { + yyerror("non-integer size argument to make(%T)", t); + goto error; + } + n->left = l; + } else + n->left = nodintconst(0); + n->op = OMAKEMAP; + break; + + case TCHAN: + l = N; + if(args != nil) { + l = args->n; + args = args->next; + typecheck(&l, Erv); + defaultlit(&l, types[TINT]); + if(l->type == T) + goto error; + if(!isint[l->type->etype]) { + yyerror("non-integer buffer argument to make(%T)", t); + goto error; + } + n->left = l; + } else + n->left = nodintconst(0); + n->op = OMAKECHAN; + break; + } + if(args != nil) { + yyerror("too many arguments to make(%T)", t); + n->op = OMAKE; + goto error; + } + n->type = t; + goto ret; + + case ONEW: + ok |= Erv; + args = n->list; + if(args == nil) { + yyerror("missing argument to new"); + goto error; + } + l = args->n; + typecheck(&l, Etype); + if((t = l->type) == T) + goto error; + if(args->next != nil) { + yyerror("too many arguments to new(%T)", t); + goto error; + } + n->left = l; + n->type = ptrto(t); + goto ret; + + case OPRINT: + case OPRINTN: + ok |= Etop; + typechecklist(n->list, Erv | Eindir); // Eindir: address does not escape + goto ret; + + case OPANIC: + ok |= Etop; + if(onearg(n, "panic") < 0) + goto error; + typecheck(&n->left, Erv); + defaultlit(&n->left, types[TINTER]); + if(n->left->type == T) + goto error; + goto ret; + + case ORECOVER: + ok |= Erv|Etop; + if(n->list != nil) { + yyerror("too many arguments to recover"); + goto error; + } + n->type = types[TINTER]; + goto ret; + + case OCLOSURE: + ok |= Erv; + typecheckclosure(n, top); + if(n->type == T) + goto error; + goto ret; + + /* + * statements + */ + case OAS: + ok |= Etop; + typecheckas(n); + goto ret; + + case OAS2: + ok |= Etop; + typecheckas2(n); + goto ret; + + case OBREAK: + case OCONTINUE: + case ODCL: + case OEMPTY: + case OGOTO: + case OLABEL: + case OXFALL: + ok |= Etop; + goto ret; + + case ODEFER: + ok |= Etop; + typecheck(&n->left, Etop); + goto ret; + + case OPROC: + ok |= Etop; + typecheck(&n->left, Etop|Eproc); + goto ret; + + case OFOR: + ok |= Etop; + typechecklist(n->ninit, Etop); + typecheck(&n->ntest, Erv); + if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL) + yyerror("non-bool %+N used as for condition", n->ntest); + typecheck(&n->nincr, Etop); + typechecklist(n->nbody, Etop); + goto ret; + + case OIF: + ok |= Etop; + typechecklist(n->ninit, Etop); + typecheck(&n->ntest, Erv); + if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL) + yyerror("non-bool %+N used as if condition", n->ntest); + typechecklist(n->nbody, Etop); + typechecklist(n->nelse, Etop); + goto ret; + + case ORETURN: + ok |= Etop; + typechecklist(n->list, Erv | Efnstruct); + if(curfn == N) { + yyerror("return outside function"); + goto error; + } + if(curfn->type->outnamed && n->list == nil) + goto ret; + typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument"); + goto ret; + + case OSELECT: + ok |= Etop; + typecheckselect(n); + goto ret; + + case OSWITCH: + ok |= Etop; + typecheckswitch(n); + goto ret; + + case ORANGE: + ok |= Etop; + typecheckrange(n); + goto ret; + + case OTYPESW: + yyerror("use of .(type) outside type switch"); + goto error; + + case OXCASE: + ok |= Etop; + typechecklist(n->list, Erv); + typechecklist(n->nbody, Etop); + goto ret; + + case ODCLFUNC: + ok |= Etop; + typecheckfunc(n); + goto ret; + + case ODCLCONST: + ok |= Etop; + typecheck(&n->left, Erv); + goto ret; + + case ODCLTYPE: + ok |= Etop; + typecheck(&n->left, Etype); + if(!incannedimport) + checkwidth(n->left->type); + goto ret; + } + +ret: + t = n->type; + if(t && !t->funarg && n->op != OTYPE) { + switch(t->etype) { + case TFUNC: // might have TANY; wait until its called + case TANY: + case TFORW: + case TIDEAL: + case TNIL: + case TBLANK: + break; + default: + checkwidth(t); + } + } + + // TODO(rsc): should not need to check importpkg, + // but reflect mentions unsafe.Pointer. + if(safemode && !incannedimport && !importpkg && t && t->etype == TUNSAFEPTR) + yyerror("cannot use unsafe.Pointer"); + + evconst(n); + if(n->op == OTYPE && !(top & Etype)) { + yyerror("type %T is not an expression", n->type); + goto error; + } + if((top & (Erv|Etype)) == Etype && n->op != OTYPE) { + yyerror("%#N is not a type", n); + goto error; + } + if((ok & Ecall) && !(top & Ecall)) { + yyerror("method %#N is not an expression, must be called", n); + goto error; + } + // TODO(rsc): simplify + if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) { + yyerror("%#N used as value", n); + goto error; + } + if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) { + if(n->diag == 0) { + yyerror("%#N not used", n); + n->diag = 1; + } + goto error; + } + + /* TODO + if(n->type == T) + fatal("typecheck nil type"); + */ + goto out; + +badcall1: + yyerror("invalid argument %#N (type %T) for %#O", n->left, n->left->type, n->op); + goto error; + +error: + n->type = T; + +out: + lineno = lno; + n->typecheck = 1; + *np = n; + return n; +} + +static void +implicitstar(Node **nn) +{ + Type *t; + Node *n; + + // insert implicit * if needed for fixed array + n = *nn; + t = n->type; + if(t == T || !isptr[t->etype]) + return; + t = t->type; + if(t == T) + return; + if(!isfixedarray(t)) + return; + n = nod(OIND, n, N); + typecheck(&n, Erv); + *nn = n; +} + +static int +onearg(Node *n, char *f, ...) +{ + va_list arg; + char *p; + + if(n->left != N) + return 0; + if(n->list == nil) { + va_start(arg, f); + p = vsmprint(f, arg); + va_end(arg); + yyerror("missing argument to %s: %#N", p, n); + return -1; + } + if(n->list->next != nil) { + va_start(arg, f); + p = vsmprint(f, arg); + va_end(arg); + yyerror("too many arguments to %s: %#N", p, n); + n->left = n->list->n; + n->list = nil; + return -1; + } + n->left = n->list->n; + n->list = nil; + return 0; +} + +static int +twoarg(Node *n) +{ + if(n->left != N) + return 0; + if(n->list == nil) { + yyerror("missing argument to %#O - %#N", n->op, n); + return -1; + } + n->left = n->list->n; + if(n->list->next == nil) { + yyerror("missing argument to %#O - %#N", n->op, n); + n->list = nil; + return -1; + } + if(n->list->next->next != nil) { + yyerror("too many arguments to %#O - %#N", n->op, n); + n->list = nil; + return -1; + } + n->right = n->list->next->n; + n->list = nil; + return 0; +} + +static Type* +lookdot1(Sym *s, Type *t, Type *f, int dostrcmp) +{ + Type *r; + + r = T; + for(; f!=T; f=f->down) { + if(dostrcmp && strcmp(f->sym->name, s->name) == 0) + return f; + if(f->sym != s) + continue; + if(r != T) { + yyerror("ambiguous DOT reference %T.%S", t, s); + break; + } + r = f; + } + return r; +} + +static int +looktypedot(Node *n, Type *t, int dostrcmp) +{ + Type *f1, *f2, *tt; + Sym *s; + + s = n->right->sym; + + if(t->etype == TINTER) { + f1 = lookdot1(s, t, t->type, dostrcmp); + if(f1 == T) + return 0; + + if(f1->width == BADWIDTH) + fatal("lookdot badwidth %T %p", f1, f1); + n->right = methodname(n->right, t); + n->xoffset = f1->width; + n->type = f1->type; + n->op = ODOTINTER; + return 1; + } + + tt = t; + if(t->sym == S && isptr[t->etype]) + tt = t->type; + + f2 = methtype(tt); + if(f2 == T) + return 0; + + expandmeth(f2->sym, f2); + f2 = lookdot1(s, f2, f2->xmethod, dostrcmp); + if(f2 == T) + return 0; + + // disallow T.m if m requires *T receiver + if(isptr[getthisx(f2->type)->type->type->etype] + && !isptr[t->etype] + && f2->embedded != 2 + && !isifacemethod(f2->type)) { + yyerror("invalid method expression %#N (needs pointer receiver: (*%T).%s)", n, t, f2->sym->name); + return 0; + } + + n->right = methodname(n->right, t); + n->xoffset = f2->width; + n->type = f2->type; + n->op = ODOTMETH; + return 1; +} + +static int +lookdot(Node *n, Type *t, int dostrcmp) +{ + Type *f1, *f2, *tt, *rcvr; + Sym *s; + + s = n->right->sym; + + dowidth(t); + f1 = T; + if(t->etype == TSTRUCT || t->etype == TINTER) + f1 = lookdot1(s, t, t->type, dostrcmp); + + f2 = T; + if(n->left->type == t || n->left->type->sym == S) { + f2 = methtype(t); + if(f2 != T) { + // Use f2->method, not f2->xmethod: adddot has + // already inserted all the necessary embedded dots. + f2 = lookdot1(s, f2, f2->method, dostrcmp); + } + } + + if(f1 != T) { + if(f2 != T) + yyerror("ambiguous DOT reference %S as both field and method", + n->right->sym); + if(f1->width == BADWIDTH) + fatal("lookdot badwidth %T %p", f1, f1); + n->xoffset = f1->width; + n->type = f1->type; + if(t->etype == TINTER) { + if(isptr[n->left->type->etype]) { + n->left = nod(OIND, n->left, N); // implicitstar + typecheck(&n->left, Erv); + } + n->op = ODOTINTER; + } + return 1; + } + + if(f2 != T) { + tt = n->left->type; + dowidth(tt); + rcvr = getthisx(f2->type)->type->type; + if(!eqtype(rcvr, tt)) { + if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { + checklvalue(n->left, "call pointer method on"); + addrescapes(n->left); + n->left = nod(OADDR, n->left, N); + n->left->implicit = 1; + typecheck(&n->left, Etype|Erv); + } else if(tt->etype == tptr && eqtype(tt->type, rcvr)) { + n->left = nod(OIND, n->left, N); + n->left->implicit = 1; + typecheck(&n->left, Etype|Erv); + } else { + // method is attached to wrong type? + fatal("method mismatch: %T for %T", rcvr, tt); + } + } + n->right = methodname(n->right, n->left->type); + n->xoffset = f2->width; + n->type = f2->type; + n->op = ODOTMETH; + return 1; + } + + return 0; +} + +static int +nokeys(NodeList *l) +{ + for(; l; l=l->next) + if(l->n->op == OKEY) + return 0; + return 1; +} + +/* + * typecheck assignment: type list = expression list + */ +static void +typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc) +{ + Type *t, *tl, *tn; + Node *n; + int lno; + char *why; + + lno = lineno; + + if(tstruct->broke) + goto out; + + if(nl != nil && nl->next == nil && (n = nl->n)->type != T) + if(n->type->etype == TSTRUCT && n->type->funarg) { + tn = n->type->type; + for(tl=tstruct->type; tl; tl=tl->down) { + if(tl->isddd) { + for(; tn; tn=tn->down) { + exportassignok(tn->type, desc); + if(assignop(tn->type, tl->type->type, &why) == 0) { + if(call != N) + yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type->type, call, why); + else + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why); + } + } + goto out; + } + if(tn == T) + goto notenough; + exportassignok(tn->type, desc); + if(assignop(tn->type, tl->type, &why) == 0) { + if(call != N) + yyerror("cannot use %T as type %T in argument to %#N%s", tn->type, tl->type, call, why); + else + yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why); + } + tn = tn->down; + } + if(tn != T) + goto toomany; + goto out; + } + + for(tl=tstruct->type; tl; tl=tl->down) { + t = tl->type; + if(tl->isddd) { + if(isddd) { + if(nl == nil) + goto notenough; + if(nl->next != nil) + goto toomany; + n = nl->n; + setlineno(n); + if(n->type != T) + nl->n = assignconv(n, t, desc); + goto out; + } + for(; nl; nl=nl->next) { + n = nl->n; + setlineno(nl->n); + if(n->type != T) + nl->n = assignconv(n, t->type, desc); + } + goto out; + } + if(nl == nil) + goto notenough; + n = nl->n; + setlineno(n); + if(n->type != T) + nl->n = assignconv(n, t, desc); + nl = nl->next; + } + if(nl != nil) + goto toomany; + if(isddd) { + if(call != N) + yyerror("invalid use of ... in call to %#N", call); + else + yyerror("invalid use of ... in %#O", op); + } + +out: + lineno = lno; + return; + +notenough: + if(call != N) + yyerror("not enough arguments in call to %#N", call); + else + yyerror("not enough arguments to %#O", op); + goto out; + +toomany: + if(call != N) + yyerror("too many arguments in call to %#N", call); + else + yyerror("too many arguments to %#O", op); + goto out; +} + +/* + * do the export rules allow writing to this type? + * cannot be implicitly assigning to any type with + * an unavailable field. + */ +int +exportassignok(Type *t, char *desc) +{ + Type *f; + Sym *s; + + if(t == T) + return 1; + if(t->trecur) + return 1; + t->trecur = 1; + + switch(t->etype) { + default: + // most types can't contain others; they're all fine. + break; + case TSTRUCT: + for(f=t->type; f; f=f->down) { + if(f->etype != TFIELD) + fatal("structas: not field"); + s = f->sym; + // s == nil doesn't happen for embedded fields (they get the type symbol). + // it only happens for fields in a ... struct. + if(s != nil && !exportname(s->name) && s->pkg != localpkg) { + char *prefix; + + prefix = ""; + if(desc != nil) + prefix = " in "; + else + desc = ""; + yyerror("implicit assignment of unexported field '%s' of %T%s%s", s->name, t, prefix, desc); + goto no; + } + if(!exportassignok(f->type, desc)) + goto no; + } + break; + + case TARRAY: + if(t->bound < 0) // slices are pointers; that's fine + break; + if(!exportassignok(t->type, desc)) + goto no; + break; + } + t->trecur = 0; + return 1; + +no: + t->trecur = 0; + return 0; +} + + +/* + * type check composite + */ + +static void +fielddup(Node *n, Node *hash[], ulong nhash) +{ + uint h; + char *s; + Node *a; + + if(n->op != ONAME) + fatal("fielddup: not ONAME"); + s = n->sym->name; + h = stringhash(s)%nhash; + for(a=hash[h]; a!=N; a=a->ntest) { + if(strcmp(a->sym->name, s) == 0) { + yyerror("duplicate field name in struct literal: %s", s); + return; + } + } + n->ntest = hash[h]; + hash[h] = n; +} + +static void +keydup(Node *n, Node *hash[], ulong nhash) +{ + uint h; + ulong b; + double d; + int i; + Node *a; + Node cmp; + char *s; + + evconst(n); + if(n->op != OLITERAL) + return; // we dont check variables + + switch(n->val.ctype) { + default: // unknown, bool, nil + b = 23; + break; + case CTINT: + b = mpgetfix(n->val.u.xval); + break; + case CTFLT: + d = mpgetflt(n->val.u.fval); + s = (char*)&d; + b = 0; + for(i=sizeof(d); i>0; i--) + b = b*PRIME1 + *s++; + break; + case CTSTR: + b = 0; + s = n->val.u.sval->s; + for(i=n->val.u.sval->len; i>0; i--) + b = b*PRIME1 + *s++; + break; + } + + h = b%nhash; + memset(&cmp, 0, sizeof(cmp)); + for(a=hash[h]; a!=N; a=a->ntest) { + cmp.op = OEQ; + cmp.left = n; + cmp.right = a; + evconst(&cmp); + b = cmp.val.u.bval; + if(b) { + // too lazy to print the literal + yyerror("duplicate key in map literal"); + return; + } + } + n->ntest = hash[h]; + hash[h] = n; +} + +static void +indexdup(Node *n, Node *hash[], ulong nhash) +{ + uint h; + Node *a; + ulong b, c; + + if(n->op != OLITERAL) + fatal("indexdup: not OLITERAL"); + + b = mpgetfix(n->val.u.xval); + h = b%nhash; + for(a=hash[h]; a!=N; a=a->ntest) { + c = mpgetfix(a->val.u.xval); + if(b == c) { + yyerror("duplicate index in array literal: %ld", b); + return; + } + } + n->ntest = hash[h]; + hash[h] = n; +} + +static int +prime(ulong h, ulong sr) +{ + ulong n; + + for(n=3; n<=sr; n+=2) + if(h%n == 0) + return 0; + return 1; +} + +static ulong +inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash) +{ + ulong h, sr; + NodeList *ll; + int i; + + // count the number of entries + h = 0; + for(ll=n->list; ll; ll=ll->next) + h++; + + // if the auto hash table is + // large enough use it. + if(h <= nautohash) { + *hash = autohash; + memset(*hash, 0, nautohash * sizeof(**hash)); + return nautohash; + } + + // make hash size odd and 12% larger than entries + h += h/8; + h |= 1; + + // calculate sqrt of h + sr = h/2; + for(i=0; i<5; i++) + sr = (sr + h/sr)/2; + + // check for primeality + while(!prime(h, sr)) + h += 2; + + // build and return a throw-away hash table + *hash = mal(h * sizeof(**hash)); + memset(*hash, 0, h * sizeof(**hash)); + return h; +} + +static void +typecheckcomplit(Node **np) +{ + int bad, i, len, nerr; + Node *l, *n, **hash; + NodeList *ll; + Type *t, *f, *pushtype; + Sym *s; + int32 lno; + ulong nhash; + Node *autohash[101]; + + n = *np; + lno = lineno; + + if(n->right == N) { + if(n->list != nil) + setlineno(n->list->n); + yyerror("missing type in composite literal"); + goto error; + } + + setlineno(n->right); + l = typecheck(&n->right /* sic */, Etype|Ecomplit); + if((t = l->type) == T) + goto error; + nerr = nerrors; + + // can omit type on composite literal values if the outer + // composite literal is array, slice, or map, and the + // element type is itself a struct, array, slice, or map. + pushtype = T; + if(t->etype == TARRAY || t->etype == TMAP) { + pushtype = t->type; + if(pushtype != T) { + switch(pushtype->etype) { + case TSTRUCT: + case TARRAY: + case TMAP: + break; + default: + pushtype = T; + break; + } + } + } + + switch(t->etype) { + default: + yyerror("invalid type for composite literal: %T", t); + n->type = T; + break; + + case TARRAY: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + + len = 0; + i = 0; + for(ll=n->list; ll; ll=ll->next) { + l = ll->n; + setlineno(l); + if(l->op != OKEY) { + l = nod(OKEY, nodintconst(i), l); + l->left->type = types[TINT]; + l->left->typecheck = 1; + ll->n = l; + } + + typecheck(&l->left, Erv); + evconst(l->left); + i = nonnegconst(l->left); + if(i < 0) { + yyerror("array index must be non-negative integer constant"); + i = -(1<<30); // stay negative for a while + } + if(i >= 0) + indexdup(l->left, hash, nhash); + i++; + if(i > len) { + len = i; + if(t->bound >= 0 && len > t->bound) { + setlineno(l); + yyerror("array index %d out of bounds [0:%d]", len, t->bound); + t->bound = -1; // no more errors + } + } + + if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T) + l->right->right = typenod(pushtype); + typecheck(&l->right, Erv); + defaultlit(&l->right, t->type); + l->right = assignconv(l->right, t->type, "array element"); + } + if(t->bound == -100) + t->bound = len; + if(t->bound < 0) + n->right = nodintconst(len); + n->op = OARRAYLIT; + break; + + case TMAP: + nhash = inithash(n, &hash, autohash, nelem(autohash)); + + for(ll=n->list; ll; ll=ll->next) { + l = ll->n; + setlineno(l); + if(l->op != OKEY) { + typecheck(&ll->n, Erv); + yyerror("missing key in map literal"); + continue; + } + + typecheck(&l->left, Erv); + defaultlit(&l->left, t->down); + l->left = assignconv(l->left, t->down, "map key"); + keydup(l->left, hash, nhash); + + if(l->right->op == OCOMPLIT && l->right->right == N && pushtype != T) + l->right->right = typenod(pushtype); + typecheck(&l->right, Erv); + defaultlit(&l->right, t->type); + l->right = assignconv(l->right, t->type, "map value"); + } + n->op = OMAPLIT; + break; + + case TSTRUCT: + bad = 0; + if(n->list != nil && nokeys(n->list)) { + // simple list of variables + f = t->type; + for(ll=n->list; ll; ll=ll->next) { + setlineno(ll->n); + typecheck(&ll->n, Erv); + if(f == nil) { + if(!bad++) + yyerror("too many values in struct initializer"); + continue; + } + s = f->sym; + if(s != nil && !exportname(s->name) && s->pkg != localpkg) + yyerror("implicit assignment of unexported field '%s' in %T literal", s->name, t); + ll->n = assignconv(ll->n, f->type, "field value"); + ll->n = nod(OKEY, newname(f->sym), ll->n); + ll->n->left->typecheck = 1; + f = f->down; + } + if(f != nil) + yyerror("too few values in struct initializer"); + } else { + nhash = inithash(n, &hash, autohash, nelem(autohash)); + + // keyed list + for(ll=n->list; ll; ll=ll->next) { + l = ll->n; + setlineno(l); + if(l->op != OKEY) { + if(!bad++) + yyerror("mixture of field:value and value initializers"); + typecheck(&ll->n, Erv); + continue; + } + s = l->left->sym; + if(s == S) { + yyerror("invalid field name %#N in struct initializer", l->left); + typecheck(&l->right, Erv); + continue; + } + // Sym might have resolved to name in other top-level + // package, because of import dot. Redirect to correct sym + // before we do the lookup. + if(s->pkg != localpkg) + s = lookup(s->name); + l->left = newname(s); + l->left->typecheck = 1; + f = lookdot1(s, t, t->type, 0); + typecheck(&l->right, Erv); + if(f == nil) { + yyerror("unknown %T field '%s' in struct literal", t, s->name); + continue; + } + s = f->sym; + fielddup(newname(s), hash, nhash); + l->right = assignconv(l->right, f->type, "field value"); + } + } + n->op = OSTRUCTLIT; + break; + } + if(nerr != nerrors) + goto error; + n->type = t; + + *np = n; + lineno = lno; + return; + +error: + n->type = T; + *np = n; + lineno = lno; +} + +/* + * the address of n has been taken and might be used after + * the current function returns. mark any local vars + * as needing to move to the heap. + */ +static void +addrescapes(Node *n) +{ + char buf[100]; + switch(n->op) { + default: + // probably a type error already. + // dump("addrescapes", n); + break; + + case ONAME: + if(n->noescape) + break; + switch(n->class) { + case PPARAMREF: + addrescapes(n->defn); + break; + case PPARAM: + case PPARAMOUT: + // if func param, need separate temporary + // to hold heap pointer. + // the function type has already been checked + // (we're in the function body) + // so the param already has a valid xoffset. + + // expression to refer to stack copy + n->stackparam = nod(OPARAM, n, N); + n->stackparam->type = n->type; + n->stackparam->addable = 1; + if(n->xoffset == BADWIDTH) + fatal("addrescapes before param assignment"); + n->stackparam->xoffset = n->xoffset; + n->xoffset = 0; + // fallthrough + case PAUTO: + + n->class |= PHEAP; + n->addable = 0; + n->ullman = 2; + n->xoffset = 0; + + // create stack variable to hold pointer to heap + n->heapaddr = nod(ONAME, N, N); + n->heapaddr->type = ptrto(n->type); + snprint(buf, sizeof buf, "&%S", n->sym); + n->heapaddr->sym = lookup(buf); + n->heapaddr->class = PHEAP-1; // defer tempname to allocparams + n->heapaddr->ullman = 1; + n->curfn->dcl = list(n->curfn->dcl, n->heapaddr); + + break; + } + break; + + case OIND: + case ODOTPTR: + break; + + case ODOT: + case OINDEX: + // ODOTPTR has already been introduced, + // so these are the non-pointer ODOT and OINDEX. + // In &x[0], if x is a slice, then x does not + // escape--the pointer inside x does, but that + // is always a heap pointer anyway. + if(!isslice(n->left->type)) + addrescapes(n->left); + break; + } +} + +/* + * lvalue etc + */ +int +islvalue(Node *n) +{ + switch(n->op) { + case OINDEX: + if(isfixedarray(n->left->type)) + return islvalue(n->left); + if(n->left->type != T && n->left->type->etype == TSTRING) + return 0; + // fall through + case OIND: + case ODOTPTR: + return 1; + case ODOT: + return islvalue(n->left); + case ONAME: + if(n->class == PFUNC) + return 0; + return 1; + } + return 0; +} + +static void +checklvalue(Node *n, char *verb) +{ + if(!islvalue(n)) + yyerror("cannot %s %#N", verb, n); +} + +static void +checkassign(Node *n) +{ + if(islvalue(n)) + return; + if(n->op == OINDEXMAP) { + n->etype = 1; + return; + } + yyerror("cannot assign to %#N", n); +} + +static void +checkassignlist(NodeList *l) +{ + for(; l; l=l->next) + checkassign(l->n); +} + +/* + * type check assignment. + * if this assignment is the definition of a var on the left side, + * fill in the var's type. + */ + +static void +typecheckas(Node *n) +{ + // delicate little dance. + // the definition of n may refer to this assignment + // as its definition, in which case it will call typecheckas. + // in that case, do not call typecheck back, or it will cycle. + // if the variable has a type (ntype) then typechecking + // will not look at defn, so it is okay (and desirable, + // so that the conversion below happens). + n->left = resolve(n->left); + if(n->left->defn != n || n->left->ntype) + typecheck(&n->left, Erv | Easgn); + + checkassign(n->left); + typecheck(&n->right, Erv); + if(n->right && n->right->type != T) { + if(n->left->type != T) + n->right = assignconv(n->right, n->left->type, "assignment"); + else if(!isblank(n->left)) + exportassignok(n->right->type, "assignment"); + } + if(n->left->defn == n && n->left->ntype == N) { + defaultlit(&n->right, T); + n->left->type = n->right->type; + } + + // second half of dance. + // now that right is done, typecheck the left + // just to get it over with. see dance above. + n->typecheck = 1; + if(n->left->typecheck == 0) + typecheck(&n->left, Erv | Easgn); +} + +static void +checkassignto(Type *src, Node *dst) +{ + char *why; + + if(assignop(src, dst->type, &why) == 0) { + yyerror("cannot assign %T to %+N in multiple assignment%s", src, dst, why); + return; + } + exportassignok(dst->type, "multiple assignment"); +} + +static void +typecheckas2(Node *n) +{ + int cl, cr; + NodeList *ll, *lr; + Node *l, *r; + Iter s; + Type *t; + + for(ll=n->list; ll; ll=ll->next) { + // delicate little dance. + ll->n = resolve(ll->n); + if(ll->n->defn != n || ll->n->ntype) + typecheck(&ll->n, Erv | Easgn); + } + cl = count(n->list); + cr = count(n->rlist); + checkassignlist(n->list); + if(cl > 1 && cr == 1) + typecheck(&n->rlist->n, Erv | Efnstruct); + else + typechecklist(n->rlist, Erv); + + if(cl == cr) { + // easy + for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) { + if(ll->n->type != T && lr->n->type != T) + lr->n = assignconv(lr->n, ll->n->type, "assignment"); + if(ll->n->defn == n && ll->n->ntype == N) { + defaultlit(&lr->n, T); + ll->n->type = lr->n->type; + } + } + goto out; + } + + + l = n->list->n; + r = n->rlist->n; + + // m[i] = x, ok + if(cl == 1 && cr == 2 && l->op == OINDEXMAP) { + if(l->type == T) + goto out; + n->op = OAS2MAPW; + n->rlist->n = assignconv(r, l->type, "assignment"); + r = n->rlist->next->n; + n->rlist->next->n = assignconv(r, types[TBOOL], "assignment"); + goto out; + } + + // x,y,z = f() + if(cr == 1) { + if(r->type == T) + goto out; + switch(r->op) { + case OCALLMETH: + case OCALLINTER: + case OCALLFUNC: + if(r->type->etype != TSTRUCT || r->type->funarg == 0) + break; + cr = structcount(r->type); + if(cr != cl) + goto mismatch; + n->op = OAS2FUNC; + t = structfirst(&s, &r->type); + for(ll=n->list; ll; ll=ll->next) { + if(ll->n->type != T) + checkassignto(t->type, ll->n); + if(ll->n->defn == n && ll->n->ntype == N) + ll->n->type = t->type; + t = structnext(&s); + } + goto out; + } + } + + // x, ok = y + if(cl == 2 && cr == 1) { + if(r->type == T) + goto out; + switch(r->op) { + case OINDEXMAP: + n->op = OAS2MAPR; + goto common; + case ORECV: + n->op = OAS2RECV; + n->right = n->rlist->n; + goto common; + case ODOTTYPE: + n->op = OAS2DOTTYPE; + r->op = ODOTTYPE2; + common: + if(l->type != T) + checkassignto(r->type, l); + if(l->defn == n) + l->type = r->type; + l = n->list->next->n; + if(l->type != T) + checkassignto(types[TBOOL], l); + if(l->defn == n && l->ntype == N) + l->type = types[TBOOL]; + goto out; + } + } + +mismatch: + yyerror("assignment count mismatch: %d = %d", cl, cr); + +out: + // second half of dance + n->typecheck = 1; + for(ll=n->list; ll; ll=ll->next) + if(ll->n->typecheck == 0) + typecheck(&ll->n, Erv | Easgn); +} + +/* + * type check function definition + */ +static void +typecheckfunc(Node *n) +{ + Type *t, *rcvr; + +//dump("nname", n->nname); + typecheck(&n->nname, Erv | Easgn); + if((t = n->nname->type) == T) + return; + n->type = t; + + rcvr = getthisx(t)->type; + if(rcvr != nil && n->shortname != N && !isblank(n->shortname)) + addmethod(n->shortname->sym, t, 1); +} + +static void +stringtoarraylit(Node **np) +{ + int32 i; + NodeList *l; + Strlit *s; + char *p, *ep; + Rune r; + Node *nn, *n; + + n = *np; + if(n->left->op != OLITERAL || n->left->val.ctype != CTSTR) + fatal("stringtoarraylit %N", n); + + s = n->left->val.u.sval; + l = nil; + p = s->s; + ep = s->s + s->len; + i = 0; + if(n->type->type->etype == TUINT8) { + // raw []byte + while(p < ep) + l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++))); + } else { + // utf-8 []int + while(p < ep) { + p += chartorune(&r, p); + l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r))); + } + } + nn = nod(OCOMPLIT, N, typenod(n->type)); + nn->list = l; + typecheck(&nn, Erv); + *np = nn; +} + +static Type* +getforwtype(Node *n) +{ + Node *f1, *f2; + + for(f1=f2=n; ; n=n->ntype) { + if((n = resolve(n)) == N || n->op != OTYPE) + return T; + + if(n->type != T && n->type->etype == TFORW) + return n->type; + + // Check for ntype cycle. + if((f2 = resolve(f2)) != N && (f1 = resolve(f2->ntype)) != N) { + f2 = resolve(f1->ntype); + if(f1 == n || f2 == n) + return T; + } + } +} + +static int ntypecheckdeftype; +static NodeList *methodqueue; + +static void +domethod(Node *n) +{ + Node *nt; + + nt = n->type->nname; + typecheck(&nt, Etype); + if(nt->type == T) { + // type check failed; leave empty func + n->type->etype = TFUNC; + n->type->nod = N; + return; + } + *n->type = *nt->type; + n->type->nod = N; + checkwidth(n->type); +} + +typedef struct NodeTypeList NodeTypeList; +struct NodeTypeList { + Node *n; + Type *t; + NodeTypeList *next; +}; + +static NodeTypeList *dntq; +static NodeTypeList *dntend; + +void +defertypecopy(Node *n, Type *t) +{ + NodeTypeList *ntl; + + if(n == N || t == T) + return; + + ntl = mal(sizeof *ntl); + ntl->n = n; + ntl->t = t; + ntl->next = nil; + + if(dntq == nil) + dntq = ntl; + else + dntend->next = ntl; + + dntend = ntl; +} + +void +resumetypecopy(void) +{ + NodeTypeList *l; + + for(l=dntq; l; l=l->next) + copytype(l->n, l->t); +} + +void +copytype(Node *n, Type *t) +{ + *n->type = *t; + + t = n->type; + t->sym = n->sym; + t->local = n->local; + t->vargen = n->vargen; + t->siggen = 0; + t->method = nil; + t->nod = N; + t->printed = 0; + t->deferwidth = 0; +} + +static void +typecheckdeftype(Node *n) +{ + int maplineno, embedlineno, lno; + Type *t; + NodeList *l; + + ntypecheckdeftype++; + lno = lineno; + setlineno(n); + n->type->sym = n->sym; + n->typecheck = 1; + typecheck(&n->ntype, Etype); + if((t = n->ntype->type) == T) { + n->diag = 1; + goto ret; + } + if(n->type == T) { + n->diag = 1; + goto ret; + } + + maplineno = n->type->maplineno; + embedlineno = n->type->embedlineno; + + // copy new type and clear fields + // that don't come along. + // anything zeroed here must be zeroed in + // typedcl2 too. + copytype(n, t); + + // double-check use of type as map key. + if(maplineno) { + lineno = maplineno; + maptype(n->type, types[TBOOL]); + } + if(embedlineno) { + lineno = embedlineno; + if(isptr[t->etype]) + yyerror("embedded type cannot be a pointer"); + } + +ret: + lineno = lno; + + // if there are no type definitions going on, it's safe to + // try to resolve the method types for the interfaces + // we just read. + if(ntypecheckdeftype == 1) { + while((l = methodqueue) != nil) { + methodqueue = nil; + for(; l; l=l->next) + domethod(l->n); + } + } + ntypecheckdeftype--; +} + +void +queuemethod(Node *n) +{ + if(ntypecheckdeftype == 0) { + domethod(n); + return; + } + methodqueue = list(methodqueue, n); +} + +Node* +typecheckdef(Node *n) +{ + int lno; + Node *e; + Type *t; + NodeList *l; + + lno = lineno; + setlineno(n); + + if(n->op == ONONAME) { + if(!n->diag) { + n->diag = 1; + if(n->lineno != 0) + lineno = n->lineno; + yyerror("undefined: %S", n->sym); + } + return n; + } + + if(n->walkdef == 1) + return n; + + l = mal(sizeof *l); + l->n = n; + l->next = typecheckdefstack; + typecheckdefstack = l; + + if(n->walkdef == 2) { + flusherrors(); + print("typecheckdef loop:"); + for(l=typecheckdefstack; l; l=l->next) + print(" %S", l->n->sym); + print("\n"); + fatal("typecheckdef loop"); + } + n->walkdef = 2; + + if(n->type != T || n->sym == S) // builtin or no name + goto ret; + + switch(n->op) { + default: + fatal("typecheckdef %O", n->op); + + case OGOTO: + case OLABEL: + // not really syms + break; + + case OLITERAL: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + n->ntype = N; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + e = n->defn; + n->defn = N; + if(e == N) { + lineno = n->lineno; + dump("typecheckdef nil defn", n); + yyerror("xxx"); + } + typecheck(&e, Erv | Eiota); + if(e->type != T && e->op != OLITERAL) { + yyerror("const initializer must be constant"); + goto ret; + } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } + t = n->type; + if(t != T) { + if(!okforconst[t->etype]) { + yyerror("invalid constant type %T", t); + goto ret; + } + if(!isideal(e->type) && !eqtype(t, e->type)) { + yyerror("cannot use %+N as type %T in const initializer", e, t); + goto ret; + } + convlit(&e, t); + } + n->val = e->val; + n->type = e->type; + break; + + case ONAME: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + if(n->type != T) + break; + if(n->defn == N) { + if(n->etype != 0) // like OPRINTN + break; + if(nsavederrors+nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } + fatal("var without type, init: %S", n->sym); + } + if(n->defn->op == ONAME) { + typecheck(&n->defn, Erv); + n->type = n->defn->type; + break; + } + typecheck(&n->defn, Etop); // fills in n->type + break; + + case OTYPE: + if(curfn) + defercheckwidth(); + n->walkdef = 1; + n->type = typ(TFORW); + n->type->sym = n->sym; + typecheckdeftype(n); + if(curfn) + resumecheckwidth(); + break; + + case OPACK: + // nothing to see here + break; + } + +ret: + if(typecheckdefstack->n != n) + fatal("typecheckdefstack mismatch"); + l = typecheckdefstack; + typecheckdefstack = l->next; + + lineno = lno; + n->walkdef = 1; + return n; +} diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c new file mode 100644 index 000000000..d304077c8 --- /dev/null +++ b/src/cmd/gc/unsafe.c @@ -0,0 +1,97 @@ +// 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" + +/* + * look for + * unsafe.Sizeof + * unsafe.Offsetof + * rewrite with a constant + */ +Node* +unsafenmagic(Node *nn) +{ + Node *r, *n; + Sym *s; + Type *t, *tr; + long v; + Val val; + Node *fn; + NodeList *args; + + fn = nn->left; + args = nn->list; + + if(safemode || fn == N || fn->op != ONAME || (s = fn->sym) == S) + goto no; + if(s->pkg != unsafepkg) + goto no; + + if(args == nil) { + yyerror("missing argument for %S", s); + goto no; + } + r = args->n; + + if(strcmp(s->name, "Sizeof") == 0) { + typecheck(&r, Erv); + defaultlit(&r, T); + tr = r->type; + if(tr == T) + goto bad; + dowidth(tr); + v = tr->width; + goto yes; + } + if(strcmp(s->name, "Offsetof") == 0) { + typecheck(&r, Erv); + if(r->op != ODOT && r->op != ODOTPTR) + goto bad; + typecheck(&r, Erv); + v = r->xoffset; + goto yes; + } + if(strcmp(s->name, "Alignof") == 0) { + typecheck(&r, Erv); + defaultlit(&r, T); + tr = r->type; + if(tr == T) + goto bad; + + // make struct { byte; T; } + t = typ(TSTRUCT); + t->type = typ(TFIELD); + t->type->type = types[TUINT8]; + t->type->down = typ(TFIELD); + t->type->down->type = tr; + // compute struct widths + dowidth(t); + + // the offset of T is its required alignment + v = t->type->down->width; + goto yes; + } + +no: + return N; + +bad: + yyerror("invalid expression %#N", nn); + v = 0; + goto ret; + +yes: + if(args->next != nil) + yyerror("extra arguments for %S", s); +ret: + // any side effects disappear; ignore init + val.ctype = CTINT; + val.u.xval = mal(sizeof(*n->val.u.xval)); + mpmovecfix(val.u.xval, v); + n = nod(OLITERAL, N, N); + n->val = val; + n->type = types[TUINTPTR]; + return n; +} diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go new file mode 100644 index 000000000..db27d7425 --- /dev/null +++ b/src/cmd/gc/unsafe.go @@ -0,0 +1,22 @@ +// 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. + +// NOTE: If you change this file you must run "./mkbuiltin" +// to update builtin.c.boot. This is not done automatically +// to avoid depending on having a working compiler binary. + +package PACKAGE + +type Pointer uintptr // not really; filled in by compiler + +// return types here are ignored; see unsafe.c +func Offsetof(any) uintptr +func Sizeof(any) uintptr +func Alignof(any) uintptr + +func Typeof(i interface{}) (typ interface{}) +func Reflect(i interface{}) (typ interface{}, addr Pointer) +func Unreflect(typ interface{}, addr Pointer) (ret interface{}) +func New(typ interface{}) Pointer +func NewArray(typ interface{}, n int) Pointer diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c new file mode 100644 index 000000000..9cd4ee919 --- /dev/null +++ b/src/cmd/gc/walk.c @@ -0,0 +1,2166 @@ +// 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" + +static Node* walkprint(Node*, NodeList**, int); +static Node* conv(Node*, Type*); +static Node* mapfn(char*, Type*); +static Node* makenewvar(Type*, NodeList**, Node**); +static Node* ascompatee1(int, Node*, Node*, NodeList**); +static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); +static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); +static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**); +static Node* convas(Node*, NodeList**); +static void heapmoves(void); +static NodeList* paramstoheap(Type **argin, int out); +static NodeList* reorder1(NodeList*); +static NodeList* reorder3(NodeList*); +static Node* addstr(Node*, NodeList**); +static Node* appendslice(Node*, NodeList**); +static Node* append(Node*, NodeList**); + +// can this code branch reach the end +// without an unconditional RETURN +// this is hard, so it is conservative +static int +walkret(NodeList *l) +{ + Node *n; + +loop: + while(l && l->next) + l = l->next; + if(l == nil) + return 1; + + // at this point, we have the last + // statement of the function + n = l->n; + switch(n->op) { + case OBLOCK: + l = n->list; + goto loop; + + case OGOTO: + case ORETURN: + case OPANIC: + return 0; + break; + } + + // all other statements + // will flow to the end + return 1; +} + +void +walk(Node *fn) +{ + char s[50]; + NodeList *l; + Node *n; + int lno; + + curfn = fn; + + if(debug['W']) { + snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym); + dumplist(s, curfn->nbody); + } + if(curfn->type->outtuple) + if(walkret(curfn->nbody)) + yyerror("function ends without a return statement"); + + lno = lineno; + for(l=fn->dcl; l; l=l->next) { + n = l->n; + if(n->op != ONAME || n->class != PAUTO) + continue; + lineno = n->lineno; + typecheck(&n, Erv | Easgn); // only needed for unused variables + if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors) + yyerror("%S declared and not used", n->sym); + } + lineno = lno; + if(nerrors != 0) + return; + walkstmtlist(curfn->nbody); + if(debug['W']) { + snprint(s, sizeof(s), "after walk %S", curfn->nname->sym); + dumplist(s, curfn->nbody); + } + heapmoves(); + if(debug['W'] && curfn->enter != nil) { + snprint(s, sizeof(s), "enter %S", curfn->nname->sym); + dumplist(s, curfn->enter); + } +} + + +void +walkstmtlist(NodeList *l) +{ + for(; l; l=l->next) + walkstmt(&l->n); +} + +static int +samelist(NodeList *a, NodeList *b) +{ + for(; a && b; a=a->next, b=b->next) + if(a->n != b->n) + return 0; + return a == b; +} + +static int +paramoutheap(Node *fn) +{ + NodeList *l; + + for(l=fn->dcl; l; l=l->next) { + switch(l->n->class) { + case PPARAMOUT|PHEAP: + return 1; + case PAUTO: + case PAUTO|PHEAP: + // stop early - parameters are over + return 0; + } + } + return 0; +} + +void +walkstmt(Node **np) +{ + NodeList *init; + NodeList *ll, *rl; + int cl; + Node *n, *f; + + n = *np; + if(n == N) + return; + + setlineno(n); + + switch(n->op) { + default: + if(n->op == ONAME) + yyerror("%S is not a top level statement", n->sym); + else + yyerror("%O is not a top level statement", n->op); + dump("nottop", n); + break; + + case OASOP: + case OAS: + case OAS2: + case OAS2DOTTYPE: + case OAS2RECV: + case OAS2FUNC: + case OAS2MAPW: + case OAS2MAPR: + case OCLOSE: + case OCOPY: + case OCALLMETH: + case OCALLINTER: + case OCALL: + case OCALLFUNC: + case OSEND: + case ORECV: + case OPRINT: + case OPRINTN: + case OPANIC: + case OEMPTY: + case ORECOVER: + if(n->typecheck == 0) { + dump("missing typecheck:", n); + fatal("missing typecheck"); + } + init = n->ninit; + n->ninit = nil; + walkexpr(&n, &init); + n->ninit = concat(init, n->ninit); + break; + + case OBREAK: + case ODCL: + case OCONTINUE: + case OFALL: + case OGOTO: + case OLABEL: + case ODCLCONST: + case ODCLTYPE: + break; + + case OBLOCK: + walkstmtlist(n->list); + break; + + case OXCASE: + yyerror("case statement out of place"); + n->op = OCASE; + case OCASE: + walkstmt(&n->right); + break; + + case ODEFER: + hasdefer = 1; + switch(n->left->op) { + case OPRINT: + case OPRINTN: + walkexprlist(n->left->list, &n->ninit); + n->left = walkprint(n->left, &n->ninit, 1); + break; + default: + walkexpr(&n->left, &n->ninit); + break; + } + break; + + case OFOR: + walkstmtlist(n->ninit); + if(n->ntest != N) { + walkstmtlist(n->ntest->ninit); + init = n->ntest->ninit; + n->ntest->ninit = nil; + walkexpr(&n->ntest, &init); + n->ntest->ninit = concat(init, n->ntest->ninit); + } + walkstmt(&n->nincr); + walkstmtlist(n->nbody); + break; + + case OIF: + walkstmtlist(n->ninit); + walkexpr(&n->ntest, &n->ninit); + walkstmtlist(n->nbody); + walkstmtlist(n->nelse); + break; + + case OPROC: + switch(n->left->op) { + case OPRINT: + case OPRINTN: + walkexprlist(n->left->list, &n->ninit); + n->left = walkprint(n->left, &n->ninit, 1); + break; + default: + walkexpr(&n->left, &n->ninit); + break; + } + break; + + case ORETURN: + walkexprlist(n->list, &n->ninit); + if(n->list == nil) + break; + if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) { + // assign to the function out parameters, + // so that reorder3 can fix up conflicts + rl = nil; + for(ll=curfn->dcl; ll != nil; ll=ll->next) { + cl = ll->n->class & ~PHEAP; + if(cl == PAUTO) + break; + if(cl == PPARAMOUT) + rl = list(rl, ll->n); + } + if(samelist(rl, n->list)) { + // special return in disguise + n->list = nil; + break; + } + if(count(n->list) == 1 && count(rl) > 1) { + // OAS2FUNC in disguise + f = n->list->n; + if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER) + fatal("expected return of call, have %#N", f); + n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); + break; + } + ll = ascompatee(n->op, rl, n->list, &n->ninit); + n->list = reorder3(ll); + break; + } + ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); + n->list = ll; + break; + + case OSELECT: + walkselect(n); + break; + + case OSWITCH: + walkswitch(n); + break; + + case ORANGE: + walkrange(n); + break; + + case OXFALL: + yyerror("fallthrough statement out of place"); + n->op = OFALL; + break; + } + + *np = n; +} + + +/* + * walk the whole tree of the body of an + * expression or simple statement. + * the types expressions are calculated. + * compile-time constants are evaluated. + * complex side effects like statements are appended to init + */ + +void +walkexprlist(NodeList *l, NodeList **init) +{ + for(; l; l=l->next) + walkexpr(&l->n, init); +} + +void +walkexprlistsafe(NodeList *l, NodeList **init) +{ + for(; l; l=l->next) { + l->n = safeexpr(l->n, init); + walkexpr(&l->n, init); + } +} + +void +walkexpr(Node **np, NodeList **init) +{ + Node *r, *l, *var, *a; + NodeList *ll, *lr, *lpost; + Type *t; + int et; + int64 v, v1, v2, len; + int32 lno; + Node *n, *fn; + char buf[100], *p; + + n = *np; + + if(n == N) + return; + + if(init == &n->ninit) { + // not okay to use n->ninit when walking n, + // because we might replace n with some other node + // and would lose the init list. + fatal("walkexpr init == &n->ninit"); + } + + // annoying case - not typechecked + if(n->op == OKEY) { + walkexpr(&n->left, init); + walkexpr(&n->right, init); + return; + } + + lno = setlineno(n); + + if(debug['w'] > 1) + dump("walk-before", n); + + if(n->typecheck != 1) { + dump("missed typecheck", n); + fatal("missed typecheck"); + } + + t = T; + et = Txxx; + + switch(n->op) { + default: + dump("walk", n); + fatal("walkexpr: switch 1 unknown op %N", n); + goto ret; + + case OTYPE: + case ONONAME: + case OINDREG: + case OEMPTY: + goto ret; + + case ONOT: + case OMINUS: + case OPLUS: + case OCOM: + case OREAL: + case OIMAG: + case ODOT: + case ODOTPTR: + case ODOTMETH: + case ODOTINTER: + case OIND: + walkexpr(&n->left, init); + goto ret; + + case OLEN: + case OCAP: + walkexpr(&n->left, init); + + // replace len(*[10]int) with 10. + // delayed until now to preserve side effects. + t = n->left->type; + if(isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) { + safeexpr(n->left, init); + nodconst(n, n->type, t->bound); + n->typecheck = 1; + } + goto ret; + + case OLSH: + case ORSH: + case OAND: + case OOR: + case OXOR: + case OSUB: + case OMUL: + case OEQ: + case ONE: + case OLT: + case OLE: + case OGE: + case OGT: + case OADD: + case OCOMPLEX: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + goto ret; + + case OANDAND: + case OOROR: + walkexpr(&n->left, init); + // cannot put side effects from n->right on init, + // because they cannot run before n->left is checked. + // save elsewhere and store on the eventual n->right. + ll = nil; + walkexpr(&n->right, &ll); + n->right->ninit = concat(n->right->ninit, ll); + goto ret; + + case OPRINT: + case OPRINTN: + walkexprlist(n->list, init); + n = walkprint(n, init, 0); + goto ret; + + case OPANIC: + n = mkcall("panic", T, init, n->left); + goto ret; + + case ORECOVER: + n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N)); + goto ret; + + case OLITERAL: + n->addable = 1; + goto ret; + + case ONAME: + if(!(n->class & PHEAP) && n->class != PPARAMREF) + n->addable = 1; + goto ret; + + case OCALLINTER: + t = n->left->type; + if(n->list && n->list->n->op == OAS) + goto ret; + walkexpr(&n->left, init); + walkexprlist(n->list, init); + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + n->list = reorder1(ll); + goto ret; + + case OCALLFUNC: + t = n->left->type; + if(n->list && n->list->n->op == OAS) + goto ret; + + if(n->left->op == OCLOSURE) { + walkcallclosure(n, init); + t = n->left->type; + } + + walkexpr(&n->left, init); + walkexprlist(n->list, init); + + ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + n->list = reorder1(ll); + goto ret; + + case OCALLMETH: + t = n->left->type; + if(n->list && n->list->n->op == OAS) + goto ret; + walkexpr(&n->left, init); + walkexprlist(n->list, init); + ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init); + lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + ll = concat(ll, lr); + n->left->left = N; + ullmancalc(n->left); + n->list = reorder1(ll); + goto ret; + + case OAS: + *init = concat(*init, n->ninit); + n->ninit = nil; + walkexpr(&n->left, init); + n->left = safeexpr(n->left, init); + + if(oaslit(n, init)) + goto ret; + + walkexpr(&n->right, init); + if(n->left != N && n->right != N) { + r = convas(nod(OAS, n->left, n->right), init); + r->dodata = n->dodata; + n = r; + } + + goto ret; + + case OAS2: + *init = concat(*init, n->ninit); + n->ninit = nil; + walkexprlistsafe(n->list, init); + walkexprlistsafe(n->rlist, init); + ll = ascompatee(OAS, n->list, n->rlist, init); + ll = reorder3(ll); + n = liststmt(ll); + goto ret; + + case OAS2FUNC: + as2func: + // a,b,... = fn() + *init = concat(*init, n->ninit); + n->ninit = nil; + r = n->rlist->n; + walkexprlistsafe(n->list, init); + walkexpr(&r, init); + l = n->list->n; + + // all the really hard stuff - explicit function calls and so on - + // is gone, but map assignments remain. + // if there are map assignments here, assign via + // temporaries, because ascompatet assumes + // the targets can be addressed without function calls + // and map index has an implicit one. + lpost = nil; + if(l->op == OINDEXMAP) { + var = nod(OXXX, N, N); + tempname(var, l->type); + n->list->n = var; + a = nod(OAS, l, var); + typecheck(&a, Etop); + lpost = list(lpost, a); + } + l = n->list->next->n; + if(l->op == OINDEXMAP) { + var = nod(OXXX, N, N); + tempname(var, l->type); + n->list->next->n = var; + a = nod(OAS, l, var); + typecheck(&a, Etop); + lpost = list(lpost, a); + } + ll = ascompatet(n->op, n->list, &r->type, 0, init); + walkexprlist(lpost, init); + n = liststmt(concat(concat(list1(r), ll), lpost)); + goto ret; + + case OAS2RECV: + *init = concat(*init, n->ninit); + n->ninit = nil; + r = n->rlist->n; + walkexprlistsafe(n->list, init); + walkexpr(&r->left, init); + fn = chanfn("chanrecv2", 2, r->left->type); + r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left); + n->rlist->n = r; + n->op = OAS2FUNC; + goto as2func; + + case OAS2MAPR: + // a,b = m[i]; + *init = concat(*init, n->ninit); + n->ninit = nil; + r = n->rlist->n; + walkexprlistsafe(n->list, init); + walkexpr(&r->left, init); + fn = mapfn("mapaccess2", r->left->type); + r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left, r->right); + n->rlist = list1(r); + n->op = OAS2FUNC; + goto as2func; + + case OAS2MAPW: + // map[] = a,b - mapassign2 + // a,b = m[i]; + *init = concat(*init, n->ninit); + n->ninit = nil; + walkexprlistsafe(n->list, init); + l = n->list->n; + t = l->left->type; + n = mkcall1(mapfn("mapassign2", t), T, init, typename(t), l->left, l->right, n->rlist->n, n->rlist->next->n); + goto ret; + + case OAS2DOTTYPE: + // a,b = i.(T) + *init = concat(*init, n->ninit); + n->ninit = nil; + r = n->rlist->n; + walkexprlistsafe(n->list, init); + r->op = ODOTTYPE2; + walkexpr(&r, init); + ll = ascompatet(n->op, n->list, &r->type, 0, init); + n = liststmt(concat(list1(r), ll)); + goto ret; + + case ODOTTYPE: + case ODOTTYPE2: + // Build name of function: assertI2E2 etc. + strcpy(buf, "assert"); + p = buf+strlen(buf); + if(isnilinter(n->left->type)) + *p++ = 'E'; + else + *p++ = 'I'; + *p++ = '2'; + if(isnilinter(n->type)) + *p++ = 'E'; + else if(isinter(n->type)) + *p++ = 'I'; + else + *p++ = 'T'; + if(n->op == ODOTTYPE2) + *p++ = '2'; + *p = '\0'; + + fn = syslook(buf, 1); + ll = list1(typename(n->type)); + ll = list(ll, n->left); + argtype(fn, n->left->type); + argtype(fn, n->type); + n = nod(OCALL, fn, N); + n->list = ll; + typecheck(&n, Erv | Efnstruct); + walkexpr(&n, init); + goto ret; + + case OCONVIFACE: + // Build name of function: convI2E etc. + // Not all names are possible + // (e.g., we'll never generate convE2E or convE2I). + walkexpr(&n->left, init); + strcpy(buf, "conv"); + p = buf+strlen(buf); + if(isnilinter(n->left->type)) + *p++ = 'E'; + else if(isinter(n->left->type)) + *p++ = 'I'; + else + *p++ = 'T'; + *p++ = '2'; + if(isnilinter(n->type)) + *p++ = 'E'; + else + *p++ = 'I'; + *p = '\0'; + + fn = syslook(buf, 1); + ll = nil; + if(!isinter(n->left->type)) + ll = list(ll, typename(n->left->type)); + if(!isnilinter(n->type)) + ll = list(ll, typename(n->type)); + ll = list(ll, n->left); + argtype(fn, n->left->type); + argtype(fn, n->type); + dowidth(fn->type); + n = nod(OCALL, fn, N); + n->list = ll; + typecheck(&n, Erv); + walkexpr(&n, init); + goto ret; + + case OCONV: + case OCONVNOP: + if(thechar == '5') { + if(isfloat[n->left->type->etype]) { + if(n->type->etype == TINT64) { + n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } + if(n->type->etype == TUINT64) { + n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); + goto ret; + } + } + if(isfloat[n->type->etype]) { + if(n->left->type->etype == TINT64) { + n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); + goto ret; + } + if(n->left->type->etype == TUINT64) { + n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); + goto ret; + } + } + } + walkexpr(&n->left, init); + goto ret; + + case OASOP: + if(n->etype == OANDNOT) { + n->etype = OAND; + n->right = nod(OCOM, n->right, N); + typecheck(&n->right, Erv); + } + n->left = safeexpr(n->left, init); + walkexpr(&n->left, init); + l = n->left; + walkexpr(&n->right, init); + + /* + * on 32-bit arch, rewrite 64-bit ops into l = l op r. + * on 386, rewrite float ops into l = l op r. + * everywhere, rewrite map ops into l = l op r. + * everywhere, rewrite string += into l = l op r. + * everywhere, rewrite complex /= into l = l op r. + * TODO(rsc): Maybe this rewrite should be done always? + */ + et = n->left->type->etype; + if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || + (thechar == '8' && isfloat[et]) || + l->op == OINDEXMAP || + et == TSTRING || + (iscomplex[et] && n->etype == ODIV)) { + l = safeexpr(n->left, init); + a = l; + if(a->op == OINDEXMAP) { + // map index has "lhs" bit set in a->etype. + // make a copy so we can clear it on the rhs. + a = nod(OXXX, N, N); + *a = *l; + a->etype = 0; + } + r = nod(OAS, l, nod(n->etype, a, n->right)); + typecheck(&r, Etop); + walkexpr(&r, init); + n = r; + } + goto ret; + + case OANDNOT: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + n->op = OAND; + n->right = nod(OCOM, n->right, N); + typecheck(&n->right, Erv); + goto ret; + + case ODIV: + case OMOD: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + /* + * rewrite complex div into function call. + */ + et = n->left->type->etype; + if(iscomplex[et] && n->op == ODIV) { + t = n->type; + n = mkcall("complex128div", types[TCOMPLEX128], init, + conv(n->left, types[TCOMPLEX128]), + conv(n->right, types[TCOMPLEX128])); + n = conv(n, t); + goto ret; + } + /* + * rewrite div and mod into function calls + * on 32-bit architectures. + */ + if(widthptr > 4 || (et != TUINT64 && et != TINT64)) + goto ret; + if(et == TINT64) + strcpy(namebuf, "int64"); + else + strcpy(namebuf, "uint64"); + if(n->op == ODIV) + strcat(namebuf, "div"); + else + strcat(namebuf, "mod"); + n = mkcall(namebuf, n->type, init, + conv(n->left, types[et]), conv(n->right, types[et])); + goto ret; + + case OINDEX: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + + // if range of type cannot exceed static array bound, + // disable bounds check + if(isfixedarray(n->left->type)) + if(n->right->type->width < 4) + if((1<<(8*n->right->type->width)) <= n->left->type->bound) + n->etype = 1; + + if(isconst(n->left, CTSTR)) + if(n->right->type->width < 4) + if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len) + n->etype = 1; + + // check for static out of bounds + if(isconst(n->right, CTINT) && !n->etype) { + v = mpgetfix(n->right->val.u.xval); + len = 1LL<<60; + t = n->left->type; + if(isconst(n->left, CTSTR)) + len = n->left->val.u.sval->len; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + if(v < 0 || v >= (1LL<<31) || v >= len) + yyerror("index out of bounds"); + else if(isconst(n->left, CTSTR)) { + // replace "abc"[2] with 'b'. + // delayed until now because "abc"[2] is not + // an ideal constant. + nodconst(n, n->type, n->left->val.u.sval->s[v]); + } + } + goto ret; + + case OINDEXMAP: + if(n->etype == 1) + goto ret; + + t = n->left->type; + n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right); + goto ret; + + case ORECV: + walkexpr(&n->left, init); + walkexpr(&n->right, init); + n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left); + goto ret; + + case OSLICE: + case OSLICEARR: + walkexpr(&n->left, init); + n->left = safeexpr(n->left, init); + walkexpr(&n->right->left, init); + n->right->left = safeexpr(n->right->left, init); + walkexpr(&n->right->right, init); + n->right->right = safeexpr(n->right->right, init); + + len = 1LL<<60; + t = n->left->type; + if(t != T && isptr[t->etype]) + t = t->type; + if(isfixedarray(t)) + len = t->bound; + + // check for static out of bounds + // NOTE: v > len not v >= len. + v1 = -1; + v2 = -1; + if(isconst(n->right->left, CTINT)) { + v1 = mpgetfix(n->right->left->val.u.xval); + if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) { + yyerror("slice index out of bounds"); + v1 = -1; + } + } + if(isconst(n->right->right, CTINT)) { + v2 = mpgetfix(n->right->right->val.u.xval); + if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) { + yyerror("slice index out of bounds"); + v2 = -1; + } + } + if(v1 >= 0 && v2 >= 0 && v1 > v2) + yyerror("inverted slice range"); + + if(n->op == OSLICEARR) + goto slicearray; + + // dynamic slice + // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any) + // sliceslice1(old []any, lb uint64, width uint64) (ary []any) + t = n->type; + et = n->etype; + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); + if(n->right->right != N) { + fn = syslook("sliceslice", 1); + argtype(fn, t->type); // any-1 + argtype(fn, t->type); // any-2 + n = mkcall1(fn, t, init, + n->left, + l, + conv(n->right->right, types[TUINT64]), + nodintconst(t->type->width)); + } else { + fn = syslook("sliceslice1", 1); + argtype(fn, t->type); // any-1 + argtype(fn, t->type); // any-2 + n = mkcall1(fn, t, init, + n->left, + l, + nodintconst(t->type->width)); + } + n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call. + goto ret; + + slicearray: + // static slice + // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any) + t = n->type; + fn = syslook("slicearray", 1); + argtype(fn, n->left->type->type); // any-1 + argtype(fn, t->type); // any-2 + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TUINT64]); + if(n->right->right == N) + r = nodintconst(n->left->type->type->bound); + else + r = conv(n->right->right, types[TUINT64]); + n = mkcall1(fn, t, init, + n->left, nodintconst(n->left->type->type->bound), + l, + r, + nodintconst(t->type->width)); + goto ret; + + case OADDR:; + Node *nvar, *nstar; + + // turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation. + // initialize with + // nvar := new(*Point); + // *nvar = Point(1, 2); + // and replace expression with nvar + switch(n->left->op) { + case OARRAYLIT: + case OMAPLIT: + case OSTRUCTLIT: + nvar = makenewvar(n->type, init, &nstar); + anylit(0, n->left, nstar, init); + n = nvar; + goto ret; + } + + walkexpr(&n->left, init); + goto ret; + + case ONEW: + n = callnew(n->type->type); + goto ret; + + case OCMPSTR: + // If one argument to the comparison is an empty string, + // comparing the lengths instead will yield the same result + // without the function call. + if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) || + (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) { + r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N)); + typecheck(&r, Erv); + walkexpr(&r, init); + n = r; + goto ret; + } + + // s + "badgerbadgerbadger" == "badgerbadgerbadger" + if((n->etype == OEQ || n->etype == ONE) && + isconst(n->right, CTSTR) && + n->left->op == OADDSTR && isconst(n->left->right, CTSTR) && + cmpslit(n->right, n->left->right) == 0) { + r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0)); + typecheck(&r, Erv); + walkexpr(&r, init); + n = r; + goto ret; + } + + // prepare for rewrite below + if(n->etype == OEQ || n->etype == ONE) { + n->left = cheapexpr(n->left, init); + n->right = cheapexpr(n->right, init); + } + + // sys_cmpstring(s1, s2) :: 0 + r = mkcall("cmpstring", types[TINT], init, + conv(n->left, types[TSTRING]), + conv(n->right, types[TSTRING])); + r = nod(n->etype, r, nodintconst(0)); + + // quick check of len before full compare for == or != + if(n->etype == OEQ || n->etype == ONE) { + if(n->etype == OEQ) + r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + else + r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); + typecheck(&r, Erv); + walkexpr(&r, nil); + } + typecheck(&r, Erv); + n = r; + goto ret; + + case OADDSTR: + n = addstr(n, init); + goto ret; + + case OSLICESTR: + // sys_slicestring(s, lb, hb) + if(n->right->left == N) + l = nodintconst(0); + else + l = conv(n->right->left, types[TINT]); + if(n->right->right) { + n = mkcall("slicestring", n->type, init, + conv(n->left, types[TSTRING]), + l, + conv(n->right->right, types[TINT])); + } else { + n = mkcall("slicestring1", n->type, init, + conv(n->left, types[TSTRING]), + l); + } + goto ret; + + case OAPPEND: + if(n->isddd) + n = appendslice(n, init); + else + n = append(n, init); + goto ret; + + case OCOPY: + if(n->right->type->etype == TSTRING) + fn = syslook("slicestringcopy", 1); + else + fn = syslook("slicecopy", 1); + argtype(fn, n->left->type); + argtype(fn, n->right->type); + n = mkcall1(fn, n->type, init, + n->left, n->right, + nodintconst(n->left->type->type->width)); + goto ret; + + case OCLOSE: + // cannot use chanfn - closechan takes any, not chan any + fn = syslook("closechan", 1); + argtype(fn, n->left->type); + n = mkcall1(fn, T, init, n->left); + goto ret; + + case OMAKECHAN: + n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, + typename(n->type), + conv(n->left, types[TINT64])); + goto ret; + + case OMAKEMAP: + t = n->type; + + fn = syslook("makemap", 1); + argtype(fn, t->down); // any-1 + argtype(fn, t->type); // any-2 + + n = mkcall1(fn, n->type, init, + typename(n->type), + conv(n->left, types[TINT64])); + goto ret; + + case OMAKESLICE: + // makeslice(t *Type, nel int64, max int64) (ary []any) + l = n->left; + r = n->right; + if(r == nil) + l = r = safeexpr(l, init); + t = n->type; + fn = syslook("makeslice", 1); + argtype(fn, t->type); // any-1 + n = mkcall1(fn, n->type, init, + typename(n->type), + conv(l, types[TINT64]), + conv(r, types[TINT64])); + goto ret; + + case ORUNESTR: + // sys_intstring(v) + n = mkcall("intstring", n->type, init, + conv(n->left, types[TINT64])); + goto ret; + + case OARRAYBYTESTR: + // slicebytetostring([]byte) string; + n = mkcall("slicebytetostring", n->type, init, n->left); + goto ret; + + case OARRAYRUNESTR: + // sliceinttostring([]int) string; + n = mkcall("sliceinttostring", n->type, init, n->left); + goto ret; + + case OSTRARRAYBYTE: + // stringtoslicebyte(string) []byte; + n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING])); + goto ret; + + case OSTRARRAYRUNE: + // stringtosliceint(string) []int + n = mkcall("stringtosliceint", n->type, init, n->left); + goto ret; + + case OCMPIFACE: + // ifaceeq(i1 any-1, i2 any-2) (ret bool); + if(!eqtype(n->left->type, n->right->type)) + fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type); + if(isnilinter(n->left->type)) + fn = syslook("efaceeq", 1); + else + fn = syslook("ifaceeq", 1); + argtype(fn, n->right->type); + argtype(fn, n->left->type); + r = mkcall1(fn, n->type, init, n->left, n->right); + if(n->etype == ONE) { + r = nod(ONOT, r, N); + typecheck(&r, Erv); + } + n = r; + goto ret; + + case OARRAYLIT: + case OMAPLIT: + case OSTRUCTLIT: + nvar = nod(OXXX, N, N); + tempname(nvar, n->type); + anylit(0, n, nvar, init); + n = nvar; + goto ret; + + case OSEND: + n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right); + goto ret; + + case OCLOSURE: + n = walkclosure(n, init); + goto ret; + } + fatal("missing switch %O", n->op); + +ret: + if(debug['w'] && n != N) + dump("walk", n); + + ullmancalc(n); + lineno = lno; + *np = n; +} + +static Node* +makenewvar(Type *t, NodeList **init, Node **nstar) +{ + Node *nvar, *nas; + + nvar = nod(OXXX, N, N); + tempname(nvar, t); + nas = nod(OAS, nvar, callnew(t->type)); + typecheck(&nas, Etop); + walkexpr(&nas, init); + *init = list(*init, nas); + + *nstar = nod(OIND, nvar, N); + typecheck(nstar, Erv); + return nvar; +} + +static Node* +ascompatee1(int op, Node *l, Node *r, NodeList **init) +{ + return convas(nod(OAS, l, r), init); +} + +static NodeList* +ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) +{ + NodeList *ll, *lr, *nn; + + /* + * check assign expression list to + * a expression list. called in + * expr-list = expr-list + */ + + // ensure order of evaluation for function calls + for(ll=nl; ll; ll=ll->next) + ll->n = safeexpr(ll->n, init); + for(lr=nr; lr; lr=lr->next) + lr->n = safeexpr(lr->n, init); + + nn = nil; + for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) + nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); + + // cannot happen: caller checked that lists had same length + if(ll || lr) + yyerror("error in shape across %O", op); + return nn; +} + +/* + * l is an lv and rt is the type of an rv + * return 1 if this implies a function call + * evaluating the lv or a function call + * in the conversion of the types + */ +static int +fncall(Node *l, Type *rt) +{ + if(l->ullman >= UINF || l->op == OINDEXMAP) + return 1; + if(eqtype(l->type, rt)) + return 0; + return 1; +} + +static NodeList* +ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) +{ + Node *l, *tmp, *a; + NodeList *ll; + Type *r; + Iter saver; + int ucount; + NodeList *nn, *mm; + + /* + * check assign type list to + * a expression list. called in + * expr-list = func() + */ + r = structfirst(&saver, nr); + nn = nil; + mm = nil; + ucount = 0; + for(ll=nl; ll; ll=ll->next) { + if(r == T) + break; + l = ll->n; + if(isblank(l)) { + r = structnext(&saver); + continue; + } + + // any lv that causes a fn call must be + // deferred until all the return arguments + // have been pulled from the output arguments + if(fncall(l, r->type)) { + tmp = nod(OXXX, N, N); + tempname(tmp, r->type); + typecheck(&tmp, Erv); + a = nod(OAS, l, tmp); + a = convas(a, init); + mm = list(mm, a); + l = tmp; + } + + a = nod(OAS, l, nodarg(r, fp)); + a = convas(a, init); + ullmancalc(a); + if(a->ullman >= UINF) + ucount++; + nn = list(nn, a); + r = structnext(&saver); + } + + if(ll != nil || r != T) + yyerror("assignment count mismatch: %d = %d", + count(nl), structcount(*nr)); + if(ucount) + fatal("reorder2: too many function calls evaluating parameters"); + return concat(nn, mm); +} + + /* + * package all the arguments that match a ... T parameter into a []T. + */ +static NodeList* +mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) +{ + Node *a, *n; + Type *tslice; + + tslice = typ(TARRAY); + tslice->type = l->type->type; + tslice->bound = -1; + + n = nod(OCOMPLIT, N, typenod(tslice)); + n->list = lr0; + typecheck(&n, Erv); + if(n->type == T) + fatal("mkdotargslice: typecheck failed"); + walkexpr(&n, init); + + a = nod(OAS, nodarg(l, fp), n); + nn = list(nn, convas(a, init)); + return nn; +} + +/* + * helpers for shape errors + */ +static char* +dumptypes(Type **nl, char *what) +{ + int first; + Type *l; + Iter savel; + Fmt fmt; + + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); + l = structfirst(&savel, nl); + first = 1; + for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { + if(first) + first = 0; + else + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", l); + } + if(first) + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); +} + +static char* +dumpnodetypes(NodeList *l, char *what) +{ + int first; + Node *r; + Fmt fmt; + + fmtstrinit(&fmt); + fmtprint(&fmt, "\t"); + first = 1; + for(; l; l=l->next) { + r = l->n; + if(first) + first = 0; + else + fmtprint(&fmt, ", "); + fmtprint(&fmt, "%T", r->type); + } + if(first) + fmtprint(&fmt, "[no arguments %s]", what); + return fmtstrflush(&fmt); +} + +/* + * check assign expression list to + * a type list. called in + * return expr-list + * func(expr-list) + */ +static NodeList* +ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) +{ + Type *l, *ll; + Node *r, *a; + NodeList *nn, *lr0, *alist; + Iter savel; + char *l1, *l2; + + lr0 = lr; + l = structfirst(&savel, nl); + r = N; + if(lr) + r = lr->n; + nn = nil; + + // f(g()) where g has multiple return values + if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) { + // optimization - can do block copy + if(eqtypenoname(r->type, *nl)) { + a = nodarg(*nl, fp); + a->type = r->type; + nn = list1(convas(nod(OAS, a, r), init)); + goto ret; + } + + // conversions involved. + // copy into temporaries. + alist = nil; + for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) { + a = nod(OXXX, N, N); + tempname(a, l->type); + alist = list(alist, a); + } + a = nod(OAS2, N, N); + a->list = alist; + a->rlist = lr; + typecheck(&a, Etop); + walkstmt(&a); + *init = list(*init, a); + lr = alist; + r = lr->n; + l = structfirst(&savel, nl); + } + +loop: + if(l != T && l->isddd) { + // the ddd parameter must be last + ll = structnext(&savel); + if(ll != T) + yyerror("... must be last argument"); + + // special case -- + // only if we are assigning a single ddd + // argument to a ddd parameter then it is + // passed thru unencapsulated + if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { + a = nod(OAS, nodarg(l, fp), r); + a = convas(a, init); + nn = list(nn, a); + goto ret; + } + + // normal case -- make a slice of all + // remaining arguments and pass it to + // the ddd parameter. + nn = mkdotargslice(lr, nn, l, fp, init); + goto ret; + } + + if(l == T || r == N) { + if(l != T || r != N) { + l1 = dumptypes(nl, "expected"); + l2 = dumpnodetypes(lr0, "given"); + if(l != T) + yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); + else + yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); + } + goto ret; + } + + a = nod(OAS, nodarg(l, fp), r); + a = convas(a, init); + nn = list(nn, a); + + l = structnext(&savel); + r = N; + lr = lr->next; + if(lr != nil) + r = lr->n; + goto loop; + +ret: + for(lr=nn; lr; lr=lr->next) + lr->n->typecheck = 1; + return nn; +} + +// generate code for print +static Node* +walkprint(Node *nn, NodeList **init, int defer) +{ + Node *r; + Node *n; + NodeList *l, *all; + Node *on; + Type *t; + int notfirst, et, op; + NodeList *calls, *intypes, *args; + Fmt fmt; + + on = nil; + op = nn->op; + all = nn->list; + calls = nil; + notfirst = 0; + intypes = nil; + args = nil; + + memset(&fmt, 0, sizeof fmt); + if(defer) { + // defer print turns into defer printf with format string + fmtstrinit(&fmt); + intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING]))); + args = list1(nod(OXXX, N, N)); + } + + for(l=all; l; l=l->next) { + if(notfirst) { + if(defer) + fmtprint(&fmt, " "); + else + calls = list(calls, mkcall("printsp", T, init)); + } + notfirst = op == OPRINTN; + + n = l->n; + if(n->op == OLITERAL) { + switch(n->val.ctype) { + case CTINT: + defaultlit(&n, types[TINT64]); + break; + case CTFLT: + defaultlit(&n, types[TFLOAT64]); + break; + } + } + if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL) + defaultlit(&n, types[TINT64]); + defaultlit(&n, nil); + l->n = n; + if(n->type == T || n->type->etype == TFORW) + continue; + + t = n->type; + et = n->type->etype; + if(isinter(n->type)) { + if(defer) { + if(isnilinter(n->type)) + fmtprint(&fmt, "%%e"); + else + fmtprint(&fmt, "%%i"); + } else { + if(isnilinter(n->type)) + on = syslook("printeface", 1); + else + on = syslook("printiface", 1); + argtype(on, n->type); // any-1 + } + } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) { + if(defer) { + fmtprint(&fmt, "%%p"); + } else { + on = syslook("printpointer", 1); + argtype(on, n->type); // any-1 + } + } else if(isslice(n->type)) { + if(defer) { + fmtprint(&fmt, "%%a"); + } else { + on = syslook("printslice", 1); + argtype(on, n->type); // any-1 + } + } else if(isint[et]) { + if(defer) { + if(et == TUINT64) + fmtprint(&fmt, "%%U"); + else { + fmtprint(&fmt, "%%D"); + t = types[TINT64]; + } + } else { + if(et == TUINT64) + on = syslook("printuint", 0); + else + on = syslook("printint", 0); + } + } else if(isfloat[et]) { + if(defer) { + fmtprint(&fmt, "%%f"); + t = types[TFLOAT64]; + } else + on = syslook("printfloat", 0); + } else if(iscomplex[et]) { + if(defer) { + fmtprint(&fmt, "%%C"); + t = types[TCOMPLEX128]; + } else + on = syslook("printcomplex", 0); + } else if(et == TBOOL) { + if(defer) + fmtprint(&fmt, "%%t"); + else + on = syslook("printbool", 0); + } else if(et == TSTRING) { + if(defer) + fmtprint(&fmt, "%%S"); + else + on = syslook("printstring", 0); + } else { + badtype(OPRINT, n->type, T); + continue; + } + + if(!defer) { + t = *getinarg(on->type); + if(t != nil) + t = t->type; + if(t != nil) + t = t->type; + } + + if(!eqtype(t, n->type)) { + n = nod(OCONV, n, N); + n->type = t; + } + + if(defer) { + intypes = list(intypes, nod(ODCLFIELD, N, typenod(t))); + args = list(args, n); + } else { + r = nod(OCALL, on, N); + r->list = list1(n); + calls = list(calls, r); + } + } + + if(defer) { + if(op == OPRINTN) + fmtprint(&fmt, "\n"); + on = syslook("goprintf", 1); + on->type = functype(nil, intypes, nil); + args->n = nod(OLITERAL, N, N); + args->n->val.ctype = CTSTR; + args->n->val.u.sval = strlit(fmtstrflush(&fmt)); + r = nod(OCALL, on, N); + r->list = args; + typecheck(&r, Etop); + walkexpr(&r, init); + } else { + if(op == OPRINTN) + calls = list(calls, mkcall("printnl", T, nil)); + typechecklist(calls, Etop); + walkexprlist(calls, init); + + r = nod(OEMPTY, N, N); + typecheck(&r, Etop); + walkexpr(&r, init); + r->ninit = calls; + } + return r; +} + +Node* +callnew(Type *t) +{ + Node *fn; + + dowidth(t); + fn = syslook("new", 1); + argtype(fn, t); + return mkcall1(fn, ptrto(t), nil, nodintconst(t->width)); +} + +static Node* +convas(Node *n, NodeList **init) +{ + Type *lt, *rt; + + if(n->op != OAS) + fatal("convas: not OAS %O", n->op); + + n->typecheck = 1; + + if(n->left == N || n->right == N) + goto out; + + lt = n->left->type; + rt = n->right->type; + if(lt == T || rt == T) + goto out; + + if(isblank(n->left)) { + defaultlit(&n->right, T); + goto out; + } + + if(n->left->op == OINDEXMAP) { + n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init, + typename(n->left->left->type), + n->left->left, n->left->right, n->right); + goto out; + } + + if(eqtype(lt, rt)) + goto out; + + n->right = assignconv(n->right, lt, "assignment"); + walkexpr(&n->right, init); + +out: + ullmancalc(n); + return n; +} + +/* + * from ascompat[te] + * evaluating actual function arguments. + * f(a,b) + * if there is exactly one function expr, + * then it is done first. otherwise must + * make temp variables + */ +NodeList* +reorder1(NodeList *all) +{ + Node *f, *a, *n; + NodeList *l, *r, *g; + int c, d, t; + + c = 0; // function calls + t = 0; // total parameters + + for(l=all; l; l=l->next) { + n = l->n; + t++; + ullmancalc(n); + if(n->ullman >= UINF) + c++; + } + if(c == 0 || t == 1) + return all; + + g = nil; // fncalls assigned to tempnames + f = N; // last fncall assigned to stack + r = nil; // non fncalls and tempnames assigned to stack + d = 0; + for(l=all; l; l=l->next) { + n = l->n; + if(n->ullman < UINF) { + r = list(r, n); + continue; + } + d++; + if(d == c) { + f = n; + continue; + } + + // make assignment of fncall to tempname + a = nod(OXXX, N, N); + tempname(a, n->right->type); + a = nod(OAS, a, n->right); + g = list(g, a); + + // put normal arg assignment on list + // with fncall replaced by tempname + n->right = a->left; + r = list(r, n); + } + + if(f != N) + g = list(g, f); + return concat(g, r); +} + +/* + * from ascompat[ee] + * a,b = c,d + * simultaneous assignment. there cannot + * be later use of an earlier lvalue. + */ + +static int +vmatch2(Node *l, Node *r) +{ + NodeList *ll; + + /* + * isolate all right sides + */ + if(r == N) + return 0; + switch(r->op) { + case ONAME: + // match each right given left + if(l == r) + return 1; + case OLITERAL: + return 0; + } + if(vmatch2(l, r->left)) + return 1; + if(vmatch2(l, r->right)) + return 1; + for(ll=r->list; ll; ll=ll->next) + if(vmatch2(l, ll->n)) + return 1; + return 0; +} + +int +vmatch1(Node *l, Node *r) +{ + NodeList *ll; + + /* + * isolate all left sides + */ + if(l == N || r == N) + return 0; + switch(l->op) { + case ONAME: + switch(l->class) { + case PPARAM: + case PPARAMREF: + case PAUTO: + break; + default: + // assignment to non-stack variable + // must be delayed if right has function calls. + if(r->ullman >= UINF) + return 1; + break; + } + return vmatch2(l, r); + case OLITERAL: + return 0; + } + if(vmatch1(l->left, r)) + return 1; + if(vmatch1(l->right, r)) + return 1; + for(ll=l->list; ll; ll=ll->next) + if(vmatch1(ll->n, r)) + return 1; + return 0; +} + +NodeList* +reorder3(NodeList *all) +{ + Node *n1, *n2, *q; + int c1, c2; + NodeList *l1, *l2, *r; + + r = nil; + for(l1=all, c1=0; l1; l1=l1->next, c1++) { + n1 = l1->n; + for(l2=all, c2=0; l2; l2=l2->next, c2++) { + n2 = l2->n; + if(c2 > c1) { + if(vmatch1(n1->left, n2->right)) { + // delay assignment to n1->left + q = nod(OXXX, N, N); + tempname(q, n1->right->type); + q = nod(OAS, n1->left, q); + n1->left = q->right; + r = list(r, q); + break; + } + } + } + } + return concat(all, r); +} + +/* + * walk through argin parameters. + * generate and return code to allocate + * copies of escaped parameters to the heap. + */ +static NodeList* +paramstoheap(Type **argin, int out) +{ + Type *t; + Iter savet; + Node *v; + NodeList *nn; + + nn = nil; + for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { + v = t->nname; + if(v == N && out && hasdefer) { + // Defer might stop a panic and show the + // return values as they exist at the time of panic. + // Make sure to zero them on entry to the function. + nn = list(nn, nod(OAS, nodarg(t, 1), N)); + } + if(v == N || !(v->class & PHEAP)) + continue; + + // generate allocation & copying code + if(v->alloc == nil) + v->alloc = callnew(v->type); + nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); + if((v->class & ~PHEAP) != PPARAMOUT) + nn = list(nn, nod(OAS, v, v->stackparam)); + } + return nn; +} + +/* + * walk through argout parameters copying back to stack + */ +static NodeList* +returnsfromheap(Type **argin) +{ + Type *t; + Iter savet; + Node *v; + NodeList *nn; + + nn = nil; + for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { + v = t->nname; + if(v == N || v->class != (PHEAP|PPARAMOUT)) + continue; + nn = list(nn, nod(OAS, v->stackparam, v)); + } + return nn; +} + +/* + * take care of migrating any function in/out args + * between the stack and the heap. adds code to + * curfn's before and after lists. + */ +static void +heapmoves(void) +{ + NodeList *nn; + int32 lno; + + lno = lineno; + lineno = curfn->lineno; + nn = paramstoheap(getthis(curfn->type), 0); + nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); + nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); + curfn->enter = concat(curfn->enter, nn); + lineno = curfn->endlineno; + curfn->exit = returnsfromheap(getoutarg(curfn->type)); + lineno = lno; +} + +static Node* +vmkcall(Node *fn, Type *t, NodeList **init, va_list va) +{ + int i, n; + Node *r; + NodeList *args; + + if(fn->type == T || fn->type->etype != TFUNC) + fatal("mkcall %#N %T", fn, fn->type); + + args = nil; + n = fn->type->intuple; + for(i=0; i<n; i++) + args = list(args, va_arg(va, Node*)); + + r = nod(OCALL, fn, N); + r->list = args; + if(fn->type->outtuple > 0) + typecheck(&r, Erv | Efnstruct); + else + typecheck(&r, Etop); + walkexpr(&r, init); + r->type = t; + return r; +} + +Node* +mkcall(char *name, Type *t, NodeList **init, ...) +{ + Node *r; + va_list va; + + va_start(va, init); + r = vmkcall(syslook(name, 0), t, init, va); + va_end(va); + return r; +} + +Node* +mkcall1(Node *fn, Type *t, NodeList **init, ...) +{ + Node *r; + va_list va; + + va_start(va, init); + r = vmkcall(fn, t, init, va); + va_end(va); + return r; +} + +static Node* +conv(Node *n, Type *t) +{ + if(eqtype(n->type, t)) + return n; + n = nod(OCONV, n, N); + n->type = t; + typecheck(&n, Erv); + return n; +} + +Node* +chanfn(char *name, int n, Type *t) +{ + Node *fn; + int i; + + if(t->etype != TCHAN) + fatal("chanfn %T", t); + fn = syslook(name, 1); + for(i=0; i<n; i++) + argtype(fn, t->type); + return fn; +} + +static Node* +mapfn(char *name, Type *t) +{ + Node *fn; + + if(t->etype != TMAP) + fatal("mapfn %T", t); + fn = syslook(name, 1); + argtype(fn, t->down); + argtype(fn, t->type); + argtype(fn, t->down); + argtype(fn, t->type); + return fn; +} + +static Node* +addstr(Node *n, NodeList **init) +{ + Node *r, *cat, *typstr; + NodeList *in, *args; + int i, count; + + count = 0; + for(r=n; r->op == OADDSTR; r=r->left) + count++; // r->right + count++; // r + + // prepare call of runtime.catstring of type int, string, string, string + // with as many strings as we have. + cat = syslook("concatstring", 1); + cat->type = T; + cat->ntype = nod(OTFUNC, N, N); + in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count + typstr = typenod(types[TSTRING]); + for(i=0; i<count; i++) + in = list(in, nod(ODCLFIELD, N, typstr)); + cat->ntype->list = in; + cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); + + args = nil; + for(r=n; r->op == OADDSTR; r=r->left) + args = concat(list1(conv(r->right, types[TSTRING])), args); + args = concat(list1(conv(r, types[TSTRING])), args); + args = concat(list1(nodintconst(count)), args); + + r = nod(OCALL, cat, N); + r->list = args; + typecheck(&r, Erv); + walkexpr(&r, init); + r->type = n->type; + + return r; +} + +static Node* +appendslice(Node *n, NodeList **init) +{ + Node *f; + + f = syslook("appendslice", 1); + argtype(f, n->type); + argtype(f, n->type->type); + argtype(f, n->type); + return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n); +} + +// expand append(src, a [, b]* ) to +// +// init { +// s := src +// const argc = len(args) - 1 +// if cap(s) - len(s) < argc { +// s = growslice(s, argc) +// } +// n := len(s) +// s = s[:n+argc] +// s[n] = a +// s[n+1] = b +// ... +// } +// s +static Node* +append(Node *n, NodeList **init) +{ + NodeList *l, *a; + Node *nsrc, *ns, *nn, *na, *nx, *fn; + int argc; + + walkexprlistsafe(n->list, init); + + nsrc = n->list->n; + argc = count(n->list) - 1; + if (argc < 1) { + return nsrc; + } + + l = nil; + + ns = nod(OXXX, N, N); // var s + tempname(ns, nsrc->type); + l = list(l, nod(OAS, ns, nsrc)); // s = src + + na = nodintconst(argc); // const argc + nx = nod(OIF, N, N); // if cap(s) - len(s) < argc + nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na); + + fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T) + argtype(fn, ns->type->type); // 1 old []any + argtype(fn, ns->type->type); // 2 ret []any + + nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit, + typename(ns->type), + ns, + conv(na, types[TINT64])))); + l = list(l, nx); + + nn = nod(OXXX, N, N); // var n + tempname(nn, types[TINT]); + l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) + + nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] + nx->etype = 1; // disable bounds check + l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] + + for (a = n->list->next; a != nil; a = a->next) { + nx = nod(OINDEX, ns, nn); // s[n] ... + nx->etype = 1; // disable bounds check + l = list(l, nod(OAS, nx, a->n)); // s[n] = arg + if (a->next != nil) + l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 + } + + typechecklist(l, Etop); + walkstmtlist(l); + *init = concat(*init, l); + return ns; +} |