root/src/cmd/gc/sinit.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init1
  2. init2
  3. init2list
  4. initreorder
  5. initfix
  6. staticinit
  7. staticcopy
  8. staticassign
  9. staticname
  10. isliteral
  11. simplename
  12. litas
  13. getdyn
  14. structlit
  15. arraylit
  16. slicelit
  17. maplit
  18. anylit
  19. oaslit
  20. getlit
  21. stataddr
  22. gen_as_init
  23. initplan
  24. addvalue
  25. iszero
  26. isvaluelit
  27. entry

// 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)        // probably zeroed but perhaps supplied externally and of unknown value
                return 0;
        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;
                                ll->orig = ll; // completely separate copy
                                if(!staticassign(ll, e->expr, out)) {
                                        // Requires computation, but we're
                                        // copying someone else's computation.
                                        rr = nod(OXXX, N, N);
                                        *rr = *orig;
                                        rr->orig = rr; // completely separate copy
                                        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;
        Strlit *sval;
        
        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 OSTRARRAYBYTE:
                if(l->class == PEXTERN && r->left->op == OLITERAL) {
                        sval = r->left->val.u.sval;
                        slicebytes(l, sval->s, sval->len);
                        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;
                                a->orig = a; // completely separate copy
                                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->alloc != N) {
                // temp allocated during order.c for dddarg
                n->alloc->type = t;
                if(vstat == N) {
                        a = nod(OAS, n->alloc, N);
                        typecheck(&a, Etop);
                        *init = list(*init, a);  // zero new temp
                }
                a = nod(OADDR, n->alloc, N);
        } else if(n->esc == EscNone) {
                a = temp(t);
                if(vstat == N) {
                        a = nod(OAS, temp(t), N);
                        typecheck(&a, Etop);
                        *init = list(*init, a);  // zero new temp
                        a = a->left;
                }
                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->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, *key, *val;
        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("maplit: 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("maplit: 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(OAS, index, nod(OADD, index, nodintconst(1)));

                typecheck(&a, Etop);
                walkstmt(&a);
                *init = list(*init, a);
        }

        // put in dynamic entries one-at-a-time
        key = nil;
        val = nil;
        for(l=n->list; l; l=l->next) {
                r = l->n;

                if(r->op != OKEY)
                        fatal("maplit: rhs not OKEY: %N", r);
                index = r->left;
                value = r->right;

                if(isliteral(index) && isliteral(value))
                        continue;
                        
                // build list of var[c] = expr.
                // use temporary so that mapassign1 can have addressable key, val.
                if(key == nil) {
                        key = temp(var->type->down);
                        val = temp(var->type->type);
                }
                a = nod(OAS, key, r->left);
                typecheck(&a, Etop);
                walkstmt(&a);
                *init = list(*init, a);
                a = nod(OAS, val, r->right);
                typecheck(&a, Etop);
                walkstmt(&a);
                *init = list(*init, a);

                a = nod(OAS, nod(OINDEX, var, key), val);
                typecheck(&a, Etop);
                walkstmt(&a);
                *init = list(*init, a);

                if(nerr != nerrors)
                        break;
        }
        
        if(key != nil) {
                a = nod(OVARKILL, key, N);
                typecheck(&a, Etop);
                *init = list(*init, a);
                a = nod(OVARKILL, val, N);
                typecheck(&a, Etop);
                *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");

                if(n->right != N) {
                        r = nod(OADDR, n->right, N);
                        typecheck(&r, Erv);
                } else {
                        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++];
}

/* [<][>][^][v][top][bottom][index][help] */