Built go1.4.2 from source.
Change-Id: If9a300f7aacf0d8fb5e720945e0aa2fc68193270
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
new file mode 100644
index 0000000..58e25fa
--- /dev/null
+++ b/src/cmd/gc/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2012 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.dist
+
+install: y.tab.h builtin.c
+
+y.tab.h: go.y go.errors bisonerrors
+ bison -v -y -d go.y
+ # 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
+ mv y1.tab.c y.tab.c
+ awk -f bisonerrors y.output go.errors >yerr.h
+
+builtin.c: runtime.go unsafe.go
+ ./mkbuiltin
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
new file mode 100644
index 0000000..63ed2e5
--- /dev/null
+++ b/src/cmd/gc/align.c
@@ -0,0 +1,680 @@
+// 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 "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;
+
+vlong
+rnd(vlong o, vlong 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;
+ int64 w;
+ int32 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);
+ if(f->type == T) {
+ // broken field, just skip it so that other valid fields
+ // get a width.
+ continue;
+ }
+ 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;
+ if(!t->broke) {
+ t->broke = 1;
+ yyerror("invalid recursive type %T", t);
+ }
+ t->width = 0;
+ lineno = lno;
+ return;
+ }
+
+ // break infinite recursion if the broken recursive type
+ // is referenced again
+ if(t->broke && t->width == 0)
+ 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 = widthreg;
+ break;
+ case TCOMPLEX128:
+ w = 16;
+ t->align = widthreg;
+ 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
+ if(!t->broke)
+ 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) {
+ if(!t->broke) {
+ yyerror("use of [...] array outside of array literal");
+ t->broke = 1;
+ }
+ }
+ 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, widthreg);
+ w = widstruct(t->type, *getoutarg(t1), w, widthreg);
+ t1->argwid = w;
+ if(w%widthreg)
+ warn("bad type %T %d\n", t1, w);
+ t->align = 1;
+ break;
+ }
+
+ if(widthptr == 4 && w != (int32)w)
+ yyerror("type %T too large", t);
+
+ 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[TCHAN] = 1;
+ okforeq[TSTRING] = 1;
+ okforeq[TBOOL] = 1;
+ okforeq[TMAP] = 1; // nil only; refined in typecheck
+ okforeq[TFUNC] = 1; // nil only; refined in typecheck
+ okforeq[TARRAY] = 1; // nil slice only; refined in typecheck
+ okforeq[TSTRUCT] = 1; // it's complicated; 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 = s1;
+
+ dowidth(t);
+ types[etype] = t;
+ s1->def = typenod(t);
+ }
+
+ Array_array = rnd(0, widthptr);
+ Array_nel = rnd(Array_array+widthptr, widthint);
+ Array_cap = rnd(Array_nel+widthint, widthint);
+ sizeof_Array = rnd(Array_cap+widthint, widthptr);
+
+ // string is same as slice wo the cap
+ sizeof_String = rnd(Array_nel+widthint, widthptr);
+
+ dowidth(types[TSTRING]);
+ dowidth(idealstring);
+}
+
+/*
+ * compute total size of f's in/out arguments.
+ */
+int
+argsize(Type *t)
+{
+ Iter save;
+ Type *fp;
+ int64 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);
+ if((int)w != w)
+ fatal("argsize too big");
+ return w;
+}
diff --git a/src/cmd/gc/array.c b/src/cmd/gc/array.c
new file mode 100644
index 0000000..611fc9f
--- /dev/null
+++ b/src/cmd/gc/array.c
@@ -0,0 +1,115 @@
+// Copyright 2013 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 "go.h"
+
+enum {
+ DEFAULTCAPACITY = 16,
+};
+
+struct Array
+{
+ int32 length; // number of elements
+ int32 size; // element size
+ int32 capacity; // size of data in elements
+ char *data; // element storage
+};
+
+Array*
+arraynew(int32 capacity, int32 size)
+{
+ Array *result;
+
+ if(capacity < 0)
+ fatal("arraynew: capacity %d is not positive", capacity);
+ if(size < 0)
+ fatal("arraynew: size %d is not positive\n", size);
+ result = malloc(sizeof(*result));
+ if(result == nil)
+ fatal("arraynew: malloc failed\n");
+ result->length = 0;
+ result->size = size;
+ result->capacity = capacity == 0 ? DEFAULTCAPACITY : capacity;
+ result->data = malloc(result->capacity * result->size);
+ if(result->data == nil)
+ fatal("arraynew: malloc failed\n");
+ return result;
+}
+
+void
+arrayfree(Array *array)
+{
+ if(array == nil)
+ return;
+ free(array->data);
+ free(array);
+}
+
+int32
+arraylength(Array *array)
+{
+ return array->length;
+}
+
+void*
+arrayget(Array *array, int32 index)
+{
+ if(array == nil)
+ fatal("arrayget: array is nil\n");
+ if(index < 0 || index >= array->length)
+ fatal("arrayget: index %d is out of bounds for length %d\n", index, array->length);
+ return array->data + index * array->size;
+}
+
+void
+arrayset(Array *array, int32 index, void *element)
+{
+ if(array == nil)
+ fatal("arrayset: array is nil\n");
+ if(element == nil)
+ fatal("arrayset: element is nil\n");
+ if(index < 0 || index >= array->length)
+ fatal("arrayget: index %d is out of bounds for length %d\n", index, array->length);
+ memmove(array->data + index * array->size, element, array->size);
+}
+
+static void
+ensurecapacity(Array *array, int32 capacity)
+{
+ int32 newcapacity;
+ char *newdata;
+
+ if(array == nil)
+ fatal("ensurecapacity: array is nil\n");
+ if(capacity < 0)
+ fatal("ensurecapacity: capacity %d is not positive", capacity);
+ if(capacity >= array->capacity) {
+ newcapacity = capacity + (capacity >> 1);
+ newdata = realloc(array->data, newcapacity * array->size);
+ if(newdata == nil)
+ fatal("ensurecapacity: realloc failed\n");
+ array->capacity = newcapacity;
+ array->data = newdata;
+ }
+}
+
+void
+arrayadd(Array *array, void *element)
+{
+ if(array == nil)
+ fatal("arrayset: array is nil\n");
+ if(element == nil)
+ fatal("arrayset: element is nil\n");
+ ensurecapacity(array, array->length + 1);
+ array->length++;
+ arrayset(array, array->length - 1, element);
+}
+
+void
+arraysort(Array *array, int (*cmp)(const void*, const void*))
+{
+ qsort(array->data, array->length, array->size, cmp);
+}
diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors
new file mode 100755
index 0000000..fa74c67
--- /dev/null
+++ b/src/cmd/gc/bisonerrors
@@ -0,0 +1,156 @@
+#!/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
+ open = 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] == 1 && $3 == "%empty") {
+ rulesize[$1] = 0
+ }
+ if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") {
+ rulesize[$1] = 0
+ }
+}
+
+# In state dumps, record shift/reduce actions.
+bison && /^[Ss]tate 0/ { grammar = 0; states = 1 }
+
+states && /^[Ss]tate / { state = $2 }
+states { statetext[state] = statetext[state] $0 "\n" }
+
+states && / shift/ {
+ n = nshift[state]++
+ if($0 ~ /and go to/)
+ shift[state,n] = $7 # GNU Bison
+ else
+ shift[state,n] = $3 # Plan 9 Yacc
+ shifttoken[state,n] = $1
+ next
+}
+states && / (go to|goto)/ {
+ n = nshift[state]++
+ if($0 ~ /go to/)
+ shift[state,n] = $5 # GNU Bison
+ else
+ shift[state,n] = $3 # Plan 9 Yacc
+ shifttoken[state,n] = $1
+ next
+}
+states && / reduce/ {
+ n = nreduce[state]++
+ if($0 ~ /reduce using rule/)
+ reduce[state,n] = $5 # GNU Bison
+ else
+ reduce[state,n] = $3 # Plan 9 yacc
+ reducetoken[state,n] = $1
+ next
+}
+
+# Skip over the summary information printed by Plan 9 yacc.
+/nonterminals$/,/^maximum spread/ { 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++) {
+ t = reducetoken[state,j]
+ if(t == tok || t == "$default" || t == ".") {
+ 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);
+ open = 1;
+ break
+ }
+ next
+}
+
+# Print other lines verbatim.
+open && /,$/ {
+ s = $0;
+ sub(",", "},", s)
+ print s
+ open = 0
+ next
+}
+
+open && /"$/ {
+ print $0 "}"
+ open = 0
+ next
+}
+
+{print}
diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c
new file mode 100644
index 0000000..2e79f6f
--- /dev/null
+++ b/src/cmd/gc/bits.c
@@ -0,0 +1,163 @@
+// 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 <u.h>
+#include <libc.h>
+#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].node == N || var[i].node->sym == S)
+ fmtprint(fp, "$%d", i);
+ else {
+ fmtprint(fp, "%s(%d)", var[i].node->sym->name, i);
+ if(var[i].offset != 0)
+ fmtprint(fp, "%+lld", (vlong)var[i].offset);
+ }
+ bits.b[i/32] &= ~(1L << (i%32));
+ }
+ return 0;
+}
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
new file mode 100644
index 0000000..fbca4ee
--- /dev/null
+++ b/src/cmd/gc/builtin.c
@@ -0,0 +1,137 @@
+// AUTO-GENERATED by mkbuiltin; DO NOT EDIT
+char *runtimeimport =
+ "package runtime\n"
+ "import runtime \"runtime\"\n"
+ "func @\"\".newobject (@\"\".typ·2 *byte) (? *any)\n"
+ "func @\"\".panicindex ()\n"
+ "func @\"\".panicslice ()\n"
+ "func @\"\".panicdivide ()\n"
+ "func @\"\".throwreturn ()\n"
+ "func @\"\".throwinit ()\n"
+ "func @\"\".panicwrap (? string, ? string, ? string)\n"
+ "func @\"\".gopanic (? interface {})\n"
+ "func @\"\".gorecover (? *int32) (? interface {})\n"
+ "func @\"\".printbool (? bool)\n"
+ "func @\"\".printfloat (? float64)\n"
+ "func @\"\".printint (? int64)\n"
+ "func @\"\".printhex (? uint64)\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 @\"\".concatstring2 (? string, ? string) (? string)\n"
+ "func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
+ "func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
+ "func @\"\".concatstring5 (? string, ? string, ? string, ? string, ? string) (? string)\n"
+ "func @\"\".concatstrings (? []string) (? string)\n"
+ "func @\"\".cmpstring (? string, ? string) (? int)\n"
+ "func @\"\".eqstring (? string, ? string) (? bool)\n"
+ "func @\"\".intstring (? int64) (? string)\n"
+ "func @\"\".slicebytetostring (? []byte) (? string)\n"
+ "func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
+ "func @\"\".slicerunetostring (? []rune) (? string)\n"
+ "func @\"\".stringtoslicebyte (? string) (? []byte)\n"
+ "func @\"\".stringtoslicerune (? string) (? []rune)\n"
+ "func @\"\".stringiter (? string, ? int) (? int)\n"
+ "func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n"
+ "func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n"
+ "func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n"
+ "func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n"
+ "func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n"
+ "func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any) (@\"\".ret·1 any)\n"
+ "func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertE2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertE2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertE2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertE2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertE2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertE2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertI2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertI2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertI2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertI2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertI2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+ "func @\"\".assertI2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+ "func @\"\".assertI2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n"
+ "func @\"\".assertE2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n"
+ "func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n"
+ "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n"
+ "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
+ "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
+ "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n"
+ "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n"
+ "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+ "func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+ "func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+ "func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 *any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+ "func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+ "func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+ "func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+ "func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any, @\"\".val·4 *any)\n"
+ "func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n"
+ "func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n"
+ "func @\"\".mapiternext (@\"\".hiter·1 *any)\n"
+ "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n"
+ "func @\"\".chanrecv1 (@\"\".chanType·1 *byte, @\"\".hchan·2 <-chan any, @\"\".elem·3 *any)\n"
+ "func @\"\".chanrecv2 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (? bool)\n"
+ "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 *any)\n"
+ "func @\"\".closechan (@\"\".hchan·1 any)\n"
+ "func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+ "func @\"\".writebarrierfat2 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+ "func @\"\".writebarrierfat3 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+ "func @\"\".writebarrierfat4 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+ "func @\"\".writebarrierfat (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n"
+ "func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n"
+ "func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n"
+ "func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n"
+ "func @\"\".newselect (@\"\".sel·1 *byte, @\"\".selsize·2 int64, @\"\".size·3 int32)\n"
+ "func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
+ "func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
+ "func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n"
+ "func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n"
+ "func @\"\".selectgo (@\"\".sel·1 *byte)\n"
+ "func @\"\".block ()\n"
+ "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n"
+ "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n"
+ "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n"
+ "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+ "func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\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·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n"
+ "func @\"\".racefuncenter (? uintptr)\n"
+ "func @\"\".racefuncexit ()\n"
+ "func @\"\".raceread (? uintptr)\n"
+ "func @\"\".racewrite (? uintptr)\n"
+ "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
+ "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\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"
+ "\n"
+ "$$\n";
diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c
new file mode 100644
index 0000000..cfd1cd2
--- /dev/null
+++ b/src/cmd/gc/bv.c
@@ -0,0 +1,213 @@
+// Copyright 2013 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 "go.h"
+
+enum {
+ WORDSIZE = sizeof(uint32),
+ WORDBITS = 32,
+ WORDMASK = WORDBITS - 1,
+ WORDSHIFT = 5,
+};
+
+static uintptr
+bvsize(uintptr n)
+{
+ return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE;
+}
+
+int32
+bvbits(Bvec *bv)
+{
+ return bv->n;
+}
+
+int32
+bvwords(Bvec *bv)
+{
+ return (bv->n + WORDBITS - 1) / WORDBITS;
+}
+
+Bvec*
+bvalloc(int32 n)
+{
+ Bvec *bv;
+ uintptr nbytes;
+
+ if(n < 0)
+ fatal("bvalloc: initial size is negative\n");
+ nbytes = sizeof(Bvec) + bvsize(n);
+ bv = malloc(nbytes);
+ if(bv == nil)
+ fatal("bvalloc: malloc failed\n");
+ memset(bv, 0, nbytes);
+ bv->n = n;
+ return bv;
+}
+
+/* difference */
+void
+bvandnot(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+ int32 i, w;
+
+ if(dst->n != src1->n || dst->n != src2->n)
+ fatal("bvand: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+ for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+ dst->b[w] = src1->b[w] & ~src2->b[w];
+}
+
+int
+bvcmp(Bvec *bv1, Bvec *bv2)
+{
+ uintptr nbytes;
+
+ if(bv1->n != bv2->n)
+ fatal("bvequal: lengths %d and %d are not equal", bv1->n, bv2->n);
+ nbytes = bvsize(bv1->n);
+ return memcmp(bv1->b, bv2->b, nbytes);
+}
+
+void
+bvcopy(Bvec *dst, Bvec *src)
+{
+ memmove(dst->b, src->b, bvsize(dst->n));
+}
+
+Bvec*
+bvconcat(Bvec *src1, Bvec *src2)
+{
+ Bvec *dst;
+ int32 i;
+
+ dst = bvalloc(src1->n + src2->n);
+ for(i = 0; i < src1->n; i++)
+ if(bvget(src1, i))
+ bvset(dst, i);
+ for(i = 0; i < src2->n; i++)
+ if(bvget(src2, i))
+ bvset(dst, i + src1->n);
+ return dst;
+}
+
+int
+bvget(Bvec *bv, int32 i)
+{
+ if(i < 0 || i >= bv->n)
+ fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
+ return (bv->b[i>>WORDSHIFT] >> (i&WORDMASK)) & 1;
+}
+
+// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
+// If there is no such index, bvnext returns -1.
+int
+bvnext(Bvec *bv, int32 i)
+{
+ uint32 w;
+
+ if(i >= bv->n)
+ return -1;
+
+ // Jump i ahead to next word with bits.
+ if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
+ i &= ~WORDMASK;
+ i += WORDBITS;
+ while(i < bv->n && bv->b[i>>WORDSHIFT] == 0)
+ i += WORDBITS;
+ }
+ if(i >= bv->n)
+ return -1;
+
+ // Find 1 bit.
+ w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
+ while((w&1) == 0) {
+ w>>=1;
+ i++;
+ }
+ return i;
+}
+
+int
+bvisempty(Bvec *bv)
+{
+ int32 i;
+
+ for(i = 0; i < bv->n; i += WORDBITS)
+ if(bv->b[i>>WORDSHIFT] != 0)
+ return 0;
+ return 1;
+}
+
+void
+bvnot(Bvec *bv)
+{
+ int32 i, w;
+
+ for(i = 0, w = 0; i < bv->n; i += WORDBITS, w++)
+ bv->b[w] = ~bv->b[w];
+}
+
+/* union */
+void
+bvor(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+ int32 i, w;
+
+ if(dst->n != src1->n || dst->n != src2->n)
+ fatal("bvor: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+ for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+ dst->b[w] = src1->b[w] | src2->b[w];
+}
+
+/* intersection */
+void
+bvand(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+ int32 i, w;
+
+ if(dst->n != src1->n || dst->n != src2->n)
+ fatal("bvor: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+ for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+ dst->b[w] = src1->b[w] & src2->b[w];
+}
+
+void
+bvprint(Bvec *bv)
+{
+ int32 i;
+
+ print("#*");
+ for(i = 0; i < bv->n; i++)
+ print("%d", bvget(bv, i));
+}
+
+void
+bvreset(Bvec *bv, int32 i)
+{
+ uint32 mask;
+
+ if(i < 0 || i >= bv->n)
+ fatal("bvreset: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = ~(1 << (i % WORDBITS));
+ bv->b[i / WORDBITS] &= mask;
+}
+
+void
+bvresetall(Bvec *bv)
+{
+ memset(bv->b, 0x00, bvsize(bv->n));
+}
+
+void
+bvset(Bvec *bv, int32 i)
+{
+ uint32 mask;
+
+ if(i < 0 || i >= bv->n)
+ fatal("bvset: index %d is out of bounds with length %d\n", i, bv->n);
+ mask = 1U << (i % WORDBITS);
+ bv->b[i / WORDBITS] |= mask;
+}
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
new file mode 100644
index 0000000..ad4e5bd
--- /dev/null
+++ b/src/cmd/gc/closure.c
@@ -0,0 +1,467 @@
+// 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 <u.h>
+#include <libc.h>
+#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;
+ func->nbody = body;
+ func->endlineno = lineno;
+ 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;
+}
+
+static Node* makeclosure(Node *func);
+
+void
+typecheckclosure(Node *func, int top)
+{
+ Node *oldfn;
+ NodeList *l;
+ Node *v;
+
+ oldfn = curfn;
+ typecheck(&func->ntype, Etype);
+ func->type = func->ntype->type;
+
+ // Type check the body now, but only if we're inside a function.
+ // At top level (in a variable initialization: curfn==nil) we're not
+ // ready to type check code yet; we'll check it later, because the
+ // underlying closure function we create is added to xtop.
+ if(curfn && 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;
+ }
+
+ // Create top-level function
+ xtop = list(xtop, makeclosure(func));
+}
+
+static Node*
+makeclosure(Node *func)
+{
+ Node *xtype, *v, *addr, *xfunc, *cv;
+ NodeList *l, *body;
+ static int closgen;
+ char *p;
+ vlong offset;
+
+ /*
+ * wrap body in external function
+ * that begins by reading closure parameters.
+ */
+ xtype = nod(OTFUNC, N, N);
+ xtype->list = func->list;
+ 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->sym->flags |= SymExported; // disable export
+ xfunc->nname->ntype = xtype;
+ xfunc->nname->defn = xfunc;
+ declare(xfunc->nname, PFUNC);
+ xfunc->nname->funcdepth = func->funcdepth;
+ xfunc->funcdepth = func->funcdepth;
+ xfunc->endlineno = func->endlineno;
+
+ // declare variables holding addresses taken from closure
+ // and initialize in entry prologue.
+ body = nil;
+ offset = widthptr;
+ xfunc->needctxt = func->cvars != nil;
+ 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 = PAUTO;
+ addr->addable = 1;
+ addr->ullman = 1;
+ addr->used = 1;
+ addr->curfn = xfunc;
+ xfunc->dcl = list(xfunc->dcl, addr);
+ v->heapaddr = addr;
+ cv = nod(OCLOSUREVAR, N, N);
+ cv->type = ptrto(v->type);
+ cv->xoffset = offset;
+ body = list(body, nod(OAS, addr, cv));
+ offset += widthptr;
+ }
+ typechecklist(body, Etop);
+ walkstmtlist(body);
+ xfunc->enter = body;
+
+ xfunc->nbody = func->nbody;
+ xfunc->dcl = concat(func->dcl, xfunc->dcl);
+ if(xfunc->nbody == nil)
+ fatal("empty body - won't generate any code");
+ typecheck(&xfunc, Etop);
+
+ xfunc->closure = func;
+ func->closure = xfunc;
+
+ func->nbody = nil;
+ func->list = nil;
+ func->rlist = nil;
+
+ return xfunc;
+}
+
+Node*
+walkclosure(Node *func, NodeList **init)
+{
+ Node *clos, *typ;
+ NodeList *l;
+ char buf[20];
+ int narg;
+
+ // If no closure vars, don't bother wrapping.
+ if(func->cvars == nil)
+ return func->closure->nname;
+
+ // Create closure in the form of a composite literal.
+ // supposing the closure captures an int i and a string s
+ // and has one float64 argument and no results,
+ // the generated code looks like:
+ //
+ // clos = &struct{F uintptr; A0 *int; A1 *string}{func·001, &i, &s}
+ //
+ // The use of the struct provides type information to the garbage
+ // collector so that it can walk the closure. We could use (in this case)
+ // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
+ // The information appears in the binary in the form of type descriptors;
+ // the struct is unnamed so that closures in multiple packages with the
+ // same struct type can share the descriptor.
+
+ narg = 0;
+ typ = nod(OTSTRUCT, N, N);
+ typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
+ for(l=func->cvars; l; l=l->next) {
+ if(l->n->op == 0)
+ continue;
+ snprint(buf, sizeof buf, "A%d", narg++);
+ typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype));
+ }
+
+ clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
+ clos->esc = func->esc;
+ clos->right->implicit = 1;
+ clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter);
+
+ // Force type conversion from *struct to the func type.
+ clos = nod(OCONVNOP, clos, N);
+ clos->type = func->type;
+
+ typecheck(&clos, Erv);
+ // typecheck will insert a PTRLIT node under CONVNOP,
+ // tag it with escape analysis result.
+ clos->left->esc = func->esc;
+ // non-escaping temp to use, if any.
+ // orderexpr did not compute the type; fill it in now.
+ if(func->alloc != N) {
+ func->alloc->type = clos->left->left->type;
+ func->alloc->orig->type = func->alloc->type;
+ clos->left->right = func->alloc;
+ func->alloc = N;
+ }
+ walkexpr(&clos, init);
+
+ return clos;
+}
+
+static Node *makepartialcall(Node*, Type*, Node*);
+
+void
+typecheckpartialcall(Node *fn, Node *sym)
+{
+ switch(fn->op) {
+ case ODOTINTER:
+ case ODOTMETH:
+ break;
+ default:
+ fatal("invalid typecheckpartialcall");
+ }
+
+ // Create top-level function.
+ fn->nname = makepartialcall(fn, fn->type, sym);
+ fn->right = sym;
+ fn->op = OCALLPART;
+ fn->type = fn->nname->type;
+}
+
+static Node*
+makepartialcall(Node *fn, Type *t0, Node *meth)
+{
+ Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv, *savecurfn;
+ Type *rcvrtype, *basetype, *t;
+ NodeList *body, *l, *callargs, *retargs;
+ char *p;
+ Sym *sym;
+ Pkg *spkg;
+ static Pkg* gopkg;
+ int i, ddd;
+
+ // TODO: names are not right
+ rcvrtype = fn->left->type;
+ if(exportname(meth->sym->name))
+ p = smprint("%-hT.%s·fm", rcvrtype, meth->sym->name);
+ else
+ p = smprint("%-hT.(%-S)·fm", rcvrtype, meth->sym);
+ basetype = rcvrtype;
+ if(isptr[rcvrtype->etype])
+ basetype = basetype->type;
+ if(basetype->etype != TINTER && basetype->sym == S)
+ fatal("missing base type for %T", rcvrtype);
+
+ spkg = nil;
+ if(basetype->sym != S)
+ spkg = basetype->sym->pkg;
+ if(spkg == nil) {
+ if(gopkg == nil)
+ gopkg = mkpkg(strlit("go"));
+ spkg = gopkg;
+ }
+ sym = pkglookup(p, spkg);
+ free(p);
+ if(sym->flags & SymUniq)
+ return sym->def;
+ sym->flags |= SymUniq;
+
+ savecurfn = curfn;
+ curfn = N;
+
+ xtype = nod(OTFUNC, N, N);
+ i = 0;
+ l = nil;
+ callargs = nil;
+ ddd = 0;
+ xfunc = nod(ODCLFUNC, N, N);
+ curfn = xfunc;
+ for(t = getinargx(t0)->type; t; t = t->down) {
+ snprint(namebuf, sizeof namebuf, "a%d", i++);
+ n = newname(lookup(namebuf));
+ n->class = PPARAM;
+ xfunc->dcl = list(xfunc->dcl, n);
+ callargs = list(callargs, n);
+ fld = nod(ODCLFIELD, n, typenod(t->type));
+ if(t->isddd) {
+ fld->isddd = 1;
+ ddd = 1;
+ }
+ l = list(l, fld);
+ }
+ xtype->list = l;
+ i = 0;
+ l = nil;
+ retargs = nil;
+ for(t = getoutargx(t0)->type; t; t = t->down) {
+ snprint(namebuf, sizeof namebuf, "r%d", i++);
+ n = newname(lookup(namebuf));
+ n->class = PPARAMOUT;
+ xfunc->dcl = list(xfunc->dcl, n);
+ retargs = list(retargs, n);
+ l = list(l, nod(ODCLFIELD, n, typenod(t->type)));
+ }
+ xtype->rlist = l;
+
+ xfunc->dupok = 1;
+ xfunc->nname = newname(sym);
+ xfunc->nname->sym->flags |= SymExported; // disable export
+ xfunc->nname->ntype = xtype;
+ xfunc->nname->defn = xfunc;
+ declare(xfunc->nname, PFUNC);
+
+ // Declare and initialize variable holding receiver.
+ body = nil;
+ xfunc->needctxt = 1;
+ cv = nod(OCLOSUREVAR, N, N);
+ cv->xoffset = widthptr;
+ cv->type = rcvrtype;
+ if(cv->type->align > widthptr)
+ cv->xoffset = cv->type->align;
+ ptr = nod(ONAME, N, N);
+ ptr->sym = lookup("rcvr");
+ ptr->class = PAUTO;
+ ptr->addable = 1;
+ ptr->ullman = 1;
+ ptr->used = 1;
+ ptr->curfn = xfunc;
+ xfunc->dcl = list(xfunc->dcl, ptr);
+ if(isptr[rcvrtype->etype] || isinter(rcvrtype)) {
+ ptr->ntype = typenod(rcvrtype);
+ body = list(body, nod(OAS, ptr, cv));
+ } else {
+ ptr->ntype = typenod(ptrto(rcvrtype));
+ body = list(body, nod(OAS, ptr, nod(OADDR, cv, N)));
+ }
+
+ call = nod(OCALL, nod(OXDOT, ptr, meth), N);
+ call->list = callargs;
+ call->isddd = ddd;
+ if(t0->outtuple == 0) {
+ body = list(body, call);
+ } else {
+ n = nod(OAS2, N, N);
+ n->list = retargs;
+ n->rlist = list1(call);
+ body = list(body, n);
+ n = nod(ORETURN, N, N);
+ body = list(body, n);
+ }
+
+ xfunc->nbody = body;
+
+ typecheck(&xfunc, Etop);
+ sym->def = xfunc;
+ xtop = list(xtop, xfunc);
+ curfn = savecurfn;
+
+ return xfunc;
+}
+
+Node*
+walkpartialcall(Node *n, NodeList **init)
+{
+ Node *clos, *typ;
+
+ // Create closure in the form of a composite literal.
+ // For x.M with receiver (x) type T, the generated code looks like:
+ //
+ // clos = &struct{F uintptr; R T}{M.T·f, x}
+ //
+ // Like walkclosure above.
+
+ if(isinter(n->left->type)) {
+ // Trigger panic for method on nil interface now.
+ // Otherwise it happens in the wrapper and is confusing.
+ n->left = cheapexpr(n->left, init);
+ checknil(n->left, init);
+ }
+
+ typ = nod(OTSTRUCT, N, N);
+ typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
+ typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup("R")), typenod(n->left->type)));
+
+ clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
+ clos->esc = n->esc;
+ clos->right->implicit = 1;
+ clos->list = list1(nod(OCFUNC, n->nname->nname, N));
+ clos->list = list(clos->list, n->left);
+
+ // Force type conversion from *struct to the func type.
+ clos = nod(OCONVNOP, clos, N);
+ clos->type = n->type;
+
+ typecheck(&clos, Erv);
+ // typecheck will insert a PTRLIT node under CONVNOP,
+ // tag it with escape analysis result.
+ clos->left->esc = n->esc;
+ // non-escaping temp to use, if any.
+ // orderexpr did not compute the type; fill it in now.
+ if(n->alloc != N) {
+ n->alloc->type = clos->left->left->type;
+ n->alloc->orig->type = n->alloc->type;
+ clos->left->right = n->alloc;
+ n->alloc = N;
+ }
+ walkexpr(&clos, init);
+
+ return clos;
+}
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
new file mode 100644
index 0000000..e418b9c
--- /dev/null
+++ b/src/cmd/gc/const.c
@@ -0,0 +1,1674 @@
+// 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 "go.h"
+#define TUP(x,y) (((x)<<16)|(y))
+/*c2go int TUP(int, int); */
+
+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;
+ Mpflt *fv;
+ Val v;
+
+ if(t == T)
+ return oldv;
+
+ memset(&v, 0, sizeof v);
+ v.ctype = CTFLT;
+ v.u.fval = oldv;
+ overflow(v, t);
+
+ fv = mal(sizeof *fv);
+ *fv = *oldv;
+
+ // convert large precision literal floating
+ // into limited precision (float64 or float32)
+ switch(t->etype) {
+ case TFLOAT64:
+ d = mpgetflt(fv);
+ mpmovecflt(fv, d);
+ break;
+
+ case TFLOAT32:
+ d = mpgetflt32(fv);
+ 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 == idealbool) {
+ if(t->etype == TBOOL)
+ n->type = t;
+ else
+ n->type = types[TBOOL];
+ }
+ 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;
+ case OCOMPLEX:
+ if(n->type->etype == TIDEAL) {
+ switch(t->etype) {
+ default:
+ // If trying to convert to non-complex type,
+ // leave as complex128 and let typechecker complain.
+ t = types[TCOMPLEX128];
+ //fallthrough
+ case TCOMPLEX128:
+ n->type = t;
+ convlit(&n->left, types[TFLOAT64]);
+ convlit(&n->right, types[TFLOAT64]);
+ break;
+ case TCOMPLEX64:
+ n->type = t;
+ convlit(&n->left, types[TFLOAT32]);
+ convlit(&n->right, types[TFLOAT32]);
+ break;
+ }
+ }
+ 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;
+
+ case TUINTPTR:
+ // A nil literal may be converted to uintptr
+ // if it is an unsafe.Pointer
+ if(n->type->etype == TUNSAFEPTR) {
+ n->val.u.xval = mal(sizeof(*n->val.u.xval));
+ mpmovecfix(n->val.u.xval, 0);
+ n->val.ctype = CTINT;
+ } else
+ goto bad;
+ }
+ break;
+
+ case CTSTR:
+ case CTBOOL:
+ if(et != n->type->etype)
+ goto bad;
+ break;
+
+ case CTINT:
+ case CTRUNE:
+ case CTFLT:
+ case CTCPLX:
+ ct = n->val.ctype;
+ if(isint[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTCPLX:
+ case CTFLT:
+ case CTRUNE:
+ 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:
+ case CTRUNE:
+ n->val = toflt(n->val);
+ // flowthrough
+ case CTFLT:
+ n->val.u.fval = truncfltlit(n->val.u.fval, t);
+ break;
+ }
+ } else
+ if(iscomplex[et]) {
+ switch(ct) {
+ default:
+ goto bad;
+ case CTFLT:
+ case CTINT:
+ case CTRUNE:
+ n->val = tocplx(n->val);
+ break;
+ case CTCPLX:
+ overflow(n->val, t);
+ break;
+ }
+ } else
+ if(et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit)
+ n->val = tostr(n->val);
+ else
+ goto bad;
+ break;
+ }
+ n->type = t;
+ return;
+
+bad:
+ if(!n->diag) {
+ if(!t->broke)
+ 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:
+ case CTRUNE:
+ 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:
+ case CTRUNE:
+ 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:
+ case CTRUNE:
+ 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 CTRUNE:
+ v.ctype = CTINT;
+ break;
+ 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;
+}
+
+int
+doesoverflow(Val v, Type *t)
+{
+ switch(v.ctype) {
+ case CTINT:
+ case CTRUNE:
+ 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)
+ return 1;
+ 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)
+ return 1;
+ 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)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+void
+overflow(Val v, Type *t)
+{
+ // v has already been converted
+ // to appropriate form for t.
+ if(t == T || t->etype == TIDEAL)
+ return;
+
+ if(!doesoverflow(v, t))
+ return;
+
+ switch(v.ctype) {
+ case CTINT:
+ case CTRUNE:
+ yyerror("constant %B overflows %T", v.u.xval, t);
+ break;
+ case CTFLT:
+ yyerror("constant %#F overflows %T", v.u.fval, t);
+ break;
+ case CTCPLX:
+ 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:
+ case CTRUNE:
+ 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)
+{
+ int t;
+
+ t = consttype(n);
+ // If the caller is asking for CTINT, allow CTRUNE too.
+ // Makes life easier for back ends.
+ return t == ct || (ct == CTINT && t == CTRUNE);
+}
+
+static Node*
+saveorig(Node *n)
+{
+ Node *n1;
+
+ if(n == n->orig) {
+ // duplicate node for n->orig.
+ n1 = nod(OLITERAL, N, N);
+ n->orig = n1;
+ *n1 = *n;
+ }
+ return n->orig;
+}
+
+/*
+ * if n is constant, rewrite as OLITERAL node.
+ */
+void
+evconst(Node *n)
+{
+ Node *nl, *nr, *norig;
+ int32 len;
+ Strlit *str;
+ int wl, wr, lno, et;
+ Val v, rv;
+ Mpint b;
+ NodeList *l1, *l2;
+
+ // pick off just the opcodes that can be
+ // constant evaluated.
+ switch(n->op) {
+ default:
+ return;
+ case OADD:
+ 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;
+
+ case OADDSTR:
+ // merge adjacent constants in the argument list.
+ for(l1=n->list; l1 != nil; l1= l1->next) {
+ if(isconst(l1->n, CTSTR) && l1->next != nil && isconst(l1->next->n, CTSTR)) {
+ l2 = l1;
+ len = 0;
+ while(l2 != nil && isconst(l2->n, CTSTR)) {
+ nr = l2->n;
+ len += nr->val.u.sval->len;
+ l2 = l2->next;
+ }
+ // merge from l1 up to but not including l2
+ str = mal(sizeof(*str) + len);
+ str->len = len;
+ len = 0;
+ l2 = l1;
+ while(l2 != nil && isconst(l2->n, CTSTR)) {
+ nr = l2->n;
+ memmove(str->s+len, nr->val.u.sval->s, nr->val.u.sval->len);
+ len += nr->val.u.sval->len;
+ l2 = l2->next;
+ }
+ nl = nod(OXXX, N, N);
+ *nl = *l1->n;
+ nl->orig = nl;
+ nl->val.ctype = CTSTR;
+ nl->val.u.sval = str;
+ l1->n = nl;
+ l1->next = l2;
+ }
+ }
+ // fix list end pointer.
+ for(l2=n->list; l2 != nil; l2=l2->next)
+ n->list->end = l2;
+ // collapse single-constant list to single constant.
+ if(count(n->list) == 1 && isconst(n->list->n, CTSTR)) {
+ n->op = OLITERAL;
+ n->val = n->list->n->val;
+ }
+ return;
+ }
+
+ 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;
+ if(nl->val.ctype != CTRUNE)
+ 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);
+ }
+
+ // Rune and int turns into rune.
+ if(v.ctype == CTRUNE && rv.ctype == CTINT)
+ rv.ctype = CTRUNE;
+ if(v.ctype == CTINT && rv.ctype == CTRUNE) {
+ if(n->op == OLSH || n->op == ORSH)
+ rv.ctype = CTINT;
+ else
+ v.ctype = CTRUNE;
+ }
+
+ 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):
+ case TUP(OADD, CTRUNE):
+ mpaddfixfix(v.u.xval, rv.u.xval, 0);
+ break;
+ case TUP(OSUB, CTINT):
+ case TUP(OSUB, CTRUNE):
+ mpsubfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OMUL, CTINT):
+ case TUP(OMUL, CTRUNE):
+ mpmulfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ODIV, CTINT):
+ case TUP(ODIV, CTRUNE):
+ 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):
+ case TUP(OMOD, CTRUNE):
+ 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):
+ case TUP(OLSH, CTRUNE):
+ mplshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(ORSH, CTINT):
+ case TUP(ORSH, CTRUNE):
+ mprshfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OOR, CTINT):
+ case TUP(OOR, CTRUNE):
+ mporfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OAND, CTINT):
+ case TUP(OAND, CTRUNE):
+ mpandfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OANDNOT, CTINT):
+ case TUP(OANDNOT, CTRUNE):
+ mpandnotfixfix(v.u.xval, rv.u.xval);
+ break;
+ case TUP(OXOR, CTINT):
+ case TUP(OXOR, CTRUNE):
+ 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(OMOD, CTFLT):
+ // The default case above would print 'ideal % ideal',
+ // which is not quite an ideal error.
+ if(!n->diag) {
+ yyerror("illegal constant expression: floating-point %% operation");
+ n->diag = 1;
+ }
+ return;
+
+ 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):
+ case TUP(OEQ, CTRUNE):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(ONE, CTINT):
+ case TUP(ONE, CTRUNE):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLT, CTINT):
+ case TUP(OLT, CTRUNE):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OLE, CTINT):
+ case TUP(OLE, CTRUNE):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGE, CTINT):
+ case TUP(OGE, CTRUNE):
+ if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0)
+ goto settrue;
+ goto setfalse;
+ case TUP(OGT, CTINT):
+ case TUP(OGT, CTRUNE):
+ 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(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, CTRUNE):
+ case TUP(OCONV, CTFLT):
+ case TUP(OCONV, CTSTR):
+ convlit1(&nl, n->type, 1);
+ v = nl->val;
+ break;
+
+ case TUP(OPLUS, CTINT):
+ case TUP(OPLUS, CTRUNE):
+ break;
+ case TUP(OMINUS, CTINT):
+ case TUP(OMINUS, CTRUNE):
+ mpnegfix(v.u.xval);
+ break;
+ case TUP(OCOM, CTINT):
+ case TUP(OCOM, CTRUNE):
+ 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:
+ norig = saveorig(n);
+ *n = *nl;
+ // restore value of n->orig.
+ n->orig = norig;
+ 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:
+ norig = saveorig(n);
+ *n = *nodbool(1);
+ n->orig = norig;
+ return;
+
+setfalse:
+ norig = saveorig(n);
+ *n = *nodbool(0);
+ n->orig = norig;
+ 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 CTRUNE:
+ 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;
+}
+
+// idealkind returns a constant kind like consttype
+// but for an arbitrary "ideal" (untyped constant) expression.
+static int
+idealkind(Node *n)
+{
+ int k1, k2;
+
+ if(n == N || !isideal(n->type))
+ return CTxxx;
+
+ switch(n->op) {
+ default:
+ return CTxxx;
+ case OLITERAL:
+ return n->val.ctype;
+ case OADD:
+ case OAND:
+ case OANDNOT:
+ case OCOM:
+ case ODIV:
+ case OMINUS:
+ case OMOD:
+ case OMUL:
+ case OSUB:
+ case OXOR:
+ case OOR:
+ case OPLUS:
+ // numeric kinds.
+ k1 = idealkind(n->left);
+ k2 = idealkind(n->right);
+ if(k1 > k2)
+ return k1;
+ else
+ return k2;
+ case OREAL:
+ case OIMAG:
+ return CTFLT;
+ case OCOMPLEX:
+ return CTCPLX;
+ case OADDSTR:
+ return CTSTR;
+ case OANDAND:
+ case OEQ:
+ case OGE:
+ case OGT:
+ case OLE:
+ case OLT:
+ case ONE:
+ case ONOT:
+ case OOROR:
+ case OCMPSTR:
+ case OCMPIFACE:
+ return CTBOOL;
+ case OLSH:
+ case ORSH:
+ // shifts (beware!).
+ return idealkind(n->left);
+ }
+}
+
+void
+defaultlit(Node **np, Type *t)
+{
+ int lno;
+ int ctype;
+ Node *n, *nn;
+ Type *t1;
+
+ n = *np;
+ if(n == N || !isideal(n->type))
+ return;
+
+ if(n->op == OLITERAL) {
+ nn = nod(OXXX, N, N);
+ *nn = *n;
+ n = nn;
+ *np = n;
+ }
+
+ lno = setlineno(n);
+ ctype = idealkind(n);
+ switch(ctype) {
+ default:
+ if(t != T) {
+ convlit(np, t);
+ return;
+ }
+ if(n->val.ctype == CTNIL) {
+ lineno = lno;
+ if(!n->diag) {
+ yyerror("use of untyped nil");
+ n->diag = 1;
+ }
+ n->type = T;
+ break;
+ }
+ if(n->val.ctype == CTSTR) {
+ t1 = types[TSTRING];
+ convlit(np, t1);
+ break;
+ }
+ yyerror("defaultlit: unknown literal: %N", n);
+ break;
+ case CTxxx:
+ fatal("defaultlit: idealkind is CTxxx: %+N", n);
+ break;
+ case CTBOOL:
+ t1 = types[TBOOL];
+ if(t != T && t->etype == TBOOL)
+ t1 = t;
+ convlit(np, t1);
+ break;
+ case CTINT:
+ t1 = types[TINT];
+ goto num;
+ case CTRUNE:
+ t1 = runetype;
+ goto num;
+ case CTFLT:
+ t1 = types[TFLOAT64];
+ goto num;
+ case CTCPLX:
+ t1 = types[TCOMPLEX128];
+ goto num;
+ num:
+ if(t != T) {
+ if(isint[t->etype]) {
+ t1 = t;
+ n->val = toint(n->val);
+ }
+ else
+ if(isfloat[t->etype]) {
+ t1 = t;
+ n->val = toflt(n->val);
+ }
+ else
+ if(iscomplex[t->etype]) {
+ t1 = t;
+ n->val = tocplx(n->val);
+ }
+ }
+ overflow(n->val, t1);
+ convlit(np, t1);
+ 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;
+ int lkind, rkind;
+
+ 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(l->type->etype == TBOOL) {
+ convlit(lp, types[TBOOL]);
+ convlit(rp, types[TBOOL]);
+ }
+ lkind = idealkind(l);
+ rkind = idealkind(r);
+ if(lkind == CTCPLX || rkind == CTCPLX) {
+ convlit(lp, types[TCOMPLEX128]);
+ convlit(rp, types[TCOMPLEX128]);
+ return;
+ }
+ if(lkind == CTFLT || rkind == CTFLT) {
+ convlit(lp, types[TFLOAT64]);
+ convlit(rp, types[TFLOAT64]);
+ return;
+ }
+
+ if(lkind == CTRUNE || rkind == CTRUNE) {
+ convlit(lp, runetype);
+ convlit(rp, runetype);
+ 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 && isconst(n, CTINT) && 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 TIDEAL:
+ case TINT64:
+ case TUINT64:
+ case TPTR64:
+ 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:
+ case CTRUNE:
+ 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)
+}
+
+static int hascallchan(Node*);
+
+// Is n a Go language constant (as opposed to a compile-time constant)?
+// Expressions derived from nil, like string([]byte(nil)), while they
+// may be known at compile time, are not Go language constants.
+// Only called for expressions known to evaluated to compile-time
+// constants.
+int
+isgoconst(Node *n)
+{
+ Node *l;
+ Type *t;
+
+ if(n->orig != N)
+ n = n->orig;
+
+ switch(n->op) {
+ case OADD:
+ case OADDSTR:
+ case OAND:
+ case OANDAND:
+ case OANDNOT:
+ 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:
+ case OIOTA:
+ case OCOMPLEX:
+ case OREAL:
+ case OIMAG:
+ if(isgoconst(n->left) && (n->right == N || isgoconst(n->right)))
+ return 1;
+ break;
+
+ case OCONV:
+ if(okforconst[n->type->etype] && isgoconst(n->left))
+ return 1;
+ break;
+
+ case OLEN:
+ case OCAP:
+ l = n->left;
+ if(isgoconst(l))
+ return 1;
+ // Special case: len/cap is constant when applied to array or
+ // pointer to array when the expression does not contain
+ // function calls or channel receive operations.
+ t = l->type;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t) && !hascallchan(l))
+ return 1;
+ break;
+
+ case OLITERAL:
+ if(n->val.ctype != CTNIL)
+ return 1;
+ break;
+
+ case ONAME:
+ l = n->sym->def;
+ if(l && l->op == OLITERAL && n->val.ctype != CTNIL)
+ return 1;
+ break;
+
+ case ONONAME:
+ if(n->sym->def != N && n->sym->def->op == OIOTA)
+ return 1;
+ break;
+
+ case OCALL:
+ // Only constant calls are unsafe.Alignof, Offsetof, and Sizeof.
+ l = n->left;
+ while(l->op == OPAREN)
+ l = l->left;
+ if(l->op != ONAME || l->sym->pkg != unsafepkg)
+ break;
+ if(strcmp(l->sym->name, "Alignof") == 0 ||
+ strcmp(l->sym->name, "Offsetof") == 0 ||
+ strcmp(l->sym->name, "Sizeof") == 0)
+ return 1;
+ break;
+ }
+
+ //dump("nonconst", n);
+ return 0;
+}
+
+static int
+hascallchan(Node *n)
+{
+ NodeList *l;
+
+ if(n == N)
+ return 0;
+ switch(n->op) {
+ case OAPPEND:
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ case OCAP:
+ case OCLOSE:
+ case OCOMPLEX:
+ case OCOPY:
+ case ODELETE:
+ case OIMAG:
+ case OLEN:
+ case OMAKE:
+ case ONEW:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ case OREAL:
+ case ORECOVER:
+ case ORECV:
+ return 1;
+ }
+
+ if(hascallchan(n->left) ||
+ hascallchan(n->right))
+ return 1;
+
+ for(l=n->list; l; l=l->next)
+ if(hascallchan(l->n))
+ return 1;
+ for(l=n->rlist; l; l=l->next)
+ if(hascallchan(l->n))
+ return 1;
+
+ return 0;
+}
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
new file mode 100644
index 0000000..c9bab7a
--- /dev/null
+++ b/src/cmd/gc/cplx.c
@@ -0,0 +1,486 @@
+// 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 "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, tmp;
+
+ 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(&tmp, f->type);
+ complexmove(f, &tmp);
+ f = &tmp;
+ }
+
+ 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);
+ }
+
+ while(n->op == OCONVNOP)
+ n = n->left;
+
+ // 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:
+ case OCALLMETH:
+ case OCALLINTER:
+ 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, int likely, 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, likely, 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 0000000..dfcf475
--- /dev/null
+++ b/src/cmd/gc/dcl.c
@@ -0,0 +1,1494 @@
+// 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 "go.h"
+#include "y.tab.h"
+
+static void funcargs(Node*);
+static void funcargs2(Type*);
+
+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;
+
+ USED(st);
+
+ 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(" %S\n", s);
+ }
+}
+
+void
+testdclstack(void)
+{
+ Sym *d;
+
+ for(d=dclstack; d!=S; d=d->link) {
+ if(d->name == nil) {
+ if(nerrors != 0)
+ errorexit();
+ yyerror("mark left on the stack");
+ continue;
+ }
+ }
+}
+
+void
+redeclare(Sym *s, char *where)
+{
+ Strlit *pkgstr;
+ int line1, line2;
+
+ if(s->lastlineno == 0) {
+ pkgstr = s->origpkg ? s->origpkg->path : s->pkg->path;
+ yyerror("%S redeclared %s\n"
+ "\tprevious declaration during import \"%Z\"",
+ s, where, pkgstr);
+ } else {
+ line1 = parserline();
+ line2 = s->lastlineno;
+
+ // When an import and a declaration collide in separate files,
+ // present the import as the "redeclared", because the declaration
+ // is visible where the import is, but not vice versa.
+ // See issue 4510.
+ if(s->def == N) {
+ line2 = line1;
+ line1 = s->lastlineno;
+ }
+
+ yyerrorl(line1, "%S redeclared %s\n"
+ "\tprevious declaration at %L",
+ s, where, line2);
+ }
+}
+
+static int vargen;
+
+/*
+ * declare individual names - var, typ, const
+ */
+void
+declare(Node *n, int ctxt)
+{
+ Sym *s;
+ int gen;
+ static int typegen;
+
+ if(ctxt == PDISCARD)
+ return;
+
+ if(isblank(n))
+ return;
+
+ n->lineno = parserline();
+ s = n->sym;
+
+ // kludgy: typecheckok means we're past parsing. Eg genwrapper may declare out of package names later.
+ if(importpkg == nil && !typecheckok && s->pkg != localpkg)
+ yyerror("cannot declare name %S", s);
+
+ if(ctxt == PEXTERN && strcmp(s->name, "init") == 0)
+ yyerror("cannot declare init - must be func", s);
+
+ 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 && ctxt == PAUTO && strstr(s->name, "·") == nil)
+ gen = ++vargen;
+ pushdcl(s);
+ n->curfn = curfn;
+ }
+ if(ctxt == PAUTO)
+ n->xoffset = 0;
+
+ if(s->block == block) {
+ // functype will print errors about duplicate function arguments.
+ // Don't repeat the error here.
+ if(ctxt != PPARAM && ctxt != PPARAMOUT)
+ 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 expression in var declaration");
+ 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 expression in var declaration");
+ 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("const declaration cannot have type without expression");
+ cl = lastconst;
+ t = lasttype;
+ } else {
+ lastconst = cl;
+ lasttype = t;
+ }
+ cl = listtreecopy(cl);
+
+ for(; vl; vl=vl->next) {
+ if(cl == nil) {
+ yyerror("missing value in const declaration");
+ 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 expression in const declaration");
+ 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;
+ n->addrtaken = 1;
+ c->closure = n;
+ c->xoffset = 0;
+ curfn->cvars = list(curfn->cvars, c);
+ }
+ // return ref to closure var, not original
+ return n->closure;
+ }
+ return n;
+}
+
+/*
+ * := 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;
+
+ for(l=left; l; l=l->next)
+ if(l->n->sym != S)
+ l->n->sym->flags |= SymUniq;
+
+ nnew = 0;
+ nerr = 0;
+ for(l=left; l; l=l->next) {
+ n = l->n;
+ if(isblank(n))
+ continue;
+ if(!colasname(n)) {
+ yyerrorl(defn->lineno, "non-name %N on left side of :=", n);
+ nerr++;
+ continue;
+ }
+ if((n->sym->flags & SymUniq) == 0) {
+ yyerrorl(defn->lineno, "%S repeated on left side of :=", n->sym);
+ n->diag++;
+ nerr++;
+ continue;
+ }
+ n->sym->flags &= ~SymUniq;
+ 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)
+ yyerrorl(defn->lineno, "no new variables on left side of :=");
+}
+
+Node*
+colas(NodeList *left, NodeList *right, int32 lno)
+{
+ Node *as;
+
+ as = nod(OAS2, N, N);
+ as->list = left;
+ as->rlist = right;
+ as->colas = 1;
+ as->lineno = lno;
+ 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");
+
+ if(isblank(n->left))
+ yyerror("methods must have a unique non-blank name");
+
+ dclcontext = PPARAM;
+ 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.
+ dclcontext = PAUTO;
+ funcbody(n);
+}
+
+/*
+ * declare the function proper
+ * and declare the arguments.
+ * called in extern-declaration context
+ * returns in auto-declaration context.
+ */
+void
+funchdr(Node *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 if (n->ntype)
+ funcargs(n->ntype);
+ else
+ funcargs2(n->type);
+}
+
+static void
+funcargs(Node *nt)
+{
+ Node *n, *nn;
+ NodeList *l;
+ int gen;
+
+ if(nt->op != OTFUNC)
+ fatal("funcargs %O", nt->op);
+
+ // re-start the variable generation number
+ // we want to use small numbers for the return variables,
+ // so let them have the chunk starting at 1.
+ vargen = count(nt->rlist);
+
+ // declare the receiver and in arguments.
+ // no n->defn because type checking of func header
+ // will not fill in the types until later
+ if(nt->left != N) {
+ n = nt->left;
+ if(n->op != ODCLFIELD)
+ fatal("funcargs receiver %O", n->op);
+ if(n->left != N) {
+ n->left->op = ONAME;
+ n->left->ntype = n->right;
+ declare(n->left, PPARAM);
+ if(dclcontext == PAUTO)
+ n->left->vargen = ++vargen;
+ }
+ }
+ for(l=nt->list; l; l=l->next) {
+ n = l->n;
+ if(n->op != ODCLFIELD)
+ fatal("funcargs in %O", n->op);
+ if(n->left != N) {
+ n->left->op = ONAME;
+ n->left->ntype = n->right;
+ declare(n->left, PPARAM);
+ if(dclcontext == PAUTO)
+ n->left->vargen = ++vargen;
+ }
+ }
+
+ // declare the out arguments.
+ gen = count(nt->list);
+ int i = 0;
+ for(l=nt->rlist; l; l=l->next) {
+ n = l->n;
+
+ if(n->op != ODCLFIELD)
+ fatal("funcargs out %O", n->op);
+
+ if(n->left == N) {
+ // Name so that escape analysis can track it. ~r stands for 'result'.
+ snprint(namebuf, sizeof(namebuf), "~r%d", gen++);
+ n->left = newname(lookup(namebuf));
+ // TODO: n->left->missing = 1;
+ }
+
+ n->left->op = ONAME;
+
+ if(isblank(n->left)) {
+ // Give it a name so we can assign to it during return. ~b stands for 'blank'.
+ // The name must be different from ~r above because if you have
+ // func f() (_ int)
+ // func g() int
+ // f is allowed to use a plain 'return' with no arguments, while g is not.
+ // So the two cases must be distinguished.
+ // We do not record a pointer to the original node (n->orig).
+ // Having multiple names causes too much confusion in later passes.
+ nn = nod(OXXX, N, N);
+ *nn = *n->left;
+ nn->orig = nn;
+ snprint(namebuf, sizeof(namebuf), "~b%d", gen++);
+ nn->sym = lookup(namebuf);
+ n->left = nn;
+ }
+
+ n->left->ntype = n->right;
+ declare(n->left, PPARAMOUT);
+ if(dclcontext == PAUTO)
+ n->left->vargen = ++i;
+ }
+}
+
+/*
+ * Same as funcargs, except run over an already constructed TFUNC.
+ * This happens during import, where the hidden_fndcl rule has
+ * used functype directly to parse the function's type.
+ */
+static void
+funcargs2(Type *t)
+{
+ Type *ft;
+ Node *n;
+
+ if(t->etype != TFUNC)
+ fatal("funcargs2 %T", t);
+
+ if(t->thistuple)
+ for(ft=getthisx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname; // no need for newname(ft->nname->sym)
+ n->type = ft->type;
+ declare(n, PPARAM);
+ }
+
+ if(t->intuple)
+ for(ft=getinargx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname;
+ n->type = ft->type;
+ declare(n, PPARAM);
+ }
+
+ if(t->outtuple)
+ for(ft=getoutargx(t)->type; ft; ft=ft->down) {
+ if(!ft->nname || !ft->nname->sym)
+ continue;
+ n = ft->nname;
+ n->type = ft->type;
+ declare(n, 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 = newname(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);
+}
+
+/*
+ * structs, functions, and methods.
+ * they don't belong here, but where do they belong?
+ */
+
+static void
+checkembeddedtype(Type *t)
+{
+ if (t == T)
+ return;
+
+ if(t->sym == S && isptr[t->etype]) {
+ t = t->type;
+ if(t->etype == TINTER)
+ yyerror("embedded type cannot be a pointer to interface");
+ }
+ if(isptr[t->etype])
+ yyerror("embedded type cannot be a pointer");
+ else if(t->etype == TFORW && t->embedlineno == 0)
+ t->embedlineno = lineno;
+}
+
+static Type*
+structfield(Node *n)
+{
+ Type *f;
+ int lno;
+
+ lno = lineno;
+ lineno = n->lineno;
+
+ if(n->op != ODCLFIELD)
+ fatal("structfield: oops %N\n", n);
+
+ f = typ(TFIELD);
+ f->isddd = n->isddd;
+
+ if(n->right != N) {
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+ if(n->left != N)
+ n->left->type = n->type;
+ if(n->embedded)
+ checkembeddedtype(n->type);
+ }
+ n->right = N;
+
+ f->type = n->type;
+ if(f->type == T)
+ f->broke = 1;
+
+ switch(n->val.ctype) {
+ case CTSTR:
+ f->note = n->val.u.sval;
+ break;
+ default:
+ yyerror("field annotation must be string");
+ // fallthrough
+ case CTxxx:
+ f->note = nil;
+ break;
+ }
+
+ if(n->left && n->left->op == ONAME) {
+ f->nname = n->left;
+ f->embedded = n->embedded;
+ f->sym = f->nname->sym;
+ }
+
+ lineno = lno;
+ return f;
+}
+
+static uint32 uniqgen;
+
+static void
+checkdupfields(Type *t, char* what)
+{
+ int lno;
+
+ lno = lineno;
+
+ for( ; t; t=t->down) {
+ if(t->sym && t->nname && !isblank(t->nname)) {
+ if(t->sym->uniqgen == uniqgen) {
+ lineno = t->nname->lineno;
+ yyerror("duplicate %s %s", what, t->sym->name);
+ } else
+ t->sym->uniqgen = uniqgen;
+ }
+ }
+
+ lineno = lno;
+}
+
+/*
+ * convert a parsed id/type list into
+ * a type for struct/interface/arglist
+ */
+Type*
+tostruct(NodeList *l)
+{
+ Type *t, *f, **tp;
+ t = typ(TSTRUCT);
+
+ for(tp = &t->type; l; l=l->next) {
+ f = structfield(l->n);
+
+ *tp = f;
+ tp = &f->down;
+ }
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ uniqgen++;
+ checkdupfields(t->type, "field");
+
+ if (!t->broke)
+ checkwidth(t);
+
+ return t;
+}
+
+static Type*
+tofunargs(NodeList *l)
+{
+ Type *t, *f, **tp;
+
+ t = typ(TSTRUCT);
+ t->funarg = 1;
+
+ for(tp = &t->type; l; l=l->next) {
+ f = structfield(l->n);
+ f->funarg = 1;
+
+ // esc.c needs to find f given a PPARAM to add the tag.
+ if(l->n->left && l->n->left->class == PPARAM)
+ l->n->left->paramfld = f;
+
+ *tp = f;
+ tp = &f->down;
+ }
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ return t;
+}
+
+static Type*
+interfacefield(Node *n)
+{
+ Type *f;
+ int lno;
+
+ lno = lineno;
+ lineno = n->lineno;
+
+ if(n->op != ODCLFIELD)
+ fatal("interfacefield: oops %N\n", n);
+
+ if (n->val.ctype != CTxxx)
+ yyerror("interface method cannot have annotation");
+
+ f = typ(TFIELD);
+ f->isddd = n->isddd;
+
+ if(n->right != N) {
+ if(n->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->left->type = n->type;
+ queuemethod(n);
+
+ if(n->left->op == ONAME) {
+ f->nname = n->left;
+ f->embedded = n->embedded;
+ f->sym = f->nname->sym;
+ }
+
+ } else {
+
+ typecheck(&n->right, Etype);
+ n->type = n->right->type;
+
+ if(n->embedded)
+ checkembeddedtype(n->type);
+
+ if(n->type)
+ switch(n->type->etype) {
+ case TINTER:
+ break;
+ case TFORW:
+ yyerror("interface type loop involving %T", n->type);
+ f->broke = 1;
+ break;
+ default:
+ yyerror("interface contains embedded non-interface %T", n->type);
+ f->broke = 1;
+ break;
+ }
+ }
+ }
+
+ n->right = N;
+
+ f->type = n->type;
+ if(f->type == T)
+ f->broke = 1;
+
+ lineno = lno;
+ return f;
+}
+
+Type*
+tointerface(NodeList *l)
+{
+ Type *t, *f, **tp, *t1;
+
+ t = typ(TINTER);
+
+ tp = &t->type;
+ for(; l; l=l->next) {
+ f = interfacefield(l->n);
+
+ if (l->n->left == N && f->type->etype == TINTER) {
+ // embedded interface, inline methods
+ for(t1=f->type->type; t1; t1=t1->down) {
+ f = typ(TFIELD);
+ f->type = t1->type;
+ f->broke = t1->broke;
+ f->sym = t1->sym;
+ if(f->sym)
+ f->nname = newname(f->sym);
+ *tp = f;
+ tp = &f->down;
+ }
+ } else {
+ *tp = f;
+ tp = &f->down;
+ }
+ }
+
+ for(f=t->type; f && !t->broke; f=f->down)
+ if(f->broke)
+ t->broke = 1;
+
+ uniqgen++;
+ checkdupfields(t->type, "method");
+ t = sortinter(t);
+ checkwidth(t);
+
+ return t;
+}
+
+Node*
+embedded(Sym *s, Pkg *pkg)
+{
+ 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;
+ }
+
+ if(exportname(name))
+ n = newname(lookup(name));
+ else if(s->pkg == builtinpkg)
+ // The name of embedded builtins belongs to pkg.
+ n = newname(pkglookup(name, pkg));
+ else
+ n = newname(pkglookup(name, s->pkg));
+ 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;
+ }
+
+ // during import l->n->op is OKEY, but l->n->left->sym == S
+ // means it was a '?', not that it was
+ // a lone type This doesn't matter for the exported
+ // declarations, which are parsed by rules that don't
+ // use checkargs, but can happen for func literals in
+ // the inline bodies.
+ // TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ?
+ if(importpkg && n->sym == S)
+ 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;
+ Sym *s;
+
+ t = typ(TFUNC);
+
+ rcvr = nil;
+ if(this)
+ rcvr = list1(this);
+ t->type = tofunargs(rcvr);
+ t->type->down = tofunargs(out);
+ t->type->down->down = tofunargs(in);
+
+ uniqgen++;
+ checkdupfields(t->type->type, "argument");
+ checkdupfields(t->type->down->type, "argument");
+ checkdupfields(t->type->down->down->type, "argument");
+
+ if (t->type->broke || t->type->down->broke || t->type->down->down->broke)
+ t->broke = 1;
+
+ if(this)
+ t->thistuple = 1;
+ t->outtuple = count(out);
+ t->intuple = count(in);
+ t->outnamed = 0;
+ if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) {
+ s = out->n->left->orig->sym;
+ if(s != S && (s->name[0] != '~' || s->name[1] != 'r')) // ~r%d is the name invented for an unnamed result
+ t->outnamed = 1;
+ }
+
+ return t;
+}
+
+Sym*
+methodsym(Sym *nsym, Type *t0, int iface)
+{
+ Sym *s;
+ char *p;
+ Type *t;
+ char *suffix;
+ Pkg *spkg;
+ static Pkg *toppkg;
+
+ t = t0;
+ if(t == T)
+ goto bad;
+ s = t->sym;
+ if(s == S && isptr[t->etype]) {
+ t = t->type;
+ if(t == T)
+ goto bad;
+ s = t->sym;
+ }
+ spkg = nil;
+ if(s != S)
+ spkg = s->pkg;
+
+ // 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((spkg == nil || nsym->pkg != spkg) && !exportname(nsym->name)) {
+ if(t0->sym == S && isptr[t0->etype])
+ p = smprint("(%-hT).%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+ else
+ p = smprint("%-hT.%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+ } else {
+ 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);
+ }
+ if(spkg == nil) {
+ if(toppkg == nil)
+ toppkg = mkpkg(strlit("go"));
+ spkg = toppkg;
+ }
+ s = pkglookup(p, spkg);
+ 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);
+
+ if(exportname(t->sym->name))
+ n = newname(lookup(p));
+ else
+ 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, int nointerface)
+{
+ Type *f, *d, *pa;
+ Node *n;
+
+ // 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, 1);
+ if(f == T) {
+ t = pa;
+ if(t == T) // rely on typecheck having complained before
+ return;
+ 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->broke) // rely on typecheck having complained before
+ return;
+ 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 (%lT / %lT)", pa, pa, t);
+ return;
+ }
+
+ pa = f;
+ if(pa->etype == TSTRUCT) {
+ for(f=pa->type; f; f=f->down) {
+ if(f->sym == sf) {
+ yyerror("type %T has both field and method named %S", pa, sf);
+ return;
+ }
+ }
+ }
+
+ if(local && !pa->local) {
+ // defining method on non-local type.
+ yyerror("cannot define new methods on non-local type %T", pa);
+ return;
+ }
+
+ 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;
+ }
+
+ f = structfield(n);
+ f->nointerface = nointerface;
+
+ // during import unexported method names should be in the type's package
+ if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg)
+ fatal("imported method name %+S in wrong package %s\n", f->sym, structpkg->name);
+
+ if(d == T)
+ pa->method = f;
+ else
+ d->down = f;
+ 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.
+ // TODO(rsc): this is the old jit closure handling code.
+ // with the new closures, isclosure is always 0; delete this block.
+ 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;
+}
+
+Sym*
+funcsym(Sym *s)
+{
+ char *p;
+ Sym *s1;
+
+ p = smprint("%s·f", s->name);
+ s1 = pkglookup(p, s->pkg);
+ free(p);
+ if(s1->def == N) {
+ s1->def = newname(s1);
+ s1->def->shortname = newname(s);
+ funcsyms = list(funcsyms, s1->def);
+ }
+ return s1;
+}
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
new file mode 100644
index 0000000..03df93a
--- /dev/null
+++ b/src/cmd/gc/doc.go
@@ -0,0 +1,95 @@
+// 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.
+
+// +build ignore
+
+/*
+
+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.
+
+Command Line
+
+Usage:
+ go tool 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.
+ -pack
+ write an archive file rather than an object file
+ -e
+ normally the compiler quits after 10 errors; -e prints all errors
+ -p path
+ assume that path is the eventual import path for this code,
+ and diagnose any attempt to import a package that depends on it.
+ -D path
+ treat a relative import as relative to path
+ -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 optimizations
+ -nolocalimports
+ disallow local (relative) imports
+ -S
+ write assembly language text to standard output (code only)
+ -S -S
+ write assembly language text to standard output (code and data)
+ -u
+ disallow importing packages not marked as safe; implies -nolocalimports
+ -V
+ print the compiler version
+ -race
+ compile with race detection enabled
+
+There are also a number of debugging flags; run the command with no arguments
+to get a usage message.
+
+Compiler Directives
+
+The compiler accepts two compiler directives in the form of // comments at the
+beginning of a line. To distinguish them from non-directive comments, the directives
+require no space between the slashes and the name of the directive. However, since
+they are comments, tools unaware of the directive convention or of a particular
+directive can skip over a directive like any other comment.
+
+ //line path/to/file:linenumber
+
+The //line directive specifies that the source line that follows should be recorded
+as having come from the given file path and line number. Successive lines are
+recorded using increasing line numbers, until the next directive. This directive
+typically appears in machine-generated code, so that compilers and debuggers
+will show lines in the original input to the generator.
+
+ //go:noescape
+
+The //go:noescape directive specifies that the next declaration in the file, which
+must be a func without a body (meaning that it has an implementation not written
+in Go) does not allow any of the pointers passed as arguments to escape into the
+heap or into the values returned from the function. This information can be used as
+during the compiler's escape analysis of Go code calling the function.
+*/
+package main
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
new file mode 100644
index 0000000..324f24f
--- /dev/null
+++ b/src/cmd/gc/esc.c
@@ -0,0 +1,1281 @@
+// 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.
+
+// Escape analysis.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Run analysis on minimal sets of mutually recursive functions
+// or single non-recursive functions, bottom up.
+//
+// Finding these sets is finding strongly connected components
+// in the static call graph. The algorithm for doing that is taken
+// from Sedgewick, Algorithms, Second Edition, p. 482, with two
+// adaptations.
+//
+// First, a hidden closure function (n->curfn != N) cannot be the
+// root of a connected component. Refusing to use it as a root
+// forces it into the component of the function in which it appears.
+// The analysis assumes that closures and the functions in which they
+// appear are analyzed together, so that the aliasing between their
+// variables can be modeled more precisely.
+//
+// Second, each function becomes two virtual nodes in the graph,
+// with numbers n and n+1. We record the function's node number as n
+// but search from node n+1. If the search tells us that the component
+// number (min) is n+1, we know that this is a trivial component: one function
+// plus its closures. If the search tells us that the component number is
+// n, then there was a path from node n+1 back to node n, meaning that
+// the function set is mutually recursive. The escape analysis can be
+// more precise when analyzing a single non-recursive function than
+// when analyzing a set of mutually recursive functions.
+
+static NodeList *stack;
+static uint32 visitgen;
+static uint32 visit(Node*);
+static uint32 visitcode(Node*, uint32);
+static uint32 visitcodelist(NodeList*, uint32);
+
+static void analyze(NodeList*, int);
+
+enum
+{
+ EscFuncUnknown = 0,
+ EscFuncPlanned,
+ EscFuncStarted,
+ EscFuncTagged,
+};
+
+void
+escapes(NodeList *all)
+{
+ NodeList *l;
+
+ for(l=all; l; l=l->next)
+ l->n->walkgen = 0;
+
+ visitgen = 0;
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC && l->n->curfn == N)
+ visit(l->n);
+
+ for(l=all; l; l=l->next)
+ l->n->walkgen = 0;
+}
+
+static uint32
+visit(Node *n)
+{
+ uint32 min, recursive;
+ NodeList *l, *block;
+
+ if(n->walkgen > 0) {
+ // already visited
+ return n->walkgen;
+ }
+
+ visitgen++;
+ n->walkgen = visitgen;
+ visitgen++;
+ min = visitgen;
+
+ l = mal(sizeof *l);
+ l->next = stack;
+ l->n = n;
+ stack = l;
+ min = visitcodelist(n->nbody, min);
+ if((min == n->walkgen || min == n->walkgen+1) && n->curfn == N) {
+ // This node is the root of a strongly connected component.
+
+ // The original min passed to visitcodelist was n->walkgen+1.
+ // If visitcodelist found its way back to n->walkgen, then this
+ // block is a set of mutually recursive functions.
+ // Otherwise it's just a lone function that does not recurse.
+ recursive = min == n->walkgen;
+
+ // Remove connected component from stack.
+ // Mark walkgen so that future visits return a large number
+ // so as not to affect the caller's min.
+ block = stack;
+ for(l=stack; l->n != n; l=l->next)
+ l->n->walkgen = (uint32)~0U;
+ n->walkgen = (uint32)~0U;
+ stack = l->next;
+ l->next = nil;
+
+ // Run escape analysis on this set of functions.
+ analyze(block, recursive);
+ }
+
+ return min;
+}
+
+static uint32
+visitcodelist(NodeList *l, uint32 min)
+{
+ for(; l; l=l->next)
+ min = visitcode(l->n, min);
+ return min;
+}
+
+static uint32
+visitcode(Node *n, uint32 min)
+{
+ Node *fn;
+ uint32 m;
+
+ if(n == N)
+ return min;
+
+ min = visitcodelist(n->ninit, min);
+ min = visitcode(n->left, min);
+ min = visitcode(n->right, min);
+ min = visitcodelist(n->list, min);
+ min = visitcode(n->ntest, min);
+ min = visitcode(n->nincr, min);
+ min = visitcodelist(n->nbody, min);
+ min = visitcodelist(n->nelse, min);
+ min = visitcodelist(n->rlist, min);
+
+ if(n->op == OCALLFUNC || n->op == OCALLMETH) {
+ fn = n->left;
+ if(n->op == OCALLMETH)
+ fn = n->left->right->sym->def;
+ if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn)
+ if((m = visit(fn->defn)) < min)
+ min = m;
+ }
+
+ if(n->op == OCLOSURE)
+ if((m = visit(n->closure)) < min)
+ min = m;
+
+ return min;
+}
+
+// An escape analysis pass for a set of functions.
+//
+// First escfunc, esc and escassign recurse over the ast of each
+// function to dig out flow(dst,src) edges between any
+// pointer-containing nodes and store them in dst->escflowsrc. For
+// variables assigned to a variable in an outer scope or used as a
+// return value, they store a flow(theSink, src) edge to a fake node
+// 'the Sink'. For variables referenced in closures, an edge
+// flow(closure, &var) is recorded and the flow of a closure itself to
+// an outer scope is tracked the same way as other variables.
+//
+// Then escflood walks the graph starting at theSink and tags all
+// variables of it can reach an & node as escaping and all function
+// parameters it can reach as leaking.
+//
+// If a value's address is taken but the address does not escape,
+// then the value can stay on the stack. If the value new(T) does
+// not escape, then new(T) can be rewritten into a stack allocation.
+// The same is true of slice literals.
+//
+// If optimizations are disabled (-N), this code is not used.
+// Instead, the compiler assumes that any value whose address
+// is taken without being immediately dereferenced
+// needs to be moved to the heap, and new(T) and slice
+// literals are always real allocations.
+
+typedef struct EscState EscState;
+
+static void escfunc(EscState*, Node *func);
+static void esclist(EscState*, NodeList *l, Node *up);
+static void esc(EscState*, Node *n, Node *up);
+static void escloopdepthlist(EscState*, NodeList *l);
+static void escloopdepth(EscState*, Node *n);
+static void escassign(EscState*, Node *dst, Node *src);
+static void esccall(EscState*, Node*, Node *up);
+static void escflows(EscState*, Node *dst, Node *src);
+static void escflood(EscState*, Node *dst);
+static void escwalk(EscState*, int level, Node *dst, Node *src);
+static void esctag(EscState*, Node *func);
+
+struct EscState {
+ // Fake node that all
+ // - return values and output variables
+ // - parameters on imported functions not marked 'safe'
+ // - assignments to global variables
+ // flow to.
+ Node theSink;
+
+ // If an analyzed function is recorded to return
+ // pieces obtained via indirection from a parameter,
+ // and later there is a call f(x) to that function,
+ // we create a link funcParam <- x to record that fact.
+ // The funcParam node is handled specially in escflood.
+ Node funcParam;
+
+ NodeList* dsts; // all dst nodes
+ int loopdepth; // for detecting nested loop scopes
+ int pdepth; // for debug printing in recursions.
+ int dstcount, edgecount; // diagnostic
+ NodeList* noesc; // list of possible non-escaping nodes, for printing
+ int recursive; // recursive function or group of mutually recursive functions.
+};
+
+static Strlit *tags[16];
+
+static Strlit*
+mktag(int mask)
+{
+ Strlit *s;
+ char buf[40];
+
+ switch(mask&EscMask) {
+ case EscNone:
+ case EscReturn:
+ break;
+ default:
+ fatal("escape mktag");
+ }
+
+ mask >>= EscBits;
+
+ if(mask < nelem(tags) && tags[mask] != nil)
+ return tags[mask];
+
+ snprint(buf, sizeof buf, "esc:0x%x", mask);
+ s = strlit(buf);
+ if(mask < nelem(tags))
+ tags[mask] = s;
+ return s;
+}
+
+static int
+parsetag(Strlit *note)
+{
+ int em;
+
+ if(note == nil)
+ return EscUnknown;
+ if(strncmp(note->s, "esc:", 4) != 0)
+ return EscUnknown;
+ em = atoi(note->s + 4);
+ if (em == 0)
+ return EscNone;
+ return EscReturn | (em << EscBits);
+}
+
+static void
+analyze(NodeList *all, int recursive)
+{
+ NodeList *l;
+ EscState es, *e;
+
+ memset(&es, 0, sizeof es);
+ e = &es;
+ e->theSink.op = ONAME;
+ e->theSink.orig = &e->theSink;
+ e->theSink.class = PEXTERN;
+ e->theSink.sym = lookup(".sink");
+ e->theSink.escloopdepth = -1;
+ e->recursive = recursive;
+
+ e->funcParam.op = ONAME;
+ e->funcParam.orig = &e->funcParam;
+ e->funcParam.class = PAUTO;
+ e->funcParam.sym = lookup(".param");
+ e->funcParam.escloopdepth = 10000000;
+
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ l->n->esc = EscFuncPlanned;
+
+ // flow-analyze functions
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ escfunc(e, l->n);
+
+ // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
+
+ // visit the upstream of each dst, mark address nodes with
+ // addrescapes, mark parameters unsafe
+ for(l = e->dsts; l; l=l->next)
+ escflood(e, l->n);
+
+ // for all top level functions, tag the typenodes corresponding to the param nodes
+ for(l=all; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ esctag(e, l->n);
+
+ if(debug['m']) {
+ for(l=e->noesc; l; l=l->next)
+ if(l->n->esc == EscNone)
+ warnl(l->n->lineno, "%S %hN does not escape",
+ (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
+ l->n);
+ }
+}
+
+
+static void
+escfunc(EscState *e, Node *func)
+{
+ Node *savefn;
+ NodeList *ll;
+ int saveld;
+
+// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
+
+ if(func->esc != 1)
+ fatal("repeat escfunc %N", func->nname);
+ func->esc = EscFuncStarted;
+
+ saveld = e->loopdepth;
+ e->loopdepth = 1;
+ savefn = curfn;
+ curfn = func;
+
+ for(ll=curfn->dcl; ll; ll=ll->next) {
+ if(ll->n->op != ONAME)
+ continue;
+ switch (ll->n->class) {
+ case PPARAMOUT:
+ // out params are in a loopdepth between the sink and all local variables
+ ll->n->escloopdepth = 0;
+ break;
+ case PPARAM:
+ ll->n->escloopdepth = 1;
+ if(ll->n->type && !haspointers(ll->n->type))
+ break;
+ if(curfn->nbody == nil && !curfn->noescape)
+ ll->n->esc = EscHeap;
+ else
+ ll->n->esc = EscNone; // prime for escflood later
+ e->noesc = list(e->noesc, ll->n);
+ break;
+ }
+ }
+
+ // in a mutually recursive group we lose track of the return values
+ if(e->recursive)
+ for(ll=curfn->dcl; ll; ll=ll->next)
+ if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
+ escflows(e, &e->theSink, ll->n);
+
+ escloopdepthlist(e, curfn->nbody);
+ esclist(e, curfn->nbody, curfn);
+ curfn = savefn;
+ e->loopdepth = saveld;
+}
+
+// Mark labels that have no backjumps to them as not increasing e->loopdepth.
+// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
+// and set it to one of the following two. Then in esc we'll clear it again.
+static Label looping;
+static Label nonlooping;
+
+static void
+escloopdepthlist(EscState *e, NodeList *l)
+{
+ for(; l; l=l->next)
+ escloopdepth(e, l->n);
+}
+
+static void
+escloopdepth(EscState *e, Node *n)
+{
+ if(n == N)
+ return;
+
+ escloopdepthlist(e, n->ninit);
+
+ switch(n->op) {
+ case OLABEL:
+ if(!n->left || !n->left->sym)
+ fatal("esc:label without label: %+N", n);
+ // Walk will complain about this label being already defined, but that's not until
+ // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
+ // if(n->left->sym->label != nil)
+ // fatal("escape analysis messed up analyzing label: %+N", n);
+ n->left->sym->label = &nonlooping;
+ break;
+ case OGOTO:
+ if(!n->left || !n->left->sym)
+ fatal("esc:goto without label: %+N", n);
+ // If we come past one that's uninitialized, this must be a (harmless) forward jump
+ // but if it's set to nonlooping the label must have preceded this goto.
+ if(n->left->sym->label == &nonlooping)
+ n->left->sym->label = &looping;
+ break;
+ }
+
+ escloopdepth(e, n->left);
+ escloopdepth(e, n->right);
+ escloopdepthlist(e, n->list);
+ escloopdepth(e, n->ntest);
+ escloopdepth(e, n->nincr);
+ escloopdepthlist(e, n->nbody);
+ escloopdepthlist(e, n->nelse);
+ escloopdepthlist(e, n->rlist);
+
+}
+
+static void
+esclist(EscState *e, NodeList *l, Node *up)
+{
+ for(; l; l=l->next)
+ esc(e, l->n, up);
+}
+
+static void
+esc(EscState *e, Node *n, Node *up)
+{
+ int lno;
+ NodeList *ll, *lr;
+ Node *a;
+
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+
+ // ninit logically runs at a different loopdepth than the rest of the for loop.
+ esclist(e, n->ninit, n);
+
+ if(n->op == OFOR || n->op == ORANGE)
+ e->loopdepth++;
+
+ // type switch variables have no ODCL.
+ // process type switch as declaration.
+ // must happen before processing of switch body,
+ // so before recursion.
+ if(n->op == OSWITCH && n->ntest && n->ntest->op == OTYPESW) {
+ for(ll=n->list; ll; ll=ll->next) { // cases
+ // ll->n->nname is the variable per case
+ if(ll->n->nname)
+ ll->n->nname->escloopdepth = e->loopdepth;
+ }
+ }
+
+ esc(e, n->left, n);
+ esc(e, n->right, n);
+ esc(e, n->ntest, n);
+ esc(e, n->nincr, n);
+ esclist(e, n->nbody, n);
+ esclist(e, n->nelse, n);
+ esclist(e, n->list, n);
+ esclist(e, n->rlist, n);
+
+ if(n->op == OFOR || n->op == ORANGE)
+ e->loopdepth--;
+
+ if(debug['m'] > 1)
+ print("%L:[%d] %S esc: %N\n", lineno, e->loopdepth,
+ (curfn && curfn->nname) ? curfn->nname->sym : S, n);
+
+ switch(n->op) {
+ case ODCL:
+ // Record loop depth at declaration.
+ if(n->left)
+ n->left->escloopdepth = e->loopdepth;
+ break;
+
+ case OLABEL:
+ if(n->left->sym->label == &nonlooping) {
+ if(debug['m'] > 1)
+ print("%L:%N non-looping label\n", lineno, n);
+ } else if(n->left->sym->label == &looping) {
+ if(debug['m'] > 1)
+ print("%L: %N looping label\n", lineno, n);
+ e->loopdepth++;
+ }
+ // See case OLABEL in escloopdepth above
+ // else if(n->left->sym->label == nil)
+ // fatal("escape analysis missed or messed up a label: %+N", n);
+
+ n->left->sym->label = nil;
+ break;
+
+ case ORANGE:
+ // Everything but fixed array is a dereference.
+ if(isfixedarray(n->type) && n->list && n->list->next)
+ escassign(e, n->list->next->n, n->right);
+ break;
+
+ case OSWITCH:
+ if(n->ntest && n->ntest->op == OTYPESW) {
+ for(ll=n->list; ll; ll=ll->next) { // cases
+ // ntest->right is the argument of the .(type),
+ // ll->n->nname is the variable per case
+ escassign(e, ll->n->nname, n->ntest->right);
+ }
+ }
+ break;
+
+ case OAS:
+ case OASOP:
+ escassign(e, n->left, n->right);
+ break;
+
+ case OAS2: // x,y = a,b
+ if(count(n->list) == count(n->rlist))
+ for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+ escassign(e, ll->n, lr->n);
+ break;
+
+ case OAS2RECV: // v, ok = <-ch
+ case OAS2MAPR: // v, ok = m[k]
+ case OAS2DOTTYPE: // v, ok = x.(type)
+ escassign(e, n->list->n, n->rlist->n);
+ break;
+
+ case OSEND: // ch <- x
+ escassign(e, &e->theSink, n->right);
+ break;
+
+ case ODEFER:
+ if(e->loopdepth == 1) // top level
+ break;
+ // arguments leak out of scope
+ // TODO: leak to a dummy node instead
+ // fallthrough
+ case OPROC:
+ // go f(x) - f and x escape
+ escassign(e, &e->theSink, n->left->left);
+ escassign(e, &e->theSink, n->left->right); // ODDDARG for call
+ for(ll=n->left->list; ll; ll=ll->next)
+ escassign(e, &e->theSink, ll->n);
+ break;
+
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCALLINTER:
+ esccall(e, n, up);
+ break;
+
+ case OAS2FUNC: // x,y = f()
+ // esccall already done on n->rlist->n. tie it's escretval to n->list
+ lr=n->rlist->n->escretval;
+ for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next)
+ escassign(e, ll->n, lr->n);
+ if(lr || ll)
+ fatal("esc oas2func");
+ break;
+
+ case ORETURN:
+ ll=n->list;
+ if(count(n->list) == 1 && curfn->type->outtuple > 1) {
+ // OAS2FUNC in disguise
+ // esccall already done on n->list->n
+ // tie n->list->n->escretval to curfn->dcl PPARAMOUT's
+ ll = n->list->n->escretval;
+ }
+
+ for(lr = curfn->dcl; lr && ll; lr=lr->next) {
+ if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
+ continue;
+ escassign(e, lr->n, ll->n);
+ ll = ll->next;
+ }
+ if (ll != nil)
+ fatal("esc return list");
+ break;
+
+ case OPANIC:
+ // Argument could leak through recover.
+ escassign(e, &e->theSink, n->left);
+ break;
+
+ case OAPPEND:
+ if(!n->isddd)
+ for(ll=n->list->next; ll; ll=ll->next)
+ escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
+ break;
+
+ case OCONV:
+ case OCONVNOP:
+ case OCONVIFACE:
+ escassign(e, n, n->left);
+ break;
+
+ case OARRAYLIT:
+ if(isslice(n->type)) {
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ n->escloopdepth = e->loopdepth;
+ // Values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(e, &e->theSink, ll->n->right);
+ } else {
+ // Link values to array.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(e, n, ll->n->right);
+ }
+ break;
+
+ case OSTRUCTLIT:
+ // Link values to struct.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(e, n, ll->n->right);
+ break;
+
+ case OPTRLIT:
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ n->escloopdepth = e->loopdepth;
+ // Contents make it to memory, lose track.
+ escassign(e, &e->theSink, n->left);
+ break;
+
+ case OCALLPART:
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ n->escloopdepth = e->loopdepth;
+ // Contents make it to memory, lose track.
+ escassign(e, &e->theSink, n->left);
+ break;
+
+ case OMAPLIT:
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ n->escloopdepth = e->loopdepth;
+ // Keys and values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next) {
+ escassign(e, &e->theSink, ll->n->left);
+ escassign(e, &e->theSink, ll->n->right);
+ }
+ break;
+
+ case OCLOSURE:
+ // Link addresses of captured variables to closure.
+ for(ll=n->cvars; ll; ll=ll->next) {
+ if(ll->n->op == OXXX) // unnamed out argument; see dcl.c:/^funcargs
+ continue;
+ a = nod(OADDR, ll->n->closure, N);
+ a->lineno = ll->n->lineno;
+ a->escloopdepth = e->loopdepth;
+ typecheck(&a, Erv);
+ escassign(e, n, a);
+ }
+ // fallthrough
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ n->escloopdepth = e->loopdepth;
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ break;
+
+ case OADDR:
+ n->esc = EscNone; // until proven otherwise
+ e->noesc = list(e->noesc, n);
+ // current loop depth is an upper bound on actual loop depth
+ // of addressed value.
+ n->escloopdepth = e->loopdepth;
+ // for &x, use loop depth of x if known.
+ // it should always be known, but if not, be conservative
+ // and keep the current loop depth.
+ if(n->left->op == ONAME) {
+ switch(n->left->class) {
+ case PAUTO:
+ if(n->left->escloopdepth != 0)
+ n->escloopdepth = n->left->escloopdepth;
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ // PPARAM is loop depth 1 always.
+ // PPARAMOUT is loop depth 0 for writes
+ // but considered loop depth 1 for address-of,
+ // so that writing the address of one result
+ // to another (or the same) result makes the
+ // first result move to the heap.
+ n->escloopdepth = 1;
+ break;
+ }
+ }
+ break;
+ }
+
+ lineno = lno;
+}
+
+// Assert that expr somehow gets assigned to dst, if non nil. for
+// dst==nil, any name node expr still must be marked as being
+// evaluated in curfn. For expr==nil, dst must still be examined for
+// evaluations inside it (e.g *f(x) = y)
+static void
+escassign(EscState *e, Node *dst, Node *src)
+{
+ int lno;
+ NodeList *ll;
+
+ if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
+ return;
+
+ if(debug['m'] > 1)
+ print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, e->loopdepth,
+ (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src);
+
+ setlineno(dst);
+
+ // Analyze lhs of assignment.
+ // Replace dst with e->theSink if we can't track it.
+ switch(dst->op) {
+ default:
+ dump("dst", dst);
+ fatal("escassign: unexpected dst");
+
+ case OARRAYLIT:
+ case OCLOSURE:
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ case OCALLPART:
+ break;
+
+ case ONAME:
+ if(dst->class == PEXTERN)
+ dst = &e->theSink;
+ break;
+ case ODOT: // treat "dst.x = src" as "dst = src"
+ escassign(e, dst->left, src);
+ return;
+ case OINDEX:
+ if(isfixedarray(dst->left->type)) {
+ escassign(e, dst->left, src);
+ return;
+ }
+ dst = &e->theSink; // lose track of dereference
+ break;
+ case OIND:
+ case ODOTPTR:
+ dst = &e->theSink; // lose track of dereference
+ break;
+ case OINDEXMAP:
+ // lose track of key and value
+ escassign(e, &e->theSink, dst->right);
+ dst = &e->theSink;
+ break;
+ }
+
+ lno = setlineno(src);
+ e->pdepth++;
+
+ switch(src->op) {
+ case OADDR: // dst = &x
+ case OIND: // dst = *x
+ case ODOTPTR: // dst = (*x).f
+ case ONAME:
+ case OPARAM:
+ case ODDDARG:
+ case OPTRLIT:
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ case OCLOSURE:
+ case OCALLPART:
+ escflows(e, dst, src);
+ break;
+
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCALLINTER:
+ // Flowing multiple returns to a single dst happens when
+ // analyzing "go f(g())": here g() flows to sink (issue 4529).
+ for(ll=src->escretval; ll; ll=ll->next)
+ escflows(e, dst, ll->n);
+ break;
+
+ case ODOT:
+ // A non-pointer escaping from a struct does not concern us.
+ if(src->type && !haspointers(src->type))
+ break;
+ // fallthrough
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case ODOTMETH: // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
+ // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ case OSLICE:
+ case OSLICE3:
+ case OSLICEARR:
+ case OSLICE3ARR:
+ // Conversions, field access, slice all preserve the input value.
+ escassign(e, dst, src->left);
+ break;
+
+ case OAPPEND:
+ // Append returns first argument.
+ escassign(e, dst, src->list->n);
+ break;
+
+ case OINDEX:
+ // Index of array preserves input value.
+ if(isfixedarray(src->left->type))
+ escassign(e, dst, src->left);
+ break;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case OPLUS:
+ case OMINUS:
+ case OCOM:
+ // Might be pointer arithmetic, in which case
+ // the operands flow into the result.
+ // TODO(rsc): Decide what the story is here. This is unsettling.
+ escassign(e, dst, src->left);
+ escassign(e, dst, src->right);
+ break;
+ }
+
+ e->pdepth--;
+ lineno = lno;
+}
+
+static int
+escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
+{
+ int em, em0;
+
+ em = parsetag(note);
+
+ if(em == EscUnknown) {
+ escassign(e, &e->theSink, src);
+ return em;
+ }
+
+ if(em == EscNone)
+ return em;
+
+ // If content inside parameter (reached via indirection)
+ // escapes back to results, mark as such.
+ if(em & EscContentEscapes)
+ escassign(e, &e->funcParam, src);
+
+ em0 = em;
+ for(em >>= EscReturnBits; em && dsts; em >>= 1, dsts=dsts->next)
+ if(em & 1)
+ escassign(e, dsts->n, src);
+
+ if (em != 0 && dsts == nil)
+ fatal("corrupt esc tag %Z or messed up escretval list\n", note);
+ return em0;
+}
+
+// This is a bit messier than fortunate, pulled out of esc's big
+// switch for clarity. We either have the paramnodes, which may be
+// connected to other things through flows or we have the parameter type
+// nodes, which may be marked "noescape". Navigating the ast is slightly
+// different for methods vs plain functions and for imported vs
+// this-package
+static void
+esccall(EscState *e, Node *n, Node *up)
+{
+ NodeList *ll, *lr;
+ Node *a, *fn, *src;
+ Type *t, *fntype;
+ char buf[40];
+ int i;
+
+ fn = N;
+ switch(n->op) {
+ default:
+ fatal("esccall");
+
+ case OCALLFUNC:
+ fn = n->left;
+ fntype = fn->type;
+ break;
+
+ case OCALLMETH:
+ fn = n->left->right->sym->def;
+ if(fn)
+ fntype = fn->type;
+ else
+ fntype = n->left->type;
+ break;
+
+ case OCALLINTER:
+ fntype = n->left->type;
+ break;
+ }
+
+ ll = n->list;
+ if(n->list != nil && n->list->next == nil) {
+ a = n->list->n;
+ if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
+ ll = a->escretval;
+ }
+
+ if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
+ // function in same mutually recursive group. Incorporate into flow graph.
+// print("esc local fn: %N\n", fn->ntype);
+ if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
+ fatal("graph inconsistency");
+
+ // set up out list on this call node
+ for(lr=fn->ntype->rlist; lr; lr=lr->next)
+ n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT)
+
+ // Receiver.
+ if(n->op != OCALLFUNC)
+ escassign(e, fn->ntype->left->left, n->left->left);
+
+ for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
+ src = ll->n;
+ if(lr->n->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->type = typ(TARRAY);
+ src->type->type = lr->n->type->type;
+ src->type->bound = count(ll);
+ src->type = ptrto(src->type); // make pointer so it will be tracked
+ src->escloopdepth = e->loopdepth;
+ src->lineno = n->lineno;
+ src->esc = EscNone; // until we find otherwise
+ e->noesc = list(e->noesc, src);
+ n->right = src;
+ }
+ if(lr->n->left != N)
+ escassign(e, lr->n->left, src);
+ if(src != ll->n)
+ break;
+ }
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(e, &e->theSink, ll->n);
+
+ return;
+ }
+
+ // Imported or completely analyzed function. Use the escape tags.
+ if(n->escretval != nil)
+ fatal("esc already decorated call %+N\n", n);
+
+ // set up out list on this call node with dummy auto ONAMES in the current (calling) function.
+ i = 0;
+ for(t=getoutargx(fntype)->type; t; t=t->down) {
+ src = nod(ONAME, N, N);
+ snprint(buf, sizeof buf, ".dum%d", i++);
+ src->sym = lookup(buf);
+ src->type = t->type;
+ src->class = PAUTO;
+ src->curfn = curfn;
+ src->escloopdepth = e->loopdepth;
+ src->used = 1;
+ src->lineno = n->lineno;
+ n->escretval = list(n->escretval, src);
+ }
+
+// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
+
+ // Receiver.
+ if(n->op != OCALLFUNC) {
+ t = getthisx(fntype)->type;
+ src = n->left->left;
+ if(haspointers(t->type))
+ escassignfromtag(e, t->note, n->escretval, src);
+ }
+
+ for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+ src = ll->n;
+ if(t->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->escloopdepth = e->loopdepth;
+ src->lineno = n->lineno;
+ src->type = typ(TARRAY);
+ src->type->type = t->type->type;
+ src->type->bound = count(ll);
+ src->type = ptrto(src->type); // make pointer so it will be tracked
+ src->esc = EscNone; // until we find otherwise
+ e->noesc = list(e->noesc, src);
+ n->right = src;
+ }
+ if(haspointers(t->type)) {
+ if(escassignfromtag(e, t->note, n->escretval, src) == EscNone && up->op != ODEFER && up->op != OPROC) {
+ a = src;
+ while(a->op == OCONVNOP)
+ a = a->left;
+ switch(a->op) {
+ case OCALLPART:
+ case OCLOSURE:
+ case ODDDARG:
+ case OARRAYLIT:
+ case OPTRLIT:
+ case OSTRUCTLIT:
+ // The callee has already been analyzed, so its arguments have esc tags.
+ // The argument is marked as not escaping at all.
+ // Record that fact so that any temporary used for
+ // synthesizing this expression can be reclaimed when
+ // the function returns.
+ // This 'noescape' is even stronger than the usual esc == EscNone.
+ // src->esc == EscNone means that src does not escape the current function.
+ // src->noescape = 1 here means that src does not escape this statement
+ // in the current function.
+ a->noescape = 1;
+ break;
+ }
+ }
+ }
+ if(src != ll->n)
+ break;
+ t = t->down;
+ }
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(e, &e->theSink, ll->n);
+}
+
+// Store the link src->dst in dst, throwing out some quick wins.
+static void
+escflows(EscState *e, Node *dst, Node *src)
+{
+ if(dst == nil || src == nil || dst == src)
+ return;
+
+ // Don't bother building a graph for scalars.
+ if(src->type && !haspointers(src->type))
+ return;
+
+ if(debug['m']>2)
+ print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
+
+ if(dst->escflowsrc == nil) {
+ e->dsts = list(e->dsts, dst);
+ e->dstcount++;
+ }
+ e->edgecount++;
+
+ dst->escflowsrc = list(dst->escflowsrc, src);
+}
+
+// Whenever we hit a reference node, the level goes up by one, and whenever
+// we hit an OADDR, the level goes down by one. as long as we're on a level > 0
+// finding an OADDR just means we're following the upstream of a dereference,
+// so this address doesn't leak (yet).
+// If level == 0, it means the /value/ of this node can reach the root of this flood.
+// so if this node is an OADDR, it's argument should be marked as escaping iff
+// it's currfn/e->loopdepth are different from the flood's root.
+// Once an object has been moved to the heap, all of it's upstream should be considered
+// escaping to the global scope.
+static void
+escflood(EscState *e, Node *dst)
+{
+ NodeList *l;
+
+ switch(dst->op) {
+ case ONAME:
+ case OCLOSURE:
+ break;
+ default:
+ return;
+ }
+
+ if(debug['m']>1)
+ print("\nescflood:%d: dst %hN scope:%S[%d]\n", walkgen, dst,
+ (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
+ dst->escloopdepth);
+
+ for(l = dst->escflowsrc; l; l=l->next) {
+ walkgen++;
+ escwalk(e, 0, dst, l->n);
+ }
+}
+
+// There appear to be some loops in the escape graph, causing
+// arbitrary recursion into deeper and deeper levels.
+// Cut this off safely by making minLevel sticky: once you
+// get that deep, you cannot go down any further but you also
+// cannot go up any further. This is a conservative fix.
+// Making minLevel smaller (more negative) would handle more
+// complex chains of indirections followed by address-of operations,
+// at the cost of repeating the traversal once for each additional
+// allowed level when a loop is encountered. Using -2 suffices to
+// pass all the tests we have written so far, which we assume matches
+// the level of complexity we want the escape analysis code to handle.
+#define MinLevel (-2)
+/*c2go enum { MinLevel = -2 };*/
+
+static void
+escwalk(EscState *e, int level, Node *dst, Node *src)
+{
+ NodeList *ll;
+ int leaks, newlevel;
+
+ if(src->walkgen == walkgen && src->esclevel <= level)
+ return;
+ src->walkgen = walkgen;
+ src->esclevel = level;
+
+ if(debug['m']>1)
+ print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n",
+ level, e->pdepth, e->pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src,
+ (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
+
+ e->pdepth++;
+
+ // Input parameter flowing to output parameter?
+ if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) {
+ if(src->op == ONAME && src->class == PPARAM && src->curfn == dst->curfn && src->esc != EscScope && src->esc != EscHeap) {
+ if(level == 0) {
+ if(debug['m'])
+ warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
+ if((src->esc&EscMask) != EscReturn)
+ src->esc = EscReturn;
+ src->esc |= 1<<((dst->vargen-1) + EscReturnBits);
+ goto recurse;
+ } else if(level > 0) {
+ if(debug['m'])
+ warnl(src->lineno, "%N leaking param %hN content to result %S", src->curfn->nname, src, dst->sym);
+ if((src->esc&EscMask) != EscReturn)
+ src->esc = EscReturn;
+ src->esc |= EscContentEscapes;
+ goto recurse;
+ }
+ }
+ }
+
+ // The second clause is for values pointed at by an object passed to a call
+ // that returns something reached via indirect from the object.
+ // We don't know which result it is or how many indirects, so we treat it as leaking.
+ leaks = level <= 0 && dst->escloopdepth < src->escloopdepth ||
+ level < 0 && dst == &e->funcParam && haspointers(src->type);
+
+ switch(src->op) {
+ case ONAME:
+ if(src->class == PPARAM && (leaks || dst->escloopdepth < 0) && src->esc != EscHeap) {
+ src->esc = EscScope;
+ if(debug['m'])
+ warnl(src->lineno, "leaking param: %hN", src);
+ }
+
+ // Treat a PPARAMREF closure variable as equivalent to the
+ // original variable.
+ if(src->class == PPARAMREF) {
+ if(leaks && debug['m'])
+ warnl(src->lineno, "leaking closure reference %hN", src);
+ escwalk(e, level, dst, src->closure);
+ }
+ break;
+
+ case OPTRLIT:
+ case OADDR:
+ if(leaks) {
+ src->esc = EscHeap;
+ addrescapes(src->left);
+ if(debug['m'])
+ warnl(src->lineno, "%hN escapes to heap", src);
+ }
+ newlevel = level;
+ if(level > MinLevel)
+ newlevel--;
+ escwalk(e, newlevel, dst, src->left);
+ break;
+
+ case OARRAYLIT:
+ if(isfixedarray(src->type))
+ break;
+ // fall through
+ case ODDDARG:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case OMAPLIT:
+ case ONEW:
+ case OCLOSURE:
+ case OCALLPART:
+ if(leaks) {
+ src->esc = EscHeap;
+ if(debug['m'])
+ warnl(src->lineno, "%hN escapes to heap", src);
+ }
+ break;
+
+ case ODOT:
+ case OSLICE:
+ case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
+ escwalk(e, level, dst, src->left);
+ break;
+
+ case OINDEX:
+ if(isfixedarray(src->left->type)) {
+ escwalk(e, level, dst, src->left);
+ break;
+ }
+ // fall through
+ case ODOTPTR:
+ case OINDEXMAP:
+ case OIND:
+ newlevel = level;
+ if(level > MinLevel)
+ newlevel++;
+ escwalk(e, newlevel, dst, src->left);
+ }
+
+recurse:
+ for(ll=src->escflowsrc; ll; ll=ll->next)
+ escwalk(e, level, dst, ll->n);
+
+ e->pdepth--;
+}
+
+static void
+esctag(EscState *e, Node *func)
+{
+ Node *savefn;
+ NodeList *ll;
+ Type *t;
+
+ USED(e);
+ func->esc = EscFuncTagged;
+
+ // External functions are assumed unsafe,
+ // unless //go:noescape is given before the declaration.
+ if(func->nbody == nil) {
+ if(func->noescape) {
+ for(t=getinargx(func->type)->type; t; t=t->down)
+ if(haspointers(t->type))
+ t->note = mktag(EscNone);
+ }
+ return;
+ }
+
+ savefn = curfn;
+ curfn = func;
+
+ for(ll=curfn->dcl; ll; ll=ll->next) {
+ if(ll->n->op != ONAME || ll->n->class != PPARAM)
+ continue;
+
+ switch (ll->n->esc&EscMask) {
+ case EscNone: // not touched by escflood
+ case EscReturn:
+ if(haspointers(ll->n->type)) // don't bother tagging for scalars
+ ll->n->paramfld->note = mktag(ll->n->esc);
+ break;
+ case EscHeap: // touched by escflood, moved to heap
+ case EscScope: // touched by escflood, value leaves scope
+ break;
+ }
+ }
+
+ curfn = savefn;
+}
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
new file mode 100644
index 0000000..da5984c
--- /dev/null
+++ b/src/cmd/gc/export.c
@@ -0,0 +1,521 @@
+// 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 "go.h"
+#include "y.tab.h"
+
+static void dumpexporttype(Type *t);
+
+// Mark n's symbol as exported
+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;
+
+ if(debug['E'])
+ print("export symbol %S\n", n->sym);
+ 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;
+}
+
+// exportedsym reports whether a symbol will be visible
+// to files that import our package.
+static int
+exportedsym(Sym *sym)
+{
+ // Builtins are visible everywhere.
+ if(sym->pkg == builtinpkg || sym->origpkg == builtinpkg)
+ return 1;
+
+ return sym->pkg == localpkg && exportname(sym->name);
+}
+
+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;
+ // -A is for cmd/gc/mkbuiltin script, so export everything
+ if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name))
+ exportsym(n);
+}
+
+static void
+dumppkg(Pkg *p)
+{
+ char *suffix;
+
+ if(p == nil || p == localpkg || p->exported || p == builtinpkg)
+ return;
+ p->exported = 1;
+ suffix = "";
+ if(!p->direct)
+ suffix = " // indirect";
+ Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix);
+}
+
+// Look for anything we need for the inline body
+static void reexportdep(Node *n);
+static void
+reexportdeplist(NodeList *ll)
+{
+ for(; ll ;ll=ll->next)
+ reexportdep(ll->n);
+}
+
+static void
+reexportdep(Node *n)
+{
+ Type *t;
+
+ if(!n)
+ return;
+
+ //print("reexportdep %+hN\n", n);
+ switch(n->op) {
+ case ONAME:
+ switch(n->class&~PHEAP) {
+ case PFUNC:
+ // methods will be printed along with their type
+ // nodes for T.Method expressions
+ if(n->left && n->left->op == OTYPE)
+ break;
+ // nodes for method calls.
+ if(!n->type || n->type->thistuple > 0)
+ break;
+ // fallthrough
+ case PEXTERN:
+ if(n->sym && !exportedsym(n->sym)) {
+ if(debug['E'])
+ print("reexport name %S\n", n->sym);
+ exportlist = list(exportlist, n);
+ }
+ }
+ break;
+
+ case ODCL:
+ // Local variables in the bodies need their type.
+ t = n->left->type;
+ if(t != types[t->etype] && t != idealbool && t != idealstring) {
+ if(isptr[t->etype])
+ t = t->type;
+ if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+ if(debug['E'])
+ print("reexport type %S from declaration\n", t->sym);
+ exportlist = list(exportlist, t->sym->def);
+ }
+ }
+ break;
+
+ case OLITERAL:
+ t = n->type;
+ if(t != types[n->type->etype] && t != idealbool && t != idealstring) {
+ if(isptr[t->etype])
+ t = t->type;
+ if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+ if(debug['E'])
+ print("reexport literal type %S\n", t->sym);
+ exportlist = list(exportlist, t->sym->def);
+ }
+ }
+ // fallthrough
+ case OTYPE:
+ if(n->sym && !exportedsym(n->sym)) {
+ if(debug['E'])
+ print("reexport literal/type %S\n", n->sym);
+ exportlist = list(exportlist, n);
+ }
+ break;
+
+ // for operations that need a type when rendered, put the type on the export list.
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case ORUNESTR:
+ case OARRAYBYTESTR:
+ case OARRAYRUNESTR:
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ case OSTRUCTLIT:
+ case OARRAYLIT:
+ case OPTRLIT:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case OMAKECHAN:
+ t = n->type;
+ if(!t->sym && t->type)
+ t = t->type;
+ if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+ if(debug['E'])
+ print("reexport type for expression %S\n", t->sym);
+ exportlist = list(exportlist, t->sym->def);
+ }
+ break;
+ }
+
+ reexportdep(n->left);
+ reexportdep(n->right);
+ reexportdeplist(n->list);
+ reexportdeplist(n->rlist);
+ reexportdeplist(n->ninit);
+ reexportdep(n->ntest);
+ reexportdep(n->nincr);
+ reexportdeplist(n->nbody);
+ reexportdeplist(n->nelse);
+}
+
+
+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
+ dumpexporttype(t);
+
+ if(t != T && !isideal(t))
+ Bprint(bout, "\tconst %#S %#T = %#V\n", s, t, &n->val);
+ else
+ Bprint(bout, "\tconst %#S = %#V\n", s, &n->val);
+}
+
+static void
+dumpexportvar(Sym *s)
+{
+ Node *n;
+ Type *t;
+
+ n = s->def;
+ typecheck(&n, Erv|Ecall);
+ if(n == N || n->type == T) {
+ yyerror("variable exported but not defined: %S", s);
+ return;
+ }
+
+ t = n->type;
+ dumpexporttype(t);
+
+ if(t->etype == TFUNC && n->class == PFUNC) {
+ if (n->inl) {
+ // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+ // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+ if(debug['l'] < 2)
+ typecheckinl(n);
+ // NOTE: The space after %#S here is necessary for ld's export data parser.
+ Bprint(bout, "\tfunc %#S %#hT { %#H }\n", s, t, n->inl);
+ reexportdeplist(n->inl);
+ } else
+ Bprint(bout, "\tfunc %#S %#hT\n", s, t);
+ } else
+ Bprint(bout, "\tvar %#S %#T\n", s, 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
+dumpexporttype(Type *t)
+{
+ Type *f;
+ Type **m;
+ int i, n;
+
+ if(t == T)
+ return;
+ if(t->printed || t == types[t->etype] || t == bytetype || t == runetype || t == errortype)
+ return;
+ t->printed = 1;
+
+ if(t->sym != S && t->etype != TFIELD)
+ dumppkg(t->sym->pkg);
+
+ dumpexporttype(t->type);
+ dumpexporttype(t->down);
+
+ if (t->sym == S || t->etype == TFIELD)
+ return;
+
+ n = 0;
+ for(f=t->method; f!=T; f=f->down) {
+ dumpexporttype(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);
+
+ Bprint(bout, "\ttype %#S %#lT\n", t->sym, t);
+ for(i=0; i<n; i++) {
+ f = m[i];
+ if(f->nointerface)
+ Bprint(bout, "\t//go:nointerface\n");
+ if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
+ // when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+ // currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+ if(debug['l'] < 2)
+ typecheckinl(f->type->nname);
+ Bprint(bout, "\tfunc (%#T) %#hhS %#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
+ reexportdeplist(f->type->nname->inl);
+ } else
+ Bprint(bout, "\tfunc (%#T) %#hhS %#hT\n", getthisx(f->type)->type, f->sym, f->type);
+ }
+}
+
+static void
+dumpsym(Sym *s)
+{
+ if(s->flags & SymExported)
+ return;
+ s->flags |= SymExported;
+
+ if(s->def == N) {
+ yyerror("unknown export symbol: %S", s);
+ return;
+ }
+// print("dumpsym %O %+S\n", s->def->op, s);
+ 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:
+ if(s->def->type->etype == TFORW)
+ yyerror("export of incomplete type %S", s);
+ else
+ dumpexporttype(s->def->type);
+ break;
+
+ case ONAME:
+ dumpexportvar(s);
+ break;
+ }
+}
+
+void
+dumpexport(void)
+{
+ NodeList *l;
+ int32 i, lno;
+ Pkg *p;
+
+ lno = lineno;
+
+ Bprint(bout, "\n$$\npackage %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$$\n");
+ lineno = lno;
+}
+
+/*
+ * import
+ */
+
+/*
+ * return the sym for ss, which should match lexical
+ */
+Sym*
+importsym(Sym *s, int op)
+{
+ char *pkgstr;
+
+ if(s->def != N && s->def->op != op) {
+ pkgstr = smprint("during import \"%Z\"", importpkg->path);
+ redeclare(s, pkgstr);
+ }
+
+ // 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 %S", s);
+ return s->def->type;
+}
+
+void
+importimport(Sym *s, Strlit *z)
+{
+ // Informational: record package name
+ // associated with import path, for use in
+ // human-readable messages.
+ Pkg *p;
+
+ if(isbadimport(z))
+ errorexit();
+ p = mkpkg(z);
+ if(p->name == nil) {
+ p->name = s->name;
+ pkglookup(s->name, nil)->npkg++;
+ } else if(strcmp(p->name, s->name) != 0)
+ yyerror("conflicting names %s and %s for package \"%Z\"", p->name, s->name, p->path);
+
+ if(!incannedimport && myimportpath != nil && strcmp(z->s, myimportpath) == 0) {
+ yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, z);
+ errorexit();
+ }
+}
+
+void
+importconst(Sym *s, Type *t, Node *n)
+{
+ Node *n1;
+
+ 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->orig = newname(s);
+ n->sym = s;
+ declare(n, PEXTERN);
+
+ if(debug['E'])
+ print("import const %S\n", s);
+}
+
+void
+importvar(Sym *s, Type *t)
+{
+ Node *n;
+
+ 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 (in \"%Z\")\n\t%T (in \"%Z\")", s, s->def->type, s->importdef->path, t, importpkg->path);
+ }
+ n = newname(s);
+ s->importdef = importpkg;
+ n->type = t;
+ declare(n, PEXTERN);
+
+ if(debug['E'])
+ print("import var %S %lT\n", s, t);
+}
+
+void
+importtype(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) {
+ n = pt->nod;
+ copytype(pt->nod, t);
+ pt->nod = n; // unzero nod
+ pt->sym->importdef = importpkg;
+ pt->sym->lastlineno = parserline();
+ declare(n, PEXTERN);
+ checkwidth(pt);
+ } else if(!eqtype(pt->orig, t))
+ yyerror("inconsistent definition for type %S during import\n\t%lT (in \"%Z\")\n\t%lT (in \"%Z\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
+
+ if(debug['E'])
+ print("import type %T %lT\n", pt, t);
+}
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
new file mode 100644
index 0000000..89d2a14
--- /dev/null
+++ b/src/cmd/gc/fmt.c
@@ -0,0 +1,1691 @@
+// 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 <u.h>
+#include <libc.h>
+#include "go.h"
+#include "opnames.h"
+
+//
+// Format conversions
+// %L int Line numbers
+//
+// %E int etype values (aka 'Kind')
+//
+// %O int Node Opcodes
+// Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
+//
+// %J Node* Node details
+// Flags: "%hJ" suppresses things not relevant until walk.
+//
+// %V Val* Constant values
+//
+// %S Sym* Symbols
+// Flags: +,- #: mode (see below)
+// "%hS" unqualified identifier in any mode
+// "%hhS" in export mode: unqualified identifier if exported, qualified if not
+//
+// %T Type* Types
+// Flags: +,- #: mode (see below)
+// 'l' definition instead of name.
+// 'h' omit "func" and receiver in function types
+// 'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
+//
+// %N Node* Nodes
+// Flags: +,- #: mode (see below)
+// 'h' (only in +/debug mode) suppress recursion
+// 'l' (only in Error mode) print "foo (type Bar)"
+//
+// %H NodeList* NodeLists
+// Flags: those of %N
+// ',' separate items with ',' instead of ';'
+//
+// %Z Strlit* String literals
+//
+// In mparith1.c:
+// %B Mpint* Big integers
+// %F Mpflt* Big floats
+//
+// %S, %T and %N obey use the following flags to set the format mode:
+enum {
+ FErr, // error mode (default)
+ FDbg, // "%+N" debug mode
+ FExp, // "%#N" export mode
+ FTypeId, // "%-N" turning-types-into-symbols-mode: identical types give identical strings
+};
+static int fmtmode;
+static int fmtpkgpfx; // %uT stickyness
+//
+// E.g. for %S: %+S %#S %-S print an identifier properly qualified for debug/export/internal mode.
+//
+// The mode flags +, - and # are sticky, meaning they persist through
+// recursions of %N, %T and %S, but not the h and l flags. The u flag is
+// sticky only on %T recursions and only used in %-/Sym mode.
+
+//
+// Useful format combinations:
+//
+// %+N %+H multiline recursive debug dump of node/nodelist
+// %+hN %+hH non recursive debug dump
+//
+// %#N %#T export format
+// %#lT type definition instead of name
+// %#hT omit"func" and receiver in function signature
+//
+// %lN "foo (type Bar)" for error messages
+//
+// %-T type identifiers
+// %-hT type identifiers without "func" and arg names in type signatures (methodsym)
+// %-uT type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
+//
+
+
+static int
+setfmode(unsigned long *flags)
+{
+ int fm;
+
+ fm = fmtmode;
+ if(*flags & FmtSign)
+ fmtmode = FDbg;
+ else if(*flags & FmtSharp)
+ fmtmode = FExp;
+ else if(*flags & FmtLeft)
+ fmtmode = FTypeId;
+
+ *flags &= ~(FmtSharp|FmtLeft|FmtSign);
+ return fm;
+}
+
+// Fmt "%L": Linenumbers
+static int
+Lconv(Fmt *fp)
+{
+ return linklinefmt(ctxt, fp);
+}
+
+static char*
+goopnames[] =
+{
+ [OADDR] = "&",
+ [OADD] = "+",
+ [OADDSTR] = "+",
+ [OANDAND] = "&&",
+ [OANDNOT] = "&^",
+ [OAND] = "&",
+ [OAPPEND] = "append",
+ [OAS] = "=",
+ [OAS2] = "=",
+ [OBREAK] = "break",
+ [OCALL] = "function call", // not actual syntax
+ [OCAP] = "cap",
+ [OCASE] = "case",
+ [OCLOSE] = "close",
+ [OCOMPLEX] = "complex",
+ [OCOM] = "^",
+ [OCONTINUE] = "continue",
+ [OCOPY] = "copy",
+ [ODEC] = "--",
+ [ODELETE] = "delete",
+ [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] = "<-",
+ [ORECOVER] = "recover",
+ [ORETURN] = "return",
+ [ORSH] = ">>",
+ [OSELECT] = "select",
+ [OSEND] = "<-",
+ [OSUB] = "-",
+ [OSWITCH] = "switch",
+ [OXOR] = "^",
+};
+
+// Fmt "%O": Node opcodes
+static int
+Oconv(Fmt *fp)
+{
+ int o;
+
+ o = va_arg(fp->args, int);
+ if((fp->flags & FmtSharp) || fmtmode != FDbg)
+ if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
+ return fmtstrcpy(fp, goopnames[o]);
+
+ if(o >= 0 && o < nelem(opnames) && opnames[o] != nil)
+ return fmtstrcpy(fp, opnames[o]);
+
+ return fmtprint(fp, "O-%d", o);
+}
+
+static const char* classnames[] = {
+ "Pxxx",
+ "PEXTERN",
+ "PAUTO",
+ "PPARAM",
+ "PPARAMOUT",
+ "PPARAMREF",
+ "PFUNC",
+};
+
+// Fmt "%J": Node details.
+static 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%+lld)", 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);
+
+ switch(n->esc) {
+ case EscUnknown:
+ break;
+ case EscHeap:
+ fmtprint(fp, " esc(h)");
+ break;
+ case EscScope:
+ fmtprint(fp, " esc(s)");
+ break;
+ case EscNone:
+ fmtprint(fp, " esc(no)");
+ break;
+ case EscNever:
+ if(!c)
+ fmtprint(fp, " esc(N)");
+ break;
+ default:
+ fmtprint(fp, " esc(%d)", n->esc);
+ break;
+ }
+
+ if(n->escloopdepth)
+ fmtprint(fp, " ld(%d)", n->escloopdepth);
+
+ 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(n->embedded != 0)
+ fmtprint(fp, " embedded(%d)", n->embedded);
+
+ if(!c && n->used != 0)
+ fmtprint(fp, " used(%d)", n->used);
+ return 0;
+}
+
+// Fmt "%V": Values
+static int
+Vconv(Fmt *fp)
+{
+ Val *v;
+ vlong x;
+
+ v = va_arg(fp->args, Val*);
+
+ switch(v->ctype) {
+ case CTINT:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "%#B", v->u.xval);
+ return fmtprint(fp, "%B", v->u.xval);
+ case CTRUNE:
+ x = mpgetfix(v->u.xval);
+ if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
+ return fmtprint(fp, "'%c'", (int)x);
+ if(0 <= x && x < (1<<16))
+ return fmtprint(fp, "'\\u%04ux'", (int)x);
+ if(0 <= x && x <= Runemax)
+ return fmtprint(fp, "'\\U%08llux'", x);
+ return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
+ case CTFLT:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "%F", v->u.fval);
+ return fmtprint(fp, "%#F", v->u.fval);
+ case CTCPLX:
+ if((fp->flags & FmtSharp) || fmtmode == FExp)
+ return fmtprint(fp, "(%F+%Fi)", &v->u.cval->real, &v->u.cval->imag);
+ if(mpcmpfltc(&v->u.cval->real, 0) == 0)
+ return fmtprint(fp, "%#Fi", &v->u.cval->imag);
+ if(mpcmpfltc(&v->u.cval->imag, 0) == 0)
+ return fmtprint(fp, "%#F", &v->u.cval->real);
+ if(mpcmpfltc(&v->u.cval->imag, 0) < 0)
+ return fmtprint(fp, "(%#F%#Fi)", &v->u.cval->real, &v->u.cval->imag);
+ return fmtprint(fp, "(%#F+%#Fi)", &v->u.cval->real, &v->u.cval->imag);
+ case CTSTR:
+ return fmtprint(fp, "\"%Z\"", v->u.sval);
+ case CTBOOL:
+ if( v->u.bval)
+ return fmtstrcpy(fp, "true");
+ return fmtstrcpy(fp, "false");
+ case CTNIL:
+ return fmtstrcpy(fp, "nil");
+ }
+ return fmtprint(fp, "<ctype=%d>", v->ctype);
+}
+
+// Fmt "%Z": escaped string literals
+static 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;
+
+ // NOTE: Keep in sync with ../ld/go.c:/^Zconv.
+ 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;
+ case 0xFEFF: // BOM, basically disallowed in source code
+ fmtstrcpy(fp, "\\uFEFF");
+ break;
+ }
+ }
+ 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",
+};
+
+// Fmt "%E": etype
+static int
+Econv(Fmt *fp)
+{
+ int et;
+
+ et = va_arg(fp->args, int);
+ if(et >= 0 && et < nelem(etnames) && etnames[et] != nil)
+ return fmtstrcpy(fp, etnames[et]);
+ return fmtprint(fp, "E-%d", et);
+}
+
+// Fmt "%S": syms
+static int
+symfmt(Fmt *fp, Sym *s)
+{
+ char *p;
+
+ if(s->pkg && !(fp->flags&FmtShort)) {
+ switch(fmtmode) {
+ case FErr: // This is for the user
+ if(s->pkg == localpkg)
+ return fmtstrcpy(fp, s->name);
+ // If the name was used by multiple packages, display the full path,
+ if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1)
+ return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ case FDbg:
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+ case FTypeId:
+ if(fp->flags&FmtUnsigned)
+ return fmtprint(fp, "%s.%s", s->pkg->name, s->name); // dcommontype, typehash
+ return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name); // (methodsym), typesym, weaksym
+ case FExp:
+ if(s->name && s->name[0] == '.')
+ fatal("exporting synthetic symbol %s", s->name);
+ if(s->pkg != builtinpkg)
+ return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name);
+ }
+ }
+
+ if(fp->flags&FmtByte) { // FmtByte (hh) implies FmtShort (h)
+ // skip leading "type." in method name
+ p = utfrrune(s->name, '.');
+ if(p)
+ p++;
+ else
+ p = s->name;
+
+ // exportname needs to see the name without the prefix too.
+ if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg)
+ return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p);
+
+ return fmtstrcpy(fp, p);
+ }
+
+ return fmtstrcpy(fp, s->name);
+}
+
+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] = "untyped number",
+ [TBLANK] = "blank",
+};
+
+static int
+typefmt(Fmt *fp, Type *t)
+{
+ Type *t1;
+ Sym *s;
+
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ if (t == bytetype || t == runetype) {
+ // in %-T mode collapse rune and byte with their originals.
+ if(fmtmode != FTypeId)
+ return fmtprint(fp, "%hS", t->sym);
+ t = types[t->etype];
+ }
+
+ if(t == errortype)
+ return fmtstrcpy(fp, "error");
+
+ // Unless the 'l' flag was specified, if the type has a name, just print that name.
+ if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
+ switch(fmtmode) {
+ case FTypeId:
+ if(fp->flags&FmtShort) {
+ if(t->vargen)
+ return fmtprint(fp, "%hS·%d", t->sym, t->vargen);
+ return fmtprint(fp, "%hS", t->sym);
+ }
+ if(fp->flags&FmtUnsigned)
+ return fmtprint(fp, "%uS", t->sym);
+ // fallthrough
+ case FExp:
+ if(t->sym->pkg == localpkg && t->vargen)
+ return fmtprint(fp, "%S·%d", t->sym, t->vargen);
+ break;
+ }
+ return fmtprint(fp, "%S", t->sym);
+ }
+
+ if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
+ if(fmtmode == FErr && (t == idealbool || t == idealstring))
+ fmtstrcpy(fp, "untyped ");
+ return fmtstrcpy(fp, basicnames[t->etype]);
+ }
+
+ if(fmtmode == FDbg)
+ fmtprint(fp, "%E-", t->etype);
+
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ if(fmtmode == FTypeId && (fp->flags&FmtShort))
+ return fmtprint(fp, "*%hT", t->type);
+ return fmtprint(fp, "*%T", t->type);
+
+ case TARRAY:
+ if(t->bound >= 0)
+ return fmtprint(fp, "[%lld]%T", t->bound, t->type);
+ if(t->bound == -100)
+ return fmtprint(fp, "[...]%T", 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 TINTER:
+ fmtstrcpy(fp, "interface {");
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(exportname(t1->sym->name)) {
+ if(t1->down)
+ fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
+ else
+ fmtprint(fp, " %hS%hT ", t1->sym, t1->type);
+ } else {
+ // non-exported method names must be qualified
+ if(t1->down)
+ fmtprint(fp, " %uS%hT;", t1->sym, t1->type);
+ else
+ fmtprint(fp, " %uS%hT ", t1->sym, t1->type);
+ }
+ fmtstrcpy(fp, "}");
+ return 0;
+
+ case TFUNC:
+ if(fp->flags & FmtShort) {
+ fmtprint(fp, "%T", getinargx(t));
+ } else {
+ if(t->thistuple)
+ fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t));
+ else
+ fmtprint(fp, "func%T", getinargx(t));
+ }
+ switch(t->outtuple) {
+ case 0:
+ break;
+ case 1:
+ if(fmtmode != FExp) {
+ fmtprint(fp, " %T", getoutargx(t)->type->type); // struct->field->field's type
+ break;
+ }
+ default:
+ fmtprint(fp, " %T", getoutargx(t));
+ break;
+ }
+ return 0;
+
+ case TSTRUCT:
+ // Format the bucket struct for map[x]y as map.bucket[x]y.
+ // This avoids a recursive print that generates very long names.
+ if(t->map != T) {
+ if(t->map->bucket == t) {
+ return fmtprint(fp, "map.bucket[%T]%T", t->map->down, t->map->type);
+ }
+ if(t->map->hmap == t) {
+ return fmtprint(fp, "map.hdr[%T]%T", t->map->down, t->map->type);
+ }
+ if(t->map->hiter == t) {
+ return fmtprint(fp, "map.iter[%T]%T", t->map->down, t->map->type);
+ }
+ yyerror("unknown internal map type");
+ }
+
+ if(t->funarg) {
+ fmtstrcpy(fp, "(");
+ if(fmtmode == FTypeId || fmtmode == FErr) { // no argument names on function signature, and no "noescape"/"nosplit" tags
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, "%hT, ", t1);
+ else
+ fmtprint(fp, "%hT", t1);
+ } else {
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, "%T, ", t1);
+ else
+ fmtprint(fp, "%T", t1);
+ }
+ fmtstrcpy(fp, ")");
+ } else {
+ fmtstrcpy(fp, "struct {");
+ for(t1=t->type; t1!=T; t1=t1->down)
+ if(t1->down)
+ fmtprint(fp, " %lT;", t1);
+ else
+ fmtprint(fp, " %lT ", t1);
+ fmtstrcpy(fp, "}");
+ }
+ return 0;
+
+ case TFIELD:
+ if(!(fp->flags&FmtShort)) {
+ s = t->sym;
+
+ // Take the name from the original, lest we substituted it with ~r%d or ~b%d.
+ // ~r%d is a (formerly) unnamed result.
+ if ((fmtmode == FErr || fmtmode == FExp) && t->nname != N) {
+ if(t->nname->orig != N) {
+ s = t->nname->orig->sym;
+ if(s != S && s->name[0] == '~') {
+ if(s->name[1] == 'r') // originally an unnamed result
+ s = S;
+ else if(s->name[1] == 'b') // originally the blank identifier _
+ s = lookup("_");
+ }
+ } else
+ s = S;
+ }
+
+ if(s != S && !t->embedded) {
+ if(t->funarg)
+ fmtprint(fp, "%N ", t->nname);
+ else if(fp->flags&FmtLong)
+ fmtprint(fp, "%hhS ", s); // qualify non-exported names (used on structs, not on funarg)
+ else
+ fmtprint(fp, "%S ", s);
+ } else if(fmtmode == FExp) {
+ // TODO(rsc) this breaks on the eliding of unused arguments in the backend
+ // when this is fixed, the special case in dcl.c checkarglist can go.
+ //if(t->funarg)
+ // fmtstrcpy(fp, "_ ");
+ //else
+ if(t->embedded && s->pkg != nil && s->pkg->path->len > 0)
+ fmtprint(fp, "@\"%Z\".? ", s->pkg->path);
+ else
+ fmtstrcpy(fp, "? ");
+ }
+ }
+
+ if(t->isddd)
+ fmtprint(fp, "...%T", t->type->type);
+ else
+ fmtprint(fp, "%T", t->type);
+
+ if(!(fp->flags&FmtShort) && t->note)
+ fmtprint(fp, " \"%Z\"", t->note);
+ return 0;
+
+ case TFORW:
+ if(t->sym)
+ return fmtprint(fp, "undefined %S", t->sym);
+ return fmtstrcpy(fp, "undefined");
+
+ case TUNSAFEPTR:
+ if(fmtmode == FExp)
+ return fmtprint(fp, "@\"unsafe\".Pointer");
+ return fmtprint(fp, "unsafe.Pointer");
+ }
+
+ if(fmtmode == FExp)
+ fatal("missing %E case during export", t->etype);
+ // Don't know how to handle - fall back to detailed prints.
+ return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type);
+}
+
+// Statements which may be rendered with a simplestmt as init.
+static int
+stmtwithinit(int op)
+{
+ switch(op) {
+ case OIF:
+ case OFOR:
+ case OSWITCH:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+stmtfmt(Fmt *f, Node *n)
+{
+ int complexinit, simpleinit, extrablock;
+
+ // some statements allow for an init, but at most one,
+ // but we may have an arbitrary number added, eg by typecheck
+ // and inlining. If it doesn't fit the syntax, emit an enclosing
+ // block starting with the init statements.
+
+ // if we can just say "for" n->ninit; ... then do so
+ simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op);
+ // otherwise, print the inits as separate statements
+ complexinit = n->ninit && !simpleinit && (fmtmode != FErr);
+ // but if it was for if/for/switch, put in an extra surrounding block to limit the scope
+ extrablock = complexinit && stmtwithinit(n->op);
+
+ if(extrablock)
+ fmtstrcpy(f, "{");
+
+ if(complexinit)
+ fmtprint(f, " %H; ", n->ninit);
+
+ switch(n->op){
+ case ODCL:
+ if(fmtmode == FExp) {
+ switch(n->left->class&~PHEAP) {
+ case PPARAM:
+ case PPARAMOUT:
+ case PAUTO:
+ fmtprint(f, "var %N %T", n->left, n->left->type);
+ goto ret;
+ }
+ }
+ fmtprint(f, "var %S %T", n->left->sym, n->left->type);
+ break;
+
+ case ODCLFIELD:
+ if(n->left)
+ fmtprint(f, "%N %N", n->left, n->right);
+ else
+ fmtprint(f, "%N", n->right);
+ break;
+
+ case OAS:
+ // Don't export "v = <N>" initializing statements, hope they're always
+ // preceded by the DCL which will be re-parsed and typecheck to reproduce
+ // the "v = <N>" again.
+ if(fmtmode == FExp && n->right == N)
+ break;
+
+ if(n->colas && !complexinit)
+ fmtprint(f, "%N := %N", n->left, n->right);
+ else
+ fmtprint(f, "%N = %N", n->left, n->right);
+ break;
+
+ case OASOP:
+ if(n->implicit) {
+ if(n->etype == OADD)
+ fmtprint(f, "%N++", n->left);
+ else
+ fmtprint(f, "%N--", n->left);
+ break;
+ }
+ fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right);
+ break;
+
+ case OAS2:
+ if(n->colas && !complexinit) {
+ fmtprint(f, "%,H := %,H", n->list, n->rlist);
+ break;
+ }
+ // fallthrough
+ case OAS2DOTTYPE:
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OAS2RECV:
+ fmtprint(f, "%,H = %,H", n->list, n->rlist);
+ break;
+
+ case ORETURN:
+ fmtprint(f, "return %,H", n->list);
+ break;
+
+ case ORETJMP:
+ fmtprint(f, "retjmp %S", n->sym);
+ break;
+
+ case OPROC:
+ fmtprint(f, "go %N", n->left);
+ break;
+
+ case ODEFER:
+ fmtprint(f, "defer %N", n->left);
+ break;
+
+ case OIF:
+ if(simpleinit)
+ fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody);
+ else
+ fmtprint(f, "if %N { %H }", n->ntest, n->nbody);
+ if(n->nelse)
+ fmtprint(f, " else { %H }", n->nelse);
+ break;
+
+ case OFOR:
+ if(fmtmode == FErr) { // TODO maybe only if FmtShort, same below
+ fmtstrcpy(f, "for loop");
+ break;
+ }
+
+ fmtstrcpy(f, "for");
+ if(simpleinit)
+ fmtprint(f, " %N;", n->ninit->n);
+ else if(n->nincr)
+ fmtstrcpy(f, " ;");
+
+ if(n->ntest)
+ fmtprint(f, " %N", n->ntest);
+
+ if(n->nincr)
+ fmtprint(f, "; %N", n->nincr);
+ else if(simpleinit)
+ fmtstrcpy(f, ";");
+
+
+ fmtprint(f, " { %H }", n->nbody);
+ break;
+
+ case ORANGE:
+ if(fmtmode == FErr) {
+ fmtstrcpy(f, "for loop");
+ break;
+ }
+
+ if(n->list == nil) {
+ fmtprint(f, "for range %N { %H }", n->right, n->nbody);
+ break;
+ }
+ fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
+ break;
+
+ case OSELECT:
+ case OSWITCH:
+ if(fmtmode == FErr) {
+ fmtprint(f, "%O statement", n->op);
+ break;
+ }
+
+ fmtprint(f, "%#O", n->op);
+ if(simpleinit)
+ fmtprint(f, " %N;", n->ninit->n);
+ if(n->ntest)
+ fmtprint(f, "%N", n->ntest);
+
+ fmtprint(f, " { %H }", n->list);
+ break;
+
+ case OCASE:
+ case OXCASE:
+ if(n->list)
+ fmtprint(f, "case %,H: %H", n->list, n->nbody);
+ else
+ fmtprint(f, "default: %H", n->nbody);
+ break;
+
+ case OBREAK:
+ case OCONTINUE:
+ case OGOTO:
+ case OFALL:
+ case OXFALL:
+ if(n->left)
+ fmtprint(f, "%#O %N", n->op, n->left);
+ else
+ fmtprint(f, "%#O", n->op);
+ break;
+
+ case OEMPTY:
+ break;
+
+ case OLABEL:
+ fmtprint(f, "%N: ", n->left);
+ break;
+
+ }
+ret:
+
+ if(extrablock)
+ fmtstrcpy(f, "}");
+
+ return 0;
+}
+
+
+static int opprec[] = {
+ [OAPPEND] = 8,
+ [OARRAYBYTESTR] = 8,
+ [OARRAYLIT] = 8,
+ [OARRAYRUNESTR] = 8,
+ [OCALLFUNC] = 8,
+ [OCALLINTER] = 8,
+ [OCALLMETH] = 8,
+ [OCALL] = 8,
+ [OCAP] = 8,
+ [OCLOSE] = 8,
+ [OCONVIFACE] = 8,
+ [OCONVNOP] = 8,
+ [OCONV] = 8,
+ [OCOPY] = 8,
+ [ODELETE] = 8,
+ [OLEN] = 8,
+ [OLITERAL] = 8,
+ [OMAKESLICE] = 8,
+ [OMAKE] = 8,
+ [OMAPLIT] = 8,
+ [ONAME] = 8,
+ [ONEW] = 8,
+ [ONONAME] = 8,
+ [OPACK] = 8,
+ [OPANIC] = 8,
+ [OPAREN] = 8,
+ [OPRINTN] = 8,
+ [OPRINT] = 8,
+ [ORUNESTR] = 8,
+ [OSTRARRAYBYTE] = 8,
+ [OSTRARRAYRUNE] = 8,
+ [OSTRUCTLIT] = 8,
+ [OTARRAY] = 8,
+ [OTCHAN] = 8,
+ [OTFUNC] = 8,
+ [OTINTER] = 8,
+ [OTMAP] = 8,
+ [OTSTRUCT] = 8,
+
+ [OINDEXMAP] = 8,
+ [OINDEX] = 8,
+ [OSLICE] = 8,
+ [OSLICESTR] = 8,
+ [OSLICEARR] = 8,
+ [OSLICE3] = 8,
+ [OSLICE3ARR] = 8,
+ [ODOTINTER] = 8,
+ [ODOTMETH] = 8,
+ [ODOTPTR] = 8,
+ [ODOTTYPE2] = 8,
+ [ODOTTYPE] = 8,
+ [ODOT] = 8,
+ [OXDOT] = 8,
+ [OCALLPART] = 8,
+
+ [OPLUS] = 7,
+ [ONOT] = 7,
+ [OCOM] = 7,
+ [OMINUS] = 7,
+ [OADDR] = 7,
+ [OIND] = 7,
+ [ORECV] = 7,
+
+ [OMUL] = 6,
+ [ODIV] = 6,
+ [OMOD] = 6,
+ [OLSH] = 6,
+ [ORSH] = 6,
+ [OAND] = 6,
+ [OANDNOT] = 6,
+
+ [OADD] = 5,
+ [OSUB] = 5,
+ [OOR] = 5,
+ [OXOR] = 5,
+
+ [OEQ] = 4,
+ [OLT] = 4,
+ [OLE] = 4,
+ [OGE] = 4,
+ [OGT] = 4,
+ [ONE] = 4,
+ [OCMPSTR] = 4,
+ [OCMPIFACE] = 4,
+
+ [OSEND] = 3,
+ [OANDAND] = 2,
+ [OOROR] = 1,
+
+ // Statements handled by stmtfmt
+ [OAS] = -1,
+ [OAS2] = -1,
+ [OAS2DOTTYPE] = -1,
+ [OAS2FUNC] = -1,
+ [OAS2MAPR] = -1,
+ [OAS2RECV] = -1,
+ [OASOP] = -1,
+ [OBREAK] = -1,
+ [OCASE] = -1,
+ [OCONTINUE] = -1,
+ [ODCL] = -1,
+ [ODCLFIELD] = -1,
+ [ODEFER] = -1,
+ [OEMPTY] = -1,
+ [OFALL] = -1,
+ [OFOR] = -1,
+ [OGOTO] = -1,
+ [OIF] = -1,
+ [OLABEL] = -1,
+ [OPROC] = -1,
+ [ORANGE] = -1,
+ [ORETURN] = -1,
+ [OSELECT] = -1,
+ [OSWITCH] = -1,
+ [OXCASE] = -1,
+ [OXFALL] = -1,
+
+ [OEND] = 0
+};
+
+static int
+exprfmt(Fmt *f, Node *n, int prec)
+{
+ int nprec;
+ int ptrlit;
+ NodeList *l;
+
+ while(n && n->implicit && (n->op == OIND || n->op == OADDR))
+ n = n->left;
+
+ if(n == N)
+ return fmtstrcpy(f, "<N>");
+
+ nprec = opprec[n->op];
+ if(n->op == OTYPE && n->sym != S)
+ nprec = 8;
+
+ if(prec > nprec)
+ return fmtprint(f, "(%N)", n);
+
+ switch(n->op) {
+ case OPAREN:
+ return fmtprint(f, "(%N)", n->left);
+
+ case ODDDARG:
+ return fmtprint(f, "... argument");
+
+ case OREGISTER:
+ return fmtprint(f, "%R", n->val.u.reg);
+
+ case OLITERAL: // this is a bit of a mess
+ if(fmtmode == FErr && n->sym != S)
+ return fmtprint(f, "%S", n->sym);
+ if(n->val.ctype == CTNIL && n->orig != N && n->orig != n)
+ return exprfmt(f, n->orig, prec);
+ if(n->type != T && n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {
+ // Need parens when type begins with what might
+ // be misinterpreted as a unary operator: * or <-.
+ if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))
+ return fmtprint(f, "(%T)(%V)", n->type, &n->val);
+ else
+ return fmtprint(f, "%T(%V)", n->type, &n->val);
+ }
+ return fmtprint(f, "%V", &n->val);
+
+ case ONAME:
+ // Special case: name used as local variable in export.
+ // _ becomes ~b%d internally; print as _ for export
+ if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b')
+ return fmtprint(f, "_");
+ if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0)
+ return fmtprint(f, "%S·%d", n->sym, n->vargen);
+
+ // Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
+ // but for export, this should be rendered as (*pkg.T).meth.
+ // These nodes have the special property that they are names with a left OTYPE and a right ONAME.
+ if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) {
+ if(isptr[n->left->type->etype])
+ return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym);
+ else
+ return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym);
+ }
+ //fallthrough
+ case OPACK:
+ case ONONAME:
+ return fmtprint(f, "%S", n->sym);
+
+ case OTYPE:
+ if(n->type == T && n->sym != S)
+ return fmtprint(f, "%S", n->sym);
+ return fmtprint(f, "%T", n->type);
+
+ case OTARRAY:
+ if(n->left)
+ return fmtprint(f, "[]%N", n->left);
+ return fmtprint(f, "[]%N", n->right); // happens before typecheck
+
+ case OTMAP:
+ return fmtprint(f, "map[%N]%N", n->left, n->right);
+
+ case OTCHAN:
+ switch(n->etype) {
+ case Crecv:
+ return fmtprint(f, "<-chan %N", n->left);
+ case Csend:
+ return fmtprint(f, "chan<- %N", n->left);
+ default:
+ if(n->left != N && n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv)
+ return fmtprint(f, "chan (%N)", n->left);
+ else
+ return fmtprint(f, "chan %N", n->left);
+ }
+
+ case OTSTRUCT:
+ return fmtprint(f, "<struct>");
+
+ case OTINTER:
+ return fmtprint(f, "<inter>");
+
+ case OTFUNC:
+ return fmtprint(f, "<func>");
+
+ case OCLOSURE:
+ if(fmtmode == FErr)
+ return fmtstrcpy(f, "func literal");
+ if(n->nbody)
+ return fmtprint(f, "%T { %H }", n->type, n->nbody);
+ return fmtprint(f, "%T { %H }", n->type, n->closure->nbody);
+
+ case OCOMPLIT:
+ ptrlit = n->right != N && n->right->implicit && n->right->type && isptr[n->right->type->etype];
+ if(fmtmode == FErr) {
+ if(n->right != N && n->right->type != T && !n->implicit) {
+ if(ptrlit)
+ return fmtprint(f, "&%T literal", n->right->type->type);
+ else
+ return fmtprint(f, "%T literal", n->right->type);
+ }
+ return fmtstrcpy(f, "composite literal");
+ }
+ if(fmtmode == FExp && ptrlit)
+ // typecheck has overwritten OIND by OTYPE with pointer type.
+ return fmtprint(f, "(&%T{ %,H })", n->right->type->type, n->list);
+ return fmtprint(f, "(%N{ %,H })", n->right, n->list);
+
+ case OPTRLIT:
+ if(fmtmode == FExp && n->left->implicit)
+ return fmtprint(f, "%N", n->left);
+ return fmtprint(f, "&%N", n->left);
+
+ case OSTRUCTLIT:
+ if(fmtmode == FExp) { // requires special handling of field names
+ if(n->implicit)
+ fmtstrcpy(f, "{");
+ else
+ fmtprint(f, "(%T{", n->type);
+ for(l=n->list; l; l=l->next) {
+ fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right);
+
+ if(l->next)
+ fmtstrcpy(f, ",");
+ else
+ fmtstrcpy(f, " ");
+ }
+ if(!n->implicit)
+ return fmtstrcpy(f, "})");
+ return fmtstrcpy(f, "}");
+ }
+ // fallthrough
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ if(fmtmode == FErr)
+ return fmtprint(f, "%T literal", n->type);
+ if(fmtmode == FExp && n->implicit)
+ return fmtprint(f, "{ %,H }", n->list);
+ return fmtprint(f, "(%T{ %,H })", n->type, n->list);
+
+ case OKEY:
+ if(n->left && n->right) {
+ if(fmtmode == FExp && n->left->type && n->left->type->etype == TFIELD) {
+ // requires special handling of field names
+ return fmtprint(f, "%hhS:%N", n->left->sym, n->right);
+ } else
+ return fmtprint(f, "%N:%N", n->left, n->right);
+ }
+ if(!n->left && n->right)
+ return fmtprint(f, ":%N", n->right);
+ if(n->left && !n->right)
+ return fmtprint(f, "%N:", n->left);
+ return fmtstrcpy(f, ":");
+
+ case OXDOT:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTINTER:
+ case ODOTMETH:
+ case OCALLPART:
+ exprfmt(f, n->left, nprec);
+ if(n->right == N || n->right->sym == S)
+ return fmtstrcpy(f, ".<nil>");
+ return fmtprint(f, ".%hhS", n->right->sym);
+
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ exprfmt(f, n->left, nprec);
+ if(n->right != N)
+ return fmtprint(f, ".(%N)", n->right);
+ return fmtprint(f, ".(%T)", n->type);
+
+ case OINDEX:
+ case OINDEXMAP:
+ case OSLICE:
+ case OSLICESTR:
+ case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
+ exprfmt(f, n->left, nprec);
+ return fmtprint(f, "[%N]", n->right);
+
+ case OCOPY:
+ case OCOMPLEX:
+ return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right);
+
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OARRAYBYTESTR:
+ case OARRAYRUNESTR:
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ case ORUNESTR:
+ if(n->type == T || n->type->sym == S)
+ return fmtprint(f, "(%T)(%N)", n->type, n->left);
+ if(n->left)
+ return fmtprint(f, "%T(%N)", n->type, n->left);
+ return fmtprint(f, "%T(%,H)", n->type, n->list);
+
+ case OREAL:
+ case OIMAG:
+ case OAPPEND:
+ case OCAP:
+ case OCLOSE:
+ case ODELETE:
+ case OLEN:
+ case OMAKE:
+ case ONEW:
+ case OPANIC:
+ case ORECOVER:
+ case OPRINT:
+ case OPRINTN:
+ if(n->left)
+ return fmtprint(f, "%#O(%N)", n->op, n->left);
+ if(n->isddd)
+ return fmtprint(f, "%#O(%,H...)", n->op, n->list);
+ return fmtprint(f, "%#O(%,H)", n->op, n->list);
+
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ exprfmt(f, n->left, nprec);
+ if(n->isddd)
+ return fmtprint(f, "(%,H...)", n->list);
+ return fmtprint(f, "(%,H)", n->list);
+
+ case OMAKEMAP:
+ case OMAKECHAN:
+ case OMAKESLICE:
+ if(n->list) // pre-typecheck
+ return fmtprint(f, "make(%T, %,H)", n->type, n->list);
+ if(n->right)
+ return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right);
+ if(n->left)
+ return fmtprint(f, "make(%T, %N)", n->type, n->left);
+ return fmtprint(f, "make(%T)", n->type);
+
+ // Unary
+ case OPLUS:
+ case OMINUS:
+ case OADDR:
+ case OCOM:
+ case OIND:
+ case ONOT:
+ case ORECV:
+ if(n->left->op == n->op)
+ fmtprint(f, "%#O ", n->op);
+ else
+ fmtprint(f, "%#O", n->op);
+ return exprfmt(f, n->left, nprec+1);
+
+ // Binary
+ case OADD:
+ case OAND:
+ 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);
+ return 0;
+
+ case OADDSTR:
+ for(l=n->list; l; l=l->next) {
+ if(l != n->list)
+ fmtprint(f, " + ");
+ exprfmt(f, l->n, nprec);
+ }
+ return 0;
+
+ case OCMPSTR:
+ case OCMPIFACE:
+ exprfmt(f, n->left, nprec);
+ fmtprint(f, " %#O ", n->etype);
+ exprfmt(f, n->right, nprec+1);
+ return 0;
+ }
+
+ return fmtprint(f, "<node %O>", n->op);
+}
+
+static int
+nodefmt(Fmt *f, Node *n)
+{
+ Type *t;
+
+ t = n->type;
+
+ // we almost always want the original, except in export mode for literals
+ // this saves the importer some work, and avoids us having to redo some
+ // special casing for package unsafe
+ if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N)
+ n = n->orig;
+
+ if(f->flags&FmtLong && t != T) {
+ if(t->etype == TNIL)
+ return fmtprint(f, "nil");
+ else
+ return fmtprint(f, "%N (type %T)", n, t);
+ }
+
+ // TODO inlining produces expressions with ninits. we can't print these yet.
+
+ if(opprec[n->op] < 0)
+ return stmtfmt(f, n);
+
+ return exprfmt(f, n, 0);
+}
+
+static int dumpdepth;
+
+static void
+indent(Fmt *fp)
+{
+ int i;
+
+ fmtstrcpy(fp, "\n");
+ for(i = 0; i < dumpdepth; ++i)
+ fmtstrcpy(fp, ". ");
+}
+
+static int
+nodedump(Fmt *fp, Node *n)
+{
+ int recur;
+
+ if(n == N)
+ return 0;
+
+ recur = !(fp->flags&FmtShort);
+
+ if(recur) {
+ indent(fp);
+ if(dumpdepth > 10)
+ return fmtstrcpy(fp, "...");
+
+ if(n->ninit != nil) {
+ fmtprint(fp, "%O-init%H", n->op, n->ninit);
+ indent(fp);
+ }
+ }
+
+// fmtprint(fp, "[%p]", n);
+
+ switch(n->op) {
+ default:
+ fmtprint(fp, "%O%J", n->op, n);
+ break;
+ case OREGISTER:
+ case OINDREG:
+ fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
+ break;
+ case OLITERAL:
+ fmtprint(fp, "%O-%V%J", n->op, &n->val, n);
+ break;
+ case ONAME:
+ case ONONAME:
+ if(n->sym != S)
+ fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
+ else
+ fmtprint(fp, "%O%J", n->op, n);
+ if(recur && n->type == T && n->ntype) {
+ indent(fp);
+ fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
+ }
+ break;
+ case OASOP:
+ fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
+ break;
+ case OTYPE:
+ fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type);
+ if(recur && n->type == T && n->ntype) {
+ indent(fp);
+ fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
+ }
+ break;
+ }
+
+ if(n->sym != S && n->op != ONAME)
+ fmtprint(fp, " %S G%d", n->sym, n->vargen);
+
+ if(n->type != T)
+ fmtprint(fp, " %T", n->type);
+
+ if(recur) {
+ if(n->left)
+ fmtprint(fp, "%N", n->left);
+ if(n->right)
+ fmtprint(fp, "%N", n->right);
+ if(n->list) {
+ indent(fp);
+ fmtprint(fp, "%O-list%H", n->op, n->list);
+ }
+ if(n->rlist) {
+ indent(fp);
+ fmtprint(fp, "%O-rlist%H", n->op, n->rlist);
+ }
+ if(n->ntest) {
+ indent(fp);
+ fmtprint(fp, "%O-test%N", n->op, n->ntest);
+ }
+ if(n->nbody) {
+ indent(fp);
+ fmtprint(fp, "%O-body%H", n->op, n->nbody);
+ }
+ if(n->nelse) {
+ indent(fp);
+ fmtprint(fp, "%O-else%H", n->op, n->nelse);
+ }
+ if(n->nincr) {
+ indent(fp);
+ fmtprint(fp, "%O-incr%N", n->op, n->nincr);
+ }
+ }
+
+ return 0;
+}
+
+// Fmt "%S": syms
+// Flags: "%hS" suppresses qualifying with package
+static int
+Sconv(Fmt *fp)
+{
+ Sym *s;
+ int r, sm;
+ unsigned long sf;
+
+ if(fp->flags&FmtLong)
+ return linksymfmt(fp);
+
+ s = va_arg(fp->args, Sym*);
+ if(s == S)
+ return fmtstrcpy(fp, "<S>");
+
+ if(s->name && s->name[0] == '_' && s->name[1] == '\0')
+ return fmtstrcpy(fp, "_");
+
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+ r = symfmt(fp, s);
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+// Fmt "%T": types.
+// Flags: 'l' print definition, not name
+// 'h' omit 'func' and receiver from function types, short type names
+// 'u' package name, not prefix (FTypeId mode, sticky)
+static int
+Tconv(Fmt *fp)
+{
+ Type *t;
+ int r, sm;
+ unsigned long sf;
+
+ t = va_arg(fp->args, Type*);
+ if(t == T)
+ return fmtstrcpy(fp, "<T>");
+
+ if(t->trecur > 4)
+ return fmtstrcpy(fp, "<...>");
+
+ t->trecur++;
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+
+ if(fmtmode == FTypeId && (sf&FmtUnsigned))
+ fmtpkgpfx++;
+ if(fmtpkgpfx)
+ fp->flags |= FmtUnsigned;
+
+ r = typefmt(fp, t);
+
+ if(fmtmode == FTypeId && (sf&FmtUnsigned))
+ fmtpkgpfx--;
+
+ fp->flags = sf;
+ fmtmode = sm;
+ t->trecur--;
+ return r;
+}
+
+// Fmt '%N': Nodes.
+// Flags: 'l' suffix with "(type %T)" where possible
+// '+h' in debug mode, don't recurse, no multiline output
+static int
+Nconv(Fmt *fp)
+{
+ Node *n;
+ int r, sm;
+ unsigned long sf;
+
+ n = va_arg(fp->args, Node*);
+ if(n == N)
+ return fmtstrcpy(fp, "<N>");
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+
+ r = -1;
+ switch(fmtmode) {
+ case FErr:
+ case FExp:
+ r = nodefmt(fp, n);
+ break;
+ case FDbg:
+ dumpdepth++;
+ r = nodedump(fp, n);
+ dumpdepth--;
+ break;
+ default:
+ fatal("unhandled %%N mode");
+ }
+
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+// Fmt '%H': NodeList.
+// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
+static int
+Hconv(Fmt *fp)
+{
+ NodeList *l;
+ int r, sm;
+ unsigned long sf;
+ char *sep;
+
+ l = va_arg(fp->args, NodeList*);
+
+ if(l == nil && fmtmode == FDbg)
+ return fmtstrcpy(fp, "<nil>");
+
+ sf = fp->flags;
+ sm = setfmode(&fp->flags);
+ r = 0;
+ sep = "; ";
+ if(fmtmode == FDbg)
+ sep = "\n";
+ else if(fp->flags & FmtComma)
+ sep = ", ";
+
+ for(;l; l=l->next) {
+ r += fmtprint(fp, "%N", l->n);
+ if(l->next)
+ r += fmtstrcpy(fp, sep);
+ }
+
+ fp->flags = sf;
+ fmtmode = sm;
+ return r;
+}
+
+void
+fmtinstallgo(void)
+{
+ fmtmode = FErr;
+ fmtinstall('E', Econv); // etype opcodes
+ fmtinstall('J', Jconv); // all the node flags
+ fmtinstall('H', Hconv); // node lists
+ fmtinstall('L', Lconv); // line number
+ fmtinstall('N', Nconv); // node pointer
+ fmtinstall('O', Oconv); // node opcodes
+ fmtinstall('S', Sconv); // sym pointer
+ fmtinstall('T', Tconv); // type pointer
+ fmtinstall('V', Vconv); // val pointer
+ fmtinstall('Z', Zconv); // escaped string
+
+ // These are in mparith1.c
+ fmtinstall('B', Bconv); // big numbers
+ fmtinstall('F', Fconv); // big float numbers
+
+}
+
+void
+dumplist(char *s, NodeList *l)
+{
+ print("%s%+H\n", s, l);
+}
+
+void
+dump(char *s, Node *n)
+{
+ print("%s [%p]%+N\n", s, n, n);
+}
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
new file mode 100644
index 0000000..c7c9fcd
--- /dev/null
+++ b/src/cmd/gc/gen.c
@@ -0,0 +1,991 @@
+// 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 <u.h>
+#include <libc.h>
+#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;
+}
+
+/*
+ * 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.
+ */
+void
+addrescapes(Node *n)
+{
+ char buf[100];
+ Node *oldfn;
+
+ switch(n->op) {
+ default:
+ // probably a type error already.
+ // dump("addrescapes", n);
+ break;
+
+ case ONAME:
+ if(n == nodfp)
+ break;
+
+ // if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
+ // on PPARAM it means something different.
+ if(n->class == PAUTO && n->esc == EscNever)
+ 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;
+ // fallthrough
+
+ case PAUTO:
+ n->class |= PHEAP;
+ n->addable = 0;
+ n->ullman = 2;
+ n->xoffset = 0;
+
+ // create stack variable to hold pointer to heap
+ oldfn = curfn;
+ curfn = n->curfn;
+ n->heapaddr = temp(ptrto(n->type));
+ snprint(buf, sizeof buf, "&%S", n->sym);
+ n->heapaddr->sym = lookup(buf);
+ n->heapaddr->orig->sym = n->heapaddr->sym;
+ n->esc = EscHeap;
+ if(debug['m'])
+ print("%L: moved to heap: %N\n", n->lineno, n);
+ curfn = oldfn;
+ 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;
+ }
+}
+
+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->defn == 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;
+
+//dump("gen", n);
+
+ lno = setlineno(n);
+ wasregalloc = anyregalloc();
+
+ if(n == N)
+ goto ret;
+
+ if(n->ninit)
+ genlist(n->ninit);
+
+ setlineno(n);
+
+ switch(n->op) {
+ default:
+ fatal("gen: unknown op %+hN", 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:
+ if(isblanksym(n->left->sym))
+ break;
+
+ 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->defn) {
+ switch(n->defn->op) {
+ case OFOR:
+ case OSWITCH:
+ case OSELECT:
+ // so stmtlabel can find the label
+ n->defn->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, -1, 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, -n->likely, 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:
+ case ORETJMP:
+ cgen_ret(n);
+ break;
+
+ case OCHECKNIL:
+ cgen_checknil(n->left);
+ break;
+
+ case OVARKILL:
+ gvarkill(n->left);
+ 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 n2;
+ Node *l;
+
+ // generate a rewrite in n2 for the method call
+ // (p.f)(...) goes to (f)(p,...)
+
+ l = n->left;
+ if(l->op != ODOTMETH)
+ fatal("cgen_callmeth: not dotmethod: %N");
+
+ n2 = *n;
+ n2.op = OCALLFUNC;
+ n2.left = l->right;
+ n2.left->type = l->type;
+
+ if(n2.left->op == ONAME)
+ n2.left->class = PFUNC;
+ cgen_call(&n2, proc);
+}
+
+/*
+ * generate code to start new proc running call n.
+ */
+static 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.
+ * 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(compiling_runtime)
+ yyerror("%N escapes to heap, not allowed in runtime.", n);
+ 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;
+
+ case OIND:
+ cgen_checknil(nr->left);
+ break;
+
+ // special enough to just evaluate
+ default:
+ tempname(&tmp, nr->type);
+ cgen_as(&tmp, nr);
+ gused(&tmp);
+ }
+}
+
+/*
+ * clearslim generates code to zero a slim node.
+ */
+void
+clearslim(Node *n)
+{
+ Node z;
+ Mpflt zero;
+
+ memset(&z, 0, sizeof(z));
+ z.op = OLITERAL;
+ z.type = n->type;
+ z.addable = 1;
+
+ switch(simtype[n->type->etype]) {
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ z.val.u.cval = mal(sizeof(*z.val.u.cval));
+ mpmovecflt(&z.val.u.cval->real, 0.0);
+ mpmovecflt(&z.val.u.cval->imag, 0.0);
+ break;
+
+ case TFLOAT32:
+ case TFLOAT64:
+ mpmovecflt(&zero, 0.0);
+ z.val.ctype = CTFLT;
+ z.val.u.fval = &zero;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TMAP:
+ z.val.ctype = CTNIL;
+ break;
+
+ case TBOOL:
+ z.val.ctype = CTBOOL;
+ break;
+
+ case TINT8:
+ case TINT16:
+ case TINT32:
+ case TINT64:
+ case TUINT8:
+ case TUINT16:
+ case TUINT32:
+ case TUINT64:
+ z.val.ctype = CTINT;
+ z.val.u.xval = mal(sizeof(*z.val.u.xval));
+ mpmovecfix(z.val.u.xval, 0);
+ break;
+
+ default:
+ fatal("clearslim called on type %T", n->type);
+ }
+
+ ullmancalc(&z);
+ cgen(&z, n);
+}
+
+/*
+ * generate assignment:
+ * nl = nr
+ * nr == N means zero nl.
+ */
+void
+cgen_as(Node *nl, Node *nr)
+{
+ Type *tl;
+
+ if(debug['g']) {
+ dump("cgen_as", nl);
+ dump("cgen_as = ", nr);
+ }
+
+ while(nr != N && nr->op == OCONVNOP)
+ nr = nr->left;
+
+ if(nl == N || isblank(nl)) {
+ cgen_discard(nr);
+ return;
+ }
+
+ if(nr == N || iszero(nr)) {
+ // heaps should already be clear
+ if(nr == N && (nl->class & PHEAP))
+ return;
+
+ tl = nl->type;
+ if(tl == T)
+ return;
+ if(isfat(tl)) {
+ if(nl->op == ONAME)
+ gvardef(nl);
+ clearfat(nl);
+ return;
+ }
+ clearslim(nl);
+ return;
+ }
+
+ tl = nl->type;
+ if(tl == T)
+ return;
+
+ cgen(nr, nl);
+}
+
+/*
+ * generate:
+ * res = iface{typ, data}
+ * n->left is typ
+ * n->right is data
+ */
+void
+cgen_eface(Node *n, Node *res)
+{
+ /*
+ * the right node of an eface may contain function calls that uses res as an argument,
+ * so it's important that it is done first
+ */
+ Node dst;
+ Node *tmp;
+
+ tmp = temp(types[tptr]);
+ cgen(n->right, tmp);
+
+ gvardef(res);
+
+ dst = *res;
+ dst.type = types[tptr];
+ dst.xoffset += widthptr;
+ cgen(tmp, &dst);
+
+ dst.xoffset -= widthptr;
+ cgen(n->left, &dst);
+}
+
+/*
+ * generate:
+ * res = s[lo, hi];
+ * n->left is s
+ * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
+ * caller (cgen) guarantees res is an addable ONAME.
+ *
+ * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
+ */
+void
+cgen_slice(Node *n, Node *res)
+{
+ Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con;
+ Prog *p1, *p2;
+
+ cap = n->list->n;
+ len = n->list->next->n;
+ offs = N;
+ if(n->list->next->next)
+ offs = n->list->next->next->n;
+
+ // evaluate base pointer first, because it is the only
+ // possibly complex expression. once that is evaluated
+ // and stored, updating the len and cap can be done
+ // without making any calls, so without doing anything that
+ // might cause preemption or garbage collection.
+ // this makes the whole slice update atomic as far as the
+ // garbage collector can see.
+
+ base = temp(types[TUINTPTR]);
+ tmplen = temp(types[TINT]);
+ if(n->op != OSLICESTR)
+ tmpcap = temp(types[TINT]);
+ else
+ tmpcap = tmplen;
+
+ if(isnil(n->left)) {
+ tempname(&src, n->left->type);
+ cgen(n->left, &src);
+ } else
+ src = *n->left;
+ if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR)
+ src.xoffset += Array_array;
+
+ if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
+ if(!isptr[n->left->type->etype])
+ fatal("slicearr is supposed to work on pointer: %+N\n", n);
+ cgen(&src, base);
+ cgen_checknil(base);
+ } else {
+ src.type = types[tptr];
+ cgen(&src, base);
+ }
+
+ // committed to the update
+ gvardef(res);
+
+ // compute len and cap.
+ // len = n-i, cap = m-i, and offs = i*width.
+ // computing offs last lets the multiply overwrite i.
+ cgen(len, tmplen);
+ if(n->op != OSLICESTR)
+ cgen(cap, tmpcap);
+
+ // if new cap != 0 { base += add }
+ // This avoids advancing base past the end of the underlying array/string,
+ // so that it cannot point at the next object in memory.
+ // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
+ // In essence we are replacing x[i:j:k] where i == j == k
+ // or x[i:j] where i == j == cap(x) with x[0:0:0].
+ if(offs != N) {
+ p1 = gjmp(P);
+ p2 = gjmp(P);
+ patch(p1, pc);
+
+ nodconst(&con, tmpcap->type, 0);
+ cmp = nod(OEQ, tmpcap, &con);
+ typecheck(&cmp, Erv);
+ bgen(cmp, 1, -1, p2);
+
+ add = nod(OADD, base, offs);
+ typecheck(&add, Erv);
+ cgen(add, base);
+
+ patch(p2, pc);
+ }
+
+ // dst.array = src.array [ + lo *width ]
+ dst = *res;
+ dst.xoffset += Array_array;
+ dst.type = types[tptr];
+ cgen(base, &dst);
+
+ // dst.len = hi [ - lo ]
+ dst = *res;
+ dst.xoffset += Array_nel;
+ dst.type = types[simtype[TUINT]];
+ cgen(tmplen, &dst);
+
+ if(n->op != OSLICESTR) {
+ // dst.cap = cap [ - lo ]
+ dst = *res;
+ dst.xoffset += Array_cap;
+ dst.type = types[simtype[TUINT]];
+ cgen(tmpcap, &dst);
+ }
+}
+
+/*
+ * gather series of offsets
+ * >=0 is direct addressed field
+ * <0 is pointer to next field (+1)
+ */
+int
+dotoffset(Node *n, int64 *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;
+
+ 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;
+ s->def = n;
+ n->type = t;
+ n->class = PAUTO;
+ n->addable = 1;
+ n->ullman = 1;
+ n->esc = EscNever;
+ n->curfn = curfn;
+ curfn->dcl = list(curfn->dcl, n);
+
+ dowidth(t);
+ n->xoffset = 0;
+ *nn = *n;
+}
+
+Node*
+temp(Type *t)
+{
+ Node *n;
+
+ n = nod(OXXX, N, N);
+ tempname(n, t);
+ n->sym->def->used = 1;
+ return n->orig;
+}
diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors
new file mode 100644
index 0000000..f90d619
--- /dev/null
+++ b/src/cmd/gc/go.errors
@@ -0,0 +1,79 @@
+// 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 LIMPORT LNAME ';'
+ "missing import path; require quoted string",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
+ "missing { after if clause",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
+ "missing { after switch clause",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
+ "missing { after for clause",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
+ "missing { after for clause",
+
+ % 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 LVAR LNAME '=' comptype '{' LNAME ';'
+ "need trailing comma before newline in composite literal",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
+ "nested func not allowed",
+
+ % loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';'
+ "else must be followed by if or statement block"
+};
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
new file mode 100644
index 0000000..bbb8835
--- /dev/null
+++ b/src/cmd/gc/go.h
@@ -0,0 +1,1555 @@
+// 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 <bio.h>
+#include <link.h>
+
+#undef OAPPEND
+
+// avoid <ctype.h>
+#undef isblank
+#define isblank goisblank
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#undef BUFSIZ
+
+// The parser's maximum stack size.
+// We have to use a #define macro here since yacc
+// or bison will check for its definition and use
+// a potentially smaller value if it is undefined.
+#define YYMAXDEPTH 500
+
+enum
+{
+ NHUNK = 50000,
+ BUFSIZ = 8192,
+ NSYMB = 500,
+ NHASH = 1024,
+ STRINGSZ = 200,
+ MAXALIGN = 7,
+ UINF = 100,
+
+ PRIME1 = 3,
+
+ AUNK = 100,
+
+ // These values are known by runtime.
+ // The MEMx and NOEQx values must run in parallel. See algtype.
+ AMEM = 0,
+ AMEM0,
+ AMEM8,
+ AMEM16,
+ AMEM32,
+ AMEM64,
+ AMEM128,
+ ANOEQ,
+ ANOEQ0,
+ ANOEQ8,
+ ANOEQ16,
+ ANOEQ32,
+ ANOEQ64,
+ ANOEQ128,
+ ASTRING,
+ AINTER,
+ ANILINTER,
+ ASLICE,
+ AFLOAT32,
+ AFLOAT64,
+ ACPLX64,
+ ACPLX128,
+
+ BADWIDTH = -1000000000,
+
+ MaxStackVarSize = 10*1024*1024,
+};
+
+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[1]; // variable
+};
+
+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, rune CTRUNE
+ Mpflt* fval; // float CTFLT
+ Mpcplx* cval; // float CTCPLX
+ Strlit* sval; // string CTSTR
+ } u;
+};
+
+// prevent incompatible type signatures between libgc and 8g on Plan 9
+#pragma incomplete struct Array
+
+typedef struct Array Array;
+typedef struct Bvec Bvec;
+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 nointerface;
+ uchar noalg;
+ uchar chan;
+ uchar trecur; // to detect loops
+ uchar printed;
+ uchar embedded; // TFIELD embedded type
+ uchar siggen;
+ uchar funarg; // on TSTRUCT and TFIELD
+ uchar copyany;
+ uchar local; // created in this file
+ uchar deferwidth;
+ uchar broke; // broken type definition.
+ uchar isddd; // TFIELD is ... argument
+ uchar align;
+ uchar haspointers; // 0 unknown, 1 no, 2 yes
+
+ Node* nod; // canonical OTYPE node
+ Type* orig; // original type (type literal or predefined type)
+ int lineno;
+
+ // TFUNC
+ int thistuple;
+ int outtuple;
+ int intuple;
+ uchar outnamed;
+
+ Type* method;
+ Type* xmethod;
+
+ Sym* sym;
+ int32 vargen; // unique name for OTYPE/ONAME
+
+ Node* nname;
+ vlong argwid;
+
+ // most nodes
+ Type* type; // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx
+ vlong width; // offset in TFIELD, width in all others
+
+ // TFIELD
+ Type* down; // next struct field, also key type in TMAP
+ Type* outer; // outer struct
+ Strlit* note; // literal string annotation
+
+ // TARRAY
+ vlong bound; // negative is dynamic array
+
+ // TMAP
+ Type* bucket; // internal type representing a hash bucket
+ Type* hmap; // internal type representing a Hmap (map header object)
+ Type* hiter; // internal type representing hash iterator state
+ Type* map; // link from the above 3 internal types back to the map type.
+
+ int32 maplineno; // first use of TFORW as map key
+ int32 embedlineno; // first use of TFORW as embedded type
+
+ // for TFORW, where to copy the eventual value to
+ NodeList *copyto;
+
+ Node *lastfn; // for usefield
+};
+#define T ((Type*)0)
+
+typedef struct InitEntry InitEntry;
+typedef struct InitPlan InitPlan;
+
+struct InitEntry
+{
+ vlong xoffset; // struct, array only
+ Node *key; // map only
+ Node *expr;
+};
+
+struct InitPlan
+{
+ vlong lit; // bytes of initialized non-zero literals
+ vlong zero; // bytes of zeros
+ vlong expr; // bytes of run-time computed expressions
+
+ InitEntry *e;
+ int len;
+ int cap;
+};
+
+enum
+{
+ EscUnknown,
+ EscHeap,
+ EscScope,
+ EscNone,
+ EscReturn,
+ EscNever,
+ EscBits = 3,
+ EscMask = (1<<EscBits) - 1,
+ EscContentEscapes = 1<<EscBits, // value obtained by indirect of parameter escapes to some returned result
+ EscReturnBits = EscBits+1,
+};
+
+struct Node
+{
+ // Tree structure.
+ // Generic recursive walks should follow these fields.
+ Node* left;
+ Node* right;
+ Node* ntest;
+ Node* nincr;
+ NodeList* ninit;
+ NodeList* nbody;
+ NodeList* nelse;
+ NodeList* list;
+ NodeList* rlist;
+
+ uchar op;
+ uchar nointerface;
+ 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 bounded; // bounds check unnecessary
+ 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; // func arguments do not escape
+ uchar nosplit; // func should not execute on separate stack
+ uchar builtin; // built-in name, like len or close
+ uchar walkdef;
+ uchar typecheck;
+ uchar local;
+ uchar dodata;
+ uchar initorder;
+ uchar used;
+ uchar isddd;
+ uchar readonly;
+ uchar implicit;
+ uchar addrtaken; // address taken, even if not moved to heap
+ uchar dupok; // duplicate definitions ok (for func)
+ uchar wrapper; // is method wrapper (for func)
+ uchar reslice; // this is a reslice x = x[0:y] or x = append(x, ...)
+ schar likely; // likeliness of if statement
+ uchar hasbreak; // has break statement
+ uchar needzero; // if it contains pointers, needs to be zeroed on function entry
+ uchar needctxt; // function uses context register (has closure variables)
+ uint esc; // EscXXX
+ int funcdepth;
+
+ // most nodes
+ Type* type;
+ Node* orig; // original form, for printing, and tracking copies of ONAMEs
+
+ // func
+ Node* nname;
+ Node* shortname;
+ NodeList* enter;
+ NodeList* exit;
+ NodeList* cvars; // closure params
+ NodeList* dcl; // autodcl for this func/closure
+ NodeList* inl; // copy of the body for use in inlining
+ NodeList* inldcl; // copy of dcl for use in inlining
+
+ // OLITERAL/OREGISTER
+ Val val;
+
+ // ONAME
+ Node* ntype;
+ Node* defn; // ONAME: initializing assignment; OLABEL: labeled statement
+ Node* pack; // real package for import . names
+ Node* curfn; // function for local variables
+ Type* paramfld; // TFIELD for this PPARAM; also for ODOT, curfn
+
+ // 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
+
+ // ONAME substitute while inlining
+ Node* inlvar;
+
+ // OPACK
+ Pkg* pkg;
+
+ // OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+ InitPlan* initplan;
+
+ // Escape analysis.
+ NodeList* escflowsrc; // flow(this, src)
+ NodeList* escretval; // on OCALLxxx, list of dummy return values
+ int escloopdepth; // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+
+ Sym* sym; // various
+ int32 vargen; // unique name for OTYPE/ONAME
+ int32 lineno;
+ int32 endlineno;
+ vlong xoffset;
+ vlong stkdelta; // offset added by stack frame compaction phase.
+ int32 ostk;
+ int32 iota;
+ uint32 walkgen;
+ int32 esclevel;
+ void* opt; // for optimization passes
+};
+#define N ((Node*)0)
+
+/*
+ * Every node has a walkgen field.
+ * If you want to do a traversal of a node graph that
+ * might contain duplicates and want to avoid
+ * visiting the same nodes twice, increment walkgen
+ * before starting. Then before processing a node, do
+ *
+ * if(n->walkgen == walkgen)
+ * return;
+ * n->walkgen = walkgen;
+ *
+ * Such a walk cannot call another such walk recursively,
+ * because of the use of the global walkgen.
+ */
+EXTERN uint32 walkgen;
+
+struct NodeList
+{
+ Node* n;
+ NodeList* next;
+ NodeList* end;
+};
+
+enum
+{
+ SymExport = 1<<0, // to be exported
+ SymPackage = 1<<1,
+ SymExported = 1<<2, // already written out by export
+ 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
+ uint32 uniqgen;
+ Pkg* importdef; // where imported definition was found
+
+ // 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
+ Pkg* origpkg; // original package for . import
+ LSym* lsym;
+};
+#define S ((Sym*)0)
+
+EXTERN Sym* dclstack;
+
+struct Pkg
+{
+ char* name; // package name
+ Strlit* path; // string literal used in import statement
+ Sym* pathsym;
+ char* prefix; // escaped path for use in symbol table
+ Pkg* link;
+ uchar imported; // export data of this package was parsed
+ char exported; // import line written in export data
+ char direct; // imported directly
+ char safe; // whether the package is marked as safe
+};
+
+typedef struct Iter Iter;
+struct Iter
+{
+ int done;
+ Type* tfunc;
+ Type* t;
+ Node** an;
+ Node* n;
+};
+
+// Node ops.
+enum
+{
+ OXXX,
+
+ // names
+ ONAME, // var, const or func name
+ ONONAME, // unnamed arg or return value: f(int, string) (int, error) { etc }
+ OTYPE, // type name
+ OPACK, // import
+ OLITERAL, // literal
+
+ // expressions
+ OADD, // x + y
+ OSUB, // x - y
+ OOR, // x | y
+ OXOR, // x ^ y
+ OADDSTR, // s + "foo"
+ OADDR, // &x
+ OANDAND, // b0 && b1
+ OAPPEND, // append
+ OARRAYBYTESTR, // string(bytes)
+ OARRAYBYTESTRTMP, // string(bytes) ephemeral
+ OARRAYRUNESTR, // string(runes)
+ OSTRARRAYBYTE, // []byte(s)
+ OSTRARRAYRUNE, // []rune(s)
+ OAS, // x = y or x := y
+ OAS2, // x, y, z = xx, yy, zz
+ OAS2FUNC, // x, y = f()
+ OAS2RECV, // x, ok = <-c
+ OAS2MAPR, // x, ok = m["foo"]
+ OAS2DOTTYPE, // x, ok = I.(int)
+ OASOP, // x += y
+ OCALL, // function call, method call or type conversion, possibly preceded by defer or go.
+ OCALLFUNC, // f()
+ OCALLMETH, // t.Method()
+ OCALLINTER, // err.Error()
+ OCALLPART, // t.Method (without ())
+ OCAP, // cap
+ OCLOSE, // close
+ OCLOSURE, // f = func() { etc }
+ OCMPIFACE, // err1 == err2
+ OCMPSTR, // s1 == s2
+ OCOMPLIT, // composite literal, typechecking may convert to a more specific OXXXLIT.
+ OMAPLIT, // M{"foo":3, "bar":4}
+ OSTRUCTLIT, // T{x:3, y:4}
+ OARRAYLIT, // [2]int{3, 4}
+ OPTRLIT, // &T{x:3, y:4}
+ OCONV, // var i int; var u uint; i = int(u)
+ OCONVIFACE, // I(t)
+ OCONVNOP, // type Int int; var i int; var j Int; i = int(j)
+ OCOPY, // copy
+ ODCL, // var x int
+ ODCLFUNC, // func f() or func (r) f()
+ ODCLFIELD, // struct field, interface field, or func/method argument/return value.
+ ODCLCONST, // const pi = 3.14
+ ODCLTYPE, // type Int int
+ ODELETE, // delete
+ ODOT, // t.x
+ ODOTPTR, // p.x that is implicitly (*p).x
+ ODOTMETH, // t.Method
+ ODOTINTER, // err.Error
+ OXDOT, // t.x, typechecking may convert to a more specific ODOTXXX.
+ ODOTTYPE, // e = err.(MyErr)
+ ODOTTYPE2, // e, ok = err.(MyErr)
+ OEQ, // x == y
+ ONE, // x != y
+ OLT, // x < y
+ OLE, // x <= y
+ OGE, // x >= y
+ OGT, // x > y
+ OIND, // *p
+ OINDEX, // a[i]
+ OINDEXMAP, // m[s]
+ OKEY, // The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc.
+ OPARAM, // The on-stack copy of a parameter or return value that escapes.
+ OLEN, // len
+ OMAKE, // make, typechecking may convert to a more specific OMAKEXXX.
+ OMAKECHAN, // make(chan int)
+ OMAKEMAP, // make(map[string]int)
+ OMAKESLICE, // make([]int, 0)
+ OMUL, // x * y
+ ODIV, // x / y
+ OMOD, // x % y
+ OLSH, // x << u
+ ORSH, // x >> u
+ OAND, // x & y
+ OANDNOT, // x &^ y
+ ONEW, // new
+ ONOT, // !b
+ OCOM, // ^x
+ OPLUS, // +x
+ OMINUS, // -y
+ OOROR, // b1 || b2
+ OPANIC, // panic
+ OPRINT, // print
+ OPRINTN, // println
+ OPAREN, // (x)
+ OSEND, // c <- x
+ OSLICE, // v[1:2], typechecking may convert to a more specific OSLICEXXX.
+ OSLICEARR, // a[1:2]
+ OSLICESTR, // s[1:2]
+ OSLICE3, // v[1:2:3], typechecking may convert to OSLICE3ARR.
+ OSLICE3ARR, // a[1:2:3]
+ ORECOVER, // recover
+ ORECV, // <-c
+ ORUNESTR, // string(i)
+ OSELRECV, // case x = <-c:
+ OSELRECV2, // case x, ok = <-c:
+ OIOTA, // iota
+ OREAL, // real
+ OIMAG, // imag
+ OCOMPLEX, // complex
+
+ // statements
+ OBLOCK, // block of code
+ OBREAK, // break
+ OCASE, // case, after being verified by swt.c's casebody.
+ OXCASE, // case, before verification.
+ OCONTINUE, // continue
+ ODEFER, // defer
+ OEMPTY, // no-op
+ OFALL, // fallthrough, after being verified by swt.c's casebody.
+ OXFALL, // fallthrough, before verification.
+ OFOR, // for
+ OGOTO, // goto
+ OIF, // if
+ OLABEL, // label:
+ OPROC, // go
+ ORANGE, // range
+ ORETURN, // return
+ OSELECT, // select
+ OSWITCH, // switch x
+ OTYPESW, // switch err.(type)
+
+ // types
+ OTCHAN, // chan int
+ OTMAP, // map[string]int
+ OTSTRUCT, // struct{}
+ OTINTER, // interface{}
+ OTFUNC, // func()
+ OTARRAY, // []int, [8]int, [N]int or [...]int
+
+ // misc
+ ODDD, // func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
+ ODDDARG, // func f(args ...int), introduced by escape analysis.
+ OINLCALL, // intermediary representation of an inlined call.
+ OEFACE, // itable and data words of an empty-interface value.
+ OITAB, // itable word of an interface value.
+ OSPTR, // base pointer of a slice or string.
+ OCLOSUREVAR, // variable reference at beginning of closure function
+ OCFUNC, // reference to c function pointer (not go func value)
+ OCHECKNIL, // emit code to ensure pointer/interface not nil
+ OVARKILL, // variable is dead
+
+ // arch-specific registers
+ OREGISTER, // a register, such as AX.
+ OINDREG, // offset plus indirect of a register, such as 8(SP).
+
+ // 386/amd64-specific opcodes
+ OCMP, // compare: ACMP.
+ ODEC, // decrement: ADEC.
+ OINC, // increment: AINC.
+ OEXTEND, // extend: ACWD/ACDQ/ACQO.
+ OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
+ OLROT, // left rotate: AROL.
+ ORROTC, // right rotate-carry: ARCR.
+ ORETJMP, // return to other function
+
+ 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,
+ CTRUNE,
+ 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,
+};
+
+// declaration context
+enum
+{
+ Pxxx,
+
+ PEXTERN, // global variable
+ PAUTO, // local variables
+ PPARAM, // input arguments
+ PPARAMOUT, // output results
+ PPARAMREF, // closure variable reference
+ PFUNC, // global function
+
+ PDISCARD, // discard during parse of duplicate import
+
+ PHEAP = 1<<7, // an extra bit to identify an escaped variable
+};
+
+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;
+
+struct Bvec
+{
+ int32 n; // number of bits
+ uint32 b[];
+};
+
+typedef struct Var Var;
+struct Var
+{
+ vlong offset;
+ Node* node;
+ Var* nextinnode;
+ 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 last;
+ 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
+};
+
+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* infile;
+EXTERN char* outfile;
+EXTERN Biobuf* bout;
+EXTERN int nerrors;
+EXTERN int nsavederrors;
+EXTERN int nsyntaxerrors;
+EXTERN int safemode;
+EXTERN int nolocalimports;
+EXTERN char namebuf[NSYMB];
+EXTERN char lexbuf[NSYMB];
+EXTERN char litbuf[NSYMB];
+EXTERN int debug[256];
+EXTERN char* debugstr;
+EXTERN int debug_checknil;
+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* itabpkg; // fake pkg for itab cache
+EXTERN Pkg* runtimepkg; // package runtime
+EXTERN Pkg* racepkg; // package runtime/race
+EXTERN Pkg* stringpkg; // fake package for C strings
+EXTERN Pkg* typepkg; // fake package for runtime type info (headers)
+EXTERN Pkg* typelinkpkg; // fake package for runtime type info (data)
+EXTERN Pkg* weaktypepkg; // weak references to runtime type info
+EXTERN Pkg* unsafepkg; // package unsafe
+EXTERN Pkg* trackpkg; // fake package for field tracking
+EXTERN Pkg* phash[128];
+EXTERN int tptr; // either TPTR32 or TPTR64
+extern char* runtimeimport;
+extern char* unsafeimport;
+EXTERN char* myimportpath;
+EXTERN Idir* idirs;
+EXTERN char* localimport;
+
+EXTERN Type* types[NTYPE];
+EXTERN Type* idealstring;
+EXTERN Type* idealbool;
+EXTERN Type* bytetype;
+EXTERN Type* runetype;
+EXTERN Type* errortype;
+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* importlist; // imported functions and methods with inlinable bodies
+EXTERN NodeList* funcsyms;
+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 vlong maxarg;
+EXTERN vlong stksize; // stack size for current frame
+EXTERN vlong stkptrsize; // prefix of stack containing pointers
+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 int widthint;
+EXTERN int widthreg;
+
+EXTERN Node* typesw;
+EXTERN Node* nblank;
+
+extern int thechar;
+extern char* thestring;
+extern LinkArch* thelinkarch;
+EXTERN int use_sse;
+
+EXTERN char* hunk;
+EXTERN int32 nhunk;
+EXTERN int32 thunk;
+
+EXTERN int funcdepth;
+EXTERN int typecheckok;
+EXTERN int compiling_runtime;
+EXTERN int compiling_wrappers;
+EXTERN int inl_nonlocal;
+EXTERN int use_writebarrier;
+EXTERN int pure_go;
+EXTERN char* flag_installsuffix;
+EXTERN int flag_race;
+EXTERN int flag_largemodel;
+EXTERN int noescape;
+EXTERN int nosplit;
+EXTERN int debuglive;
+EXTERN Link* ctxt;
+
+EXTERN int nointerface;
+EXTERN int fieldtrack_enabled;
+EXTERN int precisestack_enabled;
+EXTERN int writearchive;
+
+EXTERN Biobuf bstdout;
+
+EXTERN int nacl;
+
+/*
+ * 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);
+vlong rnd(vlong o, vlong r);
+void typeinit(void);
+
+/*
+ * array.c
+ */
+Array* arraynew(int32 capacity, int32 size);
+void arrayfree(Array *array);
+int32 arraylength(Array *array);
+void* arrayget(Array *array, int32 index);
+void arrayset(Array *array, int32 index, void *element);
+void arrayadd(Array *array, void *element);
+void arraysort(Array* array, int (*cmp)(const void*, const 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);
+
+/*
+ * bv.c
+ */
+Bvec* bvalloc(int32 n);
+void bvandnot(Bvec *dst, Bvec *src1, Bvec *src2);
+int bvcmp(Bvec *bv1, Bvec *bv2);
+void bvcopy(Bvec *dst, Bvec *src);
+Bvec* bvconcat(Bvec *src1, Bvec *src2);
+int bvget(Bvec *bv, int32 i);
+int32 bvnext(Bvec *bv, int32 i);
+int bvisempty(Bvec *bv);
+void bvnot(Bvec *bv);
+void bvor(Bvec *dst, Bvec *src1, Bvec *src2);
+void bvand(Bvec *dst, Bvec *src1, Bvec *src2);
+void bvprint(Bvec *bv);
+void bvreset(Bvec *bv, int32 i);
+void bvresetall(Bvec *bv);
+void bvset(Bvec *bv, int32 i);
+
+/*
+ * closure.c
+ */
+Node* closurebody(NodeList *body);
+void closurehdr(Node *ntype);
+void typecheckclosure(Node *func, int top);
+Node* walkclosure(Node *func, NodeList **init);
+void typecheckpartialcall(Node*, Node*);
+Node* walkpartialcall(Node*, NodeList**);
+
+/*
+ * 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);
+int isgoconst(Node *n);
+Node* nodcplxlit(Val r, Val i);
+Node* nodlit(Val v);
+long nonnegconst(Node *n);
+int doesoverflow(Val v, Type *t);
+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, int likely, 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, int nointerface);
+void addvar(Node *n, Type *t, int ctxt);
+NodeList* checkarglist(NodeList *all, int input);
+Node* colas(NodeList *left, NodeList *right, int32 lno);
+void colasdefn(NodeList *left, Node *defn);
+NodeList* constiter(NodeList *vl, Node *t, NodeList *cl);
+Node* dclname(Sym *s);
+void declare(Node *n, int ctxt);
+void dumpdcl(char *st);
+Node* embedded(Sym *s, Pkg *pkg);
+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);
+Node* oldname(Sym *s);
+void popdcl(void);
+void poptodcl(void);
+void redeclare(Sym *s, char *where);
+void testdclstack(void);
+Type* tointerface(NodeList *l);
+Type* tostruct(NodeList *l);
+Node* typedcl0(Sym *s);
+Node* typedcl1(Node *n, Node *t, int local);
+Node* typenod(Type *t);
+NodeList* variter(NodeList *vl, Node *t, NodeList *el);
+Sym* funcsym(Sym*);
+
+/*
+ * esc.c
+ */
+void escapes(NodeList*);
+
+/*
+ * 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 importimport(Sym *s, Strlit *z);
+Sym* importsym(Sym *s, int op);
+void importtype(Type *pt, Type *t);
+void importvar(Sym *s, Type *t);
+Type* pkgtype(Sym *s);
+
+/*
+ * fmt.c
+ */
+void fmtinstallgo(void);
+void dump(char *s, Node *n);
+void dumplist(char *s, NodeList *l);
+
+/*
+ * gen.c
+ */
+void addrescapes(Node *n);
+void cgen_as(Node *nl, Node *nr);
+void cgen_callmeth(Node *n, int proc);
+void cgen_eface(Node* n, Node* res);
+void cgen_slice(Node* n, Node* res);
+void clearlabels(void);
+void clearslim(Node*);
+void checklabels(void);
+int dotoffset(Node *n, int64 *oary, Node **nn);
+void gen(Node *n);
+void genlist(NodeList *l);
+Node* sysfunc(char *name);
+void tempname(Node *n, Type *t);
+Node* temp(Type*);
+
+/*
+ * init.c
+ */
+void fninit(NodeList *n);
+Sym* renameinit(void);
+
+/*
+ * inl.c
+ */
+void caninl(Node *fn);
+void inlcalls(Node *fn);
+void typecheckinl(Node *fn);
+
+/*
+ * lex.c
+ */
+void cannedimports(char *file, char *cp);
+void importfile(Val *f, int line);
+char* lexname(int lex);
+char* expstring(void);
+void mkpackage(char* pkgname);
+void unimportfile(void);
+int32 yylex(void);
+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, int);
+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);
+double mpgetflt32(Mpflt *a);
+void mpmovecflt(Mpflt *a, double c);
+void mpmulfltflt(Mpflt *a, Mpflt *b);
+void mpnegflt(Mpflt *a);
+void mpnorm(Mpflt *a);
+void mpsetexp(Mpflt *a, int exp);
+int mptestflt(Mpflt *a);
+int sigfig(Mpflt *a);
+
+/*
+ * obj.c
+ */
+void Bputname(Biobuf *b, LSym *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);
+Sym* stringsym(char*, int);
+void slicebytes(Node*, char*, int);
+LSym* linksym(Sym*);
+
+/*
+ * order.c
+ */
+void order(Node *fn);
+void orderstmtinplace(Node **stmt);
+
+/*
+ * 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);
+Sym* typenamesym(Type *t);
+Sym* tracksym(Type *t);
+Sym* typesymprefix(char *prefix, Type *t);
+int haspointers(Type *t);
+Type* hiter(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
+ */
+Node* adddot(Node *n);
+int adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase);
+void addinit(Node**, NodeList*);
+Type* aindex(Node *b, Type *t);
+int algtype(Type *t);
+int algtype1(Type *t, Type **bad);
+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);
+Node* copyexpr(Node*, Type*, NodeList**);
+int count(NodeList *l);
+int cplxsubtype(int et);
+int eqtype(Type *t1, Type *t2);
+int eqtypenoname(Type *t1, Type *t2);
+void errorexit(void);
+void expandmeth(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);
+void genhash(Sym *sym, Type *t);
+void geneq(Sym *sym, Type *t);
+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 isbadimport(Strlit *s);
+int isblank(Node *n);
+int isblanksym(Sym *s);
+int isdirectiface(Type*);
+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);
+int iszero(Node *n);
+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, int mustname);
+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);
+void saveorignode(Node *n);
+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 warnl(int line, 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 islvalue(Node *n);
+Node* typecheck(Node **np, int top);
+void typechecklist(NodeList *l, int top);
+Node* typecheckdef(Node *n);
+void copytype(Node *n, Type *t);
+void checkreturn(Node*);
+void queuemethod(Node *n);
+
+/*
+ * unsafe.c
+ */
+int isunsafebuiltin(Node *n);
+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);
+Node* conv(Node*, Type*);
+int candiscard(Node*);
+int needwritebarrier(Node*, Node*);
+Node* outervalue(Node*);
+void usefield(Node*);
+
+/*
+ * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c/plive.c
+ */
+#define P ((Prog*)0)
+
+EXTERN Prog* continpc;
+EXTERN Prog* breakpc;
+EXTERN Prog* pc;
+EXTERN Prog* firstpc;
+
+EXTERN Node* nodfp;
+EXTERN int disable_checknil;
+EXTERN vlong zerosize;
+
+int anyregalloc(void);
+void betypeinit(void);
+void bgen(Node *n, int true, int likely, Prog *to);
+void checknil(Node*, NodeList**);
+void expandchecks(Prog*);
+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_checknil(Node*);
+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 fixautoused(Prog*);
+void gdata(Node*, Node*, int);
+void gdatacomplex(Node*, Mpcplx*);
+void gdatastring(Node*, Strlit*);
+void ggloblnod(Node *nam);
+void ggloblsym(Sym *s, int32 width, int8 flags);
+void gvardef(Node*);
+void gvarkill(Node*);
+Prog* gjmp(Prog*);
+void gused(Node*);
+void movelarge(NodeList*);
+int isfat(Type*);
+void linkarchinit(void);
+void liveness(Node*, Prog*, Sym*, Sym*);
+void twobitwalktype1(Type*, vlong*, Bvec*);
+void markautoused(Prog*);
+Plist* newplist(void);
+Node* nodarg(Type*, int);
+void nopout(Prog*);
+void patch(Prog*, Prog*);
+Prog* unpatch(Prog*);
+
+#pragma varargck type "B" Mpint*
+#pragma varargck type "E" int
+#pragma varargck type "E" uint
+#pragma varargck type "F" Mpflt*
+#pragma varargck type "H" NodeList*
+#pragma varargck type "J" Node*
+#pragma varargck type "lL" int32
+#pragma varargck type "L" int32
+#pragma varargck type "N" Node*
+#pragma varargck type "lN" Node*
+#pragma varargck type "O" int
+#pragma varargck type "O" uint
+#pragma varargck type "Q" Bits
+#pragma varargck type "S" Sym*
+#pragma varargck type "lS" LSym*
+#pragma varargck type "T" Type*
+#pragma varargck type "lT" Type*
+#pragma varargck type "V" Val*
+#pragma varargck type "Z" Strlit*
+
+/*
+ * racewalk.c
+ */
+void racewalk(Node *fn);
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
new file mode 100644
index 0000000..68fccc1
--- /dev/null
+++ b/src/cmd/gc/go.y
@@ -0,0 +1,2223 @@
+// 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 <u.h>
+#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
+#include "go.h"
+
+static void fixlbrace(int);
+%}
+%union {
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int i;
+}
+
+// |sed 's/.* //' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx /'
+
+%token <val> LLITERAL
+%token <i> LASOP LCOLAS
+%token <sym> LBREAK LCASE LCHAN 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 <i> 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 bare_complitexpr
+%type <node> expr_or_type
+%type <node> fndcl hidden_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 start_complit
+
+%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 elseif elseif_list else 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 <sym> hidden_importsym hidden_pkg_importsym
+
+%type <node> hidden_constant hidden_literal hidden_funarg
+%type <node> hidden_interfacedcl hidden_structdcl
+
+%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");
+ errorexit();
+ }
+| 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(strcmp(my->name, "init") == 0) {
+ yyerror("cannot import package as init - init must be a func");
+ 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_here import_there
+ {
+ // When an invalid import path is passed to importfile,
+ // it calls yyerror and then sets up a fake import with
+ // no package statement. This allows us to test more
+ // than one invalid import statement in a single file.
+ if(nerrors == 0)
+ fatal("phase error in import");
+ }
+
+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 LNAME 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;
+ importpkg->safe = curio.importsafe;
+
+ 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;
+
+ // These nodes do not carry line numbers.
+ // Since a bare name used as an expression is an error,
+ // introduce a wrapper node to give the correct line.
+ switch($$->op) {
+ case ONAME:
+ case ONONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ $$ = nod(OPAREN, $$, N);
+ $$->implicit = 1;
+ break;
+ }
+ }
+| 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) {
+ $$ = nod(OTYPESW, N, $3->n->right);
+ 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) || isblank($1->n))
+ yyerror("invalid variable name %N in type switch", $1->n);
+ else
+ $$->left = dclname($1->n->sym); // it's a colas, so must not re-use an oldname.
+ break;
+ }
+ $$ = colas($1, $3, $2);
+ }
+| expr LINC
+ {
+ $$ = nod(OASOP, $1, nodintconst(1));
+ $$->implicit = 1;
+ $$->etype = OADD;
+ }
+| expr LDEC
+ {
+ $$ = nod(OASOP, $1, nodintconst(1));
+ $$->implicit = 1;
+ $$->etype = OSUB;
+ }
+
+case:
+ LCASE expr_or_type_list ':'
+ {
+ Node *n, *nn;
+
+ // 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
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ $$->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+| 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), $3));
+ }
+| LDEFAULT ':'
+ {
+ Node *n, *nn;
+
+ markdcl();
+ $$ = nod(OXCASE, N, N);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ $$->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+
+compound_stmt:
+ '{'
+ {
+ markdcl();
+ }
+ stmt_list '}'
+ {
+ if($3 == nil)
+ $$ = nod(OEMPTY, N, N);
+ else
+ $$ = 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;
+ $1->xoffset = block;
+ }
+ 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, $$);
+ }
+| LRANGE expr
+ {
+ $$ = nod(ORANGE, N, $2);
+ $$->etype = 0; // := flag
+ }
+
+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 cond body (ELSE IF cond body)* (ELSE block)? */
+if_stmt:
+ LIF
+ {
+ markdcl();
+ }
+ if_header
+ {
+ if($3->ntest == N)
+ yyerror("missing condition in if statement");
+ }
+ loop_body
+ {
+ $3->nbody = $5;
+ }
+ elseif_list else
+ {
+ Node *n;
+ NodeList *nn;
+
+ $$ = $3;
+ n = $3;
+ popdcl();
+ for(nn = concat($7, $8); nn; nn = nn->next) {
+ if(nn->n->op == OIF)
+ popdcl();
+ n->nelse = list1(nn->n);
+ n = nn->n;
+ }
+ }
+
+elseif:
+ LELSE LIF
+ {
+ markdcl();
+ }
+ if_header loop_body
+ {
+ if($4->ntest == N)
+ yyerror("missing condition in if statement");
+ $4->nbody = $5;
+ $$ = list1($4);
+ }
+
+elseif_list:
+ {
+ $$ = nil;
+ }
+| elseif_list elseif
+ {
+ $$ = concat($1, $2);
+ }
+
+else:
+ {
+ $$ = nil;
+ }
+| LELSE compound_stmt
+ {
+ NodeList *node;
+
+ node = mal(sizeof *node);
+ node->n = $2;
+ node->end = node;
+ $$ = node;
+ }
+
+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
+ {
+ if($2->op == OCOMPLIT) {
+ // Special case for &T{...}: turn into (*T){...}.
+ $$ = $2;
+ $$->right = nod(OIND, $$->right, N);
+ $$->right->implicit = 1;
+ } else {
+ $$ = 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));
+ }
+| pexpr '[' oexpr ':' oexpr ':' oexpr ']'
+ {
+ if($5 == N)
+ yyerror("middle index required in 3-index slice");
+ if($7 == N)
+ yyerror("final index required in 3-index slice");
+ $$ = nod(OSLICE3, $1, nod(OKEY, $3, nod(OKEY, $5, $7)));
+ }
+| pseudocall
+| convtype '(' expr ocomma ')'
+ {
+ // conversion
+ $$ = nod(OCALL, $1, N);
+ $$->list = list1($3);
+ }
+| comptype lbrace start_complit braced_keyval_list '}'
+ {
+ $$ = $3;
+ $$->right = $1;
+ $$->list = $4;
+ fixlbrace($2);
+ }
+| pexpr_no_paren '{' start_complit braced_keyval_list '}'
+ {
+ $$ = $3;
+ $$->right = $1;
+ $$->list = $4;
+ }
+| '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'
+ {
+ yyerror("cannot parenthesize type in composite literal");
+ $$ = $5;
+ $$->right = $2;
+ $$->list = $6;
+ }
+| fnliteral
+
+start_complit:
+ {
+ // composite expression.
+ // make node early so we get the right line number.
+ $$ = nod(OCOMPLIT, N, N);
+ }
+
+keyval:
+ expr ':' complitexpr
+ {
+ $$ = nod(OKEY, $1, $3);
+ }
+
+bare_complitexpr:
+ expr
+ {
+ // These nodes do not carry line numbers.
+ // Since a composite literal commonly spans several lines,
+ // the line number on errors may be misleading.
+ // Introduce a wrapper node to give the correct line.
+ $$ = $1;
+ switch($$->op) {
+ case ONAME:
+ case ONONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ $$ = nod(OPAREN, $$, N);
+ $$->implicit = 1;
+ }
+ }
+| '{' start_complit braced_keyval_list '}'
+ {
+ $$ = $2;
+ $$->list = $3;
+ }
+
+complitexpr:
+ expr
+| '{' start_complit braced_keyval_list '}'
+ {
+ $$ = $2;
+ $$->list = $3;
+ }
+
+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:
+ case OTYPESW:
+ $$ = 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
+ {
+ if($1 == S)
+ $$ = N;
+ else
+ $$ = newname($1);
+ }
+
+dcl_name:
+ sym
+ {
+ $$ = dclname($1);
+ }
+
+onew_name:
+ {
+ $$ = N;
+ }
+| new_name
+
+sym:
+ LNAME
+ {
+ $$ = $1;
+ // during imports, unqualified non-exported identifiers are from builtinpkg
+ if(importpkg != nil && !exportname($1->name))
+ $$ = pkglookup($1->name, builtinpkg);
+ }
+| hidden_importsym
+| '?'
+ {
+ $$ = S;
+ }
+
+hidden_importsym:
+ '@' LLITERAL '.' LNAME
+ {
+ Pkg *p;
+
+ if($2.u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport($2.u.sval))
+ errorexit();
+ p = mkpkg($2.u.sval);
+ }
+ $$ = pkglookup($4->name, p);
+ }
+| '@' LLITERAL '.' '?'
+ {
+ Pkg *p;
+
+ if($2.u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport($2.u.sval))
+ errorexit();
+ p = mkpkg($2.u.sval);
+ }
+ $$ = pkglookup("?", p);
+ }
+
+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 ')'
+ {
+ $$ = $2;
+ }
+
+non_expr_type:
+ recvchantype
+| fntype
+| othertype
+| '*' non_expr_type
+ {
+ $$ = nod(OIND, $2, N);
+ }
+
+non_recvchantype:
+ fntype
+| othertype
+| ptrtype
+| dotname
+| '(' ntype ')'
+ {
+ $$ = $2;
+ }
+
+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;
+ if(noescape && $3 != nil)
+ yyerror("can only use //go:noescape with external func implementations");
+ $$->nbody = $3;
+ $$->endlineno = lineno;
+ $$->noescape = noescape;
+ $$->nosplit = nosplit;
+ funcbody($$);
+ }
+
+fndcl:
+ sym '(' oarg_type_list_ocomma ')' fnres
+ {
+ Node *t;
+
+ $$ = N;
+ $3 = checkarglist($3, 1);
+
+ if(strcmp($1->name, "init") == 0) {
+ $1 = renameinit();
+ if($3 != nil || $5 != nil)
+ yyerror("func init must have no arguments and no return values");
+ }
+ if(strcmp(localpkg->name, "main") == 0 && strcmp($1->name, "main") == 0) {
+ if($3 != nil || $5 != nil)
+ yyerror("func main must have no arguments and no return values");
+ }
+
+ t = nod(OTFUNC, N, N);
+ t->list = $3;
+ t->rlist = $5;
+
+ $$ = nod(ODCLFUNC, N, N);
+ $$->nname = newname($1);
+ $$->nname->defn = $$;
+ $$->nname->ntype = t; // TODO: check if nname already has an ntype
+ declare($$->nname, PFUNC);
+
+ funchdr($$);
+ }
+| '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres
+ {
+ Node *rcvr, *t;
+
+ $$ = N;
+ $2 = checkarglist($2, 0);
+ $6 = checkarglist($6, 1);
+
+ 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;
+ }
+
+ t = nod(OTFUNC, rcvr, N);
+ t->list = $6;
+ t->rlist = $8;
+
+ $$ = nod(ODCLFUNC, N, N);
+ $$->shortname = newname($4);
+ $$->nname = methodname1($$->shortname, rcvr->right);
+ $$->nname->defn = $$;
+ $$->nname->ntype = t;
+ $$->nname->nointerface = nointerface;
+ declare($$->nname, PFUNC);
+
+ funchdr($$);
+ }
+
+hidden_fndcl:
+ hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ Sym *s;
+ Type *t;
+
+ $$ = N;
+
+ s = $1;
+ t = functype(N, $3, $5);
+
+ importsym(s, ONAME);
+ if(s->def != N && s->def->op == ONAME) {
+ if(eqtype(t, s->def->type)) {
+ dclcontext = PDISCARD; // since we skip funchdr below
+ break;
+ }
+ yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+ }
+
+ $$ = newname(s);
+ $$->type = t;
+ declare($$, PFUNC);
+
+ funchdr($$);
+ }
+| '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = methodname1(newname($4), $2->n->right);
+ $$->type = functype($2->n, $6, $8);
+
+ checkwidth($$->type);
+ addmethod($4, $$->type, 0, nointerface);
+ nointerface = 0;
+ funchdr($$);
+
+ // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+ // (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+ // out by typecheck's lookdot as this $$->ttype. So by providing
+ // this back link here we avoid special casing there.
+ $$->type->nname = $$;
+ }
+
+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();
+ nointerface = 0;
+ noescape = 0;
+ nosplit = 0;
+ }
+
+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;
+
+ Node *n;
+ l = $1;
+ if(l == nil) {
+ // ? symbol, during import (list1(N) == nil)
+ n = $2;
+ if(n->op == OIND)
+ n = n->left;
+ n = embedded(n->sym, importpkg);
+ n->right = $2;
+ n->val = $3;
+ $$ = list1(n);
+ break;
+ }
+
+ 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, localpkg);
+ }
+
+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
+| labelname ':'
+ {
+ $1 = nod(OLABEL, $1, N);
+ $1->sym = dclstack; // context, for goto restrictions
+ }
+ stmt
+ {
+ NodeList *l;
+
+ $1->defn = $4;
+ l = list1($1);
+ if($4)
+ l = list(l, $4);
+ $$ = liststmt(l);
+ }
+| LFALL
+ {
+ // will be converted to OFALL
+ $$ = nod(OXFALL, N, N);
+ $$->xoffset = block;
+ }
+| 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;
+ if($$->list == nil && curfn != N) {
+ NodeList *l;
+
+ for(l=curfn->dcl; l; l=l->next) {
+ if(l->n->class == PPARAM)
+ continue;
+ if(l->n->class != PPARAMOUT)
+ break;
+ if(l->n->sym->def != l->n)
+ yyerror("%s is shadowed during return", l->n->sym->name);
+ }
+ }
+ }
+
+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);
+ }
+| bare_complitexpr
+ {
+ $$ = list1($1);
+ }
+| keyval_list ',' keyval
+ {
+ $$ = list($1, $3);
+ }
+| keyval_list ',' bare_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 package header
+ */
+hidden_import:
+ LIMPORT LNAME LLITERAL ';'
+ {
+ importimport($2, $3.u.sval);
+ }
+| LVAR hidden_pkg_importsym hidden_type ';'
+ {
+ importvar($2, $3);
+ }
+| 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_fndcl fnbody ';'
+ {
+ if($2 == N) {
+ dclcontext = PEXTERN; // since we skip the funcbody below
+ break;
+ }
+
+ $2->inl = $3;
+
+ funcbody($2);
+ importlist = list(importlist, $2);
+
+ if(debug['E']) {
+ print("import [%Z] func %lN \n", importpkg->path, $2);
+ if(debug['m'] > 2 && $2->inl)
+ print("inl body:%+H\n", $2->inl);
+ }
+ }
+
+hidden_pkg_importsym:
+ hidden_importsym
+ {
+ $$ = $1;
+ structpkg = $$->pkg;
+ }
+
+hidden_pkgtype:
+ hidden_pkg_importsym
+ {
+ $$ = pkgtype($1);
+ importsym($1, OTYPE);
+ }
+
+/*
+ * importing types
+ */
+
+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 '}'
+ {
+ $$ = tostruct($3);
+ }
+| LINTERFACE '{' ohidden_interfacedcl_list '}'
+ {
+ $$ = tointerface($3);
+ }
+| '*' 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_funarg:
+ sym hidden_type oliteral
+ {
+ $$ = nod(ODCLFIELD, N, typenod($2));
+ if($1)
+ $$->left = newname($1);
+ $$->val = $3;
+ }
+| sym LDDD hidden_type oliteral
+ {
+ Type *t;
+
+ t = typ(TARRAY);
+ t->bound = -1;
+ t->type = $3;
+
+ $$ = nod(ODCLFIELD, N, typenod(t));
+ if($1)
+ $$->left = newname($1);
+ $$->isddd = 1;
+ $$->val = $4;
+ }
+
+hidden_structdcl:
+ sym hidden_type oliteral
+ {
+ Sym *s;
+ Pkg *p;
+
+ if($1 != S && strcmp($1->name, "?") != 0) {
+ $$ = nod(ODCLFIELD, newname($1), typenod($2));
+ $$->val = $3;
+ } else {
+ s = $2->sym;
+ if(s == S && isptr[$2->etype])
+ s = $2->type->sym;
+ p = importpkg;
+ if($1 != S)
+ p = $1->pkg;
+ $$ = embedded(s, p);
+ $$->right = typenod($2);
+ $$->val = $3;
+ }
+ }
+
+hidden_interfacedcl:
+ sym '(' ohidden_funarg_list ')' ohidden_funres
+ {
+ $$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
+ }
+| hidden_type
+ {
+ $$ = nod(ODCLFIELD, N, typenod($1));
+ }
+
+ohidden_funres:
+ {
+ $$ = nil;
+ }
+| hidden_funres
+
+hidden_funres:
+ '(' ohidden_funarg_list ')'
+ {
+ $$ = $2;
+ }
+| hidden_type
+ {
+ $$ = list1(nod(ODCLFIELD, N, typenod($1)));
+ }
+
+/*
+ * importing constants
+ */
+
+hidden_literal:
+ LLITERAL
+ {
+ $$ = nodlit($1);
+ }
+| '-' LLITERAL
+ {
+ $$ = nodlit($2);
+ switch($$->val.ctype){
+ case CTINT:
+ case CTRUNE:
+ mpnegfix($$->val.u.xval);
+ break;
+ case CTFLT:
+ mpnegflt($$->val.u.fval);
+ break;
+ case CTCPLX:
+ mpnegflt(&$$->val.u.cval->real);
+ mpnegflt(&$$->val.u.cval->imag);
+ 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 ')'
+ {
+ if($2->val.ctype == CTRUNE && $4->val.ctype == CTINT) {
+ $$ = $2;
+ mpaddfixfix($2->val.u.xval, $4->val.u.xval, 0);
+ break;
+ }
+ $4->val.u.cval->real = $4->val.u.cval->imag;
+ mpmovecflt(&$4->val.u.cval->imag, 0.0);
+ $$ = nodcplxlit($2->val, $4->val);
+ }
+
+hidden_import_list:
+| hidden_import_list hidden_import
+
+hidden_funarg_list:
+ hidden_funarg
+ {
+ $$ = list1($1);
+ }
+| hidden_funarg_list ',' hidden_funarg
+ {
+ $$ = 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 0000000..918d371
--- /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 <u.h>
+#include <libc.h>
+#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".
+ */
+Sym*
+renameinit(void)
+{
+ static int initgen;
+
+ snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
+ return lookup(namebuf);
+}
+
+/*
+ * 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;
+ case OAS:
+ if(isblank(l->n->left) && candiscard(l->n->right))
+ break;
+ // fall through
+ 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->defn = fn;
+ fn->nname->ntype = nod(OTFUNC, N, N);
+ declare(fn->nname, PFUNC);
+ 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/inl.c b/src/cmd/gc/inl.c
new file mode 100644
index 0000000..45e15bb
--- /dev/null
+++ b/src/cmd/gc/inl.c
@@ -0,0 +1,986 @@
+// 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.
+//
+// The inlining facility makes 2 passes: first caninl determines which
+// functions are suitable for inlining, and for those that are it
+// saves a copy of the body. Then inlcalls walks each function body to
+// expand calls to inlinable functions.
+//
+// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1,
+// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
+// These additional levels (beyond -l) may be buggy and are not supported.
+// 0: disabled
+// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
+// 2: early typechecking of all imported bodies
+// 3: allow variadic functions
+// 4: allow non-leaf functions , (breaks runtime.Caller)
+// 5: transitive inlining
+//
+// At some point this may get another default and become switch-offable with -N.
+//
+// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
+// which calls get inlined or not, more is for debugging, and may go away at any point.
+//
+// TODO:
+// - inline functions with ... args
+// - handle T.meth(f()) with func f() (t T, arg, arg, )
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Used by caninl.
+static Node* inlcopy(Node *n);
+static NodeList* inlcopylist(NodeList *ll);
+static int ishairy(Node *n, int *budget);
+static int ishairylist(NodeList *ll, int *budget);
+
+// Used by inlcalls
+static void inlnodelist(NodeList *l);
+static void inlnode(Node **np);
+static void mkinlcall(Node **np, Node *fn, int isddd);
+static Node* inlvar(Node *n);
+static Node* retvar(Type *n, int i);
+static Node* argvar(Type *n, int i);
+static Node* newlabel(void);
+static Node* inlsubst(Node *n);
+static NodeList* inlsubstlist(NodeList *l);
+
+static void setlno(Node*, int);
+
+// Used during inlsubst[list]
+static Node *inlfn; // function currently being inlined
+static Node *inlretlabel; // target of the goto substituted in place of a return
+static NodeList *inlretvars; // temp out variables
+
+// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
+// the ->sym can be re-used in the local package, so peel it off the receiver's type.
+static Pkg*
+fnpkg(Node *fn)
+{
+ Type *rcvr;
+
+ if(fn->type->thistuple) {
+ // method
+ rcvr = getthisx(fn->type)->type->type;
+ if(isptr[rcvr->etype])
+ rcvr = rcvr->type;
+ if(!rcvr->sym)
+ fatal("receiver with no sym: [%S] %lN (%T)", fn->sym, fn, rcvr);
+ return rcvr->sym->pkg;
+ }
+ // non-method
+ return fn->sym->pkg;
+}
+
+// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
+// because they're a copy of an already checked body.
+void
+typecheckinl(Node *fn)
+{
+ Node *savefn;
+ Pkg *pkg;
+ int save_safemode, lno;
+
+ lno = setlineno(fn);
+
+ // typecheckinl is only for imported functions;
+ // their bodies may refer to unsafe as long as the package
+ // was marked safe during import (which was checked then).
+ // the ->inl of a local function has been typechecked before caninl copied it.
+ pkg = fnpkg(fn);
+ if (pkg == localpkg || pkg == nil)
+ return; // typecheckinl on local function
+
+ if (debug['m']>2)
+ print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
+
+ save_safemode = safemode;
+ safemode = 0;
+
+ savefn = curfn;
+ curfn = fn;
+ typechecklist(fn->inl, Etop);
+ curfn = savefn;
+
+ safemode = save_safemode;
+
+ lineno = lno;
+}
+
+// Caninl determines whether fn is inlineable.
+// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
+// fn and ->nbody will already have been typechecked.
+void
+caninl(Node *fn)
+{
+ Node *savefn;
+ Type *t;
+ int budget;
+
+ if(fn->op != ODCLFUNC)
+ fatal("caninl %N", fn);
+ if(!fn->nname)
+ fatal("caninl no nname %+N", fn);
+
+ // If fn has no body (is defined outside of Go), cannot inline it.
+ if(fn->nbody == nil)
+ return;
+
+ if(fn->typecheck == 0)
+ fatal("caninl on non-typechecked function %N", fn);
+
+ // can't handle ... args yet
+ if(debug['l'] < 3)
+ for(t=fn->type->type->down->down->type; t; t=t->down)
+ if(t->isddd)
+ return;
+
+ budget = 40; // allowed hairyness
+ if(ishairylist(fn->nbody, &budget))
+ return;
+
+ savefn = curfn;
+ curfn = fn;
+
+ fn->nname->inl = fn->nbody;
+ fn->nbody = inlcopylist(fn->nname->inl);
+ fn->nname->inldcl = inlcopylist(fn->nname->defn->dcl);
+
+ // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
+ // this is so export can find the body of a method
+ fn->type->nname = fn->nname;
+
+ if(debug['m'] > 1)
+ print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl);
+ else if(debug['m'])
+ print("%L: can inline %N\n", fn->lineno, fn->nname);
+
+ curfn = savefn;
+}
+
+// Look for anything we want to punt on.
+static int
+ishairylist(NodeList *ll, int* budget)
+{
+ for(;ll;ll=ll->next)
+ if(ishairy(ll->n, budget))
+ return 1;
+ return 0;
+}
+
+static int
+ishairy(Node *n, int *budget)
+{
+ if(!n)
+ return 0;
+
+ // Things that are too hairy, irrespective of the budget
+ switch(n->op) {
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ case OPANIC:
+ case ORECOVER:
+ if(debug['l'] < 4)
+ return 1;
+ break;
+
+ case OCLOSURE:
+ case OCALLPART:
+ case ORANGE:
+ case OFOR:
+ case OSELECT:
+ case OSWITCH:
+ case OPROC:
+ case ODEFER:
+ case ODCLTYPE: // can't print yet
+ case ODCLCONST: // can't print yet
+ case ORETJMP:
+ return 1;
+
+ break;
+ }
+
+ (*budget)--;
+
+ return *budget < 0 ||
+ ishairy(n->left, budget) ||
+ ishairy(n->right, budget) ||
+ ishairylist(n->list, budget) ||
+ ishairylist(n->rlist, budget) ||
+ ishairylist(n->ninit, budget) ||
+ ishairy(n->ntest, budget) ||
+ ishairy(n->nincr, budget) ||
+ ishairylist(n->nbody, budget) ||
+ ishairylist(n->nelse, budget);
+}
+
+// Inlcopy and inlcopylist recursively copy the body of a function.
+// Any name-like node of non-local class is marked for re-export by adding it to
+// the exportlist.
+static NodeList*
+inlcopylist(NodeList *ll)
+{
+ NodeList *l;
+
+ l = nil;
+ for(; ll; ll=ll->next)
+ l = list(l, inlcopy(ll->n));
+ return l;
+}
+
+static Node*
+inlcopy(Node *n)
+{
+ Node *m;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ case OTYPE:
+ case OLITERAL:
+ return n;
+ }
+
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->inl = nil;
+ m->left = inlcopy(n->left);
+ m->right = inlcopy(n->right);
+ m->list = inlcopylist(n->list);
+ m->rlist = inlcopylist(n->rlist);
+ m->ninit = inlcopylist(n->ninit);
+ m->ntest = inlcopy(n->ntest);
+ m->nincr = inlcopy(n->nincr);
+ m->nbody = inlcopylist(n->nbody);
+ m->nelse = inlcopylist(n->nelse);
+
+ return m;
+}
+
+
+// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
+// calls made to inlineable functions. This is the external entry point.
+void
+inlcalls(Node *fn)
+{
+ Node *savefn;
+
+ savefn = curfn;
+ curfn = fn;
+ inlnode(&fn);
+ if(fn != curfn)
+ fatal("inlnode replaced curfn");
+ curfn = savefn;
+}
+
+// Turn an OINLCALL into a statement.
+static void
+inlconv2stmt(Node *n)
+{
+ n->op = OBLOCK;
+ // n->ninit stays
+ n->list = n->nbody;
+ n->nbody = nil;
+ n->rlist = nil;
+}
+
+// Turn an OINLCALL into a single valued expression.
+static void
+inlconv2expr(Node **np)
+{
+ Node *n, *r;
+ n = *np;
+ r = n->rlist->n;
+ addinit(&r, concat(n->ninit, n->nbody));
+ *np = r;
+}
+
+// Turn the rlist (with the return values) of the OINLCALL in
+// n into an expression list lumping the ninit and body
+// containing the inlined statements on the first list element so
+// order will be preserved Used in return, oas2func and call
+// statements.
+static NodeList*
+inlconv2list(Node *n)
+{
+ NodeList *l;
+
+ if(n->op != OINLCALL || n->rlist == nil)
+ fatal("inlconv2list %+N\n", n);
+
+ l = n->rlist;
+ addinit(&l->n, concat(n->ninit, n->nbody));
+ return l;
+}
+
+static void
+inlnodelist(NodeList *l)
+{
+ for(; l; l=l->next)
+ inlnode(&l->n);
+}
+
+// inlnode recurses over the tree to find inlineable calls, which will
+// be turned into OINLCALLs by mkinlcall. When the recursion comes
+// back up will examine left, right, list, rlist, ninit, ntest, nincr,
+// nbody and nelse and use one of the 4 inlconv/glue functions above
+// to turn the OINLCALL into an expression, a statement, or patch it
+// in to this nodes list or rlist as appropriate.
+// NOTE it makes no sense to pass the glue functions down the
+// recursion to the level where the OINLCALL gets created because they
+// have to edit /this/ n, so you'd have to push that one down as well,
+// but then you may as well do it here. so this is cleaner and
+// shorter and less complicated.
+static void
+inlnode(Node **np)
+{
+ Node *n;
+ NodeList *l;
+ int lno;
+
+ if(*np == nil)
+ return;
+
+ n = *np;
+
+ switch(n->op) {
+ case ODEFER:
+ case OPROC:
+ // inhibit inlining of their argument
+ switch(n->left->op) {
+ case OCALLFUNC:
+ case OCALLMETH:
+ n->left->etype = n->op;
+ }
+
+ case OCLOSURE:
+ // TODO do them here (or earlier),
+ // so escape analysis can avoid more heapmoves.
+ return;
+ }
+
+ lno = setlineno(n);
+
+ inlnodelist(n->ninit);
+ for(l=n->ninit; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ inlnode(&n->left);
+ if(n->left && n->left->op == OINLCALL)
+ inlconv2expr(&n->left);
+
+ inlnode(&n->right);
+ if(n->right && n->right->op == OINLCALL)
+ inlconv2expr(&n->right);
+
+ inlnodelist(n->list);
+ switch(n->op) {
+ case OBLOCK:
+ for(l=n->list; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+ break;
+
+ case ORETURN:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OAPPEND:
+ case OCOMPLEX:
+ // if we just replaced arg in f(arg()) or return arg with an inlined call
+ // and arg returns multiple values, glue as list
+ if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) {
+ n->list = inlconv2list(n->list->n);
+ break;
+ }
+
+ // fallthrough
+ default:
+ for(l=n->list; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2expr(&l->n);
+ }
+
+ inlnodelist(n->rlist);
+ switch(n->op) {
+ case OAS2FUNC:
+ if(n->rlist->n->op == OINLCALL) {
+ n->rlist = inlconv2list(n->rlist->n);
+ n->op = OAS2;
+ n->typecheck = 0;
+ typecheck(np, Etop);
+ break;
+ }
+
+ // fallthrough
+ default:
+ for(l=n->rlist; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2expr(&l->n);
+
+ }
+
+ inlnode(&n->ntest);
+ if(n->ntest && n->ntest->op == OINLCALL)
+ inlconv2expr(&n->ntest);
+
+ inlnode(&n->nincr);
+ if(n->nincr && n->nincr->op == OINLCALL)
+ inlconv2stmt(n->nincr);
+
+ inlnodelist(n->nbody);
+ for(l=n->nbody; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ inlnodelist(n->nelse);
+ for(l=n->nelse; l; l=l->next)
+ if(l->n->op == OINLCALL)
+ inlconv2stmt(l->n);
+
+ // with all the branches out of the way, it is now time to
+ // transmogrify this node itself unless inhibited by the
+ // switch at the top of this function.
+ switch(n->op) {
+ case OCALLFUNC:
+ case OCALLMETH:
+ if (n->etype == OPROC || n->etype == ODEFER)
+ return;
+ }
+
+ switch(n->op) {
+ case OCALLFUNC:
+ if(debug['m']>3)
+ print("%L:call to func %+N\n", n->lineno, n->left);
+ if(n->left->inl) // normal case
+ mkinlcall(np, n->left, n->isddd);
+ else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions
+ if(n->left->sym->def)
+ mkinlcall(np, n->left->sym->def, n->isddd);
+ break;
+
+ case OCALLMETH:
+ if(debug['m']>3)
+ print("%L:call to meth %lN\n", n->lineno, n->left->right);
+ // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
+ if(n->left->type == T)
+ fatal("no function type for [%p] %+N\n", n->left, n->left);
+
+ if(n->left->type->nname == N)
+ fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type);
+
+ mkinlcall(np, n->left->type->nname, n->isddd);
+
+ break;
+ }
+
+ lineno = lno;
+}
+
+static void mkinlcall1(Node **np, Node *fn, int isddd);
+
+static void
+mkinlcall(Node **np, Node *fn, int isddd)
+{
+ int save_safemode;
+ Pkg *pkg;
+
+ save_safemode = safemode;
+
+ // imported functions may refer to unsafe as long as the
+ // package was marked safe during import (already checked).
+ pkg = fnpkg(fn);
+ if(pkg != localpkg && pkg != nil)
+ safemode = 0;
+ mkinlcall1(np, fn, isddd);
+ safemode = save_safemode;
+}
+
+static Node*
+tinlvar(Type *t)
+{
+ if(t->nname && !isblank(t->nname)) {
+ if(!t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ return t->nname->inlvar;
+ }
+ typecheck(&nblank, Erv | Easgn);
+ return nblank;
+}
+
+static int inlgen;
+
+// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
+// On return ninit has the parameter assignments, the nbody is the
+// inlined function body and list, rlist contain the input, output
+// parameters.
+static void
+mkinlcall1(Node **np, Node *fn, int isddd)
+{
+ int i;
+ int chkargcount;
+ Node *n, *call, *saveinlfn, *as, *m;
+ NodeList *dcl, *ll, *ninit, *body;
+ Type *t;
+ // For variadic fn.
+ int variadic, varargcount, multiret;
+ Node *vararg;
+ NodeList *varargs;
+ Type *varargtype, *vararrtype;
+
+ if (fn->inl == nil)
+ return;
+
+ if (fn == curfn || fn->defn == curfn)
+ return;
+
+ if(debug['l']<2)
+ typecheckinl(fn);
+
+ n = *np;
+
+ // Bingo, we have a function node, and it has an inlineable body
+ if(debug['m']>1)
+ print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl);
+ else if(debug['m'])
+ print("%L: inlining call to %N\n", n->lineno, fn);
+
+ if(debug['m']>2)
+ print("%L: Before inlining: %+N\n", n->lineno, n);
+
+ saveinlfn = inlfn;
+ inlfn = fn;
+
+ ninit = n->ninit;
+
+//dumplist("ninit pre", ninit);
+
+ if(fn->defn) // local function
+ dcl = fn->inldcl;
+ else // imported function
+ dcl = fn->dcl;
+
+ inlretvars = nil;
+ i = 0;
+ // Make temp names to use instead of the originals
+ for(ll = dcl; ll; ll=ll->next) {
+ if(ll->n->class == PPARAMOUT) // return values handled below.
+ continue;
+ if(ll->n->op == ONAME) {
+ ll->n->inlvar = inlvar(ll->n);
+ // Typecheck because inlvar is not necessarily a function parameter.
+ typecheck(&ll->n->inlvar, Erv);
+ if ((ll->n->class&~PHEAP) != PAUTO)
+ ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
+ }
+ }
+
+ // temporaries for return values.
+ for(t = getoutargx(fn->type)->type; t; t = t->down) {
+ if(t != T && t->nname != N && !isblank(t->nname)) {
+ m = inlvar(t->nname);
+ typecheck(&m, Erv);
+ t->nname->inlvar = m;
+ } else {
+ // anonymous return values, synthesize names for use in assignment that replaces return
+ m = retvar(t, i++);
+ }
+ ninit = list(ninit, nod(ODCL, m, N));
+ inlretvars = list(inlretvars, m);
+ }
+
+ // assign receiver.
+ if(fn->type->thistuple && n->left->op == ODOTMETH) {
+ // method call with a receiver.
+ t = getthisx(fn->type)->type;
+ if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ if(!n->left->left)
+ fatal("method call without receiver: %+N", n);
+ if(t == T)
+ fatal("method call unknown receiver type: %+N", n);
+ as = nod(OAS, tinlvar(t), n->left->left);
+ if(as != N) {
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+ }
+
+ // check if inlined function is variadic.
+ variadic = 0;
+ varargtype = T;
+ varargcount = 0;
+ for(t=fn->type->type->down->down->type; t; t=t->down) {
+ if(t->isddd) {
+ variadic = 1;
+ varargtype = t->type;
+ }
+ }
+ // but if argument is dotted too forget about variadicity.
+ if(variadic && isddd)
+ variadic = 0;
+
+ // check if argument is actually a returned tuple from call.
+ multiret = 0;
+ if(n->list && !n->list->next) {
+ switch(n->list->n->op) {
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ if(n->list->n->left->type->outtuple > 1)
+ multiret = n->list->n->left->type->outtuple-1;
+ }
+ }
+
+ if(variadic) {
+ varargcount = count(n->list) + multiret;
+ if(n->left->op != ODOTMETH)
+ varargcount -= fn->type->thistuple;
+ varargcount -= fn->type->intuple - 1;
+ }
+
+ // assign arguments to the parameters' temp names
+ as = nod(OAS2, N, N);
+ as->rlist = n->list;
+ ll = n->list;
+
+ // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call?
+ if(fn->type->thistuple && n->left->op != ODOTMETH) {
+ // non-method call to method
+ if(!n->list)
+ fatal("non-method call to method without first arg: %+N", n);
+ // append receiver inlvar to LHS.
+ t = getthisx(fn->type)->type;
+ if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar)
+ fatal("missing inlvar for %N\n", t->nname);
+ if(t == T)
+ fatal("method call unknown receiver type: %+N", n);
+ as->list = list(as->list, tinlvar(t));
+ ll = ll->next; // track argument count.
+ }
+
+ // append ordinary arguments to LHS.
+ chkargcount = n->list && n->list->next;
+ vararg = N; // the slice argument to a variadic call
+ varargs = nil; // the list of LHS names to put in vararg.
+ if(!chkargcount) {
+ // 0 or 1 expression on RHS.
+ for(t = getinargx(fn->type)->type; t; t=t->down) {
+ if(variadic && t->isddd) {
+ vararg = tinlvar(t);
+ for(i=0; i<varargcount && ll; i++) {
+ m = argvar(varargtype, i);
+ varargs = list(varargs, m);
+ as->list = list(as->list, m);
+ }
+ break;
+ }
+ as->list = list(as->list, tinlvar(t));
+ }
+ } else {
+ // match arguments except final variadic (unless the call is dotted itself)
+ for(t = getinargx(fn->type)->type; t;) {
+ if(!ll)
+ break;
+ if(variadic && t->isddd)
+ break;
+ as->list = list(as->list, tinlvar(t));
+ t=t->down;
+ ll=ll->next;
+ }
+ // match varargcount arguments with variadic parameters.
+ if(variadic && t && t->isddd) {
+ vararg = tinlvar(t);
+ for(i=0; i<varargcount && ll; i++) {
+ m = argvar(varargtype, i);
+ varargs = list(varargs, m);
+ as->list = list(as->list, m);
+ ll=ll->next;
+ }
+ if(i==varargcount)
+ t=t->down;
+ }
+ if(ll || t)
+ fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list);
+ }
+
+ if (as->rlist) {
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+
+ // turn the variadic args into a slice.
+ if(variadic) {
+ as = nod(OAS, vararg, N);
+ if(!varargcount) {
+ as->right = nodnil();
+ as->right->type = varargtype;
+ } else {
+ vararrtype = typ(TARRAY);
+ vararrtype->type = varargtype->type;
+ vararrtype->bound = varargcount;
+
+ as->right = nod(OCOMPLIT, N, typenod(varargtype));
+ as->right->list = varargs;
+ as->right = nod(OSLICE, as->right, nod(OKEY, N, N));
+ }
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+
+ // zero the outparams
+ for(ll = inlretvars; ll; ll=ll->next) {
+ as = nod(OAS, ll->n, N);
+ typecheck(&as, Etop);
+ ninit = list(ninit, as);
+ }
+
+ inlretlabel = newlabel();
+ inlgen++;
+ body = inlsubstlist(fn->inl);
+
+ body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return
+ body = list(body, nod(OLABEL, inlretlabel, N));
+
+ typechecklist(body, Etop);
+//dumplist("ninit post", ninit);
+
+ call = nod(OINLCALL, N, N);
+ call->ninit = ninit;
+ call->nbody = body;
+ call->rlist = inlretvars;
+ call->type = n->type;
+ call->typecheck = 1;
+
+ setlno(call, n->lineno);
+//dumplist("call body", body);
+
+ *np = call;
+
+ inlfn = saveinlfn;
+
+ // transitive inlining
+ // TODO do this pre-expansion on fn->inl directly. requires
+ // either supporting exporting statemetns with complex ninits
+ // or saving inl and making inlinl
+ if(debug['l'] >= 5) {
+ body = fn->inl;
+ fn->inl = nil; // prevent infinite recursion
+ inlnodelist(call->nbody);
+ for(ll=call->nbody; ll; ll=ll->next)
+ if(ll->n->op == OINLCALL)
+ inlconv2stmt(ll->n);
+ fn->inl = body;
+ }
+
+ if(debug['m']>2)
+ print("%L: After inlining %+N\n\n", n->lineno, *np);
+
+}
+
+// Every time we expand a function we generate a new set of tmpnames,
+// PAUTO's in the calling functions, and link them off of the
+// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
+static Node*
+inlvar(Node *var)
+{
+ Node *n;
+
+ if(debug['m']>3)
+ print("inlvar %+N\n", var);
+
+ n = newname(var->sym);
+ n->type = var->type;
+ n->class = PAUTO;
+ n->used = 1;
+ n->curfn = curfn; // the calling function, not the called one
+ n->addrtaken = var->addrtaken;
+
+ // Esc pass wont run if we're inlining into a iface wrapper.
+ // Luckily, we can steal the results from the target func.
+ // If inlining a function defined in another package after
+ // escape analysis is done, treat all local vars as escaping.
+ // See issue 9537.
+ if(var->esc == EscHeap || (inl_nonlocal && var->op == ONAME))
+ addrescapes(n);
+
+ curfn->dcl = list(curfn->dcl, n);
+ return n;
+}
+
+// Synthesize a variable to store the inlined function's results in.
+static Node*
+retvar(Type *t, int i)
+{
+ Node *n;
+
+ snprint(namebuf, sizeof(namebuf), "~r%d", i);
+ n = newname(lookup(namebuf));
+ n->type = t->type;
+ n->class = PAUTO;
+ n->used = 1;
+ n->curfn = curfn; // the calling function, not the called one
+ curfn->dcl = list(curfn->dcl, n);
+ return n;
+}
+
+// Synthesize a variable to store the inlined function's arguments
+// when they come from a multiple return call.
+static Node*
+argvar(Type *t, int i)
+{
+ Node *n;
+
+ snprint(namebuf, sizeof(namebuf), "~arg%d", i);
+ n = newname(lookup(namebuf));
+ n->type = t->type;
+ n->class = PAUTO;
+ n->used = 1;
+ n->curfn = curfn; // the calling function, not the called one
+ curfn->dcl = list(curfn->dcl, n);
+ return n;
+}
+
+static Node*
+newlabel(void)
+{
+ Node *n;
+ static int label;
+
+ label++;
+ snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label);
+ n = newname(lookup(namebuf));
+ n->etype = 1; // flag 'safe' for escape analysis (no backjumps)
+ return n;
+}
+
+// inlsubst and inlsubstlist recursively copy the body of the saved
+// pristine ->inl body of the function while substituting references
+// to input/output parameters with ones to the tmpnames, and
+// substituting returns with assignments to the output.
+static NodeList*
+inlsubstlist(NodeList *ll)
+{
+ NodeList *l;
+
+ l = nil;
+ for(; ll; ll=ll->next)
+ l = list(l, inlsubst(ll->n));
+ return l;
+}
+
+static Node*
+inlsubst(Node *n)
+{
+ char *p;
+ Node *m, *as;
+ NodeList *ll;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ case ONAME:
+ if(n->inlvar) { // These will be set during inlnode
+ if (debug['m']>2)
+ print ("substituting name %+N -> %+N\n", n, n->inlvar);
+ return n->inlvar;
+ }
+ if (debug['m']>2)
+ print ("not substituting name %+N\n", n);
+ return n;
+
+ case OLITERAL:
+ case OTYPE:
+ return n;
+
+ case ORETURN:
+ // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
+
+// dump("Return before substitution", n);
+ m = nod(OGOTO, inlretlabel, N);
+ m->ninit = inlsubstlist(n->ninit);
+
+ if(inlretvars && n->list) {
+ as = nod(OAS2, N, N);
+ // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that.
+ for(ll=inlretvars; ll; ll=ll->next)
+ as->list = list(as->list, ll->n);
+ as->rlist = inlsubstlist(n->list);
+ typecheck(&as, Etop);
+ m->ninit = list(m->ninit, as);
+ }
+
+ typechecklist(m->ninit, Etop);
+ typecheck(&m, Etop);
+// dump("Return after substitution", m);
+ return m;
+
+ case OGOTO:
+ case OLABEL:
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->ninit = nil;
+ p = smprint("%s·%d", n->left->sym->name, inlgen);
+ m->left = newname(lookup(p));
+ free(p);
+ return m;
+ }
+
+
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->ninit = nil;
+
+ if(n->op == OCLOSURE)
+ fatal("cannot inline function containing closure: %+N", n);
+
+ m->left = inlsubst(n->left);
+ m->right = inlsubst(n->right);
+ m->list = inlsubstlist(n->list);
+ m->rlist = inlsubstlist(n->rlist);
+ m->ninit = concat(m->ninit, inlsubstlist(n->ninit));
+ m->ntest = inlsubst(n->ntest);
+ m->nincr = inlsubst(n->nincr);
+ m->nbody = inlsubstlist(n->nbody);
+ m->nelse = inlsubstlist(n->nelse);
+
+ return m;
+}
+
+// Plaster over linenumbers
+static void
+setlnolist(NodeList *ll, int lno)
+{
+ for(;ll;ll=ll->next)
+ setlno(ll->n, lno);
+}
+
+static void
+setlno(Node *n, int lno)
+{
+ if(!n)
+ return;
+
+ // don't clobber names, unless they're freshly synthesized
+ if(n->op != ONAME || n->lineno == 0)
+ n->lineno = lno;
+
+ setlno(n->left, lno);
+ setlno(n->right, lno);
+ setlnolist(n->list, lno);
+ setlnolist(n->rlist, lno);
+ setlnolist(n->ninit, lno);
+ setlno(n->ntest, lno);
+ setlno(n->nincr, lno);
+ setlnolist(n->nbody, lno);
+ setlnolist(n->nelse, lno);
+}
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
new file mode 100644
index 0000000..523ba37
--- /dev/null
+++ b/src/cmd/gc/lex.c
@@ -0,0 +1,2414 @@
+// 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 "go.h"
+#include "y.tab.h"
+#include <ar.h>
+
+#undef getc
+#undef ungetc
+#define getc ccgetc
+#define ungetc ccungetc
+
+extern int yychar;
+int yyprev;
+int yylast;
+
+static void lexinit(void);
+static void lexinit1(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;
+
+#define BOM 0xFEFF
+
+// Compiler experiments.
+// These are controlled by the GOEXPERIMENT environment
+// variable recorded when the compiler is built.
+static struct {
+ char *name;
+ int *val;
+} exper[] = {
+// {"rune32", &rune32},
+ {"fieldtrack", &fieldtrack_enabled},
+ {"precisestack", &precisestack_enabled},
+ {nil, nil},
+};
+
+// Debug arguments.
+// These can be specified with the -d flag, as in "-d nil"
+// to set the debug_checknil variable. In general the list passed
+// to -d can be comma-separated.
+static struct {
+ char *name;
+ int *val;
+} debugtab[] = {
+ {"nil", &debug_checknil},
+ {nil, nil},
+};
+
+static void
+addexp(char *s)
+{
+ int i;
+
+ for(i=0; exper[i].name != nil; i++) {
+ if(strcmp(exper[i].name, s) == 0) {
+ *exper[i].val = 1;
+ return;
+ }
+ }
+
+ print("unknown experiment %s\n", s);
+ exits("unknown experiment");
+}
+
+static void
+setexp(void)
+{
+ char *f[20];
+ int i, nf;
+
+ precisestack_enabled = 1; // on by default
+
+ // cmd/dist #defines GOEXPERIMENT for us.
+ nf = getfields(GOEXPERIMENT, f, nelem(f), 1, ",");
+ for(i=0; i<nf; i++)
+ addexp(f[i]);
+}
+
+char*
+expstring(void)
+{
+ int i;
+ static char buf[512];
+
+ strcpy(buf, "X");
+ for(i=0; exper[i].name != nil; i++)
+ if(*exper[i].val)
+ seprint(buf+strlen(buf), buf+sizeof buf, ",%s", exper[i].name);
+ if(strlen(buf) == 1)
+ strcpy(buf, "X,none");
+ buf[1] = ':';
+ return buf;
+}
+
+// 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 == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+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
+/*c2go void DBG(char*, ...); */
+
+enum
+{
+ EOF = -1,
+};
+
+void
+usage(void)
+{
+ print("usage: %cg [options] file.go...\n", thechar);
+ flagprint(1);
+ exits("usage");
+}
+
+void
+fault(int s)
+{
+ USED(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");
+}
+
+#ifdef PLAN9
+void
+catcher(void *v, char *s)
+{
+ USED(v);
+
+ if(strncmp(s, "sys: trap: fault read", 21) == 0) {
+ if(nsavederrors + nerrors > 0)
+ errorexit();
+ fatal("fault");
+ }
+ noted(NDFLT);
+}
+#endif
+
+void
+doversion(void)
+{
+ char *p;
+
+ p = expstring();
+ if(strcmp(p, "X:none") == 0)
+ p = "";
+ print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p);
+ exits(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ NodeList *l;
+ char *p;
+
+#ifdef SIGBUS
+ signal(SIGBUS, fault);
+ signal(SIGSEGV, fault);
+#endif
+
+#ifdef PLAN9
+ notify(catcher);
+ // Tell the FPU to handle all exceptions.
+ setfcr(FPPDBL|FPRNR);
+#endif
+ // Allow GOARCH=thestring or GOARCH=thestringsuffix,
+ // but not other values.
+ p = getgoarch();
+ if(strncmp(p, thestring, strlen(thestring)) != 0)
+ sysfatal("cannot use %cg with GOARCH=%s", thechar, p);
+ goarch = p;
+
+ linkarchinit();
+ ctxt = linknew(thelinkarch);
+ ctxt->diag = yyerror;
+ ctxt->bso = &bstdout;
+ Binit(&bstdout, 1, OWRITE);
+
+ localpkg = mkpkg(strlit(""));
+ localpkg->prefix = "\"\"";
+
+ // pseudo-package, for scoping
+ builtinpkg = mkpkg(strlit("go.builtin"));
+
+ // pseudo-package, accessed by import "unsafe"
+ unsafepkg = mkpkg(strlit("unsafe"));
+ unsafepkg->name = "unsafe";
+
+ // real package, referred to by generated runtime calls
+ runtimepkg = mkpkg(strlit("runtime"));
+ runtimepkg->name = "runtime";
+
+ // pseudo-packages used in symbol tables
+ gostringpkg = mkpkg(strlit("go.string"));
+ gostringpkg->name = "go.string";
+ gostringpkg->prefix = "go.string"; // not go%2estring
+
+ itabpkg = mkpkg(strlit("go.itab"));
+ itabpkg->name = "go.itab";
+ itabpkg->prefix = "go.itab"; // not go%2eitab
+
+ weaktypepkg = mkpkg(strlit("go.weak.type"));
+ weaktypepkg->name = "go.weak.type";
+ weaktypepkg->prefix = "go.weak.type"; // not go%2eweak%2etype
+
+ typelinkpkg = mkpkg(strlit("go.typelink"));
+ typelinkpkg->name = "go.typelink";
+ typelinkpkg->prefix = "go.typelink"; // not go%2etypelink
+
+ trackpkg = mkpkg(strlit("go.track"));
+ trackpkg->name = "go.track";
+ trackpkg->prefix = "go.track"; // not go%2etrack
+
+ typepkg = mkpkg(strlit("type"));
+ typepkg->name = "type";
+
+ goroot = getgoroot();
+ goos = getgoos();
+
+ nacl = strcmp(goos, "nacl") == 0;
+ if(nacl)
+ flag_largemodel = 1;
+
+ setexp();
+
+ outfile = nil;
+ flagcount("+", "compiling runtime", &compiling_runtime);
+ flagcount("%", "debug non-static initializers", &debug['%']);
+ flagcount("A", "for bootstrapping, allow 'any' type", &debug['A']);
+ flagcount("B", "disable bounds checking", &debug['B']);
+ flagstr("D", "path: set relative path for local imports", &localimport);
+ flagcount("E", "debug symbol export", &debug['E']);
+ flagfn1("I", "dir: add dir to import search path", addidir);
+ flagcount("K", "debug missing line numbers", &debug['K']);
+ flagcount("L", "use full (long) path in error messages", &debug['L']);
+ flagcount("M", "debug move generation", &debug['M']);
+ flagcount("N", "disable optimizations", &debug['N']);
+ flagcount("P", "debug peephole optimizer", &debug['P']);
+ flagcount("R", "debug register optimizer", &debug['R']);
+ flagcount("S", "print assembly listing", &debug['S']);
+ flagfn0("V", "print compiler version", doversion);
+ flagcount("W", "debug parse tree after type checking", &debug['W']);
+ flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
+ flagstr("d", "list: print debug information about items in list", &debugstr);
+ flagcount("e", "no limit on number of errors reported", &debug['e']);
+ flagcount("f", "debug stack frames", &debug['f']);
+ flagcount("g", "debug code generation", &debug['g']);
+ flagcount("h", "halt on error", &debug['h']);
+ flagcount("i", "debug line number stack", &debug['i']);
+ flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
+ flagcount("j", "debug runtime-initialized variables", &debug['j']);
+ flagcount("l", "disable inlining", &debug['l']);
+ flagcount("live", "debug liveness analysis", &debuglive);
+ flagcount("m", "print optimization decisions", &debug['m']);
+ flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports);
+ flagstr("o", "obj: set output file", &outfile);
+ flagstr("p", "path: set expected package import path", &myimportpath);
+ flagcount("pack", "write package file instead of object file", &writearchive);
+ flagcount("r", "debug generated wrappers", &debug['r']);
+ flagcount("race", "enable race detector", &flag_race);
+ flagcount("s", "warn about composite literals that can be simplified", &debug['s']);
+ flagstr("trimpath", "prefix: remove prefix from recorded source file paths", &ctxt->trimpath);
+ flagcount("u", "reject unsafe code", &safemode);
+ flagcount("v", "increase debug verbosity", &debug['v']);
+ flagcount("w", "debug type checking", &debug['w']);
+ use_writebarrier = 1;
+ flagcount("wb", "enable write barrier", &use_writebarrier);
+ flagcount("x", "debug lexer", &debug['x']);
+ flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']);
+ if(thechar == '6')
+ flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel);
+
+ flagparse(&argc, &argv, usage);
+ ctxt->debugasm = debug['S'];
+ ctxt->debugvlog = debug['v'];
+
+ if(argc < 1)
+ usage();
+
+ if(flag_race) {
+ racepkg = mkpkg(strlit("runtime/race"));
+ racepkg->name = "race";
+ }
+
+ // parse -d argument
+ if(debugstr) {
+ char *f[100];
+ int i, j, nf;
+
+ nf = getfields(debugstr, f, nelem(f), 1, ",");
+ for(i=0; i<nf; i++) {
+ for(j=0; debugtab[j].name != nil; j++) {
+ if(strcmp(debugtab[j].name, f[i]) == 0) {
+ *debugtab[j].val = 1;
+ break;
+ }
+ }
+ if(debugtab[j].name == nil)
+ sysfatal("unknown debug information -d '%s'\n", f[i]);
+ }
+ }
+
+ // enable inlining. for now:
+ // default: inlining on. (debug['l'] == 1)
+ // -l: inlining off (debug['l'] == 0)
+ // -ll, -lll: inlining on again, with extra debugging (debug['l'] > 1)
+ if(debug['l'] <= 1)
+ debug['l'] = 1 - debug['l'];
+
+ if(thechar == '8') {
+ p = getgo386();
+ if(strcmp(p, "387") == 0)
+ use_sse = 0;
+ else if(strcmp(p, "sse2") == 0)
+ use_sse = 1;
+ else
+ sysfatal("unsupported setting GO386=%s", p);
+ }
+
+ fmtinstallgo();
+ betypeinit();
+ if(widthptr == 0)
+ fatal("betypeinit failed");
+
+ lexinit();
+ typeinit();
+ lexinit1();
+ 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;
+ curio.eofnl = 0;
+ curio.last = 0;
+
+ // Skip initial BOM if present.
+ if(Bgetrune(curio.bin) != BOM)
+ Bungetrune(curio.bin);
+
+ 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 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.
+ defercheckwidth();
+ for(l=xtop; l; l=l->next)
+ if(l->n->op != ODCL && l->n->op != OAS)
+ typecheck(&l->n, Etop);
+
+ // Phase 2: Variable assignments.
+ // To check interface assignments, depends on phase 1.
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCL || l->n->op == OAS)
+ typecheck(&l->n, Etop);
+ resumecheckwidth();
+
+ // Phase 3: Type check function bodies.
+ 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);
+ checkreturn(l->n);
+ if(nerrors != 0)
+ l->n->nbody = nil; // type errors; do not compile
+ }
+ }
+
+ curfn = nil;
+
+ if(nsavederrors+nerrors)
+ errorexit();
+
+ // Phase 4: Inlining
+ if(debug['l'] > 1) {
+ // Typecheck imported function bodies if debug['l'] > 1,
+ // otherwise lazily when used or re-exported.
+ for(l=importlist; l; l=l->next)
+ if (l->n->inl) {
+ saveerrors();
+ typecheckinl(l->n);
+ }
+
+ if(nsavederrors+nerrors)
+ errorexit();
+ }
+
+ if(debug['l']) {
+ // Find functions that can be inlined and clone them before walk expands them.
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ caninl(l->n);
+
+ // Expand inlineable calls in all functions
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ inlcalls(l->n);
+ }
+
+ // Phase 5: Escape analysis.
+ // Required for moving heap allocations onto stack,
+ // which in turn is required by the closure implementation,
+ // which stores the addresses of stack variables into the closure.
+ // If the closure does not escape, it needs to be on the stack
+ // or else the stack copier will not update it.
+ escapes(xtop);
+
+ // Escape analysis moved escaped values off stack.
+ // Move large values off stack too.
+ movelarge(xtop);
+
+ // Phase 6: Compile top level functions.
+ for(l=xtop; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ funccompile(l->n, 0);
+
+ if(nsavederrors+nerrors == 0)
+ fninit(xtop);
+
+ // Phase 7: Check external declarations.
+ 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();
+ exits(0);
+ return 0;
+}
+
+void
+saveerrors(void)
+{
+ nsavederrors += nerrors;
+ nerrors = 0;
+}
+
+static int
+arsize(Biobuf *b, char *name)
+{
+ struct ar_hdr a;
+
+ if(Bread(b, a.name, sizeof(a.name)) != sizeof(a.name) ||
+ Bread(b, a.date, sizeof(a.date)) != sizeof(a.date) ||
+ Bread(b, a.uid, sizeof(a.uid)) != sizeof(a.uid) ||
+ Bread(b, a.gid, sizeof(a.gid)) != sizeof(a.gid) ||
+ Bread(b, a.mode, sizeof(a.mode)) != sizeof(a.mode) ||
+ Bread(b, a.size, sizeof(a.size)) != sizeof(a.size) ||
+ Bread(b, a.fmag, sizeof(a.fmag)) != sizeof(a.fmag))
+ 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 may be first; skip it */
+ sz = arsize(b, "__.GOSYMDEF");
+ if(sz >= 0)
+ Bseek(b, sz, 1);
+ else
+ Bseek(b, 8, 0);
+ /* package export block is next */
+ 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(name->len >= 1 && name->s[0] == '/')
+ return 1;
+ if(ctxt->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 == 1 && strncmp(name->s, ".", 1) == 0)
+ return 1;
+ if(name->len >= 3 && strncmp(name->s, "../", 3) == 0)
+ return 1;
+ if(name->len == 2 && strncmp(name->s, "..", 2) == 0)
+ return 1;
+ return 0;
+}
+
+static int
+findpkg(Strlit *name)
+{
+ Idir *p;
+ char *q, *suffix, *suffixsep;
+
+ if(islocalname(name)) {
+ if(safemode || nolocalimports)
+ 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 "encoding/../encoding/base64"
+ // as different from "encoding/base64".
+ 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) {
+ suffix = "";
+ suffixsep = "";
+ if(flag_installsuffix != nil) {
+ suffixsep = "_";
+ suffix = flag_installsuffix;
+ } else if(flag_race) {
+ suffixsep = "_";
+ suffix = "race";
+ }
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thechar);
+ if(access(namebuf, 0) >= 0)
+ return 1;
+ }
+ return 0;
+}
+
+static void
+fakeimport(void)
+{
+ importpkg = mkpkg(strlit("fake"));
+ cannedimports("fake.6", "$$\n");
+}
+
+void
+importfile(Val *f, int line)
+{
+ Biobuf *imp;
+ char *file, *p, *q, *tag;
+ int32 c;
+ int len;
+ Strlit *path;
+ char *cleanbuf, *prefix;
+
+ USED(line);
+
+ if(f->ctype != CTSTR) {
+ yyerror("import statement not a string");
+ fakeimport();
+ return;
+ }
+
+ if(f->u.sval->len == 0) {
+ yyerror("import path is empty");
+ fakeimport();
+ return;
+ }
+
+ if(isbadimport(f->u.sval)) {
+ fakeimport();
+ return;
+ }
+
+ // 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(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) {
+ yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval);
+ 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)) {
+ if(path->s[0] == '/') {
+ yyerror("import path cannot be absolute path");
+ fakeimport();
+ return;
+ }
+ prefix = ctxt->pathname;
+ if(localimport != nil)
+ prefix = localimport;
+ cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2);
+ strcpy(cleanbuf, prefix);
+ strcat(cleanbuf, "/");
+ strcat(cleanbuf, path->s);
+ cleanname(cleanbuf);
+ path = strlit(cleanbuf);
+
+ if(isbadimport(path)) {
+ fakeimport();
+ return;
+ }
+ }
+
+ if(!findpkg(path)) {
+ yyerror("can't find import: \"%Z\"", f->u.sval);
+ errorexit();
+ }
+ importpkg = mkpkg(path);
+
+ // If we already saw that package, feed a dummy statement
+ // to the lexer to avoid parsing export data twice.
+ if(importpkg->imported) {
+ file = strdup(namebuf);
+ tag = "";
+ if(importpkg->safe) {
+ tag = "safe";
+ }
+ p = smprint("package %s %s\n$$\n", importpkg->name, tag);
+ cannedimports(file, p);
+ return;
+ }
+ importpkg->imported = 1;
+
+ 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 %s", getgoos(), getgoarch(), getgoversion(), expstring());
+ 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 < ' ') {
+ return !yy_isspace(c); // exclude good white space
+ }
+ 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;
+ 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 == '\r')
+ continue;
+ 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 = CTRUNE;
+ 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;
+ yylval.i = lexlineno;
+ 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);
+ if(h == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ 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.i = 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 U+%04x", 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:
+ 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");
+ if(c == 'p')
+ goto caseep;
+ goto ncu;
+ }
+ }
+
+ if(c == 'p') // 0p begins floating point zero
+ goto caseep;
+
+ 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 caseep;
+ if(c == 'i')
+ goto casei;
+ if(c1)
+ yyerror("malformed octal constant");
+ goto ncu;
+
+dc:
+ if(c == '.')
+ goto casedot;
+ if(c == 'e' || c == 'E' || c == 'p' || c == 'P')
+ goto caseep;
+ 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;
+
+caseep:
+ *cp++ = c;
+ 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, *linep;
+ Hist *h;
+
+ c = getr();
+ if(c == 'g')
+ goto go;
+ if(c != 'l')
+ goto out;
+ for(i=1; i<5; i++) {
+ c = getr();
+ if(c != "line "[i])
+ goto out;
+ }
+
+ cp = lexbuf;
+ ep = lexbuf+sizeof(lexbuf)-5;
+ linep = nil;
+ for(;;) {
+ c = getr();
+ if(c == EOF)
+ goto out;
+ if(c == '\n')
+ break;
+ if(c == ' ')
+ continue;
+ if(c == ':')
+ linep = cp;
+ if(cp < ep)
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ if(linep == nil || linep >= ep)
+ goto out;
+ *linep++ = '\0';
+ n = 0;
+ for(cp=linep; *cp; cp++) {
+ if(*cp < '0' || *cp > '9')
+ goto out;
+ n = n*10 + *cp - '0';
+ if(n > 1e8) {
+ yyerror("line number out of range");
+ errorexit();
+ }
+ }
+ if(n <= 0)
+ goto out;
+
+ // try to avoid allocating file name over and over
+ for(h=ctxt->hist; h!=nil; h=h->link) {
+ if(h->name != nil && strcmp(h->name, lexbuf) == 0) {
+ linehist(h->name, n, 0);
+ goto out;
+ }
+ }
+ linehist(strdup(lexbuf), n, 0);
+ goto out;
+
+go:
+ cp = lexbuf;
+ ep = lexbuf+sizeof(lexbuf)-5;
+ *cp++ = 'g'; // already read
+ for(;;) {
+ c = getr();
+ if(c == EOF || c >= Runeself)
+ goto out;
+ if(c == '\n')
+ break;
+ if(cp < ep)
+ *cp++ = c;
+ }
+ *cp = 0;
+ ep = strchr(lexbuf, ' ');
+ if(ep != nil)
+ *ep = 0;
+
+ if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) {
+ nointerface = 1;
+ goto out;
+ }
+ if(strcmp(lexbuf, "go:noescape") == 0) {
+ noescape = 1;
+ goto out;
+ }
+ if(strcmp(lexbuf, "go:nosplit") == 0) {
+ nosplit = 1;
+ goto out;
+ }
+
+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, c1, c2;
+
+ c = curio.peekc;
+ if(c != 0) {
+ curio.peekc = curio.peekc1;
+ curio.peekc1 = 0;
+ goto check;
+ }
+
+ if(curio.bin == nil) {
+ c = *curio.cp & 0xff;
+ if(c != 0)
+ curio.cp++;
+ } else {
+ loop:
+ c = BGETC(curio.bin);
+ if(c == 0xef) {
+ c1 = BGETC(curio.bin);
+ c2 = BGETC(curio.bin);
+ if(c1 == 0xbb && c2 == 0xbf) {
+ yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file");
+ goto loop;
+ }
+ Bungetc(curio.bin);
+ Bungetc(curio.bin);
+ }
+ }
+
+check:
+ switch(c) {
+ case 0:
+ if(curio.bin != nil) {
+ yyerror("illegal NUL byte");
+ break;
+ }
+ case EOF:
+ // insert \n at EOF
+ if(curio.eofnl || curio.last == '\n')
+ return EOF;
+ curio.eofnl = 1;
+ c = '\n';
+ case '\n':
+ if(pushedio.bin == nil)
+ lexlineno++;
+ break;
+ }
+ curio.last = c;
+ 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},
+ {"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},
+ {"delete", LNAME, Txxx, ODELETE},
+ {"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;
+ Val v;
+
+ /*
+ * 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);
+ s1 = pkglookup(syms[i].name, builtinpkg);
+ t = types[etype];
+ if(t == T) {
+ t = typ(etype);
+ t->sym = s1;
+
+ if(etype != TANY && etype != TSTRING)
+ dowidth(t);
+ types[etype] = t;
+ }
+ s1->lexical = LNAME;
+ s1->def = typenod(t);
+ continue;
+ }
+
+ etype = syms[i].op;
+ if(etype != OXXX) {
+ s1 = pkglookup(syms[i].name, builtinpkg);
+ s1->lexical = LNAME;
+ s1->def = nod(ONAME, N, N);
+ s1->def->sym = s1;
+ s1->def->etype = etype;
+ s1->def->builtin = 1;
+ }
+ }
+
+ // 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;
+
+ s = pkglookup("_", builtinpkg);
+ s->block = -100;
+ s->def = nod(ONAME, N, N);
+ s->def->sym = s;
+ types[TBLANK] = typ(TBLANK);
+ s->def->type = types[TBLANK];
+
+ types[TNIL] = typ(TNIL);
+ s = pkglookup("nil", builtinpkg);
+ v.ctype = CTNIL;
+ s->def = nodlit(v);
+ s->def->sym = s;
+}
+
+static void
+lexinit1(void)
+{
+ Sym *s, *s1;
+ Type *t, *f, *rcvr, *in, *out;
+
+ // t = interface { Error() string }
+ rcvr = typ(TSTRUCT);
+ rcvr->type = typ(TFIELD);
+ rcvr->type->type = ptrto(typ(TSTRUCT));
+ rcvr->funarg = 1;
+ in = typ(TSTRUCT);
+ in->funarg = 1;
+ out = typ(TSTRUCT);
+ out->type = typ(TFIELD);
+ out->type->type = types[TSTRING];
+ out->funarg = 1;
+ f = typ(TFUNC);
+ *getthis(f) = rcvr;
+ *getoutarg(f) = out;
+ *getinarg(f) = in;
+ f->thistuple = 1;
+ f->intuple = 0;
+ f->outnamed = 0;
+ f->outtuple = 1;
+ t = typ(TINTER);
+ t->type = typ(TFIELD);
+ t->type->sym = lookup("Error");
+ t->type->type = f;
+
+ // error type
+ s = lookup("error");
+ s->lexical = LNAME;
+ s1 = pkglookup("error", builtinpkg);
+ errortype = t;
+ errortype->sym = s1;
+ s1->lexical = LNAME;
+ s1->def = typenod(errortype);
+
+ // byte alias
+ s = lookup("byte");
+ s->lexical = LNAME;
+ s1 = pkglookup("byte", builtinpkg);
+ bytetype = typ(TUINT8);
+ bytetype->sym = s1;
+ s1->lexical = LNAME;
+ s1->def = typenod(bytetype);
+
+ // rune alias
+ s = lookup("rune");
+ s->lexical = LNAME;
+ s1 = pkglookup("rune", builtinpkg);
+ runetype = typ(TINT32);
+ runetype->sym = s1;
+ s1->lexical = LNAME;
+ s1->def = typenod(runetype);
+}
+
+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]);
+ s->origpkg = builtinpkg;
+ }
+
+ 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;
+ s->origpkg = builtinpkg;
+ }
+ }
+
+ // backend-specific builtin types (e.g. int).
+ for(i=0; typedefs[i].name; i++) {
+ s = lookup(typedefs[i].name);
+ if(s->def == N) {
+ s->def = typenod(types[typedefs[i].etype]);
+ s->origpkg = builtinpkg;
+ }
+ }
+
+ // there's only so much table-driven we can handle.
+ // these are special cases.
+ s = lookup("byte");
+ if(s->def == N) {
+ s->def = typenod(bytetype);
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("error");
+ if(s->def == N) {
+ s->def = typenod(errortype);
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("rune");
+ if(s->def == N) {
+ s->def = typenod(runetype);
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("nil");
+ if(s->def == N) {
+ v.ctype = CTNIL;
+ s->def = nodlit(v);
+ s->def->sym = s;
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("iota");
+ if(s->def == N) {
+ s->def = nod(OIOTA, N, N);
+ s->def->sym = s;
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("true");
+ if(s->def == N) {
+ s->def = nodbool(1);
+ s->def->sym = s;
+ s->origpkg = builtinpkg;
+ }
+
+ s = lookup("false");
+ if(s->def == N) {
+ s->def = nodbool(0);
+ s->def->sym = s;
+ s->origpkg = builtinpkg;
+ }
+
+ nodfp = nod(ONAME, N, N);
+ nodfp->type = types[TINT32];
+ nodfp->xoffset = 0;
+ nodfp->class = PPARAM;
+ nodfp->sym = lookup(".fp");
+}
+
+struct
+{
+ int lex;
+ char* name;
+} lexn[] =
+{
+ {LANDAND, "ANDAND"},
+ {LANDNOT, "ANDNOT"},
+ {LASOP, "ASOP"},
+ {LBREAK, "BREAK"},
+ {LCASE, "CASE"},
+ {LCHAN, "CHAN"},
+ {LCOLAS, "COLAS"},
+ {LCOMM, "<-"},
+ {LCONST, "CONST"},
+ {LCONTINUE, "CONTINUE"},
+ {LDDD, "..."},
+ {LDEC, "DEC"},
+ {LDEFAULT, "DEFAULT"},
+ {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"},
+ {LSELECT, "SELECT"},
+ {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"},
+ {"LCHAN", "chan"},
+ {"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:;
+ }
+}
+
+static void
+pkgnotused(int lineno, Strlit *path, char *name)
+{
+ char *elem;
+
+ // If the package was imported with a name other than the final
+ // import path element, show it explicitly in the error message.
+ // Note that this handles both renamed imports and imports of
+ // packages containing unconventional package declarations.
+ // Note that this uses / always, even on Windows, because Go import
+ // paths always use forward slashes.
+ elem = strrchr(path->s, '/');
+ if(elem != nil)
+ elem++;
+ else
+ elem = path->s;
+ if(name == nil || strcmp(elem, name) == 0)
+ yyerrorl(lineno, "imported and not used: \"%Z\"", path);
+ else
+ yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name);
+}
+
+void
+mkpackage(char* pkgname)
+{
+ Sym *s;
+ int32 h;
+ char *p, *q;
+
+ 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)
+ pkgnotused(s->def->lineno, s->def->pkg->path, s->name);
+ 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) {
+ pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil);
+ s->def->pack->used = 1;
+ }
+ s->def = N;
+ continue;
+ }
+ }
+ }
+ }
+
+ if(outfile == nil) {
+ p = strrchr(infile, '/');
+ if(ctxt->windows) {
+ q = strrchr(infile, '\\');
+ if(q > p)
+ p = q;
+ }
+ 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 0000000..46cb6b7
--- /dev/null
+++ b/src/cmd/gc/md5.c
@@ -0,0 +1,302 @@
+// 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 ../../crypto/md5/md5*.go.
+
+#include <u.h>
+#include <libc.h>
+#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
+/*c2go
+enum {
+ _Init0 = 0x67452301,
+ _Init1 = 0xEFCDAB89,
+ _Init2 = 0x98BADCFE,
+ _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, uint64 *hi)
+{
+ 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");
+
+ if(hi != nil)
+ *hi = d->s[2] | ((uint64)d->s[3]<<32);
+ 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) | ((uint32)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 0000000..5a60106
--- /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*, uint64*);
diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin
new file mode 100755
index 0000000..1dab1c9
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin
@@ -0,0 +1,32 @@
+#!/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 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 $(go tool dist env)
+if [ -z "$GOCHAR" ]; then
+ echo 'missing $GOCHAR - go tool dist failed?' 1>&2
+ exit 1
+fi
+
+GC=${GOCHAR}g
+gcc -o mkbuiltin1 mkbuiltin1.c
+rm -f _builtin.c
+echo "// AUTO-GENERATED by mkbuiltin; DO NOT EDIT" >>_builtin.c
+for i in runtime unsafe
+do
+ go tool $GC -A $i.go
+ O=$GOCHAR ./mkbuiltin1 $i >>_builtin.c
+done
+
+# If _builtin.c has changed vs builtin.c,
+# check in the new change.
+cmp -s _builtin.c builtin.c || cp _builtin.c builtin.c
+rm _builtin.c mkbuiltin1 unsafe.$GOCHAR runtime.$GOCHAR
diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c
new file mode 100644
index 0000000..69027fd
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -0,0 +1,102 @@
+// 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.
+
+// +build ignore
+
+// Compile .go file, import data from .6 file, and generate C string version.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+void esc(char*);
+void fatal(char*, ...);
+
+int
+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");
+ fatal("in file $1.6 s/PACKAGE/$1/");
+ }
+
+ 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) {
+ fatal("open %s: %s", buf, strerror(errno));
+ }
+
+ // look for $$ that introduces imports
+ while(fgets(buf, sizeof buf, fin) != NULL)
+ if(strstr(buf, "$$"))
+ goto begin;
+ fatal("did not find beginning of imports");
+
+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")) != NULL) {
+ *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");
+ }
+ fatal("did not find end of imports");
+
+end:
+ printf("\t\"$$\\n\";\n");
+ return 0;
+}
+
+void
+esc(char *p)
+{
+ for(; *p; p++) {
+ if(*p == '\\' || *p == '\"')
+ printf("\\");
+ putchar(*p);
+ }
+}
+
+void
+fatal(char *msg, ...)
+{
+ va_list arg;
+
+ va_start(arg, msg);
+ fprintf(stderr, "fatal: ");
+ vfprintf(stderr, msg, arg);
+ fprintf(stderr, "\n");
+ exit(2);
+}
diff --git a/src/cmd/gc/mkopnames b/src/cmd/gc/mkopnames
new file mode 100755
index 0000000..d3f27e8
--- /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 ' ' '\012' |
+ tr -d ' \011,' |
+ 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 0000000..d33a81e
--- /dev/null
+++ b/src/cmd/gc/mparith1.c
@@ -0,0 +1,641 @@
+// 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 "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 c1;
+
+ mpmovecfix(&c1, c);
+ return mpcmpfixfix(b, &c1);
+}
+
+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(b, &a);
+}
+
+void
+mpsubfixfix(Mpint *a, Mpint *b)
+{
+ mpnegfix(a);
+ mpaddfixfix(a, b, 0);
+ 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, 0);
+}
+
+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);
+}
+
+static void
+mphextofix(Mpint *a, char *s, int n)
+{
+ char *hexdigitp, *end, c;
+ long d;
+ int bit;
+
+ while(*s == '0') {
+ s++;
+ n--;
+ }
+
+ // overflow
+ if(4*n > Mpscale*Mpprec) {
+ a->ovf = 1;
+ return;
+ }
+
+ end = s+n-1;
+ for(hexdigitp=end; hexdigitp>=s; hexdigitp--) {
+ c = *hexdigitp;
+ if(c >= '0' && c <= '9')
+ d = c-'0';
+ else if(c >= 'A' && c <= 'F')
+ d = c-'A'+10;
+ else
+ d = c-'a'+10;
+
+ bit = 4*(end - hexdigitp);
+ while(d > 0) {
+ if(d & 1)
+ a->a[bit/Mpscale] |= (long)1 << (bit%Mpscale);
+ bit++;
+ d = d >> 1;
+ }
+ }
+}
+
+//
+// floating point input
+// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
+//
+void
+mpatoflt(Mpflt *a, char *as)
+{
+ Mpflt b;
+ int dp, c, f, ef, ex, eb, base;
+ char *s, *start;
+
+ while(*as == ' ' || *as == '\t')
+ as++;
+
+ /* determine base */
+ s = as;
+ base = -1;
+ while(base == -1) {
+ switch(*s++) {
+ case '-':
+ case '+':
+ break;
+
+ case '0':
+ if(*s == 'x')
+ base = 16;
+ else
+ base = 10;
+ break;
+
+ default:
+ base = 10;
+ }
+ }
+
+ s = as;
+ dp = 0; /* digits after decimal point */
+ f = 0; /* sign */
+ ex = 0; /* exponent */
+ eb = 0; /* binary point */
+
+ mpmovecflt(a, 0.0);
+ if(base == 16) {
+ start = nil;
+ for(;;) {
+ c = *s;
+ if(c == '-') {
+ f = 1;
+ s++;
+ }
+ else if(c == '+') {
+ s++;
+ }
+ else if(c == '0' && s[1] == 'x') {
+ s += 2;
+ start = s;
+ }
+ else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ s++;
+ }
+ else {
+ break;
+ }
+ }
+ if(start == nil)
+ goto bad;
+
+ mphextofix(&a->val, start, s-start);
+ if(a->val.ovf)
+ goto bad;
+ a->exp = 0;
+ mpnorm(a);
+ }
+ for(;;) {
+ switch(c = *s++) {
+ default:
+ goto bad;
+
+ case '-':
+ f = 1;
+
+ case ' ':
+ case '\t':
+ case '+':
+ continue;
+
+ case '.':
+ if(base == 16)
+ goto bad;
+ 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("constant exponent out of range: %s", as);
+ errorexit();
+ }
+ continue;
+ }
+ break;
+ }
+ if(ef)
+ ex = -ex;
+
+ case 0:
+ break;
+ }
+ break;
+ }
+
+ if(eb) {
+ if(dp)
+ goto bad;
+ mpsetexp(a, 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 {
+ // 4 approximates least_upper_bound(log2(10)).
+ if(dp-ex >= (1<<(8*sizeof(dp)-3)) || (short)(4*(dp-ex)) != 4*(dp-ex)) {
+ mpmovecflt(a, 0.0);
+ }
+ else {
+ mppow10flt(&b, dp-ex);
+ mpdivfltflt(a, &b);
+ }
+ }
+ }
+
+out:
+ if(f)
+ mpnegflt(a);
+ return;
+
+bad:
+ yyerror("constant too large: %s", as);
+ mpmovecflt(a, 0.0);
+}
+
+//
+// fixed point input
+// required syntax is [+-][0[x]]d*
+//
+void
+mpatofix(Mpint *a, char *as)
+{
+ int c, f;
+ char *s, *s0;
+
+ 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:
+ s0 = s;
+ c = *s;
+ while(c) {
+ if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+ s++;
+ c = *s;
+ continue;
+ }
+ goto bad;
+ }
+ mphextofix(a, s0, s-s0);
+ if(a->ovf)
+ goto bad;
+
+out:
+ if(f)
+ mpnegfix(a);
+ return;
+
+bad:
+ yyerror("constant too large: %s", as);
+ mpmovecfix(a, 0);
+}
+
+int
+Bconv(Fmt *fp)
+{
+ char buf[500], *p;
+ Mpint *xval, q, r, ten, sixteen;
+ int f, digit;
+
+ xval = va_arg(fp->args, Mpint*);
+ mpmovefixfix(&q, xval);
+ f = 0;
+ if(mptestfix(&q) < 0) {
+ f = 1;
+ mpnegfix(&q);
+ }
+
+ p = &buf[sizeof(buf)];
+ *--p = 0;
+ if(fp->flags & FmtSharp) {
+ // Hexadecimal
+ mpmovecfix(&sixteen, 16);
+ for(;;) {
+ mpdivmodfixfix(&q, &r, &q, &sixteen);
+ digit = mpgetfix(&r);
+ if(digit < 10)
+ *--p = digit + '0';
+ else
+ *--p = digit - 10 + 'A';
+ if(mptestfix(&q) <= 0)
+ break;
+ }
+ *--p = 'x';
+ *--p = '0';
+ } else {
+ // Decimal
+ mpmovecfix(&ten, 10);
+ 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, dexp;
+ int exp;
+
+ 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
+ exp = fvp->exp + sigfig(fvp)*Mpscale;
+ if(-900 < exp && exp < 900) {
+ d = mpgetflt(fvp);
+ if(d >= 0 && (fp->flags & FmtSign))
+ fmtprint(fp, "+");
+ return fmtprint(fp, "%g", d);
+ }
+
+ // very out of range. compute decimal approximation by hand.
+ // decimal exponent
+ dexp = fvp->exp * 0.301029995663981195; // log_10(2)
+ exp = (int)dexp;
+ // decimal mantissa
+ fv = *fvp;
+ fv.val.neg = 0;
+ fv.exp = 0;
+ d = mpgetflt(&fv);
+ d *= pow(10, dexp-exp);
+ while(d >= 9.99995) {
+ d /= 10;
+ exp++;
+ }
+ if(fvp->val.neg)
+ fmtprint(fp, "-");
+ else if(fp->flags & FmtSign)
+ fmtprint(fp, "+");
+ return fmtprint(fp, "%.5fe+%d", d, exp);
+ }
+
+ 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 0000000..fd9f591
--- /dev/null
+++ b/src/cmd/gc/mparith2.c
@@ -0,0 +1,716 @@
+// 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 "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
+//
+static void
+mplsh(Mpint *a, int quiet)
+{
+ 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;
+ }
+ a->ovf = c;
+ if(a->ovf && !quiet)
+ yyerror("constant shift overflow");
+}
+
+//
+// left shift mpint by Mpscale
+// ignores sign
+//
+static void
+mplshw(Mpint *a, int quiet)
+{
+ long *a1;
+ int i;
+
+ a1 = &a->a[Mpprec-1];
+ if(*a1) {
+ a->ovf = 1;
+ if(!quiet)
+ yyerror("constant shift overflow");
+ }
+ 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) {
+ if(nsavederrors+nerrors == 0)
+ 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, 0);
+ s -= Mpscale;
+ }
+ while(s > 0) {
+ mplsh(a, 0);
+ 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 quiet)
+{
+ int i, c;
+ long x, *a1, *b1;
+
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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 && !quiet)
+ yyerror("constant addition overflow");
+
+ 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) {
+ if(nsavederrors+nerrors == 0)
+ 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) {
+ if(s.ovf) {
+ q.ovf = 1;
+ goto out;
+ }
+ mpaddfixfix(&q, &s, 1);
+ if(q.ovf)
+ goto out;
+ }
+ mplsh(&s, 1);
+ x >>= 1;
+ }
+ }
+
+out:
+ q.neg = a->neg ^ b->neg;
+ mpmovefixfix(a, &q);
+ if(a->ovf)
+ yyerror("constant multiplication overflow");
+}
+
+void
+mpmulfract(Mpint *a, Mpint *b)
+{
+
+ int i, j;
+ long *a1, x;
+ Mpint s, q;
+
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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, 1);
+ mprsh(&s);
+ }
+ }
+
+ q.neg = a->neg ^ b->neg;
+ mpmovefixfix(a, &q);
+ if(a->ovf)
+ yyerror("constant multiplication overflow");
+}
+
+void
+mporfixfix(Mpint *a, Mpint *b)
+{
+ int i;
+ long x, *a1, *b1;
+
+ x = 0;
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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;
+
+ x = 0;
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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;
+
+ x = 0;
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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;
+
+ x = 0;
+ if(a->ovf || b->ovf) {
+ if(nsavederrors+nerrors == 0)
+ 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) {
+ if(nsavederrors+nerrors == 0)
+ 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) {
+ if(nsavederrors+nerrors == 0)
+ 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) {
+ if(nsavederrors+nerrors == 0)
+ yyerror("constant overflow");
+ return 0;
+ }
+
+ v = (uvlong)a->a[0];
+ v |= (uvlong)a->a[1] << Mpscale;
+ v |= (uvlong)a->a[2] << (Mpscale+Mpscale);
+ if(a->neg)
+ v = -(uvlong)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 = -(uvlong)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, 1);
+ }
+
+ // 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("constant division overflow");
+ 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, 1);
+ 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
+mpiszero(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(!mpiszero(&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 0000000..6afd75c
--- /dev/null
+++ b/src/cmd/gc/mparith3.c
@@ -0,0 +1,346 @@
+// 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 "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;
+}
+
+/*
+ * sets the exponent.
+ * a too large exponent is an error.
+ * a too small exponent rounds the number to zero.
+ */
+void
+mpsetexp(Mpflt *a, int exp) {
+ if((short)exp != exp) {
+ if(exp > 0) {
+ yyerror("float constant is too large");
+ a->exp = 0x7fff;
+ }
+ else {
+ mpmovecflt(a, 0);
+ }
+ }
+ else {
+ a->exp = exp;
+ }
+}
+
+/*
+ * 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);
+ mpsetexp(a, 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, 0);
+ goto out;
+ }
+ if(s < 0) {
+ // b is larger, shift a right
+ mpshiftfix(&a->val, s);
+ mpsetexp(a, a->exp-s);
+ mpaddfixfix(&a->val, &b->val, 0);
+ goto out;
+ }
+ mpaddfixfix(&a->val, &b->val, 0);
+
+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);
+ mpsetexp(a, (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("constant division 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);
+ mpsetexp(a, (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1);
+
+ mpnorm(a);
+ if(Mpdebug)
+ print(" = %F\n\n", a);
+}
+
+static double
+mpgetfltN(Mpflt *a, int prec, int bias)
+{
+ int s, i, e, minexp;
+ uvlong v;
+ double f;
+
+ if(a->val.ovf && nsavederrors+nerrors == 0)
+ 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);
+ mpsetexp(a, a->exp-1); // can set 'a' to zero
+ s = sigfig(a);
+ if(s == 0)
+ return 0;
+ }
+
+ // pick up the mantissa, a rounding bit, and a tie-breaking bit in a uvlong
+ s = prec+2;
+ v = 0;
+ for(i=Mpnorm-1; s>=Mpscale; i--) {
+ v = (v<<Mpscale) | a->val.a[i];
+ s -= Mpscale;
+ }
+ if(s > 0) {
+ v = (v<<s) | (a->val.a[i]>>(Mpscale-s));
+ if((a->val.a[i]&((1<<(Mpscale-s))-1)) != 0)
+ v |= 1;
+ i--;
+ }
+ for(; i >= 0; i--) {
+ if(a->val.a[i] != 0)
+ v |= 1;
+ }
+
+ // gradual underflow
+ e = Mpnorm*Mpscale + a->exp - prec;
+ minexp = bias+1-prec+1;
+ if(e < minexp) {
+ s = minexp - e;
+ if(s > prec+1)
+ s = prec+1;
+ if((v & ((1ULL<<s)-1)) != 0)
+ v |= 1ULL<<s;
+ v >>= s;
+ e = minexp;
+ }
+
+ // round to even
+ v |= (v&4)>>2;
+ v += v&1;
+ v >>= 2;
+
+ f = (double)(v);
+ f = ldexp(f, e);
+
+ if(a->val.neg)
+ f = -f;
+
+ return f;
+}
+
+double
+mpgetflt(Mpflt *a)
+{
+ return mpgetfltN(a, 53, -1023);
+}
+
+double
+mpgetflt32(Mpflt *a)
+{
+ return mpgetfltN(a, 24, -127);
+}
+
+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 0000000..b752a13
--- /dev/null
+++ b/src/cmd/gc/obj.c
@@ -0,0 +1,284 @@
+// 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 "go.h"
+#include "../ld/textflag.h"
+
+/*
+ * architecture-independent object file output
+ */
+
+static void dumpglobls(void);
+
+enum
+{
+ ArhdrSize = 60
+};
+
+static void
+formathdr(char *arhdr, char *name, vlong size)
+{
+ snprint(arhdr, ArhdrSize, "%-16s%-12d%-6d%-6d%-8o%-10lld`",
+ name, 0, 0, 0, 0644, size);
+ arhdr[ArhdrSize-1] = '\n'; // overwrite \0 written by snprint
+}
+
+void
+dumpobj(void)
+{
+ NodeList *externs, *tmp;
+ char arhdr[ArhdrSize];
+ vlong startobj, size;
+ Sym *zero;
+
+ bout = Bopen(outfile, OWRITE);
+ if(bout == nil) {
+ flusherrors();
+ print("can't create %s: %r\n", outfile);
+ errorexit();
+ }
+
+ startobj = 0;
+ if(writearchive) {
+ Bwrite(bout, "!<arch>\n", 8);
+ memset(arhdr, 0, sizeof arhdr);
+ Bwrite(bout, arhdr, sizeof arhdr);
+ startobj = Boffset(bout);
+ }
+ Bprint(bout, "go object %s %s %s %s\n", getgoos(), getgoarch(), getgoversion(), expstring());
+ dumpexport();
+
+ if(writearchive) {
+ Bflush(bout);
+ size = Boffset(bout) - startobj;
+ if(size&1)
+ Bputc(bout, 0);
+ Bseek(bout, startobj - ArhdrSize, 0);
+ formathdr(arhdr, "__.PKGDEF", size);
+ Bwrite(bout, arhdr, ArhdrSize);
+ Bflush(bout);
+
+ Bseek(bout, startobj + size + (size&1), 0);
+ memset(arhdr, 0, ArhdrSize);
+ Bwrite(bout, arhdr, ArhdrSize);
+ startobj = Boffset(bout);
+ Bprint(bout, "go object %s %s %s %s\n", getgoos(), getgoarch(), getgoversion(), expstring());
+ }
+
+ Bprint(bout, "\n!\n");
+
+ externs = nil;
+ if(externdcl != nil)
+ externs = externdcl->end;
+
+ dumpglobls();
+ dumptypestructs();
+
+ // Dump extra globals.
+ tmp = externdcl;
+ if(externs != nil)
+ externdcl = externs->next;
+ dumpglobls();
+ externdcl = tmp;
+
+ zero = pkglookup("zerovalue", runtimepkg);
+ ggloblsym(zero, zerosize, DUPOK|RODATA);
+
+ dumpdata();
+ writeobj(ctxt, bout);
+
+ if(writearchive) {
+ Bflush(bout);
+ size = Boffset(bout) - startobj;
+ if(size&1)
+ Bputc(bout, 0);
+ Bseek(bout, startobj - ArhdrSize, 0);
+ snprint(namebuf, sizeof namebuf, "_go_.%c", thechar);
+ formathdr(arhdr, namebuf, size);
+ Bwrite(bout, arhdr, ArhdrSize);
+ }
+ 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);
+ }
+
+ for(l=funcsyms; l; l=l->next) {
+ n = l->n;
+ dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0);
+ ggloblsym(n->sym, widthptr, DUPOK|RODATA);
+ }
+
+ // Do not reprocess funcsyms on next dumpglobls call.
+ funcsyms = nil;
+}
+
+void
+Bputname(Biobuf *b, LSym *s)
+{
+ Bwrite(b, s->name, strlen(s->name)+1);
+}
+
+LSym*
+linksym(Sym *s)
+{
+ char *p;
+
+ if(s == nil)
+ return nil;
+ if(s->lsym != nil)
+ return s->lsym;
+ if(isblanksym(s))
+ s->lsym = linklookup(ctxt, "_", 0);
+ else {
+ p = smprint("%s.%s", s->pkg->prefix, s->name);
+ s->lsym = linklookup(ctxt, p, 0);
+ free(p);
+ }
+ return s->lsym;
+}
+
+int
+duintxx(Sym *s, int off, uint64 v, int wid)
+{
+ // Update symbol data directly instead of generating a
+ // DATA instruction that liblink will have to interpret later.
+ // This reduces compilation time and memory usage.
+ off = rnd(off, wid);
+ return setuintxx(ctxt, linksym(s), off, v, wid);
+}
+
+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.lit);
+ pkg = gostringpkg;
+ }
+ sym = pkglookup(namebuf, pkg);
+
+ // SymUniq flag indicates that data is generated already
+ if(sym->flags & SymUniq)
+ return sym;
+ sym->flags |= SymUniq;
+ sym->def = newname(sym);
+
+ off = 0;
+
+ // string header
+ off = dsymptr(sym, off, sym, widthptr+widthint);
+ off = duintxx(sym, off, len, widthint);
+
+ // 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, DUPOK|RODATA);
+
+ return sym;
+}
+
+void
+slicebytes(Node *nam, char *s, int len)
+{
+ int off, n, m;
+ static int gen;
+ Sym *sym;
+
+ snprint(namebuf, sizeof(namebuf), ".gobytes.%d", ++gen);
+ sym = pkglookup(namebuf, localpkg);
+ sym->def = newname(sym);
+
+ off = 0;
+ for(n=0; n<len; n+=m) {
+ m = 8;
+ if(m > len-n)
+ m = len-n;
+ off = dsname(sym, off, s+n, m);
+ }
+ ggloblsym(sym, off, NOPTR);
+
+ if(nam->op != ONAME)
+ fatal("slicebytes %N", nam);
+ off = nam->xoffset;
+ off = dsymptr(nam->sym, off, sym, 0);
+ off = duintxx(nam->sym, off, len, widthint);
+ duintxx(nam->sym, off, len, widthint);
+}
diff --git a/src/cmd/gc/opnames.h b/src/cmd/gc/opnames.h
new file mode 100644
index 0000000..be93036
--- /dev/null
+++ b/src/cmd/gc/opnames.h
@@ -0,0 +1,155 @@
+// auto generated by go tool dist
+static char *opnames[] = {
+ [OXXX] = "XXX",
+ [ONAME] = "NAME",
+ [ONONAME] = "NONAME",
+ [OTYPE] = "TYPE",
+ [OPACK] = "PACK",
+ [OLITERAL] = "LITERAL",
+ [OADD] = "ADD",
+ [OSUB] = "SUB",
+ [OOR] = "OR",
+ [OXOR] = "XOR",
+ [OADDSTR] = "ADDSTR",
+ [OADDR] = "ADDR",
+ [OANDAND] = "ANDAND",
+ [OAPPEND] = "APPEND",
+ [OARRAYBYTESTR] = "ARRAYBYTESTR",
+ [OARRAYBYTESTRTMP] = "ARRAYBYTESTRTMP",
+ [OARRAYRUNESTR] = "ARRAYRUNESTR",
+ [OSTRARRAYBYTE] = "STRARRAYBYTE",
+ [OSTRARRAYRUNE] = "STRARRAYRUNE",
+ [OAS] = "AS",
+ [OAS2] = "AS2",
+ [OAS2FUNC] = "AS2FUNC",
+ [OAS2RECV] = "AS2RECV",
+ [OAS2MAPR] = "AS2MAPR",
+ [OAS2DOTTYPE] = "AS2DOTTYPE",
+ [OASOP] = "ASOP",
+ [OCALL] = "CALL",
+ [OCALLFUNC] = "CALLFUNC",
+ [OCALLMETH] = "CALLMETH",
+ [OCALLINTER] = "CALLINTER",
+ [OCALLPART] = "CALLPART",
+ [OCAP] = "CAP",
+ [OCLOSE] = "CLOSE",
+ [OCLOSURE] = "CLOSURE",
+ [OCMPIFACE] = "CMPIFACE",
+ [OCMPSTR] = "CMPSTR",
+ [OCOMPLIT] = "COMPLIT",
+ [OMAPLIT] = "MAPLIT",
+ [OSTRUCTLIT] = "STRUCTLIT",
+ [OARRAYLIT] = "ARRAYLIT",
+ [OPTRLIT] = "PTRLIT",
+ [OCONV] = "CONV",
+ [OCONVIFACE] = "CONVIFACE",
+ [OCONVNOP] = "CONVNOP",
+ [OCOPY] = "COPY",
+ [ODCL] = "DCL",
+ [ODCLFUNC] = "DCLFUNC",
+ [ODCLFIELD] = "DCLFIELD",
+ [ODCLCONST] = "DCLCONST",
+ [ODCLTYPE] = "DCLTYPE",
+ [ODELETE] = "DELETE",
+ [ODOT] = "DOT",
+ [ODOTPTR] = "DOTPTR",
+ [ODOTMETH] = "DOTMETH",
+ [ODOTINTER] = "DOTINTER",
+ [OXDOT] = "XDOT",
+ [ODOTTYPE] = "DOTTYPE",
+ [ODOTTYPE2] = "DOTTYPE2",
+ [OEQ] = "EQ",
+ [ONE] = "NE",
+ [OLT] = "LT",
+ [OLE] = "LE",
+ [OGE] = "GE",
+ [OGT] = "GT",
+ [OIND] = "IND",
+ [OINDEX] = "INDEX",
+ [OINDEXMAP] = "INDEXMAP",
+ [OKEY] = "KEY",
+ [OPARAM] = "PARAM",
+ [OLEN] = "LEN",
+ [OMAKE] = "MAKE",
+ [OMAKECHAN] = "MAKECHAN",
+ [OMAKEMAP] = "MAKEMAP",
+ [OMAKESLICE] = "MAKESLICE",
+ [OMUL] = "MUL",
+ [ODIV] = "DIV",
+ [OMOD] = "MOD",
+ [OLSH] = "LSH",
+ [ORSH] = "RSH",
+ [OAND] = "AND",
+ [OANDNOT] = "ANDNOT",
+ [ONEW] = "NEW",
+ [ONOT] = "NOT",
+ [OCOM] = "COM",
+ [OPLUS] = "PLUS",
+ [OMINUS] = "MINUS",
+ [OOROR] = "OROR",
+ [OPANIC] = "PANIC",
+ [OPRINT] = "PRINT",
+ [OPRINTN] = "PRINTN",
+ [OPAREN] = "PAREN",
+ [OSEND] = "SEND",
+ [OSLICE] = "SLICE",
+ [OSLICEARR] = "SLICEARR",
+ [OSLICESTR] = "SLICESTR",
+ [OSLICE3] = "SLICE3",
+ [OSLICE3ARR] = "SLICE3ARR",
+ [ORECOVER] = "RECOVER",
+ [ORECV] = "RECV",
+ [ORUNESTR] = "RUNESTR",
+ [OSELRECV] = "SELRECV",
+ [OSELRECV2] = "SELRECV2",
+ [OIOTA] = "IOTA",
+ [OREAL] = "REAL",
+ [OIMAG] = "IMAG",
+ [OCOMPLEX] = "COMPLEX",
+ [OBLOCK] = "BLOCK",
+ [OBREAK] = "BREAK",
+ [OCASE] = "CASE",
+ [OXCASE] = "XCASE",
+ [OCONTINUE] = "CONTINUE",
+ [ODEFER] = "DEFER",
+ [OEMPTY] = "EMPTY",
+ [OFALL] = "FALL",
+ [OXFALL] = "XFALL",
+ [OFOR] = "FOR",
+ [OGOTO] = "GOTO",
+ [OIF] = "IF",
+ [OLABEL] = "LABEL",
+ [OPROC] = "PROC",
+ [ORANGE] = "RANGE",
+ [ORETURN] = "RETURN",
+ [OSELECT] = "SELECT",
+ [OSWITCH] = "SWITCH",
+ [OTYPESW] = "TYPESW",
+ [OTCHAN] = "TCHAN",
+ [OTMAP] = "TMAP",
+ [OTSTRUCT] = "TSTRUCT",
+ [OTINTER] = "TINTER",
+ [OTFUNC] = "TFUNC",
+ [OTARRAY] = "TARRAY",
+ [ODDD] = "DDD",
+ [ODDDARG] = "DDDARG",
+ [OINLCALL] = "INLCALL",
+ [OEFACE] = "EFACE",
+ [OITAB] = "ITAB",
+ [OSPTR] = "SPTR",
+ [OCLOSUREVAR] = "CLOSUREVAR",
+ [OCFUNC] = "CFUNC",
+ [OCHECKNIL] = "CHECKNIL",
+ [OVARKILL] = "VARKILL",
+ [OREGISTER] = "REGISTER",
+ [OINDREG] = "INDREG",
+ [OCMP] = "CMP",
+ [ODEC] = "DEC",
+ [OINC] = "INC",
+ [OEXTEND] = "EXTEND",
+ [OHMUL] = "HMUL",
+ [OLROT] = "LROT",
+ [ORROTC] = "RROTC",
+ [ORETJMP] = "RETJMP",
+ [OEND] = "END",
+};
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
new file mode 100644
index 0000000..76820fd
--- /dev/null
+++ b/src/cmd/gc/order.c
@@ -0,0 +1,1101 @@
+// Copyright 2012 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.
+
+// Rewrite tree to use separate statements to enforce
+// order of evaluation. Makes walk easier, because it
+// can (after this runs) reorder at will within an expression.
+//
+// Rewrite x op= y into x = x op y.
+//
+// Introduce temporaries as needed by runtime routines.
+// For example, the map runtime routines take the map key
+// by reference, so make sure all map keys are addressable
+// by copying them to temporaries as needed.
+// The same is true for channel operations.
+//
+// Arrange that map index expressions only appear in direct
+// assignments x = m[k] or m[k] = x, never in larger expressions.
+//
+// Arrange that receive expressions only appear in direct assignments
+// x = <-c or as standalone statements <-c, never in larger expressions.
+
+// TODO(rsc): The temporary introduction during multiple assignments
+// should be moved into this file, so that the temporaries can be cleaned
+// and so that conversions implicit in the OAS2FUNC and OAS2RECV
+// nodes can be made explicit and then have their temporaries cleaned.
+
+// TODO(rsc): Goto and multilevel break/continue can jump over
+// inserted VARKILL annotations. Work out a way to handle these.
+// The current implementation is safe, in that it will execute correctly.
+// But it won't reuse temporaries as aggressively as it might, and
+// it can result in unnecessary zeroing of those variables in the function
+// prologue.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Order holds state during the ordering process.
+typedef struct Order Order;
+struct Order
+{
+ NodeList *out; // list of generated statements
+ NodeList *temp; // head of stack of temporary variables
+ NodeList *free; // free list of NodeList* structs (for use in temp)
+};
+
+static void orderstmt(Node*, Order*);
+static void orderstmtlist(NodeList*, Order*);
+static void orderblock(NodeList **l);
+static void orderexpr(Node**, Order*);
+static void orderexprinplace(Node**, Order*);
+static void orderexprlist(NodeList*, Order*);
+static void orderexprlistinplace(NodeList*, Order*);
+
+// Order rewrites fn->nbody to apply the ordering constraints
+// described in the comment at the top of the file.
+void
+order(Node *fn)
+{
+ orderblock(&fn->nbody);
+}
+
+// Ordertemp allocates a new temporary with the given type,
+// pushes it onto the temp stack, and returns it.
+// If clear is true, ordertemp emits code to zero the temporary.
+static Node*
+ordertemp(Type *t, Order *order, int clear)
+{
+ Node *var, *a;
+ NodeList *l;
+
+ var = temp(t);
+ if(clear) {
+ a = nod(OAS, var, N);
+ typecheck(&a, Etop);
+ order->out = list(order->out, a);
+ }
+ if((l = order->free) == nil)
+ l = mal(sizeof *l);
+ order->free = l->next;
+ l->next = order->temp;
+ l->n = var;
+ order->temp = l;
+ return var;
+}
+
+// Ordercopyexpr behaves like ordertemp but also emits
+// code to initialize the temporary to the value n.
+//
+// The clear argument is provided for use when the evaluation
+// of tmp = n turns into a function call that is passed a pointer
+// to the temporary as the output space. If the call blocks before
+// tmp has been written, the garbage collector will still treat the
+// temporary as live, so we must zero it before entering that call.
+// Today, this only happens for channel receive operations.
+// (The other candidate would be map access, but map access
+// returns a pointer to the result data instead of taking a pointer
+// to be filled in.)
+static Node*
+ordercopyexpr(Node *n, Type *t, Order *order, int clear)
+{
+ Node *a, *var;
+
+ var = ordertemp(t, order, clear);
+ a = nod(OAS, var, n);
+ typecheck(&a, Etop);
+ order->out = list(order->out, a);
+ return var;
+}
+
+// Ordercheapexpr returns a cheap version of n.
+// The definition of cheap is that n is a variable or constant.
+// If not, ordercheapexpr allocates a new tmp, emits tmp = n,
+// and then returns tmp.
+static Node*
+ordercheapexpr(Node *n, Order *order)
+{
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ return n;
+ }
+ return ordercopyexpr(n, n->type, order, 0);
+}
+
+// Ordersafeexpr returns a safe version of n.
+// The definition of safe is that n can appear multiple times
+// without violating the semantics of the original program,
+// and that assigning to the safe version has the same effect
+// as assigning to the original n.
+//
+// The intended use is to apply to x when rewriting x += y into x = x + y.
+static Node*
+ordersafeexpr(Node *n, Order *order)
+{
+ Node *l, *r, *a;
+
+ switch(n->op) {
+ default:
+ fatal("ordersafeexpr %O", n->op);
+
+ case ONAME:
+ case OLITERAL:
+ return n;
+
+ case ODOT:
+ l = ordersafeexpr(n->left, order);
+ if(l == n->left)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->orig = a;
+ a->left = l;
+ typecheck(&a, Erv);
+ return a;
+
+ case ODOTPTR:
+ case OIND:
+ l = ordercheapexpr(n->left, order);
+ if(l == n->left)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->orig = a;
+ a->left = l;
+ typecheck(&a, Erv);
+ return a;
+
+ case OINDEX:
+ case OINDEXMAP:
+ if(isfixedarray(n->left->type))
+ l = ordersafeexpr(n->left, order);
+ else
+ l = ordercheapexpr(n->left, order);
+ r = ordercheapexpr(n->right, order);
+ if(l == n->left && r == n->right)
+ return n;
+ a = nod(OXXX, N, N);
+ *a = *n;
+ a->orig = a;
+ a->left = l;
+ a->right = r;
+ typecheck(&a, Erv);
+ return a;
+ }
+}
+
+// Istemp reports whether n is a temporary variable.
+static int
+istemp(Node *n)
+{
+ if(n->op != ONAME)
+ return 0;
+ return strncmp(n->sym->name, "autotmp_", 8) == 0;
+}
+
+// Isaddrokay reports whether it is okay to pass n's address to runtime routines.
+// Taking the address of a variable makes the liveness and optimization analyses
+// lose track of where the variable's lifetime ends. To avoid hurting the analyses
+// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
+// because we emit explicit VARKILL instructions marking the end of those
+// temporaries' lifetimes.
+static int
+isaddrokay(Node *n)
+{
+ return islvalue(n) && (n->op != ONAME || n->class == PEXTERN || istemp(n));
+}
+
+// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines.
+// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits
+// tmp = *np, and then sets *np to the tmp variable.
+static void
+orderaddrtemp(Node **np, Order *order)
+{
+ Node *n;
+
+ n = *np;
+ if(isaddrokay(n))
+ return;
+ *np = ordercopyexpr(n, n->type, order, 0);
+}
+
+// Marktemp returns the top of the temporary variable stack.
+static NodeList*
+marktemp(Order *order)
+{
+ return order->temp;
+}
+
+// Poptemp pops temporaries off the stack until reaching the mark,
+// which must have been returned by marktemp.
+static void
+poptemp(NodeList *mark, Order *order)
+{
+ NodeList *l;
+
+ while((l = order->temp) != mark) {
+ order->temp = l->next;
+ l->next = order->free;
+ order->free = l;
+ }
+}
+
+// Cleantempnopop emits to *out VARKILL instructions for each temporary
+// above the mark on the temporary stack, but it does not pop them
+// from the stack.
+static void
+cleantempnopop(NodeList *mark, Order *order, NodeList **out)
+{
+ NodeList *l;
+ Node *kill;
+
+ for(l=order->temp; l != mark; l=l->next) {
+ kill = nod(OVARKILL, l->n, N);
+ typecheck(&kill, Etop);
+ *out = list(*out, kill);
+ }
+}
+
+// Cleantemp emits VARKILL instructions for each temporary above the
+// mark on the temporary stack and removes them from the stack.
+static void
+cleantemp(NodeList *top, Order *order)
+{
+ cleantempnopop(top, order, &order->out);
+ poptemp(top, order);
+}
+
+// Orderstmtlist orders each of the statements in the list.
+static void
+orderstmtlist(NodeList *l, Order *order)
+{
+ for(; l; l=l->next)
+ orderstmt(l->n, order);
+}
+
+// Orderblock orders the block of statements *l onto a new list,
+// and then replaces *l with that list.
+static void
+orderblock(NodeList **l)
+{
+ Order order;
+ NodeList *mark;
+
+ memset(&order, 0, sizeof order);
+ mark = marktemp(&order);
+ orderstmtlist(*l, &order);
+ cleantemp(mark, &order);
+ *l = order.out;
+}
+
+// Orderexprinplace orders the side effects in *np and
+// leaves them as the init list of the final *np.
+static void
+orderexprinplace(Node **np, Order *outer)
+{
+ Node *n;
+ NodeList **lp;
+ Order order;
+
+ n = *np;
+ memset(&order, 0, sizeof order);
+ orderexpr(&n, &order);
+ addinit(&n, order.out);
+
+ // insert new temporaries from order
+ // at head of outer list.
+ lp = &order.temp;
+ while(*lp != nil)
+ lp = &(*lp)->next;
+ *lp = outer->temp;
+ outer->temp = order.temp;
+
+ *np = n;
+}
+
+// Orderstmtinplace orders the side effects of the single statement *np
+// and replaces it with the resulting statement list.
+void
+orderstmtinplace(Node **np)
+{
+ Node *n;
+ Order order;
+ NodeList *mark;
+
+ n = *np;
+ memset(&order, 0, sizeof order);
+ mark = marktemp(&order);
+ orderstmt(n, &order);
+ cleantemp(mark, &order);
+ *np = liststmt(order.out);
+}
+
+// Orderinit moves n's init list to order->out.
+static void
+orderinit(Node *n, Order *order)
+{
+ orderstmtlist(n->ninit, order);
+ n->ninit = nil;
+}
+
+// Ismulticall reports whether the list l is f() for a multi-value function.
+// Such an f() could appear as the lone argument to a multi-arg function.
+static int
+ismulticall(NodeList *l)
+{
+ Node *n;
+
+ // one arg only
+ if(l == nil || l->next != nil)
+ return 0;
+ n = l->n;
+
+ // must be call
+ switch(n->op) {
+ default:
+ return 0;
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ break;
+ }
+
+ // call must return multiple values
+ return n->left->type->outtuple > 1;
+}
+
+// Copyret emits t1, t2, ... = n, where n is a function call,
+// and then returns the list t1, t2, ....
+static NodeList*
+copyret(Node *n, Order *order)
+{
+ Type *t;
+ Node *tmp, *as;
+ NodeList *l1, *l2;
+ Iter tl;
+
+ if(n->type->etype != TSTRUCT || !n->type->funarg)
+ fatal("copyret %T %d", n->type, n->left->type->outtuple);
+
+ l1 = nil;
+ l2 = nil;
+ for(t=structfirst(&tl, &n->type); t; t=structnext(&tl)) {
+ tmp = temp(t->type);
+ l1 = list(l1, tmp);
+ l2 = list(l2, tmp);
+ }
+
+ as = nod(OAS2, N, N);
+ as->list = l1;
+ as->rlist = list1(n);
+ typecheck(&as, Etop);
+ orderstmt(as, order);
+
+ return l2;
+}
+
+// Ordercallargs orders the list of call arguments *l.
+static void
+ordercallargs(NodeList **l, Order *order)
+{
+ if(ismulticall(*l)) {
+ // return f() where f() is multiple values.
+ *l = copyret((*l)->n, order);
+ } else {
+ orderexprlist(*l, order);
+ }
+}
+
+// Ordercall orders the call expression n.
+// n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+static void
+ordercall(Node *n, Order *order)
+{
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order); // ODDDARG temp
+ ordercallargs(&n->list, order);
+}
+
+// Ordermapassign appends n to order->out, introducing temporaries
+// to make sure that all map assignments have the form m[k] = x,
+// where x is adressable.
+// (Orderexpr has already been called on n, so we know k is addressable.)
+//
+// If n is m[k] = x where x is not addressable, the rewrite is:
+// tmp = x
+// m[k] = tmp
+//
+// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
+// t1 = m
+// t2 = k
+// ...., t3, ... = x
+// t1[t2] = t3
+//
+// The temporaries t1, t2 are needed in case the ... being assigned
+// contain m or k. They are usually unnecessary, but in the unnecessary
+// cases they are also typically registerizable, so not much harm done.
+// And this only applies to the multiple-assignment form.
+// We could do a more precise analysis if needed, like in walk.c.
+//
+// Ordermapassign also inserts these temporaries if needed for
+// calling writebarrierfat with a pointer to n->right.
+static void
+ordermapassign(Node *n, Order *order)
+{
+ Node *m, *a;
+ NodeList *l;
+ NodeList *post;
+
+ switch(n->op) {
+ default:
+ fatal("ordermapassign %O", n->op);
+
+ case OAS:
+ order->out = list(order->out, n);
+ // We call writebarrierfat only for values > 4 pointers long. See walk.c.
+ if((n->left->op == OINDEXMAP || (needwritebarrier(n->left, n->right) && n->left->type->width > 4*widthptr)) && !isaddrokay(n->right)) {
+ m = n->left;
+ n->left = ordertemp(m->type, order, 0);
+ a = nod(OAS, m, n->left);
+ typecheck(&a, Etop);
+ order->out = list(order->out, a);
+ }
+ break;
+
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2MAPR:
+ case OAS2FUNC:
+ post = nil;
+ for(l=n->list; l != nil; l=l->next) {
+ if(l->n->op == OINDEXMAP) {
+ m = l->n;
+ if(!istemp(m->left))
+ m->left = ordercopyexpr(m->left, m->left->type, order, 0);
+ if(!istemp(m->right))
+ m->right = ordercopyexpr(m->right, m->right->type, order, 0);
+ l->n = ordertemp(m->type, order, 0);
+ a = nod(OAS, m, l->n);
+ typecheck(&a, Etop);
+ post = list(post, a);
+ }
+ }
+ order->out = list(order->out, n);
+ order->out = concat(order->out, post);
+ break;
+ }
+}
+
+// Orderstmt orders the statement n, appending to order->out.
+// Temporaries created during the statement are cleaned
+// up using VARKILL instructions as possible.
+static void
+orderstmt(Node *n, Order *order)
+{
+ int lno;
+ NodeList *l, *t, *t1;
+ Node *r, *tmp1, *tmp2, **np;
+ Type *ch;
+
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+
+ orderinit(n, order);
+
+ switch(n->op) {
+ default:
+ fatal("orderstmt %O", n->op);
+
+ case OVARKILL:
+ order->out = list(order->out, n);
+ break;
+
+ case OAS:
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OCLOSE:
+ case OCOPY:
+ case OPRINT:
+ case OPRINTN:
+ case ORECOVER:
+ case ORECV:
+ t = marktemp(order);
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ orderexprlist(n->list, order);
+ orderexprlist(n->rlist, order);
+ switch(n->op) {
+ case OAS:
+ case OAS2:
+ case OAS2DOTTYPE:
+ ordermapassign(n, order);
+ break;
+ default:
+ order->out = list(order->out, n);
+ break;
+ }
+ cleantemp(t, order);
+ break;
+
+ case OASOP:
+ // Special: rewrite l op= r into l = l op r.
+ // This simplies quite a few operations;
+ // most important is that it lets us separate
+ // out map read from map write when l is
+ // a map index expression.
+ t = marktemp(order);
+ orderexpr(&n->left, order);
+ n->left = ordersafeexpr(n->left, order);
+ tmp1 = treecopy(n->left);
+ if(tmp1->op == OINDEXMAP)
+ tmp1->etype = 0; // now an rvalue not an lvalue
+ tmp1 = ordercopyexpr(tmp1, n->left->type, order, 0);
+ n->right = nod(n->etype, tmp1, n->right);
+ typecheck(&n->right, Erv);
+ orderexpr(&n->right, order);
+ n->etype = 0;
+ n->op = OAS;
+ ordermapassign(n, order);
+ cleantemp(t, order);
+ break;
+
+ case OAS2MAPR:
+ // Special: make sure key is addressable,
+ // and make sure OINDEXMAP is not copied out.
+ t = marktemp(order);
+ orderexprlist(n->list, order);
+ r = n->rlist->n;
+ orderexpr(&r->left, order);
+ orderexpr(&r->right, order);
+ // See case OINDEXMAP below.
+ if(r->right->op == OARRAYBYTESTR)
+ r->right->op = OARRAYBYTESTRTMP;
+ orderaddrtemp(&r->right, order);
+ ordermapassign(n, order);
+ cleantemp(t, order);
+ break;
+
+ case OAS2FUNC:
+ // Special: avoid copy of func call n->rlist->n.
+ t = marktemp(order);
+ orderexprlist(n->list, order);
+ ordercall(n->rlist->n, order);
+ ordermapassign(n, order);
+ cleantemp(t, order);
+ break;
+
+ case OAS2RECV:
+ // Special: avoid copy of receive.
+ // Use temporary variables to hold result,
+ // so that chanrecv can take address of temporary.
+ t = marktemp(order);
+ orderexprlist(n->list, order);
+ orderexpr(&n->rlist->n->left, order); // arg to recv
+ ch = n->rlist->n->left->type;
+ tmp1 = ordertemp(ch->type, order, haspointers(ch->type));
+ if(!isblank(n->list->next->n))
+ tmp2 = ordertemp(n->list->next->n->type, order, 0);
+ else
+ tmp2 = ordertemp(types[TBOOL], order, 0);
+ order->out = list(order->out, n);
+ r = nod(OAS, n->list->n, tmp1);
+ typecheck(&r, Etop);
+ ordermapassign(r, order);
+ r = nod(OAS, n->list->next->n, tmp2);
+ typecheck(&r, Etop);
+ ordermapassign(r, order);
+ n->list = list(list1(tmp1), tmp2);
+ cleantemp(t, order);
+ break;
+
+ case OBLOCK:
+ case OEMPTY:
+ // Special: does not save n onto out.
+ orderstmtlist(n->list, order);
+ break;
+
+ case OBREAK:
+ case OCONTINUE:
+ case ODCL:
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OFALL:
+ case OXFALL:
+ case OGOTO:
+ case OLABEL:
+ case ORETJMP:
+ // Special: n->left is not an expression; save as is.
+ order->out = list(order->out, n);
+ break;
+
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ // Special: handle call arguments.
+ t = marktemp(order);
+ ordercall(n, order);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case ODEFER:
+ case OPROC:
+ // Special: order arguments to inner call but not call itself.
+ t = marktemp(order);
+ switch(n->left->op) {
+ case ODELETE:
+ // Delete will take the address of the key.
+ // Copy key into new temp and do not clean it
+ // (it persists beyond the statement).
+ orderexprlist(n->left->list, order);
+ t1 = marktemp(order);
+ np = &n->left->list->next->n; // map key
+ *np = ordercopyexpr(*np, (*np)->type, order, 0);
+ poptemp(t1, order);
+ break;
+ default:
+ ordercall(n->left, order);
+ break;
+ }
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case ODELETE:
+ t = marktemp(order);
+ orderexpr(&n->list->n, order);
+ orderexpr(&n->list->next->n, order);
+ orderaddrtemp(&n->list->next->n, order); // map key
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case OFOR:
+ // Clean temporaries from condition evaluation at
+ // beginning of loop body and after for statement.
+ t = marktemp(order);
+ orderexprinplace(&n->ntest, order);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nbody = concat(l, n->nbody);
+ orderblock(&n->nbody);
+ orderstmtinplace(&n->nincr);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case OIF:
+ // Clean temporaries from condition at
+ // beginning of both branches.
+ t = marktemp(order);
+ orderexprinplace(&n->ntest, order);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nbody = concat(l, n->nbody);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nelse = concat(l, n->nelse);
+ poptemp(t, order);
+ orderblock(&n->nbody);
+ orderblock(&n->nelse);
+ order->out = list(order->out, n);
+ break;
+
+ case OPANIC:
+ // Special: argument will be converted to interface using convT2E
+ // so make sure it is an addressable temporary.
+ t = marktemp(order);
+ orderexpr(&n->left, order);
+ if(!isinter(n->left->type))
+ orderaddrtemp(&n->left, order);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case ORANGE:
+ // n->right is the expression being ranged over.
+ // order it, and then make a copy if we need one.
+ // We almost always do, to ensure that we don't
+ // see any value changes made during the loop.
+ // Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny).
+ // The exception is ranging over an array value (not a slice, not a pointer to array),
+ // which must make a copy to avoid seeing updates made during
+ // the range body. Ranging over an array value is uncommon though.
+ t = marktemp(order);
+ orderexpr(&n->right, order);
+ switch(n->type->etype) {
+ default:
+ fatal("orderstmt range %T", n->type);
+ case TARRAY:
+ if(count(n->list) < 2 || isblank(n->list->next->n)) {
+ // for i := range x will only use x once, to compute len(x).
+ // No need to copy it.
+ break;
+ }
+ // fall through
+ case TCHAN:
+ case TSTRING:
+ // chan, string, slice, array ranges use value multiple times.
+ // make copy.
+ r = n->right;
+ if(r->type->etype == TSTRING && r->type != types[TSTRING]) {
+ r = nod(OCONV, r, N);
+ r->type = types[TSTRING];
+ typecheck(&r, Erv);
+ }
+ n->right = ordercopyexpr(r, r->type, order, 0);
+ break;
+ case TMAP:
+ // copy the map value in case it is a map literal.
+ // TODO(rsc): Make tmp = literal expressions reuse tmp.
+ // For maps tmp is just one word so it hardly matters.
+ r = n->right;
+ n->right = ordercopyexpr(r, r->type, order, 0);
+ // n->alloc is the temp for the iterator.
+ n->alloc = ordertemp(types[TUINT8], order, 1);
+ break;
+ }
+ for(l=n->list; l; l=l->next)
+ orderexprinplace(&l->n, order);
+ orderblock(&n->nbody);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case ORETURN:
+ ordercallargs(&n->list, order);
+ order->out = list(order->out, n);
+ break;
+
+ case OSELECT:
+ // Special: clean case temporaries in each block entry.
+ // Select must enter one of its blocks, so there is no
+ // need for a cleaning at the end.
+ // Doubly special: evaluation order for select is stricter
+ // than ordinary expressions. Even something like p.c
+ // has to be hoisted into a temporary, so that it cannot be
+ // reordered after the channel evaluation for a different
+ // case (if p were nil, then the timing of the fault would
+ // give this away).
+ t = marktemp(order);
+ for(l=n->list; l; l=l->next) {
+ if(l->n->op != OXCASE)
+ fatal("order select case %O", l->n->op);
+ r = l->n->left;
+ setlineno(l->n);
+ // Append any new body prologue to ninit.
+ // The next loop will insert ninit into nbody.
+ if(l->n->ninit != nil)
+ fatal("order select ninit");
+ if(r != nil) {
+ switch(r->op) {
+ default:
+ yyerror("unknown op in select %O", r->op);
+ dump("select case", r);
+ break;
+
+ case OSELRECV:
+ case OSELRECV2:
+ // If this is case x := <-ch or case x, y := <-ch, the case has
+ // the ODCL nodes to declare x and y. We want to delay that
+ // declaration (and possible allocation) until inside the case body.
+ // Delete the ODCL nodes here and recreate them inside the body below.
+ if(r->colas) {
+ t = r->ninit;
+ if(t != nil && t->n->op == ODCL && t->n->left == r->left)
+ t = t->next;
+ if(t != nil && t->n->op == ODCL && t->n->left == r->ntest)
+ t = t->next;
+ if(t == nil)
+ r->ninit = nil;
+ }
+ if(r->ninit != nil) {
+ yyerror("ninit on select recv");
+ dumplist("ninit", r->ninit);
+ }
+ // case x = <-c
+ // case x, ok = <-c
+ // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
+ // r->left == N means 'case <-c'.
+ // c is always evaluated; x and ok are only evaluated when assigned.
+ orderexpr(&r->right->left, order);
+ if(r->right->left->op != ONAME)
+ r->right->left = ordercopyexpr(r->right->left, r->right->left->type, order, 0);
+
+ // Introduce temporary for receive and move actual copy into case body.
+ // avoids problems with target being addressed, as usual.
+ // NOTE: If we wanted to be clever, we could arrange for just one
+ // temporary per distinct type, sharing the temp among all receives
+ // with that temp. Similarly one ok bool could be shared among all
+ // the x,ok receives. Not worth doing until there's a clear need.
+ if(r->left != N && isblank(r->left))
+ r->left = N;
+ if(r->left != N) {
+ // use channel element type for temporary to avoid conversions,
+ // such as in case interfacevalue = <-intchan.
+ // the conversion happens in the OAS instead.
+ tmp1 = r->left;
+ if(r->colas) {
+ tmp2 = nod(ODCL, tmp1, N);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ r->left = ordertemp(r->right->left->type->type, order, haspointers(r->right->left->type->type));
+ tmp2 = nod(OAS, tmp1, r->left);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ if(r->ntest != N && isblank(r->ntest))
+ r->ntest = N;
+ if(r->ntest != N) {
+ tmp1 = r->ntest;
+ if(r->colas) {
+ tmp2 = nod(ODCL, tmp1, N);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ r->ntest = ordertemp(tmp1->type, order, 0);
+ tmp2 = nod(OAS, tmp1, r->ntest);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ orderblock(&l->n->ninit);
+ break;
+
+ case OSEND:
+ if(r->ninit != nil) {
+ yyerror("ninit on select send");
+ dumplist("ninit", r->ninit);
+ }
+ // case c <- x
+ // r->left is c, r->right is x, both are always evaluated.
+ orderexpr(&r->left, order);
+ if(!istemp(r->left))
+ r->left = ordercopyexpr(r->left, r->left->type, order, 0);
+ orderexpr(&r->right, order);
+ if(!istemp(r->right))
+ r->right = ordercopyexpr(r->right, r->right->type, order, 0);
+ break;
+ }
+ }
+ orderblock(&l->n->nbody);
+ }
+ // Now that we have accumulated all the temporaries, clean them.
+ // Also insert any ninit queued during the previous loop.
+ // (The temporary cleaning must follow that ninit work.)
+ for(l=n->list; l; l=l->next) {
+ cleantempnopop(t, order, &l->n->ninit);
+ l->n->nbody = concat(l->n->ninit, l->n->nbody);
+ l->n->ninit = nil;
+ }
+ order->out = list(order->out, n);
+ poptemp(t, order);
+ break;
+
+ case OSEND:
+ // Special: value being sent is passed as a pointer; make it addressable.
+ t = marktemp(order);
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ orderaddrtemp(&n->right, order);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
+ case OSWITCH:
+ // TODO(rsc): Clean temporaries more aggressively.
+ // Note that because walkswitch will rewrite some of the
+ // switch into a binary search, this is not as easy as it looks.
+ // (If we ran that code here we could invoke orderstmt on
+ // the if-else chain instead.)
+ // For now just clean all the temporaries at the end.
+ // In practice that's fine.
+ t = marktemp(order);
+ orderexpr(&n->ntest, order);
+ for(l=n->list; l; l=l->next) {
+ if(l->n->op != OXCASE)
+ fatal("order switch case %O", l->n->op);
+ orderexprlistinplace(l->n->list, order);
+ orderblock(&l->n->nbody);
+ }
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+ }
+
+ lineno = lno;
+}
+
+// Orderexprlist orders the expression list l into order.
+static void
+orderexprlist(NodeList *l, Order *order)
+{
+ for(; l; l=l->next)
+ orderexpr(&l->n, order);
+}
+
+// Orderexprlist orders the expression list l but saves
+// the side effects on the individual expression ninit lists.
+static void
+orderexprlistinplace(NodeList *l, Order *order)
+{
+ for(; l; l=l->next)
+ orderexprinplace(&l->n, order);
+}
+
+// Orderexpr orders a single expression, appending side
+// effects to order->out as needed.
+static void
+orderexpr(Node **np, Order *order)
+{
+ Node *n;
+ NodeList *mark, *l;
+ Type *t;
+ int lno;
+
+ n = *np;
+ if(n == N)
+ return;
+
+ lno = setlineno(n);
+ orderinit(n, order);
+
+ switch(n->op) {
+ default:
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ orderexprlist(n->list, order);
+ orderexprlist(n->rlist, order);
+ break;
+
+ case OADDSTR:
+ // Addition of strings turns into a function call.
+ // Allocate a temporary to hold the strings.
+ // Fewer than 5 strings use direct runtime helpers.
+ orderexprlist(n->list, order);
+ if(count(n->list) > 5) {
+ t = typ(TARRAY);
+ t->bound = count(n->list);
+ t->type = types[TSTRING];
+ n->alloc = ordertemp(t, order, 0);
+ }
+ break;
+
+ case OINDEXMAP:
+ // key must be addressable
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+
+ // For x = m[string(k)] where k is []byte, the allocation of
+ // backing bytes for the string can be avoided by reusing
+ // the []byte backing array. This is a special case that it
+ // would be nice to handle more generally, but because
+ // there are no []byte-keyed maps, this specific case comes
+ // up in important cases in practice. See issue 3512.
+ // Nothing can change the []byte we are not copying before
+ // the map index, because the map access is going to
+ // be forced to happen immediately following this
+ // conversion (by the ordercopyexpr a few lines below).
+ if(n->etype == 0 && n->right->op == OARRAYBYTESTR)
+ n->right->op = OARRAYBYTESTRTMP;
+
+ orderaddrtemp(&n->right, order);
+ if(n->etype == 0) {
+ // use of value (not being assigned);
+ // make copy in temporary.
+ n = ordercopyexpr(n, n->type, order, 0);
+ }
+ break;
+
+ case OCONVIFACE:
+ // concrete type (not interface) argument must be addressable
+ // temporary to pass to runtime.
+ orderexpr(&n->left, order);
+ if(!isinter(n->left->type))
+ orderaddrtemp(&n->left, order);
+ break;
+
+ case OANDAND:
+ case OOROR:
+ mark = marktemp(order);
+ orderexpr(&n->left, order);
+ // Clean temporaries from first branch at beginning of second.
+ // Leave them on the stack so that they can be killed in the outer
+ // context in case the short circuit is taken.
+ l = nil;
+ cleantempnopop(mark, order, &l);
+ n->right->ninit = concat(l, n->right->ninit);
+ orderexprinplace(&n->right, order);
+ break;
+
+ case OAPPEND:
+ case OCALLFUNC:
+ case OCALLINTER:
+ case OCALLMETH:
+ case OCAP:
+ case OCOMPLEX:
+ case OCOPY:
+ case OIMAG:
+ case OLEN:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ case OREAL:
+ case ORECOVER:
+ ordercall(n, order);
+ n = ordercopyexpr(n, n->type, order, 0);
+ break;
+
+ case OCLOSURE:
+ if(n->noescape && n->cvars != nil)
+ n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
+ break;
+
+ case OARRAYLIT:
+ case OCALLPART:
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ orderexprlist(n->list, order);
+ orderexprlist(n->rlist, order);
+ if(n->noescape)
+ n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
+ break;
+
+ case ODDDARG:
+ if(n->noescape) {
+ // The ddd argument does not live beyond the call it is created for.
+ // Allocate a temporary that will be cleaned up when this statement
+ // completes. We could be more aggressive and try to arrange for it
+ // to be cleaned up when the call completes.
+ n->alloc = ordertemp(n->type->type, order, 0);
+ }
+ break;
+
+ case ORECV:
+ orderexpr(&n->left, order);
+ n = ordercopyexpr(n, n->type, order, 1);
+ break;
+
+ case OEQ:
+ case ONE:
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ t = n->left->type;
+ if(t->etype == TSTRUCT || isfixedarray(t)) {
+ // for complex comparisons, we need both args to be
+ // addressable so we can pass them to the runtime.
+ orderaddrtemp(&n->left, order);
+ orderaddrtemp(&n->right, order);
+ }
+ break;
+ }
+
+ lineno = lno;
+
+ *np = n;
+}
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
new file mode 100644
index 0000000..39028e3
--- /dev/null
+++ b/src/cmd/gc/pgen.c
@@ -0,0 +1,539 @@
+// 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.
+
+// "Portable" code generation.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
+#include <u.h>
+#include <libc.h>
+#include "md5.h"
+#include "gg.h"
+#include "opt.h"
+#include "../../runtime/funcdata.h"
+
+static void allocauto(Prog* p);
+static void emitptrargsmap(void);
+
+static Sym*
+makefuncdatasym(char *namefmt, int64 funcdatakind)
+{
+ Node nod;
+ Node *pnod;
+ Sym *sym;
+ static int32 nsym;
+
+ snprint(namebuf, sizeof(namebuf), namefmt, nsym++);
+ sym = lookup(namebuf);
+ pnod = newname(sym);
+ pnod->class = PEXTERN;
+ nodconst(&nod, types[TINT32], funcdatakind);
+ gins(AFUNCDATA, &nod, pnod);
+ return sym;
+}
+
+// gvardef inserts a VARDEF for n into the instruction stream.
+// VARDEF is an annotation for the liveness analysis, marking a place
+// where a complete initialization (definition) of a variable begins.
+// Since the liveness analysis can see initialization of single-word
+// variables quite easy, gvardef is usually only called for multi-word
+// or 'fat' variables, those satisfying isfat(n->type).
+// However, gvardef is also called when a non-fat variable is initialized
+// via a block move; the only time this happens is when you have
+// return f()
+// for a function with multiple return values exactly matching the return
+// types of the current function.
+//
+// A 'VARDEF x' annotation in the instruction stream tells the liveness
+// analysis to behave as though the variable x is being initialized at that
+// point in the instruction stream. The VARDEF must appear before the
+// actual (multi-instruction) initialization, and it must also appear after
+// any uses of the previous value, if any. For example, if compiling:
+//
+// x = x[1:]
+//
+// it is important to generate code like:
+//
+// base, len, cap = pieces of x[1:]
+// VARDEF x
+// x = {base, len, cap}
+//
+// If instead the generated code looked like:
+//
+// VARDEF x
+// base, len, cap = pieces of x[1:]
+// x = {base, len, cap}
+//
+// then the liveness analysis would decide the previous value of x was
+// unnecessary even though it is about to be used by the x[1:] computation.
+// Similarly, if the generated code looked like:
+//
+// base, len, cap = pieces of x[1:]
+// x = {base, len, cap}
+// VARDEF x
+//
+// then the liveness analysis will not preserve the new value of x, because
+// the VARDEF appears to have "overwritten" it.
+//
+// VARDEF is a bit of a kludge to work around the fact that the instruction
+// stream is working on single-word values but the liveness analysis
+// wants to work on individual variables, which might be multi-word
+// aggregates. It might make sense at some point to look into letting
+// the liveness analysis work on single-word values as well, although
+// there are complications around interface values, slices, and strings,
+// all of which cannot be treated as individual words.
+//
+// VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
+// even if its address has been taken. That is, a VARKILL annotation asserts
+// that its argument is certainly dead, for use when the liveness analysis
+// would not otherwise be able to deduce that fact.
+
+static void
+gvardefx(Node *n, int as)
+{
+ if(n == N)
+ fatal("gvardef nil");
+ if(n->op != ONAME) {
+ yyerror("gvardef %#O; %N", n->op, n);
+ return;
+ }
+ switch(n->class) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ gins(as, N, n);
+ }
+}
+
+void
+gvardef(Node *n)
+{
+ gvardefx(n, AVARDEF);
+}
+
+void
+gvarkill(Node *n)
+{
+ gvardefx(n, AVARKILL);
+}
+
+static void
+removevardef(Prog *firstp)
+{
+ Prog *p;
+
+ for(p = firstp; p != P; p = p->link) {
+ while(p->link != P && (p->link->as == AVARDEF || p->link->as == AVARKILL))
+ p->link = p->link->link;
+ if(p->to.type == D_BRANCH)
+ while(p->to.u.branch != P && (p->to.u.branch->as == AVARDEF || p->to.u.branch->as == AVARKILL))
+ p->to.u.branch = p->to.u.branch->link;
+ }
+}
+
+static void
+gcsymdup(Sym *s)
+{
+ LSym *ls;
+ uint64 lo, hi;
+
+ ls = linksym(s);
+ if(ls->nr > 0)
+ fatal("cannot rosymdup %s with relocations", ls->name);
+ MD5 d;
+ md5reset(&d);
+ md5write(&d, ls->p, ls->np);
+ lo = md5sum(&d, &hi);
+ ls->name = smprint("gclocals·%016llux%016llux", lo, hi);
+ ls->dupok = 1;
+}
+
+void
+compile(Node *fn)
+{
+ Plist *pl;
+ Node nod1, *n;
+ Prog *ptxt, *p;
+ int32 lno;
+ Type *t;
+ Iter save;
+ vlong oldstksize;
+ NodeList *l;
+ Sym *gcargs;
+ Sym *gclocals;
+
+ if(newproc == N) {
+ newproc = sysfunc("newproc");
+ deferproc = sysfunc("deferproc");
+ deferreturn = sysfunc("deferreturn");
+ panicindex = sysfunc("panicindex");
+ panicslice = sysfunc("panicslice");
+ throwreturn = sysfunc("throwreturn");
+ }
+
+ lno = setlineno(fn);
+
+ curfn = fn;
+ dowidth(curfn->type);
+
+ if(fn->nbody == nil) {
+ if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) {
+ yyerror("missing function body", fn);
+ goto ret;
+ }
+ if(debug['A'])
+ goto ret;
+ emitptrargsmap();
+ goto ret;
+ }
+
+ saveerrors();
+
+ // set up domain for labels
+ clearlabels();
+
+ 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);
+ }
+ }
+
+ order(curfn);
+ if(nerrors != 0)
+ goto ret;
+
+ hasdefer = 0;
+ walk(curfn);
+ if(nerrors != 0)
+ goto ret;
+ if(flag_race)
+ racewalk(curfn);
+ if(nerrors != 0)
+ goto ret;
+
+ continpc = P;
+ breakpc = P;
+
+ pl = newplist();
+ pl->name = linksym(curfn->nname->sym);
+
+ setlineno(curfn);
+
+ nodconst(&nod1, types[TINT32], 0);
+ ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
+ if(fn->dupok)
+ ptxt->TEXTFLAG |= DUPOK;
+ if(fn->wrapper)
+ ptxt->TEXTFLAG |= WRAPPER;
+ if(fn->needctxt)
+ ptxt->TEXTFLAG |= NEEDCTXT;
+ if(fn->nosplit)
+ ptxt->TEXTFLAG |= NOSPLIT;
+
+ // Clumsy but important.
+ // See test/recover.go for test cases and src/reflect/value.go
+ // for the actual functions being considered.
+ if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) {
+ if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0)
+ ptxt->TEXTFLAG |= WRAPPER;
+ }
+
+ afunclit(&ptxt->from, curfn->nname);
+
+ ginit();
+
+ gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
+ gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
+
+ for(t=curfn->paramfld; t; t=t->down)
+ gtrack(tracksym(t->type));
+
+ for(l=fn->dcl; l; l=l->next) {
+ n = l->n;
+ if(n->op != ONAME) // might be OTYPE or OLITERAL
+ continue;
+ switch(n->class) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ nodconst(&nod1, types[TUINTPTR], l->n->type->width);
+ p = gins(ATYPE, l->n, &nod1);
+ p->from.gotype = linksym(ngotype(l->n));
+ break;
+ }
+ }
+
+ genlist(curfn->enter);
+ genlist(curfn->nbody);
+ gclean();
+ checklabels();
+ if(nerrors != 0)
+ goto ret;
+ if(curfn->endlineno)
+ lineno = curfn->endlineno;
+
+ if(curfn->type->outtuple != 0)
+ ginscall(throwreturn, 0);
+
+ ginit();
+ // TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
+ cgen_ret(nil);
+ if(hasdefer) {
+ // deferreturn pretends to have one uintptr argument.
+ // Reserve space for it so stack scanner is happy.
+ if(maxarg < widthptr)
+ maxarg = widthptr;
+ }
+ gclean();
+ if(nerrors != 0)
+ goto ret;
+
+ pc->as = ARET; // overwrite AEND
+ pc->lineno = lineno;
+
+ fixjmp(ptxt);
+ if(!debug['N'] || debug['R'] || debug['P']) {
+ regopt(ptxt);
+ nilopt(ptxt);
+ }
+ expandchecks(ptxt);
+
+ oldstksize = stksize;
+ allocauto(ptxt);
+
+ if(0)
+ print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
+ USED(oldstksize);
+
+ setlineno(curfn);
+ if((int64)stksize+maxarg > (1ULL<<31)) {
+ yyerror("stack frame too large (>2GB)");
+ goto ret;
+ }
+
+ // Emit garbage collection symbols.
+ liveness(curfn, ptxt, gcargs, gclocals);
+ gcsymdup(gcargs);
+ gcsymdup(gclocals);
+
+ defframe(ptxt);
+
+ if(0)
+ frame(0);
+
+ // Remove leftover instrumentation from the instruction stream.
+ removevardef(ptxt);
+ret:
+ lineno = lno;
+}
+
+static void
+emitptrargsmap(void)
+{
+ int nptr, nbitmap, j, off;
+ vlong xoffset;
+ Bvec *bv;
+ Sym *sym;
+
+ sym = lookup(smprint("%s.args_stackmap", curfn->nname->sym->name));
+
+ nptr = curfn->type->argwid / widthptr;
+ bv = bvalloc(nptr*2);
+ nbitmap = 1;
+ if(curfn->type->outtuple > 0)
+ nbitmap = 2;
+ off = duint32(sym, 0, nbitmap);
+ off = duint32(sym, off, bv->n);
+ if(curfn->type->thistuple > 0) {
+ xoffset = 0;
+ twobitwalktype1(getthisx(curfn->type), &xoffset, bv);
+ }
+ if(curfn->type->intuple > 0) {
+ xoffset = 0;
+ twobitwalktype1(getinargx(curfn->type), &xoffset, bv);
+ }
+ for(j = 0; j < bv->n; j += 32)
+ off = duint32(sym, off, bv->b[j/32]);
+ if(curfn->type->outtuple > 0) {
+ xoffset = 0;
+ twobitwalktype1(getoutargx(curfn->type), &xoffset, bv);
+ for(j = 0; j < bv->n; j += 32)
+ off = duint32(sym, off, bv->b[j/32]);
+ }
+ ggloblsym(sym, off, RODATA);
+ free(bv);
+}
+
+// Sort the list of stack variables. Autos after anything else,
+// within autos, unused after used, within used, things with
+// pointers first, zeroed things first, and then decreasing size.
+// Because autos are laid out in decreasing addresses
+// on the stack, pointers first, zeroed things first and decreasing size
+// really means, in memory, things with pointers needing zeroing at
+// the top of the stack and increasing in size.
+// Non-autos sort on offset.
+static int
+cmpstackvar(Node *a, Node *b)
+{
+ int ap, bp;
+
+ if (a->class != b->class)
+ return (a->class == PAUTO) ? +1 : -1;
+ if (a->class != PAUTO) {
+ if (a->xoffset < b->xoffset)
+ return -1;
+ if (a->xoffset > b->xoffset)
+ return +1;
+ return 0;
+ }
+ if ((a->used == 0) != (b->used == 0))
+ return b->used - a->used;
+
+ ap = haspointers(a->type);
+ bp = haspointers(b->type);
+ if(ap != bp)
+ return bp - ap;
+
+ ap = a->needzero;
+ bp = b->needzero;
+ if(ap != bp)
+ return bp - ap;
+
+ if(a->type->width < b->type->width)
+ return +1;
+ if(a->type->width > b->type->width)
+ return -1;
+
+ return strcmp(a->sym->name, b->sym->name);
+}
+
+// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
+static void
+allocauto(Prog* ptxt)
+{
+ NodeList *ll;
+ Node* n;
+ vlong w;
+
+ stksize = 0;
+ stkptrsize = 0;
+
+ if(curfn->dcl == nil)
+ 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) {
+ // No locals used at all
+ curfn->dcl = nil;
+ fixautoused(ptxt);
+ 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.
+ for(ll = curfn->dcl; ll != nil; ll=ll->next) {
+ n = ll->n;
+ if (n->class != PAUTO || n->op != ONAME)
+ continue;
+
+ dowidth(n->type);
+ w = n->type->width;
+ if(w >= MAXWIDTH || w < 0)
+ fatal("bad width");
+ stksize += w;
+ stksize = rnd(stksize, n->type->align);
+ if(haspointers(n->type))
+ stkptrsize = stksize;
+ if(thechar == '5')
+ stksize = rnd(stksize, widthptr);
+ if(stksize >= (1ULL<<31)) {
+ setlineno(curfn);
+ yyerror("stack frame too large (>2GB)");
+ }
+ n->stkdelta = -stksize - n->xoffset;
+ }
+ stksize = rnd(stksize, widthreg);
+ stkptrsize = rnd(stkptrsize, widthreg);
+
+ 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;
+ }
+}
+
+static void movelargefn(Node*);
+
+void
+movelarge(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(l->n->op == ODCLFUNC)
+ movelargefn(l->n);
+}
+
+static void
+movelargefn(Node *fn)
+{
+ NodeList *l;
+ Node *n;
+
+ for(l=fn->dcl; l != nil; l=l->next) {
+ n = l->n;
+ if(n->class == PAUTO && n->type != T && n->type->width > MaxStackVarSize)
+ addrescapes(n);
+ }
+}
+
+void
+cgen_checknil(Node *n)
+{
+ Node reg;
+
+ if(disable_checknil)
+ return;
+ // Ideally we wouldn't see any integer types here, but we do.
+ if(n->type == T || (!isptr[n->type->etype] && !isint[n->type->etype] && n->type->etype != TUNSAFEPTR)) {
+ dump("checknil", n);
+ fatal("bad checknil");
+ }
+ if((thechar == '5' && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) {
+ regalloc(®, types[tptr], n);
+ cgen(n, ®);
+ gins(ACHECKNIL, ®, N);
+ regfree(®);
+ return;
+ }
+ gins(ACHECKNIL, n, N);
+}
diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c
new file mode 100644
index 0000000..0feb2c7
--- /dev/null
+++ b/src/cmd/gc/plive.c
@@ -0,0 +1,2017 @@
+// Copyright 2013 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.
+
+// Garbage collector liveness bitmap generation.
+
+// The command line flag -live causes this code to print debug information.
+// The levels are:
+//
+// -live (aka -live=1): print liveness lists as code warnings at safe points
+// -live=2: print an assembly listing with liveness annotations
+// -live=3: print information during each computation phase (much chattier)
+//
+// Each level includes the earlier output as well.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+#include "../ld/textflag.h"
+#include "../../runtime/funcdata.h"
+#include "../../runtime/mgc0.h"
+
+enum {
+ UNVISITED = 0,
+ VISITED = 1,
+};
+
+// An ordinary basic block.
+//
+// Instructions are threaded together in a doubly-linked list. To iterate in
+// program order follow the link pointer from the first node and stop after the
+// last node has been visited
+//
+// for(p = bb->first;; p = p->link) {
+// ...
+// if(p == bb->last)
+// break;
+// }
+//
+// To iterate in reverse program order by following the opt pointer from the
+// last node
+//
+// for(p = bb->last; p != nil; p = p->opt) {
+// ...
+// }
+typedef struct BasicBlock BasicBlock;
+struct BasicBlock {
+ // An array of preceding blocks. If the length of this array is 0 the
+ // block is probably the start block of the CFG.
+ Array *pred;
+
+ // An array out succeeding blocks. If the length of this array is zero,
+ // the block probably ends in a return instruction.
+ Array *succ;
+
+ // First instruction in the block. When part of a fully initialized
+ // control flow graph, the opt member will be nil.
+ Prog *first;
+
+ // Last instruction in the basic block.
+ Prog *last;
+
+ // The reverse post order number. This value is initialized to -1 and
+ // will be replaced by a non-negative value when the CFG is constructed.
+ // After CFG construction, if rpo is -1 this block is unreachable.
+ int rpo;
+
+ // State to denote whether the block has been visited during a
+ // traversal.
+ int mark;
+
+ // For use during livenessepilogue.
+ int lastbitmapindex;
+};
+
+// A collection of global state used by liveness analysis.
+typedef struct Liveness Liveness;
+struct Liveness {
+ // A pointer to the node corresponding to the function being analyzed.
+ Node *fn;
+
+ // A linked list of instructions for this function.
+ Prog *ptxt;
+
+ // A list of arguments and local variables in this function.
+ Array *vars;
+
+ // A list of basic blocks that are overlayed on the instruction list.
+ // The blocks are roughly in the same order as the instructions
+ // in the function (first block has TEXT instruction, and so on).
+ Array *cfg;
+
+ // Summary sets of block effects.
+ // The Bvec** is indexed by bb->rpo to yield a single Bvec*.
+ // That bit vector is indexed by variable number (same as lv->vars).
+ //
+ // Computed during livenessprologue using only the content of
+ // individual blocks:
+ //
+ // uevar: upward exposed variables (used before set in block)
+ // varkill: killed variables (set in block)
+ // avarinit: addrtaken variables set or used (proof of initialization)
+ //
+ // Computed during livenesssolve using control flow information:
+ //
+ // livein: variables live at block entry
+ // liveout: variables live at block exit
+ // avarinitany: addrtaken variables possibly initialized at block exit
+ // (initialized in block or at exit from any predecessor block)
+ // avarinitall: addrtaken variables certainly initialized at block exit
+ // (initialized in block or at exit from all predecessor blocks)
+ Bvec **uevar;
+ Bvec **varkill;
+ Bvec **livein;
+ Bvec **liveout;
+ Bvec **avarinit;
+ Bvec **avarinitany;
+ Bvec **avarinitall;
+
+ // An array with a bit vector for each safe point tracking live pointers
+ // in the arguments and locals area, indexed by bb->rpo.
+ Array *argslivepointers;
+ Array *livepointers;
+};
+
+static void*
+xmalloc(uintptr size)
+{
+ void *result;
+
+ result = malloc(size);
+ if(result == nil)
+ fatal("malloc failed");
+ return result;
+}
+
+// Constructs a new basic block containing a single instruction.
+static BasicBlock*
+newblock(Prog *prog)
+{
+ BasicBlock *result;
+
+ if(prog == nil)
+ fatal("newblock: prog cannot be nil");
+ result = xmalloc(sizeof(*result));
+ result->rpo = -1;
+ result->mark = UNVISITED;
+ result->first = prog;
+ result->last = prog;
+ result->pred = arraynew(2, sizeof(BasicBlock*));
+ result->succ = arraynew(2, sizeof(BasicBlock*));
+ return result;
+}
+
+// Frees a basic block and all of its leaf data structures.
+static void
+freeblock(BasicBlock *bb)
+{
+ if(bb == nil)
+ fatal("freeblock: cannot free nil");
+ arrayfree(bb->pred);
+ arrayfree(bb->succ);
+ free(bb);
+}
+
+// Adds an edge between two basic blocks by making from a predecessor of to and
+// to a successor of from.
+static void
+addedge(BasicBlock *from, BasicBlock *to)
+{
+ if(from == nil)
+ fatal("addedge: from is nil");
+ if(to == nil)
+ fatal("addedge: to is nil");
+ arrayadd(from->succ, &to);
+ arrayadd(to->pred, &from);
+}
+
+// Inserts prev before curr in the instruction
+// stream. Any control flow, such as branches or fall throughs, that target the
+// existing instruction are adjusted to target the new instruction.
+static void
+splicebefore(Liveness *lv, BasicBlock *bb, Prog *prev, Prog *curr)
+{
+ Prog *next, tmp;
+
+ USED(lv);
+
+ // There may be other instructions pointing at curr,
+ // and we want them to now point at prev. Instead of
+ // trying to find all such instructions, swap the contents
+ // so that the problem becomes inserting next after curr.
+ // The "opt" field is the backward link in the linked list.
+
+ // Overwrite curr's data with prev, but keep the list links.
+ tmp = *curr;
+ *curr = *prev;
+ curr->opt = tmp.opt;
+ curr->link = tmp.link;
+
+ // Overwrite prev (now next) with curr's old data.
+ next = prev;
+ *next = tmp;
+ next->opt = nil;
+ next->link = nil;
+
+ // Now insert next after curr.
+ next->link = curr->link;
+ next->opt = curr;
+ curr->link = next;
+ if(next->link && next->link->opt == curr)
+ next->link->opt = next;
+
+ if(bb->last == curr)
+ bb->last = next;
+}
+
+// A pretty printer for basic blocks.
+static void
+printblock(BasicBlock *bb)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+ Prog *prog;
+ int i;
+
+ print("basic block %d\n", bb->rpo);
+ print("\tpred:");
+ for(i = 0; i < arraylength(bb->pred); i++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, i);
+ print(" %d", pred->rpo);
+ }
+ print("\n");
+ print("\tsucc:");
+ for(i = 0; i < arraylength(bb->succ); i++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, i);
+ print(" %d", succ->rpo);
+ }
+ print("\n");
+ print("\tprog:\n");
+ for(prog = bb->first;; prog=prog->link) {
+ print("\t\t%P\n", prog);
+ if(prog == bb->last)
+ break;
+ }
+}
+
+
+// Iterates over a basic block applying a callback to each instruction. There
+// are two criteria for termination. If the end of basic block is reached a
+// value of zero is returned. If the callback returns a non-zero value, the
+// iteration is stopped and the value of the callback is returned.
+static int
+blockany(BasicBlock *bb, int (*callback)(Prog*))
+{
+ Prog *p;
+ int result;
+
+ for(p = bb->last; p != nil; p = p->opt) {
+ result = (*callback)(p);
+ if(result != 0)
+ return result;
+ }
+ return 0;
+}
+
+// Collects and returns and array of Node*s for functions arguments and local
+// variables.
+static Array*
+getvariables(Node *fn)
+{
+ Array *result;
+ NodeList *ll;
+
+ result = arraynew(0, sizeof(Node*));
+ for(ll = fn->dcl; ll != nil; ll = ll->next) {
+ if(ll->n->op == ONAME) {
+ // In order for GODEBUG=gcdead=1 to work, each bitmap needs
+ // to contain information about all variables covered by the bitmap.
+ // For local variables, the bitmap only covers the stkptrsize
+ // bytes in the frame where variables containing pointers live.
+ // For arguments and results, the bitmap covers all variables,
+ // so we must include all the variables, even the ones without
+ // pointers.
+ //
+ // The Node.opt field is available for use by optimization passes.
+ // We use it to hold the index of the node in the variables array, plus 1
+ // (so that 0 means the Node is not in the variables array).
+ // Each pass should clear opt when done, but you never know,
+ // so clear them all ourselves too.
+ // The Node.curfn field is supposed to be set to the current function
+ // already, but for some compiler-introduced names it seems not to be,
+ // so fix that here.
+ // Later, when we want to find the index of a node in the variables list,
+ // we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
+ // is the index in the variables list.
+ ll->n->opt = nil;
+ ll->n->curfn = curfn;
+ switch(ll->n->class) {
+ case PAUTO:
+ if(haspointers(ll->n->type)) {
+ ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
+ arrayadd(result, &ll->n);
+ }
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
+ arrayadd(result, &ll->n);
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+// A pretty printer for control flow graphs. Takes an array of BasicBlock*s.
+static void
+printcfg(Array *cfg)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ printblock(bb);
+ }
+}
+
+// Assigns a reverse post order number to each connected basic block using the
+// standard algorithm. Unconnected blocks will not be affected.
+static void
+reversepostorder(BasicBlock *root, int32 *rpo)
+{
+ BasicBlock *bb;
+ int i;
+
+ root->mark = VISITED;
+ for(i = 0; i < arraylength(root->succ); i++) {
+ bb = *(BasicBlock**)arrayget(root->succ, i);
+ if(bb->mark == UNVISITED)
+ reversepostorder(bb, rpo);
+ }
+ *rpo -= 1;
+ root->rpo = *rpo;
+}
+
+// Comparison predicate used for sorting basic blocks by their rpo in ascending
+// order.
+static int
+blockrpocmp(const void *p1, const void *p2)
+{
+ BasicBlock *bb1;
+ BasicBlock *bb2;
+
+ bb1 = *(BasicBlock**)p1;
+ bb2 = *(BasicBlock**)p2;
+ if(bb1->rpo < bb2->rpo)
+ return -1;
+ if(bb1->rpo > bb2->rpo)
+ return 1;
+ return 0;
+}
+
+// A pattern matcher for call instructions. Returns true when the instruction
+// is a call to a specific package qualified function name.
+static int
+iscall(Prog *prog, LSym *name)
+{
+ if(prog == nil)
+ fatal("iscall: prog is nil");
+ if(name == nil)
+ fatal("iscall: function name is nil");
+ if(prog->as != ACALL)
+ return 0;
+ return name == prog->to.sym;
+}
+
+// Returns true for instructions that call a runtime function implementing a
+// select communication clause.
+static int
+isselectcommcasecall(Prog *prog)
+{
+ static LSym* names[5];
+ int32 i;
+
+ if(names[0] == nil) {
+ names[0] = linksym(pkglookup("selectsend", runtimepkg));
+ names[1] = linksym(pkglookup("selectrecv", runtimepkg));
+ names[2] = linksym(pkglookup("selectrecv2", runtimepkg));
+ names[3] = linksym(pkglookup("selectdefault", runtimepkg));
+ }
+ for(i = 0; names[i] != nil; i++)
+ if(iscall(prog, names[i]))
+ return 1;
+ return 0;
+}
+
+// Returns true for call instructions that target runtime·newselect.
+static int
+isnewselect(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("newselect", runtimepkg));
+ return iscall(prog, sym);
+}
+
+// Returns true for call instructions that target runtime·selectgo.
+static int
+isselectgocall(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("selectgo", runtimepkg));
+ return iscall(prog, sym);
+}
+
+static int
+isdeferreturn(Prog *prog)
+{
+ static LSym *sym;
+
+ if(sym == nil)
+ sym = linksym(pkglookup("deferreturn", runtimepkg));
+ return iscall(prog, sym);
+}
+
+// Walk backwards from a runtime·selectgo call up to its immediately dominating
+// runtime·newselect call. Any successor nodes of communication clause nodes
+// are implicit successors of the runtime·selectgo call node. The goal of this
+// analysis is to add these missing edges to complete the control flow graph.
+static void
+addselectgosucc(BasicBlock *selectgo)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+
+ pred = selectgo;
+ for(;;) {
+ if(arraylength(pred->pred) == 0)
+ fatal("selectgo does not have a newselect");
+ pred = *(BasicBlock**)arrayget(pred->pred, 0);
+ if(blockany(pred, isselectcommcasecall)) {
+ // A select comm case block should have exactly one
+ // successor.
+ if(arraylength(pred->succ) != 1)
+ fatal("select comm case has too many successors");
+ succ = *(BasicBlock**)arrayget(pred->succ, 0);
+ // Its successor should have exactly two successors.
+ // The drop through should flow to the selectgo block
+ // and the branch should lead to the select case
+ // statements block.
+ if(arraylength(succ->succ) != 2)
+ fatal("select comm case successor has too many successors");
+ // Add the block as a successor of the selectgo block.
+ addedge(selectgo, succ);
+ }
+ if(blockany(pred, isnewselect)) {
+ // Reached the matching newselect.
+ break;
+ }
+ }
+}
+
+// The entry point for the missing selectgo control flow algorithm. Takes an
+// array of BasicBlock*s containing selectgo calls.
+static void
+fixselectgo(Array *selectgo)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(selectgo); i++) {
+ bb = *(BasicBlock**)arrayget(selectgo, i);
+ addselectgosucc(bb);
+ }
+}
+
+// Constructs a control flow graph from a sequence of instructions. This
+// procedure is complicated by various sources of implicit control flow that are
+// not accounted for using the standard cfg construction algorithm. Returns an
+// array of BasicBlock*s in control flow graph form (basic blocks ordered by
+// their RPO number).
+static Array*
+newcfg(Prog *firstp)
+{
+ Prog *p;
+ Prog *prev;
+ BasicBlock *bb;
+ Array *cfg;
+ Array *selectgo;
+ int32 i;
+ int32 rpo;
+
+ // Reset the opt field of each prog to nil. In the first and second
+ // passes, instructions that are labels temporarily use the opt field to
+ // point to their basic block. In the third pass, the opt field reset
+ // to point to the predecessor of an instruction in its basic block.
+ for(p = firstp; p != P; p = p->link)
+ p->opt = nil;
+
+ // Allocate an array to remember where we have seen selectgo calls.
+ // These blocks will be revisited to add successor control flow edges.
+ selectgo = arraynew(0, sizeof(BasicBlock*));
+
+ // Loop through all instructions identifying branch targets
+ // and fall-throughs and allocate basic blocks.
+ cfg = arraynew(0, sizeof(BasicBlock*));
+ bb = newblock(firstp);
+ arrayadd(cfg, &bb);
+ for(p = firstp; p != P; p = p->link) {
+ if(p->to.type == D_BRANCH) {
+ if(p->to.u.branch == nil)
+ fatal("prog branch to nil");
+ if(p->to.u.branch->opt == nil) {
+ p->to.u.branch->opt = newblock(p->to.u.branch);
+ arrayadd(cfg, &p->to.u.branch->opt);
+ }
+ if(p->as != AJMP && p->link != nil && p->link->opt == nil) {
+ p->link->opt = newblock(p->link);
+ arrayadd(cfg, &p->link->opt);
+ }
+ } else if(isselectcommcasecall(p) || isselectgocall(p)) {
+ // Accommodate implicit selectgo control flow.
+ if(p->link->opt == nil) {
+ p->link->opt = newblock(p->link);
+ arrayadd(cfg, &p->link->opt);
+ }
+ }
+ }
+
+ // Loop through all basic blocks maximally growing the list of
+ // contained instructions until a label is reached. Add edges
+ // for branches and fall-through instructions.
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ for(p = bb->last; p != nil; p = p->link) {
+ if(p->opt != nil && p != bb->last)
+ break;
+ bb->last = p;
+
+ // Stop before an unreachable RET, to avoid creating
+ // unreachable control flow nodes.
+ if(p->link != nil && p->link->as == ARET && p->link->mode == 1)
+ break;
+
+ // Collect basic blocks with selectgo calls.
+ if(isselectgocall(p))
+ arrayadd(selectgo, &bb);
+ }
+ if(bb->last->to.type == D_BRANCH)
+ addedge(bb, bb->last->to.u.branch->opt);
+ if(bb->last->link != nil) {
+ // Add a fall-through when the instruction is
+ // not an unconditional control transfer.
+ switch(bb->last->as) {
+ case AJMP:
+ case ARET:
+ case AUNDEF:
+ break;
+ default:
+ addedge(bb, bb->last->link->opt);
+ }
+ }
+ }
+
+ // Add back links so the instructions in a basic block can be traversed
+ // backward. This is the final state of the instruction opt field.
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ p = bb->first;
+ prev = nil;
+ for(;;) {
+ p->opt = prev;
+ if(p == bb->last)
+ break;
+ prev = p;
+ p = p->link;
+ }
+ }
+
+ // Add missing successor edges to the selectgo blocks.
+ if(arraylength(selectgo))
+ fixselectgo(selectgo);
+ arrayfree(selectgo);
+
+ // Find a depth-first order and assign a depth-first number to
+ // all basic blocks.
+ for(i = 0; i < arraylength(cfg); i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ bb->mark = UNVISITED;
+ }
+ bb = *(BasicBlock**)arrayget(cfg, 0);
+ rpo = arraylength(cfg);
+ reversepostorder(bb, &rpo);
+
+ // Sort the basic blocks by their depth first number. The
+ // array is now a depth-first spanning tree with the first
+ // node being the root.
+ arraysort(cfg, blockrpocmp);
+ bb = *(BasicBlock**)arrayget(cfg, 0);
+
+ // Unreachable control flow nodes are indicated by a -1 in the rpo
+ // field. If we see these nodes something must have gone wrong in an
+ // upstream compilation phase.
+ if(bb->rpo == -1) {
+ print("newcfg: unreachable basic block for %P\n", bb->last);
+ printcfg(cfg);
+ fatal("newcfg: invalid control flow graph");
+ }
+
+ return cfg;
+}
+
+// Frees a control flow graph (an array of BasicBlock*s) and all of its leaf
+// data structures.
+static void
+freecfg(Array *cfg)
+{
+ BasicBlock *bb;
+ BasicBlock *bb0;
+ Prog *p;
+ int32 i;
+ int32 len;
+
+ len = arraylength(cfg);
+ if(len > 0) {
+ bb0 = *(BasicBlock**)arrayget(cfg, 0);
+ for(p = bb0->first; p != P; p = p->link) {
+ p->opt = nil;
+ }
+ for(i = 0; i < len; i++) {
+ bb = *(BasicBlock**)arrayget(cfg, i);
+ freeblock(bb);
+ }
+ }
+ arrayfree(cfg);
+}
+
+// Returns true if the node names a variable that is otherwise uninteresting to
+// the liveness computation.
+static int
+isfunny(Node *node)
+{
+ char *names[] = { ".fp", ".args", nil };
+ int i;
+
+ if(node->sym != nil && node->sym->name != nil)
+ for(i = 0; names[i] != nil; i++)
+ if(strcmp(node->sym->name, names[i]) == 0)
+ return 1;
+ return 0;
+}
+
+// Computes the effects of an instruction on a set of
+// variables. The vars argument is an array of Node*s.
+//
+// The output vectors give bits for variables:
+// uevar - used by this instruction
+// varkill - killed by this instruction
+// for variables without address taken, means variable was set
+// for variables with address taken, means variable was marked dead
+// avarinit - initialized or referred to by this instruction,
+// only for variables with address taken but not escaping to heap
+//
+// The avarinit output serves as a signal that the data has been
+// initialized, because any use of a variable must come after its
+// initialization.
+static void
+progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+ ProgInfo info;
+ Addr *from;
+ Addr *to;
+ Node *node;
+ int32 i;
+ int32 pos;
+
+ bvresetall(uevar);
+ bvresetall(varkill);
+ bvresetall(avarinit);
+
+ proginfo(&info, prog);
+ if(prog->as == ARET) {
+ // Return instructions implicitly read all the arguments. For
+ // the sake of correctness, out arguments must be read. For the
+ // sake of backtrace quality, we read in arguments as well.
+ //
+ // A return instruction with a p->to is a tail return, which brings
+ // the stack pointer back up (if it ever went down) and then jumps
+ // to a new function entirely. That form of instruction must read
+ // all the parameters for correctness, and similarly it must not
+ // read the out arguments - they won't be set until the new
+ // function runs.
+ for(i = 0; i < arraylength(vars); i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class & ~PHEAP) {
+ case PPARAM:
+ bvset(uevar, i);
+ break;
+ case PPARAMOUT:
+ // If the result had its address taken, it is being tracked
+ // by the avarinit code, which does not use uevar.
+ // If we added it to uevar too, we'd not see any kill
+ // and decide that the varible was live entry, which it is not.
+ // So only use uevar in the non-addrtaken case.
+ // The p->to.type == D_NONE limits the bvset to
+ // non-tail-call return instructions; see note above
+ // the for loop for details.
+ if(!node->addrtaken && prog->to.type == D_NONE)
+ bvset(uevar, i);
+ break;
+ }
+ }
+ return;
+ }
+ if(prog->as == ATEXT) {
+ // A text instruction marks the entry point to a function and
+ // the definition point of all in arguments.
+ for(i = 0; i < arraylength(vars); i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class & ~PHEAP) {
+ case PPARAM:
+ if(node->addrtaken)
+ bvset(avarinit, i);
+ bvset(varkill, i);
+ break;
+ }
+ }
+ return;
+ }
+ if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
+ from = &prog->from;
+ if (from->node != nil && from->sym != nil && from->node->curfn == curfn) {
+ switch(from->node->class & ~PHEAP) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ pos = (int)(uintptr)from->node->opt - 1; // index in vars
+ if(pos == -1)
+ goto Next;
+ if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
+ fatal("bad bookkeeping in liveness %N %d", from->node, pos);
+ if(from->node->addrtaken) {
+ bvset(avarinit, pos);
+ } else {
+ if(info.flags & (LeftRead | LeftAddr))
+ bvset(uevar, pos);
+ if(info.flags & LeftWrite)
+ if(from->node != nil && !isfat(from->node->type))
+ bvset(varkill, pos);
+ }
+ }
+ }
+ }
+Next:
+ if(info.flags & (RightRead | RightWrite | RightAddr)) {
+ to = &prog->to;
+ if (to->node != nil && to->sym != nil && to->node->curfn == curfn) {
+ switch(to->node->class & ~PHEAP) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ pos = (int)(uintptr)to->node->opt - 1; // index in vars
+ if(pos == -1)
+ goto Next1;
+ if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
+ fatal("bad bookkeeping in liveness %N %d", to->node, pos);
+ if(to->node->addrtaken) {
+ if(prog->as != AVARKILL)
+ bvset(avarinit, pos);
+ if(prog->as == AVARDEF || prog->as == AVARKILL)
+ bvset(varkill, pos);
+ } else {
+ // RightRead is a read, obviously.
+ // RightAddr by itself is also implicitly a read.
+ //
+ // RightAddr|RightWrite means that the address is being taken
+ // but only so that the instruction can write to the value.
+ // It is not a read. It is equivalent to RightWrite except that
+ // having the RightAddr bit set keeps the registerizer from
+ // trying to substitute a register for the memory location.
+ if((info.flags & RightRead) || (info.flags & (RightAddr|RightWrite)) == RightAddr)
+ bvset(uevar, pos);
+ if(info.flags & RightWrite)
+ if(to->node != nil && (!isfat(to->node->type) || prog->as == AVARDEF))
+ bvset(varkill, pos);
+ }
+ }
+ }
+ }
+Next1:;
+}
+
+// Constructs a new liveness structure used to hold the global state of the
+// liveness computation. The cfg argument is an array of BasicBlock*s and the
+// vars argument is an array of Node*s.
+static Liveness*
+newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
+{
+ Liveness *result;
+ int32 i;
+ int32 nblocks;
+ int32 nvars;
+
+ result = xmalloc(sizeof(*result));
+ result->fn = fn;
+ result->ptxt = ptxt;
+ result->cfg = cfg;
+ result->vars = vars;
+
+ nblocks = arraylength(cfg);
+ result->uevar = xmalloc(sizeof(Bvec*) * nblocks);
+ result->varkill = xmalloc(sizeof(Bvec*) * nblocks);
+ result->livein = xmalloc(sizeof(Bvec*) * nblocks);
+ result->liveout = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinit = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinitany = xmalloc(sizeof(Bvec*) * nblocks);
+ result->avarinitall = xmalloc(sizeof(Bvec*) * nblocks);
+
+ nvars = arraylength(vars);
+ for(i = 0; i < nblocks; i++) {
+ result->uevar[i] = bvalloc(nvars);
+ result->varkill[i] = bvalloc(nvars);
+ result->livein[i] = bvalloc(nvars);
+ result->liveout[i] = bvalloc(nvars);
+ result->avarinit[i] = bvalloc(nvars);
+ result->avarinitany[i] = bvalloc(nvars);
+ result->avarinitall[i] = bvalloc(nvars);
+ }
+
+ result->livepointers = arraynew(0, sizeof(Bvec*));
+ result->argslivepointers = arraynew(0, sizeof(Bvec*));
+ return result;
+}
+
+// Frees the liveness structure and all of its leaf data structures.
+static void
+freeliveness(Liveness *lv)
+{
+ int32 i;
+
+ if(lv == nil)
+ fatal("freeliveness: cannot free nil");
+
+ for(i = 0; i < arraylength(lv->livepointers); i++)
+ free(*(Bvec**)arrayget(lv->livepointers, i));
+ arrayfree(lv->livepointers);
+
+ for(i = 0; i < arraylength(lv->argslivepointers); i++)
+ free(*(Bvec**)arrayget(lv->argslivepointers, i));
+ arrayfree(lv->argslivepointers);
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ free(lv->uevar[i]);
+ free(lv->varkill[i]);
+ free(lv->livein[i]);
+ free(lv->liveout[i]);
+ free(lv->avarinit[i]);
+ free(lv->avarinitany[i]);
+ free(lv->avarinitall[i]);
+ }
+
+ free(lv->uevar);
+ free(lv->varkill);
+ free(lv->livein);
+ free(lv->liveout);
+ free(lv->avarinit);
+ free(lv->avarinitany);
+ free(lv->avarinitall);
+
+ free(lv);
+}
+
+static void
+printeffects(Prog *p, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+ print("effects of %P", p);
+ print("\nuevar: ");
+ bvprint(uevar);
+ print("\nvarkill: ");
+ bvprint(varkill);
+ print("\navarinit: ");
+ bvprint(avarinit);
+ print("\n");
+}
+
+// Pretty print a variable node. Uses Pascal like conventions for pointers and
+// addresses to avoid confusing the C like conventions used in the node variable
+// names.
+static void
+printnode(Node *node)
+{
+ char *p;
+ char *a;
+
+ p = haspointers(node->type) ? "^" : "";
+ a = node->addrtaken ? "@" : "";
+ print(" %N%s%s", node, p, a);
+}
+
+// Pretty print a list of variables. The vars argument is an array of Node*s.
+static void
+printvars(char *name, Bvec *bv, Array *vars)
+{
+ int32 i;
+
+ print("%s:", name);
+ for(i = 0; i < arraylength(vars); i++)
+ if(bvget(bv, i))
+ printnode(*(Node**)arrayget(vars, i));
+ print("\n");
+}
+
+// Prints a basic block annotated with the information computed by liveness
+// analysis.
+static void
+livenessprintblock(Liveness *lv, BasicBlock *bb)
+{
+ BasicBlock *pred;
+ BasicBlock *succ;
+ Prog *prog;
+ Bvec *live;
+ int i;
+ int32 pos;
+
+ print("basic block %d\n", bb->rpo);
+
+ print("\tpred:");
+ for(i = 0; i < arraylength(bb->pred); i++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, i);
+ print(" %d", pred->rpo);
+ }
+ print("\n");
+
+ print("\tsucc:");
+ for(i = 0; i < arraylength(bb->succ); i++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, i);
+ print(" %d", succ->rpo);
+ }
+ print("\n");
+
+ printvars("\tuevar", lv->uevar[bb->rpo], lv->vars);
+ printvars("\tvarkill", lv->varkill[bb->rpo], lv->vars);
+ printvars("\tlivein", lv->livein[bb->rpo], lv->vars);
+ printvars("\tliveout", lv->liveout[bb->rpo], lv->vars);
+ printvars("\tavarinit", lv->avarinit[bb->rpo], lv->vars);
+ printvars("\tavarinitany", lv->avarinitany[bb->rpo], lv->vars);
+ printvars("\tavarinitall", lv->avarinitall[bb->rpo], lv->vars);
+
+ print("\tprog:\n");
+ for(prog = bb->first;; prog = prog->link) {
+ print("\t\t%P", prog);
+ if(prog->as == APCDATA && prog->from.offset == PCDATA_StackMapIndex) {
+ pos = prog->to.offset;
+ live = *(Bvec**)arrayget(lv->livepointers, pos);
+ print(" ");
+ bvprint(live);
+ }
+ print("\n");
+ if(prog == bb->last)
+ break;
+ }
+}
+
+// Prints a control flow graph annotated with any information computed by
+// liveness analysis.
+static void
+livenessprintcfg(Liveness *lv)
+{
+ BasicBlock *bb;
+ int32 i;
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ livenessprintblock(lv, bb);
+ }
+}
+
+static void
+checkauto(Node *fn, Prog *p, Node *n)
+{
+ NodeList *l;
+
+ for(l = fn->dcl; l != nil; l = l->next)
+ if(l->n->op == ONAME && l->n->class == PAUTO && l->n == n)
+ return;
+
+ print("checkauto %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+ for(l = fn->dcl; l != nil; l = l->next)
+ print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+ yyerror("checkauto: invariant lost");
+}
+
+static void
+checkparam(Node *fn, Prog *p, Node *n)
+{
+ NodeList *l;
+ Node *a;
+ int class;
+
+ if(isfunny(n))
+ return;
+ for(l = fn->dcl; l != nil; l = l->next) {
+ a = l->n;
+ class = a->class & ~PHEAP;
+ if(a->op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n)
+ return;
+ }
+
+ print("checkparam %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+ for(l = fn->dcl; l != nil; l = l->next)
+ print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+ yyerror("checkparam: invariant lost");
+}
+
+static void
+checkprog(Node *fn, Prog *p)
+{
+ if(p->from.type == D_AUTO)
+ checkauto(fn, p, p->from.node);
+ if(p->from.type == D_PARAM)
+ checkparam(fn, p, p->from.node);
+ if(p->to.type == D_AUTO)
+ checkauto(fn, p, p->to.node);
+ if(p->to.type == D_PARAM)
+ checkparam(fn, p, p->to.node);
+}
+
+// Check instruction invariants. We assume that the nodes corresponding to the
+// sources and destinations of memory operations will be declared in the
+// function. This is not strictly true, as is the case for the so-called funny
+// nodes and there are special cases to skip over that stuff. The analysis will
+// fail if this invariant blindly changes.
+static void
+checkptxt(Node *fn, Prog *firstp)
+{
+ Prog *p;
+
+ if(debuglive == 0)
+ return;
+
+ for(p = firstp; p != P; p = p->link) {
+ if(0)
+ print("analyzing '%P'\n", p);
+ switch(p->as) {
+ case ADATA:
+ case AGLOBL:
+ case ANAME:
+ case ASIGNAME:
+ case ATYPE:
+ continue;
+ }
+ checkprog(fn, p);
+ }
+}
+
+// NOTE: The bitmap for a specific type t should be cached in t after the first run
+// and then simply copied into bv at the correct offset on future calls with
+// the same type t. On https://rsc.googlecode.com/hg/testdata/slow.go, twobitwalktype1
+// accounts for 40% of the 6g execution time.
+void
+twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
+{
+ vlong fieldoffset;
+ vlong i;
+ vlong o;
+ Type *t1;
+
+ if(t->align > 0 && (*xoffset & (t->align - 1)) != 0)
+ fatal("twobitwalktype1: invalid initial alignment, %T", t);
+
+ switch(t->etype) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TFLOAT32:
+ case TFLOAT64:
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ for(i = 0; i < t->width; i++) {
+ bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar
+ }
+ *xoffset += t->width;
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TFUNC:
+ case TCHAN:
+ case TMAP:
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr
+ *xoffset += t->width;
+ break;
+
+ case TSTRING:
+ // struct { byte *str; intgo len; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+ *xoffset += t->width;
+ break;
+
+ case TINTER:
+ // struct { Itab *tab; union { void *ptr, uintptr val } data; }
+ // or, when isnilinter(t)==true:
+ // struct { Type *type; union { void *ptr, uintptr val } data; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid alignment, %T", t);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); // 3 = multiword
+ // next word contains 2 = Iface, 3 = Eface
+ if(isnilinter(t)) {
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2);
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+ } else {
+ bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+ }
+ *xoffset += t->width;
+ break;
+
+ case TARRAY:
+ // The value of t->bound is -1 for slices types and >0 for
+ // for fixed array types. All other values are invalid.
+ if(t->bound < -1)
+ fatal("twobitwalktype1: invalid bound, %T", t);
+ if(isslice(t)) {
+ // struct { byte *array; uintgo len; uintgo cap; }
+ if((*xoffset & (widthptr-1)) != 0)
+ fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
+ bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+ *xoffset += t->width;
+ } else
+ for(i = 0; i < t->bound; i++)
+ twobitwalktype1(t->type, xoffset, bv);
+ break;
+
+ case TSTRUCT:
+ o = 0;
+ for(t1 = t->type; t1 != T; t1 = t1->down) {
+ fieldoffset = t1->width;
+ *xoffset += fieldoffset - o;
+ twobitwalktype1(t1->type, xoffset, bv);
+ o = fieldoffset + t1->type->width;
+ }
+ *xoffset += t->width - o;
+ break;
+
+ default:
+ fatal("twobitwalktype1: unexpected type, %T", t);
+ }
+}
+
+// Returns the number of words of local variables.
+static int32
+localswords(void)
+{
+ return stkptrsize / widthptr;
+}
+
+// Returns the number of words of in and out arguments.
+static int32
+argswords(void)
+{
+ return curfn->type->argwid / widthptr;
+}
+
+// Generates live pointer value maps for arguments and local variables. The
+// this argument and the in arguments are always assumed live. The vars
+// argument is an array of Node*s.
+static void
+twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec *locals)
+{
+ Node *node;
+ Type *thisargtype;
+ Type *inargtype;
+ vlong xoffset;
+ int32 i;
+
+ for(i = 0; (i = bvnext(liveout, i)) >= 0; i++) {
+ node = *(Node**)arrayget(vars, i);
+ switch(node->class) {
+ case PAUTO:
+ xoffset = node->xoffset + stkptrsize;
+ twobitwalktype1(node->type, &xoffset, locals);
+ break;
+ case PPARAM:
+ case PPARAMOUT:
+ xoffset = node->xoffset;
+ twobitwalktype1(node->type, &xoffset, args);
+ break;
+ }
+ }
+
+ // The node list only contains declared names.
+ // If the receiver or arguments are unnamed, they will be omitted
+ // from the list above. Preserve those values - even though they are unused -
+ // in order to keep their addresses live for use in stack traces.
+ thisargtype = getthisx(lv->fn->type);
+ if(thisargtype != nil) {
+ xoffset = 0;
+ twobitwalktype1(thisargtype, &xoffset, args);
+ }
+ inargtype = getinargx(lv->fn->type);
+ if(inargtype != nil) {
+ xoffset = 0;
+ twobitwalktype1(inargtype, &xoffset, args);
+ }
+}
+
+// Construct a disembodied instruction.
+static Prog*
+unlinkedprog(int as)
+{
+ Prog *p;
+
+ p = mal(sizeof(*p));
+ clearp(p);
+ p->as = as;
+ return p;
+}
+
+// Construct a new PCDATA instruction associated with and for the purposes of
+// covering an existing instruction.
+static Prog*
+newpcdataprog(Prog *prog, int32 index)
+{
+ Node from, to;
+ Prog *pcdata;
+
+ nodconst(&from, types[TINT32], PCDATA_StackMapIndex);
+ nodconst(&to, types[TINT32], index);
+ pcdata = unlinkedprog(APCDATA);
+ pcdata->lineno = prog->lineno;
+ naddr(&from, &pcdata->from, 0);
+ naddr(&to, &pcdata->to, 0);
+ return pcdata;
+}
+
+// Returns true for instructions that are safe points that must be annotated
+// with liveness information.
+static int
+issafepoint(Prog *prog)
+{
+ return prog->as == ATEXT || prog->as == ACALL;
+}
+
+// Initializes the sets for solving the live variables. Visits all the
+// instructions in each basic block to summarizes the information at each basic
+// block
+static void
+livenessprologue(Liveness *lv)
+{
+ BasicBlock *bb;
+ Bvec *uevar, *varkill, *avarinit;
+ Prog *p;
+ int32 i;
+ int32 nvars;
+
+ nvars = arraylength(lv->vars);
+ uevar = bvalloc(nvars);
+ varkill = bvalloc(nvars);
+ avarinit = bvalloc(nvars);
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ // Walk the block instructions backward and update the block
+ // effects with the each prog effects.
+ for(p = bb->last; p != nil; p = p->opt) {
+ progeffects(p, lv->vars, uevar, varkill, avarinit);
+ if(debuglive >= 3)
+ printeffects(p, uevar, varkill, avarinit);
+ bvor(lv->varkill[i], lv->varkill[i], varkill);
+ bvandnot(lv->uevar[i], lv->uevar[i], varkill);
+ bvor(lv->uevar[i], lv->uevar[i], uevar);
+ }
+ // Walk the block instructions forward to update avarinit bits.
+ // avarinit describes the effect at the end of the block, not the beginning.
+ bvresetall(varkill);
+ for(p = bb->first;; p = p->link) {
+ progeffects(p, lv->vars, uevar, varkill, avarinit);
+ if(debuglive >= 3)
+ printeffects(p, uevar, varkill, avarinit);
+ bvandnot(lv->avarinit[i], lv->avarinit[i], varkill);
+ bvor(lv->avarinit[i], lv->avarinit[i], avarinit);
+ if(p == bb->last)
+ break;
+ }
+ }
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+}
+
+// Solve the liveness dataflow equations.
+static void
+livenesssolve(Liveness *lv)
+{
+ BasicBlock *bb, *succ, *pred;
+ Bvec *newlivein, *newliveout, *any, *all;
+ int32 rpo, i, j, change;
+
+ // These temporary bitvectors exist to avoid successive allocations and
+ // frees within the loop.
+ newlivein = bvalloc(arraylength(lv->vars));
+ newliveout = bvalloc(arraylength(lv->vars));
+ any = bvalloc(arraylength(lv->vars));
+ all = bvalloc(arraylength(lv->vars));
+
+ // Push avarinitall, avarinitany forward.
+ // avarinitall says the addressed var is initialized along all paths reaching the block exit.
+ // avarinitany says the addressed var is initialized along some path reaching the block exit.
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = bb->rpo;
+ if(i == 0)
+ bvcopy(lv->avarinitall[rpo], lv->avarinit[rpo]);
+ else {
+ bvresetall(lv->avarinitall[rpo]);
+ bvnot(lv->avarinitall[rpo]);
+ }
+ bvcopy(lv->avarinitany[rpo], lv->avarinit[rpo]);
+ }
+
+ change = 1;
+ while(change != 0) {
+ change = 0;
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = bb->rpo;
+ bvresetall(any);
+ bvresetall(all);
+ for(j = 0; j < arraylength(bb->pred); j++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, j);
+ if(j == 0) {
+ bvcopy(any, lv->avarinitany[pred->rpo]);
+ bvcopy(all, lv->avarinitall[pred->rpo]);
+ } else {
+ bvor(any, any, lv->avarinitany[pred->rpo]);
+ bvand(all, all, lv->avarinitall[pred->rpo]);
+ }
+ }
+ bvandnot(any, any, lv->varkill[rpo]);
+ bvandnot(all, all, lv->varkill[rpo]);
+ bvor(any, any, lv->avarinit[rpo]);
+ bvor(all, all, lv->avarinit[rpo]);
+ if(bvcmp(any, lv->avarinitany[rpo])) {
+ change = 1;
+ bvcopy(lv->avarinitany[rpo], any);
+ }
+ if(bvcmp(all, lv->avarinitall[rpo])) {
+ change = 1;
+ bvcopy(lv->avarinitall[rpo], all);
+ }
+ }
+ }
+
+ // Iterate through the blocks in reverse round-robin fashion. A work
+ // queue might be slightly faster. As is, the number of iterations is
+ // so low that it hardly seems to be worth the complexity.
+ change = 1;
+ while(change != 0) {
+ change = 0;
+ // Walk blocks in the general direction of propagation. This
+ // improves convergence.
+ for(i = arraylength(lv->cfg) - 1; i >= 0; i--) {
+ // A variable is live on output from this block
+ // if it is live on input to some successor.
+ //
+ // out[b] = \bigcup_{s \in succ[b]} in[s]
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+ rpo = bb->rpo;
+ bvresetall(newliveout);
+ for(j = 0; j < arraylength(bb->succ); j++) {
+ succ = *(BasicBlock**)arrayget(bb->succ, j);
+ bvor(newliveout, newliveout, lv->livein[succ->rpo]);
+ }
+ if(bvcmp(lv->liveout[rpo], newliveout)) {
+ change = 1;
+ bvcopy(lv->liveout[rpo], newliveout);
+ }
+
+ // A variable is live on input to this block
+ // if it is live on output from this block and
+ // not set by the code in this block.
+ //
+ // in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
+ bvandnot(newlivein, lv->liveout[rpo], lv->varkill[rpo]);
+ bvor(lv->livein[rpo], newlivein, lv->uevar[rpo]);
+ }
+ }
+
+ free(newlivein);
+ free(newliveout);
+ free(any);
+ free(all);
+}
+
+// This function is slow but it is only used for generating debug prints.
+// Check whether n is marked live in args/locals.
+static int
+islive(Node *n, Bvec *args, Bvec *locals)
+{
+ int i;
+
+ switch(n->class) {
+ case PPARAM:
+ case PPARAMOUT:
+ for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+ if(bvget(args, n->xoffset/widthptr*BitsPerPointer + i))
+ return 1;
+ break;
+ case PAUTO:
+ for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+ if(bvget(locals, (n->xoffset + stkptrsize)/widthptr*BitsPerPointer + i))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+// Visits all instructions in a basic block and computes a bit vector of live
+// variables at each safe point locations.
+static void
+livenessepilogue(Liveness *lv)
+{
+ BasicBlock *bb, *pred;
+ Bvec *ambig, *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
+ Node *n;
+ Prog *p, *next;
+ int32 i, j, numlive, startmsg, nmsg, nvars, pos;
+ vlong xoffset;
+ char **msg;
+ Fmt fmt;
+
+ nvars = arraylength(lv->vars);
+ livein = bvalloc(nvars);
+ liveout = bvalloc(nvars);
+ uevar = bvalloc(nvars);
+ varkill = bvalloc(nvars);
+ avarinit = bvalloc(nvars);
+ any = bvalloc(nvars);
+ all = bvalloc(nvars);
+ ambig = bvalloc(localswords() * BitsPerPointer);
+ msg = nil;
+ nmsg = 0;
+ startmsg = 0;
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+ // Compute avarinitany and avarinitall for entry to block.
+ // This duplicates information known during livenesssolve
+ // but avoids storing two more vectors for each block.
+ bvresetall(any);
+ bvresetall(all);
+ for(j = 0; j < arraylength(bb->pred); j++) {
+ pred = *(BasicBlock**)arrayget(bb->pred, j);
+ if(j == 0) {
+ bvcopy(any, lv->avarinitany[pred->rpo]);
+ bvcopy(all, lv->avarinitall[pred->rpo]);
+ } else {
+ bvor(any, any, lv->avarinitany[pred->rpo]);
+ bvand(all, all, lv->avarinitall[pred->rpo]);
+ }
+ }
+
+ // Walk forward through the basic block instructions and
+ // allocate liveness maps for those instructions that need them.
+ // Seed the maps with information about the addrtaken variables.
+ for(p = bb->first;; p = p->link) {
+ progeffects(p, lv->vars, uevar, varkill, avarinit);
+ bvandnot(any, any, varkill);
+ bvandnot(all, all, varkill);
+ bvor(any, any, avarinit);
+ bvor(all, all, avarinit);
+
+ if(issafepoint(p)) {
+ // Annotate ambiguously live variables so that they can
+ // be zeroed at function entry.
+ // livein and liveout are dead here and used as temporaries.
+ // For now, only enabled when using GOEXPERIMENT=precisestack
+ // during make.bash / all.bash.
+ if(precisestack_enabled) {
+ bvresetall(livein);
+ bvandnot(liveout, any, all);
+ if(!bvisempty(liveout)) {
+ for(pos = 0; pos < liveout->n; pos++) {
+ if(!bvget(liveout, pos))
+ continue;
+ bvset(all, pos); // silence future warnings in this block
+ n = *(Node**)arrayget(lv->vars, pos);
+ if(!n->needzero) {
+ n->needzero = 1;
+ if(debuglive >= 1)
+ warnl(p->lineno, "%N: %lN is ambiguously live", curfn->nname, n);
+ // Record in 'ambiguous' bitmap.
+ xoffset = n->xoffset + stkptrsize;
+ twobitwalktype1(n->type, &xoffset, ambig);
+ }
+ }
+ }
+ }
+
+ // Allocate a bit vector for each class and facet of
+ // value we are tracking.
+
+ // Live stuff first.
+ args = bvalloc(argswords() * BitsPerPointer);
+ arrayadd(lv->argslivepointers, &args);
+ locals = bvalloc(localswords() * BitsPerPointer);
+ arrayadd(lv->livepointers, &locals);
+
+ if(debuglive >= 3) {
+ print("%P\n", p);
+ printvars("avarinitany", any, lv->vars);
+ }
+
+ // Record any values with an "address taken" reaching
+ // this code position as live. Must do now instead of below
+ // because the any/all calculation requires walking forward
+ // over the block (as this loop does), while the liveout
+ // requires walking backward (as the next loop does).
+ twobitlivepointermap(lv, any, lv->vars, args, locals);
+ }
+
+ if(p == bb->last)
+ break;
+ }
+ bb->lastbitmapindex = arraylength(lv->livepointers) - 1;
+ }
+
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+ if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0 && curfn->nname->sym->name[0] != '.') {
+ nmsg = arraylength(lv->livepointers);
+ startmsg = nmsg;
+ msg = xmalloc(nmsg*sizeof msg[0]);
+ for(j=0; j<nmsg; j++)
+ msg[j] = nil;
+ }
+
+ // walk backward, emit pcdata and populate the maps
+ pos = bb->lastbitmapindex;
+ if(pos < 0) {
+ // the first block we encounter should have the ATEXT so
+ // at no point should pos ever be less than zero.
+ fatal("livenessepilogue");
+ }
+
+ bvcopy(livein, lv->liveout[bb->rpo]);
+ for(p = bb->last; p != nil; p = next) {
+ next = p->opt; // splicebefore modifies p->opt
+ // Propagate liveness information
+ progeffects(p, lv->vars, uevar, varkill, avarinit);
+ bvcopy(liveout, livein);
+ bvandnot(livein, liveout, varkill);
+ bvor(livein, livein, uevar);
+ if(debuglive >= 3 && issafepoint(p)){
+ print("%P\n", p);
+ printvars("uevar", uevar, lv->vars);
+ printvars("varkill", varkill, lv->vars);
+ printvars("livein", livein, lv->vars);
+ printvars("liveout", liveout, lv->vars);
+ }
+ if(issafepoint(p)) {
+ // Found an interesting instruction, record the
+ // corresponding liveness information.
+
+ // Useful sanity check: on entry to the function,
+ // the only things that can possibly be live are the
+ // input parameters.
+ if(p->as == ATEXT) {
+ for(j = 0; j < liveout->n; j++) {
+ if(!bvget(liveout, j))
+ continue;
+ n = *(Node**)arrayget(lv->vars, j);
+ if(n->class != PPARAM)
+ yyerrorl(p->lineno, "internal error: %N %lN recorded as live on entry", curfn->nname, n);
+ }
+ }
+
+ // Record live pointers.
+ args = *(Bvec**)arrayget(lv->argslivepointers, pos);
+ locals = *(Bvec**)arrayget(lv->livepointers, pos);
+ twobitlivepointermap(lv, liveout, lv->vars, args, locals);
+
+ // Ambiguously live variables are zeroed immediately after
+ // function entry. Mark them live for all the non-entry bitmaps
+ // so that GODEBUG=gcdead=1 mode does not poison them.
+ if(p->as == ACALL)
+ bvor(locals, locals, ambig);
+
+ // Show live pointer bitmaps.
+ // We're interpreting the args and locals bitmap instead of liveout so that we
+ // include the bits added by the avarinit logic in the
+ // previous loop.
+ if(msg != nil) {
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "%L: live at ", p->lineno);
+ if(p->as == ACALL && p->to.node)
+ fmtprint(&fmt, "call to %s:", p->to.node->sym->name);
+ else if(p->as == ACALL)
+ fmtprint(&fmt, "indirect call:");
+ else
+ fmtprint(&fmt, "entry to %s:", p->from.node->sym->name);
+ numlive = 0;
+ for(j = 0; j < arraylength(lv->vars); j++) {
+ n = *(Node**)arrayget(lv->vars, j);
+ if(islive(n, args, locals)) {
+ fmtprint(&fmt, " %N", n);
+ numlive++;
+ }
+ }
+ fmtprint(&fmt, "\n");
+ if(numlive == 0) // squelch message
+ free(fmtstrflush(&fmt));
+ else
+ msg[--startmsg] = fmtstrflush(&fmt);
+ }
+
+ // Only CALL instructions need a PCDATA annotation.
+ // The TEXT instruction annotation is implicit.
+ if(p->as == ACALL) {
+ if(isdeferreturn(p)) {
+ // runtime.deferreturn modifies its return address to return
+ // back to the CALL, not to the subsequent instruction.
+ // Because the return comes back one instruction early,
+ // the PCDATA must begin one instruction early too.
+ // The instruction before a call to deferreturn is always a
+ // no-op, to keep PC-specific data unambiguous.
+ splicebefore(lv, bb, newpcdataprog(p->opt, pos), p->opt);
+ } else {
+ splicebefore(lv, bb, newpcdataprog(p, pos), p);
+ }
+ }
+
+ pos--;
+ }
+ }
+ if(msg != nil) {
+ for(j=startmsg; j<nmsg; j++)
+ if(msg[j] != nil)
+ print("%s", msg[j]);
+ free(msg);
+ msg = nil;
+ nmsg = 0;
+ startmsg = 0;
+ }
+ }
+
+ free(livein);
+ free(liveout);
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+ free(any);
+ free(all);
+ free(ambig);
+
+ flusherrors();
+}
+
+// FNV-1 hash function constants.
+#define H0 2166136261UL
+#define Hp 16777619UL
+/*c2go
+enum
+{
+ H0 = 2166136261,
+ Hp = 16777619,
+};
+*/
+
+static uint32
+hashbitmap(uint32 h, Bvec *bv)
+{
+ uchar *p, *ep;
+
+ p = (uchar*)bv->b;
+ ep = p + 4*((bv->n+31)/32);
+ while(p < ep)
+ h = (h*Hp) ^ *p++;
+ return h;
+}
+
+// Compact liveness information by coalescing identical per-call-site bitmaps.
+// The merging only happens for a single function, not across the entire binary.
+//
+// There are actually two lists of bitmaps, one list for the local variables and one
+// list for the function arguments. Both lists are indexed by the same PCDATA
+// index, so the corresponding pairs must be considered together when
+// merging duplicates. The argument bitmaps change much less often during
+// function execution than the local variable bitmaps, so it is possible that
+// we could introduce a separate PCDATA index for arguments vs locals and
+// then compact the set of argument bitmaps separately from the set of
+// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary
+// is actually a net loss: we save about 50k of argument bitmaps but the new
+// PCDATA tables cost about 100k. So for now we keep using a single index for
+// both bitmap lists.
+static void
+livenesscompact(Liveness *lv)
+{
+ int *table, *remap, i, j, n, tablesize, uniq;
+ uint32 h;
+ Bvec *local, *arg, *jlocal, *jarg;
+ Prog *p;
+
+ // Linear probing hash table of bitmaps seen so far.
+ // The hash table has 4n entries to keep the linear
+ // scan short. An entry of -1 indicates an empty slot.
+ n = arraylength(lv->livepointers);
+ tablesize = 4*n;
+ table = xmalloc(tablesize*sizeof table[0]);
+ memset(table, 0xff, tablesize*sizeof table[0]);
+
+ // remap[i] = the new index of the old bit vector #i.
+ remap = xmalloc(n*sizeof remap[0]);
+ memset(remap, 0xff, n*sizeof remap[0]);
+ uniq = 0; // unique tables found so far
+
+ // Consider bit vectors in turn.
+ // If new, assign next number using uniq,
+ // record in remap, record in lv->livepointers and lv->argslivepointers
+ // under the new index, and add entry to hash table.
+ // If already seen, record earlier index in remap and free bitmaps.
+ for(i=0; i<n; i++) {
+ local = *(Bvec**)arrayget(lv->livepointers, i);
+ arg = *(Bvec**)arrayget(lv->argslivepointers, i);
+ h = hashbitmap(hashbitmap(H0, local), arg) % tablesize;
+
+ for(;;) {
+ j = table[h];
+ if(j < 0)
+ break;
+ jlocal = *(Bvec**)arrayget(lv->livepointers, j);
+ jarg = *(Bvec**)arrayget(lv->argslivepointers, j);
+ if(bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0) {
+ free(local);
+ free(arg);
+ remap[i] = j;
+ goto Next;
+ }
+ if(++h == tablesize)
+ h = 0;
+ }
+ table[h] = uniq;
+ remap[i] = uniq;
+ *(Bvec**)arrayget(lv->livepointers, uniq) = local;
+ *(Bvec**)arrayget(lv->argslivepointers, uniq) = arg;
+ uniq++;
+ Next:;
+ }
+
+ // We've already reordered lv->livepointers[0:uniq]
+ // and lv->argslivepointers[0:uniq] and freed the bitmaps
+ // we don't need anymore. Clear the pointers later in the
+ // array so that we can tell where the coalesced bitmaps stop
+ // and so that we don't double-free when cleaning up.
+ for(j=uniq; j<n; j++) {
+ *(Bvec**)arrayget(lv->livepointers, j) = nil;
+ *(Bvec**)arrayget(lv->argslivepointers, j) = nil;
+ }
+
+ // Rewrite PCDATA instructions to use new numbering.
+ for(p=lv->ptxt; p != P; p=p->link) {
+ if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex) {
+ i = p->to.offset;
+ if(i >= 0)
+ p->to.offset = remap[i];
+ }
+ }
+
+ free(table);
+ free(remap);
+}
+
+static int
+printbitset(int printed, char *name, Array *vars, Bvec *bits)
+{
+ int i, started;
+ Node *n;
+
+ started = 0;
+ for(i=0; i<arraylength(vars); i++) {
+ if(!bvget(bits, i))
+ continue;
+ if(!started) {
+ if(!printed)
+ print("\t");
+ else
+ print(" ");
+ started = 1;
+ printed = 1;
+ print("%s=", name);
+ } else {
+ print(",");
+ }
+ n = *(Node**)arrayget(vars, i);
+ print("%s", n->sym->name);
+ }
+ return printed;
+}
+
+// Prints the computed liveness information and inputs, for debugging.
+// This format synthesizes the information used during the multiple passes
+// into a single presentation.
+static void
+livenessprintdebug(Liveness *lv)
+{
+ int i, j, pcdata, printed;
+ BasicBlock *bb;
+ Prog *p;
+ Bvec *uevar, *varkill, *avarinit, *args, *locals;
+ Node *n;
+
+ print("liveness: %s\n", curfn->nname->sym->name);
+
+ uevar = bvalloc(arraylength(lv->vars));
+ varkill = bvalloc(arraylength(lv->vars));
+ avarinit = bvalloc(arraylength(lv->vars));
+
+ pcdata = 0;
+ for(i = 0; i < arraylength(lv->cfg); i++) {
+ if(i > 0)
+ print("\n");
+ bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+ // bb#0 pred=1,2 succ=3,4
+ print("bb#%d pred=", i);
+ for(j = 0; j < arraylength(bb->pred); j++) {
+ if(j > 0)
+ print(",");
+ print("%d", (*(BasicBlock**)arrayget(bb->pred, j))->rpo);
+ }
+ print(" succ=");
+ for(j = 0; j < arraylength(bb->succ); j++) {
+ if(j > 0)
+ print(",");
+ print("%d", (*(BasicBlock**)arrayget(bb->succ, j))->rpo);
+ }
+ print("\n");
+
+ // initial settings
+ printed = 0;
+ printed = printbitset(printed, "uevar", lv->vars, lv->uevar[bb->rpo]);
+ printed = printbitset(printed, "livein", lv->vars, lv->livein[bb->rpo]);
+ if(printed)
+ print("\n");
+
+ // program listing, with individual effects listed
+ for(p = bb->first;; p = p->link) {
+ print("%P\n", p);
+ if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex)
+ pcdata = p->to.offset;
+ progeffects(p, lv->vars, uevar, varkill, avarinit);
+ printed = 0;
+ printed = printbitset(printed, "uevar", lv->vars, uevar);
+ printed = printbitset(printed, "varkill", lv->vars, varkill);
+ printed = printbitset(printed, "avarinit", lv->vars, avarinit);
+ if(printed)
+ print("\n");
+ if(issafepoint(p)) {
+ args = *(Bvec**)arrayget(lv->argslivepointers, pcdata);
+ locals = *(Bvec**)arrayget(lv->livepointers, pcdata);
+ print("\tlive=");
+ printed = 0;
+ for(j = 0; j < arraylength(lv->vars); j++) {
+ n = *(Node**)arrayget(lv->vars, j);
+ if(islive(n, args, locals)) {
+ if(printed++)
+ print(",");
+ print("%N", n);
+ }
+ }
+ print("\n");
+ }
+ if(p == bb->last)
+ break;
+ }
+
+ // bb bitsets
+ print("end\n");
+ printed = printbitset(printed, "varkill", lv->vars, lv->varkill[bb->rpo]);
+ printed = printbitset(printed, "liveout", lv->vars, lv->liveout[bb->rpo]);
+ printed = printbitset(printed, "avarinit", lv->vars, lv->avarinit[bb->rpo]);
+ printed = printbitset(printed, "avarinitany", lv->vars, lv->avarinitany[bb->rpo]);
+ printed = printbitset(printed, "avarinitall", lv->vars, lv->avarinitall[bb->rpo]);
+ if(printed)
+ print("\n");
+ }
+ print("\n");
+
+ free(uevar);
+ free(varkill);
+ free(avarinit);
+}
+
+// Dumps an array of bitmaps to a symbol as a sequence of uint32 values. The
+// first word dumped is the total number of bitmaps. The second word is the
+// length of the bitmaps. All bitmaps are assumed to be of equal length. The
+// words that are followed are the raw bitmap words. The arr argument is an
+// array of Node*s.
+static void
+twobitwritesymbol(Array *arr, Sym *sym)
+{
+ Bvec *bv;
+ int off, i, j, len;
+ uint32 word;
+
+ len = arraylength(arr);
+ off = 0;
+ off += 4; // number of bitmaps, to fill in later
+ bv = *(Bvec**)arrayget(arr, 0);
+ off = duint32(sym, off, bv->n); // number of bits in each bitmap
+ for(i = 0; i < len; i++) {
+ // bitmap words
+ bv = *(Bvec**)arrayget(arr, i);
+ if(bv == nil)
+ break;
+ for(j = 0; j < bv->n; j += 32) {
+ word = bv->b[j/32];
+ // Runtime reads the bitmaps as byte arrays. Oblige.
+ off = duint8(sym, off, word);
+ off = duint8(sym, off, word>>8);
+ off = duint8(sym, off, word>>16);
+ off = duint8(sym, off, word>>24);
+ }
+ }
+ duint32(sym, 0, i); // number of bitmaps
+ ggloblsym(sym, off, RODATA);
+}
+
+static void
+printprog(Prog *p)
+{
+ while(p != nil) {
+ print("%P\n", p);
+ p = p->link;
+ }
+}
+
+// Entry pointer for liveness analysis. Constructs a complete CFG, solves for
+// the liveness of pointer variables in the function, and emits a runtime data
+// structure read by the garbage collector.
+void
+liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
+{
+ Array *cfg, *vars;
+ Liveness *lv;
+ int debugdelta;
+ NodeList *l;
+
+ // Change name to dump debugging information only for a specific function.
+ debugdelta = 0;
+ if(strcmp(curfn->nname->sym->name, "!") == 0)
+ debugdelta = 2;
+
+ debuglive += debugdelta;
+ if(debuglive >= 3) {
+ print("liveness: %s\n", curfn->nname->sym->name);
+ printprog(firstp);
+ }
+ checkptxt(fn, firstp);
+
+ // Construct the global liveness state.
+ cfg = newcfg(firstp);
+ if(debuglive >= 3)
+ printcfg(cfg);
+ vars = getvariables(fn);
+ lv = newliveness(fn, firstp, cfg, vars);
+
+ // Run the dataflow framework.
+ livenessprologue(lv);
+ if(debuglive >= 3)
+ livenessprintcfg(lv);
+ livenesssolve(lv);
+ if(debuglive >= 3)
+ livenessprintcfg(lv);
+ livenessepilogue(lv);
+ if(debuglive >= 3)
+ livenessprintcfg(lv);
+ livenesscompact(lv);
+
+ if(debuglive >= 2)
+ livenessprintdebug(lv);
+
+ // Emit the live pointer map data structures
+ twobitwritesymbol(lv->livepointers, livesym);
+ twobitwritesymbol(lv->argslivepointers, argssym);
+
+ // Free everything.
+ for(l=fn->dcl; l != nil; l = l->next)
+ if(l->n != N)
+ l->n->opt = nil;
+ freeliveness(lv);
+ arrayfree(vars);
+ freecfg(cfg);
+
+ debuglive -= debugdelta;
+}
diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c
new file mode 100644
index 0000000..993bb24
--- /dev/null
+++ b/src/cmd/gc/popt.c
@@ -0,0 +1,1005 @@
+// Derived from Inferno utils/6c/reg.c
+// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.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.
+
+// "Portable" optimizations.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+
+// p is a call instruction. Does the call fail to return?
+int
+noreturn(Prog *p)
+{
+ Sym *s;
+ int i;
+ static Sym* symlist[10];
+
+ if(symlist[0] == S) {
+ symlist[0] = pkglookup("panicindex", runtimepkg);
+ symlist[1] = pkglookup("panicslice", runtimepkg);
+ symlist[2] = pkglookup("throwinit", runtimepkg);
+ symlist[3] = pkglookup("gopanic", runtimepkg);
+ symlist[4] = pkglookup("panicwrap", runtimepkg);
+ symlist[5] = pkglookup("throwreturn", runtimepkg);
+ symlist[6] = pkglookup("selectgo", runtimepkg);
+ symlist[7] = pkglookup("block", runtimepkg);
+ }
+
+ if(p->to.node == nil)
+ return 0;
+ s = p->to.node->sym;
+ if(s == S)
+ return 0;
+ for(i=0; symlist[i]!=S; i++)
+ if(s == symlist[i])
+ return 1;
+ return 0;
+}
+
+// JMP chasing and removal.
+//
+// The code generator depends on being able to write out jump
+// instructions that it can jump to now but fill in later.
+// the linker will resolve them nicely, but they make the code
+// longer and more difficult to follow during debugging.
+// Remove them.
+
+/* what instruction does a JMP to p eventually land on? */
+static Prog*
+chasejmp(Prog *p, int *jmploop)
+{
+ int n;
+
+ n = 0;
+ while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
+ if(++n > 10) {
+ *jmploop = 1;
+ break;
+ }
+ p = p->to.u.branch;
+ }
+ return p;
+}
+
+/*
+ * reuse reg pointer for mark/sweep state.
+ * leave reg==nil at end because alive==nil.
+ */
+#define alive ((void*)0)
+#define dead ((void*)1)
+/*c2go
+extern void *alive;
+extern void *dead;
+*/
+
+/* mark all code reachable from firstp as alive */
+static void
+mark(Prog *firstp)
+{
+ Prog *p;
+
+ for(p=firstp; p; p=p->link) {
+ if(p->opt != dead)
+ break;
+ p->opt = alive;
+ if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
+ mark(p->to.u.branch);
+ if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
+ break;
+ }
+}
+
+void
+fixjmp(Prog *firstp)
+{
+ int jmploop;
+ Prog *p, *last;
+
+ if(debug['R'] && debug['v'])
+ print("\nfixjmp\n");
+
+ // pass 1: resolve jump to jump, mark all code as dead.
+ jmploop = 0;
+ for(p=firstp; p; p=p->link) {
+ if(debug['R'] && debug['v'])
+ print("%P\n", p);
+ if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
+ p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
+ if(debug['R'] && debug['v'])
+ print("->%P\n", p);
+ }
+ p->opt = dead;
+ }
+ if(debug['R'] && debug['v'])
+ print("\n");
+
+ // pass 2: mark all reachable code alive
+ mark(firstp);
+
+ // pass 3: delete dead code (mostly JMPs).
+ last = nil;
+ for(p=firstp; p; p=p->link) {
+ if(p->opt == dead) {
+ if(p->link == P && p->as == ARET && last && last->as != ARET) {
+ // This is the final ARET, and the code so far doesn't have one.
+ // Let it stay. The register allocator assumes that all live code in
+ // the function can be traversed by starting at all the RET instructions
+ // and following predecessor links. If we remove the final RET,
+ // this assumption will not hold in the case of an infinite loop
+ // at the end of a function.
+ // Keep the RET but mark it dead for the liveness analysis.
+ p->mode = 1;
+ } else {
+ if(debug['R'] && debug['v'])
+ print("del %P\n", p);
+ continue;
+ }
+ }
+ if(last)
+ last->link = p;
+ last = p;
+ }
+ last->link = P;
+
+ // pass 4: elide JMP to next instruction.
+ // only safe if there are no jumps to JMPs anymore.
+ if(!jmploop) {
+ last = nil;
+ for(p=firstp; p; p=p->link) {
+ if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
+ if(debug['R'] && debug['v'])
+ print("del %P\n", p);
+ continue;
+ }
+ if(last)
+ last->link = p;
+ last = p;
+ }
+ last->link = P;
+ }
+
+ if(debug['R'] && debug['v']) {
+ print("\n");
+ for(p=firstp; p; p=p->link)
+ print("%P\n", p);
+ print("\n");
+ }
+}
+
+#undef alive
+#undef dead
+
+// Control flow analysis. The Flow structures hold predecessor and successor
+// information as well as basic loop analysis.
+//
+// graph = flowstart(firstp, sizeof(Flow));
+// ... use flow graph ...
+// flowend(graph); // free graph
+//
+// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
+//
+// for(f = graph->start; f != nil; f = f->link)
+//
+// or, given an instruction f, to iterate over all the predecessors, which is
+// f->p1 and this list:
+//
+// for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
+//
+// Often the Flow struct is embedded as the first field inside a larger struct S.
+// In that case casts are needed to convert Flow* to S* in many places but the
+// idea is the same. Pass sizeof(S) instead of sizeof(Flow) to flowstart.
+
+Graph*
+flowstart(Prog *firstp, int size)
+{
+ int nf;
+ Flow *f, *f1, *start, *last;
+ Graph *graph;
+ Prog *p;
+ ProgInfo info;
+
+ // Count and mark instructions to annotate.
+ nf = 0;
+ for(p = firstp; p != P; p = p->link) {
+ p->opt = nil; // should be already, but just in case
+ proginfo(&info, p);
+ if(info.flags & Skip)
+ continue;
+ p->opt = (void*)1;
+ nf++;
+ }
+
+ if(nf == 0)
+ return nil;
+
+ if(nf >= 20000) {
+ // fatal("%S is too big (%d instructions)", curfn->nname->sym, nf);
+ return nil;
+ }
+
+ // Allocate annotations and assign to instructions.
+ graph = calloc(sizeof *graph + size*nf, 1);
+ if(graph == nil)
+ fatal("out of memory");
+ start = (Flow*)(graph+1);
+ last = nil;
+ f = start;
+ for(p = firstp; p != P; p = p->link) {
+ if(p->opt == nil)
+ continue;
+ p->opt = f;
+ f->prog = p;
+ if(last)
+ last->link = f;
+ last = f;
+
+ f = (Flow*)((uchar*)f + size);
+ }
+
+ // Fill in pred/succ information.
+ for(f = start; f != nil; f = f->link) {
+ p = f->prog;
+ proginfo(&info, p);
+ if(!(info.flags & Break)) {
+ f1 = f->link;
+ f->s1 = f1;
+ f1->p1 = f;
+ }
+ if(p->to.type == D_BRANCH) {
+ if(p->to.u.branch == P)
+ fatal("pnil %P", p);
+ f1 = p->to.u.branch->opt;
+ if(f1 == nil)
+ fatal("fnil %P / %P", p, p->to.u.branch);
+ if(f1 == f) {
+ //fatal("self loop %P", p);
+ continue;
+ }
+ f->s2 = f1;
+ f->p2link = f1->p2;
+ f1->p2 = f;
+ }
+ }
+
+ graph->start = start;
+ graph->num = nf;
+ return graph;
+}
+
+void
+flowend(Graph *graph)
+{
+ Flow *f;
+
+ for(f = graph->start; f != nil; f = f->link)
+ f->prog->opt = nil;
+ free(graph);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ * the actual dominators if the flow graph is reducible
+ * otherwise, dominators plus some other non-dominators.
+ * See Matthew S. Hecht and Jeffrey D. Ullman,
+ * "Analysis of a Simple Algorithm for Global Data Flow Problems",
+ * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ * Oct. 1-3, 1973, pp. 207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ * such a node is a loop head.
+ * recursively, all preds with a greater rpo number are in the loop
+ */
+static int32
+postorder(Flow *r, Flow **rpo2r, int32 n)
+{
+ Flow *r1;
+
+ r->rpo = 1;
+ r1 = r->s1;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ r1 = r->s2;
+ if(r1 && !r1->rpo)
+ n = postorder(r1, rpo2r, n);
+ rpo2r[n] = r;
+ n++;
+ return n;
+}
+
+static int32
+rpolca(int32 *idom, int32 rpo1, int32 rpo2)
+{
+ int32 t;
+
+ if(rpo1 == -1)
+ return rpo2;
+ while(rpo1 != rpo2){
+ if(rpo1 > rpo2){
+ t = rpo2;
+ rpo2 = rpo1;
+ rpo1 = t;
+ }
+ while(rpo1 < rpo2){
+ t = idom[rpo2];
+ if(t >= rpo2)
+ fatal("bad idom");
+ rpo2 = t;
+ }
+ }
+ return rpo1;
+}
+
+static int
+doms(int32 *idom, int32 r, int32 s)
+{
+ while(s > r)
+ s = idom[s];
+ return s == r;
+}
+
+static int
+loophead(int32 *idom, Flow *r)
+{
+ int32 src;
+
+ src = r->rpo;
+ if(r->p1 != nil && doms(idom, src, r->p1->rpo))
+ return 1;
+ for(r = r->p2; r != nil; r = r->p2link)
+ if(doms(idom, src, r->rpo))
+ return 1;
+ return 0;
+}
+
+static void
+loopmark(Flow **rpo2r, int32 head, Flow *r)
+{
+ if(r->rpo < head || r->active == head)
+ return;
+ r->active = head;
+ r->loop += LOOP;
+ if(r->p1 != nil)
+ loopmark(rpo2r, head, r->p1);
+ for(r = r->p2; r != nil; r = r->p2link)
+ loopmark(rpo2r, head, r);
+}
+
+void
+flowrpo(Graph *g)
+{
+ Flow *r1;
+ int32 i, d, me, nr, *idom;
+ Flow **rpo2r;
+
+ free(g->rpo);
+ g->rpo = calloc(g->num*sizeof g->rpo[0], 1);
+ idom = calloc(g->num*sizeof idom[0], 1);
+ if(g->rpo == nil || idom == nil)
+ fatal("out of memory");
+
+ for(r1 = g->start; r1 != nil; r1 = r1->link)
+ r1->active = 0;
+
+ rpo2r = g->rpo;
+ d = postorder(g->start, rpo2r, 0);
+ nr = g->num;
+ if(d > nr)
+ fatal("too many reg nodes %d %d", d, nr);
+ nr = d;
+ for(i = 0; i < nr / 2; i++) {
+ r1 = rpo2r[i];
+ rpo2r[i] = rpo2r[nr - 1 - i];
+ rpo2r[nr - 1 - i] = r1;
+ }
+ for(i = 0; i < nr; i++)
+ rpo2r[i]->rpo = i;
+
+ idom[0] = 0;
+ for(i = 0; i < nr; i++) {
+ r1 = rpo2r[i];
+ me = r1->rpo;
+ d = -1;
+ // rpo2r[r->rpo] == r protects against considering dead code,
+ // which has r->rpo == 0.
+ if(r1->p1 != nil && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
+ d = r1->p1->rpo;
+ for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+ if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
+ d = rpolca(idom, d, r1->rpo);
+ idom[i] = d;
+ }
+
+ for(i = 0; i < nr; i++) {
+ r1 = rpo2r[i];
+ r1->loop++;
+ if(r1->p2 != nil && loophead(idom, r1))
+ loopmark(rpo2r, i, r1);
+ }
+ free(idom);
+
+ for(r1 = g->start; r1 != nil; r1 = r1->link)
+ r1->active = 0;
+}
+
+Flow*
+uniqp(Flow *r)
+{
+ Flow *r1;
+
+ r1 = r->p1;
+ if(r1 == nil) {
+ r1 = r->p2;
+ if(r1 == nil || r1->p2link != nil)
+ return nil;
+ } else
+ if(r->p2 != nil)
+ return nil;
+ return r1;
+}
+
+Flow*
+uniqs(Flow *r)
+{
+ Flow *r1;
+
+ r1 = r->s1;
+ if(r1 == nil) {
+ r1 = r->s2;
+ if(r1 == nil)
+ return nil;
+ } else
+ if(r->s2 != nil)
+ return nil;
+ return r1;
+}
+
+// The compilers assume they can generate temporary variables
+// as needed to preserve the right semantics or simplify code
+// generation and the back end will still generate good code.
+// This results in a large number of ephemeral temporary variables.
+// Merge temps with non-overlapping lifetimes and equal types using the
+// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation",
+// ACM TOPLAS 1999.
+
+typedef struct TempVar TempVar;
+typedef struct TempFlow TempFlow;
+
+struct TempVar
+{
+ Node *node;
+ TempFlow *def; // definition of temp var
+ TempFlow *use; // use list, chained through TempFlow.uselink
+ TempVar *freelink; // next free temp in Type.opt list
+ TempVar *merge; // merge var with this one
+ vlong start; // smallest Prog.pc in live range
+ vlong end; // largest Prog.pc in live range
+ uchar addr; // address taken - no accurate end
+ uchar removed; // removed from program
+};
+
+struct TempFlow
+{
+ Flow f;
+ TempFlow *uselink;
+};
+
+static int
+startcmp(const void *va, const void *vb)
+{
+ TempVar *a, *b;
+
+ a = *(TempVar**)va;
+ b = *(TempVar**)vb;
+
+ if(a->start < b->start)
+ return -1;
+ if(a->start > b->start)
+ return +1;
+ return 0;
+}
+
+// Is n available for merging?
+static int
+canmerge(Node *n)
+{
+ return n->class == PAUTO && strncmp(n->sym->name, "autotmp", 7) == 0;
+}
+
+static void mergewalk(TempVar*, TempFlow*, uint32);
+static void varkillwalk(TempVar*, TempFlow*, uint32);
+
+void
+mergetemp(Prog *firstp)
+{
+ int i, j, nvar, ninuse, nfree, nkill;
+ TempVar *var, *v, *v1, **bystart, **inuse;
+ TempFlow *r;
+ NodeList *l, **lp;
+ Node *n;
+ Prog *p, *p1;
+ Type *t;
+ ProgInfo info, info1;
+ int32 gen;
+ Graph *g;
+
+ enum { Debug = 0 };
+
+ g = flowstart(firstp, sizeof(TempFlow));
+ if(g == nil)
+ return;
+
+ // Build list of all mergeable variables.
+ nvar = 0;
+ for(l = curfn->dcl; l != nil; l = l->next)
+ if(canmerge(l->n))
+ nvar++;
+
+ var = calloc(nvar*sizeof var[0], 1);
+ nvar = 0;
+ for(l = curfn->dcl; l != nil; l = l->next) {
+ n = l->n;
+ if(canmerge(n)) {
+ v = &var[nvar++];
+ n->opt = v;
+ v->node = n;
+ }
+ }
+
+ // Build list of uses.
+ // We assume that the earliest reference to a temporary is its definition.
+ // This is not true of variables in general but our temporaries are all
+ // single-use (that's why we have so many!).
+ for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+ p = r->f.prog;
+ proginfo(&info, p);
+
+ if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt)
+ fatal("double node %P", p);
+ if((n = p->from.node) != N && (v = n->opt) != nil ||
+ (n = p->to.node) != N && (v = n->opt) != nil) {
+ if(v->def == nil)
+ v->def = r;
+ r->uselink = v->use;
+ v->use = r;
+ if(n == p->from.node && (info.flags & LeftAddr))
+ v->addr = 1;
+ }
+ }
+
+ if(Debug > 1)
+ dumpit("before", g->start, 0);
+
+ nkill = 0;
+
+ // Special case.
+ for(v = var; v < var+nvar; v++) {
+ if(v->addr)
+ continue;
+ // Used in only one instruction, which had better be a write.
+ if((r = v->use) != nil && r->uselink == nil) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) {
+ p->as = ANOP;
+ p->to = zprog.to;
+ v->removed = 1;
+ if(Debug)
+ print("drop write-only %S\n", v->node->sym);
+ } else
+ fatal("temp used and not set: %P", p);
+ nkill++;
+ continue;
+ }
+
+ // Written in one instruction, read in the next, otherwise unused,
+ // no jumps to the next instruction. Happens mainly in 386 compiler.
+ if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ p1 = r->f.link->prog;
+ proginfo(&info1, p1);
+ enum {
+ SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD,
+ };
+ if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) &&
+ !((info.flags|info1.flags) & (LeftAddr|RightAddr)) &&
+ (info.flags & SizeAny) == (info1.flags & SizeAny)) {
+ p1->from = p->from;
+ excise(&r->f);
+ v->removed = 1;
+ if(Debug)
+ print("drop immediate-use %S\n", v->node->sym);
+ }
+ nkill++;
+ continue;
+ }
+ }
+
+ // Traverse live range of each variable to set start, end.
+ // Each flood uses a new value of gen so that we don't have
+ // to clear all the r->f.active words after each variable.
+ gen = 0;
+ for(v = var; v < var+nvar; v++) {
+ gen++;
+ for(r = v->use; r != nil; r = r->uselink)
+ mergewalk(v, r, gen);
+ if(v->addr) {
+ gen++;
+ for(r = v->use; r != nil; r = r->uselink)
+ varkillwalk(v, r, gen);
+ }
+ }
+
+ // Sort variables by start.
+ bystart = malloc(nvar*sizeof bystart[0]);
+ for(i=0; i<nvar; i++)
+ bystart[i] = &var[i];
+ qsort(bystart, nvar, sizeof bystart[0], startcmp);
+
+ // List of in-use variables, sorted by end, so that the ones that
+ // will last the longest are the earliest ones in the array.
+ // The tail inuse[nfree:] holds no-longer-used variables.
+ // In theory we should use a sorted tree so that insertions are
+ // guaranteed O(log n) and then the loop is guaranteed O(n log n).
+ // In practice, it doesn't really matter.
+ inuse = malloc(nvar*sizeof inuse[0]);
+ ninuse = 0;
+ nfree = nvar;
+ for(i=0; i<nvar; i++) {
+ v = bystart[i];
+ if(v->removed)
+ continue;
+
+ // Expire no longer in use.
+ while(ninuse > 0 && inuse[ninuse-1]->end < v->start) {
+ v1 = inuse[--ninuse];
+ inuse[--nfree] = v1;
+ }
+
+ // Find old temp to reuse if possible.
+ t = v->node->type;
+ for(j=nfree; j<nvar; j++) {
+ v1 = inuse[j];
+ // Require the types to match but also require the addrtaken bits to match.
+ // If a variable's address is taken, that disables registerization for the individual
+ // words of the variable (for example, the base,len,cap of a slice).
+ // We don't want to merge a non-addressed var with an addressed one and
+ // inhibit registerization of the former.
+ if(eqtype(t, v1->node->type) && v->node->addrtaken == v1->node->addrtaken) {
+ inuse[j] = inuse[nfree++];
+ if(v1->merge)
+ v->merge = v1->merge;
+ else
+ v->merge = v1;
+ nkill++;
+ break;
+ }
+ }
+
+ // Sort v into inuse.
+ j = ninuse++;
+ while(j > 0 && inuse[j-1]->end < v->end) {
+ inuse[j] = inuse[j-1];
+ j--;
+ }
+ inuse[j] = v;
+ }
+
+ if(Debug) {
+ print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill);
+ for(v=var; v<var+nvar; v++) {
+ print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end);
+ if(v->addr)
+ print(" addr=1");
+ if(v->removed)
+ print(" dead=1");
+ if(v->merge)
+ print(" merge %#N", v->merge->node);
+ if(v->start == v->end)
+ print(" %P", v->def->f.prog);
+ print("\n");
+ }
+
+ if(Debug > 1)
+ dumpit("after", g->start, 0);
+ }
+
+ // Update node references to use merged temporaries.
+ for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+ p = r->f.prog;
+ if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil)
+ p->from.node = v->merge->node;
+ if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil)
+ p->to.node = v->merge->node;
+ }
+
+ // Delete merged nodes from declaration list.
+ for(lp = &curfn->dcl; (l = *lp); ) {
+ curfn->dcl->end = l;
+ n = l->n;
+ v = n->opt;
+ if(v && (v->merge || v->removed)) {
+ *lp = l->next;
+ continue;
+ }
+ lp = &l->next;
+ }
+
+ // Clear aux structures.
+ for(v=var; v<var+nvar; v++)
+ v->node->opt = nil;
+ free(var);
+ free(bystart);
+ free(inuse);
+ flowend(g);
+}
+
+static void
+mergewalk(TempVar *v, TempFlow *r0, uint32 gen)
+{
+ Prog *p;
+ TempFlow *r1, *r, *r2;
+
+ for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) {
+ if(r1->f.active == gen)
+ break;
+ r1->f.active = gen;
+ p = r1->f.prog;
+ if(v->end < p->pc)
+ v->end = p->pc;
+ if(r1 == v->def) {
+ v->start = p->pc;
+ break;
+ }
+ }
+
+ for(r = r0; r != r1; r = (TempFlow*)r->f.p1)
+ for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link)
+ mergewalk(v, r2, gen);
+}
+
+static void
+varkillwalk(TempVar *v, TempFlow *r0, uint32 gen)
+{
+ Prog *p;
+ TempFlow *r1, *r;
+
+ for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.s1) {
+ if(r1->f.active == gen)
+ break;
+ r1->f.active = gen;
+ p = r1->f.prog;
+ if(v->end < p->pc)
+ v->end = p->pc;
+ if(v->start > p->pc)
+ v->start = p->pc;
+ if(p->as == ARET || (p->as == AVARKILL && p->to.node == v->node))
+ break;
+ }
+
+ for(r = r0; r != r1; r = (TempFlow*)r->f.s1)
+ varkillwalk(v, (TempFlow*)r->f.s2, gen);
+}
+
+// Eliminate redundant nil pointer checks.
+//
+// The code generation pass emits a CHECKNIL for every possibly nil pointer.
+// This pass removes a CHECKNIL if every predecessor path has already
+// checked this value for nil.
+//
+// Simple backwards flood from check to definition.
+// Run prog loop backward from end of program to beginning to avoid quadratic
+// behavior removing a run of checks.
+//
+// Assume that stack variables with address not taken can be loaded multiple times
+// from memory without being rechecked. Other variables need to be checked on
+// each load.
+
+typedef struct NilVar NilVar;
+typedef struct NilFlow NilFlow;
+
+struct NilFlow {
+ Flow f;
+ int kill;
+};
+
+static void nilwalkback(NilFlow *rcheck);
+static void nilwalkfwd(NilFlow *rcheck);
+
+void
+nilopt(Prog *firstp)
+{
+ NilFlow *r;
+ Prog *p;
+ Graph *g;
+ int ncheck, nkill;
+
+ g = flowstart(firstp, sizeof(NilFlow));
+ if(g == nil)
+ return;
+
+ if(debug_checknil > 1 /* || strcmp(curfn->nname->sym->name, "f1") == 0 */)
+ dumpit("nilopt", g->start, 0);
+
+ ncheck = 0;
+ nkill = 0;
+ for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+ p = r->f.prog;
+ if(p->as != ACHECKNIL || !regtyp(&p->from))
+ continue;
+ ncheck++;
+ if(stackaddr(&p->from)) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed nil check of SP address");
+ r->kill = 1;
+ continue;
+ }
+ nilwalkfwd(r);
+ if(r->kill) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed nil check before indirect");
+ continue;
+ }
+ nilwalkback(r);
+ if(r->kill) {
+ if(debug_checknil && p->lineno > 1)
+ warnl(p->lineno, "removed repeated nil check");
+ continue;
+ }
+ }
+
+ for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+ if(r->kill) {
+ nkill++;
+ excise(&r->f);
+ }
+ }
+
+ flowend(g);
+
+ if(debug_checknil > 1)
+ print("%S: removed %d of %d nil checks\n", curfn->nname->sym, nkill, ncheck);
+}
+
+static void
+nilwalkback(NilFlow *rcheck)
+{
+ Prog *p;
+ ProgInfo info;
+ NilFlow *r;
+
+ for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) {
+ p = r->f.prog;
+ proginfo(&info, p);
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+ // Found initialization of value we're checking for nil.
+ // without first finding the check, so this one is unchecked.
+ return;
+ }
+ if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+ }
+
+ // Here is a more complex version that scans backward across branches.
+ // It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason
+ // to keep the check (setting rcheck->kill = 0).
+ // It doesn't handle copying of aggregates as well as I would like,
+ // nor variables with their address taken,
+ // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3.
+ /*
+ for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) {
+ if(r1->f.active == gen)
+ break;
+ r1->f.active = gen;
+ p = r1->f.prog;
+
+ // If same check, stop this loop but still check
+ // alternate predecessors up to this point.
+ if(r1 != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from))
+ break;
+
+ proginfo(&info, p);
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+ // Found initialization of value we're checking for nil.
+ // without first finding the check, so this one is unchecked.
+ rcheck->kill = 0;
+ return;
+ }
+
+ if(r1->f.p1 == nil && r1->f.p2 == nil) {
+ print("lost pred for %P\n", rcheck->f.prog);
+ for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) {
+ proginfo(&info, r1->f.prog);
+ print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from);
+ }
+ fatal("lost pred trail");
+ }
+ }
+
+ for(r = r0; r != r1; r = (NilFlow*)r->f.p1)
+ for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link)
+ nilwalkback(rcheck, r2, gen);
+ */
+}
+
+static void
+nilwalkfwd(NilFlow *rcheck)
+{
+ NilFlow *r, *last;
+ Prog *p;
+ ProgInfo info;
+
+ // If the path down from rcheck dereferences the address
+ // (possibly with a small offset) before writing to memory
+ // and before any subsequent checks, it's okay to wait for
+ // that implicit check. Only consider this basic block to
+ // avoid problems like:
+ // _ = *x // should panic
+ // for {} // no writes but infinite loop may be considered visible
+ last = nil;
+ for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) {
+ p = r->f.prog;
+ proginfo(&info, p);
+
+ if((info.flags & LeftRead) && smallindir(&p->from, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+ if((info.flags & (RightRead|RightWrite)) && smallindir(&p->to, &rcheck->f.prog->from)) {
+ rcheck->kill = 1;
+ return;
+ }
+
+ // Stop if another nil check happens.
+ if(p->as == ACHECKNIL)
+ return;
+ // Stop if value is lost.
+ if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from))
+ return;
+ // Stop if memory write.
+ if((info.flags & RightWrite) && !regtyp(&p->to))
+ return;
+ // Stop if we jump backward.
+ // This test is valid because all the NilFlow* are pointers into
+ // a single contiguous array. We will need to add an explicit
+ // numbering when the code is converted to Go.
+ if(last != nil && r <= last)
+ return;
+ last = r;
+ }
+}
diff --git a/src/cmd/gc/popt.h b/src/cmd/gc/popt.h
new file mode 100644
index 0000000..8d5dfff
--- /dev/null
+++ b/src/cmd/gc/popt.h
@@ -0,0 +1,46 @@
+// Copyright 2013 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 Flow Flow;
+typedef struct Graph Graph;
+
+struct Flow {
+ Prog* prog; // actual instruction
+ Flow* p1; // predecessors of this instruction: p1,
+ Flow* p2; // and then p2 linked though p2link.
+ Flow* p2link;
+ Flow* s1; // successors of this instruction (at most two: s1 and s2).
+ Flow* s2;
+ Flow* link; // next instruction in function code
+
+ int32 active; // usable by client
+
+ int32 rpo; // reverse post ordering
+ uint16 loop; // x5 for every loop
+ uchar refset; // diagnostic generated
+};
+
+struct Graph
+{
+ Flow* start;
+ int num;
+
+ // After calling flowrpo, rpo lists the flow nodes in reverse postorder,
+ // and each non-dead Flow node f has g->rpo[f->rpo] == f.
+ Flow** rpo;
+};
+
+void fixjmp(Prog*);
+Graph* flowstart(Prog*, int);
+void flowrpo(Graph*);
+void flowend(Graph*);
+void mergetemp(Prog*);
+void nilopt(Prog*);
+int noreturn(Prog*);
+int regtyp(Addr*);
+int sameaddr(Addr*, Addr*);
+int smallindir(Addr*, Addr*);
+int stackaddr(Addr*);
+Flow* uniqp(Flow*);
+Flow* uniqs(Flow*);
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
new file mode 100644
index 0000000..c9e27fe
--- /dev/null
+++ b/src/cmd/gc/racewalk.c
@@ -0,0 +1,664 @@
+// Copyright 2012 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.
+
+// The racewalk pass modifies the code tree for the function as follows:
+//
+// 1. It inserts a call to racefuncenter at the beginning of each function.
+// 2. It inserts a call to racefuncexit at the end of each function.
+// 3. It inserts a call to raceread before each memory read.
+// 4. It inserts a call to racewrite before each memory write.
+//
+// The rewriting is not yet complete. Certain nodes are not rewritten
+// but should be.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// TODO(dvyukov): do not instrument initialization as writes:
+// a := make([]int, 10)
+
+static void racewalklist(NodeList *l, NodeList **init);
+static void racewalknode(Node **np, NodeList **init, int wr, int skip);
+static int callinstr(Node **n, NodeList **init, int wr, int skip);
+static Node* uintptraddr(Node *n);
+static void makeaddable(Node *n);
+static Node* basenod(Node *n);
+static void foreach(Node *n, void(*f)(Node*, void*), void *c);
+static void hascallspred(Node *n, void *c);
+static void appendinit(Node **np, NodeList *init);
+static Node* detachexpr(Node *n, NodeList **init);
+
+// Do not instrument the following packages at all,
+// at best instrumentation would cause infinite recursion.
+static const char *omit_pkgs[] = {"runtime", "runtime/race"};
+// Only insert racefuncenter/racefuncexit into the following packages.
+// Memory accesses in the packages are either uninteresting or will cause false positives.
+static const char *noinst_pkgs[] = {"sync", "sync/atomic"};
+
+static int
+ispkgin(const char **pkgs, int n)
+{
+ int i;
+
+ if(myimportpath) {
+ for(i=0; i<n; i++) {
+ if(strcmp(myimportpath, pkgs[i]) == 0)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+isforkfunc(Node *fn)
+{
+ // Special case for syscall.forkAndExecInChild.
+ // In the child, this function must not acquire any locks, because
+ // they might have been locked at the time of the fork. This means
+ // no rescheduling, no malloc calls, and no new stack segments.
+ // Race instrumentation does all of the above.
+ return myimportpath != nil && strcmp(myimportpath, "syscall") == 0 &&
+ strcmp(fn->nname->sym->name, "forkAndExecInChild") == 0;
+}
+
+void
+racewalk(Node *fn)
+{
+ Node *nd;
+ Node *nodpc;
+ char s[1024];
+
+ if(ispkgin(omit_pkgs, nelem(omit_pkgs)) || isforkfunc(fn))
+ return;
+
+ if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) {
+ racewalklist(fn->nbody, nil);
+ // nothing interesting for race detector in fn->enter
+ racewalklist(fn->exit, nil);
+ }
+
+ // nodpc is the PC of the caller as extracted by
+ // getcallerpc. We use -widthptr(FP) for x86.
+ // BUG: this will not work on arm.
+ nodpc = nod(OXXX, nil, nil);
+ *nodpc = *nodfp;
+ nodpc->type = types[TUINTPTR];
+ nodpc->xoffset = -widthptr;
+ nd = mkcall("racefuncenter", T, nil, nodpc);
+ fn->enter = concat(list1(nd), fn->enter);
+ nd = mkcall("racefuncexit", T, nil);
+ fn->exit = list(fn->exit, nd);
+
+ if(debug['W']) {
+ snprint(s, sizeof(s), "after racewalk %S", fn->nname->sym);
+ dumplist(s, fn->nbody);
+ snprint(s, sizeof(s), "enter %S", fn->nname->sym);
+ dumplist(s, fn->enter);
+ snprint(s, sizeof(s), "exit %S", fn->nname->sym);
+ dumplist(s, fn->exit);
+ }
+}
+
+static void
+racewalklist(NodeList *l, NodeList **init)
+{
+ NodeList *instr;
+
+ for(; l; l = l->next) {
+ instr = nil;
+ racewalknode(&l->n, &instr, 0, 0);
+ if(init == nil)
+ l->n->ninit = concat(l->n->ninit, instr);
+ else
+ *init = concat(*init, instr);
+ }
+}
+
+// walkexpr and walkstmt combined
+// walks the tree and adds calls to the
+// instrumentation code to top-level (statement) nodes' init
+static void
+racewalknode(Node **np, NodeList **init, int wr, int skip)
+{
+ Node *n, *n1;
+ NodeList *l;
+ NodeList *fini;
+
+ n = *np;
+
+ if(n == N)
+ return;
+
+ if(debug['w'] > 1)
+ dump("racewalk-before", n);
+ setlineno(n);
+ if(init == nil)
+ fatal("racewalk: bad init list");
+ if(init == &n->ninit) {
+ // If init == &n->ninit and n->ninit is non-nil,
+ // racewalknode might append it to itself.
+ // nil it out and handle it separately before putting it back.
+ l = n->ninit;
+ n->ninit = nil;
+ racewalklist(l, nil);
+ racewalknode(&n, &l, wr, skip); // recurse with nil n->ninit
+ appendinit(&n, l);
+ *np = n;
+ return;
+ }
+
+ racewalklist(n->ninit, nil);
+
+ switch(n->op) {
+ default:
+ fatal("racewalk: unknown node type %O", n->op);
+
+ case OASOP:
+ case OAS:
+ case OAS2:
+ case OAS2RECV:
+ case OAS2FUNC:
+ case OAS2MAPR:
+ racewalknode(&n->left, init, 1, 0);
+ racewalknode(&n->right, init, 0, 0);
+ goto ret;
+
+ case OCFUNC:
+ case OVARKILL:
+ // can't matter
+ goto ret;
+
+ case OBLOCK:
+ if(n->list == nil)
+ goto ret;
+
+ switch(n->list->n->op) {
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ // Blocks are used for multiple return function calls.
+ // x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]}
+ // We don't want to instrument between the statements because it will
+ // smash the results.
+ racewalknode(&n->list->n, &n->list->n->ninit, 0, 0);
+ fini = nil;
+ racewalklist(n->list->next, &fini);
+ n->list = concat(n->list, fini);
+ break;
+
+ default:
+ // Ordinary block, for loop initialization or inlined bodies.
+ racewalklist(n->list, nil);
+ break;
+ }
+ goto ret;
+
+ case ODEFER:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case OPROC:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case OCALLINTER:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case OCALLFUNC:
+ // Instrument dst argument of runtime.writebarrier* calls
+ // as we do not instrument runtime code.
+ if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) {
+ // Find the dst argument.
+ // The list can be reordered, so it's not necessary just the first or the second element.
+ for(l = n->list; l; l = l->next) {
+ if(strcmp(n->left->sym->name, "writebarrierfat") == 0) {
+ if(l->n->left->xoffset == widthptr)
+ break;
+ } else {
+ if(l->n->left->xoffset == 0)
+ break;
+ }
+ }
+ if(l == nil)
+ fatal("racewalk: writebarrier no arg");
+ if(l->n->right->op != OADDR)
+ fatal("racewalk: writebarrier bad arg");
+ callinstr(&l->n->right->left, init, 1, 0);
+ }
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case ONOT:
+ case OMINUS:
+ case OPLUS:
+ case OREAL:
+ case OIMAG:
+ case OCOM:
+ racewalknode(&n->left, init, wr, 0);
+ goto ret;
+
+ case ODOTINTER:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case ODOT:
+ racewalknode(&n->left, init, 0, 1);
+ callinstr(&n, init, wr, skip);
+ goto ret;
+
+ case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND
+ racewalknode(&n->left, init, 0, 0);
+ callinstr(&n, init, wr, skip);
+ goto ret;
+
+ case OIND: // *p
+ racewalknode(&n->left, init, 0, 0);
+ callinstr(&n, init, wr, skip);
+ goto ret;
+
+ case OSPTR:
+ case OLEN:
+ case OCAP:
+ racewalknode(&n->left, init, 0, 0);
+ if(istype(n->left->type, TMAP)) {
+ n1 = nod(OCONVNOP, n->left, N);
+ n1->type = ptrto(types[TUINT8]);
+ n1 = nod(OIND, n1, N);
+ typecheck(&n1, Erv);
+ callinstr(&n1, init, 0, skip);
+ }
+ goto ret;
+
+ case OLSH:
+ case ORSH:
+ case OLROT:
+ case OAND:
+ case OANDNOT:
+ case OOR:
+ case OXOR:
+ case OSUB:
+ case OMUL:
+ case OHMUL:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case OADD:
+ case OCOMPLEX:
+ racewalknode(&n->left, init, wr, 0);
+ racewalknode(&n->right, init, wr, 0);
+ goto ret;
+
+ case OANDAND:
+ case OOROR:
+ racewalknode(&n->left, init, wr, 0);
+ // walk has ensured the node has moved to a location where
+ // side effects are safe.
+ // n->right may not be executed,
+ // so instrumentation goes to n->right->ninit, not init.
+ racewalknode(&n->right, &n->right->ninit, wr, 0);
+ goto ret;
+
+ case ONAME:
+ callinstr(&n, init, wr, skip);
+ goto ret;
+
+ case OCONV:
+ racewalknode(&n->left, init, wr, 0);
+ goto ret;
+
+ case OCONVNOP:
+ racewalknode(&n->left, init, wr, 0);
+ goto ret;
+
+ case ODIV:
+ case OMOD:
+ racewalknode(&n->left, init, wr, 0);
+ racewalknode(&n->right, init, wr, 0);
+ goto ret;
+
+ case OINDEX:
+ if(!isfixedarray(n->left->type))
+ racewalknode(&n->left, init, 0, 0);
+ else if(!islvalue(n->left)) {
+ // index of unaddressable array, like Map[k][i].
+ racewalknode(&n->left, init, wr, 0);
+ racewalknode(&n->right, init, 0, 0);
+ goto ret;
+ }
+ racewalknode(&n->right, init, 0, 0);
+ if(n->left->type->etype != TSTRING)
+ callinstr(&n, init, wr, skip);
+ goto ret;
+
+ case OSLICE:
+ case OSLICEARR:
+ case OSLICE3:
+ case OSLICE3ARR:
+ // Seems to only lead to double instrumentation.
+ //racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ case OADDR:
+ racewalknode(&n->left, init, 0, 1);
+ goto ret;
+
+ case OEFACE:
+ racewalknode(&n->left, init, 0, 0);
+ racewalknode(&n->right, init, 0, 0);
+ goto ret;
+
+ case OITAB:
+ racewalknode(&n->left, init, 0, 0);
+ goto ret;
+
+ // should not appear in AST by now
+ case OSEND:
+ case ORECV:
+ case OCLOSE:
+ case ONEW:
+ case OXCASE:
+ case OXFALL:
+ case OCASE:
+ case OPANIC:
+ case ORECOVER:
+ case OCONVIFACE:
+ case OCMPIFACE:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case OCALL:
+ case OCOPY:
+ case OAPPEND:
+ case ORUNESTR:
+ case OARRAYBYTESTR:
+ case OARRAYRUNESTR:
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ case OINDEXMAP: // lowered to call
+ case OCMPSTR:
+ case OADDSTR:
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ case OAS2DOTTYPE:
+ case OCALLPART: // lowered to PTRLIT
+ case OCLOSURE: // lowered to PTRLIT
+ case ORANGE: // lowered to ordinary for loop
+ case OARRAYLIT: // lowered to assignments
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ yyerror("racewalk: %O must be lowered by now", n->op);
+ goto ret;
+
+ // impossible nodes: only appear in backend.
+ case ORROTC:
+ case OEXTEND:
+ yyerror("racewalk: %O cannot exist now", n->op);
+ goto ret;
+
+ // just do generic traversal
+ case OFOR:
+ case OIF:
+ case OCALLMETH:
+ case ORETURN:
+ case ORETJMP:
+ case OSWITCH:
+ case OSELECT:
+ case OEMPTY:
+ case OBREAK:
+ case OCONTINUE:
+ case OFALL:
+ case OGOTO:
+ case OLABEL:
+ goto ret;
+
+ // does not require instrumentation
+ case OPRINT: // don't bother instrumenting it
+ case OPRINTN: // don't bother instrumenting it
+ case OCHECKNIL: // always followed by a read.
+ case OPARAM: // it appears only in fn->exit to copy heap params back
+ case OCLOSUREVAR:// immutable pointer to captured variable
+ case ODOTMETH: // either part of CALLMETH or CALLPART (lowered to PTRLIT)
+ case OINDREG: // at this stage, only n(SP) nodes from nodarg
+ case ODCL: // declarations (without value) cannot be races
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OTYPE:
+ case ONONAME:
+ case OLITERAL:
+ case OSLICESTR: // always preceded by bounds checking, avoid double instrumentation.
+ case OTYPESW: // ignored by code generation, do not instrument.
+ goto ret;
+ }
+
+ret:
+ if(n->op != OBLOCK) // OBLOCK is handled above in a special way.
+ racewalklist(n->list, init);
+ if(n->ntest != N)
+ racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
+ if(n->nincr != N)
+ racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
+ racewalklist(n->nbody, nil);
+ racewalklist(n->nelse, nil);
+ racewalklist(n->rlist, nil);
+ *np = n;
+}
+
+static int
+isartificial(Node *n)
+{
+ // compiler-emitted artificial things that we do not want to instrument,
+ // cant' possibly participate in a data race.
+ if(n->op == ONAME && n->sym != S && n->sym->name != nil) {
+ if(strcmp(n->sym->name, "_") == 0)
+ return 1;
+ // autotmp's are always local
+ if(strncmp(n->sym->name, "autotmp_", sizeof("autotmp_")-1) == 0)
+ return 1;
+ // statictmp's are read-only
+ if(strncmp(n->sym->name, "statictmp_", sizeof("statictmp_")-1) == 0)
+ return 1;
+ // go.itab is accessed only by the compiler and runtime (assume safe)
+ if(n->sym->pkg && n->sym->pkg->name && strcmp(n->sym->pkg->name, "go.itab") == 0)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+callinstr(Node **np, NodeList **init, int wr, int skip)
+{
+ Node *f, *b, *n;
+ Type *t;
+ int class, hascalls;
+
+ n = *np;
+ //print("callinstr for %+N [ %O ] etype=%E class=%d\n",
+ // n, n->op, n->type ? n->type->etype : -1, n->class);
+
+ if(skip || n->type == T || n->type->etype >= TIDEAL)
+ return 0;
+ t = n->type;
+ if(isartificial(n))
+ return 0;
+
+ b = basenod(n);
+ // it skips e.g. stores to ... parameter array
+ if(isartificial(b))
+ return 0;
+ class = b->class;
+ // BUG: we _may_ want to instrument PAUTO sometimes
+ // e.g. if we've got a local variable/method receiver
+ // that has got a pointer inside. Whether it points to
+ // the heap or not is impossible to know at compile time
+ if((class&PHEAP) || class == PPARAMREF || class == PEXTERN
+ || b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {
+ hascalls = 0;
+ foreach(n, hascallspred, &hascalls);
+ if(hascalls) {
+ n = detachexpr(n, init);
+ *np = n;
+ }
+ n = treecopy(n);
+ makeaddable(n);
+ if(t->etype == TSTRUCT || isfixedarray(t)) {
+ f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n),
+ nodintconst(t->width));
+ } else
+ f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
+ *init = list(*init, f);
+ return 1;
+ }
+ return 0;
+}
+
+// makeaddable returns a node whose memory location is the
+// same as n, but which is addressable in the Go language
+// sense.
+// This is different from functions like cheapexpr that may make
+// a copy of their argument.
+static void
+makeaddable(Node *n)
+{
+ // The arguments to uintptraddr technically have an address but
+ // may not be addressable in the Go sense: for example, in the case
+ // of T(v).Field where T is a struct type and v is
+ // an addressable value.
+ switch(n->op) {
+ case OINDEX:
+ if(isfixedarray(n->left->type))
+ makeaddable(n->left);
+ break;
+ case ODOT:
+ case OXDOT:
+ // Turn T(v).Field into v.Field
+ if(n->left->op == OCONVNOP)
+ n->left = n->left->left;
+ makeaddable(n->left);
+ break;
+ case ODOTPTR:
+ default:
+ // nothing to do
+ break;
+ }
+}
+
+static Node*
+uintptraddr(Node *n)
+{
+ Node *r;
+
+ r = nod(OADDR, n, N);
+ r->bounded = 1;
+ r = conv(r, types[TUNSAFEPTR]);
+ r = conv(r, types[TUINTPTR]);
+ return r;
+}
+
+// basenod returns the simplest child node of n pointing to the same
+// memory area.
+static Node*
+basenod(Node *n)
+{
+ for(;;) {
+ if(n->op == ODOT || n->op == OXDOT || n->op == OCONVNOP || n->op == OCONV || n->op == OPAREN) {
+ n = n->left;
+ continue;
+ }
+ if(n->op == OINDEX && isfixedarray(n->type)) {
+ n = n->left;
+ continue;
+ }
+ break;
+ }
+ return n;
+}
+
+static Node*
+detachexpr(Node *n, NodeList **init)
+{
+ Node *addr, *as, *ind, *l;
+
+ addr = nod(OADDR, n, N);
+ l = temp(ptrto(n->type));
+ as = nod(OAS, l, addr);
+ typecheck(&as, Etop);
+ walkexpr(&as, init);
+ *init = list(*init, as);
+ ind = nod(OIND, l, N);
+ typecheck(&ind, Erv);
+ walkexpr(&ind, init);
+ return ind;
+}
+
+static void
+foreachnode(Node *n, void(*f)(Node*, void*), void *c)
+{
+ if(n)
+ f(n, c);
+}
+
+static void
+foreachlist(NodeList *l, void(*f)(Node*, void*), void *c)
+{
+ for(; l; l = l->next)
+ foreachnode(l->n, f, c);
+}
+
+static void
+foreach(Node *n, void(*f)(Node*, void*), void *c)
+{
+ foreachlist(n->ninit, f, c);
+ foreachnode(n->left, f, c);
+ foreachnode(n->right, f, c);
+ foreachlist(n->list, f, c);
+ foreachnode(n->ntest, f, c);
+ foreachnode(n->nincr, f, c);
+ foreachlist(n->nbody, f, c);
+ foreachlist(n->nelse, f, c);
+ foreachlist(n->rlist, f, c);
+}
+
+static void
+hascallspred(Node *n, void *c)
+{
+ switch(n->op) {
+ case OCALL:
+ case OCALLFUNC:
+ case OCALLMETH:
+ case OCALLINTER:
+ (*(int*)c)++;
+ }
+}
+
+// appendinit is like addinit in subr.c
+// but appends rather than prepends.
+static void
+appendinit(Node **np, NodeList *init)
+{
+ Node *n;
+
+ if(init == nil)
+ return;
+
+ n = *np;
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ // There may be multiple refs to this node;
+ // introduce OCONVNOP to hold init list.
+ n = nod(OCONVNOP, n, N);
+ n->type = n->left->type;
+ n->typecheck = 1;
+ *np = n;
+ break;
+ }
+ n->ninit = concat(n->ninit, init);
+ n->ullman = UINF;
+}
+
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
new file mode 100644
index 0000000..4ed4528
--- /dev/null
+++ b/src/cmd/gc/range.c
@@ -0,0 +1,296 @@
+// 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 <u.h>
+#include <libc.h>
+#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 %lN", n->right);
+ goto out;
+
+ case TARRAY:
+ t1 = types[TINT];
+ t2 = t->type;
+ break;
+
+ case TMAP:
+ t1 = t->down;
+ t2 = t->type;
+ break;
+
+ case TCHAN:
+ if(!(t->chan & Crecv)) {
+ yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type);
+ goto out;
+ }
+ t1 = t->type;
+ t2 = nil;
+ if(count(n->list) == 2)
+ goto toomany;
+ break;
+
+ case TSTRING:
+ t1 = types[TINT];
+ t2 = runetype;
+ break;
+ }
+
+ if(count(n->list) > 2) {
+ toomany:
+ yyerror("too many variables in range");
+ }
+
+ v1 = N;
+ if(n->list)
+ v1 = n->list->n;
+ v2 = N;
+ if(n->list && n->list->next)
+ v2 = n->list->next->n;
+
+ // this is not only a optimization but also a requirement in the spec.
+ // "if the second iteration variable is the blank identifier, the range
+ // clause is equivalent to the same clause with only the first variable
+ // present."
+ if(isblank(v2)) {
+ if(v1 != N)
+ n->list = list1(v1);
+ v2 = N;
+ }
+
+ if(v1) {
+ if(v1->defn == n)
+ v1->type = t1;
+ else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
+ yyerror("cannot assign type %T to %lN 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 %lN 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;
+ Node *keyname, *valname;
+ Node *key, *val;
+ NodeList *body, *init;
+ Type *th, *t;
+ int lno;
+
+ t = n->type;
+ init = nil;
+
+ a = n->right;
+ lno = setlineno(a);
+
+ v1 = N;
+ if(n->list)
+ v1 = n->list->n;
+ v2 = N;
+ if(n->list && n->list->next && !isblank(n->list->next->n))
+ v2 = n->list->next->n;
+ // n->list has no meaning anymore, clear it
+ // to avoid erroneous processing by racewalk.
+ n->list = nil;
+ hv2 = N;
+
+ switch(t->etype) {
+ default:
+ fatal("walkrange");
+
+ case TARRAY:
+ // orderstmt arranged for a copy of the array/slice variable if needed.
+ ha = a;
+ hv1 = temp(types[TINT]);
+ hn = temp(types[TINT]);
+ hp = nil;
+
+ init = list(init, nod(OAS, hv1, N));
+ init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
+ if(v2) {
+ hp = temp(ptrto(n->type->type));
+ tmp = nod(OINDEX, ha, nodintconst(0));
+ tmp->bounded = 1;
+ init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
+ }
+
+ n->ntest = nod(OLT, hv1, hn);
+ n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)));
+ if(v1 == N)
+ body = nil;
+ else if(v2 == N)
+ body = list1(nod(OAS, v1, hv1));
+ else {
+ a = nod(OAS2, N, N);
+ a->list = list(list1(v1), v2);
+ a->rlist = list(list1(hv1), nod(OIND, hp, N));
+ body = list1(a);
+
+ // Advance pointer as part of increment.
+ // We used to advance the pointer before executing the loop body,
+ // but doing so would make the pointer point past the end of the
+ // array during the final iteration, possibly causing another unrelated
+ // piece of memory not to be garbage collected until the loop finished.
+ // Advancing during the increment ensures that the pointer p only points
+ // pass the end of the array during the final "p++; i++; if(i >= len(x)) break;",
+ // after which p is dead, so it cannot confuse the collector.
+ tmp = nod(OADD, hp, nodintconst(t->type->width));
+ tmp->type = hp->type;
+ tmp->typecheck = 1;
+ tmp->right->type = types[tptr];
+ tmp->right->typecheck = 1;
+ a = nod(OAS, hp, tmp);
+ typecheck(&a, Etop);
+ n->nincr->ninit = list1(a);
+ }
+ break;
+
+ case TMAP:
+ // orderstmt allocated the iterator for us.
+ // we only use a once, so no copy needed.
+ ha = a;
+ th = hiter(t);
+ hit = n->alloc;
+ hit->type = th;
+ n->left = N;
+ keyname = newname(th->type->sym); // depends on layout of iterator struct. See reflect.c:hiter
+ valname = newname(th->type->down->sym); // ditto
+
+ 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(ODOT, hit, keyname), nodnil());
+
+ fn = syslook("mapiternext", 1);
+ argtype(fn, th);
+ n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
+
+ key = nod(ODOT, hit, keyname);
+ key = nod(OIND, key, N);
+ if(v1 == N)
+ body = nil;
+ else if(v2 == N) {
+ body = list1(nod(OAS, v1, key));
+ } else {
+ val = nod(ODOT, hit, valname);
+ val = nod(OIND, val, N);
+ a = nod(OAS2, N, N);
+ a->list = list(list1(v1), v2);
+ a->rlist = list(list1(key), val);
+ body = list1(a);
+ }
+ break;
+
+ case TCHAN:
+ // orderstmt arranged for a copy of the channel variable.
+ ha = a;
+ n->ntest = N;
+
+ hv1 = temp(t->type);
+ hv1->typecheck = 1;
+ if(haspointers(t->type))
+ init = list(init, nod(OAS, hv1, N));
+ hb = temp(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);
+ if(v1 == N)
+ body = nil;
+ else
+ body = list1(nod(OAS, v1, hv1));
+ break;
+
+ case TSTRING:
+ // orderstmt arranged for a copy of the string variable.
+ ha = a;
+
+ ohv1 = temp(types[TINT]);
+
+ hv1 = temp(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 = temp(runetype);
+ 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 = nil;
+ if(v1 != N)
+ 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 0000000..8788a67
--- /dev/null
+++ b/src/cmd/gc/reflect.c
@@ -0,0 +1,1577 @@
+// 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 "go.h"
+#include "../ld/textflag.h"
+#include "../../runtime/mgc0.h"
+#include "../../runtime/typekind.h"
+
+/*
+ * runtime interface and reflection data structures
+ */
+
+static NodeList* signatlist;
+static Sym* dtypesym(Type*);
+static Sym* weaktypesym(Type*);
+static Sym* dalgsym(Type*);
+static int usegcprog(Type*);
+static void gengcprog(Type*, Sym**, Sym**);
+static void gengcmask(Type*, uint8[16]);
+
+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;
+}
+
+// Builds a type respresenting a Bucket structure for
+// the given map type. This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../runtime/hashmap.c!
+enum {
+ BUCKETSIZE = 8,
+ MAXKEYSIZE = 128,
+ MAXVALSIZE = 128,
+};
+
+static Type*
+mapbucket(Type *t)
+{
+ Type *keytype, *valtype;
+ Type *bucket;
+ Type *overflowfield, *keysfield, *valuesfield;
+ int32 offset;
+
+ if(t->bucket != T)
+ return t->bucket;
+
+ keytype = t->down;
+ valtype = t->type;
+ dowidth(keytype);
+ dowidth(valtype);
+ if(keytype->width > MAXKEYSIZE)
+ keytype = ptrto(keytype);
+ if(valtype->width > MAXVALSIZE)
+ valtype = ptrto(valtype);
+
+ bucket = typ(TSTRUCT);
+ bucket->noalg = 1;
+
+ // The first field is: uint8 topbits[BUCKETSIZE].
+ // We don't need to encode it as GC doesn't care about it.
+ offset = BUCKETSIZE * 1;
+
+ keysfield = typ(TFIELD);
+ keysfield->type = typ(TARRAY);
+ keysfield->type->type = keytype;
+ keysfield->type->bound = BUCKETSIZE;
+ keysfield->type->width = BUCKETSIZE * keytype->width;
+ keysfield->width = offset;
+ keysfield->sym = mal(sizeof(Sym));
+ keysfield->sym->name = "keys";
+ offset += BUCKETSIZE * keytype->width;
+
+ valuesfield = typ(TFIELD);
+ valuesfield->type = typ(TARRAY);
+ valuesfield->type->type = valtype;
+ valuesfield->type->bound = BUCKETSIZE;
+ valuesfield->type->width = BUCKETSIZE * valtype->width;
+ valuesfield->width = offset;
+ valuesfield->sym = mal(sizeof(Sym));
+ valuesfield->sym->name = "values";
+ offset += BUCKETSIZE * valtype->width;
+
+ overflowfield = typ(TFIELD);
+ overflowfield->type = ptrto(bucket);
+ overflowfield->width = offset; // "width" is offset in structure
+ overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
+ overflowfield->sym->name = "overflow";
+ offset += widthptr;
+
+ // Pad to the native integer alignment.
+ // This is usually the same as widthptr; the exception (as usual) is nacl/amd64.
+ if(widthreg > widthptr)
+ offset += widthreg - widthptr;
+
+ // link up fields
+ bucket->type = keysfield;
+ keysfield->down = valuesfield;
+ valuesfield->down = overflowfield;
+ overflowfield->down = T;
+
+ bucket->width = offset;
+ bucket->local = t->local;
+ t->bucket = bucket;
+ bucket->map = t;
+ return bucket;
+}
+
+// Builds a type respresenting a Hmap structure for
+// the given map type. This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../runtime/hashmap.go!
+static Type*
+hmap(Type *t)
+{
+ Type *h, *bucket;
+ Type *bucketsfield, *oldbucketsfield;
+ int32 offset;
+
+ if(t->hmap != T)
+ return t->hmap;
+
+ bucket = mapbucket(t);
+ h = typ(TSTRUCT);
+ h->noalg = 1;
+
+ offset = widthint; // count
+ offset += 4; // flags
+ offset += 4; // hash0
+ offset += 1; // B
+ offset = (offset + widthptr - 1) / widthptr * widthptr;
+
+ bucketsfield = typ(TFIELD);
+ bucketsfield->type = ptrto(bucket);
+ bucketsfield->width = offset;
+ bucketsfield->sym = mal(sizeof(Sym));
+ bucketsfield->sym->name = "buckets";
+ offset += widthptr;
+
+ oldbucketsfield = typ(TFIELD);
+ oldbucketsfield->type = ptrto(bucket);
+ oldbucketsfield->width = offset;
+ oldbucketsfield->sym = mal(sizeof(Sym));
+ oldbucketsfield->sym->name = "oldbuckets";
+ offset += widthptr;
+
+ offset += widthptr; // nevacuate (last field in Hmap)
+
+ // link up fields
+ h->type = bucketsfield;
+ bucketsfield->down = oldbucketsfield;
+ oldbucketsfield->down = T;
+
+ h->width = offset;
+ h->local = t->local;
+ t->hmap = h;
+ h->map = t;
+ return h;
+}
+
+Type*
+hiter(Type *t)
+{
+ int32 n, off;
+ Type *field[7];
+ Type *i;
+
+ if(t->hiter != T)
+ return t->hiter;
+
+ // build a struct:
+ // hash_iter {
+ // key *Key
+ // val *Value
+ // t *MapType
+ // h *Hmap
+ // buckets *Bucket
+ // bptr *Bucket
+ // other [4]uintptr
+ // }
+ // must match ../../runtime/hashmap.c:hash_iter.
+ field[0] = typ(TFIELD);
+ field[0]->type = ptrto(t->down);
+ field[0]->sym = mal(sizeof(Sym));
+ field[0]->sym->name = "key";
+
+ field[1] = typ(TFIELD);
+ field[1]->type = ptrto(t->type);
+ field[1]->sym = mal(sizeof(Sym));
+ field[1]->sym->name = "val";
+
+ field[2] = typ(TFIELD);
+ field[2]->type = ptrto(types[TUINT8]); // TODO: is there a Type type?
+ field[2]->sym = mal(sizeof(Sym));
+ field[2]->sym->name = "t";
+
+ field[3] = typ(TFIELD);
+ field[3]->type = ptrto(hmap(t));
+ field[3]->sym = mal(sizeof(Sym));
+ field[3]->sym->name = "h";
+
+ field[4] = typ(TFIELD);
+ field[4]->type = ptrto(mapbucket(t));
+ field[4]->sym = mal(sizeof(Sym));
+ field[4]->sym->name = "buckets";
+
+ field[5] = typ(TFIELD);
+ field[5]->type = ptrto(mapbucket(t));
+ field[5]->sym = mal(sizeof(Sym));
+ field[5]->sym->name = "bptr";
+
+ // all other non-pointer fields
+ field[6] = typ(TFIELD);
+ field[6]->type = typ(TARRAY);
+ field[6]->type->type = types[TUINTPTR];
+ field[6]->type->bound = 4;
+ field[6]->type->width = 4 * widthptr;
+ field[6]->sym = mal(sizeof(Sym));
+ field[6]->sym->name = "other";
+
+ // build iterator struct holding the above fields
+ i = typ(TSTRUCT);
+ i->noalg = 1;
+ i->type = field[0];
+ off = 0;
+ for(n = 0; n < 6; n++) {
+ field[n]->down = field[n+1];
+ field[n]->width = off;
+ off += field[n]->type->width;
+ }
+ field[6]->down = T;
+ off += field[6]->type->width;
+ if(off != 10 * widthptr)
+ yyerror("hash_iter size not correct %d %d", off, 10 * widthptr);
+ t->hiter = i;
+ i->map = t;
+ return i;
+}
+
+/*
+ * 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);
+ }
+
+ t = functype(N, in, out);
+ if(f->nname) {
+ // Link to name of original method function.
+ t->nname = f->nname;
+ }
+ return t;
+}
+
+/*
+ * 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;
+
+ // method type
+ mt = methtype(t, 0);
+ if(mt == T)
+ return nil;
+ expandmeth(mt);
+
+ // type stored in interface word
+ it = t;
+ if(!isdirectiface(it))
+ it = ptrto(t);
+
+ // make list of methods for t,
+ // generating code if necessary.
+ a = nil;
+ for(f=mt->xmethod; f; f=f->down) {
+ if(f->etype != TFIELD)
+ fatal("methods: not field %T", f);
+ if (f->type->etype != TFUNC || f->type->thistuple == 0)
+ fatal("non-method on %T method %S %T\n", mt, f->sym, f);
+ if (!getthisx(f->type)->type)
+ fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f);
+ if(f->nointerface)
+ continue;
+
+ 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) {
+ compiling_wrappers = 1;
+ genwrapper(it, f, a->isym, 1);
+ compiling_wrappers = 0;
+ }
+ }
+
+ if(!(a->tsym->flags & SymSiggen)) {
+ a->tsym->flags |= SymSiggen;
+ if(!eqtype(this, t)) {
+ compiling_wrappers = 1;
+ genwrapper(t, f, a->tsym, 0);
+ compiling_wrappers = 0;
+ }
+ }
+ }
+
+ 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;
+
+ all = nil;
+ last = 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 non-blank methods.
+ if(isblanksym(method))
+ 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;
+ genwrapper(t, f, isym, 0);
+ }
+ }
+ 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, DUPOK|RODATA);
+}
+
+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
+ * ../../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
+ off = rnd(off, widthptr);
+ 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] && t != errortype)
+ 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*widthint);
+ ot = duintxx(s, ot, n, widthint);
+ ot = duintxx(s, ot, n, widthint);
+
+ // methods
+ for(a=m; a; a=a->link) {
+ // method
+ // ../../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;
+}
+
+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,
+};
+
+int
+haspointers(Type *t)
+{
+ Type *t1;
+ int ret;
+
+ if(t->haspointers != 0)
+ return t->haspointers - 1;
+
+ 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 TCOMPLEX64:
+ case TCOMPLEX128:
+ case TBOOL:
+ ret = 0;
+ break;
+ case TARRAY:
+ if(t->bound < 0) { // slice
+ ret = 1;
+ break;
+ }
+ if(t->bound == 0) { // empty array
+ ret = 0;
+ break;
+ }
+ ret = haspointers(t->type);
+ break;
+ case TSTRUCT:
+ ret = 0;
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ if(haspointers(t1->type)) {
+ ret = 1;
+ break;
+ }
+ }
+ break;
+ case TSTRING:
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TINTER:
+ case TCHAN:
+ case TMAP:
+ case TFUNC:
+ default:
+ ret = 1;
+ break;
+ }
+
+ t->haspointers = 1+ret;
+ return ret;
+}
+
+/*
+ * commonType
+ * ../../runtime/type.go:/commonType
+ */
+static int
+dcommontype(Sym *s, int ot, Type *t)
+{
+ int i, alg, sizeofAlg, gcprog;
+ Sym *sptr, *algsym, *zero, *gcprog0, *gcprog1, *sbits;
+ uint8 gcmask[16];
+ static Sym *algarray;
+ uint64 x1, x2;
+ char *p;
+
+ if(ot != 0)
+ fatal("dcommontype %d", ot);
+
+ sizeofAlg = 2*widthptr;
+ if(algarray == nil)
+ algarray = pkglookup("algarray", runtimepkg);
+ dowidth(t);
+ alg = algtype(t);
+ algsym = S;
+ if(alg < 0)
+ algsym = dalgsym(t);
+
+ if(t->sym != nil && !isptr[t->etype])
+ sptr = dtypesym(ptrto(t));
+ else
+ sptr = weaktypesym(ptrto(t));
+
+ // All (non-reflect-allocated) Types share the same zero object.
+ // Each place in the compiler where a pointer to the zero object
+ // might be returned by a runtime call (map access return value,
+ // 2-arg type cast) declares the size of the zerovalue it needs.
+ // The linker magically takes the max of all the sizes.
+ zero = pkglookup("zerovalue", runtimepkg);
+
+ // We use size 0 here so we get the pointer to the zero value,
+ // but don't allocate space for the zero value unless we need it.
+ // TODO: how do we get this symbol into bss? We really want
+ // a read-only bss, but I don't think such a thing exists.
+
+ // ../../pkg/reflect/type.go:/^type.commonType
+ // actual type structure
+ // type commonType struct {
+ // size uintptr
+ // hash uint32
+ // _ uint8
+ // align uint8
+ // fieldAlign uint8
+ // kind uint8
+ // alg unsafe.Pointer
+ // gc unsafe.Pointer
+ // string *string
+ // *extraType
+ // ptrToThis *Type
+ // zero unsafe.Pointer
+ // }
+ ot = duintptr(s, ot, t->width);
+ ot = duint32(s, ot, typehash(t));
+ ot = duint8(s, ot, 0); // unused
+
+ // runtime (and common sense) expects alignment to be a power of two.
+ i = t->align;
+ if(i == 0)
+ i = 1;
+ if((i&(i-1)) != 0)
+ fatal("invalid alignment %d for %T", t->align, t);
+ ot = duint8(s, ot, t->align); // align
+ ot = duint8(s, ot, t->align); // fieldAlign
+
+ gcprog = usegcprog(t);
+ i = kinds[t->etype];
+ if(t->etype == TARRAY && t->bound < 0)
+ i = KindSlice;
+ if(!haspointers(t))
+ i |= KindNoPointers;
+ if(isdirectiface(t))
+ i |= KindDirectIface;
+ if(gcprog)
+ i |= KindGCProg;
+ ot = duint8(s, ot, i); // kind
+ if(alg >= 0)
+ ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
+ else
+ ot = dsymptr(s, ot, algsym, 0);
+ // gc
+ if(gcprog) {
+ gengcprog(t, &gcprog0, &gcprog1);
+ if(gcprog0 != S)
+ ot = dsymptr(s, ot, gcprog0, 0);
+ else
+ ot = duintptr(s, ot, 0);
+ ot = dsymptr(s, ot, gcprog1, 0);
+ } else {
+ gengcmask(t, gcmask);
+ x1 = 0;
+ for(i=0; i<8; i++)
+ x1 = x1<<8 | gcmask[i];
+ if(widthptr == 4) {
+ p = smprint("gcbits.%#016llux", x1);
+ } else {
+ x2 = 0;
+ for(i=0; i<8; i++)
+ x2 = x2<<8 | gcmask[i+8];
+ p = smprint("gcbits.%#016llux%016llux", x1, x2);
+ }
+ sbits = pkglookup(p, runtimepkg);
+ if((sbits->flags & SymUniq) == 0) {
+ sbits->flags |= SymUniq;
+ for(i = 0; i < 2*widthptr; i++)
+ duint8(sbits, i, gcmask[i]);
+ ggloblsym(sbits, 2*widthptr, DUPOK|RODATA);
+ }
+ ot = dsymptr(s, ot, sbits, 0);
+ ot = duintptr(s, ot, 0);
+ }
+ p = smprint("%-uT", t);
+ //print("dcommontype: %s\n", p);
+ 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
+ ot = dsymptr(s, ot, zero, 0); // ptr to zero value
+ return ot;
+}
+
+Sym*
+typesym(Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%-T", t);
+ s = pkglookup(p, typepkg);
+ //print("typesym: %s -> %+S\n", p, s);
+ free(p);
+ return s;
+}
+
+Sym*
+tracksym(Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%-T.%s", t->outer, t->sym->name);
+ s = pkglookup(p, trackpkg);
+ free(p);
+ return s;
+}
+
+Sym*
+typelinksym(Type *t)
+{
+ char *p;
+ Sym *s;
+
+ // %-uT is what the generated Type's string field says.
+ // It uses (ambiguous) package names instead of import paths.
+ // %-T is the complete, unambiguous type name.
+ // We want the types to end up sorted by string field,
+ // so use that first in the name, and then add :%-T to
+ // disambiguate. The names are a little long but they are
+ // discarded by the linker and do not end up in the symbol
+ // table of the final binary.
+ p = smprint("%-uT/%-T", t, t);
+ s = pkglookup(p, typelinkpkg);
+ //print("typelinksym: %s -> %+S\n", p, s);
+ free(p);
+ return s;
+}
+
+Sym*
+typesymprefix(char *prefix, Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%s.%-T", prefix, t);
+ s = pkglookup(p, typepkg);
+ //print("algsym: %s -> %+S\n", p, s);
+ free(p);
+ return s;
+}
+
+Sym*
+typenamesym(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;
+ n->typecheck = 1;
+ s->def = n;
+
+ signatlist = list(signatlist, typenod(t));
+ }
+ return s->def->sym;
+}
+
+Node*
+typename(Type *t)
+{
+ Sym *s;
+ Node *n;
+
+ s = typenamesym(t);
+ n = nod(OADDR, s->def, N);
+ n->type = ptrto(s->def->type);
+ n->addable = 1;
+ n->ullman = 2;
+ n->typecheck = 1;
+ return n;
+}
+
+static Sym*
+weaktypesym(Type *t)
+{
+ char *p;
+ Sym *s;
+
+ p = smprint("%-T", t);
+ s = pkglookup(p, weaktypepkg);
+ //print("weaktypesym: %s -> %+S\n", p, s);
+ free(p);
+ return s;
+}
+
+static Sym*
+dtypesym(Type *t)
+{
+ int ot, xt, n, isddd, dupok;
+ Sym *s, *s1, *s2, *s3, *s4, *slink;
+ Sig *a, *m;
+ Type *t1, *tbase, *t2;
+
+ // Replace byte, rune aliases with real type.
+ // They've been separate internally to make error messages
+ // better, but we have to merge them in the reflect tables.
+ if(t == bytetype || t == runetype)
+ t = types[t->etype];
+
+ 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 = 0;
+ if(tbase->sym == S)
+ dupok = DUPOK;
+
+ if(compiling_runtime &&
+ (tbase == types[tbase->etype] ||
+ tbase == bytetype ||
+ tbase == runetype ||
+ tbase == errortype)) { // 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 - 3*widthptr;
+ break;
+
+ case TARRAY:
+ if(t->bound >= 0) {
+ // ../../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 - 3*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ ot = dsymptr(s, ot, s2, 0);
+ ot = duintptr(s, ot, t->bound);
+ } else {
+ // ../../runtime/type.go:/SliceType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 3*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ }
+ break;
+
+ case TCHAN:
+ // ../../runtime/type.go:/ChanType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 3*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 - 3*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*widthint));
+ n = t->thistuple + t->intuple;
+ ot = duintxx(s, ot, n, widthint);
+ ot = duintxx(s, ot, n, widthint);
+ ot = dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr);
+ ot = duintxx(s, ot, t->outtuple, widthint);
+ ot = duintxx(s, ot, t->outtuple, widthint);
+
+ // 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++;
+ }
+
+ // ../../runtime/type.go:/InterfaceType
+ ot = dcommontype(s, ot, t);
+ xt = ot - 3*widthptr;
+ ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
+ ot = duintxx(s, ot, n, widthint);
+ ot = duintxx(s, ot, n, widthint);
+ for(a=m; a; a=a->link) {
+ // ../../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:
+ // ../../runtime/type.go:/MapType
+ s1 = dtypesym(t->down);
+ s2 = dtypesym(t->type);
+ s3 = dtypesym(mapbucket(t));
+ s4 = dtypesym(hmap(t));
+ ot = dcommontype(s, ot, t);
+ xt = ot - 3*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ ot = dsymptr(s, ot, s2, 0);
+ ot = dsymptr(s, ot, s3, 0);
+ ot = dsymptr(s, ot, s4, 0);
+ if(t->down->width > MAXKEYSIZE) {
+ ot = duint8(s, ot, widthptr);
+ ot = duint8(s, ot, 1); // indirect
+ } else {
+ ot = duint8(s, ot, t->down->width);
+ ot = duint8(s, ot, 0); // not indirect
+ }
+ if(t->type->width > MAXVALSIZE) {
+ ot = duint8(s, ot, widthptr);
+ ot = duint8(s, ot, 1); // indirect
+ } else {
+ ot = duint8(s, ot, t->type->width);
+ ot = duint8(s, ot, 0); // not indirect
+ }
+ ot = duint16(s, ot, mapbucket(t)->width);
+ break;
+
+ case TPTR32:
+ case TPTR64:
+ if(t->type->etype == TANY) {
+ // ../../runtime/type.go:/UnsafePointerType
+ ot = dcommontype(s, ot, t);
+ break;
+ }
+ // ../../runtime/type.go:/PtrType
+ s1 = dtypesym(t->type);
+ ot = dcommontype(s, ot, t);
+ xt = ot - 3*widthptr;
+ ot = dsymptr(s, ot, s1, 0);
+ break;
+
+ case TSTRUCT:
+ // ../../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 - 3*widthptr;
+ ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
+ ot = duintxx(s, ot, n, widthint);
+ ot = duintxx(s, ot, n, widthint);
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ // ../../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);
+ if(t1->type->sym != S && t1->type->sym->pkg == builtinpkg)
+ ot = dgopkgpath(s, ot, localpkg);
+ else
+ 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|RODATA);
+
+ // generate typelink.foo pointing at s = type.foo.
+ // The linker will leave a table of all the typelinks for
+ // types in the binary, so reflect can find them.
+ // We only need the link for unnamed composites that
+ // we want be able to find.
+ if(t->sym == S) {
+ switch(t->etype) {
+ case TARRAY:
+ case TCHAN:
+ case TMAP:
+ slink = typelinksym(t);
+ dsymptr(slink, 0, s, 0);
+ ggloblsym(slink, widthptr, dupok|RODATA);
+ }
+ }
+
+ 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]));
+
+ // emit type structs for error and func(error) string.
+ // The latter is the type of an auto-generated wrapper.
+ dtypesym(ptrto(errortype));
+ dtypesym(functype(nil,
+ list1(nod(ODCLFIELD, N, typenod(errortype))),
+ list1(nod(ODCLFIELD, N, typenod(types[TSTRING])))));
+
+ // add paths for runtime and main, which 6l imports implicitly.
+ dimportpath(runtimepkg);
+ if(flag_race)
+ dimportpath(racepkg);
+ dimportpath(mkpkg(strlit("main")));
+ }
+}
+
+static Sym*
+dalgsym(Type *t)
+{
+ int ot;
+ Sym *s, *hash, *hashfunc, *eq, *eqfunc;
+
+ // dalgsym is only called for a type that needs an algorithm table,
+ // which implies that the type is comparable (or else it would use ANOEQ).
+
+ s = typesymprefix(".alg", t);
+ hash = typesymprefix(".hash", t);
+ genhash(hash, t);
+ eq = typesymprefix(".eq", t);
+ geneq(eq, t);
+
+ // make Go funcs (closures) for calling hash and equal from Go
+ hashfunc = typesymprefix(".hashfunc", t);
+ dsymptr(hashfunc, 0, hash, 0);
+ ggloblsym(hashfunc, widthptr, DUPOK|RODATA);
+ eqfunc = typesymprefix(".eqfunc", t);
+ dsymptr(eqfunc, 0, eq, 0);
+ ggloblsym(eqfunc, widthptr, DUPOK|RODATA);
+
+ // ../../runtime/alg.go:/typeAlg
+ ot = 0;
+ ot = dsymptr(s, ot, hashfunc, 0);
+ ot = dsymptr(s, ot, eqfunc, 0);
+
+ ggloblsym(s, ot, DUPOK|RODATA);
+ return s;
+}
+
+static int
+usegcprog(Type *t)
+{
+ vlong size, nptr;
+
+ if(!haspointers(t))
+ return 0;
+ if(t->width == BADWIDTH)
+ dowidth(t);
+ // Calculate size of the unrolled GC mask.
+ nptr = (t->width+widthptr-1)/widthptr;
+ size = nptr;
+ if(size%2)
+ size *= 2; // repeated
+ size = size*gcBits/8; // 4 bits per word
+ // Decide whether to use unrolled GC mask or GC program.
+ // We could use a more elaborate condition, but this seems to work well in practice.
+ // For small objects GC program can't give significant reduction.
+ // While large objects usually contain arrays; and even if it don't
+ // the program uses 2-bits per word while mask uses 4-bits per word,
+ // so the program is still smaller.
+ return size > 2*widthptr;
+}
+
+// Generates sparse GC bitmask (4 bits per word).
+static void
+gengcmask(Type *t, uint8 gcmask[16])
+{
+ Bvec *vec;
+ vlong xoffset, nptr, i, j;
+ int half, mw;
+ uint8 bits, *pos;
+
+ memset(gcmask, 0, 16);
+ if(!haspointers(t))
+ return;
+
+ // Generate compact mask as stacks use.
+ xoffset = 0;
+ vec = bvalloc(2*widthptr*8);
+ twobitwalktype1(t, &xoffset, vec);
+
+ // Unfold the mask for the GC bitmap format:
+ // 4 bits per word, 2 high bits encode pointer info.
+ pos = (uint8*)gcmask;
+ nptr = (t->width+widthptr-1)/widthptr;
+ half = 0;
+ mw = 0;
+ // If number of words is odd, repeat the mask.
+ // This makes simpler handling of arrays in runtime.
+ for(j=0; j<=(nptr%2); j++) {
+ for(i=0; i<nptr; i++) {
+ bits = bvget(vec, i*BitsPerPointer) | bvget(vec, i*BitsPerPointer+1)<<1;
+ // Some fake types (e.g. Hmap) has missing fileds.
+ // twobitwalktype1 generates BitsDead for that holes,
+ // replace BitsDead with BitsScalar.
+ if(!mw && bits == BitsDead)
+ bits = BitsScalar;
+ mw = !mw && bits == BitsMultiWord;
+ bits <<= 2;
+ if(half)
+ bits <<= 4;
+ *pos |= bits;
+ half = !half;
+ if(!half)
+ pos++;
+ }
+ }
+}
+
+// Helper object for generation of GC programs.
+typedef struct ProgGen ProgGen;
+struct ProgGen
+{
+ Sym* s;
+ int32 datasize;
+ uint8 data[256/PointersPerByte];
+ vlong ot;
+};
+
+static void
+proggeninit(ProgGen *g, Sym *s)
+{
+ g->s = s;
+ g->datasize = 0;
+ g->ot = 0;
+ memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggenemit(ProgGen *g, uint8 v)
+{
+ g->ot = duint8(g->s, g->ot, v);
+}
+
+// Emits insData block from g->data.
+static void
+proggendataflush(ProgGen *g)
+{
+ int32 i, s;
+
+ if(g->datasize == 0)
+ return;
+ proggenemit(g, insData);
+ proggenemit(g, g->datasize);
+ s = (g->datasize + PointersPerByte - 1)/PointersPerByte;
+ for(i = 0; i < s; i++)
+ proggenemit(g, g->data[i]);
+ g->datasize = 0;
+ memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggendata(ProgGen *g, uint8 d)
+{
+ g->data[g->datasize/PointersPerByte] |= d << ((g->datasize%PointersPerByte)*BitsPerPointer);
+ g->datasize++;
+ if(g->datasize == 255)
+ proggendataflush(g);
+}
+
+// Skip v bytes due to alignment, etc.
+static void
+proggenskip(ProgGen *g, vlong off, vlong v)
+{
+ vlong i;
+
+ for(i = off; i < off+v; i++) {
+ if((i%widthptr) == 0)
+ proggendata(g, BitsScalar);
+ }
+}
+
+// Emit insArray instruction.
+static void
+proggenarray(ProgGen *g, vlong len)
+{
+ int32 i;
+
+ proggendataflush(g);
+ proggenemit(g, insArray);
+ for(i = 0; i < widthptr; i++, len >>= 8)
+ proggenemit(g, len);
+}
+
+static void
+proggenarrayend(ProgGen *g)
+{
+ proggendataflush(g);
+ proggenemit(g, insArrayEnd);
+}
+
+static vlong
+proggenfini(ProgGen *g)
+{
+ proggendataflush(g);
+ proggenemit(g, insEnd);
+ return g->ot;
+}
+
+static void gengcprog1(ProgGen *g, Type *t, vlong *xoffset);
+
+// Generates GC program for large types.
+static void
+gengcprog(Type *t, Sym **pgc0, Sym **pgc1)
+{
+ Sym *gc0, *gc1;
+ vlong nptr, size, ot, xoffset;
+ ProgGen g;
+
+ nptr = (t->width+widthptr-1)/widthptr;
+ size = nptr;
+ if(size%2)
+ size *= 2; // repeated twice
+ size = size*PointersPerByte/8; // 4 bits per word
+ size++; // unroll flag in the beginning, used by runtime (see runtime.markallocated)
+ // emity space in BSS for unrolled program
+ *pgc0 = S;
+ // Don't generate it if it's too large, runtime will unroll directly into GC bitmap.
+ if(size <= MaxGCMask) {
+ gc0 = typesymprefix(".gc", t);
+ ggloblsym(gc0, size, DUPOK|NOPTR);
+ *pgc0 = gc0;
+ }
+
+ // program in RODATA
+ gc1 = typesymprefix(".gcprog", t);
+ proggeninit(&g, gc1);
+ xoffset = 0;
+ gengcprog1(&g, t, &xoffset);
+ ot = proggenfini(&g);
+ ggloblsym(gc1, ot, DUPOK|RODATA);
+ *pgc1 = gc1;
+}
+
+// Recursively walks type t and writes GC program into g.
+static void
+gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
+{
+ vlong fieldoffset, i, o, n;
+ Type *t1;
+
+ switch(t->etype) {
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TFLOAT32:
+ case TFLOAT64:
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ proggenskip(g, *xoffset, t->width);
+ *xoffset += t->width;
+ break;
+ case TPTR32:
+ case TPTR64:
+ case TUNSAFEPTR:
+ case TFUNC:
+ case TCHAN:
+ case TMAP:
+ proggendata(g, BitsPointer);
+ *xoffset += t->width;
+ break;
+ case TSTRING:
+ proggendata(g, BitsPointer);
+ proggendata(g, BitsScalar);
+ *xoffset += t->width;
+ break;
+ case TINTER:
+ proggendata(g, BitsMultiWord);
+ if(isnilinter(t))
+ proggendata(g, BitsEface);
+ else
+ proggendata(g, BitsIface);
+ *xoffset += t->width;
+ break;
+ case TARRAY:
+ if(isslice(t)) {
+ proggendata(g, BitsPointer);
+ proggendata(g, BitsScalar);
+ proggendata(g, BitsScalar);
+ } else {
+ t1 = t->type;
+ if(t1->width == 0) {
+ // ignore
+ } if(t->bound <= 1 || t->bound*t1->width < 32*widthptr) {
+ for(i = 0; i < t->bound; i++)
+ gengcprog1(g, t1, xoffset);
+ } else if(!haspointers(t1)) {
+ n = t->width;
+ n -= -*xoffset&(widthptr-1); // skip to next ptr boundary
+ proggenarray(g, (n+widthptr-1)/widthptr);
+ proggendata(g, BitsScalar);
+ proggenarrayend(g);
+ *xoffset -= (n+widthptr-1)/widthptr*widthptr - t->width;
+ } else {
+ proggenarray(g, t->bound);
+ gengcprog1(g, t1, xoffset);
+ *xoffset += (t->bound-1)*t1->width;
+ proggenarrayend(g);
+ }
+ }
+ break;
+ case TSTRUCT:
+ o = 0;
+ for(t1 = t->type; t1 != T; t1 = t1->down) {
+ fieldoffset = t1->width;
+ proggenskip(g, *xoffset, fieldoffset - o);
+ *xoffset += fieldoffset - o;
+ gengcprog1(g, t1->type, xoffset);
+ o = fieldoffset + t1->type->width;
+ }
+ proggenskip(g, *xoffset, t->width - o);
+ *xoffset += t->width - o;
+ break;
+ default:
+ fatal("gengcprog1: unexpected type, %T", t);
+ }
+}
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
new file mode 100644
index 0000000..0fb15c2
--- /dev/null
+++ b/src/cmd/gc/runtime.go
@@ -0,0 +1,164 @@
+// 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. This is not done automatically
+// to avoid depending on having a working compiler binary.
+
+// +build ignore
+
+package PACKAGE
+
+// emitted by compiler, not referred to by go programs
+
+func newobject(typ *byte) *any
+func panicindex()
+func panicslice()
+func panicdivide()
+func throwreturn()
+func throwinit()
+func panicwrap(string, string, string)
+
+func gopanic(interface{})
+func gorecover(*int32) interface{}
+
+func printbool(bool)
+func printfloat(float64)
+func printint(int64)
+func printhex(uint64)
+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 concatstring2(string, string) string
+func concatstring3(string, string, string) string
+func concatstring4(string, string, string, string) string
+func concatstring5(string, string, string, string, string) string
+func concatstrings([]string) string
+
+func cmpstring(string, string) int
+func eqstring(string, string) bool
+func intstring(int64) string
+func slicebytetostring([]byte) string
+func slicebytetostringtmp([]byte) string
+func slicerunetostring([]rune) string
+func stringtoslicebyte(string) []byte
+func stringtoslicerune(string) []rune
+func stringiter(string, int) int
+func stringiter2(string, int) (retk int, retv rune)
+func slicecopy(to any, fr any, wid uintptr) int
+func slicestringcopy(to any, fr any) int
+
+// interface conversions
+func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte)
+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, cache **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 assertI2TOK(typ *byte, iface any) (ok bool)
+func assertE2TOK(typ *byte, iface 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 mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_faststr(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 mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_faststr(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 mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
+func mapdelete(mapType *byte, hmap map[any]any, key *any)
+func mapiternext(hiter *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) bool
+func chansend1(chanType *byte, hchan chan<- any, elem *any)
+func closechan(hchan any)
+
+// *byte is really *runtime.Type
+func writebarrierptr(dst *any, src any)
+func writebarrierstring(dst *any, src any)
+func writebarrierslice(dst *any, src any)
+func writebarrieriface(dst *any, src any)
+
+// The unused *byte argument makes sure that src is 2-pointer-aligned,
+// which is the maximum alignment on NaCl amd64p32
+// (and possibly on 32-bit systems if we start 64-bit aligning uint64s).
+func writebarrierfat2(dst *any, _ *byte, src any)
+func writebarrierfat3(dst *any, _ *byte, src any)
+func writebarrierfat4(dst *any, _ *byte, src any)
+func writebarrierfat(typ *byte, dst *any, src *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(sel *byte, selsize int64, size int32)
+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 memmove(to *any, frm *any, length uintptr)
+
+func memequal(x, y *any, size uintptr) bool
+func memequal8(x, y *any, size uintptr) bool
+func memequal16(x, y *any, size uintptr) bool
+func memequal32(x, y *any, size uintptr) bool
+func memequal64(x, y *any, size uintptr) bool
+func memequal128(x, y *any, size uintptr) bool
+
+// 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)
+
+// race detection
+func racefuncenter(uintptr)
+func racefuncexit()
+func raceread(uintptr)
+func racewrite(uintptr)
+func racereadrange(addr, size uintptr)
+func racewriterange(addr, size uintptr)
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
new file mode 100644
index 0000000..965ad27
--- /dev/null
+++ b/src/cmd/gc/select.c
@@ -0,0 +1,377 @@
+// 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 <u.h>
+#include <libc.h>
+#include "go.h"
+
+static Type* selecttype(int32 size);
+
+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 OSELRECV2(x, <-c) with ntest=ok
+ if(n->rlist->n->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->list = nil;
+ n->right = n->rlist->n;
+ n->rlist = nil;
+ break;
+
+ case ORECV:
+ // convert <-c into OSELRECV(N, <-c)
+ n = nod(OSELRECV, N, n);
+ n->typecheck = 1;
+ 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, *var, *selv, *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.
+ // TODO(rsc): Reenable optimization once order.c can handle it.
+ // golang.org/issue/7672.
+ 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:
+ // ok already
+ ch = n->left;
+ break;
+
+ case OSELRECV:
+ ch = n->right->left;
+ Selrecv1:
+ if(n->left == N)
+ n = n->right;
+ else
+ n->op = OAS;
+ break;
+
+ case OSELRECV2:
+ ch = n->right->left;
+ if(n->ntest == N)
+ goto Selrecv1;
+ if(n->left == N) {
+ typecheck(&nblank, Erv | Easgn);
+ n->left = nblank;
+ }
+ n->op = OAS2;
+ n->list = list(list1(n->left), n->ntest);
+ n->rlist = list1(n->right);
+ n->right = N;
+ n->left = N;
+ n->ntest = N;
+ n->typecheck = 0;
+ 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;
+ }
+
+ // convert case value arguments to addresses.
+ // 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 OSEND:
+ n->right = nod(OADDR, n->right, N);
+ typecheck(&n->right, Erv);
+ break;
+ case OSELRECV:
+ case OSELRECV2:
+ if(n->op == OSELRECV2 && n->ntest == N)
+ n->op = OSELRECV;
+ if(n->op == OSELRECV2) {
+ n->ntest = nod(OADDR, n->ntest, N);
+ typecheck(&n->ntest, Erv);
+ }
+ if(n->left == N)
+ n->left = nodnil();
+ else {
+ n->left = nod(OADDR, n->left, N);
+ typecheck(&n->left, Erv);
+ }
+ 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 selectnbsend(c, v) { body } else { default body }
+ ch = n->left;
+ 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 = n->right->left;
+ 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 = n->right->left;
+ 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);
+ selv = temp(selecttype(sel->xoffset));
+ r = nod(OAS, selv, N);
+ typecheck(&r, Etop);
+ init = list(init, r);
+ var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
+ r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), 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->ninit = cas->ninit;
+ cas->ninit = nil;
+ if(n != nil) {
+ r->ninit = concat(r->ninit, n->ninit);
+ n->ninit = nil;
+ }
+ if(n == nil) {
+ // selectdefault(sel *byte);
+ r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
+ } else {
+ switch(n->op) {
+ default:
+ fatal("select %O", n->op);
+
+ case OSEND:
+ // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
+ r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
+ &r->ninit, 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],
+ &r->ninit, 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],
+ &r->ninit, var, n->right->left, n->left, n->ntest);
+ break;
+ }
+ }
+ // selv is no longer alive after use.
+ r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
+ 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;
+}
+
+// Keep in sync with src/runtime/chan.h.
+static Type*
+selecttype(int32 size)
+{
+ Node *sel, *sudog, *scase, *arr;
+
+ // TODO(dvyukov): it's possible to generate SudoG and Scase only once
+ // and then cache; and also cache Select per size.
+ sudog = nod(OTSTRUCT, N, N);
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32])));
+ sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8]))));
+ typecheck(&sudog, Etype);
+ sudog->type->noalg = 1;
+ sudog->type->local = 1;
+
+ scase = nod(OTSTRUCT, N, N);
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR])));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
+ scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+ typecheck(&scase, Etype);
+ scase->type->noalg = 1;
+ scase->type->local = 1;
+
+ sel = nod(OTSTRUCT, N, N);
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
+ arr = nod(OTARRAY, nodintconst(size), scase);
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
+ arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
+ arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
+ sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
+ typecheck(&sel, Etype);
+ sel->type->noalg = 1;
+ sel->type->local = 1;
+
+ return sel->type;
+}
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
new file mode 100644
index 0000000..8ad7ae7
--- /dev/null
+++ b/src/cmd/gc/sinit.c
@@ -0,0 +1,1505 @@
+// 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 <u.h>
+#include <libc.h>
+#include "go.h"
+
+enum
+{
+ InitNotStarted = 0,
+ InitDone = 1,
+ InitPending = 2,
+};
+
+static void initplan(Node*);
+static NodeList *initlist;
+static void init2(Node*, NodeList**);
+static void init2list(NodeList*, NodeList**);
+static int staticinit(Node*, NodeList**);
+static Node *staticname(Type*, int);
+
+// init1 walks the AST starting at n, and accumulates in out
+// the list of definitions needing init code in dependency order.
+static void
+init1(Node *n, NodeList **out)
+{
+ NodeList *l;
+ Node *nv;
+
+ 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->left && n->type && n->left->op == OTYPE && n->class == PFUNC) {
+ // Methods called as Type.Method(receiver, ...).
+ // Definitions for method expressions are stored in type->nname.
+ init1(n->type->nname, out);
+ }
+
+ if(n->op != ONAME)
+ return;
+ switch(n->class) {
+ case PEXTERN:
+ case PFUNC:
+ break;
+ default:
+ if(isblank(n) && n->curfn == N && n->defn != N && n->defn->initorder == InitNotStarted) {
+ // blank names initialization is part of init() but not
+ // when they are inside a function.
+ break;
+ }
+ return;
+ }
+
+ if(n->initorder == InitDone)
+ return;
+ if(n->initorder == InitPending) {
+ // Since mutually recursive sets of functions are allowed,
+ // we don't necessarily raise an error if n depends on a node
+ // which is already waiting for its dependencies to be visited.
+ //
+ // initlist contains a cycle of identifiers referring to each other.
+ // If this cycle contains a variable, then this variable refers to itself.
+ // Conversely, if there exists an initialization cycle involving
+ // a variable in the program, the tree walk will reach a cycle
+ // involving that variable.
+ if(n->class != PFUNC) {
+ nv = n;
+ goto foundinitloop;
+ }
+ for(l=initlist; l->n!=n; l=l->next) {
+ if(l->n->class != PFUNC) {
+ nv = l->n;
+ goto foundinitloop;
+ }
+ }
+ // The loop involves only functions, ok.
+ return;
+
+ foundinitloop:
+ // 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();
+
+ // There is a loop involving nv. We know about
+ // n and initlist = n1 <- ... <- nv <- ... <- n <- ...
+ print("%L: initialization loop:\n", nv->lineno);
+ // Build back pointers in initlist.
+ for(l=initlist; l; l=l->next)
+ if(l->next != nil)
+ l->next->end = l;
+ // Print nv -> ... -> n1 -> n.
+ for(l=initlist; l->n!=nv; l=l->next);
+ for(; l; l=l->end)
+ print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+ // Print n -> ... -> nv.
+ for(l=initlist; l->n!=n; l=l->next);
+ for(; l->n != nv; l=l->end)
+ print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+ print("\t%L %S\n", nv->lineno, nv->sym);
+ errorexit();
+ }
+
+ // reached a new unvisited node.
+ n->initorder = InitPending;
+ l = malloc(sizeof *l);
+ if(l == nil) {
+ flusherrors();
+ yyerror("out of memory");
+ errorexit();
+ }
+ 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;
+ if(isblank(n->defn->left) && candiscard(n->defn->right)) {
+ n->defn->op = OEMPTY;
+ n->defn->left = N;
+ n->defn->right = N;
+ break;
+ }
+
+ init2(n->defn->right, out);
+ if(debug['j'])
+ print("%S\n", n->sym);
+ if(isblank(n) || !staticinit(n, out)) {
+ if(debug['%'])
+ dump("nonstatic", n->defn);
+ *out = list(*out, n->defn);
+ }
+ break;
+
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OAS2DOTTYPE:
+ case OAS2RECV:
+ if(n->defn->initorder != InitNotStarted)
+ break;
+ n->defn->initorder = InitDone;
+ for(l=n->defn->rlist; l; l=l->next)
+ init1(l->n, out);
+ if(debug['%']) dump("nonstatic", n->defn);
+ *out = list(*out, n->defn);
+ break;
+ }
+ }
+ l = initlist;
+ initlist = l->next;
+ if(l->n != n)
+ fatal("bad initlist");
+ free(l);
+ n->initorder = InitDone;
+ 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 == InitDone)
+ return;
+
+ if(n->op == ONAME && n->ninit)
+ fatal("name %S with ninit: %+N\n", n->sym, n);
+
+ 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);
+
+ if(n->op == OCLOSURE)
+ init2list(n->closure->nbody, out);
+ if(n->op == ODOTMETH || n->op == OCALLPART)
+ init2(n->type->nname, 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);
+ }
+}
+
+// initfix computes initialization order for a list l of top-level
+// declarations and outputs the corresponding list of statements
+// to include in the init() function body.
+NodeList*
+initfix(NodeList *l)
+{
+ NodeList *lout;
+ int lno;
+
+ lout = nil;
+ lno = lineno;
+ initreorder(l, &lout);
+ lineno = lno;
+ return lout;
+}
+
+/*
+ * compilation of top-level (static) assignments
+ * into DATA statements if at all possible.
+ */
+
+static int staticassign(Node*, Node*, NodeList**);
+
+static int
+staticinit(Node *n, NodeList **out)
+{
+ Node *l, *r;
+
+ if(n->op != ONAME || n->class != PEXTERN || n->defn == N || n->defn->op != OAS)
+ fatal("staticinit");
+
+ lineno = n->lineno;
+ l = n->defn->left;
+ r = n->defn->right;
+ return staticassign(l, r, out);
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+static int
+staticcopy(Node *l, Node *r, NodeList **out)
+{
+ int i;
+ InitEntry *e;
+ InitPlan *p;
+ Node *a, *ll, *rr, *orig, n1;
+
+ if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg)
+ return 0;
+ if(r->defn == N) // probably zeroed but perhaps supplied externally and of unknown value
+ return 0;
+ if(r->defn->op != OAS)
+ return 0;
+ orig = r;
+ r = r->defn->right;
+
+ switch(r->op) {
+ case ONAME:
+ if(staticcopy(l, r, out))
+ return 1;
+ *out = list(*out, nod(OAS, l, r));
+ return 1;
+
+ case OLITERAL:
+ if(iszero(r))
+ return 1;
+ gdata(l, r, l->type->width);
+ return 1;
+
+ case OADDR:
+ switch(r->left->op) {
+ case ONAME:
+ gdata(l, r, l->type->width);
+ return 1;
+ }
+ break;
+
+ case OPTRLIT:
+ switch(r->left->op) {
+ default:
+ //dump("not static addr", r);
+ break;
+ case OARRAYLIT:
+ case OSTRUCTLIT:
+ case OMAPLIT:
+ // copy pointer
+ gdata(l, nod(OADDR, r->nname, N), l->type->width);
+ return 1;
+ }
+ break;
+
+ case OARRAYLIT:
+ if(isslice(r->type)) {
+ // copy slice
+ a = r->nname;
+ n1 = *l;
+ n1.xoffset = l->xoffset + Array_array;
+ gdata(&n1, nod(OADDR, a, N), widthptr);
+ n1.xoffset = l->xoffset + Array_nel;
+ gdata(&n1, r->right, widthint);
+ n1.xoffset = l->xoffset + Array_cap;
+ gdata(&n1, r->right, widthint);
+ return 1;
+ }
+ // fall through
+ case OSTRUCTLIT:
+ p = r->initplan;
+ n1 = *l;
+ for(i=0; i<p->len; i++) {
+ e = &p->e[i];
+ n1.xoffset = l->xoffset + e->xoffset;
+ n1.type = e->expr->type;
+ if(e->expr->op == OLITERAL)
+ gdata(&n1, e->expr, n1.type->width);
+ else {
+ ll = nod(OXXX, N, N);
+ *ll = n1;
+ ll->orig = ll; // completely separate copy
+ if(!staticassign(ll, e->expr, out)) {
+ // Requires computation, but we're
+ // copying someone else's computation.
+ rr = nod(OXXX, N, N);
+ *rr = *orig;
+ rr->orig = rr; // completely separate copy
+ rr->type = ll->type;
+ rr->xoffset += e->xoffset;
+ *out = list(*out, nod(OAS, ll, rr));
+ }
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int
+staticassign(Node *l, Node *r, NodeList **out)
+{
+ Node *a, n1;
+ Type *ta;
+ InitPlan *p;
+ InitEntry *e;
+ int i;
+ Strlit *sval;
+
+ switch(r->op) {
+ default:
+ //dump("not static", r);
+ break;
+
+ case ONAME:
+ if(r->class == PEXTERN && r->sym->pkg == localpkg)
+ return staticcopy(l, r, out);
+ break;
+
+ case OLITERAL:
+ if(iszero(r))
+ return 1;
+ gdata(l, r, l->type->width);
+ return 1;
+
+ case OADDR:
+ switch(r->left->op) {
+ default:
+ //dump("not static addr", r);
+ break;
+
+ case ONAME:
+ gdata(l, r, l->type->width);
+ return 1;
+ }
+
+ case OPTRLIT:
+ switch(r->left->op) {
+ default:
+ //dump("not static ptrlit", r);
+ break;
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ // Init pointer.
+ a = staticname(r->left->type, 1);
+ r->nname = a;
+ gdata(l, nod(OADDR, a, N), l->type->width);
+ // Init underlying literal.
+ if(!staticassign(a, r->left, out))
+ *out = list(*out, nod(OAS, a, r->left));
+ return 1;
+ }
+ break;
+
+ case OSTRARRAYBYTE:
+ if(l->class == PEXTERN && r->left->op == OLITERAL) {
+ sval = r->left->val.u.sval;
+ slicebytes(l, sval->s, sval->len);
+ return 1;
+ }
+ break;
+
+ case OARRAYLIT:
+ initplan(r);
+ if(isslice(r->type)) {
+ // Init slice.
+ ta = typ(TARRAY);
+ ta->type = r->type->type;
+ ta->bound = mpgetfix(r->right->val.u.xval);
+ a = staticname(ta, 1);
+ r->nname = a;
+ n1 = *l;
+ n1.xoffset = l->xoffset + Array_array;
+ gdata(&n1, nod(OADDR, a, N), widthptr);
+ n1.xoffset = l->xoffset + Array_nel;
+ gdata(&n1, r->right, widthint);
+ n1.xoffset = l->xoffset + Array_cap;
+ gdata(&n1, r->right, widthint);
+ // Fall through to init underlying array.
+ l = a;
+ }
+ // fall through
+ case OSTRUCTLIT:
+ initplan(r);
+ p = r->initplan;
+ n1 = *l;
+ for(i=0; i<p->len; i++) {
+ e = &p->e[i];
+ n1.xoffset = l->xoffset + e->xoffset;
+ n1.type = e->expr->type;
+ if(e->expr->op == OLITERAL)
+ gdata(&n1, e->expr, n1.type->width);
+ else {
+ a = nod(OXXX, N, N);
+ *a = n1;
+ a->orig = a; // completely separate copy
+ if(!staticassign(a, e->expr, out))
+ *out = list(*out, nod(OAS, a, e->expr));
+ }
+ }
+ return 1;
+
+ case OMAPLIT:
+ // TODO: Table-driven map insert.
+ break;
+ }
+ return 0;
+}
+
+/*
+ * 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);
+ if(pass == 1) {
+ walkexpr(&a, init); // add any assignments in r to top
+ if(a->op != OAS)
+ fatal("structlit: not as");
+ a->dodata = 2;
+ } else {
+ orderstmtinplace(&a);
+ walkstmt(&a);
+ }
+ *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);
+ if(pass == 1) {
+ walkexpr(&a, init);
+ if(a->op != OAS)
+ fatal("arraylit: not as");
+ a->dodata = 2;
+ } else {
+ orderstmtinplace(&a);
+ walkstmt(&a);
+ }
+ *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;
+ t->haspointers = 0;
+ 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 = temp(ptrto(t));
+
+ // set auto to point at new temp or heap (3 assign)
+ if(n->alloc != N) {
+ // temp allocated during order.c for dddarg
+ n->alloc->type = t;
+ if(vstat == N) {
+ a = nod(OAS, n->alloc, N);
+ typecheck(&a, Etop);
+ *init = list(*init, a); // zero new temp
+ }
+ a = nod(OADDR, n->alloc, N);
+ } else if(n->esc == EscNone) {
+ a = temp(t);
+ if(vstat == N) {
+ a = nod(OAS, temp(t), N);
+ typecheck(&a, Etop);
+ *init = list(*init, a); // zero new temp
+ a = a->left;
+ }
+ a = nod(OADDR, a, N);
+ } else {
+ 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);
+ orderstmtinplace(&a);
+ walkstmt(&a);
+ *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->bounded = 1;
+ // 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);
+ orderstmtinplace(&a);
+ walkstmt(&a);
+ *init = list(*init, a);
+ }
+}
+
+static void
+maplit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+ Node *r, *a;
+ NodeList *l;
+ int nerr;
+ int64 b;
+ Type *t, *tk, *tv, *t1;
+ Node *vstat, *index, *value, *key, *val;
+ Sym *syma, *symb;
+
+USED(ctxt);
+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("maplit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ if(isliteral(index) && isliteral(value))
+ b++;
+ }
+
+ 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("maplit: 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 = temp(types[TINT]);
+
+ a = nod(OINDEX, vstat, index);
+ a->bounded = 1;
+ a = nod(ODOT, a, newname(symb));
+
+ r = nod(OINDEX, vstat, index);
+ r->bounded = 1;
+ 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(OAS, index, nod(OADD, index, nodintconst(1)));
+
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+ }
+
+ // put in dynamic entries one-at-a-time
+ key = nil;
+ val = nil;
+ for(l=n->list; l; l=l->next) {
+ r = l->n;
+
+ if(r->op != OKEY)
+ fatal("maplit: rhs not OKEY: %N", r);
+ index = r->left;
+ value = r->right;
+
+ if(isliteral(index) && isliteral(value))
+ continue;
+
+ // build list of var[c] = expr.
+ // use temporary so that mapassign1 can have addressable key, val.
+ if(key == nil) {
+ key = temp(var->type->down);
+ val = temp(var->type->type);
+ }
+ a = nod(OAS, key, r->left);
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+ a = nod(OAS, val, r->right);
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+
+ a = nod(OAS, nod(OINDEX, var, key), val);
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+
+ if(nerr != nerrors)
+ break;
+ }
+
+ if(key != nil) {
+ a = nod(OVARKILL, key, N);
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+ a = nod(OVARKILL, val, N);
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+ }
+}
+
+void
+anylit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+ Type *t;
+ Node *a, *vstat, *r;
+
+ t = n->type;
+ switch(n->op) {
+ default:
+ fatal("anylit: not lit");
+
+ case OPTRLIT:
+ if(!isptr[t->etype])
+ fatal("anylit: not ptr");
+
+ if(n->right != N) {
+ r = nod(OADDR, n->right, N);
+ typecheck(&r, Erv);
+ } else {
+ r = nod(ONEW, N, N);
+ r->typecheck = 1;
+ r->type = t;
+ r->esc = n->esc;
+ }
+ walkexpr(&r, init);
+ a = nod(OAS, var, r);
+
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ var = nod(OIND, var, N);
+ typecheck(&var, Erv | Easgn);
+ anylit(ctxt, n->left, var, init);
+ break;
+
+ case OSTRUCTLIT:
+ if(t->etype != TSTRUCT)
+ fatal("anylit: not struct");
+
+ if(simplename(var) && count(n->list) > 4) {
+
+ 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(simplename(var) || 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) && count(n->list) > 4) {
+
+ 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(simplename(var) || 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;
+ // Check for overflow.
+ if(n->type->width != 0 && MAXWIDTH/n->type->width <= l)
+ 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:
+ gdata(&nam, nr, nr->type->width);
+ break;
+
+ case TCOMPLEX64:
+ case TCOMPLEX128:
+ gdatacomplex(&nam, nr->val.u.cval);
+ break;
+
+ case TSTRING:
+ 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[TINT], nr->type->bound);
+ gdata(&nam, &nod1, widthint);
+
+ nam.xoffset += Array_cap-Array_nel;
+ gdata(&nam, &nod1, widthint);
+
+ goto yes;
+
+no:
+ if(n->dodata == 2) {
+ dump("\ngen_as_init", n);
+ fatal("gen_as_init couldnt make data statement");
+ }
+ return 0;
+}
+
+static int isvaluelit(Node*);
+static InitEntry* entry(InitPlan*);
+static void addvalue(InitPlan*, vlong, Node*, Node*);
+
+static void
+initplan(Node *n)
+{
+ InitPlan *p;
+ Node *a;
+ NodeList *l;
+
+ if(n->initplan != nil)
+ return;
+ p = mal(sizeof *p);
+ n->initplan = p;
+ switch(n->op) {
+ default:
+ fatal("initplan");
+ case OARRAYLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY || !smallintconst(a->left))
+ fatal("initplan arraylit");
+ addvalue(p, n->type->type->width*mpgetfix(a->left->val.u.xval), N, a->right);
+ }
+ break;
+ case OSTRUCTLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY || a->left->type == T)
+ fatal("initplan structlit");
+ addvalue(p, a->left->type->width, N, a->right);
+ }
+ break;
+ case OMAPLIT:
+ for(l=n->list; l; l=l->next) {
+ a = l->n;
+ if(a->op != OKEY)
+ fatal("initplan maplit");
+ addvalue(p, -1, a->left, a->right);
+ }
+ break;
+ }
+}
+
+static void
+addvalue(InitPlan *p, vlong xoffset, Node *key, Node *n)
+{
+ int i;
+ InitPlan *q;
+ InitEntry *e;
+
+ USED(key);
+
+ // special case: zero can be dropped entirely
+ if(iszero(n)) {
+ p->zero += n->type->width;
+ return;
+ }
+
+ // special case: inline struct and array (not slice) literals
+ if(isvaluelit(n)) {
+ initplan(n);
+ q = n->initplan;
+ for(i=0; i<q->len; i++) {
+ e = entry(p);
+ *e = q->e[i];
+ e->xoffset += xoffset;
+ }
+ return;
+ }
+
+ // add to plan
+ if(n->op == OLITERAL)
+ p->lit += n->type->width;
+ else
+ p->expr += n->type->width;
+
+ e = entry(p);
+ e->xoffset = xoffset;
+ e->expr = n;
+}
+
+int
+iszero(Node *n)
+{
+ NodeList *l;
+
+ switch(n->op) {
+ case OLITERAL:
+ switch(n->val.ctype) {
+ default:
+ dump("unexpected literal", n);
+ fatal("iszero");
+
+ case CTNIL:
+ return 1;
+
+ case CTSTR:
+ return n->val.u.sval == nil || n->val.u.sval->len == 0;
+
+ case CTBOOL:
+ return n->val.u.bval == 0;
+
+ case CTINT:
+ case CTRUNE:
+ return mpcmpfixc(n->val.u.xval, 0) == 0;
+
+ case CTFLT:
+ return mpcmpfltc(n->val.u.fval, 0) == 0;
+
+ case CTCPLX:
+ return mpcmpfltc(&n->val.u.cval->real, 0) == 0 && mpcmpfltc(&n->val.u.cval->imag, 0) == 0;
+ }
+ break;
+ case OARRAYLIT:
+ if(isslice(n->type))
+ break;
+ // fall through
+ case OSTRUCTLIT:
+ for(l=n->list; l; l=l->next)
+ if(!iszero(l->n->right))
+ return 0;
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isvaluelit(Node *n)
+{
+ return (n->op == OARRAYLIT && isfixedarray(n->type)) || n->op == OSTRUCTLIT;
+}
+
+static InitEntry*
+entry(InitPlan *p)
+{
+ if(p->len >= p->cap) {
+ if(p->cap == 0)
+ p->cap = 4;
+ else
+ p->cap *= 2;
+ p->e = realloc(p->e, p->cap*sizeof p->e[0]);
+ if(p->e == nil)
+ fatal("out of memory");
+ }
+ return &p->e[p->len++];
+}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
new file mode 100644
index 0000000..26153d3
--- /dev/null
+++ b/src/cmd/gc/subr.c
@@ -0,0 +1,3849 @@
+// 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 "go.h"
+#include "md5.h"
+#include "y.tab.h"
+#include "yerr.h"
+
+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);
+ exits("error");
+}
+
+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;
+
+ fmtstrinit(&f);
+ fmtprint(&f, "%L: ", line);
+ fmtvprint(&f, fmt, arg);
+ fmtprint(&f, "\n");
+
+ 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;
+
+ Bflush(&bstdout);
+ 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)
+ remove(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(nsavederrors+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);
+
+ // An unexpected EOF caused a syntax error. Use the previous
+ // line number since getc generated a fake newline character.
+ if(curio.eofnl)
+ lexlineno = prevlineno;
+
+ // only one syntax error per line
+ if(lastsyntax == lexlineno)
+ return;
+ lastsyntax = lexlineno;
+
+ if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || 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);
+
+ // The grammar has ? and @ but only for reading imports.
+ // Silence them in ordinary errors.
+ p = strstr(buf, " or ?");
+ if(p)
+ memmove(p, p+5, strlen(p+5)+1);
+ p = strstr(buf, " or @");
+ if(p)
+ memmove(p, p+5, strlen(p+5)+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(nsavederrors+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
+warnl(int line, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(line, fmt, arg);
+ va_end(arg);
+ if(debug['m'])
+ flusherrors();
+}
+
+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)
+{
+ 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)
+ file = smprint("%s/%s", ctxt->pathname, file);
+ linklinehist(ctxt, lexlineno, file, off);
+}
+
+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)
+{
+ uint32 h;
+ int c;
+
+ h = 0;
+ for(;;) {
+ c = *p++;
+ if(c == 0)
+ break;
+ h = h*PRIME1 + c;
+ }
+
+ if((int32)h < 0) {
+ h = -h;
+ if((int32)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;
+ char *pkgerror;
+
+ 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) {
+ pkgerror = smprint("during import \"%Z\"", opkg->path);
+ redeclare(s1, pkgerror);
+ continue;
+ }
+ s1->def = s->def;
+ s1->block = s->block;
+ s1->def->pack = pack;
+ s1->origpkg = opkg;
+ 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;
+ n->curfn = curfn;
+ return n;
+}
+
+void
+saveorignode(Node *n)
+{
+ Node *norig;
+
+ if(n->orig != N)
+ return;
+ norig = nod(n->op, N, N);
+ *norig = *n;
+ n->orig = norig;
+}
+
+// ispaddedfield reports whether the given field
+// is followed by padding. For the case where t is
+// the last field, total gives the size of the enclosing struct.
+static int
+ispaddedfield(Type *t, vlong total)
+{
+ if(t->etype != TFIELD)
+ fatal("ispaddedfield called non-field %T", t);
+ if(t->down == T)
+ return t->width + t->type->width != total;
+ return t->width + t->type->width != t->down->width;
+}
+
+int
+algtype1(Type *t, Type **bad)
+{
+ int a, ret;
+ Type *t1;
+
+ if(bad)
+ *bad = T;
+ if(t->broke)
+ return AMEM;
+ if(t->noalg)
+ return ANOEQ;
+
+ switch(t->etype) {
+ case TANY:
+ case TFORW:
+ // will be defined later.
+ *bad = t;
+ return -1;
+
+ case TINT8:
+ case TUINT8:
+ case TINT16:
+ case TUINT16:
+ case TINT32:
+ case TUINT32:
+ case TINT64:
+ case TUINT64:
+ case TINT:
+ case TUINT:
+ case TUINTPTR:
+ case TBOOL:
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TUNSAFEPTR:
+ return AMEM;
+
+ case TFUNC:
+ case TMAP:
+ if(bad)
+ *bad = t;
+ return ANOEQ;
+
+ case TFLOAT32:
+ return AFLOAT32;
+
+ case TFLOAT64:
+ return AFLOAT64;
+
+ case TCOMPLEX64:
+ return ACPLX64;
+
+ case TCOMPLEX128:
+ return ACPLX128;
+
+ case TSTRING:
+ return ASTRING;
+
+ case TINTER:
+ if(isnilinter(t))
+ return ANILINTER;
+ return AINTER;
+
+ case TARRAY:
+ if(isslice(t)) {
+ if(bad)
+ *bad = t;
+ return ANOEQ;
+ }
+ a = algtype1(t->type, bad);
+ if(a == ANOEQ || a == AMEM) {
+ if(a == ANOEQ && bad)
+ *bad = t;
+ return a;
+ }
+ return -1; // needs special compare
+
+ case TSTRUCT:
+ if(t->type != T && t->type->down == T && !isblanksym(t->type->sym)) {
+ // One-field struct is same as that one field alone.
+ return algtype1(t->type->type, bad);
+ }
+ ret = AMEM;
+ for(t1=t->type; t1!=T; t1=t1->down) {
+ // All fields must be comparable.
+ a = algtype1(t1->type, bad);
+ if(a == ANOEQ)
+ return ANOEQ;
+
+ // Blank fields, padded fields, fields with non-memory
+ // equality need special compare.
+ if(a != AMEM || isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
+ ret = -1;
+ continue;
+ }
+ }
+ return ret;
+ }
+
+ fatal("algtype1: unexpected type %T", t);
+ return 0;
+}
+
+int
+algtype(Type *t)
+{
+ int a;
+
+ a = algtype1(t, nil);
+ if(a == AMEM || a == ANOEQ) {
+ if(isslice(t))
+ return ASLICE;
+ switch(t->width) {
+ case 0:
+ return a + AMEM0 - AMEM;
+ case 1:
+ return a + AMEM8 - AMEM;
+ case 2:
+ return a + AMEM16 - AMEM;
+ case 4:
+ return a + AMEM32 - AMEM;
+ case 8:
+ return a + AMEM64 - AMEM;
+ case 16:
+ return a + AMEM128 - AMEM;
+ }
+ }
+ return a;
+}
+
+Type*
+maptype(Type *key, Type *val)
+{
+ Type *t;
+ Type *bad;
+ int atype, mtype;
+
+ if(key != nil) {
+ atype = algtype1(key, &bad);
+ if(bad == T)
+ mtype = key->etype;
+ else
+ mtype = bad->etype;
+ switch(mtype) {
+ default:
+ if(atype == ANOEQ)
+ yyerror("invalid map key type %T", key);
+ break;
+ case TANY:
+ // will be resolved later.
+ break;
+ case 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;
+ break;
+ }
+ }
+ 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;
+ if(a->sym == S && b->sym == S)
+ return 0;
+ if(a->sym == S)
+ return -1;
+ if(b->sym == S)
+ return 1;
+ 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;
+ int64 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:
+ case CTRUNE:
+ 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;
+}
+
+Node*
+treecopy(Node *n)
+{
+ Node *m;
+
+ if(n == N)
+ return N;
+
+ switch(n->op) {
+ default:
+ m = nod(OXXX, N, N);
+ *m = *n;
+ m->orig = m;
+ 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
+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)
+{
+ if(n == N)
+ return 0;
+ return isblanksym(n->sym);
+}
+
+int
+isblanksym(Sym *s)
+{
+ char *p;
+
+ if(s == S)
+ return 0;
+ p = s->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, int mustname)
+{
+ 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 && (mustname || t->etype != TSTRUCT))
+ 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;
+}
+
+typedef struct TypePairList TypePairList;
+struct TypePairList
+{
+ Type *t1;
+ Type *t2;
+ TypePairList *next;
+};
+
+static int
+onlist(TypePairList *l, Type *t1, Type *t2)
+{
+ for(; l; l=l->next)
+ if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1))
+ return 1;
+ return 0;
+}
+
+static int eqtype1(Type*, Type*, TypePairList*);
+
+// 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)
+{
+ return eqtype1(t1, t2, nil);
+}
+
+static int
+eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal)
+{
+ TypePairList l;
+
+ if(t1 == t2)
+ return 1;
+ if(t1 == T || t2 == T || t1->etype != t2->etype)
+ return 0;
+ if(t1->sym || t2->sym) {
+ // Special case: we keep byte and uint8 separate
+ // for error messages. Treat them as equal.
+ switch(t1->etype) {
+ case TUINT8:
+ if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype))
+ return 1;
+ break;
+ case TINT:
+ case TINT32:
+ if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype))
+ return 1;
+ break;
+ }
+ return 0;
+ }
+
+ if(onlist(assumed_equal, t1, t2))
+ return 1;
+ l.next = assumed_equal;
+ l.t1 = t1;
+ l.t2 = t2;
+
+ 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 || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note))
+ goto no;
+ }
+ if(t1 == T && t2 == T)
+ goto yes;
+ goto no;
+
+ 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 || !eqtype1(ta->type, tb->type, &l))
+ goto no;
+ }
+ if(ta != T || tb != T)
+ goto no;
+ }
+ if(t1 == T && t2 == T)
+ goto yes;
+ goto no;
+
+ case TARRAY:
+ if(t1->bound != t2->bound)
+ goto no;
+ break;
+
+ case TCHAN:
+ if(t1->chan != t2->chan)
+ goto no;
+ break;
+ }
+
+ if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l))
+ goto yes;
+ goto no;
+
+yes:
+ return 1;
+
+no:
+ return 0;
+}
+
+// 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.
+int
+assignop(Type *src, Type *dst, char **why)
+{
+ Type *missing, *have;
+ int ptr;
+
+ if(why != nil)
+ *why = "";
+
+ // TODO(rsc,lvd): This behaves poorly in the presence of inlining.
+ // https://code.google.com/p/go/issues/detail?id=2795
+ if(safemode && importpkg == nil && 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 empty interface types.
+ // For assignable but different non-empty interface types,
+ // we want to recompute the itab.
+ if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || isnilinter(src)))
+ 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;
+
+ // we'll have complained about this method anyway, suppress spurious messages.
+ if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke))
+ 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 && have->nointerface)
+ *why = smprint(":\n\t%T does not implement %T (%S method is marked 'nointerface')",
+ src, dst, missing->sym);
+ 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 has 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 && implements(dst, src, &missing, &have, &ptr))
+ *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 []rune
+ // and dst is a string type.
+ if(isint[src->etype] && dst->etype == TSTRING)
+ return ORUNESTR;
+
+ if(isslice(src) && dst->etype == TSTRING) {
+ if(src->type->etype == bytetype->etype)
+ return OARRAYBYTESTR;
+ if(src->type->etype == runetype->etype)
+ return OARRAYRUNESTR;
+ }
+
+ // 7. src is a string and dst is []byte or []rune.
+ // String to slice.
+ if(src->etype == TSTRING && isslice(dst)) {
+ if(dst->type->etype == bytetype->etype)
+ return OSTRARRAYBYTE;
+ if(dst->type->etype == runetype->etype)
+ 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 || n->type->broke)
+ return n;
+
+ if(t->etype == TBLANK && n->type->etype == TNIL)
+ yyerror("use of untyped nil");
+
+ old = n;
+ old->diag++; // silence errors about n; we'll issue one below
+ defaultlit(&n, t);
+ old->diag--;
+ if(t->etype == TBLANK)
+ return n;
+
+ // Convert ideal bool from comparison to plain bool
+ // if the next step is non-bool (like interface{}).
+ if(n->type == idealbool && t->etype != TBOOL) {
+ if(n->op == ONAME || n->op == OLITERAL) {
+ r = nod(OCONVNOP, n, N);
+ r->type = types[TBOOL];
+ r->typecheck = 1;
+ r->implicit = 1;
+ n = r;
+ }
+ }
+
+ if(eqtype(n->type, t))
+ return n;
+
+ op = assignop(n->type, t, &why);
+ if(op == 0) {
+ yyerror("cannot use %lN 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;
+ r->orig = n->orig;
+ 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 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;
+
+ if(t->thistuple) {
+ // hide method receiver from Tpretty
+ t->thistuple = 0;
+ p = smprint("%-uT", t);
+ t->thistuple = 1;
+ } else
+ p = smprint("%-uT", t);
+ //print("typehash: %s\n", p);
+ md5reset(&d);
+ md5write(&d, (uchar*)p, strlen(p));
+ free(p);
+ return md5sum(&d, nil);
+}
+
+Type*
+ptrto(Type *t)
+{
+ Type *t1;
+
+ if(tptr == 0)
+ fatal("ptrto: no tptr");
+ 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;
+
+ if(n->ninit != nil) {
+ ul = UINF;
+ goto out;
+ }
+
+ 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;
+ case OANDAND:
+ case OOROR:
+ // hard with race detector
+ if(flag_race) {
+ 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:
+ if(ul > 200)
+ ul = 200; // clamp to uchar with room to grow
+ 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;
+
+ if(n->ninit) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
+ 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);
+}
+
+Node*
+copyexpr(Node *n, Type *t, NodeList **init)
+{
+ Node *a, *l;
+
+ l = temp(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.
+ * the value is guaranteed not to change except by direct
+ * assignment to it.
+ */
+Node*
+localexpr(Node *n, Type *t, NodeList **init)
+{
+ if(n->op == ONAME && (!n->addrtaken || strncmp(n->sym->name, "autotmp_", 8) == 0) &&
+ (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)
+{
+ int64 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
+ucistrcmp(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 && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0)) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ u = methtype(t, 0);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down)
+ if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
+ if(save)
+ *save = f;
+ c++;
+ }
+ }
+ return c;
+}
+
+// search depth d for field/method s --
+// 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);
+ n->diag |= n->left->diag;
+ 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 selector %N", n);
+ n->left = N;
+ return n;
+ }
+
+ // 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(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, 0);
+ if(u != T) {
+ for(f=u->method; f!=T; f=f->down) {
+ 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(Type *t)
+{
+ Symlink *sl;
+ Type *f;
+ int c, d;
+
+ 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) {
+ // addot1 may have dug out arbitrary fields, we only want methods.
+ if(f->type->etype == TFUNC && f->type->thistuple > 0) {
+ 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(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) {
+ // invent a name so that we can refer to it in the trampoline
+ snprint(buf, sizeof buf, ".anon%d", gen++);
+ n = newname(lookup(buf));
+ } else if(t->sym)
+ n = newname(t->sym);
+ 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, *dot, *as;
+ NodeList *l, *args, *in, *out;
+ Type *tpad, *methodrcvr;
+ int isddd;
+ Val v;
+ static int linehistdone = 0;
+
+ if(0 && debug['r'])
+ print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
+ rcvr, method, newnam);
+
+ lexlineno++;
+ lineno = lexlineno;
+ if (linehistdone == 0) {
+ // All the wrappers can share the same linehist entry.
+ linehist("<autogenerated>", 0, 0);
+ linehistdone = 1;
+ }
+
+ 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);
+
+ 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 = nod(ODCLFUNC, N, N);
+ fn->nname = newname(newnam);
+ fn->nname->defn = fn;
+ fn->nname->ntype = t;
+ declare(fn->nname, PFUNC);
+ 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;
+ }
+
+ methodrcvr = getthisx(method->type)->type->type;
+
+ // generate nil pointer check for better error
+ if(isptr[rcvr->etype] && rcvr->type == methodrcvr) {
+ // 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);
+ }
+
+ dot = adddot(nod(OXDOT, this->left, newname(method->sym)));
+
+ // generate call
+ if(!flag_race && isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) {
+ // generate tail call: adjust pointer receiver and jump to embedded method.
+ dot = dot->left; // skip final .M
+ if(!isptr[dotlist[0].field->type->etype])
+ dot = nod(OADDR, dot, N);
+ as = nod(OAS, this->left, nod(OCONVNOP, dot, N));
+ as->right->type = rcvr;
+ fn->nbody = list(fn->nbody, as);
+ n = nod(ORETJMP, N, N);
+ n->left = newname(methodsym(method->sym, methodrcvr, 0));
+ fn->nbody = list(fn->nbody, n);
+ } else {
+ fn->wrapper = 1; // ignore frame for panic+recover matching
+ call = nod(OCALL, dot, 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;
+ // wrappers where T is anonymous (struct or interface) can be duplicated.
+ if(rcvr->etype == TSTRUCT ||
+ rcvr->etype == TINTER ||
+ isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT)
+ fn->dupok = 1;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+
+ // Set inl_nonlocal to whether we are calling a method on a
+ // type defined in a different package. Checked in inlvar.
+ if(!methodrcvr->local)
+ inl_nonlocal = 1;
+
+ inlcalls(fn);
+
+ inl_nonlocal = 0;
+
+ curfn = nil;
+ funccompile(fn, 0);
+}
+
+static Node*
+hashmem(Type *t)
+{
+ Node *tfn, *n;
+ Sym *sym;
+
+ sym = pkglookup("memhash", runtimepkg);
+
+ n = newname(sym);
+ n->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->rlist = list(tfn->rlist, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ typecheck(&tfn, Etype);
+ n->type = tfn->type;
+ return n;
+}
+
+static Node*
+hashfor(Type *t)
+{
+ int a;
+ Sym *sym;
+ Node *tfn, *n;
+
+ a = algtype1(t, nil);
+ switch(a) {
+ case AMEM:
+ return hashmem(t);
+ case AINTER:
+ sym = pkglookup("interhash", runtimepkg);
+ break;
+ case ANILINTER:
+ sym = pkglookup("nilinterhash", runtimepkg);
+ break;
+ case ASTRING:
+ sym = pkglookup("strhash", runtimepkg);
+ break;
+ case AFLOAT32:
+ sym = pkglookup("f32hash", runtimepkg);
+ break;
+ case AFLOAT64:
+ sym = pkglookup("f64hash", runtimepkg);
+ break;
+ case ACPLX64:
+ sym = pkglookup("c64hash", runtimepkg);
+ break;
+ case ACPLX128:
+ sym = pkglookup("c128hash", runtimepkg);
+ break;
+ default:
+ sym = typesymprefix(".hash", t);
+ break;
+ }
+
+ n = newname(sym);
+ n->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ tfn->rlist = list(tfn->rlist, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ typecheck(&tfn, Etype);
+ n->type = tfn->type;
+ return n;
+}
+
+/*
+ * Generate a helper function to compute the hash of a value of type t.
+ */
+void
+genhash(Sym *sym, Type *t)
+{
+ Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn, *r;
+ Node *hashel;
+ Type *first, *t1;
+ int old_safemode;
+ int64 size, mul, offend;
+
+ if(debug['r'])
+ print("genhash %S %T\n", sym, t);
+
+ lineno = 1; // less confusing than end of input
+ dclcontext = PEXTERN;
+ markdcl();
+
+ // func sym(p *T, s uintptr, h uintptr) uintptr
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(sym);
+ fn->nname->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ fn->nname->ntype = tfn;
+
+ n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ np = n->left;
+ n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ n = nod(ODCLFIELD, newname(lookup("h")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ nh = n->left;
+ n = nod(ODCLFIELD, N, typenod(types[TUINTPTR])); // return value
+ tfn->rlist = list(tfn->rlist, n);
+
+ funchdr(fn);
+ typecheck(&fn->nname->ntype, Etype);
+
+ // genhash is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch(t->etype) {
+ default:
+ fatal("genhash %T", t);
+ case TARRAY:
+ if(isslice(t))
+ fatal("genhash %T", t);
+ // An array of pure memory would be handled by the
+ // standard algorithm, so the element type must not be
+ // pure memory.
+ hashel = hashfor(t->type);
+ n = nod(ORANGE, N, nod(OIND, np, N));
+ ni = newname(lookup("i"));
+ ni->type = types[TINT];
+ n->list = list1(ni);
+ n->colas = 1;
+ colasdefn(n->list, n);
+ ni = n->list->n;
+
+ // TODO: with aeshash we don't need these shift/mul parts
+
+ // h = h<<3 | h>>61
+ n->nbody = list(n->nbody,
+ nod(OAS,
+ nh,
+ nod(OOR,
+ nod(OLSH, nh, nodintconst(3)),
+ nod(ORSH, nh, nodintconst(widthptr*8-3)))));
+
+ // h *= mul
+ // Same multipliers as in runtime.memhash.
+ if(widthptr == 4)
+ mul = 3267000013LL;
+ else
+ mul = 23344194077549503LL;
+ n->nbody = list(n->nbody,
+ nod(OAS,
+ nh,
+ nod(OMUL, nh, nodintconst(mul))));
+
+ // h = hashel(&p[i], sizeof(p[i]), h)
+ call = nod(OCALL, hashel, N);
+ nx = nod(OINDEX, np, ni);
+ nx->bounded = 1;
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ call->list = list(call->list, nodintconst(t->type->width));
+ call->list = list(call->list, nh);
+ n->nbody = list(n->nbody, nod(OAS, nh, call));
+
+ fn->nbody = list(fn->nbody, n);
+ break;
+
+ case TSTRUCT:
+ // Walk the struct using memhash for runs of AMEM
+ // and calling specific hash functions for the others.
+ first = T;
+ offend = 0;
+ for(t1=t->type;; t1=t1->down) {
+ if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+ offend = t1->width + t1->type->width;
+ if(first == T)
+ first = t1;
+ // If it's a memory field but it's padded, stop here.
+ if(ispaddedfield(t1, t->width))
+ t1 = t1->down;
+ else
+ continue;
+ }
+ // Run memhash for fields up to this one.
+ if(first != T) {
+ size = offend - first->width; // first->width is offset
+ hashel = hashmem(first->type);
+ // h = hashel(&p.first, size, h)
+ call = nod(OCALL, hashel, N);
+ nx = nod(OXDOT, np, newname(first->sym)); // TODO: fields from other packages?
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ call->list = list(call->list, nodintconst(size));
+ call->list = list(call->list, nh);
+ fn->nbody = list(fn->nbody, nod(OAS, nh, call));
+
+ first = T;
+ }
+ if(t1 == T)
+ break;
+ if(isblanksym(t1->sym))
+ continue;
+
+ // Run hash for this field.
+ hashel = hashfor(t1->type);
+ // h = hashel(&p.t1, size, h)
+ call = nod(OCALL, hashel, N);
+ nx = nod(OXDOT, np, newname(t1->sym)); // TODO: fields from other packages?
+ na = nod(OADDR, nx, N);
+ na->etype = 1; // no escape to heap
+ call->list = list(call->list, na);
+ call->list = list(call->list, nodintconst(t1->type->width));
+ call->list = list(call->list, nh);
+ fn->nbody = list(fn->nbody, nod(OAS, nh, call));
+ }
+ break;
+ }
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nh);
+ fn->nbody = list(fn->nbody, r);
+
+ if(debug['r'])
+ dumplist("genhash body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ fn->dupok = 1;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode = safemode;
+ safemode = 0;
+ funccompile(fn, 0);
+ safemode = old_safemode;
+}
+
+// Return node for
+// if p.field != q.field { return false }
+static Node*
+eqfield(Node *p, Node *q, Node *field)
+{
+ Node *nif, *nx, *ny, *r;
+
+ nx = nod(OXDOT, p, field);
+ ny = nod(OXDOT, q, field);
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(ONE, nx, ny);
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
+ return nif;
+}
+
+static Node*
+eqmemfunc(vlong size, Type *type)
+{
+ char buf[30];
+ Node *fn;
+
+ switch(size) {
+ default:
+ fn = syslook("memequal", 1);
+ break;
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ snprint(buf, sizeof buf, "memequal%d", (int)size*8);
+ fn = syslook(buf, 1);
+ break;
+ }
+ argtype(fn, type);
+ argtype(fn, type);
+ return fn;
+}
+
+// Return node for
+// if !memequal(&p.field, &q.field, size) { return false }
+static Node*
+eqmem(Node *p, Node *q, Node *field, vlong size)
+{
+ Node *nif, *nx, *ny, *call, *r;
+
+ nx = nod(OADDR, nod(OXDOT, p, field), N);
+ nx->etype = 1; // does not escape
+ ny = nod(OADDR, nod(OXDOT, q, field), N);
+ ny->etype = 1; // does not escape
+ typecheck(&nx, Erv);
+ typecheck(&ny, Erv);
+
+ call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
+ call->list = list(call->list, nx);
+ call->list = list(call->list, ny);
+ call->list = list(call->list, nodintconst(size));
+
+ nif = nod(OIF, N, N);
+ nif->ninit = list(nif->ninit, call);
+ nif->ntest = nod(ONOT, call, N);
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
+ return nif;
+}
+
+/*
+ * Generate a helper function to check equality of two values of type t.
+ */
+void
+geneq(Sym *sym, Type *t)
+{
+ Node *n, *fn, *np, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange, *r;
+ Type *t1, *first;
+ int old_safemode;
+ int64 size;
+ int64 offend;
+
+ if(debug['r'])
+ print("geneq %S %T\n", sym, t);
+
+ lineno = 1; // less confusing than end of input
+ dclcontext = PEXTERN;
+ markdcl();
+
+ // func sym(p, q *T, s uintptr) bool
+ fn = nod(ODCLFUNC, N, N);
+ fn->nname = newname(sym);
+ fn->nname->class = PFUNC;
+ tfn = nod(OTFUNC, N, N);
+ fn->nname->ntype = tfn;
+
+ n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ np = n->left;
+ n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
+ tfn->list = list(tfn->list, n);
+ nq = n->left;
+ n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+ tfn->list = list(tfn->list, n);
+ n = nod(ODCLFIELD, N, typenod(types[TBOOL]));
+ tfn->rlist = list(tfn->rlist, n);
+
+ funchdr(fn);
+
+ // geneq is only called for types that have equality but
+ // cannot be handled by the standard algorithms,
+ // so t must be either an array or a struct.
+ switch(t->etype) {
+ default:
+ fatal("geneq %T", t);
+ case TARRAY:
+ if(isslice(t))
+ fatal("geneq %T", t);
+ // An array of pure memory would be handled by the
+ // standard memequal, so the element type must not be
+ // pure memory. Even if we unrolled the range loop,
+ // each iteration would be a function call, so don't bother
+ // unrolling.
+ nrange = nod(ORANGE, N, nod(OIND, np, N));
+ ni = newname(lookup("i"));
+ ni->type = types[TINT];
+ nrange->list = list1(ni);
+ nrange->colas = 1;
+ colasdefn(nrange->list, nrange);
+ ni = nrange->list->n;
+
+ // if p[i] != q[i] { return false }
+ nx = nod(OINDEX, np, ni);
+ nx->bounded = 1;
+ ny = nod(OINDEX, nq, ni);
+ ny->bounded = 1;
+
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(ONE, nx, ny);
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(0));
+ nif->nbody = list(nif->nbody, r);
+ nrange->nbody = list(nrange->nbody, nif);
+ fn->nbody = list(fn->nbody, nrange);
+ break;
+
+ case TSTRUCT:
+ // Walk the struct using memequal for runs of AMEM
+ // and calling specific equality tests for the others.
+ // Skip blank-named fields.
+ first = T;
+ offend = 0;
+ for(t1=t->type;; t1=t1->down) {
+ if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+ offend = t1->width + t1->type->width;
+ if(first == T)
+ first = t1;
+ // If it's a memory field but it's padded, stop here.
+ if(ispaddedfield(t1, t->width))
+ t1 = t1->down;
+ else
+ continue;
+ }
+ // Run memequal for fields up to this one.
+ // TODO(rsc): All the calls to newname are wrong for
+ // cross-package unexported fields.
+ if(first != T) {
+ if(first->down == t1) {
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+ } else if(first->down->down == t1) {
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+ first = first->down;
+ if(!isblanksym(first->sym))
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+ } else {
+ // More than two fields: use memequal.
+ size = offend - first->width; // first->width is offset
+ fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size));
+ }
+ first = T;
+ }
+ if(t1 == T)
+ break;
+ if(isblanksym(t1->sym))
+ continue;
+
+ // Check this field, which is not just memory.
+ fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym)));
+ }
+
+ break;
+ }
+
+ // return true
+ r = nod(ORETURN, N, N);
+ r->list = list(r->list, nodbool(1));
+ fn->nbody = list(fn->nbody, r);
+
+ if(debug['r'])
+ dumplist("geneq body", fn->nbody);
+
+ funcbody(fn);
+ curfn = fn;
+ fn->dupok = 1;
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ curfn = nil;
+
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode = safemode;
+ safemode = 0;
+ funccompile(fn, 0);
+ safemode = old_safemode;
+}
+
+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, 0);
+ if(t != T)
+ expandmeth(t);
+ for(im=iface->type; im; im=im->down) {
+ imtype = methodfunc(im->type, 0);
+ tm = ifacelookdot(im->sym, t, &followptr, 0);
+ if(tm == T || tm->nointerface || !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) {
+ // Flatten list and steal storage.
+ // Poison pointer to catch errant uses.
+ l = n->list;
+ n->list = (NodeList*)1;
+ return l;
+ }
+ 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)
+{
+ vlong n;
+
+ n = 0;
+ for(; l; l=l->next)
+ n++;
+ if((int)n != n) { // Overflow.
+ yyerror("too many elements in list");
+ }
+ 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 = 0xffffffffffffffffULL;
+ break;
+ }
+ two31 = mask ^ (mask>>1);
+
+ p = m->w-1;
+ ad = m->sd;
+ if(m->sd < 0)
+ ad = -(uvlong)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 = 0xffffffffffffffffULL;
+ 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->type != T)
+ return typenamesym(n->type);
+ return S;
+}
+
+/*
+ * Convert raw string to the prefix that will be used in the symbol
+ * table. All control characters, space, '%' and '"', as well as
+ * non-7-bit clean bytes turn into %xx. The period needs escaping
+ * only in the last segment of the path, and it makes for happier
+ * users if we escape that as little as possible.
+ *
+ * If you edit this, edit ../ld/lib.c:/^pathtoprefix too.
+ * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too.
+ */
+static char*
+pathtoprefix(char *s)
+{
+ static char hex[] = "0123456789abcdef";
+ char *p, *r, *w, *l;
+ int n;
+
+ // find first character past the last slash, if any.
+ l = s;
+ for(r=s; *r; r++)
+ if(*r == '/')
+ l = r+1;
+
+ // check for chars that need escaping
+ n = 0;
+ for(r=s; *r; r++)
+ if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
+ 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 >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
+ *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;
+
+ 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;
+}
+
+void
+addinit(Node **np, NodeList *init)
+{
+ Node *n;
+
+ if(init == nil)
+ return;
+
+ n = *np;
+ switch(n->op) {
+ case ONAME:
+ case OLITERAL:
+ // There may be multiple refs to this node;
+ // introduce OCONVNOP to hold init list.
+ n = nod(OCONVNOP, n, N);
+ n->type = n->left->type;
+ n->typecheck = 1;
+ *np = n;
+ break;
+ }
+ n->ninit = concat(init, n->ninit);
+ n->ullman = UINF;
+}
+
+static char* reservedimports[] = {
+ "go",
+ "type",
+};
+
+int
+isbadimport(Strlit *path)
+{
+ int i;
+ char *s;
+ Rune r;
+
+ if(strlen(path->s) != path->len) {
+ yyerror("import path contains NUL");
+ return 1;
+ }
+
+ for(i=0; i<nelem(reservedimports); i++) {
+ if(strcmp(path->s, reservedimports[i]) == 0) {
+ yyerror("import path \"%s\" is reserved and cannot be used", path->s);
+ return 1;
+ }
+ }
+
+ s = path->s;
+ while(*s) {
+ s += chartorune(&r, s);
+ if(r == Runeerror) {
+ yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
+ return 1;
+ }
+ if(r < 0x20 || r == 0x7f) {
+ yyerror("import path contains control character: \"%Z\"", path);
+ return 1;
+ }
+ if(r == '\\') {
+ yyerror("import path contains backslash; use slash: \"%Z\"", path);
+ return 1;
+ }
+ if(isspacerune(r)) {
+ yyerror("import path contains space character: \"%Z\"", path);
+ return 1;
+ }
+ if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
+ yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+checknil(Node *x, NodeList **init)
+{
+ Node *n;
+
+ if(isinter(x->type)) {
+ x = nod(OITAB, x, N);
+ typecheck(&x, Erv);
+ }
+ n = nod(OCHECKNIL, x, N);
+ n->typecheck = 1;
+ *init = list(*init, n);
+}
+
+/*
+ * Can this type be stored directly in an interface word?
+ */
+int
+isdirectiface(Type *t)
+{
+ // Setting IfacePointerOnly = 1 changes the
+ // interface representation so that the data word
+ // in an interface value must always be a pointer.
+ // Setting it to 0 uses the original representation,
+ // where the data word can hold a pointer or any
+ // non-pointer value no bigger than a pointer.
+ enum {
+ IfacePointerOnly = 1,
+ };
+
+ if(IfacePointerOnly) {
+ switch(t->etype) {
+ case TPTR32:
+ case TPTR64:
+ case TCHAN:
+ case TMAP:
+ case TFUNC:
+ case TUNSAFEPTR:
+ return 1;
+ case TARRAY:
+ // Array of 1 direct iface type can be direct.
+ return t->bound == 1 && isdirectiface(t->type);
+ case TSTRUCT:
+ // Struct with 1 field of direct iface type can be direct.
+ return t->type != T && t->type->down == T && isdirectiface(t->type->type);
+ }
+ return 0;
+ }
+
+ dowidth(t);
+ return t->width <= widthptr;
+}
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
new file mode 100644
index 0000000..e1d8af8
--- /dev/null
+++ b/src/cmd/gc/swt.c
@@ -0,0 +1,948 @@
+// 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 "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)
+/*c2go Case *C; */
+
+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;
+
+ // sort by type (for switches on interface)
+ ct = n1->val.ctype;
+ if(ct != n2->val.ctype)
+ return ct - n2->val.ctype;
+ if(!eqtype(n1->type, n2->type)) {
+ if(n1->type->vargen > n2->type->vargen)
+ return +1;
+ else
+ return -1;
+ }
+
+ // sort by constant value
+ n = 0;
+ switch(ct) {
+ case CTFLT:
+ n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
+ break;
+ case CTINT:
+ case CTRUNE:
+ 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;
+
+ if(sw->list == nil)
+ return;
+
+ lno = setlineno(sw);
+
+ 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;
+ 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 - shouldn't fall thru declaration
+ last = stat->end->n;
+ if(last->xoffset == n->xoffset && last->op == OXFALL) {
+ if(typeswvar) {
+ setlineno(last);
+ yyerror("cannot fallthrough in type switch");
+ }
+ if(l->next == nil) {
+ setlineno(last);
+ yyerror("cannot fallthrough final case in 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++;
+ if((uint16)ord != ord)
+ fatal("too many cases in switch");
+ 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;
+ c->hash = typehash(n->left->type);
+ switch(consttype(n->left)) {
+ case CTFLT:
+ case CTINT:
+ case CTRUNE:
+ 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 %T in type switch\n\tprevious case at %L", c2->node->left->type, 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 %N in switch\n\tprevious case at %L", c1->node->left, 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);
+
+ if(assignop(n->left->type, exprname->type, nil) == OCONVIFACE ||
+ assignop(exprname->type, n->left->type, nil) == OCONVIFACE)
+ goto snorm;
+
+ 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:
+ snorm:
+ 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 = temp(sw->ntest->type);
+ cas = list1(nod(OAS, exprname, sw->ntest));
+ typechecklist(cas, Etop);
+ } else {
+ exprname = nodbool(arg == Strue);
+ }
+
+ 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(!okforcmp[t->etype] || 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 = temp(sw->ntest->right->type);
+ a = nod(OAS, facename, sw->ntest->right);
+ typecheck(&a, Etop);
+ cas = list(cas, a);
+
+ casebody(sw, facename);
+
+ boolname = temp(types[TBOOL]);
+ typecheck(&boolname, Erv);
+
+ hashname = temp(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
+ */
+ 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);
+ // Discard old AST elements after a walk. They can confuse racewealk.
+ sw->ntest = nil;
+ sw->list = nil;
+}
+
+/*
+ * type check switch statement
+ */
+void
+typecheckswitch(Node *n)
+{
+ int top, lno, ptr;
+ char *nilonly;
+ Type *t, *badtype, *missing, *have;
+ NodeList *l, *ll;
+ Node *ncase, *nvar;
+ Node *def;
+
+ lno = lineno;
+ typechecklist(n->ninit, Etop);
+ nilonly = nil;
+
+ 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 %lN", 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];
+ if(t) {
+ if(!okforeq[t->etype])
+ yyerror("cannot switch on %lN", n->ntest);
+ else if(t->etype == TARRAY && !isfixedarray(t))
+ nilonly = "slice";
+ else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ)
+ yyerror("cannot switch on %lN", n->ntest);
+ else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ)
+ yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype);
+ else if(t->etype == TFUNC)
+ nilonly = "func";
+ else if(t->etype == TMAP)
+ nilonly = "map";
+ }
+ }
+ 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;
+ setlineno(ncase);
+ 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 && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) {
+ if(n->ntest)
+ yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t);
+ else
+ yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type);
+ } else if(nilonly && !isconst(ll->n, CTNIL)) {
+ yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest);
+ }
+ 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) { // should this be ||?
+ yyerror("%lN is not a type", ll->n);
+ // reset to original type
+ ll->n = n->ntest->right;
+ } else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) {
+ if(have && !missing->broke && !have->broke)
+ yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+ " (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT",
+ n->ntest->right, ll->n->type, missing->sym, have->sym, have->type,
+ missing->sym, missing->type);
+ else if(!missing->broke)
+ yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+ " (missing %S method)", n->ntest->right, ll->n->type, missing->sym);
+ }
+ 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 0000000..ce12f15
--- /dev/null
+++ b/src/cmd/gc/typecheck.c
@@ -0,0 +1,3582 @@
+// 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 <u.h>
+#include <libc.h>
+#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(Node*, Sym *s, Type *t, Type *f, int);
+static int nokeys(NodeList*);
+static void typecheckcomplit(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 void checkdefergo(Node*);
+static int checkmake(Type*, char*, Node*);
+static int checksliceindex(Node*, Node*, Type*);
+static int checksliceconst(Node*, Node*);
+
+static NodeList* typecheckdefstack;
+
+/*
+ * resolve ONONAME to definition, if any.
+ */
+static Node*
+resolve(Node *n)
+{
+ Node *r;
+
+ if(n != N && n->op == ONONAME && n->sym != S && (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",
+ [TUNSAFEPTR] = "unsafe.Pointer",
+ [TSTRUCT] = "struct",
+ [TINTER] = "interface",
+ [TCHAN] = "chan",
+ [TMAP] = "map",
+ [TARRAY] = "array",
+ [TFUNC] = "func",
+ [TNIL] = "nil",
+ [TIDEAL] = "untyped number",
+};
+
+static char*
+typekind(Type *t)
+{
+ int et;
+ static char buf[50];
+ char *s;
+
+ if(isslice(t))
+ return "slice";
+ et = t->etype;
+ if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
+ return s;
+ snprint(buf, sizeof buf, "etype=%d", et);
+ return buf;
+}
+
+/*
+ * sprint_depchain prints a dependency chain
+ * of nodes into fmt.
+ * It is used by typecheck in the case of OLITERAL nodes
+ * to print constant definition loops.
+ */
+static void
+sprint_depchain(Fmt *fmt, NodeList *stack, Node *cur, Node *first)
+{
+ NodeList *l;
+
+ for(l = stack; l; l=l->next) {
+ if(l->n->op == cur->op) {
+ if(l->n != first)
+ sprint_depchain(fmt, l->next, l->n, first);
+ fmtprint(fmt, "\n\t%L: %N uses %N", l->n->lineno, l->n, cur);
+ return;
+ }
+ }
+}
+
+/*
+ * type check node *np.
+ * replaces *np with a new pointer in some cases.
+ * returns the final value of *np as a convenience.
+ */
+static void typecheck1(Node **, int);
+Node*
+typecheck(Node **np, int top)
+{
+ Node *n;
+ int lno;
+ Fmt fmt;
+ NodeList *l;
+ static NodeList *tcstack, *tcfree;
+
+ // 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) {
+ // Typechecking loop. Trying printing a meaningful message,
+ // otherwise a stack trace of typechecking.
+ switch(n->op) {
+ case ONAME:
+ // We can already diagnose variables used as types.
+ if((top & (Erv|Etype)) == Etype)
+ yyerror("%N is not a type", n);
+ break;
+ case OLITERAL:
+ if((top & (Erv|Etype)) == Etype) {
+ yyerror("%N is not a type", n);
+ break;
+ }
+ fmtstrinit(&fmt);
+ sprint_depchain(&fmt, tcstack, n, n);
+ yyerrorl(n->lineno, "constant definition loop%s", fmtstrflush(&fmt));
+ break;
+ }
+ if(nsavederrors+nerrors == 0) {
+ fmtstrinit(&fmt);
+ for(l=tcstack; l; l=l->next)
+ fmtprint(&fmt, "\n\t%L %N", l->n->lineno, l->n);
+ yyerror("typechecking loop involving %N%s", n, fmtstrflush(&fmt));
+ }
+ lineno = lno;
+ return n;
+ }
+ n->typecheck = 2;
+
+ if(tcfree != nil) {
+ l = tcfree;
+ tcfree = l->next;
+ } else
+ l = mal(sizeof *l);
+ l->next = tcstack;
+ l->n = n;
+ tcstack = l;
+
+ typecheck1(&n, top);
+ *np = n;
+ n->typecheck = 1;
+
+ if(tcstack != l)
+ fatal("typecheck stack out of sync");
+ tcstack = l->next;
+ l->next = tcfree;
+ tcfree = l;
+
+ lineno = lno;
+ return n;
+}
+
+/*
+ * does n contain a call or receive operation?
+ */
+static int callrecvlist(NodeList*);
+
+static int
+callrecv(Node *n)
+{
+ if(n == nil)
+ return 0;
+
+ switch(n->op) {
+ case OCALL:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALLFUNC:
+ case ORECV:
+ case OCAP:
+ case OLEN:
+ case OCOPY:
+ case ONEW:
+ case OAPPEND:
+ case ODELETE:
+ return 1;
+ }
+
+ return callrecv(n->left) ||
+ callrecv(n->right) ||
+ callrecv(n->ntest) ||
+ callrecv(n->nincr) ||
+ callrecvlist(n->ninit) ||
+ callrecvlist(n->nbody) ||
+ callrecvlist(n->nelse) ||
+ callrecvlist(n->list) ||
+ callrecvlist(n->rlist);
+}
+
+static int
+callrecvlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(callrecv(l->n))
+ return 1;
+ return 0;
+}
+
+// indexlit implements typechecking of untyped values as
+// array/slice indexes. It is equivalent to defaultlit
+// except for constants of numerical kind, which are acceptable
+// whenever they can be represented by a value of type int.
+static void
+indexlit(Node **np)
+{
+ Node *n;
+
+ n = *np;
+ if(n == N || !isideal(n->type))
+ return;
+ switch(consttype(n)) {
+ case CTINT:
+ case CTRUNE:
+ case CTFLT:
+ case CTCPLX:
+ defaultlit(np, types[TINT]);
+ break;
+ }
+ defaultlit(np, T);
+}
+
+static void
+typecheck1(Node **np, int top)
+{
+ int et, aop, op, ptr;
+ Node *n, *l, *r, *lo, *mid, *hi;
+ NodeList *args;
+ int ok, ntop;
+ Type *t, *tp, *missing, *have, *badtype;
+ Val v;
+ char *why, *desc, descbuf[64];
+ vlong x;
+
+ n = *np;
+
+ 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;
+ }
+
+ typecheckdef(n);
+ 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;
+ }
+ if(!(top &Ecall) && isunsafebuiltin(n)) {
+ yyerror("%N is not an expression, must be called", n);
+ goto error;
+ }
+ ok |= Erv;
+ goto ret;
+
+ case OPACK:
+ yyerror("use of package %S without 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 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) && !n->diag) {
+ t->broke = 1;
+ n->diag = 1;
+ yyerror("use of [...] array outside of array literal");
+ }
+ } else {
+ l = typecheck(&n->left, Erv);
+ switch(consttype(l)) {
+ case CTINT:
+ case CTRUNE:
+ v = l->val;
+ break;
+ case CTFLT:
+ v = toint(l->val);
+ break;
+ default:
+ if(l->type != T && isint[l->type->etype] && l->op != OLITERAL)
+ yyerror("non-constant array bound %N", l);
+ else
+ yyerror("invalid array bound %N", l);
+ goto error;
+ }
+ t->bound = mpgetfix(v.u.xval);
+ if(doesoverflow(v, types[TINT])) {
+ yyerror("array bound is too large");
+ goto error;
+ } else if(t->bound < 0) {
+ yyerror("array bound must be non-negative");
+ goto error;
+ }
+ }
+ 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 = tostruct(n->list);
+ if(n->type == T || n->type->broke)
+ goto error;
+ n->list = nil;
+ break;
+
+ case OTINTER:
+ ok |= Etype;
+ n->op = OTYPE;
+ n->type = tointerface(n->list);
+ 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)) // The *x in &*x is not an indirect.
+ ntop |= Eindir;
+ ntop |= top & Ecomplit;
+ 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]) {
+ if(top & (Erv | Etop)) {
+ yyerror("invalid indirect of %lN", n->left);
+ goto error;
+ }
+ goto ret;
+ }
+ 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.
+ // in that case, check comparability of the concrete type.
+ if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
+ if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type));
+ goto error;
+ }
+ 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) {
+ if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type));
+ goto error;
+ }
+ 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);
+ if(n->op == OASOP && n->implicit) {
+ yyerror("invalid operation: %N (non-numeric type %T)", n, l->type);
+ goto error;
+ }
+ yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
+ goto error;
+ }
+ if(!okfor[op][et]) {
+ yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t));
+ goto error;
+ }
+ // okfor allows any array == array, map == map, func == func.
+ // restrict to slice/map/func == nil and nil == slice/map/func.
+ if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) {
+ yyerror("invalid operation: %N (%T cannot be compared)", n, l->type);
+ goto error;
+ }
+ if(isslice(l->type) && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %N (slice can only be compared to nil)", n);
+ goto error;
+ }
+ if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %N (map can only be compared to nil)", n);
+ goto error;
+ }
+ if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) {
+ yyerror("invalid operation: %N (func can only be compared to nil)", n);
+ goto error;
+ }
+ if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) {
+ yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype);
+ goto error;
+ }
+
+ t = l->type;
+ if(iscmp[n->op]) {
+ evconst(n);
+ t = idealbool;
+ if(n->op != OLITERAL) {
+ defaultlit2(&l, &r, 1);
+ n->left = l;
+ n->right = r;
+ }
+ // non-comparison operators on ideal bools should make them lose their ideal-ness
+ } else if(t == idealbool)
+ t = types[TBOOL];
+
+ if(et == TSTRING) {
+ if(iscmp[n->op]) {
+ n->etype = n->op;
+ n->op = OCMPSTR;
+ } else if(n->op == OADD) {
+ // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
+ n->op = OADDSTR;
+ if(l->op == OADDSTR)
+ n->list = l->list;
+ else
+ n->list = list1(l);
+ if(r->op == OADDSTR)
+ n->list = concat(n->list, r->list);
+ else
+ n->list = list(n->list, r);
+ n->left = N;
+ n->right = N;
+ }
+ }
+ 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;
+ }
+ }
+
+ if((op == ODIV || op == OMOD) && isconst(r, CTINT))
+ if(mpcmpfixc(r->val.u.xval, 0) == 0) {
+ yyerror("division by zero");
+ goto error;
+ }
+
+ 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;
+ checklvalue(n->left, "take the address of");
+ r = outervalue(n->left);
+ for(l = n->left; l != r; l = l->left)
+ l->addrtaken = 1;
+ if(l->orig != l && l->op == ONAME)
+ fatal("found non-orig name node %N", l);
+ l->addrtaken = 1;
+ defaultlit(&n->left, T);
+ l = n->left;
+ if((t = l->type) == T)
+ goto error;
+ 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;
+ if(n->left == N)
+ goto error;
+ // fall through
+ case ODOT:
+ typecheck(&n->left, Erv|Etype);
+ defaultlit(&n->left, T);
+ if((t = n->left->type) == T)
+ goto error;
+ if(n->right->op != ONAME) {
+ yyerror("rhs of . must be a name"); // impossible
+ goto error;
+ }
+ r = n->right;
+
+ if(n->left->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, n->right->sym);
+ n->type = T;
+ goto error;
+ }
+ n->op = ONAME;
+ n->sym = n->right->sym;
+ n->type = methodfunc(n->type, n->left->type);
+ n->xoffset = 0;
+ n->class = PFUNC;
+ ok = Erv;
+ goto ret;
+ }
+ if(isptr[t->etype] && t->type->etype != TINTER) {
+ t = t->type;
+ if(t == T)
+ goto error;
+ n->op = ODOTPTR;
+ checkwidth(t);
+ }
+ if(isblank(n->right)) {
+ yyerror("cannot refer to blank field or method");
+ goto error;
+ }
+ 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, n->left->type, n->right->sym);
+ goto error;
+ }
+ switch(n->op) {
+ case ODOTINTER:
+ case ODOTMETH:
+ if(top&Ecall)
+ ok |= Ecall;
+ else {
+ typecheckpartialcall(n, r);
+ ok |= Erv;
+ }
+ 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 && have->sym == missing->sym)
+ yyerror("impossible type assertion:\n\t%T does not implement %T (wrong type for %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else if(ptr)
+ yyerror("impossible type assertion:\n\t%T does not implement %T (%S method has pointer receiver)",
+ n->type, t, missing->sym);
+ else if(have)
+ yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n"
+ "\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
+ have->sym, have->type, missing->sym, missing->type);
+ else
+ yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)",
+ n->type, t, 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 (type %T does not support indexing)", n, t);
+ goto error;
+
+
+ case TSTRING:
+ case TARRAY:
+ indexlit(&n->right);
+ if(t->etype == TSTRING)
+ n->type = types[TUINT8];
+ else
+ n->type = t->type;
+ why = "string";
+ if(t->etype == TARRAY) {
+ if(isfixedarray(t))
+ why = "array";
+ else
+ why = "slice";
+ }
+ if(n->right->type != T && !isint[n->right->type->etype]) {
+ yyerror("non-integer %s index %N", why, n->right);
+ break;
+ }
+ if(isconst(n->right, CTINT)) {
+ x = mpgetfix(n->right->val.u.xval);
+ if(x < 0)
+ yyerror("invalid %s index %N (index must be non-negative)", why, n->right);
+ else if(isfixedarray(t) && t->bound > 0 && x >= t->bound)
+ yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound);
+ else if(isconst(n->left, CTSTR) && x >= n->left->val.u.sval->len)
+ yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len);
+ else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
+ yyerror("invalid %s index %N (index too large)", why, n->right);
+ }
+ 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;
+ }
+ 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:
+ ok |= Etop;
+ 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(r->type == T)
+ goto error;
+ n->right = 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);
+ indexlit(&n->right->left);
+ indexlit(&n->right->right);
+ l = n->left;
+ if(isfixedarray(l->type)) {
+ if(!islvalue(n->left)) {
+ yyerror("invalid operation %N (slice of unaddressable value)", n);
+ goto error;
+ }
+ n->left = nod(OADDR, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Erv);
+ l = n->left;
+ }
+ if((t = l->type) == T)
+ goto error;
+ tp = nil;
+ if(istype(t, TSTRING)) {
+ n->type = t;
+ n->op = OSLICESTR;
+ } else if(isptr[t->etype] && isfixedarray(t->type)) {
+ tp = t->type;
+ n->type = typ(TARRAY);
+ n->type->type = tp->type;
+ n->type->bound = -1;
+ dowidth(n->type);
+ n->op = OSLICEARR;
+ } else if(isslice(t)) {
+ n->type = t;
+ } else {
+ yyerror("cannot slice %N (type %T)", l, t);
+ goto error;
+ }
+ if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
+ goto error;
+ if((hi = n->right->right) != N && checksliceindex(l, hi, tp) < 0)
+ goto error;
+ if(checksliceconst(lo, hi) < 0)
+ goto error;
+ goto ret;
+
+ case OSLICE3:
+ ok |= Erv;
+ typecheck(&n->left, top);
+ typecheck(&n->right->left, Erv);
+ typecheck(&n->right->right->left, Erv);
+ typecheck(&n->right->right->right, Erv);
+ defaultlit(&n->left, T);
+ indexlit(&n->right->left);
+ indexlit(&n->right->right->left);
+ indexlit(&n->right->right->right);
+ l = n->left;
+ if(isfixedarray(l->type)) {
+ if(!islvalue(n->left)) {
+ yyerror("invalid operation %N (slice of unaddressable value)", n);
+ goto error;
+ }
+ n->left = nod(OADDR, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Erv);
+ l = n->left;
+ }
+ if((t = l->type) == T)
+ goto error;
+ tp = nil;
+ if(istype(t, TSTRING)) {
+ yyerror("invalid operation %N (3-index slice of string)", n);
+ goto error;
+ }
+ if(isptr[t->etype] && isfixedarray(t->type)) {
+ tp = t->type;
+ n->type = typ(TARRAY);
+ n->type->type = tp->type;
+ n->type->bound = -1;
+ dowidth(n->type);
+ n->op = OSLICE3ARR;
+ } else if(isslice(t)) {
+ n->type = t;
+ } else {
+ yyerror("cannot slice %N (type %T)", l, t);
+ goto error;
+ }
+ if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
+ goto error;
+ if((mid = n->right->right->left) != N && checksliceindex(l, mid, tp) < 0)
+ goto error;
+ if((hi = n->right->right->right) != N && checksliceindex(l, hi, tp) < 0)
+ goto error;
+ if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0)
+ goto error;
+ goto ret;
+
+ /*
+ * 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));
+ n->diag |= n->left->diag;
+ 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) {
+ if(!l->type->broke)
+ yyerror("invalid use of ... in type conversion", l);
+ n->diag = 1;
+ }
+ // 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 && !n->isddd)
+ 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;
+ }
+ if(snprint(descbuf, sizeof descbuf, "argument to %N", n->left) < sizeof descbuf)
+ desc = descbuf;
+ else
+ desc = "function argument";
+ typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, desc);
+ 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)){
+ r = n;
+ if(n->op == OREAL)
+ n = nodfltconst(&l->val.u.cval->real);
+ else
+ n = nodfltconst(&l->val.u.cval->imag);
+ n->orig = r;
+ }
+ 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) // slice
+ break;
+ if(callrecv(l)) // has call or receive
+ break;
+ 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(count(n->list) == 1) {
+ typechecklist(n->list, Efnstruct);
+ t = n->list->n->left->type;
+ if(t->outtuple != 2) {
+ yyerror("invalid operation: complex expects two arguments, %N returns %d results", n->list->n, t->outtuple);
+ goto error;
+ }
+ t = n->list->n->type->type;
+ l = t->nname;
+ r = t->down->nname;
+ } else {
+ 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);
+ if(l->type == T || r->type == T)
+ goto error;
+ n->left = l;
+ n->right = r;
+ }
+ if(!eqtype(l->type, r->type)) {
+ yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
+ goto error;
+ }
+ switch(l->type->etype) {
+ default:
+ yyerror("invalid operation: %N (arguments have type %T, expected floating-point)", n, l->type, r->type);
+ goto error;
+ 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
+ r = nodcplxlit(l->val, r->val);
+ r->orig = n;
+ n = r;
+ }
+ 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;
+ }
+ if(!(t->chan & Csend)) {
+ yyerror("invalid operation: %N (cannot close receive-only channel)", n);
+ goto error;
+ }
+ ok |= Etop;
+ goto ret;
+
+ case ODELETE:
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing arguments to delete");
+ goto error;
+ }
+ if(args->next == nil) {
+ yyerror("missing second (key) argument to delete");
+ goto error;
+ }
+ if(args->next->next != nil) {
+ yyerror("too many arguments to delete");
+ goto error;
+ }
+ ok |= Etop;
+ typechecklist(args, Erv);
+ l = args->n;
+ r = args->next->n;
+ if(l->type != T && l->type->etype != TMAP) {
+ yyerror("first argument to delete must be map; have %lT", l->type);
+ goto error;
+ }
+ args->next->n = assignconv(r, l->type->down, "delete");
+ goto ret;
+
+ case OAPPEND:
+ ok |= Erv;
+ args = n->list;
+ if(args == nil) {
+ yyerror("missing arguments to append");
+ goto error;
+ }
+
+ if(count(args) == 1 && !n->isddd)
+ typecheck(&args->n, Erv | Efnstruct);
+ else
+ typechecklist(args, Erv);
+
+ if((t = args->n->type) == T)
+ goto error;
+
+ // Unpack multiple-return result before type-checking.
+ if(istype(t, TSTRUCT) && t->funarg) {
+ t = t->type;
+ if(istype(t, TFIELD))
+ t = t->type;
+ }
+
+ n->type = t;
+ if(!isslice(t)) {
+ if(isconst(args->n, CTNIL)) {
+ yyerror("first argument to append must be typed slice; have untyped nil", t);
+ goto error;
+ }
+ 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;
+ }
+ if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) {
+ defaultlit(&args->next->n, types[TSTRING]);
+ goto ret;
+ }
+ 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->list = nil;
+ 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);
+ if(n->left->type == T || n->right->type == T)
+ goto error;
+
+ // copy([]byte, string)
+ if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
+ if(eqtype(n->left->type->type, bytetype))
+ 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;
+ saveorignode(n);
+ 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) {
+ if(!n->diag && !n->type->broke) {
+ yyerror("cannot convert %lN to type %T%s", n->left, n->type, why);
+ n->diag = 1;
+ }
+ n->op = OCONV;
+ }
+ switch(n->op) {
+ case OCONVNOP:
+ if(n->left->op == OLITERAL) {
+ r = nod(OXXX, N, N);
+ n->op = OCONV;
+ n->orig = r;
+ *r = *n;
+ n->op = OLITERAL;
+ n->val = n->left->val;
+ }
+ break;
+ case OSTRARRAYBYTE:
+ // do not use stringtoarraylit.
+ // generated code and compiler memory footprint is better without it.
+ break;
+ 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;
+ }
+ n->list = nil;
+ 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);
+ r = N;
+ if(args != nil) {
+ r = args->n;
+ args = args->next;
+ typecheck(&r, Erv);
+ }
+ if(l->type == T || (r && r->type == T))
+ goto error;
+ et = checkmake(t, "len", l) < 0;
+ et |= r && checkmake(t, "cap", r) < 0;
+ if(et)
+ goto error;
+ if(isconst(l, CTINT) && r && isconst(r, CTINT) && mpcmpfixfix(l->val.u.xval, r->val.u.xval) > 0) {
+ yyerror("len larger than cap in 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(checkmake(t, "size", l) < 0)
+ 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(checkmake(t, "buffer", l) < 0)
+ 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
+ for(args=n->list; args; args=args->next) {
+ // Special case for print: int constant is int64, not int.
+ if(isconst(args->n, CTINT))
+ defaultlit(&args->n, types[TINT64]);
+ else
+ defaultlit(&args->n, T);
+ }
+ 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;
+
+ case OITAB:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ if((t = n->left->type) == T)
+ goto error;
+ if(t->etype != TINTER)
+ fatal("OITAB of %T", t);
+ n->type = ptrto(types[TUINTPTR]);
+ goto ret;
+
+ case OSPTR:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ if((t = n->left->type) == T)
+ goto error;
+ if(!isslice(t) && t->etype != TSTRING)
+ fatal("OSPTR of %T", t);
+ if(t->etype == TSTRING)
+ n->type = ptrto(types[TUINT8]);
+ else
+ n->type = ptrto(t->type);
+ goto ret;
+
+ case OCLOSUREVAR:
+ ok |= Erv;
+ goto ret;
+
+ case OCFUNC:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ n->type = types[TUINTPTR];
+ goto ret;
+
+ case OCONVNOP:
+ ok |= Erv;
+ typecheck(&n->left, Erv);
+ 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:
+ case OVARKILL:
+ ok |= Etop;
+ goto ret;
+
+ case ODEFER:
+ ok |= Etop;
+ typecheck(&n->left, Etop|Erv);
+ if(!n->left->diag)
+ checkdefergo(n);
+ goto ret;
+
+ case OPROC:
+ ok |= Etop;
+ typecheck(&n->left, Etop|Eproc|Erv);
+ checkdefergo(n);
+ 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 %lN 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 %lN used as if condition", n->ntest);
+ typechecklist(n->nbody, Etop);
+ typechecklist(n->nelse, Etop);
+ goto ret;
+
+ case ORETURN:
+ ok |= Etop;
+ if(count(n->list) == 1)
+ typechecklist(n->list, Erv | Efnstruct);
+ else
+ typechecklist(n->list, Erv);
+ 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 ORETJMP:
+ ok |= Etop;
+ 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);
+ }
+ }
+
+ if(safemode && !incannedimport && !importpkg && !compiling_wrappers && 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;
+ }
+ // 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 evaluated but not used", n);
+ n->diag = 1;
+ }
+ goto error;
+ }
+
+ /* TODO
+ if(n->type == T)
+ fatal("typecheck nil type");
+ */
+ goto out;
+
+badcall1:
+ yyerror("invalid argument %lN for %O", n->left, n->op);
+ goto error;
+
+error:
+ n->type = T;
+
+out:
+ *np = n;
+}
+
+static int
+checksliceindex(Node *l, Node *r, Type *tp)
+{
+ Type *t;
+
+ if((t = r->type) == T)
+ return -1;
+ if(!isint[t->etype]) {
+ yyerror("invalid slice index %N (type %T)", r, t);
+ return -1;
+ }
+ if(r->op == OLITERAL) {
+ if(mpgetfix(r->val.u.xval) < 0) {
+ yyerror("invalid slice index %N (index must be non-negative)", r);
+ return -1;
+ } else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) {
+ yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound);
+ return -1;
+ } else if(isconst(l, CTSTR) && mpgetfix(r->val.u.xval) > l->val.u.sval->len) {
+ yyerror("invalid slice index %N (out of bounds for %d-byte string)", r, l->val.u.sval->len);
+ return -1;
+ } else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) {
+ yyerror("invalid slice index %N (index too large)", r);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+checksliceconst(Node *lo, Node *hi)
+{
+ if(lo != N && hi != N && lo->op == OLITERAL && hi->op == OLITERAL
+ && mpcmpfixfix(lo->val.u.xval, hi->val.u.xval) > 0) {
+ yyerror("invalid slice index: %N > %N", lo, hi);
+ return -1;
+ }
+ return 0;
+}
+
+static void
+checkdefergo(Node *n)
+{
+ char *what;
+
+ what = "defer";
+ if(n->op == OPROC)
+ what = "go";
+
+ switch(n->left->op) {
+ case OCALLINTER:
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCLOSE:
+ case OCOPY:
+ case ODELETE:
+ case OPANIC:
+ case OPRINT:
+ case OPRINTN:
+ case ORECOVER:
+ // ok
+ break;
+ case OAPPEND:
+ case OCAP:
+ case OCOMPLEX:
+ case OIMAG:
+ case OLEN:
+ case OMAKE:
+ case OMAKESLICE:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case ONEW:
+ case OREAL:
+ case OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
+ if(n->left->orig != N && n->left->orig->op == OCONV)
+ goto conv;
+ yyerror("%s discards result of %N", what, n->left);
+ break;
+ default:
+ conv:
+ // type is broken or missing, most likely a method call on a broken type
+ // we will warn about the broken type elsewhere. no need to emit a potentially confusing error
+ if(n->left->type == T || n->left->type->broke)
+ break;
+
+ if(!n->diag) {
+ // The syntax made sure it was a call, so this must be
+ // a conversion.
+ n->diag = 1;
+ yyerror("%s requires function call, not conversion", what);
+ }
+ break;
+ }
+}
+
+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);
+ n->implicit = 1;
+ 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(Node *errnode, 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) {
+ if(errnode)
+ yyerror("ambiguous selector %N", errnode);
+ else if(isptr[t->etype])
+ yyerror("ambiguous selector (%T).%S", t, s);
+ else
+ yyerror("ambiguous selector %T.%S", t, s);
+ break;
+ }
+ r = f;
+ }
+ return r;
+}
+
+static int
+looktypedot(Node *n, Type *t, int dostrcmp)
+{
+ Type *f1, *f2;
+ Sym *s;
+
+ s = n->right->sym;
+
+ if(t->etype == TINTER) {
+ f1 = lookdot1(n, s, t, t->type, dostrcmp);
+ if(f1 == T)
+ return 0;
+
+ n->right = methodname(n->right, t);
+ n->xoffset = f1->width;
+ n->type = f1->type;
+ n->op = ODOTINTER;
+ return 1;
+ }
+
+ // Find the base type: methtype will fail if t
+ // is not of the form T or *T.
+ f2 = methtype(t, 0);
+ if(f2 == T)
+ return 0;
+
+ expandmeth(f2);
+ f2 = lookdot1(n, 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).%hS)", n, t, f2->sym);
+ return 0;
+ }
+
+ n->right = methodname(n->right, t);
+ n->xoffset = f2->width;
+ n->type = f2->type;
+ n->op = ODOTMETH;
+ return 1;
+}
+
+static Type*
+derefall(Type* t)
+{
+ while(t && t->etype == tptr)
+ t = t->type;
+ return t;
+}
+
+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(n, s, t, t->type, dostrcmp);
+
+ f2 = T;
+ if(n->left->type == t || n->left->type->sym == S) {
+ f2 = methtype(t, 0);
+ if(f2 != T) {
+ // Use f2->method, not f2->xmethod: adddot has
+ // already inserted all the necessary embedded dots.
+ f2 = lookdot1(n, s, f2, f2->method, dostrcmp);
+ }
+ }
+
+ if(f1 != T) {
+ if(f2 != T)
+ yyerror("%S is 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;
+ n->paramfld = f1;
+ if(t->etype == TINTER) {
+ if(isptr[n->left->type->etype]) {
+ n->left = nod(OIND, n->left, N); // implicitstar
+ n->left->implicit = 1;
+ 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");
+ n->left = nod(OADDR, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ } else if(tt->etype == tptr && rcvr->etype != tptr && eqtype(tt->type, rcvr)) {
+ n->left = nod(OIND, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ } else if(tt->etype == tptr && tt->type->etype == tptr && eqtype(derefall(tt), derefall(rcvr))) {
+ yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left);
+ while(tt->etype == tptr) {
+ // Stop one level early for method with pointer receiver.
+ if(rcvr->etype == tptr && tt->type->etype != tptr)
+ break;
+ n->left = nod(OIND, n->left, N);
+ n->left->implicit = 1;
+ typecheck(&n->left, Etype|Erv);
+ tt = tt->type;
+ }
+ } else {
+ fatal("method mismatch: %T for %T", rcvr, tt);
+ }
+ }
+ n->right = methodname(n->right, n->left->type);
+ n->xoffset = f2->width;
+ n->type = f2->type;
+// print("lookdot found [%p] %T\n", f2->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;
+}
+
+static int
+hasddd(Type *t)
+{
+ Type *tl;
+
+ for(tl=t->type; tl; tl=tl->down) {
+ if(tl->isddd)
+ return 1;
+ }
+ return 0;
+}
+
+static int
+downcount(Type *t)
+{
+ Type *tl;
+ int n;
+
+ n = 0;
+ for(tl=t->type; tl; tl=tl->down) {
+ n++;
+ }
+ return n;
+}
+
+/*
+ * 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;
+ int n1, n2;
+
+ lno = lineno;
+
+ if(tstruct->broke)
+ goto out;
+
+ n = N;
+ if(nl != nil && nl->next == nil && (n = nl->n)->type != T)
+ if(n->type->etype == TSTRUCT && n->type->funarg) {
+ if(!hasddd(tstruct)) {
+ n1 = downcount(tstruct);
+ n2 = downcount(n->type);
+ if(n2 > n1)
+ goto toomany;
+ if(n2 < n1)
+ goto notenough;
+ }
+
+ tn = n->type->type;
+ for(tl=tstruct->type; tl; tl=tl->down) {
+ if(tl->isddd) {
+ for(; tn; tn=tn->down) {
+ 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;
+ 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;
+ }
+
+ n1 = downcount(tstruct);
+ n2 = count(nl);
+ if(!hasddd(tstruct)) {
+ if(n2 > n1)
+ goto toomany;
+ if(n2 < n1)
+ goto notenough;
+ }
+ else {
+ if(!isddd) {
+ if(n2 < n1-1)
+ goto notenough;
+ } else {
+ if(n2 > n1)
+ goto toomany;
+ if(n2 < n1)
+ goto notenough;
+ }
+ }
+
+ 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(n == N || !n->diag) {
+ if(call != N)
+ yyerror("not enough arguments in call to %N", call);
+ else
+ yyerror("not enough arguments to %O", op);
+ if(n != N)
+ n->diag = 1;
+ }
+ 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;
+}
+
+/*
+ * 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, *orign;
+ Node cmp;
+ char *s;
+
+ orign = n;
+ if(n->op == OCONVIFACE)
+ n = n->left;
+ evconst(n);
+ if(n->op != OLITERAL)
+ return; // we dont check variables
+
+ switch(n->val.ctype) {
+ default: // unknown, bool, nil
+ b = 23;
+ break;
+ case CTINT:
+ case CTRUNE:
+ 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;
+ b = 0;
+ if(a->op == OCONVIFACE && orign->op == OCONVIFACE) {
+ if(eqtype(a->left->type, n->type)) {
+ cmp.right = a->left;
+ evconst(&cmp);
+ b = cmp.val.u.bval;
+ }
+ } else if(eqtype(a->type, n->type)) {
+ cmp.right = a;
+ evconst(&cmp);
+ b = cmp.val.u.bval;
+ }
+ if(b) {
+ yyerror("duplicate key %N in map literal", n);
+ return;
+ }
+ }
+ orign->ntest = hash[h];
+ hash[h] = orign;
+}
+
+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 int
+iscomptype(Type *t)
+{
+ switch(t->etype) {
+ case TARRAY:
+ case TSTRUCT:
+ case TMAP:
+ return 1;
+ case TPTR32:
+ case TPTR64:
+ switch(t->type->etype) {
+ case TARRAY:
+ case TSTRUCT:
+ case TMAP:
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+static void
+pushtype(Node *n, Type *t)
+{
+ if(n == N || n->op != OCOMPLIT || !iscomptype(t))
+ return;
+
+ if(n->right == N) {
+ n->right = typenod(t);
+ n->implicit = 1; // don't print
+ n->right->implicit = 1; // * is okay
+ }
+ else if(debug['s']) {
+ typecheck(&n->right, Etype);
+ if(n->right->type != T && eqtype(n->right->type, t))
+ print("%lL: redundant type: %T\n", n->lineno, t);
+ }
+}
+
+static void
+typecheckcomplit(Node **np)
+{
+ int bad, i, nerr;
+ int64 len;
+ Node *l, *n, *norig, *r, **hash;
+ NodeList *ll;
+ Type *t, *f;
+ Sym *s, *s1;
+ 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;
+ }
+
+ // Save original node (including n->right)
+ norig = nod(n->op, N, N);
+ *norig = *n;
+
+ setlineno(n->right);
+ l = typecheck(&n->right /* sic */, Etype|Ecomplit);
+ if((t = l->type) == T)
+ goto error;
+ nerr = nerrors;
+ n->type = t;
+
+ if(isptr[t->etype]) {
+ // For better or worse, we don't allow pointers as the composite literal type,
+ // except when using the &T syntax, which sets implicit on the OIND.
+ if(!n->right->implicit) {
+ yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type);
+ goto error;
+ }
+ // Also, the underlying type must be a struct, map, slice, or array.
+ if(!iscomptype(t)) {
+ yyerror("invalid pointer type %T for composite literal", t);
+ goto error;
+ }
+ t = t->type;
+ }
+
+ 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 && !l->left->diag) {
+ yyerror("array index must be non-negative integer constant");
+ l->left->diag = 1;
+ 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 %lld out of bounds [0:%lld]", len-1, t->bound);
+ t->bound = -1; // no more errors
+ }
+ }
+
+ r = l->right;
+ pushtype(r, t->type);
+ typecheck(&r, Erv);
+ defaultlit(&r, t->type);
+ l->right = assignconv(r, 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");
+ if (l->left->op != OCONV)
+ keydup(l->left, hash, nhash);
+
+ r = l->right;
+ pushtype(r, t->type);
+ typecheck(&r, Erv);
+ defaultlit(&r, t->type);
+ l->right = assignconv(r, 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);
+ // No pushtype allowed here. Must name fields for that.
+ ll->n = assignconv(ll->n, f->type, "field value");
+ ll->n = nod(OKEY, newname(f->sym), ll->n);
+ ll->n->left->type = f;
+ 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 && exportname(s->name)) {
+ s1 = lookup(s->name);
+ if(s1->origpkg == s->pkg)
+ s = s1;
+ }
+ f = lookdot1(nil, s, t, t->type, 0);
+ if(f == nil) {
+ yyerror("unknown %T field '%S' in struct literal", t, s);
+ continue;
+ }
+ l->left = newname(s);
+ l->left->typecheck = 1;
+ l->left->type = f;
+ s = f->sym;
+ fielddup(newname(s), hash, nhash);
+ r = l->right;
+ // No pushtype allowed here. Tried and rejected.
+ typecheck(&r, Erv);
+ l->right = assignconv(r, f->type, "field value");
+ }
+ }
+ n->op = OSTRUCTLIT;
+ break;
+ }
+ if(nerr != nerrors)
+ goto error;
+
+ n->orig = norig;
+ if(isptr[n->type->etype]) {
+ n = nod(OPTRLIT, n, N);
+ n->typecheck = 1;
+ n->type = n->left->type;
+ n->left->type = t;
+ n->left->typecheck = 1;
+ }
+
+ n->orig = norig;
+ *np = n;
+ lineno = lno;
+ return;
+
+error:
+ n->type = T;
+ *np = n;
+ lineno = lno;
+}
+
+/*
+ * 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:
+ case OCLOSUREVAR:
+ 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;
+ }
+
+ // have already complained about n being undefined
+ if(n->op == ONONAME)
+ return;
+
+ yyerror("cannot assign to %N", n);
+}
+
+static void
+checkassignlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ checkassign(l->n);
+}
+
+// Check whether l and r are the same side effect-free expression,
+// so that it is safe to reuse one instead of computing both.
+static int
+samesafeexpr(Node *l, Node *r)
+{
+ if(l->op != r->op || !eqtype(l->type, r->type))
+ return 0;
+
+ switch(l->op) {
+ case ONAME:
+ case OCLOSUREVAR:
+ return l == r;
+
+ case ODOT:
+ case ODOTPTR:
+ return l->right != nil && r->right != nil && l->right->sym == r->right->sym && samesafeexpr(l->left, r->left);
+
+ case OIND:
+ return samesafeexpr(l->left, r->left);
+
+ case OINDEX:
+ return samesafeexpr(l->left, r->left) && samesafeexpr(l->right, r->right);
+ }
+
+ return 0;
+}
+
+/*
+ * 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");
+ }
+ 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);
+
+ // Recognize slices being updated in place, for better code generation later.
+ // Don't rewrite if using race detector, to avoid needing to teach race detector
+ // about this optimization.
+ if(n->left && n->left->op != OINDEXMAP && n->right && !flag_race) {
+ switch(n->right->op) {
+ case OSLICE:
+ case OSLICE3:
+ case OSLICESTR:
+ // For x = x[0:y], x can be updated in place, without touching pointer.
+ if(samesafeexpr(n->left, n->right->left) && (n->right->right->left == N || iszero(n->right->right->left)))
+ n->right->reslice = 1;
+ break;
+
+ case OAPPEND:
+ // For x = append(x, ...), x can be updated in place when there is capacity,
+ // without touching the pointer; otherwise the emitted code to growslice
+ // can take care of updating the pointer, and only in that case.
+ if(n->right->list != nil && samesafeexpr(n->left, n->right->list->n))
+ n->right->reslice = 1;
+ break;
+ }
+ }
+}
+
+static void
+checkassignto(Type *src, Node *dst)
+{
+ char *why;
+
+ if(assignop(src, dst->type, &why) == 0) {
+ yyerror("cannot assign %T to %lN in multiple assignment%s", src, dst, why);
+ return;
+ }
+}
+
+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;
+ yyerror("assignment count mismatch: %d = %d (use delete)", cl, cr);
+ 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(t->type != T && 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;
+ 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 && l->type->etype != TBOOL)
+ 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;
+
+ typecheck(&n->nname, Erv | Easgn);
+ if((t = n->nname->type) == T)
+ return;
+ n->type = t;
+ t->nname = n->nname;
+ rcvr = getthisx(t)->type;
+ if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
+ addmethod(n->shortname->sym, t, 1, n->nname->nointerface);
+}
+
+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 []rune
+ 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 int ntypecheckdeftype;
+static NodeList *methodqueue;
+
+static void
+domethod(Node *n)
+{
+ Node *nt;
+ Type *t;
+
+ 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;
+ }
+
+ // If we have
+ // type I interface {
+ // M(_ int)
+ // }
+ // then even though I.M looks like it doesn't care about the
+ // value of its argument, a specific implementation of I may
+ // care. The _ would suppress the assignment to that argument
+ // while generating a call, so remove it.
+ for(t=getinargx(nt->type)->type; t; t=t->down) {
+ if(t->sym != nil && strcmp(t->sym->name, "_") == 0)
+ t->sym = nil;
+ }
+
+ *n->type = *nt->type;
+ n->type->nod = N;
+ checkwidth(n->type);
+}
+
+static NodeList *mapqueue;
+
+void
+copytype(Node *n, Type *t)
+{
+ int maplineno, embedlineno, lno;
+ NodeList *l;
+
+ if(t->etype == TFORW) {
+ // This type isn't computed yet; when it is, update n.
+ t->copyto = list(t->copyto, n);
+ return;
+ }
+
+ maplineno = n->type->maplineno;
+ embedlineno = n->type->embedlineno;
+
+ l = n->type->copyto;
+ *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->xmethod = nil;
+ t->nod = N;
+ t->printed = 0;
+ t->deferwidth = 0;
+ t->copyto = nil;
+
+ // Update nodes waiting on this type.
+ for(; l; l=l->next)
+ copytype(l->n, t);
+
+ // Double-check use of type as embedded type.
+ lno = lineno;
+ if(embedlineno) {
+ lineno = embedlineno;
+ if(isptr[t->etype])
+ yyerror("embedded type cannot be a pointer");
+ }
+ lineno = lno;
+
+ // Queue check for map until all the types are done settling.
+ if(maplineno) {
+ t->maplineno = maplineno;
+ mapqueue = list(mapqueue, n);
+ }
+}
+
+static void
+typecheckdeftype(Node *n)
+{
+ int 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;
+ n->type = T;
+ goto ret;
+ }
+ if(n->type == T) {
+ n->diag = 1;
+ goto ret;
+ }
+
+ // copy new type and clear fields
+ // that don't come along.
+ // anything zeroed here must be zeroed in
+ // typedcl2 too.
+ copytype(n, t);
+
+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);
+ }
+ for(l=mapqueue; l; l=l->next) {
+ lineno = l->n->type->maplineno;
+ maptype(l->n->type, types[TBOOL]);
+ }
+ lineno = lno;
+ }
+ ntypecheckdeftype--;
+}
+
+void
+queuemethod(Node *n)
+{
+ if(ntypecheckdeftype == 0) {
+ domethod(n);
+ return;
+ }
+ methodqueue = list(methodqueue, n);
+}
+
+Node*
+typecheckdef(Node *n)
+{
+ int lno, nerrors0;
+ 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(isconst(e, CTNIL)) {
+ yyerror("const initializer cannot be nil");
+ goto ret;
+ }
+ if(e->type != T && e->op != OLITERAL || !isgoconst(e)) {
+ if(!e->diag) {
+ yyerror("const initializer %N is not a constant", e);
+ e->diag = 1;
+ }
+ 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 %lN 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;
+ nerrors0 = nerrors;
+ typecheckdeftype(n);
+ if(n->type->etype == TFORW && nerrors > nerrors0) {
+ // Something went wrong during type-checking,
+ // but it was reported. Silence future errors.
+ n->type->broke = 1;
+ }
+ if(curfn)
+ resumecheckwidth();
+ break;
+
+ case OPACK:
+ // nothing to see here
+ break;
+ }
+
+ret:
+ if(n->op != OLITERAL && n->type != T && isideal(n->type))
+ fatal("got %T for %N", n->type, n);
+ if(typecheckdefstack->n != n)
+ fatal("typecheckdefstack mismatch");
+ l = typecheckdefstack;
+ typecheckdefstack = l->next;
+
+ lineno = lno;
+ n->walkdef = 1;
+ return n;
+}
+
+static int
+checkmake(Type *t, char *arg, Node *n)
+{
+ if(n->op == OLITERAL) {
+ switch(n->val.ctype) {
+ case CTINT:
+ case CTRUNE:
+ case CTFLT:
+ case CTCPLX:
+ n->val = toint(n->val);
+ if(mpcmpfixc(n->val.u.xval, 0) < 0) {
+ yyerror("negative %s argument in make(%T)", arg, t);
+ return -1;
+ }
+ if(mpcmpfixfix(n->val.u.xval, maxintval[TINT]) > 0) {
+ yyerror("%s argument too large in make(%T)", arg, t);
+ return -1;
+ }
+
+ // Delay defaultlit until after we've checked range, to avoid
+ // a redundant "constant NNN overflows int" error.
+ defaultlit(&n, types[TINT]);
+ return 0;
+ default:
+ break;
+ }
+ }
+
+ if(!isint[n->type->etype] && n->type->etype != TIDEAL) {
+ yyerror("non-integer %s argument in make(%T) - %T", arg, t, n->type);
+ return -1;
+ }
+
+ // Defaultlit still necessary for non-constant: n might be 1<<k.
+ defaultlit(&n, types[TINT]);
+
+ return 0;
+}
+
+static void markbreaklist(NodeList*, Node*);
+
+static void
+markbreak(Node *n, Node *implicit)
+{
+ Label *lab;
+
+ if(n == N)
+ return;
+
+ switch(n->op) {
+ case OBREAK:
+ if(n->left == N) {
+ if(implicit)
+ implicit->hasbreak = 1;
+ } else {
+ lab = n->left->sym->label;
+ if(lab != L)
+ lab->def->hasbreak = 1;
+ }
+ break;
+
+ case OFOR:
+ case OSWITCH:
+ case OTYPESW:
+ case OSELECT:
+ case ORANGE:
+ implicit = n;
+ // fall through
+
+ default:
+ markbreak(n->left, implicit);
+ markbreak(n->right, implicit);
+ markbreak(n->ntest, implicit);
+ markbreak(n->nincr, implicit);
+ markbreaklist(n->ninit, implicit);
+ markbreaklist(n->nbody, implicit);
+ markbreaklist(n->nelse, implicit);
+ markbreaklist(n->list, implicit);
+ markbreaklist(n->rlist, implicit);
+ break;
+ }
+}
+
+static void
+markbreaklist(NodeList *l, Node *implicit)
+{
+ Node *n;
+ Label *lab;
+
+ for(; l; l=l->next) {
+ n = l->n;
+ if(n->op == OLABEL && l->next && n->defn == l->next->n) {
+ switch(n->defn->op) {
+ case OFOR:
+ case OSWITCH:
+ case OTYPESW:
+ case OSELECT:
+ case ORANGE:
+ lab = mal(sizeof *lab);
+ lab->def = n->defn;
+ n->left->sym->label = lab;
+ markbreak(n->defn, n->defn);
+ n->left->sym->label = L;
+ l = l->next;
+ continue;
+ }
+ }
+ markbreak(n, implicit);
+ }
+}
+
+static int
+isterminating(NodeList *l, int top)
+{
+ int def;
+ Node *n;
+
+ if(l == nil)
+ return 0;
+ if(top) {
+ while(l->next && l->n->op != OLABEL)
+ l = l->next;
+ markbreaklist(l, nil);
+ }
+ while(l->next)
+ l = l->next;
+ n = l->n;
+
+ if(n == N)
+ return 0;
+
+ switch(n->op) {
+ // NOTE: OLABEL is treated as a separate statement,
+ // not a separate prefix, so skipping to the last statement
+ // in the block handles the labeled statement case by
+ // skipping over the label. No case OLABEL here.
+
+ case OBLOCK:
+ return isterminating(n->list, 0);
+
+ case OGOTO:
+ case ORETURN:
+ case ORETJMP:
+ case OPANIC:
+ case OXFALL:
+ return 1;
+
+ case OFOR:
+ if(n->ntest != N)
+ return 0;
+ if(n->hasbreak)
+ return 0;
+ return 1;
+
+ case OIF:
+ return isterminating(n->nbody, 0) && isterminating(n->nelse, 0);
+
+ case OSWITCH:
+ case OTYPESW:
+ case OSELECT:
+ if(n->hasbreak)
+ return 0;
+ def = 0;
+ for(l=n->list; l; l=l->next) {
+ if(!isterminating(l->n->nbody, 0))
+ return 0;
+ if(l->n->list == nil) // default
+ def = 1;
+ }
+ if(n->op != OSELECT && !def)
+ return 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+checkreturn(Node *fn)
+{
+ if(fn->type->outtuple && fn->nbody != nil)
+ if(!isterminating(fn->nbody, 1))
+ yyerrorl(fn->endlineno, "missing return at end of function");
+}
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
new file mode 100644
index 0000000..ff08c0e
--- /dev/null
+++ b/src/cmd/gc/unsafe.c
@@ -0,0 +1,148 @@
+// 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 "go.h"
+
+/*
+ * look for
+ * unsafe.Sizeof
+ * unsafe.Offsetof
+ * unsafe.Alignof
+ * rewrite with a constant
+ */
+Node*
+unsafenmagic(Node *nn)
+{
+ Node *r, *n, *base, *r1;
+ Sym *s;
+ Type *t, *tr;
+ vlong 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) {
+ // must be a selector.
+ if(r->op != OXDOT)
+ goto bad;
+ // Remember base of selector to find it back after dot insertion.
+ // Since r->left may be mutated by typechecking, check it explicitly
+ // first to track it correctly.
+ typecheck(&r->left, Erv);
+ base = r->left;
+ typecheck(&r, Erv);
+ switch(r->op) {
+ case ODOT:
+ case ODOTPTR:
+ break;
+ case OCALLPART:
+ yyerror("invalid expression %N: argument is a method value", nn);
+ v = 0;
+ goto ret;
+ default:
+ goto bad;
+ }
+ v = 0;
+ // add offsets for inserted dots.
+ for(r1=r; r1->left!=base; r1=r1->left) {
+ switch(r1->op) {
+ case ODOT:
+ v += r1->xoffset;
+ break;
+ case ODOTPTR:
+ yyerror("invalid expression %N: selector implies indirection of embedded %N", nn, r1->left);
+ goto ret;
+ default:
+ dump("unsafenmagic", r);
+ fatal("impossible %#O node after dot insertion", r1->op);
+ goto bad;
+ }
+ }
+ v += r1->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->orig = nn;
+ n->val = val;
+ n->type = types[TUINTPTR];
+ nn->type = types[TUINTPTR];
+ return n;
+}
+
+int
+isunsafebuiltin(Node *n)
+{
+ if(n == N || n->op != ONAME || n->sym == S || n->sym->pkg != unsafepkg)
+ return 0;
+ if(strcmp(n->sym->name, "Sizeof") == 0)
+ return 1;
+ if(strcmp(n->sym->name, "Offsetof") == 0)
+ return 1;
+ if(strcmp(n->sym->name, "Alignof") == 0)
+ return 1;
+ return 0;
+}
diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go
new file mode 100644
index 0000000..c3c6278
--- /dev/null
+++ b/src/cmd/gc/unsafe.go
@@ -0,0 +1,18 @@
+// 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.
+
+// +build ignore
+
+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
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
new file mode 100644
index 0000000..ff9b362
--- /dev/null
+++ b/src/cmd/gc/walk.c
@@ -0,0 +1,3937 @@
+// 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 "go.h"
+#include "../ld/textflag.h"
+
+static Node* walkprint(Node*, NodeList**);
+static Node* writebarrierfn(char*, Type*, Type*);
+static Node* applywritebarrier(Node*, NodeList**);
+static Node* mapfn(char*, Type*);
+static Node* mapfndel(char*, Type*);
+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, Node*, 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**);
+static Node* copyany(Node*, NodeList**, int);
+static Node* sliceany(Node*, NodeList**);
+static void walkcompare(Node**, NodeList**);
+static void walkrotate(Node**);
+static void walkmul(Node**, NodeList**);
+static void walkdiv(Node**, NodeList**);
+static int bounded(Node*, int64);
+static Mpint mpzero;
+static void walkprintfunc(Node**, NodeList**);
+
+void
+walk(Node *fn)
+{
+ char s[50];
+ NodeList *l;
+ int lno;
+
+ curfn = fn;
+
+ if(debug['W']) {
+ snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
+ dumplist(s, curfn->nbody);
+ }
+
+ lno = lineno;
+
+ // Final typecheck for any unused variables.
+ // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
+ for(l=fn->dcl; l; l=l->next)
+ if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO)
+ typecheck(&l->n, Erv | Easgn);
+
+ // Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
+ for(l=fn->dcl; l; l=l->next)
+ if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used)
+ l->n->defn->left->used++;
+
+ for(l=fn->dcl; l; l=l->next) {
+ if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used)
+ continue;
+ if(l->n->defn && l->n->defn->op == OTYPESW) {
+ if(l->n->defn->left->used)
+ continue;
+ lineno = l->n->defn->left->lineno;
+ yyerror("%S declared and not used", l->n->sym);
+ l->n->defn->left->used = 1; // suppress repeats
+ } else {
+ lineno = l->n->lineno;
+ yyerror("%S declared and not used", l->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:
+ case PPARAMOUT|PHEAP:
+ return l->n->addrtaken;
+ 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;
+ if(n->dodata == 2) // don't walk, generated by anylit.
+ return;
+
+ setlineno(n);
+
+ walkstmtlist(n->ninit);
+
+ 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 OAS:
+ case OASOP:
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2RECV:
+ case OAS2FUNC:
+ case OAS2MAPR:
+ case OCLOSE:
+ case OCOPY:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALL:
+ case OCALLFUNC:
+ case ODELETE:
+ case OSEND:
+ case OPRINT:
+ case OPRINTN:
+ case OPANIC:
+ case OEMPTY:
+ case ORECOVER:
+ if(n->typecheck == 0)
+ fatal("missing typecheck: %+N", n);
+ init = n->ninit;
+ n->ninit = nil;
+ walkexpr(&n, &init);
+ addinit(&n, init);
+ if((*np)->op == OCOPY && n->op == OCONVNOP)
+ n->op = OEMPTY; // don't leave plain values as statements.
+ break;
+
+ case ORECV:
+ // special case for a receive where we throw away
+ // the value received.
+ if(n->typecheck == 0)
+ fatal("missing typecheck: %+N", n);
+ init = n->ninit;
+ n->ninit = nil;
+
+ walkexpr(&n->left, &init);
+ n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, &init, typename(n->left->type), n->left, nodnil());
+ walkexpr(&n, &init);
+
+ addinit(&n, init);
+ break;
+
+ case OBREAK:
+ case ODCL:
+ case OCONTINUE:
+ case OFALL:
+ case OGOTO:
+ case OLABEL:
+ case ODCLCONST:
+ case ODCLTYPE:
+ case OCHECKNIL:
+ case OVARKILL:
+ 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:
+ walkprintfunc(&n->left, &n->ninit);
+ break;
+ case OCOPY:
+ n->left = copyany(n->left, &n->ninit, 1);
+ break;
+ default:
+ walkexpr(&n->left, &n->ninit);
+ break;
+ }
+ break;
+
+ case OFOR:
+ if(n->ntest != N) {
+ walkstmtlist(n->ntest->ninit);
+ init = n->ntest->ninit;
+ n->ntest->ninit = nil;
+ walkexpr(&n->ntest, &init);
+ addinit(&n->ntest, init);
+ }
+ walkstmt(&n->nincr);
+ walkstmtlist(n->nbody);
+ break;
+
+ case OIF:
+ walkexpr(&n->ntest, &n->ninit);
+ walkstmtlist(n->nbody);
+ walkstmtlist(n->nelse);
+ break;
+
+ case OPROC:
+ switch(n->left->op) {
+ case OPRINT:
+ case OPRINTN:
+ walkprintfunc(&n->left, &n->ninit);
+ break;
+ case OCOPY:
+ n->left = copyany(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;
+ }
+
+ // move function calls out, to make reorder3's job easier.
+ walkexprlistsafe(n->list, &n->ninit);
+ ll = ascompatee(n->op, rl, n->list, &n->ninit);
+ n->list = reorder3(ll);
+ break;
+ }
+ ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+ n->list = ll;
+ break;
+
+ case ORETJMP:
+ 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;
+ }
+
+ if(n->op == ONAME)
+ fatal("walkstmt ended up with name: %+N", n);
+
+ *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;
+ Node *map, *key;
+ NodeList *ll, *lr;
+ Type *t;
+ int et, old_safemode;
+ int64 v;
+ int32 lno;
+ Node *n, *fn, *n1, *n2;
+ Sym *sym;
+ 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");
+ }
+
+ if(n->ninit != nil) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
+ // 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)
+ fatal("missed typecheck: %+N\n", n);
+
+ switch(n->op) {
+ default:
+ dump("walk", n);
+ fatal("walkexpr: switch 1 unknown op %+hN", n);
+ break;
+
+ case OTYPE:
+ case ONONAME:
+ case OINDREG:
+ case OEMPTY:
+ goto ret;
+
+ case ONOT:
+ case OMINUS:
+ case OPLUS:
+ case OCOM:
+ case OREAL:
+ case OIMAG:
+ case ODOTMETH:
+ case ODOTINTER:
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OIND:
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case ODOT:
+ usefield(n);
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case ODOTPTR:
+ usefield(n);
+ if(n->op == ODOTPTR && n->left->type->type->width == 0) {
+ // No actual copy will be generated, so emit an explicit nil check.
+ n->left = cheapexpr(n->left, init);
+ checknil(n->left, init);
+ }
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OEFACE:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ goto ret;
+
+ case OSPTR:
+ case OITAB:
+ 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:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ t = n->left->type;
+ n->bounded = bounded(n->right, 8*t->width);
+ if(debug['m'] && n->etype && !isconst(n->right, CTINT))
+ warn("shift bounds check elided");
+ goto ret;
+
+ case OAND:
+ case OSUB:
+ case OHMUL:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case OADD:
+ case OCOMPLEX:
+ case OLROT:
+ // Use results from call expression as arguments for complex.
+ if(n->op == OCOMPLEX && n->left == N && n->right == N) {
+ n->left = n->list->n;
+ n->right = n->list->next->n;
+ }
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ goto ret;
+
+ case OOR:
+ case OXOR:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ walkrotate(&n);
+ goto ret;
+
+ case OEQ:
+ case ONE:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ // Disable safemode while compiling this code: the code we
+ // generate internally can refer to unsafe.Pointer.
+ // In this case it can happen if we need to generate an ==
+ // for a struct containing a reflect.Value, which itself has
+ // an unexported field of type unsafe.Pointer.
+ old_safemode = safemode;
+ safemode = 0;
+ walkcompare(&n, init);
+ safemode = old_safemode;
+ 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);
+ addinit(&n->right, ll);
+ goto ret;
+
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->list, init);
+ n = walkprint(n, init);
+ goto ret;
+
+ case OPANIC:
+ n = mkcall("gopanic", T, init, n->left);
+ goto ret;
+
+ case ORECOVER:
+ n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N));
+ goto ret;
+
+ case OLITERAL:
+ n->addable = 1;
+ goto ret;
+
+ case OCLOSUREVAR:
+ case OCFUNC:
+ 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, 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;
+
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+
+ ll = ascompatte(n->op, n, 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, n, 0, getthis(t), list1(n->left->left), 0, init);
+ lr = ascompatte(n->op, n, 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;
+
+ if(n->right == N || iszero(n->right) && !flag_race)
+ goto ret;
+
+ switch(n->right->op) {
+ default:
+ walkexpr(&n->right, init);
+ break;
+
+ case ORECV:
+ // x = <-c; n->left is x, n->right->left is c.
+ // orderstmt made sure x is addressable.
+ walkexpr(&n->right->left, init);
+ n1 = nod(OADDR, n->left, N);
+ r = n->right->left; // the channel
+ n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1);
+ walkexpr(&n, init);
+ goto ret;
+ }
+
+ if(n->left != N && n->right != N) {
+ r = convas(nod(OAS, n->left, n->right), init);
+ r->dodata = n->dodata;
+ n = r;
+ n = applywritebarrier(n, init);
+ }
+
+ 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);
+ for(lr = ll; lr != nil; lr = lr->next)
+ lr->n = applywritebarrier(lr->n, init);
+ n = liststmt(ll);
+ goto ret;
+
+ case OAS2FUNC:
+ // a,b,... = fn()
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r, init);
+
+ ll = ascompatet(n->op, n->list, &r->type, 0, init);
+ for(lr = ll; lr != nil; lr = lr->next)
+ lr->n = applywritebarrier(lr->n, init);
+ n = liststmt(concat(list1(r), ll));
+ goto ret;
+
+ case OAS2RECV:
+ // x, y = <-c
+ // orderstmt made sure x is addressable.
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r->left, init);
+ if(isblank(n->list->n))
+ n1 = nodnil();
+ else
+ n1 = nod(OADDR, n->list->n, N);
+ n1->etype = 1; // addr does not escape
+ fn = chanfn("chanrecv2", 2, r->left->type);
+ r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1);
+ n = nod(OAS, n->list->next->n, r);
+ typecheck(&n, Etop);
+ goto ret;
+
+ 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);
+ walkexpr(&r->right, init);
+ t = r->left->type;
+ p = nil;
+ if(t->type->width <= 128) { // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing.
+ switch(simsimtype(t->down)) {
+ case TINT32:
+ case TUINT32:
+ p = "mapaccess2_fast32";
+ break;
+ case TINT64:
+ case TUINT64:
+ p = "mapaccess2_fast64";
+ break;
+ case TSTRING:
+ p = "mapaccess2_faststr";
+ break;
+ }
+ }
+ if(p != nil) {
+ // fast versions take key by value
+ key = r->right;
+ } else {
+ // standard version takes key by reference
+ // orderexpr made sure key is addressable.
+ key = nod(OADDR, r->right, N);
+ p = "mapaccess2";
+ }
+
+ // from:
+ // a,b = m[i]
+ // to:
+ // var,b = mapaccess2*(t, m, i)
+ // a = *var
+ a = n->list->n;
+ var = temp(ptrto(t->type));
+ var->typecheck = 1;
+ fn = mapfn(p, t);
+ r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key);
+
+ // mapaccess2* returns a typed bool, but due to spec changes,
+ // the boolean result of i.(T) is now untyped so we make it the
+ // same type as the variable on the lhs.
+ if(!isblank(n->list->next->n))
+ r->type->type->down->type = n->list->next->n->type;
+ n->rlist = list1(r);
+ n->op = OAS2FUNC;
+ n->list->n = var;
+ walkexpr(&n, init);
+ *init = list(*init, n);
+ n = nod(OAS, a, nod(OIND, var, N));
+ typecheck(&n, Etop);
+ walkexpr(&n, init);
+ // mapaccess needs a zero value to be at least this big.
+ if(zerosize < t->type->width)
+ zerosize = t->type->width;
+ // TODO: ptr is always non-nil, so disable nil check for this OIND op.
+ goto ret;
+
+ case ODELETE:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ map = n->list->n;
+ key = n->list->next->n;
+ walkexpr(&map, init);
+ walkexpr(&key, init);
+ // orderstmt made sure key is addressable.
+ key = nod(OADDR, key, N);
+ t = map->type;
+ n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key);
+ goto ret;
+
+ case OAS2DOTTYPE:
+ // a,b = i.(T)
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ if(isblank(n->list->n) && !isinter(r->type)) {
+ strcpy(buf, "assert");
+ p = buf+strlen(buf);
+ if(isnilinter(r->left->type))
+ *p++ = 'E';
+ else
+ *p++ = 'I';
+ *p++ = '2';
+ *p++ = 'T';
+ *p++ = 'O';
+ *p++ = 'K';
+ *p = '\0';
+
+ fn = syslook(buf, 1);
+
+ // runtime.assert(E|I)2TOK returns a typed bool, but due
+ // to spec changes, the boolean result of i.(T) is now untyped
+ // so we make it the same type as the variable on the lhs.
+ if(!isblank(n->list->next->n))
+ fn->type->type->down->type->type = n->list->next->n->type;
+ ll = list1(typename(r->type));
+ ll = list(ll, r->left);
+ argtype(fn, r->left->type);
+ n1 = nod(OCALL, fn, N);
+ n1->list = ll;
+ n = nod(OAS, n->list->next->n, n1);
+ typecheck(&n, Etop);
+ walkexpr(&n, init);
+ goto ret;
+ }
+
+ 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:
+ walkexpr(&n->left, init);
+
+ // Optimize convT2E as a two-word copy when T is uintptr-shaped.
+ if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
+ l = nod(OEFACE, typename(n->left->type), n->left);
+ l->type = n->type;
+ l->typecheck = n->typecheck;
+ n = l;
+ goto ret;
+ }
+
+ // Build name of function: convI2E etc.
+ // Not all names are possible
+ // (e.g., we'll never generate convE2E or convE2I).
+ 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));
+ if(!isinter(n->left->type) && !isnilinter(n->type)){
+ sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg);
+ if(sym->def == N) {
+ l = nod(ONAME, N, N);
+ l->sym = sym;
+ l->type = ptrto(types[TUINT8]);
+ l->addable = 1;
+ l->class = PEXTERN;
+ l->xoffset = 0;
+ sym->def = l;
+ ggloblsym(sym, widthptr, DUPOK|NOPTR);
+ }
+ l = nod(OADDR, sym->def, N);
+ l->addable = 1;
+ ll = list(ll, l);
+
+ if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
+ /* For pointer types, we can make a special form of optimization
+ *
+ * These statements are put onto the expression init list:
+ * Itab *tab = atomicloadtype(&cache);
+ * if(tab == nil)
+ * tab = typ2Itab(type, itype, &cache);
+ *
+ * The CONVIFACE expression is replaced with this:
+ * OEFACE{tab, ptr};
+ */
+ l = temp(ptrto(types[TUINT8]));
+
+ n1 = nod(OAS, l, sym->def);
+ typecheck(&n1, Etop);
+ *init = list(*init, n1);
+
+ fn = syslook("typ2Itab", 1);
+ n1 = nod(OCALL, fn, N);
+ n1->list = ll;
+ typecheck(&n1, Erv);
+ walkexpr(&n1, init);
+
+ n2 = nod(OIF, N, N);
+ n2->ntest = nod(OEQ, l, nodnil());
+ n2->nbody = list1(nod(OAS, l, n1));
+ n2->likely = -1;
+ typecheck(&n2, Etop);
+ *init = list(*init, n2);
+
+ l = nod(OEFACE, l, n->left);
+ l->typecheck = n->typecheck;
+ l->type = n->type;
+ n = l;
+ goto ret;
+ }
+ }
+ if(isinter(n->left->type)) {
+ ll = list(ll, n->left);
+ } else {
+ // regular types are passed by reference to avoid C vararg calls
+ // orderexpr arranged for n->left to be a temporary for all
+ // the conversions it could see. comparison of an interface
+ // with a non-interface, especially in a switch on interface value
+ // with non-interface cases, is not visible to orderstmt, so we
+ // have to fall back on allocating a temp here.
+ if(islvalue(n->left))
+ ll = list(ll, nod(OADDR, n->left, N));
+ else
+ ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N));
+ }
+ 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 OANDNOT:
+ walkexpr(&n->left, init);
+ n->op = OAND;
+ n->right = nod(OCOM, n->right, N);
+ typecheck(&n->right, Erv);
+ walkexpr(&n->right, init);
+ goto ret;
+
+ case OMUL:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ walkmul(&n, init);
+ 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;
+ }
+ // Nothing to do for float divisions.
+ if(isfloat[et])
+ goto ret;
+
+ // Try rewriting as shifts or magic multiplies.
+ walkdiv(&n, init);
+
+ /*
+ * rewrite 64-bit div and mod into function calls
+ * on 32-bit architectures.
+ */
+ switch(n->op) {
+ case OMOD:
+ case ODIV:
+ if(widthreg >= 8 || (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]));
+ break;
+ default:
+ break;
+ }
+ goto ret;
+
+ case OINDEX:
+ walkexpr(&n->left, init);
+ // save the original node for bounds checking elision.
+ // If it was a ODIV/OMOD walk might rewrite it.
+ r = n->right;
+ walkexpr(&n->right, init);
+
+ // if range of type cannot exceed static array bound,
+ // disable bounds check.
+ if(n->bounded)
+ goto ret;
+ t = n->left->type;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t)) {
+ n->bounded = bounded(r, t->bound);
+ if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
+ warn("index bounds check elided");
+ if(smallintconst(n->right) && !n->bounded)
+ yyerror("index out of bounds");
+ } else if(isconst(n->left, CTSTR)) {
+ n->bounded = bounded(r, n->left->val.u.sval->len);
+ if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
+ warn("index bounds check elided");
+ if(smallintconst(n->right)) {
+ if(!n->bounded)
+ yyerror("index out of bounds");
+ else {
+ // replace "abc"[1] with 'b'.
+ // delayed until now because "abc"[1] is not
+ // an ideal constant.
+ v = mpgetfix(n->right->val.u.xval);
+ nodconst(n, n->type, n->left->val.u.sval->s[v]);
+ n->typecheck = 1;
+ }
+ }
+ }
+
+ if(isconst(n->right, CTINT))
+ if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 ||
+ mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
+ yyerror("index out of bounds");
+ goto ret;
+
+ case OINDEXMAP:
+ if(n->etype == 1)
+ goto ret;
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+
+ t = n->left->type;
+ p = nil;
+ if(t->type->width <= 128) { // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing.
+ switch(simsimtype(t->down)) {
+ case TINT32:
+ case TUINT32:
+ p = "mapaccess1_fast32";
+ break;
+ case TINT64:
+ case TUINT64:
+ p = "mapaccess1_fast64";
+ break;
+ case TSTRING:
+ p = "mapaccess1_faststr";
+ break;
+ }
+ }
+ if(p != nil) {
+ // fast versions take key by value
+ key = n->right;
+ } else {
+ // standard version takes key by reference.
+ // orderexpr made sure key is addressable.
+ key = nod(OADDR, n->right, N);
+ p = "mapaccess1";
+ }
+ n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key);
+ n = nod(OIND, n, N);
+ n->type = t->type;
+ n->typecheck = 1;
+ // mapaccess needs a zero value to be at least this big.
+ if(zerosize < t->type->width)
+ zerosize = t->type->width;
+ goto ret;
+
+ case ORECV:
+ fatal("walkexpr ORECV"); // should see inside OAS only
+
+ case OSLICE:
+ if(n->right != N && n->right->left == N && n->right->right == N) { // noop
+ walkexpr(&n->left, init);
+ n = n->left;
+ goto ret;
+ }
+ // fallthrough
+ case OSLICEARR:
+ case OSLICESTR:
+ if(n->right == N) // already processed
+ goto ret;
+
+ walkexpr(&n->left, init);
+ // cgen_slice can't handle string literals as source
+ // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
+ if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX))
+ n->left = copyexpr(n->left, n->left->type, init);
+ else
+ 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);
+ n = sliceany(n, init); // chops n->right, sets n->list
+ goto ret;
+
+ case OSLICE3:
+ case OSLICE3ARR:
+ if(n->right == N) // already processed
+ goto ret;
+
+ walkexpr(&n->left, init);
+ // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi]
+ // TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
+ if(n->left->op == OINDEX)
+ n->left = copyexpr(n->left, n->left->type, init);
+ else
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->right->left, init);
+ n->right->left = safeexpr(n->right->left, init);
+ walkexpr(&n->right->right->left, init);
+ n->right->right->left = safeexpr(n->right->right->left, init);
+ walkexpr(&n->right->right->right, init);
+ n->right->right->right = safeexpr(n->right->right->right, init);
+ n = sliceany(n, init); // chops n->right, sets n->list
+ goto ret;
+
+ case OADDR:
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case ONEW:
+ if(n->esc == EscNone && n->type->type->width < (1<<16)) {
+ r = temp(n->type->type);
+ r = nod(OAS, r, N); // zero temp
+ typecheck(&r, Etop);
+ *init = list(*init, r);
+ r = nod(OADDR, r->left, N);
+ typecheck(&r, Erv);
+ n = r;
+ } else {
+ 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);
+ r->type = n->type;
+ n = r;
+ goto ret;
+ }
+
+ // s + "badgerbadgerbadger" == "badgerbadgerbadger"
+ if((n->etype == OEQ || n->etype == ONE) &&
+ isconst(n->right, CTSTR) &&
+ n->left->op == OADDSTR && count(n->left->list) == 2 &&
+ isconst(n->left->list->next->n, CTSTR) &&
+ cmpslit(n->right, n->left->list->next->n) == 0) {
+ r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0));
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ r->type = n->type;
+ n = r;
+ goto ret;
+ }
+
+ if(n->etype == OEQ || n->etype == ONE) {
+ // prepare for rewrite below
+ n->left = cheapexpr(n->left, init);
+ n->right = cheapexpr(n->right, init);
+
+ r = mkcall("eqstring", types[TBOOL], init,
+ conv(n->left, types[TSTRING]),
+ conv(n->right, types[TSTRING]));
+
+ // quick check of len before full compare for == or !=
+ if(n->etype == OEQ) {
+ // len(left) == len(right) && eqstring(left, right)
+ r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ } else {
+ // len(left) != len(right) || !eqstring(left, right)
+ r = nod(ONOT, r, N);
+ r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ }
+ typecheck(&r, Erv);
+ walkexpr(&r, nil);
+ } else {
+ // 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));
+ }
+
+ typecheck(&r, Erv);
+ if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
+ r->type = n->type;
+ n = r;
+ goto ret;
+
+ case OADDSTR:
+ n = addstr(n, init);
+ goto ret;
+
+ case OAPPEND:
+ if(n->isddd)
+ n = appendslice(n, init); // also works for append(slice, string).
+ else
+ n = append(n, init);
+ goto ret;
+
+ case OCOPY:
+ n = copyany(n, init, flag_race);
+ 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:
+ l = n->left;
+ r = n->right;
+ if(r == nil)
+ l = r = safeexpr(l, init);
+ t = n->type;
+ if(n->esc == EscNone
+ && smallintconst(l) && smallintconst(r)
+ && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) {
+ // var arr [r]T
+ // n = arr[:l]
+ t = aindex(r, t->type); // [r]T
+ var = temp(t);
+ a = nod(OAS, var, N); // zero temp
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+ r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l]
+ r = conv(r, n->type); // in case n->type is named.
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ } else {
+ // makeslice(t *Type, nel int64, max int64) (ary []any)
+ 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 OARRAYBYTESTRTMP:
+ // slicebytetostringtmp([]byte) string;
+ n = mkcall("slicebytetostringtmp", n->type, init, n->left);
+ goto ret;
+
+ case OARRAYRUNESTR:
+ // slicerunetostring([]rune) string;
+ n = mkcall("slicerunetostring", 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:
+ // stringtoslicerune(string) []rune
+ n = mkcall("stringtoslicerune", 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);
+
+ n->right = cheapexpr(n->right, init);
+ n->left = cheapexpr(n->left, init);
+ 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);
+
+ // check itable/type before full compare.
+ if(n->etype == OEQ)
+ r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+ else
+ r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ r->type = n->type;
+ n = r;
+ goto ret;
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ case OPTRLIT:
+ var = temp(n->type);
+ anylit(0, n, var, init);
+ n = var;
+ goto ret;
+
+ case OSEND:
+ n1 = n->right;
+ n1 = assignconv(n1, n->left->type->type, "chan send");
+ walkexpr(&n1, init);
+ n1 = nod(OADDR, n1, N);
+ n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1);
+ goto ret;
+
+ case OCLOSURE:
+ n = walkclosure(n, init);
+ goto ret;
+
+ case OCALLPART:
+ n = walkpartialcall(n, init);
+ goto ret;
+ }
+ fatal("missing switch %O", n->op);
+
+ret:
+ // Expressions that are constant at run time but not
+ // considered const by the language spec are not turned into
+ // constants until walk. For example, if n is y%1 == 0, the
+ // walk of y%1 may have replaced it by 0.
+ // Check whether n with its updated args is itself now a constant.
+ t = n->type;
+ evconst(n);
+ n->type = t;
+ if(n->op == OLITERAL)
+ typecheck(&n, Erv);
+
+ ullmancalc(n);
+
+ if(debug['w'] && n != N)
+ dump("walk", n);
+
+ lineno = lno;
+ *np = n;
+}
+
+static Node*
+ascompatee1(int op, Node *l, Node *r, NodeList **init)
+{
+ Node *n;
+ USED(op);
+
+ // convas will turn map assigns into function calls,
+ // making it impossible for reorder3 to work.
+ n = nod(OAS, l, r);
+ if(l->op == OINDEXMAP)
+ return n;
+
+ return convas(n, 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) {
+ // Do not generate 'x = x' during return. See issue 4014.
+ if(op == ORETURN && ll->n == lr->n)
+ continue;
+ 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 %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name);
+ 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)
+{
+ Node r;
+
+ if(l->ullman >= UINF || l->op == OINDEXMAP)
+ return 1;
+ memset(&r, 0, sizeof r);
+ if(needwritebarrier(l, &r))
+ 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;
+
+ USED(op);
+
+ /*
+ * 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 = temp(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) {
+ dump("ascompatet ucount", a);
+ ucount++;
+ }
+ nn = list(nn, a);
+ r = structnext(&saver);
+ }
+
+ if(ll != nil || r != T)
+ yyerror("ascompatet: assignment count mismatch: %d = %d",
+ count(nl), structcount(*nr));
+
+ if(ucount)
+ fatal("ascompatet: 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 *ddd)
+{
+ Node *a, *n;
+ Type *tslice;
+ int esc;
+
+ esc = EscUnknown;
+ if(ddd != nil)
+ esc = ddd->esc;
+
+ tslice = typ(TARRAY);
+ tslice->type = l->type->type;
+ tslice->bound = -1;
+
+ if(count(lr0) == 0) {
+ n = nodnil();
+ n->type = tslice;
+ } else {
+ n = nod(OCOMPLIT, N, typenod(tslice));
+ if(ddd != nil)
+ n->alloc = ddd->alloc; // temporary to use
+ n->list = lr0;
+ n->esc = esc;
+ 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");
+ 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, Node *call, 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);
+ r = nod(OCONVNOP, r, N);
+ r->type = a->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 = temp(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, call->right);
+ 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)
+{
+ Node *r;
+ Node *n;
+ NodeList *l, *all;
+ Node *on;
+ Type *t;
+ int notfirst, et, op;
+ NodeList *calls;
+
+ on = nil;
+ op = nn->op;
+ all = nn->list;
+ calls = nil;
+ notfirst = 0;
+
+ for(l=all; l; l=l->next) {
+ if(notfirst) {
+ calls = list(calls, mkcall("printsp", T, init));
+ }
+ notfirst = op == OPRINTN;
+
+ n = l->n;
+ if(n->op == OLITERAL) {
+ switch(n->val.ctype) {
+ case CTRUNE:
+ defaultlit(&n, runetype);
+ break;
+ 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(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) {
+ on = syslook("printpointer", 1);
+ argtype(on, n->type); // any-1
+ } else if(isslice(n->type)) {
+ on = syslook("printslice", 1);
+ argtype(on, n->type); // any-1
+ } else if(isint[et]) {
+ if(et == TUINT64) {
+ if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
+ on = syslook("printhex", 0);
+ else
+ on = syslook("printuint", 0);
+ } else
+ on = syslook("printint", 0);
+ } else if(isfloat[et]) {
+ on = syslook("printfloat", 0);
+ } else if(iscomplex[et]) {
+ on = syslook("printcomplex", 0);
+ } else if(et == TBOOL) {
+ on = syslook("printbool", 0);
+ } else if(et == TSTRING) {
+ on = syslook("printstring", 0);
+ } else {
+ badtype(OPRINT, n->type, T);
+ continue;
+ }
+
+ 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;
+ }
+
+ r = nod(OCALL, on, N);
+ r->list = list1(n);
+ calls = list(calls, r);
+ }
+
+ 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("newobject", 1);
+ argtype(fn, t);
+ return mkcall1(fn, ptrto(t), nil, typename(t));
+}
+
+static int
+isstack(Node *n)
+{
+ while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type))
+ n = n->left;
+
+ switch(n->op) {
+ case OINDREG:
+ // OINDREG only ends up in walk if it's indirect of SP.
+ return 1;
+
+ case ONAME:
+ switch(n->class) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int
+isglobal(Node *n)
+{
+ while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type))
+ n = n->left;
+
+ switch(n->op) {
+ case ONAME:
+ switch(n->class) {
+ case PEXTERN:
+ return 1;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+// Do we need a write barrier for the assignment l = r?
+int
+needwritebarrier(Node *l, Node *r)
+{
+ if(!use_writebarrier)
+ return 0;
+
+ if(l == N || isblank(l))
+ return 0;
+
+ // No write barrier for write of non-pointers.
+ dowidth(l->type);
+ if(!haspointers(l->type))
+ return 0;
+
+ // No write barrier for write to stack.
+ if(isstack(l))
+ return 0;
+
+ // No write barrier for implicit or explicit zeroing.
+ if(r == N || iszero(r))
+ return 0;
+
+ // No write barrier for initialization to constant.
+ if(r->op == OLITERAL)
+ return 0;
+
+ // No write barrier for storing static (read-only) data.
+ if(r->op == ONAME && strncmp(r->sym->name, "statictmp_", 10) == 0)
+ return 0;
+
+ // No write barrier for storing address of stack values,
+ // which are guaranteed only to be written to the stack.
+ if(r->op == OADDR && isstack(r->left))
+ return 0;
+
+ // No write barrier for storing address of global, which
+ // is live no matter what.
+ if(r->op == OADDR && isglobal(r->left))
+ return 0;
+
+ // No write barrier for reslice: x = x[0:y] or x = append(x, ...).
+ // Both are compiled to modify x directly.
+ // In the case of append, a write barrier may still be needed
+ // if the underlying array grows, but the append code can
+ // generate the write barrier directly in that case.
+ // (It does not yet, but the cost of the write barrier will be
+ // small compared to the cost of the allocation.)
+ if(r->reslice) {
+ switch(r->op) {
+ case OSLICE:
+ case OSLICE3:
+ case OSLICESTR:
+ case OAPPEND:
+ break;
+ default:
+ dump("bad reslice-l", l);
+ dump("bad reslice-r", r);
+ break;
+ }
+ return 0;
+ }
+
+ // Otherwise, be conservative and use write barrier.
+ return 1;
+}
+
+// TODO(rsc): Perhaps componentgen should run before this.
+static Node*
+applywritebarrier(Node *n, NodeList **init)
+{
+ Node *l, *r;
+ Type *t;
+
+ if(n->left && n->right && needwritebarrier(n->left, n->right)) {
+ t = n->left->type;
+ l = nod(OADDR, n->left, N);
+ l->etype = 1; // addr does not escape
+ if(t->width == widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierptr", t, n->right->type), T, init,
+ l, n->right);
+ } else if(t->etype == TSTRING) {
+ n = mkcall1(writebarrierfn("writebarrierstring", t, n->right->type), T, init,
+ l, n->right);
+ } else if(isslice(t)) {
+ n = mkcall1(writebarrierfn("writebarrierslice", t, n->right->type), T, init,
+ l, n->right);
+ } else if(isinter(t)) {
+ n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init,
+ l, n->right);
+ } else if(t->width == 2*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat2", t, n->right->type), T, init,
+ l, nodnil(), n->right);
+ } else if(t->width == 3*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat3", t, n->right->type), T, init,
+ l, nodnil(), n->right);
+ } else if(t->width == 4*widthptr) {
+ n = mkcall1(writebarrierfn("writebarrierfat4", t, n->right->type), T, init,
+ l, nodnil(), n->right);
+ } else {
+ r = n->right;
+ while(r->op == OCONVNOP)
+ r = r->left;
+ r = nod(OADDR, r, N);
+ r->etype = 1; // addr does not escape
+ //warnl(n->lineno, "writebarrierfat %T %N", t, r);
+ n = mkcall1(writebarrierfn("writebarrierfat", t, r->left->type), T, init,
+ typename(t), l, r);
+ }
+ }
+ return n;
+}
+
+static Node*
+convas(Node *n, NodeList **init)
+{
+ Type *lt, *rt;
+ Node *map, *key, *val;
+
+ 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) {
+ map = n->left->left;
+ key = n->left->right;
+ val = n->right;
+ walkexpr(&map, init);
+ walkexpr(&key, init);
+ walkexpr(&val, init);
+ // orderexpr made sure key and val are addressable.
+ key = nod(OADDR, key, N);
+ val = nod(OADDR, val, N);
+ n = mkcall1(mapfn("mapassign1", map->type), T, init,
+ typename(map->type), map, key, val);
+ goto out;
+ }
+
+ if(!eqtype(lt, rt)) {
+ 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
+ */
+static 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 = temp(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);
+}
+
+static void reorder3save(Node**, NodeList*, NodeList*, NodeList**);
+static int aliased(Node*, NodeList*, NodeList*);
+
+/*
+ * from ascompat[ee]
+ * a,b = c,d
+ * simultaneous assignment. there cannot
+ * be later use of an earlier lvalue.
+ *
+ * function calls have been removed.
+ */
+static NodeList*
+reorder3(NodeList *all)
+{
+ NodeList *list, *early, *mapinit;
+ Node *l;
+
+ // If a needed expression may be affected by an
+ // earlier assignment, make an early copy of that
+ // expression and use the copy instead.
+ early = nil;
+ mapinit = nil;
+ for(list=all; list; list=list->next) {
+ l = list->n->left;
+
+ // Save subexpressions needed on left side.
+ // Drill through non-dereferences.
+ for(;;) {
+ if(l->op == ODOT || l->op == OPAREN) {
+ l = l->left;
+ continue;
+ }
+ if(l->op == OINDEX && isfixedarray(l->left->type)) {
+ reorder3save(&l->right, all, list, &early);
+ l = l->left;
+ continue;
+ }
+ break;
+ }
+ switch(l->op) {
+ default:
+ fatal("reorder3 unexpected lvalue %#O", l->op);
+ case ONAME:
+ break;
+ case OINDEX:
+ case OINDEXMAP:
+ reorder3save(&l->left, all, list, &early);
+ reorder3save(&l->right, all, list, &early);
+ if(l->op == OINDEXMAP)
+ list->n = convas(list->n, &mapinit);
+ break;
+ case OIND:
+ case ODOTPTR:
+ reorder3save(&l->left, all, list, &early);
+ }
+
+ // Save expression on right side.
+ reorder3save(&list->n->right, all, list, &early);
+ }
+
+ early = concat(mapinit, early);
+ return concat(early, all);
+}
+
+static int vmatch2(Node*, Node*);
+static int varexpr(Node*);
+
+/*
+ * if the evaluation of *np would be affected by the
+ * assignments in all up to but not including stop,
+ * copy into a temporary during *early and
+ * replace *np with that temp.
+ */
+static void
+reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early)
+{
+ Node *n, *q;
+
+ n = *np;
+ if(!aliased(n, all, stop))
+ return;
+
+ q = temp(n->type);
+ q = nod(OAS, q, n);
+ typecheck(&q, Etop);
+ *early = list(*early, q);
+ *np = q->left;
+}
+
+/*
+ * what's the outer value that a write to n affects?
+ * outer value means containing struct or array.
+ */
+Node*
+outervalue(Node *n)
+{
+ for(;;) {
+ if(n->op == ODOT || n->op == OPAREN) {
+ n = n->left;
+ continue;
+ }
+ if(n->op == OINDEX && isfixedarray(n->left->type)) {
+ n = n->left;
+ continue;
+ }
+ break;
+ }
+ return n;
+}
+
+/*
+ * Is it possible that the computation of n might be
+ * affected by writes in as up to but not including stop?
+ */
+static int
+aliased(Node *n, NodeList *all, NodeList *stop)
+{
+ int memwrite, varwrite;
+ Node *a;
+ NodeList *l;
+
+ if(n == N)
+ return 0;
+
+ // Look for obvious aliasing: a variable being assigned
+ // during the all list and appearing in n.
+ // Also record whether there are any writes to main memory.
+ // Also record whether there are any writes to variables
+ // whose addresses have been taken.
+ memwrite = 0;
+ varwrite = 0;
+ for(l=all; l!=stop; l=l->next) {
+ a = outervalue(l->n->left);
+ if(a->op != ONAME) {
+ memwrite = 1;
+ continue;
+ }
+ switch(n->class) {
+ default:
+ varwrite = 1;
+ continue;
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ if(n->addrtaken) {
+ varwrite = 1;
+ continue;
+ }
+ if(vmatch2(a, n)) {
+ // Direct hit.
+ return 1;
+ }
+ }
+ }
+
+ // The variables being written do not appear in n.
+ // However, n might refer to computed addresses
+ // that are being written.
+
+ // If no computed addresses are affected by the writes, no aliasing.
+ if(!memwrite && !varwrite)
+ return 0;
+
+ // If n does not refer to computed addresses
+ // (that is, if n only refers to variables whose addresses
+ // have not been taken), no aliasing.
+ if(varexpr(n))
+ return 0;
+
+ // Otherwise, both the writes and n refer to computed memory addresses.
+ // Assume that they might conflict.
+ return 1;
+}
+
+/*
+ * does the evaluation of n only refer to variables
+ * whose addresses have not been taken?
+ * (and no other memory)
+ */
+static int
+varexpr(Node *n)
+{
+ if(n == N)
+ return 1;
+
+ switch(n->op) {
+ case OLITERAL:
+ return 1;
+ case ONAME:
+ switch(n->class) {
+ case PAUTO:
+ case PPARAM:
+ case PPARAMOUT:
+ if(!n->addrtaken)
+ return 1;
+ }
+ return 0;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case OPLUS:
+ case OMINUS:
+ case OCOM:
+ case OPAREN:
+ case OANDAND:
+ case OOROR:
+ case ODOT: // but not ODOTPTR
+ case OCONV:
+ case OCONVNOP:
+ case OCONVIFACE:
+ case ODOTTYPE:
+ return varexpr(n->left) && varexpr(n->right);
+ }
+
+ // Be conservative.
+ return 0;
+}
+
+/*
+ * is the name l mentioned in r?
+ */
+static int
+vmatch2(Node *l, Node *r)
+{
+ NodeList *ll;
+
+ if(r == N)
+ return 0;
+ switch(r->op) {
+ case ONAME:
+ // match each right given left
+ return l == r;
+ 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;
+}
+
+/*
+ * is any name mentioned in l also mentioned in r?
+ * called by sinit.c
+ */
+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;
+}
+
+/*
+ * 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 && v->sym && v->sym->name[0] == '~' && v->sym->name[1] == 'r') // unnamed result
+ v = N;
+ // In precisestack mode, the garbage collector assumes results
+ // are always live, so zero them always.
+ if(out && (precisestack_enabled || (v == N && 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(compiling_runtime)
+ yyerror("%N escapes to heap, not allowed in runtime.", v);
+ 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;
+}
+
+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*
+mapfndel(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);
+ return fn;
+}
+
+static Node*
+writebarrierfn(char *name, Type *l, Type *r)
+{
+ Node *fn;
+
+ fn = syslook(name, 1);
+ argtype(fn, l);
+ argtype(fn, r);
+ return fn;
+}
+
+static Node*
+addstr(Node *n, NodeList **init)
+{
+ Node *r, *cat, *slice;
+ NodeList *args, *l;
+ int c;
+ Type *t;
+
+ // orderexpr rewrote OADDSTR to have a list of strings.
+ c = count(n->list);
+ if(c < 2)
+ yyerror("addstr count %d too small", c);
+
+ // build list of string arguments
+ args = nil;
+ for(l=n->list; l != nil; l=l->next)
+ args = list(args, conv(l->n, types[TSTRING]));
+
+ if(c <= 5) {
+ // small numbers of strings use direct runtime helpers.
+ // note: orderexpr knows this cutoff too.
+ snprint(namebuf, sizeof(namebuf), "concatstring%d", c);
+ } else {
+ // large numbers of strings are passed to the runtime as a slice.
+ strcpy(namebuf, "concatstrings");
+ t = typ(TARRAY);
+ t->type = types[TSTRING];
+ t->bound = -1;
+ slice = nod(OCOMPLIT, N, typenod(t));
+ slice->alloc = n->alloc;
+ slice->list = args;
+ slice->esc = EscNone;
+ args = list1(slice);
+ }
+ cat = syslook(namebuf, 1);
+ r = nod(OCALL, cat, N);
+ r->list = args;
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ r->type = n->type;
+
+ return r;
+}
+
+// expand append(l1, l2...) to
+// init {
+// s := l1
+// if n := len(l1) + len(l2) - cap(s); n > 0 {
+// s = growslice(s, n)
+// }
+// s = s[:len(l1)+len(l2)]
+// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+// }
+// s
+//
+// l2 is allowed to be a string.
+static Node*
+appendslice(Node *n, NodeList **init)
+{
+ NodeList *l;
+ Node *l1, *l2, *nt, *nif, *fn;
+ Node *nptr1, *nptr2, *nwid;
+ Node *s;
+
+ walkexprlistsafe(n->list, init);
+
+ // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+ // and n are name or literal, but those may index the slice we're
+ // modifying here. Fix explicitly.
+ for(l=n->list; l; l=l->next)
+ l->n = cheapexpr(l->n, init);
+
+ l1 = n->list->n;
+ l2 = n->list->next->n;
+
+ s = temp(l1->type); // var s []T
+ l = nil;
+ l = list(l, nod(OAS, s, l1)); // s = l1
+
+ nt = temp(types[TINT]);
+ nif = nod(OIF, N, N);
+ // n := len(s) + len(l2) - cap(s)
+ nif->ninit = list1(nod(OAS, nt,
+ nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N))));
+ nif->ntest = nod(OGT, nt, nodintconst(0));
+ // instantiate growslice(Type*, []any, int64) []any
+ fn = syslook("growslice", 1);
+ argtype(fn, s->type->type);
+ argtype(fn, s->type->type);
+
+ // s = growslice(T, s, n)
+ nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit,
+ typename(s->type),
+ s,
+ conv(nt, types[TINT64]))));
+
+ l = list(l, nif);
+
+ if(flag_race) {
+ // rely on runtime to instrument copy.
+ // copy(s[len(l1):len(l1)+len(l2)], l2)
+ nptr1 = nod(OSLICE, s, nod(OKEY,
+ nod(OLEN, l1, N),
+ nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N))));
+ nptr1->etype = 1;
+ nptr2 = l2;
+ if(l2->type->etype == TSTRING)
+ fn = syslook("slicestringcopy", 1);
+ else
+ fn = syslook("slicecopy", 1);
+ argtype(fn, l1->type);
+ argtype(fn, l2->type);
+ nt = mkcall1(fn, types[TINT], &l,
+ nptr1, nptr2,
+ nodintconst(s->type->type->width));
+ l = list(l, nt);
+ } else {
+ // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+ nptr1 = nod(OINDEX, s, nod(OLEN, l1, N));
+ nptr1->bounded = 1;
+ nptr1 = nod(OADDR, nptr1, N);
+
+ nptr2 = nod(OSPTR, l2, N);
+
+ fn = syslook("memmove", 1);
+ argtype(fn, s->type->type); // 1 old []any
+ argtype(fn, s->type->type); // 2 ret []any
+
+ nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l);
+ nwid = nod(OMUL, nwid, nodintconst(s->type->type->width));
+ nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid);
+ l = list(l, nt);
+ }
+
+ // s = s[:len(l1)+len(l2)]
+ nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N));
+ nt = nod(OSLICE, s, nod(OKEY, N, nt));
+ nt->etype = 1;
+ l = list(l, nod(OAS, s, nt));
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return s;
+}
+
+// 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);
+
+ // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+ // and n are name or literal, but those may index the slice we're
+ // modifying here. Fix explicitly.
+ for(l=n->list; l; l=l->next)
+ l->n = cheapexpr(l->n, init);
+
+ nsrc = n->list->n;
+
+ // Resolve slice type of multi-valued return.
+ if(istype(nsrc->type, TSTRUCT))
+ nsrc->type = nsrc->type->type->type;
+ argc = count(n->list) - 1;
+ if (argc < 1) {
+ return nsrc;
+ }
+
+ l = nil;
+
+ ns = temp(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 = temp(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;
+ 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->bounded = 1;
+ 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;
+}
+
+// Lower copy(a, b) to a memmove call or a runtime call.
+//
+// init {
+// n := len(a)
+// if n > len(b) { n = len(b) }
+// memmove(a.ptr, b.ptr, n*sizeof(elem(a)))
+// }
+// n;
+//
+// Also works if b is a string.
+//
+static Node*
+copyany(Node *n, NodeList **init, int runtimecall)
+{
+ Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn;
+ NodeList *l;
+
+ if(runtimecall) {
+ 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);
+ return mkcall1(fn, n->type, init,
+ n->left, n->right,
+ nodintconst(n->left->type->type->width));
+ }
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ nl = temp(n->left->type);
+ nr = temp(n->right->type);
+ l = nil;
+ l = list(l, nod(OAS, nl, n->left));
+ l = list(l, nod(OAS, nr, n->right));
+
+ nfrm = nod(OSPTR, nr, N);
+ nto = nod(OSPTR, nl, N);
+
+ nlen = temp(types[TINT]);
+ // n = len(to)
+ l = list(l, nod(OAS, nlen, nod(OLEN, nl, N)));
+ // if n > len(frm) { n = len(frm) }
+ nif = nod(OIF, N, N);
+ nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N));
+ nif->nbody = list(nif->nbody,
+ nod(OAS, nlen, nod(OLEN, nr, N)));
+ l = list(l, nif);
+
+ // Call memmove.
+ fn = syslook("memmove", 1);
+ argtype(fn, nl->type->type);
+ argtype(fn, nl->type->type);
+ nwid = temp(types[TUINTPTR]);
+ l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR])));
+ nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width));
+ l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid));
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return nlen;
+}
+
+// Generate frontend part for OSLICE[3][ARR|STR]
+//
+static Node*
+sliceany(Node* n, NodeList **init)
+{
+ int bounded, slice3;
+ Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2;
+ int64 lbv, hbv, cbv, bv, w;
+ Type *bt;
+
+// print("before sliceany: %+N\n", n);
+
+ src = n->left;
+ lb = n->right->left;
+ slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR;
+ if(slice3) {
+ hb = n->right->right->left;
+ cb = n->right->right->right;
+ } else {
+ hb = n->right->right;
+ cb = N;
+ }
+
+ bounded = n->etype;
+
+ if(n->op == OSLICESTR)
+ bound = nod(OLEN, src, N);
+ else
+ bound = nod(OCAP, src, N);
+
+ typecheck(&bound, Erv);
+ walkexpr(&bound, init); // if src is an array, bound will be a const now.
+
+ // static checks if possible
+ bv = 1LL<<50;
+ if(isconst(bound, CTINT)) {
+ if(!smallintconst(bound))
+ yyerror("array len too large");
+ else
+ bv = mpgetfix(bound->val.u.xval);
+ }
+
+ if(isconst(cb, CTINT)) {
+ cbv = mpgetfix(cb->val.u.xval);
+ if(cbv < 0 || cbv > bv)
+ yyerror("slice index out of bounds");
+ }
+ if(isconst(hb, CTINT)) {
+ hbv = mpgetfix(hb->val.u.xval);
+ if(hbv < 0 || hbv > bv)
+ yyerror("slice index out of bounds");
+ }
+ if(isconst(lb, CTINT)) {
+ lbv = mpgetfix(lb->val.u.xval);
+ if(lbv < 0 || lbv > bv) {
+ yyerror("slice index out of bounds");
+ lbv = -1;
+ }
+ if(lbv == 0)
+ lb = N;
+ }
+
+ // Checking src[lb:hb:cb] or src[lb:hb].
+ // if chk0 || chk1 || chk2 { panicslice() }
+ chk = N;
+ chk0 = N; // cap(src) < cb
+ chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
+ chk2 = N; // hb < lb
+
+ // All comparisons are unsigned to avoid testing < 0.
+ bt = types[simtype[TUINT]];
+ if(cb != N && cb->type->width > 4)
+ bt = types[TUINT64];
+ if(hb != N && hb->type->width > 4)
+ bt = types[TUINT64];
+ if(lb != N && lb->type->width > 4)
+ bt = types[TUINT64];
+
+ bound = cheapexpr(conv(bound, bt), init);
+
+ if(cb != N) {
+ cb = cheapexpr(conv(cb, bt), init);
+ if(!bounded)
+ chk0 = nod(OLT, bound, cb);
+ } else if(slice3) {
+ // When we figure out what this means, implement it.
+ fatal("slice3 with cb == N"); // rejected by parser
+ }
+
+ if(hb != N) {
+ hb = cheapexpr(conv(hb, bt), init);
+ if(!bounded) {
+ if(cb != N)
+ chk1 = nod(OLT, cb, hb);
+ else
+ chk1 = nod(OLT, bound, hb);
+ }
+ } else if(slice3) {
+ // When we figure out what this means, implement it.
+ fatal("slice3 with hb == N"); // rejected by parser
+ } else if(n->op == OSLICEARR) {
+ hb = bound;
+ } else {
+ hb = nod(OLEN, src, N);
+ typecheck(&hb, Erv);
+ walkexpr(&hb, init);
+ hb = cheapexpr(conv(hb, bt), init);
+ }
+
+ if(lb != N) {
+ lb = cheapexpr(conv(lb, bt), init);
+ if(!bounded)
+ chk2 = nod(OLT, hb, lb);
+ }
+
+ if(chk0 != N || chk1 != N || chk2 != N) {
+ chk = nod(OIF, N, N);
+ chk->nbody = list1(mkcall("panicslice", T, init));
+ chk->likely = -1;
+ if(chk0 != N)
+ chk->ntest = chk0;
+ if(chk1 != N) {
+ if(chk->ntest == N)
+ chk->ntest = chk1;
+ else
+ chk->ntest = nod(OOROR, chk->ntest, chk1);
+ }
+ if(chk2 != N) {
+ if(chk->ntest == N)
+ chk->ntest = chk2;
+ else
+ chk->ntest = nod(OOROR, chk->ntest, chk2);
+ }
+ typecheck(&chk, Etop);
+ walkstmt(&chk);
+ *init = concat(*init, chk->ninit);
+ chk->ninit = nil;
+ *init = list(*init, chk);
+ }
+
+ // prepare new cap, len and offs for backend cgen_slice
+ // cap = bound [ - lo ]
+ n->right = N;
+ n->list = nil;
+ if(!slice3)
+ cb = bound;
+ if(lb == N)
+ bound = conv(cb, types[simtype[TUINT]]);
+ else
+ bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
+ typecheck(&bound, Erv);
+ walkexpr(&bound, init);
+ n->list = list(n->list, bound);
+
+ // len = hi [ - lo]
+ if(lb == N)
+ hb = conv(hb, types[simtype[TUINT]]);
+ else
+ hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
+ typecheck(&hb, Erv);
+ walkexpr(&hb, init);
+ n->list = list(n->list, hb);
+
+ // offs = [width *] lo, but omit if zero
+ if(lb != N) {
+ if(n->op == OSLICESTR)
+ w = 1;
+ else
+ w = n->type->type->width;
+ lb = conv(lb, types[TUINTPTR]);
+ if(w > 1)
+ lb = nod(OMUL, nodintconst(w), lb);
+ typecheck(&lb, Erv);
+ walkexpr(&lb, init);
+ n->list = list(n->list, lb);
+ }
+
+// print("after sliceany: %+N\n", n);
+
+ return n;
+}
+
+static Node*
+eqfor(Type *t)
+{
+ int a;
+ Node *n;
+ Node *ntype;
+ Sym *sym;
+
+ // Should only arrive here with large memory or
+ // a struct/array containing a non-memory field/element.
+ // Small memory is handled inline, and single non-memory
+ // is handled during type check (OCMPSTR etc).
+ a = algtype1(t, nil);
+ if(a != AMEM && a != -1)
+ fatal("eqfor %T", t);
+
+ if(a == AMEM) {
+ n = syslook("memequal", 1);
+ argtype(n, t);
+ argtype(n, t);
+ return n;
+ }
+
+ sym = typesymprefix(".eq", t);
+ n = newname(sym);
+ n->class = PFUNC;
+ ntype = nod(OTFUNC, N, N);
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+ ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+ ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, N, typenod(types[TBOOL])));
+ typecheck(&ntype, Etype);
+ n->type = ntype->type;
+ return n;
+}
+
+static int
+countfield(Type *t)
+{
+ Type *t1;
+ int n;
+
+ n = 0;
+ for(t1=t->type; t1!=T; t1=t1->down)
+ n++;
+ return n;
+}
+
+static void
+walkcompare(Node **np, NodeList **init)
+{
+ Node *n, *l, *r, *call, *a, *li, *ri, *expr, *cmpl, *cmpr;
+ int andor, i;
+ Type *t, *t1;
+
+ n = *np;
+
+ // Must be comparison of array or struct.
+ // Otherwise back end handles it.
+ t = n->left->type;
+ switch(t->etype) {
+ default:
+ return;
+ case TARRAY:
+ if(isslice(t))
+ return;
+ break;
+ case TSTRUCT:
+ break;
+ }
+
+ cmpl = n->left;
+ while(cmpl != N && cmpl->op == OCONVNOP)
+ cmpl = cmpl->left;
+ cmpr = n->right;
+ while(cmpr != N && cmpr->op == OCONVNOP)
+ cmpr = cmpr->left;
+
+ if(!islvalue(cmpl) || !islvalue(cmpr)) {
+ fatal("arguments of comparison must be lvalues - %N %N", cmpl, cmpr);
+ }
+
+ l = temp(ptrto(t));
+ a = nod(OAS, l, nod(OADDR, cmpl, N));
+ a->right->etype = 1; // addr does not escape
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ r = temp(ptrto(t));
+ a = nod(OAS, r, nod(OADDR, cmpr, N));
+ a->right->etype = 1; // addr does not escape
+ typecheck(&a, Etop);
+ *init = list(*init, a);
+
+ expr = N;
+ andor = OANDAND;
+ if(n->op == ONE)
+ andor = OOROR;
+
+ if(t->etype == TARRAY &&
+ t->bound <= 4 &&
+ issimple[t->type->etype]) {
+ // Four or fewer elements of a basic type.
+ // Unroll comparisons.
+ for(i=0; i<t->bound; i++) {
+ li = nod(OINDEX, l, nodintconst(i));
+ ri = nod(OINDEX, r, nodintconst(i));
+ a = nod(n->op, li, ri);
+ if(expr == N)
+ expr = a;
+ else
+ expr = nod(andor, expr, a);
+ }
+ if(expr == N)
+ expr = nodbool(n->op == OEQ);
+ r = expr;
+ goto ret;
+ }
+
+ if(t->etype == TSTRUCT && countfield(t) <= 4) {
+ // Struct of four or fewer fields.
+ // Inline comparisons.
+ for(t1=t->type; t1; t1=t1->down) {
+ if(isblanksym(t1->sym))
+ continue;
+ li = nod(OXDOT, l, newname(t1->sym));
+ ri = nod(OXDOT, r, newname(t1->sym));
+ a = nod(n->op, li, ri);
+ if(expr == N)
+ expr = a;
+ else
+ expr = nod(andor, expr, a);
+ }
+ if(expr == N)
+ expr = nodbool(n->op == OEQ);
+ r = expr;
+ goto ret;
+ }
+
+ // Chose not to inline. Call equality function directly.
+ call = nod(OCALL, eqfor(t), N);
+ call->list = list(call->list, l);
+ call->list = list(call->list, r);
+ call->list = list(call->list, nodintconst(t->width));
+ r = call;
+ if(n->op != OEQ)
+ r = nod(ONOT, r, N);
+ goto ret;
+
+ret:
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ if(r->type != n->type) {
+ r = nod(OCONVNOP, r, N);
+ r->type = n->type;
+ r->typecheck = 1;
+ }
+ *np = r;
+ return;
+}
+
+static int
+samecheap(Node *a, Node *b)
+{
+ Node *ar, *br;
+ while(a != N && b != N && a->op == b->op) {
+ switch(a->op) {
+ default:
+ return 0;
+ case ONAME:
+ return a == b;
+ case ODOT:
+ case ODOTPTR:
+ ar = a->right;
+ br = b->right;
+ if(ar->op != ONAME || br->op != ONAME || ar->sym != br->sym)
+ return 0;
+ break;
+ case OINDEX:
+ ar = a->right;
+ br = b->right;
+ if(!isconst(ar, CTINT) || !isconst(br, CTINT) || mpcmpfixfix(ar->val.u.xval, br->val.u.xval) != 0)
+ return 0;
+ break;
+ }
+ a = a->left;
+ b = b->left;
+ }
+ return 0;
+}
+
+static void
+walkrotate(Node **np)
+{
+ int w, sl, sr, s;
+ Node *l, *r;
+ Node *n;
+
+ n = *np;
+
+ // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value.
+ l = n->left;
+ r = n->right;
+ if((n->op != OOR && n->op != OXOR) ||
+ (l->op != OLSH && l->op != ORSH) ||
+ (r->op != OLSH && r->op != ORSH) ||
+ n->type == T || issigned[n->type->etype] ||
+ l->op == r->op) {
+ return;
+ }
+
+ // Want same, side effect-free expression on lhs of both shifts.
+ if(!samecheap(l->left, r->left))
+ return;
+
+ // Constants adding to width?
+ w = l->type->width * 8;
+ if(smallintconst(l->right) && smallintconst(r->right)) {
+ if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w)
+ goto yes;
+ return;
+ }
+
+ // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31).
+ return;
+
+yes:
+ // Rewrite left shift half to left rotate.
+ if(l->op == OLSH)
+ n = l;
+ else
+ n = r;
+ n->op = OLROT;
+
+ // Remove rotate 0 and rotate w.
+ s = mpgetfix(n->right->val.u.xval);
+ if(s == 0 || s == w)
+ n = n->left;
+
+ *np = n;
+ return;
+}
+
+/*
+ * walkmul rewrites integer multiplication by powers of two as shifts.
+ */
+static void
+walkmul(Node **np, NodeList **init)
+{
+ Node *n, *nl, *nr;
+ int pow, neg, w;
+
+ n = *np;
+ if(!isint[n->type->etype])
+ return;
+
+ if(n->right->op == OLITERAL) {
+ nl = n->left;
+ nr = n->right;
+ } else if(n->left->op == OLITERAL) {
+ nl = n->right;
+ nr = n->left;
+ } else
+ return;
+
+ neg = 0;
+
+ // x*0 is 0 (and side effects of x).
+ if(mpgetfix(nr->val.u.xval) == 0) {
+ cheapexpr(nl, init);
+ nodconst(n, n->type, 0);
+ goto ret;
+ }
+
+ // nr is a constant.
+ pow = powtwo(nr);
+ if(pow < 0)
+ return;
+ if(pow >= 1000) {
+ // negative power of 2, like -16
+ neg = 1;
+ pow -= 1000;
+ }
+
+ w = nl->type->width*8;
+ if(pow+1 >= w)// too big, shouldn't happen
+ return;
+
+ nl = cheapexpr(nl, init);
+
+ if(pow == 0) {
+ // x*1 is x
+ n = nl;
+ goto ret;
+ }
+
+ n = nod(OLSH, nl, nodintconst(pow));
+
+ret:
+ if(neg)
+ n = nod(OMINUS, n, N);
+
+ typecheck(&n, Erv);
+ walkexpr(&n, init);
+ *np = n;
+}
+
+/*
+ * walkdiv rewrites division by a constant as less expensive
+ * operations.
+ */
+static void
+walkdiv(Node **np, NodeList **init)
+{
+ Node *n, *nl, *nr, *nc;
+ Node *n1, *n2, *n3, *n4;
+ int pow; // if >= 0, nr is 1<<pow
+ int s; // 1 if nr is negative.
+ int w;
+ Type *twide;
+ Magic m;
+
+ n = *np;
+ if(n->right->op != OLITERAL)
+ return;
+ // nr is a constant.
+ nl = cheapexpr(n->left, init);
+ nr = n->right;
+
+ // special cases of mod/div
+ // by a constant
+ w = nl->type->width*8;
+ s = 0;
+ pow = powtwo(nr);
+ if(pow >= 1000) {
+ // negative power of 2
+ s = 1;
+ pow -= 1000;
+ }
+
+ if(pow+1 >= w) {
+ // divisor too large.
+ return;
+ }
+ if(pow < 0) {
+ goto divbymul;
+ }
+
+ switch(pow) {
+ case 0:
+ if(n->op == OMOD) {
+ // nl % 1 is zero.
+ nodconst(n, n->type, 0);
+ } else if(s) {
+ // divide by -1
+ n->op = OMINUS;
+ n->right = N;
+ } else {
+ // divide by 1
+ n = nl;
+ }
+ break;
+ default:
+ if(issigned[n->type->etype]) {
+ if(n->op == OMOD) {
+ // signed modulo 2^pow is like ANDing
+ // with the last pow bits, but if nl < 0,
+ // nl & (2^pow-1) is (nl+1)%2^pow - 1.
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[simtype[TUINT]], w-1);
+ n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
+ if(pow == 1) {
+ typecheck(&n1, Erv);
+ n1 = cheapexpr(n1, init);
+ // n = (nl+ε)&1 -ε where ε=1 iff nl<0.
+ n2 = nod(OSUB, nl, n1);
+ nc = nod(OXXX, N, N);
+ nodconst(nc, nl->type, 1);
+ n3 = nod(OAND, n2, nc);
+ n = nod(OADD, n3, n1);
+ } else {
+ // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
+ nc = nod(OXXX, N, N);
+ nodconst(nc, nl->type, (1LL<<pow)-1);
+ n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0.
+ typecheck(&n2, Erv);
+ n2 = cheapexpr(n2, init);
+
+ n3 = nod(OADD, nl, n2);
+ n4 = nod(OAND, n3, nc);
+ n = nod(OSUB, n4, n2);
+ }
+ break;
+ } else {
+ // arithmetic right shift does not give the correct rounding.
+ // if nl >= 0, nl >> n == nl / nr
+ // if nl < 0, we want to add 2^n-1 first.
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[simtype[TUINT]], w-1);
+ n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
+ if(pow == 1) {
+ // nl+1 is nl-(-1)
+ n->left = nod(OSUB, nl, n1);
+ } else {
+ // Do a logical right right on -1 to keep pow bits.
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[simtype[TUINT]], w-pow);
+ n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc);
+ n->left = nod(OADD, nl, conv(n2, nl->type));
+ }
+ // n = (nl + 2^pow-1) >> pow
+ n->op = ORSH;
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[simtype[TUINT]], pow);
+ n->right = nc;
+ n->typecheck = 0;
+ }
+ if(s)
+ n = nod(OMINUS, n, N);
+ break;
+ }
+ nc = nod(OXXX, N, N);
+ if(n->op == OMOD) {
+ // n = nl & (nr-1)
+ n->op = OAND;
+ nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1);
+ } else {
+ // n = nl >> pow
+ n->op = ORSH;
+ nodconst(nc, types[simtype[TUINT]], pow);
+ }
+ n->typecheck = 0;
+ n->right = nc;
+ break;
+ }
+ goto ret;
+
+divbymul:
+ // try to do division by multiply by (2^w)/d
+ // see hacker's delight chapter 10
+ // TODO: support 64-bit magic multiply here.
+ m.w = w;
+ if(issigned[nl->type->etype]) {
+ m.sd = mpgetfix(nr->val.u.xval);
+ smagic(&m);
+ } else {
+ m.ud = mpgetfix(nr->val.u.xval);
+ umagic(&m);
+ }
+ if(m.bad)
+ return;
+
+ // We have a quick division method so use it
+ // for modulo too.
+ if(n->op == OMOD)
+ goto longmod;
+
+ switch(simtype[nl->type->etype]) {
+ default:
+ return;
+
+ case TUINT8:
+ case TUINT16:
+ case TUINT32:
+ // n1 = nl * magic >> w (HMUL)
+ nc = nod(OXXX, N, N);
+ nodconst(nc, nl->type, m.um);
+ n1 = nod(OMUL, nl, nc);
+ typecheck(&n1, Erv);
+ n1->op = OHMUL;
+ if(m.ua) {
+ // Select a Go type with (at least) twice the width.
+ switch(simtype[nl->type->etype]) {
+ default:
+ return;
+ case TUINT8:
+ case TUINT16:
+ twide = types[TUINT32];
+ break;
+ case TUINT32:
+ twide = types[TUINT64];
+ break;
+ case TINT8:
+ case TINT16:
+ twide = types[TINT32];
+ break;
+ case TINT32:
+ twide = types[TINT64];
+ break;
+ }
+
+ // add numerator (might overflow).
+ // n2 = (n1 + nl)
+ n2 = nod(OADD, conv(n1, twide), conv(nl, twide));
+
+ // shift by m.s
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[TUINT], m.s);
+ n = conv(nod(ORSH, n2, nc), nl->type);
+ } else {
+ // n = n1 >> m.s
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[TUINT], m.s);
+ n = nod(ORSH, n1, nc);
+ }
+ break;
+
+ case TINT8:
+ case TINT16:
+ case TINT32:
+ // n1 = nl * magic >> w
+ nc = nod(OXXX, N, N);
+ nodconst(nc, nl->type, m.sm);
+ n1 = nod(OMUL, nl, nc);
+ typecheck(&n1, Erv);
+ n1->op = OHMUL;
+ if(m.sm < 0) {
+ // add the numerator.
+ n1 = nod(OADD, n1, nl);
+ }
+ // shift by m.s
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[TUINT], m.s);
+ n2 = conv(nod(ORSH, n1, nc), nl->type);
+ // add 1 iff n1 is negative.
+ nc = nod(OXXX, N, N);
+ nodconst(nc, types[TUINT], w-1);
+ n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative.
+ n = nod(OSUB, n2, n3);
+ // apply sign.
+ if(m.sd < 0)
+ n = nod(OMINUS, n, N);
+ break;
+ }
+ goto ret;
+
+longmod:
+ // rewrite as A%B = A - (A/B*B).
+ n1 = nod(ODIV, nl, nr);
+ n2 = nod(OMUL, n1, nr);
+ n = nod(OSUB, nl, n2);
+ goto ret;
+
+ret:
+ typecheck(&n, Erv);
+ walkexpr(&n, init);
+ *np = n;
+}
+
+// return 1 if integer n must be in range [0, max), 0 otherwise
+static int
+bounded(Node *n, int64 max)
+{
+ int64 v;
+ int32 bits;
+ int sign;
+
+ if(n->type == T || !isint[n->type->etype])
+ return 0;
+
+ sign = issigned[n->type->etype];
+ bits = 8*n->type->width;
+
+ if(smallintconst(n)) {
+ v = mpgetfix(n->val.u.xval);
+ return 0 <= v && v < max;
+ }
+
+ switch(n->op) {
+ case OAND:
+ v = -1;
+ if(smallintconst(n->left)) {
+ v = mpgetfix(n->left->val.u.xval);
+ } else if(smallintconst(n->right)) {
+ v = mpgetfix(n->right->val.u.xval);
+ }
+ if(0 <= v && v < max)
+ return 1;
+ break;
+
+ case OMOD:
+ if(!sign && smallintconst(n->right)) {
+ v = mpgetfix(n->right->val.u.xval);
+ if(0 <= v && v <= max)
+ return 1;
+ }
+ break;
+
+ case ODIV:
+ if(!sign && smallintconst(n->right)) {
+ v = mpgetfix(n->right->val.u.xval);
+ while(bits > 0 && v >= 2) {
+ bits--;
+ v >>= 1;
+ }
+ }
+ break;
+
+ case ORSH:
+ if(!sign && smallintconst(n->right)) {
+ v = mpgetfix(n->right->val.u.xval);
+ if(v > bits)
+ return 1;
+ bits -= v;
+ }
+ break;
+ }
+
+ if(!sign && bits <= 62 && (1LL<<bits) <= max)
+ return 1;
+
+ return 0;
+}
+
+void
+usefield(Node *n)
+{
+ Type *field, *l;
+
+ if(!fieldtrack_enabled)
+ return;
+
+ switch(n->op) {
+ default:
+ fatal("usefield %O", n->op);
+ case ODOT:
+ case ODOTPTR:
+ break;
+ }
+
+ field = n->paramfld;
+ if(field == T)
+ fatal("usefield %T %S without paramfld", n->left->type, n->right->sym);
+ if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil)
+ return;
+
+ // dedup on list
+ if(field->lastfn == curfn)
+ return;
+ field->lastfn = curfn;
+ field->outer = n->left->type;
+ if(isptr[field->outer->etype])
+ field->outer = field->outer->type;
+ if(field->outer->sym == S)
+ yyerror("tracked field must be in named struct type");
+ if(!exportname(field->sym->name))
+ yyerror("tracked field must be exported (upper case)");
+
+ l = typ(0);
+ l->type = field;
+ l->down = curfn->paramfld;
+ curfn->paramfld = l;
+}
+
+static int
+candiscardlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ if(!candiscard(l->n))
+ return 0;
+ return 1;
+}
+
+int
+candiscard(Node *n)
+{
+ if(n == N)
+ return 1;
+
+ switch(n->op) {
+ default:
+ return 0;
+
+ case ONAME:
+ case ONONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OADDSTR:
+ case OADDR:
+ case OANDAND:
+ case OARRAYBYTESTR:
+ case OARRAYRUNESTR:
+ case OSTRARRAYBYTE:
+ case OSTRARRAYRUNE:
+ case OCAP:
+ case OCMPIFACE:
+ case OCMPSTR:
+ case OCOMPLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ case OARRAYLIT:
+ case OPTRLIT:
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case ODOT:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGT:
+ case OGE:
+ case OKEY:
+ case OLEN:
+ case OMUL:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case ONEW:
+ case ONOT:
+ case OCOM:
+ case OPLUS:
+ case OMINUS:
+ case OOROR:
+ case OPAREN:
+ case ORUNESTR:
+ case OREAL:
+ case OIMAG:
+ case OCOMPLEX:
+ // Discardable as long as the subpieces are.
+ break;
+
+ case ODIV:
+ case OMOD:
+ // Discardable as long as we know it's not division by zero.
+ if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0)
+ break;
+ if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0)
+ break;
+ return 0;
+
+ case OMAKECHAN:
+ case OMAKEMAP:
+ // Discardable as long as we know it won't fail because of a bad size.
+ if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0)
+ break;
+ return 0;
+
+ case OMAKESLICE:
+ // Difficult to tell what sizes are okay.
+ return 0;
+ }
+
+ if(!candiscard(n->left) ||
+ !candiscard(n->right) ||
+ !candiscard(n->ntest) ||
+ !candiscard(n->nincr) ||
+ !candiscardlist(n->ninit) ||
+ !candiscardlist(n->nbody) ||
+ !candiscardlist(n->nelse) ||
+ !candiscardlist(n->list) ||
+ !candiscardlist(n->rlist)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+// rewrite
+// print(x, y, z)
+// into
+// func(a1, a2, a3) {
+// print(a1, a2, a3)
+// }(x, y, z)
+// and same for println.
+static void
+walkprintfunc(Node **np, NodeList **init)
+{
+ Node *n;
+ Node *a, *fn, *t, *oldfn;
+ NodeList *l, *printargs;
+ int num;
+ char buf[100];
+ static int prgen;
+
+ n = *np;
+
+ if(n->ninit != nil) {
+ walkstmtlist(n->ninit);
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ }
+
+ t = nod(OTFUNC, N, N);
+ num = 0;
+ printargs = nil;
+ for(l=n->list; l != nil; l=l->next) {
+ snprint(buf, sizeof buf, "a%d", num++);
+ a = nod(ODCLFIELD, newname(lookup(buf)), typenod(l->n->type));
+ t->list = list(t->list, a);
+ printargs = list(printargs, a->left);
+ }
+
+ fn = nod(ODCLFUNC, N, N);
+ snprint(buf, sizeof buf, "print·%d", ++prgen);
+ fn->nname = newname(lookup(buf));
+ fn->nname->defn = fn;
+ fn->nname->ntype = t;
+ declare(fn->nname, PFUNC);
+
+ oldfn = curfn;
+ curfn = nil;
+ funchdr(fn);
+
+ a = nod(n->op, N, N);
+ a->list = printargs;
+ typecheck(&a, Etop);
+ walkstmt(&a);
+
+ fn->nbody = list1(a);
+
+ funcbody(fn);
+
+ typecheck(&fn, Etop);
+ typechecklist(fn->nbody, Etop);
+ xtop = list(xtop, fn);
+ curfn = oldfn;
+
+ a = nod(OCALL, N, N);
+ a->left = fn->nname;
+ a->list = n->list;
+ typecheck(&a, Etop);
+ walkexpr(&a, init);
+ *np = a;
+}
diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c
new file mode 100644
index 0000000..f464126
--- /dev/null
+++ b/src/cmd/gc/y.tab.c
@@ -0,0 +1,5132 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+ simplifying the original so-called "semantic" parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Bison version. */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LLITERAL = 258,
+ LASOP = 259,
+ LCOLAS = 260,
+ LBREAK = 261,
+ LCASE = 262,
+ LCHAN = 263,
+ LCONST = 264,
+ LCONTINUE = 265,
+ LDDD = 266,
+ LDEFAULT = 267,
+ LDEFER = 268,
+ LELSE = 269,
+ LFALL = 270,
+ LFOR = 271,
+ LFUNC = 272,
+ LGO = 273,
+ LGOTO = 274,
+ LIF = 275,
+ LIMPORT = 276,
+ LINTERFACE = 277,
+ LMAP = 278,
+ LNAME = 279,
+ LPACKAGE = 280,
+ LRANGE = 281,
+ LRETURN = 282,
+ LSELECT = 283,
+ LSTRUCT = 284,
+ LSWITCH = 285,
+ LTYPE = 286,
+ LVAR = 287,
+ LANDAND = 288,
+ LANDNOT = 289,
+ LBODY = 290,
+ LCOMM = 291,
+ LDEC = 292,
+ LEQ = 293,
+ LGE = 294,
+ LGT = 295,
+ LIGNORE = 296,
+ LINC = 297,
+ LLE = 298,
+ LLSH = 299,
+ LLT = 300,
+ LNE = 301,
+ LOROR = 302,
+ LRSH = 303,
+ NotPackage = 304,
+ NotParen = 305,
+ PreferToRightParen = 306
+ };
+#endif
+/* Tokens. */
+#define LLITERAL 258
+#define LASOP 259
+#define LCOLAS 260
+#define LBREAK 261
+#define LCASE 262
+#define LCHAN 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 20 "go.y"
+
+#include <u.h>
+#include <stdio.h> /* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
+#include "go.h"
+
+static void fixlbrace(int);
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table. */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 28 "go.y"
+{
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int i;
+}
+/* Line 193 of yacc.c. */
+#line 216 "y.tab.c"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 216 of yacc.c. */
+#line 229 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+# define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+# define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# else
+# define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions. */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+ int i;
+#endif
+{
+ return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# elif defined __BUILTIN_VA_ARG_INCR
+# include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+# elif defined _AIX
+# define YYSTACK_ALLOC __alloca
+# elif defined _MSC_VER
+# include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+# define alloca _alloca
+# else
+# define YYSTACK_ALLOC alloca
+# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+# ifndef YYSTACK_ALLOC_MAXIMUM
+ /* The OS might guarantee only one guard page at the bottom of the stack,
+ and a page size can be as small as 4096 bytes. So we cannot safely
+ invoke alloca (N) if N exceeds 4096. Use a slightly smaller number
+ to allow for a few compiler-allocated temporary stack slots. */
+# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+# endif
+# else
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# ifndef YYSTACK_ALLOC_MAXIMUM
+# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+# endif
+# if (defined __cplusplus && ! defined _STDLIB_H \
+ && ! ((defined YYMALLOC || defined malloc) \
+ && (defined YYFREE || defined free)))
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# ifndef _STDLIB_H
+# define _STDLIB_H 1
+# endif
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# ifndef YYFREE
+# define YYFREE free
+# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+# endif
+# endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+ && (! defined __cplusplus \
+ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ yytype_int16 yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined __GNUC__ && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (YYID (0))
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 4
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 2201
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 76
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 142
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 352
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 669
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 306
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const yytype_uint8 yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 69, 2, 2, 64, 55, 56, 2,
+ 59, 60, 53, 49, 75, 50, 63, 54, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 66, 62,
+ 2, 65, 2, 73, 74, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 71, 2, 72, 52, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 67, 51, 68, 70, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 57, 58, 61
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const yytype_uint16 yyprhs[] =
+{
+ 0, 0, 3, 8, 9, 13, 14, 18, 19, 23,
+ 26, 32, 36, 40, 43, 45, 49, 51, 54, 57,
+ 62, 63, 65, 66, 71, 72, 74, 76, 78, 80,
+ 83, 89, 93, 96, 102, 110, 114, 117, 123, 127,
+ 129, 132, 137, 141, 146, 150, 152, 155, 157, 159,
+ 162, 164, 168, 172, 176, 179, 182, 186, 192, 198,
+ 201, 202, 207, 208, 212, 213, 216, 217, 222, 227,
+ 232, 235, 241, 243, 245, 248, 249, 253, 255, 259,
+ 260, 261, 262, 271, 272, 278, 279, 282, 283, 286,
+ 287, 288, 296, 297, 303, 305, 309, 313, 317, 321,
+ 325, 329, 333, 337, 341, 345, 349, 353, 357, 361,
+ 365, 369, 373, 377, 381, 385, 387, 390, 393, 396,
+ 399, 402, 405, 408, 411, 415, 421, 428, 430, 432,
+ 436, 442, 448, 453, 460, 469, 471, 477, 483, 489,
+ 497, 499, 500, 504, 506, 511, 513, 518, 520, 524,
+ 526, 528, 530, 532, 534, 536, 538, 539, 541, 543,
+ 545, 547, 552, 557, 559, 561, 563, 566, 568, 570,
+ 572, 574, 576, 580, 582, 584, 586, 589, 591, 593,
+ 595, 597, 601, 603, 605, 607, 609, 611, 613, 615,
+ 617, 619, 623, 628, 633, 636, 640, 646, 648, 650,
+ 653, 657, 663, 667, 673, 677, 681, 687, 696, 702,
+ 711, 717, 718, 722, 723, 725, 729, 731, 736, 739,
+ 740, 744, 746, 750, 752, 756, 758, 762, 764, 768,
+ 770, 774, 778, 781, 786, 790, 796, 802, 804, 808,
+ 810, 813, 815, 819, 824, 826, 829, 832, 834, 836,
+ 840, 841, 844, 845, 847, 849, 851, 853, 855, 857,
+ 859, 861, 863, 864, 869, 871, 874, 877, 880, 883,
+ 886, 889, 891, 895, 897, 901, 903, 907, 909, 913,
+ 915, 919, 921, 923, 927, 931, 932, 935, 936, 938,
+ 939, 941, 942, 944, 945, 947, 948, 950, 951, 953,
+ 954, 956, 957, 959, 960, 962, 967, 972, 978, 985,
+ 990, 995, 997, 999, 1001, 1003, 1005, 1007, 1009, 1011,
+ 1013, 1017, 1022, 1028, 1033, 1038, 1041, 1044, 1049, 1053,
+ 1057, 1063, 1067, 1072, 1076, 1082, 1084, 1085, 1087, 1091,
+ 1093, 1095, 1098, 1100, 1102, 1108, 1109, 1112, 1114, 1118,
+ 1120, 1124, 1126
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yytype_int16 yyrhs[] =
+{
+ 77, 0, -1, 79, 78, 81, 166, -1, -1, 25,
+ 141, 62, -1, -1, 80, 86, 88, -1, -1, 81,
+ 82, 62, -1, 21, 83, -1, 21, 59, 84, 190,
+ 60, -1, 21, 59, 60, -1, 85, 86, 88, -1,
+ 85, 88, -1, 83, -1, 84, 62, 83, -1, 3,
+ -1, 141, 3, -1, 63, 3, -1, 25, 24, 87,
+ 62, -1, -1, 24, -1, -1, 89, 214, 64, 64,
+ -1, -1, 91, -1, 158, -1, 181, -1, 1, -1,
+ 32, 93, -1, 32, 59, 167, 190, 60, -1, 32,
+ 59, 60, -1, 92, 94, -1, 92, 59, 94, 190,
+ 60, -1, 92, 59, 94, 62, 168, 190, 60, -1,
+ 92, 59, 60, -1, 31, 97, -1, 31, 59, 169,
+ 190, 60, -1, 31, 59, 60, -1, 9, -1, 185,
+ 146, -1, 185, 146, 65, 186, -1, 185, 65, 186,
+ -1, 185, 146, 65, 186, -1, 185, 65, 186, -1,
+ 94, -1, 185, 146, -1, 185, -1, 141, -1, 96,
+ 146, -1, 126, -1, 126, 4, 126, -1, 186, 65,
+ 186, -1, 186, 5, 186, -1, 126, 42, -1, 126,
+ 37, -1, 7, 187, 66, -1, 7, 187, 65, 126,
+ 66, -1, 7, 187, 5, 126, 66, -1, 12, 66,
+ -1, -1, 67, 101, 183, 68, -1, -1, 99, 103,
+ 183, -1, -1, 104, 102, -1, -1, 35, 106, 183,
+ 68, -1, 186, 65, 26, 126, -1, 186, 5, 26,
+ 126, -1, 26, 126, -1, 194, 62, 194, 62, 194,
+ -1, 194, -1, 107, -1, 108, 105, -1, -1, 16,
+ 111, 109, -1, 194, -1, 194, 62, 194, -1, -1,
+ -1, -1, 20, 114, 112, 115, 105, 116, 119, 120,
+ -1, -1, 14, 20, 118, 112, 105, -1, -1, 119,
+ 117, -1, -1, 14, 100, -1, -1, -1, 30, 122,
+ 112, 123, 35, 104, 68, -1, -1, 28, 125, 35,
+ 104, 68, -1, 127, -1, 126, 47, 126, -1, 126,
+ 33, 126, -1, 126, 38, 126, -1, 126, 46, 126,
+ -1, 126, 45, 126, -1, 126, 43, 126, -1, 126,
+ 39, 126, -1, 126, 40, 126, -1, 126, 49, 126,
+ -1, 126, 50, 126, -1, 126, 51, 126, -1, 126,
+ 52, 126, -1, 126, 53, 126, -1, 126, 54, 126,
+ -1, 126, 55, 126, -1, 126, 56, 126, -1, 126,
+ 34, 126, -1, 126, 44, 126, -1, 126, 48, 126,
+ -1, 126, 36, 126, -1, 134, -1, 53, 127, -1,
+ 56, 127, -1, 49, 127, -1, 50, 127, -1, 69,
+ 127, -1, 70, 127, -1, 52, 127, -1, 36, 127,
+ -1, 134, 59, 60, -1, 134, 59, 187, 191, 60,
+ -1, 134, 59, 187, 11, 191, 60, -1, 3, -1,
+ 143, -1, 134, 63, 141, -1, 134, 63, 59, 135,
+ 60, -1, 134, 63, 59, 31, 60, -1, 134, 71,
+ 126, 72, -1, 134, 71, 192, 66, 192, 72, -1,
+ 134, 71, 192, 66, 192, 66, 192, 72, -1, 128,
+ -1, 149, 59, 126, 191, 60, -1, 150, 137, 130,
+ 189, 68, -1, 129, 67, 130, 189, 68, -1, 59,
+ 135, 60, 67, 130, 189, 68, -1, 165, -1, -1,
+ 126, 66, 133, -1, 126, -1, 67, 130, 189, 68,
+ -1, 126, -1, 67, 130, 189, 68, -1, 129, -1,
+ 59, 135, 60, -1, 126, -1, 147, -1, 146, -1,
+ 35, -1, 67, -1, 141, -1, 141, -1, -1, 138,
+ -1, 24, -1, 142, -1, 73, -1, 74, 3, 63,
+ 24, -1, 74, 3, 63, 73, -1, 141, -1, 138,
+ -1, 11, -1, 11, 146, -1, 155, -1, 161, -1,
+ 153, -1, 154, -1, 152, -1, 59, 146, 60, -1,
+ 155, -1, 161, -1, 153, -1, 53, 147, -1, 161,
+ -1, 153, -1, 154, -1, 152, -1, 59, 146, 60,
+ -1, 161, -1, 153, -1, 153, -1, 155, -1, 161,
+ -1, 153, -1, 154, -1, 152, -1, 143, -1, 143,
+ 63, 141, -1, 71, 192, 72, 146, -1, 71, 11,
+ 72, 146, -1, 8, 148, -1, 8, 36, 146, -1,
+ 23, 71, 146, 72, 146, -1, 156, -1, 157, -1,
+ 53, 146, -1, 36, 8, 146, -1, 29, 137, 170,
+ 190, 68, -1, 29, 137, 68, -1, 22, 137, 171,
+ 190, 68, -1, 22, 137, 68, -1, 17, 159, 162,
+ -1, 141, 59, 179, 60, 163, -1, 59, 179, 60,
+ 141, 59, 179, 60, 163, -1, 200, 59, 195, 60,
+ 210, -1, 59, 215, 60, 141, 59, 195, 60, 210,
+ -1, 17, 59, 179, 60, 163, -1, -1, 67, 183,
+ 68, -1, -1, 151, -1, 59, 179, 60, -1, 161,
+ -1, 164, 137, 183, 68, -1, 164, 1, -1, -1,
+ 166, 90, 62, -1, 93, -1, 167, 62, 93, -1,
+ 95, -1, 168, 62, 95, -1, 97, -1, 169, 62,
+ 97, -1, 172, -1, 170, 62, 172, -1, 175, -1,
+ 171, 62, 175, -1, 184, 146, 198, -1, 174, 198,
+ -1, 59, 174, 60, 198, -1, 53, 174, 198, -1,
+ 59, 53, 174, 60, 198, -1, 53, 59, 174, 60,
+ 198, -1, 24, -1, 24, 63, 141, -1, 173, -1,
+ 138, 176, -1, 173, -1, 59, 173, 60, -1, 59,
+ 179, 60, 163, -1, 136, -1, 141, 136, -1, 141,
+ 145, -1, 145, -1, 177, -1, 178, 75, 177, -1,
+ -1, 178, 191, -1, -1, 100, -1, 91, -1, 181,
+ -1, 1, -1, 98, -1, 110, -1, 121, -1, 124,
+ -1, 113, -1, -1, 144, 66, 182, 180, -1, 15,
+ -1, 6, 140, -1, 10, 140, -1, 18, 128, -1,
+ 13, 128, -1, 19, 138, -1, 27, 193, -1, 180,
+ -1, 183, 62, 180, -1, 138, -1, 184, 75, 138,
+ -1, 139, -1, 185, 75, 139, -1, 126, -1, 186,
+ 75, 126, -1, 135, -1, 187, 75, 135, -1, 131,
+ -1, 132, -1, 188, 75, 131, -1, 188, 75, 132,
+ -1, -1, 188, 191, -1, -1, 62, -1, -1, 75,
+ -1, -1, 126, -1, -1, 186, -1, -1, 98, -1,
+ -1, 215, -1, -1, 216, -1, -1, 217, -1, -1,
+ 3, -1, 21, 24, 3, 62, -1, 32, 200, 202,
+ 62, -1, 9, 200, 65, 213, 62, -1, 9, 200,
+ 202, 65, 213, 62, -1, 31, 201, 202, 62, -1,
+ 17, 160, 162, 62, -1, 142, -1, 200, -1, 204,
+ -1, 205, -1, 206, -1, 204, -1, 206, -1, 142,
+ -1, 24, -1, 71, 72, 202, -1, 71, 3, 72,
+ 202, -1, 23, 71, 202, 72, 202, -1, 29, 67,
+ 196, 68, -1, 22, 67, 197, 68, -1, 53, 202,
+ -1, 8, 203, -1, 8, 59, 205, 60, -1, 8,
+ 36, 202, -1, 36, 8, 202, -1, 17, 59, 195,
+ 60, 210, -1, 141, 202, 198, -1, 141, 11, 202,
+ 198, -1, 141, 202, 198, -1, 141, 59, 195, 60,
+ 210, -1, 202, -1, -1, 211, -1, 59, 195, 60,
+ -1, 202, -1, 3, -1, 50, 3, -1, 141, -1,
+ 212, -1, 59, 212, 49, 212, 60, -1, -1, 214,
+ 199, -1, 207, -1, 215, 75, 207, -1, 208, -1,
+ 216, 62, 208, -1, 209, -1, 217, 62, 209, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const yytype_uint16 yyrline[] =
+{
+ 0, 124, 124, 133, 139, 150, 150, 165, 166, 169,
+ 170, 171, 174, 211, 222, 223, 226, 233, 240, 249,
+ 263, 264, 271, 271, 284, 288, 289, 293, 298, 304,
+ 308, 312, 316, 322, 328, 334, 339, 343, 347, 353,
+ 359, 363, 367, 373, 377, 383, 384, 388, 394, 403,
+ 409, 427, 432, 444, 460, 466, 474, 494, 512, 521,
+ 540, 539, 554, 553, 585, 588, 595, 594, 605, 611,
+ 618, 625, 636, 642, 645, 653, 652, 663, 669, 681,
+ 685, 690, 680, 711, 710, 723, 726, 732, 735, 747,
+ 751, 746, 769, 768, 784, 785, 789, 793, 797, 801,
+ 805, 809, 813, 817, 821, 825, 829, 833, 837, 841,
+ 845, 849, 853, 857, 862, 868, 869, 873, 884, 888,
+ 892, 896, 901, 905, 915, 919, 924, 932, 936, 937,
+ 948, 952, 956, 960, 964, 972, 973, 979, 986, 992,
+ 999, 1002, 1009, 1015, 1032, 1039, 1040, 1047, 1048, 1067,
+ 1068, 1071, 1074, 1078, 1089, 1098, 1104, 1107, 1110, 1117,
+ 1118, 1124, 1137, 1152, 1160, 1172, 1177, 1183, 1184, 1185,
+ 1186, 1187, 1188, 1194, 1195, 1196, 1197, 1203, 1204, 1205,
+ 1206, 1207, 1213, 1214, 1217, 1220, 1221, 1222, 1223, 1224,
+ 1227, 1228, 1241, 1245, 1250, 1255, 1260, 1264, 1265, 1268,
+ 1274, 1281, 1287, 1294, 1300, 1311, 1326, 1355, 1393, 1418,
+ 1436, 1445, 1448, 1456, 1460, 1464, 1471, 1477, 1482, 1494,
+ 1497, 1508, 1509, 1515, 1516, 1522, 1526, 1532, 1533, 1539,
+ 1543, 1549, 1572, 1577, 1583, 1589, 1596, 1605, 1614, 1629,
+ 1635, 1640, 1644, 1651, 1664, 1665, 1671, 1677, 1680, 1684,
+ 1690, 1693, 1702, 1705, 1706, 1710, 1711, 1717, 1718, 1719,
+ 1720, 1721, 1723, 1722, 1737, 1743, 1747, 1751, 1755, 1759,
+ 1764, 1783, 1789, 1797, 1801, 1807, 1811, 1817, 1821, 1827,
+ 1831, 1840, 1844, 1848, 1852, 1858, 1861, 1869, 1870, 1872,
+ 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894, 1897, 1900,
+ 1903, 1906, 1909, 1912, 1915, 1921, 1925, 1929, 1933, 1937,
+ 1941, 1961, 1968, 1979, 1980, 1981, 1984, 1985, 1988, 1992,
+ 2002, 2006, 2010, 2014, 2018, 2022, 2026, 2032, 2038, 2046,
+ 2054, 2060, 2067, 2083, 2105, 2109, 2115, 2118, 2121, 2125,
+ 2135, 2139, 2158, 2166, 2167, 2179, 2180, 2183, 2187, 2193,
+ 2197, 2203, 2207
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+const char *yytname[] =
+{
+ "$end", "error", "$undefined", "LLITERAL", "LASOP", "LCOLAS", "LBREAK",
+ "LCASE", "LCHAN", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER",
+ "LELSE", "LFALL", "LFOR", "LFUNC", "LGO", "LGOTO", "LIF", "LIMPORT",
+ "LINTERFACE", "LMAP", "LNAME", "LPACKAGE", "LRANGE", "LRETURN",
+ "LSELECT", "LSTRUCT", "LSWITCH", "LTYPE", "LVAR", "LANDAND", "LANDNOT",
+ "LBODY", "LCOMM", "LDEC", "LEQ", "LGE", "LGT", "LIGNORE", "LINC", "LLE",
+ "LLSH", "LLT", "LNE", "LOROR", "LRSH", "'+'", "'-'", "'|'", "'^'", "'*'",
+ "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'",
+ "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'",
+ "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file",
+ "package", "loadsys", "@1", "imports", "import", "import_stmt",
+ "import_stmt_list", "import_here", "import_package", "import_safety",
+ "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl",
+ "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case",
+ "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body",
+ "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6",
+ "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list",
+ "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr",
+ "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval",
+ "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
+ "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym",
+ "hidden_importsym", "name", "labelname", "dotdotdot", "ntype",
+ "non_expr_type", "non_recvchantype", "convtype", "comptype",
+ "fnret_type", "dotname", "othertype", "ptrtype", "recvchantype",
+ "structtype", "interfacetype", "xfndcl", "fndcl", "hidden_fndcl",
+ "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", "xdcl_list",
+ "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list",
+ "interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl",
+ "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt",
+ "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list",
+ "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list",
+ "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt",
+ "ohidden_funarg_list", "ohidden_structdcl_list",
+ "ohidden_interfacedcl_list", "oliteral", "hidden_import",
+ "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type",
+ "hidden_type_non_recv_chan", "hidden_type_misc", "hidden_type_recv_chan",
+ "hidden_type_func", "hidden_funarg", "hidden_structdcl",
+ "hidden_interfacedcl", "ohidden_funres", "hidden_funres",
+ "hidden_literal", "hidden_constant", "hidden_import_list",
+ "hidden_funarg_list", "hidden_structdcl_list",
+ "hidden_interfacedcl_list", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const yytype_uint16 yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 276, 277, 278, 279, 280, 281, 282, 283, 284,
+ 285, 286, 287, 288, 289, 290, 291, 292, 293, 294,
+ 295, 296, 297, 298, 299, 300, 301, 302, 303, 43,
+ 45, 124, 94, 42, 47, 37, 38, 304, 305, 40,
+ 41, 306, 59, 46, 36, 61, 58, 123, 125, 33,
+ 126, 91, 93, 63, 64, 44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const yytype_uint8 yyr1[] =
+{
+ 0, 76, 77, 78, 78, 80, 79, 81, 81, 82,
+ 82, 82, 83, 83, 84, 84, 85, 85, 85, 86,
+ 87, 87, 89, 88, 90, 90, 90, 90, 90, 91,
+ 91, 91, 91, 91, 91, 91, 91, 91, 91, 92,
+ 93, 93, 93, 94, 94, 95, 95, 95, 96, 97,
+ 98, 98, 98, 98, 98, 98, 99, 99, 99, 99,
+ 101, 100, 103, 102, 104, 104, 106, 105, 107, 107,
+ 107, 108, 108, 108, 109, 111, 110, 112, 112, 114,
+ 115, 116, 113, 118, 117, 119, 119, 120, 120, 122,
+ 123, 121, 125, 124, 126, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 127, 127, 127, 127, 127,
+ 127, 127, 127, 127, 128, 128, 128, 129, 129, 129,
+ 129, 129, 129, 129, 129, 129, 129, 129, 129, 129,
+ 129, 130, 131, 132, 132, 133, 133, 134, 134, 135,
+ 135, 136, 137, 137, 138, 139, 140, 140, 141, 141,
+ 141, 142, 142, 143, 144, 145, 145, 146, 146, 146,
+ 146, 146, 146, 147, 147, 147, 147, 148, 148, 148,
+ 148, 148, 149, 149, 150, 151, 151, 151, 151, 151,
+ 152, 152, 153, 153, 153, 153, 153, 153, 153, 154,
+ 155, 156, 156, 157, 157, 158, 159, 159, 160, 160,
+ 161, 162, 162, 163, 163, 163, 164, 165, 165, 166,
+ 166, 167, 167, 168, 168, 169, 169, 170, 170, 171,
+ 171, 172, 172, 172, 172, 172, 172, 173, 173, 174,
+ 175, 175, 175, 176, 177, 177, 177, 177, 178, 178,
+ 179, 179, 180, 180, 180, 180, 180, 181, 181, 181,
+ 181, 181, 182, 181, 181, 181, 181, 181, 181, 181,
+ 181, 183, 183, 184, 184, 185, 185, 186, 186, 187,
+ 187, 188, 188, 188, 188, 189, 189, 190, 190, 191,
+ 191, 192, 192, 193, 193, 194, 194, 195, 195, 196,
+ 196, 197, 197, 198, 198, 199, 199, 199, 199, 199,
+ 199, 200, 201, 202, 202, 202, 203, 203, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 205,
+ 206, 207, 207, 208, 209, 209, 210, 210, 211, 211,
+ 212, 212, 212, 213, 213, 214, 214, 215, 215, 216,
+ 216, 217, 217
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const yytype_uint8 yyr2[] =
+{
+ 0, 2, 4, 0, 3, 0, 3, 0, 3, 2,
+ 5, 3, 3, 2, 1, 3, 1, 2, 2, 4,
+ 0, 1, 0, 4, 0, 1, 1, 1, 1, 2,
+ 5, 3, 2, 5, 7, 3, 2, 5, 3, 1,
+ 2, 4, 3, 4, 3, 1, 2, 1, 1, 2,
+ 1, 3, 3, 3, 2, 2, 3, 5, 5, 2,
+ 0, 4, 0, 3, 0, 2, 0, 4, 4, 4,
+ 2, 5, 1, 1, 2, 0, 3, 1, 3, 0,
+ 0, 0, 8, 0, 5, 0, 2, 0, 2, 0,
+ 0, 7, 0, 5, 1, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 5, 6, 1, 1, 3,
+ 5, 5, 4, 6, 8, 1, 5, 5, 5, 7,
+ 1, 0, 3, 1, 4, 1, 4, 1, 3, 1,
+ 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
+ 1, 4, 4, 1, 1, 1, 2, 1, 1, 1,
+ 1, 1, 3, 1, 1, 1, 2, 1, 1, 1,
+ 1, 3, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 3, 4, 4, 2, 3, 5, 1, 1, 2,
+ 3, 5, 3, 5, 3, 3, 5, 8, 5, 8,
+ 5, 0, 3, 0, 1, 3, 1, 4, 2, 0,
+ 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 3, 2, 4, 3, 5, 5, 1, 3, 1,
+ 2, 1, 3, 4, 1, 2, 2, 1, 1, 3,
+ 0, 2, 0, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0, 4, 1, 2, 2, 2, 2, 2,
+ 2, 1, 3, 1, 3, 1, 3, 1, 3, 1,
+ 3, 1, 1, 3, 3, 0, 2, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 4, 4, 5, 6, 4,
+ 4, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 4, 5, 4, 4, 2, 2, 4, 3, 3,
+ 5, 3, 4, 3, 5, 1, 0, 1, 3, 1,
+ 1, 2, 1, 1, 5, 0, 2, 1, 3, 1,
+ 3, 1, 3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const yytype_uint16 yydefact[] =
+{
+ 5, 0, 3, 0, 1, 0, 7, 0, 22, 158,
+ 160, 0, 0, 159, 219, 20, 6, 345, 0, 4,
+ 0, 0, 0, 21, 0, 0, 0, 16, 0, 0,
+ 9, 22, 0, 8, 28, 127, 156, 0, 39, 156,
+ 0, 264, 75, 0, 0, 0, 79, 0, 0, 293,
+ 92, 0, 89, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 291, 0, 25, 0, 257, 258,
+ 261, 259, 260, 50, 94, 135, 147, 115, 164, 163,
+ 128, 0, 0, 0, 184, 197, 198, 26, 216, 0,
+ 140, 27, 0, 19, 0, 0, 0, 0, 0, 0,
+ 346, 161, 162, 11, 14, 287, 18, 22, 13, 17,
+ 157, 265, 154, 0, 0, 0, 0, 163, 190, 194,
+ 180, 178, 179, 177, 266, 135, 0, 295, 250, 0,
+ 211, 135, 269, 295, 152, 153, 0, 0, 277, 294,
+ 270, 0, 0, 295, 0, 0, 36, 48, 0, 29,
+ 275, 155, 0, 123, 118, 119, 122, 116, 117, 0,
+ 0, 149, 0, 150, 175, 173, 174, 120, 121, 0,
+ 292, 0, 220, 0, 32, 0, 0, 0, 0, 0,
+ 55, 0, 0, 0, 54, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 141,
+ 0, 0, 291, 262, 0, 141, 218, 0, 0, 0,
+ 0, 311, 0, 0, 211, 0, 0, 312, 0, 0,
+ 23, 288, 0, 12, 250, 0, 0, 195, 171, 169,
+ 170, 167, 168, 199, 0, 0, 0, 296, 73, 0,
+ 76, 0, 72, 165, 244, 163, 247, 151, 248, 289,
+ 0, 250, 0, 205, 80, 77, 158, 0, 204, 0,
+ 287, 241, 229, 0, 64, 0, 0, 202, 273, 287,
+ 227, 239, 303, 0, 90, 38, 225, 287, 49, 31,
+ 221, 287, 0, 0, 40, 0, 176, 148, 0, 0,
+ 35, 287, 0, 0, 51, 96, 111, 114, 97, 101,
+ 102, 100, 112, 99, 98, 95, 113, 103, 104, 105,
+ 106, 107, 108, 109, 110, 285, 124, 279, 289, 0,
+ 129, 292, 0, 0, 289, 285, 256, 60, 254, 253,
+ 271, 255, 0, 53, 52, 278, 0, 0, 0, 0,
+ 319, 0, 0, 0, 0, 0, 318, 0, 313, 314,
+ 315, 0, 347, 0, 0, 297, 0, 0, 0, 15,
+ 10, 0, 0, 0, 181, 191, 70, 66, 74, 0,
+ 0, 295, 166, 245, 246, 290, 251, 213, 0, 0,
+ 0, 295, 0, 237, 0, 250, 240, 288, 0, 0,
+ 0, 0, 303, 0, 0, 288, 0, 304, 232, 0,
+ 303, 0, 288, 0, 288, 0, 42, 276, 0, 0,
+ 0, 200, 171, 169, 170, 168, 141, 193, 192, 288,
+ 0, 44, 0, 141, 143, 281, 282, 289, 0, 289,
+ 290, 0, 0, 0, 132, 291, 263, 290, 0, 0,
+ 0, 0, 217, 0, 0, 326, 316, 317, 297, 301,
+ 0, 299, 0, 325, 340, 0, 0, 342, 343, 0,
+ 0, 0, 0, 0, 303, 0, 0, 310, 0, 298,
+ 305, 309, 306, 213, 172, 0, 0, 0, 0, 249,
+ 250, 163, 214, 189, 187, 188, 185, 186, 210, 213,
+ 212, 81, 78, 238, 242, 0, 230, 203, 196, 0,
+ 0, 93, 62, 65, 0, 234, 0, 303, 228, 201,
+ 274, 231, 64, 226, 37, 222, 30, 41, 0, 285,
+ 45, 223, 287, 47, 33, 43, 285, 0, 290, 286,
+ 138, 0, 280, 125, 131, 130, 0, 136, 137, 0,
+ 272, 328, 0, 0, 319, 0, 318, 0, 335, 351,
+ 302, 0, 0, 0, 349, 300, 329, 341, 0, 307,
+ 0, 320, 0, 303, 331, 0, 348, 336, 0, 69,
+ 68, 295, 0, 250, 206, 85, 213, 0, 59, 0,
+ 303, 303, 233, 0, 172, 0, 288, 0, 46, 0,
+ 141, 145, 142, 283, 284, 126, 291, 133, 61, 327,
+ 336, 297, 324, 0, 0, 303, 323, 0, 0, 321,
+ 308, 332, 297, 297, 339, 208, 337, 67, 71, 215,
+ 0, 87, 243, 0, 0, 56, 0, 63, 236, 235,
+ 91, 139, 224, 34, 144, 285, 0, 330, 0, 352,
+ 322, 333, 350, 0, 0, 0, 213, 0, 86, 82,
+ 0, 0, 0, 134, 336, 344, 336, 338, 207, 83,
+ 88, 58, 57, 146, 334, 209, 295, 0, 84
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yytype_int16 yydefgoto[] =
+{
+ -1, 1, 6, 2, 3, 14, 21, 30, 105, 31,
+ 8, 24, 16, 17, 65, 328, 67, 149, 520, 521,
+ 145, 146, 68, 502, 329, 440, 503, 579, 390, 368,
+ 475, 238, 239, 240, 69, 127, 254, 70, 133, 380,
+ 575, 648, 666, 621, 649, 71, 143, 401, 72, 141,
+ 73, 74, 75, 76, 315, 425, 426, 592, 77, 317,
+ 244, 136, 78, 150, 111, 117, 13, 80, 81, 246,
+ 247, 163, 119, 82, 83, 482, 228, 84, 230, 231,
+ 85, 86, 87, 130, 214, 88, 253, 488, 89, 90,
+ 22, 281, 522, 277, 269, 260, 270, 271, 272, 262,
+ 386, 248, 249, 250, 330, 331, 323, 332, 273, 152,
+ 92, 318, 427, 428, 222, 376, 171, 140, 255, 468,
+ 553, 547, 398, 100, 212, 218, 614, 445, 348, 349,
+ 350, 352, 554, 549, 615, 616, 458, 459, 25, 469,
+ 555, 550
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -473
+static const yytype_int16 yypact[] =
+{
+ -473, 65, 22, 49, -473, 261, -473, 64, -473, -473,
+ -473, 95, 52, -473, 143, 145, -473, -473, 104, -473,
+ 68, 128, 1049, -473, 142, 305, 16, -473, 56, 204,
+ -473, 49, 220, -473, -473, -473, 261, 974, -473, 261,
+ 562, -473, -473, 288, 562, 261, -473, 14, 147, 1615,
+ -473, 14, -473, 395, 401, 1615, 1615, 1615, 1615, 1615,
+ 1615, 1658, 1615, 1615, 737, 168, -473, 414, -473, -473,
+ -473, -473, -473, 649, -473, -473, 165, 122, -473, 169,
+ -473, 177, 218, 14, 219, -473, -473, -473, 235, 89,
+ -473, -473, 34, -473, 206, 124, 286, 206, 206, 260,
+ -473, -473, -473, -473, -473, 265, -473, -473, -473, -473,
+ -473, -473, -473, 270, 1803, 1803, 1803, -473, 269, -473,
+ -473, -473, -473, -473, -473, 39, 122, 882, 1777, 283,
+ 277, 230, -473, 1615, -473, -473, 292, 1803, 2097, 280,
+ -473, 332, 315, 1615, 215, 1803, -473, -473, 244, -473,
+ -473, -473, 949, -473, -473, -473, -473, -473, -473, 1701,
+ 1658, 2097, 298, -473, 9, -473, 59, -473, -473, 303,
+ 2097, 319, -473, 330, -473, 1744, 1615, 1615, 1615, 1615,
+ -473, 1615, 1615, 1615, -473, 1615, 1615, 1615, 1615, 1615,
+ 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, 1615, -473,
+ 1297, 455, 1615, -473, 1615, -473, -473, 1225, 1615, 1615,
+ 1615, -473, 594, 261, 277, 328, 403, -473, 1308, 1308,
+ -473, 152, 352, -473, 1777, 405, 1803, -473, -473, -473,
+ -473, -473, -473, -473, 354, 261, 1615, -473, -473, 382,
+ -473, 47, 360, 1803, -473, 1777, -473, -473, -473, 351,
+ 367, 1777, 1225, -473, -473, 366, 84, 407, -473, 374,
+ 373, -473, -473, 372, -473, 138, 42, -473, -473, 377,
+ -473, -473, 442, 1769, -473, -473, -473, 384, -473, -473,
+ -473, 389, 1615, 261, 391, 1830, -473, 394, 1803, 1803,
+ -473, 409, 1615, 411, 2097, 1935, -473, 2121, 1080, 1080,
+ 1080, 1080, -473, 1080, 1080, 2145, -473, 503, 503, 503,
+ 503, -473, -473, -473, -473, 1352, -473, -473, 27, 1407,
+ -473, 1995, 412, 1147, 1962, 1352, -473, -473, -473, -473,
+ -473, -473, 7, 280, 280, 2097, 698, 418, 415, 413,
+ -473, 416, 477, 1308, 188, 31, -473, 425, -473, -473,
+ -473, 1897, -473, 221, 433, 261, 434, 436, 439, -473,
+ -473, 432, 1803, 452, -473, -473, 2097, -473, -473, 1462,
+ 1517, 1615, -473, -473, -473, 1777, -473, 1856, 453, 91,
+ 382, 1615, 261, 454, 456, 1777, -473, 475, 451, 1803,
+ 133, 407, 442, 407, 460, 326, 462, -473, -473, 261,
+ 442, 467, 261, 478, 261, 486, 280, -473, 1615, 1864,
+ 1803, -473, 26, 248, 264, 430, -473, -473, -473, 261,
+ 492, 280, 1615, -473, 2025, -473, -473, 485, 493, 487,
+ 1658, 504, 506, 508, -473, 1615, -473, -473, 512, 505,
+ 1225, 1147, -473, 1308, 517, -473, -473, -473, 261, 1889,
+ 1308, 261, 1308, -473, -473, 571, 155, -473, -473, 514,
+ 509, 1308, 188, 1308, 442, 261, 261, -473, 518, 507,
+ -473, -473, -473, 1856, -473, 1225, 1615, 1615, 521, -473,
+ 1777, 528, -473, -473, -473, -473, -473, -473, -473, 1856,
+ -473, -473, -473, -473, -473, 520, -473, -473, -473, 1658,
+ 522, -473, -473, -473, 530, -473, 532, 442, -473, -473,
+ -473, -473, -473, -473, -473, -473, -473, 280, 535, 1352,
+ -473, -473, 536, 1744, -473, 280, 1352, 1560, 1352, -473,
+ -473, 539, -473, -473, -473, -473, 247, -473, -473, 308,
+ -473, -473, 541, 543, 545, 546, 547, 544, -473, -473,
+ 551, 548, 1308, 554, -473, 557, -473, -473, 576, -473,
+ 1308, -473, 564, 442, -473, 568, -473, 1923, 318, 2097,
+ 2097, 1615, 569, 1777, -473, -473, 1856, 156, -473, 1147,
+ 442, 442, -473, 243, 483, 563, 261, 577, 411, 570,
+ -473, 2097, -473, -473, -473, -473, 1615, -473, -473, -473,
+ 1923, 261, -473, 1889, 1308, 442, -473, 261, 155, -473,
+ -473, -473, 261, 261, -473, -473, -473, -473, -473, -473,
+ 579, 627, -473, 1615, 1615, -473, 1658, 580, -473, -473,
+ -473, -473, -473, -473, -473, 1352, 572, -473, 583, -473,
+ -473, -473, -473, 585, 586, 590, 1856, 77, -473, -473,
+ 2049, 2073, 584, -473, 1923, -473, 1923, -473, -473, -473,
+ -473, -473, -473, -473, -473, -473, 1615, 382, -473
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yytype_int16 yypgoto[] =
+{
+ -473, -473, -473, -473, -473, -473, -473, -12, -473, -473,
+ 624, -473, -1, -473, -473, 635, -473, -137, -48, 74,
+ -473, -130, -112, -473, 11, -473, -473, -473, 149, -372,
+ -473, -473, -473, -473, -473, -473, -140, -473, -473, -473,
+ -473, -473, -473, -473, -473, -473, -473, -473, -473, -473,
+ 662, 448, 257, -473, -196, 135, 139, -473, 262, -59,
+ 424, -16, -3, 387, 632, 427, 313, 20, -473, 428,
+ -89, 524, -473, -473, -473, -473, -36, -37, -31, -49,
+ -473, -473, -473, -473, -473, -32, 458, -472, -473, -473,
+ -473, -473, -473, -473, -473, -473, 279, -108, -211, 290,
+ -473, 306, -473, -214, -291, 658, -473, -230, -473, -63,
+ -6, 191, -473, -302, -219, -254, -195, -473, -107, -435,
+ -473, -473, -347, -473, 323, -473, 72, -473, 371, 268,
+ 380, 242, 102, 110, -468, -473, -438, 255, -473, 515,
+ -473, -473
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -278
+static const yytype_int16 yytable[] =
+{
+ 121, 120, 162, 274, 175, 123, 122, 322, 491, 325,
+ 361, 280, 165, 543, 276, 237, 104, 574, 558, 174,
+ 242, 237, 379, 439, 164, 227, 233, 234, 261, 166,
+ 108, 237, 436, 110, 460, 142, 110, 378, 429, 208,
+ 101, 388, 132, 139, -184, 505, -268, 5, 263, 134,
+ 396, -268, 369, 511, 392, 394, 278, 118, 403, 27,
+ -216, -180, 405, 284, 431, 4, 383, 205, -183, 441,
+ 438, 27, 420, 207, 7, 442, -184, 229, 229, 229,
+ 9, 135, 232, 232, 232, -180, 293, -237, 15, 102,
+ 206, 229, 9, -180, -216, 393, 232, 659, 18, 209,
+ 229, -268, 430, 461, 622, 232, 223, -268, 229, 210,
+ 175, 165, 370, 232, 19, 229, 103, 564, -182, 29,
+ 232, 241, 210, 164, 134, 291, -216, 28, 166, 10,
+ 11, 29, 637, 259, 118, 118, 118, 363, 229, 268,
+ 499, 10, 11, 232, 327, 500, -237, 382, 118, 384,
+ 540, 165, -237, 441, 372, 27, 135, 118, 454, 490,
+ 582, 623, 383, 164, 20, 118, 638, 26, 166, 23,
+ 643, 495, 118, 529, 658, 531, 9, 644, 645, 9,
+ 504, 200, 506, 213, 400, 201, 664, 229, 665, 229,
+ 33, 454, 232, 202, 232, 118, 411, 391, 11, 417,
+ 418, 501, 333, 334, 93, 455, 229, 106, 229, 359,
+ 539, 232, 9, 232, 229, 29, 611, 585, 137, 232,
+ 519, 624, 625, 109, 589, 10, 11, 526, 10, 11,
+ 172, 626, 199, 628, 629, -154, 229, -267, 455, 9,
+ 536, 232, -267, 203, 118, 568, 118, 456, 413, 412,
+ 499, 229, 229, 415, 414, 500, 232, 232, 641, 237,
+ 433, 10, 11, 118, 478, 118, 572, 515, 9, 237,
+ 165, 118, 513, 411, 492, 275, 406, 204, -183, 261,
+ 11, 465, 164, -178, 347, 9, 421, 166, 10, 11,
+ 357, 358, -267, 118, -182, 668, 466, 125, -267, -179,
+ 498, 131, 126, 587, 279, 118, 126, -178, 118, 118,
+ 216, 630, 9, 596, 94, -178, 256, 10, 11, 597,
+ 227, 518, 95, -179, 220, 229, 96, 221, 486, 224,
+ 232, -179, 235, 652, 10, 11, 97, 98, 229, 256,
+ 484, 483, 251, 232, 252, 487, 485, 128, 229, 627,
+ 256, 257, 229, 232, 9, 210, 523, 232, 287, 620,
+ 258, 10, 11, 333, 334, 10, 11, 264, 265, 99,
+ 441, 532, 229, 229, 266, 288, 598, 232, 232, 265,
+ 441, 165, 118, 267, 259, 266, 617, 355, 10, 11,
+ 290, 289, 268, 164, 635, 118, 510, 118, 166, 10,
+ 11, 636, 517, 10, 11, 118, 356, 211, 211, 118,
+ 211, 211, 360, 362, 364, 453, 525, 367, 215, 9,
+ 217, 219, 371, 464, 486, 9, 375, 377, 381, 118,
+ 118, 383, 12, 385, 588, 387, 484, 483, 9, 395,
+ 486, 487, 485, 229, 389, 397, 402, 32, 232, 79,
+ 165, 404, 484, 483, 144, 32, 408, 487, 485, 237,
+ 148, 416, 164, 112, 618, -177, 112, 166, 10, 11,
+ 129, 419, 112, 173, 10, 11, 422, 448, 435, 9,
+ 147, 151, 449, 451, 450, 452, 229, 10, 11, -177,
+ 462, 232, 473, 118, 151, 467, 470, -177, 471, 256,
+ 118, 472, 512, 153, 154, 155, 156, 157, 158, 118,
+ 167, 168, 474, 489, 319, 541, 494, 382, -181, 497,
+ 507, 548, 551, 523, 556, 346, 667, 486, 10, 11,
+ 509, 346, 346, 561, 257, 563, 229, 178, 514, 484,
+ 483, 232, -181, 118, 487, 485, 516, 186, 10, 11,
+ -181, 190, 524, 342, 237, 245, 195, 196, 197, 198,
+ 528, 530, 437, 112, 533, 35, 534, 532, 535, 112,
+ 37, 147, 537, 538, 557, 151, 559, 165, 567, 113,
+ 576, 560, 466, 571, 47, 48, 9, 573, 578, 164,
+ 580, 51, 581, 118, 166, 584, 118, 486, 586, 595,
+ 151, 599, 336, 600, -158, 601, -159, 153, 157, 484,
+ 483, 337, 602, 603, 487, 485, 338, 339, 340, 607,
+ 604, 61, 606, 341, 605, 608, 610, 612, 320, 619,
+ 342, 631, 609, 64, 79, 10, 11, 633, 634, 646,
+ 351, 647, 441, 654, 653, 655, 656, 343, 32, 346,
+ 657, 245, 663, 176, -277, 107, 346, 66, 660, 344,
+ 632, 583, 365, 593, 346, 345, 118, 594, 11, 373,
+ 407, 124, 354, 374, 508, 548, 640, 496, 245, 79,
+ 91, 479, 177, 178, 286, 179, 180, 181, 182, 183,
+ 577, 184, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 336, 446, 566, 642,
+ 151, 138, 542, 639, -277, 337, 447, 562, 0, 0,
+ 338, 339, 340, 161, -277, 0, 170, 341, 353, 0,
+ 0, 0, 0, 0, 443, 0, 0, 0, 0, 0,
+ 35, 0, 0, 0, 0, 37, 0, 0, 169, 0,
+ 79, 343, 0, 0, 113, 0, 346, 444, 0, 47,
+ 48, 9, 546, 346, 0, 346, 51, 0, 0, 345,
+ 0, 457, 11, 55, 346, 0, 346, 0, 0, 0,
+ 0, 0, 351, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 0, 61, 0, 0, 0,
+ 0, 0, 245, 0, 481, 0, 62, 63, 64, 493,
+ 10, 11, 245, 0, 112, 0, 0, 0, 0, 0,
+ 0, 0, 112, 0, 0, 0, 112, 0, 0, 147,
+ 0, 151, 0, 0, 0, 0, 0, 0, 294, 295,
+ 296, 297, 0, 298, 299, 300, 151, 301, 302, 303,
+ 304, 305, 306, 307, 308, 309, 310, 311, 312, 313,
+ 314, 0, 161, 0, 321, 346, 324, 79, 79, 0,
+ 138, 138, 335, 346, 0, 351, 545, 0, 552, 0,
+ 346, 0, 0, 457, 0, 35, 0, 0, 0, 457,
+ 37, 0, 565, 351, 0, 0, 0, 0, 366, 113,
+ 0, 0, 79, 0, 47, 48, 9, 245, 236, 0,
+ 0, 51, 0, 346, 0, 0, 546, 346, 55, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 59, 0, 0, 60, 0,
+ 0, 61, 0, 0, 138, 0, 0, 0, 0, 0,
+ 0, 62, 63, 64, 138, 10, 11, 37, 0, 0,
+ 0, 0, 0, 0, 0, 0, 113, 346, 0, 346,
+ 0, 47, 48, 9, 0, 0, 0, 424, 51, 0,
+ 0, 161, 37, 0, 0, 225, 0, 424, 0, 0,
+ 0, 113, 0, 0, 0, 0, 47, 48, 9, 0,
+ 245, 0, 115, 51, 0, 0, 79, 0, 226, 0,
+ 114, 0, 0, 151, 282, 0, 0, 0, 0, 0,
+ 64, 0, 10, 11, 283, 0, 0, 115, 351, 0,
+ 545, 138, 138, 116, 552, 457, 0, 0, 0, 351,
+ 351, 0, 0, 0, 0, 64, 0, 10, 11, -2,
+ 34, 0, 35, 0, 0, 36, 0, 37, 38, 39,
+ 0, 0, 40, 0, 41, 42, 43, 44, 45, 46,
+ 138, 47, 48, 9, 0, 0, 49, 50, 51, 52,
+ 53, 54, 0, 0, 138, 55, 0, 0, 0, 0,
+ 0, 0, 161, 0, 0, 0, 0, 170, 56, 57,
+ 0, 58, 59, 0, 0, 60, 0, 0, 61, 0,
+ 0, -24, 0, 0, 178, 0, 0, 0, 62, 63,
+ 64, 0, 10, 11, 186, 0, 0, 0, 190, 191,
+ 192, 193, 194, 195, 196, 197, 198, 0, 569, 570,
+ 0, 0, 0, 0, 0, 0, 0, 0, 326, 0,
+ 35, 0, 0, 36, -252, 37, 38, 39, 0, -252,
+ 40, 161, 41, 42, 113, 44, 45, 46, 0, 47,
+ 48, 9, 0, 0, 49, 50, 51, 52, 53, 54,
+ 0, 424, 0, 55, 0, 0, 0, 0, 424, 591,
+ 424, 0, 0, 0, 0, 0, 56, 57, 0, 58,
+ 59, 0, 0, 60, 0, 0, 61, 0, 0, -252,
+ 0, 0, 0, 0, 327, -252, 62, 63, 64, 0,
+ 10, 11, 0, 0, 0, 0, 326, 0, 35, 0,
+ 0, 36, 0, 37, 38, 39, 0, 0, 40, 0,
+ 41, 42, 113, 44, 45, 46, 0, 47, 48, 9,
+ 0, 0, 49, 50, 51, 52, 53, 54, 170, 0,
+ 0, 55, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 56, 57, 0, 58, 59, 0,
+ 0, 60, 0, 0, 61, 650, 651, -252, 161, 0,
+ 0, 0, 327, -252, 62, 63, 64, 424, 10, 11,
+ 35, 0, 0, 0, 0, 37, 0, 0, 0, 0,
+ 0, 0, 0, 0, 113, 0, 336, 0, 0, 47,
+ 48, 9, 0, 0, 0, 337, 51, 0, 0, 0,
+ 338, 339, 340, 159, 0, 0, 0, 341, 0, 0,
+ 0, 0, 0, 0, 342, 0, 56, 57, 0, 58,
+ 160, 0, 0, 60, 0, 35, 61, 316, 0, 0,
+ 37, 343, 0, 0, 0, 0, 62, 63, 64, 113,
+ 10, 11, 0, 0, 47, 48, 9, 0, 0, 345,
+ 0, 51, 11, 0, 0, 0, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 59, 0, 0, 60, 0,
+ 35, 61, 0, 0, 0, 37, 0, 0, 0, 423,
+ 0, 62, 63, 64, 113, 10, 11, 0, 0, 47,
+ 48, 9, 0, 0, 0, 0, 51, 0, 432, 0,
+ 0, 0, 0, 159, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 56, 57, 0, 58,
+ 160, 0, 0, 60, 0, 35, 61, 0, 0, 0,
+ 37, 0, 0, 0, 0, 0, 62, 63, 64, 113,
+ 10, 11, 0, 0, 47, 48, 9, 0, 476, 0,
+ 0, 51, 0, 0, 0, 0, 0, 0, 55, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 56, 57, 0, 58, 59, 0, 0, 60, 0,
+ 35, 61, 0, 0, 0, 37, 0, 0, 0, 0,
+ 0, 62, 63, 64, 113, 10, 11, 0, 0, 47,
+ 48, 9, 0, 477, 0, 0, 51, 0, 0, 0,
+ 0, 0, 0, 55, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 35, 0, 0, 56, 57, 37, 58,
+ 59, 0, 0, 60, 0, 0, 61, 113, 0, 0,
+ 0, 0, 47, 48, 9, 0, 62, 63, 64, 51,
+ 10, 11, 0, 0, 0, 0, 55, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 56,
+ 57, 0, 58, 59, 0, 0, 60, 0, 35, 61,
+ 0, 0, 0, 37, 0, 0, 0, 590, 0, 62,
+ 63, 64, 113, 10, 11, 0, 0, 47, 48, 9,
+ 0, 0, 0, 0, 51, 0, 0, 0, 0, 0,
+ 0, 55, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 35, 0, 0, 56, 57, 37, 58, 59, 0,
+ 0, 60, 0, 0, 61, 113, 0, 0, 0, 0,
+ 47, 48, 9, 0, 62, 63, 64, 51, 10, 11,
+ 0, 0, 0, 0, 159, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 35, 0, 0, 56, 57, 285,
+ 58, 160, 0, 0, 60, 0, 0, 61, 113, 0,
+ 0, 0, 0, 47, 48, 9, 0, 62, 63, 64,
+ 51, 10, 11, 0, 0, 0, 0, 55, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 56, 57, 37, 58, 59, 0, 0, 60, 0, 0,
+ 61, 113, 0, 0, 0, 0, 47, 48, 9, 0,
+ 62, 63, 64, 51, 10, 11, 0, 37, 0, 0,
+ 225, 0, 0, 0, 0, 37, 113, 0, 243, 0,
+ 0, 47, 48, 9, 113, 0, 0, 115, 51, 47,
+ 48, 9, 0, 226, 0, 225, 51, 0, 0, 292,
+ 0, 37, 0, 225, 0, 64, 0, 10, 11, 283,
+ 113, 0, 115, 0, 0, 47, 48, 9, 226, 0,
+ 115, 0, 51, 0, 0, 0, 226, 0, 37, 225,
+ 64, 0, 10, 11, 399, 0, 0, 113, 64, 0,
+ 10, 11, 47, 48, 9, 0, 115, 0, 0, 51,
+ 0, 0, 226, 0, 37, 0, 409, 0, 0, 0,
+ 0, 0, 285, 113, 64, 0, 10, 11, 47, 48,
+ 9, 113, 0, 115, 0, 51, 47, 48, 9, 410,
+ 0, 0, 225, 51, 0, 0, 0, 336, 0, 0,
+ 225, 64, 0, 10, 11, 336, 337, 0, 463, 115,
+ 0, 338, 339, 544, 337, 480, 0, 115, 341, 338,
+ 339, 340, 0, 226, 0, 342, 341, 64, 0, 10,
+ 11, 336, 0, 342, 0, 64, 0, 10, 11, 0,
+ 337, 0, 343, 0, 0, 338, 339, 340, 0, 0,
+ 343, 0, 341, 0, 0, 0, 0, 0, 0, 342,
+ 345, 0, 10, 11, 0, 0, 0, 0, 345, 178,
+ 0, 11, 0, 181, 182, 183, 343, 0, 185, 186,
+ 187, 188, 613, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 0, 0, 345, 177, 178, 11, 179, 0,
+ 181, 182, 183, 0, 0, 185, 186, 187, 188, 189,
+ 190, 191, 192, 193, 194, 195, 196, 197, 198, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 177, 178,
+ 0, 179, 0, 181, 182, 183, 0, 437, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 0, 0, 0, 0, 0, 0, 177, 178,
+ 0, 179, 0, 181, 182, 183, 0, 434, 185, 186,
+ 187, 188, 189, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198, 177, 178, 0, 179, 0, 181, 182, 183,
+ 0, 527, 185, 186, 187, 188, 189, 190, 191, 192,
+ 193, 194, 195, 196, 197, 198, 177, 178, 0, 179,
+ 0, 181, 182, 183, 0, 661, 185, 186, 187, 188,
+ 189, 190, 191, 192, 193, 194, 195, 196, 197, 198,
+ 177, 178, 0, 179, 0, 181, 182, 183, 0, 662,
+ 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+ 195, 196, 197, 198, 177, 178, 0, 0, 0, 181,
+ 182, 183, 0, 0, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198, 177, 178,
+ 0, 0, 0, 181, 182, 183, 0, 0, 185, 186,
+ 187, 188, 0, 190, 191, 192, 193, 194, 195, 196,
+ 197, 198
+};
+
+static const yytype_int16 yycheck[] =
+{
+ 37, 37, 61, 143, 67, 37, 37, 202, 380, 205,
+ 224, 148, 61, 448, 144, 127, 28, 489, 456, 67,
+ 127, 133, 252, 325, 61, 114, 115, 116, 136, 61,
+ 31, 143, 323, 36, 3, 51, 39, 251, 11, 5,
+ 24, 260, 45, 49, 35, 392, 7, 25, 137, 35,
+ 269, 12, 5, 400, 265, 266, 145, 37, 277, 3,
+ 1, 35, 281, 152, 318, 0, 24, 83, 59, 62,
+ 324, 3, 291, 89, 25, 68, 67, 114, 115, 116,
+ 24, 67, 114, 115, 116, 59, 175, 3, 24, 73,
+ 1, 128, 24, 67, 35, 53, 128, 20, 3, 65,
+ 137, 62, 75, 72, 576, 137, 107, 68, 145, 75,
+ 173, 160, 65, 145, 62, 152, 60, 464, 59, 63,
+ 152, 127, 75, 160, 35, 173, 67, 59, 160, 73,
+ 74, 63, 600, 136, 114, 115, 116, 226, 175, 142,
+ 7, 73, 74, 175, 67, 12, 62, 63, 128, 257,
+ 441, 200, 68, 62, 243, 3, 67, 137, 3, 68,
+ 507, 5, 24, 200, 21, 145, 601, 63, 200, 24,
+ 608, 385, 152, 427, 646, 429, 24, 612, 613, 24,
+ 391, 59, 393, 59, 273, 63, 654, 224, 656, 226,
+ 62, 3, 224, 71, 226, 175, 285, 59, 74, 288,
+ 289, 68, 208, 209, 62, 50, 243, 3, 245, 221,
+ 440, 243, 24, 245, 251, 63, 563, 519, 71, 251,
+ 416, 65, 66, 3, 526, 73, 74, 423, 73, 74,
+ 62, 75, 67, 580, 581, 66, 273, 7, 50, 24,
+ 435, 273, 12, 66, 224, 475, 226, 59, 285, 285,
+ 7, 288, 289, 285, 285, 12, 288, 289, 605, 371,
+ 319, 73, 74, 243, 371, 245, 480, 404, 24, 381,
+ 319, 251, 402, 362, 381, 60, 282, 59, 59, 387,
+ 74, 60, 319, 35, 212, 24, 292, 319, 73, 74,
+ 218, 219, 62, 273, 59, 667, 75, 40, 68, 35,
+ 389, 44, 40, 522, 60, 285, 44, 59, 288, 289,
+ 24, 68, 24, 66, 9, 67, 24, 73, 74, 72,
+ 409, 410, 17, 59, 64, 362, 21, 62, 377, 59,
+ 362, 67, 63, 635, 73, 74, 31, 32, 375, 24,
+ 377, 377, 59, 375, 67, 377, 377, 59, 385, 579,
+ 24, 59, 389, 385, 24, 75, 419, 389, 60, 573,
+ 68, 73, 74, 369, 370, 73, 74, 35, 53, 64,
+ 62, 430, 409, 410, 59, 72, 68, 409, 410, 53,
+ 62, 430, 362, 68, 387, 59, 68, 59, 73, 74,
+ 60, 72, 395, 430, 590, 375, 399, 377, 430, 73,
+ 74, 596, 408, 73, 74, 385, 3, 94, 95, 389,
+ 97, 98, 60, 8, 60, 343, 422, 35, 95, 24,
+ 97, 98, 62, 351, 473, 24, 75, 60, 62, 409,
+ 410, 24, 5, 59, 523, 62, 473, 473, 24, 62,
+ 489, 473, 473, 480, 72, 3, 62, 20, 480, 22,
+ 499, 62, 489, 489, 59, 28, 65, 489, 489, 571,
+ 59, 67, 499, 36, 571, 35, 39, 499, 73, 74,
+ 43, 62, 45, 59, 73, 74, 65, 59, 66, 24,
+ 53, 54, 67, 67, 71, 8, 523, 73, 74, 59,
+ 65, 523, 60, 473, 67, 62, 62, 67, 62, 24,
+ 480, 62, 35, 55, 56, 57, 58, 59, 60, 489,
+ 62, 63, 60, 60, 59, 443, 60, 63, 35, 68,
+ 60, 449, 450, 586, 452, 212, 666, 576, 73, 74,
+ 68, 218, 219, 461, 59, 463, 573, 34, 60, 576,
+ 576, 573, 59, 523, 576, 576, 60, 44, 73, 74,
+ 67, 48, 60, 36, 666, 128, 53, 54, 55, 56,
+ 75, 68, 75, 136, 60, 3, 60, 626, 60, 142,
+ 8, 144, 60, 68, 3, 148, 62, 626, 60, 17,
+ 60, 72, 75, 62, 22, 23, 24, 59, 66, 626,
+ 60, 29, 60, 573, 626, 60, 576, 646, 62, 60,
+ 173, 60, 8, 60, 59, 59, 59, 159, 160, 646,
+ 646, 17, 68, 62, 646, 646, 22, 23, 24, 62,
+ 72, 59, 68, 29, 552, 49, 62, 59, 201, 60,
+ 36, 68, 560, 71, 207, 73, 74, 60, 68, 60,
+ 213, 14, 62, 60, 72, 60, 60, 53, 221, 336,
+ 60, 224, 68, 4, 5, 31, 343, 22, 647, 65,
+ 586, 512, 235, 528, 351, 71, 646, 528, 74, 245,
+ 283, 39, 214, 245, 395, 603, 604, 387, 251, 252,
+ 22, 375, 33, 34, 160, 36, 37, 38, 39, 40,
+ 499, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 8, 336, 466, 607,
+ 283, 49, 444, 603, 65, 17, 336, 462, -1, -1,
+ 22, 23, 24, 61, 75, -1, 64, 29, 213, -1,
+ -1, -1, -1, -1, 36, -1, -1, -1, -1, -1,
+ 3, -1, -1, -1, -1, 8, -1, -1, 11, -1,
+ 323, 53, -1, -1, 17, -1, 443, 59, -1, 22,
+ 23, 24, 449, 450, -1, 452, 29, -1, -1, 71,
+ -1, 344, 74, 36, 461, -1, 463, -1, -1, -1,
+ -1, -1, 355, -1, -1, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, -1, 59, -1, -1, -1,
+ -1, -1, 375, -1, 377, -1, 69, 70, 71, 382,
+ 73, 74, 385, -1, 387, -1, -1, -1, -1, -1,
+ -1, -1, 395, -1, -1, -1, 399, -1, -1, 402,
+ -1, 404, -1, -1, -1, -1, -1, -1, 176, 177,
+ 178, 179, -1, 181, 182, 183, 419, 185, 186, 187,
+ 188, 189, 190, 191, 192, 193, 194, 195, 196, 197,
+ 198, -1, 200, -1, 202, 552, 204, 440, 441, -1,
+ 208, 209, 210, 560, -1, 448, 449, -1, 451, -1,
+ 567, -1, -1, 456, -1, 3, -1, -1, -1, 462,
+ 8, -1, 465, 466, -1, -1, -1, -1, 236, 17,
+ -1, -1, 475, -1, 22, 23, 24, 480, 26, -1,
+ -1, 29, -1, 600, -1, -1, 603, 604, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 49, 50, -1, 52, 53, -1, -1, 56, -1,
+ -1, 59, -1, -1, 282, -1, -1, -1, -1, -1,
+ -1, 69, 70, 71, 292, 73, 74, 8, -1, -1,
+ -1, -1, -1, -1, -1, -1, 17, 654, -1, 656,
+ -1, 22, 23, 24, -1, -1, -1, 315, 29, -1,
+ -1, 319, 8, -1, -1, 36, -1, 325, -1, -1,
+ -1, 17, -1, -1, -1, -1, 22, 23, 24, -1,
+ 573, -1, 53, 29, -1, -1, 579, -1, 59, -1,
+ 36, -1, -1, 586, 65, -1, -1, -1, -1, -1,
+ 71, -1, 73, 74, 75, -1, -1, 53, 601, -1,
+ 603, 369, 370, 59, 607, 608, -1, -1, -1, 612,
+ 613, -1, -1, -1, -1, 71, -1, 73, 74, 0,
+ 1, -1, 3, -1, -1, 6, -1, 8, 9, 10,
+ -1, -1, 13, -1, 15, 16, 17, 18, 19, 20,
+ 408, 22, 23, 24, -1, -1, 27, 28, 29, 30,
+ 31, 32, -1, -1, 422, 36, -1, -1, -1, -1,
+ -1, -1, 430, -1, -1, -1, -1, 435, 49, 50,
+ -1, 52, 53, -1, -1, 56, -1, -1, 59, -1,
+ -1, 62, -1, -1, 34, -1, -1, -1, 69, 70,
+ 71, -1, 73, 74, 44, -1, -1, -1, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, -1, 476, 477,
+ -1, -1, -1, -1, -1, -1, -1, -1, 1, -1,
+ 3, -1, -1, 6, 7, 8, 9, 10, -1, 12,
+ 13, 499, 15, 16, 17, 18, 19, 20, -1, 22,
+ 23, 24, -1, -1, 27, 28, 29, 30, 31, 32,
+ -1, 519, -1, 36, -1, -1, -1, -1, 526, 527,
+ 528, -1, -1, -1, -1, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, -1, 59, -1, -1, 62,
+ -1, -1, -1, -1, 67, 68, 69, 70, 71, -1,
+ 73, 74, -1, -1, -1, -1, 1, -1, 3, -1,
+ -1, 6, -1, 8, 9, 10, -1, -1, 13, -1,
+ 15, 16, 17, 18, 19, 20, -1, 22, 23, 24,
+ -1, -1, 27, 28, 29, 30, 31, 32, 596, -1,
+ -1, 36, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 49, 50, -1, 52, 53, -1,
+ -1, 56, -1, -1, 59, 623, 624, 62, 626, -1,
+ -1, -1, 67, 68, 69, 70, 71, 635, 73, 74,
+ 3, -1, -1, -1, -1, 8, -1, -1, -1, -1,
+ -1, -1, -1, -1, 17, -1, 8, -1, -1, 22,
+ 23, 24, -1, -1, -1, 17, 29, -1, -1, -1,
+ 22, 23, 24, 36, -1, -1, -1, 29, -1, -1,
+ -1, -1, -1, -1, 36, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, 3, 59, 60, -1, -1,
+ 8, 53, -1, -1, -1, -1, 69, 70, 71, 17,
+ 73, 74, -1, -1, 22, 23, 24, -1, -1, 71,
+ -1, 29, 74, -1, -1, -1, -1, -1, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 49, 50, -1, 52, 53, -1, -1, 56, -1,
+ 3, 59, -1, -1, -1, 8, -1, -1, -1, 67,
+ -1, 69, 70, 71, 17, 73, 74, -1, -1, 22,
+ 23, 24, -1, -1, -1, -1, 29, -1, 31, -1,
+ -1, -1, -1, 36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 49, 50, -1, 52,
+ 53, -1, -1, 56, -1, 3, 59, -1, -1, -1,
+ 8, -1, -1, -1, -1, -1, 69, 70, 71, 17,
+ 73, 74, -1, -1, 22, 23, 24, -1, 26, -1,
+ -1, 29, -1, -1, -1, -1, -1, -1, 36, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 49, 50, -1, 52, 53, -1, -1, 56, -1,
+ 3, 59, -1, -1, -1, 8, -1, -1, -1, -1,
+ -1, 69, 70, 71, 17, 73, 74, -1, -1, 22,
+ 23, 24, -1, 26, -1, -1, 29, -1, -1, -1,
+ -1, -1, -1, 36, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 3, -1, -1, 49, 50, 8, 52,
+ 53, -1, -1, 56, -1, -1, 59, 17, -1, -1,
+ -1, -1, 22, 23, 24, -1, 69, 70, 71, 29,
+ 73, 74, -1, -1, -1, -1, 36, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 49,
+ 50, -1, 52, 53, -1, -1, 56, -1, 3, 59,
+ -1, -1, -1, 8, -1, -1, -1, 67, -1, 69,
+ 70, 71, 17, 73, 74, -1, -1, 22, 23, 24,
+ -1, -1, -1, -1, 29, -1, -1, -1, -1, -1,
+ -1, 36, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 3, -1, -1, 49, 50, 8, 52, 53, -1,
+ -1, 56, -1, -1, 59, 17, -1, -1, -1, -1,
+ 22, 23, 24, -1, 69, 70, 71, 29, 73, 74,
+ -1, -1, -1, -1, 36, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 3, -1, -1, 49, 50, 8,
+ 52, 53, -1, -1, 56, -1, -1, 59, 17, -1,
+ -1, -1, -1, 22, 23, 24, -1, 69, 70, 71,
+ 29, 73, 74, -1, -1, -1, -1, 36, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 49, 50, 8, 52, 53, -1, -1, 56, -1, -1,
+ 59, 17, -1, -1, -1, -1, 22, 23, 24, -1,
+ 69, 70, 71, 29, 73, 74, -1, 8, -1, -1,
+ 36, -1, -1, -1, -1, 8, 17, -1, 11, -1,
+ -1, 22, 23, 24, 17, -1, -1, 53, 29, 22,
+ 23, 24, -1, 59, -1, 36, 29, -1, -1, 65,
+ -1, 8, -1, 36, -1, 71, -1, 73, 74, 75,
+ 17, -1, 53, -1, -1, 22, 23, 24, 59, -1,
+ 53, -1, 29, -1, -1, -1, 59, -1, 8, 36,
+ 71, -1, 73, 74, 75, -1, -1, 17, 71, -1,
+ 73, 74, 22, 23, 24, -1, 53, -1, -1, 29,
+ -1, -1, 59, -1, 8, -1, 36, -1, -1, -1,
+ -1, -1, 8, 17, 71, -1, 73, 74, 22, 23,
+ 24, 17, -1, 53, -1, 29, 22, 23, 24, 59,
+ -1, -1, 36, 29, -1, -1, -1, 8, -1, -1,
+ 36, 71, -1, 73, 74, 8, 17, -1, 11, 53,
+ -1, 22, 23, 24, 17, 59, -1, 53, 29, 22,
+ 23, 24, -1, 59, -1, 36, 29, 71, -1, 73,
+ 74, 8, -1, 36, -1, 71, -1, 73, 74, -1,
+ 17, -1, 53, -1, -1, 22, 23, 24, -1, -1,
+ 53, -1, 29, -1, -1, -1, -1, -1, -1, 36,
+ 71, -1, 73, 74, -1, -1, -1, -1, 71, 34,
+ -1, 74, -1, 38, 39, 40, 53, -1, 43, 44,
+ 45, 46, 59, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, -1, 71, 33, 34, 74, 36, -1,
+ 38, 39, 40, -1, -1, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, 33, 34,
+ -1, 36, -1, 38, 39, 40, -1, 75, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, -1, -1, -1, -1, -1, -1, 33, 34,
+ -1, 36, -1, 38, 39, 40, -1, 72, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56, 33, 34, -1, 36, -1, 38, 39, 40,
+ -1, 66, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 52, 53, 54, 55, 56, 33, 34, -1, 36,
+ -1, 38, 39, 40, -1, 66, 43, 44, 45, 46,
+ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 33, 34, -1, 36, -1, 38, 39, 40, -1, 66,
+ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ 53, 54, 55, 56, 33, 34, -1, -1, -1, 38,
+ 39, 40, -1, -1, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 52, 53, 54, 55, 56, 33, 34,
+ -1, -1, -1, 38, 39, 40, -1, -1, 43, 44,
+ 45, 46, -1, 48, 49, 50, 51, 52, 53, 54,
+ 55, 56
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const yytype_uint8 yystos[] =
+{
+ 0, 77, 79, 80, 0, 25, 78, 25, 86, 24,
+ 73, 74, 141, 142, 81, 24, 88, 89, 3, 62,
+ 21, 82, 166, 24, 87, 214, 63, 3, 59, 63,
+ 83, 85, 141, 62, 1, 3, 6, 8, 9, 10,
+ 13, 15, 16, 17, 18, 19, 20, 22, 23, 27,
+ 28, 29, 30, 31, 32, 36, 49, 50, 52, 53,
+ 56, 59, 69, 70, 71, 90, 91, 92, 98, 110,
+ 113, 121, 124, 126, 127, 128, 129, 134, 138, 141,
+ 143, 144, 149, 150, 153, 156, 157, 158, 161, 164,
+ 165, 181, 186, 62, 9, 17, 21, 31, 32, 64,
+ 199, 24, 73, 60, 83, 84, 3, 86, 88, 3,
+ 138, 140, 141, 17, 36, 53, 59, 141, 143, 148,
+ 152, 153, 154, 161, 140, 128, 134, 111, 59, 141,
+ 159, 128, 138, 114, 35, 67, 137, 71, 126, 186,
+ 193, 125, 137, 122, 59, 96, 97, 141, 59, 93,
+ 139, 141, 185, 127, 127, 127, 127, 127, 127, 36,
+ 53, 126, 135, 147, 153, 155, 161, 127, 127, 11,
+ 126, 192, 62, 59, 94, 185, 4, 33, 34, 36,
+ 37, 38, 39, 40, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 67,
+ 59, 63, 71, 66, 59, 137, 1, 137, 5, 65,
+ 75, 142, 200, 59, 160, 200, 24, 200, 201, 200,
+ 64, 62, 190, 88, 59, 36, 59, 146, 152, 153,
+ 154, 155, 161, 146, 146, 63, 26, 98, 107, 108,
+ 109, 186, 194, 11, 136, 141, 145, 146, 177, 178,
+ 179, 59, 67, 162, 112, 194, 24, 59, 68, 138,
+ 171, 173, 175, 146, 35, 53, 59, 68, 138, 170,
+ 172, 173, 174, 184, 112, 60, 97, 169, 146, 60,
+ 93, 167, 65, 75, 146, 8, 147, 60, 72, 72,
+ 60, 94, 65, 146, 126, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 126, 126, 126, 126, 126,
+ 126, 126, 126, 126, 126, 130, 60, 135, 187, 59,
+ 141, 126, 192, 182, 126, 130, 1, 67, 91, 100,
+ 180, 181, 183, 186, 186, 126, 8, 17, 22, 23,
+ 24, 29, 36, 53, 65, 71, 142, 202, 204, 205,
+ 206, 141, 207, 215, 162, 59, 3, 202, 202, 83,
+ 60, 179, 8, 146, 60, 141, 126, 35, 105, 5,
+ 65, 62, 146, 136, 145, 75, 191, 60, 179, 183,
+ 115, 62, 63, 24, 173, 59, 176, 62, 190, 72,
+ 104, 59, 174, 53, 174, 62, 190, 3, 198, 75,
+ 146, 123, 62, 190, 62, 190, 186, 139, 65, 36,
+ 59, 146, 152, 153, 154, 161, 67, 146, 146, 62,
+ 190, 186, 65, 67, 126, 131, 132, 188, 189, 11,
+ 75, 191, 31, 135, 72, 66, 180, 75, 191, 189,
+ 101, 62, 68, 36, 59, 203, 204, 206, 59, 67,
+ 71, 67, 8, 202, 3, 50, 59, 141, 212, 213,
+ 3, 72, 65, 11, 202, 60, 75, 62, 195, 215,
+ 62, 62, 62, 60, 60, 106, 26, 26, 194, 177,
+ 59, 141, 151, 152, 153, 154, 155, 161, 163, 60,
+ 68, 105, 194, 141, 60, 179, 175, 68, 146, 7,
+ 12, 68, 99, 102, 174, 198, 174, 60, 172, 68,
+ 138, 198, 35, 97, 60, 93, 60, 186, 146, 130,
+ 94, 95, 168, 185, 60, 186, 130, 66, 75, 191,
+ 68, 191, 135, 60, 60, 60, 192, 60, 68, 183,
+ 180, 202, 205, 195, 24, 141, 142, 197, 202, 209,
+ 217, 202, 141, 196, 208, 216, 202, 3, 212, 62,
+ 72, 202, 213, 202, 198, 141, 207, 60, 183, 126,
+ 126, 62, 179, 59, 163, 116, 60, 187, 66, 103,
+ 60, 60, 198, 104, 60, 189, 62, 190, 146, 189,
+ 67, 126, 133, 131, 132, 60, 66, 72, 68, 60,
+ 60, 59, 68, 62, 72, 202, 68, 62, 49, 202,
+ 62, 198, 59, 59, 202, 210, 211, 68, 194, 60,
+ 179, 119, 163, 5, 65, 66, 75, 183, 198, 198,
+ 68, 68, 95, 60, 68, 130, 192, 210, 195, 209,
+ 202, 198, 208, 212, 195, 195, 60, 14, 117, 120,
+ 126, 126, 189, 72, 60, 60, 60, 60, 163, 20,
+ 100, 66, 66, 68, 210, 210, 118, 112, 105
+};
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK (1); \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror (YY_("syntax error: cannot back up")); \
+ YYERROR; \
+ } \
+while (YYID (0))
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (YYID (N)) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yy_symbol_print (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (!yyvaluep)
+ return;
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+ YYUSE (yyoutput);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE const * const yyvaluep;
+#endif
+{
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+ yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+ YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ yytype_int16 *bottom;
+ yytype_int16 *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+ YYSTYPE *yyvsp;
+ int yyrule;
+#endif
+{
+ int yynrhs = yyr2[yyrule];
+ int yyi;
+ unsigned long int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+ yyrule - 1, yylno);
+ /* The symbols being reduced. */
+ for (yyi = 0; yyi < yynrhs; yyi++)
+ {
+ fprintf (stderr, " $%d = ", yyi + 1);
+ yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+ &(yyvsp[(yyi + 1) - (yynrhs)])
+ );
+ fprintf (stderr, "\n");
+ }
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined __GLIBC__ && defined _STRING_H
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+ const char *yystr;
+#endif
+{
+ YYSIZE_T yylen;
+ for (yylen = 0; yystr[yylen]; yylen++)
+ continue;
+ return yylen;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+#endif
+{
+ char *yyd = yydest;
+ const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+ quotes and backslashes, so that it's suitable for yyerror. The
+ heuristic is that double-quoting is unnecessary unless the string
+ contains an apostrophe, a comma, or backslash (other than
+ backslash-backslash). YYSTR is taken from yytname. If YYRES is
+ null, do not copy; instead, return the length of what the result
+ would have been. */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+ if (*yystr == '"')
+ {
+ YYSIZE_T yyn = 0;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ /* Fall through. */
+ default:
+ if (yyres)
+ yyres[yyn] = *yyp;
+ yyn++;
+ break;
+
+ case '"':
+ if (yyres)
+ yyres[yyn] = '\0';
+ return yyn;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ if (! yyres)
+ return yystrlen (yystr);
+
+ return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+ YYCHAR while in state YYSTATE. Return the number of bytes copied,
+ including the terminating null byte. If YYRESULT is null, do not
+ copy anything; just return the number of bytes that would be
+ copied. As a special case, return 0 if an ordinary "syntax error"
+ message will do. Return YYSIZE_MAXIMUM if overflow occurs during
+ size calculation. */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+ int yyn = yypact[yystate];
+
+ if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+ return 0;
+ else
+ {
+ int yytype = YYTRANSLATE (yychar);
+ YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+ YYSIZE_T yysize = yysize0;
+ YYSIZE_T yysize1;
+ int yysize_overflow = 0;
+ enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+ int yyx;
+
+# if 0
+ /* This is so xgettext sees the translatable formats that are
+ constructed on the fly. */
+ YY_("syntax error, unexpected %s");
+ YY_("syntax error, unexpected %s, expecting %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+ YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+ char *yyfmt;
+ char const *yyf;
+ static char const yyunexpected[] = "syntax error, unexpected %s";
+ static char const yyexpecting[] = ", expecting %s";
+ static char const yyor[] = " or %s";
+ char yyformat[sizeof yyunexpected
+ + sizeof yyexpecting - 1
+ + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+ * (sizeof yyor - 1))];
+ char const *yyprefix = yyexpecting;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn + 1;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 1;
+
+ yyarg[0] = yytname[yytype];
+ yyfmt = yystpcpy (yyformat, yyunexpected);
+
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+ {
+ yycount = 1;
+ yysize = yysize0;
+ yyformat[sizeof yyunexpected - 1] = '\0';
+ break;
+ }
+ yyarg[yycount++] = yytname[yyx];
+ yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+ yyfmt = yystpcpy (yyfmt, yyprefix);
+ yyprefix = yyor;
+ }
+
+ yyf = YY_(yyformat);
+ yysize1 = yysize + yystrlen (yyf);
+ yysize_overflow |= (yysize1 < yysize);
+ yysize = yysize1;
+
+ if (yysize_overflow)
+ return YYSIZE_MAXIMUM;
+
+ if (yyresult)
+ {
+ /* Avoid sprintf, as that infringes on the user's name space.
+ Don't have undefined behavior even if the translation
+ produced a string with the wrong number of "%s"s. */
+ char *yyp = yyresult;
+ int yyi = 0;
+ while ((*yyp = *yyf) != '\0')
+ {
+ if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+ {
+ yyp += yytnamerr (yyp, yyarg[yyi++]);
+ yyf += 2;
+ }
+ else
+ {
+ yyp++;
+ yyf++;
+ }
+ }
+ }
+ return yysize;
+ }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ YYUSE (yyvaluep);
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar, yystate;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+ || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+#if YYERROR_VERBOSE
+ /* Buffer for error messages, and its allocated size. */
+ char yymsgbuf[128];
+ char *yymsg = yymsgbuf;
+ YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ yytype_int16 yyssa[YYINITDEPTH];
+ yytype_int16 *yyss = yyssa;
+ yytype_int16 *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N))
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* The number of symbols on the RHS of the reduced rule.
+ Keep to zero when no symbol should be popped. */
+ int yylen = 0;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. So pushing a state here evens the stacks. */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ yytype_int16 *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow (YY_("memory exhausted"),
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyexhaustedlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyexhaustedlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ yytype_int16 *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyexhaustedlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+ /* Do appropriate processing given the current state. Read a
+ look-ahead token if we need one and don't already have one. */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the shifted token unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ yystate = yyn;
+ *++yyvsp = yylval;
+
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 128 "go.y"
+ {
+ xtop = concat(xtop, (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 3:
+#line 134 "go.y"
+ {
+ prevlineno = lineno;
+ yyerror("package statement must be first");
+ errorexit();
+ }
+ break;
+
+ case 4:
+#line 140 "go.y"
+ {
+ mkpackage((yyvsp[(2) - (3)].sym)->name);
+ }
+ break;
+
+ case 5:
+#line 150 "go.y"
+ {
+ importpkg = runtimepkg;
+
+ if(debug['A'])
+ cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
+ else
+ cannedimports("runtime.builtin", runtimeimport);
+ curio.importsafe = 1;
+ }
+ break;
+
+ case 6:
+#line 161 "go.y"
+ {
+ importpkg = nil;
+ }
+ break;
+
+ case 12:
+#line 175 "go.y"
+ {
+ 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 = (yyvsp[(1) - (3)].i);
+
+ if(my->name[0] == '.') {
+ importdot(ipkg, pack);
+ break;
+ }
+ if(strcmp(my->name, "init") == 0) {
+ yyerror("cannot import package as init - init must be a func");
+ break;
+ }
+ if(my->name[0] == '_' && my->name[1] == '\0')
+ break;
+ if(my->def) {
+ lineno = (yyvsp[(1) - (3)].i);
+ redeclare(my, "as imported package name");
+ }
+ my->def = pack;
+ my->lastlineno = (yyvsp[(1) - (3)].i);
+ my->block = 1; // at top level
+ }
+ break;
+
+ case 13:
+#line 212 "go.y"
+ {
+ // When an invalid import path is passed to importfile,
+ // it calls yyerror and then sets up a fake import with
+ // no package statement. This allows us to test more
+ // than one invalid import statement in a single file.
+ if(nerrors == 0)
+ fatal("phase error in import");
+ }
+ break;
+
+ case 16:
+#line 227 "go.y"
+ {
+ // import with original name
+ (yyval.i) = parserline();
+ importmyname = S;
+ importfile(&(yyvsp[(1) - (1)].val), (yyval.i));
+ }
+ break;
+
+ case 17:
+#line 234 "go.y"
+ {
+ // import with given name
+ (yyval.i) = parserline();
+ importmyname = (yyvsp[(1) - (2)].sym);
+ importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+ }
+ break;
+
+ case 18:
+#line 241 "go.y"
+ {
+ // import into my name space
+ (yyval.i) = parserline();
+ importmyname = lookup(".");
+ importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+ }
+ break;
+
+ case 19:
+#line 250 "go.y"
+ {
+ if(importpkg->name == nil) {
+ importpkg->name = (yyvsp[(2) - (4)].sym)->name;
+ pkglookup((yyvsp[(2) - (4)].sym)->name, nil)->npkg++;
+ } else if(strcmp(importpkg->name, (yyvsp[(2) - (4)].sym)->name) != 0)
+ yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, (yyvsp[(2) - (4)].sym)->name, importpkg->path);
+ importpkg->direct = 1;
+ importpkg->safe = curio.importsafe;
+
+ if(safemode && !curio.importsafe)
+ yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
+ }
+ break;
+
+ case 21:
+#line 265 "go.y"
+ {
+ if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0)
+ curio.importsafe = 1;
+ }
+ break;
+
+ case 22:
+#line 271 "go.y"
+ {
+ defercheckwidth();
+ }
+ break;
+
+ case 23:
+#line 275 "go.y"
+ {
+ resumecheckwidth();
+ unimportfile();
+ }
+ break;
+
+ case 24:
+#line 284 "go.y"
+ {
+ yyerror("empty top-level declaration");
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 26:
+#line 290 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 27:
+#line 294 "go.y"
+ {
+ yyerror("non-declaration statement outside function body");
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 28:
+#line 299 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 29:
+#line 305 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (2)].list);
+ }
+ break;
+
+ case 30:
+#line 309 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 31:
+#line 313 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 32:
+#line 317 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (2)].list);
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 33:
+#line 323 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 34:
+#line 329 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list));
+ iota = -100000;
+ lastconst = nil;
+ }
+ break;
+
+ case 35:
+#line 335 "go.y"
+ {
+ (yyval.list) = nil;
+ iota = -100000;
+ }
+ break;
+
+ case 36:
+#line 340 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 37:
+#line 344 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 38:
+#line 348 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 39:
+#line 354 "go.y"
+ {
+ iota = 0;
+ }
+ break;
+
+ case 40:
+#line 360 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+ }
+ break;
+
+ case 41:
+#line 364 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 42:
+#line 368 "go.y"
+ {
+ (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 43:
+#line 374 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+ }
+ break;
+
+ case 44:
+#line 378 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 46:
+#line 385 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+ }
+ break;
+
+ case 47:
+#line 389 "go.y"
+ {
+ (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil);
+ }
+ break;
+
+ case 48:
+#line 395 "go.y"
+ {
+ // different from dclname because the name
+ // becomes visible right here, not at the end
+ // of the declaration.
+ (yyval.node) = typedcl0((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 49:
+#line 404 "go.y"
+ {
+ (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1);
+ }
+ break;
+
+ case 50:
+#line 410 "go.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+
+ // These nodes do not carry line numbers.
+ // Since a bare name used as an expression is an error,
+ // introduce a wrapper node to give the correct line.
+ switch((yyval.node)->op) {
+ case ONAME:
+ case ONONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ (yyval.node) = nod(OPAREN, (yyval.node), N);
+ (yyval.node)->implicit = 1;
+ break;
+ }
+ }
+ break;
+
+ case 51:
+#line 428 "go.y"
+ {
+ (yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ (yyval.node)->etype = (yyvsp[(2) - (3)].i); // rathole to pass opcode
+ }
+ break;
+
+ case 52:
+#line 433 "go.y"
+ {
+ if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) {
+ // simple
+ (yyval.node) = nod(OAS, (yyvsp[(1) - (3)].list)->n, (yyvsp[(3) - (3)].list)->n);
+ break;
+ }
+ // multiple
+ (yyval.node) = nod(OAS2, N, N);
+ (yyval.node)->list = (yyvsp[(1) - (3)].list);
+ (yyval.node)->rlist = (yyvsp[(3) - (3)].list);
+ }
+ break;
+
+ case 53:
+#line 445 "go.y"
+ {
+ if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) {
+ (yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right);
+ if((yyvsp[(3) - (3)].list)->next != nil)
+ yyerror("expr.(type) must be alone in list");
+ if((yyvsp[(1) - (3)].list)->next != nil)
+ yyerror("argument count mismatch: %d = %d", count((yyvsp[(1) - (3)].list)), 1);
+ else if(((yyvsp[(1) - (3)].list)->n->op != ONAME && (yyvsp[(1) - (3)].list)->n->op != OTYPE && (yyvsp[(1) - (3)].list)->n->op != ONONAME) || isblank((yyvsp[(1) - (3)].list)->n))
+ yyerror("invalid variable name %N in type switch", (yyvsp[(1) - (3)].list)->n);
+ else
+ (yyval.node)->left = dclname((yyvsp[(1) - (3)].list)->n->sym); // it's a colas, so must not re-use an oldname.
+ break;
+ }
+ (yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list), (yyvsp[(2) - (3)].i));
+ }
+ break;
+
+ case 54:
+#line 461 "go.y"
+ {
+ (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+ (yyval.node)->implicit = 1;
+ (yyval.node)->etype = OADD;
+ }
+ break;
+
+ case 55:
+#line 467 "go.y"
+ {
+ (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+ (yyval.node)->implicit = 1;
+ (yyval.node)->etype = OSUB;
+ }
+ break;
+
+ case 56:
+#line 475 "go.y"
+ {
+ Node *n, *nn;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ (yyval.node)->list = (yyvsp[(2) - (3)].list);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ (yyval.node)->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+ break;
+
+ case 57:
+#line 495 "go.y"
+ {
+ Node *n;
+
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ if((yyvsp[(2) - (5)].list)->next == nil)
+ n = nod(OAS, (yyvsp[(2) - (5)].list)->n, (yyvsp[(4) - (5)].node));
+ else {
+ n = nod(OAS2, N, N);
+ n->list = (yyvsp[(2) - (5)].list);
+ n->rlist = list1((yyvsp[(4) - (5)].node));
+ }
+ (yyval.node)->list = list1(n);
+ }
+ break;
+
+ case 58:
+#line 513 "go.y"
+ {
+ // will be converted to OCASE
+ // right will point to next case
+ // done in casebody()
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ (yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)), (yyvsp[(3) - (5)].i)));
+ }
+ break;
+
+ case 59:
+#line 522 "go.y"
+ {
+ Node *n, *nn;
+
+ markdcl();
+ (yyval.node) = nod(OXCASE, N, N);
+ if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+ // type switch - declare variable
+ nn = newname(n->sym);
+ declare(nn, dclcontext);
+ (yyval.node)->nname = nn;
+
+ // keep track of the instances for reporting unused
+ nn->defn = typesw->right;
+ }
+ }
+ break;
+
+ case 60:
+#line 540 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 61:
+#line 544 "go.y"
+ {
+ if((yyvsp[(3) - (4)].list) == nil)
+ (yyval.node) = nod(OEMPTY, N, N);
+ else
+ (yyval.node) = liststmt((yyvsp[(3) - (4)].list));
+ popdcl();
+ }
+ break;
+
+ case 62:
+#line 554 "go.y"
+ {
+ // 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;
+ (yyvsp[(1) - (1)].node)->xoffset = block;
+ }
+ break;
+
+ case 63:
+#line 565 "go.y"
+ {
+ 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");
+ (yyval.node) = (yyvsp[(1) - (3)].node);
+ (yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+ popdcl();
+ }
+ break;
+
+ case 64:
+#line 585 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 65:
+#line 589 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 66:
+#line 595 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 67:
+#line 599 "go.y"
+ {
+ (yyval.list) = (yyvsp[(3) - (4)].list);
+ popdcl();
+ }
+ break;
+
+ case 68:
+#line 606 "go.y"
+ {
+ (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+ (yyval.node)->list = (yyvsp[(1) - (4)].list);
+ (yyval.node)->etype = 0; // := flag
+ }
+ break;
+
+ case 69:
+#line 612 "go.y"
+ {
+ (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+ (yyval.node)->list = (yyvsp[(1) - (4)].list);
+ (yyval.node)->colas = 1;
+ colasdefn((yyvsp[(1) - (4)].list), (yyval.node));
+ }
+ break;
+
+ case 70:
+#line 619 "go.y"
+ {
+ (yyval.node) = nod(ORANGE, N, (yyvsp[(2) - (2)].node));
+ (yyval.node)->etype = 0; // := flag
+ }
+ break;
+
+ case 71:
+#line 626 "go.y"
+ {
+ // init ; test ; incr
+ if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0)
+ yyerror("cannot declare in the for-increment");
+ (yyval.node) = nod(OFOR, N, N);
+ if((yyvsp[(1) - (5)].node) != N)
+ (yyval.node)->ninit = list1((yyvsp[(1) - (5)].node));
+ (yyval.node)->ntest = (yyvsp[(3) - (5)].node);
+ (yyval.node)->nincr = (yyvsp[(5) - (5)].node);
+ }
+ break;
+
+ case 72:
+#line 637 "go.y"
+ {
+ // normal test
+ (yyval.node) = nod(OFOR, N, N);
+ (yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 74:
+#line 646 "go.y"
+ {
+ (yyval.node) = (yyvsp[(1) - (2)].node);
+ (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list));
+ }
+ break;
+
+ case 75:
+#line 653 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 76:
+#line 657 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (3)].node);
+ popdcl();
+ }
+ break;
+
+ case 77:
+#line 664 "go.y"
+ {
+ // test
+ (yyval.node) = nod(OIF, N, N);
+ (yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+ }
+ break;
+
+ case 78:
+#line 670 "go.y"
+ {
+ // init ; test
+ (yyval.node) = nod(OIF, N, N);
+ if((yyvsp[(1) - (3)].node) != N)
+ (yyval.node)->ninit = list1((yyvsp[(1) - (3)].node));
+ (yyval.node)->ntest = (yyvsp[(3) - (3)].node);
+ }
+ break;
+
+ case 79:
+#line 681 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 80:
+#line 685 "go.y"
+ {
+ if((yyvsp[(3) - (3)].node)->ntest == N)
+ yyerror("missing condition in if statement");
+ }
+ break;
+
+ case 81:
+#line 690 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
+ }
+ break;
+
+ case 82:
+#line 694 "go.y"
+ {
+ Node *n;
+ NodeList *nn;
+
+ (yyval.node) = (yyvsp[(3) - (8)].node);
+ n = (yyvsp[(3) - (8)].node);
+ popdcl();
+ for(nn = concat((yyvsp[(7) - (8)].list), (yyvsp[(8) - (8)].list)); nn; nn = nn->next) {
+ if(nn->n->op == OIF)
+ popdcl();
+ n->nelse = list1(nn->n);
+ n = nn->n;
+ }
+ }
+ break;
+
+ case 83:
+#line 711 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 84:
+#line 715 "go.y"
+ {
+ if((yyvsp[(4) - (5)].node)->ntest == N)
+ yyerror("missing condition in if statement");
+ (yyvsp[(4) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
+ (yyval.list) = list1((yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 85:
+#line 723 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 86:
+#line 727 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list));
+ }
+ break;
+
+ case 87:
+#line 732 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 88:
+#line 736 "go.y"
+ {
+ NodeList *node;
+
+ node = mal(sizeof *node);
+ node->n = (yyvsp[(2) - (2)].node);
+ node->end = node;
+ (yyval.list) = node;
+ }
+ break;
+
+ case 89:
+#line 747 "go.y"
+ {
+ markdcl();
+ }
+ break;
+
+ case 90:
+#line 751 "go.y"
+ {
+ Node *n;
+ n = (yyvsp[(3) - (3)].node)->ntest;
+ if(n != N && n->op != OTYPESW)
+ n = N;
+ typesw = nod(OXXX, typesw, n);
+ }
+ break;
+
+ case 91:
+#line 759 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (7)].node);
+ (yyval.node)->op = OSWITCH;
+ (yyval.node)->list = (yyvsp[(6) - (7)].list);
+ typesw = typesw->left;
+ popdcl();
+ }
+ break;
+
+ case 92:
+#line 769 "go.y"
+ {
+ typesw = nod(OXXX, typesw, N);
+ }
+ break;
+
+ case 93:
+#line 773 "go.y"
+ {
+ (yyval.node) = nod(OSELECT, N, N);
+ (yyval.node)->lineno = typesw->lineno;
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ typesw = typesw->left;
+ }
+ break;
+
+ case 95:
+#line 786 "go.y"
+ {
+ (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 96:
+#line 790 "go.y"
+ {
+ (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 97:
+#line 794 "go.y"
+ {
+ (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 98:
+#line 798 "go.y"
+ {
+ (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 99:
+#line 802 "go.y"
+ {
+ (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 100:
+#line 806 "go.y"
+ {
+ (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 101:
+#line 810 "go.y"
+ {
+ (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 102:
+#line 814 "go.y"
+ {
+ (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 103:
+#line 818 "go.y"
+ {
+ (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 104:
+#line 822 "go.y"
+ {
+ (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 105:
+#line 826 "go.y"
+ {
+ (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 106:
+#line 830 "go.y"
+ {
+ (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 107:
+#line 834 "go.y"
+ {
+ (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 108:
+#line 838 "go.y"
+ {
+ (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 109:
+#line 842 "go.y"
+ {
+ (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 110:
+#line 846 "go.y"
+ {
+ (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 111:
+#line 850 "go.y"
+ {
+ (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 112:
+#line 854 "go.y"
+ {
+ (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 113:
+#line 858 "go.y"
+ {
+ (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 114:
+#line 863 "go.y"
+ {
+ (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 116:
+#line 870 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 117:
+#line 874 "go.y"
+ {
+ if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) {
+ // Special case for &T{...}: turn into (*T){...}.
+ (yyval.node) = (yyvsp[(2) - (2)].node);
+ (yyval.node)->right = nod(OIND, (yyval.node)->right, N);
+ (yyval.node)->right->implicit = 1;
+ } else {
+ (yyval.node) = nod(OADDR, (yyvsp[(2) - (2)].node), N);
+ }
+ }
+ break;
+
+ case 118:
+#line 885 "go.y"
+ {
+ (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 119:
+#line 889 "go.y"
+ {
+ (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 120:
+#line 893 "go.y"
+ {
+ (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 121:
+#line 897 "go.y"
+ {
+ yyerror("the bitwise complement operator is ^");
+ (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 122:
+#line 902 "go.y"
+ {
+ (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 123:
+#line 906 "go.y"
+ {
+ (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 124:
+#line 916 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N);
+ }
+ break;
+
+ case 125:
+#line 920 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ }
+ break;
+
+ case 126:
+#line 925 "go.y"
+ {
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N);
+ (yyval.node)->list = (yyvsp[(3) - (6)].list);
+ (yyval.node)->isddd = 1;
+ }
+ break;
+
+ case 127:
+#line 933 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+ }
+ break;
+
+ case 129:
+#line 938 "go.y"
+ {
+ if((yyvsp[(1) - (3)].node)->op == OPACK) {
+ Sym *s;
+ s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+ (yyvsp[(1) - (3)].node)->used = 1;
+ (yyval.node) = oldname(s);
+ break;
+ }
+ (yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+ }
+ break;
+
+ case 130:
+#line 949 "go.y"
+ {
+ (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 131:
+#line 953 "go.y"
+ {
+ (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node));
+ }
+ break;
+
+ case 132:
+#line 957 "go.y"
+ {
+ (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+ }
+ break;
+
+ case 133:
+#line 961 "go.y"
+ {
+ (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)));
+ }
+ break;
+
+ case 134:
+#line 965 "go.y"
+ {
+ if((yyvsp[(5) - (8)].node) == N)
+ yyerror("middle index required in 3-index slice");
+ if((yyvsp[(7) - (8)].node) == N)
+ yyerror("final index required in 3-index slice");
+ (yyval.node) = nod(OSLICE3, (yyvsp[(1) - (8)].node), nod(OKEY, (yyvsp[(3) - (8)].node), nod(OKEY, (yyvsp[(5) - (8)].node), (yyvsp[(7) - (8)].node))));
+ }
+ break;
+
+ case 136:
+#line 974 "go.y"
+ {
+ // conversion
+ (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
+ (yyval.node)->list = list1((yyvsp[(3) - (5)].node));
+ }
+ break;
+
+ case 137:
+#line 980 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (5)].node);
+ (yyval.node)->right = (yyvsp[(1) - (5)].node);
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 138:
+#line 987 "go.y"
+ {
+ (yyval.node) = (yyvsp[(3) - (5)].node);
+ (yyval.node)->right = (yyvsp[(1) - (5)].node);
+ (yyval.node)->list = (yyvsp[(4) - (5)].list);
+ }
+ break;
+
+ case 139:
+#line 993 "go.y"
+ {
+ yyerror("cannot parenthesize type in composite literal");
+ (yyval.node) = (yyvsp[(5) - (7)].node);
+ (yyval.node)->right = (yyvsp[(2) - (7)].node);
+ (yyval.node)->list = (yyvsp[(6) - (7)].list);
+ }
+ break;
+
+ case 141:
+#line 1002 "go.y"
+ {
+ // composite expression.
+ // make node early so we get the right line number.
+ (yyval.node) = nod(OCOMPLIT, N, N);
+ }
+ break;
+
+ case 142:
+#line 1010 "go.y"
+ {
+ (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 143:
+#line 1016 "go.y"
+ {
+ // These nodes do not carry line numbers.
+ // Since a composite literal commonly spans several lines,
+ // the line number on errors may be misleading.
+ // Introduce a wrapper node to give the correct line.
+ (yyval.node) = (yyvsp[(1) - (1)].node);
+ switch((yyval.node)->op) {
+ case ONAME:
+ case ONONAME:
+ case OTYPE:
+ case OPACK:
+ case OLITERAL:
+ (yyval.node) = nod(OPAREN, (yyval.node), N);
+ (yyval.node)->implicit = 1;
+ }
+ }
+ break;
+
+ case 144:
+#line 1033 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ (yyval.node)->list = (yyvsp[(3) - (4)].list);
+ }
+ break;
+
+ case 146:
+#line 1041 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (4)].node);
+ (yyval.node)->list = (yyvsp[(3) - (4)].list);
+ }
+ break;
+
+ case 148:
+#line 1049 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+
+ // 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((yyval.node)->op) {
+ case ONAME:
+ case ONONAME:
+ case OPACK:
+ case OTYPE:
+ case OLITERAL:
+ case OTYPESW:
+ (yyval.node) = nod(OPAREN, (yyval.node), N);
+ }
+ }
+ break;
+
+ case 152:
+#line 1075 "go.y"
+ {
+ (yyval.i) = LBODY;
+ }
+ break;
+
+ case 153:
+#line 1079 "go.y"
+ {
+ (yyval.i) = '{';
+ }
+ break;
+
+ case 154:
+#line 1090 "go.y"
+ {
+ if((yyvsp[(1) - (1)].sym) == S)
+ (yyval.node) = N;
+ else
+ (yyval.node) = newname((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 155:
+#line 1099 "go.y"
+ {
+ (yyval.node) = dclname((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 156:
+#line 1104 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 158:
+#line 1111 "go.y"
+ {
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ // during imports, unqualified non-exported identifiers are from builtinpkg
+ if(importpkg != nil && !exportname((yyvsp[(1) - (1)].sym)->name))
+ (yyval.sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+ }
+ break;
+
+ case 160:
+#line 1119 "go.y"
+ {
+ (yyval.sym) = S;
+ }
+ break;
+
+ case 161:
+#line 1125 "go.y"
+ {
+ Pkg *p;
+
+ if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport((yyvsp[(2) - (4)].val).u.sval))
+ errorexit();
+ p = mkpkg((yyvsp[(2) - (4)].val).u.sval);
+ }
+ (yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, p);
+ }
+ break;
+
+ case 162:
+#line 1138 "go.y"
+ {
+ Pkg *p;
+
+ if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+ p = importpkg;
+ else {
+ if(isbadimport((yyvsp[(2) - (4)].val).u.sval))
+ errorexit();
+ p = mkpkg((yyvsp[(2) - (4)].val).u.sval);
+ }
+ (yyval.sym) = pkglookup("?", p);
+ }
+ break;
+
+ case 163:
+#line 1153 "go.y"
+ {
+ (yyval.node) = oldname((yyvsp[(1) - (1)].sym));
+ if((yyval.node)->pack != N)
+ (yyval.node)->pack->used = 1;
+ }
+ break;
+
+ case 165:
+#line 1173 "go.y"
+ {
+ yyerror("final argument in variadic function missing type");
+ (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N);
+ }
+ break;
+
+ case 166:
+#line 1178 "go.y"
+ {
+ (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 172:
+#line 1189 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 176:
+#line 1198 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 181:
+#line 1208 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ }
+ break;
+
+ case 191:
+#line 1229 "go.y"
+ {
+ if((yyvsp[(1) - (3)].node)->op == OPACK) {
+ Sym *s;
+ s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+ (yyvsp[(1) - (3)].node)->used = 1;
+ (yyval.node) = oldname(s);
+ break;
+ }
+ (yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+ }
+ break;
+
+ case 192:
+#line 1242 "go.y"
+ {
+ (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 193:
+#line 1246 "go.y"
+ {
+ // array literal of nelem
+ (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node));
+ }
+ break;
+
+ case 194:
+#line 1251 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N);
+ (yyval.node)->etype = Cboth;
+ }
+ break;
+
+ case 195:
+#line 1256 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+ (yyval.node)->etype = Csend;
+ }
+ break;
+
+ case 196:
+#line 1261 "go.y"
+ {
+ (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+ }
+ break;
+
+ case 199:
+#line 1269 "go.y"
+ {
+ (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 200:
+#line 1275 "go.y"
+ {
+ (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+ (yyval.node)->etype = Crecv;
+ }
+ break;
+
+ case 201:
+#line 1282 "go.y"
+ {
+ (yyval.node) = nod(OTSTRUCT, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 202:
+#line 1288 "go.y"
+ {
+ (yyval.node) = nod(OTSTRUCT, N, N);
+ fixlbrace((yyvsp[(2) - (3)].i));
+ }
+ break;
+
+ case 203:
+#line 1295 "go.y"
+ {
+ (yyval.node) = nod(OTINTER, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ fixlbrace((yyvsp[(2) - (5)].i));
+ }
+ break;
+
+ case 204:
+#line 1301 "go.y"
+ {
+ (yyval.node) = nod(OTINTER, N, N);
+ fixlbrace((yyvsp[(2) - (3)].i));
+ }
+ break;
+
+ case 205:
+#line 1312 "go.y"
+ {
+ (yyval.node) = (yyvsp[(2) - (3)].node);
+ if((yyval.node) == N)
+ break;
+ if(noescape && (yyvsp[(3) - (3)].list) != nil)
+ yyerror("can only use //go:noescape with external func implementations");
+ (yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+ (yyval.node)->endlineno = lineno;
+ (yyval.node)->noescape = noescape;
+ (yyval.node)->nosplit = nosplit;
+ funcbody((yyval.node));
+ }
+ break;
+
+ case 206:
+#line 1327 "go.y"
+ {
+ Node *t;
+
+ (yyval.node) = N;
+ (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+
+ if(strcmp((yyvsp[(1) - (5)].sym)->name, "init") == 0) {
+ (yyvsp[(1) - (5)].sym) = renameinit();
+ if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+ yyerror("func init must have no arguments and no return values");
+ }
+ if(strcmp(localpkg->name, "main") == 0 && strcmp((yyvsp[(1) - (5)].sym)->name, "main") == 0) {
+ if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+ yyerror("func main must have no arguments and no return values");
+ }
+
+ t = nod(OTFUNC, N, N);
+ t->list = (yyvsp[(3) - (5)].list);
+ t->rlist = (yyvsp[(5) - (5)].list);
+
+ (yyval.node) = nod(ODCLFUNC, N, N);
+ (yyval.node)->nname = newname((yyvsp[(1) - (5)].sym));
+ (yyval.node)->nname->defn = (yyval.node);
+ (yyval.node)->nname->ntype = t; // TODO: check if nname already has an ntype
+ declare((yyval.node)->nname, PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 207:
+#line 1356 "go.y"
+ {
+ Node *rcvr, *t;
+
+ (yyval.node) = N;
+ (yyvsp[(2) - (8)].list) = checkarglist((yyvsp[(2) - (8)].list), 0);
+ (yyvsp[(6) - (8)].list) = checkarglist((yyvsp[(6) - (8)].list), 1);
+
+ if((yyvsp[(2) - (8)].list) == nil) {
+ yyerror("method has no receiver");
+ break;
+ }
+ if((yyvsp[(2) - (8)].list)->next != nil) {
+ yyerror("method has multiple receivers");
+ break;
+ }
+ rcvr = (yyvsp[(2) - (8)].list)->n;
+ if(rcvr->op != ODCLFIELD) {
+ yyerror("bad receiver in method");
+ break;
+ }
+
+ t = nod(OTFUNC, rcvr, N);
+ t->list = (yyvsp[(6) - (8)].list);
+ t->rlist = (yyvsp[(8) - (8)].list);
+
+ (yyval.node) = nod(ODCLFUNC, N, N);
+ (yyval.node)->shortname = newname((yyvsp[(4) - (8)].sym));
+ (yyval.node)->nname = methodname1((yyval.node)->shortname, rcvr->right);
+ (yyval.node)->nname->defn = (yyval.node);
+ (yyval.node)->nname->ntype = t;
+ (yyval.node)->nname->nointerface = nointerface;
+ declare((yyval.node)->nname, PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 208:
+#line 1394 "go.y"
+ {
+ Sym *s;
+ Type *t;
+
+ (yyval.node) = N;
+
+ s = (yyvsp[(1) - (5)].sym);
+ t = functype(N, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+
+ importsym(s, ONAME);
+ if(s->def != N && s->def->op == ONAME) {
+ if(eqtype(t, s->def->type)) {
+ dclcontext = PDISCARD; // since we skip funchdr below
+ break;
+ }
+ yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+ }
+
+ (yyval.node) = newname(s);
+ (yyval.node)->type = t;
+ declare((yyval.node), PFUNC);
+
+ funchdr((yyval.node));
+ }
+ break;
+
+ case 209:
+#line 1419 "go.y"
+ {
+ (yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right);
+ (yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list));
+
+ checkwidth((yyval.node)->type);
+ addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0, nointerface);
+ nointerface = 0;
+ funchdr((yyval.node));
+
+ // inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+ // (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+ // out by typecheck's lookdot as this $$->ttype. So by providing
+ // this back link here we avoid special casing there.
+ (yyval.node)->type->nname = (yyval.node);
+ }
+ break;
+
+ case 210:
+#line 1437 "go.y"
+ {
+ (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+ (yyval.node) = nod(OTFUNC, N, N);
+ (yyval.node)->list = (yyvsp[(3) - (5)].list);
+ (yyval.node)->rlist = (yyvsp[(5) - (5)].list);
+ }
+ break;
+
+ case 211:
+#line 1445 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 212:
+#line 1449 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ if((yyval.list) == nil)
+ (yyval.list) = list1(nod(OEMPTY, N, N));
+ }
+ break;
+
+ case 213:
+#line 1457 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 214:
+#line 1461 "go.y"
+ {
+ (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node)));
+ }
+ break;
+
+ case 215:
+#line 1465 "go.y"
+ {
+ (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0);
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ }
+ break;
+
+ case 216:
+#line 1472 "go.y"
+ {
+ closurehdr((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 217:
+#line 1478 "go.y"
+ {
+ (yyval.node) = closurebody((yyvsp[(3) - (4)].list));
+ fixlbrace((yyvsp[(2) - (4)].i));
+ }
+ break;
+
+ case 218:
+#line 1483 "go.y"
+ {
+ (yyval.node) = closurebody(nil);
+ }
+ break;
+
+ case 219:
+#line 1494 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 220:
+#line 1498 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list));
+ if(nsyntaxerrors == 0)
+ testdclstack();
+ nointerface = 0;
+ noescape = 0;
+ nosplit = 0;
+ }
+ break;
+
+ case 222:
+#line 1510 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 224:
+#line 1517 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 225:
+#line 1523 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 226:
+#line 1527 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 228:
+#line 1534 "go.y"
+ {
+ (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+ }
+ break;
+
+ case 229:
+#line 1540 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 230:
+#line 1544 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 231:
+#line 1550 "go.y"
+ {
+ NodeList *l;
+
+ Node *n;
+ l = (yyvsp[(1) - (3)].list);
+ if(l == nil) {
+ // ? symbol, during import (list1(N) == nil)
+ n = (yyvsp[(2) - (3)].node);
+ if(n->op == OIND)
+ n = n->left;
+ n = embedded(n->sym, importpkg);
+ n->right = (yyvsp[(2) - (3)].node);
+ n->val = (yyvsp[(3) - (3)].val);
+ (yyval.list) = list1(n);
+ break;
+ }
+
+ for(l=(yyvsp[(1) - (3)].list); l; l=l->next) {
+ l->n = nod(ODCLFIELD, l->n, (yyvsp[(2) - (3)].node));
+ l->n->val = (yyvsp[(3) - (3)].val);
+ }
+ }
+ break;
+
+ case 232:
+#line 1573 "go.y"
+ {
+ (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val);
+ (yyval.list) = list1((yyvsp[(1) - (2)].node));
+ }
+ break;
+
+ case 233:
+#line 1578 "go.y"
+ {
+ (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val);
+ (yyval.list) = list1((yyvsp[(2) - (4)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 234:
+#line 1584 "go.y"
+ {
+ (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N);
+ (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val);
+ (yyval.list) = list1((yyvsp[(2) - (3)].node));
+ }
+ break;
+
+ case 235:
+#line 1590 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+ (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+ (yyval.list) = list1((yyvsp[(3) - (5)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 236:
+#line 1597 "go.y"
+ {
+ (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+ (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+ (yyval.list) = list1((yyvsp[(3) - (5)].node));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 237:
+#line 1606 "go.y"
+ {
+ Node *n;
+
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ n = oldname((yyvsp[(1) - (1)].sym));
+ if(n->pack != N)
+ n->pack->used = 1;
+ }
+ break;
+
+ case 238:
+#line 1615 "go.y"
+ {
+ Pkg *pkg;
+
+ if((yyvsp[(1) - (3)].sym)->def == N || (yyvsp[(1) - (3)].sym)->def->op != OPACK) {
+ yyerror("%S is not a package", (yyvsp[(1) - (3)].sym));
+ pkg = localpkg;
+ } else {
+ (yyvsp[(1) - (3)].sym)->def->used = 1;
+ pkg = (yyvsp[(1) - (3)].sym)->def->pkg;
+ }
+ (yyval.sym) = restrictlookup((yyvsp[(3) - (3)].sym)->name, pkg);
+ }
+ break;
+
+ case 239:
+#line 1630 "go.y"
+ {
+ (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg);
+ }
+ break;
+
+ case 240:
+#line 1636 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+ ifacedcl((yyval.node));
+ }
+ break;
+
+ case 241:
+#line 1641 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym)));
+ }
+ break;
+
+ case 242:
+#line 1645 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym)));
+ yyerror("cannot parenthesize embedded type");
+ }
+ break;
+
+ case 243:
+#line 1652 "go.y"
+ {
+ // without func keyword
+ (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1);
+ (yyval.node) = nod(OTFUNC, fakethis(), N);
+ (yyval.node)->list = (yyvsp[(2) - (4)].list);
+ (yyval.node)->rlist = (yyvsp[(4) - (4)].list);
+ }
+ break;
+
+ case 245:
+#line 1666 "go.y"
+ {
+ (yyval.node) = nod(ONONAME, N, N);
+ (yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+ (yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 246:
+#line 1672 "go.y"
+ {
+ (yyval.node) = nod(ONONAME, N, N);
+ (yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+ (yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+ }
+ break;
+
+ case 248:
+#line 1681 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 249:
+#line 1685 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 250:
+#line 1690 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 251:
+#line 1694 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (2)].list);
+ }
+ break;
+
+ case 252:
+#line 1702 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 254:
+#line 1707 "go.y"
+ {
+ (yyval.node) = liststmt((yyvsp[(1) - (1)].list));
+ }
+ break;
+
+ case 256:
+#line 1712 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 262:
+#line 1723 "go.y"
+ {
+ (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N);
+ (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions
+ }
+ break;
+
+ case 263:
+#line 1728 "go.y"
+ {
+ NodeList *l;
+
+ (yyvsp[(1) - (4)].node)->defn = (yyvsp[(4) - (4)].node);
+ l = list1((yyvsp[(1) - (4)].node));
+ if((yyvsp[(4) - (4)].node))
+ l = list(l, (yyvsp[(4) - (4)].node));
+ (yyval.node) = liststmt(l);
+ }
+ break;
+
+ case 264:
+#line 1738 "go.y"
+ {
+ // will be converted to OFALL
+ (yyval.node) = nod(OXFALL, N, N);
+ (yyval.node)->xoffset = block;
+ }
+ break;
+
+ case 265:
+#line 1744 "go.y"
+ {
+ (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 266:
+#line 1748 "go.y"
+ {
+ (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 267:
+#line 1752 "go.y"
+ {
+ (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 268:
+#line 1756 "go.y"
+ {
+ (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N);
+ }
+ break;
+
+ case 269:
+#line 1760 "go.y"
+ {
+ (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N);
+ (yyval.node)->sym = dclstack; // context, for goto restrictions
+ }
+ break;
+
+ case 270:
+#line 1765 "go.y"
+ {
+ (yyval.node) = nod(ORETURN, N, N);
+ (yyval.node)->list = (yyvsp[(2) - (2)].list);
+ if((yyval.node)->list == nil && curfn != N) {
+ NodeList *l;
+
+ for(l=curfn->dcl; l; l=l->next) {
+ if(l->n->class == PPARAM)
+ continue;
+ if(l->n->class != PPARAMOUT)
+ break;
+ if(l->n->sym->def != l->n)
+ yyerror("%s is shadowed during return", l->n->sym->name);
+ }
+ }
+ }
+ break;
+
+ case 271:
+#line 1784 "go.y"
+ {
+ (yyval.list) = nil;
+ if((yyvsp[(1) - (1)].node) != N)
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 272:
+#line 1790 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (3)].list);
+ if((yyvsp[(3) - (3)].node) != N)
+ (yyval.list) = list((yyval.list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 273:
+#line 1798 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 274:
+#line 1802 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 275:
+#line 1808 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 276:
+#line 1812 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 277:
+#line 1818 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 278:
+#line 1822 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 279:
+#line 1828 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 280:
+#line 1832 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 281:
+#line 1841 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 282:
+#line 1845 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 283:
+#line 1849 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 284:
+#line 1853 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 285:
+#line 1858 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 286:
+#line 1862 "go.y"
+ {
+ (yyval.list) = (yyvsp[(1) - (2)].list);
+ }
+ break;
+
+ case 291:
+#line 1876 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 293:
+#line 1882 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 295:
+#line 1888 "go.y"
+ {
+ (yyval.node) = N;
+ }
+ break;
+
+ case 297:
+#line 1894 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 299:
+#line 1900 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 301:
+#line 1906 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 303:
+#line 1912 "go.y"
+ {
+ (yyval.val).ctype = CTxxx;
+ }
+ break;
+
+ case 305:
+#line 1922 "go.y"
+ {
+ importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval);
+ }
+ break;
+
+ case 306:
+#line 1926 "go.y"
+ {
+ importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type));
+ }
+ break;
+
+ case 307:
+#line 1930 "go.y"
+ {
+ importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node));
+ }
+ break;
+
+ case 308:
+#line 1934 "go.y"
+ {
+ importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node));
+ }
+ break;
+
+ case 309:
+#line 1938 "go.y"
+ {
+ importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type));
+ }
+ break;
+
+ case 310:
+#line 1942 "go.y"
+ {
+ if((yyvsp[(2) - (4)].node) == N) {
+ dclcontext = PEXTERN; // since we skip the funcbody below
+ break;
+ }
+
+ (yyvsp[(2) - (4)].node)->inl = (yyvsp[(3) - (4)].list);
+
+ funcbody((yyvsp[(2) - (4)].node));
+ importlist = list(importlist, (yyvsp[(2) - (4)].node));
+
+ if(debug['E']) {
+ print("import [%Z] func %lN \n", importpkg->path, (yyvsp[(2) - (4)].node));
+ if(debug['m'] > 2 && (yyvsp[(2) - (4)].node)->inl)
+ print("inl body:%+H\n", (yyvsp[(2) - (4)].node)->inl);
+ }
+ }
+ break;
+
+ case 311:
+#line 1962 "go.y"
+ {
+ (yyval.sym) = (yyvsp[(1) - (1)].sym);
+ structpkg = (yyval.sym)->pkg;
+ }
+ break;
+
+ case 312:
+#line 1969 "go.y"
+ {
+ (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+ importsym((yyvsp[(1) - (1)].sym), OTYPE);
+ }
+ break;
+
+ case 318:
+#line 1989 "go.y"
+ {
+ (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+ }
+ break;
+
+ case 319:
+#line 1993 "go.y"
+ {
+ // predefined name like uint8
+ (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+ if((yyvsp[(1) - (1)].sym)->def == N || (yyvsp[(1) - (1)].sym)->def->op != OTYPE) {
+ yyerror("%s is not a type", (yyvsp[(1) - (1)].sym)->name);
+ (yyval.type) = T;
+ } else
+ (yyval.type) = (yyvsp[(1) - (1)].sym)->def->type;
+ }
+ break;
+
+ case 320:
+#line 2003 "go.y"
+ {
+ (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type));
+ }
+ break;
+
+ case 321:
+#line 2007 "go.y"
+ {
+ (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type));
+ }
+ break;
+
+ case 322:
+#line 2011 "go.y"
+ {
+ (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type));
+ }
+ break;
+
+ case 323:
+#line 2015 "go.y"
+ {
+ (yyval.type) = tostruct((yyvsp[(3) - (4)].list));
+ }
+ break;
+
+ case 324:
+#line 2019 "go.y"
+ {
+ (yyval.type) = tointerface((yyvsp[(3) - (4)].list));
+ }
+ break;
+
+ case 325:
+#line 2023 "go.y"
+ {
+ (yyval.type) = ptrto((yyvsp[(2) - (2)].type));
+ }
+ break;
+
+ case 326:
+#line 2027 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(2) - (2)].type);
+ (yyval.type)->chan = Cboth;
+ }
+ break;
+
+ case 327:
+#line 2033 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (4)].type);
+ (yyval.type)->chan = Cboth;
+ }
+ break;
+
+ case 328:
+#line 2039 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (3)].type);
+ (yyval.type)->chan = Csend;
+ }
+ break;
+
+ case 329:
+#line 2047 "go.y"
+ {
+ (yyval.type) = typ(TCHAN);
+ (yyval.type)->type = (yyvsp[(3) - (3)].type);
+ (yyval.type)->chan = Crecv;
+ }
+ break;
+
+ case 330:
+#line 2055 "go.y"
+ {
+ (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+ }
+ break;
+
+ case 331:
+#line 2061 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type)));
+ if((yyvsp[(1) - (3)].sym))
+ (yyval.node)->left = newname((yyvsp[(1) - (3)].sym));
+ (yyval.node)->val = (yyvsp[(3) - (3)].val);
+ }
+ break;
+
+ case 332:
+#line 2068 "go.y"
+ {
+ Type *t;
+
+ t = typ(TARRAY);
+ t->bound = -1;
+ t->type = (yyvsp[(3) - (4)].type);
+
+ (yyval.node) = nod(ODCLFIELD, N, typenod(t));
+ if((yyvsp[(1) - (4)].sym))
+ (yyval.node)->left = newname((yyvsp[(1) - (4)].sym));
+ (yyval.node)->isddd = 1;
+ (yyval.node)->val = (yyvsp[(4) - (4)].val);
+ }
+ break;
+
+ case 333:
+#line 2084 "go.y"
+ {
+ Sym *s;
+ Pkg *p;
+
+ if((yyvsp[(1) - (3)].sym) != S && strcmp((yyvsp[(1) - (3)].sym)->name, "?") != 0) {
+ (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (3)].sym)), typenod((yyvsp[(2) - (3)].type)));
+ (yyval.node)->val = (yyvsp[(3) - (3)].val);
+ } else {
+ s = (yyvsp[(2) - (3)].type)->sym;
+ if(s == S && isptr[(yyvsp[(2) - (3)].type)->etype])
+ s = (yyvsp[(2) - (3)].type)->type->sym;
+ p = importpkg;
+ if((yyvsp[(1) - (3)].sym) != S)
+ p = (yyvsp[(1) - (3)].sym)->pkg;
+ (yyval.node) = embedded(s, p);
+ (yyval.node)->right = typenod((yyvsp[(2) - (3)].type));
+ (yyval.node)->val = (yyvsp[(3) - (3)].val);
+ }
+ }
+ break;
+
+ case 334:
+#line 2106 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list))));
+ }
+ break;
+
+ case 335:
+#line 2110 "go.y"
+ {
+ (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)));
+ }
+ break;
+
+ case 336:
+#line 2115 "go.y"
+ {
+ (yyval.list) = nil;
+ }
+ break;
+
+ case 338:
+#line 2122 "go.y"
+ {
+ (yyval.list) = (yyvsp[(2) - (3)].list);
+ }
+ break;
+
+ case 339:
+#line 2126 "go.y"
+ {
+ (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))));
+ }
+ break;
+
+ case 340:
+#line 2136 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+ }
+ break;
+
+ case 341:
+#line 2140 "go.y"
+ {
+ (yyval.node) = nodlit((yyvsp[(2) - (2)].val));
+ switch((yyval.node)->val.ctype){
+ case CTINT:
+ case CTRUNE:
+ mpnegfix((yyval.node)->val.u.xval);
+ break;
+ case CTFLT:
+ mpnegflt((yyval.node)->val.u.fval);
+ break;
+ case CTCPLX:
+ mpnegflt(&(yyval.node)->val.u.cval->real);
+ mpnegflt(&(yyval.node)->val.u.cval->imag);
+ break;
+ default:
+ yyerror("bad negated constant");
+ }
+ }
+ break;
+
+ case 342:
+#line 2159 "go.y"
+ {
+ (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg));
+ if((yyval.node)->op != OLITERAL)
+ yyerror("bad constant %S", (yyval.node)->sym);
+ }
+ break;
+
+ case 344:
+#line 2168 "go.y"
+ {
+ if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) {
+ (yyval.node) = (yyvsp[(2) - (5)].node);
+ mpaddfixfix((yyvsp[(2) - (5)].node)->val.u.xval, (yyvsp[(4) - (5)].node)->val.u.xval, 0);
+ break;
+ }
+ (yyvsp[(4) - (5)].node)->val.u.cval->real = (yyvsp[(4) - (5)].node)->val.u.cval->imag;
+ mpmovecflt(&(yyvsp[(4) - (5)].node)->val.u.cval->imag, 0.0);
+ (yyval.node) = nodcplxlit((yyvsp[(2) - (5)].node)->val, (yyvsp[(4) - (5)].node)->val);
+ }
+ break;
+
+ case 347:
+#line 2184 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 348:
+#line 2188 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 349:
+#line 2194 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 350:
+#line 2198 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+ case 351:
+#line 2204 "go.y"
+ {
+ (yyval.list) = list1((yyvsp[(1) - (1)].node));
+ }
+ break;
+
+ case 352:
+#line 2208 "go.y"
+ {
+ (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+ }
+ break;
+
+
+/* Line 1267 of yacc.c. */
+#line 4907 "y.tab.c"
+ default: break;
+ }
+ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if ! YYERROR_VERBOSE
+ yyerror (YY_("syntax error"));
+#else
+ {
+ YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+ if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+ {
+ YYSIZE_T yyalloc = 2 * yysize;
+ if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+ yyalloc = YYSTACK_ALLOC_MAXIMUM;
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+ yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+ if (yymsg)
+ yymsg_alloc = yyalloc;
+ else
+ {
+ yymsg = yymsgbuf;
+ yymsg_alloc = sizeof yymsgbuf;
+ }
+ }
+
+ if (0 < yysize && yysize <= yymsg_alloc)
+ {
+ (void) yysyntax_error (yymsg, yystate, yychar);
+ yyerror (yymsg);
+ }
+ else
+ {
+ yyerror (YY_("syntax error"));
+ if (yysize != 0)
+ goto yyexhaustedlab;
+ }
+ }
+#endif
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* Return failure if at end of input. */
+ if (yychar == YYEOF)
+ YYABORT;
+ }
+ else
+ {
+ yydestruct ("Error: discarding",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+ /* Pacify compilers like GCC when the user code never invokes
+ YYERROR and the label yyerrorlab therefore never appears in user
+ code. */
+ if (/*CONSTCOND*/ 0)
+ goto yyerrorlab;
+
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYERROR. */
+ YYPOPSTACK (yylen);
+ yylen = 0;
+ YY_STACK_PRINT (yyss, yyssp);
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping",
+ yystos[yystate], yyvsp);
+ YYPOPSTACK (1);
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here. |
+`-------------------------------------------------*/
+yyexhaustedlab:
+ yyerror (YY_("memory exhausted"));
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+ if (yychar != YYEOF && yychar != YYEMPTY)
+ yydestruct ("Cleanup: discarding lookahead",
+ yytoken, &yylval);
+ /* Do not reclaim the symbols of the rule which action triggered
+ this YYABORT or YYACCEPT. */
+ YYPOPSTACK (yylen);
+ YY_STACK_PRINT (yyss, yyssp);
+ while (yyssp != yyss)
+ {
+ yydestruct ("Cleanup: popping",
+ yystos[*yyssp], yyvsp);
+ YYPOPSTACK (1);
+ }
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+ if (yymsg != yymsgbuf)
+ YYSTACK_FREE (yymsg);
+#endif
+ /* Make sure YYID is used. */
+ return YYID (yyresult);
+}
+
+
+#line 2212 "go.y"
+
+
+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/y.tab.h b/src/cmd/gc/y.tab.h
new file mode 100644
index 0000000..d01fbe1
--- /dev/null
+++ b/src/cmd/gc/y.tab.h
@@ -0,0 +1,167 @@
+/* A Bison parser, made by GNU Bison 2.3. */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+ Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, you may create a larger work that contains
+ part or all of the Bison parser skeleton and distribute that work
+ under terms of your choice, so long as that work isn't itself a
+ parser generator using the skeleton or a modified version thereof
+ as a parser skeleton. Alternatively, if you modify or redistribute
+ the parser skeleton itself, you may (at your option) remove this
+ special exception, which will cause the skeleton and the resulting
+ Bison output files to be licensed under the GNU General Public
+ License without this special exception.
+
+ This special exception was added by the Free Software Foundation in
+ version 2.2 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ LLITERAL = 258,
+ LASOP = 259,
+ LCOLAS = 260,
+ LBREAK = 261,
+ LCASE = 262,
+ LCHAN = 263,
+ LCONST = 264,
+ LCONTINUE = 265,
+ LDDD = 266,
+ LDEFAULT = 267,
+ LDEFER = 268,
+ LELSE = 269,
+ LFALL = 270,
+ LFOR = 271,
+ LFUNC = 272,
+ LGO = 273,
+ LGOTO = 274,
+ LIF = 275,
+ LIMPORT = 276,
+ LINTERFACE = 277,
+ LMAP = 278,
+ LNAME = 279,
+ LPACKAGE = 280,
+ LRANGE = 281,
+ LRETURN = 282,
+ LSELECT = 283,
+ LSTRUCT = 284,
+ LSWITCH = 285,
+ LTYPE = 286,
+ LVAR = 287,
+ LANDAND = 288,
+ LANDNOT = 289,
+ LBODY = 290,
+ LCOMM = 291,
+ LDEC = 292,
+ LEQ = 293,
+ LGE = 294,
+ LGT = 295,
+ LIGNORE = 296,
+ LINC = 297,
+ LLE = 298,
+ LLSH = 299,
+ LLT = 300,
+ LNE = 301,
+ LOROR = 302,
+ LRSH = 303,
+ NotPackage = 304,
+ NotParen = 305,
+ PreferToRightParen = 306
+ };
+#endif
+/* Tokens. */
+#define LLITERAL 258
+#define LASOP 259
+#define LCOLAS 260
+#define LBREAK 261
+#define LCASE 262
+#define LCHAN 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 28 "go.y"
+{
+ Node* node;
+ NodeList* list;
+ Type* type;
+ Sym* sym;
+ struct Val val;
+ int i;
+}
+/* Line 1529 of yacc.c. */
+#line 160 "y.tab.h"
+ YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h
new file mode 100644
index 0000000..d0dd639
--- /dev/null
+++ b/src/cmd/gc/yerr.h
@@ -0,0 +1,79 @@
+// 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.
+
+ {222, ',',
+ "unexpected comma during import block"},
+
+ {32, ';',
+ "missing import path; require quoted string"},
+
+ {380, ';',
+ "missing { after if clause"},
+
+ {401, ';',
+ "missing { after switch clause"},
+
+ {239, ';',
+ "missing { after for clause"},
+
+ {478, LBODY,
+ "missing { after for clause"},
+
+ {22, '{',
+ "unexpected semicolon or newline before {"},
+
+ {145, ';',
+ "unexpected semicolon or newline in type declaration"},
+
+ {37, '}',
+ "unexpected } in channel type"},
+
+ {37, ')',
+ "unexpected ) in channel type"},
+
+ {37, ',',
+ "unexpected comma in channel type"},
+
+ {441, LELSE,
+ "unexpected semicolon or newline before else"},
+
+ {259, ',',
+ "name list not allowed in interface type"},
+
+ {239, LVAR,
+ "var declaration not allowed in for initializer"},
+
+ {65, '{',
+ "unexpected { at end of statement"},
+
+ {379, '{',
+ "unexpected { at end of statement"},
+
+ {126, ';',
+ "argument to go/defer must be function call"},
+
+ {428, ';',
+ "need trailing comma before newline in composite literal"},
+
+ {439, ';',
+ "need trailing comma before newline in composite literal"},
+
+ {113, LNAME,
+ "nested func not allowed"},
+
+ {647, ';',
+ "else must be followed by if or statement block"}
+};