utah rcsid 1.13 87/05/02 15:50:50: Add ZI feature -- generate result of
[unix-history] / usr / src / old / pcc / ccom.tahoe / local2.c
index a05f551..382b2b0 100644 (file)
@@ -1,5 +1,5 @@
 #ifndef lint
 #ifndef lint
-static char sccsid[] = "@(#)local2.c   1.11 (Berkeley) %G%";
+static char sccsid[] = "@(#)local2.c   1.22 (Berkeley) %G%";
 #endif
 
 # include "pass2.h"
 #endif
 
 # include "pass2.h"
@@ -196,70 +196,67 @@ zzzcode( p, c ) register NODE *p; {
                cbgen( p->in.op, p->bn.label, c );
                return;
 
                cbgen( p->in.op, p->bn.label, c );
                return;
 
-       case 'A':       /* assignment and load (integer only) */
+       case 'G':       /* i *= f; asgops with int lhs and float rhs */
                {
                {
-               register NODE *l, *r;
+               register NODE *l, *r, *s;
+               int lt, rt;
 
 
-               if (xdebug) eprint(p, 0, &val, &val);
-               r = getlr(p, 'R');
-               if (optype(p->in.op) == LTYPE || p->in.op == UNARY MUL) {
-                       l = resc;
-                       l->in.type = INT;
-               } else
-                       l = getlr(p, 'L');
-               if(r->in.type==FLOAT || r->in.type==DOUBLE
-                || l->in.type==FLOAT || l->in.type==DOUBLE)
-                       cerror("float in ZA");
-               if (r->in.op == ICON)
-                       if(r->in.name[0] == '\0') {
-                               if (r->tn.lval == 0) {
-                                       putstr("clr");
-                                       prtype(l);
-                                       putchar('\t');
-                                       adrput(l);
-                                       return;
-                               }
-                               if (r->tn.lval < 0 && r->tn.lval >= -63) {
-                                       putstr("mneg");
-                                       prtype(l);
-                                       r->tn.lval = -r->tn.lval;
-                                       goto ops;
-                               }
-#ifdef MOVAFASTER
-                       } else {
-                               putstr("movab\t");
-                               acon(r);
-                               putchar(',');
-                               adrput(l);
-                               return;
-#endif MOVAFASTER
-                       }
+               l = p->in.left;
+               r = p->in.right;
+               s = talloc();
+               rt = r->in.type;
+               lt = l->in.type;
+
+               if (lt != INT && lt != UNSIGNED) {
+                       s->in.op = SCONV;
+                       s->in.left = l;
+                       s->in.type = ISUNSIGNED(lt) ? UNSIGNED : INT;
+                       zzzcode(s, 'U');
+                       putstr("\n\t");
+               }
 
 
-               if (l->in.op == REG) {
-                       if( tlen(l) < tlen(r) ) {
-                               putstr(!ISUNSIGNED(l->in.type)?
-                                       "cvt": "movz");
-                               prtype(l);
-                               putchar('l');
-                               goto ops;
-                       } else
-                               l->in.type = INT;
+               if (ISUNSIGNED(lt)) {
+                       s->in.op = SCONV;
+                       s->in.left = lt == UNSIGNED ? l : resc;
+                       s->in.type = rt;
+                       unsigned_to_float(s);
+               } else {
+                       putstr("cvl");
+                       prtype(r);
+                       putchar('\t');
+                       adrput(lt == INT ? l : resc);
                }
                }
-               if (tlen(l) == tlen(r)) {
-                       putstr("mov");
-                       prtype(l);
-                       goto ops;
-               } else if (tlen(l) > tlen(r) && ISUNSIGNED(r->in.type))
-                       putstr("movz");
-               else
-                       putstr("cvt");
-               prtype(r);
-               prtype(l);
-       ops:
+               putstr("\n\t");
+
+               hopcode(rt == FLOAT ? 'F' : 'D', p->in.op);
                putchar('\t');
                adrput(r);
                putchar('\t');
                adrput(r);
-               putchar(',');
-               adrput(l);
+
+               if (ISUNSIGNED(lt)) {
+                       putstr("\n\t");
+                       s->in.op = SCONV;
+                       s->in.left = r;         /* we need only the type */
+                       s->in.type = UNSIGNED;
+                       float_to_unsigned(s);
+               } else {
+                       putstr("\n\tcv");
+                       prtype(r);
+                       putstr("l\t");
+                       if (lt == INT)
+                               adrput(l);
+                       else
+                               adrput(resc);
+               }
+               if (lt != INT) {
+                       putstr("\n\t");
+                       s->in.op = ASSIGN;
+                       s->in.left = l;
+                       s->in.right = resc;
+                       s->in.type = lt;
+                       zzzcode(s, 'U');
+               }
+
+               s->in.op = FREE;
                return;
                }
 
                return;
                }
 
@@ -295,7 +292,7 @@ zzzcode( p, c ) register NODE *p; {
                }
 
        case 'D':       /* INCR and DECR */
                }
 
        case 'D':       /* INCR and DECR */
