| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include <u.h> |
| #include <libc.h> |
| #include "go.h" |
| #include "../ld/textflag.h" |
| |
| static Node* walkprint(Node*, NodeList**); |
| static Node* writebarrierfn(char*, Type*, Type*); |
| static Node* applywritebarrier(Node*, NodeList**); |
| static Node* mapfn(char*, Type*); |
| static Node* mapfndel(char*, Type*); |
| static Node* ascompatee1(int, Node*, Node*, NodeList**); |
| static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); |
| static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); |
| static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**); |
| static Node* convas(Node*, NodeList**); |
| static void heapmoves(void); |
| static NodeList* paramstoheap(Type **argin, int out); |
| static NodeList* reorder1(NodeList*); |
| static NodeList* reorder3(NodeList*); |
| static Node* addstr(Node*, NodeList**); |
| static Node* appendslice(Node*, NodeList**); |
| static Node* append(Node*, NodeList**); |
| static Node* copyany(Node*, NodeList**, int); |
| static Node* sliceany(Node*, NodeList**); |
| static void walkcompare(Node**, NodeList**); |
| static void walkrotate(Node**); |
| static void walkmul(Node**, NodeList**); |
| static void walkdiv(Node**, NodeList**); |
| static int bounded(Node*, int64); |
| static Mpint mpzero; |
| static void walkprintfunc(Node**, NodeList**); |
| |
| void |
| walk(Node *fn) |
| { |
| char s[50]; |
| NodeList *l; |
| int lno; |
| |
| curfn = fn; |
| |
| if(debug['W']) { |
| snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym); |
| dumplist(s, curfn->nbody); |
| } |
| |
| lno = lineno; |
| |
| // Final typecheck for any unused variables. |
| // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below. |
| for(l=fn->dcl; l; l=l->next) |
| if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO) |
| typecheck(&l->n, Erv | Easgn); |
| |
| // Propagate the used flag for typeswitch variables up to the NONAME in it's definition. |
| for(l=fn->dcl; l; l=l->next) |
| if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used) |
| l->n->defn->left->used++; |
| |
| for(l=fn->dcl; l; l=l->next) { |
| if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used) |
| continue; |
| if(l->n->defn && l->n->defn->op == OTYPESW) { |
| if(l->n->defn->left->used) |
| continue; |
| lineno = l->n->defn->left->lineno; |
| yyerror("%S declared and not used", l->n->sym); |
| l->n->defn->left->used = 1; // suppress repeats |
| } else { |
| lineno = l->n->lineno; |
| yyerror("%S declared and not used", l->n->sym); |
| } |
| } |
| |
| lineno = lno; |
| if(nerrors != 0) |
| return; |
| walkstmtlist(curfn->nbody); |
| if(debug['W']) { |
| snprint(s, sizeof(s), "after walk %S", curfn->nname->sym); |
| dumplist(s, curfn->nbody); |
| } |
| heapmoves(); |
| if(debug['W'] && curfn->enter != nil) { |
| snprint(s, sizeof(s), "enter %S", curfn->nname->sym); |
| dumplist(s, curfn->enter); |
| } |
| } |
| |
| |
| void |
| walkstmtlist(NodeList *l) |
| { |
| for(; l; l=l->next) |
| walkstmt(&l->n); |
| } |
| |
| static int |
| samelist(NodeList *a, NodeList *b) |
| { |
| for(; a && b; a=a->next, b=b->next) |
| if(a->n != b->n) |
| return 0; |
| return a == b; |
| } |
| |
| static int |
| paramoutheap(Node *fn) |
| { |
| NodeList *l; |
| |
| for(l=fn->dcl; l; l=l->next) { |
| switch(l->n->class) { |
| case PPARAMOUT: |
| case PPARAMOUT|PHEAP: |
| return l->n->addrtaken; |
| case PAUTO: |
| case PAUTO|PHEAP: |
| // stop early - parameters are over |
| return 0; |
| } |
| } |
| return 0; |
| } |
| |
| void |
| walkstmt(Node **np) |
| { |
| NodeList *init; |
| NodeList *ll, *rl; |
| int cl; |
| Node *n, *f; |
| |
| n = *np; |
| if(n == N) |
| return; |
| if(n->dodata == 2) // don't walk, generated by anylit. |
| return; |
| |
| setlineno(n); |
| |
| walkstmtlist(n->ninit); |
| |
| switch(n->op) { |
| default: |
| if(n->op == ONAME) |
| yyerror("%S is not a top level statement", n->sym); |
| else |
| yyerror("%O is not a top level statement", n->op); |
| dump("nottop", n); |
| break; |
| |
| case OAS: |
| case OASOP: |
| case OAS2: |
| case OAS2DOTTYPE: |
| case OAS2RECV: |
| case OAS2FUNC: |
| case OAS2MAPR: |
| case OCLOSE: |
| case OCOPY: |
| case OCALLMETH: |
| case OCALLINTER: |
| case OCALL: |
| case OCALLFUNC: |
| case ODELETE: |
| case OSEND: |
| case OPRINT: |
| case OPRINTN: |
| case OPANIC: |
| case OEMPTY: |
| case ORECOVER: |
| if(n->typecheck == 0) |
| fatal("missing typecheck: %+N", n); |
| init = n->ninit; |
| n->ninit = nil; |
| walkexpr(&n, &init); |
| addinit(&n, init); |
| if((*np)->op == OCOPY && n->op == OCONVNOP) |
| n->op = OEMPTY; // don't leave plain values as statements. |
| break; |
| |
| case ORECV: |
| // special case for a receive where we throw away |
| // the value received. |
| if(n->typecheck == 0) |
| fatal("missing typecheck: %+N", n); |
| init = n->ninit; |
| n->ninit = nil; |
| |
| walkexpr(&n->left, &init); |
| n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, &init, typename(n->left->type), n->left, nodnil()); |
| walkexpr(&n, &init); |
| |
| addinit(&n, init); |
| break; |
| |
| case OBREAK: |
| case ODCL: |
| case OCONTINUE: |
| case OFALL: |
| case OGOTO: |
| case OLABEL: |
| case ODCLCONST: |
| case ODCLTYPE: |
| case OCHECKNIL: |
| case OVARKILL: |
| break; |
| |
| case OBLOCK: |
| walkstmtlist(n->list); |
| break; |
| |
| case OXCASE: |
| yyerror("case statement out of place"); |
| n->op = OCASE; |
| case OCASE: |
| walkstmt(&n->right); |
| break; |
| |
| case ODEFER: |
| hasdefer = 1; |
| switch(n->left->op) { |
| case OPRINT: |
| case OPRINTN: |
| walkprintfunc(&n->left, &n->ninit); |
| break; |
| case OCOPY: |
| n->left = copyany(n->left, &n->ninit, 1); |
| break; |
| default: |
| walkexpr(&n->left, &n->ninit); |
| break; |
| } |
| break; |
| |
| case OFOR: |
| if(n->ntest != N) { |
| walkstmtlist(n->ntest->ninit); |
| init = n->ntest->ninit; |
| n->ntest->ninit = nil; |
| walkexpr(&n->ntest, &init); |
| addinit(&n->ntest, init); |
| } |
| walkstmt(&n->nincr); |
| walkstmtlist(n->nbody); |
| break; |
| |
| case OIF: |
| walkexpr(&n->ntest, &n->ninit); |
| walkstmtlist(n->nbody); |
| walkstmtlist(n->nelse); |
| break; |
| |
| case OPROC: |
| switch(n->left->op) { |
| case OPRINT: |
| case OPRINTN: |
| walkprintfunc(&n->left, &n->ninit); |
| break; |
| case OCOPY: |
| n->left = copyany(n->left, &n->ninit, 1); |
| break; |
| default: |
| walkexpr(&n->left, &n->ninit); |
| break; |
| } |
| break; |
| |
| case ORETURN: |
| walkexprlist(n->list, &n->ninit); |
| if(n->list == nil) |
| break; |
| if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) { |
| // assign to the function out parameters, |
| // so that reorder3 can fix up conflicts |
| rl = nil; |
| for(ll=curfn->dcl; ll != nil; ll=ll->next) { |
| cl = ll->n->class & ~PHEAP; |
| if(cl == PAUTO) |
| break; |
| if(cl == PPARAMOUT) |
| rl = list(rl, ll->n); |
| } |
| if(samelist(rl, n->list)) { |
| // special return in disguise |
| n->list = nil; |
| break; |
| } |
| if(count(n->list) == 1 && count(rl) > 1) { |
| // OAS2FUNC in disguise |
| f = n->list->n; |
| if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER) |
| fatal("expected return of call, have %N", f); |
| n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit)); |
| break; |
| } |
| |
| // move function calls out, to make reorder3's job easier. |
| walkexprlistsafe(n->list, &n->ninit); |
| ll = ascompatee(n->op, rl, n->list, &n->ninit); |
| n->list = reorder3(ll); |
| break; |
| } |
| ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); |
| n->list = ll; |
| break; |
| |
| case ORETJMP: |
| break; |
| |
| case OSELECT: |
| walkselect(n); |
| break; |
| |
| case OSWITCH: |
| walkswitch(n); |
| break; |
| |
| case ORANGE: |
| walkrange(n); |
| break; |
| |
| case OXFALL: |
| yyerror("fallthrough statement out of place"); |
| n->op = OFALL; |
| break; |
| } |
| |
| if(n->op == ONAME) |
| fatal("walkstmt ended up with name: %+N", n); |
| |
| *np = n; |
| } |
| |
| |
| /* |
| * walk the whole tree of the body of an |
| * expression or simple statement. |
| * the types expressions are calculated. |
| * compile-time constants are evaluated. |
| * complex side effects like statements are appended to init |
| */ |
| |
| void |
| walkexprlist(NodeList *l, NodeList **init) |
| { |
| for(; l; l=l->next) |
| walkexpr(&l->n, init); |
| } |
| |
| void |
| walkexprlistsafe(NodeList *l, NodeList **init) |
| { |
| for(; l; l=l->next) { |
| l->n = safeexpr(l->n, init); |
| walkexpr(&l->n, init); |
| } |
| } |
| |
| void |
| walkexpr(Node **np, NodeList **init) |
| { |
| Node *r, *l, *var, *a; |
| Node *map, *key; |
| NodeList *ll, *lr; |
| Type *t; |
| int et, old_safemode; |
| int64 v; |
| int32 lno; |
| Node *n, *fn, *n1, *n2; |
| Sym *sym; |
| char buf[100], *p; |
| |
| n = *np; |
| |
| if(n == N) |
| return; |
| |
| if(init == &n->ninit) { |
| // not okay to use n->ninit when walking n, |
| // because we might replace n with some other node |
| // and would lose the init list. |
| fatal("walkexpr init == &n->ninit"); |
| } |
| |
| if(n->ninit != nil) { |
| walkstmtlist(n->ninit); |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| } |
| |
| // annoying case - not typechecked |
| if(n->op == OKEY) { |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| return; |
| } |
| |
| lno = setlineno(n); |
| |
| if(debug['w'] > 1) |
| dump("walk-before", n); |
| |
| if(n->typecheck != 1) |
| fatal("missed typecheck: %+N\n", n); |
| |
| switch(n->op) { |
| default: |
| dump("walk", n); |
| fatal("walkexpr: switch 1 unknown op %+hN", n); |
| break; |
| |
| case OTYPE: |
| case ONONAME: |
| case OINDREG: |
| case OEMPTY: |
| goto ret; |
| |
| case ONOT: |
| case OMINUS: |
| case OPLUS: |
| case OCOM: |
| case OREAL: |
| case OIMAG: |
| case ODOTMETH: |
| case ODOTINTER: |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OIND: |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case ODOT: |
| usefield(n); |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case ODOTPTR: |
| usefield(n); |
| if(n->op == ODOTPTR && n->left->type->type->width == 0) { |
| // No actual copy will be generated, so emit an explicit nil check. |
| n->left = cheapexpr(n->left, init); |
| checknil(n->left, init); |
| } |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OEFACE: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| goto ret; |
| |
| case OSPTR: |
| case OITAB: |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OLEN: |
| case OCAP: |
| walkexpr(&n->left, init); |
| |
| // replace len(*[10]int) with 10. |
| // delayed until now to preserve side effects. |
| t = n->left->type; |
| if(isptr[t->etype]) |
| t = t->type; |
| if(isfixedarray(t)) { |
| safeexpr(n->left, init); |
| nodconst(n, n->type, t->bound); |
| n->typecheck = 1; |
| } |
| goto ret; |
| |
| case OLSH: |
| case ORSH: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| t = n->left->type; |
| n->bounded = bounded(n->right, 8*t->width); |
| if(debug['m'] && n->etype && !isconst(n->right, CTINT)) |
| warn("shift bounds check elided"); |
| goto ret; |
| |
| case OAND: |
| case OSUB: |
| case OHMUL: |
| case OLT: |
| case OLE: |
| case OGE: |
| case OGT: |
| case OADD: |
| case OCOMPLEX: |
| case OLROT: |
| // Use results from call expression as arguments for complex. |
| if(n->op == OCOMPLEX && n->left == N && n->right == N) { |
| n->left = n->list->n; |
| n->right = n->list->next->n; |
| } |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| goto ret; |
| |
| case OOR: |
| case OXOR: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| walkrotate(&n); |
| goto ret; |
| |
| case OEQ: |
| case ONE: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| // Disable safemode while compiling this code: the code we |
| // generate internally can refer to unsafe.Pointer. |
| // In this case it can happen if we need to generate an == |
| // for a struct containing a reflect.Value, which itself has |
| // an unexported field of type unsafe.Pointer. |
| old_safemode = safemode; |
| safemode = 0; |
| walkcompare(&n, init); |
| safemode = old_safemode; |
| goto ret; |
| |
| case OANDAND: |
| case OOROR: |
| walkexpr(&n->left, init); |
| // cannot put side effects from n->right on init, |
| // because they cannot run before n->left is checked. |
| // save elsewhere and store on the eventual n->right. |
| ll = nil; |
| walkexpr(&n->right, &ll); |
| addinit(&n->right, ll); |
| goto ret; |
| |
| case OPRINT: |
| case OPRINTN: |
| walkexprlist(n->list, init); |
| n = walkprint(n, init); |
| goto ret; |
| |
| case OPANIC: |
| n = mkcall("gopanic", T, init, n->left); |
| goto ret; |
| |
| case ORECOVER: |
| n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N)); |
| goto ret; |
| |
| case OLITERAL: |
| n->addable = 1; |
| goto ret; |
| |
| case OCLOSUREVAR: |
| case OCFUNC: |
| n->addable = 1; |
| goto ret; |
| |
| case ONAME: |
| if(!(n->class & PHEAP) && n->class != PPARAMREF) |
| n->addable = 1; |
| goto ret; |
| |
| case OCALLINTER: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); |
| n->list = reorder1(ll); |
| goto ret; |
| |
| case OCALLFUNC: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| |
| ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); |
| n->list = reorder1(ll); |
| goto ret; |
| |
| case OCALLMETH: |
| t = n->left->type; |
| if(n->list && n->list->n->op == OAS) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexprlist(n->list, init); |
| ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init); |
| lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); |
| ll = concat(ll, lr); |
| n->left->left = N; |
| ullmancalc(n->left); |
| n->list = reorder1(ll); |
| goto ret; |
| |
| case OAS: |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| |
| walkexpr(&n->left, init); |
| n->left = safeexpr(n->left, init); |
| |
| if(oaslit(n, init)) |
| goto ret; |
| |
| if(n->right == N || iszero(n->right) && !flag_race) |
| goto ret; |
| |
| switch(n->right->op) { |
| default: |
| walkexpr(&n->right, init); |
| break; |
| |
| case ORECV: |
| // x = <-c; n->left is x, n->right->left is c. |
| // orderstmt made sure x is addressable. |
| walkexpr(&n->right->left, init); |
| n1 = nod(OADDR, n->left, N); |
| r = n->right->left; // the channel |
| n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1); |
| walkexpr(&n, init); |
| goto ret; |
| } |
| |
| if(n->left != N && n->right != N) { |
| r = convas(nod(OAS, n->left, n->right), init); |
| r->dodata = n->dodata; |
| n = r; |
| n = applywritebarrier(n, init); |
| } |
| |
| goto ret; |
| |
| case OAS2: |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| walkexprlistsafe(n->list, init); |
| walkexprlistsafe(n->rlist, init); |
| ll = ascompatee(OAS, n->list, n->rlist, init); |
| ll = reorder3(ll); |
| for(lr = ll; lr != nil; lr = lr->next) |
| lr->n = applywritebarrier(lr->n, init); |
| n = liststmt(ll); |
| goto ret; |
| |
| case OAS2FUNC: |
| // a,b,... = fn() |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlistsafe(n->list, init); |
| walkexpr(&r, init); |
| |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| for(lr = ll; lr != nil; lr = lr->next) |
| lr->n = applywritebarrier(lr->n, init); |
| n = liststmt(concat(list1(r), ll)); |
| goto ret; |
| |
| case OAS2RECV: |
| // x, y = <-c |
| // orderstmt made sure x is addressable. |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlistsafe(n->list, init); |
| walkexpr(&r->left, init); |
| if(isblank(n->list->n)) |
| n1 = nodnil(); |
| else |
| n1 = nod(OADDR, n->list->n, N); |
| n1->etype = 1; // addr does not escape |
| fn = chanfn("chanrecv2", 2, r->left->type); |
| r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1); |
| n = nod(OAS, n->list->next->n, r); |
| typecheck(&n, Etop); |
| goto ret; |
| |
| case OAS2MAPR: |
| // a,b = m[i]; |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlistsafe(n->list, init); |
| walkexpr(&r->left, init); |
| walkexpr(&r->right, init); |
| t = r->left->type; |
| p = nil; |
| if(t->type->width <= 128) { // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing. |
| switch(simsimtype(t->down)) { |
| case TINT32: |
| case TUINT32: |
| p = "mapaccess2_fast32"; |
| break; |
| case TINT64: |
| case TUINT64: |
| p = "mapaccess2_fast64"; |
| break; |
| case TSTRING: |
| p = "mapaccess2_faststr"; |
| break; |
| } |
| } |
| if(p != nil) { |
| // fast versions take key by value |
| key = r->right; |
| } else { |
| // standard version takes key by reference |
| // orderexpr made sure key is addressable. |
| key = nod(OADDR, r->right, N); |
| p = "mapaccess2"; |
| } |
| |
| // from: |
| // a,b = m[i] |
| // to: |
| // var,b = mapaccess2*(t, m, i) |
| // a = *var |
| a = n->list->n; |
| var = temp(ptrto(t->type)); |
| var->typecheck = 1; |
| fn = mapfn(p, t); |
| r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key); |
| |
| // mapaccess2* returns a typed bool, but due to spec changes, |
| // the boolean result of i.(T) is now untyped so we make it the |
| // same type as the variable on the lhs. |
| if(!isblank(n->list->next->n)) |
| r->type->type->down->type = n->list->next->n->type; |
| n->rlist = list1(r); |
| n->op = OAS2FUNC; |
| n->list->n = var; |
| walkexpr(&n, init); |
| *init = list(*init, n); |
| n = nod(OAS, a, nod(OIND, var, N)); |
| typecheck(&n, Etop); |
| walkexpr(&n, init); |
| // mapaccess needs a zero value to be at least this big. |
| if(zerosize < t->type->width) |
| zerosize = t->type->width; |
| // TODO: ptr is always non-nil, so disable nil check for this OIND op. |
| goto ret; |
| |
| case ODELETE: |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| map = n->list->n; |
| key = n->list->next->n; |
| walkexpr(&map, init); |
| walkexpr(&key, init); |
| // orderstmt made sure key is addressable. |
| key = nod(OADDR, key, N); |
| t = map->type; |
| n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key); |
| goto ret; |
| |
| case OAS2DOTTYPE: |
| // a,b = i.(T) |
| *init = concat(*init, n->ninit); |
| n->ninit = nil; |
| r = n->rlist->n; |
| walkexprlistsafe(n->list, init); |
| if(isblank(n->list->n) && !isinter(r->type)) { |
| strcpy(buf, "assert"); |
| p = buf+strlen(buf); |
| if(isnilinter(r->left->type)) |
| *p++ = 'E'; |
| else |
| *p++ = 'I'; |
| *p++ = '2'; |
| *p++ = 'T'; |
| *p++ = 'O'; |
| *p++ = 'K'; |
| *p = '\0'; |
| |
| fn = syslook(buf, 1); |
| |
| // runtime.assert(E|I)2TOK returns a typed bool, but due |
| // to spec changes, the boolean result of i.(T) is now untyped |
| // so we make it the same type as the variable on the lhs. |
| if(!isblank(n->list->next->n)) |
| fn->type->type->down->type->type = n->list->next->n->type; |
| ll = list1(typename(r->type)); |
| ll = list(ll, r->left); |
| argtype(fn, r->left->type); |
| n1 = nod(OCALL, fn, N); |
| n1->list = ll; |
| n = nod(OAS, n->list->next->n, n1); |
| typecheck(&n, Etop); |
| walkexpr(&n, init); |
| goto ret; |
| } |
| |
| r->op = ODOTTYPE2; |
| walkexpr(&r, init); |
| ll = ascompatet(n->op, n->list, &r->type, 0, init); |
| n = liststmt(concat(list1(r), ll)); |
| goto ret; |
| |
| case ODOTTYPE: |
| case ODOTTYPE2: |
| // Build name of function: assertI2E2 etc. |
| strcpy(buf, "assert"); |
| p = buf+strlen(buf); |
| if(isnilinter(n->left->type)) |
| *p++ = 'E'; |
| else |
| *p++ = 'I'; |
| *p++ = '2'; |
| if(isnilinter(n->type)) |
| *p++ = 'E'; |
| else if(isinter(n->type)) |
| *p++ = 'I'; |
| else |
| *p++ = 'T'; |
| if(n->op == ODOTTYPE2) |
| *p++ = '2'; |
| *p = '\0'; |
| |
| fn = syslook(buf, 1); |
| ll = list1(typename(n->type)); |
| ll = list(ll, n->left); |
| argtype(fn, n->left->type); |
| argtype(fn, n->type); |
| n = nod(OCALL, fn, N); |
| n->list = ll; |
| typecheck(&n, Erv | Efnstruct); |
| walkexpr(&n, init); |
| goto ret; |
| |
| case OCONVIFACE: |
| walkexpr(&n->left, init); |
| |
| // Optimize convT2E as a two-word copy when T is uintptr-shaped. |
| if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) { |
| l = nod(OEFACE, typename(n->left->type), n->left); |
| l->type = n->type; |
| l->typecheck = n->typecheck; |
| n = l; |
| goto ret; |
| } |
| |
| // Build name of function: convI2E etc. |
| // Not all names are possible |
| // (e.g., we'll never generate convE2E or convE2I). |
| strcpy(buf, "conv"); |
| p = buf+strlen(buf); |
| if(isnilinter(n->left->type)) |
| *p++ = 'E'; |
| else if(isinter(n->left->type)) |
| *p++ = 'I'; |
| else |
| *p++ = 'T'; |
| *p++ = '2'; |
| if(isnilinter(n->type)) |
| *p++ = 'E'; |
| else |
| *p++ = 'I'; |
| *p = '\0'; |
| |
| fn = syslook(buf, 1); |
| ll = nil; |
| if(!isinter(n->left->type)) |
| ll = list(ll, typename(n->left->type)); |
| if(!isnilinter(n->type)) |
| ll = list(ll, typename(n->type)); |
| if(!isinter(n->left->type) && !isnilinter(n->type)){ |
| sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg); |
| if(sym->def == N) { |
| l = nod(ONAME, N, N); |
| l->sym = sym; |
| l->type = ptrto(types[TUINT8]); |
| l->addable = 1; |
| l->class = PEXTERN; |
| l->xoffset = 0; |
| sym->def = l; |
| ggloblsym(sym, widthptr, DUPOK|NOPTR); |
| } |
| l = nod(OADDR, sym->def, N); |
| l->addable = 1; |
| ll = list(ll, l); |
| |
| if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) { |
| /* For pointer types, we can make a special form of optimization |
| * |
| * These statements are put onto the expression init list: |
| * Itab *tab = atomicloadtype(&cache); |
| * if(tab == nil) |
| * tab = typ2Itab(type, itype, &cache); |
| * |
| * The CONVIFACE expression is replaced with this: |
| * OEFACE{tab, ptr}; |
| */ |
| l = temp(ptrto(types[TUINT8])); |
| |
| n1 = nod(OAS, l, sym->def); |
| typecheck(&n1, Etop); |
| *init = list(*init, n1); |
| |
| fn = syslook("typ2Itab", 1); |
| n1 = nod(OCALL, fn, N); |
| n1->list = ll; |
| typecheck(&n1, Erv); |
| walkexpr(&n1, init); |
| |
| n2 = nod(OIF, N, N); |
| n2->ntest = nod(OEQ, l, nodnil()); |
| n2->nbody = list1(nod(OAS, l, n1)); |
| n2->likely = -1; |
| typecheck(&n2, Etop); |
| *init = list(*init, n2); |
| |
| l = nod(OEFACE, l, n->left); |
| l->typecheck = n->typecheck; |
| l->type = n->type; |
| n = l; |
| goto ret; |
| } |
| } |
| if(isinter(n->left->type)) { |
| ll = list(ll, n->left); |
| } else { |
| // regular types are passed by reference to avoid C vararg calls |
| // orderexpr arranged for n->left to be a temporary for all |
| // the conversions it could see. comparison of an interface |
| // with a non-interface, especially in a switch on interface value |
| // with non-interface cases, is not visible to orderstmt, so we |
| // have to fall back on allocating a temp here. |
| if(islvalue(n->left)) |
| ll = list(ll, nod(OADDR, n->left, N)); |
| else |
| ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N)); |
| } |
| argtype(fn, n->left->type); |
| argtype(fn, n->type); |
| dowidth(fn->type); |
| n = nod(OCALL, fn, N); |
| n->list = ll; |
| typecheck(&n, Erv); |
| walkexpr(&n, init); |
| goto ret; |
| |
| case OCONV: |
| case OCONVNOP: |
| if(thechar == '5') { |
| if(isfloat[n->left->type->etype]) { |
| if(n->type->etype == TINT64) { |
| n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64])); |
| goto ret; |
| } |
| if(n->type->etype == TUINT64) { |
| n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64])); |
| goto ret; |
| } |
| } |
| if(isfloat[n->type->etype]) { |
| if(n->left->type->etype == TINT64) { |
| n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64])); |
| goto ret; |
| } |
| if(n->left->type->etype == TUINT64) { |
| n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64])); |
| goto ret; |
| } |
| } |
| } |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case OANDNOT: |
| walkexpr(&n->left, init); |
| n->op = OAND; |
| n->right = nod(OCOM, n->right, N); |
| typecheck(&n->right, Erv); |
| walkexpr(&n->right, init); |
| goto ret; |
| |
| case OMUL: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| walkmul(&n, init); |
| goto ret; |
| |
| case ODIV: |
| case OMOD: |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| /* |
| * rewrite complex div into function call. |
| */ |
| et = n->left->type->etype; |
| if(iscomplex[et] && n->op == ODIV) { |
| t = n->type; |
| n = mkcall("complex128div", types[TCOMPLEX128], init, |
| conv(n->left, types[TCOMPLEX128]), |
| conv(n->right, types[TCOMPLEX128])); |
| n = conv(n, t); |
| goto ret; |
| } |
| // Nothing to do for float divisions. |
| if(isfloat[et]) |
| goto ret; |
| |
| // Try rewriting as shifts or magic multiplies. |
| walkdiv(&n, init); |
| |
| /* |
| * rewrite 64-bit div and mod into function calls |
| * on 32-bit architectures. |
| */ |
| switch(n->op) { |
| case OMOD: |
| case ODIV: |
| if(widthreg >= 8 || (et != TUINT64 && et != TINT64)) |
| goto ret; |
| if(et == TINT64) |
| strcpy(namebuf, "int64"); |
| else |
| strcpy(namebuf, "uint64"); |
| if(n->op == ODIV) |
| strcat(namebuf, "div"); |
| else |
| strcat(namebuf, "mod"); |
| n = mkcall(namebuf, n->type, init, |
| conv(n->left, types[et]), conv(n->right, types[et])); |
| break; |
| default: |
| break; |
| } |
| goto ret; |
| |
| case OINDEX: |
| walkexpr(&n->left, init); |
| // save the original node for bounds checking elision. |
| // If it was a ODIV/OMOD walk might rewrite it. |
| r = n->right; |
| walkexpr(&n->right, init); |
| |
| // if range of type cannot exceed static array bound, |
| // disable bounds check. |
| if(n->bounded) |
| goto ret; |
| t = n->left->type; |
| if(t != T && isptr[t->etype]) |
| t = t->type; |
| if(isfixedarray(t)) { |
| n->bounded = bounded(r, t->bound); |
| if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) |
| warn("index bounds check elided"); |
| if(smallintconst(n->right) && !n->bounded) |
| yyerror("index out of bounds"); |
| } else if(isconst(n->left, CTSTR)) { |
| n->bounded = bounded(r, n->left->val.u.sval->len); |
| if(debug['m'] && n->bounded && !isconst(n->right, CTINT)) |
| warn("index bounds check elided"); |
| if(smallintconst(n->right)) { |
| if(!n->bounded) |
| yyerror("index out of bounds"); |
| else { |
| // replace "abc"[1] with 'b'. |
| // delayed until now because "abc"[1] is not |
| // an ideal constant. |
| v = mpgetfix(n->right->val.u.xval); |
| nodconst(n, n->type, n->left->val.u.sval->s[v]); |
| n->typecheck = 1; |
| } |
| } |
| } |
| |
| if(isconst(n->right, CTINT)) |
| if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 || |
| mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0) |
| yyerror("index out of bounds"); |
| goto ret; |
| |
| case OINDEXMAP: |
| if(n->etype == 1) |
| goto ret; |
| walkexpr(&n->left, init); |
| walkexpr(&n->right, init); |
| |
| t = n->left->type; |
| p = nil; |
| if(t->type->width <= 128) { // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing. |
| switch(simsimtype(t->down)) { |
| case TINT32: |
| case TUINT32: |
| p = "mapaccess1_fast32"; |
| break; |
| case TINT64: |
| case TUINT64: |
| p = "mapaccess1_fast64"; |
| break; |
| case TSTRING: |
| p = "mapaccess1_faststr"; |
| break; |
| } |
| } |
| if(p != nil) { |
| // fast versions take key by value |
| key = n->right; |
| } else { |
| // standard version takes key by reference. |
| // orderexpr made sure key is addressable. |
| key = nod(OADDR, n->right, N); |
| p = "mapaccess1"; |
| } |
| n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key); |
| n = nod(OIND, n, N); |
| n->type = t->type; |
| n->typecheck = 1; |
| // mapaccess needs a zero value to be at least this big. |
| if(zerosize < t->type->width) |
| zerosize = t->type->width; |
| goto ret; |
| |
| case ORECV: |
| fatal("walkexpr ORECV"); // should see inside OAS only |
| |
| case OSLICE: |
| if(n->right != N && n->right->left == N && n->right->right == N) { // noop |
| walkexpr(&n->left, init); |
| n = n->left; |
| goto ret; |
| } |
| // fallthrough |
| case OSLICEARR: |
| case OSLICESTR: |
| if(n->right == N) // already processed |
| goto ret; |
| |
| walkexpr(&n->left, init); |
| // cgen_slice can't handle string literals as source |
| // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] |
| if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX)) |
| n->left = copyexpr(n->left, n->left->type, init); |
| else |
| n->left = safeexpr(n->left, init); |
| walkexpr(&n->right->left, init); |
| n->right->left = safeexpr(n->right->left, init); |
| walkexpr(&n->right->right, init); |
| n->right->right = safeexpr(n->right->right, init); |
| n = sliceany(n, init); // chops n->right, sets n->list |
| goto ret; |
| |
| case OSLICE3: |
| case OSLICE3ARR: |
| if(n->right == N) // already processed |
| goto ret; |
| |
| walkexpr(&n->left, init); |
| // TODO the OINDEX case is a bug elsewhere that needs to be traced. it causes a crash on ([2][]int{ ... })[1][lo:hi] |
| // TODO the comment on the previous line was copied from case OSLICE. it might not even be true. |
| if(n->left->op == OINDEX) |
| n->left = copyexpr(n->left, n->left->type, init); |
| else |
| n->left = safeexpr(n->left, init); |
| walkexpr(&n->right->left, init); |
| n->right->left = safeexpr(n->right->left, init); |
| walkexpr(&n->right->right->left, init); |
| n->right->right->left = safeexpr(n->right->right->left, init); |
| walkexpr(&n->right->right->right, init); |
| n->right->right->right = safeexpr(n->right->right->right, init); |
| n = sliceany(n, init); // chops n->right, sets n->list |
| goto ret; |
| |
| case OADDR: |
| walkexpr(&n->left, init); |
| goto ret; |
| |
| case ONEW: |
| if(n->esc == EscNone && n->type->type->width < (1<<16)) { |
| r = temp(n->type->type); |
| r = nod(OAS, r, N); // zero temp |
| typecheck(&r, Etop); |
| *init = list(*init, r); |
| r = nod(OADDR, r->left, N); |
| typecheck(&r, Erv); |
| n = r; |
| } else { |
| n = callnew(n->type->type); |
| } |
| goto ret; |
| |
| case OCMPSTR: |
| // If one argument to the comparison is an empty string, |
| // comparing the lengths instead will yield the same result |
| // without the function call. |
| if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) || |
| (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) { |
| r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N)); |
| typecheck(&r, Erv); |
| walkexpr(&r, init); |
| r->type = n->type; |
| n = r; |
| goto ret; |
| } |
| |
| // s + "badgerbadgerbadger" == "badgerbadgerbadger" |
| if((n->etype == OEQ || n->etype == ONE) && |
| isconst(n->right, CTSTR) && |
| n->left->op == OADDSTR && count(n->left->list) == 2 && |
| isconst(n->left->list->next->n, CTSTR) && |
| cmpslit(n->right, n->left->list->next->n) == 0) { |
| r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0)); |
| typecheck(&r, Erv); |
| walkexpr(&r, init); |
| r->type = n->type; |
| n = r; |
| goto ret; |
| } |
| |
| if(n->etype == OEQ || n->etype == ONE) { |
| // prepare for rewrite below |
| n->left = cheapexpr(n->left, init); |
| n->right = cheapexpr(n->right, init); |
| |
| r = mkcall("eqstring", types[TBOOL], init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TSTRING])); |
| |
| // quick check of len before full compare for == or != |
| if(n->etype == OEQ) { |
| // len(left) == len(right) && eqstring(left, right) |
| r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); |
| } else { |
| // len(left) != len(right) || !eqstring(left, right) |
| r = nod(ONOT, r, N); |
| r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r); |
| } |
| typecheck(&r, Erv); |
| walkexpr(&r, nil); |
| } else { |
| // sys_cmpstring(s1, s2) :: 0 |
| r = mkcall("cmpstring", types[TINT], init, |
| conv(n->left, types[TSTRING]), |
| conv(n->right, types[TSTRING])); |
| r = nod(n->etype, r, nodintconst(0)); |
| } |
| |
| typecheck(&r, Erv); |
| if(n->type->etype != TBOOL) fatal("cmp %T", n->type); |
| r->type = n->type; |
| n = r; |
| goto ret; |
| |
| case OADDSTR: |
| n = addstr(n, init); |
| goto ret; |
| |
| case OAPPEND: |
| if(n->isddd) |
| n = appendslice(n, init); // also works for append(slice, string). |
| else |
| n = append(n, init); |
| goto ret; |
| |
| case OCOPY: |
| n = copyany(n, init, flag_race); |
| goto ret; |
| |
| case OCLOSE: |
| // cannot use chanfn - closechan takes any, not chan any |
| fn = syslook("closechan", 1); |
| argtype(fn, n->left->type); |
| n = mkcall1(fn, T, init, n->left); |
| goto ret; |
| |
| case OMAKECHAN: |
| n = mkcall1(chanfn("makechan", 1, n->type), n->type, init, |
| typename(n->type), |
| conv(n->left, types[TINT64])); |
| goto ret; |
| |
| case OMAKEMAP: |
| t = n->type; |
| |
| fn = syslook("makemap", 1); |
| argtype(fn, t->down); // any-1 |
| argtype(fn, t->type); // any-2 |
| |
| n = mkcall1(fn, n->type, init, |
| typename(n->type), |
| conv(n->left, types[TINT64])); |
| goto ret; |
| |
| case OMAKESLICE: |
| l = n->left; |
| r = n->right; |
| if(r == nil) |
| l = r = safeexpr(l, init); |
| t = n->type; |
| if(n->esc == EscNone |
| && smallintconst(l) && smallintconst(r) |
| && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) { |
| // var arr [r]T |
| // n = arr[:l] |
| t = aindex(r, t->type); // [r]T |
| var = temp(t); |
| a = nod(OAS, var, N); // zero temp |
| typecheck(&a, Etop); |
| *init = list(*init, a); |
| r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l] |
| r = conv(r, n->type); // in case n->type is named. |
| typecheck(&r, Erv); |
| walkexpr(&r, init); |
| n = r; |
| } else { |
| // makeslice(t *Type, nel int64, max int64) (ary []any) |
| fn = syslook("makeslice", 1); |
| argtype(fn, t->type); // any-1 |
| n = mkcall1(fn, n->type, init, |
| typename(n->type), |
| conv(l, types[TINT64]), |
| conv(r, types[TINT64])); |
| } |
| goto ret; |
| |
| case ORUNESTR: |
| // sys_intstring(v) |
| n = mkcall("intstring", n->type, init, |
| conv(n->left, types[TINT64])); |
| goto ret; |
| |
| case OARRAYBYTESTR: |
| // slicebytetostring([]byte) string; |
| n = mkcall("slicebytetostring", n->type, init, n->left); |
| goto ret; |
| |
| case OARRAYBYTESTRTMP: |
| // slicebytetostringtmp([]byte) string; |
| n = mkcall("slicebytetostringtmp", n->type, init, n->left); |
| goto ret; |
| |
| case OARRAYRUNESTR: |
| // slicerunetostring([]rune) string; |
| n = mkcall("slicerunetostring", n->type, init, n->left); |
| goto ret; |
| |
| case OSTRARRAYBYTE: |
| // stringtoslicebyte(string) []byte; |
| n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING])); |
| goto ret; |
| |
| case OSTRARRAYRUNE: |
| // stringtoslicerune(string) []rune |
| n = mkcall("stringtoslicerune", n->type, init, n->left); |
| goto ret; |
| |
| case OCMPIFACE: |
| // ifaceeq(i1 any-1, i2 any-2) (ret bool); |
| if(!eqtype(n->left->type, n->right->type)) |
| fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type); |
| if(isnilinter(n->left->type)) |
| fn = syslook("efaceeq", 1); |
| else |
| fn = syslook("ifaceeq", 1); |
| |
| n->right = cheapexpr(n->right, init); |
| n->left = cheapexpr(n->left, init); |
| argtype(fn, n->right->type); |
| argtype(fn, n->left->type); |
| r = mkcall1(fn, n->type, init, n->left, n->right); |
| if(n->etype == ONE) |
| r = nod(ONOT, r, N); |
| |
| // check itable/type before full compare. |
| if(n->etype == OEQ) |
| r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); |
| else |
| r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r); |
| typecheck(&r, Erv); |
| walkexpr(&r, init); |
| r->type = n->type; |
| n = r; |
| goto ret; |
| |
| case OARRAYLIT: |
| case OMAPLIT: |
| case OSTRUCTLIT: |
| case OPTRLIT: |
| var = temp(n->type); |
| anylit(0, n, var, init); |
| n = var; |
| goto ret; |
| |
| case OSEND: |
| n1 = n->right; |
| n1 = assignconv(n1, n->left->type->type, "chan send"); |
| walkexpr(&n1, init); |
| n1 = nod(OADDR, n1, N); |
| n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1); |
| goto ret; |
| |
| case OCLOSURE: |
| n = walkclosure(n, init); |
| goto ret; |
| |
| case OCALLPART: |
| n = walkpartialcall(n, init); |
| goto ret; |
| } |
| fatal("missing switch %O", n->op); |
| |
| ret: |
| // Expressions that are constant at run time but not |
| // considered const by the language spec are not turned into |
| // constants until walk. For example, if n is y%1 == 0, the |
| // walk of y%1 may have replaced it by 0. |
| // Check whether n with its updated args is itself now a constant. |
| t = n->type; |
| evconst(n); |
| n->type = t; |
| if(n->op == OLITERAL) |
| typecheck(&n, Erv); |
| |
| ullmancalc(n); |
| |
| if(debug['w'] && n != N) |
| dump("walk", n); |
| |
| lineno = lno; |
| *np = n; |
| } |
| |
| static Node* |
| ascompatee1(int op, Node *l, Node *r, NodeList **init) |
| { |
| Node *n; |
| USED(op); |
| |
| // convas will turn map assigns into function calls, |
| // making it impossible for reorder3 to work. |
| n = nod(OAS, l, r); |
| if(l->op == OINDEXMAP) |
| return n; |
| |
| return convas(n, init); |
| } |
| |
| static NodeList* |
| ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init) |
| { |
| NodeList *ll, *lr, *nn; |
| |
| /* |
| * check assign expression list to |
| * a expression list. called in |
| * expr-list = expr-list |
| */ |
| |
| // ensure order of evaluation for function calls |
| for(ll=nl; ll; ll=ll->next) |
| ll->n = safeexpr(ll->n, init); |
| for(lr=nr; lr; lr=lr->next) |
| lr->n = safeexpr(lr->n, init); |
| |
| nn = nil; |
| for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) { |
| // Do not generate 'x = x' during return. See issue 4014. |
| if(op == ORETURN && ll->n == lr->n) |
| continue; |
| nn = list(nn, ascompatee1(op, ll->n, lr->n, init)); |
| } |
| |
| // cannot happen: caller checked that lists had same length |
| if(ll || lr) |
| yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name); |
| return nn; |
| } |
| |
| /* |
| * l is an lv and rt is the type of an rv |
| * return 1 if this implies a function call |
| * evaluating the lv or a function call |
| * in the conversion of the types |
| */ |
| static int |
| fncall(Node *l, Type *rt) |
| { |
| Node r; |
| |
| if(l->ullman >= UINF || l->op == OINDEXMAP) |
| return 1; |
| memset(&r, 0, sizeof r); |
| if(needwritebarrier(l, &r)) |
| return 1; |
| if(eqtype(l->type, rt)) |
| return 0; |
| return 1; |
| } |
| |
| static NodeList* |
| ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) |
| { |
| Node *l, *tmp, *a; |
| NodeList *ll; |
| Type *r; |
| Iter saver; |
| int ucount; |
| NodeList *nn, *mm; |
| |
| USED(op); |
| |
| /* |
| * check assign type list to |
| * a expression list. called in |
| * expr-list = func() |
| */ |
| r = structfirst(&saver, nr); |
| nn = nil; |
| mm = nil; |
| ucount = 0; |
| for(ll=nl; ll; ll=ll->next) { |
| if(r == T) |
| break; |
| l = ll->n; |
| if(isblank(l)) { |
| r = structnext(&saver); |
| continue; |
| } |
| |
| // any lv that causes a fn call must be |
| // deferred until all the return arguments |
| // have been pulled from the output arguments |
| if(fncall(l, r->type)) { |
| tmp = temp(r->type); |
| typecheck(&tmp, Erv); |
| a = nod(OAS, l, tmp); |
| a = convas(a, init); |
| mm = list(mm, a); |
| l = tmp; |
| } |
| |
| a = nod(OAS, l, nodarg(r, fp)); |
| a = convas(a, init); |
| ullmancalc(a); |
| if(a->ullman >= UINF) { |
| dump("ascompatet ucount", a); |
| ucount++; |
| } |
| nn = list(nn, a); |
| r = structnext(&saver); |
| } |
| |
| if(ll != nil || r != T) |
| yyerror("ascompatet: assignment count mismatch: %d = %d", |
| count(nl), structcount(*nr)); |
| |
| if(ucount) |
| fatal("ascompatet: too many function calls evaluating parameters"); |
| return concat(nn, mm); |
| } |
| |
| /* |
| * package all the arguments that match a ... T parameter into a []T. |
| */ |
| static NodeList* |
| mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd) |
| { |
| Node *a, *n; |
| Type *tslice; |
| int esc; |
| |
| esc = EscUnknown; |
| if(ddd != nil) |
| esc = ddd->esc; |
| |
| tslice = typ(TARRAY); |
| tslice->type = l->type->type; |
| tslice->bound = -1; |
| |
| if(count(lr0) == 0) { |
| n = nodnil(); |
| n->type = tslice; |
| } else { |
| n = nod(OCOMPLIT, N, typenod(tslice)); |
| if(ddd != nil) |
| n->alloc = ddd->alloc; // temporary to use |
| n->list = lr0; |
| n->esc = esc; |
| typecheck(&n, Erv); |
| if(n->type == T) |
| fatal("mkdotargslice: typecheck failed"); |
| walkexpr(&n, init); |
| } |
| |
| a = nod(OAS, nodarg(l, fp), n); |
| nn = list(nn, convas(a, init)); |
| return nn; |
| } |
| |
| /* |
| * helpers for shape errors |
| */ |
| static char* |
| dumptypes(Type **nl, char *what) |
| { |
| int first; |
| Type *l; |
| Iter savel; |
| Fmt fmt; |
| |
| fmtstrinit(&fmt); |
| fmtprint(&fmt, "\t"); |
| first = 1; |
| for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) { |
| if(first) |
| first = 0; |
| else |
| fmtprint(&fmt, ", "); |
| fmtprint(&fmt, "%T", l); |
| } |
| if(first) |
| fmtprint(&fmt, "[no arguments %s]", what); |
| return fmtstrflush(&fmt); |
| } |
| |
| static char* |
| dumpnodetypes(NodeList *l, char *what) |
| { |
| int first; |
| Node *r; |
| Fmt fmt; |
| |
| fmtstrinit(&fmt); |
| fmtprint(&fmt, "\t"); |
| first = 1; |
| for(; l; l=l->next) { |
| r = l->n; |
| if(first) |
| first = 0; |
| else |
| fmtprint(&fmt, ", "); |
| fmtprint(&fmt, "%T", r->type); |
| } |
| if(first) |
| fmtprint(&fmt, "[no arguments %s]", what); |
| return fmtstrflush(&fmt); |
| } |
| |
| /* |
| * check assign expression list to |
| * a type list. called in |
| * return expr-list |
| * func(expr-list) |
| */ |
| static NodeList* |
| ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) |
| { |
| Type *l, *ll; |
| Node *r, *a; |
| NodeList *nn, *lr0, *alist; |
| Iter savel; |
| char *l1, *l2; |
| |
| lr0 = lr; |
| l = structfirst(&savel, nl); |
| r = N; |
| if(lr) |
| r = lr->n; |
| nn = nil; |
| |
| // f(g()) where g has multiple return values |
| if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) { |
| // optimization - can do block copy |
| if(eqtypenoname(r->type, *nl)) { |
| a = nodarg(*nl, fp); |
| r = nod(OCONVNOP, r, N); |
| r->type = a->type; |
| nn = list1(convas(nod(OAS, a, r), init)); |
| goto ret; |
| } |
| |
| // conversions involved. |
| // copy into temporaries. |
| alist = nil; |
| for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) { |
| a = temp(l->type); |
| alist = list(alist, a); |
| } |
| a = nod(OAS2, N, N); |
| a->list = alist; |
| a->rlist = lr; |
| typecheck(&a, Etop); |
| walkstmt(&a); |
| *init = list(*init, a); |
| lr = alist; |
| r = lr->n; |
| l = structfirst(&savel, nl); |
| } |
| |
| loop: |
| if(l != T && l->isddd) { |
| // the ddd parameter must be last |
| ll = structnext(&savel); |
| if(ll != T) |
| yyerror("... must be last argument"); |
| |
| // special case -- |
| // only if we are assigning a single ddd |
| // argument to a ddd parameter then it is |
| // passed thru unencapsulated |
| if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) { |
| a = nod(OAS, nodarg(l, fp), r); |
| a = convas(a, init); |
| nn = list(nn, a); |
| goto ret; |
| } |
| |
| // normal case -- make a slice of all |
| // remaining arguments and pass it to |
| // the ddd parameter. |
| nn = mkdotargslice(lr, nn, l, fp, init, call->right); |
| goto ret; |
| } |
| |
| if(l == T || r == N) { |
| if(l != T || r != N) { |
| l1 = dumptypes(nl, "expected"); |
| l2 = dumpnodetypes(lr0, "given"); |
| if(l != T) |
| yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2); |
| else |
| yyerror("too many arguments to %O\n%s\n%s", op, l1, l2); |
| } |
| goto ret; |
| } |
| |
| a = nod(OAS, nodarg(l, fp), r); |
| a = convas(a, init); |
| nn = list(nn, a); |
| |
| l = structnext(&savel); |
| r = N; |
| lr = lr->next; |
| if(lr != nil) |
| r = lr->n; |
| goto loop; |
| |
| ret: |
| for(lr=nn; lr; lr=lr->next) |
| lr->n->typecheck = 1; |
| return nn; |
| } |
| |
| // generate code for print |
| static Node* |
| walkprint(Node *nn, NodeList **init) |
| { |
| Node *r; |
| Node *n; |
| NodeList *l, *all; |
| Node *on; |
| Type *t; |
| int notfirst, et, op; |
| NodeList *calls; |
| |
| on = nil; |
| op = nn->op; |
| all = nn->list; |
| calls = nil; |
| notfirst = 0; |
| |
| for(l=all; l; l=l->next) { |
| if(notfirst) { |
| calls = list(calls, mkcall("printsp", T, init)); |
| } |
| notfirst = op == OPRINTN; |
| |
| n = l->n; |
| if(n->op == OLITERAL) { |
| switch(n->val.ctype) { |
| case CTRUNE: |
| defaultlit(&n, runetype); |
| break; |
| case CTINT: |
| defaultlit(&n, types[TINT64]); |
| break; |
| case CTFLT: |
| defaultlit(&n, types[TFLOAT64]); |
| break; |
| } |
| } |
| if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL) |
| defaultlit(&n, types[TINT64]); |
| defaultlit(&n, nil); |
| l->n = n; |
| if(n->type == T || n->type->etype == TFORW) |
| continue; |
| |
| t = n->type; |
| et = n->type->etype; |
| if(isinter(n->type)) { |
| if(isnilinter(n->type)) |
| on = syslook("printeface", 1); |
| else |
| on = syslook("printiface", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) { |
| on = syslook("printpointer", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isslice(n->type)) { |
| on = syslook("printslice", 1); |
| argtype(on, n->type); // any-1 |
| } else if(isint[et]) { |
| if(et == TUINT64) { |
| if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0) |
| on = syslook("printhex", 0); |
| else |
| on = syslook("printuint", 0); |
| } else |
| on = syslook("printint", 0); |
| } else if(isfloat[et]) { |
| on = syslook("printfloat", 0); |
| } else if(iscomplex[et]) { |
| on = syslook("printcomplex", 0); |
| } else if(et == TBOOL) { |
| on = syslook("printbool", 0); |
| } else if(et == TSTRING) { |
| on = syslook("printstring", 0); |
| } else { |
| badtype(OPRINT, n->type, T); |
| continue; |
| } |
| |
| t = *getinarg(on->type); |
| if(t != nil) |
| t = t->type; |
| if(t != nil) |
| t = t->type; |
| |
| if(!eqtype(t, n->type)) { |
| n = nod(OCONV, n, N); |
| n->type = t; |
| } |
| |
| r = nod(OCALL, on, N); |
| r->list = list1(n); |
| calls = list(calls, r); |
| } |
| |
| if(op == OPRINTN) |
| calls = list(calls, mkcall("printnl", T, nil)); |
| typechecklist(calls, Etop); |
| walkexprlist(calls, init); |
| |
| r = nod(OEMPTY, N, N); |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| r->ninit = calls; |
| return r; |
| } |
| |
| Node* |
| callnew(Type *t) |
| { |
| Node *fn; |
| |
| dowidth(t); |
| fn = syslook("newobject", 1); |
| argtype(fn, t); |
| return mkcall1(fn, ptrto(t), nil, typename(t)); |
| } |
| |
| static int |
| isstack(Node *n) |
| { |
| while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type)) |
| n = n->left; |
| |
| switch(n->op) { |
| case OINDREG: |
| // OINDREG only ends up in walk if it's indirect of SP. |
| return 1; |
| |
| case ONAME: |
| switch(n->class) { |
| case PAUTO: |
| case PPARAM: |
| case PPARAMOUT: |
| return 1; |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| isglobal(Node *n) |
| { |
| while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type)) |
| n = n->left; |
| |
| switch(n->op) { |
| case ONAME: |
| switch(n->class) { |
| case PEXTERN: |
| return 1; |
| } |
| break; |
| } |
| |
| return 0; |
| } |
| |
| // Do we need a write barrier for the assignment l = r? |
| int |
| needwritebarrier(Node *l, Node *r) |
| { |
| if(!use_writebarrier) |
| return 0; |
| |
| if(l == N || isblank(l)) |
| return 0; |
| |
| // No write barrier for write of non-pointers. |
| dowidth(l->type); |
| if(!haspointers(l->type)) |
| return 0; |
| |
| // No write barrier for write to stack. |
| if(isstack(l)) |
| return 0; |
| |
| // No write barrier for implicit or explicit zeroing. |
| if(r == N || iszero(r)) |
| return 0; |
| |
| // No write barrier for initialization to constant. |
| if(r->op == OLITERAL) |
| return 0; |
| |
| // No write barrier for storing static (read-only) data. |
| if(r->op == ONAME && strncmp(r->sym->name, "statictmp_", 10) == 0) |
| return 0; |
| |
| // No write barrier for storing address of stack values, |
| // which are guaranteed only to be written to the stack. |
| if(r->op == OADDR && isstack(r->left)) |
| return 0; |
| |
| // No write barrier for storing address of global, which |
| // is live no matter what. |
| if(r->op == OADDR && isglobal(r->left)) |
| return 0; |
| |
| // No write barrier for reslice: x = x[0:y] or x = append(x, ...). |
| // Both are compiled to modify x directly. |
| // In the case of append, a write barrier may still be needed |
| // if the underlying array grows, but the append code can |
| // generate the write barrier directly in that case. |
| // (It does not yet, but the cost of the write barrier will be |
| // small compared to the cost of the allocation.) |
| if(r->reslice) { |
| switch(r->op) { |
| case OSLICE: |
| case OSLICE3: |
| case OSLICESTR: |
| case OAPPEND: |
| break; |
| default: |
| dump("bad reslice-l", l); |
| dump("bad reslice-r", r); |
| break; |
| } |
| return 0; |
| } |
| |
| // Otherwise, be conservative and use write barrier. |
| return 1; |
| } |
| |
| // TODO(rsc): Perhaps componentgen should run before this. |
| static Node* |
| applywritebarrier(Node *n, NodeList **init) |
| { |
| Node *l, *r; |
| Type *t; |
| |
| if(n->left && n->right && needwritebarrier(n->left, n->right)) { |
| t = n->left->type; |
| l = nod(OADDR, n->left, N); |
| l->etype = 1; // addr does not escape |
| if(t->width == widthptr) { |
| n = mkcall1(writebarrierfn("writebarrierptr", t, n->right->type), T, init, |
| l, n->right); |
| } else if(t->etype == TSTRING) { |
| n = mkcall1(writebarrierfn("writebarrierstring", t, n->right->type), T, init, |
| l, n->right); |
| } else if(isslice(t)) { |
| n = mkcall1(writebarrierfn("writebarrierslice", t, n->right->type), T, init, |
| l, n->right); |
| } else if(isinter(t)) { |
| n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init, |
| l, n->right); |
| } else if(t->width == 2*widthptr) { |
| n = mkcall1(writebarrierfn("writebarrierfat2", t, n->right->type), T, init, |
| l, nodnil(), n->right); |
| } else if(t->width == 3*widthptr) { |
| n = mkcall1(writebarrierfn("writebarrierfat3", t, n->right->type), T, init, |
| l, nodnil(), n->right); |
| } else if(t->width == 4*widthptr) { |
| n = mkcall1(writebarrierfn("writebarrierfat4", t, n->right->type), T, init, |
| l, nodnil(), n->right); |
| } else { |
| r = n->right; |
| while(r->op == OCONVNOP) |
| r = r->left; |
| r = nod(OADDR, r, N); |
| r->etype = 1; // addr does not escape |
| //warnl(n->lineno, "writebarrierfat %T %N", t, r); |
| n = mkcall1(writebarrierfn("writebarrierfat", t, r->left->type), T, init, |
| typename(t), l, r); |
| } |
| } |
| return n; |
| } |
| |
| static Node* |
| convas(Node *n, NodeList **init) |
| { |
| Type *lt, *rt; |
| Node *map, *key, *val; |
| |
| if(n->op != OAS) |
| fatal("convas: not OAS %O", n->op); |
| |
| n->typecheck = 1; |
| |
| if(n->left == N || n->right == N) |
| goto out; |
| |
| lt = n->left->type; |
| rt = n->right->type; |
| if(lt == T || rt == T) |
| goto out; |
| |
| if(isblank(n->left)) { |
| defaultlit(&n->right, T); |
| goto out; |
| } |
| |
| if(n->left->op == OINDEXMAP) { |
| map = n->left->left; |
| key = n->left->right; |
| val = n->right; |
| walkexpr(&map, init); |
| walkexpr(&key, init); |
| walkexpr(&val, init); |
| // orderexpr made sure key and val are addressable. |
| key = nod(OADDR, key, N); |
| val = nod(OADDR, val, N); |
| n = mkcall1(mapfn("mapassign1", map->type), T, init, |
| typename(map->type), map, key, val); |
| goto out; |
| } |
| |
| if(!eqtype(lt, rt)) { |
| n->right = assignconv(n->right, lt, "assignment"); |
| walkexpr(&n->right, init); |
| } |
| |
| out: |
| ullmancalc(n); |
| return n; |
| } |
| |
| /* |
| * from ascompat[te] |
| * evaluating actual function arguments. |
| * f(a,b) |
| * if there is exactly one function expr, |
| * then it is done first. otherwise must |
| * make temp variables |
| */ |
| static NodeList* |
| reorder1(NodeList *all) |
| { |
| Node *f, *a, *n; |
| NodeList *l, *r, *g; |
| int c, d, t; |
| |
| c = 0; // function calls |
| t = 0; // total parameters |
| |
| for(l=all; l; l=l->next) { |
| n = l->n; |
| t++; |
| ullmancalc(n); |
| if(n->ullman >= UINF) |
| c++; |
| } |
| if(c == 0 || t == 1) |
| return all; |
| |
| g = nil; // fncalls assigned to tempnames |
| f = N; // last fncall assigned to stack |
| r = nil; // non fncalls and tempnames assigned to stack |
| d = 0; |
| for(l=all; l; l=l->next) { |
| n = l->n; |
| if(n->ullman < UINF) { |
| r = list(r, n); |
| continue; |
| } |
| d++; |
| if(d == c) { |
| f = n; |
| continue; |
| } |
| |
| // make assignment of fncall to tempname |
| a = temp(n->right->type); |
| a = nod(OAS, a, n->right); |
| g = list(g, a); |
| |
| // put normal arg assignment on list |
| // with fncall replaced by tempname |
| n->right = a->left; |
| r = list(r, n); |
| } |
| |
| if(f != N) |
| g = list(g, f); |
| return concat(g, r); |
| } |
| |
| static void reorder3save(Node**, NodeList*, NodeList*, NodeList**); |
| static int aliased(Node*, NodeList*, NodeList*); |
| |
| /* |
| * from ascompat[ee] |
| * a,b = c,d |
| * simultaneous assignment. there cannot |
| * be later use of an earlier lvalue. |
| * |
| * function calls have been removed. |
| */ |
| static NodeList* |
| reorder3(NodeList *all) |
| { |
| NodeList *list, *early, *mapinit; |
| Node *l; |
| |
| // If a needed expression may be affected by an |
| // earlier assignment, make an early copy of that |
| // expression and use the copy instead. |
| early = nil; |
| mapinit = nil; |
| for(list=all; list; list=list->next) { |
| l = list->n->left; |
| |
| // Save subexpressions needed on left side. |
| // Drill through non-dereferences. |
| for(;;) { |
| if(l->op == ODOT || l->op == OPAREN) { |
| l = l->left; |
| continue; |
| } |
| if(l->op == OINDEX && isfixedarray(l->left->type)) { |
| reorder3save(&l->right, all, list, &early); |
| l = l->left; |
| continue; |
| } |
| break; |
| } |
| switch(l->op) { |
| default: |
| fatal("reorder3 unexpected lvalue %#O", l->op); |
| case ONAME: |
| break; |
| case OINDEX: |
| case OINDEXMAP: |
| reorder3save(&l->left, all, list, &early); |
| reorder3save(&l->right, all, list, &early); |
| if(l->op == OINDEXMAP) |
| list->n = convas(list->n, &mapinit); |
| break; |
| case OIND: |
| case ODOTPTR: |
| reorder3save(&l->left, all, list, &early); |
| } |
| |
| // Save expression on right side. |
| reorder3save(&list->n->right, all, list, &early); |
| } |
| |
| early = concat(mapinit, early); |
| return concat(early, all); |
| } |
| |
| static int vmatch2(Node*, Node*); |
| static int varexpr(Node*); |
| |
| /* |
| * if the evaluation of *np would be affected by the |
| * assignments in all up to but not including stop, |
| * copy into a temporary during *early and |
| * replace *np with that temp. |
| */ |
| static void |
| reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early) |
| { |
| Node *n, *q; |
| |
| n = *np; |
| if(!aliased(n, all, stop)) |
| return; |
| |
| q = temp(n->type); |
| q = nod(OAS, q, n); |
| typecheck(&q, Etop); |
| *early = list(*early, q); |
| *np = q->left; |
| } |
| |
| /* |
| * what's the outer value that a write to n affects? |
| * outer value means containing struct or array. |
| */ |
| Node* |
| outervalue(Node *n) |
| { |
| for(;;) { |
| if(n->op == ODOT || n->op == OPAREN) { |
| n = n->left; |
| continue; |
| } |
| if(n->op == OINDEX && isfixedarray(n->left->type)) { |
| n = n->left; |
| continue; |
| } |
| break; |
| } |
| return n; |
| } |
| |
| /* |
| * Is it possible that the computation of n might be |
| * affected by writes in as up to but not including stop? |
| */ |
| static int |
| aliased(Node *n, NodeList *all, NodeList *stop) |
| { |
| int memwrite, varwrite; |
| Node *a; |
| NodeList *l; |
| |
| if(n == N) |
| return 0; |
| |
| // Look for obvious aliasing: a variable being assigned |
| // during the all list and appearing in n. |
| // Also record whether there are any writes to main memory. |
| // Also record whether there are any writes to variables |
| // whose addresses have been taken. |
| memwrite = 0; |
| varwrite = 0; |
| for(l=all; l!=stop; l=l->next) { |
| a = outervalue(l->n->left); |
| if(a->op != ONAME) { |
| memwrite = 1; |
| continue; |
| } |
| switch(n->class) { |
| default: |
| varwrite = 1; |
| continue; |
| case PAUTO: |
| case PPARAM: |
| case PPARAMOUT: |
| if(n->addrtaken) { |
| varwrite = 1; |
| continue; |
| } |
| if(vmatch2(a, n)) { |
| // Direct hit. |
| return 1; |
| } |
| } |
| } |
| |
| // The variables being written do not appear in n. |
| // However, n might refer to computed addresses |
| // that are being written. |
| |
| // If no computed addresses are affected by the writes, no aliasing. |
| if(!memwrite && !varwrite) |
| return 0; |
| |
| // If n does not refer to computed addresses |
| // (that is, if n only refers to variables whose addresses |
| // have not been taken), no aliasing. |
| if(varexpr(n)) |
| return 0; |
| |
| // Otherwise, both the writes and n refer to computed memory addresses. |
| // Assume that they might conflict. |
| return 1; |
| } |
| |
| /* |
| * does the evaluation of n only refer to variables |
| * whose addresses have not been taken? |
| * (and no other memory) |
| */ |
| static int |
| varexpr(Node *n) |
| { |
| if(n == N) |
| return 1; |
| |
| switch(n->op) { |
| case OLITERAL: |
| return 1; |
| case ONAME: |
| switch(n->class) { |
| case PAUTO: |
| case PPARAM: |
| case PPARAMOUT: |
| if(!n->addrtaken) |
| return 1; |
| } |
| return 0; |
| |
| case OADD: |
| case OSUB: |
| case OOR: |
| case OXOR: |
| case OMUL: |
| case ODIV: |
| case OMOD: |
| case OLSH: |
| case ORSH: |
| case OAND: |
| case OANDNOT: |
| case OPLUS: |
| case OMINUS: |
| case OCOM: |
| case OPAREN: |
| case OANDAND: |
| case OOROR: |
| case ODOT: // but not ODOTPTR |
| case OCONV: |
| case OCONVNOP: |
| case OCONVIFACE: |
| case ODOTTYPE: |
| return varexpr(n->left) && varexpr(n->right); |
| } |
| |
| // Be conservative. |
| return 0; |
| } |
| |
| /* |
| * is the name l mentioned in r? |
| */ |
| static int |
| vmatch2(Node *l, Node *r) |
| { |
| NodeList *ll; |
| |
| if(r == N) |
| return 0; |
| switch(r->op) { |
| case ONAME: |
| // match each right given left |
| return l == r; |
| case OLITERAL: |
| return 0; |
| } |
| if(vmatch2(l, r->left)) |
| return 1; |
| if(vmatch2(l, r->right)) |
| return 1; |
| for(ll=r->list; ll; ll=ll->next) |
| if(vmatch2(l, ll->n)) |
| return 1; |
| return 0; |
| } |
| |
| /* |
| * is any name mentioned in l also mentioned in r? |
| * called by sinit.c |
| */ |
| int |
| vmatch1(Node *l, Node *r) |
| { |
| NodeList *ll; |
| |
| /* |
| * isolate all left sides |
| */ |
| if(l == N || r == N) |
| return 0; |
| switch(l->op) { |
| case ONAME: |
| switch(l->class) { |
| case PPARAM: |
| case PPARAMREF: |
| case PAUTO: |
| break; |
| default: |
| // assignment to non-stack variable |
| // must be delayed if right has function calls. |
| if(r->ullman >= UINF) |
| return 1; |
| break; |
| } |
| return vmatch2(l, r); |
| case OLITERAL: |
| return 0; |
| } |
| if(vmatch1(l->left, r)) |
| return 1; |
| if(vmatch1(l->right, r)) |
| return 1; |
| for(ll=l->list; ll; ll=ll->next) |
| if(vmatch1(ll->n, r)) |
| return 1; |
| return 0; |
| } |
| |
| /* |
| * walk through argin parameters. |
| * generate and return code to allocate |
| * copies of escaped parameters to the heap. |
| */ |
| static NodeList* |
| paramstoheap(Type **argin, int out) |
| { |
| Type *t; |
| Iter savet; |
| Node *v; |
| NodeList *nn; |
| |
| nn = nil; |
| for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { |
| v = t->nname; |
| if(v && v->sym && v->sym->name[0] == '~' && v->sym->name[1] == 'r') // unnamed result |
| v = N; |
| // In precisestack mode, the garbage collector assumes results |
| // are always live, so zero them always. |
| if(out && (precisestack_enabled || (v == N && hasdefer))) { |
| // Defer might stop a panic and show the |
| // return values as they exist at the time of panic. |
| // Make sure to zero them on entry to the function. |
| nn = list(nn, nod(OAS, nodarg(t, 1), N)); |
| } |
| if(v == N || !(v->class & PHEAP)) |
| continue; |
| |
| // generate allocation & copying code |
| if(compiling_runtime) |
| yyerror("%N escapes to heap, not allowed in runtime.", v); |
| if(v->alloc == nil) |
| v->alloc = callnew(v->type); |
| nn = list(nn, nod(OAS, v->heapaddr, v->alloc)); |
| if((v->class & ~PHEAP) != PPARAMOUT) |
| nn = list(nn, nod(OAS, v, v->stackparam)); |
| } |
| return nn; |
| } |
| |
| /* |
| * walk through argout parameters copying back to stack |
| */ |
| static NodeList* |
| returnsfromheap(Type **argin) |
| { |
| Type *t; |
| Iter savet; |
| Node *v; |
| NodeList *nn; |
| |
| nn = nil; |
| for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) { |
| v = t->nname; |
| if(v == N || v->class != (PHEAP|PPARAMOUT)) |
| continue; |
| nn = list(nn, nod(OAS, v->stackparam, v)); |
| } |
| return nn; |
| } |
| |
| /* |
| * take care of migrating any function in/out args |
| * between the stack and the heap. adds code to |
| * curfn's before and after lists. |
| */ |
| static void |
| heapmoves(void) |
| { |
| NodeList *nn; |
| int32 lno; |
| |
| lno = lineno; |
| lineno = curfn->lineno; |
| nn = paramstoheap(getthis(curfn->type), 0); |
| nn = concat(nn, paramstoheap(getinarg(curfn->type), 0)); |
| nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1)); |
| curfn->enter = concat(curfn->enter, nn); |
| lineno = curfn->endlineno; |
| curfn->exit = returnsfromheap(getoutarg(curfn->type)); |
| lineno = lno; |
| } |
| |
| static Node* |
| vmkcall(Node *fn, Type *t, NodeList **init, va_list va) |
| { |
| int i, n; |
| Node *r; |
| NodeList *args; |
| |
| if(fn->type == T || fn->type->etype != TFUNC) |
| fatal("mkcall %N %T", fn, fn->type); |
| |
| args = nil; |
| n = fn->type->intuple; |
| for(i=0; i<n; i++) |
| args = list(args, va_arg(va, Node*)); |
| |
| r = nod(OCALL, fn, N); |
| r->list = args; |
| if(fn->type->outtuple > 0) |
| typecheck(&r, Erv | Efnstruct); |
| else |
| typecheck(&r, Etop); |
| walkexpr(&r, init); |
| r->type = t; |
| return r; |
| } |
| |
| Node* |
| mkcall(char *name, Type *t, NodeList **init, ...) |
| { |
| Node *r; |
| va_list va; |
| |
| va_start(va, init); |
| r = vmkcall(syslook(name, 0), t, init, va); |
| va_end(va); |
| return r; |
| } |
| |
| Node* |
| mkcall1(Node *fn, Type *t, NodeList **init, ...) |
| { |
| Node *r; |
| va_list va; |
| |
| va_start(va, init); |
| r = vmkcall(fn, t, init, va); |
| va_end(va); |
| return r; |
| } |
| |
| Node* |
| conv(Node *n, Type *t) |
| { |
| if(eqtype(n->type, t)) |
| return n; |
| n = nod(OCONV, n, N); |
| n->type = t; |
| typecheck(&n, Erv); |
| return n; |
| } |
| |
| Node* |
| chanfn(char *name, int n, Type *t) |
| { |
| Node *fn; |
| int i; |
| |
| if(t->etype != TCHAN) |
| fatal("chanfn %T", t); |
| fn = syslook(name, 1); |
| for(i=0; i<n; i++) |
| argtype(fn, t->type); |
| return fn; |
| } |
| |
| static Node* |
| mapfn(char *name, Type *t) |
| { |
| Node *fn; |
| |
| if(t->etype != TMAP) |
| fatal("mapfn %T", t); |
| fn = syslook(name, 1); |
| argtype(fn, t->down); |
| argtype(fn, t->type); |
| argtype(fn, t->down); |
| argtype(fn, t->type); |
| return fn; |
| } |
| |
| static Node* |
| mapfndel(char *name, Type *t) |
| { |
| Node *fn; |
| |
| if(t->etype != TMAP) |
| fatal("mapfn %T", t); |
| fn = syslook(name, 1); |
| argtype(fn, t->down); |
| argtype(fn, t->type); |
| argtype(fn, t->down); |
| return fn; |
| } |
| |
| static Node* |
| writebarrierfn(char *name, Type *l, Type *r) |
| { |
| Node *fn; |
| |
| fn = syslook(name, 1); |
| argtype(fn, l); |
| argtype(fn, r); |
| return fn; |
| } |
| |
| static Node* |
| addstr(Node *n, NodeList **init) |
| { |
| Node *r, *cat, *slice; |
| NodeList *args, *l; |
| int c; |
| Type *t; |
| |
| // orderexpr rewrote OADDSTR to have a list of strings. |
| c = count(n->list); |
| if(c < 2) |
| yyerror("addstr count %d too small", c); |
| |
| // build list of string arguments |
| args = nil; |
| for(l=n->list; l != nil; l=l->next) |
| args = list(args, conv(l->n, types[TSTRING])); |
| |
| if(c <= 5) { |
| // small numbers of strings use direct runtime helpers. |
| // note: orderexpr knows this cutoff too. |
| snprint(namebuf, sizeof(namebuf), "concatstring%d", c); |
| } else { |
| // large numbers of strings are passed to the runtime as a slice. |
| strcpy(namebuf, "concatstrings"); |
| t = typ(TARRAY); |
| t->type = types[TSTRING]; |
| t->bound = -1; |
| slice = nod(OCOMPLIT, N, typenod(t)); |
| slice->alloc = n->alloc; |
| slice->list = args; |
| slice->esc = EscNone; |
| args = list1(slice); |
| } |
| cat = syslook(namebuf, 1); |
| r = nod(OCALL, cat, N); |
| r->list = args; |
| typecheck(&r, Erv); |
| walkexpr(&r, init); |
| r->type = n->type; |
| |
| return r; |
| } |
| |
| // expand append(l1, l2...) to |
| // init { |
| // s := l1 |
| // if n := len(l1) + len(l2) - cap(s); n > 0 { |
| // s = growslice(s, n) |
| // } |
| // s = s[:len(l1)+len(l2)] |
| // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) |
| // } |
| // s |
| // |
| // l2 is allowed to be a string. |
| static Node* |
| appendslice(Node *n, NodeList **init) |
| { |
| NodeList *l; |
| Node *l1, *l2, *nt, *nif, *fn; |
| Node *nptr1, *nptr2, *nwid; |
| Node *s; |
| |
| walkexprlistsafe(n->list, init); |
| |
| // walkexprlistsafe will leave OINDEX (s[n]) alone if both s |
| // and n are name or literal, but those may index the slice we're |
| // modifying here. Fix explicitly. |
| for(l=n->list; l; l=l->next) |
| l->n = cheapexpr(l->n, init); |
| |
| l1 = n->list->n; |
| l2 = n->list->next->n; |
| |
| s = temp(l1->type); // var s []T |
| l = nil; |
| l = list(l, nod(OAS, s, l1)); // s = l1 |
| |
| nt = temp(types[TINT]); |
| nif = nod(OIF, N, N); |
| // n := len(s) + len(l2) - cap(s) |
| nif->ninit = list1(nod(OAS, nt, |
| nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N)))); |
| nif->ntest = nod(OGT, nt, nodintconst(0)); |
| // instantiate growslice(Type*, []any, int64) []any |
| fn = syslook("growslice", 1); |
| argtype(fn, s->type->type); |
| argtype(fn, s->type->type); |
| |
| // s = growslice(T, s, n) |
| nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit, |
| typename(s->type), |
| s, |
| conv(nt, types[TINT64])))); |
| |
| l = list(l, nif); |
| |
| if(flag_race) { |
| // rely on runtime to instrument copy. |
| // copy(s[len(l1):len(l1)+len(l2)], l2) |
| nptr1 = nod(OSLICE, s, nod(OKEY, |
| nod(OLEN, l1, N), |
| nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)))); |
| nptr1->etype = 1; |
| nptr2 = l2; |
| if(l2->type->etype == TSTRING) |
| fn = syslook("slicestringcopy", 1); |
| else |
| fn = syslook("slicecopy", 1); |
| argtype(fn, l1->type); |
| argtype(fn, l2->type); |
| nt = mkcall1(fn, types[TINT], &l, |
| nptr1, nptr2, |
| nodintconst(s->type->type->width)); |
| l = list(l, nt); |
| } else { |
| // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T)) |
| nptr1 = nod(OINDEX, s, nod(OLEN, l1, N)); |
| nptr1->bounded = 1; |
| nptr1 = nod(OADDR, nptr1, N); |
| |
| nptr2 = nod(OSPTR, l2, N); |
| |
| fn = syslook("memmove", 1); |
| argtype(fn, s->type->type); // 1 old []any |
| argtype(fn, s->type->type); // 2 ret []any |
| |
| nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l); |
| nwid = nod(OMUL, nwid, nodintconst(s->type->type->width)); |
| nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid); |
| l = list(l, nt); |
| } |
| |
| // s = s[:len(l1)+len(l2)] |
| nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N)); |
| nt = nod(OSLICE, s, nod(OKEY, N, nt)); |
| nt->etype = 1; |
| l = list(l, nod(OAS, s, nt)); |
| |
| typechecklist(l, Etop); |
| walkstmtlist(l); |
| *init = concat(*init, l); |
| return s; |
| } |
| |
| // expand append(src, a [, b]* ) to |
| // |
| // init { |
| // s := src |
| // const argc = len(args) - 1 |
| // if cap(s) - len(s) < argc { |
| // s = growslice(s, argc) |
| // } |
| // n := len(s) |
| // s = s[:n+argc] |
| // s[n] = a |
| // s[n+1] = b |
| // ... |
| // } |
| // s |
| static Node* |
| append(Node *n, NodeList **init) |
| { |
| NodeList *l, *a; |
| Node *nsrc, *ns, *nn, *na, *nx, *fn; |
| int argc; |
| |
| walkexprlistsafe(n->list, init); |
| |
| // walkexprlistsafe will leave OINDEX (s[n]) alone if both s |
| // and n are name or literal, but those may index the slice we're |
| // modifying here. Fix explicitly. |
| for(l=n->list; l; l=l->next) |
| l->n = cheapexpr(l->n, init); |
| |
| nsrc = n->list->n; |
| |
| // Resolve slice type of multi-valued return. |
| if(istype(nsrc->type, TSTRUCT)) |
| nsrc->type = nsrc->type->type->type; |
| argc = count(n->list) - 1; |
| if (argc < 1) { |
| return nsrc; |
| } |
| |
| l = nil; |
| |
| ns = temp(nsrc->type); |
| l = list(l, nod(OAS, ns, nsrc)); // s = src |
| |
| na = nodintconst(argc); // const argc |
| nx = nod(OIF, N, N); // if cap(s) - len(s) < argc |
| nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na); |
| |
| fn = syslook("growslice", 1); // growslice(<type>, old []T, n int64) (ret []T) |
| argtype(fn, ns->type->type); // 1 old []any |
| argtype(fn, ns->type->type); // 2 ret []any |
| |
| nx->nbody = list1(nod(OAS, ns, mkcall1(fn, ns->type, &nx->ninit, |
| typename(ns->type), |
| ns, |
| conv(na, types[TINT64])))); |
| l = list(l, nx); |
| |
| nn = temp(types[TINT]);
|