root/src/cmd/gc/walk.c

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

DEFINITIONS

This source file includes following definitions.
  1. walk
  2. walkstmtlist
  3. samelist
  4. paramoutheap
  5. walkstmt
  6. walkexprlist
  7. walkexprlistsafe
  8. walkexpr
  9. ascompatee1
  10. ascompatee
  11. fncall
  12. ascompatet
  13. mkdotargslice
  14. dumptypes
  15. dumpnodetypes
  16. ascompatte
  17. walkprint
  18. callnew
  19. convas
  20. reorder1
  21. reorder3
  22. reorder3save
  23. outervalue
  24. aliased
  25. varexpr
  26. vmatch2
  27. vmatch1
  28. paramstoheap
  29. returnsfromheap
  30. heapmoves
  31. vmkcall
  32. mkcall
  33. mkcall1
  34. conv
  35. chanfn
  36. mapfn
  37. mapfndel
  38. addstr
  39. appendslice
  40. append
  41. copyany
  42. sliceany
  43. eqfor
  44. countfield
  45. walkcompare
  46. samecheap
  47. walkrotate
  48. walkmul
  49. walkdiv
  50. bounded
  51. usefield
  52. candiscardlist
  53. candiscard

// 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        "go.h"

static  Node*   walkprint(Node*, NodeList**, int);
static  Node*   mapfn(char*, Type*);
static  Node*   mapfndel(char*, Type*);
static  Node*   ascompatee1(int, Node*, Node*, NodeList**);
static  NodeList*       ascompatee(int, NodeList*, NodeList*, NodeList**);
static  NodeList*       ascompatet(int, NodeList*, Type**, int, NodeList**);
static  NodeList*       ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
static  Node*   convas(Node*, NodeList**);
static  void    heapmoves(void);
static  NodeList*       paramstoheap(Type **argin, int out);
static  NodeList*       reorder1(NodeList*);
static  NodeList*       reorder3(NodeList*);
static  Node*   addstr(Node*, NodeList**);
static  Node*   appendslice(Node*, NodeList**);
static  Node*   append(Node*, NodeList**);
static  Node*   copyany(Node*, NodeList**, int);
static  Node*   sliceany(Node*, NodeList**);
static  void    walkcompare(Node**, NodeList**);
static  void    walkrotate(Node**);
static  void    walkmul(Node**, NodeList**);
static  void    walkdiv(Node**, NodeList**);
static  int     bounded(Node*, int64);
static  Mpint   mpzero;

void
walk(Node *fn)
{
        char s[50];
        NodeList *l;
        int lno;

        curfn = fn;

        if(debug['W']) {
                snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
                dumplist(s, curfn->nbody);
        }

        lno = lineno;

        // Final typecheck for any unused variables.
        // It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
        for(l=fn->dcl; l; l=l->next)
                if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO)
                        typecheck(&l->n, Erv | Easgn);

        // Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
        for(l=fn->dcl; l; l=l->next)
                if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used)
                        l->n->defn->left->used++;
        
        for(l=fn->dcl; l; l=l->next) {
                if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used)
                        continue;
                if(l->n->defn && l->n->defn->op == OTYPESW) {
                        if(l->n->defn->left->used)
                                continue;
                        lineno = l->n->defn->left->lineno;
                        yyerror("%S declared and not used", l->n->sym);
                        l->n->defn->left->used = 1; // suppress repeats
                } else {
                        lineno = l->n->lineno;
                        yyerror("%S declared and not used", l->n->sym);
                }
        }       

        lineno = lno;
        if(nerrors != 0)
                return;
        walkstmtlist(curfn->nbody);
        if(debug['W']) {
                snprint(s, sizeof(s), "after walk %S", curfn->nname->sym);
                dumplist(s, curfn->nbody);
        }
        heapmoves();
        if(debug['W'] && curfn->enter != nil) {
                snprint(s, sizeof(s), "enter %S", curfn->nname->sym);
                dumplist(s, curfn->enter);
        }
}


void
walkstmtlist(NodeList *l)
{
        for(; l; l=l->next)
                walkstmt(&l->n);
}

static int
samelist(NodeList *a, NodeList *b)
{
        for(; a && b; a=a->next, b=b->next)
                if(a->n != b->n)
                        return 0;
        return a == b;
}

static int
paramoutheap(Node *fn)
{
        NodeList *l;

        for(l=fn->dcl; l; l=l->next) {
                switch(l->n->class) {
                case PPARAMOUT:
                case PPARAMOUT|PHEAP:
                        return l->n->addrtaken;
                case PAUTO:
                case PAUTO|PHEAP:
                        // stop early - parameters are over
                        return 0;
                }
        }
        return 0;
}

void
walkstmt(Node **np)
{
        NodeList *init;
        NodeList *ll, *rl;
        int cl;
        Node *n, *f;

        n = *np;
        if(n == N)
                return;

        setlineno(n);

        walkstmtlist(n->ninit);

        switch(n->op) {
        default:
                if(n->op == ONAME)
                        yyerror("%S is not a top level statement", n->sym);
                else
                        yyerror("%O is not a top level statement", n->op);
                dump("nottop", n);
                break;

        case OAS:
        case OASOP:
        case OAS2:
        case OAS2DOTTYPE:
        case OAS2RECV:
        case OAS2FUNC:
        case OAS2MAPR:
        case OCLOSE:
        case OCOPY:
        case OCALLMETH:
        case OCALLINTER:
        case OCALL:
        case OCALLFUNC:
        case ODELETE:
        case OSEND:
        case OPRINT:
        case OPRINTN:
        case OPANIC:
        case OEMPTY:
        case ORECOVER:
                if(n->typecheck == 0)
                        fatal("missing typecheck: %+N", n);
                init = n->ninit;
                n->ninit = nil;
                walkexpr(&n, &init);
                addinit(&n, init);
                if((*np)->op == OCOPY && n->op == OCONVNOP)
                        n->op = OEMPTY; // don't leave plain values as statements.
                break;

        case ORECV:
                // special case for a receive where we throw away
                // the value received.
                if(n->typecheck == 0)
                        fatal("missing typecheck: %+N", n);
                init = n->ninit;
                n->ninit = nil;

                walkexpr(&n->left, &init);
                n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, &init, typename(n->left->type), n->left, nodnil());
                walkexpr(&n, &init);

                addinit(&n, init);
                break;

        case OBREAK:
        case ODCL:
        case OCONTINUE:
        case OFALL:
        case OGOTO:
        case OLABEL:
        case ODCLCONST:
        case ODCLTYPE:
        case OCHECKNIL:
        case OVARKILL:
                break;

        case OBLOCK:
                walkstmtlist(n->list);
                break;

        case OXCASE:
                yyerror("case statement out of place");
                n->op = OCASE;
        case OCASE:
                walkstmt(&n->right);
                break;

        case ODEFER:
                hasdefer = 1;
                switch(n->left->op) {
                case OPRINT:
                case OPRINTN:
                        walkexprlist(n->left->list, &n->ninit);
                        n->left = walkprint(n->left, &n->ninit, 1);
                        break;
                case OCOPY:
                        n->left = copyany(n->left, &n->ninit, 1);
                        break;
                default:
                        walkexpr(&n->left, &n->ninit);
                        break;
                }
                break;

        case OFOR:
                if(n->ntest != N) {
                        walkstmtlist(n->ntest->ninit);
                        init = n->ntest->ninit;
                        n->ntest->ninit = nil;
                        walkexpr(&n->ntest, &init);
                        addinit(&n->ntest, init);
                }
                walkstmt(&n->nincr);
                walkstmtlist(n->nbody);
                break;

        case OIF:
                walkexpr(&n->ntest, &n->ninit);
                walkstmtlist(n->nbody);
                walkstmtlist(n->nelse);
                break;

        case OPROC:
                switch(n->left->op) {
                case OPRINT:
                case OPRINTN:
                        walkexprlist(n->left->list, &n->ninit);
                        n->left = walkprint(n->left, &n->ninit, 1);
                        break;
                case OCOPY:
                        n->left = copyany(n->left, &n->ninit, 1);
                        break;
                default:
                        walkexpr(&n->left, &n->ninit);
                        break;
                }
                break;

        case ORETURN:
                walkexprlist(n->list, &n->ninit);
                if(n->list == nil)
                        break;
                if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) {
                        // assign to the function out parameters,
                        // so that reorder3 can fix up conflicts
                        rl = nil;
                        for(ll=curfn->dcl; ll != nil; ll=ll->next) {
                                cl = ll->n->class & ~PHEAP;
                                if(cl == PAUTO)
                                        break;
                                if(cl == PPARAMOUT)
                                        rl = list(rl, ll->n);
                        }
                        if(samelist(rl, n->list)) {
                                // special return in disguise
                                n->list = nil;
                                break;
                        }
                        if(count(n->list) == 1 && count(rl) > 1) {
                                // OAS2FUNC in disguise
                                f = n->list->n;
                                if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER)
                                        fatal("expected return of call, have %N", f);
                                n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
                                break;
                        }

                        // move function calls out, to make reorder3's job easier.
                        walkexprlistsafe(n->list, &n->ninit);
                        ll = ascompatee(n->op, rl, n->list, &n->ninit);
                        n->list = reorder3(ll);
                        break;
                }
                ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
                n->list = ll;
                break;

        case ORETJMP:
                break;

        case OSELECT:
                walkselect(n);
                break;

        case OSWITCH:
                walkswitch(n);
                break;

        case ORANGE:
                walkrange(n);
                break;

        case OXFALL:
                yyerror("fallthrough statement out of place");
                n->op = OFALL;
                break;
        }

        if(n->op == ONAME)
                fatal("walkstmt ended up with name: %+N", n);
        
        *np = n;
}


/*
 * walk the whole tree of the body of an
 * expression or simple statement.
 * the types expressions are calculated.
 * compile-time constants are evaluated.
 * complex side effects like statements are appended to init
 */

void
walkexprlist(NodeList *l, NodeList **init)
{
        for(; l; l=l->next)
                walkexpr(&l->n, init);
}

void
walkexprlistsafe(NodeList *l, NodeList **init)
{
        for(; l; l=l->next) {
                l->n = safeexpr(l->n, init);
                walkexpr(&l->n, init);
        }
}

