#include "gc.h"

/* ,x/^(print|prtree)\(/i/\/\/ */

void
cgen(Node *n, Node *nn)
{
       Node *l, *r, *t;
       Prog *p1;
       Node nod, nod1, nod2, nod3, nod4;
       int o, hardleft;
       long v, curs;
       vlong c;

       if(debug['g']) {
               prtree(nn, "cgen lhs");
               prtree(n, "cgen");
       }
       if(n == Z || n->type == T)
               return;
       if(typesuv[n->type->etype]) {
               sugen(n, nn, n->type->width);
               return;
       }
       l = n->left;
       r = n->right;
       o = n->op;
// Go's version does the following, but it's the wrong place: doesn't allow assignment
//      if(o == OEXREG || nn != Z && nn->op == OEXREG) {
//              gmove(n, nn);
//              return;
//      }

       if(n->addable >= INDEXED) {
               if(nn == Z) {
                       switch(o) {
                       default:
                               nullwarn(Z, Z);
                               break;
                       case OINDEX:
                               nullwarn(l, r);
                               break;
                       }
                       return;
               }
               gmove(n, nn);
               return;
       }
       curs = cursafe;

       if(l->complex >= FNX)
       if(r != Z && r->complex >= FNX)
       switch(o) {
       default:
               if(cond(o) && typesuv[l->type->etype])
                       break;

               regret(&nod, r);
               cgen(r, &nod);

               regsalloc(&nod1, r);
               gmove(&nod, &nod1);

               regfree(&nod);
               nod = *n;
               nod.right = &nod1;

               cgen(&nod, nn);
               return;

       case OFUNC:
       case OCOMMA:
       case OANDAND:
       case OOROR:
       case OCOND:
       case ODOT:
               break;
       }

       hardleft = l->addable < INDEXED || l->complex >= FNX;
       switch(o) {
       default:
               diag(n, "unknown op in cgen: %O", o);
               break;

       case ONEG:
       case OCOM:
               if(nn == Z) {
                       nullwarn(l, Z);
                       break;
               }
               regalloc(&nod, l, nn);
               cgen(l, &nod);
               gopcode(o, n->type, Z, &nod);
               gmove(&nod, nn);
               regfree(&nod);
               break;

       case OAS:
               if(typefd[n->type->etype]) {
                       cgen(r, &fregnode0);
                       if(nn != Z)
                               gins(AFMOVD, &fregnode0, &fregnode0);
                       if(l->addable < INDEXED) {
                               reglcgen(&nod, l, Z);
                               gmove(&fregnode0, &nod);
                               regfree(&nod);
                       } else
                               gmove(&fregnode0, l);
                       if(nn != Z)
                               gmove(&fregnode0, nn);
                       return;
               }
               if(l->op == OBIT)
                       goto bitas;
               if(!hardleft) {
                       if(nn != Z || r->addable < INDEXED) {
                               if(r->complex >= FNX && nn == Z)
                                       regret(&nod, r);
                               else
                                       regalloc(&nod, r, nn);
                               cgen(r, &nod);
                               gmove(&nod, l);
                               if(nn != Z)
                                       gmove(&nod, nn);
                               regfree(&nod);
                       } else
                               gmove(r, l);
                       break;
               }
               if(l->complex >= r->complex) {
                       if(l->op == OINDEX && r->op == OCONST) {
                               gmove(r, l);
                               break;
                       }
                       reglcgen(&nod1, l, Z);
                       if(r->addable >= INDEXED) {
                               gmove(r, &nod1);
                               if(nn != Z)
                                       gmove(r, nn);
                               regfree(&nod1);
                               break;
                       }
                       regalloc(&nod, r, nn);
                       cgen(r, &nod);
               } else {
                       regalloc(&nod, r, nn);
                       cgen(r, &nod);
                       reglcgen(&nod1, l, Z);
               }
               gmove(&nod, &nod1);
               regfree(&nod);
               regfree(&nod1);
               break;

       bitas:
               n = l->left;
               regalloc(&nod, r, nn);
               if(l->complex >= r->complex) {
                       reglcgen(&nod1, n, Z);
                       cgen(r, &nod);
               } else {
                       cgen(r, &nod);
                       reglcgen(&nod1, n, Z);
               }
               regalloc(&nod2, n, Z);
               gmove(&nod1, &nod2);
               bitstore(l, &nod, &nod1, &nod2, nn);
               break;

       case OBIT:
               if(nn == Z) {
                       nullwarn(l, Z);
                       break;
               }
               bitload(n, &nod, Z, Z, nn);
               gmove(&nod, nn);
               regfree(&nod);
               break;

       case OROL:
       case OLSHR:
       case OASHL:
       case OASHR:
               if(nn == Z) {
                       nullwarn(l, r);
                       break;
               }
               if(r->op == OCONST) {
                       if(r->vconst == 0) {
                               cgen(l, nn);
                               break;
                       }
                       regalloc(&nod, l, nn);
                       cgen(l, &nod);
                       if(o == OASHL && r->vconst == 1)
                               gopcode(OADD, n->type, &nod, &nod);
                       else
                               gopcode(o, n->type, r, &nod);
                       gmove(&nod, nn);
                       regfree(&nod);
                       break;
               }

               /*
                * get nod to be D_CX
                */
               if(nodreg(&nod, nn, D_CX)) {
                       regsalloc(&nod1, n);
                       gmove(&nod, &nod1);
                       cgen(n, &nod);          /* probably a bug */
                       gmove(&nod, nn);
                       gmove(&nod1, &nod);
                       break;
               }
               reg[D_CX]++;
               if(nn->op == OREGISTER && nn->reg == D_CX)
                       regalloc(&nod1, l, Z);
               else
                       regalloc(&nod1, l, nn);
               if(r->complex >= l->complex) {
                       cgen(r, &nod);
                       cgen(l, &nod1);
               } else {
                       cgen(l, &nod1);
                       cgen(r, &nod);
               }
               gopcode(o, n->type, &nod, &nod1);
               gmove(&nod1, nn);
               regfree(&nod);
               regfree(&nod1);
               break;

       case OADD:
       case OSUB:
       case OOR:
       case OXOR:
       case OAND:
               if(nn == Z) {
                       nullwarn(l, r);
                       break;
               }
               if(typefd[n->type->etype])
                       goto fop;
               if(r->op == OCONST) {
                       if(r->vconst == 0 && o != OAND) {
                               cgen(l, nn);
                               break;
                       }
               }
               if(n->op == OADD && l->op == OASHL && l->right->op == OCONST
               && (r->op != OCONST || r->vconst < -128 || r->vconst > 127)) {
                       c = l->right->vconst;
                       if(c > 0 && c <= 3 && nareg(1) >= 4) {
                               if(l->left->complex >= r->complex) {
                                       regalloc(&nod, l->left, nn);
                                       cgen(l->left, &nod);
                                       if(r->addable < INDEXED) {
                                               regalloc(&nod1, r, Z);
                                               cgen(r, &nod1);
                                               genmuladd(&nod, &nod, 1 << c, &nod1);
                                               regfree(&nod1);
                                       }
                                       else
                                               genmuladd(&nod, &nod, 1 << c, r);
                               }
                               else {
                                       regalloc(&nod, r, nn);
                                       cgen(r, &nod);
                                       regalloc(&nod1, l->left, Z);
                                       cgen(l->left, &nod1);
                                       genmuladd(&nod, &nod1, 1 << c, &nod);
                                       regfree(&nod1);
                               }
                               gmove(&nod, nn);
                               regfree(&nod);
                               break;
                       }
               }
               if(r->addable >= INDEXED) {
                       regalloc(&nod, l, nn);
                       cgen(l, &nod);
                       gopcode(o, n->type, r, &nod);
                       gmove(&nod, nn);
                       regfree(&nod);
                       break;
               }
               if(l->complex >= r->complex) {
                       regalloc(&nod, l, nn);
                       cgen(l, &nod);
                       regalloc(&nod1, r, Z);
                       cgen(r, &nod1);
                       gopcode(o, n->type, &nod1, &nod);
               } else {
                       regalloc(&nod1, r, nn);
                       cgen(r, &nod1);
                       regalloc(&nod, l, Z);
                       cgen(l, &nod);
                       gopcode(o, n->type, &nod1, &nod);
               }
               gmove(&nod, nn);
               regfree(&nod);
               regfree(&nod1);
               break;

       case OLMOD:
       case OMOD:
       case OLMUL:
       case OLDIV:
       case OMUL:
       case ODIV:
               if(nn == Z) {
                       nullwarn(l, r);
                       break;
               }
               if(typefd[n->type->etype])
                       goto fop;
               if(r->op == OCONST) {
                       SET(v);
                       switch(o) {
                       case ODIV:
                       case OMOD:
                               c = r->vconst;
                               if(c < 0)
                                       c = -c;
                               v = log2(c);
                               if(v < 0)
                                       break;
                               /* fall thru */
                       case OMUL:
                       case OLMUL:
                               regalloc(&nod, l, nn);
                               cgen(l, &nod);
                               switch(o) {
                               case OMUL:
                               case OLMUL:
                                       mulgen(n->type, r, &nod);
                                       break;
                               case ODIV:
                                       sdiv2(r->vconst, v, l, &nod);
                                       break;
                               case OMOD:
                                       smod2(r->vconst, v, l, &nod);
                                       break;
                               }
                               gmove(&nod, nn);
                               regfree(&nod);
                               goto done;
                       case OLDIV:
                               c = r->vconst;
                               if((c & 0x80000000) == 0)
                                       break;
                               regalloc(&nod1, l, Z);
                               cgen(l, &nod1);
                               regalloc(&nod, l, nn);
                               zeroregm(&nod);
                               gins(ACMPL, &nod1, nodconst(c));
                               gins(ASBBL, nodconst(-1), &nod);
                               regfree(&nod1);
                               gmove(&nod, nn);
                               regfree(&nod);
                               goto done;
                       }
               }

               if(o == OMUL) {
                       if(l->addable >= INDEXED) {
                               t = l;
                               l = r;
                               r = t;
                               goto imula;
                       }
                       else if(r->addable >= INDEXED) {
                       imula:
/* should favour AX */
                               regalloc(&nod, l, nn);
                               cgen(l, &nod);
                               gopcode(OMUL, n->type, r, &nod);
                       }
                       else {
/* should favour AX */
                               regalloc(&nod, l, nn);
                               cgen(l, &nod);
                               regalloc(&nod1, r, Z);
                               cgen(r, &nod1);
                               gopcode(OMUL, n->type, &nod1, &nod);
                               regfree(&nod1);
                       }
                       gmove(&nod, nn);
                       regfree(&nod);
                       goto done;
               }

               /*
                * get nod to be D_AX
                * get nod1 to be D_DX
                */
               if(nodreg(&nod, nn, D_AX)) {
                       regsalloc(&nod2, n);
                       gmove(&nod, &nod2);
                       v = reg[D_AX];
                       reg[D_AX] = 0;

                       if(isreg(l, D_AX)) {
                               nod3 = *n;
                               nod3.left = &nod2;
                               cgen(&nod3, nn);
                       } else
                       if(isreg(r, D_AX)) {
                               nod3 = *n;
                               nod3.right = &nod2;
                               cgen(&nod3, nn);
                       } else
                               cgen(n, nn);

                       gmove(&nod2, &nod);
                       reg[D_AX] = v;
                       break;
               }
               if(nodreg(&nod1, nn, D_DX)) {
                       regsalloc(&nod2, n);
                       gmove(&nod1, &nod2);
                       v = reg[D_DX];
                       reg[D_DX] = 0;

                       if(isreg(l, D_DX)) {
                               nod3 = *n;
                               nod3.left = &nod2;
                               cgen(&nod3, nn);
                       } else
                       if(isreg(r, D_DX)) {
                               nod3 = *n;
                               nod3.right = &nod2;
                               cgen(&nod3, nn);
                       } else
                               cgen(n, nn);

                       gmove(&nod2, &nod1);
                       reg[D_DX] = v;
                       break;
               }
               reg[D_AX]++;

               if(r->op == OCONST) {
                       switch(o) {
                       case ODIV:
                               reg[D_DX]++;
                               if(l->addable < INDEXED) {
                                       regalloc(&nod2, l, Z);
                                       cgen(l, &nod2);
                                       l = &nod2;
                               }
                               sdivgen(l, r, &nod, &nod1);
                               gmove(&nod1, nn);
                               if(l == &nod2)
                                       regfree(l);
                               goto freeaxdx;
                       case OLDIV:
                               reg[D_DX]++;
                               if(l->addable < INDEXED) {
                                       regalloc(&nod2, l, Z);
                                       cgen(l, &nod2);
                                       l = &nod2;
                               }
                               udivgen(l, r, &nod, &nod1);
                               gmove(&nod1, nn);
                               if(l == &nod2)
                                       regfree(l);
                               goto freeaxdx;
                       }
               }

               if(l->complex >= r->complex) {
                       cgen(l, &nod);
                       reg[D_DX]++;
                       if(o == ODIV || o == OMOD)
                               gins(ACDQ, Z, Z);
                       if(o == OLDIV || o == OLMOD)
                               zeroregm(&nod1);
                       if(r->addable < INDEXED || r->op == OCONST) {
                               regsalloc(&nod3, r);
                               cgen(r, &nod3);
                               gopcode(o, n->type, &nod3, Z);
                       } else
                               gopcode(o, n->type, r, Z);
               } else {
                       regsalloc(&nod3, r);
                       cgen(r, &nod3);
                       cgen(l, &nod);
                       reg[D_DX]++;
                       if(o == ODIV || o == OMOD)
                               gins(ACDQ, Z, Z);
                       if(o == OLDIV || o == OLMOD)
                               zeroregm(&nod1);
                       gopcode(o, n->type, &nod3, Z);
               }
               if(o == OMOD || o == OLMOD)
                       gmove(&nod1, nn);
               else
                       gmove(&nod, nn);
       freeaxdx:
               regfree(&nod);
               regfree(&nod1);
               break;

       case OASLSHR:
       case OASASHL:
       case OASASHR:
               if(r->op == OCONST)
                       goto asand;
               if(l->op == OBIT)
                       goto asbitop;
               if(typefd[n->type->etype])
                       goto asfop;

               /*
                * get nod to be D_CX
                */
               if(nodreg(&nod, nn, D_CX)) {
                       regsalloc(&nod1, n);
                       gmove(&nod, &nod1);
                       cgen(n, &nod);
                       if(nn != Z)
                               gmove(&nod, nn);
                       gmove(&nod1, &nod);
                       break;
               }
               reg[D_CX]++;

               if(r->complex >= l->complex) {
                       cgen(r, &nod);
                       if(hardleft)
                               reglcgen(&nod1, l, Z);
                       else
                               nod1 = *l;
               } else {
                       if(hardleft)
                               reglcgen(&nod1, l, Z);
                       else
                               nod1 = *l;
                       cgen(r, &nod);
               }

               gopcode(o, l->type, &nod, &nod1);
               regfree(&nod);
               if(nn != Z)
                       gmove(&nod1, nn);
               if(hardleft)
                       regfree(&nod1);
               break;

       case OASAND:
       case OASADD:
       case OASSUB:
       case OASXOR:
       case OASOR:
       asand:
               if(l->op == OBIT)
                       goto asbitop;
               if(typefd[n->type->etype]||typefd[r->type->etype])
                       goto asfop;
               if(l->complex >= r->complex) {
                       if(hardleft)
                               reglcgen(&nod, l, Z);
                       else
                               nod = *l;
                       if(r->op != OCONST) {
                               regalloc(&nod1, r, nn);
                               cgen(r, &nod1);
                               gopcode(o, l->type, &nod1, &nod);
                               regfree(&nod1);
                       } else
                               gopcode(o, l->type, r, &nod);
               } else {
                       regalloc(&nod1, r, nn);
                       cgen(r, &nod1);
                       if(hardleft)
                               reglcgen(&nod, l, Z);
                       else
                               nod = *l;
                       gopcode(o, l->type, &nod1, &nod);
                       regfree(&nod1);
               }
               if(nn != Z)
                       gmove(&nod, nn);
               if(hardleft)
                       regfree(&nod);
               break;

       case OASLMUL:
       case OASLDIV:
       case OASLMOD:
       case OASMUL:
       case OASDIV:
       case OASMOD:
               if(l->op == OBIT)
                       goto asbitop;
               if(typefd[n->type->etype]||typefd[r->type->etype])
                       goto asfop;
               if(r->op == OCONST) {
                       SET(v);
                       switch(o) {
                       case OASDIV:
                       case OASMOD:
                               c = r->vconst;
                               if(c < 0)
                                       c = -c;
                               v = log2(c);
                               if(v < 0)
                                       break;
                               /* fall thru */
                       case OASMUL:
                       case OASLMUL:
                               if(hardleft)
                                       reglcgen(&nod2, l, Z);
                               else
                                       nod2 = *l;
                               regalloc(&nod, l, nn);
                               cgen(&nod2, &nod);
                               switch(o) {
                               case OASMUL:
                               case OASLMUL:
                                       mulgen(n->type, r, &nod);
                                       break;
                               case OASDIV:
                                       sdiv2(r->vconst, v, l, &nod);
                                       break;
                               case OASMOD:
                                       smod2(r->vconst, v, l, &nod);
                                       break;
                               }
                       havev:
                               gmove(&nod, &nod2);
                               if(nn != Z)
                                       gmove(&nod, nn);
                               if(hardleft)
                                       regfree(&nod2);
                               regfree(&nod);
                               goto done;
                       case OASLDIV:
                               c = r->vconst;
                               if((c & 0x80000000) == 0)
                                       break;
                               if(hardleft)
                                       reglcgen(&nod2, l, Z);
                               else
                                       nod2 = *l;
                               regalloc(&nod1, l, nn);
                               cgen(&nod2, &nod1);
                               regalloc(&nod, l, nn);
                               zeroregm(&nod);
                               gins(ACMPL, &nod1, nodconst(c));
                               gins(ASBBL, nodconst(-1), &nod);
                               regfree(&nod1);
                               goto havev;
                       }
               }

               if(o == OASMUL) {
/* should favour AX */
                       regalloc(&nod, l, nn);
                       if(r->complex >= FNX) {
                               regalloc(&nod1, r, Z);
                               cgen(r, &nod1);
                               r = &nod1;
                       }
                       if(hardleft)
                               reglcgen(&nod2, l, Z);
                       else
                               nod2 = *l;
                       cgen(&nod2, &nod);
                       if(r->addable < INDEXED) {
                               if(r->complex < FNX) {
                                       regalloc(&nod1, r, Z);
                                       cgen(r, &nod1);
                               }
                               gopcode(OASMUL, n->type, &nod1, &nod);
                               regfree(&nod1);
                       }
                       else
                               gopcode(OASMUL, n->type, r, &nod);
                       if(r == &nod1)
                               regfree(r);
                       gmove(&nod, &nod2);
                       if(nn != Z)
                               gmove(&nod, nn);
                       regfree(&nod);
                       if(hardleft)
                               regfree(&nod2);
                       goto done;
               }

               /*
                * get nod to be D_AX
                * get nod1 to be D_DX
                */
               if(nodreg(&nod, nn, D_AX)) {
                       regsalloc(&nod2, n);
                       gmove(&nod, &nod2);
                       v = reg[D_AX];
                       reg[D_AX] = 0;

                       if(isreg(l, D_AX)) {
                               nod3 = *n;
                               nod3.left = &nod2;
                               cgen(&nod3, nn);
                       } else
                       if(isreg(r, D_AX)) {
                               nod3 = *n;
                               nod3.right = &nod2;
                               cgen(&nod3, nn);
                       } else
                               cgen(n, nn);

                       gmove(&nod2, &nod);
                       reg[D_AX] = v;
                       break;
               }
               if(nodreg(&nod1, nn, D_DX)) {
                       regsalloc(&nod2, n);
                       gmove(&nod1, &nod2);
                       v = reg[D_DX];
                       reg[D_DX] = 0;

                       if(isreg(l, D_DX)) {
                               nod3 = *n;
                               nod3.left = &nod2;
                               cgen(&nod3, nn);
                       } else
                       if(isreg(r, D_DX)) {
                               nod3 = *n;
                               nod3.right = &nod2;
                               cgen(&nod3, nn);
                       } else
                               cgen(n, nn);

                       gmove(&nod2, &nod1);
                       reg[D_DX] = v;
                       break;
               }
               reg[D_AX]++;
               reg[D_DX]++;

               if(l->complex >= r->complex) {
                       if(hardleft)
                               reglcgen(&nod2, l, Z);
                       else
                               nod2 = *l;
                       cgen(&nod2, &nod);
                       if(r->op == OCONST) {
                               switch(o) {
                               case OASDIV:
                                       sdivgen(&nod2, r, &nod, &nod1);
                                       goto divdone;
                               case OASLDIV:
                                       udivgen(&nod2, r, &nod, &nod1);
                               divdone:
                                       gmove(&nod1, &nod2);
                                       if(nn != Z)
                                               gmove(&nod1, nn);
                                       goto freelxaxdx;
                               }
                       }
                       if(o == OASDIV || o == OASMOD)
                               gins(ACDQ, Z, Z);
                       if(o == OASLDIV || o == OASLMOD)
                               zeroregm(&nod1);
                       if(r->addable < INDEXED || r->op == OCONST ||
                          !typeil[r->type->etype]) {
                               regalloc(&nod3, r, Z);
                               cgen(r, &nod3);
                               gopcode(o, l->type, &nod3, Z);
                               regfree(&nod3);
                       } else
                               gopcode(o, n->type, r, Z);
               } else {
                       regalloc(&nod3, r, Z);
                       cgen(r, &nod3);
                       if(hardleft)
                               reglcgen(&nod2, l, Z);
                       else
                               nod2 = *l;
                       cgen(&nod2, &nod);
                       if(o == OASDIV || o == OASMOD)
                               gins(ACDQ, Z, Z);
                       if(o == OASLDIV || o == OASLMOD)
                               zeroregm(&nod1);
                       gopcode(o, l->type, &nod3, Z);
                       regfree(&nod3);
               }
               if(o == OASMOD || o == OASLMOD) {
                       gmove(&nod1, &nod2);
                       if(nn != Z)
                               gmove(&nod1, nn);
               } else {
                       gmove(&nod, &nod2);
                       if(nn != Z)
                               gmove(&nod, nn);
               }
       freelxaxdx:
               if(hardleft)
                       regfree(&nod2);
               regfree(&nod);
               regfree(&nod1);
               break;

       fop:
               if(l->complex >= r->complex) {
                       cgen(l, &fregnode0);
                       if(r->addable < INDEXED) {
                               cgen(r, &fregnode0);
                               fgopcode(o, &fregnode0, &fregnode1, 1, 0);
                       } else
                               fgopcode(o, r, &fregnode0, 0, 0);
               } else {
                       cgen(r, &fregnode0);
                       if(l->addable < INDEXED) {
                               cgen(l, &fregnode0);
                               fgopcode(o, &fregnode0, &fregnode1, 1, 1);
                       } else
                               fgopcode(o, l, &fregnode0, 0, 1);
               }
               gmove(&fregnode0, nn);
               break;

       asfop:
               if(l->complex >= r->complex) {
                       if(hardleft)
                               reglcgen(&nod, l, Z);
                       else
                               nod = *l;
                       cgen(r, &fregnode0);
               } else {
                       cgen(r, &fregnode0);
                       if(hardleft)
                               reglcgen(&nod, l, Z);
                       else
                               nod = *l;
               }
               if(!typefd[l->type->etype]) {
                       gmove(&nod, &fregnode0);
                       fgopcode(o, &fregnode0, &fregnode1, 1, 1);
               } else
                       fgopcode(o, &nod, &fregnode0, 0, 1);
               if(nn != Z)
                       gins(AFMOVD, &fregnode0, &fregnode0);
               gmove(&fregnode0, &nod);
               if(nn != Z)
                       gmove(&fregnode0, nn);
               if(hardleft)
                       regfree(&nod);
               break;

       asbitop:
               regalloc(&nod4, n, nn);
               if(l->complex >= r->complex) {
                       bitload(l, &nod, &nod1, &nod2, &nod4);
                       regalloc(&nod3, r, Z);
                       cgen(r, &nod3);
               } else {
                       regalloc(&nod3, r, Z);
                       cgen(r, &nod3);
                       bitload(l, &nod, &nod1, &nod2, &nod4);
               }
               gmove(&nod, &nod4);

               if(typefd[nod3.type->etype])
                       fgopcode(o, &fregnode0, &fregnode1, 1, 1);
               else {
                       Node onod;

                       /* incredible grot ... */
                       onod = nod3;
                       onod.op = o;
                       onod.complex = 2;
                       onod.addable = 0;
                       onod.type = tfield;
                       onod.left = &nod4;
                       onod.right = &nod3;
                       cgen(&onod, Z);
               }
               regfree(&nod3);
               gmove(&nod4, &nod);
               regfree(&nod4);
               bitstore(l, &nod, &nod1, &nod2, nn);
               break;

       case OADDR:
               if(nn == Z) {
                       nullwarn(l, Z);
                       break;
               }
               lcgen(l, nn);
               break;

       case OFUNC:
               l = uncomma(l);
               if(l->complex >= FNX) {
                       if(l->op != OIND)
                               diag(n, "bad function call");

                       regret(&nod, l->left);
                       cgen(l->left, &nod);
                       regsalloc(&nod1, l->left);
                       gmove(&nod, &nod1);
                       regfree(&nod);

                       nod = *n;
                       nod.left = &nod2;
                       nod2 = *l;
                       nod2.left = &nod1;
                       nod2.complex = 1;
                       cgen(&nod, nn);

                       return;
               }
               gargs(r, &nod, &nod1);
               if(l->addable < INDEXED) {
                       reglcgen(&nod, l, nn);
                       nod.op = OREGISTER;
                       gopcode(OFUNC, n->type, Z, &nod);
                       regfree(&nod);
               } else
                       gopcode(OFUNC, n->type, Z, l);
               if(REGARG>=0 && reg[REGARG])
                       reg[REGARG]--;
               if(nn != Z) {
                       regret(&nod, n);
                       gmove(&nod, nn);
                       regfree(&nod);
               } else
               if(typefd[n->type->etype])
                       gins(AFMOVDP, &fregnode0, &fregnode0);
               break;

       case OIND:
               if(nn == Z) {
                       nullwarn(l, Z);
                       break;
               }
               regialloc(&nod, n, nn);
               r = l;
               while(r->op == OADD)
                       r = r->right;
               if(sconst(r)) {
                       v = r->vconst;
                       r->vconst = 0;
                       cgen(l, &nod);
                       nod.xoffset += v;
                       r->vconst = v;
               } else
                       cgen(l, &nod);
               regind(&nod, n);
               gmove(&nod, nn);
               regfree(&nod);
               break;

       case OEQ:
       case ONE:
       case OLE:
       case OLT:
       case OGE:
       case OGT:
       case OLO:
       case OLS:
       case OHI:
       case OHS:
               if(nn == Z) {
                       nullwarn(l, r);
                       break;
               }
               boolgen(n, 1, nn);
               break;

       case OANDAND:
       case OOROR:
               boolgen(n, 1, nn);
               if(nn == Z)
                       patch(p, pc);
               break;

       case ONOT:
               if(nn == Z) {
                       nullwarn(l, Z);
                       break;
               }
               boolgen(n, 1, nn);
               break;

       case OCOMMA:
               cgen(l, Z);
               cgen(r, nn);
               break;

       case OCAST:
               if(nn == Z) {
                       cgen(l, Z);
                       break;
               }
               /*
                * convert from types l->n->nn
                */
               if(nocast(l->type, n->type) && nocast(n->type, nn->type)) {
                       /* both null, gen l->nn */
                       cgen(l, nn);
                       break;
               }
               if(typev[l->type->etype]) {
                       cgen64(n, nn);
                       break;
               }
               regalloc(&nod, l, nn);
               cgen(l, &nod);
               regalloc(&nod1, n, &nod);
               gmove(&nod, &nod1);
               gmove(&nod1, nn);
               regfree(&nod1);
               regfree(&nod);
               break;

       case ODOT:
               sugen(l, nodrat, l->type->width);
               if(nn == Z)
                       break;
               warn(n, "non-interruptable temporary");
               nod = *nodrat;
               if(!r || r->op != OCONST) {
                       diag(n, "DOT and no offset");
                       break;
               }
               nod.xoffset += (long)r->vconst;
               nod.type = n->type;
               cgen(&nod, nn);
               break;

       case OCOND:
               bcgen(l, 1);
               p1 = p;
               cgen(r->left, nn);
               gbranch(OGOTO);
               patch(p1, pc);
               p1 = p;
               cgen(r->right, nn);
               patch(p1, pc);
               break;

       case OPOSTINC:
       case OPOSTDEC:
               v = 1;
               if(l->type->etype == TIND)
                       v = l->type->link->width;
               if(o == OPOSTDEC)
                       v = -v;
               if(l->op == OBIT)
                       goto bitinc;
               if(nn == Z)
                       goto pre;

               if(hardleft)
                       reglcgen(&nod, l, Z);
               else
                       nod = *l;

               if(typefd[n->type->etype])
                       goto fltinc;
               gmove(&nod, nn);
               gopcode(OADD, n->type, nodconst(v), &nod);
               if(hardleft)
                       regfree(&nod);
               break;

       case OPREINC:
       case OPREDEC:
               v = 1;
               if(l->type->etype == TIND)
                       v = l->type->link->width;
               if(o == OPREDEC)
                       v = -v;
               if(l->op == OBIT)
                       goto bitinc;

       pre:
               if(hardleft)
                       reglcgen(&nod, l, Z);
               else
                       nod = *l;
               if(typefd[n->type->etype])
                       goto fltinc;
               gopcode(OADD, n->type, nodconst(v), &nod);
               if(nn != Z)
                       gmove(&nod, nn);
               if(hardleft)
                       regfree(&nod);
               break;

       fltinc:
               gmove(&nod, &fregnode0);
               if(nn != Z && (o == OPOSTINC || o == OPOSTDEC))
                       gins(AFMOVD, &fregnode0, &fregnode0);
               gins(AFLD1, Z, Z);
               if(v < 0)
                       fgopcode(OSUB, &fregnode0, &fregnode1, 1, 0);
               else
                       fgopcode(OADD, &fregnode0, &fregnode1, 1, 0);
               if(nn != Z && (o == OPREINC || o == OPREDEC))
                       gins(AFMOVD, &fregnode0, &fregnode0);
               gmove(&fregnode0, &nod);
               if(hardleft)
                       regfree(&nod);
               break;

       bitinc:
               if(nn != Z && (o == OPOSTINC || o == OPOSTDEC)) {
                       bitload(l, &nod, &nod1, &nod2, Z);
                       gmove(&nod, nn);
                       gopcode(OADD, tfield, nodconst(v), &nod);
                       bitstore(l, &nod, &nod1, &nod2, Z);
                       break;
               }
               bitload(l, &nod, &nod1, &nod2, nn);
               gopcode(OADD, tfield, nodconst(v), &nod);
               bitstore(l, &nod, &nod1, &nod2, nn);
               break;
       }
done:
       cursafe = curs;
}