-               zzzcode(p->in.left, 'A');
+               zzzcode(p->in.left, 'U');
                putstr("\n      ");
 
        case 'E':       /* INCR and DECR, FOREFF */
                putstr("\n      ");
 
        case 'E':       /* INCR and DECR, FOREFF */
@@ -319,6 +316,25 @@ zzzcode( p, c ) register NODE *p; {
                printf(ACONFMT, (p->in.right->tn.lval&((1<<fldsz)-1))<<fldshf);
                return;
 
                printf(ACONFMT, (p->in.right->tn.lval&((1<<fldsz)-1))<<fldshf);
                return;
 
+       case 'I':       /* produce value of bitfield assignment */
+                       /* avoid shifts -- shifts are SLOW on this machine */
+               {
+                       register NODE *r = p->in.right;
+                       if(r->in.op == ICON && r->tn.name[0] == '\0') {
+                               putstr("movl\t");
+                               printf(ACONFMT, r->tn.lval & ((1<<fldsz)-1));
+                               }
+                       else {
+                               putstr("andl3\t");
+                               printf(ACONFMT, (1 << fldsz) - 1);
+                               putchar(',');
+                               adrput(r);
+                               }
+                       putchar(',');
+                       adrput(resc);
+                       break;
+                       }
+
        case 'H':       /* opcode for shift */
                if(p->in.op == LS || p->in.op == ASG LS)
                        putstr("shll");
        case 'H':       /* opcode for shift */
                if(p->in.op == LS || p->in.op == ASG LS)
                        putstr("shll");
@@ -395,6 +411,30 @@ zzzcode( p, c ) register NODE *p; {
                sconv(p, c == 'V');
                break;
 
                sconv(p, c == 'V');
                break;
 
+       case 'W': {             /* SCONV or ASSIGN float/double => unsigned */
+               NODE *src = p->in.op == SCONV ? p->in.left : p->in.right;
+
+               putstr("ld");
+               prtype(src);
+               putchar('\t');
+               adrput(src);
+               putstr("\n\t");
+               float_to_unsigned(p);
+               break;
+       }
+
+       case 'Y':               /* SCONV or ASSIGN unsigned => float/double */
+               unsigned_to_float(p);   /* stores into accumulator */
+               putstr("\n\tst");
+               prtype(p);
+               putchar('\t');
+               if (p->in.op == SCONV)
+                       adrput(resc);
+               else
+                       adrput(p->in.left);
+               rtyflg = 1;
+               break;
+
        case 'Z':
                p = p->in.right;
                switch (p->in.type) {
        case 'Z':
                p = p->in.right;
                switch (p->in.type) {
@@ -579,115 +619,107 @@ upput(p, size)
        }
 }
 
        }
 }
 
-#ifdef old
 /*
 /*
- * Generate code for storage conversions.
+ * Convert a float or double in the accumulator into an unsigned int.
+ * Unlike the vax, the tahoe stores 0 into the destination
+ *     on a conversion of > 2 ** 31, so we compensate.
  */
  */