void
walkexpr(Node **np, NodeList **init)
{
        Node *r, *l, *var, *a;
        Node *map, *key;
        NodeList *ll, *lr;
        Type *t;
        int et, old_safemode;
        int64 v;
        int32 lno;
        Node *n, *fn, *n1, *n2;
        Sym *sym;
        char buf[100], *p;

        n = *np;

        if(n == N)
                return;

        if(init == &n->ninit) {
                // not okay to use n->ninit when walking n,
                // because we might replace n with some other node
                // and would lose the init list.
                fatal("walkexpr init == &n->ninit");
        }

        if(n->ninit != nil) {
                walkstmtlist(n->ninit);
                *init = concat(*init, n->ninit);
                n->ninit = nil;
        }

        // annoying case - not typechecked
        if(n->op == OKEY) {
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                return;
        }

        lno = setlineno(n);

        if(debug['w'] > 1)
                dump("walk-before", n);

        if(n->typecheck != 1)
                fatal("missed typecheck: %+N\n", n);

        switch(n->op) {
        default:
                dump("walk", n);
                fatal("walkexpr: switch 1 unknown op %+hN", n);
                break;

        case OTYPE:
        case ONONAME:
        case OINDREG:
        case OEMPTY:
                goto ret;

        case ONOT:
        case OMINUS:
        case OPLUS:
        case OCOM:
        case OREAL:
        case OIMAG:
        case ODOTMETH:
        case ODOTINTER:
                walkexpr(&n->left, init);
                goto ret;

        case OIND:
                walkexpr(&n->left, init);
                goto ret;

        case ODOT:
                usefield(n);
                walkexpr(&n->left, init);
                goto ret;

        case ODOTPTR:
                usefield(n);
                if(n->op == ODOTPTR && n->left->type->type->width == 0) {
                        // No actual copy will be generated, so emit an explicit nil check.
                        n->left = cheapexpr(n->left, init);
                        checknil(n->left, init);
                }
                walkexpr(&n->left, init);
                goto ret;

        case OEFACE:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                goto ret;

        case OSPTR:
        case OITAB:
                walkexpr(&n->left, init);
                goto ret;

        case OLEN:
        case OCAP:
                walkexpr(&n->left, init);

                // replace len(*[10]int) with 10.
                // delayed until now to preserve side effects.
                t = n->left->type;
                if(isptr[t->etype])
                        t = t->type;
                if(isfixedarray(t)) {
                        safeexpr(n->left, init);
                        nodconst(n, n->type, t->bound);
                        n->typecheck = 1;
                }
                goto ret;

        case OLSH:
        case ORSH:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                t = n->left->type;
                n->bounded = bounded(n->right, 8*t->width);
                if(debug['m'] && n->etype && !isconst(n->right, CTINT))
                        warn("shift bounds check elided");
                goto ret;

        case OAND:
        case OSUB:
        case OHMUL:
        case OLT:
        case OLE:
        case OGE:
        case OGT:
        case OADD:
        case OCOMPLEX:
        case OLROT:
                // Use results from call expression as arguments for complex.
                if(n->op == OCOMPLEX && n->left == N && n->right == N) {
                        n->left = n->list->n;
                        n->right = n->list->next->n;
                }
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                goto ret;

        case OOR:
        case OXOR:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                walkrotate(&n);
                goto ret;

        case OEQ:
        case ONE:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                // Disable safemode while compiling this code: the code we
                // generate internally can refer to unsafe.Pointer.
                // In this case it can happen if we need to generate an ==
                // for a struct containing a reflect.Value, which itself has
                // an unexported field of type unsafe.Pointer.
                old_safemode = safemode;
                safemode = 0;
                walkcompare(&n, init);
                safemode = old_safemode;
                goto ret;

        case OANDAND:
        case OOROR:
                walkexpr(&n->left, init);
                // cannot put side effects from n->right on init,
                // because they cannot run before n->left is checked.
                // save elsewhere and store on the eventual n->right.
                ll = nil;
                walkexpr(&n->right, &ll);
                addinit(&n->right, ll);
                goto ret;

        case OPRINT:
        case OPRINTN:
                walkexprlist(n->list, init);
                n = walkprint(n, init, 0);
                goto ret;

        case OPANIC:
                n = mkcall("panic", T, init, n->left);
                goto ret;

        case ORECOVER:
                n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N));
                goto ret;

        case OLITERAL:
                n->addable = 1;
                goto ret;

        case OCLOSUREVAR:
        case OCFUNC:
                n->addable = 1;
                goto ret;

        case ONAME:
                if(!(n->class & PHEAP) && n->class != PPARAMREF)
                        n->addable = 1;
                goto ret;

        case OCALLINTER:
                t = n->left->type;
                if(n->list && n->list->n->op == OAS)
                        goto ret;
                walkexpr(&n->left, init);
                walkexprlist(n->list, init);
                ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
                n->list = reorder1(ll);
                goto ret;

        case OCALLFUNC:
                t = n->left->type;
                if(n->list && n->list->n->op == OAS)
                        goto ret;

                walkexpr(&n->left, init);
                walkexprlist(n->list, init);

                ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
                n->list = reorder1(ll);
                goto ret;

        case OCALLMETH:
                t = n->left->type;
                if(n->list && n->list->n->op == OAS)
                        goto ret;
                walkexpr(&n->left, init);
                walkexprlist(n->list, init);
                ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
                lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
                ll = concat(ll, lr);
                n->left->left = N;
                ullmancalc(n->left);
                n->list = reorder1(ll);
                goto ret;

        case OAS:
                *init = concat(*init, n->ninit);
                n->ninit = nil;

                walkexpr(&n->left, init);
                n->left = safeexpr(n->left, init);

                if(oaslit(n, init))
                        goto ret;

                if(n->right == N)
                        goto ret;

                switch(n->right->op) {
                default:
                        walkexpr(&n->right, init);
                        break;
                
                case ORECV:
                        // x = <-c; n->left is x, n->right->left is c.
                        // orderstmt made sure x is addressable.
                        walkexpr(&n->right->left, init);
                        n1 = nod(OADDR, n->left, N);
                        r = n->right->left; // the channel
                        n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1);
                        walkexpr(&n, init);
                        goto ret;
                }

                if(n->left != N && n->right != N) {
                        r = convas(nod(OAS, n->left, n->right), init);
                        r->dodata = n->dodata;
                        n = r;
                }

                goto ret;

        case OAS2:
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                walkexprlistsafe(n->list, init);
                walkexprlistsafe(n->rlist, init);
                ll = ascompatee(OAS, n->list, n->rlist, init);
                ll = reorder3(ll);
                n = liststmt(ll);
                goto ret;

        case OAS2FUNC:
                // a,b,... = fn()
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                r = n->rlist->n;
                walkexprlistsafe(n->list, init);
                walkexpr(&r, init);

                ll = ascompatet(n->op, n->list, &r->type, 0, init);
                n = liststmt(concat(list1(r), ll));
                goto ret;

        case OAS2RECV:
                // x, y = <-c
                // orderstmt made sure x is addressable.
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                r = n->rlist->n;
                walkexprlistsafe(n->list, init);
                walkexpr(&r->left, init);
                if(isblank(n->list->n))
                        n1 = nodnil();
                else
                        n1 = nod(OADDR, n->list->n, N);
                n1->etype = 1; // addr does not escape
                fn = chanfn("chanrecv2", 2, r->left->type);
                r = mkcall1(fn, types[TBOOL], init, typename(r->left->type), r->left, n1);
                n = nod(OAS, n->list->next->n, r);
                typecheck(&n, Etop);
                goto ret;

        case OAS2MAPR:
                // a,b = m[i];
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                r = n->rlist->n;
                walkexprlistsafe(n->list, init);
                walkexpr(&r->left, init);
                walkexpr(&r->right, init);
                t = r->left->type;
                p = nil;
                if(t->type->width <= 128) { // Check ../../pkg/runtime/hashmap.c:MAXVALUESIZE before changing.
                        switch(simsimtype(t->down)) {
                        case TINT32:
                        case TUINT32:
                                p = "mapaccess2_fast32";
                                break;
                        case TINT64:
                        case TUINT64:
                                p = "mapaccess2_fast64";
                                break;
                        case TSTRING:
                                p = "mapaccess2_faststr";
                                break;
                        }
                }
                if(p != nil) {
                        // fast versions take key by value
                        key = r->right;
                } else {
                        // standard version takes key by reference
                        // orderexpr made sure key is addressable.
                        key = nod(OADDR, r->right, N);
                        p = "mapaccess2";
                }

                // from:
                //   a,b = m[i]
                // to:
                //   var,b = mapaccess2*(t, m, i)
                //   a = *var
                a = n->list->n;
                var = temp(ptrto(t->type));
                var->typecheck = 1;
                fn = mapfn(p, t);
                r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key);
                n->rlist = list1(r);
                n->op = OAS2FUNC;
                n->list->n = var;
                walkexpr(&n, init);
                *init = list(*init, n);
                n = nod(OAS, a, nod(OIND, var, N));
                typecheck(&n, Etop);
                walkexpr(&n, init);
                // mapaccess needs a zero value to be at least this big.
                if(zerosize < t->type->width)
                        zerosize = t->type->width;
                // TODO: ptr is always non-nil, so disable nil check for this OIND op.
                goto ret;

        case ODELETE:
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                map = n->list->n;
                key = n->list->next->n;
                walkexpr(&map, init);
                walkexpr(&key, init);
                // orderstmt made sure key is addressable.
                key = nod(OADDR, key, N);
                t = map->type;
                n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key);
                goto ret;

        case OAS2DOTTYPE:
                // a,b = i.(T)
                *init = concat(*init, n->ninit);
                n->ninit = nil;
                r = n->rlist->n;
                walkexprlistsafe(n->list, init);
                if(isblank(n->list->n) && !isinter(r->type)) {
                        strcpy(buf, "assert");
                        p = buf+strlen(buf);
                        if(isnilinter(r->left->type))
                                *p++ = 'E';
                        else
                                *p++ = 'I';
                        *p++ = '2';
                        *p++ = 'T';
                        *p++ = 'O';
                        *p++ = 'K';
                        *p = '\0';
                        
                        fn = syslook(buf, 1);
                        ll = list1(typename(r->type));
                        ll = list(ll, r->left);
                        argtype(fn, r->left->type);
                        n1 = nod(OCALL, fn, N);
                        n1->list = ll;
                        n = nod(OAS, n->list->next->n, n1);
                        typecheck(&n, Etop);
                        walkexpr(&n, init);
                        goto ret;
                }

                r->op = ODOTTYPE2;
                walkexpr(&r, init);
                ll = ascompatet(n->op, n->list, &r->type, 0, init);
                n = liststmt(concat(list1(r), ll));
                goto ret;

        case ODOTTYPE:
        case ODOTTYPE2:
                // Build name of function: assertI2E2 etc.
                strcpy(buf, "assert");
                p = buf+strlen(buf);
                if(isnilinter(n->left->type))
                        *p++ = 'E';
                else
                        *p++ = 'I';
                *p++ = '2';
                if(isnilinter(n->type))
                        *p++ = 'E';
                else if(isinter(n->type))
                        *p++ = 'I';
                else
                        *p++ = 'T';
                if(n->op == ODOTTYPE2)
                        *p++ = '2';
                *p = '\0';

                fn = syslook(buf, 1);
                ll = list1(typename(n->type));
                ll = list(ll, n->left);
                argtype(fn, n->left->type);
                argtype(fn, n->type);
                n = nod(OCALL, fn, N);
                n->list = ll;
                typecheck(&n, Erv | Efnstruct);
                walkexpr(&n, init);
                goto ret;

        case OCONVIFACE:
                walkexpr(&n->left, init);

                // Optimize convT2E as a two-word copy when T is uintptr-shaped.
                if(!isinter(n->left->type) && isnilinter(n->type) &&
                   (n->left->type->width == widthptr) &&
                   isint[simsimtype(n->left->type)]) {
                        l = nod(OEFACE, typename(n->left->type), n->left);
                        l->type = n->type;
                        l->typecheck = n->typecheck;
                        n = l;
                        goto ret;
                }

                // Build name of function: convI2E etc.
                // Not all names are possible
                // (e.g., we'll never generate convE2E or convE2I).
                strcpy(buf, "conv");
                p = buf+strlen(buf);
                if(isnilinter(n->left->type))
                        *p++ = 'E';
                else if(isinter(n->left->type))
                        *p++ = 'I';
                else
                        *p++ = 'T';
                *p++ = '2';
                if(isnilinter(n->type))
                        *p++ = 'E';
                else
                        *p++ = 'I';
                *p = '\0';

                fn = syslook(buf, 1);
                ll = nil;
                if(!isinter(n->left->type))
                        ll = list(ll, typename(n->left->type));
                if(!isnilinter(n->type))
                        ll = list(ll, typename(n->type));
                if(!isinter(n->left->type) && !isnilinter(n->type)){
                        sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg);
                        if(sym->def == N) {
                                l = nod(ONAME, N, N);
                                l->sym = sym;
                                l->type = ptrto(types[TUINT8]);
                                l->addable = 1;
                                l->class = PEXTERN;
                                l->xoffset = 0;
                                sym->def = l;
                                ggloblsym(sym, widthptr, 1, 0);
                        }
                        l = nod(OADDR, sym->def, N);
                        l->addable = 1;
                        ll = list(ll, l);

                        if(n->left->type->width == widthptr &&
                           isint[simsimtype(n->left->type)]) {
                                /* For pointer types, we can make a special form of optimization
                                 *
                                 * These statements are put onto the expression init list:
                                 *      Itab *tab = atomicloadtype(&cache);
                                 *      if(tab == nil)
                                 *              tab = typ2Itab(type, itype, &cache);
                                 *
                                 * The CONVIFACE expression is replaced with this:
                                 *      OEFACE{tab, ptr};
                                 */
                                l = temp(ptrto(types[TUINT8]));

                                n1 = nod(OAS, l, sym->def);
                                typecheck(&n1, Etop);
                                *init = list(*init, n1);

                                fn = syslook("typ2Itab", 1);
                                n1 = nod(OCALL, fn, N);
                                n1->list = ll;
                                typecheck(&n1, Erv);
                                walkexpr(&n1, init);

                                n2 = nod(OIF, N, N);
                                n2->ntest = nod(OEQ, l, nodnil());
                                n2->nbody = list1(nod(OAS, l, n1));
                                n2->likely = -1;
                                typecheck(&n2, Etop);
                                *init = list(*init, n2);

                                l = nod(OEFACE, l, n->left);
                                l->typecheck = n->typecheck; 
                                l->type = n->type;
                                n = l;
                                goto ret;
                        }
                }
                if(isinter(n->left->type)) {
                        ll = list(ll, n->left);
                } else {
                        // regular types are passed by reference to avoid C vararg calls
                        // orderexpr arranged for n->left to be a temporary for all
                        // the conversions it could see. comparison of an interface
                        // with a non-interface, especially in a switch on interface value
                        // with non-interface cases, is not visible to orderstmt, so we
                        // have to fall back on allocating a temp here.
                        if(islvalue(n->left))
                                ll = list(ll, nod(OADDR, n->left, N));
                        else
                                ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N));
                }
                argtype(fn, n->left->type);
                argtype(fn, n->type);
                dowidth(fn->type);
                n = nod(OCALL, fn, N);
                n->list = ll;
                typecheck(&n, Erv);
                walkexpr(&n, init);
                goto ret;

        case OCONV:
        case OCONVNOP:
                if(thechar == '5') {
                        if(isfloat[n->left->type->etype]) {
                                if(n->type->etype == TINT64) {
                                        n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64]));
                                        goto ret;
                                }
                                if(n->type->etype == TUINT64) {
                                        n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64]));
                                        goto ret;
                                }
                        }
                        if(isfloat[n->type->etype]) {
                                if(n->left->type->etype == TINT64) {
                                        n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64]));
                                        goto ret;
                                }
                                if(n->left->type->etype == TUINT64) {
                                        n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64]));
                                        goto ret;
                                }
                        }
                }
                walkexpr(&n->left, init);
                goto ret;

        case OANDNOT:
                walkexpr(&n->left, init);
                n->op = OAND;
                n->right = nod(OCOM, n->right, N);
                typecheck(&n->right, Erv);
                walkexpr(&n->right, init);
                goto ret;

        case OMUL:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                walkmul(&n, init);
                goto ret;

        case ODIV:
        case OMOD:
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);
                /*
                 * rewrite complex div into function call.
                 */
                et = n->left->type->etype;
                if(iscomplex[et] && n->op == ODIV) {
                        t = n->type;
                        n = mkcall("complex128div", types[TCOMPLEX128], init,
                                conv(n->left, types[TCOMPLEX128]),
                                conv(n->right, types[TCOMPLEX128]));
                        n = conv(n, t);
                        goto ret;
                }
                // Nothing to do for float divisions.
                if(isfloat[et])
                        goto ret;

                // Try rewriting as shifts or magic multiplies.
                walkdiv(&n, init);

                /*
                 * rewrite 64-bit div and mod into function calls
                 * on 32-bit architectures.
                 */
                switch(n->op) {
                case OMOD:
                case ODIV:
                        if(widthreg >= 8 || (et != TUINT64 && et != TINT64))
                                goto ret;
                        if(et == TINT64)
                                strcpy(namebuf, "int64");
                        else
                                strcpy(namebuf, "uint64");
                        if(n->op == ODIV)
                                strcat(namebuf, "div");
                        else
                                strcat(namebuf, "mod");
                        n = mkcall(namebuf, n->type, init,
                                conv(n->left, types[et]), conv(n->right, types[et]));
                        break;
                default:
                        break;
                }
                goto ret;

        case OINDEX:
                walkexpr(&n->left, init);
                // save the original node for bounds checking elision.
                // If it was a ODIV/OMOD walk might rewrite it.
                r = n->right;
                walkexpr(&n->right, init);

                // if range of type cannot exceed static array bound,
                // disable bounds check.
                if(n->bounded)
                        goto ret;
                t = n->left->type;
                if(t != T && isptr[t->etype])
                        t = t->type;
                if(isfixedarray(t)) {
                        n->bounded = bounded(r, t->bound);
                        if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
                                warn("index bounds check elided");
                        if(smallintconst(n->right) && !n->bounded)
                                yyerror("index out of bounds");
                } else if(isconst(n->left, CTSTR)) {
                        n->bounded = bounded(r, n->left->val.u.sval->len);
                        if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
                                warn("index bounds check elided");
                        if(smallintconst(n->right)) {
                                if(!n->bounded)
                                        yyerror("index out of bounds");
                                else {
                                        // replace "abc"[1] with 'b'.
                                        // delayed until now because "abc"[1] is not
                                        // an ideal constant.
                                        v = mpgetfix(n->right->val.u.xval);
                                        nodconst(n, n->type, n->left->val.u.sval->s[v]);
                                        n->typecheck = 1;
                                }
                        }
                }

                if(isconst(n->right, CTINT))
                if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 ||
                   mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
                        yyerror("index out of bounds");
                goto ret;

        case OINDEXMAP:
                if(n->etype == 1)
                        goto ret;
                walkexpr(&n->left, init);
                walkexpr(&n->right, init);

                t = n->left->type;
                p = nil;
                if(t->type->width <= 128) {  // Check ../../pkg/runtime/hashmap.c:MAXVALUESIZE before changing.
                        switch(simsimtype(t->down)) {
                        case TINT32:
                        case TUINT32:
                                p = "mapaccess1_fast32";
                                break;
                        case TINT64:
                        case TUINT64:
                                p = "mapaccess1_fast64";
                                break;
                        case TSTRING:
                                p = "mapaccess1_faststr";
                                break;
                        }
                }
                if(p != nil) {
                        // fast versions take key by value
                        key = n->right;
                } else {
                        // standard version takes key by reference.
                        // orderexpr made sure key is addressable.
                        key = nod(OADDR, n->right, N);
                        p = "mapaccess1";
                }
                n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key);
                n = nod(OIND, n, N);
                n->type = t->type;
                n->typecheck = 1;
                // mapaccess needs a zero value to be at least this big.
                if(zerosize < t->type->width)
                        zerosize = t->type->width;
                goto ret;

        case ORECV:
                fatal("walkexpr ORECV"); // should see inside OAS only

        case OSLICE:
                if(n->right != N && n->right->left == N && n->right->right == N) { // noop
                        walkexpr(&n->left, init);
                        n = n->left;
                        goto ret;
                }
                // fallthrough
        case OSLICEARR:
        case OSLICESTR:
                if(n->right == N) // already processed
                        goto ret;

                walkexpr(&n->left, init);
                // cgen_slice can't handle string literals as source
                // TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
                if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX))
                        n->left = copyexpr(n->left, n->left->type, init);
                else
                        n->left = safeexpr(n->left, init);
                walkexpr(&n->right->left, init);
                n->right->left = safeexpr(n->right->left, init);
                walkexpr(&n->right->right, init);
                n->right->right = safeexpr(n->right->right, init);
                n = sliceany(n, init);  // chops n->right, sets n->list
                goto ret;
        
        case OSLICE3:
        case OSLICE3ARR:
                if(n->right == N) // already processed
                        goto ret;

                walkexpr(&n->left, init);
                // TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
                // TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
                if(n->left->op == OINDEX)
                        n->left = copyexpr(n->left, n->left->type, init);
                else
                        n->left = safeexpr(n->left, init);
                walkexpr(&n->right->left, init);
                n->right->left = safeexpr(n->right->left, init);
                walkexpr(&n->right->right->left, init);
                n->right->right->left = safeexpr(n->right->right->left, init);
                walkexpr(&n->right->right->right, init);
                n->right->right->right = safeexpr(n->right->right->right, init);
                n = sliceany(n, init);  // chops n->right, sets n->list
                goto ret;

        case OADDR:
                walkexpr(&n->left, init);
                goto ret;

        case ONEW:
                if(n->esc == EscNone && n->type->type->width < (1<<16)) {
                        r = temp(n->type->type);
                        r = nod(OAS, r, N);  // zero temp
                        typecheck(&r, Etop);
                        *init = list(*init, r);
                        r = nod(OADDR, r->left, N);
                        typecheck(&r, Erv);
                        n = r;
                } else {
                        n = callnew(n->type->type);
                }
                goto ret;

        case OCMPSTR:
                // If one argument to the comparison is an empty string,
                // comparing the lengths instead will yield the same result
                // without the function call.
                if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) ||
                   (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) {
                        r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N));
                        typecheck(&r, Erv);
                        walkexpr(&r, init);
                        r->type = n->type;
                        n = r;
                        goto ret;
                }

                // s + "badgerbadgerbadger" == "badgerbadgerbadger"
                if((n->etype == OEQ || n->etype == ONE) &&
                   isconst(n->right, CTSTR) &&
                   n->left->op == OADDSTR && count(n->left->list) == 2 &&
                   isconst(n->left->list->next->n, CTSTR) &&
                   cmpslit(n->right, n->left->list->next->n) == 0) {
                        r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0));
                        typecheck(&r, Erv);
                        walkexpr(&r, init);
                        r->type = n->type;
                        n = r;
                        goto ret;
                }

                if(n->etype == OEQ || n->etype == ONE) {
                        // prepare for rewrite below
                        n->left = cheapexpr(n->left, init);
                        n->right = cheapexpr(n->right, init);

                        r = mkcall("eqstring", types[TBOOL], init,
                                conv(n->left, types[TSTRING]),
                                conv(n->right, types[TSTRING]));

                        // quick check of len before full compare for == or !=
                        if(n->etype == OEQ) {
                                // len(left) == len(right) && eqstring(left, right)
                                r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
                        } else {
                                // len(left) != len(right) || !eqstring(left, right)
                                r = nod(ONOT, r, N);
                                r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
                        }
                        typecheck(&r, Erv);
                        walkexpr(&r, nil);
                } else {
                        // sys_cmpstring(s1, s2) :: 0
                        r = mkcall("cmpstring", types[TINT], init,
                                conv(n->left, types[TSTRING]),
                                conv(n->right, types[TSTRING]));
                        r = nod(n->etype, r, nodintconst(0));
                }

                typecheck(&r, Erv);
                if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
                r->type = n->type;
                n = r;
                goto ret;

        case OADDSTR:
                n = addstr(n, init);
                goto ret;
        
        case OAPPEND:
                if(n->isddd)
                        n = appendslice(n, init); // also works for append(slice, string).
                else
                        n = append(n, init);
                goto ret;

        case OCOPY:
                n = copyany(n, init, flag_race);
                goto ret;

        case OCLOSE:
                // cannot use chanfn - closechan takes any, not chan any
                fn = syslook("closechan", 1);
                argtype(fn, n->left->type);
                n = mkcall1(fn, T, init, n->left);
                goto ret;

        case OMAKECHAN:
                n = mkcall1(chanfn("makechan", 1, n->type), n->type, init,
                        typename(n->type),
                        conv(n->left, types[TINT64]));
                goto ret;

        case OMAKEMAP:
                t = n->type;

                fn = syslook("makemap", 1);
                argtype(fn, t->down);   // any-1
                argtype(fn, t->type);   // any-2

                n = mkcall1(fn, n->type, init,
                        typename(n->type),
                        conv(n->left, types[TINT64]));
                goto ret;

        case OMAKESLICE:
                l = n->left;
                r = n->right;
                if(r == nil)
                        l = r = safeexpr(l, init);
                t = n->type;
                if(n->esc == EscNone
                        && smallintconst(l) && smallintconst(r)
                        && (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) {
                        // var arr [r]T
                        // n = arr[:l]
                        t = aindex(r, t->type); // [r]T
                        var = temp(t);
                        a = nod(OAS, var, N); // zero temp
                        typecheck(&a, Etop);
                        *init = list(*init, a);
                        r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l]
                        r = conv(r, n->type); // in case n->type is named.
                        typecheck(&r, Erv);
                        walkexpr(&r, init);
                        n = r;
                } else {
                        // makeslice(t *Type, nel int64, max int64) (ary []any)
                        fn = syslook("makeslice", 1);
                        argtype(fn, t->type);                   // any-1
                        n = mkcall1(fn, n->type, init,
                                typename(n->type),
                                conv(l, types[TINT64]),
                                conv(r, types[TINT64]));
                }
                goto ret;

        case ORUNESTR:
                // sys_intstring(v)
                n = mkcall("intstring", n->type, init,
                        conv(n->left, types[TINT64]));
                goto ret;

        case OARRAYBYTESTR:
                // slicebytetostring([]byte) string;
                n = mkcall("slicebytetostring", n->type, init, n->left);
                goto ret;

        case OARRAYBYTESTRTMP:
                // slicebytetostringtmp([]byte) string;
                n = mkcall("slicebytetostringtmp", n->type, init, n->left);
                goto ret;

        case OARRAYRUNESTR:
                // slicerunetostring([]rune) string;
                n = mkcall("slicerunetostring", n->type, init, n->left);
                goto ret;

        case OSTRARRAYBYTE:
                // stringtoslicebyte(string) []byte;
                n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
                goto ret;

        case OSTRARRAYRUNE:
                // stringtoslicerune(string) []rune
                n = mkcall("stringtoslicerune", n->type, init, n->left);
                goto ret;

        case OCMPIFACE:
                // ifaceeq(i1 any-1, i2 any-2) (ret bool);
                if(!eqtype(n->left->type, n->right->type))
                        fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type);
                if(isnilinter(n->left->type))
                        fn = syslook("efaceeq", 1);
                else
                        fn = syslook("ifaceeq", 1);

                n->right = cheapexpr(n->right, init);
                n->left = cheapexpr(n->left, init);
                argtype(fn, n->right->type);
                argtype(fn, n->left->type);
                r = mkcall1(fn, n->type, init, n->left, n->right);
                if(n->etype == ONE)
                        r = nod(ONOT, r, N);
                
                // check itable/type before full compare.
                if(n->etype == OEQ)
                        r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
                else
                        r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
                typecheck(&r, Erv);
                walkexpr(&r, init);
                r->type = n->type;
                n = r;
                goto ret;

        case OARRAYLIT:
        case OMAPLIT:
        case OSTRUCTLIT:
        case OPTRLIT:
                // XXX TODO do we need to clear var?
                var = temp(n->type);
                anylit(0, n, var, init);
                n = var;
                goto ret;

        case OSEND:
                n1 = n->right;
                n1 = assignconv(n1, n->left->type->type, "chan send");
                walkexpr(&n1, init);
                n1 = nod(OADDR, n1, N);
                n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1);
                goto ret;

        case OCLOSURE:
                n = walkclosure(n, init);
                goto ret;
        
        case OCALLPART:
                n = walkpartialcall(n, init);
                goto ret;
        }
        fatal("missing switch %O", n->op);

