| // 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 "go.h" | |
| static NodeList *initlist; | |
| static void init2(Node*, NodeList**); | |
| static void init2list(NodeList*, NodeList**); | |
| 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)) | |
| *out = list(*out, n->defn); | |
| return; | |
| } | |
| if(n->initorder == 1) | |
| return; | |
| if(n->initorder == 2) { | |
| 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 = 2; | |
| 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; | |
| case OAS2FUNC: | |
| if(n->defn->initorder) | |
| break; | |
| n->defn->initorder = 1; | |
| for(l=n->defn->rlist; l; l=l->next) | |
| init1(l->n, out); | |
| *out = list(*out, n->defn); | |
| break; | |
| case OAS2MAPR: | |
| if(n->defn->initorder) | |
| break; | |
| n->defn->initorder = 1; | |
| 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 = 1; | |
| 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 == 1) | |
| 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; | |
| lout = nil; | |
| initreorder(l, &lout); | |
| return lout; | |
| } | |
| /* | |
| * from here down is the walk analysis | |
| * of composit literals. | |
| * most of the work is to generate | |
| * data statements for the constant | |
| * part of the composit literal. | |
| */ | |
| 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 arraylit(Node *n, Node *var, int pass, NodeList **init); | |
| static void | |
| structlit(Node *n, Node *var, int pass, 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) | |
| break; | |
| a = nod(ODOT, var, newname(index->sym)); | |
| arraylit(value, a, pass, init); | |
| continue; | |
| case OSTRUCTLIT: | |
| a = nod(ODOT, var, newname(index->sym)); | |
| structlit(value, a, pass, 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(Node *n, Node *var, int pass, 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) | |
| break; | |
| a = nod(OINDEX, var, index); | |
| arraylit(value, a, pass, init); | |
| continue; | |
| case OSTRUCTLIT: | |
| a = nod(OINDEX, var, index); | |
| structlit(value, a, pass, 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(Node *n, Node *var, NodeList **init) | |
| { | |
| Node *r, *a; | |
| NodeList *l; | |
| Type *t; | |
| Node *vstat, *vheap; | |
| Node *index, *value; | |
| // make an array type | |
| t = shallow(n->type); | |
| t->bound = mpgetfix(n->right->val.u.xval); | |
| t->width = 0; | |
| t->sym = nil; | |
| dowidth(t); | |
| // make static initialized array | |
| vstat = staticname(t); | |
| arraylit(n, vstat, 1, init); | |
| // make new *array heap | |
| vheap = nod(OXXX, N, N); | |
| tempname(vheap, ptrto(t)); | |
| a = nod(ONEW, N, N); | |
| a->list = list1(typenod(t)); | |
| a = nod(OAS, vheap, a); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); | |
| *init = list(*init, a); | |
| // copy static to heap | |
| a = nod(OIND, vheap, N); | |
| a = nod(OAS, a, vstat); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); | |
| *init = list(*init, a); | |
| // make slice out of heap | |
| a = nod(OAS, var, vheap); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); | |
| *init = list(*init, a); | |
| // put dynamics into slice | |
| 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; | |
| switch(value->op) { | |
| case OARRAYLIT: | |
| if(value->type->bound < 0) | |
| break; | |
| a = nod(OINDEX, var, index); | |
| arraylit(value, a, 2, init); | |
| continue; | |
| case OSTRUCTLIT: | |
| a = nod(OINDEX, var, index); | |
| structlit(value, a, 2, init); | |
| continue; | |
| } | |
| if(isliteral(index) && isliteral(value)) | |
| continue; | |
| // build list of var[c] = expr | |
| a = nod(OINDEX, var, index); | |
| a = nod(OAS, a, value); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); // add any assignments in r to top | |
| *init = list(*init, a); | |
| } | |
| } | |
| static void | |
| maplit(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; | |
| // make the map var | |
| nerr = nerrors; | |
| a = nod(OMAKE, N, N); | |
| a->list = list1(typenod(n->type)); | |
| a = nod(OAS, var, a); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); | |
| *init = list(*init, a); | |
| // 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++; | |
| } | |
| t = T; | |
| 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); | |
| 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 = nod(OXXX, N, N); | |
| tempname(index, types[TINT]); | |
| a = nod(OINDEX, vstat, index); | |
| a = nod(ODOT, a, newname(symb)); | |
| r = nod(OINDEX, vstat, index); | |
| 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(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)) { | |
| // lay out static data | |
| vstat = staticname(t); | |
| structlit(n, vstat, 1, init); | |
| // copy static to automatic | |
| a = nod(OAS, var, vstat); | |
| typecheck(&a, Etop); | |
| walkexpr(&a, init); | |
| *init = list(*init, a); | |
| // add expressions to automatic | |
| structlit(n, var, 2, 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(n, var, 3, init); | |
| break; | |
| case OARRAYLIT: | |
| if(t->etype != TARRAY) | |
| fatal("anylit: not array"); | |
| if(t->bound < 0) { | |
| slicelit(n, var, init); | |
| break; | |
| } | |
| if(simplename(var)) { | |
| // lay out static data | |
| vstat = staticname(t); | |
| arraylit(n, vstat, 1, 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(n, var, 2, 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(n, var, 3, init); | |
| break; | |
| case OMAPLIT: | |
| if(t->etype != TMAP) | |
| fatal("anylit: not map"); | |
| maplit(n, var, init); | |
| break; | |
| } | |
| } | |
| int | |
| oaslit(Node *n, NodeList **init) | |
| { | |
| Type *t; | |
| Node *vstat, *a; | |
| 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; | |
| if(n->dodata == 1) | |
| goto initctxt; | |
| switch(n->right->op) { | |
| default: | |
| goto no; | |
| case OSTRUCTLIT: | |
| case OARRAYLIT: | |
| case OMAPLIT: | |
| if(vmatch1(n->left, n->right)) | |
| goto no; | |
| anylit(n->right, n->left, init); | |
| break; | |
| } | |
| n->op = OEMPTY; | |
| return 1; | |
| no: | |
| // not a special composit literal assignment | |
| return 0; | |
| initctxt: | |
| // in the initialization context | |
| // we are trying to put data statements | |
| // right into the initialized variables | |
| switch(n->right->op) { | |
| default: | |
| goto no; | |
| case OSTRUCTLIT: | |
| structlit(n->right, n->left, 1, init); | |
| structlit(n->right, n->left, 2, init); | |
| break; | |
| case OARRAYLIT: | |
| t = n->right->type; | |
| if(t == T) | |
| goto no; | |
| if(t->bound >= 0) { | |
| arraylit(n->right, n->left, 1, init); | |
| arraylit(n->right, n->left, 2, init); | |
| break; | |
| } | |
| // make a static slice | |
| // make an array type | |
| t = shallow(t); | |
| t->bound = mpgetfix(n->right->right->val.u.xval); | |
| t->width = 0; | |
| t->sym = nil; | |
| dowidth(t); | |
| // make static initialized array | |
| vstat = staticname(t); | |
| arraylit(n->right, vstat, 1, init); | |
| arraylit(n->right, vstat, 2, init); | |
| // copy static to slice | |
| a = nod(OADDR, vstat, N); | |
| a = nod(OAS, n->left, a); | |
| typecheck(&a, Etop); | |
| // turns into a function that is hard to parse | |
| // in ggen where it is turned into DATA statements | |
| // walkexpr(&a, init); | |
| a->dodata = 2; | |
| *init = list(*init, a); | |
| break; | |
| case OMAPLIT: | |
| maplit(n->right, n->left, init); | |
| break; | |
| } | |
| n->op = OEMPTY; | |
| return 1; | |
| } | |
| 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 OCONVSLICE: | |
| goto slice; | |
| 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: | |
| case TFLOAT: | |
| gused(N); // in case the data is the dest of a goto | |
| gdata(&nam, nr, nr->type->width); | |
| break; | |
| case TCOMPLEX64: | |
| case TCOMPLEX128: | |
| case TCOMPLEX: | |
| gused(N); // in case the data is the dest of a goto | |
| gdatacomplex(&nam, nr->val.u.cval); | |
| break; | |
| case TSTRING: | |
| gused(N); // in case the data is the dest of a goto | |
| gdatastring(&nam, nr->val.u.sval); | |
| break; | |
| } | |
| yes: | |
| return 1; | |
| slice: | |
| gused(N); // in case the data is the dest of a goto | |
| nr = n->right->left; | |
| 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, n->right->left, 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; | |
| } | |