#include "cc.h"

typedef struct Com Com;
struct Com
{
       int     n;
       Node    *t[500];
};

int compar(Node*, int);
static void comma(Node*);
static Node*    commas(Com*, Node*);

void
complex(Node *n)
{

       if(n == Z)
               return;

       nearln = n->lineno;
       if(debug['t'])
               if(n->op != OCONST)
                       prtree(n, "pre complex");
       if(tcom(n))
               return;
       if(debug['y'] || 1)
               comma(n);
       if(debug['t'])
               if(n->op != OCONST)
                       prtree(n, "t complex");
       ccom(n);
       if(debug['t'])
               if(n->op != OCONST)
                       prtree(n, "c complex");
       acom(n);
       if(debug['t'])
               if(n->op != OCONST)
                       prtree(n, "a complex");
       xcom(n);
       if(debug['t'])
               if(n->op != OCONST)
                       prtree(n, "x complex");
}

/*
* evaluate types
* evaluate lvalues (addable == 1)
*/
enum
{
       ADDROF  = 1<<0,
       CASTOF  = 1<<1,
       ADDROP  = 1<<2,
};

int
tcom(Node *n)
{

       return tcomo(n, ADDROF);
}

int
tcomo(Node *n, int f)
{
       Node *l, *r;
       Type *t;
       int o;

       if(n == Z) {
               diag(Z, "Z in tcom");
               errorexit();
       }
       n->addable = 0;
       l = n->left;
       r = n->right;

       switch(n->op) {
       default:
               diag(n, "unknown op in type complex: %O", n->op);
               goto bad;

       case ODOTDOT:
               /*
                * tcom has already been called on this subtree
                */
               *n = *n->left;
               if(n->type == T)
                       goto bad;
               break;

       case OCAST:
               if(n->type == T)
                       break;
               if(n->type->width == types[TLONG]->width) {
                       if(tcomo(l, ADDROF|CASTOF))
                               goto bad;
               } else
                       if(tcom(l))
                               goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, n->type, tcast))
                       goto bad;
               break;

       case ORETURN:
               if(l == Z) {
                       if(n->type->etype != TVOID)
                               warn(n, "null return of a typed function");
                       break;
               }
               if(tcom(l))
                       goto bad;
               typeext(n->type, l);
               if(tcompat(n, n->type, l->type, tasign))
                       break;
               constas(n, n->type, l->type);
               if(!sametype(n->type, l->type)) {
                       l = new1(OCAST, l, Z);
                       l->type = n->type;
                       n->left = l;
               }
               break;

       case OASI:      /* same as as, but no test for const */
               n->op = OAS;
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;

               typeext(l->type, r);
               if(tlvalue(l) || tcompat(n, l->type, r->type, tasign))
                       goto bad;
               if(!sametype(l->type, r->type)) {
                       r = new1(OCAST, r, Z);
                       r->type = l->type;
                       n->right = r;
               }
               n->type = l->type;
               break;

       case OAS:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               typeext(l->type, r);
               if(tcompat(n, l->type, r->type, tasign))
                       goto bad;
               constas(n, l->type, r->type);
               if(!sametype(l->type, r->type)) {
                       r = new1(OCAST, r, Z);
                       r->type = l->type;
                       n->right = r;
               }
               n->type = l->type;
               break;

       case OASADD:
       case OASSUB:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               typeext1(l->type, r);
               if(tcompat(n, l->type, r->type, tasadd))
                       goto bad;
               constas(n, l->type, r->type);
               t = l->type;
               arith(n, 0);
               while(n->left->op == OCAST)
                       n->left = n->left->left;
               if(!mixedasop(t, n->type)) {
                       if(!sametype(t, n->type)) {
                               r = new1(OCAST, n->right, Z);
                               r->type = t;
                               n->right = r;
                               n->type = t;
                       }
               } else
                       n->type = t;
               break;

       case OASMUL:
       case OASLMUL:
       case OASDIV:
       case OASLDIV:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               typeext1(l->type, r);
               if(tcompat(n, l->type, r->type, tmul))
                       goto bad;
               constas(n, l->type, r->type);
               t = l->type;
               arith(n, 0);
               while(n->left->op == OCAST)
                       n->left = n->left->left;
               if(!mixedasop(t, n->type)) {
                       if(!sametype(t, n->type)) {
                               r = new1(OCAST, n->right, Z);
                               r->type = t;
                               n->right = r;
                               n->type = t;
                       }
               } else {
                       n->type = t;
                       break;
               }
               if(typeu[n->type->etype]) {
                       if(n->op == OASDIV)
                               n->op = OASLDIV;
                       if(n->op == OASMUL)
                               n->op = OASLMUL;
               }
               break;

       case OASLSHR:
       case OASASHR:
       case OASASHL:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tand))
                       goto bad;
               n->type = l->type;
               n->right = new1(OCAST, r, Z);
               n->right->type = types[TINT];
               if(typeu[n->type->etype]) {
                       if(n->op == OASASHR)
                               n->op = OASLSHR;
               }
               break;

       case OASMOD:
       case OASLMOD:
       case OASOR:
       case OASAND:
       case OASXOR:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tand))
                       goto bad;
               t = l->type;
               arith(n, 0);
               while(n->left->op == OCAST)
                       n->left = n->left->left;
               if(!mixedasop(t, n->type)) {
                       if(!sametype(t, n->type)) {
                               r = new1(OCAST, n->right, Z);
                               r->type = t;
                               n->right = r;
                               n->type = t;
                       }
               }else
                       n->type = t;
               if(typeu[n->type->etype]) {
                       if(n->op == OASMOD)
                               n->op = OASLMOD;
               }
               break;

       case OPREINC:
       case OPREDEC:
       case OPOSTINC:
       case OPOSTDEC:
               if(tcom(l))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, types[TINT], tadd))
                       goto bad;
               n->type = l->type;
               if(n->type->etype == TIND)
               if(n->type->link->width < 1) {
                       snap(n->type->link);
                       if(n->type->link->width < 1)
                               diag(n, "inc/dec of a void pointer");
               }
               break;

       case OEQ:
       case ONE:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               typeext(l->type, r);
               typeext(r->type, l);
               if(tcompat(n, l->type, r->type, trel))
                       goto bad;
               arith(n, 0);
               n->type = types[TINT];
               break;

       case OLT:
       case OGE:
       case OGT:
       case OLE:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               typeext1(l->type, r);
               typeext1(r->type, l);
               if(tcompat(n, l->type, r->type, trel))
                       goto bad;
               arith(n, 0);
               if(typeu[n->type->etype])
                       n->op = logrel[relindex(n->op)];
               n->type = types[TINT];
               break;

       case OCOND:
               o = tcom(l);
               o |= tcom(r->left);
               if(o | tcom(r->right))
                       goto bad;
               if(r->right->type->etype == TIND && zpconst(r->left)) {
                       r->type = r->right->type;
                       r->left->type = r->right->type;
                       r->left->vconst = 0;
               }
               if(r->left->type->etype == TIND && zpconst(r->right)) {
                       r->type = r->left->type;
                       r->right->type = r->left->type;
                       r->right->vconst = 0;
               }
               if(sametype(r->right->type, r->left->type)) {
                       r->type = r->right->type;
                       n->type = r->type;
                       break;
               }
               if(tcompat(r, r->left->type, r->right->type, trel))
                       goto bad;
               arith(r, 0);
               n->type = r->type;
               break;

       case OADD:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tadd))
                       goto bad;
               arith(n, 1);
               break;

       case OSUB:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tsub))
                       goto bad;
               arith(n, 1);
               break;

       case OMUL:
       case OLMUL:
       case ODIV:
       case OLDIV:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tmul))
                       goto bad;
               arith(n, 1);
               if(typeu[n->type->etype]) {
                       if(n->op == ODIV)
                               n->op = OLDIV;
                       if(n->op == OMUL)
                               n->op = OLMUL;
               }
               break;

       case OLSHR:
       case OASHL:
       case OASHR:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tand))
                       goto bad;
               n->right = Z;
               arith(n, 1);
               n->right = new1(OCAST, r, Z);
               n->right->type = types[TINT];
               if(typeu[n->type->etype])
                       if(n->op == OASHR)
                               n->op = OLSHR;
               break;

       case OAND:
       case OOR:
       case OXOR:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tand))
                       goto bad;
               arith(n, 1);
               break;

       case OMOD:
       case OLMOD:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, l->type, r->type, tand))
                       goto bad;
               arith(n, 1);
               if(typeu[n->type->etype])
                       n->op = OLMOD;
               break;

       case OPOS:
               if(tcom(l))
                       goto bad;
               if(isfunct(n))
                       break;

               r = l;
               l = new(OCONST, Z, Z);
               l->vconst = 0;
               l->type = types[TINT];
               n->op = OADD;
               n->right = r;
               n->left = l;

               if(tcom(l))
                       goto bad;
               if(tcompat(n, l->type, r->type, tsub))
                       goto bad;
               arith(n, 1);
               break;

       case ONEG:
               if(tcom(l))
                       goto bad;
               if(isfunct(n))
                       break;

               if(!machcap(n)) {
                       r = l;
                       l = new(OCONST, Z, Z);
                       l->vconst = 0;
                       l->type = types[TINT];
                       n->op = OSUB;
                       n->right = r;
                       n->left = l;

                       if(tcom(l))
                               goto bad;
                       if(tcompat(n, l->type, r->type, tsub))
                               goto bad;
               }
               arith(n, 1);
               break;

       case OCOM:
               if(tcom(l))
                       goto bad;
               if(isfunct(n))
                       break;

               if(!machcap(n)) {
                       r = l;
                       l = new(OCONST, Z, Z);
                       l->vconst = -1;
                       l->type = types[TINT];
                       n->op = OXOR;
                       n->right = r;
                       n->left = l;

                       if(tcom(l))
                               goto bad;
                       if(tcompat(n, l->type, r->type, tand))
                               goto bad;
               }
               arith(n, 1);
               break;

       case ONOT:
               if(tcom(l))
                       goto bad;
               if(isfunct(n))
                       break;
               if(tcompat(n, T, l->type, tnot))
                       goto bad;
               n->type = types[TINT];
               break;

       case OANDAND:
       case OOROR:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               if(tcompat(n, T, l->type, tnot) |
                  tcompat(n, T, r->type, tnot))
                       goto bad;
               n->type = types[TINT];
               break;

       case OCOMMA:
               o = tcom(l);
               if(o | tcom(r))
                       goto bad;
               n->type = r->type;
               break;


       case OSIGN:     /* extension signof(type) returns a hash */
               if(l != Z) {
                       if(l->op != OSTRING && l->op != OLSTRING)
                               if(tcomo(l, 0))
                                       goto bad;
                       if(l->op == OBIT) {
                               diag(n, "signof bitfield");
                               goto bad;
                       }
                       n->type = l->type;
               }
               if(n->type == T)
                       goto bad;
               if(n->type->width < 0) {
                       diag(n, "signof undefined type");
                       goto bad;
               }
               n->op = OCONST;
               n->left = Z;
               n->right = Z;
               n->vconst = convvtox(signature(n->type), TULONG);
               n->type = types[TULONG];
               break;

       case OSIZE:
               if(l != Z) {
                       if(l->op != OSTRING && l->op != OLSTRING)
                               if(tcomo(l, 0))
                                       goto bad;
                       if(l->op == OBIT) {
                               diag(n, "sizeof bitfield");
                               goto bad;
                       }
                       n->type = l->type;
               }
               if(n->type == T)
                       goto bad;
               if(n->type->width <= 0) {
                       diag(n, "sizeof undefined type");
                       goto bad;
               }
               if(n->type->etype == TFUNC) {
                       diag(n, "sizeof function");
                       goto bad;
               }
               n->op = OCONST;
               n->left = Z;
               n->right = Z;
               n->vconst = convvtox(n->type->width, TINT);
               n->type = types[TINT];
               break;

       case OFUNC:
               o = tcomo(l, 0);
               if(o)
                       goto bad;
               if(l->type->etype == TIND && l->type->link->etype == TFUNC) {
                       l = new1(OIND, l, Z);
                       l->type = l->left->type->link;
                       n->left = l;
               }
               if(tcompat(n, T, l->type, tfunct))
                       goto bad;
               if(o | tcoma(l, r, l->type->down, 1))
                       goto bad;
               n->type = l->type->link;
               if(!debug['B']){
                       if(l->type->down == T){
                               if(!debug['T'])
                                       nerrors--;
                               diag(n, "function not declared: %F", l);
                       }else if(l->type->down->etype == TOLD) {
                               nerrors--;
                               diag(n, "function args not checked: %F", l);
                       }
               }
               dpcheck(n);
               break;

       case ONAME:
               if(n->type == T) {
                       diag(n, "name not declared: %F", n);
                       goto bad;
               }
               if(n->type->etype == TENUM) {
                       n->op = OCONST;
                       n->type = n->sym->tenum;
                       if(!typefd[n->type->etype])
                               n->vconst = n->sym->vconst;
                       else
                               n->fconst = n->sym->fconst;
                       break;
               }
               n->addable = 1;
               if(n->class == CEXREG) {
                       n->op = OREGISTER;
                       if(thechar == '8')
                               n->op = OEXREG;
                       n->reg = n->sym->offset;
                       n->xoffset = 0;
                       break;
               }
               break;

       case OLSTRING:
               if(n->type->link != types[TRUNE]) {
                       o = outstring(0, 0);
                       while(o & 3) {
                               Rune str[1] = {0};
                               outlstring(str, sizeof(Rune));
                               o = outlstring(0, 0);
                       }
               }
               n->op = ONAME;
               n->xoffset = outlstring(n->rstring, n->type->width);
               n->addable = 1;
               break;

       case OSTRING:
               if(n->type->link != types[TCHAR]) {
                       o = outstring(0, 0);
                       while(o & 3) {
                               outstring("", 1);
                               o = outstring(0, 0);
                       }
               }
               n->op = ONAME;
               n->xoffset = outstring(n->cstring, n->type->width);
               n->addable = 1;
               break;

       case OCONST:
               break;

       case ODOT:
               if(tcom(l))
                       goto bad;
               if(tcompat(n, T, l->type, tdot))
                       goto bad;
               if(tcomd(n))
                       goto bad;
               break;

       case OADDR:
               if(tcomo(l, ADDROP))
                       goto bad;
               if(tlvalue(l))
                       goto bad;
               if(l->type->nbits) {
                       diag(n, "address of a bit field");
                       goto bad;
               }
               if(l->op == OREGISTER) {
                       diag(n, "address of a register");
                       goto bad;
               }
               n->type = typ(TIND, l->type);
               n->type->width = types[TIND]->width;
               break;

       case OIND:
               if(tcom(l))
                       goto bad;
               if(tcompat(n, T, l->type, tindir))
                       goto bad;
               n->type = l->type->link;
               n->addable = 1;
               break;

       case OSTRUCT:
               if(tcomx(n))
                       goto bad;
               break;
       }
       t = n->type;
       if(t == T)
               goto bad;
       if(t->width < 0) {
               snap(t);
               if(t->width < 0) {
                       if(typesu[t->etype] && t->tag)
                               diag(n, "structure not fully declared %s", t->tag->name);
                       else
                               diag(n, "structure not fully declared");
                       goto bad;
               }
       }
       if(typeaf[t->etype]) {
               if(f & ADDROF)
                       goto addaddr;
               if(f & ADDROP)
                       warn(n, "address of array/func ignored");
       }
       return 0;