ret:
        // Expressions that are constant at run time but not
        // considered const by the language spec are not turned into
        // constants until walk. For example, if n is y%1 == 0, the
        // walk of y%1 may have replaced it by 0.
        // Check whether n with its updated args is itself now a constant.
        t = n->type;
        evconst(n);
        n->type = t;
        if(n->op == OLITERAL)
                typecheck(&n, Erv);

        ullmancalc(n);

        if(debug['w'] && n != N)
                dump("walk", n);

        lineno = lno;
        *np = n;
}

static Node*
ascompatee1(int op, Node *l, Node *r, NodeList **init)
{
        Node *n;
        USED(op);
        
        // convas will turn map assigns into function calls,
        // making it impossible for reorder3 to work.
        n = nod(OAS, l, r);
        if(l->op == OINDEXMAP)
                return n;

        return convas(n, init);
}

static NodeList*
ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
{
        NodeList *ll, *lr, *nn;

        /*
         * check assign expression list to
         * a expression list. called in
         *      expr-list = expr-list
         */

        // ensure order of evaluation for function calls
        for(ll=nl; ll; ll=ll->next)
                ll->n = safeexpr(ll->n, init);
        for(lr=nr; lr; lr=lr->next)
                lr->n = safeexpr(lr->n, init);

        nn = nil;
        for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) {
                // Do not generate 'x = x' during return. See issue 4014.
                if(op == ORETURN && ll->n == lr->n)
                        continue;
                nn = list(nn, ascompatee1(op, ll->n, lr->n, init));
        }

        // cannot happen: caller checked that lists had same length
        if(ll || lr)
                yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name);
        return nn;
}

