summaryrefslogtreecommitdiff
path: root/src/cmd/gc/walk.c
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
committerOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
commit5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch)
treec0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/gc/walk.c
parent80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff)
downloadgolang-5ff4c17907d5b19510a62e08fd8d3b11e62b431d.tar.gz
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/gc/walk.c')
-rw-r--r--src/cmd/gc/walk.c2166
1 files changed, 2166 insertions, 0 deletions
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
new file mode 100644
index 000000000..9cd4ee919
--- /dev/null
+++ b/src/cmd/gc/walk.c
@@ -0,0 +1,2166 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go.h"
+
+static Node* walkprint(Node*, NodeList**, int);
+static Node* conv(Node*, Type*);
+static Node* mapfn(char*, Type*);
+static Node* makenewvar(Type*, NodeList**, Node**);
+static Node* ascompatee1(int, Node*, Node*, NodeList**);
+static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
+static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
+static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**);
+static Node* convas(Node*, NodeList**);
+static void heapmoves(void);
+static NodeList* paramstoheap(Type **argin, int out);
+static NodeList* reorder1(NodeList*);
+static NodeList* reorder3(NodeList*);
+static Node* addstr(Node*, NodeList**);
+static Node* appendslice(Node*, NodeList**);
+static Node* append(Node*, NodeList**);
+
+// can this code branch reach the end
+// without an unconditional RETURN
+// this is hard, so it is conservative
+static int
+walkret(NodeList *l)
+{
+ Node *n;
+
+loop:
+ while(l && l->next)
+ l = l->next;
+ if(l == nil)
+ return 1;
+
+ // at this point, we have the last
+ // statement of the function
+ n = l->n;
+ switch(n->op) {
+ case OBLOCK:
+ l = n->list;
+ goto loop;
+
+ case OGOTO:
+ case ORETURN:
+ case OPANIC:
+ return 0;
+ break;
+ }
+
+ // all other statements
+ // will flow to the end
+ return 1;
+}
+
+void
+walk(Node *fn)
+{
+ char s[50];
+ NodeList *l;
+ Node *n;
+ int lno;
+
+ curfn = fn;
+
+ if(debug['W']) {
+ snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
+ dumplist(s, curfn->nbody);
+ }
+ if(curfn->type->outtuple)
+ if(walkret(curfn->nbody))
+ yyerror("function ends without a return statement");
+
+ lno = lineno;
+ for(l=fn->dcl; l; l=l->next) {
+ n = l->n;
+ if(n->op != ONAME || n->class != PAUTO)
+ continue;
+ lineno = n->lineno;
+ typecheck(&n, Erv | Easgn); // only needed for unused variables
+ if(!n->used && n->sym->name[0] != '&' && !nsyntaxerrors)
+ yyerror("%S declared and not used", n->sym);
+ }
+ lineno = lno;
+ if(nerrors != 0)
+ return;
+ walkstmtlist(curfn->nbody);
+ if(debug['W']) {
+ snprint(s, sizeof(s), "after walk %S", curfn->nname->sym);
+ dumplist(s, curfn->nbody);
+ }
+ heapmoves();
+ if(debug['W'] && curfn->enter != nil) {
+ snprint(s, sizeof(s), "enter %S", curfn->nname->sym);
+ dumplist(s, curfn->enter);
+ }
+}
+
+
+void
+walkstmtlist(NodeList *l)
+{
+ for(; l; l=l->next)
+ walkstmt(&l->n);
+}
+
+static int
+samelist(NodeList *a, NodeList *b)
+{
+ for(; a && b; a=a->next, b=b->next)
+ if(a->n != b->n)
+ return 0;
+ return a == b;
+}
+
+static int
+paramoutheap(Node *fn)
+{
+ NodeList *l;
+
+ for(l=fn->dcl; l; l=l->next) {
+ switch(l->n->class) {
+ case PPARAMOUT|PHEAP:
+ return 1;
+ case PAUTO:
+ case PAUTO|PHEAP:
+ // stop early - parameters are over
+ return 0;
+ }
+ }
+ return 0;
+}
+
+void
+walkstmt(Node **np)
+{
+ NodeList *init;
+ NodeList *ll, *rl;
+ int cl;
+ Node *n, *f;
+
+ n = *np;
+ if(n == N)
+ return;
+
+ setlineno(n);
+
+ switch(n->op) {
+ default:
+ if(n->op == ONAME)
+ yyerror("%S is not a top level statement", n->sym);
+ else
+ yyerror("%O is not a top level statement", n->op);
+ dump("nottop", n);
+ break;
+
+ case OASOP:
+ case OAS:
+ case OAS2:
+ case OAS2DOTTYPE:
+ case OAS2RECV:
+ case OAS2FUNC:
+ case OAS2MAPW:
+ case OAS2MAPR:
+ case OCLOSE:
+ case OCOPY:
+ case OCALLMETH:
+ case OCALLINTER:
+ case OCALL:
+ case OCALLFUNC:
+ case OSEND:
+ case ORECV:
+ case OPRINT:
+ case OPRINTN:
+ case OPANIC:
+ case OEMPTY:
+ case ORECOVER:
+ if(n->typecheck == 0) {
+ dump("missing typecheck:", n);
+ fatal("missing typecheck");
+ }
+ init = n->ninit;
+ n->ninit = nil;
+ walkexpr(&n, &init);
+ n->ninit = concat(init, n->ninit);
+ break;
+
+ case OBREAK:
+ case ODCL:
+ case OCONTINUE:
+ case OFALL:
+ case OGOTO:
+ case OLABEL:
+ case ODCLCONST:
+ case ODCLTYPE:
+ break;
+
+ case OBLOCK:
+ walkstmtlist(n->list);
+ break;
+
+ case OXCASE:
+ yyerror("case statement out of place");
+ n->op = OCASE;
+ case OCASE:
+ walkstmt(&n->right);
+ break;
+
+ case ODEFER:
+ hasdefer = 1;
+ switch(n->left->op) {
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->left->list, &n->ninit);
+ n->left = walkprint(n->left, &n->ninit, 1);
+ break;
+ default:
+ walkexpr(&n->left, &n->ninit);
+ break;
+ }
+ break;
+
+ case OFOR:
+ walkstmtlist(n->ninit);
+ if(n->ntest != N) {
+ walkstmtlist(n->ntest->ninit);
+ init = n->ntest->ninit;
+ n->ntest->ninit = nil;
+ walkexpr(&n->ntest, &init);
+ n->ntest->ninit = concat(init, n->ntest->ninit);
+ }
+ walkstmt(&n->nincr);
+ walkstmtlist(n->nbody);
+ break;
+
+ case OIF:
+ walkstmtlist(n->ninit);
+ walkexpr(&n->ntest, &n->ninit);
+ walkstmtlist(n->nbody);
+ walkstmtlist(n->nelse);
+ break;
+
+ case OPROC:
+ switch(n->left->op) {
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->left->list, &n->ninit);
+ n->left = walkprint(n->left, &n->ninit, 1);
+ break;
+ default:
+ walkexpr(&n->left, &n->ninit);
+ break;
+ }
+ break;
+
+ case ORETURN:
+ walkexprlist(n->list, &n->ninit);
+ if(n->list == nil)
+ break;
+ if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) {
+ // assign to the function out parameters,
+ // so that reorder3 can fix up conflicts
+ rl = nil;
+ for(ll=curfn->dcl; ll != nil; ll=ll->next) {
+ cl = ll->n->class & ~PHEAP;
+ if(cl == PAUTO)
+ break;
+ if(cl == PPARAMOUT)
+ rl = list(rl, ll->n);
+ }
+ if(samelist(rl, n->list)) {
+ // special return in disguise
+ n->list = nil;
+ break;
+ }
+ if(count(n->list) == 1 && count(rl) > 1) {
+ // OAS2FUNC in disguise
+ f = n->list->n;
+ if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER)
+ fatal("expected return of call, have %#N", f);
+ n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
+ break;
+ }
+ ll = ascompatee(n->op, rl, n->list, &n->ninit);
+ n->list = reorder3(ll);
+ break;
+ }
+ ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+ n->list = ll;
+ break;
+
+ case OSELECT:
+ walkselect(n);
+ break;
+
+ case OSWITCH:
+ walkswitch(n);
+ break;
+
+ case ORANGE:
+ walkrange(n);
+ break;
+
+ case OXFALL:
+ yyerror("fallthrough statement out of place");
+ n->op = OFALL;
+ break;
+ }
+
+ *np = n;
+}
+
+
+/*
+ * walk the whole tree of the body of an
+ * expression or simple statement.
+ * the types expressions are calculated.
+ * compile-time constants are evaluated.
+ * complex side effects like statements are appended to init
+ */
+
+void
+walkexprlist(NodeList *l, NodeList **init)
+{
+ for(; l; l=l->next)
+ walkexpr(&l->n, init);
+}
+
+void
+walkexprlistsafe(NodeList *l, NodeList **init)
+{
+ for(; l; l=l->next) {
+ l->n = safeexpr(l->n, init);
+ walkexpr(&l->n, init);
+ }
+}
+
+void
+walkexpr(Node **np, NodeList **init)
+{
+ Node *r, *l, *var, *a;
+ NodeList *ll, *lr, *lpost;
+ Type *t;
+ int et;
+ int64 v, v1, v2, len;
+ int32 lno;
+ Node *n, *fn;
+ char buf[100], *p;
+
+ n = *np;
+
+ if(n == N)
+ return;
+
+ if(init == &n->ninit) {
+ // not okay to use n->ninit when walking n,
+ // because we might replace n with some other node
+ // and would lose the init list.
+ fatal("walkexpr init == &n->ninit");
+ }
+
+ // annoying case - not typechecked
+ if(n->op == OKEY) {
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ return;
+ }
+
+ lno = setlineno(n);
+
+ if(debug['w'] > 1)
+ dump("walk-before", n);
+
+ if(n->typecheck != 1) {
+ dump("missed typecheck", n);
+ fatal("missed typecheck");
+ }
+
+ t = T;
+ et = Txxx;
+
+ switch(n->op) {
+ default:
+ dump("walk", n);
+ fatal("walkexpr: switch 1 unknown op %N", n);
+ goto ret;
+
+ case OTYPE:
+ case ONONAME:
+ case OINDREG:
+ case OEMPTY:
+ goto ret;
+
+ case ONOT:
+ case OMINUS:
+ case OPLUS:
+ case OCOM:
+ case OREAL:
+ case OIMAG:
+ case ODOT:
+ case ODOTPTR:
+ case ODOTMETH:
+ case ODOTINTER:
+ case OIND:
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OLEN:
+ case OCAP:
+ walkexpr(&n->left, init);
+
+ // replace len(*[10]int) with 10.
+ // delayed until now to preserve side effects.
+ t = n->left->type;
+ if(isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t)) {
+ safeexpr(n->left, init);
+ nodconst(n, n->type, t->bound);
+ n->typecheck = 1;
+ }
+ goto ret;
+
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OOR:
+ case OXOR:
+ case OSUB:
+ case OMUL:
+ case OEQ:
+ case ONE:
+ case OLT:
+ case OLE:
+ case OGE:
+ case OGT:
+ case OADD:
+ case OCOMPLEX:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ goto ret;
+
+ case OANDAND:
+ case OOROR:
+ walkexpr(&n->left, init);
+ // cannot put side effects from n->right on init,
+ // because they cannot run before n->left is checked.
+ // save elsewhere and store on the eventual n->right.
+ ll = nil;
+ walkexpr(&n->right, &ll);
+ n->right->ninit = concat(n->right->ninit, ll);
+ goto ret;
+
+ case OPRINT:
+ case OPRINTN:
+ walkexprlist(n->list, init);
+ n = walkprint(n, init, 0);
+ goto ret;
+
+ case OPANIC:
+ n = mkcall("panic", T, init, n->left);
+ goto ret;
+
+ case ORECOVER:
+ n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
+ goto ret;
+
+ case OLITERAL:
+ n->addable = 1;
+ goto ret;
+
+ case ONAME:
+ if(!(n->class & PHEAP) && n->class != PPARAMREF)
+ n->addable = 1;
+ goto ret;
+
+ case OCALLINTER:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+ ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OCALLFUNC:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+
+ if(n->left->op == OCLOSURE) {
+ walkcallclosure(n, init);
+ t = n->left->type;
+ }
+
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+
+ ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OCALLMETH:
+ t = n->left->type;
+ if(n->list && n->list->n->op == OAS)
+ goto ret;
+ walkexpr(&n->left, init);
+ walkexprlist(n->list, init);
+ ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
+ lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = concat(ll, lr);
+ n->left->left = N;
+ ullmancalc(n->left);
+ n->list = reorder1(ll);
+ goto ret;
+
+ case OAS:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexpr(&n->left, init);
+ n->left = safeexpr(n->left, init);
+
+ if(oaslit(n, init))
+ goto ret;
+
+ walkexpr(&n->right, init);
+ if(n->left != N && n->right != N) {
+ r = convas(nod(OAS, n->left, n->right), init);
+ r->dodata = n->dodata;
+ n = r;
+ }
+
+ goto ret;
+
+ case OAS2:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexprlistsafe(n->list, init);
+ walkexprlistsafe(n->rlist, init);
+ ll = ascompatee(OAS, n->list, n->rlist, init);
+ ll = reorder3(ll);
+ n = liststmt(ll);
+ goto ret;
+
+ case OAS2FUNC:
+ as2func:
+ // a,b,... = fn()
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r, init);
+ l = n->list->n;
+
+ // all the really hard stuff - explicit function calls and so on -
+ // is gone, but map assignments remain.
+ // if there are map assignments here, assign via
+ // temporaries, because ascompatet assumes
+ // the targets can be addressed without function calls
+ // and map index has an implicit one.
+ lpost = nil;
+ if(l->op == OINDEXMAP) {
+ var = nod(OXXX, N, N);
+ tempname(var, l->type);
+ n->list->n = var;
+ a = nod(OAS, l, var);
+ typecheck(&a, Etop);
+ lpost = list(lpost, a);
+ }
+ l = n->list->next->n;
+ if(l->op == OINDEXMAP) {
+ var = nod(OXXX, N, N);
+ tempname(var, l->type);
+ n->list->next->n = var;
+ a = nod(OAS, l, var);
+ typecheck(&a, Etop);
+ lpost = list(lpost, a);
+ }
+ ll = ascompatet(n->op, n->list, &r->type, 0, init);
+ walkexprlist(lpost, init);
+ n = liststmt(concat(concat(list1(r), ll), lpost));
+ goto ret;
+
+ case OAS2RECV:
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r->left, init);
+ fn = chanfn("chanrecv2", 2, r->left->type);
+ r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left);
+ n->rlist->n = r;
+ n->op = OAS2FUNC;
+ goto as2func;
+
+ case OAS2MAPR:
+ // a,b = m[i];
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ walkexpr(&r->left, init);
+ fn = mapfn("mapaccess2", r->left->type);
+ r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left, r->right);
+ n->rlist = list1(r);
+ n->op = OAS2FUNC;
+ goto as2func;
+
+ case OAS2MAPW:
+ // map[] = a,b - mapassign2
+ // a,b = m[i];
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ walkexprlistsafe(n->list, init);
+ l = n->list->n;
+ t = l->left->type;
+ n = mkcall1(mapfn("mapassign2", t), T, init, typename(t), l->left, l->right, n->rlist->n, n->rlist->next->n);
+ goto ret;
+
+ case OAS2DOTTYPE:
+ // a,b = i.(T)
+ *init = concat(*init, n->ninit);
+ n->ninit = nil;
+ r = n->rlist->n;
+ walkexprlistsafe(n->list, init);
+ r->op = ODOTTYPE2;
+ walkexpr(&r, init);
+ ll = ascompatet(n->op, n->list, &r->type, 0, init);
+ n = liststmt(concat(list1(r), ll));
+ goto ret;
+
+ case ODOTTYPE:
+ case ODOTTYPE2:
+ // Build name of function: assertI2E2 etc.
+ strcpy(buf, "assert");
+ p = buf+strlen(buf);
+ if(isnilinter(n->left->type))
+ *p++ = 'E';
+ else
+ *p++ = 'I';
+ *p++ = '2';
+ if(isnilinter(n->type))
+ *p++ = 'E';
+ else if(isinter(n->type))
+ *p++ = 'I';
+ else
+ *p++ = 'T';
+ if(n->op == ODOTTYPE2)
+ *p++ = '2';
+ *p = '\0';
+
+ fn = syslook(buf, 1);
+ ll = list1(typename(n->type));
+ ll = list(ll, n->left);
+ argtype(fn, n->left->type);
+ argtype(fn, n->type);
+ n = nod(OCALL, fn, N);
+ n->list = ll;
+ typecheck(&n, Erv | Efnstruct);
+ walkexpr(&n, init);
+ goto ret;
+
+ case OCONVIFACE:
+ // Build name of function: convI2E etc.
+ // Not all names are possible
+ // (e.g., we'll never generate convE2E or convE2I).
+ walkexpr(&n->left, init);
+ strcpy(buf, "conv");
+ p = buf+strlen(buf);
+ if(isnilinter(n->left->type))
+ *p++ = 'E';
+ else if(isinter(n->left->type))
+ *p++ = 'I';
+ else
+ *p++ = 'T';
+ *p++ = '2';
+ if(isnilinter(n->type))
+ *p++ = 'E';
+ else
+ *p++ = 'I';
+ *p = '\0';
+
+ fn = syslook(buf, 1);
+ ll = nil;
+ if(!isinter(n->left->type))
+ ll = list(ll, typename(n->left->type));
+ if(!isnilinter(n->type))
+ ll = list(ll, typename(n->type));
+ ll = list(ll, n->left);
+ argtype(fn, n->left->type);
+ argtype(fn, n->type);
+ dowidth(fn->type);
+ n = nod(OCALL, fn, N);
+ n->list = ll;
+ typecheck(&n, Erv);
+ walkexpr(&n, init);
+ goto ret;
+
+ case OCONV:
+ case OCONVNOP:
+ if(thechar == '5') {
+ if(isfloat[n->left->type->etype]) {
+ if(n->type->etype == TINT64) {
+ n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64]));
+ goto ret;
+ }
+ if(n->type->etype == TUINT64) {
+ n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64]));
+ goto ret;
+ }
+ }
+ if(isfloat[n->type->etype]) {
+ if(n->left->type->etype == TINT64) {
+ n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64]));
+ goto ret;
+ }
+ if(n->left->type->etype == TUINT64) {
+ n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64]));
+ goto ret;
+ }
+ }
+ }
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case OASOP:
+ if(n->etype == OANDNOT) {
+ n->etype = OAND;
+ n->right = nod(OCOM, n->right, N);
+ typecheck(&n->right, Erv);
+ }
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->left, init);
+ l = n->left;
+ walkexpr(&n->right, init);
+
+ /*
+ * on 32-bit arch, rewrite 64-bit ops into l = l op r.
+ * on 386, rewrite float ops into l = l op r.
+ * everywhere, rewrite map ops into l = l op r.
+ * everywhere, rewrite string += into l = l op r.
+ * everywhere, rewrite complex /= into l = l op r.
+ * TODO(rsc): Maybe this rewrite should be done always?
+ */
+ et = n->left->type->etype;
+ if((widthptr == 4 && (et == TUINT64 || et == TINT64)) ||
+ (thechar == '8' && isfloat[et]) ||
+ l->op == OINDEXMAP ||
+ et == TSTRING ||
+ (iscomplex[et] && n->etype == ODIV)) {
+ l = safeexpr(n->left, init);
+ a = l;
+ if(a->op == OINDEXMAP) {
+ // map index has "lhs" bit set in a->etype.
+ // make a copy so we can clear it on the rhs.
+ a = nod(OXXX, N, N);
+ *a = *l;
+ a->etype = 0;
+ }
+ r = nod(OAS, l, nod(n->etype, a, n->right));
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ n = r;
+ }
+ goto ret;
+
+ case OANDNOT:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ n->op = OAND;
+ n->right = nod(OCOM, n->right, N);
+ typecheck(&n->right, Erv);
+ goto ret;
+
+ case ODIV:
+ case OMOD:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ /*
+ * rewrite complex div into function call.
+ */
+ et = n->left->type->etype;
+ if(iscomplex[et] && n->op == ODIV) {
+ t = n->type;
+ n = mkcall("complex128div", types[TCOMPLEX128], init,
+ conv(n->left, types[TCOMPLEX128]),
+ conv(n->right, types[TCOMPLEX128]));
+ n = conv(n, t);
+ goto ret;
+ }
+ /*
+ * rewrite div and mod into function calls
+ * on 32-bit architectures.
+ */
+ if(widthptr > 4 || (et != TUINT64 && et != TINT64))
+ goto ret;
+ if(et == TINT64)
+ strcpy(namebuf, "int64");
+ else
+ strcpy(namebuf, "uint64");
+ if(n->op == ODIV)
+ strcat(namebuf, "div");
+ else
+ strcat(namebuf, "mod");
+ n = mkcall(namebuf, n->type, init,
+ conv(n->left, types[et]), conv(n->right, types[et]));
+ goto ret;
+
+ case OINDEX:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+
+ // if range of type cannot exceed static array bound,
+ // disable bounds check
+ if(isfixedarray(n->left->type))
+ if(n->right->type->width < 4)
+ if((1<<(8*n->right->type->width)) <= n->left->type->bound)
+ n->etype = 1;
+
+ if(isconst(n->left, CTSTR))
+ if(n->right->type->width < 4)
+ if((1<<(8*n->right->type->width)) <= n->left->val.u.sval->len)
+ n->etype = 1;
+
+ // check for static out of bounds
+ if(isconst(n->right, CTINT) && !n->etype) {
+ v = mpgetfix(n->right->val.u.xval);
+ len = 1LL<<60;
+ t = n->left->type;
+ if(isconst(n->left, CTSTR))
+ len = n->left->val.u.sval->len;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t))
+ len = t->bound;
+ if(v < 0 || v >= (1LL<<31) || v >= len)
+ yyerror("index out of bounds");
+ else if(isconst(n->left, CTSTR)) {
+ // replace "abc"[2] with 'b'.
+ // delayed until now because "abc"[2] is not
+ // an ideal constant.
+ nodconst(n, n->type, n->left->val.u.sval->s[v]);
+ }
+ }
+ goto ret;
+
+ case OINDEXMAP:
+ if(n->etype == 1)
+ goto ret;
+
+ t = n->left->type;
+ n = mkcall1(mapfn("mapaccess1", t), t->type, init, typename(t), n->left, n->right);
+ goto ret;
+
+ case ORECV:
+ walkexpr(&n->left, init);
+ walkexpr(&n->right, init);
+ n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left);
+ goto ret;
+
+ case OSLICE:
+ case OSLICEARR:
+ walkexpr(&n->left, init);
+ n->left = safeexpr(n->left, init);
+ walkexpr(&n->right->left, init);
+ n->right->left = safeexpr(n->right->left, init);
+ walkexpr(&n->right->right, init);
+ n->right->right = safeexpr(n->right->right, init);
+
+ len = 1LL<<60;
+ t = n->left->type;
+ if(t != T && isptr[t->etype])
+ t = t->type;
+ if(isfixedarray(t))
+ len = t->bound;
+
+ // check for static out of bounds
+ // NOTE: v > len not v >= len.
+ v1 = -1;
+ v2 = -1;
+ if(isconst(n->right->left, CTINT)) {
+ v1 = mpgetfix(n->right->left->val.u.xval);
+ if(v1 < 0 || v1 >= (1LL<<31) || v1 > len) {
+ yyerror("slice index out of bounds");
+ v1 = -1;
+ }
+ }
+ if(isconst(n->right->right, CTINT)) {
+ v2 = mpgetfix(n->right->right->val.u.xval);
+ if(v2 < 0 || v2 >= (1LL<<31) || v2 > len) {
+ yyerror("slice index out of bounds");
+ v2 = -1;
+ }
+ }
+ if(v1 >= 0 && v2 >= 0 && v1 > v2)
+ yyerror("inverted slice range");
+
+ if(n->op == OSLICEARR)
+ goto slicearray;
+
+ // dynamic slice
+ // sliceslice(old []any, lb uint64, hb uint64, width uint64) (ary []any)
+ // sliceslice1(old []any, lb uint64, width uint64) (ary []any)
+ t = n->type;
+ et = n->etype;
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TUINT64]);
+ if(n->right->right != N) {
+ fn = syslook("sliceslice", 1);
+ argtype(fn, t->type); // any-1
+ argtype(fn, t->type); // any-2
+ n = mkcall1(fn, t, init,
+ n->left,
+ l,
+ conv(n->right->right, types[TUINT64]),
+ nodintconst(t->type->width));
+ } else {
+ fn = syslook("sliceslice1", 1);
+ argtype(fn, t->type); // any-1
+ argtype(fn, t->type); // any-2
+ n = mkcall1(fn, t, init,
+ n->left,
+ l,
+ nodintconst(t->type->width));
+ }
+ n->etype = et; // preserve no-typecheck flag from OSLICE to the slice* call.
+ goto ret;
+
+ slicearray:
+ // static slice
+ // slicearray(old *any, uint64 nel, lb uint64, hb uint64, width uint64) (ary []any)
+ t = n->type;
+ fn = syslook("slicearray", 1);
+ argtype(fn, n->left->type->type); // any-1
+ argtype(fn, t->type); // any-2
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TUINT64]);
+ if(n->right->right == N)
+ r = nodintconst(n->left->type->type->bound);
+ else
+ r = conv(n->right->right, types[TUINT64]);
+ n = mkcall1(fn, t, init,
+ n->left, nodintconst(n->left->type->type->bound),
+ l,
+ r,
+ nodintconst(t->type->width));
+ goto ret;
+
+ case OADDR:;
+ Node *nvar, *nstar;
+
+ // turn &Point(1, 2) or &[]int(1, 2) or &[...]int(1, 2) into allocation.
+ // initialize with
+ // nvar := new(*Point);
+ // *nvar = Point(1, 2);
+ // and replace expression with nvar
+ switch(n->left->op) {
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ nvar = makenewvar(n->type, init, &nstar);
+ anylit(0, n->left, nstar, init);
+ n = nvar;
+ goto ret;
+ }
+
+ walkexpr(&n->left, init);
+ goto ret;
+
+ case ONEW:
+ n = callnew(n->type->type);
+ goto ret;
+
+ case OCMPSTR:
+ // If one argument to the comparison is an empty string,
+ // comparing the lengths instead will yield the same result
+ // without the function call.
+ if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) ||
+ (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) {
+ r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N));
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ goto ret;
+ }
+
+ // s + "badgerbadgerbadger" == "badgerbadgerbadger"
+ if((n->etype == OEQ || n->etype == ONE) &&
+ isconst(n->right, CTSTR) &&
+ n->left->op == OADDSTR && isconst(n->left->right, CTSTR) &&
+ cmpslit(n->right, n->left->right) == 0) {
+ r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0));
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ n = r;
+ goto ret;
+ }
+
+ // prepare for rewrite below
+ if(n->etype == OEQ || n->etype == ONE) {
+ n->left = cheapexpr(n->left, init);
+ n->right = cheapexpr(n->right, init);
+ }
+
+ // sys_cmpstring(s1, s2) :: 0
+ r = mkcall("cmpstring", types[TINT], init,
+ conv(n->left, types[TSTRING]),
+ conv(n->right, types[TSTRING]));
+ r = nod(n->etype, r, nodintconst(0));
+
+ // quick check of len before full compare for == or !=
+ if(n->etype == OEQ || n->etype == ONE) {
+ if(n->etype == OEQ)
+ r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ else
+ r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+ typecheck(&r, Erv);
+ walkexpr(&r, nil);
+ }
+ typecheck(&r, Erv);
+ n = r;
+ goto ret;
+
+ case OADDSTR:
+ n = addstr(n, init);
+ goto ret;
+
+ case OSLICESTR:
+ // sys_slicestring(s, lb, hb)
+ if(n->right->left == N)
+ l = nodintconst(0);
+ else
+ l = conv(n->right->left, types[TINT]);
+ if(n->right->right) {
+ n = mkcall("slicestring", n->type, init,
+ conv(n->left, types[TSTRING]),
+ l,
+ conv(n->right->right, types[TINT]));
+ } else {
+ n = mkcall("slicestring1", n->type, init,
+ conv(n->left, types[TSTRING]),
+ l);
+ }
+ goto ret;
+
+ case OAPPEND:
+ if(n->isddd)
+ n = appendslice(n, init);
+ else
+ n = append(n, init);
+ goto ret;
+
+ case OCOPY:
+ if(n->right->type->etype == TSTRING)
+ fn = syslook("slicestringcopy", 1);
+ else
+ fn = syslook("slicecopy", 1);
+ argtype(fn, n->left->type);
+ argtype(fn, n->right->type);
+ n = mkcall1(fn, n->type, init,
+ n->left, n->right,
+ nodintconst(n->left->type->type->width));
+ goto ret;
+
+ case OCLOSE:
+ // cannot use chanfn - closechan takes any, not chan any
+ fn = syslook("closechan", 1);
+ argtype(fn, n->left->type);
+ n = mkcall1(fn, T, init, n->left);
+ goto ret;
+
+ case OMAKECHAN:
+ n = mkcall1(chanfn("makechan", 1, n->type), n->type, init,
+ typename(n->type),
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OMAKEMAP:
+ t = n->type;
+
+ fn = syslook("makemap", 1);
+ argtype(fn, t->down); // any-1
+ argtype(fn, t->type); // any-2
+
+ n = mkcall1(fn, n->type, init,
+ typename(n->type),
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OMAKESLICE:
+ // makeslice(t *Type, nel int64, max int64) (ary []any)
+ l = n->left;
+ r = n->right;
+ if(r == nil)
+ l = r = safeexpr(l, init);
+ t = n->type;
+ fn = syslook("makeslice", 1);
+ argtype(fn, t->type); // any-1
+ n = mkcall1(fn, n->type, init,
+ typename(n->type),
+ conv(l, types[TINT64]),
+ conv(r, types[TINT64]));
+ goto ret;
+
+ case ORUNESTR:
+ // sys_intstring(v)
+ n = mkcall("intstring", n->type, init,
+ conv(n->left, types[TINT64]));
+ goto ret;
+
+ case OARRAYBYTESTR:
+ // slicebytetostring([]byte) string;
+ n = mkcall("slicebytetostring", n->type, init, n->left);
+ goto ret;
+
+ case OARRAYRUNESTR:
+ // sliceinttostring([]int) string;
+ n = mkcall("sliceinttostring", n->type, init, n->left);
+ goto ret;
+
+ case OSTRARRAYBYTE:
+ // stringtoslicebyte(string) []byte;
+ n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
+ goto ret;
+
+ case OSTRARRAYRUNE:
+ // stringtosliceint(string) []int
+ n = mkcall("stringtosliceint", n->type, init, n->left);
+ goto ret;
+
+ case OCMPIFACE:
+ // ifaceeq(i1 any-1, i2 any-2) (ret bool);
+ if(!eqtype(n->left->type, n->right->type))
+ fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type);
+ if(isnilinter(n->left->type))
+ fn = syslook("efaceeq", 1);
+ else
+ fn = syslook("ifaceeq", 1);
+ argtype(fn, n->right->type);
+ argtype(fn, n->left->type);
+ r = mkcall1(fn, n->type, init, n->left, n->right);
+ if(n->etype == ONE) {
+ r = nod(ONOT, r, N);
+ typecheck(&r, Erv);
+ }
+ n = r;
+ goto ret;
+
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ nvar = nod(OXXX, N, N);
+ tempname(nvar, n->type);
+ anylit(0, n, nvar, init);
+ n = nvar;
+ goto ret;
+
+ case OSEND:
+ n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right);
+ goto ret;
+
+ case OCLOSURE:
+ n = walkclosure(n, init);
+ goto ret;
+ }
+ fatal("missing switch %O", n->op);
+
+ret:
+ if(debug['w'] && n != N)
+ dump("walk", n);
+
+ ullmancalc(n);
+ lineno = lno;
+ *np = n;
+}
+
+static Node*
+makenewvar(Type *t, NodeList **init, Node **nstar)
+{
+ Node *nvar, *nas;
+
+ nvar = nod(OXXX, N, N);
+ tempname(nvar, t);
+ nas = nod(OAS, nvar, callnew(t->type));
+ typecheck(&nas, Etop);
+ walkexpr(&nas, init);
+ *init = list(*init, nas);
+
+ *nstar = nod(OIND, nvar, N);
+ typecheck(nstar, Erv);
+ return nvar;
+}
+
+static Node*
+ascompatee1(int op, Node *l, Node *r, NodeList **init)
+{
+ return convas(nod(OAS, l, r), init);
+}
+
+static NodeList*
+ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
+{
+ NodeList *ll, *lr, *nn;
+
+ /*
+ * check assign expression list to
+ * a expression list. called in
+ * expr-list = expr-list
+ */
+
+ // ensure order of evaluation for function calls
+ for(ll=nl; ll; ll=ll->next)
+ ll->n = safeexpr(ll->n, init);
+ for(lr=nr; lr; lr=lr->next)
+ lr->n = safeexpr(lr->n, init);
+
+ nn = nil;
+ for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next)
+ nn = list(nn, ascompatee1(op, ll->n, lr->n, init));
+
+ // cannot happen: caller checked that lists had same length
+ if(ll || lr)
+ yyerror("error in shape across %O", op);
+ return nn;
+}
+
+/*
+ * l is an lv and rt is the type of an rv
+ * return 1 if this implies a function call
+ * evaluating the lv or a function call
+ * in the conversion of the types
+ */
+static int
+fncall(Node *l, Type *rt)
+{
+ if(l->ullman >= UINF || l->op == OINDEXMAP)
+ return 1;
+ if(eqtype(l->type, rt))
+ return 0;
+ return 1;
+}
+
+static NodeList*
+ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
+{
+ Node *l, *tmp, *a;
+ NodeList *ll;
+ Type *r;
+ Iter saver;
+ int ucount;
+ NodeList *nn, *mm;
+
+ /*
+ * check assign type list to
+ * a expression list. called in
+ * expr-list = func()
+ */
+ r = structfirst(&saver, nr);
+ nn = nil;
+ mm = nil;
+ ucount = 0;
+ for(ll=nl; ll; ll=ll->next) {
+ if(r == T)
+ break;
+ l = ll->n;
+ if(isblank(l)) {
+ r = structnext(&saver);
+ continue;
+ }
+
+ // any lv that causes a fn call must be
+ // deferred until all the return arguments
+ // have been pulled from the output arguments
+ if(fncall(l, r->type)) {
+ tmp = nod(OXXX, N, N);
+ tempname(tmp, r->type);
+ typecheck(&tmp, Erv);
+ a = nod(OAS, l, tmp);
+ a = convas(a, init);
+ mm = list(mm, a);
+ l = tmp;
+ }
+
+ a = nod(OAS, l, nodarg(r, fp));
+ a = convas(a, init);
+ ullmancalc(a);
+ if(a->ullman >= UINF)
+ ucount++;
+ nn = list(nn, a);
+ r = structnext(&saver);
+ }
+
+ if(ll != nil || r != T)
+ yyerror("assignment count mismatch: %d = %d",
+ count(nl), structcount(*nr));
+ if(ucount)
+ fatal("reorder2: too many function calls evaluating parameters");
+ return concat(nn, mm);
+}
+
+ /*
+ * package all the arguments that match a ... T parameter into a []T.
+ */
+static NodeList*
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
+{
+ Node *a, *n;
+ Type *tslice;
+
+ tslice = typ(TARRAY);
+ tslice->type = l->type->type;
+ tslice->bound = -1;
+
+ n = nod(OCOMPLIT, N, typenod(tslice));
+ n->list = lr0;
+ typecheck(&n, Erv);
+ if(n->type == T)
+ fatal("mkdotargslice: typecheck failed");
+ walkexpr(&n, init);
+
+ a = nod(OAS, nodarg(l, fp), n);
+ nn = list(nn, convas(a, init));
+ return nn;
+}
+
+/*
+ * helpers for shape errors
+ */
+static char*
+dumptypes(Type **nl, char *what)
+{
+ int first;
+ Type *l;
+ Iter savel;
+ Fmt fmt;
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "\t");
+ l = structfirst(&savel, nl);
+ first = 1;
+ for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
+ if(first)
+ first = 0;
+ else
+ fmtprint(&fmt, ", ");
+ fmtprint(&fmt, "%T", l);
+ }
+ if(first)
+ fmtprint(&fmt, "[no arguments %s]", what);
+ return fmtstrflush(&fmt);
+}
+
+static char*
+dumpnodetypes(NodeList *l, char *what)
+{
+ int first;
+ Node *r;
+ Fmt fmt;
+
+ fmtstrinit(&fmt);
+ fmtprint(&fmt, "\t");
+ first = 1;
+ for(; l; l=l->next) {
+ r = l->n;
+ if(first)
+ first = 0;
+ else
+ fmtprint(&fmt, ", ");
+ fmtprint(&fmt, "%T", r->type);
+ }
+ if(first)
+ fmtprint(&fmt, "[no arguments %s]", what);
+ return fmtstrflush(&fmt);
+}
+
+/*
+ * check assign expression list to
+ * a type list. called in
+ * return expr-list
+ * func(expr-list)
+ */
+static NodeList*
+ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+{
+ Type *l, *ll;
+ Node *r, *a;
+ NodeList *nn, *lr0, *alist;
+ Iter savel;
+ char *l1, *l2;
+
+ lr0 = lr;
+ l = structfirst(&savel, nl);
+ r = N;
+ if(lr)
+ r = lr->n;
+ nn = nil;
+
+ // f(g()) where g has multiple return values
+ if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
+ // optimization - can do block copy
+ if(eqtypenoname(r->type, *nl)) {
+ a = nodarg(*nl, fp);
+ a->type = r->type;
+ nn = list1(convas(nod(OAS, a, r), init));
+ goto ret;
+ }
+
+ // conversions involved.
+ // copy into temporaries.
+ alist = nil;
+ for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
+ a = nod(OXXX, N, N);
+ tempname(a, l->type);
+ alist = list(alist, a);
+ }
+ a = nod(OAS2, N, N);
+ a->list = alist;
+ a->rlist = lr;
+ typecheck(&a, Etop);
+ walkstmt(&a);
+ *init = list(*init, a);
+ lr = alist;
+ r = lr->n;
+ l = structfirst(&savel, nl);
+ }
+
+loop:
+ if(l != T && l->isddd) {
+ // the ddd parameter must be last
+ ll = structnext(&savel);
+ if(ll != T)
+ yyerror("... must be last argument");
+
+ // special case --
+ // only if we are assigning a single ddd
+ // argument to a ddd parameter then it is
+ // passed thru unencapsulated
+ if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) {
+ a = nod(OAS, nodarg(l, fp), r);
+ a = convas(a, init);
+ nn = list(nn, a);
+ goto ret;
+ }
+
+ // normal case -- make a slice of all
+ // remaining arguments and pass it to
+ // the ddd parameter.
+ nn = mkdotargslice(lr, nn, l, fp, init);
+ goto ret;
+ }
+
+ if(l == T || r == N) {
+ if(l != T || r != N) {
+ l1 = dumptypes(nl, "expected");
+ l2 = dumpnodetypes(lr0, "given");
+ if(l != T)
+ yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2);
+ else
+ yyerror("too many arguments to %O\n%s\n%s", op, l1, l2);
+ }
+ goto ret;
+ }
+
+ a = nod(OAS, nodarg(l, fp), r);
+ a = convas(a, init);
+ nn = list(nn, a);
+
+ l = structnext(&savel);
+ r = N;
+ lr = lr->next;
+ if(lr != nil)
+ r = lr->n;
+ goto loop;
+
+ret:
+ for(lr=nn; lr; lr=lr->next)
+ lr->n->typecheck = 1;
+ return nn;
+}
+
+// generate code for print
+static Node*
+walkprint(Node *nn, NodeList **init, int defer)
+{
+ Node *r;
+ Node *n;
+ NodeList *l, *all;
+ Node *on;
+ Type *t;
+ int notfirst, et, op;
+ NodeList *calls, *intypes, *args;
+ Fmt fmt;
+
+ on = nil;
+ op = nn->op;
+ all = nn->list;
+ calls = nil;
+ notfirst = 0;
+ intypes = nil;
+ args = nil;
+
+ memset(&fmt, 0, sizeof fmt);
+ if(defer) {
+ // defer print turns into defer printf with format string
+ fmtstrinit(&fmt);
+ intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING])));
+ args = list1(nod(OXXX, N, N));
+ }
+
+ for(l=all; l; l=l->next) {
+ if(notfirst) {
+ if(defer)
+ fmtprint(&fmt, " ");
+ else
+ calls = list(calls, mkcall("printsp", T, init));
+ }
+ notfirst = op == OPRINTN;
+
+ n = l->n;
+ if(n->op == OLITERAL) {
+ switch(n->val.ctype) {
+ case CTINT:
+ defaultlit(&n, types[TINT64]);
+ break;
+ case CTFLT:
+ defaultlit(&n, types[TFLOAT64]);
+ break;
+ }
+ }
+ if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL)
+ defaultlit(&n, types[TINT64]);
+ defaultlit(&n, nil);
+ l->n = n;
+ if(n->type == T || n->type->etype == TFORW)
+ continue;
+
+ t = n->type;
+ et = n->type->etype;
+ if(isinter(n->type)) {
+ if(defer) {
+ if(isnilinter(n->type))
+ fmtprint(&fmt, "%%e");
+ else
+ fmtprint(&fmt, "%%i");
+ } else {
+ if(isnilinter(n->type))
+ on = syslook("printeface", 1);
+ else
+ on = syslook("printiface", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
+ if(defer) {
+ fmtprint(&fmt, "%%p");
+ } else {
+ on = syslook("printpointer", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isslice(n->type)) {
+ if(defer) {
+ fmtprint(&fmt, "%%a");
+ } else {
+ on = syslook("printslice", 1);
+ argtype(on, n->type); // any-1
+ }
+ } else if(isint[et]) {
+ if(defer) {
+ if(et == TUINT64)
+ fmtprint(&fmt, "%%U");
+ else {
+ fmtprint(&fmt, "%%D");
+ t = types[TINT64];
+ }
+ } else {
+ if(et == TUINT64)
+ on = syslook("printuint", 0);
+ else
+ on = syslook("printint", 0);
+ }
+ } else if(isfloat[et]) {
+ if(defer) {
+ fmtprint(&fmt, "%%f");
+ t = types[TFLOAT64];
+ } else
+ on = syslook("printfloat", 0);
+ } else if(iscomplex[et]) {
+ if(defer) {
+ fmtprint(&fmt, "%%C");
+ t = types[TCOMPLEX128];
+ } else
+ on = syslook("printcomplex", 0);
+ } else if(et == TBOOL) {
+ if(defer)
+ fmtprint(&fmt, "%%t");
+ else
+ on = syslook("printbool", 0);
+ } else if(et == TSTRING) {
+ if(defer)
+ fmtprint(&fmt, "%%S");
+ else
+ on = syslook("printstring", 0);
+ } else {
+ badtype(OPRINT, n->type, T);
+ continue;
+ }
+
+ if(!defer) {
+ t = *getinarg(on->type);
+ if(t != nil)
+ t = t->type;
+ if(t != nil)
+ t = t->type;
+ }
+
+ if(!eqtype(t, n->type)) {
+ n = nod(OCONV, n, N);
+ n->type = t;
+ }
+
+ if(defer) {
+ intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
+ args = list(args, n);
+ } else {
+ r = nod(OCALL, on, N);
+ r->list = list1(n);
+ calls = list(calls, r);
+ }
+ }
+
+ if(defer) {
+ if(op == OPRINTN)
+ fmtprint(&fmt, "\n");
+ on = syslook("goprintf", 1);
+ on->type = functype(nil, intypes, nil);
+ args->n = nod(OLITERAL, N, N);
+ args->n->val.ctype = CTSTR;
+ args->n->val.u.sval = strlit(fmtstrflush(&fmt));
+ r = nod(OCALL, on, N);
+ r->list = args;
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ } else {
+ if(op == OPRINTN)
+ calls = list(calls, mkcall("printnl", T, nil));
+ typechecklist(calls, Etop);
+ walkexprlist(calls, init);
+
+ r = nod(OEMPTY, N, N);
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ r->ninit = calls;
+ }
+ return r;
+}
+
+Node*
+callnew(Type *t)
+{
+ Node *fn;
+
+ dowidth(t);
+ fn = syslook("new", 1);
+ argtype(fn, t);
+ return mkcall1(fn, ptrto(t), nil, nodintconst(t->width));
+}
+
+static Node*
+convas(Node *n, NodeList **init)
+{
+ Type *lt, *rt;
+
+ if(n->op != OAS)
+ fatal("convas: not OAS %O", n->op);
+
+ n->typecheck = 1;
+
+ if(n->left == N || n->right == N)
+ goto out;
+
+ lt = n->left->type;
+ rt = n->right->type;
+ if(lt == T || rt == T)
+ goto out;
+
+ if(isblank(n->left)) {
+ defaultlit(&n->right, T);
+ goto out;
+ }
+
+ if(n->left->op == OINDEXMAP) {
+ n = mkcall1(mapfn("mapassign1", n->left->left->type), T, init,
+ typename(n->left->left->type),
+ n->left->left, n->left->right, n->right);
+ goto out;
+ }
+
+ if(eqtype(lt, rt))
+ goto out;
+
+ n->right = assignconv(n->right, lt, "assignment");
+ walkexpr(&n->right, init);
+
+out:
+ ullmancalc(n);
+ return n;
+}
+
+/*
+ * from ascompat[te]
+ * evaluating actual function arguments.
+ * f(a,b)
+ * if there is exactly one function expr,
+ * then it is done first. otherwise must
+ * make temp variables
+ */
+NodeList*
+reorder1(NodeList *all)
+{
+ Node *f, *a, *n;
+ NodeList *l, *r, *g;
+ int c, d, t;
+
+ c = 0; // function calls
+ t = 0; // total parameters
+
+ for(l=all; l; l=l->next) {
+ n = l->n;
+ t++;
+ ullmancalc(n);
+ if(n->ullman >= UINF)
+ c++;
+ }
+ if(c == 0 || t == 1)
+ return all;
+
+ g = nil; // fncalls assigned to tempnames
+ f = N; // last fncall assigned to stack
+ r = nil; // non fncalls and tempnames assigned to stack
+ d = 0;
+ for(l=all; l; l=l->next) {
+ n = l->n;
+ if(n->ullman < UINF) {
+ r = list(r, n);
+ continue;
+ }
+ d++;
+ if(d == c) {
+ f = n;
+ continue;
+ }
+
+ // make assignment of fncall to tempname
+ a = nod(OXXX, N, N);
+ tempname(a, n->right->type);
+ a = nod(OAS, a, n->right);
+ g = list(g, a);
+
+ // put normal arg assignment on list
+ // with fncall replaced by tempname
+ n->right = a->left;
+ r = list(r, n);
+ }
+
+ if(f != N)
+ g = list(g, f);
+ return concat(g, r);
+}
+
+/*
+ * from ascompat[ee]
+ * a,b = c,d
+ * simultaneous assignment. there cannot
+ * be later use of an earlier lvalue.
+ */
+
+static int
+vmatch2(Node *l, Node *r)
+{
+ NodeList *ll;
+
+ /*
+ * isolate all right sides
+ */
+ if(r == N)
+ return 0;
+ switch(r->op) {
+ case ONAME:
+ // match each right given left
+ if(l == r)
+ return 1;
+ case OLITERAL:
+ return 0;
+ }
+ if(vmatch2(l, r->left))
+ return 1;
+ if(vmatch2(l, r->right))
+ return 1;
+ for(ll=r->list; ll; ll=ll->next)
+ if(vmatch2(l, ll->n))
+ return 1;
+ return 0;
+}
+
+int
+vmatch1(Node *l, Node *r)
+{
+ NodeList *ll;
+
+ /*
+ * isolate all left sides
+ */
+ if(l == N || r == N)
+ return 0;
+ switch(l->op) {
+ case ONAME:
+ switch(l->class) {
+ case PPARAM:
+ case PPARAMREF:
+ case PAUTO:
+ break;
+ default:
+ // assignment to non-stack variable
+ // must be delayed if right has function calls.
+ if(r->ullman >= UINF)
+ return 1;
+ break;
+ }
+ return vmatch2(l, r);
+ case OLITERAL:
+ return 0;
+ }
+ if(vmatch1(l->left, r))
+ return 1;
+ if(vmatch1(l->right, r))
+ return 1;
+ for(ll=l->list; ll; ll=ll->next)
+ if(vmatch1(ll->n, r))
+ return 1;
+ return 0;
+}
+
+NodeList*
+reorder3(NodeList *all)
+{
+ Node *n1, *n2, *q;
+ int c1, c2;
+ NodeList *l1, *l2, *r;
+
+ r = nil;
+ for(l1=all, c1=0; l1; l1=l1->next, c1++) {
+ n1 = l1->n;
+ for(l2=all, c2=0; l2; l2=l2->next, c2++) {
+ n2 = l2->n;
+ if(c2 > c1) {
+ if(vmatch1(n1->left, n2->right)) {
+ // delay assignment to n1->left
+ q = nod(OXXX, N, N);
+ tempname(q, n1->right->type);
+ q = nod(OAS, n1->left, q);
+ n1->left = q->right;
+ r = list(r, q);
+ break;
+ }
+ }
+ }
+ }
+ return concat(all, r);
+}
+
+/*
+ * walk through argin parameters.
+ * generate and return code to allocate
+ * copies of escaped parameters to the heap.
+ */
+static NodeList*
+paramstoheap(Type **argin, int out)
+{
+ Type *t;
+ Iter savet;
+ Node *v;
+ NodeList *nn;
+
+ nn = nil;
+ for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+ v = t->nname;
+ if(v == N && out && hasdefer) {
+ // Defer might stop a panic and show the
+ // return values as they exist at the time of panic.
+ // Make sure to zero them on entry to the function.
+ nn = list(nn, nod(OAS, nodarg(t, 1), N));
+ }
+ if(v == N || !(v->class & PHEAP))
+ continue;
+
+ // generate allocation & copying code
+ if(v->alloc == nil)
+ v->alloc = callnew(v->type);
+ nn = list(nn, nod(OAS, v->heapaddr, v->alloc));
+ if((v->class & ~PHEAP) != PPARAMOUT)
+ nn = list(nn, nod(OAS, v, v->stackparam));
+ }
+ return nn;
+}
+
+/*
+ * walk through argout parameters copying back to stack
+ */
+static NodeList*
+returnsfromheap(Type **argin)
+{
+ Type *t;
+ Iter savet;
+ Node *v;
+ NodeList *nn;
+
+ nn = nil;
+ for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+ v = t->nname;
+ if(v == N || v->class != (PHEAP|PPARAMOUT))
+ continue;
+ nn = list(nn, nod(OAS, v->stackparam, v));
+ }
+ return nn;
+}
+
+/*
+ * take care of migrating any function in/out args
+ * between the stack and the heap. adds code to
+ * curfn's before and after lists.
+ */
+static void
+heapmoves(void)
+{
+ NodeList *nn;
+ int32 lno;
+
+ lno = lineno;
+ lineno = curfn->lineno;
+ nn = paramstoheap(getthis(curfn->type), 0);
+ nn = concat(nn, paramstoheap(getinarg(curfn->type), 0));
+ nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1));
+ curfn->enter = concat(curfn->enter, nn);
+ lineno = curfn->endlineno;
+ curfn->exit = returnsfromheap(getoutarg(curfn->type));
+ lineno = lno;
+}
+
+static Node*
+vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
+{
+ int i, n;
+ Node *r;
+ NodeList *args;
+
+ if(fn->type == T || fn->type->etype != TFUNC)
+ fatal("mkcall %#N %T", fn, fn->type);
+
+ args = nil;
+ n = fn->type->intuple;
+ for(i=0; i<n; i++)
+ args = list(args, va_arg(va, Node*));
+
+ r = nod(OCALL, fn, N);
+ r->list = args;
+ if(fn->type->outtuple > 0)
+ typecheck(&r, Erv | Efnstruct);
+ else
+ typecheck(&r, Etop);
+ walkexpr(&r, init);
+ r->type = t;
+ return r;
+}
+
+Node*
+mkcall(char *name, Type *t, NodeList **init, ...)
+{
+ Node *r;
+ va_list va;
+
+ va_start(va, init);
+ r = vmkcall(syslook(name, 0), t, init, va);
+ va_end(va);
+ return r;
+}
+
+Node*
+mkcall1(Node *fn, Type *t, NodeList **init, ...)
+{
+ Node *r;
+ va_list va;
+
+ va_start(va, init);
+ r = vmkcall(fn, t, init, va);
+ va_end(va);
+ return r;
+}
+
+static Node*
+conv(Node *n, Type *t)
+{
+ if(eqtype(n->type, t))
+ return n;
+ n = nod(OCONV, n, N);
+ n->type = t;
+ typecheck(&n, Erv);
+ return n;
+}
+
+Node*
+chanfn(char *name, int n, Type *t)
+{
+ Node *fn;
+ int i;
+
+ if(t->etype != TCHAN)
+ fatal("chanfn %T", t);
+ fn = syslook(name, 1);
+ for(i=0; i<n; i++)
+ argtype(fn, t->type);
+ return fn;
+}
+
+static Node*
+mapfn(char *name, Type *t)
+{
+ Node *fn;
+
+ if(t->etype != TMAP)
+ fatal("mapfn %T", t);
+ fn = syslook(name, 1);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ argtype(fn, t->down);
+ argtype(fn, t->type);
+ return fn;
+}
+
+static Node*
+addstr(Node *n, NodeList **init)
+{
+ Node *r, *cat, *typstr;
+ NodeList *in, *args;
+ int i, count;
+
+ count = 0;
+ for(r=n; r->op == OADDSTR; r=r->left)
+ count++; // r->right
+ count++; // r
+
+ // prepare call of runtime.catstring of type int, string, string, string
+ // with as many strings as we have.
+ cat = syslook("concatstring", 1);
+ cat->type = T;
+ cat->ntype = nod(OTFUNC, N, N);
+ in = list1(nod(ODCLFIELD, N, typenod(types[TINT]))); // count
+ typstr = typenod(types[TSTRING]);
+ for(i=0; i<count; i++)
+ in = list(in, nod(ODCLFIELD, N, typstr));
+ cat->ntype->list = in;
+ cat->ntype->rlist = list1(nod(ODCLFIELD, N, typstr));
+
+ args = nil;
+ for(r=n; r->op == OADDSTR; r=r->left)
+ args = concat(list1(conv(r->right, types[TSTRING])), args);
+ args = concat(list1(conv(r, types[TSTRING])), args);
+ args = concat(list1(nodintconst(count)), args);
+
+ r = nod(OCALL, cat, N);
+ r->list = args;
+ typecheck(&r, Erv);
+ walkexpr(&r, init);
+ r->type = n->type;
+
+ return r;
+}
+
+static Node*
+appendslice(Node *n, NodeList **init)
+{
+ Node *f;
+
+ f = syslook("appendslice", 1);
+ argtype(f, n->type);
+ argtype(f, n->type->type);
+ argtype(f, n->type);
+ return mkcall1(f, n->type, init, typename(n->type), n->list->n, n->list->next->n);
+}
+
+// expand append(src, a [, b]* ) to
+//
+// init {
+// s := src
+// const argc = len(args) - 1
+// if cap(s) - len(s) < argc {
+// s = growslice(s, argc)
+// }
+// n := len(s)
+// s = s[:n+argc]
+// s[n] = a
+// s[n+1] = b
+// ...
+// }
+// s
+static Node*
+append(Node *n, NodeList **init)
+{
+ NodeList *l, *a;
+ Node *nsrc, *ns, *nn, *na, *nx, *fn;
+ int argc;
+
+ walkexprlistsafe(n->list, init);
+
+ nsrc = n->list->n;
+ argc = count(n->list) - 1;
+ if (argc < 1) {
+ return nsrc;
+ }
+
+ l = nil;
+
+ ns = nod(OXXX, N, N); // var s
+ tempname(ns, nsrc->type);
+ l = list(l, nod(OAS, ns, nsrc)); // s = src
+
+ na = nodintconst(argc); // const argc
+ nx = nod(OIF, N, N); // if cap(s) - len(s) < argc
+ nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);
+
+ fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T)
+ argtype(fn, ns->type->type); // 1 old []any
+ argtype(fn, ns->type->type); // 2 ret []any
+
+ nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit,
+ typename(ns->type),
+ ns,
+ conv(na, types[TINT64]))));
+ l = list(l, nx);
+
+ nn = nod(OXXX, N, N); // var n
+ tempname(nn, types[TINT]);
+ l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s)
+
+ nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc]
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc]
+
+ for (a = n->list->next; a != nil; a = a->next) {
+ nx = nod(OINDEX, ns, nn); // s[n] ...
+ nx->etype = 1; // disable bounds check
+ l = list(l, nod(OAS, nx, a->n)); // s[n] = arg
+ if (a->next != nil)
+ l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1
+ }
+
+ typechecklist(l, Etop);
+ walkstmtlist(l);
+ *init = concat(*init, l);
+ return ns;
+}