| // 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); | |
| static void | |
| init1(Node *n, NodeList **out) | |
| { | |
| NodeList *l; | |
| 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->op != ONAME) | |
| return; | |
| switch(n->class) { | |
| case PEXTERN: | |
| case PFUNC: | |
| break; | |
| default: | |
| if(isblank(n) && n->defn != N && n->defn->initorder == InitNotStarted) { | |
| n->defn->initorder = InitDone; | |
| *out = list(*out, n->defn); | |
| } | |
| return; | |
| } | |
| if(n->initorder == InitDone) | |
| return; | |
| if(n->initorder == InitPending) { | |
| if(n->class == PFUNC) | |
| return; | |
| // 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(); | |
| print("initialization loop:\n"); | |
| for(l=initlist;; l=l->next) { | |
| if(l->next == nil) | |
| break; | |
| l->next->end = l; | |
| } | |
| for(; l; l=l->end) | |
| print("\t%L %S refers to\n", l->n->lineno, l->n->sym); | |
| print("\t%L %S\n", n->lineno, n->sym); | |
| errorexit(); | |
| } | |
| n->initorder = InitPending; | |
| l = malloc(sizeof *l); | |
| 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; | |
| /* | |
| n->defn->dodata = 1; | |
| init1(n->defn->right, out); | |
| if(debug['j']) | |
| print("%S\n", n->sym); | |
| *out = list(*out, n->defn); | |
| break; | |
| */ | |
| if(1) { | |
| init1(n->defn->right, out); | |
| if(debug['j']) | |
| print("%S\n", n->sym); | |
| if(!staticinit(n, out)) { | |
| if(debug['%']) dump("nonstatic", n->defn); | |
| *out = list(*out, n->defn); | |
| } | |
| } else if(0) { | |
| n->defn->dodata = 1; | |
| init1(n->defn->right, out); | |
| if(debug['j']) | |
| print("%S\n", n->sym); | |
| *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); | |
| *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; | |
| 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); | |
| } | |
| 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); | |
| } | |
| } | |
| 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; | |
| 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, 4); | |
| n1.xoffset = l->xoffset + Array_cap; | |
| gdata(&n1, r->right, 4); | |
| 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 if(staticassign(&n1, e->expr, out)) { | |
| // Done | |
| } else { | |
| // Requires computation, but we're | |
| // copying someone else's computation. | |
| ll = nod(OXXX, N, N); | |
| *ll = n1; | |
| 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 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, 4); | |
| n1.xoffset = l->xoffset + Array_cap; | |
| gdata(&n1, r->right, 4); | |
| // 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 if(staticassign(&n1, e->expr, out)) { | |
| // done | |
| } else { | |
| a = nod(OXXX, N, N); | |
| *a = n1; | |
| *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; | |
| 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 = temp(t); | |
| *init = list(*init, nod(OAS, a, N)); // zero new temp | |
| a = nod(OADDR, a, 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->etype = 1; // no bounds checking | |
| // 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, 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->etype = 1; // no bounds checking | |
| a = nod(ODOT, a, newname(symb)); | |
| r = nod(OINDEX, vstat, index); | |
| r->etype = 1; // no bounds checking | |
| 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; | |
| t = n->type; | |
| switch(n->op) { | |
| default: | |
| fatal("anylit: not lit"); | |
| 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; | |
| 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[TINT32], nr->type->bound); | |
| gdata(&nam, &nod1, types[TINT32]->width); | |
| nam.xoffset += Array_cap-Array_nel; | |
| gdata(&nam, &nod1, types[TINT32]->width); | |
| 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: | |
| 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++]; | |
| } |