/*
 * l is an lv and rt is the type of an rv
 * return 1 if this implies a function call
 * evaluating the lv or a function call
 * in the conversion of the types
 */
static int
fncall(Node *l, Type *rt)
{
        if(l->ullman >= UINF || l->op == OINDEXMAP)
                return 1;
        if(eqtype(l->type, rt))
                return 0;
        return 1;
}

static NodeList*
ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
{
        Node *l, *tmp, *a;
        NodeList *ll;
        Type *r;
        Iter saver;
        int ucount;
        NodeList *nn, *mm;

        USED(op);

        /*
         * check assign type list to
         * a expression list. called in
         *      expr-list = func()
         */
        r = structfirst(&saver, nr);
        nn = nil;
        mm = nil;
        ucount = 0;
        for(ll=nl; ll; ll=ll->next) {
                if(r == T)
                        break;
                l = ll->n;
                if(isblank(l)) {
                        r = structnext(&saver);
                        continue;
                }

                // any lv that causes a fn call must be
                // deferred until all the return arguments
                // have been pulled from the output arguments
                if(fncall(l, r->type)) {
                        tmp = temp(r->type);
                        typecheck(&tmp, Erv);
                        a = nod(OAS, l, tmp);
                        a = convas(a, init);
                        mm = list(mm, a);
                        l = tmp;
                }

                a = nod(OAS, l, nodarg(r, fp));
                a = convas(a, init);
                ullmancalc(a);
                if(a->ullman >= UINF)
                        ucount++;
                nn = list(nn, a);
                r = structnext(&saver);
        }

        if(ll != nil || r != T)
                yyerror("ascompatet: assignment count mismatch: %d = %d",
                        count(nl), structcount(*nr));

        if(ucount)
                fatal("ascompatet: too many function calls evaluating parameters");
        return concat(nn, mm);
}

 /*
 * package all the arguments that match a ... T parameter into a []T.
 */
static NodeList*
mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd)
{
        Node *a, *n;
        Type *tslice;
        int esc;
        
        esc = EscUnknown;
        if(ddd != nil)
                esc = ddd->esc;
        
        tslice = typ(TARRAY);
        tslice->type = l->type->type;
        tslice->bound = -1;

        if(count(lr0) == 0) {
                n = nodnil();
                n->type = tslice;
        } else {
                n = nod(OCOMPLIT, N, typenod(tslice));
                if(ddd != nil)
                        n->alloc = ddd->alloc; // temporary to use
                n->list = lr0;
                n->esc = esc;
                typecheck(&n, Erv);
                if(n->type == T)
                        fatal("mkdotargslice: typecheck failed");
                walkexpr(&n, init);
        }

        a = nod(OAS, nodarg(l, fp), n);
        nn = list(nn, convas(a, init));
        return nn;
}

/*
 * helpers for shape errors
 */
static char*
dumptypes(Type **nl, char *what)
{
        int first;
        Type *l;
        Iter savel;
        Fmt fmt;

        fmtstrinit(&fmt);
        fmtprint(&fmt, "\t");
        first = 1;
        for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
                if(first)
                        first = 0;
                else
                        fmtprint(&fmt, ", ");
                fmtprint(&fmt, "%T", l);
        }
        if(first)
                fmtprint(&fmt, "[no arguments %s]", what);
        return fmtstrflush(&fmt);
}

static char*
dumpnodetypes(NodeList *l, char *what)
{
        int first;
        Node *r;
        Fmt fmt;

        fmtstrinit(&fmt);
        fmtprint(&fmt, "\t");
        first = 1;
        for(; l; l=l->next) {
                r = l->n;
                if(first)
                        first = 0;
                else
                        fmtprint(&fmt, ", ");
                fmtprint(&fmt, "%T", r->type);
        }
        if(first)
                fmtprint(&fmt, "[no arguments %s]", what);
        return fmtstrflush(&fmt);
}

/*
 * check assign expression list to
 * a type list. called in
 *      return expr-list
 *      func(expr-list)
 */
static NodeList*
ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
{
        Type *l, *ll;
        Node *r, *a;
        NodeList *nn, *lr0, *alist;
        Iter savel;
        char *l1, *l2;

        lr0 = lr;
        l = structfirst(&savel, nl);
        r = N;
        if(lr)
                r = lr->n;
        nn = nil;

        // f(g()) where g has multiple return values
        if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
                // optimization - can do block copy
                if(eqtypenoname(r->type, *nl)) {
                        a = nodarg(*nl, fp);
                        r = nod(OCONVNOP, r, N);
                        r->type = a->type;
                        nn = list1(convas(nod(OAS, a, r), init));
                        goto ret;
                }

                // conversions involved.
                // copy into temporaries.
                alist = nil;
                for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
                        a = temp(l->type);
                        alist = list(alist, a);
                }
                a = nod(OAS2, N, N);
                a->list = alist;
                a->rlist = lr;
                typecheck(&a, Etop);
                walkstmt(&a);
                *init = list(*init, a);
                lr = alist;
                r = lr->n;
                l = structfirst(&savel, nl);
        }

loop:
        if(l != T && l->isddd) {
                // the ddd parameter must be last
                ll = structnext(&savel);
                if(ll != T)
                        yyerror("... must be last argument");

                // special case --
                // only if we are assigning a single ddd
                // argument to a ddd parameter then it is
                // passed thru unencapsulated
                if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) {
                        a = nod(OAS, nodarg(l, fp), r);
                        a = convas(a, init);
                        nn = list(nn, a);
                        goto ret;
                }

                // normal case -- make a slice of all
                // remaining arguments and pass it to
                // the ddd parameter.
                nn = mkdotargslice(lr, nn, l, fp, init, call->right);
                goto ret;
        }

        if(l == T || r == N) {
                if(l != T || r != N) {
                        l1 = dumptypes(nl, "expected");
                        l2 = dumpnodetypes(lr0, "given");
                        if(l != T)
                                yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2);
                        else
                                yyerror("too many arguments to %O\n%s\n%s", op, l1, l2);
                }
                goto ret;
        }

        a = nod(OAS, nodarg(l, fp), r);
        a = convas(a, init);
        nn = list(nn, a);

        l = structnext(&savel);
        r = N;
        lr = lr->next;
        if(lr != nil)
                r = lr->n;
        goto loop;

ret:
        for(lr=nn; lr; lr=lr->next)
                lr->n->typecheck = 1;
        return nn;
}

// generate code for print
static Node*
walkprint(Node *nn, NodeList **init, int defer)
{
        Node *r;
        Node *n;
        NodeList *l, *all;
        Node *on;
        Type *t;
        int notfirst, et, op;
        NodeList *calls, *intypes, *args;
        Fmt fmt;

        on = nil;
        op = nn->op;
        all = nn->list;
        calls = nil;
        notfirst = 0;
        intypes = nil;
        args = nil;

        memset(&fmt, 0, sizeof fmt);
        if(defer) {
                // defer print turns into defer printf with format string
                fmtstrinit(&fmt);
                intypes = list(intypes, nod(ODCLFIELD, N, typenod(types[TSTRING])));
                args = list1(nod(OXXX, N, N));
        }

        for(l=all; l; l=l->next) {
                if(notfirst) {
                        if(defer)
                                fmtprint(&fmt, " ");
                        else
                                calls = list(calls, mkcall("printsp", T, init));
                }
                notfirst = op == OPRINTN;

                n = l->n;
                if(n->op == OLITERAL) {
                        switch(n->val.ctype) {
                        case CTRUNE:
                                defaultlit(&n, runetype);
                                break;
                        case CTINT:
                                defaultlit(&n, types[TINT64]);
                                break;
                        case CTFLT:
                                defaultlit(&n, types[TFLOAT64]);
                                break;
                        }
                }
                if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL)
                        defaultlit(&n, types[TINT64]);
                defaultlit(&n, nil);
                l->n = n;
                if(n->type == T || n->type->etype == TFORW)
                        continue;

                t = n->type;
                et = n->type->etype;
                if(isinter(n->type)) {
                        if(defer) {
                                if(isnilinter(n->type))
                                        fmtprint(&fmt, "%%e");
                                else
                                        fmtprint(&fmt, "%%i");
                        } else {
                                if(isnilinter(n->type))
                                        on = syslook("printeface", 1);
                                else
                                        on = syslook("printiface", 1);
                                argtype(on, n->type);           // any-1
                        }
                } else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
                        if(defer) {
                                fmtprint(&fmt, "%%p");
                        } else {
                                on = syslook("printpointer", 1);
                                argtype(on, n->type);   // any-1
                        }
                } else if(isslice(n->type)) {
                        if(defer) {
                                fmtprint(&fmt, "%%a");
                        } else {
                                on = syslook("printslice", 1);
                                argtype(on, n->type);   // any-1
                        }
                } else if(isint[et]) {
                        if(defer) {
                                if(et == TUINT64)
                                        fmtprint(&fmt, "%%U");
                                else {
                                        fmtprint(&fmt, "%%D");
                                        t = types[TINT64];
                                }
                        } else {
                                if(et == TUINT64)
                                        on = syslook("printuint", 0);
                                else
                                        on = syslook("printint", 0);
                        }
                } else if(isfloat[et]) {
                        if(defer) {
                                fmtprint(&fmt, "%%f");
                                t = types[TFLOAT64];
                        } else
                                on = syslook("printfloat", 0);
                } else if(iscomplex[et]) {
                        if(defer) {
                                fmtprint(&fmt, "%%C");
                                t = types[TCOMPLEX128];
                        } else
                                on = syslook("printcomplex", 0);
                } else if(et == TBOOL) {
                        if(defer)
                                fmtprint(&fmt, "%%t");
                        else
                                on = syslook("printbool", 0);
                } else if(et == TSTRING) {
                        if(defer)
                                fmtprint(&fmt, "%%S");
                        else
                                on = syslook("printstring", 0);
                } else {
                        badtype(OPRINT, n->type, T);
                        continue;
                }

                if(!defer) {
                        t = *getinarg(on->type);
                        if(t != nil)
                                t = t->type;
                        if(t != nil)
                                t = t->type;
                }

                if(!eqtype(t, n->type)) {
                        n = nod(OCONV, n, N);
                        n->type = t;
                }

                if(defer) {
                        intypes = list(intypes, nod(ODCLFIELD, N, typenod(t)));
                        args = list(args, n);
                } else {
                        r = nod(OCALL, on, N);
                        r->list = list1(n);
                        calls = list(calls, r);
                }
        }

        if(defer) {
                if(op == OPRINTN)
                        fmtprint(&fmt, "\n");
                on = syslook("goprintf", 1);
                on->type = functype(nil, intypes, nil);
                args->n = nod(OLITERAL, N, N);
                args->n->val.ctype = CTSTR;
                args->n->val.u.sval = strlit(fmtstrflush(&fmt));
                r = nod(OCALL, on, N);
                r->list = args;
                typecheck(&r, Etop);
                walkexpr(&r, init);
        } else {
                if(op == OPRINTN)
                        calls = list(calls, mkcall("printnl", T, nil));
                typechecklist(calls, Etop);
                walkexprlist(calls, init);

                r = nod(OEMPTY, N, N);
                typecheck(&r, Etop);
                walkexpr(&r, init);
                r->ninit = calls;
        }
        return r;
}