addaddr:
       if(tlvalue(n))
               goto bad;
       l = new1(OXXX, Z, Z);
       *l = *n;
       n->op = OADDR;
       if(l->type->etype == TARRAY)
               l->type = l->type->link;
       n->left = l;
       n->right = Z;
       n->addable = 0;
       n->type = typ(TIND, l->type);
       n->type->width = types[TIND]->width;
       return 0;

bad:
       n->type = T;
       return 1;
}

int
tcoma(Node *l, Node *n, Type *t, int f)
{
       Node *n1;
       int o;

       if(t != T)
       if(t->etype == TOLD || t->etype == TDOT)        /* .../old in prototype */
               t = T;
       if(n == Z) {
               if(t != T && !sametype(t, types[TVOID])) {
                       diag(n, "not enough function arguments: %F", l);
                       return 1;
               }
               return 0;
       }
       if(n->op == OLIST) {
               o = tcoma(l, n->left, t, 0);
               if(t != T) {
                       t = t->down;
                       if(t == T)
                               t = types[TVOID];
               }
               return o | tcoma(l, n->right, t, 1);
       }
       if(f && t != T)
               tcoma(l, Z, t->down, 0);
       if(tcom(n) || tcompat(n, T, n->type, targ))
               return 1;
       if(sametype(t, types[TVOID])) {
               diag(n, "too many function arguments: %F", l);
               return 1;
       }
       if(t != T) {
               typeext(t, n);
               if(stcompat(nodproto, t, n->type, tasign)) {
                       diag(l, "argument prototype mismatch \"%T\" for \"%T\": %F",
                               n->type, t, l);
                       return 1;
               }
               switch(t->etype) {
               case TCHAR:
               case TSHORT:
                       t = types[TINT];
                       break;

               case TUCHAR:
               case TUSHORT:
                       t = types[TUINT];
                       break;
               }
       } else
       switch(n->type->etype)
       {
       case TCHAR:
       case TSHORT:
               t = types[TINT];
               break;

       case TUCHAR:
       case TUSHORT:
               t = types[TUINT];
               break;

       case TFLOAT:
               t = types[TDOUBLE];
       }
       if(t != T && !sametype(t, n->type)) {
               n1 = new1(OXXX, Z, Z);
               *n1 = *n;
               n->op = OCAST;
               n->left = n1;
               n->right = Z;
               n->type = t;
               n->addable = 0;
       }
       return 0;
}

