blob: 446b1110ac1c25062d1bf61a445fab46b55fca67 [file] [log] [blame]
// 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.
/*
* static initialization
*/
#include <u.h>
#include <libc.h>
#include "go.h"
enum
{
InitNotStarted = 0,
InitDone = 1,
InitPending = 2,
};
static int iszero(Node*);
static void initplan(Node*);
static NodeList *initlist;
static void init2(Node*, NodeList**);
static void init2list(NodeList*, NodeList**);
static int staticinit(Node*, NodeList**);
static Node *staticname(Type*, int);
// init1 walks the AST starting at n, and accumulates in out
// the list of definitions needing init code in dependency order.
static void
init1(Node *n, NodeList **out)
{
NodeList *l;
Node *nv;
if(n == N)
return;
init1(n->left, out);
init1(n->right, out);
for(l=n->list; l; l=l->next)
init1(l->n, out);
if(n->left && n->type && n->left->op == OTYPE && n->class == PFUNC) {
// Methods called as Type.Method(receiver, ...).
// Definitions for method expressions are stored in type->nname.
init1(n->type->nname, out);
}
if(n->op != ONAME)
return;
switch(n->class) {
case PEXTERN:
case PFUNC:
break;
default:
if(isblank(n) && n->curfn == N && n->defn != N && n->defn->initorder == InitNotStarted) {
// blank names initialization is part of init() but not
// when they are inside a function.
break;
}
return;
}
if(n->initorder == InitDone)
return;
if(n->initorder == InitPending) {
// Since mutually recursive sets of functions are allowed,
// we don't necessarily raise an error if n depends on a node
// which is already waiting for its dependencies to be visited.
//
// initlist contains a cycle of identifiers referring to each other.
// If this cycle contains a variable, then this variable refers to itself.
// Conversely, if there exists an initialization cycle involving
// a variable in the program, the tree walk will reach a cycle
// involving that variable.
if(n->class != PFUNC) {
nv = n;
goto foundinitloop;
}
for(l=initlist; l->n!=n; l=l->next) {
if(l->n->class != PFUNC) {
nv = l->n;
goto foundinitloop;
}
}
// The loop involves only functions, ok.
return;
foundinitloop:
// if there have already been errors printed,
// those errors probably confused us and
// there might not be a loop. let the user
// fix those first.
flusherrors();
if(nerrors > 0)
errorexit();
// There is a loop involving nv. We know about
// n and initlist = n1 <- ... <- nv <- ... <- n <- ...
print("%L: initialization loop:\n", nv->lineno);
// Build back pointers in initlist.
for(l=initlist; l; l=l->next)
if(l->next != nil)
l->next->end = l;
// Print nv -> ... -> n1 -> n.
for(l=initlist; l->n!=nv; l=l->next);
for(; l; l=l->end)
print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
// Print n -> ... -> nv.
for(l=initlist; l->n!=n; l=l->next);
for(; l->n != nv; l=l->end)
print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
print("\t%L %S\n", nv->lineno, nv->sym);
errorexit();
}
// reached a new unvisited node.
n->initorder = InitPending;
l = malloc(sizeof *l);
if(l == nil) {
flusherrors();
yyerror("out of memory");
errorexit();
}
l->next = initlist;
l->n = n;
l->end = nil;
initlist = l;
// make sure that everything n depends on is initialized.
// n->defn is an assignment to n
if(n->defn != N) {
switch(n->defn->op) {
default:
goto bad;
case ODCLFUNC:
init2list(n->defn->nbody, out);
break;
case OAS:
if(n->defn->left != n)
goto bad;
if(isblank(n->defn->left) && candiscard(n->defn->right)) {
n->defn->op = OEMPTY;
n->defn->left = N;
n->defn->right = N;
break;
}
init2(n->defn->right, out);
if(debug['j'])
print("%S\n", n->sym);
if(isblank(n) || !staticinit(n, out)) {
if(debug['%'])
dump("nonstatic", n->defn);
*out = list(*out, n->defn);
}
break;
case OAS2FUNC:
case OAS2MAPR:
case OAS2DOTTYPE:
case OAS2RECV:
if(n->defn->initorder != InitNotStarted)
break;
n->defn->initorder = InitDone;
for(l=n->defn->rlist; l; l=l->next)
init1(l->n, out);
if(debug['%']) dump("nonstatic", n->defn);
*out = list(*out, n->defn);
break;
}
}
l = initlist;
initlist = l->next;
if(l->n != n)
fatal("bad initlist");
free(l);
n->initorder = InitDone;
return;
bad:
dump("defn", n->defn);
fatal("init1: bad defn");
}
// recurse over n, doing init1 everywhere.
static void
init2(Node *n, NodeList **out)
{
if(n == N || n->initorder == InitDone)
return;
if(n->op == ONAME && n->ninit)
fatal("name %S with ninit: %+N\n", n->sym, n);
init1(n, out);
init2(n->left, out);
init2(n->right, out);
init2(n->ntest, out);
init2list(n->ninit, out);
init2list(n->list, out);
init2list(n->rlist, out);
init2list(n->nbody, out);
init2list(n->nelse, out);
if(n->op == OCLOSURE)
init2list(n->closure->nbody, out);
if(n->op == ODOTMETH)
init2(n->type->nname, out);
}
static void
init2list(NodeList *l, NodeList **out)
{
for(; l; l=l->next)
init2(l->n, out);
}
static void
initreorder(NodeList *l, NodeList **out)
{
Node *n;
for(; l; l=l->next) {
n = l->n;
switch(n->op) {
case ODCLFUNC:
case ODCLCONST:
case ODCLTYPE:
continue;
}
initreorder(n->ninit, out);
n->ninit = nil;
init1(n, out);
}
}
// initfix computes initialization order for a list l of top-level
// declarations and outputs the corresponding list of statements
// to include in the init() function body.
NodeList*
initfix(NodeList *l)
{
NodeList *lout;
int lno;
lout = nil;
lno = lineno;
initreorder(l, &lout);
lineno = lno;
return lout;
}
/*
* compilation of top-level (static) assignments
* into DATA statements if at all possible.
*/
static int staticassign(Node*, Node*, NodeList**);
static int
staticinit(Node *n, NodeList **out)
{
Node *l, *r;
if(n->op != ONAME || n->class != PEXTERN || n->defn == N || n->defn->op != OAS)
fatal("staticinit");
lineno = n->lineno;
l = n->defn->left;
r = n->defn->right;
return staticassign(l, r, out);
}
// like staticassign but we are copying an already
// initialized value r.
static int
staticcopy(Node *l, Node *r, NodeList **out)
{
int i;
InitEntry *e;
InitPlan *p;
Node *a, *ll, *rr, *orig, n1;
if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg)
return 0;
if(r->defn == N) // zeroed
return 1;
if(r->defn->op != OAS)
return 0;
orig = r;
r = r->defn->right;
switch(r->op) {
case ONAME:
if(staticcopy(l, r, out))
return 1;
*out = list(*out, nod(OAS, l, r));
return 1;
case OLITERAL:
if(iszero(r))
return 1;
gdata(l, r, l->type->width);
return 1;
case OADDR:
switch(r->left->op) {
case ONAME:
gdata(l, r, l->type->width);
return 1;
}
break;
case OPTRLIT:
switch(r->left->op) {
default:
//dump("not static addr", r);
break;
case OARRAYLIT:
case OSTRUCTLIT:
case OMAPLIT:
// copy pointer
gdata(l, nod(OADDR, r->nname, N), l->type->width);
return 1;
}
break;
case OARRAYLIT:
if(isslice(r->type)) {
// copy slice
a = r->nname;
n1 = *l;
n1.xoffset = l->xoffset + Array_array;
gdata(&n1, nod(OADDR, a, N), widthptr);
n1.xoffset = l->xoffset + Array_nel;
gdata(&n1, r->right, widthint);
n1.xoffset = l->xoffset + Array_cap;
gdata(&n1, r->right, widthint);
return 1;
}
// fall through
case OSTRUCTLIT:
p = r->initplan;
n1 = *l;
for(i=0; i<p->len; i++) {
e = &p->e[i];
n1.xoffset = l->xoffset + e->xoffset;
n1.type = e->expr->type;
if(e->expr->op == OLITERAL)
gdata(&n1, e->expr, n1.type->width);
else {
ll = nod(OXXX, N, N);
*ll = n1;
if(!staticassign(ll, e->expr, out)) {
// Requires computation, but we're
// copying someone else's computation.
rr = nod(OXXX, N, N);
*rr = *orig;
rr->type = ll->type;
rr->xoffset += e->xoffset;
*out = list(*out, nod(OAS, ll, rr));
}
}
}
return 1;
}
return 0;
}
static int
staticassign(Node *l, Node *r, NodeList **out)
{
Node *a, n1;
Type *ta;
InitPlan *p;
InitEntry *e;
int i;
switch(r->op) {
default:
//dump("not static", r);
break;
case ONAME:
if(r->class == PEXTERN && r->sym->pkg == localpkg)
return staticcopy(l, r, out);
break;
case OLITERAL:
if(iszero(r))
return 1;
gdata(l, r, l->type->width);
return 1;
case OADDR:
switch(r->left->op) {
default:
//dump("not static addr", r);
break;
case ONAME:
gdata(l, r, l->type->width);
return 1;
}
case OPTRLIT:
switch(r->left->op) {
default:
//dump("not static ptrlit", r);
break;
case OARRAYLIT:
case OMAPLIT:
case OSTRUCTLIT:
// Init pointer.
a = staticname(r->left->type, 1);
r->nname = a;
gdata(l, nod(OADDR, a, N), l->type->width);
// Init underlying literal.
if(!staticassign(a, r->left, out))
*out = list(*out, nod(OAS, a, r->left));
return 1;
}
break;
case OARRAYLIT:
initplan(r);
if(isslice(r->type)) {
// Init slice.
ta = typ(TARRAY);
ta->type = r->type->type;
ta->bound = mpgetfix(r->right->val.u.xval);
a = staticname(ta, 1);
r->nname = a;
n1 = *l;
n1.xoffset = l->xoffset + Array_array;
gdata(&n1, nod(OADDR, a, N), widthptr);
n1.xoffset = l->xoffset + Array_nel;
gdata(&n1, r->right, widthint);
n1.xoffset = l->xoffset + Array_cap;
gdata(&n1, r->right, widthint);
// Fall through to init underlying array.
l = a;
}
// fall through
case OSTRUCTLIT:
initplan(r);
p = r->initplan;
n1 = *l;
for(i=0; i<p->len; i++) {
e = &p->e[i];
n1.xoffset = l->xoffset + e->xoffset;
n1.type = e->expr->type;
if(e->expr->op == OLITERAL)
gdata(&n1, e->expr, n1.type->width);
else {
a = nod(OXXX, N, N);
*a = n1;
if(!staticassign(a, e->expr, out))
*out = list(*out, nod(OAS, a, e->expr));
}
}
return 1;
case OMAPLIT:
// TODO: Table-driven map insert.
break;
}
return 0;
}
/*
* from here down is the walk analysis
* of composite literals.
* most of the work is to generate
* data statements for the constant
* part of the composite literal.
*/
static void structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
static void arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
static void slicelit(int ctxt, Node *n, Node *var, NodeList **init);
static void maplit(int ctxt, Node *n, Node *var, NodeList **init);
static Node*
staticname(Type *t, int ctxt)
{
Node *n;
snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen);
statuniqgen++;
n = newname(lookup(namebuf));
if(!ctxt)
n->readonly = 1;
addvar(n, t, PEXTERN);
return n;
}
static int
isliteral(Node *n)
{
if(n->op == OLITERAL)
if(n->val.ctype != CTNIL)
return 1;
return 0;
}
static int
simplename(Node *n)
{
if(n->op != ONAME)
goto no;
if(!n->addable)
goto no;
if(n->class & PHEAP)
goto no;
if(n->class == PPARAMREF)
goto no;
return 1;
no:
return 0;
}
static void
litas(Node *l, Node *r, NodeList **init)
{
Node *a;
a = nod(OAS, l, r);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
}
enum
{
MODEDYNAM = 1,
MODECONST = 2,
};
static int
getdyn(Node *n, int top)
{
NodeList *nl;
Node *value;
int mode;
mode = 0;
switch(n->op) {
default:
if(isliteral(n))
return MODECONST;
return MODEDYNAM;
case OARRAYLIT:
if(!top && n->type->bound < 0)
return MODEDYNAM;
case OSTRUCTLIT:
break;
}
for(nl=n->list; nl; nl=nl->next) {
value = nl->n->right;
mode |= getdyn(value, 0);
if(mode == (MODEDYNAM|MODECONST))
break;
}
return mode;
}
static void
structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
{
Node *r, *a;
NodeList *nl;
Node *index, *value;
for(nl=n->list; nl; nl=nl->next) {
r = nl->n;
if(r->op != OKEY)
fatal("structlit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
switch(value->op) {
case OARRAYLIT:
if(value->type->bound < 0) {
if(pass == 1 && ctxt != 0) {
a = nod(ODOT, var, newname(index->sym));
slicelit(ctxt, value, a, init);
} else
if(pass == 2 && ctxt == 0) {
a = nod(ODOT, var, newname(index->sym));
slicelit(ctxt, value, a, init);
} else
if(pass == 3)
break;
continue;
}
a = nod(ODOT, var, newname(index->sym));
arraylit(ctxt, pass, value, a, init);
continue;
case OSTRUCTLIT:
a = nod(ODOT, var, newname(index->sym));
structlit(ctxt, pass, value, a, init);
continue;
}
if(isliteral(value)) {
if(pass == 2)
continue;
} else
if(pass == 1)
continue;
// build list of var.field = expr
a = nod(ODOT, var, newname(index->sym));
a = nod(OAS, a, value);
typecheck(&a, Etop);
walkexpr(&a, init);
if(pass == 1) {
if(a->op != OAS)
fatal("structlit: not as");
a->dodata = 2;
}
*init = list(*init, a);
}
}
static void
arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
{
Node *r, *a;
NodeList *l;
Node *index, *value;
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY)
fatal("arraylit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
switch(value->op) {
case OARRAYLIT:
if(value->type->bound < 0) {
if(pass == 1 && ctxt != 0) {
a = nod(OINDEX, var, index);
slicelit(ctxt, value, a, init);
} else
if(pass == 2 && ctxt == 0) {
a = nod(OINDEX, var, index);
slicelit(ctxt, value, a, init);
} else
if(pass == 3)
break;
continue;
}
a = nod(OINDEX, var, index);
arraylit(ctxt, pass, value, a, init);
continue;
case OSTRUCTLIT:
a = nod(OINDEX, var, index);
structlit(ctxt, pass, value, a, init);
continue;
}
if(isliteral(index) && isliteral(value)) {
if(pass == 2)
continue;
} else
if(pass == 1)
continue;
// build list of var[index] = value
a = nod(OINDEX, var, index);
a = nod(OAS, a, value);
typecheck(&a, Etop);
walkexpr(&a, init); // add any assignments in r to top
if(pass == 1) {
if(a->op != OAS)
fatal("structlit: not as");
a->dodata = 2;
}
*init = list(*init, a);
}
}
static void
slicelit(int ctxt, Node *n, Node *var, NodeList **init)
{
Node *r, *a;
NodeList *l;
Type *t;
Node *vstat, *vauto;
Node *index, *value;
int mode;
// make an array type
t = shallow(n->type);
t->bound = mpgetfix(n->right->val.u.xval);
t->width = 0;
t->sym = nil;
t->haspointers = 0;
dowidth(t);
if(ctxt != 0) {
// put everything into static array
vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init);
arraylit(ctxt, 2, n, vstat, init);
// copy static to slice
a = nod(OSLICE, vstat, nod(OKEY, N, N));
a = nod(OAS, var, a);
typecheck(&a, Etop);
a->dodata = 2;
*init = list(*init, a);
return;
}
// recipe for var = []t{...}
// 1. make a static array
// var vstat [...]t
// 2. assign (data statements) the constant part
// vstat = constpart{}
// 3. make an auto pointer to array and allocate heap to it
// var vauto *[...]t = new([...]t)
// 4. copy the static array to the auto array
// *vauto = vstat
// 5. assign slice of allocated heap to var
// var = [0:]*auto
// 6. for each dynamic part assign to the slice
// var[i] = dynamic part
//
// an optimization is done if there is no constant part
// 3. var vauto *[...]t = new([...]t)
// 5. var = [0:]*auto
// 6. var[i] = dynamic part
// if the literal contains constants,
// make static initialized array (1),(2)
vstat = N;
mode = getdyn(n, 1);
if(mode & MODECONST) {
vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init);
}
// make new auto *array (3 declare)
vauto = temp(ptrto(t));
// set auto to point at new temp or heap (3 assign)
if(n->esc == EscNone) {
a = nod(OAS, temp(t), N);
typecheck(&a, Etop);
*init = list(*init, a); // zero new temp
a = nod(OADDR, a->left, N);
} else {
a = nod(ONEW, N, N);
a->list = list1(typenod(t));
}
a = nod(OAS, vauto, a);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
if(vstat != N) {
// copy static to heap (4)
a = nod(OIND, vauto, N);
a = nod(OAS, a, vstat);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
}
// make slice out of heap (5)
a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N)));
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
// put dynamics into slice (6)
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY)
fatal("slicelit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
a = nod(OINDEX, var, index);
a->bounded = 1;
// TODO need to check bounds?
switch(value->op) {
case OARRAYLIT:
if(value->type->bound < 0)
break;
arraylit(ctxt, 2, value, a, init);
continue;
case OSTRUCTLIT:
structlit(ctxt, 2, value, a, init);
continue;
}
if(isliteral(index) && isliteral(value))
continue;
// build list of var[c] = expr
a = nod(OAS, a, value);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
}
}
static void
maplit(int ctxt, Node *n, Node *var, NodeList **init)
{
Node *r, *a;
NodeList *l;
int nerr;
int64 b;
Type *t, *tk, *tv, *t1;
Node *vstat, *index, *value;
Sym *syma, *symb;
USED(ctxt);
ctxt = 0;
// make the map var
nerr = nerrors;
a = nod(OMAKE, N, N);
a->list = list1(typenod(n->type));
litas(var, a, init);
// count the initializers
b = 0;
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY)
fatal("slicelit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
if(isliteral(index) && isliteral(value))
b++;
}
if(b != 0) {
// build type [count]struct { a Tindex, b Tvalue }
t = n->type;
tk = t->down;
tv = t->type;
symb = lookup("b");
t = typ(TFIELD);
t->type = tv;
t->sym = symb;
syma = lookup("a");
t1 = t;
t = typ(TFIELD);
t->type = tk;
t->sym = syma;
t->down = t1;
t1 = t;
t = typ(TSTRUCT);
t->type = t1;
t1 = t;
t = typ(TARRAY);
t->bound = b;
t->type = t1;
dowidth(t);
// make and initialize static array
vstat = staticname(t, ctxt);
b = 0;
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY)
fatal("slicelit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
if(isliteral(index) && isliteral(value)) {
// build vstat[b].a = key;
a = nodintconst(b);
a = nod(OINDEX, vstat, a);
a = nod(ODOT, a, newname(syma));
a = nod(OAS, a, index);
typecheck(&a, Etop);
walkexpr(&a, init);
a->dodata = 2;
*init = list(*init, a);
// build vstat[b].b = value;
a = nodintconst(b);
a = nod(OINDEX, vstat, a);
a = nod(ODOT, a, newname(symb));
a = nod(OAS, a, value);
typecheck(&a, Etop);
walkexpr(&a, init);
a->dodata = 2;
*init = list(*init, a);
b++;
}
}
// loop adding structure elements to map
// for i = 0; i < len(vstat); i++ {
// map[vstat[i].a] = vstat[i].b
// }
index = temp(types[TINT]);
a = nod(OINDEX, vstat, index);
a->bounded = 1;
a = nod(ODOT, a, newname(symb));
r = nod(OINDEX, vstat, index);
r->bounded = 1;
r = nod(ODOT, r, newname(syma));
r = nod(OINDEX, var, r);
r = nod(OAS, r, a);
a = nod(OFOR, N, N);
a->nbody = list1(r);
a->ninit = list1(nod(OAS, index, nodintconst(0)));
a->ntest = nod(OLT, index, nodintconst(t->bound));
a->nincr = nod(OASOP, index, nodintconst(1));
a->nincr->etype = OADD;
typecheck(&a, Etop);
walkstmt(&a);
*init = list(*init, a);
}
// put in dynamic entries one-at-a-time
for(l=n->list; l; l=l->next) {
r = l->n;
if(r->op != OKEY)
fatal("slicelit: rhs not OKEY: %N", r);
index = r->left;
value = r->right;
if(isliteral(index) && isliteral(value))
continue;
// build list of var[c] = expr
a = nod(OINDEX, var, r->left);
a = nod(OAS, a, r->right);
typecheck(&a, Etop);
walkexpr(&a, init);
if(nerr != nerrors)
break;
*init = list(*init, a);
}
}
void
anylit(int ctxt, Node *n, Node *var, NodeList **init)
{
Type *t;
Node *a, *vstat, *r;
t = n->type;
switch(n->op) {
default:
fatal("anylit: not lit");
case OPTRLIT:
if(!isptr[t->etype])
fatal("anylit: not ptr");
r = nod(ONEW, N, N);
r->typecheck = 1;
r->type = t;
r->esc = n->esc;
walkexpr(&r, init);
a = nod(OAS, var, r);
typecheck(&a, Etop);
*init = list(*init, a);
var = nod(OIND, var, N);
typecheck(&var, Erv | Easgn);
anylit(ctxt, n->left, var, init);
break;
case OSTRUCTLIT:
if(t->etype != TSTRUCT)
fatal("anylit: not struct");
if(simplename(var)) {
if(ctxt == 0) {
// lay out static data
vstat = staticname(t, ctxt);
structlit(ctxt, 1, n, vstat, init);
// copy static to var
a = nod(OAS, var, vstat);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
// add expressions to automatic
structlit(ctxt, 2, n, var, init);
break;
}
structlit(ctxt, 1, n, var, init);
structlit(ctxt, 2, n, var, init);
break;
}
// initialize of not completely specified
if(count(n->list) < structcount(t)) {
a = nod(OAS, var, N);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
}
structlit(ctxt, 3, n, var, init);
break;
case OARRAYLIT:
if(t->etype != TARRAY)
fatal("anylit: not array");
if(t->bound < 0) {
slicelit(ctxt, n, var, init);
break;
}
if(simplename(var)) {
if(ctxt == 0) {
// lay out static data
vstat = staticname(t, ctxt);
arraylit(1, 1, n, vstat, init);
// copy static to automatic
a = nod(OAS, var, vstat);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
// add expressions to automatic
arraylit(ctxt, 2, n, var, init);
break;
}
arraylit(ctxt, 1, n, var, init);
arraylit(ctxt, 2, n, var, init);
break;
}
// initialize of not completely specified
if(count(n->list) < t->bound) {
a = nod(OAS, var, N);
typecheck(&a, Etop);
walkexpr(&a, init);
*init = list(*init, a);
}
arraylit(ctxt, 3, n, var, init);
break;
case OMAPLIT:
if(t->etype != TMAP)
fatal("anylit: not map");
maplit(ctxt, n, var, init);
break;
}
}
int
oaslit(Node *n, NodeList **init)
{
int ctxt;
if(n->left == N || n->right == N)
goto no;
if(n->left->type == T || n->right->type == T)
goto no;
if(!simplename(n->left))
goto no;
if(!eqtype(n->left->type, n->right->type))
goto no;
// context is init() function.
// implies generated data executed
// exactly once and not subject to races.
ctxt = 0;
// if(n->dodata == 1)
// ctxt = 1;
switch(n->right->op) {
default:
goto no;
case OSTRUCTLIT:
case OARRAYLIT:
case OMAPLIT:
if(vmatch1(n->left, n->right))
goto no;
anylit(ctxt, n->right, n->left, init);
break;
}
n->op = OEMPTY;
return 1;
no:
// not a special composit literal assignment
return 0;
}
static int
getlit(Node *lit)
{
if(smallintconst(lit))
return mpgetfix(lit->val.u.xval);
return -1;
}
int
stataddr(Node *nam, Node *n)
{
int l;
if(n == N)
goto no;
switch(n->op) {
case ONAME:
*nam = *n;
return n->addable;
case ODOT:
if(!stataddr(nam, n->left))
break;
nam->xoffset += n->xoffset;
nam->type = n->type;
return 1;
case OINDEX:
if(n->left->type->bound < 0)
break;
if(!stataddr(nam, n->left))
break;
l = getlit(n->right);
if(l < 0)
break;
// Check for overflow.
if(n->type->width != 0 && MAXWIDTH/n->type->width <= l)
break;
nam->xoffset += l*n->type->width;
nam->type = n->type;
return 1;
}
no:
return 0;
}
int
gen_as_init(Node *n)
{
Node *nr, *nl;
Node nam, nod1;
if(n->dodata == 0)
goto no;
nr = n->right;
nl = n->left;
if(nr == N) {
if(!stataddr(&nam, nl))
goto no;
if(nam.class != PEXTERN)
goto no;
goto yes;
}
if(nr->type == T || !eqtype(nl->type, nr->type))
goto no;
if(!stataddr(&nam, nl))
goto no;
if(nam.class != PEXTERN)
goto no;
switch(nr->op) {
default:
goto no;
case OCONVNOP:
nr = nr->left;
if(nr == N || nr->op != OSLICEARR)
goto no;
// fall through
case OSLICEARR:
if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) {
nr = nr->left;
goto slice;
}
goto no;
case OLITERAL:
break;
}
switch(nr->type->etype) {
default:
goto no;
case TBOOL:
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TINT:
case TUINT:
case TUINTPTR:
case TPTR32:
case TPTR64:
case TFLOAT32:
case TFLOAT64:
gdata(&nam, nr, nr->type->width);
break;
case TCOMPLEX64:
case TCOMPLEX128:
gdatacomplex(&nam, nr->val.u.cval);
break;
case TSTRING:
gdatastring(&nam, nr->val.u.sval);
break;
}
yes:
return 1;
slice:
gused(N); // in case the data is the dest of a goto
nl = nr;
if(nr == N || nr->op != OADDR)
goto no;
nr = nr->left;
if(nr == N || nr->op != ONAME)
goto no;
// nr is the array being converted to a slice
if(nr->type == T || nr->type->etype != TARRAY || nr->type->bound < 0)
goto no;
nam.xoffset += Array_array;
gdata(&nam, nl, types[tptr]->width);
nam.xoffset += Array_nel-Array_array;
nodconst(&nod1, types[TINT], nr->type->bound);
gdata(&nam, &nod1, widthint);
nam.xoffset += Array_cap-Array_nel;
gdata(&nam, &nod1, widthint);
goto yes;
no:
if(n->dodata == 2) {
dump("\ngen_as_init", n);
fatal("gen_as_init couldnt make data statement");
}
return 0;
}
static int iszero(Node*);
static int isvaluelit(Node*);
static InitEntry* entry(InitPlan*);
static void addvalue(InitPlan*, vlong, Node*, Node*);
static void
initplan(Node *n)
{
InitPlan *p;
Node *a;
NodeList *l;
if(n->initplan != nil)
return;
p = mal(sizeof *p);
n->initplan = p;
switch(n->op) {
default:
fatal("initplan");
case OARRAYLIT:
for(l=n->list; l; l=l->next) {
a = l->n;
if(a->op != OKEY || !smallintconst(a->left))
fatal("initplan arraylit");
addvalue(p, n->type->type->width*mpgetfix(a->left->val.u.xval), N, a->right);
}
break;
case OSTRUCTLIT:
for(l=n->list; l; l=l->next) {
a = l->n;
if(a->op != OKEY || a->left->type == T)
fatal("initplan structlit");
addvalue(p, a->left->type->width, N, a->right);
}
break;
case OMAPLIT:
for(l=n->list; l; l=l->next) {
a = l->n;
if(a->op != OKEY)
fatal("initplan maplit");
addvalue(p, -1, a->left, a->right);
}
break;
}
}
static void
addvalue(InitPlan *p, vlong xoffset, Node *key, Node *n)
{
int i;
InitPlan *q;
InitEntry *e;
USED(key);
// special case: zero can be dropped entirely
if(iszero(n)) {
p->zero += n->type->width;
return;
}
// special case: inline struct and array (not slice) literals
if(isvaluelit(n)) {
initplan(n);
q = n->initplan;
for(i=0; i<q->len; i++) {
e = entry(p);
*e = q->e[i];
e->xoffset += xoffset;
}
return;
}
// add to plan
if(n->op == OLITERAL)
p->lit += n->type->width;
else
p->expr += n->type->width;
e = entry(p);
e->xoffset = xoffset;
e->expr = n;
}
static int
iszero(Node *n)
{
NodeList *l;
switch(n->op) {
case OLITERAL:
switch(n->val.ctype) {
default:
dump("unexpected literal", n);
fatal("iszero");
case CTNIL:
return 1;
case CTSTR:
return n->val.u.sval == nil || n->val.u.sval->len == 0;
case CTBOOL:
return n->val.u.bval == 0;
case CTINT:
case CTRUNE:
return mpcmpfixc(n->val.u.xval, 0) == 0;
case CTFLT:
return mpcmpfltc(n->val.u.fval, 0) == 0;
case CTCPLX:
return mpcmpfltc(&n->val.u.cval->real, 0) == 0 && mpcmpfltc(&n->val.u.cval->imag, 0) == 0;
}
break;
case OARRAYLIT:
if(isslice(n->type))
break;
// fall through
case OSTRUCTLIT:
for(l=n->list; l; l=l->next)
if(!iszero(l->n->right))
return 0;
return 1;
}
return 0;
}
static int
isvaluelit(Node *n)
{
return (n->op == OARRAYLIT && isfixedarray(n->type)) || n->op == OSTRUCTLIT;
}
static InitEntry*
entry(InitPlan *p)
{
if(p->len >= p->cap) {
if(p->cap == 0)
p->cap = 4;
else
p->cap *= 2;
p->e = realloc(p->e, p->cap*sizeof p->e[0]);
if(p->e == nil)
fatal("out of memory");
}
return &p->e[p->len++];
}