V8/usr/src/cmd/ccom/vax/gencode.c

Compare this file to the similar file:
Show the results in this format:

#include "gencode.h"
#define fieldbotch(p) if(p->in.left->in.op == FLD) {rewritefld(p); longjmp(back, 1);}
jmp_buf back;
int acnt, Pflag, bbcnt;

gencode(p)
NODE *p;
{	NODE *q;
	ret s;
	int svtemp, svregvar, i, svbb;
	extern int bothdebug;
	svtemp = tmpoff;
	svregvar = regvar;
	svbb = ++bbcnt;
	if(setjmp(back)) {
		pr("#\treg\t%d\n", ++acnt)/*, prtree(q), putchar('\n')*/;
		if(acnt > 20) {
			prtree(q);
			outpr();
			tmpoff = svtemp;
			bbcnt = svbb;
			uerror("expression too complicated");
			return;
		}
	}
	else
		q = copytree(p);
	buf = bufs[1];
	prptr = prbuf;
	if(Pflag && q->in.op != INIT) {
		pr("#\tincl\tlocprof+%d\n", 4*(svbb+3));
	}
	/*printx("#%d ", acnt), prtree(q), printx("\n"), outpr();*/
	s = doit(q, 0, 0, REGMASK);
	if(s.flag & FAIL)
		uerror("codegen failed at top level");
	acnt = ntree = 0;
	if(Pflag && q->in.op != INIT)
		printx("#%d ", svbb), prtree(q), printx("\n");
	outpr();
	tmpoff = svtemp;
	regvar = svregvar;
	if(M32 && !bothdebug)
		for(i = 0; i < NRGS; i++)
			busy[i] = 0;
}