int
tcomd(Node *n)
{
       Type *t;
       long o;

       o = 0;
       t = dotsearch(n->sym, n->left->type->link, n, &o);
       if(t == T) {
               diag(n, "not a member of struct/union: %F", n);
               return 1;
       }
       makedot(n, t, o);
       return 0;
}

int
tcomx(Node *n)
{
       Type *t;
       Node *l, *r, **ar, **al;
       int e;

       e = 0;
       if(n->type->etype != TSTRUCT) {
               diag(n, "constructor must be a structure");
               return 1;
       }
       l = invert(n->left);
       n->left = l;
       al = &n->left;
       for(t = n->type->link; t != T; t = t->down) {
               if(l == Z) {
                       diag(n, "constructor list too short");
                       return 1;
               }
               if(l->op == OLIST) {
                       r = l->left;
                       ar = &l->left;
                       al = &l->right;
                       l = l->right;
               } else {
                       r = l;
                       ar = al;
                       l = Z;
               }
               if(tcom(r))
                       e++;
               typeext(t, r);
               if(tcompat(n, t, r->type, tasign))
                       e++;
               constas(n, t, r->type);
               if(!e && !sametype(t, r->type)) {
                       r = new1(OCAST, r, Z);
                       r->type = t;
                       *ar = r;
               }
       }
       if(l != Z) {
               diag(n, "constructor list too long");
               return 1;
       }
       return e;
}

