root/src/cmd/5g/cgen.c

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

DEFINITIONS

This source file includes following definitions.
  1. cgen
  2. cgenindex
  3. agen
  4. igen
  5. cgenr
  6. agenr
  7. gencmp0
  8. bgen
  9. stkof
  10. sgen
  11. cadable
  12. componentgen

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <u.h>
#include <libc.h>
#include "gg.h"

/*
 * generate:
 *      res = n;
 * simplifies and calls gmove.
 */
void
cgen(Node *n, Node *res)
{
        Node *nl, *nr, *r;
        Node n1, n2, f0, f1;
        int a, w, rg;
        Prog *p1, *p2, *p3;
        Addr addr;

        if(debug['g']) {
                dump("\ncgen-n", n);
                dump("cgen-res", res);
        }
        if(n == N || n->type == T)
                goto ret;

        if(res == N || res->type == T)
                fatal("cgen: res nil");

        switch(n->op) {
        case OSLICE:
        case OSLICEARR:
        case OSLICESTR:
        case OSLICE3:
        case OSLICE3ARR:
                if (res->op != ONAME || !res->addable) {
                        tempname(&n1, n->type);
                        cgen_slice(n, &n1);
                        cgen(&n1, res);
                } else
                        cgen_slice(n, res);
                return;
        case OEFACE:
                if (res->op != ONAME || !res->addable) {
                        tempname(&n1, n->type);
                        cgen_eface(n, &n1);
                        cgen(&n1, res);
                } else
                        cgen_eface(n, res);
                return;
        }

        while(n->op == OCONVNOP)
                n = n->left;

        if(n->ullman >= UINF) {
                if(n->op == OINDREG)
                        fatal("cgen: this is going to misscompile");
                if(res->ullman >= UINF) {
                        tempname(&n1, n->type);
                        cgen(n, &n1);
                        cgen(&n1, res);
                        goto ret;
                }
        }

        if(isfat(n->type)) {
                if(n->type->width < 0)
                        fatal("forgot to compute width for %T", n->type);
                sgen(n, res, n->type->width);
                goto ret;
        }


        // update addressability for string, slice
        // can't do in walk because n->left->addable
        // changes if n->left is an escaping local variable.
        switch(n->op) {
        case OSPTR:
        case OLEN:
                if(isslice(n->left->type) || istype(n->left->type, TSTRING))
                        n->addable = n->left->addable;
                break;
        case OCAP:
                if(isslice(n->left->type))
                        n->addable = n->left->addable;
                break;
        case OITAB:
                n->addable = n->left->addable;
                break;
        }

        // if both are addressable, move
        if(n->addable && res->addable) {
                if(is64(n->type) || is64(res->type) ||
                   n->op == OREGISTER || res->op == OREGISTER ||
                   iscomplex[n->type->etype] || iscomplex[res->type->etype]) {
                        gmove(n, res);
                } else {
                        regalloc(&n1, n->type, N);
                        gmove(n, &n1);
                        cgen(&n1, res);
                        regfree(&n1);
                }
                goto ret;
        }

        // if both are not addressable, use a temporary.
        if(!n->addable && !res->addable) {
                // could use regalloc here sometimes,
                // but have to check for ullman >= UINF.
                tempname(&n1, n->type);
                cgen(n, &n1);
                cgen(&n1, res);
                return;
        }

        // if result is not addressable directly but n is,
        // compute its address and then store via the address.
        if(!res->addable) {
                igen(res, &n1, N);
                cgen(n, &n1);
                regfree(&n1);
                return;
        }

        if(complexop(n, res)) {
                complexgen(n, res);
                return;
        }

        // if n is sudoaddable generate addr and move
        if (!is64(n->type) && !is64(res->type) && !iscomplex[n->type->etype] && !iscomplex[res->type->etype]) {
                a = optoas(OAS, n->type);
                if(sudoaddable(a, n, &addr, &w)) {
                        if (res->op != OREGISTER) {
                                regalloc(&n2, res->type, N);
                                p1 = gins(a, N, &n2);
                                p1->from = addr;
                                if(debug['g'])
                                        print("%P [ignore previous line]\n", p1);
                                gmove(&n2, res);
                                regfree(&n2);
                        } else {
                                p1 = gins(a, N, res);
                                p1->from = addr;
                                if(debug['g'])
                                        print("%P [ignore previous line]\n", p1);
                        }
                        sudoclean();
                        goto ret;
                }
        }

        // otherwise, the result is addressable but n is not.
        // let's do some computation.

        nl = n->left;
        nr = n->right;

        if(nl != N && nl->ullman >= UINF)
        if(nr != N && nr->ullman >= UINF) {
                tempname(&n1, nl->type);
                cgen(nl, &n1);
                n2 = *n;
                n2.left = &n1;
                cgen(&n2, res);
                goto ret;
        }

        // 64-bit ops are hard on 32-bit machine.
        if(is64(n->type) || is64(res->type) || n->left != N && is64(n->left->type)) {
                switch(n->op) {
                // math goes to cgen64.
                case OMINUS:
                case OCOM:
                case OADD:
                case OSUB:
                case OMUL:
                case OLROT:
                case OLSH:
                case ORSH:
                case OAND:
                case OOR:
                case OXOR:
                        cgen64(n, res);
                        return;
                }
        }

        if(nl != N && isfloat[n->type->etype] && isfloat[nl->type->etype])
                goto flt;
        switch(n->op) {
        default:
                dump("cgen", n);
                fatal("cgen: unknown op %+hN", n);
                break;

        case OREAL:
        case OIMAG:
        case OCOMPLEX:
                fatal("unexpected complex");
                break;

        // these call bgen to get a bool value
        case OOROR:
        case OANDAND:
        case OEQ:
        case ONE:
        case OLT:
        case OLE:
        case OGE:
        case OGT:
        case ONOT:
                p1 = gbranch(AB, T, 0);
                p2 = pc;
                gmove(nodbool(1), res);
                p3 = gbranch(AB, T, 0);
                patch(p1, pc);
                bgen(n, 1, 0, p2);
                gmove(nodbool(0), res);
                patch(p3, pc);
                goto ret;

        case OPLUS:
                cgen(nl, res);
                goto ret;

        // unary
        case OCOM:
                a = optoas(OXOR, nl->type);
                regalloc(&n1, nl->type, N);
                cgen(nl, &n1);
                nodconst(&n2, nl->type, -1);
                gins(a, &n2, &n1);
                gmove(&n1, res);
                regfree(&n1);
                goto ret;

        case OMINUS:
                regalloc(&n1, nl->type, N);
                cgen(nl, &n1);
                nodconst(&n2, nl->type, 0);
                gins(optoas(OMINUS, nl->type), &n2, &n1);
                gmove(&n1, res);
                regfree(&n1);
                goto ret;

        // symmetric binary
        case OAND:
        case OOR:
        case OXOR:
        case OADD:
        case OADDPTR:
        case OMUL:
                a = optoas(n->op, nl->type);
                goto sbop;

        // asymmetric binary
        case OSUB:
                a = optoas(n->op, nl->type);
                goto abop;

        case OHMUL:
                cgen_hmul(nl, nr, res);
                break;

        case OLROT:
        case OLSH:
        case ORSH:
                cgen_shift(n->op, n->bounded, nl, nr, res);
                break;

        case OCONV:
                if(eqtype(n->type, nl->type) || noconv(n->type, nl->type)) {
                        cgen(nl, res);
                        break;
                }
                if(nl->addable && !is64(nl->type)) {
                        regalloc(&n1, nl->type, res);
                        gmove(nl, &n1);
                } else {
                        if(n->type->width > widthptr || is64(nl->type) || isfloat[nl->type->etype])
                                tempname(&n1, nl->type);
                        else
                                regalloc(&n1, nl->type, res);
                        cgen(nl, &n1);
                }
                if(n->type->width > widthptr || is64(n->type) || isfloat[n->type->etype])
                        tempname(&n2, n->type);
                else
                        regalloc(&n2, n->type, N);
                gmove(&n1, &n2);
                gmove(&n2, res);
                if(n1.op == OREGISTER)
                        regfree(&n1);
                if(n2.op == OREGISTER)
                        regfree(&n2);
                break;

        case ODOT:
        case ODOTPTR:
        case OINDEX:
        case OIND:
        case ONAME:     // PHEAP or PPARAMREF var
                igen(n, &n1, res);
                gmove(&n1, res);
                regfree(&n1);
                break;

        case OITAB:
                // interface table is first word of interface value
                igen(nl, &n1, res);
                n1.type = n->type;
                gmove(&n1, res);
                regfree(&n1);
                break;

        case OSPTR:
                // pointer is the first word of string or slice.
                if(isconst(nl, CTSTR)) {
                        regalloc(&n1, types[tptr], res);
                        p1 = gins(AMOVW, N, &n1);
                        datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
                        gmove(&n1, res);
                        regfree(&n1);
                        break;
                }
                igen(nl, &n1, res);
                n1.type = n->type;
                gmove(&n1, res);
                regfree(&n1);
                break;

        case OLEN:
                if(istype(nl->type, TMAP) || istype(nl->type, TCHAN)) {
                        // map has len in the first 32-bit word.
                        // a zero pointer means zero length
                        regalloc(&n1, types[tptr], res);
                        cgen(nl, &n1);

                        nodconst(&n2, types[tptr], 0);
                        gcmp(optoas(OCMP, types[tptr]), &n1, &n2);
                        p1 = gbranch(optoas(OEQ, types[tptr]), T, -1);

                        n2 = n1;
                        n2.op = OINDREG;
                        n2.type = types[TINT32];
                        gmove(&n2, &n1);

                        patch(p1, pc);

                        gmove(&n1, res);
                        regfree(&n1);
                        break;
                }
                if(istype(nl->type, TSTRING) || isslice(nl->type)) {
                        // both slice and string have len one pointer into the struct.
                        igen(nl, &n1, res);
                        n1.type = types[TUINT32];
                        n1.xoffset += Array_nel;
                        gmove(&n1, res);
                        regfree(&n1);
                        break;
                }
                fatal("cgen: OLEN: unknown type %lT", nl->type);
                break;

        case OCAP:
                if(istype(nl->type, TCHAN)) {
                        // chan has cap in the second 32-bit word.
                        // a zero pointer means zero length
                        regalloc(&n1, types[tptr], res);
                        cgen(nl, &n1);

                        nodconst(&n2, types[tptr], 0);
                        gcmp(optoas(OCMP, types[tptr]), &n1, &n2);
                        p1 = gbranch(optoas(OEQ, types[tptr]), T, -1);

                        n2 = n1;
                        n2.op = OINDREG;
                        n2.xoffset = 4;
                        n2.type = types[TINT32];
                        gmove(&n2, &n1);

                        patch(p1, pc);

                        gmove(&n1, res);
                        regfree(&n1);
                        break;
                }
                if(isslice(nl->type)) {
                        igen(nl, &n1, res);
                        n1.type = types[TUINT32];
                        n1.xoffset += Array_cap;
                        gmove(&n1, res);
                        regfree(&n1);
                        break;
                }
                fatal("cgen: OCAP: unknown type %lT", nl->type);
                break;

        case OADDR:
                agen(nl, res);
                break;

        case OCALLMETH:
        case OCALLFUNC:
                // Release res so that it is available for cgen_call.
                // Pick it up again after the call.
                rg = -1;
                if(n->ullman >= UINF) {
                        if(res != N && (res->op == OREGISTER || res->op == OINDREG)) {
                                rg = res->val.u.reg;
                                reg[rg]--;
                        }
                }
                if(n->op == OCALLMETH)
                        cgen_callmeth(n, 0);
                else
                        cgen_call(n, 0);
                if(rg >= 0)
                        reg[rg]++;
                cgen_callret(n, res);
                break;

        case OCALLINTER:
                cgen_callinter(n, res, 0);
                cgen_callret(n, res);
                break;

        case OMOD:
        case ODIV:
                a = optoas(n->op, nl->type);
                goto abop;
        }
        goto ret;

sbop:   // symmetric binary
        if(nl->ullman < nr->ullman) {
                r = nl;
                nl = nr;
                nr = r;
        }

abop:   // asymmetric binary
        // TODO(kaib): use fewer registers here.
        if(nl->ullman >= nr->ullman) {
                regalloc(&n1, nl->type, res);
                cgen(nl, &n1);
                switch(n->op) {
                case OADD:
                case OSUB:
                case OAND:
                case OOR:
                case OXOR:
                        if(smallintconst(nr)) {
                                n2 = *nr;
                                break;
                        }
                default:
                        regalloc(&n2, nr->type, N);
                        cgen(nr, &n2);
                }
        } else {
                switch(n->op) {
                case OADD:
                case OSUB:
                case OAND:
                case OOR:
                case OXOR:
                        if(smallintconst(nr)) {
                                n2 = *nr;
                                break;
                        }
                default:
                        regalloc(&n2, nr->type, res);
                        cgen(nr, &n2);
                }
                regalloc(&n1, nl->type, N);
                cgen(nl, &n1);
        }
        gins(a, &n2, &n1);
        // Normalize result for types smaller than word.
        if(n->type->width < widthptr) {
                switch(n->op) {
                case OADD:
                case OSUB:
                case OMUL:
                        gins(optoas(OAS, n->type), &n1, &n1);
                        break;
                }
        }
        gmove(&n1, res);
        regfree(&n1);
        if(n2.op != OLITERAL)
                regfree(&n2);
        goto ret;

flt:    // floating-point.
        regalloc(&f0, nl->type, res);
        if(nr != N)
                goto flt2;

        if(n->op == OMINUS) {
                nr = nodintconst(-1);
                convlit(&nr, n->type);
                n->op = OMUL;
                goto flt2;
        }

        // unary
        cgen(nl, &f0);
        if(n->op != OCONV && n->op != OPLUS)
                gins(optoas(n->op, n->type), &f0, &f0);
        gmove(&f0, res);
        regfree(&f0);
        goto ret;

flt2:   // binary
        if(nl->ullman >= nr->ullman) {
                cgen(nl, &f0);
                regalloc(&f1, n->type, N);
                gmove(&f0, &f1);
                cgen(nr, &f0);
                gins(optoas(n->op, n->type), &f0, &f1);
        } else {
                cgen(nr, &f0);
                regalloc(&f1, n->type, N);
                cgen(nl, &f1);
                gins(optoas(n->op, n->type), &f0, &f1);
        }
        gmove(&f1, res);
        regfree(&f0);
        regfree(&f1);
        goto ret;

ret:
        ;
}