Node*
callnew(Type *t)
{
        Node *fn;

        dowidth(t);
        fn = syslook("new", 1);
        argtype(fn, t);
        return mkcall1(fn, ptrto(t), nil, typename(t));
}

static Node*
convas(Node *n, NodeList **init)
{
        Type *lt, *rt;
        Node *map, *key, *val;

        if(n->op != OAS)
                fatal("convas: not OAS %O", n->op);

        n->typecheck = 1;

        if(n->left == N || n->right == N)
                goto out;

        lt = n->left->type;
        rt = n->right->type;
        if(lt == T || rt == T)
                goto out;

        if(isblank(n->left)) {
                defaultlit(&n->right, T);
                goto out;
        }

        if(n->left->op == OINDEXMAP) {
                map = n->left->left;
                key = n->left->right;
                val = n->right;
                walkexpr(&map, init);
                walkexpr(&key, init);
                walkexpr(&val, init);
                // orderexpr made sure key and val are addressable.
                key = nod(OADDR, key, N);
                val = nod(OADDR, val, N);
                n = mkcall1(mapfn("mapassign1", map->type), T, init,
                        typename(map->type), map, key, val);
                goto out;
        }

        if(eqtype(lt, rt))
                goto out;

        n->right = assignconv(n->right, lt, "assignment");
        walkexpr(&n->right, init);

out:
        ullmancalc(n);
        return n;
}

/*
 * from ascompat[te]
 * evaluating actual function arguments.
 *      f(a,b)
 * if there is exactly one function expr,
 * then it is done first. otherwise must
 * make temp variables
 */
static NodeList*
reorder1(NodeList *all)
{
        Node *f, *a, *n;
        NodeList *l, *r, *g;
        int c, d, t;

        c = 0;  // function calls
        t = 0;  // total parameters

        for(l=all; l; l=l->next) {
                n = l->n;
                t++;
                ullmancalc(n);
                if(n->ullman >= UINF)
                        c++;
        }
        if(c == 0 || t == 1)
                return all;

        g = nil;        // fncalls assigned to tempnames
        f = N;  // last fncall assigned to stack
        r = nil;        // non fncalls and tempnames assigned to stack
        d = 0;
        for(l=all; l; l=l->next) {
                n = l->n;
                if(n->ullman < UINF) {
                        r = list(r, n);
                        continue;
                }
                d++;
                if(d == c) {
                        f = n;
                        continue;
                }

                // make assignment of fncall to tempname
                a = temp(n->right->type);
                a = nod(OAS, a, n->right);
                g = list(g, a);

                // put normal arg assignment on list
                // with fncall replaced by tempname
                n->right = a->left;
                r = list(r, n);
        }

        if(f != N)
                g = list(g, f);
        return concat(g, r);
}

static void reorder3save(Node**, NodeList*, NodeList*, NodeList**);
static int aliased(Node*, NodeList*, NodeList*);

/*
 * from ascompat[ee]
 *      a,b = c,d
 * simultaneous assignment. there cannot
 * be later use of an earlier lvalue.
 *
 * function calls have been removed.
 */
static NodeList*
reorder3(NodeList *all)
{
        NodeList *list, *early, *mapinit;
        Node *l;

        // If a needed expression may be affected by an
        // earlier assignment, make an early copy of that
        // expression and use the copy instead.
        early = nil;
        mapinit = nil;
        for(list=all; list; list=list->next) {
                l = list->n->left;

                // Save subexpressions needed on left side.
                // Drill through non-dereferences.
                for(;;) {
                        if(l->op == ODOT || l->op == OPAREN) {
                                l = l->left;
                                continue;
                        }
                        if(l->op == OINDEX && isfixedarray(l->left->type)) {
                                reorder3save(&l->right, all, list, &early);
                                l = l->left;
                                continue;
                        }
                        break;
                }
                switch(l->op) {
                default:
                        fatal("reorder3 unexpected lvalue %#O", l->op);
                case ONAME:
                        break;
                case OINDEX:
                case OINDEXMAP:
                        reorder3save(&l->left, all, list, &early);
                        reorder3save(&l->right, all, list, &early);
                        if(l->op == OINDEXMAP)
                                list->n = convas(list->n, &mapinit);
                        break;
                case OIND:
                case ODOTPTR:
                        reorder3save(&l->left, all, list, &early);
                }

                // Save expression on right side.
                reorder3save(&list->n->right, all, list, &early);
        }

        early = concat(mapinit, early);
        return concat(early, all);
}

static int vmatch2(Node*, Node*);
static int varexpr(Node*);

/*
 * if the evaluation of *np would be affected by the 
 * assignments in all up to but not including stop,
 * copy into a temporary during *early and
 * replace *np with that temp.
 */
static void
reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early)
{
        Node *n, *q;

        n = *np;
        if(!aliased(n, all, stop))
                return;
        
        q = temp(n->type);
        q = nod(OAS, q, n);
        typecheck(&q, Etop);
        *early = list(*early, q);
        *np = q->left;
}

/*
 * what's the outer value that a write to n affects?
 * outer value means containing struct or array.
 */
Node*
outervalue(Node *n)
{       
        for(;;) {
                if(n->op == ODOT || n->op == OPAREN) {
                        n = n->left;
                        continue;
                }
                if(n->op == OINDEX && isfixedarray(n->left->type)) {
                        n = n->left;
                        continue;
                }
                break;
        }
        return n;
}

/*
 * Is it possible that the computation of n might be
 * affected by writes in as up to but not including stop?
 */
static int
aliased(Node *n, NodeList *all, NodeList *stop)
{
        int memwrite, varwrite;
        Node *a;
        NodeList *l;

        if(n == N)
                return 0;

        // Look for obvious aliasing: a variable being assigned
        // during the all list and appearing in n.
        // Also record whether there are any writes to main memory.
        // Also record whether there are any writes to variables
        // whose addresses have been taken.
        memwrite = 0;
        varwrite = 0;
        for(l=all; l!=stop; l=l->next) {
                a = outervalue(l->n->left);
                if(a->op != ONAME) {
                        memwrite = 1;
                        continue;
                }
                switch(n->class) {
                default:
                        varwrite = 1;
                        continue;
                case PAUTO:
                case PPARAM:
                case PPARAMOUT:
                        if(n->addrtaken) {
                                varwrite = 1;
                                continue;
                        }
                        if(vmatch2(a, n)) {
                                // Direct hit.
                                return 1;
                        }
                }
        }

        // The variables being written do not appear in n.
        // However, n might refer to computed addresses
        // that are being written.
        
        // If no computed addresses are affected by the writes, no aliasing.
        if(!memwrite && !varwrite)
                return 0;

        // If n does not refer to computed addresses
        // (that is, if n only refers to variables whose addresses
        // have not been taken), no aliasing.
        if(varexpr(n))
                return 0;

        // Otherwise, both the writes and n refer to computed memory addresses.
        // Assume that they might conflict.
        return 1;
}

/*
 * does the evaluation of n only refer to variables
 * whose addresses have not been taken?
 * (and no other memory)
 */
static int
varexpr(Node *n)
{
        if(n == N)
                return 1;

        switch(n->op) {
        case OLITERAL:  
                return 1;
        case ONAME:
                switch(n->class) {
                case PAUTO:
                case PPARAM:
                case PPARAMOUT:
                        if(!n->addrtaken)
                                return 1;
                }
                return 0;

        case OADD:
        case OSUB:
        case OOR:
        case OXOR:
        case OMUL:
        case ODIV:
        case OMOD:
        case OLSH:
        case ORSH:
        case OAND:
        case OANDNOT:
        case OPLUS:
        case OMINUS:
        case OCOM:
        case OPAREN:
        case OANDAND:
        case OOROR:
        case ODOT:  // but not ODOTPTR
        case OCONV:
        case OCONVNOP:
        case OCONVIFACE:
        case ODOTTYPE:
                return varexpr(n->left) && varexpr(n->right);
        }

        // Be conservative.
        return 0;
}

/*
 * is the name l mentioned in r?
 */
static int
vmatch2(Node *l, Node *r)
{
        NodeList *ll;

        if(r == N)
                return 0;
        switch(r->op) {
        case ONAME:
                // match each right given left
                return l == r;
        case OLITERAL:
                return 0;
        }
        if(vmatch2(l, r->left))
                return 1;
        if(vmatch2(l, r->right))
                return 1;
        for(ll=r->list; ll; ll=ll->next)
                if(vmatch2(l, ll->n))
                        return 1;
        return 0;
}

/*
 * is any name mentioned in l also mentioned in r?
 * called by sinit.c
 */
int
vmatch1(Node *l, Node *r)
{
        NodeList *ll;

        /*
         * isolate all left sides
         */
        if(l == N || r == N)
                return 0;
        switch(l->op) {
        case ONAME:
                switch(l->class) {
                case PPARAM:
                case PPARAMREF:
                case PAUTO:
                        break;
                default:
                        // assignment to non-stack variable
                        // must be delayed if right has function calls.
                        if(r->ullman >= UINF)
                                return 1;
                        break;
                }
                return vmatch2(l, r);
        case OLITERAL:
                return 0;
        }
        if(vmatch1(l->left, r))
                return 1;
        if(vmatch1(l->right, r))
                return 1;
        for(ll=l->list; ll; ll=ll->next)
                if(vmatch1(ll->n, r))
                        return 1;
        return 0;
}

/*
 * walk through argin parameters.
 * generate and return code to allocate
 * copies of escaped parameters to the heap.
 */
static NodeList*
paramstoheap(Type **argin, int out)
{
        Type *t;
        Iter savet;
        Node *v;
        NodeList *nn;

        nn = nil;
        for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
                v = t->nname;
                if(v && v->sym && v->sym->name[0] == '~' && v->sym->name[1] == 'r') // unnamed result
                        v = N;
                // In precisestack mode, the garbage collector assumes results
                // are always live, so zero them always.
                if(out && (precisestack_enabled || (v == N && hasdefer))) {
                        // Defer might stop a panic and show the
                        // return values as they exist at the time of panic.
                        // Make sure to zero them on entry to the function.
                        nn = list(nn, nod(OAS, nodarg(t, 1), N));
                }
                if(v == N || !(v->class & PHEAP))
                        continue;

                // generate allocation & copying code
                if(v->alloc == nil)
                        v->alloc = callnew(v->type);
                nn = list(nn, nod(OAS, v->heapaddr, v->alloc));
                if((v->class & ~PHEAP) != PPARAMOUT)
                        nn = list(nn, nod(OAS, v, v->stackparam));
        }
        return nn;
}

/*
 * walk through argout parameters copying back to stack
 */