-sconv(p, forcc)
+float_to_unsigned(p)
        NODE *p;
 {
        NODE *p;
 {
-       register NODE *l, *r;
-       register wfrom, wto;
-       int oltype;
-
-       l = getlr(p, '1');
-       oltype = l->in.type, l->in.type = r->in.type;
-       r = getlr(p, 'L');
-       wfrom = tlen(r), wto = tlen(l);
-       if (wfrom == wto)               /* e.g. int -> unsigned */
-               goto done;
-       /*
-        * Conversion in registers requires care
-        * as cvt and movz instruction don't work
-        * as expected (they end up as plain mov's).
-        */
-       if (l->in.op == REG && r->in.op == REG) {
-               if ((wfrom < wto && ISUNSIGNED(r->in.type)) ||
-                   (wto < wfrom && ISUNSIGNED(l->in.type))) {
-                       /* unsigned, mask */
-                       if (r->tn.rval != l->tn.rval) {
-                               printf("\tandl3\t$%d,", (1<<(wto*SZCHAR))-1);
-                               adrput(r);
-                               putchar(',');
-                       } else
-                               printf("\tandl2\t$%d,", (1<<(wto*SZCHAR))-1);
-                       adrput(l);
-               } else {                                /* effect sign-extend */
-                       printf("\tpushl\t"); adrput(r);
-                       printf("\n\tcvt"); prtype(l);
-                       printf("l\t%d(sp),", sizeof (int) - wto); adrput(l);
-                       printf("\n\tmovab\t4(sp),sp");
-               }
-               /*
-                * If condition codes are required then we must generate a
-                * test of the appropriate type.
-                */
-               if (forcc) {
-                       printf("\n\tcmp");
-                       prtype(l);
-                       putchar('\t');
-                       printf("$0,");
-                       adrput(l);
-               }
+       register NODE *l = p->in.left;
+       int label1 = getlab();
+       int label2 = getlab();
+       int label3 = getlab();
+       NODE *src, *dst;
+
+       if (p->in.op == SCONV) {
+               src = p->in.left;
+               dst = resc;
        } else {
        } else {
-               /*
-                * Conversion with at least one parameter in memory.
-                */
-               if (wfrom < wto) {              /* expanding datum */
-                       if (ISUNSIGNED(r->in.type)) {
-                               printf("\tmovz");
-                               prtype(r);
-                               /*
-                                * If target is a register, generate
-                                * movz?l so optimizer can compress
-                                * argument pushes.
-                                */
-                               if (l->in.op == REG)
-                                       putchar('l');
-                               else
-                                       prtype(l);
-                       } else {
-                               printf("\tcvt");
-                               prtype(r), prtype(l);
-                       }
-                       putchar('\t');
-                       adrput(r);
-               } else {                        /* shrinking dataum */
-                       int off = wfrom - wto;
-                       if (l->in.op == REG) {
-                               printf("\tmovz");
-                               prtype(l);
-                               putchar('l');
-                       } else {
-                               printf("\tcvt");
-                               prtype(l), prtype(r);
-                       }
-                       putchar('\t');
-                       switch (r->in.op) {
-                       case NAME: case OREG:
-                               r->tn.lval += off;
-                               adrput(r);
-                               r->tn.lval -= off;
-                               break;
-                       case REG: case ICON: case UNARY MUL:
-                               adrput(r);
-                               break;
-                       default:
-                               cerror("sconv: bad shrink op");
-                               /*NOTREACHED*/
-                       }
-               }
-               putchar(',');
-               adrput(l);
+               src = p->in.right;
+               dst = p->in.left;
+       }
+
+       printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50000000", label1);
+       if (src->in.type == DOUBLE)
+               putstr(", 0x00000000 # .double");
+       else
+               putstr(" # .float");
+       putstr(" 2147483648\n\t.text\n\tcmp");
+       prtype(src);
+       printf("\tL%d\n\tjlss\tL%d\n\tsub", label1, label2);
+       prtype(src);
+       printf("\tL%d\n\tcv", label1);
+       prtype(src);
+       putstr("l\t");
+       adrput(dst);
+       putstr("\n\taddl2\t$-2147483648,");
+       adrput(dst);
+       printf("\n\tjbr\tL%d\nL%d:\n\tcv", label3, label2);
+       prtype(src);
+       putstr("l\t");
+       adrput(dst);
+       printf("\nL%d:", label3);
+}
+
+/*
+ * Convert an unsigned int into a float or double, leaving the result
+ *     in the accumulator.
+ */
+unsigned_to_float(p)
+       register NODE *p;
+{
+       int label1 = getlab();
+       int label2 = getlab();
+       NODE *src, *dst;
+
+       if (p->in.op == SCONV) {
+               src = p->in.left;
+               dst = resc;
+       } else {
+               src = p->in.right;
+               dst = p->in.left;
        }
        }
-       putchar('\n');
-done:
-       l->in.type = oltype;
+
+       printf(".data\n\t.align\t2\nL%d:\n\t.long\t0x50800000", label2);
+       if (p->in.type == DOUBLE)
+               putstr(", 0x00000000 # .double");
+       else
+               putstr(" # .float");
+       putstr(" 4294967296\n\t.text\n\tmovl\t");
+       adrput(src);
+       putchar(',');
+       adrput(dst);
+       putstr("\n\tcvl");
+       prtype(p);
+       putchar('\t');
+       adrput(dst);
+       printf("\n\tjgeq\tL%d\n\tadd", label1);
+       prtype(p);
+       printf("\tL%d\nL%d:", label2, label1);
 }
 }