/*
 * generate array index into res.
 * n might be any size; res is 32-bit.
 * returns Prog* to patch to panic call.
 */
Prog*
cgenindex(Node *n, Node *res, int bounded)
{
        Node tmp, lo, hi, zero, n1, n2;

        if(!is64(n->type)) {
                cgen(n, res);
                return nil;
        }

        tempname(&tmp, types[TINT64]);
        cgen(n, &tmp);
        split64(&tmp, &lo, &hi);
        gmove(&lo, res);
        if(bounded) {
                splitclean();
                return nil;
        }
        regalloc(&n1, types[TINT32], N);
        regalloc(&n2, types[TINT32], N);
        nodconst(&zero, types[TINT32], 0);
        gmove(&hi, &n1);
        gmove(&zero, &n2);
        gcmp(ACMP, &n1, &n2);
        regfree(&n2);
        regfree(&n1);
        splitclean();
        return gbranch(ABNE, T, -1);
}

/*
 * generate:
 *      res = &n;
 * The generated code checks that the result is not nil.
 */
void
agen(Node *n, Node *res)
{
        Node *nl;
        Node n1, n2, n3;
        int r;

        if(debug['g']) {
                dump("\nagen-res", res);
                dump("agen-r", n);
        }
        if(n == N || n->type == T || res == N || res->type == T)
                fatal("agen");

        while(n->op == OCONVNOP)
                n = n->left;

        if(isconst(n, CTNIL) && n->type->width > widthptr) {
                // Use of a nil interface or nil slice.
                // Create a temporary we can take the address of and read.
                // The generated code is just going to panic, so it need not
                // be terribly efficient. See issue 3670.
                tempname(&n1, n->type);
                gvardef(&n1);
                clearfat(&n1);
                regalloc(&n2, types[tptr], res);
                gins(AMOVW, &n1, &n2);
                gmove(&n2, res);
                regfree(&n2);
                goto ret;
        }
                

        if(n->addable) {
                memset(&n1, 0, sizeof n1);
                n1.op = OADDR;
                n1.left = n;
                regalloc(&n2, types[tptr], res);
                gins(AMOVW, &n1, &n2);
                gmove(&n2, res);
                regfree(&n2);
                goto ret;
        }

        nl = n->left;

        switch(n->op) {
        default:
                fatal("agen: unknown op %+hN", n);
                break;

        case OCALLMETH:
        case OCALLFUNC:
                // Release res so that it is available for cgen_call.
                // Pick it up again after the call.
                r = -1;
                if(n->ullman >= UINF) {
                        if(res->op == OREGISTER || res->op == OINDREG) {
                                r = res->val.u.reg;
                                reg[r]--;
                        }
                }
                if(n->op == OCALLMETH)
                        cgen_callmeth(n, 0);
                else
                        cgen_call(n, 0);
                if(r >= 0)
                        reg[r]++;
                cgen_aret(n, res);
                break;

        case OCALLINTER:
                cgen_callinter(n, res, 0);
                cgen_aret(n, res);
                break;

        case OSLICE:
        case OSLICEARR:
        case OSLICESTR:
        case OSLICE3:
        case OSLICE3ARR:
                tempname(&n1, n->type);
                cgen_slice(n, &n1);
                agen(&n1, res);
                break;

        case OEFACE:
                tempname(&n1, n->type);
                cgen_eface(n, &n1);
                agen(&n1, res);
                break;

        case OINDEX:
                agenr(n, &n1, res);
                gmove(&n1, res);
                regfree(&n1);
                break;

        case ONAME:
                // should only get here with names in this func.
                if(n->funcdepth > 0 && n->funcdepth != funcdepth) {
                        dump("bad agen", n);
                        fatal("agen: bad ONAME funcdepth %d != %d",
                                n->funcdepth, funcdepth);
                }

                // should only get here for heap vars or paramref
                if(!(n->class & PHEAP) && n->class != PPARAMREF) {
                        dump("bad agen", n);
                        fatal("agen: bad ONAME class %#x", n->class);
                }
                cgen(n->heapaddr, res);
                if(n->xoffset != 0) {
                        nodconst(&n1, types[TINT32], n->xoffset);
                        regalloc(&n2, n1.type, N);
                        regalloc(&n3, types[TINT32], N);
                        gmove(&n1, &n2);
                        gmove(res, &n3);
                        gins(optoas(OADD, types[tptr]), &n2, &n3);
                        gmove(&n3, res);
                        regfree(&n2);
                        regfree(&n3);
                }
                break;

        case OIND:
                cgen(nl, res);
                cgen_checknil(res);
                break;

        case ODOT:
                agen(nl, res);
                if(n->xoffset != 0) {
                        nodconst(&n1, types[TINT32], n->xoffset);
                        regalloc(&n2, n1.type, N);
                        regalloc(&n3, types[TINT32], N);
                        gmove(&n1, &n2);
                        gmove(res, &n3);
                        gins(optoas(OADD, types[tptr]), &n2, &n3);
                        gmove(&n3, res);
                        regfree(&n2);
                        regfree(&n3);
                }
                break;

        case ODOTPTR:
                cgen(nl, res);
                cgen_checknil(res);
                if(n->xoffset != 0) {
                        nodconst(&n1, types[TINT32], n->xoffset);
                        regalloc(&n2, n1.type, N);
                        regalloc(&n3, types[tptr], N);
                        gmove(&n1, &n2);
                        gmove(res, &n3);
                        gins(optoas(OADD, types[tptr]), &n2, &n3);
                        gmove(&n3, res);
                        regfree(&n2);
                        regfree(&n3);
                }
                break;
        }

ret:
        ;
}