void
reglcgen(Node *t, Node *n, Node *nn)
{
       Node *r;
       long v;

       regialloc(t, n, nn);
       if(n->op == OIND) {
               r = n->left;
               while(r->op == OADD)
                       r = r->right;
               if(sconst(r)) {
                       v = r->vconst;
                       r->vconst = 0;
                       lcgen(n, t);
                       t->xoffset += v;
                       r->vconst = v;
                       regind(t, n);
                       return;
               }
       }
       lcgen(n, t);
       regind(t, n);
}

void
lcgen(Node *n, Node *nn)
{
       Prog *p1;
       Node nod;

       if(debug['g']) {
               prtree(nn, "lcgen lhs");
               prtree(n, "lcgen");
       }
       if(n == Z || n->type == T)
               return;
       if(nn == Z) {
               nn = &nod;
               regalloc(&nod, n, Z);
       }
       switch(n->op) {
       default:
               if(n->addable < INDEXED) {
                       diag(n, "unknown op in lcgen: %O", n->op);
                       break;
               }
               gopcode(OADDR, n->type, n, nn);
               break;

       case OCOMMA:
               cgen(n->left, n->left);
               lcgen(n->right, nn);
               break;

       case OIND:
               cgen(n->left, nn);
               break;

       case OCOND:
               bcgen(n->left, 1);
               p1 = p;
               lcgen(n->right->left, nn);
               gbranch(OGOTO);
               patch(p1, pc);
               p1 = p;
               lcgen(n->right->right, nn);
               patch(p1, pc);
               break;
       }
}