-#else /* new */
+
+/*
+ * Prlen() is a cheap prtype()...
+ */
+static char convtab[SZINT/SZCHAR + 1] = {
+       '?', 'b', 'w', '?', 'l'
+};
+#define        prlen(len)      putchar(convtab[len])
+
+
 /*
  * Generate code for integral scalar conversions.
 /*
  * Generate code for integral scalar conversions.
- * Many work-arounds for brain-damaged Tahoe register behavior.
+ * Some of this code is designed to work around a tahoe misfeature
+ *     that causes sign- and zero- extension to be defeated in
+ *     certain circumstances.
+ * Basically if the source operand of a CVT or MOVZ instruction is
+ *     shorter than the destination, and the source is a register
+ *     or an immediate constant, sign- and zero- extension are
+ *     ignored and the high bits of the source are copied.  (Note
+ *     that zero-extension is not a problem for immediate
+ *     constants.)
  */
 sconv(p, forcc)
        NODE *p;
  */
 sconv(p, forcc)
        NODE *p;
@@ -698,28 +730,73 @@ sconv(p, forcc)
        register int srclen, dstlen;
        int srctype, dsttype;
        int val;
        register int srclen, dstlen;
        int srctype, dsttype;
        int val;
+       int neg = 0;
 
        if (p->in.op == ASSIGN) {
 
        if (p->in.op == ASSIGN) {
-               src = getlr(p, 'R');
-               dst = getlr(p, 'L');
+               src = p->in.right;
+               dst = p->in.left;
                dstlen = tlen(dst);
                dsttype = dst->in.type;
                dstlen = tlen(dst);
                dsttype = dst->in.type;
-       } else /* if (p->in.op == SCONV || optype(p->in.op) == LTYPE) */ {
-               src = getlr(p, 'L');
-               dst = getlr(p, '1');
+       } else if (p->in.op == SCONV) {
+               src = p->in.left;
+               dst = resc;
                dstlen = tlen(p);
                dsttype = p->in.type;
                dstlen = tlen(p);
                dsttype = p->in.type;
+       } else /* if (p->in.op == OPLEAF) */ {
+               src = p;
+               dst = resc;
+               dstlen = SZINT/SZCHAR;
+               dsttype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
        }
 
        }
 