/*
 * generate:
 *      newreg = &n;
 *      res = newreg
 *
 * on exit, a has been changed to be *newreg.
 * caller must regfree(a).
 * The generated code checks that the result is not *nil.
 */
void
igen(Node *n, Node *a, Node *res)
{
        Node n1;
        int r;

        if(debug['g']) {
                dump("\nigen-n", n);
        }
        switch(n->op) {
        case ONAME:
                if((n->class&PHEAP) || n->class == PPARAMREF)
                        break;
                *a = *n;
                return;

        case OINDREG:
                // Increase the refcount of the register so that igen's caller
                // has to call regfree.
                if(n->val.u.reg != REGSP)
                        reg[n->val.u.reg]++;
                *a = *n;
                return;

        case ODOT:
                igen(n->left, a, res);
                a->xoffset += n->xoffset;
                a->type = n->type;
                return;

        case ODOTPTR:
                if(n->left->addable
                        || n->left->op == OCALLFUNC
                        || n->left->op == OCALLMETH
                        || n->left->op == OCALLINTER) {
                        // igen-able nodes.
                        igen(n->left, &n1, res);
                        regalloc(a, types[tptr], &n1);
                        gmove(&n1, a);
                        regfree(&n1);
                } else {
                        regalloc(a, types[tptr], res);
                        cgen(n->left, a);
                }
                cgen_checknil(a);
                a->op = OINDREG;
                a->xoffset = n->xoffset;
                a->type = n->type;
                return;

        case OCALLMETH:
        case OCALLFUNC:
        case OCALLINTER:
                // Release res so that it is available for cgen_call.
                // Pick it up again after the call.
                r = -1;
                if(n->ullman >= UINF) {
                        if(res != N && (res->op == OREGISTER || res->op == OINDREG)) {
                                r = res->val.u.reg;
                                reg[r]--;
                        }
                }
                switch(n->op) {
                case OCALLMETH:
                        cgen_callmeth(n, 0);
                        break;
                case OCALLFUNC:
                        cgen_call(n, 0);
                        break;
                case OCALLINTER:
                        cgen_callinter(n, N, 0);
                        break;
                }
                if(r >= 0)
                        reg[r]++;
                regalloc(a, types[tptr], res);
                cgen_aret(n, a);
                a->op = OINDREG;
                a->type = n->type;
                return;
        }

        agenr(n, a, res);
        a->op = OINDREG;
        a->type = n->type;
}