ret
doit(p, flag, dest, regmask)
NODE *p;
ret dest;
{	ret s, t, x, y;
	char *pp;
	NODE snode, *q;
	int i, j, svmask = regmask;
	switch(p->in.op) {
	default:
		pr("#\tweird??? %d\n", p->in.op);
		return(dest);
	case ASG AND:
		fieldbotch(p)
		if(dest.ans && p->in.left->in.op == STAR)
			longjmp(back, mediumstar(p));
		flag |= DESTISLEFT;
		flag &= ~CC;
	case AND:
		if((flag & CC)) {
			if(p->in.left->in.op == CONV && incrsize(p->in.left->in.left) < 4)
				p->in.left = p->in.left->in.left;
			if(p->in.right->in.op == CONV
				&& incrsize(p->in.right->in.left) < 4)
				p->in.right = p->in.right->in.left;
		}
		t = doit(p->in.left, VALUE|USED, 0, regmask);
		if(t.flag & FAIL)
			return(t);
		regmask &= ~t.regmask;
		s = doit(p->in.right, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			goto binfail;
		if(flag & CC) {
			if(incrsize(p->in.left) <= incrsize(p->in.right))
				i = childtype(p);
			else
				i = type(p->in.right);
			pr("#\tbit%c\t%s,%s\n", i, str(s), str(t));
			dest.ans = 0;
			dest.flag = CC;
			dest.regmask = 0;
			return(dest);
		}
		regmask &= ~s.regmask;
#if VAX==1
		/* p->in.right->in.op == COMPL is a useful special case */
		if((flag & DESTISLEFT) && dest.ans == 0)
			dest = x = t;
		if(dest.ans == 0)
			if(s.flag & SCRATCH)
				dest = x = s;
			else if(t.flag & SCRATCH) {
				dest = t;
				x = allocreg(p, regmask);
			}
			else
				dest = x = allocreg(p, regmask);
		else
			x = allocreg(p, regmask);
		if(p->in.right->in.op == ICON) {
			x = tostack();	/* to get a buf */
			sprintx(str(x), "$%d", -p->in.right->tn.lval - 1);
		}
		else
			pr("#\tmcom%c\t%s,%s\n", childtype(p), str(s), str(x));
		if(strcmp(str(t), str(dest)) == 0)
			pr("#\tbic%c2\t%s,%s\n", childtype(p), str(x), str(dest));
		else if(flag & DESTISLEFT) {
			pr("#\tbic%c2\t%s,%s\n", childtype(p), str(x), str(t));
			if(strcmp(str(t), str(dest))) {
				x = t;
				goto movexdest;
			}
		}
		else
			pr("#\tbic%c3\t%s,%s,%s\n", childtype(p), str(x),
				str(t), str(dest));
		dest.flag |= CC;
		return(dest);
#endif
	case CALL:
call:
		s.flag = funargs(p->in.right, regmask);
		if(s.flag & FAIL)
			return(s);
		i = p->stn.argsize/32;
called:
		s = doit(p->in.left, VALUE|ASADDR|USED, 0, regmask);
		pp = str(s);
aftercall:
		if(svmask != REGMASK) {
			s.flag = FAIL;
			return(s);
		}
		x = specialreg(p, regmask);
		if(M32 && (p->in.op == STCALL || p->in.op == UNARY STCALL))
			pr("#\tmovaw\t%d(%%fp),%%r2\n", gimmetemp(p->stn.stsize/SZINT));
		pr(VAX? "#\tcalls\t$%d,%s\n": "#\tcall\t&%d,%s\n", i, pp);
		if(flag & ASADDR) {
			strcat(str(x), ")");
			strshift(str(x), 1);
			str(x)[0] = '(';
		}
		if(dest.ans == 0 && (VAX || !(flag & TOSTACK)))
			if(flag & DESTISLEFT)
				dest = doit(p->in.left->in.op == CONV?
					p->in.left->in.left:
					p->in.left, 0/* ? */, 0, regmask & ~x.regmask);
			else
				return(x);
movexdest:	/* type(p), not childtype, for a = a % b */
		if(strcmp(str(x), str(dest)) == 0)
			return(x);
		if((flag & TOSTACK) && incrsize(p) == 4)
			pr("#\tpush%c\t%s\n", type(p), str(x));
		else if(x.flag & ICON0)
			pr("#\tclr%c\t%s\n", type(p), str(dest));
		else if(VAX && isfloat(p) != isfloat(p->in.left)
			&& p->in.op != CALL && p->in.op != UNARY CALL)
			pr("#\tcvt%c%c\t%s,%s\n", childtype(p), type(p), str(x), str(dest));
		else if(VAX && (flag & TOSTACK) && (incrsize(p) != 8))
			pr("#\tcvt%cl\t%s,-(sp)\n", type(p), str(x));
		else {
			pr("#\tmov%c\t%s,%s\n", type(p), str(x), str(dest));
			dest = simpler(x, dest);
		}
		dest.flag |= CC;
		return(dest);
	case CMP:
		if(p->in.left->in.op == CONV && p->in.right->in.op == CONV
			&& childtype(p->in.left) == childtype(p->in.right)) {
			p->in.left = p->in.left->in.left;
			p->in.right = p->in.right->in.left;
		}
		else if(p->in.left->in.op == CONV && p->in.right->in.op == ICON
			&& p->in.right->tn.lval >= 0
			&& ((incrsize(p->in.left->in.left) == 1
				&& p->in.right->tn.lval < 128)
			|| (incrsize(p->in.left->in.left) == 2
				&& p->in.right->tn.lval < 32768)))
			p->in.left = p->in.left->in.left;
		/* the above rewriting depends on childype being of p->left */
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		t = doit(p->in.right, VALUE|USED, 0, regmask);
		if(t.flag & FAIL) {
			totemp(p, LEFT);
			longjmp(back, 1);
		}
		pr("#\tcmp%c\t%s,%s\n", childtype(p), str(s), str(t));
		dest.ans = dest.regmask = 0;
		dest.flag = CC;
		return(dest);
	case COMOP:	/* qnodes lurking underneath */
		if(p->in.left->in.op == GENLAB) {
			if(dest.ans == 0) {
				dest = allocreg(p, regmask);
			}
			t = doit(p->in.left, VALUE|(flag & CC), dest, regmask);
		}
		else
			t = doit(p->in.left, 0, 0, regmask);
		if(t.flag & FAIL)
			return(t);
		s = doit(p->in.right, VALUE|(flag & (USED|CC)), dest, regmask);
		if(s.flag & FAIL)
			return(s);
		if(flag & ASADDR) {
			dest.ans = 0;
			i = (s.flag & (ISREG|CANINDIR|SCRATCH));
			j = s.regmask;
			buf = str(s);
			goto convbuf;
		}
		return(s);
	case COMPL:
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		if(dest.ans == 0)
			if(s.flag & SCRATCH)
				dest = s;
			else
				dest = allocreg(p, regmask & ~s.regmask);
		pr("#\tmcom%c\t%s,%s\n", childtype(p), str(s), str(dest));
		dest.flag |= CC;
		return(dest);
	case CONV:
		if(p->in.left->in.op == ASSIGN && p->in.left->in.left->in.op == STAR) {
			if(dest.ans == 0) {
				dest = allocreg(p, regmask);
				s = doit(p->in.left, VALUE|USED, dest, regmask);
			}
			else {
				x = allocreg(p, regmask);
				s = doit(p->in.left, VALUE|USED, x, regmask & ~x.regmask);
			}
			if(s.flag & FAIL) {
				asgwrite(p->in.left);
				longjmp(back, 1);
			}
		}
		else
			s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(childtype(p) == type(p) && dest.ans == 0)
			return(s);
		if(s.flag & FAIL)
			return(s);
		if(dest.ans == 0)
			if(s.flag & SCRATCH)
				dest = checksize(p, s, regmask);
			else
				dest = allocreg(p, regmask);
		if(isunsigned(p->in.left) && incrsize(p) > incrsize(p->in.left)) {
			if(type(p) != 'f' && type(p) != 'd')
				pp = "movz";
			else {	/* uns to float or double */
				rewriteconv(p);
				longjmp(back, 1);
			}
		}
		else
			pp = VAX? "cvt": "movb";
		if(isfloat(p) != isfloat(p->in.left))
			goto cvtop;
		if(incrsize(p) < incrsize(p->in.left)) {
			if(isfloat(p))
				goto cvtop;
			pr("#\tmov%c\t%s,%s\n", type(p), str(s), str(dest));
			dest = simpler(s, dest);
		}
		else if(incrsize(p) > incrsize(p->in.left))
cvtop:
			pr("#\t%s%c%c\t%s,%s\n", pp, childtype(p), type(p), str(s), str(dest));
		else {	/* types were the same, but dest.ans != 0 */
			pr("#\tmov%c\t%s,%s\n", type(p), str(s), str(dest));
			dest = simpler(s, dest);
		}
		if(M32 && (flag & TOSTACK))
			pr("#\tpushw\t%s\n", str(dest));
		dest.flag |= CC;
		return(dest);
	case DECR:
		i = -1;
		pp = "sub";
incrop:
		if(p->in.type == TFLOAT)
			uerror("no float ++/--");
		fieldbotch(p)
		/* if the dest uses regs, it may be from a qnode, so that reg
		 * shouldn't be used (i?*a->b++:x()) where b has offset > 0
		 */
		s = doit(p->in.left, USED, 0, dest.ans? regmask & ~dest.regmask: regmask);
		if(s.flag & FAIL)
			return(s);
		t = doit(p->in.right, 0, 0, 0);
		if(flag & VALUE) {
			if(dest.ans == 0)
				dest = allocreg(p, regmask & ~s.regmask);
			pr("#\tmov%c\t%s,%s\n", childtype(p), str(s), str(dest));
			if(t.flag & ICON1) {
				if(i == 1)
					pp = "inc";
				else
					pp = "dec";
				pr("#\t%s%c\t%s\n", pp, childtype(p), str(s));
			}
			else
				pr("#\t%s%c2\t%s,%s\n", pp, childtype(p),
					str(t), str(s));
			if(flag & ASADDR) {
				if(dest.flag & ISREG) {
					sprintx(buf, "0(%s)", str(dest));
					done(dest, CANINDIR, dest.regmask);
				}
				if(dest.flag & CANINDIR) {
					sprintx(buf, "*%s", str(dest));
					done(dest, 0, dest.regmask);
				}
				uerror("weird asaddr in incrop");
			}
			dest.flag &= ~CC;
			return(dest);
		}
		if(t.flag & ICON1) {
			if(i == 1)
				pp = "inc";
			else
				pp = "dec";
			pr("#\t%s%c\t%s\n", pp, childtype(p), str(s));
		}
		else
			pr("#\t%s%c2\t%s,%s\n", pp, childtype(p), str(t), str(s));
		if(dest.ans) {
			x = s;
			goto movexdest;
		}
		s.flag &= ~CC;
		return(s);
	case ASG DIV:
		fieldbotch(p)
		if(rewriteasgop(p))
			goto assign;
		flag |= DESTISLEFT;
	case DIV:
		/* trees wrong: uns/=int and int/=uns */
		if(!isunsigned(p->in.right) && !isunsigned(p->in.left)) {
			pp = "div";
			goto binop;
		}
		pp = "udiv";
#if M32==1
		goto binop;
#else if VAX==1
unsdiv:
		if(incrsize(p->in.right) != 4) {
			snode.in.op = CONV;
			snode.in.left = p->in.right;
			snode.in.type = TULONG;
			s = doit(&snode, VALUE|TOSTACK, tostack(), regmask);
		}
		else
			s = doit(p->in.right, TOSTACK, tostack(), regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		if(incrsize(p->in.left) != 4) {		
			snode.in.op = CONV;
			snode.in.left = p->in.left;
			snode.in.type = TULONG;
			t = doit(&snode, VALUE|TOSTACK, tostack(), regmask);
		}
		else
			t = doit(p->in.left, TOSTACK, tostack(), regmask);
		if(t.flag & FAIL) {
			if(dest.ans || svmask != REGMASK) {
				t = doit(p->in.left, VALUE|TOSTACK, tostack(), svmask);
				if(t.flag & FAIL)
					return(t);
			}
			totemp(p, RIGHT);
			longjmp(back, 1);
		}
		i = 2;
		goto aftercall;
#endif
	case ASG ER:
		fieldbotch(p)
		flag |= DESTISLEFT;
	case ER:
		pp = "xor";
binop:
		if((flag & DESTISLEFT) && dest.ans && p->in.left->in.op == STAR)
			longjmp(back, mediumstar(p));
		t = doit(p->in.left, VALUE|USED, 0, regmask);
		if(t.flag & FAIL) {
			return(t);
		}
		regmask &= ~t.regmask;
		s = doit(p->in.right, VALUE|USED, 0, regmask);
		if(s.flag & FAIL) {
binfail:
			if(dest.ans || svmask != REGMASK) {
				s = doit(p->in.right, VALUE|USED, 0, svmask);
				if(s.flag & FAIL)
					return(s);
			}
			/* *foo op= expr  requires care */
			if((flag & DESTISLEFT) && p->in.left->in.op == STAR)
				totemp(p->in.left, LEFT);
			else
				totemp(p, LEFT);
			longjmp(back, 1);
		}
		if(type(p) != childtype(p)) {
			x = allocreg(p, regmask);
			pr("#\t%s%c3\t%s,%s,%s\n", pp, childtype(p), str(s),
				str(t), str(x));
			if(dest.ans == 0)
				dest = x;
			if(!isfloat(p) && !isfloat(p->in.left)
				&& incrsize(p) < incrsize(p->in.left)) {
				pr("#\tmov%c\t%s,%s\n", type(p), str(x), str(dest));
				dest = simpler(x, dest);
			}
			else
				pr("#\tcvt%c%c\t%s,%s\n", childtype(p), type(p),
					str(x), str(dest));
			dest.flag |= CC;
			return(dest);
		}	
		if(dest.ans == 0)
			if((t.flag & SCRATCH) || (flag & DESTISLEFT)) {
twoop:
				dest = t;
				if(*pp == 'a' && (s.flag & ICON1))
					pr("#\tinc%c\t%s\n", childtype(p), str(t));
				else if(*pp == 's' && (s.flag & ICON1))
					pr("#\tdec%c\t%s\n", childtype(p), str(t));
				else
					pr("#\t%s%c2\t%s,%s\n", pp, childtype(p),
						str(s), str(t));
				if(flag & ASADDR)
					goto binopaddr;
				if(flag & TOSTACK)
					pr("#\tpush%c\t%s\n", type(p), str(t));
				dest.flag |= CC;
				return(dest);
			}
			else if(s.flag & SCRATCH)
				dest = s;
			else
				dest = allocreg(p, regmask);
		if(dest.ans && (flag & DESTISLEFT)) {
			pr("#\t%s%c2\t%s,%s\n", pp, childtype(p), str(s), str(t));
			x = t;
			goto movexdest;
		}
		if((dope[p->in.op] & (COMMFLG|MULFLG)) && strcmp(str(s), str(dest)) == 0) {
			dest = s;
			s = t;
			t = dest;
			goto twoop;
		}
		if(strcmp(str(t), str(dest)) == 0)
			goto twoop;
		pr("#\t%s%c3\t%s,%s,%s\n", pp, childtype(p), str(s), str(t), str(dest));
		if(M32 && (flag & TOSTACK)) {
			pr("#\tpushw\t%s\n", str(dest));
			return(dest);
		}
binopaddr:
		if(flag & ASADDR) {
			if(dest.flag & ISREG) {
				strcat(str(dest), ")");
				strshift(str(dest), 1);
				str(dest)[0] = '(';
				return(dest);
			}
			if(dest.flag & CANINDIR) {
				strshift(str(dest), 1);
				str(dest)[0] = '*';
				dest.flag &= ~CANINDIR;
				return(dest);
			}
			dest.flag = FAIL;	/* doubtful */
			return(dest);
		}
		dest.flag |= CC;
		return(dest);	
	case FLD:
		/* this is rvalues */
		s = doit(p->in.left, USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		if(dest.ans == 0)
			dest = allocreg(p, regmask);
		if((s.flag & INDEX) && p->in.left->in.op == STAR
			&& incrsize(p->in.left) != 1) {
		/* over-enthusiastic use of index mode */
			if(dest.flag & SCRATCH)
				x = dest;
			else
				x = allocreg(p, regmask);
			pr("#\tmov%c\t%s,%s\n", childtype(p), str(s), str(x));
			s = x;
		}
#if VAX==1
		pr("#\text%sv\t$%d,$%d,%s,%s\n", isunsigned(p->in.left)? "z": "",
			p->tn.rval/64, p->tn.rval%64, str(s), str(dest));
#else if M32==1
		i = p->tn.rval % 64;
		j = 32 - p->tn.rval/64 - i;
		pr("#\textzv\t&%d,&%d,%s,%s\n", j, i, str(s), str(dest));
#endif
		dest.flag |= CC;
		return(dest);
	case GENBR:
		if(p->in.left->in.op == CONV)
			p->in.left = p->in.left->in.left;
		s = doit(p->in.left, CC|VALUE|USED, 0, regmask);
		pp = genjmp(p->bn.lop);
		if(s.flag & CC)
			pr("#\t%s\t", pp);
		else if(VAX)
			pr("#\ttst%c\t%s\n#\t%s\t", childtype(p), str(s), pp);
		else if(M32)
			pr("#\tcmp%c\t%s,&0\n#\t%s\t", childtype(p), str(s), pp);
		pr(VAX? "L%d\n": ".L%d\n", p->bn.label);
		s.flag |= CC;	/* ?? */
		if(Pflag) {
			++bbcnt;
			pr("#\tincl\tlocprof+%d\n", 4*(bbcnt+3));
		}
		return(s);
	case GENLAB:
		s = doit(p->in.left, flag, dest, regmask);
		pr(VAX? "#L%d:\n": "#.L%d:\n", p->bn.label);
		if(M32 && (flag & TOSTACK))
			pr("#\tpushw\t%s\n", str(s));
		if(Pflag) {
			++bbcnt;
			pr("#\tincl\tlocprof+%d\n", 4*(bbcnt+3));
		}
		return(s);
	case GENUBR:
		s = doit(p->in.left, flag & CC, dest, regmask);
		if((flag & CC) && !(s.flag & CC))
			pr("#\ttst%c\t%s\n", childtype(p), str(s));
		pr(VAX? "#\tjbr\tL%d\n": "#\tjmp\t.L%d\n", p->bn.label);
		return(s);
	case ICON:
		if(p->tn.name)
			if(p->tn.lval)
				sprintx(buf, "%s+%d", p->tn.name, p->tn.lval);
			else
				sprintx(buf, "%s", p->tn.name);
		else
			sprintx(buf, "%d", p->tn.lval);
		if(!(flag & ASADDR)) {
			strshift(buf, 1);;
			buf[0] = VAX? '$': '&';
		}
		i = 0;
		if(VAX && p->tn.name == 0)
			if(p->tn.lval == 0)
				i = ICON0;
			else if(p->tn.lval == 1)
				i = ICON1;
		j = 0;
convbuf:	/* the cookie is in buf */
		if(dest.ans == 0 && !(flag & TOSTACK)) {
			if(p->in.op == ICON || !(flag & ASADDR)) {
				done(s, i, j);
			}
			if(i & CANINDIR) {
				strshift(buf, 1);
				buf[0] = '*';
				done(s, i & ~CANINDIR, j);
			}
			if(i & ISREG) {
				strcat(buf, ")");
				strshift(buf, 1);
				buf[0] = '(';
				done(s, i, j);
			}
			pp = buf;
			buf += BUF;
			s = allocreg(p, regmask);
			pr("#\tmov%c\t%s,%s\n", type(p), pp, str(s));
			flag &= ~ASADDR;
			goto inreg;
		}
		if(flag & TOSTACK)
			if(incrsize(p) == 4)
				pr(VAX? "#\tpushl\t%s\n": "#\tpushw\t%s\n", buf);
			else if(incrsize(p) == 8)
				pr("#\tmovd\t%s,%s\n", buf, str(dest));
			else
				pr("#\tcvt%cl\t%s,%s\n", type(p), buf, str(dest));
		else if(i & ICON0)
			pr("#\tclr%c\t%s\n", type(p), str(dest));
		else {	
			pr("#\tmov%c\t%s,%s\n", type(p), buf, str(dest));
			if(!(dest.flag & INDEX) || !index(str(dest), '+')
				&& !index(str(dest), '-'))
				;
			else if(!(i & INDEX) || !index(buf, '+')
				&& !index(buf, '-')) {
				done(s, (i|CC), j);
			}
			else
				dest.flag |= USED;
		}
		dest.flag |= CC;
		return(dest);
	case INCR:
		i = 1;
		pp = "add";
		goto incrop;
	case INIT:	/* knows it is ICON */
		s = doit(p->in.left, ASADDR, 0, 0);
		pr(VAX? "#\t.long\t%s\n": "#\t.word\t%s\n", str(s));
		return(dest);
	case ASG LS:
		if(VAX && incrsize(p) != 4)
			lsconv(p);
		if(rewriteasgop(p))
			goto assign;
		fieldbotch(p)
		flag |= DESTISLEFT;
		if(dest.ans && p->in.left->in.op == STAR)
			longjmp(back, mediumstar(p));
	case LS:	/* stupid vax */
#if M32==1
		pp = "LLS";
shiftop:
#endif
		if(VAX && incrsize(p) != 4)
			lsconv(p);
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		t = doit(p->in.right, VALUE|USED, 0, regmask);
		if(t.flag & FAIL)
			goto binfail;
		if(dest.ans == 0)
			if((s.flag & SCRATCH) || (flag & DESTISLEFT))
				dest = s;
			else
				dest = allocreg(p, regmask);
#if VAX==1
		if(incrsize(p) != 4) {	/* stupid vax */
			x = allocreg(p, regmask);
			pr("#\tashl\t%s,%s,%s\n", str(t), str(s), str(x));
			pr("#\tmov%c\t%s,%s\n", type(p), str(x), str(dest));
			if(flag & DESTISLEFT)
				pr("#\tmovl\t%s,%s\n", str(x), str(s));
			dest.flag |= CC;
			return(dest);
		}
		if(p->in.right->in.op == ICON && (i = p->in.right->tn.lval) <= 4
			&& (dest.flag & ISREG)) {
			if(strcmp(str(s), str(dest))) {
				i--;
				pr("#\taddl3\t%s,%s,%s\n", str(s), str(s), str(dest));
			}
			while(i-- > 0)
				pr("#\taddl2\t%s,%s\n", str(dest), str(dest));
		}
		else if(flag & DESTISLEFT)
			pr("#\tashl\t%s,%s,%s\n", str(t), str(s), str(s));
		else
			pr("#\tashl\t%s,%s,%s\n", str(t), str(s), str(dest));
		if((flag & DESTISLEFT) && strcmp(str(dest), str(s)))
			pr("#\tmovl\t%s,%s\n", str(s), str(dest));
#else if M32==1
		pr("#\t%s%c3\t%s,%s,%s\n", pp, childtype(p) + UP, str(t), str(s),
			str(dest));
		if(flag & TOSTACK)
			pr("#\tpush%c\t%s\n", type(p), str(dest));
#endif
		dest.flag |= CC;
		return(dest);
	case ASG MINUS:
		fieldbotch(p)
		if(rewriteasgop(p))
			goto assign;
		flag |= DESTISLEFT;
	case MINUS:
		pp = "sub";
		/* you won't believe this: type x[], *y where type is shorter than
		 * int, generates subx $_x,y which the assembler barfs on.
		 * instead, generate crummy code (stupid assembler)
		 */
		if(incrsize(p) < 4) {
			if(p->in.left->in.op == ICON && p->in.left->tn.name) {
				p->in.left->in.type = TINT;
				totemp(p, LEFT);
			}
			if(p->in.right->in.op == ICON && p->in.right->tn.name) {
				p->in.right->in.type = TINT;
				totemp(p, RIGHT);
			}
		}
		goto binop;
	case ASG MOD:
		fieldbotch(p)
		if(rewriteasgop(p))
			goto assign;
		if(dest.ans && p->in.left->in.op == STAR)
			longjmp(back, mediumstar(p));
		flag |= DESTISLEFT;
	case MOD:
#if M32==1
		if(isunsigned(p->in.right) || isunsigned(p->in.left))
			pp = "umod";
		else
			pp = "mod";
		goto binop;
#else if VAX==1
		if(isunsigned(p->in.right) || isunsigned(p->in.left)) {
			pp = "urem";
			goto unsdiv;
		}
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		t = doit(p->in.right, VALUE|USED, 0, regmask);
		if(t.flag & FAIL)
			goto binfail;
		regmask &= ~t.regmask;
		x = allocreg(p, regmask);
		pr("#\tdiv%c3\t%s,%s,%s\n", childtype(p), str(t), str(s), str(x));
		pr("#\tmul%c2\t%s,%s\n", childtype(p), str(t), str(x));
		pr("#\tsub%c3\t%s,%s,%s\n", childtype(p), str(x), str(s), str(x));
		if(dest.ans)
			goto movexdest;
		if(flag & DESTISLEFT) {
			dest = s;
			goto movexdest;
		}
		x.flag |= CC;
		return(x);
#endif
	case ASG MUL:
		fieldbotch(p)
		if(rewriteasgop(p))
			goto assign;
		flag |= DESTISLEFT;
	case MUL:
		pp = "mul";
		goto binop;
	case NAME:	/* ASADDR? */
		if(p->tn.lval && p->tn.name)
			sprintx(buf, "%s+%d", p->tn.name, p->tn.lval);
		else if(p->tn.name)
			sprintx(buf, "%s", p->tn.name);
		else if(p->tn.lval)
			sprintf(buf, "%d", p->tn.lval);
		else
			sprintx(buf, "0");
		j = 0;
		i = CANINDIR;
		goto convbuf;
	case ASG OR:
		fieldbotch(p)
		flag |= DESTISLEFT;
	case OR:
		pp = VAX? "bis": "or";
		goto binop;
	case ASG PLUS:
		fieldbotch(p)
		if(rewriteasgop(p))
			goto assign;
		flag |= DESTISLEFT;
	case PLUS:
		pp = "add";
		goto binop;
	case REG:
		sprintx(buf, "%s", regnames[p->tn.rval]);
		j = 0;
		i = ISREG;
		if(p->tn.lval == 1)
			i |= SCRATCH;
		goto convbuf;
	case RNODE: case SNODE:
		x = specialreg(p, 3);
		x.regmask = 0;
		if(dest.ans)
			goto movexdest;
		return(x);
	case QNODE:
		return(specialreg(p, regmask));
	case ASG RS:
		fieldbotch(p)
		if(VAX && incrsize(p) != 4)
			lsconv(p);
		if(rewriteasgop(p))
			goto assign;
		flag |= DESTISLEFT;
	case RS:	/* all right shifts are unsigned */
		if(incrsize(p) != 4)
			lsconv(p);
#if M32==1
		pp = "LRS";
		goto shiftop;
#else if VAX==1
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		if(p->in.left->in.op == ICON ||
			(s.flag & INDEX) && p->in.left->in.op == STAR
			&& incrsize(p->in.left) != 1 ||
			(s.flag & AUTO) && incrsize(p->in.left) != 1) {
		/* over-enthusiastic use of index mode */
		/* 12 >> i generates a byte immediate */
			if((s.flag & INDEX) && (flag & DESTISLEFT)) {/* *a++ >>= 1 */
				lsconv(p);	/* not best */
				longjmp(back, 1);
			}
			if((flag & DESTISLEFT) && dest.ans == 0) {
				dest = s;
				regmask &= ~s.regmask;
			}
			if(dest.flag & SCRATCH)
				x = dest;
			else
				x = allocreg(p, regmask);
			pr("#\tmovl\t%s,%s\n", str(s), str(x));
			s = x;
		}
		regmask &= ~s.regmask;
		if(dest.ans == 0)
			if((flag & DESTISLEFT) || (s.flag & SCRATCH))
				dest = s;
			else
				dest = allocreg(p, regmask);
		dest.flag |= CC;
		regmask &= ~dest.regmask;
		if(incrsize(p) != 4)
			x = allocreg(p, regmask);
		else
			x = dest;
		if(p->in.right->in.op == ICON && (i = p->in.right->tn.lval) >= 0) {
			pr("#\textzv\t$%d,$%d,%s,%s\n", i, 32 - i, str(s), str(x));
			goto movexdest;
		}
		t = doit(p->in.right, VALUE|USED, 0, regmask);
		if(t.flag & FAIL)
			goto binfail;
		y = allocreg(p, regmask & ~t.regmask);
		pr("#\tsubl3\t%s,$32,%s\n", str(t), str(y));
		pr("#\textzv\t%s,%s,%s,%s\n", str(t), str(y), str(s), str(x));
		goto movexdest;
#endif
	case STAR:
		switch(p->in.left->in.op) {
		case ICON:	/* as in foo((a, a)), where a is a struct */
			s = doit(p->in.left, VALUE, 0, regmask);
			sprintx(buf, "%s", str(s)+1);
			i = j = 0;
			goto convbuf;
		case VAUTO:
		case VPARAM:
		case NAME:
			s = doit(p->in.left, VALUE, 0, regmask);
			sprintx(buf, "*%s", str(s));
			j = i = 0;
			goto convbuf;
		case REG:
			s = doit(p->in.left, VALUE, 0, regmask);
inreg:
			if(s.flag & ISREG) {
				sprintx(buf, VAX? "(%s)": "0(%s)", str(s));
				j = s.regmask;
				i = CANINDIR;
				goto convbuf;
			}
			else if(s.flag & CANINDIR) {
				sprintx(buf, "*%s", str(s));
				j = s.regmask;
				i = 0;
				goto convbuf;
			}
			else {
				s.flag = FAIL;
				return(s);
			}
		}
		q = p->in.left;
		if(VAX && q->in.op == INCR && !(flag & INDEX)
			&& q->in.right->in.op == ICON && q->in.left->in.op == REG
			&& incrsize(p) == (int) q->in.right->tn.lval) {
			s = doit(q->in.left, VALUE, 0, regmask);
			sprintx(buf, "(%s)+", str(s));
			i = INDEX|AUTO;
			j = s.regmask;
			goto convbuf;
		}
		if(VAX && q->in.op == PLUS && q->in.left->in.op == LS
			&& !(flag & INDEX)
			&& q->in.left->in.right->in.op == ICON
			&& shiftsize(p) == (int) q->in.left->in.right->tn.lval) {
			s = doit(q->in.left->in.left, VALUE|USED, 0, regmask);
			if(s.flag & FAIL)
				return(s);
			regmask &= ~s.regmask;
			if(!(s.flag & ISREG)) {
				x = allocreg(p, regmask);
				pr("#\tmov%c\t%s,%s\n", childtype(q), str(s), str(x));
				regmask |= s.regmask;
				regmask &= ~x.regmask;
				s = x;
				if(!(s.flag & ISREG)) {
					s.flag = FAIL;
					return(s);
				}
			}
			t = doit(q->in.right, ASADDR|USED, 0, regmask);
			sprintx(buf, "%s[%s]", str(t), str(s));
			i = INDEX;
			j = s.regmask | t.regmask;
			goto convbuf;
		}
		if(q->in.op == PLUS && q->in.right->in.op == ICON) {
			s = doit(q->in.left, VALUE|USED, 0, regmask);
			if(s.flag & FAIL)
				return(s);
			if(!(s.flag & ISREG)) {
				x = allocreg(p, regmask);
				pr("#\tmov%c\t%s,%s\n", childtype(q), str(s), str(x));
				s = x;
				if(!(s.flag & ISREG)) {
					s.flag = FAIL;
					return(s);
				}
			}
			regmask &= ~s.regmask;
			t = doit(q->in.right, ASADDR|USED, 0, regmask);
			sprintx(buf, "%s(%s)", str(t), str(s));
			i = CANINDIR;
			j = s.regmask;
			goto convbuf;
		}
		if(q->in.op == MINUS && q->in.right->in.op == ICON) {
			s = doit(q->in.left, VALUE|USED, 0, regmask);
			if(s.flag & FAIL)
				return(s);
			if(!(s.flag & ISREG)) {
				x = allocreg(p, regmask);
				pr("#\tmov%c\t%s,%s\n", childtype(q), str(s), str(x));
				s = x;
				if(!(s.flag & ISREG)) {
					s.flag = FAIL;
					return(s);
				}
			}
			regmask &= ~s.regmask;
			sprintx(buf, "%d(%s)", -q->in.right->tn.lval, str(s));
			i = CANINDIR;
			j = s.regmask;
			goto convbuf;
		}
		if(VAX && q->in.op == ASG MINUS && !(flag & (INDEX|USED))
			&& q->in.right->in.op == ICON && q->in.left->in.op == REG
			&& incrsize(p) == (int) q->in.right->tn.lval) {
			s = doit(q->in.left, VALUE, 0, regmask);
			sprintx(buf, "-(%s)", str(s));
			i = INDEX|AUTO;
			j = s.regmask;
			goto convbuf;
		}
		if(VAX && q->in.op == PLUS && q->in.left->in.op == REG
			&& !(flag & INDEX) && q->in.right->in.op == REG
			&& incrsize(p) == 1) {
			sprintx(buf, "(r%d)[r%d]", q->in.right->tn.rval,
				q->in.left->tn.rval);
			i = INDEX;
			j = 0;
			goto convbuf;
		}
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		if(s.flag & CANINDIR) {
			sprintx(buf, "*%s", str(s));
			i = 0;
			j = s.regmask;
			goto convbuf;
		}
		if(!(s.flag & ISREG)) {
			x = allocreg(p, regmask);
			pr("#\tmov%c\t%s,%s\n", childtype(q), str(s), str(x));
			regmask |= s.regmask;
			regmask &= ~x.regmask;
			s = x;
		}
		if(!(s.flag & ISREG)) {
			s.flag = FAIL;
			return(s);
		}
		sprintx(buf, VAX? "(%s)": "0(%s)", str(s));
		i = 0;
		j = s.regmask;
		goto convbuf;
	case STASG:
		if(regmask != REGMASK) {
			s.flag = FAIL;
			return(s);
		}
		if(p->stn.stsize/8 == 4) {
			i = VAX? 'l': 'w';
stasg:
			s = doit(p->in.left, VALUE|ASADDR|USED, 0, regmask);
			if(s.flag & FAIL)
				return(s);
			regmask &= ~s.regmask;
			t = doit(p->in.right, VALUE|ASADDR|USED, 0, regmask);
			if(t.flag & FAIL) {
				totemp(p, LEFT);
				longjmp(back, 1);
			}
			pr("#\tmov%c\t%s,%s\n", i, str(t), str(s));
			s.regmask = s.flag = 0;
			return(simpler(t, s));
		}
		else if(VAX && p->stn.stsize/8 == 8) {
			i = 'q';
			goto stasg;
		}
#if VAX==1
		s = doit(p->in.right, VALUE|USED, 0, regmask);
		if(s.flag & INDEX) {
		/* over-enthusiastic use of index mode */
			x = allocreg(p, regmask);
			pr("#\tmovl\t%s,%s\n", str(s), str(x));
			s = x;
		}
		regmask &= ~s.regmask;
		t = doit(p->in.left, VALUE|USED, 0, regmask);
		if(t.flag & FAIL) {
			totemp(p, RIGHT);
			longjmp(back, 1);
		}
		if(t.flag & INDEX) {
		/* over-enthusiastic use of index mode */
			x = allocreg(p, regmask);
			pr("#\tmovl\t%s,%s\n", str(t), str(x));
			t = x;
		}
		t = indirit(t);
		if(t.flag & FAIL) {
			if(p->in.left->in.op != STAR)
				uerror("codegen failure in struct asg");
			totemp(p->in.left, LEFT);
			longjmp(back, 1);
		}
		if(p->in.right->in.op == STASG) {	/* secret knowledge */
			pr("#\tsubl2\t$%d,r3\n", p->stn.stsize/8);
			pr("#\tmovc3\t$%d,(r3),%s\n", p->stn.stsize/8, str(t));
		}
		else {
			s = indirit(s);
			if(s.flag & FAIL) {
				if(p->in.right->in.op != STAR)
					uerror("codgen fail in stasg");
				totemp(p->in.right, LEFT);
				longjmp(back, 1);
			}
			pr("#\tmovc3\t$%d,%s,%s\n", p->stn.stsize/8, str(s), str(t));
		}
		t.regmask = t.flag = 0;
#else if M32==1
		x = allocreg(p, regmask);	/* gets reg 0 */
		s = doit(p->in.right, VALUE, x, regmask);
		regmask &= ~x.regmask;
		y = allocreg(p, regmask);	/* gets reg 1 */
		i = p->stn.stsize/32;
		if(p->in.right->in.op == STASG && i >= 7) {	/* secret knowledge */
			pr("#\tsubw3\t&%d,%%r1,%%r0\n", p->stn.stsize/8);
		}
		t = doit(p->in.left, VALUE, y, regmask);
		if(t.flag & FAIL)
			stasgrewrite(p);
		if(i >= 7) {
			pr("#\tmovw\t&%d,%%r2\n", i);
			pr("#\tMOVBLW\n");
		}
		else
			while(--i >= 0)
				pr("#\tmovw\t%d(%r0),%d(%r1)\n", 4 * i, 4 * i);
#endif
		return(t);
	case STCALL:
		goto call;
	case VAUTO:
		sprintx(buf, "%d(%s)", p->tn.lval, frameptr);
		j = 0;
		i = CANINDIR;
		goto convbuf;
	case VPARAM:
		sprintx(buf, "%d(%s)", p->tn.lval, argptr);
		j = 0;
		i = CANINDIR;
		goto convbuf;
	case UNARY AND:
		s = doit(p->in.left, USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		regmask &= ~s.regmask;
		if(dest.ans == 0) {
			dest = allocreg(p, regmask);
		}
		if(s.flag & ISREG) {
			x = alloctmp(p);
			pr("#\tmov%c\t%s,%s\n", childtype(p), str(s), str(x));
			s = x;
		}
		if(flag & TOSTACK)
			pr(VAX? "#\tpushal\t%s\n": "#\tpushaw\t%s\n", str(s));
		else
			pr("#\tmova%c\t%s,%s\n", type(p), str(s), str(dest));
		if(flag & ASADDR) {
			if(dest.flag & ISREG) {
				strcat(str(dest), ")");
				strshift(str(dest), 1);
				str(dest)[0] = '(';
			}
			else {
				strshift(str(dest), 1);
				str(dest)[0] = '*';
			}
		}
		return(dest);
	case UNARY CALL:
		i = 0;
		goto called;
	case UNARY MINUS:
		s = doit(p->in.left, VALUE|USED, 0, regmask);
		if(s.flag & FAIL)
			return(s);
		if(dest.ans == 0)
			if(s.flag & SCRATCH)
				dest = s;
			else
				dest = allocreg(p, regmask & ~s.regmask);
		pr("#\tmneg%c\t%s,%s\n", childtype(p), str(s), str(dest));
		dest.flag |= CC;
		return(dest);
	case UNARY STCALL:
		i = 0;
		goto called;
	case ASSIGN:
assign:
		if(p->in.left->in.op == QNODE && dest.ans) {
			s = doit(p->in.right, VALUE|(flag&USED? USED: 0),
				dest, regmask);
			return(s);
		}
		if(p->in.left->in.op == RNODE || p->in.left->in.op == SNODE) {
			s = doit(p->in.right, VALUE|(flag&USED? USED: 0),
				dest, regmask);
			if(s.flag & FAIL)
				return(s);
			x = s;
			dest = doit(p->in.left, USED, 0, 0);
			goto movexdest;
		}
		if(p->in.left->in.op == FLD) {
			s = doit(p->in.left->in.left, INDEX, 0, regmask);
			regmask &= ~s.regmask;
			if(p->in.right->in.op == ASSIGN) {
				x = allocreg(p->in.right, regmask);
				t = doit(p->in.right, VALUE|USED, x, regmask & ~x.regmask);
			}
			else
				t = doit(p->in.right, VALUE|USED, 0, regmask);
			if(t.flag & FAIL) {
				totemp(p->in.left, LEFT);
				longjmp(back, 1);
			}
			i = VAX? '$': '&';
			pr("#\tinsv\t%s,%c%d,%c%d,%s\n", str(t), i,
				p->in.left->tn.rval/64, i,
				p->in.left->tn.rval % 64, str(s));
			if(dest.ans)
				pr("#\tmovl\t%s,%s\n", str(t), str(dest));
			t.flag |= CC;
			return(t);
		}
		if(dest.ans && p->in.left->in.op == STAR) {
			if(dest.flag & SCRATCH)
				x = dest;
			else
				x = allocreg(p, regmask);
			s = doit(p->in.right, VALUE|USED, x, regmask);
			if(s.flag & FAIL)
				return(s);
			regmask &= ~s.regmask;
			t = doit(p->in.left, USED, 0, regmask);
			if(t.flag & FAIL) {
			/* did we fail because of a dest, or was it us? */
				if(dest.ans || svmask != REGMASK) {
					t = doit(p->in.left, USED, 0, svmask);
					if(t.flag & FAIL) {
						return(t);
					}
					starasg(p);
				}
				else
					asgwrite(p);
				longjmp(back, 1);
			}
			pr("#\tmov%c\t%s,%s\n", type(p), str(x), str(t));
			if(strcmp(str(x), str(dest)) == 0) {
				dest.flag |= CC;
				return(dest);
			}
			goto movexdest;
		}
		t = doit(p->in.left, USED, 0, regmask);
		regmask &= ~t.regmask;
		if(p->in.right->in.op == GENLAB)
			extracheck(p);	/* slightly worse code in usual case */
		s = doit(p->in.right, VALUE, t, regmask);
		if(s.flag & NOGOOD) {
		/* did we fail because of a dest, or was it us? */
			if(s.flag & FAIL) {
				if(dest.ans || svmask != REGMASK) {
					s = doit(p->in.right, VALUE, t, svmask);
					if(s.flag & FAIL) {
						return(s);
					}
				}
				asgwrite(p);
				longjmp(back, 1);
			}
			else if((flag & USED) && !((flag & CC) && (s.flag & CC))) {
				/* presumably USED as in *p++=*q++ */
				totemp(p, RIGHT);
				longjmp(back, 1);
			}
		}
		if(dest.ans) {
			x = s;
			goto movexdest;
		}
		else if(M32 && (flag & TOSTACK)) {
			pr("#\tpushw\t%s\n", str(s));
			return(s);
		}
		else if(flag & ASADDR) {
			i = (s.flag & (ISREG|CANINDIR|SCRATCH));
			j = s.regmask;
			buf = str(s);
			goto convbuf;
		}
		return(s);
	}
}