diff options
Diffstat (limited to 'src/cmd/gc/walk.c')
-rw-r--r-- | src/cmd/gc/walk.c | 4189 |
1 files changed, 0 insertions, 4189 deletions
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c deleted file mode 100644 index 50dae8ca68..0000000000 --- a/src/cmd/gc/walk.c +++ /dev/null @@ -1,4189 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -#include <u.h> -#include <libc.h> -#include "go.h" -#include "../ld/textflag.h" -#include "../../runtime/mgc0.h" - -static Node* walkprint(Node*, NodeList**); -static Node* writebarrierfn(char*, Type*, Type*); -static Node* applywritebarrier(Node*, NodeList**); -static Node* mapfn(char*, Type*); -static Node* mapfndel(char*, Type*); -static Node* ascompatee1(int, Node*, Node*, NodeList**); -static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); -static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); -static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**); -static Node* convas(Node*, NodeList**); -static void heapmoves(void); -static NodeList* paramstoheap(Type **argin, int out); -static NodeList* reorder1(NodeList*); -static NodeList* reorder3(NodeList*); -static Node* addstr(Node*, NodeList**); -static Node* appendslice(Node*, NodeList**); -static Node* append(Node*, NodeList**); -static Node* copyany(Node*, NodeList**, int); -static Node* sliceany(Node*, NodeList**); -static void walkcompare(Node**, NodeList**); -static void walkrotate(Node**); -static void walkmul(Node**, NodeList**); -static void walkdiv(Node**, NodeList**); -static int bounded(Node*, int64); -static Mpint mpzero; -static void walkprintfunc(Node**, NodeList**); - -// The constant is known to runtime. -enum -{ - tmpstringbufsize = 32, -}; - -void -walk(Node *fn) -{ - char s[50]; - NodeList *l; - int lno; - - curfn = fn; - - if(debug['W']) { - snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym); - dumplist(s, curfn->nbody); - } - - lno = lineno; - - // Final typecheck for any unused variables. - // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. - for(l=fn->dcl; l; l=l->next) - if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO) - typecheck(&l->n, Erv | Easgn); - - // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. - for(l=fn->dcl; l; l=l->next) - if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used) - l->n->defn->left->used++; - - for(l=fn->dcl; l; l=l->next) { - if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used) - continue; - if(l->n->defn && l->n->defn->op == OTYPESW) { - if(l->n->defn->left->used) - continue; - lineno = l->n->defn->left->lineno; - yyerror("%S declared and not used", l->n->sym); - l->n->defn->left->used = 1; // suppress repeats - } else { - lineno = l->n->lineno; - yyerror("%S declared and not used", l->n->sym); - } - } - - lineno = lno; - if(nerrors != 0) - return; - walkstmtlist(curfn->nbody); - if(debug['W']) { - snprint(s, sizeof(s), "after walk %S", curfn->nname->sym); - dumplist(s, curfn->nbody); - } - heapmoves(); - if(debug['W'] && curfn->enter != nil) { - snprint(s, sizeof(s), "enter %S", curfn->nname->sym); - dumplist(s, curfn->enter); - } -} - - -void -walkstmtlist(NodeList *l) -{ - for(; l; l=l->next) - walkstmt(&l->n); -} - -static int -samelist(NodeList *a, NodeList *b) -{ - for(; a && b; a=a->next, b=b->next) - if(a->n != b->n) - return 0; - return a == b; -} - -static int -paramoutheap(Node *fn) -{ - NodeList *l; - - for(l=fn->dcl; l; l=l->next) { - switch(l->n->class) { - case PPARAMOUT: - case PPARAMOUT|PHEAP: - return l->n->addrtaken; - case PAUTO: - case PAUTO|PHEAP: - // stop early - parameters are over - return 0; - } - } - return 0; -} - -// adds "adjust" to all the argument locations for the call n. -// n must be a defer or go node that has already been walked. -static void -adjustargs(Node *n, int adjust) -{ - Node *callfunc, *arg, *lhs; - NodeList *args; - - callfunc = n->left; - for(args = callfunc->list; args != 0; args = args->next) { - arg = args->n; - if(arg->op != OAS) - yyerror("call arg not assignment"); - lhs = arg->left; - if(lhs->op == ONAME) { - // This is a temporary introduced by reorder1. - // The real store to the stack appears later in the arg list. - continue; - } - if(lhs->op != OINDREG) { - yyerror("call argument store does not use OINDREG"); - } - // can't really check this in machine-indep code. - //if(lhs->val.u.reg != D_SP) - // yyerror("call arg assign not indreg(SP)"); - lhs->xoffset += adjust; - } -} - -void -walkstmt(Node **np) -{ - NodeList *init; - NodeList *ll, *rl; - int cl; - Node *n, *f; - - n = *np; - if(n == N) - return; - if(n->dodata == 2) // don't walk, generated by anylit. - return; - - setlineno(n); - - walkstmtlist(n->ninit); - - switch(n->op) { - default: - if(n->op == ONAME) - yyerror("%S is not a top level statement", n->sym); - else - yyerror("%O is not a top level statement", n->op); - dump("nottop", n); - break; - - case OAS: - case OASOP: - case OAS2: - case OAS2DOTTYPE: - case OAS2RECV: - case OAS2FUNC: - case OAS2MAPR: - case OCLOSE: - case OCOPY: - case OCALLMETH: - case OCALLINTER: - case OCALL: - case OCALLFUNC: - case ODELETE: - case OSEND: - case OPRINT: - case OPRINTN: - case OPANIC: - case OEMPTY: - case ORECOVER: - if(n->typecheck == 0) - fatal("missing typecheck: %+N", n); - init = n->ninit; - n->ninit = nil; - walkexpr(&n, &init); - addinit(&n, init); - if((*np)->op == OCOPY && n->op == OCONVNOP) - n->op = OEMPTY; // don't leave plain values as statements. - break; - - case ORECV: - // special case for a receive where we throw away - // the value received. - if(n->typecheck == 0) - fatal("missing typecheck: %+N", n); - init = n->ninit; - n->ninit = nil; - - walkexpr(&n->left, &init); - n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, &init, typename(n->left->type), n->left, nodnil()); - walkexpr(&n, &init); - - addinit(&n, init); - break; - - case OBREAK: - case ODCL: - case OCONTINUE: - case OFALL: - case OGOTO: - case OLABEL: - case ODCLCONST: - case ODCLTYPE: - case OCHECKNIL: - case OVARKILL: - break; - - case OBLOCK: - walkstmtlist(n->list); - break; - - case OXCASE: - yyerror("case statement out of place"); - n->op = OCASE; - case OCASE: - walkstmt(&n->right); - break; - - case ODEFER: - hasdefer = 1; - switch(n->left->op) { - case OPRINT: - case OPRINTN: - walkprintfunc(&n->left, &n->ninit); - break; - case OCOPY: - n->left = copyany(n->left, &n->ninit, 1); - break; - default: - walkexpr(&n->left, &n->ninit); - break; - } - // make room for size & fn arguments. - adjustargs(n, 2 * widthptr); - break; - - case OFOR: - if(n->ntest != N) { - walkstmtlist(n->ntest->ninit); - init = n->ntest->ninit; - n->ntest->ninit = nil; - walkexpr(&n->ntest, &init); - addinit(&n->ntest, init); - } - walkstmt(&n->nincr); - walkstmtlist(n->nbody); - break; - - case OIF: - walkexpr(&n->ntest, &n->ninit); - walkstmtlist(n->nbody); - walkstmtlist(n->nelse); - break; - - case OPROC: - switch(n->left->op) { - case OPRINT: - case OPRINTN: - walkprintfunc(&n->left, &n->ninit); - break; - case OCOPY: - n->left = copyany(n->left, &n->ninit, 1); - break; - default: - walkexpr(&n->left, &n->ninit); - break; - } - // make room for size & fn arguments. - adjustargs(n, 2 * widthptr); - break; - - case ORETURN: - walkexprlist(n->list, &n->ninit); - if(n->list == nil) - break; - if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) { - // assign to the function out parameters, - // so that reorder3 can fix up conflicts - rl = nil; - for(ll=curfn->dcl; ll != nil; ll=ll->next) { - cl = ll->n->class & ~PHEAP; - if(cl == PAUTO) - break; - if(cl == PPARAMOUT) - rl = list(rl, ll->n); - } - if(samelist(rl, n->list)) { - // special return in disguise - n->list = nil; - break; - } - if(count(n->list) == 1 && count(rl) > 1) { - // OAS2FUNC in disguise - f = n->list->n; - if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER) - fatal("expected return of call, have %N", f); - n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); - break; - } - - // move function calls out, to make reorder3's job easier. - walkexprlistsafe(n->list, &n->ninit); - ll = ascompatee(n->op, rl, n->list, &n->ninit); - n->list = reorder3(ll); - break; - } - ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); - n->list = ll; - break; - - case ORETJMP: - break; - - case OSELECT: - walkselect(n); - break; - - case OSWITCH: - walkswitch(n); - break; - - case ORANGE: - walkrange(n); - break; - - case OXFALL: - yyerror("fallthrough statement out of place"); - n->op = OFALL; - break; - } - - if(n->op == ONAME) - fatal("walkstmt ended up with name: %+N", n); - - *np = n; -} - - -/* - * walk the whole tree of the body of an - * expression or simple statement. - * the types expressions are calculated. - * compile-time constants are evaluated. - * complex side effects like statements are appended to init - */ - -void -walkexprlist(NodeList *l, NodeList **init) -{ - for(; l; l=l->next) - walkexpr(&l->n, init); -} - -void -walkexprlistsafe(NodeList *l, NodeList **init) -{ - for(; l; l=l->next) { - l->n = safeexpr(l->n, init); - walkexpr(&l->n, init); - } -} - -void -walkexprlistcheap(NodeList *l, NodeList **init) -{ - for(; l; l=l->next) { - l->n = cheapexpr(l->n, init); - walkexpr(&l->n, init); - } -} - -void -walkexpr(Node **np, NodeList **init) -{ - Node *r, *l, *var, *a, *ok; - Node *map, *key; - NodeList *ll, *lr; - Type *t; - int et, old_safemode; - int64 v; - int32 lno; - Node *n, *fn, *n1, *n2; - Sym *sym; - char buf[100], *p, *from, *to; - - n = *np; - - if(n == N) - return; - - if(init == &n->ninit) { - // not okay to use n->ninit when walking n, - // because we might replace n with some other node - // and would lose the init list. - fatal("walkexpr init == &n->ninit"); - } - - if(n->ninit != nil) { - walkstmtlist(n->ninit); - *init = concat(*init, n->ninit); - n->ninit = nil; - } - - // annoying case - not typechecked - if(n->op == OKEY) { - walkexpr(&n->left, init); - walkexpr(&n->right, init); - return; - } - - lno = setlineno(n); - - if(debug['w'] > 1) - dump("walk-before", n); - - if(n->typecheck != 1) - fatal("missed typecheck: %+N\n", n); - - switch(n->op) { - default: - dump("walk", n); - fatal("walkexpr: switch 1 unknown op %+hN", n); - break; - - case OTYPE: - case ONONAME: - case OINDREG: - case OEMPTY: - case OPARAM: - goto ret; - - case ONOT: - case OMINUS: - case OPLUS: - case OCOM: - case OREAL: - case OIMAG: - case ODOTMETH: - case ODOTINTER: - walkexpr(&n->left, init); - goto ret; - - case OIND: - walkexpr(&n->left, init); - goto ret; - - case ODOT: - usefield(n); - walkexpr(&n->left, init); - goto ret; - - case ODOTPTR: - usefield(n); - if(n->op == ODOTPTR && n->left->type->type->width == 0) { - // No actual copy will be generated, so emit an explicit nil check. - n->left = cheapexpr(n->left, init); - checknil(n->left, init); - } - walkexpr(&n->left, init); - goto ret; - - case OEFACE: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - goto ret; - - case OSPTR: - case OITAB: - walkexpr(&n->left, init); - goto ret; - - case OLEN: - case OCAP: - walkexpr(&n->left, init); - - // replace len(*[10]int) with 10. - // delayed until now to preserve side effects. - t = n->left->type; - if(isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) { - safeexpr(n->left, init); - nodconst(n, n->type, t->bound); - n->typecheck = 1; - } - goto ret; - - case OLSH: - case ORSH: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - t = n->left->type; - n->bounded = bounded(n->right, 8*t->width); - if(debug['m'] && n->etype && !isconst(n->right, CTINT)) - warn("shift bounds check elided"); - goto ret; - - case OAND: - case OSUB: - case OHMUL: - case OLT: - case OLE: - case OGE: - case OGT: - case OADD: - case OCOMPLEX: - case OLROT: - // Use results from call expression as arguments for complex. - if(n->op == OCOMPLEX && n->left == N && n->right == N) { - n->left = n->list->n; - n->right = n->list->next->n; - } - walkexpr(&n->left, init); - walkexpr(&n->right, init); - goto ret; - - case OOR: - case OXOR: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - walkrotate(&n); - goto ret; - - case OEQ: - case ONE: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - // Disable safemode while compiling this code: the code we - // generate internally can refer to unsafe.Pointer. - // In this case it can happen if we need to generate an == - // for a struct containing a reflect.Value, which itself has - // an unexported field of type unsafe.Pointer. - old_safemode = safemode; - safemode = 0; - walkcompare(&n, init); - safemode = old_safemode; - goto ret; - - case OANDAND: - case OOROR: - walkexpr(&n->left, init); - // cannot put side effects from n->right on init, - // because they cannot run before n->left is checked. - // save elsewhere and store on the eventual n->right. - ll = nil; - walkexpr(&n->right, &ll); - addinit(&n->right, ll); - goto ret; - - case OPRINT: - case OPRINTN: - walkexprlist(n->list, init); - n = walkprint(n, init); - goto ret; - - case OPANIC: - n = mkcall("gopanic", T, init, n->left); - goto ret; - - case ORECOVER: - n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N)); - goto ret; - - case OLITERAL: - n->addable = 1; - goto ret; - - case OCLOSUREVAR: - case OCFUNC: - n->addable = 1; - goto ret; - - case ONAME: - if(!(n->class & PHEAP) && n->class != PPARAMREF) - n->addable = 1; - goto ret; - - case OCALLINTER: - t = n->left->type; - if(n->list && n->list->n->op == OAS) - goto ret; - walkexpr(&n->left, init); - walkexprlist(n->list, init); - ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); - n->list = reorder1(ll); - goto ret; - - case OCALLFUNC: - if(n->left->op == OCLOSURE) { - // Transform direct call of a closure to call of a normal function. - // transformclosure already did all preparation work. - - // Append captured variables to argument list. - n->list = concat(n->list, n->left->enter); - n->left->enter = nil; - // Replace OCLOSURE with ONAME/PFUNC. - n->left = n->left->closure->nname; - // Update type of OCALLFUNC node. - // Output arguments had not changed, but their offsets could. - if(n->left->type->outtuple == 1) { - t = getoutargx(n->left->type)->type; - if(t->etype == TFIELD) - t = t->type; - n->type = t; - } else - n->type = getoutargx(n->left->type); - } - - t = n->left->type; - if(n->list && n->list->n->op == OAS) - goto ret; - - walkexpr(&n->left, init); - walkexprlist(n->list, init); - - ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); - n->list = reorder1(ll); - goto ret; - - case OCALLMETH: - t = n->left->type; - if(n->list && n->list->n->op == OAS) - goto ret; - walkexpr(&n->left, init); - walkexprlist(n->list, init); - ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init); - lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); - ll = concat(ll, lr); - n->left->left = N; - ullmancalc(n->left); - n->list = reorder1(ll); - goto ret; - - case OAS: - *init = concat(*init, n->ninit); - n->ninit = nil; - - walkexpr(&n->left, init); - n->left = safeexpr(n->left, init); - - if(oaslit(n, init)) - goto ret; - - if(n->right == N || iszero(n->right) && !flag_race) - goto ret; - - switch(n->right->op) { - default: - walkexpr(&n->right, init); - break; - - case ODOTTYPE: - // x = i.(T); n->left is x, n->right->left is i. - // orderstmt made sure x is addressable. - walkexpr(&n->right->left, init); - n1 = nod(OADDR, n->left, N); - r = n->right; // i.(T) - - from = "I"; - to = "T"; - if(isnilinter(r->left->type)) - from = "E"; - if(isnilinter(r->type)) - to = "E"; - else if(isinter(r->type)) - to = "I"; - - snprint(buf, sizeof buf, "assert%s2%s", from, to); - - fn = syslook(buf, 1); - argtype(fn, r->left->type); - argtype(fn, r->type); - - n = mkcall1(fn, T, init, typename(r->type), r->left, n1); - walkexpr(&n, init); - goto ret; - - case ORECV: - // x = <-c; n->left is x, n->right->left is c. - // orderstmt made sure x is addressable. - walkexpr(&n->right->left, init); - n1 = nod(OADDR, n->left, N); - r = n->right->left; // the channel - n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1); - walkexpr(&n, init); - goto ret; - } - - if(n->left != N && n->right != N) { - r = convas(nod(OAS, n->left, n->right), init); - r->dodata = n->dodata; - n = r; - n = applywritebarrier(n, init); - } - - goto ret; - - case OAS2: - *init = concat(*init, n->ninit); - n->ninit = nil; - walkexprlistsafe(n->list, init); - walkexprlistsafe(n->rlist, init); - ll = ascompatee(OAS, n->list, n->rlist, init); - ll = reorder3(ll); - for(lr = ll; lr != nil; lr = lr->next) - lr->n = applywritebarrier(lr->n, init); - n = liststmt(ll); - goto ret; - - case OAS2FUNC: - // a,b,... = fn() - *init = concat(*init, n->ninit); - n->ninit = nil; - r = n->rlist->n; - walkexprlistsafe(n->list, init); - walkexpr(&r, init); - - ll = ascompatet(n->op, n->list, &r->type, 0, init); - for(lr = ll; lr != nil; lr = lr->next) - lr->n = applywritebarrier(lr->n, init); - n = liststmt(concat(list1(r), ll)); - goto ret; - - case OAS2RECV: - // x, y = <-c - // orderstmt made sure x is addressable. - *init = concat(*init, n->ninit); - n->ninit = nil; - r = n->rlist->n; - walkexprlistsafe(n->list, init); - walkexpr(&r->left, init); - if(isblank(n->list->n)) - n1 = nodnil(); - else - n1 = nod(OADDR, n->list->n, N); - n1->etype = 1; // addr does not escape - fn = chanfn("chanrecv2", 2, r->left->type); - r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1); - n = nod(OAS, n->list->next->n, r); - typecheck(&n, Etop); - goto ret; - - case OAS2MAPR: - // a,b = m[i]; - *init = concat(*init, n->ninit); - n->ninit = nil; - r = n->rlist->n; - walkexprlistsafe(n->list, init); - walkexpr(&r->left, init); - walkexpr(&r->right, init); - t = r->left->type; - p = nil; - if(t->type->width <= 128) { // Check ../../runtime/hashmap.go:maxValueSize before changing. - switch(simsimtype(t->down)) { - case TINT32: - case TUINT32: - p = "mapaccess2_fast32"; - break; - case TINT64: - case TUINT64: - p = "mapaccess2_fast64"; - break; - case TSTRING: - p = "mapaccess2_faststr"; - break; - } - } - if(p != nil) { - // fast versions take key by value - key = r->right; - } else { - // standard version takes key by reference - // orderexpr made sure key is addressable. - key = nod(OADDR, r->right, N); - p = "mapaccess2"; - } - - // from: - // a,b = m[i] - // to: - // var,b = mapaccess2*(t, m, i) - // a = *var - a = n->list->n; - fn = mapfn(p, t); - r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key); - - // mapaccess2* returns a typed bool, but due to spec changes, - // the boolean result of i.(T) is now untyped so we make it the - // same type as the variable on the lhs. - if(!isblank(n->list->next->n)) - r->type->type->down->type = n->list->next->n->type; - n->rlist = list1(r); - n->op = OAS2FUNC; - - // don't generate a = *var if a is _ - if(!isblank(a)) { - var = temp(ptrto(t->type)); - var->typecheck = 1; - n->list->n = var; - walkexpr(&n, init); - *init = list(*init, n); - n = nod(OAS, a, nod(OIND, var, N)); - } - - typecheck(&n, Etop); - walkexpr(&n, init); - // mapaccess needs a zero value to be at least this big. - if(zerosize < t->type->width) - zerosize = t->type->width; - // TODO: ptr is always non-nil, so disable nil check for this OIND op. - goto ret; - - case ODELETE: - *init = concat(*init, n->ninit); - n->ninit = nil; - map = n->list->n; - key = n->list->next->n; - walkexpr(&map, init); - walkexpr(&key, init); - // orderstmt made sure key is addressable. - key = nod(OADDR, key, N); - t = map->type; - n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key); - goto ret; - - case OAS2DOTTYPE: - // a,b = i.(T) - // orderstmt made sure a 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 - - from = "I"; - to = "T"; - if(isnilinter(r->left->type)) - from = "E"; - if(isnilinter(r->type)) - to = "E"; - else if(isinter(r->type)) - to = "I"; - snprint(buf, sizeof buf, "assert%s2%s2", from, to); - - fn = syslook(buf, 1); - argtype(fn, r->left->type); - argtype(fn, r->type); - - t = types[TBOOL]; - ok = n->list->next->n; - if(!isblank(ok)) - t = ok->type; - r = mkcall1(fn, t, init, typename(r->type), r->left, n1); - n = nod(OAS, ok, r); - typecheck(&n, Etop); - goto ret; - - case ODOTTYPE: - case ODOTTYPE2: - fatal("walkexpr ODOTTYPE"); // should see inside OAS or OAS2 only - - case OCONVIFACE: - walkexpr(&n->left, init); - - // Optimize convT2E as a two-word copy when T is pointer-shaped. - if(isnilinter(n->type) && isdirectiface(n->left->type)) { - l = nod(OEFACE, typename(n->left->type), n->left); - l->type = n->type; - l->typecheck = n->typecheck; - n = l; - goto ret; - } - - // Build name of function: convI2E etc. - // Not all names are possible - // (e.g., we'll never generate convE2E or convE2I). - from = "T"; - to = "I"; - if(isnilinter(n->left->type)) - from = "E"; - else if(isinter(n->left->type)) - from = "I"; - if(isnilinter(n->type)) - to = "E"; - snprint(buf, sizeof buf, "conv%s2%s", from, to); - - fn = syslook(buf, 1); - ll = nil; - if(!isinter(n->left->type)) - ll = list(ll, typename(n->left->type)); - if(!isnilinter(n->type)) - ll = list(ll, typename(n->type)); - if(!isinter(n->left->type) && !isnilinter(n->type)){ - sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg); - if(sym->def == N) { - l = nod(ONAME, N, N); - l->sym = sym; - l->type = ptrto(types[TUINT8]); - l->addable = 1; - l->class = PEXTERN; - l->xoffset = 0; - sym->def = l; - ggloblsym(sym, widthptr, DUPOK|NOPTR); - } - l = nod(OADDR, sym->def, N); - l->addable = 1; - ll = list(ll, l); - - if(isdirectiface(n->left->type)) { - /* For pointer types, we can make a special form of optimization - * - * These statements are put onto the expression init list: - * Itab *tab = atomicloadtype(&cache); - * if(tab == nil) - * tab = typ2Itab(type, itype, &cache); - * - * The CONVIFACE expression is replaced with this: - * OEFACE{tab, ptr}; - */ - l = temp(ptrto(types[TUINT8])); - - n1 = nod(OAS, l, sym->def); - typecheck(&n1, Etop); - *init = list(*init, n1); - - fn = syslook("typ2Itab", 1); - n1 = nod(OCALL, fn, N); - n1->list = ll; - typecheck(&n1, Erv); - walkexpr(&n1, init); - - n2 = nod(OIF, N, N); - n2->ntest = nod(OEQ, l, nodnil()); - n2->nbody = list1(nod(OAS, l, n1)); - n2->likely = -1; - typecheck(&n2, Etop); - *init = list(*init, n2); - - l = nod(OEFACE, l, n->left); - l->typecheck = n->typecheck; - l->type = n->type; - n = l; - goto ret; - } - } - if(isinter(n->left->type)) { - ll = list(ll, n->left); - } else { - // regular types are passed by reference to avoid C vararg calls - // orderexpr arranged for n->left to be a temporary for all - // the conversions it could see. comparison of an interface - // with a non-interface, especially in a switch on interface value - // with non-interface cases, is not visible to orderstmt, so we - // have to fall back on allocating a temp here. - if(islvalue(n->left)) - ll = list(ll, nod(OADDR, n->left, N)); - else - ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N)); - } - argtype(fn, n->left->type); - argtype(fn, n->type); - dowidth(fn->type); - n = nod(OCALL, fn, N); - n->list = ll; - typecheck(&n, Erv); - walkexpr(&n, init); - goto ret; - - case OCONV: - case OCONVNOP: - if(thearch.thechar == '5') { - if(isfloat[n->left->type->etype]) { - if(n->type->etype == TINT64) { - n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); - goto ret; - } - if(n->type->etype == TUINT64) { - n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); - goto ret; - } - } - if(isfloat[n->type->etype]) { - if(n->left->type->etype == TINT64) { - n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); - goto ret; - } - if(n->left->type->etype == TUINT64) { - n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); - goto ret; - } - } - } - walkexpr(&n->left, init); - goto ret; - - case OANDNOT: - walkexpr(&n->left, init); - n->op = OAND; - n->right = nod(OCOM, n->right, N); - typecheck(&n->right, Erv); - walkexpr(&n->right, init); - goto ret; - - case OMUL: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - walkmul(&n, init); - goto ret; - - case ODIV: - case OMOD: - walkexpr(&n->left, init); - walkexpr(&n->right, init); - /* - * rewrite complex div into function call. - */ - et = n->left->type->etype; - if(iscomplex[et] && n->op == ODIV) { - t = n->type; - n = mkcall("complex128div", types[TCOMPLEX128], init, - conv(n->left, types[TCOMPLEX128]), - conv(n->right, types[TCOMPLEX128])); - n = conv(n, t); - goto ret; - } - // Nothing to do for float divisions. - if(isfloat[et]) - goto ret; - - // Try rewriting as shifts or magic multiplies. - walkdiv(&n, init); - - /* - * rewrite 64-bit div and mod into function calls - * on 32-bit architectures. - */ - switch(n->op) { - case OMOD: - case ODIV: - if(widthreg >= 8 || (et != TUINT64 && et != TINT64)) - goto ret; - if(et == TINT64) - strcpy(namebuf, "int64"); - else - strcpy(namebuf, "uint64"); - if(n->op == ODIV) - strcat(namebuf, "div"); - else - strcat(namebuf, "mod"); - n = mkcall(namebuf, n->type, init, - conv(n->left, types[et]), conv(n->right, types[et])); - break; - default: - break; - } - goto ret; - - case OINDEX: - walkexpr(&n->left, init); - // save the original node for bounds checking elision. - // If it was a ODIV/OMOD walk might rewrite it. - r = n->right; - walkexpr(&n->right, init); - - // if range of type cannot exceed static array bound, - // disable bounds check. - if(n->bounded) - goto ret; - t = n->left->type; - if(t != T && isptr[t->etype]) - t = t->type; - if(isfixedarray(t)) { - n->bounded = bounded(r, t->bound); - if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) - warn("index bounds check elided"); - if(smallintconst(n->right) && !n->bounded) - yyerror("index out of bounds"); - } else if(isconst(n->left, CTSTR)) { - n->bounded = bounded(r, n->left->val.u.sval->len); - if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) - warn("index bounds check elided"); - if(smallintconst(n->right)) { - if(!n->bounded) - yyerror("index out of bounds"); - else { - // replace "abc"[1] with 'b'. - // delayed until now because "abc"[1] is not - // an ideal constant. - v = mpgetfix(n->right->val.u.xval); - nodconst(n, n->type, n->left->val.u.sval->s[v]); - n->typecheck = 1; - } - } - } - - if(isconst(n->right, CTINT)) - if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 || - mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) - yyerror("index out of bounds"); - goto ret; - - case OINDEXMAP: - if(n->etype == 1) - goto ret; - walkexpr(&n->left, init); - walkexpr(&n->right, init); - - t = n->left->type; - p = nil; - if(t->type->width <= 128) { // Check ../../runtime/hashmap.go:maxValueSize before changing. - switch(simsimtype(t->down)) { - case TINT32: - case TUINT32: - p = "mapaccess1_fast32"; - break; - case TINT64: - case TUINT64: - p = "mapaccess1_fast64"; - break; - case TSTRING: - p = "mapaccess1_faststr"; - break; - } - } - if(p != nil) { - // fast versions take key by value - key = n->right; - } else { - // standard version takes key by reference. - // orderexpr made sure key is addressable. - key = nod(OADDR, n->right, N); - p = "mapaccess1"; - } - n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key); - n = nod(OIND, n, N); - n->type = t->type; - n->typecheck = 1; - // mapaccess needs a zero value to be at least this big. - if(zerosize < t->type->width) - zerosize = t->type->width; - goto ret; - - case ORECV: - fatal("walkexpr ORECV"); // should see inside OAS only - - case OSLICE: - if(n->right != N && n->right->left == N && n->right->right == N) { // noop - walkexpr(&n->left, init); - n = n->left; - goto ret; - } - // fallthrough - case OSLICEARR: - case OSLICESTR: - if(n->right == N) // already processed - goto ret; - - walkexpr(&n->left, init); - // cgen_slice can't handle string literals as source - // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] - if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX)) - n->left = copyexpr(n->left, n->left->type, init); - else - n->left = safeexpr(n->left, init); - walkexpr(&n->right->left, init); - n->right->left = safeexpr(n->right->left, init); - walkexpr(&n->right->right, init); - n->right->right = safeexpr(n->right->right, init); - n = sliceany(n, init); // chops n->right, sets n->list - goto ret; - - case OSLICE3: - case OSLICE3ARR: - if(n->right == N) // already processed - goto ret; - - walkexpr(&n->left, init); - // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] - // TODO the comment on the previous line was copied from case OSLICE. it might not even be true. - if(n->left->op == OINDEX) - n->left = copyexpr(n->left, n->left->type, init); - else - n->left = safeexpr(n->left, init); - walkexpr(&n->right->left, init); - n->right->left = safeexpr(n->right->left, init); - walkexpr(&n->right->right->left, init); - n->right->right->left = safeexpr(n->right->right->left, init); - walkexpr(&n->right->right->right, init); - n->right->right->right = safeexpr(n->right->right->right, init); - n = sliceany(n, init); // chops n->right, sets n->list - goto ret; - - case OADDR: - walkexpr(&n->left, init); - goto ret; - - case ONEW: - if(n->esc == EscNone && n->type->type->width < (1<<16)) { - r = temp(n->type->type); - r = nod(OAS, r, N); // zero temp - typecheck(&r, Etop); - *init = list(*init, r); - r = nod(OADDR, r->left, N); - typecheck(&r, Erv); - n = r; - } else { - n = callnew(n->type->type); - } - goto ret; - - case OCMPSTR: - // If one argument to the comparison is an empty string, - // comparing the lengths instead will yield the same result - // without the function call. - if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) || - (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) { - r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N)); - typecheck(&r, Erv); - walkexpr(&r, init); - r->type = n->type; - n = r; - goto ret; - } - - // s + "badgerbadgerbadger" == "badgerbadgerbadger" - if((n->etype == OEQ || n->etype == ONE) && - isconst(n->right, CTSTR) && - n->left->op == OADDSTR && count(n->left->list) == 2 && - isconst(n->left->list->next->n, CTSTR) && - cmpslit(n->right, n->left->list->next->n) == 0) { - r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0)); - typecheck(&r, Erv); - walkexpr(&r, init); - r->type = n->type; - n = r; - goto ret; - } - - if(n->etype == OEQ || n->etype == ONE) { - // prepare for rewrite below - n->left = cheapexpr(n->left, init); - n->right = cheapexpr(n->right, init); - - r = mkcall("eqstring", types[TBOOL], init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); - - // quick check of len before full compare for == or != - // eqstring assumes that the lengths are equal - if(n->etype == OEQ) { - // len(left) == len(right) && eqstring(left, right) - r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); - } else { - // len(left) != len(right) || !eqstring(left, right) - r = nod(ONOT, r, N); - r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); - } - typecheck(&r, Erv); - walkexpr(&r, nil); - } else { - // sys_cmpstring(s1, s2) :: 0 - r = mkcall("cmpstring", types[TINT], init, - conv(n->left, types[TSTRING]), - conv(n->right, types[TSTRING])); - r = nod(n->etype, r, nodintconst(0)); - } - - typecheck(&r, Erv); - if(n->type->etype != TBOOL) fatal("cmp %T", n->type); - r->type = n->type; - n = r; - goto ret; - - case OADDSTR: - n = addstr(n, init); - goto ret; - - case OAPPEND: - if(n->isddd) - n = appendslice(n, init); // also works for append(slice, string). - else - n = append(n, init); - goto ret; - - case OCOPY: - n = copyany(n, init, flag_race); - goto ret; - - case OCLOSE: - // cannot use chanfn - closechan takes any, not chan any - fn = syslook("closechan", 1); - argtype(fn, n->left->type); - n = mkcall1(fn, T, init, n->left); - goto ret; - - case OMAKECHAN: - n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, - typename(n->type), - conv(n->left, types[TINT64])); - goto ret; - - case OMAKEMAP: - t = n->type; - - fn = syslook("makemap", 1); - - a = nodnil(); // hmap buffer - r = nodnil(); // bucket buffer - if(n->esc == EscNone) { - // Allocate hmap buffer on stack. - var = temp(hmap(t)); - a = nod(OAS, var, N); // zero temp - typecheck(&a, Etop); - *init = list(*init, a); - a = nod(OADDR, var, N); - - // Allocate one bucket on stack. - // Maximum key/value size is 128 bytes, larger objects - // are stored with an indirection. So max bucket size is 2048+eps. - var = temp(mapbucket(t)); - r = nod(OAS, var, N); // zero temp - typecheck(&r, Etop); - *init = list(*init, r); - r = nod(OADDR, var, N); - } - - argtype(fn, hmap(t)); // hmap buffer - argtype(fn, mapbucket(t)); // bucket buffer - argtype(fn, t->down); // key type - argtype(fn, t->type); // value type - n = mkcall1(fn, n->type, init, typename(n->type), conv(n->left, types[TINT64]), a, r); - goto ret; - - case OMAKESLICE: - l = n->left; - r = n->right; - if(r == nil) - l = r = safeexpr(l, init); - t = n->type; - if(n->esc == EscNone - && smallintconst(l) && smallintconst(r) - && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) { - // var arr [r]T - // n = arr[:l] - t = aindex(r, t->type); // [r]T - var = temp(t); - a = nod(OAS, var, N); // zero temp - typecheck(&a, Etop); - *init = list(*init, a); - r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l] - r = conv(r, n->type); // in case n->type is named. - typecheck(&r, Erv); - walkexpr(&r, init); - n = r; - } else { - // makeslice(t *Type, nel int64, max int64) (ary []any) - fn = syslook("makeslice", 1); - argtype(fn, t->type); // any-1 - n = mkcall1(fn, n->type, init, - typename(n->type), - conv(l, types[TINT64]), - conv(r, types[TINT64])); - } - goto ret; - - case ORUNESTR: - a = nodnil(); - if(n->esc == EscNone) { - t = aindex(nodintconst(4), types[TUINT8]); - var = temp(t); - a = nod(OADDR, var, N); - } - // intstring(*[4]byte, rune) - n = mkcall("intstring", n->type, init, a, conv(n->left, types[TINT64])); - goto ret; - - case OARRAYBYTESTR: - a = nodnil(); - if(n->esc == EscNone) { - // Create temporary buffer for string on stack. - t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); - a = nod(OADDR, temp(t), N); - } - // slicebytetostring(*[32]byte, []byte) string; - n = mkcall("slicebytetostring", n->type, init, a, n->left); - goto ret; - - case OARRAYBYTESTRTMP: - // slicebytetostringtmp([]byte) string; - n = mkcall("slicebytetostringtmp", n->type, init, n->left); - goto ret; - - case OARRAYRUNESTR: - // slicerunetostring(*[32]byte, []rune) string; - a = nodnil(); - if(n->esc == EscNone) { - // Create temporary buffer for string on stack. - t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); - a = nod(OADDR, temp(t), N); - } - n = mkcall("slicerunetostring", n->type, init, a, n->left); - goto ret; - - case OSTRARRAYBYTE: - // stringtoslicebyte(*32[byte], string) []byte; - a = nodnil(); - if(n->esc == EscNone) { - // Create temporary buffer for slice on stack. - t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); - a = nod(OADDR, temp(t), N); - } - n = mkcall("stringtoslicebyte", n->type, init, a, conv(n->left, types[TSTRING])); - goto ret; - - case OSTRARRAYBYTETMP: - // stringtoslicebytetmp(string) []byte; - n = mkcall("stringtoslicebytetmp", n->type, init, conv(n->left, types[TSTRING])); - goto ret; - - case OSTRARRAYRUNE: - // stringtoslicerune(*[32]rune, string) []rune - a = nodnil(); - if(n->esc == EscNone) { - // Create temporary buffer for slice on stack. - t = aindex(nodintconst(tmpstringbufsize), types[TINT32]); - a = nod(OADDR, temp(t), N); - } - n = mkcall("stringtoslicerune", n->type, init, a, n->left); - goto ret; - - case OCMPIFACE: - // ifaceeq(i1 any-1, i2 any-2) (ret bool); - if(!eqtype(n->left->type, n->right->type)) - fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type); - if(isnilinter(n->left->type)) - fn = syslook("efaceeq", 1); - else - fn = syslook("ifaceeq", 1); - - n->right = cheapexpr(n->right, init); - n->left = cheapexpr(n->left, init); - argtype(fn, n->right->type); - argtype(fn, n->left->type); - r = mkcall1(fn, n->type, init, n->left, n->right); - if(n->etype == ONE) - r = nod(ONOT, r, N); - - // check itable/type before full compare. - if(n->etype == OEQ) - r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); - else - r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); - typecheck(&r, Erv); - walkexpr(&r, init); - r->type = n->type; - n = r; - goto ret; - - case OARRAYLIT: - case OMAPLIT: - case OSTRUCTLIT: - case OPTRLIT: - var = temp(n->type); - anylit(0, n, var, init); - n = var; - goto ret; - - case OSEND: - n1 = n->right; - n1 = assignconv(n1, n->left->type->type, "chan send"); - walkexpr(&n1, init); - n1 = nod(OADDR, n1, N); - n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1); - goto ret; - - case OCLOSURE: - n = walkclosure(n, init); - goto ret; - - case OCALLPART: - n = walkpartialcall(n, init); - goto ret; - } - fatal("missing switch %O", n->op); - -ret: - // Expressions that are constant at run time but not - // considered const by the language spec are not turned into - // constants until walk. For example, if n is y%1 == 0, the - // walk of y%1 may have replaced it by 0. - // Check whether n with its updated args is itself now a constant. - t = n->type; - evconst(n); - n->type = t; - if(n->op == OLITERAL) - typecheck(&n, Erv); - - ullmancalc(n); - - if(debug['w'] && n != N) - dump("walk", n); - - lineno = lno; - *np = n; -} - -static Node* -ascompatee1(int op, Node *l, Node *r, NodeList **init) -{ - Node *n; - USED(op); - - // convas will turn map assigns into function calls, - // making it impossible for reorder3 to work. - n = nod(OAS, l, r); - if(l->op == OINDEXMAP) - return n; - - return convas(n, init); -} - -static NodeList* -ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) -{ - NodeList *ll, *lr, *nn; - - /* - * check assign expression list to - * a expression list. called in - * expr-list = expr-list - */ - - // ensure order of evaluation for function calls - for(ll=nl; ll; ll=ll->next) - ll->n = safeexpr(ll->n, init); - for(lr=nr; lr; lr=lr->next) - lr->n = safeexpr(lr->n, init); - - nn = nil; - for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) { - // Do not generate 'x = x' during return. See issue 4014. - if(op == ORETURN && ll->n == lr->n) - continue; - nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); - } - - // cannot happen: caller checked that lists had same length - if(ll || lr) - yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name); - return nn; -} - -/* - * l is an lv and rt is the type of an rv - * return 1 if this implies a function call - * evaluating the lv or a function call - * in the conversion of the types - */ -static int -fncall(Node *l, Type *rt) -{ - Node r; - - if(l->ullman >= UINF || l->op == OINDEXMAP) - return 1; - memset(&r, 0, sizeof r); - if(needwritebarrier(l, &r)) - return 1; - if(eqtype(l->type, rt)) - return 0; - return 1; -} - -static NodeList* -ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) -{ - Node *l, *tmp, *a; - NodeList *ll; - Type *r; - Iter saver; - int ucount; - NodeList *nn, *mm; - - USED(op); - - /* - * check assign type list to - * a expression list. called in - * expr-list = func() - */ - r = structfirst(&saver, nr); - nn = nil; - mm = nil; - ucount = 0; - for(ll=nl; ll; ll=ll->next) { - if(r == T) - break; - l = ll->n; - if(isblank(l)) { - r = structnext(&saver); - continue; - } - - // any lv that causes a fn call must be - // deferred until all the return arguments - // have been pulled from the output arguments - if(fncall(l, r->type)) { - tmp = temp(r->type); - typecheck(&tmp, Erv); - a = nod(OAS, l, tmp); - a = convas(a, init); - mm = list(mm, a); - l = tmp; - } - - a = nod(OAS, l, nodarg(r, fp)); - a = convas(a, init); - ullmancalc(a); - if(a->ullman >= UINF) { - dump("ascompatet ucount", a); - ucount++; - } - nn = list(nn, a); - r = structnext(&saver); - } - - if(ll != nil || r != T) - yyerror("ascompatet: assignment count mismatch: %d = %d", - count(nl), structcount(*nr)); - - if(ucount) - fatal("ascompatet: too many function calls evaluating parameters"); - return concat(nn, mm); -} - - /* - * package all the arguments that match a ... T parameter into a []T. - */ -static NodeList* -mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd) -{ - Node *a, *n; - Type *tslice; - int esc; - - esc = EscUnknown; - if(ddd != nil) - esc = ddd->esc; - - tslice = typ(TARRAY); - tslice->type = l->type->type; - tslice->bound = -1; - - if(count(lr0) == 0) { - n = nodnil(); - n->type = tslice; - } else { - n = nod(OCOMPLIT, N, typenod(tslice)); - if(ddd != nil) - n->alloc = ddd->alloc; // temporary to use - n->list = lr0; - n->esc = esc; - typecheck(&n, Erv); - if(n->type == T) - fatal("mkdotargslice: typecheck failed"); - walkexpr(&n, init); - } - - a = nod(OAS, nodarg(l, fp), n); - nn = list(nn, convas(a, init)); - return nn; -} - -/* - * helpers for shape errors - */ -static char* -dumptypes(Type **nl, char *what) -{ - int first; - Type *l; - Iter savel; - Fmt fmt; - - fmtstrinit(&fmt); - fmtprint(&fmt, "\t"); - first = 1; - for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { - if(first) - first = 0; - else - fmtprint(&fmt, ", "); - fmtprint(&fmt, "%T", l); - } - if(first) - fmtprint(&fmt, "[no arguments %s]", what); - return fmtstrflush(&fmt); -} - -static char* -dumpnodetypes(NodeList *l, char *what) -{ - int first; - Node *r; - Fmt fmt; - - fmtstrinit(&fmt); - fmtprint(&fmt, "\t"); - first = 1; - for(; l; l=l->next) { - r = l->n; - if(first) - first = 0; - else - fmtprint(&fmt, ", "); - fmtprint(&fmt, "%T", r->type); - } - if(first) - fmtprint(&fmt, "[no arguments %s]", what); - return fmtstrflush(&fmt); -} - -/* - * check assign expression list to - * a type list. called in - * return expr-list - * func(expr-list) - */ -static NodeList* -ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) -{ - Type *l, *ll; - Node *r, *a; - NodeList *nn, *lr0, *alist; - Iter savel; - char *l1, *l2; - - lr0 = lr; - l = structfirst(&savel, nl); - r = N; - if(lr) - r = lr->n; - nn = nil; - - // f(g()) where g has multiple return values - if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) { - // optimization - can do block copy - if(eqtypenoname(r->type, *nl)) { - a = nodarg(*nl, fp); - r = nod(OCONVNOP, r, N); - r->type = a->type; - nn = list1(convas(nod(OAS, a, r), init)); - goto ret; - } - - // conversions involved. - // copy into temporaries. - alist = nil; - for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) { - a = temp(l->type); - alist = list(alist, a); - } - a = nod(OAS2, N, N); - a->list = alist; - a->rlist = lr; - typecheck(&a, Etop); - walkstmt(&a); - *init = list(*init, a); - lr = alist; - r = lr->n; - l = structfirst(&savel, nl); - } - -loop: - if(l != T && l->isddd) { - // the ddd parameter must be last - ll = structnext(&savel); - if(ll != T) - yyerror("... must be last argument"); - - // special case -- - // only if we are assigning a single ddd - // argument to a ddd parameter then it is - // passed thru unencapsulated - if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { - a = nod(OAS, nodarg(l, fp), r); - a = convas(a, init); - nn = list(nn, a); - goto ret; - } - - // normal case -- make a slice of all - // remaining arguments and pass it to - // the ddd parameter. - nn = mkdotargslice(lr, nn, l, fp, init, call->right); - goto ret; - } - - if(l == T || r == N) { - if(l != T || r != N) { - l1 = dumptypes(nl, "expected"); - l2 = dumpnodetypes(lr0, "given"); - if(l != T) - yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); - else - yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); - } - goto ret; - } - - a = nod(OAS, nodarg(l, fp), r); - a = convas(a, init); - nn = list(nn, a); - - l = structnext(&savel); - r = N; - lr = lr->next; - if(lr != nil) - r = lr->n; - goto loop; - -ret: - for(lr=nn; lr; lr=lr->next) - lr->n->typecheck = 1; - return nn; -} - -// generate code for print -static Node* -walkprint(Node *nn, NodeList **init) -{ - Node *r; - Node *n; - NodeList *l, *all; - Node *on; - Type *t; - int notfirst, et, op; - NodeList *calls; - - op = nn->op; - all = nn->list; - calls = nil; - notfirst = 0; - - // Hoist all the argument evaluation up before the lock. - walkexprlistcheap(all, init); - - calls = list(calls, mkcall("printlock", T, init)); - - for(l=all; l; l=l->next) { - if(notfirst) { - calls = list(calls, mkcall("printsp", T, init)); - } - notfirst = op == OPRINTN; - - n = l->n; - if(n->op == OLITERAL) { - switch(n->val.ctype) { - case CTRUNE: - defaultlit(&n, runetype); - break; - case CTINT: - defaultlit(&n, types[TINT64]); - break; - case CTFLT: - defaultlit(&n, types[TFLOAT64]); - break; - } - } - if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL) - defaultlit(&n, types[TINT64]); - defaultlit(&n, nil); - l->n = n; - if(n->type == T || n->type->etype == TFORW) - continue; - - t = n->type; - et = n->type->etype; - if(isinter(n->type)) { - if(isnilinter(n->type)) - on = syslook("printeface", 1); - else - on = syslook("printiface", 1); - argtype(on, n->type); // any-1 - } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) { - on = syslook("printpointer", 1); - argtype(on, n->type); // any-1 - } else if(isslice(n->type)) { - on = syslook("printslice", 1); - argtype(on, n->type); // any-1 - } else if(isint[et]) { - if(et == TUINT64) { - if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0) - on = syslook("printhex", 0); - else - on = syslook("printuint", 0); - } else - on = syslook("printint", 0); - } else if(isfloat[et]) { - on = syslook("printfloat", 0); - } else if(iscomplex[et]) { - on = syslook("printcomplex", 0); - } else if(et == TBOOL) { - on = syslook("printbool", 0); - } else if(et == TSTRING) { - on = syslook("printstring", 0); - } else { - badtype(OPRINT, n->type, T); - continue; - } - - t = *getinarg(on->type); - if(t != nil) - t = t->type; - if(t != nil) - t = t->type; - - if(!eqtype(t, n->type)) { - n = nod(OCONV, n, N); - n->type = t; - } - - r = nod(OCALL, on, N); - r->list = list1(n); - calls = list(calls, r); - } - - if(op == OPRINTN) - calls = list(calls, mkcall("printnl", T, nil)); - - calls = list(calls, mkcall("printunlock", T, init)); - - typechecklist(calls, Etop); - walkexprlist(calls, init); - - r = nod(OEMPTY, N, N); - typecheck(&r, Etop); - walkexpr(&r, init); - r->ninit = calls; - return r; -} - -Node* -callnew(Type *t) -{ - Node *fn; - - dowidth(t); - fn = syslook("newobject", 1); - argtype(fn, t); - return mkcall1(fn, ptrto(t), nil, typename(t)); -} - -static int -isstack(Node *n) -{ - Node *defn; - - n = outervalue(n); - - // If n is *autotmp and autotmp = &foo, replace n with foo. - // We introduce such temps when initializing struct literals. - if(n->op == OIND && n->left->op == ONAME && strncmp(n->left->sym->name, "autotmp_", 8) == 0) { - defn = n->left->defn; - if(defn != N && defn->op == OAS && defn->right->op == OADDR) - n = defn->right->left; - } - - switch(n->op) { - case OINDREG: - // OINDREG only ends up in walk if it's indirect of SP. - return 1; - - case ONAME: - switch(n->class) { - case PAUTO: - case PPARAM: - case PPARAMOUT: - return 1; - } - break; - } - - return 0; -} - -static int -isglobal(Node *n) -{ - n = outervalue(n); - - switch(n->op) { - case ONAME: - switch(n->class) { - case PEXTERN: - return 1; - } - break; - } - - return 0; -} - -// Do we need a write barrier for the assignment l = r? -int -needwritebarrier(Node *l, Node *r) -{ - if(!use_writebarrier) - return 0; - - if(l == N || isblank(l)) - return 0; - - // No write barrier for write of non-pointers. - dowidth(l->type); - if(!haspointers(l->type)) - return 0; - - // No write barrier for write to stack. - if(isstack(l)) - return 0; - - // No write barrier for implicit or explicit zeroing. - if(r == N || iszero(r)) - return 0; - - // No write barrier for initialization to constant. - if(r->op == OLITERAL) - return 0; - - // No write barrier for storing static (read-only) data. - if(r->op == ONAME && strncmp(r->sym->name, "statictmp_", 10) == 0) - return 0; - - // No write barrier for storing address of stack values, - // which are guaranteed only to be written to the stack. - if(r->op == OADDR && isstack(r->left)) - return 0; - - // No write barrier for storing address of global, which - // is live no matter what. - if(r->op == OADDR && isglobal(r->left)) - return 0; - - // No write barrier for reslice: x = x[0:y] or x = append(x, ...). - // Both are compiled to modify x directly. - // In the case of append, a write barrier may still be needed - // if the underlying array grows, but the append code can - // generate the write barrier directly in that case. - // (It does not yet, but the cost of the write barrier will be - // small compared to the cost of the allocation.) - if(r->reslice) { - switch(r->op) { - case OSLICE: - case OSLICE3: - case OSLICESTR: - case OAPPEND: - break; - default: - dump("bad reslice-l", l); - dump("bad reslice-r", r); - break; - } - return 0; - } - - // Otherwise, be conservative and use write barrier. - return 1; -} - -// TODO(rsc): Perhaps componentgen should run before this. -static Node* -applywritebarrier(Node *n, NodeList **init) -{ - Node *l, *r; - Type *t; - vlong x; - static Bvec *bv; - char name[32]; - - if(n->left && n->right && needwritebarrier(n->left, n->right)) { - if(curfn && curfn->nowritebarrier) - yyerror("write barrier prohibited"); - t = n->left->type; - l = nod(OADDR, n->left, N); - l->etype = 1; // addr does not escape - if(t->width == widthptr) { - n = mkcall1(writebarrierfn("writebarrierptr", t, n->right->type), T, init, - l, n->right); - } else if(t->etype == TSTRING) { - n = mkcall1(writebarrierfn("writebarrierstring", t, n->right->type), T, init, - l, n->right); - } else if(isslice(t)) { - n = mkcall1(writebarrierfn("writebarrierslice", t, n->right->type), T, init, - l, n->right); - } else if(isinter(t)) { - n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init, - l, n->right); - } else if(t->width <= 4*widthptr) { - x = 0; - if(bv == nil) - bv = bvalloc(BitsPerPointer*4); - bvresetall(bv); - twobitwalktype1(t, &x, bv); - // The bvgets are looking for BitsPointer in successive slots. - enum { - PtrBit = 1, - }; - if(BitsPointer != (1<<PtrBit)) - fatal("wrong PtrBit"); - switch(t->width/widthptr) { - default: - fatal("found writebarrierfat for %d-byte object of type %T", (int)t->width, t); - case 2: - snprint(name, sizeof name, "writebarrierfat%d%d", - bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit)); - break; - case 3: - snprint(name, sizeof name, "writebarrierfat%d%d%d", - bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit)); - break; - case 4: - snprint(name, sizeof name, "writebarrierfat%d%d%d%d", - bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit), bvget(bv, 3*BitsPerPointer+PtrBit)); - break; - } - n = mkcall1(writebarrierfn(name, t, n->right->type), T, init, - l, nodnil(), n->right); - } else { - r = n->right; - while(r->op == OCONVNOP) - r = r->left; - r = nod(OADDR, r, N); - r->etype = 1; // addr does not escape - //warnl(n->lineno, "typedmemmove %T %N", t, r); - n = mkcall1(writebarrierfn("typedmemmove", t, r->left->type), T, init, - typename(t), l, r); - } - } - return n; -} - -static Node* -convas(Node *n, NodeList **init) -{ - Type *lt, *rt; - Node *map, *key, *val; - - if(n->op != OAS) - fatal("convas: not OAS %O", n->op); - - n->typecheck = 1; - - if(n->left == N || n->right == N) - goto out; - - lt = n->left->type; - rt = n->right->type; - if(lt == T || rt == T) - goto out; - - if(isblank(n->left)) { - defaultlit(&n->right, T); - goto out; - } - - if(n->left->op == OINDEXMAP) { - map = n->left->left; - key = n->left->right; - val = n->right; - walkexpr(&map, init); - walkexpr(&key, init); - walkexpr(&val, init); - // orderexpr made sure key and val are addressable. - key = nod(OADDR, key, N); - val = nod(OADDR, val, N); - n = mkcall1(mapfn("mapassign1", map->type), T, init, - typename(map->type), map, key, val); - goto out; - } - - if(!eqtype(lt, rt)) { - n->right = assignconv(n->right, lt, "assignment"); - walkexpr(&n->right, init); - } - -out: - ullmancalc(n); - return n; -} - -/* - * from ascompat[te] - * evaluating actual function arguments. - * f(a,b) - * if there is exactly one function expr, - * then it is done first. otherwise must - * make temp variables - */ -static NodeList* -reorder1(NodeList *all) -{ - Node *f, *a, *n; - NodeList *l, *r, *g; - int c, d, t; - - c = 0; // function calls - t = 0; // total parameters - - for(l=all; l; l=l->next) { - n = l->n; - t++; - ullmancalc(n); - if(n->ullman >= UINF) - c++; - } - if(c == 0 || t == 1) - return all; - - g = nil; // fncalls assigned to tempnames - f = N; // last fncall assigned to stack - r = nil; // non fncalls and tempnames assigned to stack - d = 0; - for(l=all; l; l=l->next) { - n = l->n; - if(n->ullman < UINF) { - r = list(r, n); - continue; - } - d++; - if(d == c) { - f = n; - continue; - } - - // make assignment of fncall to tempname - a = temp(n->right->type); - a = nod(OAS, a, n->right); - g = list(g, a); - - // put normal arg assignment on list - // with fncall replaced by tempname - n->right = a->left; - r = list(r, n); - } - - if(f != N) - g = list(g, f); - return concat(g, r); -} - -static void reorder3save(Node**, NodeList*, NodeList*, NodeList**); -static int aliased(Node*, NodeList*, NodeList*); - -/* - * from ascompat[ee] - * a,b = c,d - * simultaneous assignment. there cannot - * be later use of an earlier lvalue. - * - * function calls have been removed. - */ -static NodeList* -reorder3(NodeList *all) -{ - NodeList *list, *early, *mapinit; - Node *l; - - // If a needed expression may be affected by an - // earlier assignment, make an early copy of that - // expression and use the copy instead. - early = nil; - mapinit = nil; - for(list=all; list; list=list->next) { - l = list->n->left; - - // Save subexpressions needed on left side. - // Drill through non-dereferences. - for(;;) { - if(l->op == ODOT || l->op == OPAREN) { - l = l->left; - continue; - } - if(l->op == OINDEX && isfixedarray(l->left->type)) { - reorder3save(&l->right, all, list, &early); - l = l->left; - continue; - } - break; - } - switch(l->op) { - default: - fatal("reorder3 unexpected lvalue %#O", l->op); - case ONAME: - break; - case OINDEX: - case OINDEXMAP: - reorder3save(&l->left, all, list, &early); - reorder3save(&l->right, all, list, &early); - if(l->op == OINDEXMAP) - list->n = convas(list->n, &mapinit); - break; - case OIND: - case ODOTPTR: - reorder3save(&l->left, all, list, &early); - } - - // Save expression on right side. - reorder3save(&list->n->right, all, list, &early); - } - - early = concat(mapinit, early); - return concat(early, all); -} - -static int vmatch2(Node*, Node*); -static int varexpr(Node*); - -/* - * if the evaluation of *np would be affected by the - * assignments in all up to but not including stop, - * copy into a temporary during *early and - * replace *np with that temp. - */ -static void -reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early) -{ - Node *n, *q; - - n = *np; - if(!aliased(n, all, stop)) - return; - - q = temp(n->type); - q = nod(OAS, q, n); - typecheck(&q, Etop); - *early = list(*early, q); - *np = q->left; -} - -/* - * what's the outer value that a write to n affects? - * outer value means containing struct or array. - */ -Node* -outervalue(Node *n) -{ - for(;;) { - if(n->op == OXDOT) - fatal("OXDOT in walk"); - if(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP) { - n = n->left; - continue; - } - if(n->op == OINDEX && isfixedarray(n->left->type)) { - n = n->left; - continue; - } - break; - } - return n; -} - -/* - * Is it possible that the computation of n might be - * affected by writes in as up to but not including stop? - */ -static int -aliased(Node *n, NodeList *all, NodeList *stop) -{ - int memwrite, varwrite; - Node *a; - NodeList *l; - - if(n == N) - return 0; - - // Look for obvious aliasing: a variable being assigned - // during the all list and appearing in n. - // Also record whether there are any writes to main memory. - // Also record whether there are any writes to variables - // whose addresses have been taken. - memwrite = 0; - varwrite = 0; - for(l=all; l!=stop; l=l->next) { - a = outervalue(l->n->left); - if(a->op != ONAME) { - memwrite = 1; - continue; - } - switch(n->class) { - default: - varwrite = 1; - continue; - case PAUTO: - case PPARAM: - case PPARAMOUT: - if(n->addrtaken) { - varwrite = 1; - continue; - } - if(vmatch2(a, n)) { - // Direct hit. - return 1; - } - } - } - - // The variables being written do not appear in n. - // However, n might refer to computed addresses - // that are being written. - - // If no computed addresses are affected by the writes, no aliasing. - if(!memwrite && !varwrite) - return 0; - - // If n does not refer to computed addresses - // (that is, if n only refers to variables whose addresses - // have not been taken), no aliasing. - if(varexpr(n)) - return 0; - - // Otherwise, both the writes and n refer to computed memory addresses. - // Assume that they might conflict. - return 1; -} - -/* - * does the evaluation of n only refer to variables - * whose addresses have not been taken? - * (and no other memory) - */ -static int -varexpr(Node *n) -{ - if(n == N) - return 1; - - switch(n->op) { - case OLITERAL: - return 1; - case ONAME: - switch(n->class) { - case PAUTO: - case PPARAM: - case PPARAMOUT: - if(!n->addrtaken) - return 1; - } - return 0; - - case OADD: - case OSUB: - case OOR: - case OXOR: - case OMUL: - case ODIV: - case OMOD: - case OLSH: - case ORSH: - case OAND: - case OANDNOT: - case OPLUS: - case OMINUS: - case OCOM: - case OPAREN: - case OANDAND: - case OOROR: - case ODOT: // but not ODOTPTR - case OCONV: - case OCONVNOP: - case OCONVIFACE: - case ODOTTYPE: - return varexpr(n->left) && varexpr(n->right); - } - - // Be conservative. - return 0; -} - -/* - * is the name l mentioned in r? - */ -static int -vmatch2(Node *l, Node *r) -{ - NodeList *ll; - - if(r == N) - return 0; - switch(r->op) { - case ONAME: - // match each right given left - return l == r; - case OLITERAL: - return 0; - } - if(vmatch2(l, r->left)) - return 1; - if(vmatch2(l, r->right)) - return 1; - for(ll=r->list; ll; ll=ll->next) - if(vmatch2(l, ll->n)) - return 1; - return 0; -} - -/* - * is any name mentioned in l also mentioned in r? - * called by sinit.c - */ -int -vmatch1(Node *l, Node *r) -{ - NodeList *ll; - - /* - * isolate all left sides - */ - if(l == N || r == N) - return 0; - switch(l->op) { - case ONAME: - switch(l->class) { - case PPARAM: - case PPARAMREF: - case PAUTO: - break; - default: - // assignment to non-stack variable - // must be delayed if right has function calls. - if(r->ullman >= UINF) - return 1; - break; - } - return vmatch2(l, r); - case OLITERAL: - return 0; - } - if(vmatch1(l->left, r)) - return 1; - if(vmatch1(l->right, r)) - return 1; - for(ll=l->list; ll; ll=ll->next) - if(vmatch1(ll->n, r)) - return 1; - return 0; -} - -/* - * walk through argin parameters. - * generate and return code to allocate - * copies of escaped parameters to the heap. - */ -static NodeList* -paramstoheap(Type **argin, int out) -{ - Type *t; - Iter savet; - Node *v, *as; - NodeList *nn; - - nn = nil; - for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { - v = t->nname; - if(v && v->sym && v->sym->name[0] == '~' && v->sym->name[1] == 'r') // unnamed result - v = N; - // For precise stacks, the garbage collector assumes results - // are always live, so zero them always. - if(out) { - // Defer might stop a panic and show the - // return values as they exist at the time of panic. - // Make sure to zero them on entry to the function. - nn = list(nn, nod(OAS, nodarg(t, 1), N)); - } - if(v == N || !(v->class & PHEAP)) - continue; - - // generate allocation & copying code - if(compiling_runtime) - yyerror("%N escapes to heap, not allowed in runtime.", v); - if(v->alloc == nil) - v->alloc = callnew(v->type); - nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); - if((v->class & ~PHEAP) != PPARAMOUT) { - as = nod(OAS, v, v->stackparam); - v->stackparam->typecheck = 1; - typecheck(&as, Etop); - as = applywritebarrier(as, &nn); - nn = list(nn, as); - } - } - return nn; -} - -/* - * walk through argout parameters copying back to stack - */ -static NodeList* -returnsfromheap(Type **argin) -{ - Type *t; - Iter savet; - Node *v; - NodeList *nn; - - nn = nil; - for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { - v = t->nname; - if(v == N || v->class != (PHEAP|PPARAMOUT)) - continue; - nn = list(nn, nod(OAS, v->stackparam, v)); - } - return nn; -} - -/* - * take care of migrating any function in/out args - * between the stack and the heap. adds code to - * curfn's before and after lists. - */ -static void -heapmoves(void) -{ - NodeList *nn; - int32 lno; - - lno = lineno; - lineno = curfn->lineno; - nn = paramstoheap(getthis(curfn->type), 0); - nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); - nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); - curfn->enter = concat(curfn->enter, nn); - lineno = curfn->endlineno; - curfn->exit = returnsfromheap(getoutarg(curfn->type)); - lineno = lno; -} - -static Node* -vmkcall(Node *fn, Type *t, NodeList **init, va_list va) -{ - int i, n; - Node *r; - NodeList *args; - - if(fn->type == T || fn->type->etype != TFUNC) - fatal("mkcall %N %T", fn, fn->type); - - args = nil; - n = fn->type->intuple; - for(i=0; i<n; i++) - args = list(args, va_arg(va, Node*)); - - r = nod(OCALL, fn, N); - r->list = args; - if(fn->type->outtuple > 0) - typecheck(&r, Erv | Efnstruct); - else - typecheck(&r, Etop); - walkexpr(&r, init); - r->type = t; - return r; -} - -Node* -mkcall(char *name, Type *t, NodeList **init, ...) -{ - Node *r; - va_list va; - - va_start(va, init); - r = vmkcall(syslook(name, 0), t, init, va); - va_end(va); - return r; -} - -Node* -mkcall1(Node *fn, Type *t, NodeList **init, ...) -{ - Node *r; - va_list va; - - va_start(va, init); - r = vmkcall(fn, t, init, va); - va_end(va); - return r; -} - -Node* -conv(Node *n, Type *t) -{ - if(eqtype(n->type, t)) - return n; - n = nod(OCONV, n, N); - n->type = t; - typecheck(&n, Erv); - return n; -} - -Node* -chanfn(char *name, int n, Type *t) -{ - Node *fn; - int i; - - if(t->etype != TCHAN) - fatal("chanfn %T", t); - fn = syslook(name, 1); - for(i=0; i<n; i++) - argtype(fn, t->type); - return fn; -} - -static Node* -mapfn(char *name, Type *t) -{ - Node *fn; - - if(t->etype != TMAP) - fatal("mapfn %T", t); - fn = syslook(name, 1); - argtype(fn, t->down); - argtype(fn, t->type); - argtype(fn, t->down); - argtype(fn, t->type); - return fn; -} - -static Node* -mapfndel(char *name, Type *t) -{ - Node *fn; - - if(t->etype != TMAP) - fatal("mapfn %T", t); - fn = syslook(name, 1); - argtype(fn, t->down); - argtype(fn, t->type); - argtype(fn, t->down); - return fn; -} - -static Node* -writebarrierfn(char *name, Type *l, Type *r) -{ - Node *fn; - - fn = syslook(name, 1); - argtype(fn, l); - argtype(fn, r); - return fn; -} - -static Node* -addstr(Node *n, NodeList **init) -{ - Node *r, *cat, *slice, *buf; - NodeList *args, *l; - int c; - vlong sz; - 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); - - buf = nodnil(); - if(n->esc == EscNone) { - sz = 0; - for(l=n->list; l != nil; l=l->next) { - if(n->op == OLITERAL) - sz += n->val.u.sval->len; - } - // Don't allocate the buffer if the result won't fit. - if(sz < tmpstringbufsize) { - // Create temporary buffer for result string on stack. - t = aindex(nodintconst(tmpstringbufsize), types[TUINT8]); - buf = nod(OADDR, temp(t), N); - } - } - - // build list of string arguments - args = list1(buf); - 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->next; // skip buf arg - args = list1(buf); - args = list(args, slice); - slice->esc = EscNone; - } - cat = syslook(namebuf, 1); - r = nod(OCALL, cat, N); - r->list = args; - typecheck(&r, Erv); - walkexpr(&r, init); - r->type = n->type; - - return r; -} - -// expand append(l1, l2...) to -// init { -// s := l1 -// if n := len(l1) + len(l2) - cap(s); n > 0 { -// s = growslice(s, n) -// } -// s = s[:len(l1)+len(l2)] -// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) -// } -// s -// -// l2 is allowed to be a string. -static Node* -appendslice(Node *n, NodeList **init) -{ - NodeList *l; - Node *l1, *l2, *nt, *nif, *fn; - Node *nptr1, *nptr2, *nwid; - Node *s; - - walkexprlistsafe(n->list, init); - - // walkexprlistsafe will leave OINDEX (s[n]) alone if both s - // and n are name or literal, but those may index the slice we're - // modifying here. Fix explicitly. - for(l=n->list; l; l=l->next) - l->n = cheapexpr(l->n, init); - - l1 = n->list->n; - l2 = n->list->next->n; - - s = temp(l1->type); // var s []T - l = nil; - l = list(l, nod(OAS, s, l1)); // s = l1 - - nt = temp(types[TINT]); - nif = nod(OIF, N, N); - // n := len(s) + len(l2) - cap(s) - nif->ninit = list1(nod(OAS, nt, - nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N)))); - nif->ntest = nod(OGT, nt, nodintconst(0)); - // instantiate growslice(Type*, []any, int64) []any - fn = syslook("growslice", 1); - argtype(fn, s->type->type); - argtype(fn, s->type->type); - - // s = growslice(T, s, n) - nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit, - typename(s->type), - s, - conv(nt, types[TINT64])))); - - l = list(l, nif); - - if(haspointers(l1->type->type)) { - // copy(s[len(l1):len(l1)+len(l2)], l2) - nptr1 = nod(OSLICE, s, nod(OKEY, - nod(OLEN, l1, N), - nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)))); - nptr1->etype = 1; - nptr2 = l2; - fn = syslook("typedslicecopy", 1); - argtype(fn, l1->type); - argtype(fn, l2->type); - nt = mkcall1(fn, types[TINT], &l, - typename(l1->type->type), - nptr1, nptr2); - l = list(l, nt); - } else if(flag_race) { - // rely on runtime to instrument copy. - // copy(s[len(l1):len(l1)+len(l2)], l2) - nptr1 = nod(OSLICE, s, nod(OKEY, - nod(OLEN, l1, N), - nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)))); - nptr1->etype = 1; - nptr2 = l2; - if(l2->type->etype == TSTRING) - fn = syslook("slicestringcopy", 1); - else - fn = syslook("slicecopy", 1); - argtype(fn, l1->type); - argtype(fn, l2->type); - nt = mkcall1(fn, types[TINT], &l, - nptr1, nptr2, - nodintconst(s->type->type->width)); - l = list(l, nt); - } else { - // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) - nptr1 = nod(OINDEX, s, nod(OLEN, l1, N)); - nptr1->bounded = 1; - nptr1 = nod(OADDR, nptr1, N); - - nptr2 = nod(OSPTR, l2, N); - - fn = syslook("memmove", 1); - argtype(fn, s->type->type); // 1 old []any - argtype(fn, s->type->type); // 2 ret []any - - nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l); - nwid = nod(OMUL, nwid, nodintconst(s->type->type->width)); - nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid); - l = list(l, nt); - } - - // s = s[:len(l1)+len(l2)] - nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)); - nt = nod(OSLICE, s, nod(OKEY, N, nt)); - nt->etype = 1; - l = list(l, nod(OAS, s, nt)); - - typechecklist(l, Etop); - walkstmtlist(l); - *init = concat(*init, l); - return s; -} - -// expand append(src, a [, b]* ) to -// -// init { -// s := src -// const argc = len(args) - 1 -// if cap(s) - len(s) < argc { -// s = growslice(s, argc) -// } -// n := len(s) -// s = s[:n+argc] -// s[n] = a -// s[n+1] = b -// ... -// } -// s -static Node* -append(Node *n, NodeList **init) -{ - NodeList *l, *a; - Node *nsrc, *ns, *nn, *na, *nx, *fn; - int argc; - - walkexprlistsafe(n->list, init); - - // walkexprlistsafe will leave OINDEX (s[n]) alone if both s - // and n are name or literal, but those may index the slice we're - // modifying here. Fix explicitly. - for(l=n->list; l; l=l->next) - l->n = cheapexpr(l->n, init); - - nsrc = n->list->n; - - // Resolve slice type of multi-valued return. - if(istype(nsrc->type, TSTRUCT)) - nsrc->type = nsrc->type->type->type; - argc = count(n->list) - 1; - if (argc < 1) { - return nsrc; - } - - l = nil; - - ns = temp(nsrc->type); - l = list(l, nod(OAS, ns, nsrc)); // s = src - - na = nodintconst(argc); // const argc - nx = nod(OIF, N, N); // if cap(s) - len(s) < argc - nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na); - - fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T) - argtype(fn, ns->type->type); // 1 old []any - argtype(fn, ns->type->type); // 2 ret []any - - nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit, - typename(ns->type), - ns, - conv(na, types[TINT64])))); - l = list(l, nx); - - nn = temp(types[TINT]); - l = list(l, nod(OAS, nn, nod(OLEN, ns, N))); // n = len(s) - - nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na))); // ...s[:n+argc] - nx->etype = 1; - l = list(l, nod(OAS, ns, nx)); // s = s[:n+argc] - - for (a = n->list->next; a != nil; a = a->next) { - nx = nod(OINDEX, ns, nn); // s[n] ... - nx->bounded = 1; - l = list(l, nod(OAS, nx, a->n)); // s[n] = arg - if (a->next != nil) - l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1)))); // n = n + 1 - } - - typechecklist(l, Etop); - walkstmtlist(l); - *init = concat(*init, l); - return ns; -} - -// Lower copy(a, b) to a memmove call or a runtime call. -// -// init { -// n := len(a) -// if n > len(b) { n = len(b) } -// memmove(a.ptr, b.ptr, n*sizeof(elem(a))) -// } -// n; -// -// Also works if b is a string. -// -static Node* -copyany(Node *n, NodeList **init, int runtimecall) -{ - Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn; - NodeList *l; - - if(haspointers(n->left->type->type)) { - fn = writebarrierfn("typedslicecopy", n->left->type, n->right->type); - return mkcall1(fn, n->type, init, typename(n->left->type->type), n->left, n->right); - } - - if(runtimecall) { - if(n->right->type->etype == TSTRING) - fn = syslook("slicestringcopy", 1); - else - fn = syslook("slicecopy", 1); - argtype(fn, n->left->type); - argtype(fn, n->right->type); - return mkcall1(fn, n->type, init, - n->left, n->right, - nodintconst(n->left->type->type->width)); - } - walkexpr(&n->left, init); - walkexpr(&n->right, init); - nl = temp(n->left->type); - nr = temp(n->right->type); - l = nil; - l = list(l, nod(OAS, nl, n->left)); - l = list(l, nod(OAS, nr, n->right)); - - nfrm = nod(OSPTR, nr, N); - nto = nod(OSPTR, nl, N); - - nlen = temp(types[TINT]); - // n = len(to) - l = list(l, nod(OAS, nlen, nod(OLEN, nl, N))); - // if n > len(frm) { n = len(frm) } - nif = nod(OIF, N, N); - nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N)); - nif->nbody = list(nif->nbody, - nod(OAS, nlen, nod(OLEN, nr, N))); - l = list(l, nif); - - // Call memmove. - fn = syslook("memmove", 1); - argtype(fn, nl->type->type); - argtype(fn, nl->type->type); - nwid = temp(types[TUINTPTR]); - l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR]))); - nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width)); - l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid)); - - typechecklist(l, Etop); - walkstmtlist(l); - *init = concat(*init, l); - return nlen; -} - -// Generate frontend part for OSLICE[3][ARR|STR] -// -static Node* -sliceany(Node* n, NodeList **init) -{ - int bounded, slice3; - Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2; - int64 lbv, hbv, cbv, bv, w; - Type *bt; - -// print("before sliceany: %+N\n", n); - - src = n->left; - lb = n->right->left; - slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR; - if(slice3) { - hb = n->right->right->left; - cb = n->right->right->right; - } else { - hb = n->right->right; - cb = N; - } - - bounded = n->etype; - - if(n->op == OSLICESTR) - bound = nod(OLEN, src, N); - else - bound = nod(OCAP, src, N); - - typecheck(&bound, Erv); - walkexpr(&bound, init); // if src is an array, bound will be a const now. - - // static checks if possible - bv = 1LL<<50; - if(isconst(bound, CTINT)) { - if(!smallintconst(bound)) - yyerror("array len too large"); - else - bv = mpgetfix(bound->val.u.xval); - } - - if(isconst(cb, CTINT)) { - cbv = mpgetfix(cb->val.u.xval); - if(cbv < 0 || cbv > bv) - yyerror("slice index out of bounds"); - } - if(isconst(hb, CTINT)) { - hbv = mpgetfix(hb->val.u.xval); - if(hbv < 0 || hbv > bv) - yyerror("slice index out of bounds"); - } - if(isconst(lb, CTINT)) { - lbv = mpgetfix(lb->val.u.xval); - if(lbv < 0 || lbv > bv) { - yyerror("slice index out of bounds"); - lbv = -1; - } - if(lbv == 0) - lb = N; - } - - // Checking src[lb:hb:cb] or src[lb:hb]. - // if chk0 || chk1 || chk2 { panicslice() } - chk = N; - chk0 = N; // cap(src) < cb - chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb] - chk2 = N; // hb < lb - - // All comparisons are unsigned to avoid testing < 0. - bt = types[simtype[TUINT]]; - if(cb != N && cb->type->width > 4) - bt = types[TUINT64]; - if(hb != N && hb->type->width > 4) - bt = types[TUINT64]; - if(lb != N && lb->type->width > 4) - bt = types[TUINT64]; - - bound = cheapexpr(conv(bound, bt), init); - - if(cb != N) { - cb = cheapexpr(conv(cb, bt), init); - if(!bounded) - chk0 = nod(OLT, bound, cb); - } else if(slice3) { - // When we figure out what this means, implement it. - fatal("slice3 with cb == N"); // rejected by parser - } - - if(hb != N) { - hb = cheapexpr(conv(hb, bt), init); - if(!bounded) { - if(cb != N) - chk1 = nod(OLT, cb, hb); - else - chk1 = nod(OLT, bound, hb); - } - } else if(slice3) { - // When we figure out what this means, implement it. - fatal("slice3 with hb == N"); // rejected by parser - } else if(n->op == OSLICEARR) { - hb = bound; - } else { - hb = nod(OLEN, src, N); - typecheck(&hb, Erv); - walkexpr(&hb, init); - hb = cheapexpr(conv(hb, bt), init); - } - - if(lb != N) { - lb = cheapexpr(conv(lb, bt), init); - if(!bounded) - chk2 = nod(OLT, hb, lb); - } - - if(chk0 != N || chk1 != N || chk2 != N) { - chk = nod(OIF, N, N); - chk->nbody = list1(mkcall("panicslice", T, init)); - chk->likely = -1; - if(chk0 != N) - chk->ntest = chk0; - if(chk1 != N) { - if(chk->ntest == N) - chk->ntest = chk1; - else - chk->ntest = nod(OOROR, chk->ntest, chk1); - } - if(chk2 != N) { - if(chk->ntest == N) - chk->ntest = chk2; - else - chk->ntest = nod(OOROR, chk->ntest, chk2); - } - typecheck(&chk, Etop); - walkstmt(&chk); - *init = concat(*init, chk->ninit); - chk->ninit = nil; - *init = list(*init, chk); - } - - // prepare new cap, len and offs for backend cgen_slice - // cap = bound [ - lo ] - n->right = N; - n->list = nil; - if(!slice3) - cb = bound; - if(lb == N) - bound = conv(cb, types[simtype[TUINT]]); - else - bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); - typecheck(&bound, Erv); - walkexpr(&bound, init); - n->list = list(n->list, bound); - - // len = hi [ - lo] - if(lb == N) - hb = conv(hb, types[simtype[TUINT]]); - else - hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]])); - typecheck(&hb, Erv); - walkexpr(&hb, init); - n->list = list(n->list, hb); - - // offs = [width *] lo, but omit if zero - if(lb != N) { - if(n->op == OSLICESTR) - w = 1; - else - w = n->type->type->width; - lb = conv(lb, types[TUINTPTR]); - if(w > 1) - lb = nod(OMUL, nodintconst(w), lb); - typecheck(&lb, Erv); - walkexpr(&lb, init); - n->list = list(n->list, lb); - } - -// print("after sliceany: %+N\n", n); - - return n; -} - -static Node* -eqfor(Type *t, int *needsize) -{ - int a; - Node *n; - Node *ntype; - Sym *sym; - - // Should only arrive here with large memory or - // a struct/array containing a non-memory field/element. - // Small memory is handled inline, and single non-memory - // is handled during type check (OCMPSTR etc). - a = algtype1(t, nil); - if(a != AMEM && a != -1) - fatal("eqfor %T", t); - - if(a == AMEM) { - n = syslook("memequal", 1); - argtype(n, t); - argtype(n, t); - *needsize = 1; - return n; - } - - sym = typesymprefix(".eq", t); - n = newname(sym); - n->class = PFUNC; - ntype = nod(OTFUNC, N, N); - ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); - ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); - ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, N, typenod(types[TBOOL]))); - typecheck(&ntype, Etype); - n->type = ntype->type; - *needsize = 0; - return n; -} - -static int -countfield(Type *t) -{ - Type *t1; - int n; - - n = 0; - for(t1=t->type; t1!=T; t1=t1->down) - n++; - return n; -} - -static void -walkcompare(Node **np, NodeList **init) -{ - Node *n, *l, *r, *call, *a, *li, *ri, *expr, *cmpl, *cmpr; - Node *x, *ok; - int andor, i, needsize; - Type *t, *t1; - - n = *np; - - // Given interface value l and concrete value r, rewrite - // l == r - // to - // x, ok := l.(type(r)); ok && x == r - // Handle != similarly. - // This avoids the allocation that would be required - // to convert r to l for comparison. - l = N; - r = N; - if(isinter(n->left->type) && !isinter(n->right->type)) { - l = n->left; - r = n->right; - } else if(!isinter(n->left->type) && isinter(n->right->type)) { - l = n->right; - r = n->left; - } - if(l != N) { - x = temp(r->type); - ok = temp(types[TBOOL]); - - // l.(type(r)) - a = nod(ODOTTYPE, l, N); - a->type = r->type; - - // x, ok := l.(type(r)) - expr = nod(OAS2, N, N); - expr->list = list1(x); - expr->list = list(expr->list, ok); - expr->rlist = list1(a); - typecheck(&expr, Etop); - walkexpr(&expr, init); - - if(n->op == OEQ) - r = nod(OANDAND, ok, nod(OEQ, x, r)); - else - r = nod(OOROR, nod(ONOT, ok, N), nod(ONE, x, r)); - *init = list(*init, expr); - goto ret; - } - - // Must be comparison of array or struct. - // Otherwise back end handles it. - t = n->left->type; - switch(t->etype) { - default: - return; - case TARRAY: - if(isslice(t)) - return; - break; - case TSTRUCT: - break; - } - - cmpl = n->left; - while(cmpl != N && cmpl->op == OCONVNOP) - cmpl = cmpl->left; - cmpr = n->right; - while(cmpr != N && cmpr->op == OCONVNOP) - cmpr = cmpr->left; - - if(!islvalue(cmpl) || !islvalue(cmpr)) { - fatal("arguments of comparison must be lvalues - %N %N", cmpl, cmpr); - } - - l = temp(ptrto(t)); - a = nod(OAS, l, nod(OADDR, cmpl, N)); - a->right->etype = 1; // addr does not escape - typecheck(&a, Etop); - *init = list(*init, a); - - r = temp(ptrto(t)); - a = nod(OAS, r, nod(OADDR, cmpr, N)); - a->right->etype = 1; // addr does not escape - typecheck(&a, Etop); - *init = list(*init, a); - - expr = N; - andor = OANDAND; - if(n->op == ONE) - andor = OOROR; - - if(t->etype == TARRAY && - t->bound <= 4 && - issimple[t->type->etype]) { - // Four or fewer elements of a basic type. - // Unroll comparisons. - for(i=0; i<t->bound; i++) { - li = nod(OINDEX, l, nodintconst(i)); - ri = nod(OINDEX, r, nodintconst(i)); - a = nod(n->op, li, ri); - if(expr == N) - expr = a; - else - expr = nod(andor, expr, a); - } - if(expr == N) - expr = nodbool(n->op == OEQ); - r = expr; - goto ret; - } - - if(t->etype == TSTRUCT && countfield(t) <= 4) { - // Struct of four or fewer fields. - // Inline comparisons. - for(t1=t->type; t1; t1=t1->down) { - if(isblanksym(t1->sym)) - continue; - li = nod(OXDOT, l, newname(t1->sym)); - ri = nod(OXDOT, r, newname(t1->sym)); - a = nod(n->op, li, ri); - if(expr == N) - expr = a; - else - expr = nod(andor, expr, a); - } - if(expr == N) - expr = nodbool(n->op == OEQ); - r = expr; - goto ret; - } - - // Chose not to inline. Call equality function directly. - call = nod(OCALL, eqfor(t, &needsize), N); - call->list = list(call->list, l); - call->list = list(call->list, r); - if(needsize) - call->list = list(call->list, nodintconst(t->width)); - r = call; - if(n->op != OEQ) - r = nod(ONOT, r, N); - goto ret; - -ret: - typecheck(&r, Erv); - walkexpr(&r, init); - if(r->type != n->type) { - r = nod(OCONVNOP, r, N); - r->type = n->type; - r->typecheck = 1; - } - *np = r; - return; -} - -static int -samecheap(Node *a, Node *b) -{ - Node *ar, *br; - while(a != N && b != N && a->op == b->op) { - switch(a->op) { - default: - return 0; - case ONAME: - return a == b; - case ODOT: - case ODOTPTR: - ar = a->right; - br = b->right; - if(ar->op != ONAME || br->op != ONAME || ar->sym != br->sym) - return 0; - break; - case OINDEX: - ar = a->right; - br = b->right; - if(!isconst(ar, CTINT) || !isconst(br, CTINT) || mpcmpfixfix(ar->val.u.xval, br->val.u.xval) != 0) - return 0; - break; - } - a = a->left; - b = b->left; - } - return 0; -} - -static void -walkrotate(Node **np) -{ - int w, sl, sr, s; - Node *l, *r; - Node *n; - - if(thearch.thechar == '9') - return; - - n = *np; - - // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value. - l = n->left; - r = n->right; - if((n->op != OOR && n->op != OXOR) || - (l->op != OLSH && l->op != ORSH) || - (r->op != OLSH && r->op != ORSH) || - n->type == T || issigned[n->type->etype] || - l->op == r->op) { - return; - } - - // Want same, side effect-free expression on lhs of both shifts. - if(!samecheap(l->left, r->left)) - return; - - // Constants adding to width? - w = l->type->width * 8; - if(smallintconst(l->right) && smallintconst(r->right)) { - if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w) - goto yes; - return; - } - - // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31). - return; - -yes: - // Rewrite left shift half to left rotate. - if(l->op == OLSH) - n = l; - else - n = r; - n->op = OLROT; - - // Remove rotate 0 and rotate w. - s = mpgetfix(n->right->val.u.xval); - if(s == 0 || s == w) - n = n->left; - - *np = n; - return; -} - -/* - * walkmul rewrites integer multiplication by powers of two as shifts. - */ -static void -walkmul(Node **np, NodeList **init) -{ - Node *n, *nl, *nr; - int pow, neg, w; - - n = *np; - if(!isint[n->type->etype]) - return; - - if(n->right->op == OLITERAL) { - nl = n->left; - nr = n->right; - } else if(n->left->op == OLITERAL) { - nl = n->right; - nr = n->left; - } else - return; - - neg = 0; - - // x*0 is 0 (and side effects of x). - if(mpgetfix(nr->val.u.xval) == 0) { - cheapexpr(nl, init); - nodconst(n, n->type, 0); - goto ret; - } - - // nr is a constant. - pow = powtwo(nr); - if(pow < 0) - return; - if(pow >= 1000) { - // negative power of 2, like -16 - neg = 1; - pow -= 1000; - } - - w = nl->type->width*8; - if(pow+1 >= w)// too big, shouldn't happen - return; - - nl = cheapexpr(nl, init); - - if(pow == 0) { - // x*1 is x - n = nl; - goto ret; - } - - n = nod(OLSH, nl, nodintconst(pow)); - -ret: - if(neg) - n = nod(OMINUS, n, N); - - typecheck(&n, Erv); - walkexpr(&n, init); - *np = n; -} - -/* - * walkdiv rewrites division by a constant as less expensive - * operations. - */ -static void -walkdiv(Node **np, NodeList **init) -{ - Node *n, *nl, *nr, *nc; - Node *n1, *n2, *n3, *n4; - int pow; // if >= 0, nr is 1<<pow - int s; // 1 if nr is negative. - int w; - Type *twide; - Magic m; - - // TODO(minux) - if(thearch.thechar == '9') - return; - - n = *np; - if(n->right->op != OLITERAL) - return; - // nr is a constant. - nl = cheapexpr(n->left, init); - nr = n->right; - - // special cases of mod/div - // by a constant - w = nl->type->width*8; - s = 0; - pow = powtwo(nr); - if(pow >= 1000) { - // negative power of 2 - s = 1; - pow -= 1000; - } - - if(pow+1 >= w) { - // divisor too large. - return; - } - if(pow < 0) { - goto divbymul; - } - - switch(pow) { - case 0: - if(n->op == OMOD) { - // nl % 1 is zero. - nodconst(n, n->type, 0); - } else if(s) { - // divide by -1 - n->op = OMINUS; - n->right = N; - } else { - // divide by 1 - n = nl; - } - break; - default: - if(issigned[n->type->etype]) { - if(n->op == OMOD) { - // signed modulo 2^pow is like ANDing - // with the last pow bits, but if nl < 0, - // nl & (2^pow-1) is (nl+1)%2^pow - 1. - nc = nod(OXXX, N, N); - nodconst(nc, types[simtype[TUINT]], w-1); - n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. - if(pow == 1) { - typecheck(&n1, Erv); - n1 = cheapexpr(n1, init); - // n = (nl+ε)&1 -ε where ε=1 iff nl<0. - n2 = nod(OSUB, nl, n1); - nc = nod(OXXX, N, N); - nodconst(nc, nl->type, 1); - n3 = nod(OAND, n2, nc); - n = nod(OADD, n3, n1); - } else { - // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. - nc = nod(OXXX, N, N); - nodconst(nc, nl->type, (1LL<<pow)-1); - n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0. - typecheck(&n2, Erv); - n2 = cheapexpr(n2, init); - - n3 = nod(OADD, nl, n2); - n4 = nod(OAND, n3, nc); - n = nod(OSUB, n4, n2); - } - break; - } else { - // arithmetic right shift does not give the correct rounding. - // if nl >= 0, nl >> n == nl / nr - // if nl < 0, we want to add 2^n-1 first. - nc = nod(OXXX, N, N); - nodconst(nc, types[simtype[TUINT]], w-1); - n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. - if(pow == 1) { - // nl+1 is nl-(-1) - n->left = nod(OSUB, nl, n1); - } else { - // Do a logical right right on -1 to keep pow bits. - nc = nod(OXXX, N, N); - nodconst(nc, types[simtype[TUINT]], w-pow); - n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc); - n->left = nod(OADD, nl, conv(n2, nl->type)); - } - // n = (nl + 2^pow-1) >> pow - n->op = ORSH; - nc = nod(OXXX, N, N); - nodconst(nc, types[simtype[TUINT]], pow); - n->right = nc; - n->typecheck = 0; - } - if(s) - n = nod(OMINUS, n, N); - break; - } - nc = nod(OXXX, N, N); - if(n->op == OMOD) { - // n = nl & (nr-1) - n->op = OAND; - nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1); - } else { - // n = nl >> pow - n->op = ORSH; - nodconst(nc, types[simtype[TUINT]], pow); - } - n->typecheck = 0; - n->right = nc; - break; - } - goto ret; - -divbymul: - // try to do division by multiply by (2^w)/d - // see hacker's delight chapter 10 - // TODO: support 64-bit magic multiply here. - m.w = w; - if(issigned[nl->type->etype]) { - m.sd = mpgetfix(nr->val.u.xval); - smagic(&m); - } else { - m.ud = mpgetfix(nr->val.u.xval); - umagic(&m); - } - if(m.bad) - return; - - // We have a quick division method so use it - // for modulo too. - if(n->op == OMOD) - goto longmod; - - switch(simtype[nl->type->etype]) { - default: - return; - - case TUINT8: - case TUINT16: - case TUINT32: - // n1 = nl * magic >> w (HMUL) - nc = nod(OXXX, N, N); - nodconst(nc, nl->type, m.um); - n1 = nod(OMUL, nl, nc); - typecheck(&n1, Erv); - n1->op = OHMUL; - if(m.ua) { - // Select a Go type with (at least) twice the width. - switch(simtype[nl->type->etype]) { - default: - return; - case TUINT8: - case TUINT16: - twide = types[TUINT32]; - break; - case TUINT32: - twide = types[TUINT64]; - break; - case TINT8: - case TINT16: - twide = types[TINT32]; - break; - case TINT32: - twide = types[TINT64]; - break; - } - - // add numerator (might overflow). - // n2 = (n1 + nl) - n2 = nod(OADD, conv(n1, twide), conv(nl, twide)); - - // shift by m.s - nc = nod(OXXX, N, N); - nodconst(nc, types[TUINT], m.s); - n = conv(nod(ORSH, n2, nc), nl->type); - } else { - // n = n1 >> m.s - nc = nod(OXXX, N, N); - nodconst(nc, types[TUINT], m.s); - n = nod(ORSH, n1, nc); - } - break; - - case TINT8: - case TINT16: - case TINT32: - // n1 = nl * magic >> w - nc = nod(OXXX, N, N); - nodconst(nc, nl->type, m.sm); - n1 = nod(OMUL, nl, nc); - typecheck(&n1, Erv); - n1->op = OHMUL; - if(m.sm < 0) { - // add the numerator. - n1 = nod(OADD, n1, nl); - } - // shift by m.s - nc = nod(OXXX, N, N); - nodconst(nc, types[TUINT], m.s); - n2 = conv(nod(ORSH, n1, nc), nl->type); - // add 1 iff n1 is negative. - nc = nod(OXXX, N, N); - nodconst(nc, types[TUINT], w-1); - n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative. - n = nod(OSUB, n2, n3); - // apply sign. - if(m.sd < 0) - n = nod(OMINUS, n, N); - break; - } - goto ret; - -longmod: - // rewrite as A%B = A - (A/B*B). - n1 = nod(ODIV, nl, nr); - n2 = nod(OMUL, n1, nr); - n = nod(OSUB, nl, n2); - goto ret; - -ret: - typecheck(&n, Erv); - walkexpr(&n, init); - *np = n; -} - -// return 1 if integer n must be in range [0, max), 0 otherwise -static int -bounded(Node *n, int64 max) -{ - int64 v; - int32 bits; - int sign; - - if(n->type == T || !isint[n->type->etype]) - return 0; - - sign = issigned[n->type->etype]; - bits = 8*n->type->width; - - if(smallintconst(n)) { - v = mpgetfix(n->val.u.xval); - return 0 <= v && v < max; - } - - switch(n->op) { - case OAND: - v = -1; - if(smallintconst(n->left)) { - v = mpgetfix(n->left->val.u.xval); - } else if(smallintconst(n->right)) { - v = mpgetfix(n->right->val.u.xval); - } - if(0 <= v && v < max) - return 1; - break; - - case OMOD: - if(!sign && smallintconst(n->right)) { - v = mpgetfix(n->right->val.u.xval); - if(0 <= v && v <= max) - return 1; - } - break; - - case ODIV: - if(!sign && smallintconst(n->right)) { - v = mpgetfix(n->right->val.u.xval); - while(bits > 0 && v >= 2) { - bits--; - v >>= 1; - } - } - break; - - case ORSH: - if(!sign && smallintconst(n->right)) { - v = mpgetfix(n->right->val.u.xval); - if(v > bits) - return 1; - bits -= v; - } - break; - } - - if(!sign && bits <= 62 && (1LL<<bits) <= max) - return 1; - - return 0; -} - -void -usefield(Node *n) -{ - Type *field, *l; - - if(!fieldtrack_enabled) - return; - - switch(n->op) { - default: - fatal("usefield %O", n->op); - case ODOT: - case ODOTPTR: - break; - } - - field = n->paramfld; - if(field == T) - fatal("usefield %T %S without paramfld", n->left->type, n->right->sym); - if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil) - return; - - // dedup on list - if(field->lastfn == curfn) - return; - field->lastfn = curfn; - field->outer = n->left->type; - if(isptr[field->outer->etype]) - field->outer = field->outer->type; - if(field->outer->sym == S) - yyerror("tracked field must be in named struct type"); - if(!exportname(field->sym->name)) - yyerror("tracked field must be exported (upper case)"); - - l = typ(0); - l->type = field; - l->down = curfn->paramfld; - curfn->paramfld = l; -} - -static int -candiscardlist(NodeList *l) -{ - for(; l; l=l->next) - if(!candiscard(l->n)) - return 0; - return 1; -} - -int -candiscard(Node *n) -{ - if(n == N) - return 1; - - switch(n->op) { - default: - return 0; - - case ONAME: - case ONONAME: - case OTYPE: - case OPACK: - case OLITERAL: - case OADD: - case OSUB: - case OOR: - case OXOR: - case OADDSTR: - case OADDR: - case OANDAND: - case OARRAYBYTESTR: - case OARRAYRUNESTR: - case OSTRARRAYBYTE: - case OSTRARRAYRUNE: - case OCAP: - case OCMPIFACE: - case OCMPSTR: - case OCOMPLIT: - case OMAPLIT: - case OSTRUCTLIT: - case OARRAYLIT: - case OPTRLIT: - case OCONV: - case OCONVIFACE: - case OCONVNOP: - case ODOT: - case OEQ: - case ONE: - case OLT: - case OLE: - case OGT: - case OGE: - case OKEY: - case OLEN: - case OMUL: - case OLSH: - case ORSH: - case OAND: - case OANDNOT: - case ONEW: - case ONOT: - case OCOM: - case OPLUS: - case OMINUS: - case OOROR: - case OPAREN: - case ORUNESTR: - case OREAL: - case OIMAG: - case OCOMPLEX: - // Discardable as long as the subpieces are. - break; - - case ODIV: - case OMOD: - // Discardable as long as we know it's not division by zero. - if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0) - break; - if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0) - break; - return 0; - - case OMAKECHAN: - case OMAKEMAP: - // Discardable as long as we know it won't fail because of a bad size. - if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0) - break; - return 0; - - case OMAKESLICE: - // Difficult to tell what sizes are okay. - return 0; - } - - if(!candiscard(n->left) || - !candiscard(n->right) || - !candiscard(n->ntest) || - !candiscard(n->nincr) || - !candiscardlist(n->ninit) || - !candiscardlist(n->nbody) || - !candiscardlist(n->nelse) || - !candiscardlist(n->list) || - !candiscardlist(n->rlist)) { - return 0; - } - - return 1; -} - -// rewrite -// print(x, y, z) -// into -// func(a1, a2, a3) { -// print(a1, a2, a3) -// }(x, y, z) -// and same for println. -static void -walkprintfunc(Node **np, NodeList **init) -{ - Node *n; - Node *a, *fn, *t, *oldfn; - NodeList *l, *printargs; - int num; - char buf[100]; - static int prgen; - - n = *np; - - if(n->ninit != nil) { - walkstmtlist(n->ninit); - *init = concat(*init, n->ninit); - n->ninit = nil; - } - - t = nod(OTFUNC, N, N); - num = 0; - printargs = nil; - for(l=n->list; l != nil; l=l->next) { - snprint(buf, sizeof buf, "a%d", num++); - a = nod(ODCLFIELD, newname(lookup(buf)), typenod(l->n->type)); - t->list = list(t->list, a); - printargs = list(printargs, a->left); - } - - fn = nod(ODCLFUNC, N, N); - snprint(buf, sizeof buf, "print·%d", ++prgen); - fn->nname = newname(lookup(buf)); - fn->nname->defn = fn; - fn->nname->ntype = t; - declare(fn->nname, PFUNC); - - oldfn = curfn; - curfn = nil; - funchdr(fn); - - a = nod(n->op, N, N); - a->list = printargs; - typecheck(&a, Etop); - walkstmt(&a); - - fn->nbody = list1(a); - - funcbody(fn); - - typecheck(&fn, Etop); - typechecklist(fn->nbody, Etop); - xtop = list(xtop, fn); - curfn = oldfn; - - a = nod(OCALL, N, N); - a->left = fn->nname; - a->list = n->list; - typecheck(&a, Etop); - walkexpr(&a, init); - *np = a; -} |