void
bcgen(Node *n, int true)
{

       if(n->type == T)
               gbranch(OGOTO);
       else
               boolgen(n, true, Z);
}

void
boolgen(Node *n, int true, Node *nn)
{
       int o;
       Prog *p1, *p2;
       Node *l, *r, nod, nod1;
       long curs;

       if(debug['g']) {
               prtree(nn, "boolgen lhs");
               prtree(n, "boolgen");
       }
       curs = cursafe;
       l = n->left;
       r = n->right;
       switch(n->op) {

       default:
               if(typev[n->type->etype]) {
                       testv(n, true);
                       goto com;
               }
               o = ONE;
               if(true)
                       o = OEQ;
               if(typefd[n->type->etype]) {
                       if(n->addable < INDEXED) {
                               cgen(n, &fregnode0);
                               gins(AFLDZ, Z, Z);
                               fgopcode(o, &fregnode0, &fregnode1, 1, 1);
                       } else {
                               gins(AFLDZ, Z, Z);
                               fgopcode(o, n, &fregnode0, 0, 1);
                       }
                       goto com;
               }
               /* bad, 13 is address of external that becomes constant */
               if(n->addable >= INDEXED && n->addable != 13) {
                       gopcode(o, n->type, n, nodconst(0));
                       goto com;
               }
               regalloc(&nod, n, nn);
               cgen(n, &nod);
               gopcode(o, n->type, &nod, nodconst(0));
               regfree(&nod);
               goto com;

       case OCONST:
               o = vconst(n);
               if(!true)
                       o = !o;
               gbranch(OGOTO);
               if(o) {
                       p1 = p;
                       gbranch(OGOTO);
                       patch(p1, pc);
               }
               goto com;

       case OCOMMA:
               cgen(l, Z);
               boolgen(r, true, nn);
               break;

       case ONOT:
               boolgen(l, !true, nn);
               break;

       case OCOND:
               bcgen(l, 1);
               p1 = p;
               bcgen(r->left, true);
               p2 = p;
               gbranch(OGOTO);
               patch(p1, pc);
               p1 = p;
               bcgen(r->right, !true);
               patch(p2, pc);
               p2 = p;
               gbranch(OGOTO);
               patch(p1, pc);
               patch(p2, pc);
               goto com;

       case OANDAND:
               if(!true)
                       goto caseor;

       caseand:
               bcgen(l, true);
               p1 = p;
               bcgen(r, !true);
               p2 = p;
               patch(p1, pc);
               gbranch(OGOTO);
               patch(p2, pc);
               goto com;

       case OOROR:
               if(!true)
                       goto caseand;

       caseor:
               bcgen(l, !true);
               p1 = p;
               bcgen(r, !true);
               p2 = p;
               gbranch(OGOTO);
               patch(p1, pc);
               patch(p2, pc);
               goto com;

       case OEQ:
       case ONE:
       case OLE:
       case OLT:
       case OGE:
       case OGT:
       case OHI:
       case OHS:
       case OLO:
       case OLS:
               o = n->op;
               if(typev[l->type->etype]) {
                       if(!true)
                               n->op = comrel[relindex(o)];
                       cgen64(n, Z);
                       goto com;
               }
               if(true)
                       o = comrel[relindex(o)];
               if(l->complex >= FNX && r->complex >= FNX) {
                       regret(&nod, r);
                       cgen(r, &nod);
                       regsalloc(&nod1, r);
                       gmove(&nod, &nod1);
                       regfree(&nod);
                       nod = *n;
                       nod.right = &nod1;
                       boolgen(&nod, true, nn);
                       break;
               }
               if(typefd[l->type->etype]) {
                       if(l->complex >= r->complex) {
                               cgen(l, &fregnode0);
                               if(r->addable < INDEXED) {
                                       cgen(r, &fregnode0);
                                       o = invrel[relindex(o)];
                                       fgopcode(o, &fregnode0, &fregnode1, 1, 1);
                               } else
                                       fgopcode(o, r, &fregnode0, 0, 1);
                       } else {
                               o = invrel[relindex(o)];
                               cgen(r, &fregnode0);
                               if(l->addable < INDEXED) {
                                       cgen(l, &fregnode0);
                                       o = invrel[relindex(o)];
                                       fgopcode(o, &fregnode0, &fregnode1, 1, 1);
                               } else
                                       fgopcode(o, l, &fregnode0, 0, 1);
                       }
                       goto com;
               }
               if(l->op == OCONST) {
                       o = invrel[relindex(o)];
                       /* bad, 13 is address of external that becomes constant */
                       if(r->addable < INDEXED || r->addable == 13) {
                               regalloc(&nod, r, nn);
                               cgen(r, &nod);
                               gopcode(o, l->type, &nod, l);
                               regfree(&nod);
                       } else
                               gopcode(o, l->type, r, l);
                       goto com;
               }
               if(l->complex >= r->complex) {
                       regalloc(&nod, l, nn);
                       cgen(l, &nod);
                       if(r->addable < INDEXED) {
                               regalloc(&nod1, r, Z);
                               cgen(r, &nod1);
                               gopcode(o, l->type, &nod, &nod1);
                               regfree(&nod1);
                       } else
                               gopcode(o, l->type, &nod, r);
                       regfree(&nod);
                       goto com;
               }
               regalloc(&nod, r, nn);
               cgen(r, &nod);
               if(l->addable < INDEXED || l->addable == 13) {
                       regalloc(&nod1, l, Z);
                       cgen(l, &nod1);
                       if(typechlp[l->type->etype])
                               gopcode(o, types[TINT], &nod1, &nod);
                       else
                               gopcode(o, l->type, &nod1, &nod);
                       regfree(&nod1);
               } else
                       gopcode(o, l->type, l, &nod);
               regfree(&nod);

       com:
               if(nn != Z) {
                       p1 = p;
                       gmove(nodconst(1L), nn);
                       gbranch(OGOTO);
                       p2 = p;
                       patch(p1, pc);
                       gmove(nodconst(0L), nn);
                       patch(p2, pc);
               }
               break;
       }
       cursafe = curs;
}

