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