/*
 * allocate a register in res and generate
 *  newreg = &n
 * The caller must call regfree(a).
 */
void
cgenr(Node *n, Node *a, Node *res)
{
        Node n1;

        if(debug['g'])
                dump("cgenr-n", n);

        if(isfat(n->type))
                fatal("cgenr on fat node");

        if(n->addable) {
                regalloc(a, types[tptr], res);
                gmove(n, a);
                return;
        }

        switch(n->op) {
        case ONAME:
        case ODOT:
        case ODOTPTR:
        case OINDEX:
        case OCALLFUNC:
        case OCALLMETH:
        case OCALLINTER:
                igen(n, &n1, res);
                regalloc(a, types[tptr], &n1);
                gmove(&n1, a);
                regfree(&n1);
                break;
        default:
                regalloc(a, n->type, res);
                cgen(n, a);
                break;
        }
}

/*
 * generate:
 *      newreg = &n;
 *
 * caller must regfree(a).
 * The generated code checks that the result is not nil.
 */
void
agenr(Node *n, Node *a, Node *res)
{
        Node *nl, *nr;
        Node n1, n2, n3, n4, tmp;
        Prog *p1, *p2;
        uint32 w;
        uint64 v;
        int bounded;

        if(debug['g'])
                dump("agenr-n", n);

        nl = n->left;
        nr = n->right;

        switch(n->op) {
        case ODOT:
        case ODOTPTR:
        case OCALLFUNC:
        case OCALLMETH:
        case OCALLINTER:
                igen(n, &n1, res);
                regalloc(a, types[tptr], &n1);
                agen(&n1, a);
                regfree(&n1);
                break;

        case OIND:
                cgenr(n->left, a, res);
                cgen_checknil(a);
                break;

        case OINDEX:
                p2 = nil;  // to be patched to panicindex.
                w = n->type->width;
                bounded = debug['B'] || n->bounded;
                if(nr->addable) {
                        if(!isconst(nr, CTINT))
                                tempname(&tmp, types[TINT32]);
                        if(!isconst(nl, CTSTR))
                                agenr(nl, &n3, res);
                        if(!isconst(nr, CTINT)) {
                                p2 = cgenindex(nr, &tmp, bounded);
                                regalloc(&n1, tmp.type, N);
                                gmove(&tmp, &n1);
                        }
                } else
                if(nl->addable) {
                        if(!isconst(nr, CTINT)) {
                                tempname(&tmp, types[TINT32]);
                                p2 = cgenindex(nr, &tmp, bounded);
                                regalloc(&n1, tmp.type, N);
                                gmove(&tmp, &n1);
                        }
                        if(!isconst(nl, CTSTR)) {
                                agenr(nl, &n3, res);
                        }
                } else {
                        tempname(&tmp, types[TINT32]);
                        p2 = cgenindex(nr, &tmp, bounded);
                        nr = &tmp;
                        if(!isconst(nl, CTSTR))
                                agenr(nl, &n3, res);
                        regalloc(&n1, tmp.type, N);
                        gins(optoas(OAS, tmp.type), &tmp, &n1);
                }

                // &a is in &n3 (allocated in res)
                // i is in &n1 (if not constant)
                // w is width

                // constant index
                if(isconst(nr, CTINT)) {
                        if(isconst(nl, CTSTR))
                                fatal("constant string constant index");
                        v = mpgetfix(nr->val.u.xval);
                        if(isslice(nl->type) || nl->type->etype == TSTRING) {
                                if(!debug['B'] && !n->bounded) {
                                        n1 = n3;
                                        n1.op = OINDREG;
                                        n1.type = types[tptr];
                                        n1.xoffset = Array_nel;
                                        regalloc(&n4, n1.type, N);
                                        gmove(&n1, &n4);
                                        nodconst(&n2, types[TUINT32], v);
                                        gcmp(optoas(OCMP, types[TUINT32]), &n4, &n2);
                                        regfree(&n4);
                                        p1 = gbranch(optoas(OGT, types[TUINT32]), T, +1);
                                        ginscall(panicindex, 0);
                                        patch(p1, pc);
                                }

                                n1 = n3;
                                n1.op = OINDREG;
                                n1.type = types[tptr];
                                n1.xoffset = Array_array;
                                gmove(&n1, &n3);
                        }

                        nodconst(&n2, types[tptr], v*w);
                        gins(optoas(OADD, types[tptr]), &n2, &n3);
                        *a = n3;
                        break;
                }

                regalloc(&n2, types[TINT32], &n1);                      // i
                gmove(&n1, &n2);
                regfree(&n1);

                if(!debug['B'] && !n->bounded) {
                        // check bounds
                        if(isconst(nl, CTSTR)) {
                                nodconst(&n4, types[TUINT32], nl->val.u.sval->len);
                        } else if(isslice(nl->type) || nl->type->etype == TSTRING) {
                                n1 = n3;
                                n1.op = OINDREG;
                                n1.type = types[tptr];
                                n1.xoffset = Array_nel;
                                regalloc(&n4, types[TUINT32], N);
                                gmove(&n1, &n4);
                        } else {
                                nodconst(&n4, types[TUINT32], nl->type->bound);
                        }
                        gcmp(optoas(OCMP, types[TUINT32]), &n2, &n4);
                        if(n4.op == OREGISTER)
                                regfree(&n4);
                        p1 = gbranch(optoas(OLT, types[TUINT32]), T, +1);
                        if(p2)
                                patch(p2, pc);
                        ginscall(panicindex, 0);
                        patch(p1, pc);
                }
                
                if(isconst(nl, CTSTR)) {
                        regalloc(&n3, types[tptr], res);
                        p1 = gins(AMOVW, N, &n3);
                        datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from);
                        p1->from.type = D_CONST;
                } else
                if(isslice(nl->type) || nl->type->etype == TSTRING) {
                        n1 = n3;
                        n1.op = OINDREG;
                        n1.type = types[tptr];
                        n1.xoffset = Array_array;
                        gmove(&n1, &n3);
                }

                if(w == 0) {
                        // nothing to do
                } else if(w == 1 || w == 2 || w == 4 || w == 8) {
                        memset(&n4, 0, sizeof n4);
                        n4.op = OADDR;
                        n4.left = &n2;
                        cgen(&n4, &n3);
                        if (w == 1)
                                gins(AADD, &n2, &n3);
                        else if(w == 2)
                                gshift(AADD, &n2, SHIFT_LL, 1, &n3);
                        else if(w == 4)
                                gshift(AADD, &n2, SHIFT_LL, 2, &n3);
                        else if(w == 8)
                                gshift(AADD, &n2, SHIFT_LL, 3, &n3);
                } else {
                        regalloc(&n4, types[TUINT32], N);
                        nodconst(&n1, types[TUINT32], w);
                        gmove(&n1, &n4);
                        gins(optoas(OMUL, types[TUINT32]), &n4, &n2);
                        gins(optoas(OADD, types[tptr]), &n2, &n3);
                        regfree(&n4);
                }

                *a = n3;
                regfree(&n2);
                break;

        default:
                regalloc(a, types[tptr], res);
                agen(n, a);
                break;
        }
}