int
tlvalue(Node *n)
{

       if(!n->addable) {
               diag(n, "not an l-value");
               return 1;
       }
       return 0;
}

/*
* hoist comma operators out of expressions
*      (a,b) OP c => (a, b OP c)
*      OP(a,b) =>      (a, OP b)
*      a OP (b,c) => (b, a OP c)
*/

static Node*
comargs(Com *com, Node *n)
{
       if(n != Z && n->op == OLIST){
               n->left = comargs(com, n->left);
               n->right = comargs(com, n->right);
       }
       return commas(com, n);
}

static Node*
commas(Com *com, Node *n)
{
       Node *t;

       if(n == Z)
               return n;
       switch(n->op){
       case OREGISTER:
       case OINDREG:
       case OCONST:
       case ONAME:
       case OSTRING:
               /* leaf */
               return n;

       case OCOMMA:
               t = commas(com, n->left);
               if(com->n >= nelem(com->t))
                       fatal(n, "comma list overflow");
               com->t[com->n++] = t;
               return commas(com, n->right);

       case OFUNC:
               n->left = commas(com, n->left);
               n->right = comargs(com, n->right);
               return n;

       case OCOND:
               n->left = commas(com, n->left);
               comma(n->right->left);
               comma(n->right->right);
               return n;

       case OANDAND:
       case OOROR:
               n->left = commas(com, n->left);
               comma(n->right);
               return n;

       case ORETURN:
               comma(n->left);
               return n;
       }
       n->left = commas(com, n->left);
       if(n->right != Z)
               n->right = commas(com, n->right);
       return n;
}

