diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2014-06-19 09:22:53 +0200 |
---|---|---|
committer | Michael Stapelberg <stapelberg@debian.org> | 2014-06-19 09:22:53 +0200 |
commit | 8a39ee361feb9bf46d728ff1ba4f07ca1d9610b1 (patch) | |
tree | 4449f2036cccf162e8417cc5841a35815b3e7ac5 /src/cmd/gc | |
parent | c8bf49ef8a92e2337b69c14b9b88396efe498600 (diff) | |
download | golang-51f2ca399fb8da86b2e7b3a0582e083fab731a98.tar.gz |
Imported Upstream version 1.3upstream/1.3
Diffstat (limited to 'src/cmd/gc')
39 files changed, 5453 insertions, 2538 deletions
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c index 8e9677e75..b809640e4 100644 --- a/src/cmd/gc/align.c +++ b/src/cmd/gc/align.c @@ -175,11 +175,11 @@ dowidth(Type *t) case TFLOAT64: case TCOMPLEX64: w = 8; - t->align = widthptr; + t->align = widthreg; break; case TCOMPLEX128: w = 16; - t->align = widthptr; + t->align = widthreg; break; case TPTR32: w = 4; @@ -288,10 +288,10 @@ dowidth(Type *t) // compute their widths as side-effect. t1 = t->type; w = widstruct(t->type, *getthis(t1), 0, 0); - w = widstruct(t->type, *getinarg(t1), w, widthptr); - w = widstruct(t->type, *getoutarg(t1), w, widthptr); + w = widstruct(t->type, *getinarg(t1), w, widthreg); + w = widstruct(t->type, *getoutarg(t1), w, widthreg); t1->argwid = w; - if(w%widthptr) + if(w%widthreg) warn("bad type %T %d\n", t1, w); t->align = 1; break; diff --git a/src/cmd/gc/array.c b/src/cmd/gc/array.c new file mode 100644 index 000000000..5e53c1ff0 --- /dev/null +++ b/src/cmd/gc/array.c @@ -0,0 +1,129 @@ +// 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); +} + +int32 +arrayindexof(Array *array, void *element) +{ + void *p; + int32 i; + + for(i = 0; i < array->length; i++) { + p = arrayget(array, i); + if(memcmp(p, &element, array->size) == 0) + return i; + } + return -1; +} + +void +arraysort(Array *array, int (*cmp)(const void*, const void*)) +{ + qsort(array->data, array->length, array->size, cmp); +} diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c index c0fd4d85e..2e79f6f1d 100644 --- a/src/cmd/gc/bits.c +++ b/src/cmd/gc/bits.c @@ -153,7 +153,7 @@ Qconv(Fmt *fp) if(var[i].node == N || var[i].node->sym == S) fmtprint(fp, "$%d", i); else { - fmtprint(fp, "%s", var[i].node->sym->name); + fmtprint(fp, "%s(%d)", var[i].node->sym->name, i); if(var[i].offset != 0) fmtprint(fp, "%+lld", (vlong)var[i].offset); } diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index 309dc1ea0..5ca5aeb77 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -5,6 +5,7 @@ char *runtimeimport = "func @\"\".new (@\"\".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" @@ -23,11 +24,16 @@ char *runtimeimport = "func @\"\".printnl ()\n" "func @\"\".printsp ()\n" "func @\"\".goprintf ()\n" - "func @\"\".concatstring ()\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" @@ -38,8 +44,8 @@ char *runtimeimport = "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 @\"\".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" @@ -60,26 +66,24 @@ char *runtimeimport = "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\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 (@\"\".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 (@\"\".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 @\"\".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 @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n" "func @\"\".mapiternext (@\"\".hiter·1 *any)\n" - "func @\"\".mapiter1 (@\"\".hiter·2 *any) (@\"\".key·1 any)\n" - "func @\"\".mapiter2 (@\"\".hiter·3 *any) (@\"\".key·1 any, @\"\".val·2 any)\n" "func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n" - "func @\"\".chanrecv1 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any) (@\"\".elem·1 any)\n" - "func @\"\".chanrecv2 (@\"\".chanType·3 *byte, @\"\".hchan·4 <-chan any) (@\"\".elem·1 any, @\"\".received·2 bool)\n" - "func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 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 @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 any) (? bool)\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 (@\"\".size·2 int32) (@\"\".sel·1 *byte)\n" diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c index 92834a97b..2efbbc565 100644 --- a/src/cmd/gc/bv.c +++ b/src/cmd/gc/bv.c @@ -11,12 +11,24 @@ enum { WORDBITS = 32, }; -uintptr +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) { @@ -34,26 +46,49 @@ bvalloc(int32 n) return bv; } +/* difference */ void -bvset(Bvec *bv, int32 i) +bvandnot(Bvec *dst, Bvec *src1, Bvec *src2) { - uint32 mask; + int32 i, w; - 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; + 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 -bvres(Bvec *bv, int32 i) +bvcopy(Bvec *dst, Bvec *src) { - uint32 mask; + memmove(dst->b, src->b, bvsize(dst->n)); +} - if(i < 0 || i >= bv->n) - fatal("bvres: index %d is out of bounds with length %d\n", i, bv->n); - mask = ~(1 << (i % WORDBITS)); - bv->b[i / WORDBITS] &= mask; +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 @@ -63,7 +98,7 @@ 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); - mask = 1 << (i % WORDBITS); + mask = 1U << (i % WORDBITS); word = bv->b[i / WORDBITS] & mask; return word ? 1 : 0; } @@ -78,3 +113,74 @@ bvisempty(Bvec *bv) 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 index 5a84dfb1b..ad4e5bd02 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -161,6 +161,7 @@ makeclosure(Node *func) // 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) @@ -252,6 +253,14 @@ walkclosure(Node *func, NodeList **init) // 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; @@ -361,9 +370,12 @@ makepartialcall(Node *fn, Type *t0, Node *meth) // 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; @@ -441,6 +453,14 @@ walkpartialcall(Node *n, NodeList **init) // 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 index cfb1f0ade..143c1730d 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -22,19 +22,22 @@ Mpflt* truncfltlit(Mpflt *oldv, Type *t) { double d; - float f; 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) - // botch -- this assumes that compiler fp - // has same precision as runtime fp switch(t->etype) { case TFLOAT64: d = mpgetflt(fv); @@ -42,10 +45,9 @@ truncfltlit(Mpflt *oldv, Type *t) break; case TFLOAT32: - d = mpgetflt(fv); - f = d; - d = f; + d = mpgetflt32(fv); mpmovecflt(fv, d); + break; } return fv; @@ -235,7 +237,6 @@ convlit1(Node **np, Type *t, int explicit) n->val = toflt(n->val); // flowthrough case CTFLT: - overflow(n->val, t); n->val.u.fval = truncfltlit(n->val.u.fval, t); break; } @@ -521,6 +522,7 @@ evconst(Node *n) int wl, wr, lno, et; Val v, rv; Mpint b; + NodeList *l1, *l2; // pick off just the opcodes that can be // constant evaluated. @@ -528,7 +530,6 @@ evconst(Node *n) default: return; case OADD: - case OADDSTR: case OAND: case OANDAND: case OANDNOT: @@ -559,6 +560,47 @@ evconst(Node *n) 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; @@ -861,15 +903,6 @@ evconst(Node *n) if(cmpslit(nl, nr) > 0) goto settrue; goto setfalse; - case TUP(OADDSTR, CTSTR): - len = v.u.sval->len + rv.u.sval->len; - str = mal(sizeof(*str) + len); - str->len = len; - memcpy(str->s, v.u.sval->s, v.u.sval->len); - memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len); - str->len = len; - v.u.sval = str; - break; case TUP(OOROR, CTBOOL): if(v.u.bval || rv.u.bval) @@ -918,6 +951,7 @@ unary: case TUP(OCONV, CTFLT): case TUP(OCONV, CTSTR): convlit1(&nl, n->type, 1); + v = nl->val; break; case TUP(OPLUS, CTINT): @@ -1144,7 +1178,10 @@ defaultlit(Node **np, Type *t) } if(n->val.ctype == CTNIL) { lineno = lno; - yyerror("use of untyped nil"); + if(!n->diag) { + yyerror("use of untyped nil"); + n->diag = 1; + } n->type = T; break; } @@ -1559,7 +1596,7 @@ isgoconst(Node *n) case ONAME: l = n->sym->def; - if(l->op == OLITERAL && n->val.ctype != CTNIL) + if(l && l->op == OLITERAL && n->val.ctype != CTNIL) return 1; break; @@ -1594,10 +1631,25 @@ hascallchan(Node *n) if(n == N) return 0; switch(n->op) { + case OAPPEND: case OCALL: case OCALLFUNC: - case OCALLMETH: 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; } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index c7d13ef06..73c2581be 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -130,7 +130,7 @@ dumpdcl(char *st) } print(" '%s'", d->name); s = pkglookup(d->name, d->pkg); - print(" %lS\n", s); + print(" %S\n", s); } } @@ -643,8 +643,8 @@ funcargs(Node *nt) fatal("funcargs out %O", n->op); if(n->left == N) { - // give it a name so escape analysis has nodes to work with - snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); + // 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; } @@ -652,14 +652,20 @@ funcargs(Node *nt) n->left->op = ONAME; if(isblank(n->left)) { - // Give it a name so we can assign to it during return. - // preserve the original in ->orig + // 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; - - snprint(namebuf, sizeof(namebuf), "~anon%d", gen++); - n->left->sym = lookup(namebuf); } n->left->ntype = n->right; @@ -941,8 +947,6 @@ interfacefield(Node *n) f->nname = n->left; f->embedded = n->embedded; f->sym = f->nname->sym; - if(importpkg && !exportname(f->sym->name)) - f->sym = pkglookup(f->sym->name, structpkg); } } else { @@ -1211,7 +1215,7 @@ functype(Node *this, NodeList *in, NodeList *out) 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] != '~') + if(s != S && (s->name[0] != '~' || s->name[1] != 'r')) // ~r%d is the name invented for an unnamed result t->outnamed = 1; } @@ -1434,6 +1438,8 @@ funccompile(Node *n, int isclosure) // 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; diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 791967708..03df93a3e 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -37,6 +37,8 @@ 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 @@ -50,12 +52,14 @@ Flags: 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 + disallow importing packages not marked as safe; implies -nolocalimports -V print the compiler version -race diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index b84b66ef1..78624d7cb 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -185,12 +185,12 @@ visitcode(Node *n, uint32 min) typedef struct EscState EscState; static void escfunc(EscState*, Node *func); -static void esclist(EscState*, NodeList *l); -static void esc(EscState*, Node *n); +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*); +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); @@ -204,6 +204,13 @@ struct EscState { // 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. @@ -269,7 +276,13 @@ analyze(NodeList *all, int recursive) 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; @@ -328,6 +341,7 @@ escfunc(EscState *e, Node *func) 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) @@ -335,7 +349,6 @@ escfunc(EscState *e, Node *func) else ll->n->esc = EscNone; // prime for escflood later e->noesc = list(e->noesc, ll->n); - ll->n->escloopdepth = 1; break; } } @@ -347,7 +360,7 @@ escfunc(EscState *e, Node *func) escflows(e, &e->theSink, ll->n); escloopdepthlist(e, curfn->nbody); - esclist(e, curfn->nbody); + esclist(e, curfn->nbody, curfn); curfn = savefn; e->loopdepth = saveld; } @@ -405,14 +418,14 @@ escloopdepth(EscState *e, Node *n) } static void -esclist(EscState *e, NodeList *l) +esclist(EscState *e, NodeList *l, Node *up) { for(; l; l=l->next) - esc(e, l->n); + esc(e, l->n, up); } static void -esc(EscState *e, Node *n) +esc(EscState *e, Node *n, Node *up) { int lno; NodeList *ll, *lr; @@ -423,18 +436,32 @@ esc(EscState *e, Node *n) 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++; - esc(e, n->left); - esc(e, n->right); - esc(e, n->ntest); - esc(e, n->nincr); - esclist(e, n->ninit); - esclist(e, n->nbody); - esclist(e, n->nelse); - esclist(e, n->list); - esclist(e, n->rlist); + // 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--; @@ -520,7 +547,7 @@ esc(EscState *e, Node *n) case OCALLMETH: case OCALLFUNC: case OCALLINTER: - esccall(e, n); + esccall(e, n, up); break; case OAS2FUNC: // x,y = f() @@ -628,7 +655,6 @@ esc(EscState *e, Node *n) escassign(e, n, a); } // fallthrough - case OADDR: case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: @@ -637,6 +663,35 @@ esc(EscState *e, Node *n) 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; @@ -748,8 +803,8 @@ escassign(EscState *e, Node *dst, Node *src) case ODOTTYPE: case ODOTTYPE2: case OSLICE: - case OSLICEARR: case OSLICE3: + case OSLICEARR: case OSLICE3ARR: // Conversions, field access, slice all preserve the input value. escassign(e, dst, src->left); @@ -792,24 +847,34 @@ escassign(EscState *e, Node *dst, Node *src) lineno = lno; } -static void +static int escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src) { - int em; + int em, em0; em = parsetag(note); - + if(em == EscUnknown) { escassign(e, &e->theSink, src); - return; + return em; } - - for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next) + + 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 @@ -819,7 +884,7 @@ escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src) // different for methods vs plain functions and for imported vs // this-package static void -esccall(EscState *e, Node *n) +esccall(EscState *e, Node *n, Node *up) { NodeList *ll, *lr; Node *a, *fn, *src; @@ -856,7 +921,7 @@ esccall(EscState *e, Node *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); @@ -876,6 +941,10 @@ esccall(EscState *e, Node *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 @@ -916,8 +985,12 @@ esccall(EscState *e, Node *n) // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval); // Receiver. - if(n->op != OCALLFUNC) - escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left); + 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; @@ -926,11 +999,40 @@ esccall(EscState *e, Node *n) 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; } - escassignfromtag(e, t->note, n->escretval, 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; @@ -1029,19 +1131,30 @@ escwalk(EscState *e, int level, Node *dst, Node *src) // Input parameter flowing to output parameter? if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) { - if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) { - if(src->esc != EscScope && src->esc != EscHeap) { + 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) + EscBits); + 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; } } } - leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth); + // 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: @@ -1094,6 +1207,10 @@ escwalk(EscState *e, int level, Node *dst, Node *src) break; case ODOT: + case OSLICE: + case OSLICEARR: + case OSLICE3: + case OSLICE3ARR: escwalk(e, level, dst, src->left); break; @@ -1103,7 +1220,6 @@ escwalk(EscState *e, int level, Node *dst, Node *src) break; } // fall through - case OSLICE: case ODOTPTR: case OINDEXMAP: case OIND: diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c index 31bcdf8e7..da5984ceb 100644 --- a/src/cmd/gc/export.c +++ b/src/cmd/gc/export.c @@ -354,7 +354,7 @@ dumpexport(void) lno = lineno; - Bprint(bout, "\n$$ // exports\n package %s", localpkg->name); + Bprint(bout, "\n$$\npackage %s", localpkg->name); if(safemode) Bprint(bout, " safe"); Bprint(bout, "\n"); @@ -369,8 +369,7 @@ dumpexport(void) dumpsym(l->n->sym); } - Bprint(bout, "\n$$ // local types\n\n$$\n"); // 6l expects this. (see ld/go.c) - + Bprint(bout, "\n$$\n"); lineno = lno; } diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 9cd344870..b5f8a834f 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -17,7 +17,7 @@ // Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg) // // %J Node* Node details -// Flags: "%hJ" supresses things not relevant until walk. +// Flags: "%hJ" suppresses things not relevant until walk. // // %V Val* Constant values // @@ -102,75 +102,7 @@ setfmode(unsigned long *flags) static int Lconv(Fmt *fp) { - struct - { - Hist* incl; /* start of this include file */ - int32 idel; /* delta line number to apply to include */ - Hist* line; /* start of this #line directive */ - int32 ldel; /* delta line number to apply to #line */ - } a[HISTSZ]; - int32 lno, d; - int i, n; - Hist *h; - - lno = va_arg(fp->args, int32); - - n = 0; - for(h=hist; h!=H; h=h->link) { - if(h->offset < 0) - continue; - if(lno < h->line) - break; - if(h->name) { - if(h->offset > 0) { - // #line directive - if(n > 0 && n < HISTSZ) { - a[n-1].line = h; - a[n-1].ldel = h->line - h->offset + 1; - } - } else { - // beginning of file - if(n < HISTSZ) { - a[n].incl = h; - a[n].idel = h->line; - a[n].line = 0; - } - n++; - } - continue; - } - n--; - if(n > 0 && n < HISTSZ) { - d = h->line - a[n].incl->line; - a[n-1].ldel += d; - a[n-1].idel += d; - } - } - - if(n > HISTSZ) - n = HISTSZ; - - for(i=n-1; i>=0; i--) { - if(i != n-1) { - if(fp->flags & ~(FmtWidth|FmtPrec)) - break; - fmtprint(fp, " "); - } - if(debug['L'] || (fp->flags&FmtLong)) - fmtprint(fp, "%s/", pathname); - if(a[i].line) - fmtprint(fp, "%s:%d[%s:%d]", - a[i].line->name, lno-a[i].ldel+1, - a[i].incl->name, lno-a[i].idel+1); - else - fmtprint(fp, "%s:%d", - a[i].incl->name, lno-a[i].idel+1); - lno = a[i].incl->line - 1; // now print out start of this file - } - if(n == 0) - fmtprint(fp, "<unknown line number>"); - - return 0; + return linklinefmt(ctxt, fp); } static char* @@ -702,9 +634,17 @@ typefmt(Fmt *fp, Type *t) 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->hmap != T) { - t = t->hmap; - return fmtprint(fp, "map.bucket[%T]%T", t->down, t->type); + 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) { @@ -738,12 +678,17 @@ typefmt(Fmt *fp, Type *t) if(!(fp->flags&FmtShort)) { s = t->sym; - // Take the name from the original, lest we substituted it with ~anon%d + // 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] == '~') - s = S; + 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; } @@ -1099,6 +1044,7 @@ static int opprec[] = { [OEMPTY] = -1, [OFALL] = -1, [OFOR] = -1, + [OGOTO] = -1, [OIF] = -1, [OLABEL] = -1, [OPROC] = -1, @@ -1163,7 +1109,10 @@ exprfmt(Fmt *f, Node *n, int prec) case PAUTO: case PPARAM: case PPARAMOUT: - if(fmtmode == FExp && n->sym && !isblanksym(n->sym) && n->vargen > 0) + // _ 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); } @@ -1390,7 +1339,6 @@ exprfmt(Fmt *f, Node *n, int prec) // Binary case OADD: - case OADDSTR: case OAND: case OANDAND: case OANDNOT: @@ -1415,6 +1363,14 @@ exprfmt(Fmt *f, Node *n, int prec) 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); @@ -1572,6 +1528,9 @@ Sconv(Fmt *fp) 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>"); diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index ada16eacc..cf630f348 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -301,6 +301,9 @@ gen(Node *n) break; case OLABEL: + if(isblanksym(n->left->sym)) + break; + lab = newlab(n); // if there are pending gotos, resolve them all to the current pc. @@ -495,6 +498,11 @@ gen(Node *n) case OCHECKNIL: cgen_checknil(n->left); + break; + + case OVARKILL: + gvarkill(n->left); + break; } ret: @@ -562,8 +570,7 @@ cgen_proc(Node *n, int proc) /* * generate declaration. - * nothing to do for on-stack automatics, - * but might have to allocate heap copy + * have to allocate heap copy * for escaped variables. */ static void @@ -739,6 +746,8 @@ cgen_as(Node *nl, Node *nr) if(tl == T) return; if(isfat(tl)) { + if(nl->op == ONAME) + gvardef(nl); clearfat(nl); return; } @@ -767,10 +776,18 @@ cgen_eface(Node *n, Node *res) * 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(n->right, &dst); + cgen(tmp, &dst); + dst.xoffset -= widthptr; cgen(n->left, &dst); } @@ -787,7 +804,7 @@ cgen_eface(Node *n, Node *res) void cgen_slice(Node *n, Node *res) { - Node src, dst, *cap, *len, *offs, *add; + Node src, dst, *cap, *len, *offs, *add, *base; cap = n->list->n; len = n->list->next->n; @@ -795,24 +812,15 @@ cgen_slice(Node *n, Node *res) if(n->list->next->next) offs = n->list->next->next->n; - // dst.len = hi [ - lo ] - dst = *res; - dst.xoffset += Array_nel; - dst.type = types[simtype[TUINT]]; - cgen(len, &dst); - - if(n->op != OSLICESTR) { - // dst.cap = cap [ - lo ] - dst = *res; - dst.xoffset += Array_cap; - dst.type = types[simtype[TUINT]]; - cgen(cap, &dst); - } - - // dst.array = src.array [ + lo *width ] - dst = *res; - dst.xoffset += Array_array; - dst.type = types[TUINTPTR]; + // 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]); if(isnil(n->left)) { tempname(&src, n->left->type); @@ -821,24 +829,49 @@ cgen_slice(Node *n, Node *res) src = *n->left; if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR) src.xoffset += Array_array; - src.type = types[TUINTPTR]; 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, &dst); - cgen_checknil(&dst); + cgen(&src, base); + cgen_checknil(base); if(offs != N) { - add = nod(OADD, &dst, offs); + add = nod(OADD, base, offs); typecheck(&add, Erv); - cgen(add, &dst); + cgen(add, base); } } else if(offs == N) { - cgen(&src, &dst); + src.type = types[tptr]; + cgen(&src, base); } else { - add = nod(OADD, &src, offs); + src.type = types[tptr]; + add = nod(OADDPTR, &src, offs); typecheck(&add, Erv); - cgen(add, &dst); + cgen(add, base); + } + + // committed to the update + gvardef(res); + + // 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(len, &dst); + + if(n->op != OSLICESTR) { + // dst.cap = cap [ - lo ] + dst = *res; + dst.xoffset += Array_cap; + dst.type = types[simtype[TUINT]]; + cgen(cap, &dst); } } @@ -935,5 +968,5 @@ temp(Type *t) n = nod(OXXX, N, N); tempname(n, t); n->sym->def->used = 1; - return n; + return n->orig; } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 562f16890..413e71069 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -3,6 +3,7 @@ // license that can be found in the LICENSE file. #include <bio.h> +#include <link.h> #undef OAPPEND @@ -31,7 +32,6 @@ enum STRINGSZ = 200, MAXALIGN = 7, UINF = 100, - HISTSZ = 10, PRIME1 = 3, @@ -129,6 +129,10 @@ struct Val } 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; @@ -190,6 +194,8 @@ struct Type // 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 @@ -230,8 +236,10 @@ enum EscNone, EscReturn, EscNever, - EscBits = 4, + 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 @@ -277,6 +285,7 @@ struct Node 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; @@ -392,6 +401,7 @@ struct Sym int32 block; // blocknumber to catch redeclaration int32 lastlineno; // last declaration for diagnostic Pkg* origpkg; // original package for . import + LSym* lsym; }; #define S ((Sym*)0) @@ -420,16 +430,6 @@ struct Iter Node* n; }; -typedef struct Hist Hist; -struct Hist -{ - Hist* link; - char* name; - int32 line; - int32 offset; -}; -#define H ((Hist*)0) - // Node ops. enum { @@ -447,11 +447,13 @@ enum OSUB, // x - y OOR, // x | y OXOR, // x ^ y + OADDPTR, // ptr + uintptr, inserted by compiler only, used to avoid unsafe type changes during codegen 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) @@ -506,7 +508,7 @@ enum 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 specfic OMAKEXXX. + OMAKE, // make, typechecking may convert to a more specific OMAKEXXX. OMAKECHAN, // make(chan int) OMAKEMAP, // make(map[string]int) OMAKESLICE, // make([]int, 0) @@ -528,7 +530,7 @@ enum OPRINTN, // println OPAREN, // (x) OSEND, // c <- x - OSLICE, // v[1:2], typechecking may convert to a more specfic OSLICEXXX. + 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. @@ -583,6 +585,7 @@ enum 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. @@ -724,6 +727,7 @@ struct Var { vlong offset; Node* node; + Var* nextinnode; int width; char name; char etype; @@ -804,9 +808,6 @@ struct Magic int ua; // output - adder }; -typedef struct Prog Prog; -#pragma incomplete Prog - struct Label { uchar used; @@ -859,9 +860,6 @@ EXTERN Io pushedio; EXTERN int32 lexlineno; EXTERN int32 lineno; EXTERN int32 prevlineno; -EXTERN char* pathname; -EXTERN Hist* hist; -EXTERN Hist* ehist; EXTERN char* infile; EXTERN char* outfile; @@ -870,6 +868,7 @@ 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]; @@ -950,7 +949,6 @@ EXTERN Node* lasttype; EXTERN vlong maxarg; EXTERN vlong stksize; // stack size for current frame EXTERN vlong stkptrsize; // prefix of stack containing pointers -EXTERN vlong stkzerosize; // prefix of stack that must be zeroed on entry EXTERN int32 blockgen; // max block number EXTERN int32 block; // current block number EXTERN int hasdefer; // flag that curfn has defer statetment @@ -959,12 +957,14 @@ 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; @@ -980,10 +980,17 @@ EXTERN char* flag_installsuffix; EXTERN int flag_race; EXTERN int flag_largemodel; EXTERN int noescape; +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 @@ -1002,6 +1009,18 @@ 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); +int32 arrayindexof(Array* array, void *element); +void arraysort(Array* array, int (*cmp)(const void*, const void*)); + +/* * bits.c */ int Qconv(Fmt *fp); @@ -1019,11 +1038,19 @@ int bset(Bits a, uint n); * bv.c */ Bvec* bvalloc(int32 n); -void bvset(Bvec *bv, int32 i); -void bvres(Bvec *bv, int32 i); +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); int bvisempty(Bvec *bv); -int bvcmp(Bvec *bv1, Bvec *bv2); +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 @@ -1173,7 +1200,6 @@ char* expstring(void); void mkpackage(char* pkgname); void unimportfile(void); int32 yylex(void); -extern int windows; extern int yylast; extern int yyprev; @@ -1230,17 +1256,19 @@ void mpxorfixfix(Mpint *a, Mpint *b); 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, Sym *s); +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); @@ -1248,8 +1276,9 @@ int duint8(Sym *s, int off, uint8 v); int duintptr(Sym *s, int off, uint64 v); int dsname(Sym *s, int off, char *dat, int ndat); void dumpobj(void); -void ieeedtod(uint64 *ieee, double native); Sym* stringsym(char*, int); +void slicebytes(Node*, char*, int); +LSym* linksym(Sym*); /* * order.c @@ -1274,6 +1303,7 @@ Sym* tracksym(Type *t); Sym* typesymprefix(char *prefix, Type *t); int haspointers(Type *t); void usefield(Node*); +Type* hiter(Type* t); /* * select.c @@ -1434,32 +1464,21 @@ void walkstmt(Node **np); void walkstmtlist(NodeList *l); Node* conv(Node*, Type*); int candiscard(Node*); +Node* outervalue(Node*); /* - * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c + * arch-specific ggen.c/gsubr.c/gobj.c/pgen.c/plive.c */ #define P ((Prog*)0) -typedef struct Plist Plist; -struct Plist -{ - Node* name; - Prog* firstpc; - int recur; - Plist* link; -}; - -EXTERN Plist* plist; -EXTERN Plist* plast; - EXTERN Prog* continpc; EXTERN Prog* breakpc; EXTERN Prog* pc; EXTERN Prog* firstpc; -EXTERN Prog* retpc; EXTERN Node* nodfp; EXTERN int disable_checknil; +EXTERN vlong zerosize; int anyregalloc(void); void betypeinit(void); @@ -1474,59 +1493,52 @@ void cgen_checknil(Node*); void cgen_ret(Node *n); void clearfat(Node *n); void compile(Node*); -void defframe(Prog*, Bvec*); +void defframe(Prog*); int dgostringptr(Sym*, int off, char *str); int dgostrlitptr(Sym*, int off, Strlit*); int dstringptr(Sym *s, int off, char *str); int dsymptr(Sym *s, int off, Sym *x, int xoff); int duintxx(Sym *s, int off, uint64 v, int wid); void dumpdata(void); -void dumpfuncs(void); void fixautoused(Prog*); void gdata(Node*, Node*, int); void gdatacomplex(Node*, Mpcplx*); void gdatastring(Node*, Strlit*); void ggloblnod(Node *nam); void ggloblsym(Sym *s, int32 width, int dupok, int rodata); +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 markautoused(Prog*); Plist* newplist(void); Node* nodarg(Type*, int); void nopout(Prog*); void patch(Prog*, Prog*); Prog* unpatch(Prog*); -void zfile(Biobuf *b, char *p, int n); -void zhist(Biobuf *b, int line, vlong offset); -void zname(Biobuf *b, Sym *s, int t); -#pragma varargck type "A" int #pragma varargck type "B" Mpint* -#pragma varargck type "D" Addr* -#pragma varargck type "lD" Addr* #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" int -#pragma varargck type "lL" uint -#pragma varargck type "L" int -#pragma varargck type "L" uint +#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 "P" Prog* #pragma varargck type "Q" Bits -#pragma varargck type "R" int #pragma varargck type "S" Sym* -#pragma varargck type "lS" Sym* +#pragma varargck type "lS" LSym* #pragma varargck type "T" Type* #pragma varargck type "lT" Type* #pragma varargck type "V" Val* -#pragma varargck type "Y" char* #pragma varargck type "Z" Strlit* /* diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index 5432eca85..2f354f723 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -557,6 +557,7 @@ caseblock: // 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 { @@ -1730,6 +1731,7 @@ non_dcl_stmt: { // will be converted to OFALL $$ = nod(OXFALL, N, N); + $$->xoffset = block; } | LBREAK onew_name { @@ -2138,6 +2140,10 @@ hidden_literal: 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"); } diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c index 6800884a0..cf89b0090 100644 --- a/src/cmd/gc/inl.c +++ b/src/cmd/gc/inl.c @@ -392,6 +392,8 @@ inlnode(Node **np) 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) { @@ -800,6 +802,7 @@ inlvar(Node *var) 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 diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 8c739391a..a50101c42 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -14,7 +14,6 @@ #define ungetc ccungetc extern int yychar; -int windows; int yyprev; int yylast; @@ -61,7 +60,7 @@ 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; @@ -78,8 +77,10 @@ setexp(void) { char *f[20]; int i, nf; - - // The makefile #defines GOEXPERIMENT for us. + + 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]); @@ -165,6 +166,21 @@ fault(int s) 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) { @@ -189,6 +205,24 @@ main(int argc, char *argv[]) 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 = "\"\""; @@ -229,8 +263,11 @@ main(int argc, char *argv[]) goroot = getgoroot(); goos = getgoos(); - goarch = thestring; - + + nacl = strcmp(goos, "nacl") == 0; + if(nacl) + flag_largemodel = 1; + setexp(); outfile = nil; @@ -260,12 +297,16 @@ main(int argc, char *argv[]) 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']); @@ -275,6 +316,7 @@ main(int argc, char *argv[]) flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel); flagparse(&argc, &argv, usage); + ctxt->debugasm = debug['S']; if(argc < 1) usage(); @@ -319,20 +361,6 @@ main(int argc, char *argv[]) sysfatal("unsupported setting GO386=%s", p); } - pathname = mal(1000); - if(getwd(pathname, 999) == 0) - strcpy(pathname, "/???"); - - if(yy_isalpha(pathname[0]) && pathname[1] == ':') { - // On Windows. - windows = 1; - - // Canonicalize path by converting \ to / (Windows accepts both). - for(p=pathname; *p; p++) - if(*p == '\\') - *p = '/'; - } - fmtinstallgo(); betypeinit(); if(widthptr == 0) @@ -527,12 +555,13 @@ skiptopkgdef(Biobuf *b) return 0; if(memcmp(p, "!<arch>\n", 8) != 0) return 0; - /* symbol table is first; skip it */ + /* symbol table may be first; skip it */ sz = arsize(b, "__.GOSYMDEF"); - if(sz < 0) - return 0; - Bseek(b, sz, 1); - /* package export block is second */ + 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; @@ -560,7 +589,7 @@ islocalname(Strlit *name) { if(name->len >= 1 && name->s[0] == '/') return 1; - if(windows && name->len >= 3 && + 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) @@ -581,7 +610,7 @@ findpkg(Strlit *name) char *q, *suffix, *suffixsep; if(islocalname(name)) { - if(safemode) + if(safemode || nolocalimports) return 0; // try .a before .6. important for building libraries: // if there is an array.6 in the array.a library, @@ -702,7 +731,7 @@ importfile(Val *f, int line) fakeimport(); return; } - prefix = pathname; + prefix = ctxt->pathname; if(localimport != nil) prefix = localimport; cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2); @@ -760,7 +789,7 @@ importfile(Val *f, int line) yyerror("import %s: not a go object file", file); errorexit(); } - q = smprint("%s %s %s %s", getgoos(), thestring, getgoversion(), expstring()); + 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(); @@ -1528,7 +1557,7 @@ getlinepragma(void) goto out; // try to avoid allocating file name over and over - for(h=hist; h!=H; h=h->link) { + 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; @@ -2143,14 +2172,18 @@ struct } 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", @@ -2177,6 +2210,7 @@ struct LRANGE, "RANGE", LRETURN, "RETURN", LRSH, "RSH", + LSELECT, "SELECT", LSTRUCT, "STRUCT", LSWITCH, "SWITCH", LTYPE, "TYPE", @@ -2207,6 +2241,7 @@ struct "LASOP", "op=", "LBREAK", "break", "LCASE", "case", + "LCHAN", "chan", "LCOLAS", ":=", "LCONST", "const", "LCONTINUE", "continue", @@ -2354,7 +2389,7 @@ mkpackage(char* pkgname) if(outfile == nil) { p = strrchr(infile, '/'); - if(windows) { + if(ctxt->windows) { q = strrchr(infile, '\\'); if(q > p) p = q; diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c index bbd4e298f..0051ac964 100644 --- a/src/cmd/gc/md5.c +++ b/src/cmd/gc/md5.c @@ -63,7 +63,7 @@ md5write(MD5 *d, uchar *p, int nn) } uint64 -md5sum(MD5 *d) +md5sum(MD5 *d, uint64 *hi) { uchar tmp[64]; int i; @@ -87,6 +87,8 @@ md5sum(MD5 *d) 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); } diff --git a/src/cmd/gc/md5.h b/src/cmd/gc/md5.h index f153e30f2..5a60106b2 100644 --- a/src/cmd/gc/md5.h +++ b/src/cmd/gc/md5.h @@ -13,4 +13,4 @@ struct MD5 void md5reset(MD5*); void md5write(MD5*, uchar*, int); -uint64 md5sum(MD5*); +uint64 md5sum(MD5*, uint64*); diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index e25044a8b..1519caec7 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -65,7 +65,7 @@ mpcmpfltc(Mpflt *b, double c) Mpflt a; mpmovecflt(&a, c); - return mpcmpfltflt(&a, b); + return mpcmpfltflt(b, &a); } void @@ -416,7 +416,7 @@ mpatoflt(Mpflt *a, char *as) if(eb) { if(dp) goto bad; - a->exp += ex; + mpsetexp(a, a->exp+ex); goto out; } @@ -427,8 +427,14 @@ mpatoflt(Mpflt *a, char *as) mppow10flt(&b, ex-dp); mpmulfltflt(a, &b); } else { - mppow10flt(&b, dp-ex); - mpdivfltflt(a, &b); + // 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); + } } } @@ -573,20 +579,40 @@ Fconv(Fmt *fp) { char buf[500]; Mpflt *fvp, fv; - double d; + 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 - if(-900 < fvp->exp && fvp->exp < 900) { + 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); + return fmtprint(fp, "%g", d, exp, fvp); + } + + // 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++; } - // TODO(rsc): for well out of range, print - // an approximation like 1.234e1000 + if(fvp->val.neg) + fmtprint(fp, "-"); + else if(fp->flags & FmtSign) + fmtprint(fp, "+"); + return fmtprint(fp, "%.5fe+%d", d, exp); } if(sigfig(fvp) == 0) { diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c index 9b2f664f7..5cf98c62c 100644 --- a/src/cmd/gc/mparith2.c +++ b/src/cmd/gc/mparith2.c @@ -300,13 +300,21 @@ mpmulfixfix(Mpint *a, Mpint *b) for(i=0; i<na; i++) { x = *a1++; for(j=0; j<Mpscale; j++) { - if(x & 1) + 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) diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c index f8344c9b4..95618f1c6 100644 --- a/src/cmd/gc/mparith3.c +++ b/src/cmd/gc/mparith3.c @@ -23,6 +23,27 @@ sigfig(Mpflt *a) } /* + * 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 */ @@ -60,7 +81,7 @@ mpnorm(Mpflt *a) } mpshiftfix(&a->val, s); - a->exp -= s; + mpsetexp(a, a->exp-s); } /// implements float arihmetic @@ -95,7 +116,7 @@ mpaddfltflt(Mpflt *a, Mpflt *b) if(s < 0) { // b is larger, shift a right mpshiftfix(&a->val, s); - a->exp -= s; + mpsetexp(a, a->exp-s); mpaddfixfix(&a->val, &b->val, 0); goto out; } @@ -131,7 +152,7 @@ mpmulfltflt(Mpflt *a, Mpflt *b) } mpmulfract(&a->val, &b->val); - a->exp = (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1; + mpsetexp(a, (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1); mpnorm(a); if(Mpdebug) @@ -171,18 +192,18 @@ mpdivfltflt(Mpflt *a, Mpflt *b) // divide mpdivfract(&a->val, &c.val); - a->exp = (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1; + mpsetexp(a, (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1); mpnorm(a); if(Mpdebug) print(" = %F\n\n", a); } -double -mpgetflt(Mpflt *a) +static double +mpgetfltN(Mpflt *a, int prec, int bias) { - int s, i, e; - uvlong v, vm; + int s, i, e, minexp; + uvlong v; double f; if(a->val.ovf && nsavederrors+nerrors == 0) @@ -199,59 +220,69 @@ mpgetflt(Mpflt *a) while((a->val.a[Mpnorm-1] & Mpsign) == 0) { mpshiftfix(&a->val, 1); - a->exp -= 1; + mpsetexp(a, a->exp-1); // can set 'a' to zero + s = sigfig(a); + if(s == 0) + return 0; } - // the magic numbers (64, 63, 53, 10, -1074) are - // IEEE specific. this should be done machine - // independently or in the 6g half of the compiler - - // pick up the mantissa and a rounding bit in a uvlong - s = 53+1; + // 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; } - vm = v; - if(s > 0) - vm = (vm<<s) | (a->val.a[i]>>(Mpscale-s)); - - // continue with 64 more bits - s += 64; - for(; s>=Mpscale; i--) { - v = (v<<Mpscale) | a->val.a[i]; - s -= Mpscale; - } - if(s > 0) + 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 - 53; - if(e < -1074) { - s = -e - 1074; - if(s > 54) - s = 54; - v |= vm & ((1ULL<<s) - 1); - vm >>= s; - e = -1074; + 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 & ((1<<s)-1)) != 0) + v |= 1<<s; + v >>= s; + e = minexp; } + + // round to even + v |= (v&4)>>2; + v += v&1; + v >>= 2; -//print("vm=%.16llux v=%.16llux\n", vm, v); - // round toward even - if(v != 0 || (vm&2ULL) != 0) - vm = (vm>>1) + (vm&1ULL); - else - vm >>= 1; - - f = (double)(vm); + f = (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) { diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c index 4cad08924..4eeb03aa8 100644 --- a/src/cmd/gc/obj.c +++ b/src/cmd/gc/obj.c @@ -10,13 +10,28 @@ * architecture-independent object file output */ -static void outhist(Biobuf *b); 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) { @@ -25,13 +40,34 @@ dumpobj(void) errorexit(); } - Bprint(bout, "go object %s %s %s %s\n", getgoos(), thestring, getgoversion(), expstring()); - Bprint(bout, " exports automatically generated from\n"); - Bprint(bout, " %s in package \"%s\"\n", curio.infile, localpkg->name); + 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(); - Bprint(bout, "\n!\n"); + + 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()); + } - outhist(bout); + Bprint(bout, "\n!\n"); externs = nil; if(externdcl != nil) @@ -47,9 +83,22 @@ dumpobj(void) dumpglobls(); externdcl = tmp; - dumpdata(); - dumpfuncs(); + zero = pkglookup("zerovalue", runtimepkg); + ggloblsym(zero, zerosize, 1, 1); + 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); } @@ -75,184 +124,50 @@ dumpglobls(void) 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, 1, 1); } + + // Do not reprocess funcsyms on next dumpglobls call. + funcsyms = nil; } void -Bputname(Biobuf *b, Sym *s) +Bputname(Biobuf *b, LSym *s) { - Bprint(b, "%s", s->pkg->prefix); - BPUTC(b, '.'); Bwrite(b, s->name, strlen(s->name)+1); } -static void -outzfile(Biobuf *b, char *p) -{ - char *q, *q2; - - while(p) { - q = utfrune(p, '/'); - if(windows) { - q2 = utfrune(p, '\\'); - if(q2 && (!q || q2 < q)) - q = q2; - } - if(!q) { - zfile(b, p, strlen(p)); - return; - } - if(q > p) - zfile(b, p, q-p); - p = q + 1; - } -} - -#define isdelim(c) (c == '/' || c == '\\') - -static void -outwinname(Biobuf *b, Hist *h, char *ds, char *p) +LSym* +linksym(Sym *s) { - if(isdelim(p[0])) { - // full rooted name - zfile(b, ds, 3); // leading "c:/" - outzfile(b, p+1); - } else { - // relative name - if(h->offset >= 0 && pathname && pathname[1] == ':') { - if(tolowerrune(ds[0]) == tolowerrune(pathname[0])) { - // using current drive - zfile(b, pathname, 3); // leading "c:/" - outzfile(b, pathname+3); - } else { - // using drive other then current, - // we don't have any simple way to - // determine current working directory - // there, therefore will output name as is - zfile(b, ds, 2); // leading "c:" - } - } - outzfile(b, p); + 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; } -static void -outhist(Biobuf *b) -{ - Hist *h; - char *p, ds[] = {'c', ':', '/', 0}; - char *tofree; - int n; - static int first = 1; - static char *goroot, *goroot_final; - - if(first) { - // Decide whether we need to rewrite paths from $GOROOT to $GOROOT_FINAL. - first = 0; - goroot = getenv("GOROOT"); - goroot_final = getenv("GOROOT_FINAL"); - if(goroot == nil) - goroot = ""; - if(goroot_final == nil) - goroot_final = goroot; - if(strcmp(goroot, goroot_final) == 0) { - goroot = nil; - goroot_final = nil; - } - } - - tofree = nil; - for(h = hist; h != H; h = h->link) { - p = h->name; - if(p) { - if(goroot != nil) { - n = strlen(goroot); - if(strncmp(p, goroot, strlen(goroot)) == 0 && p[n] == '/') { - tofree = smprint("%s%s", goroot_final, p+n); - p = tofree; - } - } - if(windows) { - // if windows variable is set, then, we know already, - // pathname is started with windows drive specifier - // and all '\' were replaced with '/' (see lex.c) - if(isdelim(p[0]) && isdelim(p[1])) { - // file name has network name in it, - // like \\server\share\dir\file.go - zfile(b, "//", 2); // leading "//" - outzfile(b, p+2); - } else if(p[1] == ':') { - // file name has drive letter in it - ds[0] = p[0]; - outwinname(b, h, ds, p+2); - } else { - // no drive letter in file name - outwinname(b, h, pathname, p); - } - } else { - if(p[0] == '/') { - // full rooted name, like /home/rsc/dir/file.go - zfile(b, "/", 1); // leading "/" - outzfile(b, p+1); - } else { - // relative name, like dir/file.go - if(h->offset >= 0 && pathname && pathname[0] == '/') { - zfile(b, "/", 1); // leading "/" - outzfile(b, pathname+1); - } - outzfile(b, p); - } - } - } - zhist(b, h->line, h->offset); - if(tofree) { - free(tofree); - tofree = nil; - } - } -} - -void -ieeedtod(uint64 *ieee, double native) +int +duintxx(Sym *s, int off, uint64 v, int wid) { - double fr, ho, f; - int exp; - uint32 h, l; - uint64 bits; - - if(native < 0) { - ieeedtod(ieee, -native); - *ieee |= 1ULL<<63; - return; - } - if(native == 0) { - *ieee = 0; - return; - } - fr = frexp(native, &exp); - f = 2097152L; /* shouldn't use fp constants here */ - fr = modf(fr*f, &ho); - h = ho; - h &= 0xfffffL; - f = 65536L; - fr = modf(fr*f, &ho); - l = ho; - l <<= 16; - l |= (int32)(fr*f); - bits = ((uint64)h<<32) | l; - if(exp < -1021) { - // gradual underflow - bits |= 1LL<<52; - bits >>= -1021 - exp; - exp = -1022; - } - bits |= (uint64)(exp+1022L) << 52; - *ieee = bits; + // 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 @@ -338,3 +253,31 @@ stringsym(char *s, int len) 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, 0, 0); + + 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/order.c b/src/cmd/gc/order.c index 7552510e9..30dbc7dac 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -5,79 +5,343 @@ // 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" -static void orderstmt(Node*, NodeList**); -static void orderstmtlist(NodeList*, NodeList**); +// 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**, NodeList**); -static void orderexprlist(NodeList*, NodeList**); +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 -orderstmtlist(NodeList *l, NodeList **out) +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, out); + orderstmt(l->n, order); } -// Order the block of statements *l onto a new list, -// and then replace *l with that list. +// Orderblock orders the block of statements *l onto a new list, +// and then replaces *l with that list. static void orderblock(NodeList **l) { - NodeList *out; + Order order; + NodeList *mark; - out = nil; - orderstmtlist(*l, &out); - *l = out; + memset(&order, 0, sizeof order); + mark = marktemp(&order); + orderstmtlist(*l, &order); + cleantemp(mark, &order); + *l = order.out; } -// Order the side effects in *np and leave them as -// the init list of the final *np. +// Orderexprinplace orders the side effects in *np and +// leaves them as the init list of the final *np. static void -orderexprinplace(Node **np) +orderexprinplace(Node **np, Order *outer) { Node *n; - NodeList *out; + NodeList **lp; + Order order; n = *np; - out = nil; - orderexpr(&n, &out); - addinit(&n, out); + 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; } -// Like orderblock, but applied to a single statement. +// Orderstmtinplace orders the side effects of the single statement *np +// and replaces it with the resulting statement list. static void orderstmtinplace(Node **np) { Node *n; - NodeList *out; - + Order order; + NodeList *mark; + n = *np; - out = nil; - orderstmt(n, &out); - *np = liststmt(out); + memset(&order, 0, sizeof order); + mark = marktemp(&order); + orderstmt(n, &order); + cleantemp(mark, &order); + *np = liststmt(order.out); } -// Move n's init list to *out. +// Orderinit moves n's init list to order->out. static void -orderinit(Node *n, NodeList **out) +orderinit(Node *n, Order *order) { - orderstmtlist(n->ninit, out); + orderstmtlist(n->ninit, order); n->ninit = nil; } -// Is the list l actually just f() for a multi-value function? +// 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) { @@ -102,10 +366,10 @@ ismulticall(NodeList *l) return n->left->type->outtuple > 1; } -// n is a multi-value function call. Add t1, t2, .. = n to out -// and return the list t1, t2, ... +// Copyret emits t1, t2, ... = n, where n is a function call, +// and then returns the list t1, t2, .... static NodeList* -copyret(Node *n, NodeList **out) +copyret(Node *n, Order *order) { Type *t; Node *tmp, *as; @@ -127,86 +391,224 @@ copyret(Node *n, NodeList **out) as->list = l1; as->rlist = list1(n); typecheck(&as, Etop); - orderstmt(as, out); + orderstmt(as, order); return l2; } +// Ordercallargs orders the list of call arguments *l. static void -ordercallargs(NodeList **l, NodeList **out) +ordercallargs(NodeList **l, Order *order) { if(ismulticall(*l)) { // return f() where f() is multiple values. - *l = copyret((*l)->n, out); + *l = copyret((*l)->n, order); } else { - orderexprlist(*l, out); + 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, NodeList **out) +ordercall(Node *n, Order *order) { - orderexpr(&n->left, out); - ordercallargs(&n->list, out); + 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. static void -orderstmt(Node *n, NodeList **out) +ordermapassign(Node *n, Order *order) { - int lno; + Node *m, *a; NodeList *l; - Node *r; + NodeList *post; + + switch(n->op) { + default: + fatal("ordermapassign %O", n->op); + + case OAS: + order->out = list(order->out, n); + if(n->left->op == OINDEXMAP && !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, out); + 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 OAS2MAPR: - case OAS: - case OASOP: case OCLOSE: case OCOPY: - case ODELETE: - case OPANIC: case OPRINT: case OPRINTN: case ORECOVER: case ORECV: - case OSEND: - orderexpr(&n->left, out); - orderexpr(&n->right, out); - orderexprlist(n->list, out); - orderexprlist(n->rlist, out); - *out = list(*out, n); + 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. - orderexprlist(n->list, out); - ordercall(n->rlist->n, out); - *out = list(*out, 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. - orderexprlist(n->list, out); - orderexpr(&n->rlist->n->left, out); // arg to recv - *out = list(*out, n); + // 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)); + 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, out); + orderstmtlist(n->list, order); break; case OBREAK: @@ -215,109 +617,329 @@ orderstmt(Node *n, NodeList **out) case ODCLCONST: case ODCLTYPE: case OFALL: - case_OFALL: + case OXFALL: case OGOTO: case OLABEL: case ORETJMP: // Special: n->left is not an expression; save as is. - *out = list(*out, n); + order->out = list(order->out, n); break; case OCALLFUNC: case OCALLINTER: case OCALLMETH: // Special: handle call arguments. - ordercall(n, out); - *out = list(*out, n); + 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. - ordercall(n->left, out); - *out = list(*out, n); + 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: - orderexprinplace(&n->ntest); - orderstmtinplace(&n->nincr); + // 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); - *out = list(*out, n); + orderstmtinplace(&n->nincr); + order->out = list(order->out, n); + cleantemp(t, order); break; case OIF: - orderexprinplace(&n->ntest); + // 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); - *out = list(*out, n); + 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: - orderexpr(&n->right, out); + // 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); + orderexprinplace(&l->n, order); orderblock(&n->nbody); - *out = list(*out, n); + order->out = list(order->out, n); + cleantemp(t, order); break; case ORETURN: - ordercallargs(&n->list, out); - *out = list(*out, n); + 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. + 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; - if(r == nil) - continue; - switch(r->op) { - case OSELRECV: - case OSELRECV2: - orderexprinplace(&r->left); - orderexprinplace(&r->ntest); - orderexpr(&r->right->left, &l->n->ninit); - break; - case OSEND: - orderexpr(&r->left, &l->n->ninit); - orderexpr(&r->right, &l->n->ninit); - break; + 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); + + // 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); } - *out = list(*out, n); + // 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: - orderexpr(&n->ntest, out); + // 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); - orderexpr(&l->n->left, &l->n->ninit); + orderexprlistinplace(l->n->list, order); + orderblock(&l->n->nbody); } - *out = list(*out, n); + order->out = list(order->out, n); + cleantemp(t, order); break; - - case OXFALL: - yyerror("fallthrough statement out of place"); - n->op = OFALL; - goto case_OFALL; } 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 -orderexprlist(NodeList *l, NodeList **out) +orderexprlistinplace(NodeList *l, Order *order) { for(; l; l=l->next) - orderexpr(&l->n, out); + orderexprinplace(&l->n, order); } +// Orderexpr orders a single expression, appending side +// effects to order->out as needed. static void -orderexpr(Node **np, NodeList **out) +orderexpr(Node **np, Order *order) { Node *n; + NodeList *mark, *l; + Type *t; int lno; n = *np; @@ -325,31 +947,113 @@ orderexpr(Node **np, NodeList **out) return; lno = setlineno(n); - orderinit(n, out); + orderinit(n, order); switch(n->op) { default: - orderexpr(&n->left, out); - orderexpr(&n->right, out); - orderexprlist(n->list, out); - orderexprlist(n->rlist, out); + 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: - orderexpr(&n->left, out); - orderexprinplace(&n->right); + 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 OCALLFUNC: case OCALLMETH: case OCALLINTER: - ordercall(n, out); - n = copyexpr(n, n->type, out); + case OAPPEND: + case OCOMPLEX: + 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: - n = copyexpr(n, n->type, out); + orderexpr(&n->left, order); + n = ordercopyexpr(n, n->type, order, 1); break; } diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 2850af6bb..40620c3da 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -8,30 +8,159 @@ #include <u.h> #include <libc.h> +#include "md5.h" #include "gg.h" #include "opt.h" #include "../../pkg/runtime/funcdata.h" -enum { BitsPerPointer = 2 }; - static void allocauto(Prog* p); -static void dumpgcargs(Node*, Sym*); -static Bvec* dumpgclocals(Node*, Sym*); + +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) { - Bvec *bv; Plist *pl; - Node nod1, *n, *gcargsnod, *gclocalsnod; - Prog *ptxt, *p, *p1; + Node nod1, *n; + Prog *ptxt, *p; int32 lno; Type *t; Iter save; vlong oldstksize; NodeList *l; - Sym *gcargssym, *gclocalssym; - static int ngcargs, ngclocals; + Sym *gcargs; + Sym *gclocals; if(newproc == N) { newproc = sysfunc("newproc"); @@ -45,7 +174,7 @@ compile(Node *fn) lno = setlineno(fn); if(fn->nbody == nil) { - if(pure_go || memcmp(fn->nname->sym->name, "init·", 6) == 0) + if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) yyerror("missing function body", fn); goto ret; } @@ -88,7 +217,7 @@ compile(Node *fn) breakpc = P; pl = newplist(); - pl->name = curfn->nname; + pl->name = linksym(curfn->nname->sym); setlineno(curfn); @@ -98,6 +227,8 @@ compile(Node *fn) ptxt->TEXTFLAG |= DUPOK; if(fn->wrapper) ptxt->TEXTFLAG |= WRAPPER; + if(fn->needctxt) + ptxt->TEXTFLAG |= NEEDCTXT; // Clumsy but important. // See test/recover.go for test cases and src/pkg/reflect/value.go @@ -111,21 +242,8 @@ compile(Node *fn) ginit(); - snprint(namebuf, sizeof namebuf, "gcargs·%d", ngcargs++); - gcargssym = lookup(namebuf); - gcargsnod = newname(gcargssym); - gcargsnod->class = PEXTERN; - - nodconst(&nod1, types[TINT32], FUNCDATA_GCArgs); - gins(AFUNCDATA, &nod1, gcargsnod); - - snprint(namebuf, sizeof(namebuf), "gclocals·%d", ngclocals++); - gclocalssym = lookup(namebuf); - gclocalsnod = newname(gclocalssym); - gclocalsnod->class = PEXTERN; - - nodconst(&nod1, types[TINT32], FUNCDATA_GCLocals); - gins(AFUNCDATA, &nod1, gclocalsnod); + gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); + gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps); for(t=curfn->paramfld; t; t=t->down) gtrack(tracksym(t->type)); @@ -140,20 +258,12 @@ compile(Node *fn) case PPARAMOUT: nodconst(&nod1, types[TUINTPTR], l->n->type->width); p = gins(ATYPE, l->n, &nod1); - p->from.gotype = ngotype(l->n); + p->from.gotype = linksym(ngotype(l->n)); break; } } genlist(curfn->enter); - - retpc = nil; - if(hasdefer || curfn->exit) { - p1 = gjmp(nil); - retpc = gjmp(nil); - patch(p1, pc); - } - genlist(curfn->nbody); gclean(); checklabels(); @@ -165,13 +275,15 @@ compile(Node *fn) if(curfn->type->outtuple != 0) ginscall(throwreturn, 0); - if(retpc) - patch(retpc, pc); ginit(); - if(hasdefer) - ginscall(deferreturn, 0); - if(curfn->exit) - genlist(curfn->exit); + // 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; @@ -179,6 +291,7 @@ compile(Node *fn) pc->as = ARET; // overwrite AEND pc->lineno = lineno; + fixjmp(ptxt); if(!debug['N'] || debug['R'] || debug['P']) { regopt(ptxt); nilopt(ptxt); @@ -190,6 +303,7 @@ compile(Node *fn) if(0) print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize); + USED(oldstksize); setlineno(curfn); if((int64)stksize+maxarg > (1ULL<<31)) { @@ -198,184 +312,21 @@ compile(Node *fn) } // Emit garbage collection symbols. - dumpgcargs(fn, gcargssym); - bv = dumpgclocals(curfn, gclocalssym); + liveness(curfn, ptxt, gcargs, gclocals); + gcsymdup(gcargs); + gcsymdup(gclocals); - defframe(ptxt, bv); - free(bv); + defframe(ptxt); if(0) frame(0); + // Remove leftover instrumentation from the instruction stream. + removevardef(ptxt); ret: lineno = lno; } -static void -walktype1(Type *t, vlong *xoffset, Bvec *bv) -{ - vlong fieldoffset, i, o; - Type *t1; - - if(t->align > 0 && (*xoffset % t->align) != 0) - fatal("walktype1: 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: - *xoffset += t->width; - break; - - case TPTR32: - case TPTR64: - case TUNSAFEPTR: - case TFUNC: - case TCHAN: - case TMAP: - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *xoffset += t->width; - break; - - case TSTRING: - // struct { byte *str; intgo len; } - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *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 != 0) - fatal("walktype1: invalid alignment, %T", t); - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); - if(isnilinter(t)) - bvset(bv, ((*xoffset / widthptr) * BitsPerPointer)); - *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("walktype1: invalid bound, %T", t); - if(isslice(t)) { - // struct { byte* array; uintgo len; uintgo cap; } - if(*xoffset % widthptr != 0) - fatal("walktype1: invalid TARRAY alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer); - *xoffset += t->width; - } else if(!haspointers(t->type)) - *xoffset += t->width; - else - for(i = 0; i < t->bound; ++i) - walktype1(t->type, xoffset, bv); - break; - - case TSTRUCT: - o = 0; - for(t1 = t->type; t1 != T; t1 = t1->down) { - fieldoffset = t1->width; - *xoffset += fieldoffset - o; - walktype1(t1->type, xoffset, bv); - o = fieldoffset + t1->type->width; - } - *xoffset += t->width - o; - break; - - default: - fatal("walktype1: unexpected type, %T", t); - } -} - -static void -walktype(Type *type, Bvec *bv) -{ - vlong xoffset; - - // Start the walk at offset 0. The correct offset will be - // filled in by the first type encountered during the walk. - xoffset = 0; - walktype1(type, &xoffset, bv); -} - -// Compute a bit vector to describe the pointer-containing locations -// in the in and out argument list and dump the bitvector length and -// data to the provided symbol. -static void -dumpgcargs(Node *fn, Sym *sym) -{ - Type *thistype, *inargtype, *outargtype; - Bvec *bv; - int32 i; - int off; - - thistype = getthisx(fn->type); - inargtype = getinargx(fn->type); - outargtype = getoutargx(fn->type); - bv = bvalloc((fn->type->argwid / widthptr) * BitsPerPointer); - if(thistype != nil) - walktype(thistype, bv); - if(inargtype != nil) - walktype(inargtype, bv); - if(outargtype != nil) - walktype(outargtype, bv); - off = duint32(sym, 0, bv->n); - for(i = 0; i < bv->n; i += 32) - off = duint32(sym, off, bv->b[i/32]); - free(bv); - ggloblsym(sym, off, 0, 1); -} - -// Compute a bit vector to describe the pointer-containing locations -// in local variables and dump the bitvector length and data out to -// the provided symbol. Return the vector for use and freeing by caller. -static Bvec* -dumpgclocals(Node* fn, Sym *sym) -{ - Bvec *bv; - NodeList *ll; - Node *node; - vlong xoffset; - int32 i; - int off; - - bv = bvalloc((stkptrsize / widthptr) * BitsPerPointer); - for(ll = fn->dcl; ll != nil; ll = ll->next) { - node = ll->n; - if(node->class == PAUTO && node->op == ONAME) { - if(haspointers(node->type)) { - xoffset = node->xoffset + stkptrsize; - walktype1(node->type, &xoffset, bv); - } - } - } - off = duint32(sym, 0, bv->n); - for(i = 0; i < bv->n; i += 32) { - off = duint32(sym, off, bv->b[i/32]); - } - ggloblsym(sym, off, 0, 1); - return 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. @@ -415,7 +366,8 @@ cmpstackvar(Node *a, Node *b) return +1; if(a->type->width > b->type->width) return -1; - return 0; + + return strcmp(a->sym->name, b->sym->name); } // TODO(lvd) find out where the PAUTO/OLITERAL nodes come from. @@ -428,7 +380,6 @@ allocauto(Prog* ptxt) stksize = 0; stkptrsize = 0; - stkzerosize = 0; if(curfn->dcl == nil) return; @@ -440,13 +391,6 @@ allocauto(Prog* ptxt) markautoused(ptxt); - if(precisestack_enabled) { - // TODO: Remove when liveness analysis sets needzero instead. - for(ll=curfn->dcl; ll != nil; ll=ll->next) - if(ll->n->class == PAUTO) - ll->n->needzero = 1; // ll->n->addrtaken; - } - listsort(&curfn->dcl, cmpstackvar); // Unused autos are at the end, chop 'em off. @@ -480,11 +424,8 @@ allocauto(Prog* ptxt) fatal("bad width"); stksize += w; stksize = rnd(stksize, n->type->align); - if(haspointers(n->type)) { + if(haspointers(n->type)) stkptrsize = stksize; - if(n->needzero) - stkzerosize = stksize; - } if(thechar == '5') stksize = rnd(stksize, widthptr); if(stksize >= (1ULL<<31)) { @@ -493,9 +434,8 @@ allocauto(Prog* ptxt) } n->stkdelta = -stksize - n->xoffset; } - stksize = rnd(stksize, widthptr); - stkptrsize = rnd(stkptrsize, widthptr); - stkzerosize = rnd(stkzerosize, widthptr); + stksize = rnd(stksize, widthreg); + stkptrsize = rnd(stkptrsize, widthreg); fixautoused(ptxt); @@ -538,12 +478,12 @@ cgen_checknil(Node *n) if(disable_checknil) return; - // Ideally we wouldn't see any TUINTPTR here, but we do. - if(n->type == T || (!isptr[n->type->etype] && n->type->etype != TUINTPTR && n->type->etype != TUNSAFEPTR)) { + // 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) { + if((thechar == '5' && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) { regalloc(®, types[tptr], n); cgen(n, ®); gins(ACHECKNIL, ®, N); diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c new file mode 100644 index 000000000..eb8901733 --- /dev/null +++ b/src/cmd/gc/plive.c @@ -0,0 +1,1985 @@ +// 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 "../../pkg/runtime/funcdata.h" + +enum { BitsPerPointer = 2 }; + +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. + switch(ll->n->class) { + case PAUTO: + if(haspointers(ll->n->type)) + arrayadd(result, &ll->n); + break; + case PPARAM: + case PPARAMOUT: + 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; + Adr *from; + Adr *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) { + switch(from->node->class & ~PHEAP) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + pos = arrayindexof(vars, from->node); + if(pos == -1) + goto Next; + 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) { + switch(to->node->class & ~PHEAP) { + case PAUTO: + case PPARAM: + case PPARAMOUT: + pos = arrayindexof(vars, to->node); + if(pos == -1) + goto Next1; + 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; + + 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. +static 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 + 0); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string + *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 + 0); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice + *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 < arraylength(vars); i++) { + node = *(Node**)arrayget(vars, i); + switch(node->class) { + case PAUTO: + if(bvget(liveout, i)) { + xoffset = node->xoffset + stkptrsize; + twobitwalktype1(node->type, &xoffset, locals); + } + break; + case PPARAM: + case PPARAMOUT: + if(bvget(liveout, i)) { + 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 + +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]; + off = duint32(sym, off, word); + } + } + duint32(sym, 0, i); // number of bitmaps + ggloblsym(sym, off, 0, 1); +} + +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; + + // 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. + freeliveness(lv); + arrayfree(vars); + freecfg(cfg); + + debuglive -= debugdelta; +} diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c index 8d7afa011..ea88b94db 100644 --- a/src/cmd/gc/popt.c +++ b/src/cmd/gc/popt.c @@ -51,9 +51,14 @@ noreturn(Prog *p) symlist[2] = pkglookup("throwinit", runtimepkg); symlist[3] = pkglookup("panic", runtimepkg); symlist[4] = pkglookup("panicwrap", runtimepkg); + symlist[5] = pkglookup("throwreturn", runtimepkg); + symlist[6] = pkglookup("selectgo", runtimepkg); + symlist[7] = pkglookup("block", runtimepkg); } - s = p->to.sym; + if(p->to.node == nil) + return 0; + s = p->to.node->sym; if(s == S) return 0; for(i=0; symlist[i]!=S; i++) @@ -144,7 +149,13 @@ fixjmp(Prog *firstp) 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. + // 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); @@ -489,8 +500,8 @@ struct TempVar TempFlow *use; // use list, chained through TempFlow.uselink TempVar *freelink; // next free temp in Type.opt list TempVar *merge; // merge var with this one - uint32 start; // smallest Prog.loc in live range - uint32 end; // largest Prog.loc in live range + 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 }; @@ -520,10 +531,11 @@ startcmp(const void *va, const void *vb) static int canmerge(Node *n) { - return n->class == PAUTO && !n->addrtaken && strncmp(n->sym->name, "autotmp", 7) == 0; + 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) @@ -544,7 +556,7 @@ mergetemp(Prog *firstp) 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) @@ -640,6 +652,11 @@ mergetemp(Prog *firstp) 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. @@ -659,7 +676,7 @@ mergetemp(Prog *firstp) nfree = nvar; for(i=0; i<nvar; i++) { v = bystart[i]; - if(v->addr || v->removed) + if(v->removed) continue; // Expire no longer in use. @@ -672,7 +689,12 @@ mergetemp(Prog *firstp) t = v->node->type; for(j=nfree; j<nvar; j++) { v1 = inuse[j]; - if(eqtype(t, v1->node->type)) { + // 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; @@ -695,7 +717,7 @@ mergetemp(Prog *firstp) if(Debug) { print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill); for(v=var; v<var+nvar; v++) { - print("var %#N %T %d-%d", v->node, v->node->type, v->start, v->end); + print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end); if(v->addr) print(" addr=1"); if(v->removed) @@ -752,10 +774,10 @@ mergewalk(TempVar *v, TempFlow *r0, uint32 gen) break; r1->f.active = gen; p = r1->f.prog; - if(v->end < p->loc) - v->end = p->loc; + if(v->end < p->pc) + v->end = p->pc; if(r1 == v->def) { - v->start = p->loc; + v->start = p->pc; break; } } @@ -765,6 +787,29 @@ mergewalk(TempVar *v, TempFlow *r0, uint32 gen) 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. @@ -911,7 +956,7 @@ nilwalkback(NilFlow *rcheck) static void nilwalkfwd(NilFlow *rcheck) { - NilFlow *r; + NilFlow *r, *last; Prog *p; ProgInfo info; @@ -922,6 +967,7 @@ nilwalkfwd(NilFlow *rcheck) // 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); @@ -944,5 +990,12 @@ nilwalkfwd(NilFlow *rcheck) // 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/racewalk.c b/src/cmd/gc/racewalk.c index d6a5b3cce..285bd78a2 100644 --- a/src/cmd/gc/racewalk.c +++ b/src/cmd/gc/racewalk.c @@ -166,6 +166,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) goto ret; case OCFUNC: + case OVARKILL: // can't matter goto ret; @@ -181,7 +182,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) // 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->ninit, 0, 0); + racewalknode(&n->list->n, &n->list->n->ninit, 0, 0); fini = nil; racewalklist(n->list->next, &fini); n->list = concat(n->list, fini); diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index bd271da38..45aa521b3 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -111,6 +111,8 @@ walkrange(Node *n) 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; @@ -120,34 +122,23 @@ walkrange(Node *n) a = n->right; lno = setlineno(a); - if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) { - a = nod(OCONV, n->right, N); - a->type = types[TSTRING]; - } v1 = n->list->n; v2 = N; - if(n->list->next) + if(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; - if(v2 == N && t->etype == TARRAY) { - // will have just one reference to argument. - // no need to make a potentially expensive copy. - ha = a; - } else { - ha = temp(a->type); - init = list(init, nod(OAS, ha, a)); - } - 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; @@ -162,8 +153,7 @@ walkrange(Node *n) } n->ntest = nod(OLT, hv1, hn); - n->nincr = nod(OASOP, hv1, nodintconst(1)); - n->nincr->etype = OADD; + n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1))); if(v2 == N) body = list1(nod(OAS, v1, hv1)); else { @@ -171,54 +161,70 @@ walkrange(Node *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; - body = list(body, nod(OAS, hp, tmp)); + a = nod(OAS, hp, tmp); + typecheck(&a, Etop); + n->nincr->ninit = list1(a); } break; case TMAP: - th = typ(TARRAY); - th->type = ptrto(types[TUINT8]); - // see ../../pkg/runtime/hashmap.c:/hash_iter - // Size of hash_iter in # of pointers. - th->bound = 11; - hit = temp(th); + // 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(OINDEX, hit, nodintconst(0)), nodnil()); + 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(v2 == N) { - fn = syslook("mapiter1", 1); - argtype(fn, th); - argtype(fn, t->down); - a = nod(OAS, v1, mkcall1(fn, t->down, nil, nod(OADDR, hit, N))); + a = nod(OAS, v1, key); } else { - fn = syslook("mapiter2", 1); - argtype(fn, th); - argtype(fn, t->down); - argtype(fn, t->type); + val = nod(ODOT, hit, valname); + val = nod(OIND, val, N); a = nod(OAS2, N, N); a->list = list(list1(v1), v2); - a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, nod(OADDR, hit, N))); + 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); + if(haspointers(t->type)) + init = list(init, nod(OAS, hv1, N)); hb = temp(types[TBOOL]); n->ntest = nod(ONE, hb, nodbool(0)); @@ -231,6 +237,9 @@ walkrange(Node *n) break; case TSTRING: + // orderstmt arranged for a copy of the string variable. + ha = a; + ohv1 = temp(types[TINT]); hv1 = temp(types[TINT]); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 0a8aa8d7a..dbb447e4e 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -125,6 +125,8 @@ mapbucket(Type *t) keytype = t->down; valtype = t->type; + dowidth(keytype); + dowidth(valtype); if(keytype->width > MAXKEYSIZE) keytype = ptrto(keytype); if(valtype->width > MAXVALSIZE) @@ -143,6 +145,11 @@ mapbucket(Type *t) overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name overflowfield->sym->name = "overflow"; offset += widthptr; + + // The keys are padded 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; keysfield = typ(TFIELD); keysfield->type = typ(TARRAY); @@ -173,6 +180,7 @@ mapbucket(Type *t) bucket->width = offset; bucket->local = t->local; t->bucket = bucket; + bucket->map = t; return bucket; } @@ -229,10 +237,89 @@ hmap(Type *t) h->width = offset; h->local = t->local; t->hmap = h; - h->hmap = t; + 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 ../../pkg/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). @@ -396,9 +483,8 @@ imethods(Type *t) last->link = a; last = a; - // Compiler can only refer to wrappers for - // named interface types and non-blank methods. - if(t->sym == S || isblanksym(method)) + // Compiler can only refer to wrappers for non-blank methods. + if(isblanksym(method)) continue; // NOTE(rsc): Perhaps an oversight that @@ -620,6 +706,10 @@ haspointers(Type *t) ret = 1; break; } + if(t->bound == 0) { // empty array + ret = 0; + break; + } ret = haspointers(t->type); break; case TSTRUCT: @@ -656,7 +746,7 @@ static int dcommontype(Sym *s, int ot, Type *t) { int i, alg, sizeofAlg; - Sym *sptr, *algsym; + Sym *sptr, *algsym, *zero; static Sym *algarray; char *p; @@ -677,6 +767,18 @@ dcommontype(Sym *s, int ot, Type *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 { @@ -691,6 +793,7 @@ dcommontype(Sym *s, int ot, Type *t) // string *string // *extraType // ptrToThis *Type + // zero unsafe.Pointer // } ot = duintptr(s, ot, t->width); ot = duint32(s, ot, typehash(t)); @@ -728,6 +831,7 @@ dcommontype(Sym *s, int ot, Type *t) ot += widthptr; ot = dsymptr(s, ot, sptr, 0); // ptrto type + ot = dsymptr(s, ot, zero, 0); // ptr to zero value return ot; } @@ -893,7 +997,7 @@ ok: switch(t->etype) { default: ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; break; case TARRAY: @@ -905,7 +1009,7 @@ ok: t2->bound = -1; // slice s2 = dtypesym(t2); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s2, 0); ot = duintptr(s, ot, t->bound); @@ -913,7 +1017,7 @@ ok: // ../../pkg/runtime/type.go:/SliceType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = dsymptr(s, ot, s1, 0); } break; @@ -922,7 +1026,7 @@ ok: // ../../pkg/runtime/type.go:/ChanType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = dsymptr(s, ot, s1, 0); ot = duintptr(s, ot, t->chan); break; @@ -939,7 +1043,7 @@ ok: dtypesym(t1->type); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = duint8(s, ot, isddd); // two slice headers: in and out. @@ -971,7 +1075,7 @@ ok: // ../../pkg/runtime/type.go:/InterfaceType ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + 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); @@ -990,7 +1094,7 @@ ok: s3 = dtypesym(mapbucket(t)); s4 = dtypesym(hmap(t)); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = dsymptr(s, ot, s1, 0); ot = dsymptr(s, ot, s2, 0); ot = dsymptr(s, ot, s3, 0); @@ -1007,7 +1111,7 @@ ok: // ../../pkg/runtime/type.go:/PtrType s1 = dtypesym(t->type); ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + xt = ot - 3*widthptr; ot = dsymptr(s, ot, s1, 0); break; @@ -1020,7 +1124,7 @@ ok: n++; } ot = dcommontype(s, ot, t); - xt = ot - 2*widthptr; + 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); @@ -1218,7 +1322,22 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size) // NOTE: Any changes here need to be made to reflect.PtrTo as well. if(*off % widthptr != 0) fatal("dgcsym1: invalid alignment, %T", t); - if(!haspointers(t->type) || t->type->etype == TUINT8) { + + // NOTE(rsc): Emitting GC_APTR here for *nonptrtype + // (pointer to non-pointer-containing type) means that + // we do not record 'nonptrtype' and instead tell the + // garbage collector to look up the type of the memory in + // type information stored in the heap. In effect we are telling + // the collector "we don't trust our information - use yours". + // It's not completely clear why we want to do this. + // It does have the effect that if you have a *SliceHeader and a *[]int + // pointing at the same actual slice header, *SliceHeader will not be + // used as an authoritative type for the memory, which is good: + // if the collector scanned the memory as type *SliceHeader, it would + // see no pointers inside but mark the block as scanned, preventing + // the seeing of pointers when we followed the *[]int pointer. + // Perhaps that kind of situation is the rationale. + if(!haspointers(t->type)) { ot = duintptr(s, ot, GC_APTR); ot = duintptr(s, ot, *off); } else { diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index c8d57ab33..fb5c2a150 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -15,6 +15,7 @@ package PACKAGE func new(typ *byte) *any func panicindex() func panicslice() +func panicdivide() func throwreturn() func throwinit() func panicwrap(string, string, string) @@ -36,13 +37,17 @@ func printnl() func printsp() func goprintf() -// filled in by compiler: int n, string, string, ... -func concatstring() +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 @@ -55,8 +60,8 @@ func slicestringcopy(to any, fr any) int 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) +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) @@ -83,29 +88,27 @@ func equal(typ *byte, x1, x2 any) (ret bool) // *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(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(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 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 mapdelete(mapType *byte, hmap map[any]any, key *any) func mapiternext(hiter *any) -func mapiter1(hiter *any) (key any) -func mapiter2(hiter *any) (key any, val any) // *byte is really *runtime.Type func makechan(chanType *byte, hint int64) (hchan chan any) -func chanrecv1(chanType *byte, hchan <-chan any) (elem any) -func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool) -func chansend1(chanType *byte, hchan chan<- any, elem any) +func 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) -func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool +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 diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index cd3de8c7b..58a120674 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -69,6 +69,7 @@ typecheckselect(Node *sel) 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; @@ -94,7 +95,7 @@ void walkselect(Node *sel) { int lno, i; - Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch; + Node *n, *r, *a, *var, *cas, *dflt, *ch; NodeList *l, *init; if(sel->list == nil && sel->xoffset != 0) @@ -110,6 +111,8 @@ walkselect(Node *sel) } // 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); @@ -123,32 +126,34 @@ walkselect(Node *sel) fatal("select %O", n->op); case OSEND: - ch = cheapexpr(n->left, &l); - n->left = ch; + // ok already + ch = n->left; break; case OSELRECV: - r = n->right; - ch = cheapexpr(r->left, &l); - r->left = ch; - + ch = n->right->left; + Selrecv1: if(n->left == N) - n = r; - else { - n = nod(OAS, n->left, r); - typecheck(&n, Etop); - } + n = n->right; + else + n->op = OAS; break; case OSELRECV2: - r = n->right; - ch = cheapexpr(r->left, &l); - r->left = ch; - - a = nod(OAS2, N, N); - a->list = n->list; - a->rlist = list1(n->right); - n = a; + 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; } @@ -166,7 +171,7 @@ walkselect(Node *sel) goto out; } - // introduce temporary variables for OSELRECV where needed. + // 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; @@ -175,58 +180,24 @@ walkselect(Node *sel) 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: - ch = n->right->left; - - // If we can use the address of the target without - // violating addressability or order of operations, do so. - // Otherwise introduce a temporary. - // Also introduce a temporary for := variables that escape, - // so that we can delay the heap allocation until the case - // is selected. + if(n->op == OSELRECV2 && n->ntest == N) + n->op = OSELRECV; if(n->op == OSELRECV2) { - if(n->ntest == N || isblank(n->ntest)) - n->ntest = nodnil(); - else if(n->ntest->op == ONAME && - (!n->colas || (n->ntest->class&PHEAP) == 0) && - convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) { - n->ntest = nod(OADDR, n->ntest, N); - n->ntest->etype = 1; // pointer does not escape - typecheck(&n->ntest, Erv); - } else { - tmp = temp(types[TBOOL]); - a = nod(OADDR, tmp, N); - a->etype = 1; // pointer does not escape - typecheck(&a, Erv); - r = nod(OAS, n->ntest, tmp); - typecheck(&r, Etop); - cas->nbody = concat(list1(r), cas->nbody); - n->ntest = a; - } + n->ntest = nod(OADDR, n->ntest, N); + typecheck(&n->ntest, Erv); } - - if(n->left == N || isblank(n->left)) + if(n->left == N) n->left = nodnil(); - else if(n->left->op == ONAME && - (!n->colas || (n->left->class&PHEAP) == 0) && - convertop(ch->type->type, n->left->type, nil) == OCONVNOP) { + else { n->left = nod(OADDR, n->left, N); - n->left->etype = 1; // pointer does not escape typecheck(&n->left, Erv); - } else { - tmp = temp(ch->type->type); - a = nod(OADDR, tmp, N); - a->etype = 1; // pointer does not escape - typecheck(&a, Erv); - r = nod(OAS, n->left, tmp); - typecheck(&r, Etop); - cas->nbody = concat(list1(r), cas->nbody); - n->left = a; - } - - cas->nbody = concat(n->ninit, cas->nbody); - n->ninit = nil; + } break; } } @@ -250,8 +221,8 @@ walkselect(Node *sel) fatal("select %O", n->op); case OSEND: - // if c != nil && selectnbsend(c, v) { body } else { default body } - ch = cheapexpr(n->left, &r->ninit); + // 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; @@ -260,7 +231,7 @@ walkselect(Node *sel) // if c != nil && selectnbrecv(&v, c) { body } else { default body } r = nod(OIF, N, N); r->ninit = cas->ninit; - ch = cheapexpr(n->right->left, &r->ninit); + ch = n->right->left; r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); break; @@ -269,7 +240,7 @@ walkselect(Node *sel) // if c != nil && selectnbrecv2(&v, c) { body } else { default body } r = nod(OIF, N, N); r->ninit = cas->ninit; - ch = cheapexpr(n->right->left, &r->ninit); + 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; @@ -313,11 +284,6 @@ walkselect(Node *sel) case OSEND: // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); - n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit); - n->right = localexpr(n->right, n->left->type->type, &r->ninit); - n->right = nod(OADDR, n->right, N); - n->right->etype = 1; // pointer does not escape - typecheck(&n->right, Erv); r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &r->ninit, var, n->left, n->right); break; diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 446b1110a..59804cd8d 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -286,8 +286,8 @@ staticcopy(Node *l, Node *r, NodeList **out) if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg) return 0; - if(r->defn == N) // zeroed - return 1; + 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; @@ -354,11 +354,13 @@ staticcopy(Node *l, Node *r, NodeList **out) 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)); @@ -378,6 +380,7 @@ staticassign(Node *l, Node *r, NodeList **out) InitPlan *p; InitEntry *e; int i; + Strlit *sval; switch(r->op) { default: @@ -426,6 +429,14 @@ staticassign(Node *l, Node *r, NodeList **out) } 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)) { @@ -459,6 +470,7 @@ staticassign(Node *l, Node *r, NodeList **out) 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)); } @@ -756,11 +768,24 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) vauto = temp(ptrto(t)); // set auto to point at new temp or heap (3 assign) - if(n->esc == EscNone) { - a = nod(OAS, temp(t), N); - typecheck(&a, Etop); - *init = list(*init, a); // zero new temp - a = nod(OADDR, a->left, N); + 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)); @@ -827,7 +852,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init) int nerr; int64 b; Type *t, *tk, *tv, *t1; - Node *vstat, *index, *value; + Node *vstat, *index, *value, *key, *val; Sym *syma, *symb; USED(ctxt); @@ -846,7 +871,7 @@ ctxt = 0; r = l->n; if(r->op != OKEY) - fatal("slicelit: rhs not OKEY: %N", r); + fatal("maplit: rhs not OKEY: %N", r); index = r->left; value = r->right; @@ -890,7 +915,7 @@ ctxt = 0; r = l->n; if(r->op != OKEY) - fatal("slicelit: rhs not OKEY: %N", r); + fatal("maplit: rhs not OKEY: %N", r); index = r->left; value = r->right; @@ -941,8 +966,7 @@ ctxt = 0; a->ninit = list1(nod(OAS, index, nodintconst(0))); a->ntest = nod(OLT, index, nodintconst(t->bound)); - a->nincr = nod(OASOP, index, nodintconst(1)); - a->nincr->etype = OADD; + a->nincr = nod(OAS, index, nod(OADD, index, nodintconst(1))); typecheck(&a, Etop); walkstmt(&a); @@ -950,25 +974,49 @@ ctxt = 0; } // 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("slicelit: rhs not OKEY: %N", r); + 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); - // build list of var[c] = expr - a = nod(OINDEX, var, r->left); - a = nod(OAS, a, r->right); + a = nod(OAS, nod(OINDEX, var, key), val); typecheck(&a, Etop); - walkexpr(&a, init); + 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); } } @@ -988,12 +1036,16 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init) if(!isptr[t->etype]) fatal("anylit: not ptr"); - r = nod(ONEW, N, N); - r->typecheck = 1; - r->type = t; - r->esc = n->esc; + 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); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index bea90b87b..72a9ac20c 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -88,6 +88,7 @@ flusherrors(void) { int i; + Bflush(&bstdout); if(nerr == 0) return; qsort(err, nerr, sizeof err[0], errcmp); @@ -258,9 +259,6 @@ fatal(char *fmt, ...) void linehist(char *file, int32 off, int relative) { - Hist *h; - char *cp; - if(debug['i']) { if(file != nil) { if(off < 0) @@ -274,25 +272,10 @@ linehist(char *file, int32 off, int relative) print("end of import"); print(" at line %L\n", lexlineno); } - - if(off < 0 && file[0] != '/' && !relative) { - cp = mal(strlen(file) + strlen(pathname) + 2); - sprint(cp, "%s/%s", pathname, file); - file = cp; - } - - h = mal(sizeof(Hist)); - h->name = file; - h->line = lexlineno; - h->offset = off; - h->link = H; - if(ehist == H) { - hist = h; - ehist = h; - return; - } - ehist->link = h; - ehist = h; + + if(off < 0 && file[0] != '/' && !relative) + file = smprint("%s/%s", ctxt->pathname, file); + linklinehist(ctxt, lexlineno, file, off); } int32 @@ -607,8 +590,6 @@ algtype1(Type *t, Type **bad) *bad = t; return ANOEQ; } - if(t->bound == 0) - return AMEM; a = algtype1(t->type, bad); if(a == ANOEQ || a == AMEM) { if(a == ANOEQ && bad) @@ -1242,8 +1223,10 @@ assignop(Type *src, Type *dst, char **why) // 2. src and dst have identical underlying types // and either src or dst is not a named type or - // both are interface types. - if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || src->etype == TINTER)) + // 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. @@ -1251,13 +1234,16 @@ assignop(Type *src, Type *dst, char **why) if(implements(src, dst, &missing, &have, &ptr)) return OCONVIFACE; - // we'll have complained about this method anyway, supress spurious messages. + // 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, @@ -1692,7 +1678,7 @@ typehash(Type *t) md5reset(&d); md5write(&d, (uchar*)p, strlen(p)); free(p); - return md5sum(&d); + return md5sum(&d, nil); } Type* @@ -2115,7 +2101,7 @@ cheapexpr(Node *n, NodeList **init) Node* localexpr(Node *n, Type *t, NodeList **init) { - if(n->op == ONAME && !n->addrtaken && + 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; @@ -2257,6 +2243,7 @@ adddot(Node *n) int c, d; typecheck(&n->left, Etype|Erv); + n->diag |= n->left->diag; t = n->left->type; if(t == T) goto ret; @@ -2506,12 +2493,19 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) 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); - lineno = 1; // less confusing than end of input + lexlineno++; + lineno = lexlineno; + if (linehistdone == 0) { + // All the wrappers can share the same linehist entry. + linehist("<autogenerated>", 0, 0); + linehistdone = 1; + } dclcontext = PEXTERN; markdcl(); @@ -2608,8 +2602,10 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface) funcbody(fn); curfn = fn; - // wrappers where T is anonymous (struct{ NamedType }) can be duplicated. - if(rcvr->etype == TSTRUCT || isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT) + // 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); @@ -3631,7 +3627,8 @@ ngotype(Node *n) * 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 copy too. + * If you edit this, edit ../ld/lib.c:/^pathtoprefix too. + * If you edit this, edit ../../pkg/debug/goobj/read.go:/importPathToPrefix too. */ static char* pathtoprefix(char *s) diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c index d6aa021a9..ce0190507 100644 --- a/src/cmd/gc/swt.c +++ b/src/cmd/gc/swt.c @@ -317,7 +317,7 @@ casebody(Node *sw, Node *typeswvar) // botch - shouldn't fall thru declaration last = stat->end->n; - if(last->op == OXFALL) { + if(last->xoffset == n->xoffset && last->op == OXFALL) { if(typeswvar) { setlineno(last); yyerror("cannot fallthrough in type switch"); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 31a2f2c5c..c50b2285b 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -310,7 +310,7 @@ typecheck1(Node **np, int top) int ok, ntop; Type *t, *tp, *missing, *have, *badtype; Val v; - char *why; + char *why, *desc, descbuf[64]; n = *np; @@ -368,7 +368,7 @@ reswitch: goto ret; case OPACK: - yyerror("use of package %S not in selector", n->sym); + yyerror("use of package %S without selector", n->sym); goto error; case ODDD: @@ -535,6 +535,19 @@ reswitch: op = n->etype; goto arith; + case OADDPTR: + ok |= Erv; + l = typecheck(&n->left, Erv); + r = typecheck(&n->right, Erv); + if(l->type == T || r->type == T) + goto error; + if(l->type->etype != tptr) + fatal("bad OADDPTR left type %E for %N", l->type->etype, n->left); + if(r->type->etype != TUINTPTR) + fatal("bad OADDPTR right type %E for %N", r->type->etype, n->right); + n->type = types[tptr]; + goto ret; + case OADD: case OAND: case OANDAND: @@ -654,8 +667,20 @@ reswitch: if(iscmp[n->op]) { n->etype = n->op; n->op = OCMPSTR; - } else if(n->op == OADD) + } 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) { @@ -721,8 +746,11 @@ reswitch: if(n->left->type == T) goto error; checklvalue(n->left, "take the address of"); - for(l=n->left; l->op == ODOT; l=l->left) + 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; @@ -864,7 +892,7 @@ reswitch: goto error; switch(t->etype) { default: - yyerror("invalid operation: %N (index of type %T)", n, t); + yyerror("invalid operation: %N (type %T does not support indexing)", n, t); goto error; @@ -947,7 +975,7 @@ reswitch: r = n->right; if(r->type == T) goto error; - r = assignconv(r, l->type->type, "send"); + n->right = assignconv(r, l->type->type, "send"); // TODO: more aggressive n->etype = 0; n->type = T; @@ -1062,6 +1090,7 @@ reswitch: 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) @@ -1123,7 +1152,11 @@ reswitch: } break; } - typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, "function argument"); + 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; @@ -1209,17 +1242,29 @@ reswitch: case OCOMPLEX: ok |= Erv; - if(twoarg(n) < 0) - goto error; - l = typecheck(&n->left, Erv | (top & Eiota)); - r = typecheck(&n->right, Erv | (top & Eiota)); - if(l->type == T || r->type == T) - goto error; - defaultlit2(&l, &r, 0); - if(l->type == T || r->type == T) - goto error; - n->left = l; - n->right = r; + 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; @@ -1298,9 +1343,22 @@ reswitch: yyerror("missing arguments to append"); goto error; } - typechecklist(args, Erv); + + 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 = t->type; + if(istype(t, TFIELD)) + t = t->type; + } + n->type = t; if(!isslice(t)) { if(isconst(args->n, CTNIL)) { @@ -1355,6 +1413,8 @@ reswitch: 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) { @@ -1406,6 +1466,9 @@ reswitch: } 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); @@ -1621,6 +1684,7 @@ reswitch: case OGOTO: case OLABEL: case OXFALL: + case OVARKILL: ok |= Etop; goto ret; @@ -2116,6 +2180,31 @@ nokeys(NodeList *l) 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 */ @@ -2126,14 +2215,25 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * 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) { @@ -2162,6 +2262,26 @@ typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char * 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) { @@ -2206,10 +2326,14 @@ out: return; notenough: - 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) { + 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: @@ -2252,10 +2376,13 @@ keydup(Node *n, Node *hash[], ulong nhash) ulong b; double d; int i; - Node *a; + 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 @@ -2288,17 +2415,25 @@ keydup(Node *n, Node *hash[], ulong nhash) for(a=hash[h]; a!=N; a=a->ntest) { cmp.op = OEQ; cmp.left = n; - cmp.right = a; - evconst(&cmp); - b = cmp.val.u.bval; + 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) { - // too lazy to print the literal yyerror("duplicate key %N in map literal", n); return; } } - n->ntest = hash[h]; - hash[h] = n; + orign->ntest = hash[h]; + hash[h] = orign; } static void @@ -2486,8 +2621,9 @@ typecheckcomplit(Node **np) typecheck(&l->left, Erv); evconst(l->left); i = nonnegconst(l->left); - if(i < 0) { + 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) @@ -2497,7 +2633,7 @@ typecheckcomplit(Node **np) len = i; if(t->bound >= 0 && len > t->bound) { setlineno(l); - yyerror("array index %d out of bounds [0:%d]", len, t->bound); + yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound); t->bound = -1; // no more errors } } @@ -2680,6 +2816,11 @@ checkassign(Node *n) n->etype = 1; return; } + + // have already complained about n being undefined + if(n->op == ONONAME) + return; + yyerror("cannot assign to %N", n); } @@ -2804,7 +2945,7 @@ typecheckas2(Node *n) n->op = OAS2FUNC; t = structfirst(&s, &r->type); for(ll=n->list; ll; ll=ll->next) { - if(ll->n->type != T) + 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; @@ -3130,7 +3271,10 @@ typecheckdef(Node *n) goto ret; } if(e->type != T && e->op != OLITERAL || !isgoconst(e)) { - yyerror("const initializer %N is not a constant", e); + if(!e->diag) { + yyerror("const initializer %N is not a constant", e); + e->diag = 1; + } goto ret; } t = n->type; @@ -3220,29 +3364,38 @@ static int checkmake(Type *t, char *arg, Node *n) { if(n->op == OLITERAL) { - 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; + 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; } - - // Delay defaultlit until after we've checked range, to avoid - // a redundant "constant NNN overflows int" error. - defaultlit(&n, types[TINT]); - return 0; } - - // Defaultlit still necessary for non-constant: n might be 1<<k. - defaultlit(&n, types[TINT]); - if(!isint[n->type->etype]) { + 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; } diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 66409d530..1cb25512e 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,7 +21,7 @@ static NodeList* reorder3(NodeList*); static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); -static Node* copyany(Node*, NodeList**); +static Node* copyany(Node*, NodeList**, int); static Node* sliceany(Node*, NodeList**); static void walkcompare(Node**, NodeList**); static void walkrotate(Node**); @@ -163,7 +163,6 @@ walkstmt(Node **np) case OCALLFUNC: case ODELETE: case OSEND: - case ORECV: case OPRINT: case OPRINTN: case OPANIC: @@ -179,6 +178,21 @@ walkstmt(Node **np) 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: @@ -188,6 +202,7 @@ walkstmt(Node **np) case ODCLCONST: case ODCLTYPE: case OCHECKNIL: + case OVARKILL: break; case OBLOCK: @@ -209,6 +224,9 @@ walkstmt(Node **np) walkexprlist(n->left->list, &n->ninit); n->left = walkprint(n->left, &n->ninit, 1); break; + case OCOPY: + n->left = copyany(n->left, &n->ninit, 1); + break; default: walkexpr(&n->left, &n->ninit); break; @@ -240,6 +258,9 @@ walkstmt(Node **np) walkexprlist(n->left->list, &n->ninit); n->left = walkprint(n->left, &n->ninit, 1); break; + case OCOPY: + n->left = copyany(n->left, &n->ninit, 1); + break; default: walkexpr(&n->left, &n->ninit); break; @@ -341,7 +362,8 @@ void walkexpr(Node **np, NodeList **init) { Node *r, *l, *var, *a; - NodeList *ll, *lr, *lpost; + Node *map, *key; + NodeList *ll, *lr; Type *t; int et, old_safemode; int64 v; @@ -455,7 +477,6 @@ walkexpr(Node **np, NodeList **init) case ORSH: walkexpr(&n->left, init); walkexpr(&n->right, init); - shiftwalked: t = n->left->type; n->bounded = bounded(n->right, 8*t->width); if(debug['m'] && n->etype && !isconst(n->right, CTINT)) @@ -472,6 +493,11 @@ walkexpr(Node **np, NodeList **init) 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; @@ -576,13 +602,32 @@ walkexpr(Node **np, NodeList **init) case OAS: *init = concat(*init, n->ninit); n->ninit = nil; + walkexpr(&n->left, init); n->left = safeexpr(n->left, init); if(oaslit(n, init)) goto ret; - walkexpr(&n->right, init); + if(n->right == N) + 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; @@ -602,53 +647,35 @@ walkexpr(Node **np, NodeList **init) goto ret; case OAS2FUNC: - as2func: // a,b,... = fn() *init = concat(*init, n->ninit); n->ninit = nil; r = n->rlist->n; walkexprlistsafe(n->list, init); walkexpr(&r, init); - l = n->list->n; - - // all the really hard stuff - explicit function calls and so on - - // is gone, but map assignments remain. - // if there are map assignments here, assign via - // temporaries, because ascompatet assumes - // the targets can be addressed without function calls - // and map index has an implicit one. - lpost = nil; - if(l->op == OINDEXMAP) { - var = temp(l->type); - n->list->n = var; - a = nod(OAS, l, var); - typecheck(&a, Etop); - lpost = list(lpost, a); - } - l = n->list->next->n; - if(l->op == OINDEXMAP) { - var = temp(l->type); - n->list->next->n = var; - a = nod(OAS, l, var); - typecheck(&a, Etop); - lpost = list(lpost, a); - } + ll = ascompatet(n->op, n->list, &r->type, 0, init); - walkexprlist(lpost, init); - n = liststmt(concat(concat(list1(r), ll), lpost)); + 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, getoutargx(fn->type), init, typename(r->left->type), r->left); - n->rlist->n = r; - n->op = OAS2FUNC; - goto as2func; + r = mkcall1(fn, types[TBOOL], 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]; @@ -657,6 +684,7 @@ walkexpr(Node **np, NodeList **init) 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 ../../pkg/runtime/hashmap.c:MAXVALUESIZE before changing. @@ -675,41 +703,50 @@ walkexpr(Node **np, NodeList **init) } } if(p != nil) { - // from: - // a,b = m[i] - // to: - // var,b = mapaccess2_fast*(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, r->right); - 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); - goto ret; - } - fn = mapfn("mapaccess2", t); - r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, r->right); + // 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); n->rlist = list1(r); n->op = OAS2FUNC; - goto as2func; + 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; - l = n->list->n; - r = n->list->next->n; - t = l->type; - n = mkcall1(mapfndel("mapdelete", t), t->down, init, typename(t), l, r); + 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: @@ -872,7 +909,20 @@ walkexpr(Node **np, NodeList **init) goto ret; } } - ll = list(ll, n->left); + 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); @@ -909,51 +959,6 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); goto ret; - case OASOP: - if(n->etype == OANDNOT) { - n->etype = OAND; - n->right = nod(OCOM, n->right, N); - typecheck(&n->right, Erv); - } - n->left = safeexpr(n->left, init); - walkexpr(&n->left, init); - l = n->left; - walkexpr(&n->right, init); - - /* - * on 32-bit arch, rewrite 64-bit ops into l = l op r. - * on 386, rewrite float ops into l = l op r. - * everywhere, rewrite map ops into l = l op r. - * everywhere, rewrite string += into l = l op r. - * everywhere, rewrite integer/complex /= into l = l op r. - * TODO(rsc): Maybe this rewrite should be done always? - */ - et = n->left->type->etype; - if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || - (thechar == '8' && isfloat[et]) || - l->op == OINDEXMAP || - et == TSTRING || - (!isfloat[et] && n->etype == ODIV) || - n->etype == OMOD) { - l = safeexpr(n->left, init); - a = l; - if(a->op == OINDEXMAP) { - // map index has "lhs" bit set in a->etype. - // make a copy so we can clear it on the rhs. - a = nod(OXXX, N, N); - *a = *l; - a->etype = 0; - } - r = nod(OAS, l, nod(n->etype, a, n->right)); - typecheck(&r, Etop); - walkexpr(&r, init); - n = r; - goto ret; - } - if(n->etype == OLSH || n->etype == ORSH) - goto shiftwalked; - goto ret; - case OANDNOT: walkexpr(&n->left, init); n->op = OAND; @@ -998,7 +1003,7 @@ walkexpr(Node **np, NodeList **init) switch(n->op) { case OMOD: case ODIV: - if(widthptr > 4 || (et != TUINT64 && et != TINT64)) + if(widthreg >= 8 || (et != TUINT64 && et != TINT64)) goto ret; if(et == TINT64) strcpy(namebuf, "int64"); @@ -1063,6 +1068,8 @@ walkexpr(Node **np, NodeList **init) case OINDEXMAP: if(n->etype == 1) goto ret; + walkexpr(&n->left, init); + walkexpr(&n->right, init); t = n->left->type; p = nil; @@ -1082,23 +1089,25 @@ walkexpr(Node **np, NodeList **init) } } if(p != nil) { - // use fast version. The fast versions return a pointer to the value - we need - // to dereference it to get the result. - n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, n->right); - n = nod(OIND, n, N); - n->type = t->type; - n->typecheck = 1; + // fast versions take key by value + key = n->right; } else { - // no fast version for this key - n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right); - } + // 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: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left); - goto ret; + fatal("walkexpr ORECV"); // should see inside OAS only case OSLICE: if(n->right != N && n->right->left == N && n->right->right == N) { // noop @@ -1182,9 +1191,10 @@ walkexpr(Node **np, NodeList **init) // s + "badgerbadgerbadger" == "badgerbadgerbadger" if((n->etype == OEQ || n->etype == ONE) && isconst(n->right, CTSTR) && - n->left->op == OADDSTR && isconst(n->left->right, CTSTR) && - cmpslit(n->right, n->left->right) == 0) { - r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0)); + 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; @@ -1238,19 +1248,7 @@ walkexpr(Node **np, NodeList **init) goto ret; case OCOPY: - if(flag_race) { - if(n->right->type->etype == TSTRING) - fn = syslook("slicestringcopy", 1); - else - fn = syslook("copy", 1); - argtype(fn, n->left->type); - argtype(fn, n->right->type); - n = mkcall1(fn, n->type, init, - n->left, n->right, - nodintconst(n->left->type->type->width)); - goto ret; - } - n = copyany(n, init); + n = copyany(n, init, flag_race); goto ret; case OCLOSE: @@ -1321,6 +1319,11 @@ walkexpr(Node **np, NodeList **init) 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); @@ -1368,13 +1371,18 @@ walkexpr(Node **np, NodeList **init) case OMAPLIT: case OSTRUCTLIT: case OPTRLIT: + // XXX TODO do we need to clear var? var = temp(n->type); anylit(0, n, var, init); n = var; goto ret; case OSEND: - n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right); + 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: @@ -1534,11 +1542,16 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) * 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, int esc) +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; @@ -1548,6 +1561,8 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int 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); @@ -1619,7 +1634,6 @@ dumpnodetypes(NodeList *l, char *what) static NodeList* ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) { - int esc; Type *l, *ll; Node *r, *a; NodeList *nn, *lr0, *alist; @@ -1638,7 +1652,8 @@ ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeL // optimization - can do block copy if(eqtypenoname(r->type, *nl)) { a = nodarg(*nl, fp); - a->type = r->type; + r = nod(OCONVNOP, r, N); + r->type = a->type; nn = list1(convas(nod(OAS, a, r), init)); goto ret; } @@ -1682,10 +1697,7 @@ loop: // normal case -- make a slice of all // remaining arguments and pass it to // the ddd parameter. - esc = EscUnknown; - if(call->right) - esc = call->right->esc; - nn = mkdotargslice(lr, nn, l, fp, init, esc); + nn = mkdotargslice(lr, nn, l, fp, init, call->right); goto ret; } @@ -1911,6 +1923,7 @@ 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); @@ -1931,9 +1944,17 @@ convas(Node *n, NodeList **init) } if(n->left->op == OINDEXMAP) { - n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init, - typename(n->left->left->type), - n->left->left, n->left->right, n->right); + 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; } @@ -2101,7 +2122,7 @@ reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early) * what's the outer value that a write to n affects? * outer value means containing struct or array. */ -static Node* +Node* outervalue(Node *n) { for(;;) { @@ -2320,7 +2341,7 @@ paramstoheap(Type **argin, int out) nn = nil; for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { v = t->nname; - if(v && v->sym && v->sym->name[0] == '~') + 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. @@ -2493,33 +2514,38 @@ mapfndel(char *name, Type *t) static Node* addstr(Node *n, NodeList **init) { - Node *r, *cat, *typstr; - NodeList *in, *args; - int i, count; - - count = 0; - for(r=n; r->op == OADDSTR; r=r->left) - count++; // r->right - count++; // r - - // prepare call of runtime.catstring of type int, string, string, string - // with as many strings as we have. - cat = syslook("concatstring", 1); - cat->type = T; - cat->ntype = nod(OTFUNC, N, N); - in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count - typstr = typenod(types[TSTRING]); - for(i=0; i<count; i++) - in = list(in, nod(ODCLFIELD, N, typstr)); - cat->ntype->list = in; - cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr)); + 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(r=n; r->op == OADDSTR; r=r->left) - args = concat(list1(conv(r->right, types[TSTRING])), args); - args = concat(list1(conv(r, types[TSTRING])), args); - args = concat(list1(nodintconst(count)), args); + 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); @@ -2597,9 +2623,10 @@ appendslice(Node *n, NodeList **init) fn = syslook("copy", 1); argtype(fn, l1->type); argtype(fn, l2->type); - l = list(l, mkcall1(fn, types[TINT], init, + nt = mkcall1(fn, types[TINT], &l, nptr1, nptr2, - nodintconst(s->type->type->width))); + 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)); @@ -2614,7 +2641,8 @@ appendslice(Node *n, NodeList **init) nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l); nwid = nod(OMUL, nwid, nodintconst(s->type->type->width)); - l = list(l, mkcall1(fn, T, init, nptr1, nptr2, nwid)); + nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid); + l = list(l, nt); } // s = s[:len(l1)+len(l2)] @@ -2660,6 +2688,10 @@ append(Node *n, NodeList **init) 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; @@ -2705,7 +2737,7 @@ append(Node *n, NodeList **init) return ns; } -// Lower copy(a, b) to a memmove call. +// Lower copy(a, b) to a memmove call or a runtime call. // // init { // n := len(a) @@ -2717,11 +2749,22 @@ append(Node *n, NodeList **init) // Also works if b is a string. // static Node* -copyany(Node *n, NodeList **init) +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("copy", 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); @@ -2802,17 +2845,13 @@ sliceany(Node* n, NodeList **init) if(isconst(cb, CTINT)) { cbv = mpgetfix(cb->val.u.xval); - if(cbv < 0 || cbv > bv) { + if(cbv < 0 || cbv > bv) yyerror("slice index out of bounds"); - cbv = -1; - } } if(isconst(hb, CTINT)) { hbv = mpgetfix(hb->val.u.xval); - if(hbv < 0 || hbv > bv) { + if(hbv < 0 || hbv > bv) yyerror("slice index out of bounds"); - hbv = -1; - } } if(isconst(lb, CTINT)) { lbv = mpgetfix(lb->val.u.xval); @@ -3052,13 +3091,10 @@ walkcompare(Node **np, NodeList **init) } if(expr == N) expr = nodbool(n->op == OEQ); - typecheck(&expr, Erv); - walkexpr(&expr, init); - expr->type = n->type; - *np = expr; - return; + r = expr; + goto ret; } - + if(t->etype == TSTRUCT && countfield(t) <= 4) { // Struct of four or fewer fields. // Inline comparisons. @@ -3075,13 +3111,10 @@ walkcompare(Node **np, NodeList **init) } if(expr == N) expr = nodbool(n->op == OEQ); - typecheck(&expr, Erv); - walkexpr(&expr, init); - expr->type = n->type; - *np = expr; - return; + r = expr; + goto ret; } - + // Chose not to inline, but still have addresses. // Call equality function directly. // The equality function requires a bool pointer for @@ -3114,10 +3147,7 @@ walkcompare(Node **np, NodeList **init) if(n->op != OEQ) r = nod(ONOT, r, N); - typecheck(&r, Erv); - walkexpr(&r, init); - *np = r; - return; + goto ret; hard: // Cannot take address of one or both of the operands. @@ -3133,7 +3163,16 @@ hard: r = mkcall1(fn, n->type, init, typename(n->left->type), l, r); if(n->op == ONE) { r = nod(ONOT, r, N); - typecheck(&r, Erv); + } + 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; diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c index 390ad80b3..08d8ecff2 100644 --- a/src/cmd/gc/y.tab.c +++ b/src/cmd/gc/y.tab.c @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison implementation for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* 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 3 of the License, or - (at your option) any later version. - + 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, see <http://www.gnu.org/licenses/>. */ + 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 @@ -26,7 +29,7 @@ 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. */ @@ -44,7 +47,7 @@ #define YYBISON 1 /* Bison version. */ -#define YYBISON_VERSION "2.5" +#define YYBISON_VERSION "2.3" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" @@ -52,52 +55,11 @@ /* Pure parsers. */ #define YYPURE 0 -/* Push parsers. */ -#define YYPUSH 0 - -/* Pull parsers. */ -#define YYPULL 1 - /* Using locations. */ #define YYLSP_NEEDED 0 -/* Copy the first part of user declarations. */ - -/* Line 268 of yacc.c */ -#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); - - -/* Line 268 of yacc.c */ -#line 81 "y.tab.c" - -/* 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 - - /* Tokens. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE @@ -209,36 +171,61 @@ static void fixlbrace(int); +/* 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 293 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 293 of yacc.c */ -#line 230 "y.tab.c" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* 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 343 of yacc.c */ -#line 242 "y.tab.c" +/* Line 216 of yacc.c. */ +#line 229 "y.tab.c" #ifdef short # undef short @@ -313,14 +300,14 @@ typedef short int yytype_int16; #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static int -YYID (int yyi) +YYID (int i) #else static int -YYID (yyi) - int yyi; +YYID (i) + int i; #endif { - return yyi; + return i; } #endif @@ -341,11 +328,11 @@ YYID (yyi) # define alloca _alloca # else # define YYSTACK_ALLOC alloca -# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# 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 EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # endif @@ -368,24 +355,24 @@ YYID (yyi) # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif -# if (defined __cplusplus && ! defined EXIT_SUCCESS \ +# if (defined __cplusplus && ! defined _STDLIB_H \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include <stdlib.h> /* INFRINGES ON USER NAME SPACE */ -# ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 +# ifndef _STDLIB_H +# define _STDLIB_H 1 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc -# if ! defined malloc && ! defined EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# 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 EXIT_SUCCESS && (defined __STDC__ || defined __C99__FUNC__ \ +# 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 @@ -401,9 +388,9 @@ void free (void *); /* INFRINGES ON USER NAME SPACE */ /* A type that is properly aligned for any stack member. */ union yyalloc { - yytype_int16 yyss_alloc; - YYSTYPE yyvs_alloc; -}; + 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) @@ -414,27 +401,6 @@ union yyalloc ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) -# define YYCOPY_NEEDED 1 - -/* 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_alloc, Stack) \ - do \ - { \ - YYSIZE_T yynewbytes; \ - YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ - Stack = &yyptr->Stack_alloc; \ - yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ - yyptr += yynewbytes / sizeof (*yyptr); \ - } \ - while (YYID (0)) - -#endif - -#if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from FROM to TO. The source and destination do not overlap. */ # ifndef YYCOPY @@ -452,7 +418,24 @@ union yyalloc while (YYID (0)) # endif # endif -#endif /* !YYCOPY_NEEDED */ + +/* 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 @@ -681,36 +664,36 @@ static const yytype_uint16 yyrline[] = 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, 465, 472, 492, 510, 519, - 538, 537, 552, 551, 582, 585, 592, 591, 602, 608, - 617, 628, 634, 637, 645, 644, 655, 661, 673, 677, - 682, 672, 703, 702, 715, 718, 724, 727, 739, 743, - 738, 761, 760, 776, 777, 781, 785, 789, 793, 797, - 801, 805, 809, 813, 817, 821, 825, 829, 833, 837, - 841, 845, 849, 854, 860, 861, 865, 876, 880, 884, - 888, 893, 897, 907, 911, 916, 924, 928, 929, 940, - 944, 948, 952, 956, 964, 965, 971, 978, 984, 991, - 994, 1001, 1007, 1024, 1031, 1032, 1039, 1040, 1059, 1060, - 1063, 1066, 1070, 1081, 1090, 1096, 1099, 1102, 1109, 1110, - 1116, 1129, 1144, 1152, 1164, 1169, 1175, 1176, 1177, 1178, - 1179, 1180, 1186, 1187, 1188, 1189, 1195, 1196, 1197, 1198, - 1199, 1205, 1206, 1209, 1212, 1213, 1214, 1215, 1216, 1219, - 1220, 1233, 1237, 1242, 1247, 1252, 1256, 1257, 1260, 1266, - 1273, 1279, 1286, 1292, 1303, 1317, 1346, 1386, 1411, 1429, - 1438, 1441, 1449, 1453, 1457, 1464, 1470, 1475, 1487, 1490, - 1500, 1501, 1507, 1508, 1514, 1518, 1524, 1525, 1531, 1535, - 1541, 1564, 1569, 1575, 1581, 1588, 1597, 1606, 1621, 1627, - 1632, 1636, 1643, 1656, 1657, 1663, 1669, 1672, 1676, 1682, - 1685, 1694, 1697, 1698, 1702, 1703, 1709, 1710, 1711, 1712, - 1713, 1715, 1714, 1729, 1734, 1738, 1742, 1746, 1750, 1755, - 1774, 1780, 1788, 1792, 1798, 1802, 1808, 1812, 1818, 1822, - 1831, 1835, 1839, 1843, 1849, 1852, 1860, 1861, 1863, 1864, - 1867, 1870, 1873, 1876, 1879, 1882, 1885, 1888, 1891, 1894, - 1897, 1900, 1903, 1906, 1912, 1916, 1920, 1924, 1928, 1932, - 1952, 1959, 1970, 1971, 1972, 1975, 1976, 1979, 1983, 1993, - 1997, 2001, 2005, 2009, 2013, 2017, 2023, 2029, 2037, 2045, - 2051, 2058, 2074, 2096, 2100, 2106, 2109, 2112, 2116, 2126, - 2130, 2145, 2153, 2154, 2166, 2167, 2170, 2174, 2180, 2184, - 2190, 2194 + 538, 537, 552, 551, 583, 586, 593, 592, 603, 609, + 618, 629, 635, 638, 646, 645, 656, 662, 674, 678, + 683, 673, 704, 703, 716, 719, 725, 728, 740, 744, + 739, 762, 761, 777, 778, 782, 786, 790, 794, 798, + 802, 806, 810, 814, 818, 822, 826, 830, 834, 838, + 842, 846, 850, 855, 861, 862, 866, 877, 881, 885, + 889, 894, 898, 908, 912, 917, 925, 929, 930, 941, + 945, 949, 953, 957, 965, 966, 972, 979, 985, 992, + 995, 1002, 1008, 1025, 1032, 1033, 1040, 1041, 1060, 1061, + 1064, 1067, 1071, 1082, 1091, 1097, 1100, 1103, 1110, 1111, + 1117, 1130, 1145, 1153, 1165, 1170, 1176, 1177, 1178, 1179, + 1180, 1181, 1187, 1188, 1189, 1190, 1196, 1197, 1198, 1199, + 1200, 1206, 1207, 1210, 1213, 1214, 1215, 1216, 1217, 1220, + 1221, 1234, 1238, 1243, 1248, 1253, 1257, 1258, 1261, 1267, + 1274, 1280, 1287, 1293, 1304, 1318, 1347, 1387, 1412, 1430, + 1439, 1442, 1450, 1454, 1458, 1465, 1471, 1476, 1488, 1491, + 1501, 1502, 1508, 1509, 1515, 1519, 1525, 1526, 1532, 1536, + 1542, 1565, 1570, 1576, 1582, 1589, 1598, 1607, 1622, 1628, + 1633, 1637, 1644, 1657, 1658, 1664, 1670, 1673, 1677, 1683, + 1686, 1695, 1698, 1699, 1703, 1704, 1710, 1711, 1712, 1713, + 1714, 1716, 1715, 1730, 1736, 1740, 1744, 1748, 1752, 1757, + 1776, 1782, 1790, 1794, 1800, 1804, 1810, 1814, 1820, 1824, + 1833, 1837, 1841, 1845, 1851, 1854, 1862, 1863, 1865, 1866, + 1869, 1872, 1875, 1878, 1881, 1884, 1887, 1890, 1893, 1896, + 1899, 1902, 1905, 1908, 1914, 1918, 1922, 1926, 1930, 1934, + 1954, 1961, 1972, 1973, 1974, 1977, 1978, 1981, 1985, 1995, + 1999, 2003, 2007, 2011, 2015, 2019, 2025, 2031, 2039, 2047, + 2053, 2060, 2076, 2098, 2102, 2108, 2111, 2114, 2118, 2128, + 2132, 2151, 2159, 2160, 2172, 2173, 2176, 2180, 2186, 2190, + 2196, 2200 }; #endif @@ -729,16 +712,16 @@ const char *yytname[] = "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'", "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'", "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file", - "package", "loadsys", "$@1", "imports", "import", "import_stmt", + "package", "loadsys", "@1", "imports", "import", "import_stmt", "import_stmt_list", "import_here", "import_package", "import_safety", - "import_there", "$@2", "xdcl", "common_dcl", "lconst", "vardcl", + "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", + "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", @@ -748,7 +731,7 @@ const char *yytname[] = "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", + "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", @@ -861,8 +844,8 @@ static const yytype_uint8 yyr2[] = 1, 3 }; -/* YYDEFACT[STATE-NAME] -- Default reduction number in state STATE-NUM. - Performed when YYTABLE doesn't specify something else to do. Zero +/* 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[] = { @@ -1051,7 +1034,8 @@ static const yytype_int16 yypgoto[] = /* 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 YYTABLE_NINF, syntax error. */ + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ #define YYTABLE_NINF -277 static const yytype_int16 yytable[] = { @@ -1285,12 +1269,6 @@ static const yytype_int16 yytable[] = 198 }; -#define yypact_value_is_default(yystate) \ - ((yystate) == (-474)) - -#define yytable_value_is_error(yytable_value) \ - YYID (0) - static const yytype_int16 yycheck[] = { 37, 37, 61, 143, 67, 37, 37, 202, 324, 251, @@ -1608,18 +1586,9 @@ static const yytype_uint8 yystos[] = /* 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. However, - YYFAIL appears to be in use. Nevertheless, it is formally deprecated - in Bison 2.4.2's NEWS entry, where a plan to phase it out is - discussed. */ + Once GCC version 2 has supplanted version 1, this can go. */ #define YYFAIL goto yyerrlab -#if defined YYFAIL - /* This is here to suppress warnings from the GCC cpp's - -Wunused-macros. Normally we don't worry about that warning, but - some users do, and we want to make it easy for users to remove - YYFAIL uses, which will produce warnings from Bison 2.5. */ -#endif #define YYRECOVERING() (!!yyerrstatus) @@ -1629,6 +1598,7 @@ do \ { \ yychar = (Token); \ yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ YYPOPSTACK (1); \ goto yybackup; \ } \ @@ -1670,10 +1640,19 @@ while (YYID (0)) #endif -/* This macro is provided for backward compatibility. */ +/* 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 -# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# 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 @@ -1777,20 +1756,17 @@ yy_symbol_print (yyoutput, yytype, yyvaluep) #if (defined __STDC__ || defined __C99__FUNC__ \ || defined __cplusplus || defined _MSC_VER) static void -yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) #else static void -yy_stack_print (yybottom, yytop) - yytype_int16 *yybottom; - yytype_int16 *yytop; +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; #endif { YYFPRINTF (stderr, "Stack now"); - for (; yybottom <= yytop; yybottom++) - { - int yybot = *yybottom; - YYFPRINTF (stderr, " %d", yybot); - } + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); YYFPRINTF (stderr, "\n"); } @@ -1824,11 +1800,11 @@ yy_reduce_print (yyvsp, yyrule) /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { - YYFPRINTF (stderr, " $%d = ", yyi + 1); + fprintf (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], &(yyvsp[(yyi + 1) - (yynrhs)]) ); - YYFPRINTF (stderr, "\n"); + fprintf (stderr, "\n"); } } @@ -1865,6 +1841,7 @@ int yydebug; # define YYMAXDEPTH 10000 #endif + #if YYERROR_VERBOSE @@ -1967,142 +1944,115 @@ yytnamerr (char *yyres, const char *yystr) } # endif -/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message - about the unexpected token YYTOKEN for the state stack whose top is - YYSSP. - - Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is - not large enough to hold the message. In that case, also set - *YYMSG_ALLOC to the required number of bytes. Return 2 if the - required number of bytes is too large to store. */ -static int -yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, - yytype_int16 *yyssp, int yytoken) +/* 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) { - YYSIZE_T yysize0 = yytnamerr (0, yytname[yytoken]); - YYSIZE_T yysize = yysize0; - YYSIZE_T yysize1; - enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; - /* Internationalized format string. */ - const char *yyformat = 0; - /* Arguments of yyformat. */ - char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; - /* Number of reported tokens (one for the "unexpected", one per - "expected"). */ - int yycount = 0; - - /* There are many possibilities here to consider: - - Assume YYFAIL is not used. It's too flawed to consider. See - <http://lists.gnu.org/archive/html/bison-patches/2009-12/msg00024.html> - for details. YYERROR is fine as it does not invoke this - function. - - If this state is a consistent state with a default action, then - the only way this function was invoked is if the default action - is an error action. In that case, don't check for expected - tokens because there are none. - - The only way there can be no lookahead present (in yychar) is if - this state is a consistent state with a default action. Thus, - detecting the absence of a lookahead is sufficient to determine - that there is no unexpected or expected token to report. In that - case, just report a simple "syntax error". - - Don't assume there isn't a lookahead just because this state is a - consistent state with a default action. There might have been a - previous inconsistent state, consistent state with a non-default - action, or user semantic action that manipulated yychar. - - Of course, the expected token list depends on states to have - correct lookahead information, and it depends on the parser not - to perform extra reductions after fetching a lookahead from the - scanner and before detecting a syntax error. Thus, state merging - (from LALR or IELR) and default reductions corrupt the expected - token list. However, the list is correct for canonical LR with - one exception: it will still contain any token that will not be - accepted due to an error action in a later state. - */ - if (yytoken != YYEMPTY) - { - int yyn = yypact[*yyssp]; - yyarg[yycount++] = yytname[yytoken]; - if (!yypact_value_is_default (yyn)) - { - /* Start YYX at -YYN if negative to avoid negative indexes in - YYCHECK. In other words, skip the first -YYN actions for - this state because they are default actions. */ - 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 yyx; - - for (yyx = yyxbegin; yyx < yyxend; ++yyx) - if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR - && !yytable_value_is_error (yytable[yyx + yyn])) - { - if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) - { - yycount = 1; - yysize = yysize0; - break; - } - yyarg[yycount++] = yytname[yyx]; - yysize1 = yysize + yytnamerr (0, yytname[yyx]); - if (! (yysize <= yysize1 - && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; - } - } - } + int yyn = yypact[yystate]; - switch (yycount) - { -# define YYCASE_(N, S) \ - case N: \ - yyformat = S; \ - break - YYCASE_(0, YY_("syntax error")); - YYCASE_(1, YY_("syntax error, unexpected %s")); - YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); - YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); - YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); - YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); -# undef YYCASE_ - } + 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; + } - yysize1 = yysize + yystrlen (yyformat); - if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) - return 2; - yysize = yysize1; + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; - if (*yymsg_alloc < yysize) - { - *yymsg_alloc = 2 * yysize; - if (! (yysize <= *yymsg_alloc - && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) - *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; - return 1; - } + if (yysize_overflow) + return YYSIZE_MAXIMUM; - /* 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 = *yymsg; - int yyi = 0; - while ((*yyp = *yyformat) != '\0') - if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) - { - yyp += yytnamerr (yyp, yyarg[yyi++]); - yyformat += 2; - } - else - { - yyp++; - yyformat++; - } - } - return 0; + 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. | @@ -2134,9 +2084,10 @@ yydestruct (yymsg, yytype, yyvaluep) break; } } - + /* Prevent warnings from -Wmissing-prototypes. */ + #ifdef YYPARSE_PARAM #if defined __STDC__ || defined __cplusplus int yyparse (void *YYPARSE_PARAM); @@ -2152,16 +2103,18 @@ int yyparse (); #endif /* ! YYPARSE_PARAM */ -/* The lookahead symbol. */ + +/* The look-ahead symbol. */ int yychar, yystate; -/* The semantic value of the lookahead symbol. */ +/* The semantic value of the look-ahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; + /*----------. | yyparse. | `----------*/ @@ -2188,36 +2141,13 @@ yyparse () #endif #endif { - /* Number of tokens to shift before error messages enabled. */ - int yyerrstatus; - - /* The stacks and their tools: - `yyss': related to states. - `yyvs': related to semantic values. - - Refer to the stacks thru separate pointers, to allow yyoverflow - to reallocate them elsewhere. */ - - /* The state stack. */ - yytype_int16 yyssa[YYINITDEPTH]; - yytype_int16 *yyss; - yytype_int16 *yyssp; - - /* The semantic value stack. */ - YYSTYPE yyvsa[YYINITDEPTH]; - YYSTYPE *yyvs; - YYSTYPE *yyvsp; - - YYSIZE_T yystacksize; - + int yyn; int yyresult; - /* Lookahead token as an internal (translated) token number. */ - int yytoken; - /* The variables used to return semantic value and location from the - action routines. */ - YYSTYPE yyval; - + /* 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]; @@ -2225,28 +2155,51 @@ yyparse () 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; - yytoken = 0; - yyss = yyssa; - yyvs = yyvsa; - yystacksize = YYINITDEPTH; - YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; - yychar = YYEMPTY; /* Cause a token to be read. */ + 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; @@ -2276,6 +2229,7 @@ yyparse () 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 @@ -2283,6 +2237,7 @@ yyparse () yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); yyss = yyss1; @@ -2305,8 +2260,9 @@ yyparse () (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; - YYSTACK_RELOCATE (yyss_alloc, yyss); - YYSTACK_RELOCATE (yyvs_alloc, yyvs); + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); @@ -2317,6 +2273,7 @@ yyparse () yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; + YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); @@ -2326,9 +2283,6 @@ yyparse () YYDPRINTF ((stderr, "Entering state %d\n", yystate)); - if (yystate == YYFINAL) - YYACCEPT; - goto yybackup; /*-----------. @@ -2337,16 +2291,16 @@ yyparse () yybackup: /* Do appropriate processing given the current state. Read a - lookahead token if we need one and don't already have one. */ + look-ahead token if we need one and don't already have one. */ - /* First try to decide what to do without reference to lookahead token. */ + /* First try to decide what to do without reference to look-ahead token. */ yyn = yypact[yystate]; - if (yypact_value_is_default (yyn)) + if (yyn == YYPACT_NINF) goto yydefault; - /* Not known => get a lookahead token if don't already have one. */ + /* Not known => get a look-ahead token if don't already have one. */ - /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); @@ -2372,22 +2326,26 @@ yybackup: yyn = yytable[yyn]; if (yyn <= 0) { - if (yytable_value_is_error (yyn)) - goto yyerrlab; + 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 lookahead token. */ + /* Shift the look-ahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); - /* Discard the shifted token. */ - yychar = YYEMPTY; + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; yystate = yyn; *++yyvsp = yylval; @@ -2427,8 +2385,6 @@ yyreduce: switch (yyn) { case 2: - -/* Line 1806 of yacc.c */ #line 128 "go.y" { xtop = concat(xtop, (yyvsp[(4) - (4)].list)); @@ -2436,8 +2392,6 @@ yyreduce: break; case 3: - -/* Line 1806 of yacc.c */ #line 134 "go.y" { prevlineno = lineno; @@ -2447,8 +2401,6 @@ yyreduce: break; case 4: - -/* Line 1806 of yacc.c */ #line 140 "go.y" { mkpackage((yyvsp[(2) - (3)].sym)->name); @@ -2456,8 +2408,6 @@ yyreduce: break; case 5: - -/* Line 1806 of yacc.c */ #line 150 "go.y" { importpkg = runtimepkg; @@ -2471,8 +2421,6 @@ yyreduce: break; case 6: - -/* Line 1806 of yacc.c */ #line 161 "go.y" { importpkg = nil; @@ -2480,8 +2428,6 @@ yyreduce: break; case 12: - -/* Line 1806 of yacc.c */ #line 175 "go.y" { Pkg *ipkg; @@ -2522,8 +2468,6 @@ yyreduce: break; case 13: - -/* Line 1806 of yacc.c */ #line 212 "go.y" { // When an invalid import path is passed to importfile, @@ -2536,8 +2480,6 @@ yyreduce: break; case 16: - -/* Line 1806 of yacc.c */ #line 227 "go.y" { // import with original name @@ -2548,8 +2490,6 @@ yyreduce: break; case 17: - -/* Line 1806 of yacc.c */ #line 234 "go.y" { // import with given name @@ -2560,8 +2500,6 @@ yyreduce: break; case 18: - -/* Line 1806 of yacc.c */ #line 241 "go.y" { // import into my name space @@ -2572,8 +2510,6 @@ yyreduce: break; case 19: - -/* Line 1806 of yacc.c */ #line 250 "go.y" { if(importpkg->name == nil) { @@ -2590,8 +2526,6 @@ yyreduce: break; case 21: - -/* Line 1806 of yacc.c */ #line 265 "go.y" { if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0) @@ -2600,8 +2534,6 @@ yyreduce: break; case 22: - -/* Line 1806 of yacc.c */ #line 271 "go.y" { defercheckwidth(); @@ -2609,8 +2541,6 @@ yyreduce: break; case 23: - -/* Line 1806 of yacc.c */ #line 275 "go.y" { resumecheckwidth(); @@ -2619,8 +2549,6 @@ yyreduce: break; case 24: - -/* Line 1806 of yacc.c */ #line 284 "go.y" { yyerror("empty top-level declaration"); @@ -2629,8 +2557,6 @@ yyreduce: break; case 26: - -/* Line 1806 of yacc.c */ #line 290 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); @@ -2638,8 +2564,6 @@ yyreduce: break; case 27: - -/* Line 1806 of yacc.c */ #line 294 "go.y" { yyerror("non-declaration statement outside function body"); @@ -2648,8 +2572,6 @@ yyreduce: break; case 28: - -/* Line 1806 of yacc.c */ #line 299 "go.y" { (yyval.list) = nil; @@ -2657,8 +2579,6 @@ yyreduce: break; case 29: - -/* Line 1806 of yacc.c */ #line 305 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2666,8 +2586,6 @@ yyreduce: break; case 30: - -/* Line 1806 of yacc.c */ #line 309 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2675,8 +2593,6 @@ yyreduce: break; case 31: - -/* Line 1806 of yacc.c */ #line 313 "go.y" { (yyval.list) = nil; @@ -2684,8 +2600,6 @@ yyreduce: break; case 32: - -/* Line 1806 of yacc.c */ #line 317 "go.y" { (yyval.list) = (yyvsp[(2) - (2)].list); @@ -2695,8 +2609,6 @@ yyreduce: break; case 33: - -/* Line 1806 of yacc.c */ #line 323 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2706,8 +2618,6 @@ yyreduce: break; case 34: - -/* Line 1806 of yacc.c */ #line 329 "go.y" { (yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list)); @@ -2717,8 +2627,6 @@ yyreduce: break; case 35: - -/* Line 1806 of yacc.c */ #line 335 "go.y" { (yyval.list) = nil; @@ -2727,8 +2635,6 @@ yyreduce: break; case 36: - -/* Line 1806 of yacc.c */ #line 340 "go.y" { (yyval.list) = list1((yyvsp[(2) - (2)].node)); @@ -2736,8 +2642,6 @@ yyreduce: break; case 37: - -/* Line 1806 of yacc.c */ #line 344 "go.y" { (yyval.list) = (yyvsp[(3) - (5)].list); @@ -2745,8 +2649,6 @@ yyreduce: break; case 38: - -/* Line 1806 of yacc.c */ #line 348 "go.y" { (yyval.list) = nil; @@ -2754,8 +2656,6 @@ yyreduce: break; case 39: - -/* Line 1806 of yacc.c */ #line 354 "go.y" { iota = 0; @@ -2763,8 +2663,6 @@ yyreduce: break; case 40: - -/* Line 1806 of yacc.c */ #line 360 "go.y" { (yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2772,8 +2670,6 @@ yyreduce: break; case 41: - -/* Line 1806 of yacc.c */ #line 364 "go.y" { (yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2781,8 +2677,6 @@ yyreduce: break; case 42: - -/* Line 1806 of yacc.c */ #line 368 "go.y" { (yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list)); @@ -2790,8 +2684,6 @@ yyreduce: break; case 43: - -/* Line 1806 of yacc.c */ #line 374 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list)); @@ -2799,8 +2691,6 @@ yyreduce: break; case 44: - -/* Line 1806 of yacc.c */ #line 378 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list)); @@ -2808,8 +2698,6 @@ yyreduce: break; case 46: - -/* Line 1806 of yacc.c */ #line 385 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil); @@ -2817,8 +2705,6 @@ yyreduce: break; case 47: - -/* Line 1806 of yacc.c */ #line 389 "go.y" { (yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil); @@ -2826,8 +2712,6 @@ yyreduce: break; case 48: - -/* Line 1806 of yacc.c */ #line 395 "go.y" { // different from dclname because the name @@ -2838,8 +2722,6 @@ yyreduce: break; case 49: - -/* Line 1806 of yacc.c */ #line 404 "go.y" { (yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1); @@ -2847,8 +2729,6 @@ yyreduce: break; case 50: - -/* Line 1806 of yacc.c */ #line 410 "go.y" { (yyval.node) = (yyvsp[(1) - (1)].node); @@ -2870,8 +2750,6 @@ yyreduce: break; case 51: - -/* Line 1806 of yacc.c */ #line 428 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); @@ -2880,8 +2758,6 @@ yyreduce: break; case 52: - -/* Line 1806 of yacc.c */ #line 433 "go.y" { if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) { @@ -2897,8 +2773,6 @@ yyreduce: break; case 53: - -/* Line 1806 of yacc.c */ #line 445 "go.y" { if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) { @@ -2918,8 +2792,6 @@ yyreduce: break; case 54: - -/* Line 1806 of yacc.c */ #line 461 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); @@ -2928,8 +2800,6 @@ yyreduce: break; case 55: - -/* Line 1806 of yacc.c */ #line 466 "go.y" { (yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1)); @@ -2938,8 +2808,6 @@ yyreduce: break; case 56: - -/* Line 1806 of yacc.c */ #line 473 "go.y" { Node *n, *nn; @@ -2963,8 +2831,6 @@ yyreduce: break; case 57: - -/* Line 1806 of yacc.c */ #line 493 "go.y" { Node *n; @@ -2986,8 +2852,6 @@ yyreduce: break; case 58: - -/* Line 1806 of yacc.c */ #line 511 "go.y" { // will be converted to OCASE @@ -3000,8 +2864,6 @@ yyreduce: break; case 59: - -/* Line 1806 of yacc.c */ #line 520 "go.y" { Node *n, *nn; @@ -3021,8 +2883,6 @@ yyreduce: break; case 60: - -/* Line 1806 of yacc.c */ #line 538 "go.y" { markdcl(); @@ -3030,8 +2890,6 @@ yyreduce: break; case 61: - -/* Line 1806 of yacc.c */ #line 542 "go.y" { if((yyvsp[(3) - (4)].list) == nil) @@ -3043,8 +2901,6 @@ yyreduce: break; case 62: - -/* Line 1806 of yacc.c */ #line 552 "go.y" { // If the last token read by the lexer was consumed @@ -3054,13 +2910,12 @@ yyreduce: // 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 1806 of yacc.c */ -#line 562 "go.y" +#line 563 "go.y" { int last; @@ -3082,36 +2937,28 @@ yyreduce: break; case 64: - -/* Line 1806 of yacc.c */ -#line 582 "go.y" +#line 583 "go.y" { (yyval.list) = nil; } break; case 65: - -/* Line 1806 of yacc.c */ -#line 586 "go.y" +#line 587 "go.y" { (yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node)); } break; case 66: - -/* Line 1806 of yacc.c */ -#line 592 "go.y" +#line 593 "go.y" { markdcl(); } break; case 67: - -/* Line 1806 of yacc.c */ -#line 596 "go.y" +#line 597 "go.y" { (yyval.list) = (yyvsp[(3) - (4)].list); popdcl(); @@ -3119,9 +2966,7 @@ yyreduce: break; case 68: - -/* Line 1806 of yacc.c */ -#line 603 "go.y" +#line 604 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3130,9 +2975,7 @@ yyreduce: break; case 69: - -/* Line 1806 of yacc.c */ -#line 609 "go.y" +#line 610 "go.y" { (yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node)); (yyval.node)->list = (yyvsp[(1) - (4)].list); @@ -3142,9 +2985,7 @@ yyreduce: break; case 70: - -/* Line 1806 of yacc.c */ -#line 618 "go.y" +#line 619 "go.y" { // init ; test ; incr if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0) @@ -3158,9 +2999,7 @@ yyreduce: break; case 71: - -/* Line 1806 of yacc.c */ -#line 629 "go.y" +#line 630 "go.y" { // normal test (yyval.node) = nod(OFOR, N, N); @@ -3169,9 +3008,7 @@ yyreduce: break; case 73: - -/* Line 1806 of yacc.c */ -#line 638 "go.y" +#line 639 "go.y" { (yyval.node) = (yyvsp[(1) - (2)].node); (yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list)); @@ -3179,18 +3016,14 @@ yyreduce: break; case 74: - -/* Line 1806 of yacc.c */ -#line 645 "go.y" +#line 646 "go.y" { markdcl(); } break; case 75: - -/* Line 1806 of yacc.c */ -#line 649 "go.y" +#line 650 "go.y" { (yyval.node) = (yyvsp[(3) - (3)].node); popdcl(); @@ -3198,9 +3031,7 @@ yyreduce: break; case 76: - -/* Line 1806 of yacc.c */ -#line 656 "go.y" +#line 657 "go.y" { // test (yyval.node) = nod(OIF, N, N); @@ -3209,9 +3040,7 @@ yyreduce: break; case 77: - -/* Line 1806 of yacc.c */ -#line 662 "go.y" +#line 663 "go.y" { // init ; test (yyval.node) = nod(OIF, N, N); @@ -3222,18 +3051,14 @@ yyreduce: break; case 78: - -/* Line 1806 of yacc.c */ -#line 673 "go.y" +#line 674 "go.y" { markdcl(); } break; case 79: - -/* Line 1806 of yacc.c */ -#line 677 "go.y" +#line 678 "go.y" { if((yyvsp[(3) - (3)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3241,18 +3066,14 @@ yyreduce: break; case 80: - -/* Line 1806 of yacc.c */ -#line 682 "go.y" +#line 683 "go.y" { (yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list); } break; case 81: - -/* Line 1806 of yacc.c */ -#line 686 "go.y" +#line 687 "go.y" { Node *n; NodeList *nn; @@ -3270,18 +3091,14 @@ yyreduce: break; case 82: - -/* Line 1806 of yacc.c */ -#line 703 "go.y" +#line 704 "go.y" { markdcl(); } break; case 83: - -/* Line 1806 of yacc.c */ -#line 707 "go.y" +#line 708 "go.y" { if((yyvsp[(4) - (5)].node)->ntest == N) yyerror("missing condition in if statement"); @@ -3291,36 +3108,28 @@ yyreduce: break; case 84: - -/* Line 1806 of yacc.c */ -#line 715 "go.y" +#line 716 "go.y" { (yyval.list) = nil; } break; case 85: - -/* Line 1806 of yacc.c */ -#line 719 "go.y" +#line 720 "go.y" { (yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list)); } break; case 86: - -/* Line 1806 of yacc.c */ -#line 724 "go.y" +#line 725 "go.y" { (yyval.list) = nil; } break; case 87: - -/* Line 1806 of yacc.c */ -#line 728 "go.y" +#line 729 "go.y" { NodeList *node; @@ -3332,18 +3141,14 @@ yyreduce: break; case 88: - -/* Line 1806 of yacc.c */ -#line 739 "go.y" +#line 740 "go.y" { markdcl(); } break; case 89: - -/* Line 1806 of yacc.c */ -#line 743 "go.y" +#line 744 "go.y" { Node *n; n = (yyvsp[(3) - (3)].node)->ntest; @@ -3354,9 +3159,7 @@ yyreduce: break; case 90: - -/* Line 1806 of yacc.c */ -#line 751 "go.y" +#line 752 "go.y" { (yyval.node) = (yyvsp[(3) - (7)].node); (yyval.node)->op = OSWITCH; @@ -3367,18 +3170,14 @@ yyreduce: break; case 91: - -/* Line 1806 of yacc.c */ -#line 761 "go.y" +#line 762 "go.y" { typesw = nod(OXXX, typesw, N); } break; case 92: - -/* Line 1806 of yacc.c */ -#line 765 "go.y" +#line 766 "go.y" { (yyval.node) = nod(OSELECT, N, N); (yyval.node)->lineno = typesw->lineno; @@ -3388,198 +3187,154 @@ yyreduce: break; case 94: - -/* Line 1806 of yacc.c */ -#line 778 "go.y" +#line 779 "go.y" { (yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 95: - -/* Line 1806 of yacc.c */ -#line 782 "go.y" +#line 783 "go.y" { (yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 96: - -/* Line 1806 of yacc.c */ -#line 786 "go.y" +#line 787 "go.y" { (yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 97: - -/* Line 1806 of yacc.c */ -#line 790 "go.y" +#line 791 "go.y" { (yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 98: - -/* Line 1806 of yacc.c */ -#line 794 "go.y" +#line 795 "go.y" { (yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 99: - -/* Line 1806 of yacc.c */ -#line 798 "go.y" +#line 799 "go.y" { (yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 100: - -/* Line 1806 of yacc.c */ -#line 802 "go.y" +#line 803 "go.y" { (yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 101: - -/* Line 1806 of yacc.c */ -#line 806 "go.y" +#line 807 "go.y" { (yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 102: - -/* Line 1806 of yacc.c */ -#line 810 "go.y" +#line 811 "go.y" { (yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 103: - -/* Line 1806 of yacc.c */ -#line 814 "go.y" +#line 815 "go.y" { (yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 104: - -/* Line 1806 of yacc.c */ -#line 818 "go.y" +#line 819 "go.y" { (yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 105: - -/* Line 1806 of yacc.c */ -#line 822 "go.y" +#line 823 "go.y" { (yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 106: - -/* Line 1806 of yacc.c */ -#line 826 "go.y" +#line 827 "go.y" { (yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 107: - -/* Line 1806 of yacc.c */ -#line 830 "go.y" +#line 831 "go.y" { (yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 108: - -/* Line 1806 of yacc.c */ -#line 834 "go.y" +#line 835 "go.y" { (yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 109: - -/* Line 1806 of yacc.c */ -#line 838 "go.y" +#line 839 "go.y" { (yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 110: - -/* Line 1806 of yacc.c */ -#line 842 "go.y" +#line 843 "go.y" { (yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 111: - -/* Line 1806 of yacc.c */ -#line 846 "go.y" +#line 847 "go.y" { (yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 112: - -/* Line 1806 of yacc.c */ -#line 850 "go.y" +#line 851 "go.y" { (yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 113: - -/* Line 1806 of yacc.c */ -#line 855 "go.y" +#line 856 "go.y" { (yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 115: - -/* Line 1806 of yacc.c */ -#line 862 "go.y" +#line 863 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 116: - -/* Line 1806 of yacc.c */ -#line 866 "go.y" +#line 867 "go.y" { if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) { // Special case for &T{...}: turn into (*T){...}. @@ -3593,36 +3348,28 @@ yyreduce: break; case 117: - -/* Line 1806 of yacc.c */ -#line 877 "go.y" +#line 878 "go.y" { (yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N); } break; case 118: - -/* Line 1806 of yacc.c */ -#line 881 "go.y" +#line 882 "go.y" { (yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N); } break; case 119: - -/* Line 1806 of yacc.c */ -#line 885 "go.y" +#line 886 "go.y" { (yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N); } break; case 120: - -/* Line 1806 of yacc.c */ -#line 889 "go.y" +#line 890 "go.y" { yyerror("the bitwise complement operator is ^"); (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); @@ -3630,36 +3377,28 @@ yyreduce: break; case 121: - -/* Line 1806 of yacc.c */ -#line 894 "go.y" +#line 895 "go.y" { (yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N); } break; case 122: - -/* Line 1806 of yacc.c */ -#line 898 "go.y" +#line 899 "go.y" { (yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N); } break; case 123: - -/* Line 1806 of yacc.c */ -#line 908 "go.y" +#line 909 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N); } break; case 124: - -/* Line 1806 of yacc.c */ -#line 912 "go.y" +#line 913 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -3667,9 +3406,7 @@ yyreduce: break; case 125: - -/* Line 1806 of yacc.c */ -#line 917 "go.y" +#line 918 "go.y" { (yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N); (yyval.node)->list = (yyvsp[(3) - (6)].list); @@ -3678,18 +3415,14 @@ yyreduce: break; case 126: - -/* Line 1806 of yacc.c */ -#line 925 "go.y" +#line 926 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 128: - -/* Line 1806 of yacc.c */ -#line 930 "go.y" +#line 931 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -3703,45 +3436,35 @@ yyreduce: break; case 129: - -/* Line 1806 of yacc.c */ -#line 941 "go.y" +#line 942 "go.y" { (yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node)); } break; case 130: - -/* Line 1806 of yacc.c */ -#line 945 "go.y" +#line 946 "go.y" { (yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node)); } break; case 131: - -/* Line 1806 of yacc.c */ -#line 949 "go.y" +#line 950 "go.y" { (yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node)); } break; case 132: - -/* Line 1806 of yacc.c */ -#line 953 "go.y" +#line 954 "go.y" { (yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node))); } break; case 133: - -/* Line 1806 of yacc.c */ -#line 957 "go.y" +#line 958 "go.y" { if((yyvsp[(5) - (8)].node) == N) yyerror("middle index required in 3-index slice"); @@ -3752,9 +3475,7 @@ yyreduce: break; case 135: - -/* Line 1806 of yacc.c */ -#line 966 "go.y" +#line 967 "go.y" { // conversion (yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N); @@ -3763,9 +3484,7 @@ yyreduce: break; case 136: - -/* Line 1806 of yacc.c */ -#line 972 "go.y" +#line 973 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3775,9 +3494,7 @@ yyreduce: break; case 137: - -/* Line 1806 of yacc.c */ -#line 979 "go.y" +#line 980 "go.y" { (yyval.node) = (yyvsp[(3) - (5)].node); (yyval.node)->right = (yyvsp[(1) - (5)].node); @@ -3786,9 +3503,7 @@ yyreduce: break; case 138: - -/* Line 1806 of yacc.c */ -#line 985 "go.y" +#line 986 "go.y" { yyerror("cannot parenthesize type in composite literal"); (yyval.node) = (yyvsp[(5) - (7)].node); @@ -3798,9 +3513,7 @@ yyreduce: break; case 140: - -/* Line 1806 of yacc.c */ -#line 994 "go.y" +#line 995 "go.y" { // composite expression. // make node early so we get the right line number. @@ -3809,18 +3522,14 @@ yyreduce: break; case 141: - -/* Line 1806 of yacc.c */ -#line 1002 "go.y" +#line 1003 "go.y" { (yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node)); } break; case 142: - -/* Line 1806 of yacc.c */ -#line 1008 "go.y" +#line 1009 "go.y" { // These nodes do not carry line numbers. // Since a composite literal commonly spans several lines, @@ -3840,9 +3549,7 @@ yyreduce: break; case 143: - -/* Line 1806 of yacc.c */ -#line 1025 "go.y" +#line 1026 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3850,9 +3557,7 @@ yyreduce: break; case 145: - -/* Line 1806 of yacc.c */ -#line 1033 "go.y" +#line 1034 "go.y" { (yyval.node) = (yyvsp[(2) - (4)].node); (yyval.node)->list = (yyvsp[(3) - (4)].list); @@ -3860,9 +3565,7 @@ yyreduce: break; case 147: - -/* Line 1806 of yacc.c */ -#line 1041 "go.y" +#line 1042 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); @@ -3882,27 +3585,21 @@ yyreduce: break; case 151: - -/* Line 1806 of yacc.c */ -#line 1067 "go.y" +#line 1068 "go.y" { (yyval.i) = LBODY; } break; case 152: - -/* Line 1806 of yacc.c */ -#line 1071 "go.y" +#line 1072 "go.y" { (yyval.i) = '{'; } break; case 153: - -/* Line 1806 of yacc.c */ -#line 1082 "go.y" +#line 1083 "go.y" { if((yyvsp[(1) - (1)].sym) == S) (yyval.node) = N; @@ -3912,27 +3609,21 @@ yyreduce: break; case 154: - -/* Line 1806 of yacc.c */ -#line 1091 "go.y" +#line 1092 "go.y" { (yyval.node) = dclname((yyvsp[(1) - (1)].sym)); } break; case 155: - -/* Line 1806 of yacc.c */ -#line 1096 "go.y" +#line 1097 "go.y" { (yyval.node) = N; } break; case 157: - -/* Line 1806 of yacc.c */ -#line 1103 "go.y" +#line 1104 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); // during imports, unqualified non-exported identifiers are from builtinpkg @@ -3942,18 +3633,14 @@ yyreduce: break; case 159: - -/* Line 1806 of yacc.c */ -#line 1111 "go.y" +#line 1112 "go.y" { (yyval.sym) = S; } break; case 160: - -/* Line 1806 of yacc.c */ -#line 1117 "go.y" +#line 1118 "go.y" { Pkg *p; @@ -3969,9 +3656,7 @@ yyreduce: break; case 161: - -/* Line 1806 of yacc.c */ -#line 1130 "go.y" +#line 1131 "go.y" { Pkg *p; @@ -3987,9 +3672,7 @@ yyreduce: break; case 162: - -/* Line 1806 of yacc.c */ -#line 1145 "go.y" +#line 1146 "go.y" { (yyval.node) = oldname((yyvsp[(1) - (1)].sym)); if((yyval.node)->pack != N) @@ -3998,9 +3681,7 @@ yyreduce: break; case 164: - -/* Line 1806 of yacc.c */ -#line 1165 "go.y" +#line 1166 "go.y" { yyerror("final argument in variadic function missing type"); (yyval.node) = nod(ODDD, typenod(typ(TINTER)), N); @@ -4008,45 +3689,35 @@ yyreduce: break; case 165: - -/* Line 1806 of yacc.c */ -#line 1170 "go.y" +#line 1171 "go.y" { (yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N); } break; case 171: - -/* Line 1806 of yacc.c */ -#line 1181 "go.y" +#line 1182 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; case 175: - -/* Line 1806 of yacc.c */ -#line 1190 "go.y" +#line 1191 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 180: - -/* Line 1806 of yacc.c */ -#line 1200 "go.y" +#line 1201 "go.y" { (yyval.node) = nod(OTPAREN, (yyvsp[(2) - (3)].node), N); } break; case 190: - -/* Line 1806 of yacc.c */ -#line 1221 "go.y" +#line 1222 "go.y" { if((yyvsp[(1) - (3)].node)->op == OPACK) { Sym *s; @@ -4060,18 +3731,14 @@ yyreduce: break; case 191: - -/* Line 1806 of yacc.c */ -#line 1234 "go.y" +#line 1235 "go.y" { (yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node)); } break; case 192: - -/* Line 1806 of yacc.c */ -#line 1238 "go.y" +#line 1239 "go.y" { // array literal of nelem (yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node)); @@ -4079,9 +3746,7 @@ yyreduce: break; case 193: - -/* Line 1806 of yacc.c */ -#line 1243 "go.y" +#line 1244 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N); (yyval.node)->etype = Cboth; @@ -4089,9 +3754,7 @@ yyreduce: break; case 194: - -/* Line 1806 of yacc.c */ -#line 1248 "go.y" +#line 1249 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Csend; @@ -4099,27 +3762,21 @@ yyreduce: break; case 195: - -/* Line 1806 of yacc.c */ -#line 1253 "go.y" +#line 1254 "go.y" { (yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node)); } break; case 198: - -/* Line 1806 of yacc.c */ -#line 1261 "go.y" +#line 1262 "go.y" { (yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N); } break; case 199: - -/* Line 1806 of yacc.c */ -#line 1267 "go.y" +#line 1268 "go.y" { (yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N); (yyval.node)->etype = Crecv; @@ -4127,9 +3784,7 @@ yyreduce: break; case 200: - -/* Line 1806 of yacc.c */ -#line 1274 "go.y" +#line 1275 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -4138,9 +3793,7 @@ yyreduce: break; case 201: - -/* Line 1806 of yacc.c */ -#line 1280 "go.y" +#line 1281 "go.y" { (yyval.node) = nod(OTSTRUCT, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -4148,9 +3801,7 @@ yyreduce: break; case 202: - -/* Line 1806 of yacc.c */ -#line 1287 "go.y" +#line 1288 "go.y" { (yyval.node) = nod(OTINTER, N, N); (yyval.node)->list = (yyvsp[(3) - (5)].list); @@ -4159,9 +3810,7 @@ yyreduce: break; case 203: - -/* Line 1806 of yacc.c */ -#line 1293 "go.y" +#line 1294 "go.y" { (yyval.node) = nod(OTINTER, N, N); fixlbrace((yyvsp[(2) - (3)].i)); @@ -4169,9 +3818,7 @@ yyreduce: break; case 204: - -/* Line 1806 of yacc.c */ -#line 1304 "go.y" +#line 1305 "go.y" { (yyval.node) = (yyvsp[(2) - (3)].node); if((yyval.node) == N) @@ -4186,9 +3833,7 @@ yyreduce: break; case 205: - -/* Line 1806 of yacc.c */ -#line 1318 "go.y" +#line 1319 "go.y" { Node *t; @@ -4220,9 +3865,7 @@ yyreduce: break; case 206: - -/* Line 1806 of yacc.c */ -#line 1347 "go.y" +#line 1348 "go.y" { Node *rcvr, *t; @@ -4263,9 +3906,7 @@ yyreduce: break; case 207: - -/* Line 1806 of yacc.c */ -#line 1387 "go.y" +#line 1388 "go.y" { Sym *s; Type *t; @@ -4293,9 +3934,7 @@ yyreduce: break; case 208: - -/* Line 1806 of yacc.c */ -#line 1412 "go.y" +#line 1413 "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)); @@ -4314,9 +3953,7 @@ yyreduce: break; case 209: - -/* Line 1806 of yacc.c */ -#line 1430 "go.y" +#line 1431 "go.y" { (yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1); (yyval.node) = nod(OTFUNC, N, N); @@ -4326,18 +3963,14 @@ yyreduce: break; case 210: - -/* Line 1806 of yacc.c */ -#line 1438 "go.y" +#line 1439 "go.y" { (yyval.list) = nil; } break; case 211: - -/* Line 1806 of yacc.c */ -#line 1442 "go.y" +#line 1443 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); if((yyval.list) == nil) @@ -4346,27 +3979,21 @@ yyreduce: break; case 212: - -/* Line 1806 of yacc.c */ -#line 1450 "go.y" +#line 1451 "go.y" { (yyval.list) = nil; } break; case 213: - -/* Line 1806 of yacc.c */ -#line 1454 "go.y" +#line 1455 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node))); } break; case 214: - -/* Line 1806 of yacc.c */ -#line 1458 "go.y" +#line 1459 "go.y" { (yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0); (yyval.list) = (yyvsp[(2) - (3)].list); @@ -4374,18 +4001,14 @@ yyreduce: break; case 215: - -/* Line 1806 of yacc.c */ -#line 1465 "go.y" +#line 1466 "go.y" { closurehdr((yyvsp[(1) - (1)].node)); } break; case 216: - -/* Line 1806 of yacc.c */ -#line 1471 "go.y" +#line 1472 "go.y" { (yyval.node) = closurebody((yyvsp[(3) - (4)].list)); fixlbrace((yyvsp[(2) - (4)].i)); @@ -4393,27 +4016,21 @@ yyreduce: break; case 217: - -/* Line 1806 of yacc.c */ -#line 1476 "go.y" +#line 1477 "go.y" { (yyval.node) = closurebody(nil); } break; case 218: - -/* Line 1806 of yacc.c */ -#line 1487 "go.y" +#line 1488 "go.y" { (yyval.list) = nil; } break; case 219: - -/* Line 1806 of yacc.c */ -#line 1491 "go.y" +#line 1492 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list)); if(nsyntaxerrors == 0) @@ -4424,72 +4041,56 @@ yyreduce: break; case 221: - -/* Line 1806 of yacc.c */ -#line 1502 "go.y" +#line 1503 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 223: - -/* Line 1806 of yacc.c */ -#line 1509 "go.y" +#line 1510 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 224: - -/* Line 1806 of yacc.c */ -#line 1515 "go.y" +#line 1516 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 225: - -/* Line 1806 of yacc.c */ -#line 1519 "go.y" +#line 1520 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 227: - -/* Line 1806 of yacc.c */ -#line 1526 "go.y" +#line 1527 "go.y" { (yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list)); } break; case 228: - -/* Line 1806 of yacc.c */ -#line 1532 "go.y" +#line 1533 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 229: - -/* Line 1806 of yacc.c */ -#line 1536 "go.y" +#line 1537 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 230: - -/* Line 1806 of yacc.c */ -#line 1542 "go.y" +#line 1543 "go.y" { NodeList *l; @@ -4515,9 +4116,7 @@ yyreduce: break; case 231: - -/* Line 1806 of yacc.c */ -#line 1565 "go.y" +#line 1566 "go.y" { (yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val); (yyval.list) = list1((yyvsp[(1) - (2)].node)); @@ -4525,9 +4124,7 @@ yyreduce: break; case 232: - -/* Line 1806 of yacc.c */ -#line 1570 "go.y" +#line 1571 "go.y" { (yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val); (yyval.list) = list1((yyvsp[(2) - (4)].node)); @@ -4536,9 +4133,7 @@ yyreduce: break; case 233: - -/* Line 1806 of yacc.c */ -#line 1576 "go.y" +#line 1577 "go.y" { (yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N); (yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val); @@ -4547,9 +4142,7 @@ yyreduce: break; case 234: - -/* Line 1806 of yacc.c */ -#line 1582 "go.y" +#line 1583 "go.y" { (yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N); (yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val); @@ -4559,9 +4152,7 @@ yyreduce: break; case 235: - -/* Line 1806 of yacc.c */ -#line 1589 "go.y" +#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); @@ -4571,9 +4162,7 @@ yyreduce: break; case 236: - -/* Line 1806 of yacc.c */ -#line 1598 "go.y" +#line 1599 "go.y" { Node *n; @@ -4585,9 +4174,7 @@ yyreduce: break; case 237: - -/* Line 1806 of yacc.c */ -#line 1607 "go.y" +#line 1608 "go.y" { Pkg *pkg; @@ -4603,18 +4190,14 @@ yyreduce: break; case 238: - -/* Line 1806 of yacc.c */ -#line 1622 "go.y" +#line 1623 "go.y" { (yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg); } break; case 239: - -/* Line 1806 of yacc.c */ -#line 1628 "go.y" +#line 1629 "go.y" { (yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node)); ifacedcl((yyval.node)); @@ -4622,18 +4205,14 @@ yyreduce: break; case 240: - -/* Line 1806 of yacc.c */ -#line 1633 "go.y" +#line 1634 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym))); } break; case 241: - -/* Line 1806 of yacc.c */ -#line 1637 "go.y" +#line 1638 "go.y" { (yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym))); yyerror("cannot parenthesize embedded type"); @@ -4641,9 +4220,7 @@ yyreduce: break; case 242: - -/* Line 1806 of yacc.c */ -#line 1644 "go.y" +#line 1645 "go.y" { // without func keyword (yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1); @@ -4654,9 +4231,7 @@ yyreduce: break; case 244: - -/* Line 1806 of yacc.c */ -#line 1658 "go.y" +#line 1659 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4665,9 +4240,7 @@ yyreduce: break; case 245: - -/* Line 1806 of yacc.c */ -#line 1664 "go.y" +#line 1665 "go.y" { (yyval.node) = nod(ONONAME, N, N); (yyval.node)->sym = (yyvsp[(1) - (2)].sym); @@ -4676,72 +4249,56 @@ yyreduce: break; case 247: - -/* Line 1806 of yacc.c */ -#line 1673 "go.y" +#line 1674 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 248: - -/* Line 1806 of yacc.c */ -#line 1677 "go.y" +#line 1678 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 249: - -/* Line 1806 of yacc.c */ -#line 1682 "go.y" +#line 1683 "go.y" { (yyval.list) = nil; } break; case 250: - -/* Line 1806 of yacc.c */ -#line 1686 "go.y" +#line 1687 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 251: - -/* Line 1806 of yacc.c */ -#line 1694 "go.y" +#line 1695 "go.y" { (yyval.node) = N; } break; case 253: - -/* Line 1806 of yacc.c */ -#line 1699 "go.y" +#line 1700 "go.y" { (yyval.node) = liststmt((yyvsp[(1) - (1)].list)); } break; case 255: - -/* Line 1806 of yacc.c */ -#line 1704 "go.y" +#line 1705 "go.y" { (yyval.node) = N; } break; case 261: - -/* Line 1806 of yacc.c */ -#line 1715 "go.y" +#line 1716 "go.y" { (yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N); (yyvsp[(1) - (2)].node)->sym = dclstack; // context, for goto restrictions @@ -4749,9 +4306,7 @@ yyreduce: break; case 262: - -/* Line 1806 of yacc.c */ -#line 1720 "go.y" +#line 1721 "go.y" { NodeList *l; @@ -4764,55 +4319,44 @@ yyreduce: break; case 263: - -/* Line 1806 of yacc.c */ -#line 1730 "go.y" +#line 1731 "go.y" { // will be converted to OFALL (yyval.node) = nod(OXFALL, N, N); + (yyval.node)->xoffset = block; } break; case 264: - -/* Line 1806 of yacc.c */ -#line 1735 "go.y" +#line 1737 "go.y" { (yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N); } break; case 265: - -/* Line 1806 of yacc.c */ -#line 1739 "go.y" +#line 1741 "go.y" { (yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N); } break; case 266: - -/* Line 1806 of yacc.c */ -#line 1743 "go.y" +#line 1745 "go.y" { (yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N); } break; case 267: - -/* Line 1806 of yacc.c */ -#line 1747 "go.y" +#line 1749 "go.y" { (yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N); } break; case 268: - -/* Line 1806 of yacc.c */ -#line 1751 "go.y" +#line 1753 "go.y" { (yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N); (yyval.node)->sym = dclstack; // context, for goto restrictions @@ -4820,9 +4364,7 @@ yyreduce: break; case 269: - -/* Line 1806 of yacc.c */ -#line 1756 "go.y" +#line 1758 "go.y" { (yyval.node) = nod(ORETURN, N, N); (yyval.node)->list = (yyvsp[(2) - (2)].list); @@ -4842,9 +4384,7 @@ yyreduce: break; case 270: - -/* Line 1806 of yacc.c */ -#line 1775 "go.y" +#line 1777 "go.y" { (yyval.list) = nil; if((yyvsp[(1) - (1)].node) != N) @@ -4853,9 +4393,7 @@ yyreduce: break; case 271: - -/* Line 1806 of yacc.c */ -#line 1781 "go.y" +#line 1783 "go.y" { (yyval.list) = (yyvsp[(1) - (3)].list); if((yyvsp[(3) - (3)].node) != N) @@ -4864,243 +4402,189 @@ yyreduce: break; case 272: - -/* Line 1806 of yacc.c */ -#line 1789 "go.y" +#line 1791 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 273: - -/* Line 1806 of yacc.c */ -#line 1793 "go.y" +#line 1795 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 274: - -/* Line 1806 of yacc.c */ -#line 1799 "go.y" +#line 1801 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 275: - -/* Line 1806 of yacc.c */ -#line 1803 "go.y" +#line 1805 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 276: - -/* Line 1806 of yacc.c */ -#line 1809 "go.y" +#line 1811 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 277: - -/* Line 1806 of yacc.c */ -#line 1813 "go.y" +#line 1815 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 278: - -/* Line 1806 of yacc.c */ -#line 1819 "go.y" +#line 1821 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 279: - -/* Line 1806 of yacc.c */ -#line 1823 "go.y" +#line 1825 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 280: - -/* Line 1806 of yacc.c */ -#line 1832 "go.y" +#line 1834 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 281: - -/* Line 1806 of yacc.c */ -#line 1836 "go.y" +#line 1838 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 282: - -/* Line 1806 of yacc.c */ -#line 1840 "go.y" +#line 1842 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 283: - -/* Line 1806 of yacc.c */ -#line 1844 "go.y" +#line 1846 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 284: - -/* Line 1806 of yacc.c */ -#line 1849 "go.y" +#line 1851 "go.y" { (yyval.list) = nil; } break; case 285: - -/* Line 1806 of yacc.c */ -#line 1853 "go.y" +#line 1855 "go.y" { (yyval.list) = (yyvsp[(1) - (2)].list); } break; case 290: - -/* Line 1806 of yacc.c */ -#line 1867 "go.y" +#line 1869 "go.y" { (yyval.node) = N; } break; case 292: - -/* Line 1806 of yacc.c */ -#line 1873 "go.y" +#line 1875 "go.y" { (yyval.list) = nil; } break; case 294: - -/* Line 1806 of yacc.c */ -#line 1879 "go.y" +#line 1881 "go.y" { (yyval.node) = N; } break; case 296: - -/* Line 1806 of yacc.c */ -#line 1885 "go.y" +#line 1887 "go.y" { (yyval.list) = nil; } break; case 298: - -/* Line 1806 of yacc.c */ -#line 1891 "go.y" +#line 1893 "go.y" { (yyval.list) = nil; } break; case 300: - -/* Line 1806 of yacc.c */ -#line 1897 "go.y" +#line 1899 "go.y" { (yyval.list) = nil; } break; case 302: - -/* Line 1806 of yacc.c */ -#line 1903 "go.y" +#line 1905 "go.y" { (yyval.val).ctype = CTxxx; } break; case 304: - -/* Line 1806 of yacc.c */ -#line 1913 "go.y" +#line 1915 "go.y" { importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval); } break; case 305: - -/* Line 1806 of yacc.c */ -#line 1917 "go.y" +#line 1919 "go.y" { importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type)); } break; case 306: - -/* Line 1806 of yacc.c */ -#line 1921 "go.y" +#line 1923 "go.y" { importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node)); } break; case 307: - -/* Line 1806 of yacc.c */ -#line 1925 "go.y" +#line 1927 "go.y" { importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node)); } break; case 308: - -/* Line 1806 of yacc.c */ -#line 1929 "go.y" +#line 1931 "go.y" { importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type)); } break; case 309: - -/* Line 1806 of yacc.c */ -#line 1933 "go.y" +#line 1935 "go.y" { if((yyvsp[(2) - (4)].node) == N) { dclcontext = PEXTERN; // since we skip the funcbody below @@ -5121,9 +4605,7 @@ yyreduce: break; case 310: - -/* Line 1806 of yacc.c */ -#line 1953 "go.y" +#line 1955 "go.y" { (yyval.sym) = (yyvsp[(1) - (1)].sym); structpkg = (yyval.sym)->pkg; @@ -5131,9 +4613,7 @@ yyreduce: break; case 311: - -/* Line 1806 of yacc.c */ -#line 1960 "go.y" +#line 1962 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); importsym((yyvsp[(1) - (1)].sym), OTYPE); @@ -5141,18 +4621,14 @@ yyreduce: break; case 317: - -/* Line 1806 of yacc.c */ -#line 1980 "go.y" +#line 1982 "go.y" { (yyval.type) = pkgtype((yyvsp[(1) - (1)].sym)); } break; case 318: - -/* Line 1806 of yacc.c */ -#line 1984 "go.y" +#line 1986 "go.y" { // predefined name like uint8 (yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg); @@ -5165,63 +4641,49 @@ yyreduce: break; case 319: - -/* Line 1806 of yacc.c */ -#line 1994 "go.y" +#line 1996 "go.y" { (yyval.type) = aindex(N, (yyvsp[(3) - (3)].type)); } break; case 320: - -/* Line 1806 of yacc.c */ -#line 1998 "go.y" +#line 2000 "go.y" { (yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type)); } break; case 321: - -/* Line 1806 of yacc.c */ -#line 2002 "go.y" +#line 2004 "go.y" { (yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type)); } break; case 322: - -/* Line 1806 of yacc.c */ -#line 2006 "go.y" +#line 2008 "go.y" { (yyval.type) = tostruct((yyvsp[(3) - (4)].list)); } break; case 323: - -/* Line 1806 of yacc.c */ -#line 2010 "go.y" +#line 2012 "go.y" { (yyval.type) = tointerface((yyvsp[(3) - (4)].list)); } break; case 324: - -/* Line 1806 of yacc.c */ -#line 2014 "go.y" +#line 2016 "go.y" { (yyval.type) = ptrto((yyvsp[(2) - (2)].type)); } break; case 325: - -/* Line 1806 of yacc.c */ -#line 2018 "go.y" +#line 2020 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(2) - (2)].type); @@ -5230,9 +4692,7 @@ yyreduce: break; case 326: - -/* Line 1806 of yacc.c */ -#line 2024 "go.y" +#line 2026 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (4)].type); @@ -5241,9 +4701,7 @@ yyreduce: break; case 327: - -/* Line 1806 of yacc.c */ -#line 2030 "go.y" +#line 2032 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5252,9 +4710,7 @@ yyreduce: break; case 328: - -/* Line 1806 of yacc.c */ -#line 2038 "go.y" +#line 2040 "go.y" { (yyval.type) = typ(TCHAN); (yyval.type)->type = (yyvsp[(3) - (3)].type); @@ -5263,18 +4719,14 @@ yyreduce: break; case 329: - -/* Line 1806 of yacc.c */ -#line 2046 "go.y" +#line 2048 "go.y" { (yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)); } break; case 330: - -/* Line 1806 of yacc.c */ -#line 2052 "go.y" +#line 2054 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type))); if((yyvsp[(1) - (3)].sym)) @@ -5284,9 +4736,7 @@ yyreduce: break; case 331: - -/* Line 1806 of yacc.c */ -#line 2059 "go.y" +#line 2061 "go.y" { Type *t; @@ -5303,9 +4753,7 @@ yyreduce: break; case 332: - -/* Line 1806 of yacc.c */ -#line 2075 "go.y" +#line 2077 "go.y" { Sym *s; Pkg *p; @@ -5328,63 +4776,49 @@ yyreduce: break; case 333: - -/* Line 1806 of yacc.c */ -#line 2097 "go.y" +#line 2099 "go.y" { (yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list)))); } break; case 334: - -/* Line 1806 of yacc.c */ -#line 2101 "go.y" +#line 2103 "go.y" { (yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))); } break; case 335: - -/* Line 1806 of yacc.c */ -#line 2106 "go.y" +#line 2108 "go.y" { (yyval.list) = nil; } break; case 337: - -/* Line 1806 of yacc.c */ -#line 2113 "go.y" +#line 2115 "go.y" { (yyval.list) = (yyvsp[(2) - (3)].list); } break; case 338: - -/* Line 1806 of yacc.c */ -#line 2117 "go.y" +#line 2119 "go.y" { (yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)))); } break; case 339: - -/* Line 1806 of yacc.c */ -#line 2127 "go.y" +#line 2129 "go.y" { (yyval.node) = nodlit((yyvsp[(1) - (1)].val)); } break; case 340: - -/* Line 1806 of yacc.c */ -#line 2131 "go.y" +#line 2133 "go.y" { (yyval.node) = nodlit((yyvsp[(2) - (2)].val)); switch((yyval.node)->val.ctype){ @@ -5395,6 +4829,10 @@ yyreduce: 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"); } @@ -5402,9 +4840,7 @@ yyreduce: break; case 341: - -/* Line 1806 of yacc.c */ -#line 2146 "go.y" +#line 2152 "go.y" { (yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg)); if((yyval.node)->op != OLITERAL) @@ -5413,9 +4849,7 @@ yyreduce: break; case 343: - -/* Line 1806 of yacc.c */ -#line 2155 "go.y" +#line 2161 "go.y" { if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) { (yyval.node) = (yyvsp[(2) - (5)].node); @@ -5429,76 +4863,52 @@ yyreduce: break; case 346: - -/* Line 1806 of yacc.c */ -#line 2171 "go.y" +#line 2177 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 347: - -/* Line 1806 of yacc.c */ -#line 2175 "go.y" +#line 2181 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 348: - -/* Line 1806 of yacc.c */ -#line 2181 "go.y" +#line 2187 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 349: - -/* Line 1806 of yacc.c */ -#line 2185 "go.y" +#line 2191 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; case 350: - -/* Line 1806 of yacc.c */ -#line 2191 "go.y" +#line 2197 "go.y" { (yyval.list) = list1((yyvsp[(1) - (1)].node)); } break; case 351: - -/* Line 1806 of yacc.c */ -#line 2195 "go.y" +#line 2201 "go.y" { (yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node)); } break; - -/* Line 1806 of yacc.c */ -#line 5490 "y.tab.c" +/* Line 1267 of yacc.c. */ +#line 4911 "y.tab.c" default: break; } - /* User semantic actions sometimes alter yychar, and that requires - that yytoken be updated with the new translation. We take the - approach of translating immediately before every use of yytoken. - One alternative is translating here after every semantic action, - but that translation would be missed if the semantic action invokes - YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or - if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an - incorrect destructor might then be invoked immediately. In the - case of YYERROR or YYBACKUP, subsequent parser actions might lead - to an incorrect destructor call or verbose syntax error message - before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); @@ -5507,6 +4917,7 @@ yyreduce: *++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. */ @@ -5526,10 +4937,6 @@ yyreduce: | yyerrlab -- here on detecting error | `------------------------------------*/ yyerrlab: - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); - /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { @@ -5537,36 +4944,37 @@ yyerrlab: #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else -# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ - yyssp, yytoken) { - char *yymsgp = YY_("syntax error"); - int yysyntax_error_status; - yysyntax_error_status = YYSYNTAX_ERROR; - if (yysyntax_error_status == 0) - yymsgp = yymsg; - else if (yysyntax_error_status == 1) - { - if (yymsg != yymsgbuf) - YYSTACK_FREE (yymsg); - yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); - if (!yymsg) - { - yymsg = yymsgbuf; - yymsg_alloc = sizeof yymsgbuf; - yysyntax_error_status = 2; - } - else - { - yysyntax_error_status = YYSYNTAX_ERROR; - yymsgp = yymsg; - } - } - yyerror (yymsgp); - if (yysyntax_error_status == 2) - goto yyexhaustedlab; + 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; + } } -# undef YYSYNTAX_ERROR #endif } @@ -5574,7 +4982,7 @@ yyerrlab: if (yyerrstatus == 3) { - /* If just tried and failed to reuse lookahead token after an + /* If just tried and failed to reuse look-ahead token after an error, discard it. */ if (yychar <= YYEOF) @@ -5591,7 +4999,7 @@ yyerrlab: } } - /* Else will try to reuse lookahead token after shifting the error + /* Else will try to reuse look-ahead token after shifting the error token. */ goto yyerrlab1; @@ -5625,7 +5033,7 @@ yyerrlab1: for (;;) { yyn = yypact[yystate]; - if (!yypact_value_is_default (yyn)) + if (yyn != YYPACT_NINF) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) @@ -5648,6 +5056,9 @@ yyerrlab1: YY_STACK_PRINT (yyss, yyssp); } + if (yyn == YYFINAL) + YYACCEPT; + *++yyvsp = yylval; @@ -5672,7 +5083,7 @@ yyabortlab: yyresult = 1; goto yyreturn; -#if !defined(yyoverflow) || YYERROR_VERBOSE +#ifndef yyoverflow /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ @@ -5683,14 +5094,9 @@ yyexhaustedlab: #endif yyreturn: - if (yychar != YYEMPTY) - { - /* Make sure we have latest lookahead translation. See comments at - user semantic actions for why this is necessary. */ - yytoken = YYTRANSLATE (yychar); - yydestruct ("Cleanup: discarding lookahead", - yytoken, &yylval); - } + 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); @@ -5714,9 +5120,7 @@ yyreturn: } - -/* Line 2067 of yacc.c */ -#line 2199 "go.y" +#line 2205 "go.y" static void diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h index 6eeb831b2..d01fbe198 100644 --- a/src/cmd/gc/y.tab.h +++ b/src/cmd/gc/y.tab.h @@ -1,21 +1,24 @@ -/* A Bison parser, made by GNU Bison 2.5. */ +/* A Bison parser, made by GNU Bison 2.3. */ -/* Bison interface for Yacc-like parsers in C - - Copyright (C) 1984, 1989-1990, 2000-2011 Free Software Foundation, Inc. - - This program is free software: you can redistribute it and/or modify +/* 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 3 of the License, or - (at your option) any later version. - + 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, see <http://www.gnu.org/licenses/>. */ + 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 @@ -26,11 +29,10 @@ 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 @@ -144,28 +146,22 @@ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED typedef union YYSTYPE -{ - -/* Line 2068 of yacc.c */ #line 28 "go.y" - +{ Node* node; NodeList* list; Type* type; Sym* sym; struct Val val; int i; - - - -/* Line 2068 of yacc.c */ -#line 163 "y.tab.h" -} YYSTYPE; -# define YYSTYPE_IS_TRIVIAL 1 +} +/* 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; - |