void
gencmp0(Node *n, Type *t, int o, int likely, Prog *to)
{
        Node n1, n2, n3;
        int a;

        regalloc(&n1, t, N);
        cgen(n, &n1);
        a = optoas(OCMP, t);
        if(a != ACMP) {
                nodconst(&n2, t, 0);
                regalloc(&n3, t, N);
                gmove(&n2, &n3);
                gcmp(a, &n1, &n3);
                regfree(&n3);
        } else
                gins(ATST, &n1, N);
        a = optoas(o, t);
        patch(gbranch(a, t, likely), to);
        regfree(&n1);
}

/*
 * generate:
 *      if(n == true) goto to;
 */
void
bgen(Node *n, int true, int likely, Prog *to)
{
        int et, a;
        Node *nl, *nr, *r;
        Node n1, n2, n3, n4, tmp;
        NodeList *ll;
        Prog *p1, *p2;

        USED(n4);                       // in unreachable code below
        if(debug['g']) {
                dump("\nbgen", n);
        }

        if(n == N)
                n = nodbool(1);

        if(n->ninit != nil)
                genlist(n->ninit);

        if(n->type == T) {
                convlit(&n, types[TBOOL]);
                if(n->type == T)
                        goto ret;
        }

        et = n->type->etype;
        if(et != TBOOL) {
                yyerror("cgen: bad type %T for %O", n->type, n->op);
                patch(gins(AEND, N, N), to);
                goto ret;
        }
        nr = N;

        switch(n->op) {
        default:
                a = ONE;
                if(!true)
                        a = OEQ;
                gencmp0(n, n->type, a, likely, to);
                goto ret;

        case OLITERAL:
                // need to ask if it is bool?
                if(!true == !n->val.u.bval)
                        patch(gbranch(AB, T, 0), to);
                goto ret;

        case OANDAND:
                if(!true)
                        goto caseor;

        caseand:
                p1 = gbranch(AB, T, 0);
                p2 = gbranch(AB, T, 0);
                patch(p1, pc);
                bgen(n->left, !true, -likely, p2);
                bgen(n->right, !true, -likely, p2);
                p1 = gbranch(AB, T, 0);
                patch(p1, to);
                patch(p2, pc);
                goto ret;

        case OOROR:
                if(!true)
                        goto caseand;

        caseor:
                bgen(n->left, true, likely, to);
                bgen(n->right, true, likely, to);
                goto ret;

        case OEQ:
        case ONE:
        case OLT:
        case OGT:
        case OLE:
        case OGE:
                nr = n->right;
                if(nr == N || nr->type == T)
                        goto ret;

        case ONOT:      // unary
                nl = n->left;
                if(nl == N || nl->type == T)
                        goto ret;
        }

        switch(n->op) {

        case ONOT:
                bgen(nl, !true, likely, to);
                goto ret;

        case OEQ:
        case ONE:
        case OLT:
        case OGT:
        case OLE:
        case OGE:
                a = n->op;
                if(!true) {
                        if(isfloat[nl->type->etype]) {
                                // brcom is not valid on floats when NaN is involved.
                                p1 = gbranch(AB, T, 0);
                                p2 = gbranch(AB, T, 0);
                                patch(p1, pc);
                                ll = n->ninit;
                                n->ninit = nil;
                                bgen(n, 1, -likely, p2);
                                n->ninit = ll;
                                patch(gbranch(AB, T, 0), to);
                                patch(p2, pc);
                                goto ret;
                        }                               
                        a = brcom(a);
                        true = !true;
                }

                // make simplest on right
                if(nl->op == OLITERAL || (nl->ullman < UINF && nl->ullman < nr->ullman)) {
                        a = brrev(a);
                        r = nl;
                        nl = nr;
                        nr = r;
                }

                if(isslice(nl->type)) {
                        // only valid to cmp darray to literal nil
                        if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
                                yyerror("illegal array comparison");
                                break;
                        }

                        igen(nl, &n1, N);
                        n1.xoffset += Array_array;
                        n1.type = types[tptr];
                        gencmp0(&n1, types[tptr], a, likely, to);
                        regfree(&n1);
                        break;
                }

                if(isinter(nl->type)) {
                        // front end shold only leave cmp to literal nil
                        if((a != OEQ && a != ONE) || nr->op != OLITERAL) {
                                yyerror("illegal interface comparison");
                                break;
                        }

                        igen(nl, &n1, N);
                        n1.type = types[tptr];
                        n1.xoffset += 0;
                        gencmp0(&n1, types[tptr], a, likely, to);
                        regfree(&n1);
                        break;
                }

                if(iscomplex[nl->type->etype]) {
                        complexbool(a, nl, nr, true, likely, to);
                        break;
                }

                if(is64(nr->type)) {
                        if(!nl->addable) {
                                tempname(&n1, nl->type);
                                cgen(nl, &n1);
                                nl = &n1;
                        }
                        if(!nr->addable) {
                                tempname(&n2, nr->type);
                                cgen(nr, &n2);
                                nr = &n2;
                        }
                        cmp64(nl, nr, a, likely, to);
                        break;
                }

                if(nr->op == OLITERAL) {
                        if(isconst(nr, CTINT) &&  mpgetfix(nr->val.u.xval) == 0) {
                                gencmp0(nl, nl->type, a, likely, to);
                                break;
                        }
                        if(nr->val.ctype == CTNIL) {
                                gencmp0(nl, nl->type, a, likely, to);
                                break;
                        }
                }

                a = optoas(a, nr->type);

                if(nr->ullman >= UINF) {
                        regalloc(&n1, nl->type, N);
                        cgen(nl, &n1);

                        tempname(&tmp, nl->type);
                        gmove(&n1, &tmp);
                        regfree(&n1);

                        regalloc(&n2, nr->type, N);
                        cgen(nr, &n2);

                        regalloc(&n1, nl->type, N);
                        cgen(&tmp, &n1);

                        gcmp(optoas(OCMP, nr->type), &n1, &n2);
                        patch(gbranch(a, nr->type, likely), to);

                        regfree(&n1);
                        regfree(&n2);
                        break;
                }

                tempname(&n3, nl->type);
                cgen(nl, &n3);

                tempname(&tmp, nr->type);
                cgen(nr, &tmp);

                regalloc(&n1, nl->type, N);
                gmove(&n3, &n1);

                regalloc(&n2, nr->type, N);
                gmove(&tmp, &n2);

                gcmp(optoas(OCMP, nr->type), &n1, &n2);
                if(isfloat[nl->type->etype]) {
                        if(n->op == ONE) {
                                p1 = gbranch(ABVS, nr->type, likely);
                                patch(gbranch(a, nr->type, likely), to);
                                patch(p1, to);
                        } else {
                                p1 = gbranch(ABVS, nr->type, -likely);
                                patch(gbranch(a, nr->type, likely), to);
                                patch(p1, pc);
                        }
                } else {
                        patch(gbranch(a, nr->type, likely), to);
                }
                regfree(&n1);
                regfree(&n2);
                break;
        }
        goto ret;