static void
comma(Node *n)
{
       Com com;
       Node *nn;

       com.n = 0;
       nn = commas(&com, n);
       if(com.n > 0){
if(debug['y'])print("n=%d\n", com.n);
if(debug['y']) prtree(nn, "res");
               if(nn != n)
                       *n = *nn;
               while(com.n > 0){
if(debug['y']) prtree(com.t[com.n-1], "tree");
                       nn = new1(OXXX, Z, Z);
                       *nn = *n;
                       n->op = OCOMMA;
                       n->type = nn->type;
                       n->left = com.t[--com.n];
                       n->right = nn;
                       n->lineno = n->left->lineno;
               }
if(debug['y']) prtree(n, "final");
       }else if(n != nn)
               fatal(n, "odd tree");
}

/*
*      general rewrite
*      (IND(ADDR x)) ==> x
*      (ADDR(IND x)) ==> x
*      remove some zero operands
*      remove no op casts
*      evaluate constants
* Note: ccom may be called on the same node
* multiple times.
*/
void
ccom(Node *n)
{
       Node *l, *r;
       int t;

loop:
       if(n == Z)
               return;
       l = n->left;
       r = n->right;
       switch(n->op) {

       case OAS:
       case OASXOR:
       case OASAND:
       case OASOR:
       case OASMOD:
       case OASLMOD:
       case OASLSHR:
       case OASASHR:
       case OASASHL:
       case OASDIV:
       case OASLDIV:
       case OASMUL:
       case OASLMUL:
       case OASSUB:
       case OASADD:
               ccom(l);
               ccom(r);
               if(n->op == OASMOD || n->op == OASLMOD || n->op == OASDIV || n->op == OASLDIV)
               if(r->op == OCONST){
                       if(!typefd[r->type->etype] && r->vconst == 0) {
                               if(n->op == OASMOD || n->op == OASLMOD)
                                       diag(n, "modulo by zero");
                               else
                                       diag(n, "divide by zero");
                               r->vconst = ~0;
                       }
                       if(typefd[r->type->etype] && r->fconst == 0.) {
                               if(n->op == OASMOD || n->op == OASLMOD)
                                       diag(n, "modulo by zero");
                               else
                                       diag(n, "divide by zero");
                               r->fconst = 1e10;
                       }
               }
               if(n->op == OASLSHR || n->op == OASASHR || n->op == OASASHL)
               if(r->op == OCONST) {
                       t = n->type->width * 8; /* bits per byte */
                       if(r->vconst >= t || r->vconst < 0)
                               warn(n, "stupid shift: %lld", r->vconst);
               }
               break;

       case OCAST:
               if(n->type == types[TVOID] && !side(l)){
                       n->left = Z;
                       n->type = T;
               }
               if(n->left == Z)
                       break;
               if(castucom(n))
                       warn(n, "32-bit unsigned complement zero-extended to 64 bits");
               ccom(l);
               if(l->type == T){
                       n->left = Z;
                       n->type = T;
                       break;
               }
               if(l->op == OCONST) {
                       evconst(n);
                       if(n->op == OCONST)
                               break;
               }
               if(nocast(l->type, n->type) &&
                  (!typefd[l->type->etype] || typeu[l->type->etype] && typeu[n->type->etype])) {
                       l->type = n->type;
                       *n = *l;
               }
               break;

       case OCOND:
               ccom(l);
               ccom(r);
               if(l->op == OCONST)
                       if(vconst(l) == 0)
                               *n = *r->right;
                       else
                               *n = *r->left;
               break;

       case OREGISTER:
       case OINDREG:
       case OCONST:
       case ONAME:
               break;

       case OADDR:
               ccom(l);
               l->etype = TVOID;
               if(l->op == OIND) {
                       l->left->type = n->type;
                       *n = *l->left;
                       break;
               }
               goto common;

       case OIND:
               ccom(l);
               if(l->op == OADDR) {
                       l->left->type = n->type;
                       *n = *l->left;
                       break;
               }
               goto common;

       case OEQ:
       case ONE:

       case OLE:
       case OGE:
       case OLT:
       case OGT:

       case OLS:
       case OHS:
       case OLO:
       case OHI:
               ccom(l);
               ccom(r);
               if(compar(n, 0) || compar(n, 1))
                       break;
               relcon(l, r);
               relcon(r, l);
               goto common;

       case OASHR:
       case OASHL:
       case OLSHR:
               ccom(l);
               if(vconst(l) == 0 && !side(r)) {
                       *n = *l;
                       break;
               }
               ccom(r);
               if(vconst(r) == 0) {
                       *n = *l;
                       break;
               }
               if(r->op == OCONST) {
                       t = n->type->width * 8; /* bits per byte */
                       if(r->vconst >= t || r->vconst <= -t)
                               warn(n, "stupid shift: %lld", r->vconst);
               }
               goto common;

       case OMUL:
       case OLMUL:
               ccom(l);
               t = vconst(l);
               if(t == 0 && !side(r)) {
                       *n = *l;
                       break;
               }
               if(t == 1) {
                       *n = *r;
                       goto loop;
               }
               ccom(r);
               t = vconst(r);
               if(t == 0 && !side(l)) {
                       *n = *r;
                       break;
               }
               if(t == 1) {
                       *n = *l;
                       break;
               }
               goto common;

       case ODIV:
       case OLDIV:
               ccom(l);
               if(vconst(l) == 0 && !side(r)) {
                       *n = *l;
                       break;
               }
               ccom(r);
               t = vconst(r);
               if(t == 0) {
                       diag(n, "divide check");
                       *n = *r;
                       break;
               }
               if(t == 1) {
                       *n = *l;
                       break;
               }
               goto common;

       case OSUB:
               ccom(r);
               if(r->op == OCONST) {
                       if(typefd[r->type->etype]) {
                               n->op = OADD;
                               r->fconst = -r->fconst;
                               goto loop;
                       } else {
                               n->op = OADD;
                               r->vconst = -r->vconst;
                               goto loop;
                       }
               }
               ccom(l);
               goto common;

       case OXOR:
       case OOR:
       case OADD:
               ccom(l);
               if(vconst(l) == 0) {
                       *n = *r;
                       goto loop;
               }
               ccom(r);
               if(vconst(r) == 0) {
                       *n = *l;
                       break;
               }
               goto commute;

       case OAND:
               ccom(l);
               ccom(r);
               if(vconst(l) == 0 && !side(r)) {
                       *n = *l;
                       break;
               }
               if(vconst(r) == 0 && !side(l)) {
                       *n = *r;
                       break;
               }

       commute:
               /* look for commutative constant */
               if(r->op == OCONST) {
                       if(l->op == n->op) {
                               if(l->left->op == OCONST) {
                                       n->right = l->right;
                                       l->right = r;
                                       goto loop;
                               }
                               if(l->right->op == OCONST) {
                                       n->right = l->left;
                                       l->left = r;
                                       goto loop;
                               }
                       }
               }
               if(l->op == OCONST) {
                       if(r->op == n->op) {
                               if(r->left->op == OCONST) {
                                       n->left = r->right;
                                       r->right = l;
                                       goto loop;
                               }
                               if(r->right->op == OCONST) {
                                       n->left = r->left;
                                       r->left = l;
                                       goto loop;
                               }
                       }
               }
               goto common;

       case OANDAND:
               ccom(l);
               if(vconst(l) == 0) {
                       *n = *l;
                       break;
               }
               ccom(r);
               goto common;

       case OOROR:
               ccom(l);
               if(l->op == OCONST && l->vconst != 0) {
                       *n = *l;
                       n->vconst = 1;
                       break;
               }
               ccom(r);
               goto common;

       default:
               if(l != Z)
                       ccom(l);
               if(r != Z)
                       ccom(r);
       common:
               if(l != Z)
               if(l->op != OCONST)
                       break;
               if(r != Z)
               if(r->op != OCONST)
                       break;
               evconst(n);
       }
}