-       srclen = tlen(src);
-       srctype = src->in.op == REG ?
-               ISUNSIGNED(src->in.type) ? UNSIGNED : INT :
-               src->in.type;
+       if (src->in.op == REG) {
+               srclen = SZINT/SZCHAR;
+               srctype = ISUNSIGNED(src->in.type) ? UNSIGNED : INT;
+       } else {
+               srclen = tlen(src);
+               srctype = src->in.type;
+       }
+
+       if (src->in.op == ICON && src->tn.name[0] == '\0') {
+               if (src->tn.lval == 0) {
+                       putstr("clr");
+                       prtype(dst);
+                       putchar('\t');
+                       adrput(dst);
+                       return;
+               }
+               if (dstlen < srclen) {
+                       switch (dsttype) {
+                       case CHAR:
+                               src->tn.lval = (char) src->tn.lval;
+                               break;
+                       case UCHAR:
+                               src->tn.lval = (unsigned char) src->tn.lval;
+                               break;
+                       case SHORT:
+                               src->tn.lval = (short) src->tn.lval;
+                               break;
+                       case USHORT:
+                               src->tn.lval = (unsigned short) src->tn.lval;
+                               break;
+                       }
+               }
+               if (dst->in.op == REG) {
+                       dsttype = INT;
+                       dstlen = SZINT/SZCHAR;
+               }
+               srctype = dsttype;
+               srclen = dstlen;
+               if ((val = src->tn.lval) & 1 << dstlen * SZCHAR - 1) {
+                       src->tn.lval = -(val | ~((1 << dstlen * SZCHAR) - 1));
+                       ++neg;          /* MNEGx may be shorter */
+               }
+       }
 
        if (srclen < dstlen) {
                if (srctype == CHAR && dsttype == USHORT && dst->in.op == REG) {
                        /* (unsigned short) c; => sign extend to 16 bits */
 
        if (srclen < dstlen) {
                if (srctype == CHAR && dsttype == USHORT && dst->in.op == REG) {
                        /* (unsigned short) c; => sign extend to 16 bits */
-                       putstr("\tcvtbl\t");
+                       putstr("cvtbl\t");
                        adrput(src);
                        putstr(",-(sp)\n\tmovzwl\t2(sp),");
                        adrput(dst);
                        adrput(src);
                        putstr(",-(sp)\n\tmovzwl\t2(sp),");
                        adrput(dst);
@@ -738,29 +815,39 @@ sconv(p, forcc)
        }
 
        if (srclen > dstlen && dst->in.op == REG) {
        }
 
        if (srclen > dstlen && dst->in.op == REG) {
+               /* if dst is a register, the result must look like an int */
                if (src->in.op == REG) {
                        if (ISUNSIGNED(dsttype)) {
                                val = (1 << dstlen * SZCHAR) - 1;
                                if (src->tn.rval == dst->tn.rval)
                                        /* conversion in place */
                if (src->in.op == REG) {
                        if (ISUNSIGNED(dsttype)) {
                                val = (1 << dstlen * SZCHAR) - 1;
                                if (src->tn.rval == dst->tn.rval)
                                        /* conversion in place */
-                                       printf("\tandl2\t$%#x,", val);
+                                       printf("andl2\t$%#x,", val);
                                else {
                                else {
-                                       printf("\tandl3\t$%#x,", val);
+                                       printf("andl3\t$%#x,", val);
                                        adrput(src);
                                        putchar(',');
                                }
                                adrput(dst);
                                return;
                        }
                                        adrput(src);
                                        putchar(',');
                                }
                                adrput(dst);
                                return;
                        }
-                       val = SZINT - srclen * SZCHAR;
-                       printf("\tshll\t$%d,", val);
+                       /*
+                        * Sign extension in register can also be
+                        * accomplished by shifts, but unfortunately
+                        * shifts are extremely slow, due to the lack
+                        * of a barrel shifter.
+                        */
+                       putstr("pushl\t");
                        adrput(src);
                        adrput(src);
-                       putchar(',');
-                       adrput(dst);
-                       printf("\n\tshar\t$%d,", val);
-                       adrput(dst);
-                       putchar(',');
+                       putstr("\n\tcvt");
+                       prlen(dstlen);
+                       printf("l\t%d(sp),", SZINT/SZCHAR - dstlen);
                        adrput(dst);
                        adrput(dst);
+                       putstr("\n\tmovab\t4(sp),sp");
+                       if (forcc) {
+                               /* inverted test */
+                               putstr("\n\tcmpl\t$0,");
+                               adrput(dst);
+                       }
                        return;
                }
                tmp = talloc();
                        return;
                }
                tmp = talloc();
@@ -774,11 +861,13 @@ sconv(p, forcc)
                } else {
                        /* we must store src's address */
                        *tmp = *dst;
                } else {
                        /* we must store src's address */
                        *tmp = *dst;
-                       putstr("\tmovab\t");
+                       putstr("mova");
+                       prlen(srclen);
+                       putchar('\t');
                        adrput(src);
                        putchar(',');
                        adrput(tmp);
                        adrput(src);
                        putchar(',');
                        adrput(tmp);
-                       putchar('\n');
+                       putstr("\n\t");
                        tmp->tn.op = OREG;
                        tmp->tn.lval = srclen - dstlen;
                }
                        tmp->tn.op = OREG;
                        tmp->tn.lval = srclen - dstlen;
                }
@@ -787,34 +876,32 @@ sconv(p, forcc)
                return;
        }
 
                return;
        }
 