ret:
        ;
}

/*
 * n is on stack, either local variable
 * or return value from function call.
 * return n's offset from SP.
 */
int32
stkof(Node *n)
{
        Type *t;
        Iter flist;
        int32 off;

        switch(n->op) {
        case OINDREG:
                return n->xoffset;

        case ODOT:
                t = n->left->type;
                if(isptr[t->etype])
                        break;
                off = stkof(n->left);
                if(off == -1000 || off == 1000)
                        return off;
                return off + n->xoffset;

        case OINDEX:
                t = n->left->type;
                if(!isfixedarray(t))
                        break;
                off = stkof(n->left);
                if(off == -1000 || off == 1000)
                        return off;
                if(isconst(n->right, CTINT))
                        return off + t->type->width * mpgetfix(n->right->val.u.xval);
                return 1000;
                
        case OCALLMETH:
        case OCALLINTER:
        case OCALLFUNC:
                t = n->left->type;
                if(isptr[t->etype])
                        t = t->type;

                t = structfirst(&flist, getoutarg(t));
                if(t != T)
                        return t->width + 4;    // correct for LR
                break;
        }

        // botch - probably failing to recognize address
        // arithmetic on the above. eg INDEX and DOT
        return -1000;
}

/*
 * block copy:
 *      memmove(&res, &n, w);
 * NB: character copy assumed little endian architecture
 */