void
sugen(Node *n, Node *nn, long w)
{
       Prog *p1;
       Node nod0, nod1, nod2, nod3, nod4, *h, *l, *r;
       Type *t;
       int c, v, x;

       if(n == Z || n->type == T)
               return;
       if(debug['g']) {
               prtree(nn, "sugen lhs");
               prtree(n, "sugen");
       }
       if(nn == nodrat)
               if(w > nrathole)
                       nrathole = w;
       switch(n->op) {
       case OIND:
               if(nn == Z) {
                       nullwarn(n->left, Z);
                       break;
               }

       default:
               goto copy;

       case OCONST:
               if(n->type && typev[n->type->etype]) {
                       if(nn == Z) {
                               nullwarn(n->left, Z);
                               break;
                       }

                       if(nn->op == OREGPAIR) {
                               loadpair(n, nn);
                               break;
                       }
                       else if(!vaddr(nn, 0)) {
                               t = nn->type;
                               nn->type = types[TLONG];
                               reglcgen(&nod1, nn, Z);
                               nn->type = t;

                               gmove(lo64(n), &nod1);
                               nod1.xoffset += SZ_LONG;
                               gmove(hi64(n), &nod1);
                               regfree(&nod1);
                       }
                       else {
                               gins(AMOVL, lo64(n), nn);
                               nn->xoffset += SZ_LONG;
                               gins(AMOVL, hi64(n), nn);
                               nn->xoffset -= SZ_LONG;
                               break;
                       }
                       break;
               }
               goto copy;

       case ODOT:
               l = n->left;
               sugen(l, nodrat, l->type->width);
               if(nn == Z)
                       break;
               warn(n, "non-interruptable temporary");
               nod1 = *nodrat;
               r = n->right;
               if(!r || r->op != OCONST) {
                       diag(n, "DOT and no offset");
                       break;
               }
               nod1.xoffset += (long)r->vconst;
               nod1.type = n->type;
               sugen(&nod1, nn, w);
               break;

       case OSTRUCT:
               /*
                * rewrite so lhs has no fn call
                */
               if(nn != Z && side(nn)) {
                       nod1 = *n;
                       nod1.type = typ(TIND, n->type);
                       regret(&nod2, &nod1);
                       lcgen(nn, &nod2);
                       regsalloc(&nod0, &nod1);
                       cgen(&nod2, &nod0);
                       regfree(&nod2);

                       nod1 = *n;
                       nod1.op = OIND;
                       nod1.left = &nod0;
                       nod1.right = Z;
                       nod1.complex = 1;

                       sugen(n, &nod1, w);
                       return;
               }

               r = n->left;
               for(t = n->type->link; t != T; t = t->down) {
                       l = r;
                       if(r->op == OLIST) {
                               l = r->left;
                               r = r->right;
                       }
                       if(nn == Z) {
                               cgen(l, nn);
                               continue;
                       }
                       /*
                        * hand craft *(&nn + o) = l
                        */
                       nod0 = znode;
                       nod0.op = OAS;
                       nod0.type = t;
                       nod0.left = &nod1;
                       nod0.right = nil;

                       nod1 = znode;
                       nod1.op = OIND;
                       nod1.type = t;
                       nod1.left = &nod2;

                       nod2 = znode;
                       nod2.op = OADD;
                       nod2.type = typ(TIND, t);
                       nod2.left = &nod3;
                       nod2.right = &nod4;

                       nod3 = znode;
                       nod3.op = OADDR;
                       nod3.type = nod2.type;
                       nod3.left = nn;

                       nod4 = znode;
                       nod4.op = OCONST;
                       nod4.type = nod2.type;
                       nod4.vconst = t->offset;

                       ccom(&nod0);
                       acom(&nod0);
                       xcom(&nod0);
                       nod0.addable = 0;
                       nod0.right = l;

                       /* prtree(&nod0, "hand craft"); /* */
                       cgen(&nod0, Z);
               }
               break;

       case OAS:
               if(nn == Z) {
                       if(n->addable < INDEXED)
                               sugen(n->right, n->left, w);
                       break;
               }

               sugen(n->right, nodrat, w);
               warn(n, "non-interruptable temporary");
               sugen(nodrat, n->left, w);
               sugen(nodrat, nn, w);
               break;

       case OFUNC:
               if(nn == Z) {
                       sugen(n, nodrat, w);
                       break;
               }
               h = nn;
               if(nn->op == OREGPAIR) {
                       regsalloc(&nod1, nn);
                       nn = &nod1;
               }
               if(nn->op != OIND) {
                       nn = new1(OADDR, nn, Z);
                       nn->type = types[TIND];
                       nn->addable = 0;
               } else
                       nn = nn->left;
               n = new(OFUNC, n->left, new(OLIST, nn, n->right));
               n->complex = FNX;
               n->type = types[TVOID];
               n->left->type = types[TVOID];
               cgen(n, Z);
               if(h->op == OREGPAIR)
                       loadpair(nn->left, h);
               break;

       case OCOND:
               bcgen(n->left, 1);
               p1 = p;
               sugen(n->right->left, nn, w);
               gbranch(OGOTO);
               patch(p1, pc);
               p1 = p;
               sugen(n->right->right, nn, w);
               patch(p1, pc);
               break;

       case OCOMMA:
               cgen(n->left, Z);
               sugen(n->right, nn, w);
               break;
       }
       return;

copy:
       if(nn == Z) {
               switch(n->op) {
               case OASADD:
               case OASSUB:
               case OASAND:
               case OASOR:
               case OASXOR:

               case OASMUL:
               case OASLMUL:

               case OASASHL:
               case OASASHR:
               case OASLSHR:
                       break;

               case OPOSTINC:
               case OPOSTDEC:
               case OPREINC:
               case OPREDEC:
                       break;

               default:
                       return;
               }
       }

       if(n->complex >= FNX && nn != nil && nn->complex >= FNX) {
               t = nn->type;
               nn->type = types[TLONG];
               regialloc(&nod1, nn, Z);
               lcgen(nn, &nod1);
               regsalloc(&nod2, &nod1);
               nn->type = t;

               gins(AMOVL, &nod1, &nod2);
               regfree(&nod1);

               nod2.type = typ(TIND, t);

               nod1 = nod2;
               nod1.op = OIND;
               nod1.left = &nod2;
               nod1.right = Z;
               nod1.complex = 1;
               nod1.type = t;

               sugen(n, &nod1, w);
               return;
       }

       x = 0;
       v = w == 8;
       c = cursafe;
       if(v) {
               if(n->left != Z && n->left->complex >= FNX
               && n->right != Z && n->right->complex >= FNX) {
                       regsalloc(&nod1, n->right);
                       cgen(n->right, &nod1);
                       nod2 = *n;
                       nod2.right = &nod1;
                       cgen(&nod2, nn);
                       cursafe = c;
                       return;
               }
               if(cgen64(n, nn)) {
                       cursafe = c;
                       return;
               }
               if(n->op == OCOM) {
                       n = n->left;
                       x = 1;
               }

               if(n->complex > nn->complex){
                       t = n->type;
                       n->type = types[TLONG];
                       regalloc(&nod0, n, Z);
                       if(!vaddr(n, 0)) {
                               reglcgen(&nod1, n, Z);
                               n->type = t;
                               n = &nod1;
                       }
                       else
                               n->type = t;

                       t = nn->type;
                       nn->type = types[TLONG];
                       if(!vaddr(nn, 0)) {
                               reglcgen(&nod2, nn, Z);
                               nn->type = t;
                               nn = &nod2;
                       }
                       else
                               nn->type = t;
               } else {
                       t = nn->type;
                       nn->type = types[TLONG];
                       regalloc(&nod0, nn, Z);
                       if(!vaddr(nn, 0)) {
                               reglcgen(&nod2, nn, Z);
                               nn->type = t;
                               nn = &nod2;
                       }
                       else
                               nn->type = t;

                       t = n->type;
                       n->type = types[TLONG];
                       if(!vaddr(n, 0)) {
                               reglcgen(&nod1, n, Z);
                               n->type = t;
                               n = &nod1;
                       }
                       else
                               n->type = t;
               }
               gins(AMOVL, n, &nod0);
               if(x)
                       gins(ANOTL, Z, &nod0);
               gins(AMOVL, &nod0, nn);
               n->xoffset += SZ_LONG;
               nn->xoffset += SZ_LONG;
               gins(AMOVL, n, &nod0);
               if(x)
                       gins(ANOTL, Z, &nod0);
               gins(AMOVL, &nod0, nn);
               n->xoffset -= SZ_LONG;
               nn->xoffset -= SZ_LONG;
               if(nn == &nod2)
                       regfree(&nod2);
               if(n == &nod1)
                       regfree(&nod1);
               regfree(&nod0);
               return;
       }

       t = n->type;
       if(t != types[TIND]){
               n->type = types[TIND];
               sugen(n, nn, w);
               n->type = t;
               return;
       }
       t = nn->type;
       if(t != types[TIND]){
               nn->type = types[TIND];
               sugen(n, nn, w);
               nn->type = t;
               return;
       }

       if(nodreg(&nod1, n, D_SI)) {
               regsalloc(&nod4, &nod1);
               gmove(&nod1, &nod4);
               v = reg[D_SI];
               reg[D_SI] = 0;
               sugen(n, nn, w);
               reg[D_SI] = v;
               gmove(&nod4, &nod1);
               cursafe = c;
               return;
       }
       if(nodreg(&nod2, nn, D_DI)) {
               regsalloc(&nod4, &nod2);
               gmove(&nod2, &nod4);
               v = reg[D_DI];
               reg[D_DI] = 0;
               sugen(n, nn, w);
               reg[D_DI] = v;
               gmove(&nod4, &nod2);
               cursafe = c;
               return;
       }
       if(nodreg(&nod3, Z, D_CX)) {
               regsalloc(&nod4, &nod3);
               gmove(&nod3, &nod4);
               v = reg[D_CX];
               reg[D_CX] = 0;
               sugen(n, nn, w);
               reg[D_CX] = v;
               gmove(&nod4, &nod3);
               cursafe = c;
               return;
       }

       if(n->complex > nn->complex){
               reg[nod1.reg]++;
               lcgen(n, &nod1);

               reg[nod2.reg]++;
               lcgen(nn, &nod2);
       } else {
               reg[nod2.reg]++;
               lcgen(nn, &nod2);

               reg[nod1.reg]++;
               lcgen(n, &nod1);
       }
       reg[nod3.reg]++;

       gins(AMOVL, nodconst(w/SZ_LONG), &nod3);
       gins(ACLD, Z, Z);
       gins(AREP, Z, Z);
       gins(AMOVSL, Z, Z);
       if(w & (SZ_LONG-1)) {
               /* odd length of packed structure */
               gins(AMOVL, nodconst(w & (SZ_LONG-1)), &nod3);
               gins(AREP, Z, Z);
               gins(AMOVSB, Z, Z);
       }

       reg[nod3.reg]--;
       reg[nod2.reg]--;
       reg[nod1.reg]--;
}