-       genconv(ISUNSIGNED(dsttype),
+       genconv(neg ? -1 : ISUNSIGNED(dsttype),
                srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
                src, dst);
 }
 
                srclen, dst->in.op == REG ? SZINT/SZCHAR : dstlen,
                src, dst);
 }
 
-genconv(usrc, srclen, dstlen, src, dst)
-       int usrc, srclen, dstlen;
+genconv(srcflag, srclen, dstlen, src, dst)
+       int srcflag;
+       register int srclen, dstlen;
        NODE *src, *dst;
 {
        NODE *src, *dst;
 {
-       static char convtab[SZINT/SZCHAR + 1] = {
-               '?', 'b', 'w', '?', 'l'
-       };
-
        if (srclen != dstlen) {
        if (srclen != dstlen) {
-               if (usrc && srclen < dstlen)
-                       putstr("\tmovz");
+               if (srcflag > 0 && srclen < dstlen)
+                       putstr("movz");
                else
                else
-                       putstr("\tcvt");
-               putchar(convtab[srclen]);
-       } else
-               putstr("\tmov");
-       putchar(convtab[dstlen]);
+                       putstr("cvt");
+               prlen(srclen);
+       } else if (srcflag < 0)
+               putstr("mneg");
+       else
+               putstr("mov");
+       prlen(dstlen);
        putchar('\t');
        adrput(src);
        putchar(',');
        adrput(dst);
 }
        putchar('\t');
        adrput(src);
        putchar(',');
        adrput(dst);
 }
