/* Copyright 1982 Alcyon Corporation 8716 Production Ave. San Diego, Ca. 92121 @(#)codegen.c 1.13 12/30/83 */ #include "cgen.h" #include "cskel.h" char null[]; char *opname[]; /* scodegen - over-all code generation for expression*/ /* Picks off post-fix ++, --.*/ scodegen(tp,cookie,reg) /* returns register result is in*/ struct tnode *tp; int cookie; int reg; { struct tnode *clist[20]; struct tnode **clp; register struct tnode **cp; register short r, ccflag; short lconst; register struct tnode *rtp; if( tp->t_op == COMMA ) { scodegen(tp->t_left,FOREFF,reg); return(scodegen(tp->t_right,cookie,reg)); } ccflag = 0; clp = clist; tp = addptree(tp,&clp); if( clp > clist ) { /* * post ++, -- in tree. We need to compile the tree post operators * then generate code to do the post operators, then do any fix up of * condition codes since the Stupid 68000 architect was a nimnul. */ if( cookie == FORCC ) { /* * here we make the observation that if we are comparing something with * zero OR the top operator of the tree is not a comparison operator, * we can compile the tree to a register, and then set the condition * codes OK with a tst instruction at the end. */ if( RELOP(tp->t_op) ) { if( (rtp=constant(tp->t_right,&lconst)) && CONSTZERO(lconst,rtp)) { /* [vlh] */ ccflag = 1; tp = tp->t_left; cookie = FORREG; } else ccflag = 2; } else { ccflag = 1; cookie = FORREG; } } } r = codegen(tp,cookie,reg); if( clp > clist ) { if( ccflag == 2 ) { if (!m68010) /* [vlh] 4.2, added differentiation */ OUTSRSAVE(r); else OUTCCSAVE(r); } for( cp = clist; cp < clp; cp++ ) codegen(*cp,FOREFF,r+1); if( ccflag == 1 ) outcmp0(r,tp); else if( ccflag == 2 ) OUTCCRESTORE(r); } return(r); } /* addptree - prune off postfix ++, -- from expression tree*/ /* This prunes off ++, -- and collects those expressions for*/ /* scodegen.*/ char * addptree(tp,clp) /* returns pointer to pruned tree*/ struct tnode *tp; struct tnode ***clp; { register short op; register struct tnode *stp; op = tp->t_op; if( LEAFOP(op) || op == QMARK || op == LAND || op == LOR ) /* [mac] 4.2 */ return(tp); if( op == POSTINC || op == POSTDEC ) { stp = tcopy(tp->t_left,A_DOPRE);/* [mac] 4.2 c[i=+1]++ & c[*p++]++ */ *(*clp)++ = tcopy(tp,A_DOPOST); return( stp ); } if( BINOP(op) ) tp->t_right = addptree(tp->t_right,clp); tp->t_left = addptree(tp->t_left,clp); return(tp); } /** * codegen - generate code for expression * This calls up rcodegen, which prunes off any special register * optimization code, then calls ucodegen (unrecursive) code * generation. **/ codegen(tp,cookie,reg) /* returns reg result is in*/ struct tnode *tp; /* tree pointer*/ int cookie; /* code generation goals*/ int reg; /* first available register*/ { register short savestk, ssize, r, i; register struct tnode *rtp; #ifdef DEBUG if( cflag ) printf("codegen op=%d cookie=%d reg=%d\n",tp->t_op,cookie,reg); #endif switch( tp->t_op ) { case CALL: case NACALL: ssize = 0; savestk = stacksize; if( tp->t_left->t_op != SYMBOL ) stacksize++; if( tp->t_op == CALL ) { rtp = tp->t_right; while( rtp->t_op == COMMA ) { ssize += dofarg(rtp->t_right); rtp = rtp->t_left; } ssize += dofarg(rtp); } tp->t_op = FJSR; /*generate JSR (unary op)*/ codegen(tp,FORREG,reg); popstack(ssize); stacksize = savestk; fixresult(tp,cookie,0); return(0); /*result in R0*/ case COMMA: codegen(tp->t_left,FOREFF,reg); return(codegen(tp->t_right,cookie,reg)); case AND: if( cookie == FORCC && (i=isonebit(tp->t_right)) >= 0 && (i=dobitadd(tp->t_left,i)) >= 0 ) { if( CONVOP(tp->t_right->t_op) ) tp->t_right = tp->t_right->t_left; tp->t_right->t_value = i; tp->t_right->t_op = CINT; /* [vlh] 4.1 */ tp->t_right->t_type = INT; /* [vlh] 4.1 */ tp->t_op = BTST; tp = canon(tp); sucomp(tp,reg,1); } break; } if( rcodegen(&tp,cookie,reg) ) { if( cookie == FORCC && tp->t_op == SYMBOL && tp->t_sc == REGISTER && ISDREG(tp->t_reg)) return(reg); } r = ucodegen(tp,cookie,reg); return(r); } /* fixresult - fix result of code generation*/ fixresult(tp,cookie,reg) /* returns - none*/ struct tnode *tp; int cookie; /* wanted this cookie*/ int reg; { #ifdef DEBUG if (cflag) printf("fixresult cookie=%d reg=%d op=%d\n",cookie,reg,tp->t_op); #endif switch( cookie ) { case FORCC: outcmp0(reg,tp); break; case FORSP: case FORSTACK: stacksize++; outrpush(reg,tp,cookie==FORSTACK); break; } return(reg); } /** * ucodegen - generate code for tree given cookie and starting register * Handles the matching of the expression tree node with the * corresponding code generation table. When a match is found, * expand is called to expand the code skeleton macro. **/ ucodegen(tp,cookie,reg) /* returns reg result is in*/ struct tnode *tp; /* pointer to expression tree*/ int cookie; /* (FORCC,FOREFF,FORREG,FORSTACK)*/ int reg; /* first available register*/ { register short r; register char *p; register struct tnode *ltp; short lconst; PUTEXPR(cflag,"ucodegen",tp); switch( tp->t_op ) { case ADDR: /* 4.2 */ ltp = tp->t_left; if(cookie==FORCC && ltp->t_op==SYMBOL && (ltp->t_sc==EXTERNAL || ltp->t_sc==STATIC)) { warning("function existence test"); printf("move.l #"); outaexpr(ltp,A_NOIMMED); printf(",-(sp)\ntst.l (sp)+\n"); return(reg); } break; case ASSIGN: /* 4.3 [mac] */ if ((BASETYPE(tp->t_left->t_type) == CHAR) && cookie == FORREG && ISAREG(reg)) reg = DREG(reg); break; case STASSIGN: /*[vlh]*/ outstrcpy(codegen(tp->t_left,FORREG,AREG(reg)), codegen(tp->t_right,FORREG,AREG(reg+1)),tp->t_type); return(reg); break; case SYMBOL: if( cookie == FOREFF ) return(reg); break; case LSH: if( (ISAREG(reg)) && (p=constant(tp->t_right,&lconst)) && !(UNSIGN(tp->t_left->t_type)) && ((!lconst && (p->t_value == 1 || p->t_value == 2) ) || (lconst && (p->t_lvalue == 1 || p->t_lvalue == 2) ))) { r = codegen(tp->t_left,FORREG,reg); outmovr(r,reg,tp->t_left); if( p->t_value == 2 ) OUTADDR(reg,reg,tp); OUTADDR(reg,reg,tp); fixresult(tp,cookie,reg); return(reg); } break; case EQMULT: case EQDIV: case LEQMULT: case LEQDIV: case EQMOD: case LEQMOD: case EQRSH: case EQLSH: case EQAND: case EQOR: case EQXOR: if( indexreg(tp->t_left) ) { reg = DREG(reg); outmovr(r=tp->t_left->t_reg,reg,tp); tp->t_left->t_reg = reg; codegen(tp,cookie,reg+1); outmovr(reg,r,tp); return(reg); } if (cookie == FORREG && ISAREG(reg)) { /* 4.3 [mac] */ reg = DREG(reg); codegen(tp,cookie,reg); return(reg); } break; case ADD: case EQADD: if( (p=constant(tp->t_right,&lconst))) if (!lconst && (p->t_value < 0 && p->t_value >= -QUICKVAL)) { p->t_value = - p->t_value; tp->t_op += (SUB-ADD); } else if (lconst && p->t_lvalue<0 && p->t_lvalue >= -QUICKVAL) { p->t_lvalue = -p->t_lvalue; tp->t_op += (SUB-ADD); } break; } /* end of case statement..... */ sucomp(tp,reg,1); if( (r=loadexpr(tp,cookie,reg)) >= 0 ) return(r); if( (r=cqmark(tp,cookie,reg)) >= 0 ) return(r); if( (r=hardrel(tp,cookie,reg)) >= 0 ) return(r); if( cookie == FORCC && (p=match(tp,FOREFF,reg)) != 0 ) { r = expand(tp,FOREFF,reg,p); if( ISASGOP(tp->t_op) && indexreg(tp->t_left) ) outcmp0(tp->t_left->t_reg,tp->t_left); } else if( p = match(tp,cookie,reg) ) r = expand(tp,cookie,reg,p); else if( cookie != FORREG ) r = fixresult(tp,cookie,ucodegen(tp,FORREG,reg)); else { if(tp->t_type != STRUCT) /* [vlh] 4.3 */ error("no code table for %s",opname[tp->t_op]); else error("illegal structure operation"); } return(r); } /* outstrcpy - output structure copy */ outstrcpy(lr,rr,size) /*[vlh]*/ int lr, rr; /* left register, right register */ int size; /* structure size to copy */ { register short lab; #ifdef DEBUG if (cflag) printf("size is %d\n",size); #endif if (ISDREG(lr)) { printf("move.l r%d,r%d\n",lr,AREG(lr)); lr = AREG(lr); } if (ISDREG(rr)) { printf("move.l r%d,r%d\n",rr,AREG(rr)); rr = AREG(rr); } lab = nextlabel++; printf("move #%d,r%d\n",(size/INTSIZE)-1,DREG(lr)); OUTLAB(lab); printf("move (r%d)+,(r%d)+\ndbra r%d,L%d\n",rr,lr,DREG(lr),lab); } /** * loadexpr - load an addressable expression into a register * This checks for any possible usage of the register indexed * addressing mode. Note that this relies on the good graces of the * load code skeletons not to muck up the compiler registers before * loading an addressable expression... **/ loadexpr(tp,cookie,reg) /* returns register loaded or -1*/ struct tnode *tp; /* pointer to expression tree*/ int reg; /* register to load*/ { register struct tnode *rtp, *ltp, *xtp, *atp; register short off, r, type, nr, ar, xr, xt; if( tp->t_op == INDR || LOADABLE(tp) ) { PUTEXPR((cflag>1),"loadexpr",tp); type = tp->t_type; if( tp->t_op == INDR && (ltp=tp->t_left)->t_op == ADD ) { rtp = ltp->t_right; ltp = ltp->t_left; off = 0; if( rtp->t_op == CINT && ((off=rtp->t_value) < -128 || off > 127 || ltp->t_op != ADD ) ) { tp = snalloc(type,AUTO,(long)off,0,0); if( indexreg(ltp) ) tp->t_reg = ltp->t_reg; else { r = codegen(ltp,FORREG,AREG(reg)); if( ISDREG(r) ) outmovr(r,AREG(r),ltp); tp->t_reg = AREG(r); } } else { if( rtp->t_op == CINT ) { rtp = ltp->t_right; ltp = ltp->t_left; } if(indexreg(rtp) || (!(indexreg(ltp)) && (ISREG(rtp)))) { xtp = ltp; ltp = rtp; rtp = xtp; } xtp = atp = 0; if( indexreg(ltp) ) { ar = ltp->t_reg; if( (ISREG(rtp)) && rtp->t_type != CHAR ) { xr = rtp->t_reg; xt = rtp->t_type; } else xtp = rtp; } else if( (ISREG(ltp)) && ltp->t_type != CHAR && (lflag || rtp->t_op != ADDR) ) { xr = ltp->t_reg; xt = ltp->t_type; atp = rtp; } else if( rtp->t_op == ADDR ) { atp = ltp; xtp = rtp; } else { atp = rtp; xtp = ltp; } nr = 0; if( atp ) nr++; if( xtp && (xtp->t_op != ADDR || lflag ) ) nr++; if( DREG(nr+reg) <= HICREG ) { r = reg; if( atp ) { ar = codegen(atp,FORREG,AREG(r)); if( ISDREG(ar) ) { outmovr(ar,AREG(ar),atp); ar = AREG(ar); } r++; } if( xtp && xtp->t_op == ADDR && !lflag ) { tp = xtp->t_left; tp->t_sc += (EXTOFF-EXTERNAL); tp->t_offset += off; tp->t_reg = ar; } else { if( xtp ) { xr = codegen(xtp,FORREG,AREG(r)); xt = xtp->t_type; } tp = xnalloc(type,ar,(long)off,xr,xt); } } } } if( (ISAREG(reg)) && tp->t_type == CHAR ) reg = DREG(reg); tp = tnalloc(LOAD,tp->t_type,SU_EASY,0,tp,null); PUTEXPR((cflag>1),"loadexpr final",tp); return( codegen(tp,cookie,reg) ); } return(-1); } /* coffset - check offset for addressable node*/ char *coffset(tp) /* returns ptr to const off node*/ struct tnode *tp; /* pointer to node*/ { register struct tnode *rtp; if( tp->t_op == ADD ) { rtp = tp->t_right; if( rtp->t_op == CINT ) return(rtp); if(!lflag) { if( rtp->t_op == ADDR ) return(rtp->t_left); rtp = tp->t_left; if( rtp->t_op == ADDR ) { tp->t_left = tp->t_right; tp->t_right = rtp; return(rtp->t_left); } } } return(0); } /* hardrel - do relationals returning a value*/ hardrel(tp,cookie,reg) /* returns reg or -1*/ struct tnode *tp; /* pointer to tree*/ int cookie; /* cookie for code generation*/ int reg; /* low register*/ { register char *p; register short op, lab1, lab2; short islong; #ifdef DEBUG if (cflag) printf("hardrel: cookie=%d op=%d reg=%d\n",cookie,tp->t_op,reg); #endif op = tp->t_op; if (RELOP(op) && tp->t_left->t_type == CHAR && (p=constant(tp->t_right,&islong))) { if (islong) tp->t_left = tnalloc(LOAD,LONG,SU_EASY,0,tp->t_left,null); else if (p->t_value > 255 || p->t_value < -128) tp->t_left = tnalloc(LOAD,INT,SU_EASY,0,tp->t_left,null); } /* [vlh] 4.3, if char compared to large constant.... */ if (cookie != FORCC && (RELOP(op) || op==LOR || op==LAND || op==NOT)) { lab1 = nextlabel++; condbr(tp,TRUE,lab1,reg); p = canon(cnalloc(INT,0)); codegen(p,cookie,reg); lab2 = nextlabel++; OUTGOTO(lab2); OUTLAB(lab1); p = canon(cnalloc(INT,1)); codegen(p,cookie,reg); OUTLAB(lab2); return(reg); } return(-1); } /* cqmark - compile question mark operator*/ /* This does the compilation of the question mark operator.*/ cqmark(tp,cookie,reg) /* returns reg or -1*/ struct tnode *tp; int cookie; int reg; { register short lab1, lab2, savestk, r; if( tp->t_op == QMARK && cookie != FORCC ) { lab1 = nextlabel++; condbr(tp->t_left,FALSE,lab1,reg); savestk = stacksize; r = scodegen(tp->t_right->t_left,cookie,reg); /* [mc] 4.0 */ outmovr(r,reg,tp); stacksize = savestk; OUTGOTO(lab2=nextlabel++); OUTLAB(lab1); r = scodegen(tp->t_right->t_right,cookie,reg); /* [mc] 4.0 */ outmovr(r,reg,tp); OUTLAB(lab2); return(reg); } return(-1); } /** * condbr - handle conditional branch code generation * This handles the conditional branch code generation, handling * the special cases for constants, ||, &&, ! and generating the * correct conditional branch instruction. **/ condbr(tp,dir,lab,reg) struct tnode *tp; int dir; int lab; int reg; { register struct tnode *ltp, *rtp; register short lab1, optype, op, subdir; ltp = tp->t_left; if( BINOP(op=tp->t_op) ) rtp = tp->t_right; subdir = dir; /*set up for LOR*/ #ifdef DEBUG if (cflag) printf("condbr: dir = %d, lab = %d, reg = %d\n",dir,lab,reg); #endif switch( op ) { case CINT: if( !tp->t_value ) { if( dir == FALSE ) OUTGOTO(lab); } else if( dir != FALSE ) OUTGOTO(lab); break; case NOT: condbr(ltp,!dir,lab,reg); break; case LAND: dir = !dir; case LOR: if( dir == FALSE ) { lab1 = nextlabel++; condbr(ltp,!subdir,lab1,reg); condbr(rtp,subdir,lab,reg); OUTLAB(lab1); } else { condbr(ltp,subdir,lab,reg); condbr(rtp,subdir,lab,reg); } break; case COMMA: scodegen(tp->t_left,FOREFF,reg); condbr(tp->t_right,dir,lab,reg); break; default: /* [mc] 4.2.b */ if(outdbra(dir,op,ltp,rtp,lab)) break; if( RELOP(op) && ltp->t_op == AUTOINC && rtp->t_op == AUTOINC && ltp->t_type == rtp->t_type ) outcmpm(tp); else scodegen(tp,FORCC,reg); optype = 0; if( RELOP(op) ) { if( UNORPTR(ltp->t_type) || UNORPTR(rtp->t_type) ) optype++; } else op = NEQUALS; if(!dir) op = invrel[op-EQUALS]; optype = brtab[op-EQUALS][optype]; printf("%s L%d\n",mnemonics[optype],lab); break; } } rcodegen(tpp,cookie,reg) /* returns changed flag*/ struct tnode **tpp; /* pointer to tree*/ int cookie; /* code generation cookie*/ int reg; /* register to use for code*/ { register short change, op; register struct tnode *tp; tp = *tpp; op = tp->t_op; change = 0; if (NOTLEAFOP(op) && op!=COMMA && op!=QMARK && op!=LAND && op!=LOR && op!=LESS && op!=LESSEQ && op!=GREAT && op!=GREATEQ) { change += rcodegen(&tp->t_left,cookie,reg); if( BINOP(op) ) change += rcodegen(&tp->t_right,cookie,reg); change += rcgen(tpp,cookie,reg); } /* [vlh] 4.2 stop getting to this code on <, >, <=, >= */ if( change ) *tpp = canon(*tpp); return(change); } rcgen(tpp,cookie,reg) /* returns changed flag*/ struct tnode **tpp; /* pointer to tree*/ int cookie; /* code generation goals*/ int reg; /* register to use*/ { register struct tnode *tp, *p, *ltp, *rtp; register short op, change; change = 0; for( tp = *tpp ; BINOP(op=tp->t_op); *tpp=tp=canon(tp), change++ ) { ltp = tp->t_left; if( ltp->t_op != SYMBOL ) break; rtp = tp->t_right; switch( op ) { case ASSIGN: if( ltp->t_sc != REGISTER ) return(change); switch( rtp->t_op ) { case DIV: case MOD: if( rtp->t_left->t_type==LONG && ltp->t_type==INT ) return(change); /* [vlh]4.3, ri = rl / 10 */ case MULT: if( (rtp->t_left->t_type==INT || rtp->t_left->t_op==INT2L) && (rtp->t_right->t_type==INT || rtp->t_right->t_op==INT2L) && ltp->t_type==LONG) return(change); /* [vlh]4.3, rl = i * i */ case AND: case OR: case XOR: case LSH: case RSH: if( ISAREG(ltp->t_reg) ) return(change); case ADD: case SUB: p = rtp->t_right; if(NOTADDRESSABLE(ltp) || !noref(rtp->t_right,ltp->t_reg)) return(change); p = rtp->t_left; if( p->t_op != SYMBOL || p->t_sc != REGISTER || p->t_reg != ltp->t_reg ) { tp->t_right = p; PUTEXPR((cflag>1),"rcgen",tp); codegen(tp,FOREFF,reg); } tp->t_right = rtp->t_right; tp->t_op = rtp->t_op + (EQADD-ADD); continue; } case EQLSH: case EQRSH: if( ltp->t_sc != REGISTER ) return(change); case EQADD: case EQSUB: case EQAND: case EQOR: case EQXOR: PUTEXPR((cflag>1),"rcgen EQOP",tp); if( ltp->t_type == CHAR ) { #ifdef DEBUG if (cflag>1) printf("exiting rcgen with ltp type CHAR change %d\n",change); #endif return(change); } ucodegen(tp,FOREFF,reg); tp = ltp; continue; case PREDEC: case PREINC: if(cookie==FORCC || ltp->t_type==CHAR) return(change); ucodegen(tp,FOREFF,reg); tp = ltp; continue; } break; } return(change); } noref(tp,reg) /* 4.0 change */ struct tnode *tp; /* returns 1 if no reference in tree to reg */ int reg; { if ( LEAFOP(tp->t_op) ) { if (tp->t_op == SYMBOL && tp->t_sc == REGISTER && tp->t_reg == reg) return(0); return(1); } if ( !noref(tp->t_left,reg) ) return(0); if (BINOP(tp->t_op)) return( noref(tp->t_right,reg) ); return(1); } /* cdsize - compute size of data item*/ cdsize(tp) /* returns data size in bytes*/ struct tnode *tp; { register short type; type = tp->t_type; if( SUPTYPE(type) ) return(PTRSIZE); switch( type ) { case INT: case CHAR: case UCHAR: /* [vlh] 4.2 */ case UNSIGNED: return(INTSIZE); case LONG: case ULONG: /* [vlh] 4.2 */ case FLOAT: /* [vlh] 3.4 */ return(LONGSIZE); } error("cdsize: invalid type %d",type); return(0); } dofarg(tp) /* returns number of bytes pushed*/ struct tnode *tp; /* pointer to expression tree*/ { register short nb; nb = 0; if( tp->t_op == SYMBOL && tp->t_sc == STRUCT ) error("structure operation not implemented"); else if( stacksize ) { codegen(tp,FORSTACK,0); nb = cdsize(tp); } else codegen(tp,FORSP,0); return( nb ); } /* dobitadd - do bit operation address checking and fixup*/ dobitadd(tp,bitno) /* returns -1 if can't or bitno*/ struct tnode *tp; int bitno; { register short offset; if( tp->t_type == CHAR ) offset = 0; else offset = cdsize(tp) - (bitno/BITSPBYTE) - 1; if( tp->t_op == SYMBOL ) { switch( tp->t_sc ) { case REGISTER: if( ISDREG(tp->t_reg) ) return(bitno); default: return(-1); case EXTERNAL: case STATIC: case REGOFF: case STATOFF: case EXTOFF: tp->t_offset += offset; return( bitno % BITSPBYTE ); } } else if( tp->t_op == INDR ) { tp->t_left = tnalloc(ADD,tp->t_left->t_type,0,0,tp->t_left, cnalloc(INT,offset)); return( bitno % BITSPBYTE ); } return(-1); } isonebit(tp) /* returns -1 if not 1 bit, else bitno*/ struct tnode *tp; /* pointer to tree*/ { short lconst; long bvalue; if (!(tp = constant(tp,&lconst))) /* [vlh] 4.1 added long... */ return(-1); bvalue = (lconst) ? tp->t_lvalue : (long) tp->t_value; return(onebit(bvalue));