static NodeList*
returnsfromheap(Type **argin)
{
        Type *t;
        Iter savet;
        Node *v;
        NodeList *nn;

        nn = nil;
        for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
                v = t->nname;
                if(v == N || v->class != (PHEAP|PPARAMOUT))
                        continue;
                nn = list(nn, nod(OAS, v->stackparam, v));
        }
        return nn;
}

/*
 * take care of migrating any function in/out args
 * between the stack and the heap.  adds code to
 * curfn's before and after lists.
 */
static void
heapmoves(void)
{
        NodeList *nn;
        int32 lno;

        lno = lineno;
        lineno = curfn->lineno;
        nn = paramstoheap(getthis(curfn->type), 0);
        nn = concat(nn, paramstoheap(getinarg(curfn->type), 0));
        nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1));
        curfn->enter = concat(curfn->enter, nn);
        lineno = curfn->endlineno;
        curfn->exit = returnsfromheap(getoutarg(curfn->type));
        lineno = lno;
}

static Node*
vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
{
        int i, n;
        Node *r;
        NodeList *args;

        if(fn->type == T || fn->type->etype != TFUNC)
                fatal("mkcall %N %T", fn, fn->type);

        args = nil;
        n = fn->type->intuple;
        for(i=0; i<n; i++)
                args = list(args, va_arg(va, Node*));

        r = nod(OCALL, fn, N);
        r->list = args;
        if(fn->type->outtuple > 0)
                typecheck(&r, Erv | Efnstruct);
        else
                typecheck(&r, Etop);
        walkexpr(&r, init);
        r->type = t;
        return r;
}

Node*
mkcall(char *name, Type *t, NodeList **init, ...)
{
        Node *r;
        va_list va;

        va_start(va, init);
        r = vmkcall(syslook(name, 0), t, init, va);
        va_end(va);
        return r;
}

Node*
mkcall1(Node *fn, Type *t, NodeList **init, ...)
{
        Node *r;
        va_list va;

        va_start(va, init);
        r = vmkcall(fn, t, init, va);
        va_end(va);
        return r;
}

Node*
conv(Node *n, Type *t)
{
        if(eqtype(n->type, t))
                return n;
        n = nod(OCONV, n, N);
        n->type = t;
        typecheck(&n, Erv);
        return n;
}

Node*
chanfn(char *name, int n, Type *t)
{
        Node *fn;
        int i;

        if(t->etype != TCHAN)
                fatal("chanfn %T", t);
        fn = syslook(name, 1);
        for(i=0; i<n; i++)
                argtype(fn, t->type);
        return fn;
}

static Node*
mapfn(char *name, Type *t)
{
        Node *fn;

        if(t->etype != TMAP)
                fatal("mapfn %T", t);
        fn = syslook(name, 1);
        argtype(fn, t->down);
        argtype(fn, t->type);
        argtype(fn, t->down);
        argtype(fn, t->type);
        return fn;
}

static Node*
mapfndel(char *name, Type *t)
{
        Node *fn;

        if(t->etype != TMAP)
                fatal("mapfn %T", t);
        fn = syslook(name, 1);
        argtype(fn, t->down);
        argtype(fn, t->type);
        argtype(fn, t->down);
        return fn;
}

static Node*
addstr(Node *n, NodeList **init)
{
        Node *r, *cat, *slice;
        NodeList *args, *l;
        int c;
        Type *t;

        // orderexpr rewrote OADDSTR to have a list of strings.
        c = count(n->list);
        if(c < 2)
                yyerror("addstr count %d too small", c);

        // build list of string arguments
        args = nil;
        for(l=n->list; l != nil; l=l->next)
                args = list(args, conv(l->n, types[TSTRING]));

        if(c <= 5) {
                // small numbers of strings use direct runtime helpers.
                // note: orderexpr knows this cutoff too.
                snprint(namebuf, sizeof(namebuf), "concatstring%d", c);
        } else {
                // large numbers of strings are passed to the runtime as a slice.
                strcpy(namebuf, "concatstrings");
                t = typ(TARRAY);
                t->type = types[TSTRING];
                t->bound = -1;
                slice = nod(OCOMPLIT, N, typenod(t));
                slice->alloc = n->alloc;
                slice->list = args;
                slice->esc = EscNone;
                args = list1(slice);
        }
        cat = syslook(namebuf, 1);
        r = nod(OCALL, cat, N);
        r->list = args;
        typecheck(&r, Erv);
        walkexpr(&r, init);
        r->type = n->type;

        return r;
}

// expand append(l1, l2...) to
//   init {
//     s := l1
//     if n := len(l1) + len(l2) - cap(s); n > 0 {
//       s = growslice(s, n)
//     }
//     s = s[:len(l1)+len(l2)]
//     memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
//   }
//   s
//
// l2 is allowed to be a string.
static Node*
appendslice(Node *n, NodeList **init)
{
        NodeList *l;
        Node *l1, *l2, *nt, *nif, *fn;
        Node *nptr1, *nptr2, *nwid;
        Node *s;

        walkexprlistsafe(n->list, init);

        // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
        // and n are name or literal, but those may index the slice we're
        // modifying here.  Fix explicitly.
        for(l=n->list; l; l=l->next)
                l->n = cheapexpr(l->n, init);

        l1 = n->list->n;
        l2 = n->list->next->n;

        s = temp(l1->type); // var s []T
        l = nil;
        l = list(l, nod(OAS, s, l1)); // s = l1

        nt = temp(types[TINT]);
        nif = nod(OIF, N, N);
        // n := len(s) + len(l2) - cap(s)
        nif->ninit = list1(nod(OAS, nt,
                nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N))));
        nif->ntest = nod(OGT, nt, nodintconst(0));
        // instantiate growslice(Type*, []any, int64) []any
        fn = syslook("growslice", 1);
        argtype(fn, s->type->type);
        argtype(fn, s->type->type);

        // s = growslice(T, s, n)
        nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit,
                                               typename(s->type),
                                               s,
                                               conv(nt, types[TINT64]))));

        l = list(l, nif);

        if(flag_race) {
                // rely on runtime to instrument copy.
                // copy(s[len(l1):len(l1)+len(l2)], l2)
                nptr1 = nod(OSLICE, s, nod(OKEY,
                        nod(OLEN, l1, N),
                        nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N))));
                nptr1->etype = 1;
                nptr2 = l2;
                if(l2->type->etype == TSTRING)
                        fn = syslook("slicestringcopy", 1);
                else
                        fn = syslook("copy", 1);
                argtype(fn, l1->type);
                argtype(fn, l2->type);
                nt = mkcall1(fn, types[TINT], &l,
                                nptr1, nptr2,
                                nodintconst(s->type->type->width));
                l = list(l, nt);
        } else {
                // memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
                nptr1 = nod(OINDEX, s, nod(OLEN, l1, N));
                nptr1->bounded = 1;
                nptr1 = nod(OADDR, nptr1, N);

                nptr2 = nod(OSPTR, l2, N);

                fn = syslook("memmove", 1);
                argtype(fn, s->type->type);     // 1 old []any
                argtype(fn, s->type->type);     // 2 ret []any

                nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l);
                nwid = nod(OMUL, nwid, nodintconst(s->type->type->width));
                nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid);
                l = list(l, nt);
        }

        // s = s[:len(l1)+len(l2)]
        nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N));
        nt = nod(OSLICE, s, nod(OKEY, N, nt));
        nt->etype = 1;
        l = list(l, nod(OAS, s, nt));

        typechecklist(l, Etop);
        walkstmtlist(l);
        *init = concat(*init, l);
        return s;
}

// expand append(src, a [, b]* ) to
//
//   init {
//     s := src
//     const argc = len(args) - 1
//     if cap(s) - len(s) < argc {
//          s = growslice(s, argc)
//     }
//     n := len(s)
//     s = s[:n+argc]
//     s[n] = a
//     s[n+1] = b
//     ...
//   }
//   s
static Node*
append(Node *n, NodeList **init)
{
        NodeList *l, *a;
        Node *nsrc, *ns, *nn, *na, *nx, *fn;
        int argc;

        walkexprlistsafe(n->list, init);

        // walkexprlistsafe will leave OINDEX (s[n]) alone if both s
        // and n are name or literal, but those may index the slice we're
        // modifying here.  Fix explicitly.
        for(l=n->list; l; l=l->next)
                l->n = cheapexpr(l->n, init);

        nsrc = n->list->n;

        // Resolve slice type of multi-valued return.
        if(istype(nsrc->type, TSTRUCT))
                nsrc->type = nsrc->type->type->type;
        argc = count(n->list) - 1;
        if (argc < 1) {
                return nsrc;
        }

        l = nil;

        ns = temp(nsrc->type);
        l = list(l, nod(OAS, ns, nsrc));  // s = src

        na = nodintconst(argc);         // const argc
        nx = nod(OIF, N, N);            // if cap(s) - len(s) < argc
        nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);

        fn = syslook("growslice", 1);   //   growslice(<type>, old []T, n int64) (ret []T)
        argtype(fn, ns->type->type);    // 1 old []any
        argtype(fn, ns->type->type);    // 2 ret []any

        nx->nbody = list1(nod(OAS, ns, mkcall1(fn,  ns->type, &nx->ninit,
                                               typename(ns->type),
                                               ns,
                                               conv(na, types[TINT64]))));
        l = list(l, nx);

        nn = temp(types[TINT]);
        l = list(l, nod(OAS, nn, nod(OLEN, ns, N)));     // n = len(s)

        nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na)));   // ...s[:n+argc]
        nx->etype = 1;
        l = list(l, nod(OAS, ns, nx));                  // s = s[:n+argc]

        for (a = n->list->next;  a != nil; a = a->next) {
                nx = nod(OINDEX, ns, nn);               // s[n] ...
                nx->bounded = 1;
                l = list(l, nod(OAS, nx, a->n));        // s[n] = arg
                if (a->next != nil)
                        l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1))));  // n = n + 1
        }

        typechecklist(l, Etop);
        walkstmtlist(l);
        *init = concat(*init, l);
        return ns;
}

// Lower copy(a, b) to a memmove call or a runtime call.
//
// init {
//   n := len(a)
//   if n > len(b) { n = len(b) }
//   memmove(a.ptr, b.ptr, n*sizeof(elem(a)))
// }
// n;
//
// Also works if b is a string.
//
static Node*
copyany(Node *n, NodeList **init, int runtimecall)
{
        Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn;
        NodeList *l;

        if(runtimecall) {
                if(n->right->type->etype == TSTRING)
                        fn = syslook("slicestringcopy", 1);
                else
                        fn = syslook("copy", 1);
                argtype(fn, n->left->type);
                argtype(fn, n->right->type);
                return mkcall1(fn, n->type, init,
                                n->left, n->right,
                                nodintconst(n->left->type->type->width));
        }
        walkexpr(&n->left, init);
        walkexpr(&n->right, init);
        nl = temp(n->left->type);
        nr = temp(n->right->type);
        l = nil;
        l = list(l, nod(OAS, nl, n->left));
        l = list(l, nod(OAS, nr, n->right));

        nfrm = nod(OSPTR, nr, N);
        nto = nod(OSPTR, nl, N);

        nlen = temp(types[TINT]);
        // n = len(to)
        l = list(l, nod(OAS, nlen, nod(OLEN, nl, N)));
        // if n > len(frm) { n = len(frm) }
        nif = nod(OIF, N, N);
        nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N));
        nif->nbody = list(nif->nbody,
                nod(OAS, nlen, nod(OLEN, nr, N)));
        l = list(l, nif);

        // Call memmove.
        fn = syslook("memmove", 1);
        argtype(fn, nl->type->type);
        argtype(fn, nl->type->type);
        nwid = temp(types[TUINTPTR]);
        l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR])));
        nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width));
        l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid));

        typechecklist(l, Etop);
        walkstmtlist(l);
        *init = concat(*init, l);
        return nlen;
}