-#endif /* new */
 
 rmove( rt, rs, t ) TWORD t;{
        printf( "       movl    %s,%s\n", rname(rs), rname(rt) );
 
 rmove( rt, rs, t ) TWORD t;{
        printf( "       movl    %s,%s\n", rname(rs), rname(rt) );
@@ -1235,9 +1322,23 @@ optim2( p ) register NODE *p; {
 
        case SCONV:
                l = p->in.left;
 
        case SCONV:
                l = p->in.left;
-               /* clobber conversions w/o side effects */
-               if (!anyfloat(p, l) && l->in.op != PCONV &&
+               if (anyfloat(p, l)) {
+                       /* save some labor later */
+                       NODE *t = talloc();
+
+                       if (p->in.type == UCHAR || p->in.type == USHORT) {
+                               *t = *p;
+                               t->in.type = UNSIGNED;
+                               p->in.left = t;
+                       } else if (l->in.type == UCHAR || l->in.type == USHORT) {
+                               *t = *p;
+                               t->in.type = INT;
+                               p->in.left = t;
+                       }
+               } else if (l->in.op != PCONV &&
+                   l->in.op != CALL && l->in.op != UNARY CALL &&
                    tlen(p) == tlen(l)) {
                    tlen(p) == tlen(l)) {
+                       /* clobber conversions w/o side effects */
                        if (l->in.op != FLD)
                                l->in.type = p->in.type;
                        ncopy(p, l);
                        if (l->in.op != FLD)
                                l->in.type = p->in.type;
                        ncopy(p, l);
@@ -1250,8 +1351,26 @@ optim2( p ) register NODE *p; {
                 * Try to zap storage conversions of non-float items.
                 */
                r = p->in.right;
                 * Try to zap storage conversions of non-float items.
                 */
                r = p->in.right;
-               if (r->in.op == SCONV && !anyfloat(r->in.left, r)) {
+               if (r->in.op == SCONV) {
                        int wdest, wconv, wsrc;
                        int wdest, wconv, wsrc;
+
+                       if (p->in.left->in.op == FLD)
+                               return;
+                       if (anyfloat(r, r->in.left)) {
+                               /* let the code table handle two cases */
+                               if (p->in.left->in.type == UNSIGNED && 
+                                          r->in.type == UNSIGNED) {
+                                       p->in.right = r->in.left;
+                                       r->in.op = FREE;
+                               } else if ((p->in.left->in.type == FLOAT ||
+                                           p->in.left->in.type == DOUBLE) &&
+                                          p->in.left->in.type == r->in.type &&
+                                          r->in.left->in.type == UNSIGNED) {
+                                       p->in.right = r->in.left;
+                                       r->in.op = FREE;
+                               }
+                               return;
+                       }
                        wdest = tlen(p->in.left);
                        wconv = tlen(r);
                        /*
                        wdest = tlen(p->in.left);
                        wconv = tlen(r);
                        /*
@@ -1273,82 +1392,112 @@ optim2( p ) register NODE *p; {
 
 struct functbl {
        int fop;
 
 struct functbl {
        int fop;
+       TWORD ftype;
        char *func;
        char *func;
-} opfunc[] = {
-       DIV,            "udiv", 
-       ASG DIV,        "udiv", 
-       0
-};
+       } opfunc[] = {
+       DIV,            TANY,   "udiv",
+       MOD,            TANY,   "urem",
+       ASG DIV,        TANY,   "audiv",
+       ASG MOD,        TANY,   "aurem",
+       0,      0,      0 };
 
 hardops(p)  register NODE *p; {
        /* change hard to do operators into function calls.  */
        register NODE *q;
        register struct functbl *f;
 
 hardops(p)  register NODE *p; {
        /* change hard to do operators into function calls.  */
        register NODE *q;
        register struct functbl *f;
-       register int o;
-       register TWORD t, t1, t2;
+       register o;
+       NODE *old,*temp;
 
        o = p->in.op;
 
        o = p->in.op;
+       if( ! (optype(o)==BITYPE &&
+              (ISUNSIGNED(p->in.left->in.type) ||
+               ISUNSIGNED(p->in.right->in.type))) )
+               return;
 
        for( f=opfunc; f->fop; f++ ) {
                if( o==f->fop ) goto convert;
 
        for( f=opfunc; f->fop; f++ ) {
                if( o==f->fop ) goto convert;
-       }
+               }
        return;
 
        convert:
        return;
 
        convert:
-       t = p->in.type;
-       t1 = p->in.left->in.type;
-       t2 = p->in.right->in.type;
-
-       if (!((ISUNSIGNED(t1) && !(ISUNSIGNED(t2))) || 
-            ( t2 == UNSIGNED))) return;
-
-       /* need to rewrite tree for ASG OP */
-       /* must change ASG OP to a simple OP */
        if( asgop( o ) ) {
        if( asgop( o ) ) {
-               q = talloc();
-               q->in.op = NOASG ( o );
-               q->in.rall = NOPREF;
-               q->in.type = p->in.type;
-               q->in.left = tcopy(p->in.left);
-               q->in.right = p->in.right;
-               p->in.op = ASSIGN;
-               p->in.right = q;
-               zappost(q->in.left); /* remove post-INCR(DECR) from new node */
-               fixpre(q->in.left);     /* change pre-INCR(DECR) to +/- */
-               p = q;
+               old = NIL;
+               switch( p->in.left->in.op ){
+               case FLD:
+                       q = p->in.left->in.left;
+                       /*
+                        * rewrite (lval.fld /= rval); as
+                        *  ((*temp).fld = udiv((*(temp = &lval)).fld,rval));
+                        * else the compiler will evaluate lval twice.
+                        */
+                       if( q->in.op == UNARY MUL ){
+                               /* first allocate a temp storage */
+                               temp = talloc();
+                               temp->in.op = OREG;
+                               temp->tn.rval = TMPREG;
+                               temp->tn.lval = BITOOR(freetemp(1));
+                               temp->in.type = INCREF(p->in.type);
+#ifdef FLEXNAMES
+                               temp->in.name = "";
+#else
+                               temp->in.name[0] = '\0';
+#endif
+                               old = q->in.left;
+                               q->in.left = temp;
+                       }
+                       /* fall thru ... */
 
 
-       }
-       /* turn logicals to compare 0 */
-       else if( logop( o ) ) {
-               ncopy(q = talloc(), p);
-               p->in.left = q;
-               p->in.right = q = talloc();
-               q->in.op = ICON;
-               q->in.type = INT;
-#ifndef FLEXNAMES
-               q->in.name[0] = '\0';
+               case REG:
+               case NAME:
+               case OREG:
+                       /* change ASG OP to a simple OP */
+                       q = talloc();
+                       q->in.op = NOASG p->in.op;
+                       q->in.rall = NOPREF;
+                       q->in.type = p->in.type;
+                       q->in.left = tcopy(p->in.left);
+                       q->in.right = p->in.right;
+                       p->in.op = ASSIGN;
+                       p->in.right = q;
+                       p = q;
+                       f -= 2; /* Note: this depends on the table order */
+                       /* on the right side only - replace *temp with
+                        *(temp = &lval), build the assignment node */
+                       if( old ){
+                               temp = q->in.left->in.left; /* the "*" node */
+                               q = talloc();
+                               q->in.op = ASSIGN;
+                               q->in.left = temp->in.left;
+                               q->in.right = old;
+                               q->in.type = old->in.type;
+#ifdef FLEXNAMES
+                               q->in.name = "";
 #else
 #else
-               q->in.name = "";
+                               q->in.name[0] = '\0';
 #endif
 #endif
-               q->tn.lval = 0;
-               q->tn.rval = 0;
-               p = p->in.left;
-       }
+                               temp->in.left = q;
+                       }
+                       break;
 
 
-       /* build comma op for args to function */
-       t1 = p->in.left->in.type;
-       t2 = 0;
-       if ( optype(p->in.op) == BITYPE) {
-               q = talloc();
-               q->in.op = CM;
-               q->in.rall = NOPREF;
-               q->in.type = INT;
-               q->in.left = p->in.left;
-               q->in.right = p->in.right;
-               t2 = p->in.right->in.type;
-       } else
-               q = p->in.left;
+               case UNARY MUL:
+                       /* avoid doing side effects twice */
+                       q = p->in.left;
+                       p->in.left = q->in.left;
+                       q->in.op = FREE;
+                       break;
 
 
+               default:
+                       cerror( "hardops: can't compute & LHS" );
+                       }
+               }
+
+       /* build comma op for args to function */
+       q = talloc();
+       q->in.op = CM;
+       q->in.rall = NOPREF;
+       q->in.type = INT;
+       q->in.left = p->in.left;
+       q->in.right = p->in.right;
        p->in.op = CALL;
        p->in.right = q;
 
        p->in.op = CALL;
        p->in.right = q;
 
@@ -1358,9 +1507,9 @@ hardops(p)  register NODE *p; {
        q->in.rall = NOPREF;
        q->in.type = INCREF( FTN + p->in.type );
 #ifndef FLEXNAMES
        q->in.rall = NOPREF;
        q->in.type = INCREF( FTN + p->in.type );
 #ifndef FLEXNAMES
-               strcpy( q->in.name, f->func );
+       strcpy( q->in.name, f->func );
 #else
 #else
-               q->in.name = f->func;
+       q->in.name = f->func;
 #endif
        q->tn.lval = 0;
        q->tn.rval = 0;
 #endif
        q->tn.lval = 0;
        q->tn.rval = 0;