void
sgen(Node *n, Node *res, int64 w)
{
        Node dst, src, tmp, nend, r0, r1, r2, *f;
        int32 c, odst, osrc;
        int dir, align, op;
        Prog *p, *ploop;
        NodeList *l;

        if(debug['g']) {
                print("\nsgen w=%lld\n", w);
                dump("r", n);
                dump("res", res);
        }

        if(n->ullman >= UINF && res->ullman >= UINF)
                fatal("sgen UINF");

        if(w < 0 || (int32)w != w)
                fatal("sgen copy %lld", w);

        if(n->type == T)
                fatal("sgen: missing type");

        if(w == 0) {
                // evaluate side effects only.
                regalloc(&dst, types[tptr], N);
                agen(res, &dst);
                agen(n, &dst);
                regfree(&dst);
                return;
        }

        // If copying .args, that's all the results, so record definition sites
        // for them for the liveness analysis.
        if(res->op == ONAME && strcmp(res->sym->name, ".args") == 0)
                for(l = curfn->dcl; l != nil; l = l->next)
                        if(l->n->class == PPARAMOUT)
                                gvardef(l->n);

        // Avoid taking the address for simple enough types.
        if(componentgen(n, res))
                return;
        
        // determine alignment.
        // want to avoid unaligned access, so have to use
        // smaller operations for less aligned types.
        // for example moving [4]byte must use 4 MOVB not 1 MOVW.
        align = n->type->align;
        switch(align) {
        default:
                fatal("sgen: invalid alignment %d for %T", align, n->type);
        case 1:
                op = AMOVB;
                break;
        case 2:
                op = AMOVH;
                break;
        case 4:
                op = AMOVW;
                break;
        }
        if(w%align)
                fatal("sgen: unaligned size %lld (align=%d) for %T", w, align, n->type);
        c = w / align;

        // offset on the stack
        osrc = stkof(n);
        odst = stkof(res);
        if(osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000)) {
                // osrc and odst both on stack, and at least one is in
                // an unknown position.  Could generate code to test
                // for forward/backward copy, but instead just copy
                // to a temporary location first.
                tempname(&tmp, n->type);
                sgen(n, &tmp, w);
                sgen(&tmp, res, w);
                return;
        }
        if(osrc%align != 0 || odst%align != 0)
                fatal("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align);

        // if we are copying forward on the stack and
        // the src and dst overlap, then reverse direction
        dir = align;
        if(osrc < odst && odst < osrc+w)
                dir = -dir;

        if(op == AMOVW && dir > 0 && c >= 4 && c <= 128) {
                r0.op = OREGISTER;
                r0.val.u.reg = REGALLOC_R0;
                r1.op = OREGISTER;
                r1.val.u.reg = REGALLOC_R0 + 1;
                r2.op = OREGISTER;
                r2.val.u.reg = REGALLOC_R0 + 2;

                regalloc(&src, types[tptr], &r1);
                regalloc(&dst, types[tptr], &r2);
                if(n->ullman >= res->ullman) {
                        // eval n first
                        agen(n, &src);
                        if(res->op == ONAME)
                                gvardef(res);
                        agen(res, &dst);
                } else {
                        // eval res first
                        if(res->op == ONAME)
                                gvardef(res);
                        agen(res, &dst);
                        agen(n, &src);
                }
                regalloc(&tmp, types[tptr], &r0);
                f = sysfunc("duffcopy");
                p = gins(ADUFFCOPY, N, f);
                afunclit(&p->to, f);
                // 8 and 128 = magic constants: see ../../pkg/runtime/asm_arm.s
                p->to.offset = 8*(128-c);

                regfree(&tmp);
                regfree(&src);
                regfree(&dst);
                return;
        }
        
        if(n->ullman >= res->ullman) {
                agenr(n, &dst, res);    // temporarily use dst
                regalloc(&src, types[tptr], N);
                gins(AMOVW, &dst, &src);
                if(res->op == ONAME)
                        gvardef(res);
                agen(res, &dst);
        } else {
                if(res->op == ONAME)
                        gvardef(res);
                agenr(res, &dst, res);
                agenr(n, &src, N);
        }

        regalloc(&tmp, types[TUINT32], N);

        // set up end marker
        memset(&nend, 0, sizeof nend);
        if(c >= 4) {
                regalloc(&nend, types[TUINT32], N);

                p = gins(AMOVW, &src, &nend);
                p->from.type = D_CONST;
                if(dir < 0)
                        p->from.offset = dir;
                else
                        p->from.offset = w;
        }

        // move src and dest to the end of block if necessary
        if(dir < 0) {
                p = gins(AMOVW, &src, &src);
                p->from.type = D_CONST;
                p->from.offset = w + dir;

                p = gins(AMOVW, &dst, &dst);
                p->from.type = D_CONST;
                p->from.offset = w + dir;
        }
        
        // move
        if(c >= 4) {
                p = gins(op, &src, &tmp);
                p->from.type = D_OREG;
                p->from.offset = dir;
                p->scond |= C_PBIT;
                ploop = p;

                p = gins(op, &tmp, &dst);
                p->to.type = D_OREG;
                p->to.offset = dir;
                p->scond |= C_PBIT;

                p = gins(ACMP, &src, N);
                raddr(&nend, p);

                patch(gbranch(ABNE, T, 0), ploop);
                regfree(&nend);
        } else {
                while(c-- > 0) {
                        p = gins(op, &src, &tmp);
                        p->from.type = D_OREG;
                        p->from.offset = dir;
                        p->scond |= C_PBIT;
        
                        p = gins(op, &tmp, &dst);
                        p->to.type = D_OREG;
                        p->to.offset = dir;
                        p->scond |= C_PBIT;
                }
        }

        regfree(&dst);
        regfree(&src);
        regfree(&tmp);
}