// Generate frontend part for OSLICE[3][ARR|STR]
// 
static  Node*
sliceany(Node* n, NodeList **init)
{
        int bounded, slice3;
        Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2;
        int64 lbv, hbv, cbv, bv, w;
        Type *bt;

//      print("before sliceany: %+N\n", n);

        src = n->left;
        lb = n->right->left;
        slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR;
        if(slice3) {
                hb = n->right->right->left;
                cb = n->right->right->right;
        } else {
                hb = n->right->right;
                cb = N;
        }

        bounded = n->etype;
        
        if(n->op == OSLICESTR)
                bound = nod(OLEN, src, N);
        else
                bound = nod(OCAP, src, N);

        typecheck(&bound, Erv);
        walkexpr(&bound, init);  // if src is an array, bound will be a const now.

        // static checks if possible
        bv = 1LL<<50;
        if(isconst(bound, CTINT)) {
                if(!smallintconst(bound))
                        yyerror("array len too large");
                else
                        bv = mpgetfix(bound->val.u.xval);
        }

        if(isconst(cb, CTINT)) {
                cbv = mpgetfix(cb->val.u.xval);
                if(cbv < 0 || cbv > bv)
                        yyerror("slice index out of bounds");
        }
        if(isconst(hb, CTINT)) {
                hbv = mpgetfix(hb->val.u.xval);
                if(hbv < 0 || hbv > bv)
                        yyerror("slice index out of bounds");
        }
        if(isconst(lb, CTINT)) {
                lbv = mpgetfix(lb->val.u.xval);
                if(lbv < 0 || lbv > bv) {
                        yyerror("slice index out of bounds");
                        lbv = -1;
                }
                if(lbv == 0)
                        lb = N;
        }

        // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison
        // generate
        //     if hb > bound || lb > hb { panicslice() }
        chk = N;
        chk0 = N;
        chk1 = N;
        chk2 = N;

        bt = types[simtype[TUINT]];
        if(cb != N && cb->type->width > 4)
                bt = types[TUINT64];
        if(hb != N && hb->type->width > 4)
                bt = types[TUINT64];
        if(lb != N && lb->type->width > 4)
                bt = types[TUINT64];

        bound = cheapexpr(conv(bound, bt), init);

        if(cb != N) {
                cb = cheapexpr(conv(cb, bt), init);
                if(!bounded)
                        chk0 = nod(OLT, bound, cb);
        } else if(slice3) {
                // When we figure out what this means, implement it.
                fatal("slice3 with cb == N"); // rejected by parser
        }
                
        if(hb != N) {
                hb = cheapexpr(conv(hb, bt), init);
                if(!bounded) {
                        if(cb != N)
                                chk1 = nod(OLT, cb, hb);
                        else
                                chk1 = nod(OLT, bound, hb);
                }
        } else if(slice3) {
                // When we figure out what this means, implement it.
                fatal("slice3 with hb == N"); // rejected by parser
        } else if(n->op == OSLICEARR) {
                hb = bound;
        } else {
                hb = nod(OLEN, src, N);
                typecheck(&hb, Erv);
                walkexpr(&hb, init);
                hb = cheapexpr(conv(hb, bt), init);
        }

        if(lb != N) {
                lb = cheapexpr(conv(lb, bt), init);
                if(!bounded)
                        chk2 = nod(OLT, hb, lb);  
        }

        if(chk0 != N || chk1 != N || chk2 != N) {
                chk = nod(OIF, N, N);
                chk->nbody = list1(mkcall("panicslice", T, init));
                chk->likely = -1;
                if(chk0 != N)
                        chk->ntest = chk0;
                if(chk1 != N) {
                        if(chk->ntest == N)
                                chk->ntest = chk1;
                        else
                                chk->ntest = nod(OOROR, chk->ntest, chk1);
                }
                if(chk2 != N) {
                        if(chk->ntest == N)
                                chk->ntest = chk2;
                        else
                                chk->ntest = nod(OOROR, chk->ntest, chk2);
                }
                typecheck(&chk, Etop);
                walkstmt(&chk);
                *init = concat(*init, chk->ninit);
                chk->ninit = nil;
                *init = list(*init, chk);
        }
        
        // prepare new cap, len and offs for backend cgen_slice
        // cap = bound [ - lo ]
        n->right = N;
        n->list = nil;
        if(!slice3)
                cb = bound;
        if(lb == N)
                bound = conv(cb, types[simtype[TUINT]]);
        else
                bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
        typecheck(&bound, Erv);
        walkexpr(&bound, init);
        n->list = list(n->list, bound);

        // len = hi [ - lo]
        if(lb == N)
                hb = conv(hb, types[simtype[TUINT]]);
        else
                hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
        typecheck(&hb, Erv);
        walkexpr(&hb, init);
        n->list = list(n->list, hb);

        // offs = [width *] lo, but omit if zero
        if(lb != N) {
                if(n->op == OSLICESTR)
                        w = 1;
                else
                        w = n->type->type->width;
                lb = conv(lb, types[TUINTPTR]);
                if(w > 1)
                        lb = nod(OMUL, nodintconst(w), lb);
                typecheck(&lb, Erv);
                walkexpr(&lb, init);
                n->list = list(n->list, lb);
        }

//      print("after sliceany: %+N\n", n);

        return n;
}

static Node*
eqfor(Type *t)
{
        int a;
        Node *n;
        Node *ntype;
        Sym *sym;

        // Should only arrive here with large memory or
        // a struct/array containing a non-memory field/element.
        // Small memory is handled inline, and single non-memory
        // is handled during type check (OCMPSTR etc).
        a = algtype1(t, nil);
        if(a != AMEM && a != -1)
                fatal("eqfor %T", t);

        if(a == AMEM) {
                n = syslook("memequal", 1);
                argtype(n, t);
                argtype(n, t);
                return n;
        }

        sym = typesymprefix(".eq", t);
        n = newname(sym);
        n->class = PFUNC;
        ntype = nod(OTFUNC, N, N);
        ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL]))));
        ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
        ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
        ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
        typecheck(&ntype, Etype);
        n->type = ntype->type;
        return n;
}

static int
countfield(Type *t)
{
        Type *t1;
        int n;
        
        n = 0;
        for(t1=t->type; t1!=T; t1=t1->down)
                n++;
        return n;
}

static void
walkcompare(Node **np, NodeList **init)
{
        Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr;
        int andor, i;
        Type *t, *t1;
        static Node *tempbool;
        
        n = *np;
        
        // Must be comparison of array or struct.
        // Otherwise back end handles it.
        t = n->left->type;
        switch(t->etype) {
        default:
                return;
        case TARRAY:
                if(isslice(t))
                        return;
                break;
        case TSTRUCT:
                break;
        }
        
        if(!islvalue(n->left) || !islvalue(n->right))
                goto hard;

        l = temp(ptrto(t));
        a = nod(OAS, l, nod(OADDR, n->left, N));
        a->right->etype = 1;  // addr does not escape
        typecheck(&a, Etop);
        *init = list(*init, a);

        r = temp(ptrto(t));
        a = nod(OAS, r, nod(OADDR, n->right, N));
        a->right->etype = 1;  // addr does not escape
        typecheck(&a, Etop);
        *init = list(*init, a);

        expr = N;
        andor = OANDAND;
        if(n->op == ONE)
                andor = OOROR;

        if(t->etype == TARRAY &&
                t->bound <= 4 &&
                issimple[t->type->etype]) {
                // Four or fewer elements of a basic type.
                // Unroll comparisons.
                for(i=0; i<t->bound; i++) {
                        li = nod(OINDEX, l, nodintconst(i));
                        ri = nod(OINDEX, r, nodintconst(i));
                        a = nod(n->op, li, ri);
                        if(expr == N)
                                expr = a;
                        else
                                expr = nod(andor, expr, a);
                }
                if(expr == N)
                        expr = nodbool(n->op == OEQ);
                r = expr;
                goto ret;
        }

        if(t->etype == TSTRUCT && countfield(t) <= 4) {
                // Struct of four or fewer fields.
                // Inline comparisons.
                for(t1=t->type; t1; t1=t1->down) {
                        if(isblanksym(t1->sym))
                                continue;
                        li = nod(OXDOT, l, newname(t1->sym));
                        ri = nod(OXDOT, r, newname(t1->sym));
                        a = nod(n->op, li, ri);
                        if(expr == N)
                                expr = a;
                        else
                                expr = nod(andor, expr, a);
                }
                if(expr == N)
                        expr = nodbool(n->op == OEQ);
                r = expr;
                goto ret;
        }

        // Chose not to inline, but still have addresses.
        // Call equality function directly.
        // The equality function requires a bool pointer for
        // storing its address, because it has to be callable
        // from C, and C can't access an ordinary Go return value.
        // To avoid creating many temporaries, cache one per function.
        if(tempbool == N || tempbool->curfn != curfn)
                tempbool = temp(types[TBOOL]);
        
        call = nod(OCALL, eqfor(t), N);
        a = nod(OADDR, tempbool, N);
        a->etype = 1;  // does not escape
        call->list = list(call->list, a);
        call->list = list(call->list, nodintconst(t->width));
        call->list = list(call->list, l);
        call->list = list(call->list, r);
        typecheck(&call, Etop);
        walkstmt(&call);
        *init = list(*init, call);

        // tempbool cannot be used directly as multiple comparison
        // expressions may exist in the same statement. Create another
        // temporary to hold the value (its address is not taken so it can
        // be optimized away).
        r = temp(types[TBOOL]);
        a = nod(OAS, r, tempbool);
        typecheck(&a, Etop);
        walkstmt(&a);
        *init = list(*init, a);

        if(n->op != OEQ)
                r = nod(ONOT, r, N);
        goto ret;

hard:
        // Cannot take address of one or both of the operands.
        // Instead, pass directly to runtime helper function.
        // Easier on the stack than passing the address
        // of temporary variables, because we are better at reusing
        // the argument space than temporary variable space.
        fn = syslook("equal", 1);
        l = n->left;
        r = n->right;
        argtype(fn, n->left->type);
        argtype(fn, n->left->type);
        r = mkcall1(fn, n->type, init, typename(n->left->type), l, r);
        if(n->op == ONE) {
                r = nod(ONOT, r, N);
        }
        goto ret;

ret:
        typecheck(&r, Erv);
        walkexpr(&r, init);
        if(r->type != n->type) {
                r = nod(OCONVNOP, r, N);
                r->type = n->type;
                r->typecheck = 1;
        }
        *np = r;
        return;
}

static int
samecheap(Node *a, Node *b)
{
        Node *ar, *br;
        while(a != N && b != N && a->op == b->op) {
                switch(a->op) {
                default:
                        return 0;
                case ONAME:
                        return a == b;
                case ODOT:
                case ODOTPTR:
                        ar = a->right;
                        br = b->right;
                        if(ar->op != ONAME || br->op != ONAME || ar->sym != br->sym)
                                return 0;
                        break;
                case OINDEX:
                        ar = a->right;
                        br = b->right;
                        if(!isconst(ar, CTINT) || !isconst(br, CTINT) || mpcmpfixfix(ar->val.u.xval, br->val.u.xval) != 0)
                                return 0;
                        break;
                }
                a = a->left;
                b = b->left;
        }
        return 0;
}

static void
walkrotate(Node **np)
{
        int w, sl, sr, s;
        Node *l, *r;
        Node *n;
        
        n = *np;

        // Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value.
        l = n->left;
        r = n->right;
        if((n->op != OOR && n->op != OXOR) ||
           (l->op != OLSH && l->op != ORSH) ||
           (r->op != OLSH && r->op != ORSH) ||
           n->type == T || issigned[n->type->etype] ||
           l->op == r->op) {
                return;
        }

        // Want same, side effect-free expression on lhs of both shifts.
        if(!samecheap(l->left, r->left))
                return;
        
        // Constants adding to width?
        w = l->type->width * 8;
        if(smallintconst(l->right) && smallintconst(r->right)) {
                if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w)
                        goto yes;
                return;
        }
        
        // TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31).
        return;
        
yes:
        // Rewrite left shift half to left rotate.
        if(l->op == OLSH)
                n = l;
        else
                n = r;
        n->op = OLROT;
        
        // Remove rotate 0 and rotate w.
        s = mpgetfix(n->right->val.u.xval);
        if(s == 0 || s == w)
                n = n->left;

        *np = n;
        return;
}

/*
 * walkmul rewrites integer multiplication by powers of two as shifts.
 */
static void
walkmul(Node **np, NodeList **init)
{
        Node *n, *nl, *nr;
        int pow, neg, w;
        
        n = *np;
        if(!isint[n->type->etype])
                return;

        if(n->right->op == OLITERAL) {
                nl = n->left;
                nr = n->right;
        } else if(n->left->op == OLITERAL) {
                nl = n->right;
                nr = n->left;
        } else
                return;

        neg = 0;

        // x*0 is 0 (and side effects of x).
        if(mpgetfix(nr->val.u.xval) == 0) {
                cheapexpr(nl, init);
                nodconst(n, n->type, 0);
                goto ret;
        }

        // nr is a constant.
        pow = powtwo(nr);
        if(pow < 0)
                return;
        if(pow >= 1000) {
                // negative power of 2, like -16
                neg = 1;
                pow -= 1000;
        }

        w = nl->type->width*8;
        if(pow+1 >= w)// too big, shouldn't happen
                return;

        nl = cheapexpr(nl, init);

        if(pow == 0) {
                // x*1 is x
                n = nl;
                goto ret;
        }
        
        n = nod(OLSH, nl, nodintconst(pow));

ret:
        if(neg)
                n = nod(OMINUS, n, N);

        typecheck(&n, Erv);
        walkexpr(&n, init);
        *np = n;
}