/*      OEQ, ONE, OLE, OLS, OLT, OLO, OGE, OHS, OGT, OHI */
static char *cmps[12] =
{
       "==", "!=", "<=", "<=", "<", "<", ">=", ">=", ">", ">",
};

/* 128-bit numbers */
typedef struct Big Big;
struct Big
{
       vlong a;
       uvlong b;
};
static int
cmp(Big x, Big y)
{
       if(x.a != y.a){
               if(x.a < y.a)
                       return -1;
               return 1;
       }
       if(x.b != y.b){
               if(x.b < y.b)
                       return -1;
               return 1;
       }
       return 0;
}
static Big
add(Big x, int y)
{
       uvlong ob;

       ob = x.b;
       x.b += y;
       if(y > 0 && x.b < ob)
               x.a++;
       if(y < 0 && x.b > ob)
               x.a--;
       return x;
}

Big
big(vlong a, uvlong b)
{
       Big x;

       x.a = a;
       x.b = b;
       return x;
}

int
compar(Node *n, int reverse)
{
       Big lo, hi, x;
       int op;
       char xbuf[40], cmpbuf[50];
       Node *l, *r;
       Type *lt, *rt;

       /*
        * The point of this function is to diagnose comparisons
        * that can never be true or that look misleading because
        * of the `usual arithmetic conversions'.  As an example
        * of the latter, if x is a ulong, then if(x <= -1) really means
        * if(x <= 0xFFFFFFFF), while if(x <= -1LL) really means
        * what it says (but 8c compiles it wrong anyway).
        */

       if(reverse){
               r = n->left;
               l = n->right;
               op = comrel[relindex(n->op)];
       }else{
               l = n->left;
               r = n->right;
               op = n->op;
       }

       /*
        * Skip over left casts to find out the original expression range.
        */
       while(l->op == OCAST){
               lt = l->type;
               rt = l->left->type;
               if(lt == T || rt == T)
                       return 0;
               if(lt->width < rt->width)
                       break;
               if(lt->width == rt->width && ((lt->etype ^ rt->etype) & 1) != 0)
                       break;
               l = l->left;
       }
       if(l->op == OCONST)
               return 0;
       lt = l->type;
       if(lt == T || lt->etype == TXXX || lt->etype > TUVLONG)
               return 0;

       /*
        * Skip over the right casts to find the on-screen value.
        */
       if(r->op != OCONST)
               return 0;
       while(r->oldop == OCAST && !r->xcast)
               r = r->left;
       rt = r->type;
       if(rt == T)
               return 0;

       x.b = r->vconst;
       x.a = 0;
       if((rt->etype&1) && r->vconst < 0)      /* signed negative */
               x.a = ~0ULL;

       if(lt->etype & 1){
               /* signed */
               lo = big(~0ULL, -(1LL<<(lt->width*8-1)));
               hi = big(0, (1LL<<(lt->width*8-1))-1);
       } else {
               /* unsigned */
               lo = big(0, 0);
               if(lt->width == 8)
                       hi = big(0, ~0ULL);
               else
                       hi = big(0, (1LL<<(lt->width*8))-1);
       }

       switch(op){
       case OLT:
       case OLO:
       case OGE:
       case OHS:
               if(cmp(x, lo) <= 0)
                       goto useless;
               if(cmp(x, add(hi, 1)) >= 0)
                       goto useless;
               break;
       case OLE:
       case OLS:
       case OGT:
       case OHI:
               if(cmp(x, add(lo, -1)) <= 0)
                       goto useless;
               if(cmp(x, hi) >= 0)
                       goto useless;
               break;
       case OEQ:
       case ONE:
               /*
                * Don't warn about comparisons if the expression
                * is as wide as the value: the compiler-supplied casts
                * will make both outcomes possible.
                */
               if(lt->width >= rt->width && debug['w'] < 2)
                       return 0;
               if(cmp(x, lo) < 0 || cmp(x, hi) > 0)
                       goto useless;
               break;
       }
       return 0;

useless:
       if((x.a==0 && x.b<=9) || (x.a==~0LL && x.b >= -9ULL))
               snprint(xbuf, sizeof xbuf, "%lld", x.b);
       else if(x.a == 0)
               snprint(xbuf, sizeof xbuf, "%#llux", x.b);
       else
               snprint(xbuf, sizeof xbuf, "%#llx", x.b);
       if(reverse)
               snprint(cmpbuf, sizeof cmpbuf, "%s %s %T",
                       xbuf, cmps[relindex(n->op)], lt);
       else
               snprint(cmpbuf, sizeof cmpbuf, "%T %s %s",
                       lt, cmps[relindex(n->op)], xbuf);
if(debug['y']) prtree(n, "strange");
       warn(n, "useless or misleading comparison: %s", cmpbuf);
       return 0;
}