static int
cadable(Node *n)
{
        if(!n->addable) {
                // dont know how it happens,
                // but it does
                return 0;
        }

        switch(n->op) {
        case ONAME:
                return 1;
        }
        return 0;
}

/*
 * copy a composite value by moving its individual components.
 * Slices, strings and interfaces are supported.
 * nr is N when assigning a zero value.
 * return 1 if can do, 0 if cant.
 */
int
componentgen(Node *nr, Node *nl)
{
        Node nodl, nodr, tmp;
        int freel, freer;

        freel = 0;
        freer = 0;

        switch(nl->type->etype) {
        default:
                goto no;

        case TARRAY:
                if(!isslice(nl->type))
                        goto no;
        case TSTRING:
        case TINTER:
                break;
        }

        nodl = *nl;
        if(!cadable(nl)) {
                if(nr == N || !cadable(nr))
                        goto no;
                igen(nl, &nodl, N);
                freel = 1;
        }

        if(nr != N) {
                nodr = *nr;
                if(!cadable(nr)) {
                        igen(nr, &nodr, N);
                        freer = 1;
                }
        } else {
                // When zeroing, prepare a register containing zero.
                nodconst(&tmp, nl->type, 0);
                regalloc(&nodr, types[TUINT], N);
                gmove(&tmp, &nodr);
                freer = 1;
        }

        // nl and nr are 'cadable' which basically means they are names (variables) now.
        // If they are the same variable, don't generate any code, because the
        // VARDEF we generate will mark the old value as dead incorrectly.
        // (And also the assignments are useless.)
        if(nr != N && nl->op == ONAME && nr->op == ONAME && nl == nr)
                goto yes;

        switch(nl->type->etype) {
        case TARRAY:
                if(nl->op == ONAME)
                        gvardef(nl);
                nodl.xoffset += Array_array;
                nodl.type = ptrto(nl->type->type);

                if(nr != N) {
                        nodr.xoffset += Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                nodl.xoffset += Array_nel-Array_array;
                nodl.type = types[simtype[TUINT]];

                if(nr != N) {
                        nodr.xoffset += Array_nel-Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                nodl.xoffset += Array_cap-Array_nel;
                nodl.type = types[simtype[TUINT]];

                if(nr != N) {
                        nodr.xoffset += Array_cap-Array_nel;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                goto yes;

        case TSTRING:
                if(nl->op == ONAME)
                        gvardef(nl);
                nodl.xoffset += Array_array;
                nodl.type = ptrto(types[TUINT8]);

                if(nr != N) {
                        nodr.xoffset += Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                nodl.xoffset += Array_nel-Array_array;
                nodl.type = types[simtype[TUINT]];

                if(nr != N) {
                        nodr.xoffset += Array_nel-Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                goto yes;

        case TINTER:
                if(nl->op == ONAME)
                        gvardef(nl);
                nodl.xoffset += Array_array;
                nodl.type = ptrto(types[TUINT8]);

                if(nr != N) {
                        nodr.xoffset += Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                nodl.xoffset += Array_nel-Array_array;
                nodl.type = ptrto(types[TUINT8]);

                if(nr != N) {
                        nodr.xoffset += Array_nel-Array_array;
                        nodr.type = nodl.type;
                }
                gmove(&nodr, &nodl);

                goto yes;
        }

no:
        if(freer)
                regfree(&nodr);
        if(freel)
                regfree(&nodl);
        return 0;

yes:
        if(freer)
                regfree(&nodr);
        if(freel)
                regfree(&nodl);
        return 1;
}

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