/*
 * walkdiv rewrites division by a constant as less expensive
 * operations.
 */
static void
walkdiv(Node **np, NodeList **init)
{
        Node *n, *nl, *nr, *nc;
        Node *n1, *n2, *n3, *n4;
        int pow; // if >= 0, nr is 1<<pow
        int s; // 1 if nr is negative.
        int w;
        Type *twide;
        Magic m;

        n = *np;
        if(n->right->op != OLITERAL)
                return;
        // nr is a constant.
        nl = cheapexpr(n->left, init);
        nr = n->right;

        // special cases of mod/div
        // by a constant
        w = nl->type->width*8;
        s = 0;
        pow = powtwo(nr);
        if(pow >= 1000) {
                // negative power of 2
                s = 1;
                pow -= 1000;
        }

        if(pow+1 >= w) {
                // divisor too large.
                return;
        }
        if(pow < 0) {
                goto divbymul;
        }

        switch(pow) {
        case 0:
                if(n->op == OMOD) {
                        // nl % 1 is zero.
                        nodconst(n, n->type, 0);
                } else if(s) {
                        // divide by -1
                        n->op = OMINUS;
                        n->right = N;
                } else {
                        // divide by 1
                        n = nl;
                }
                break;
        default:
                if(issigned[n->type->etype]) {
                        if(n->op == OMOD) {
                                // signed modulo 2^pow is like ANDing
                                // with the last pow bits, but if nl < 0,
                                // nl & (2^pow-1) is (nl+1)%2^pow - 1.
                                nc = nod(OXXX, N, N);
                                nodconst(nc, types[simtype[TUINT]], w-1);
                                n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
                                if(pow == 1) {
                                        typecheck(&n1, Erv);
                                        n1 = cheapexpr(n1, init);
                                        // n = (nl+ε)&1 -ε where ε=1 iff nl<0.
                                        n2 = nod(OSUB, nl, n1);
                                        nc = nod(OXXX, N, N);
                                        nodconst(nc, nl->type, 1);
                                        n3 = nod(OAND, n2, nc);
                                        n = nod(OADD, n3, n1);
                                } else {
                                        // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
                                        nc = nod(OXXX, N, N);
                                        nodconst(nc, nl->type, (1LL<<pow)-1);
                                        n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0.
                                        typecheck(&n2, Erv);
                                        n2 = cheapexpr(n2, init);

                                        n3 = nod(OADD, nl, n2);
                                        n4 = nod(OAND, n3, nc);
                                        n = nod(OSUB, n4, n2);
                                }
                                break;
                        } else {
                                // arithmetic right shift does not give the correct rounding.
                                // if nl >= 0, nl >> n == nl / nr
                                // if nl < 0, we want to add 2^n-1 first.
                                nc = nod(OXXX, N, N);
                                nodconst(nc, types[simtype[TUINT]], w-1);
                                n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
                                if(pow == 1) {
                                        // nl+1 is nl-(-1)
                                        n->left = nod(OSUB, nl, n1);
                                } else {
                                        // Do a logical right right on -1 to keep pow bits.
                                        nc = nod(OXXX, N, N);
                                        nodconst(nc, types[simtype[TUINT]], w-pow);
                                        n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc);
                                        n->left = nod(OADD, nl, conv(n2, nl->type));
                                }
                                // n = (nl + 2^pow-1) >> pow
                                n->op = ORSH;
                                nc = nod(OXXX, N, N);
                                nodconst(nc, types[simtype[TUINT]], pow);
                                n->right = nc;
                                n->typecheck = 0;
                        }
                        if(s)
                                n = nod(OMINUS, n, N);
                        break;
                }
                nc = nod(OXXX, N, N);
                if(n->op == OMOD) {
                        // n = nl & (nr-1)
                        n->op = OAND;
                        nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1);
                } else {
                        // n = nl >> pow
                        n->op = ORSH;
                        nodconst(nc, types[simtype[TUINT]], pow);
                }
                n->typecheck = 0;
                n->right = nc;
                break;
        }
        goto ret;

divbymul:
        // try to do division by multiply by (2^w)/d
        // see hacker's delight chapter 10
        // TODO: support 64-bit magic multiply here.
        m.w = w;
        if(issigned[nl->type->etype]) {
                m.sd = mpgetfix(nr->val.u.xval);
                smagic(&m);
        } else {
                m.ud = mpgetfix(nr->val.u.xval);
                umagic(&m);
        }
        if(m.bad)
                return;

        // We have a quick division method so use it
        // for modulo too.
        if(n->op == OMOD)
                goto longmod;

        switch(simtype[nl->type->etype]) {
        default:
                return;

        case TUINT8:
        case TUINT16:
        case TUINT32:
                // n1 = nl * magic >> w (HMUL)
                nc = nod(OXXX, N, N);
                nodconst(nc, nl->type, m.um);
                n1 = nod(OMUL, nl, nc);
                typecheck(&n1, Erv);
                n1->op = OHMUL;
                if(m.ua) {
                        // Select a Go type with (at least) twice the width.
                        switch(simtype[nl->type->etype]) {
                        default:
                                return;
                        case TUINT8:
                        case TUINT16:
                                twide = types[TUINT32];
                                break;
                        case TUINT32:
                                twide = types[TUINT64];
                                break;
                        case TINT8:
                        case TINT16:
                                twide = types[TINT32];
                                break;
                        case TINT32:
                                twide = types[TINT64];
                                break;
                        }

                        // add numerator (might overflow).
                        // n2 = (n1 + nl)
                        n2 = nod(OADD, conv(n1, twide), conv(nl, twide));

                        // shift by m.s
                        nc = nod(OXXX, N, N);
                        nodconst(nc, types[TUINT], m.s);
                        n = conv(nod(ORSH, n2, nc), nl->type);
                } else {
                        // n = n1 >> m.s
                        nc = nod(OXXX, N, N);
                        nodconst(nc, types[TUINT], m.s);
                        n = nod(ORSH, n1, nc);
                }
                break;

        case TINT8:
        case TINT16:
        case TINT32:
                // n1 = nl * magic >> w
                nc = nod(OXXX, N, N);
                nodconst(nc, nl->type, m.sm);
                n1 = nod(OMUL, nl, nc);
                typecheck(&n1, Erv);
                n1->op = OHMUL;
                if(m.sm < 0) {
                        // add the numerator.
                        n1 = nod(OADD, n1, nl);
                }
                // shift by m.s
                nc = nod(OXXX, N, N);
                nodconst(nc, types[TUINT], m.s);
                n2 = conv(nod(ORSH, n1, nc), nl->type);
                // add 1 iff n1 is negative.
                nc = nod(OXXX, N, N);
                nodconst(nc, types[TUINT], w-1);
                n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative.
                n = nod(OSUB, n2, n3);
                // apply sign.
                if(m.sd < 0)
                        n = nod(OMINUS, n, N);
                break;
        }
        goto ret;

longmod:
        // rewrite as A%B = A - (A/B*B).
        n1 = nod(ODIV, nl, nr);
        n2 = nod(OMUL, n1, nr);
        n = nod(OSUB, nl, n2);
        goto ret;

ret:
        typecheck(&n, Erv);
        walkexpr(&n, init);
        *np = n;
}

// return 1 if integer n must be in range [0, max), 0 otherwise
static int
bounded(Node *n, int64 max)
{
        int64 v;
        int32 bits;
        int sign;

        if(n->type == T || !isint[n->type->etype])
                return 0;

        sign = issigned[n->type->etype];
        bits = 8*n->type->width;

        if(smallintconst(n)) {
                v = mpgetfix(n->val.u.xval);
                return 0 <= v && v < max;
        }

        switch(n->op) {
        case OAND:
                v = -1;
                if(smallintconst(n->left)) {
                        v = mpgetfix(n->left->val.u.xval);
                } else if(smallintconst(n->right)) {
                        v = mpgetfix(n->right->val.u.xval);
                }
                if(0 <= v && v < max)
                        return 1;
                break;

        case OMOD:
                if(!sign && smallintconst(n->right)) {
                        v = mpgetfix(n->right->val.u.xval);
                        if(0 <= v && v <= max)
                                return 1;
                }
                break;
        
        case ODIV:
                if(!sign && smallintconst(n->right)) {
                        v = mpgetfix(n->right->val.u.xval);
                        while(bits > 0 && v >= 2) {
                                bits--;
                                v >>= 1;
                        }
                }
                break;
        
        case ORSH:
                if(!sign && smallintconst(n->right)) {
                        v = mpgetfix(n->right->val.u.xval);
                        if(v > bits)
                                return 1;
                        bits -= v;
                }
                break;
        }
        
        if(!sign && bits <= 62 && (1LL<<bits) <= max)
                return 1;
        
        return 0;
}

void
usefield(Node *n)
{
        Type *field, *l;

        if(!fieldtrack_enabled)
                return;

        switch(n->op) {
        default:
                fatal("usefield %O", n->op);
        case ODOT:
        case ODOTPTR:
                break;
        }
        
        field = n->paramfld;
        if(field == T)
                fatal("usefield %T %S without paramfld", n->left->type, n->right->sym);
        if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil)
                return;

        // dedup on list
        if(field->lastfn == curfn)
                return;
        field->lastfn = curfn;
        field->outer = n->left->type;
        if(isptr[field->outer->etype])
                field->outer = field->outer->type;
        if(field->outer->sym == S)
                yyerror("tracked field must be in named struct type");
        if(!exportname(field->sym->name))
                yyerror("tracked field must be exported (upper case)");

        l = typ(0);
        l->type = field;
        l->down = curfn->paramfld;
        curfn->paramfld = l;
}

static int
candiscardlist(NodeList *l)
{
        for(; l; l=l->next)
                if(!candiscard(l->n))
                        return 0;
        return 1;
}

int
candiscard(Node *n)
{
        if(n == N)
                return 1;
        
        switch(n->op) {
        default:
                return 0;

        case ONAME:
        case ONONAME:
        case OTYPE:
        case OPACK:
        case OLITERAL:
        case OADD:
        case OSUB:
        case OOR:
        case OXOR:
        case OADDSTR:
        case OADDR:
        case OANDAND:
        case OARRAYBYTESTR:
        case OARRAYRUNESTR:
        case OSTRARRAYBYTE:
        case OSTRARRAYRUNE:
        case OCAP:
        case OCMPIFACE:
        case OCMPSTR:
        case OCOMPLIT:
        case OMAPLIT:
        case OSTRUCTLIT:
        case OARRAYLIT:
        case OPTRLIT:
        case OCONV:
        case OCONVIFACE:
        case OCONVNOP:
        case ODOT:
        case OEQ:
        case ONE:
        case OLT:
        case OLE:
        case OGT:
        case OGE:
        case OKEY:
        case OLEN:
        case OMUL:
        case OLSH:
        case ORSH:
        case OAND:
        case OANDNOT:
        case ONEW:
        case ONOT:
        case OCOM:
        case OPLUS:
        case OMINUS:
        case OOROR:
        case OPAREN:
        case ORUNESTR:
        case OREAL:
        case OIMAG:
        case OCOMPLEX:
                // Discardable as long as the subpieces are.
                break;

        case ODIV:
        case OMOD:
                // Discardable as long as we know it's not division by zero.
                if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0)
                        break;
                if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0)
                        break;
                return 0;

        case OMAKECHAN:
        case OMAKEMAP:
                // Discardable as long as we know it won't fail because of a bad size.
                if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0)
                        break;
                return 0;
        
        case OMAKESLICE:
                // Difficult to tell what sizes are okay.
                return 0;               
        }
        
        if(!candiscard(n->left) ||
           !candiscard(n->right) ||
           !candiscard(n->ntest) ||
           !candiscard(n->nincr) ||
           !candiscardlist(n->ninit) ||
           !candiscardlist(n->nbody) ||
           !candiscardlist(n->nelse) ||
           !candiscardlist(n->list) ||
           !candiscardlist(n->rlist)) {
                return 0;
        }
        
        return 1;
}

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