/* CCGEN2.C - Generate code for parse-tree expressions ** ** (c) Copyright Ken Harrenstien 1989 ** All changes after v.295, 14-May-1989 ** (c) Copyright Ken Harrenstien, SRI International 1985, 1986 ** All changes after v.79, 8-Aug-1985 ** ** Original version (C) 1981 K. Chen */ #define NEWTERN 1 /* Try new ternary code */ #include "cc.h" #include "ccgen.h" #include /* Imported functions */ extern SYMBOL *newlabel(void); /* CCSYM */ extern int elembsize(TYPE *); /* CCSYM */ extern INT sizetype(TYPE *), sizeptobj(TYPE *); extern void /* CCCODE */ codek0(int, VREG *, VREG *), codek4(int, VREG *, VREG *), code4s(int, VREG *, VREG *, int, INT), code0(int, VREG *, VREG *), code1(int, VREG *, INT), codebp(int, int, INT, int, SYMBOL *, INT), code3(int, VREG *, SYMBOL *), code4(int, VREG *, VREG *), code5(int, VREG *), code6(int, VREG *, SYMBOL *), codemdx(int, int, SYMBOL *, INT, int), code8(int, VREG *, INT), code9(int, VREG *, double, int), code10(int, VREG *, SYMBOL *, INT, INT), code13(int, VREG *, INT), codestr(char *, int), codlabel(SYMBOL *), fixprev(void), flushcode(void); extern void code4m(int, VREG *, VREG *, char *), code5m(int, VREG *, char *); extern void code00(int, int, int), code40(int,int,int,INT); /* Reg linkage */ extern int codcreg(VREG *, VREG *); /* CCCODE */ extern int sideffp(NODE *); /* CCEVAL */ extern int vrispair(VREG *); extern void folddiv(VREG *); extern int unjump(SYMBOL *); extern SYMBOL *cregupto(SYMBOL *); /* CCCREG for gternary() */ extern PCODE *before(PCODE *); extern VREG *vrdget(void); extern void vrfree (VREG *); extern VREG *vrget(void); extern int vrispair(VREG *); extern void vrnarrow (VREG *); extern int vrreal (VREG *); extern int vrstoreal (VREG *, VREG *); extern int vrtoreal(VREG *); extern void vrufcreg(VREG *); extern void vralspill(void); extern void vrlowiden (VREG *); extern VREG *vrretdget(void); extern VREG *vrretget(void); extern void vrallspill(void); /* VERY non-optimal!! */ /* Exported functions */ VREG *genexpr(NODE *); void genxrelease(NODE *); void relflush(VREG *reg); /* Maybe move to CCOPT */ void gboolean(NODE *n, SYMBOL *false, int reverse); VREG *getmem(VREG *reg, TYPE *t, int byte, int keep), *stomem(VREG *reg, VREG *ra, INT siz, int byteptr); /* CCGEN1 auto inits */ VREG *gmuuo(NODE *); /* Internal functions */ static VREG *gexpr(NODE *), *gternary(NODE *); static void gor(NODE *, SYMBOL *, int), gand(NODE *, SYMBOL *, int), gboolop(NODE *, int); static VREG *gassign(NODE *), *gbinary(NODE *), *garithop(int, VREG *, VREG *, int), *gptrop(int, VREG *, VREG *, TYPE *, TYPE *), *gptraddend(TYPE *, NODE *), *glogical(NODE *), *gunary(NODE *), *gcast(NODE *), *gcastr(int, VREG *, TYPE *, TYPE *, NODE *), *gintwiden(VREG *, TYPE *, TYPE *, NODE *), *guintwiden(VREG *, int, NODE *), *gincdec(NODE *, int, int), *gprimary(NODE *), *gcall(NODE *); static void emit_blissargs(NODE *); static INT sizeargs(NODE *); static void gfnarg(NODE *); static VREG *gaddress(NODE *); static void pitopc(VREG *, int, int, int); static int bptrref(NODE *); static void gasm(NODE *); #if 0 static VREG *rgetmem(VREG *reg, TYPE *t, int byte, int keep); #else static VREG *rgetmem(VREG *reg, TYPE *t, int keep); #endif #if 0 static VREG* rstomem(VREG *reg, int ra, INT siz, int byteptr); static VREG *gexpr(), *gternary(); static void gor(), gand(), gboolop(); static VREG *gassign(), *gbinary(), *garithop(), *gptrop(), *gptraddend(), *glogical(), *gunary(), *gcast(), *gcastr(), *gintwiden(), *guintwiden(), *gincdec(), *gprimary(), *gcall(); static void emit_blissargs(); static INT sizeargs(); static void gfnarg(); static VREG *gaddress(); static void pitopc(); static int bptrref(); static void gasm(); #endif /* GENEXPR - Main function for expression code generation. ** Argument is pointer to a parse-tree node expression; ** Result is pointer to a virtual register. ** NOTE: the result may be NULL if the expression was marked ** to be discarded, or was cast to (void), or an error ** was encountered. */ VREG * genexpr(NODE *n) { if (!n) return NULL; /* Check for null exprs here */ if (n->Nflag & NF_DISCARD) /* If discarding result, */ { relflush(gexpr(n)); /* flush any resulting register(s) */ return NULL; } return gexpr(n); /* Normal case, generate code & return reg */ } /* GENXRELEASE - Auxiliary like genexpr but called when we want to make ** sure that the resulting value is forced to be discarded. */ void genxrelease(NODE *n) { n->Nflag |= NF_DISCARD; /* Will be discarding this value */ genexpr(n); } /* GEXPR - workhorse routine for genexpr(). Note arg is guaranteed non-null. */ static VREG * gexpr(NODE *n) { switch (tok[n->Nop].tktype) { case TKTY_ASOP: return gassign(n); case TKTY_TERNARY: return gternary(n); case TKTY_BINOP: return gbinary(n); case TKTY_BOOLOP: case TKTY_BOOLUN: return glogical(n); case TKTY_UNOP: return gunary(n); case TKTY_RWOP: /* For now, RW's go below */ case TKTY_PRIMARY: return gprimary(n); case TKTY_SEQ: /* Comma operator */ if (n->Nleft) genxrelease(n->Nleft); /* Flush result of left */ return genexpr(n->Nright); /* and return that of right */ } int_error("gexpr: bad op %N", n); return NULL; } /* ** RELFLUSH - Flush no-longer-wanted register value ** Mainly called by genexpr(); also called by gcastr() when casting ** a value to (void). ** Note that the reg argument may be NULL. This can happen for ** a generated value of type "void" (size 0). */ void relflush (VREG *reg) { int r; /* get physical register */ PCODE *p, *before(); if (reg == NULL) return; r = vrtoreal(reg); /* Save physical reg # */ vrfree(reg); /* Now release virtual reg or reg pair */ /* This should be moved to someplace in CCOPT */ if (optobj) for (p = previous; p != NULL; p = before (p)) { if (p->Pop == P_ADJSP) continue; /* skip back across P_ADJSP */ else if (p->Pop != P_MOVE || p->Preg != r || prevskips(p)) break; /* not flushable */ else /* Do NOT discard preserved reg values. * Later, add optimization at end of basic block to do it. */ if (Register_Nopreserve(p->Preg)) { p->Pop = P_NOP; /* drop pointless NOP */ fixprev(); /* fix up for drop */ } } } /* GTERNARY - generate code for ternary operators ** Note: handles case where one or both result expression pointers may be ** NULL. The overall value had better be "void" if so. ** ** There is some suboptimal code here, namely the call to "vrallspill()" to ** save any registers that are active at the time this operand is executed. ** This is necessary because we don't know at this point whether either the ** true or false path will require saving registers (eg if a function call is ** done), but control still has to merge back to the same place. If one ** path saves regs and the other doesn't, the stack is going to be confused ** at the point where control merges (ie at end of ternary expression). ** The active registers have to be in the same state afterwards as they ** were before, and at the moment the only safe way of doing this is to ** bite the bullet and save them all prior to doing the ternary expression. ** A similar problem exists for the logical operands; anything that ** branches during expression evaluation. Conditional statements like "if" ** are not affected because there are never any registers active across a ** statement. */ static VREG * gternary (NODE *n) { SYMBOL *false, *done; int siz; NODE *nfirst, *nsecond; VREG *reg; #if NEWTERN int uptof = 0; SYMBOL *savupto; #endif /* find the pieces of code we're going to use */ siz = sizetype(n->Ntype); /* Find size of overall result */ if (n->Nflag & NF_DISCARD) /* If result being discarded, */ siz = 0; /* pretend size is 0 (void) */ nfirst = n->Nright->Nleft; /* First (if-true) result expr */ nsecond = n->Nright->Nright; /* Second (if-false) result expr */ /* Check for (very unlikely) case of both results non-existent. ** CCEVAL's evaldiscard() should have substituted some other node op, ** so this may be a bug. We handle this mainly to ensure following ** code is guaranteed of having at least one result expr. */ if (!nfirst && !nsecond) /* Check for unlikely case */ { if (siz != 0) /* Overall result must be void! */ int_error("gternary: no operands %N", n); genxrelease(n->Nleft); /* Generate condition */ return NULL; } if (n->Ntype->Tspec == TS_ARRAY) /* Another just-in-case check */ { int_error("gternary: array type %N", n); siz = 0; } #if 1 /* Clean up previously allocated registers */ if (siz == 2) vrfree(vrretdget()); /* Make sure ACs 1 & 2 free */ else if (siz >= 1) vrfree(vrretget()); /* else just ensure AC1 free */ /* Else void return value */ #endif false = (nfirst && nsecond) /* If we'll need it, */ ? newlabel() : NULL; /* get a new label for false */ done = (n->Nendlab ? /* Get overall end label */ n->Nendlab : newlabel()); if (nfirst) nfirst->Nendlab = done; /* Make that be expr endlab */ if (nsecond) nsecond->Nendlab = done; if (!false) /* If don't have both exprs */ false = done; /* then use endlab as false jump */ /* Now just before generating the code to test a condition and branch, ** we have to ensure that any active registers are saved. See note at ** top of page. */ vrallspill(); /* There are three possible configurations: ** (1) Both nfirst and nsecond exist. Failing test jumps to "false". ** Both results must be moved to a common register. ** (2) Only nfirst exists. Failing test jumps to "done". ** We return the register nfirst gives, if any. ** (3) Only nsecond exists. Test is REVERSED; if fails, jumps to "done". ** We return the register nsecond gives, if any. ** We've already set up the "false" label to be the same as "done" ** if either of the latter two cases holds. */ reg = NULL; /* Ensure no return reg initially */ gboolean(n->Nleft, false, /* Generate code to test condition */ nfirst == NULL); /* (reverse sense if 1st is gone) */ if (nfirst) { if (siz > 0) { #if 0 /*NEWTERN*/ reg = genexpr(nfirst); /* Just return what we get */ if (optgen && nsecond /* If optimizing, and other */ && (nsecond->Nop == N_FNCALL /* val is a function call, */ || (nsecond->Nop == N_CAST && nsecond->Nleft->Nop == N_FNCALL)) && vrtoreal(reg) != R_RETVAL) /* and 1st val in diff reg, */ { code0(siz == 2 ? P_DMOVE : P_MOVE, /* then put 1st val */ VR_RETVAL, reg); /* into this reg! */ reg = (siz == 2 ? vrdget() : vrget()); } else if (optgen) /* One more optimization try */ backreg(reg); /* Flush a MOVE R,S as we don't care */ /* at this point what phys reg is */ #else if (Register_Id(nfirst)) code00(P_MOVE, R_RETVAL, nfirst->Nid->Sreg); else code0(siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nfirst)); #endif } else genxrelease(nfirst); if (nsecond) { code6(P_JRST, (VREG *)NULL, done); /* skip over the hard way */ codlabel(false); /* now start second part */ } } if (nsecond) { if (siz > 0) { #if NEWTERN if (nfirst) { savupto = cregupto(done); /* Set fence for changereg */ uptof++; /* say fence set */ } if (Register_Id(nsecond)) code00(P_MOVE, R_RETVAL, nsecond->Nid->Sreg); else code0 (siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nsecond)); #else code0 (siz == 2 ? P_DMOVE : P_MOVE, VR_RETVAL, genexpr(nsecond)); #endif } else genxrelease(nsecond); } if (n->Nendlab == NULL) { codlabel(done); /* second clause done here */ #if NEWTERN if (uptof) { cregupto(savupto); /* Restore saved fence value */ uptof = 0; } #if 1 /* If only one result register was used, try to make it a "normal" ** (non-return) reg to avoid hogging reg 1 and interfering with ** common sub-expression matching. */ if (siz > 0 && siz != 2 /* Only one register? */ && optobj) { reg = vrget(); /* Get normal reg */ reg->Vrtype = n->Ntype; /* Set C type of result */ if (codcreg(reg, VR_RETVAL)) return reg; vrfree(reg); /* didn't work, put back that reg. */ } #endif #endif } #if NEWTERN if (uptof) cregupto(savupto); /* Restore saved fence if one */ #if 0 if (reg) #else if (siz <= 0) return NULL; /* Void */ reg = (siz == 2 ? vrretdget() : vrretget()); /* One or two return regs */ #endif #endif reg->Vrtype = n->Ntype; /* Set C type of result obj */ return reg; } /* GBOOLEAN - Generate code for boolean expressions ** jump to false label if expr not true ** reverse sense of test if reverse bit set */ void gboolean(NODE *n, SYMBOL *false, int reverse) { VREG *r; int op; if (n == NULL) /* Paranoia: bug catcher */ { int_error("gboolean: null arg"); return; } /* ** The big switch. Either we call some handler routine such as ** gor or gand, or we make a skip and then a jump. If the former, ** we are done, and the call should tail recurse. Otherwise, we ** need to add the jump to the given label, so we break from the switch. */ switch (n->Nop) { case Q_NOT: n->Nleft->Nendlab = n->Nendlab; /* set up variables */ n = n->Nleft; /* with parity switched */ reverse = !reverse; /* for tail recursive call */ gboolean(n, false, reverse); /* to self */ return; case Q_LAND: if (reverse) gor(n, false, reverse); /* more tail recursion */ else gand(n, false, reverse); return; case Q_LOR: if (reverse) gand(n, false, reverse); /* still more */ else gor(n, false, reverse); return; case Q_NEQ: case Q_LEQ: case Q_GEQ: case Q_LESS: case Q_GREAT: case Q_EQUAL: gboolop(n, reverse); /* comparison, make skip */ break; /* followed by GOTO */ case N_ICONST: case N_PCONST: op = n->Niconst; /* unconditional condition */ if (reverse && op) break; /* jump when true and true? */ if (!reverse && !op) break; /* jump when false and false? */ return; default: n->Nendlab = NULL; /* cond endlab is not expr endlab */ if ((r = genexpr(n)) != NULL) /* get expression into reg (may be discarded)*/ { int bits = tbitsize(n->Ntype); /* Find # bits of value */ if (bits < TGSIZ_WORD) /* If not full wd, then */ r = guintwiden(r, bits, n); /* widen it unsignedly! */ code6(reverse? P_JUMP+POS_SKPN : P_JUMP+POS_SKPE, r, false); /* test and jump */ vrfree(r); /* now done with register */ } return; /* don't make spurious P_JRST */ } code6(P_JRST, (VREG *)NULL, false); /* broke out, want a GOTO */ } /* GOR - Generate || expression */ static void gor(NODE *n, SYMBOL *false, int reverse) { SYMBOL *lab; if ((lab = n->Nendlab) == 0) lab = newlabel(); /* get label */ gboolean(n->Nleft, lab, !reverse); /* output first clause */ n->Nright->Nendlab = lab; /* no more labels in second clause */ gboolean(n->Nright, false, reverse); if (n->Nendlab == 0) codlabel(lab); /* send out made label */ } /* GAND - Generate && expression */ static void gand(NODE *n, SYMBOL *false, int reverse) { n->Nright->Nendlab = n->Nendlab; gboolean(n->Nleft, false, reverse); gboolean(n->Nright, false, reverse); } /* GBOOLOP - Generate code for == > < <= >= != ** */ static void gboolop(NODE *n, int reverse) { int op; VREG *r1, *r2; /* ** Generate operands and skip instruction for the test ** ** Note that floating point can use the same comparison ** instructions as integers, so we don't have to test for them. */ switch (n->Nop) { case Q_EQUAL: op = P_CAM+POF_ISSKIP+POS_SKPE; break; case Q_NEQ: op = P_CAM+POF_ISSKIP+POS_SKPN; break; case Q_LEQ: op = P_CAM+POF_ISSKIP+POS_SKPLE; break; case Q_GEQ: op = P_CAM+POF_ISSKIP+POS_SKPGE; break; case Q_LESS: op = P_CAM+POF_ISSKIP+POS_SKPL; break; case Q_GREAT: op = P_CAM+POF_ISSKIP+POS_SKPG; break; } /* May need to munch on char pointers to get into comparable form */ switch (n->Nop) { case Q_LEQ: case Q_GEQ: case Q_LESS: case Q_GREAT: if (tisunsign(n->Nleft->Ntype)) /* If operands are unsigned */ { r1 = genexpr(n->Nleft); /* Get operand 1 */ code8(P_TLC, r1, 0400000L); /* and flip sign bit */ r2 = genexpr(n->Nright); /* Ditto for operand 2 */ code8(P_TLC, r2, 0400000L); break; } else if (tisbytepointer(n->Nleft->Ntype)) { /* If operands are byte pointers */ /* Note that: ** OWGBPs can omit the SKIP+TLC, or use this: ** MOVE R,PTR1 ** SUB R,PTR2 ** ROT R,6 ** CAIx R,0 ** Local-fmt BPs can use the sequence: ** MOVE R,PTR1 ** MOVE R+1,PTR2 ; Needs double reg ** ROTC R,6 ; Yes this really works! ** CAMx R,R+1 */ r1 = genexpr(n->Nleft); /* Get operand 1 */ code0(P_SKIP+POF_ISSKIP+POS_SKPL, r1, r1); code8(P_TLC, r1, 0770000L); /* Zap P bits if local */ code8(P_ROT, r1, 6); /* Get P or PS into low bits */ /* Repeat for 2nd operand */ r2 = genexpr(n->Nright); /* Get operand 2 */ code0(P_SKIP+POF_ISSKIP+POS_SKPL, r2, r2); code8(P_TLC, r2, 0770000L); /* Zap P bits if local */ code8(P_ROT, r2, 6); /* Get P or PS into low bits */ /* Now can compare the registers with normal CAM! */ break; } /* Else just fall through for normal expression evaluation */ case Q_EQUAL: case Q_NEQ: r1 = genexpr (n->Nleft); /* calculate values to compare */ r2 = genexpr (n->Nright); break; } if (reverse) op = revop (op); /* maybe invert test */ /* ** Generate and optimize the test. ** ** If we are comparing double precision floating point we need ** to look at both pairs of words, so we use a cascaded pair or ** trio of comparisons. */ if ( n->Nleft->Ntype->Tspec == TS_DOUBLE || n->Nleft->Ntype->Tspec == TS_LNGDBL ) { switch (op) { case P_CAM+POF_ISSKIP+POS_SKPL: flushcode(); /* don't confuse peepholer */ code0(P_CAM+POF_ISSKIP+POS_SKPL, r1, r2); code0(P_CAM+POF_ISSKIP+POS_SKPGE, VR2(r1), VR2(r2)); op = P_CAM+POF_ISSKIP+POS_SKPLE; break; case P_CAM+POF_ISSKIP+POS_SKPLE: flushcode(); /* don't confuse peepholer */ code0(P_CAM+POF_ISSKIP+POS_SKPL, r1, r2); code0(P_CAM+POF_ISSKIP+POS_SKPG, VR2(r1), VR2(r2)); break; case P_CAM+POF_ISSKIP+POS_SKPG: flushcode(); /* don't confuse peepholer */ code0(P_CAM+POF_ISSKIP+POS_SKPG, r1, r2); code0(P_CAM+POF_ISSKIP+POS_SKPLE, VR2(r1), VR2(r2)); op = P_CAM+POF_ISSKIP+POS_SKPGE; break; case P_CAM+POF_ISSKIP+POS_SKPGE: flushcode(); /* don't confuse peepholer */ code0(P_CAM+POF_ISSKIP+POS_SKPG, r1, r2); code0(P_CAM+POF_ISSKIP+POS_SKPL, VR2(r1), VR2(r2)); break; case P_CAM+POF_ISSKIP+POS_SKPE: code0(P_CAM+POF_ISSKIP+POS_SKPN, VR2(r1), VR2(r2)); break; case P_CAM+POF_ISSKIP+POS_SKPN: code0(P_CAM+POF_ISSKIP+POS_SKPN, VR2(r1), VR2(r2)); code0(P_CAM+POF_ISSKIP+POS_SKPE, r1, r2); code5(P_TRN+POF_ISSKIP+POS_SKPA, 0); vrfree(r1); return; } } code0(op, r1, r2); /* generate and optimize test */ vrfree(r1); } /* GASSIGN - Generate assignment expression. ** Various tricky stuff involved. ** Note the hair needed for handling compound assignment, because the f*ed-up ** peepholer zaps index registers with wild abandon. We have to compensate ** for this by being careful how we generate the address of the destination. ** ** Also note hair for storing into volatile objects! This is the counterpart ** to the fetch checking in gprimary() and gunary(). The other store code is ** in gincdec(). */ static VREG * gassign(NODE *n) { VREG *r1, *r2, *ra; int ptr, savaddr; INT siz; NODE *nod; /* Points to lvalue (without conversion) */ int lconv; /* Holds lvalue conversion op if any */ int volat; /* True if obj is volatile */ TYPE *fromt, *tot; nod = n->Nleft; if (nod->Nop == N_CAST) /* If lvalue needs conversion before the op */ { lconv = nod->Ncast; /* Remember conversion op for lvalue */ tot = nod->Ntype; /* cast to this type */ nod = nod->Nleft; /* Then get ptr to real lvalue */ fromt = nod->Ntype; /* cast from this type */ } else lconv = CAST_NONE; siz = sizetype(n->Ntype); /* Get size of result type, in words */ /* See if object will be referenced via a byte pointer */ if ((ptr = bptrref(nod)) < 0) { int_error("gassign: bad op %N", nod); ptr = 0; } if ((volat = tisanyvolat(nod->Ntype)) != 0) flushcode(); /* Barf, foil peepholer if volatile obj */ if (n->Nop == Q_ASGN) /* Simple assignment? */ { r1 = genexpr(n->Nright); /* Generate value first */ /* Special check for doing IDPB. Safer to do here instead of ** in peephole, at least until peepholer fixed to allow keeping ** an index reg around! */ #if 0 /* Later, add many cases here when new MACRO instructions are defined. * Imitate case N_PTR: in gunary() below. */ #endif if (optgen && ptr /* If a byte ptr */ && nod->Nop == N_PTR /* and op is "*++(exp)" */ && nod->Nleft->Nop == N_PREINC) { #if 0 /* Later, fix Reg linkage for pointers */ if (Register_Id(nod->Nleft->Nleft)) code40(P_IDPB, r1->Vrloc, nod->Nleft->Nleft->Nid->Sreg, 0); else #endif code4(P_IDPB, r1, gaddress(nod->Nleft->Nleft)); return r1; } else if (Register_Id(nod)) /* approximate stomem for registers */ { r_preserve = nod->Nid->Sreg; if (siz == 1) { ra = vrget(); ra->Vrtype = nod->Ntype; code00(P_MOVE, ra->Vrloc, r1->Vrloc); } else int_error ("gassign: reg argument size > 1"); vrfree (r1); return ra; } else r1 = stomem(r1, /* Store the value */ gaddress(nod), /* into address of lvalue */ /* Operand and operation types are same, so siz is correct */ siz, ptr); /* and flag saying if addr is ptr */ if (volat) flushcode(); return r1; } /* Some compound assignment type. ** First, generate the right operand, including any conversions. */ r2 = (n->Ntype->Tspec == TS_PTR) ? /* Doing pointer arith? */ gptraddend(n->Nleft->Ntype, n->Nright) /* Operand for ptr arith */ : genexpr(n->Nright); /* General-type operand */ /* Then generate the left operand. For the time being, the peephole ** optimizer is so screwed up that we can't keep the address around ** and have to generate it twice. */ savaddr = sideffp(nod); /* Warn user if we'll fail */ if (Register_Id(nod)) /* approximate getmem() for registers */ { r_preserve = nod->Nid->Sreg; ra = vrget(); ra->Vrtype = nod->Ntype; #if 0 r1 = rgetmem(ra, nod->Ntype, ptr, savaddr); #else r1 = rgetmem(ra, nod->Ntype, savaddr); #endif } else r1 = getmem(ra=gaddress(nod), /* Get left operand, WITHOUT releasing addr!*/ nod->Ntype, /* using its real type */ ptr, /* addr may be a byte pointer */ savaddr); /* Keep the address reg! */ /* Now have left operand in R1. Convert it for operation, if needed. */ if (lconv != CAST_NONE) /* Convert left operand if necessary */ r1 = gcastr(lconv, r1, fromt, tot, nod); /* Apply the arithmetic operation, checking to make sure pointer ** arithmetic is handled properly. r2 is released. */ if (n->Ntype->Tspec == TS_PTR) /* If doing pointer arith */ r1 = gptrop(n->Nop, r1, r2, n->Ntype, n->Nright->Ntype); else r1 = garithop(n->Nop, r1, r2, n->Nleft->Ntype->Tspec); /* Now see if there's any assignment conversion to perform on ** the result of the operation. */ if (n->Nascast != CAST_NONE) { r1 = gcastr(n->Nascast, r1, n->Nleft->Ntype, n->Ntype, (NODE *)NULL); } if (Register_Id(nod)) /* approximate stomem for registers */ { if (siz == 1) { code00(P_MOVE, nod->Nid->Sreg, r1->Vrloc); if (Register_Nopreserve(ra->Vrloc)) /* garithop changed it!? */ vrfree (ra); return r1; } else int_error ("gassign: reg argument size > 1"); return ra; } else { /* Finally, can store the value back. We either use the ** saved address, if one, or generate it all over again. */ if (!savaddr) ra = gaddress(nod); /* Else re-use saved addr */ r1 = stomem(r1, ra, siz, ptr); } if (volat) flushcode(); /* Barf bletch */ return r1; } /* GBINARY - Generate code for binary operators. ** */ static VREG * gbinary(NODE *n) { VREG *r1, *r2; /* ** First, check for pointer arithmetic. Legal operations are: ** Operation Result ** (1) num + ptr ptr ** (2) ptr + num ptr ** (3) ptr - num ptr ** (4) ptr - ptr int or long ** ** If the pointer is a byte pointer, we always make the number first. ** This is only because the current optimizer is too stupid to recognize ** certain patterns any other way. */ if (n->Ntype->Tspec == TS_PTR /* Catch cases 1, 2, 3 */ || n->Nleft->Ntype->Tspec == TS_PTR) /* Catch case 4 */ { if (n->Nop == Q_MINUS) /* Cases 3 and 4 */ { if (n->Nright->Ntype->Tspec == TS_PTR) /* Case 4: ptr-ptr */ { r1 = genexpr(n->Nleft); /* Make the left operand 1st */ return gptrop(n->Nop, r1, genexpr(n->Nright), n->Nleft->Ntype, n->Nright->Ntype); } else /* Case 3: ptr-num */ { r1 = genexpr(n->Nleft); /* Make ptr */ r2 = gptraddend(n->Nleft->Ntype, n->Nright); /* Make num */ return gptrop(n->Nop, r1, r2, n->Nleft->Ntype, n->Nright->Ntype); } } /* Cases 1 and 2 */ if (n->Nleft->Ntype->Tspec != TS_PTR) /* Do case 1: num+ptr */ { r2 = gptraddend(n->Nright->Ntype,n->Nleft); /* Make num 1st */ return gptrop(n->Nop, genexpr(n->Nright), r2, n->Nright->Ntype, n->Nleft->Ntype); /* reversed */ } else /* Do case 2: ptr+num */ { r1 = genexpr(n->Nleft); /* Make ptr 1st */ r2 = gptraddend(n->Nleft->Ntype,n->Nright); /* num 2nd */ return gptrop(n->Nop, r1, r2, n->Nleft->Ntype, n->Nright->Ntype); } } /* No pointer arithmetic involved, can just generate arithmetic stuff. ** Normally we generate the left operand first, but if the right operand ** is a function call then we reverse the order so as to avoid ** saving/restoring registers across the call. ** Also, if using normal ordering, we check to see whether the left ** operand will need to be widened (since integer division requires ** a doubleword register), and if so widen it ahead of time so that ** the generation of the right operand won't suboptimally seize the ** 2nd register and then have to be shuffled around later. */ if (n->Nright->Nop == N_FNCALL && optgen) { r2 = genexpr(n->Nright); /* Do function call first */ r1 = genexpr(n->Nleft); /* then left operand */ } else { r1 = genexpr(n->Nleft); /* Normal order, left first */ if ((n->Nop == Q_DIV || n->Nop == Q_MOD) && tisinteg(n->Ntype) && optgen) vrlowiden(r1); /* Widen in preparation for div */ r2 = genexpr(n->Nright); /* Now generate right operand */ } return garithop(n->Nop, r1, r2, n->Ntype->Tspec); } /* GARITHOP - Generate code for binary arithmetic operators ** given values in registers. ** The only types permitted are: ** TS_FLOAT, TS_DOUBLE, TS_LNGDBL ** TS_INT, TS_UINT ** TS_LONG, TS_ULONG ** Note that types "char" and "short" should already have been converted ** (via usual unary/binary conversions) to "int" before the operation ** is performed. */ static VREG * garithop(op, r1, r2, ts) int op; /* Operation to generate code for */ int ts; /* Type of the operands (TS_ value) */ VREG *r1, *r2; /* Registers operands are in (r2 is released) */ { switch(op) { case Q_ASPLUS: case Q_PLUS: switch (ts) { default: int_error("garithop: bad +"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: code0(P_ADD, r1, r2); break; case TS_FLOAT: code0(P_FADR, r1, r2); break; case TS_DOUBLE: case TS_LNGDBL: code0(P_DFAD, r1, r2); break; } break; case Q_ASMINUS: case Q_MINUS: switch (ts) { default: int_error("garithop: bad -"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: code0(P_SUB, r1, r2); break; case TS_FLOAT: code0(P_FSBR, r1, r2); break; case TS_DOUBLE: case TS_LNGDBL: code0(P_DFSB, r1, r2); break; } break; /* * Unsigned Multiplication ** MUL R,E ** TRNE R,1 or LSH R+1,1 or LSH R+1,1 ** TLOA R+1,400000 LSHC R,-1 LSHC R,-35. ** TLZ R+1,400000 ** result in R+1 result in R+1 result in R */ case Q_ASMPLY: case Q_MPLY: switch (ts) { default: int_error("garithop: bad *"); case TS_UINT: case TS_ULONG: if (!vrispair(r1)) /* Unless already widened, */ vrlowiden(r1); /* grab two words for the multiply */ code0(P_MUL, r1, r2); code8(P_TRN+POF_ISSKIP+POS_SKPE, r1, 1); code8(P_TLO+POF_ISSKIP+POS_SKPA, VR2(r1), 0400000L); code8(P_TLZ, VR2(r1), 0400000L); vrnarrow(r1 = VR2(r1)); /* Narrow back, keep 2nd wd */ break; case TS_INT: case TS_LONG: code0(P_IMUL, r1, r2); break; case TS_FLOAT: code0(P_FMPR, r1, r2); break; case TS_DOUBLE: case TS_LNGDBL: code0(P_DFMP, r1, r2); break; } break; /* Integer division is done differently from other integer operations ** because the IDIV instruction produces a doubleword result. ** Note that we can't do the apparent optimization of using ASH or AND ** when the divisor is a constant power of two, because they perform ** inconsistently with IDIV on negative numbers. */ case Q_ASDIV: case Q_DIV: switch (ts) { default: int_error("garithop: bad /"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: /* Hair for integer division */ { int save_reg = r1->Vrloc; if (!vrispair(r1)) /*Unless already widened by gbinary,*/ vrlowiden(r1); /* grab two words for the division. */ code0((tspisunsigned(ts) ? P_UIDIV : P_IDIV), r1, r2); vrnarrow(r1); /* Narrow back, keep 1st word */ if (Register_Preserve(save_reg) && save_reg != r1->Vrloc) { code00(P_MOVE, save_reg, r1->Vrloc); r1->Vrloc = save_reg; } folddiv(r1); /* Do cse on result */ break; } case TS_FLOAT: code0(P_FDVR, r1, r2); break; case TS_DOUBLE: case TS_LNGDBL: code0(P_DFDV, r1, r2); break; } break; case Q_ASMOD: case Q_MOD: switch (ts) { default: int_error("garithop: bad %%"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: /* Hair for integer remainder */ if (!vrispair(r1)) /* Unless already widened by gbinary,*/ vrlowiden(r1); /* grab two words for the division. */ code0((tspisunsigned(ts) ? P_UIDIV : P_IDIV), r1, r2); vrnarrow(r1 = VR2(r1)); /* Narrow back, keep 2nd word */ folddiv(r1); /* Do cse on result */ break; } break; case Q_ASRSH: case Q_RSHFT: code0(P_MOVN, r2, r2); /* negate arg to make right shift */ /* Then drop through to do shift */ case Q_ASLSH: case Q_LSHFT: switch (ts) { default: int_error("garithop: bad shift"); case TS_INT: /* Signed values use arith shift for >> */ case TS_LONG: if (op == Q_ASRSH || op == Q_RSHFT) { code4(P_ASH, r1, r2); break; } /* Drop thru if <<, for logical shift. */ /* According to CARM, << is always logical even if signed */ case TS_UINT: /* Unsigned values use logical shift */ case TS_ULONG: code4(P_LSH, r1, r2); /* this takes arg as if PTA_RCONST */ break; } break; case Q_ASOR: case Q_OR: switch (ts) { default: int_error("garithop: bad |"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: code0 (P_IOR, r1, r2); break; } break; case Q_ASAND: case Q_ANDT: switch (ts) { default: int_error("garithop: bad &"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: code0 (P_AND, r1, r2); break; } break; case Q_ASXOR: case Q_XORT: switch (ts) { default: int_error("garithop: bad ^"); case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: code0 (P_XOR, r1, r2); break; } break; default: int_error("garithop: bad op %d", op); vrfree(r2); } return r1; } /* GPTROP - Generate code for pointer arithmetic operations. ** Legal pointer arithmetic operations are: ** Operation Result ** * (1) num + ptr ptr ** (2) ptr + num ptr ** (3) ptr - num ptr ** (4) ptr - ptr int or long ** ** NOTE: It is the caller's responsibility to swap the operands of case 1 to ** transform it into case 2. It is up to the caller to decide which one ** to generate first; however, for case 4 it is probably best to do the ** left operand first. ** If the 2nd operand is a number it must have been generated by ** gptraddend (rather than genexpr). In this case, r2 may be NULL if ** gptraddend has determined that the number is zero and nothing needs ** to be added or subtracted. */ static VREG * gptrop(op, r1, r2, lt, rt) int op; /* Q_PLUS, Q_MINUS, Q_ASPLUS, Q_ASMINUS */ VREG *r1, *r2; /* Registers holding left and right operands */ TYPE *lt, *rt; /* Types of left and right operands */ { INT size; switch (op) { case Q_ASMINUS: case Q_MINUS: if (rt->Tspec == TS_PTR) /* Handle case 4 */ /* Handle case 4: ptr-ptr (make left operand first) */ { if (tisbytepointer(lt)) { vrlowiden(r1); /* Must widen */ code0(P_SUBBP, r1, r2); /* Do the sub */ vrnarrow(r1 = VR2(r1)); /* Result in 2nd word */ } else code0(P_SUB, r1, r2); if ((size = sizeptobj(lt)) > 1) { vrlowiden(r1); /* Ugh, must adjust result */ code1(P_IDIV, r1, size); vrnarrow(r1); /* Narrow to get result in 1st wd */ folddiv(r1); } break; } /* Handle case 3: ptr-num. Num must be generated by gptraddend. */ if (r2 == NULL) return r1; /* Ensure have something to subtract */ if (tisbytepointer(lt)) { code0(P_MOVN, r2, r2); code0(P_ADJBP, r2, r1); /* Adjust char pointer */ return r2; } code0(P_SUB, r1, r2); /* Adjust word pointer */ break; case Q_ASPLUS: case Q_PLUS: /* Handle case 2: ptr+num. Num must be generated by gptraddend. */ /* Note that case 1 should be transformed into case 2 by caller. */ if (r2 == NULL) return r1; /* Ensure something to add */ if (tisbytepointer(lt)) /* If ptr is a char ptr */ { code0(P_ADJBP, r2, r1); /* Adjust char pointer */ return r2; } code0(P_ADD, r2, r1); /* Adjust word pointer */ return r2; default: int_error("gptrop: bad op %d", op); } return r1; } /* GPTRADDEND - Auxiliary to GPTROP. This routine generates the ** proper value for adding or subtracting from a pointer. ** Note that it may return NULL if it determines that the value ** is zero; that is, no value (and no operation) is necessary. */ static VREG * gptraddend(TYPE *t, NODE *n) /* Type of the pointer this value is being added to and Addend(or subtrahend) * expression */ { VREG *r; INT size; if (n->Nop == N_ICONST && optgen) /* Do optimization */ { size = sizeptobj(t) * n->Niconst; /* If num is a constant */ if (size == 0) return NULL; /* Zero value, gen nothing! */ r = vrget(); code1(P_MOVE, r, size); r->Vrtype = n->Ntype; /* Set C type of object in reg */ return r; } r = genexpr(n); /* First generate value as given */ if ((size = sizeptobj(t)) > 1) /* Then check to see if mult needed */ code1(P_IMUL, r, size); /* Yeah, multiply it by size of obj */ return r; } /* GLOGICAL - Generate code for boolean binary & unary operators */ static VREG * glogical(NODE *n) { VREG *reg; SYMBOL *false, *true, *temp; int reverse; reverse = (optgen && n->Nop == Q_LOR); n->Nendlab = true = newlabel(); /* get label for true case */ false = newlabel(); /* get label for false case */ /* ** See gternary() for an explanation of why this call is needed. */ vrallspill(); gboolean (n, false, reverse); /* make the boolean code */ if (optgen && unjump (false)) /* can put false case first? */ { temp = false; /* yes, swap meaning of false */ false = true; /* and true, so labels go out */ true = temp; /* in correct order. */ reverse = !reverse; /* also invert reversal switch */ } if (n->Nflag & NF_RETEXPR) reg = vrretget(); /* get value in return reg */ else reg = vrget(); /* not for return, use normal reg */ reg->Vrtype = n->Ntype; /* Set C type of object in reg */ codlabel(true); /* true label goes here */ if (reverse) code0(P_TDZ+POF_ISSKIP+POS_SKPA, reg, reg); /* make zero, skip */ else code1(P_SKIP+POF_ISSKIP+POS_SKPA, reg, 1); /* make one, skip */ codlabel(false); /* now make false label */ if (reverse) code1(P_MOVE, reg, 1); /* reversed false makes one */ else code5(P_SETZ, reg); /* normal false makes zero */ return reg; /* return the register */ } extern int _chnl; /* GUNARY - Generate code for unary operators */ static VREG * gunary(NODE *n) { VREG *r; int volat; switch (n->Nop) { case N_PREINC: return gincdec(n, 1, 1); case N_PREDEC: return gincdec(n, -1, 1); case N_POSTINC: return gincdec(n, 1, 0); case N_POSTDEC: return gincdec(n, -1, 0); case N_CAST: return gcast(n); case N_ADDR: return gaddress(n->Nleft); case N_PTR: /* See comments at gprimary() about volatile objects. */ if ((volat = tisvolatile(n->Ntype)) != 0) flushcode(); /* Obj is volatile, avoid optimiz */ if (debcsi == KCC_DBG_NULL) { _chnl = n->sfline; switch (n->Nleft->Nop) { case Q_IDENT: code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft)); break; case N_PREINC: case N_PREDEC: case N_POSTINC: case N_POSTDEC: code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft->Nleft)); break; default: _chnl = -1; break; } } /* Special check for doing ILDB. Safer to do here instead of ** in peephole, at least until peepholer fixed to allow keeping ** an index reg around! */ if (Register_Id(n->Nleft)) { #if 0 /* Reg linkage */ if (optgen && tisbytepointer(n->Nleft->Ntype)) /* if byte ptr */ { r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ if (n->Nleft->Nop == N_PREINC) /* "*++(reg)" */ code0 (P_ILDB, r, gaddress (n->Nleft->Nleft)); #if 0 /* add when LDBI, LDBD, and DLDB are defined here, below, and in * gassign() for case N_PTR: */ else if (n->Nleft->Nop == N_PREDEC) /* "*--(reg) */ code0 (P_DLDB, r, gaddress (n->Nleft->Nleft)); else if (n->Nleft->Nop == N_POSTINC) /* "*(reg)++" */ code0 (P_LDBI, r, gaddress (n->Nleft->Nleft)); else if (n->Nleft->Nop == N_POSTDEC) /* "*(reg)--" */ code0 (P_LDBD, r, gaddress (n->Nleft->Nleft)); else if (n->Nleft->Nop == NULL) code0 (P_LDB, r, gaddress (n->Nleft->Nleft)); #endif else /* approximate getmem() for registers */ #if 0 r = rgetmem(genexpr(n->Nleft), n->Ntype, tisbytepointer(n->Nleft->Ntype), 0); #else r = rgetmem(genexpr(n->Nleft), n->Ntype, 0); #endif } else /* approximate getmem() for registers */ #endif #if 0 r = rgetmem(genexpr(n->Nleft), n->Ntype, tisbytepointer(n->Nleft->Ntype), 0); #else r = rgetmem(genexpr(n->Nleft), n->Ntype, 0); #endif } else /* if "*++(exp)" of a byte pointer */ if (optgen && tisbytepointer(n->Nleft->Ntype) && n->Nleft->Nop == N_PREINC) { r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code4 (P_ILDB, r, gaddress(n->Nleft->Nleft)); } else r = getmem(genexpr(n->Nleft), n->Ntype, tisbytepointer(n->Nleft->Ntype), 0); if (volat) flushcode(); return r; case Q_MUUO: return gmuuo(n); case N_NEG: if (Register_Id(n->Nleft)) { #if 0 if ( n->Ntype->Tspec == TS_DOUBLE || n->Ntype->Tspec == TS_LNGDBL) { r = vrdget(); r->Vrtype = n->Nleft->Ntype; code00(P_DMOVN, r->Vrloc, n->Nleft->Nid->Sreg); } else #endif { r = vrget(); r->Vrtype = n->Nleft->Ntype; code00(P_MOVN, r->Vrloc, n->Nleft->Nid->Sreg); } return r; } r = genexpr(n->Nleft); if ( n->Ntype->Tspec == TS_DOUBLE || n->Ntype->Tspec == TS_LNGDBL) code0(P_DMOVN, r, r); else code0(P_MOVN, r, r); return r; case Q_COMPL: if (Register_Id(n->Nleft)) { r = vrget(); r->Vrtype = n->Nleft->Ntype; code00(P_SETCM, r->Vrloc, n->Nleft->Nid->Sreg); return r; } r = genexpr(n->Nleft); code0(P_SETCM, r, r); return r; default: int_error("gunary: bad op %N", n); return 0; } } /* GCAST - Generate code for type conversion (cast) ** ** Note that the way we manage the task of keeping char values ** masked off is NOT by implementing a mask for casts to (char) type. ** Rather, we mask the register value only when widening. This works ** because a value of type (char) is always either assigned to a (char) object ** (in which case a byte pointer is used and the mask is automatic) or ** it is used in an expression -- and always promoted to an int or u_int. ** The masking would be wasteful and unnecessary for the first case, and ** the second case will always have an explicit N_CAST to widen the integer. ** See the INTERN.DOC file for a better explanation. */ static VREG * gcast(NODE *n) { VREG *r; /* If this expression is a return value, see if we can pass on ** the flag which marks it thusly. This basically benefits ** gcall() which uses the flag to do tail recursion; we want to ensure ** that a no-op cast won't prevent this optimization. */ if ((n->Nflag & NF_RETEXPR) /* This expr is a return val? */ && gcastr(n->Ncast, (VREG *)NULL, /* and cast is a no-op? */ n->Nleft->Ntype, n->Ntype, n->Nleft) == NULL) { n->Nleft->Nflag |= NF_RETEXPR; /* Yes, pass flag on! */ if ((r = genexpr(n->Nleft)) != NULL) /* No cast, just generate expr */ r->Vrtype = n->Ntype; /* and reflect correct type */ return r; } return gcastr(n->Ncast, genexpr(n->Nleft), n->Nleft->Ntype, n->Ntype, n->Nleft); } static VREG * gcastr(cop, r, tfrom, tto, ln) int cop; /* Cast op (a CAST_ value) */ VREG *r; /* Virtual reg holding value to cast. ** NOTE NOTE NOTE!!! If this is NULL, we are merely testing ** to see whether a cast would be produced. If there is ** no cast, NULL will be returned, else (VREG *)-1. */ TYPE *tfrom, *tto; NODE *ln; /* If non-null, is node that R was generated from. */ { switch (cop) { case CAST_NONE: /* No actual action required */ break; case CAST_VOID: /* Throwing away the value */ if (r) relflush(r); /* Release the register */ return NULL; case CAST_IT_PT: if (!r) /* Just checking? */ return gintwiden(r, tfrom, uinttype, ln); else r = gintwiden(r, tfrom, uinttype, ln); /* Widen int to uint */ break; case CAST_IT_EN: case CAST_IT_IT: if (!r) /* Just checking? */ return gintwiden(r, tfrom, tto, ln); else #if 0 /* Later, Reg linkage */ if (Register_Nopreserve (r->Vrloc) && unsigned) #endif r = gintwiden(r, tfrom, tto, ln); /*Widen integer if needed */ break; case CAST_EN_EN: case CAST_EN_IT: case CAST_PT_IT: /* No representation change needed */ break; case CAST_PT_PT: /* General ptr to ptr conversion */ if (tisbytepointer(tfrom)) { if (tisbytepointer(tto)) { /* Byte pointer to byte pointer, check sizes */ int fsiz = elembsize(tfrom); int tsiz = elembsize(tto); if (!fsiz) { /* (void *) to byte pointer. */ if (tischarpointer(tto)) /* If any kind of char obj, */ break; /* do no conversion. */ fsiz = TGSIZ_CHAR; /* Else cvt as if (char *) */ } if (!tsiz) { /* Byte pointer to (void *) */ if (tischarpointer(tfrom)) /* If any kind of char obj, */ break; /* do no conversion. */ tsiz = TGSIZ_CHAR; /* Else cvt as if (char *) */ } if (fsiz == tsiz) break; /* No conversion needed? */ if (!r) return (VREG *)-1; /* Need, stop if just chking */ /* If converting between char and short ** (9 and 18 bit bytes), use special op. */ if ( (fsiz == TGSIZ_CHAR && tsiz == TGSIZ_SHORT) || (fsiz == TGSIZ_SHORT && tsiz == TGSIZ_CHAR)) { code10(P_PTRCNV, r, (SYMBOL *)NULL, tsiz, fsiz); break; } /* Odd size, convert to word pointer, then to byte pointer. */ code10(P_TDZ+POF_ISSKIP+POS_SKPE, /* Check for NULL */ r, (SYMBOL *)NULL, -1, 0); /* Mask off P+S */ code10(P_IOR, r, (SYMBOL *) NULL, tsiz, 0); /* make BP */ } else { /* Byte pointer (any kind!) to word pointer */ if (!r) return (VREG *)-1; /* Stop if just checking. */ code10(P_TDZ, r, (SYMBOL *) NULL, -1, 0); /* Mask off P+S */ } } else if (tisbytepointer(tto)) { int tsiz; /* Word pointer to byte pointer */ if (!r) return (VREG *)-1; /* Stop if just checking. */ if ((tsiz = elembsize(tto)) == 0) /* Check for (void *) */ tsiz = TGSIZ_CHAR; pitopc(r, tsiz, 0, 0); } break; case CAST_FP_IT: if (!r) return (VREG *)-1; /* Stop if just checking. */ switch (tfrom->Tspec) { case TS_FLOAT: code0(P_FIX, r, r); /* just use that! */ break; case TS_DOUBLE: case TS_LNGDBL: code0(P_DFIX, r, r); /* r must be a register pair */ vrnarrow(r); /* Use 1st AC as result */ break; } /* Narrow the int here if needed */ break; case CAST_FP_FP: switch (castidx(tfrom->Tspec,tto->Tspec)) { case castidx(TS_DOUBLE,TS_FLOAT): case castidx(TS_LNGDBL,TS_FLOAT): if (!r) return (VREG *)-1; /* Stop if just checking. */ code0(P_DSNGL, r, r); /* r must be a register pair! */ vrnarrow(r); /* Forget about the second word */ break; case castidx(TS_FLOAT,TS_DOUBLE): case castidx(TS_FLOAT,TS_LNGDBL): if (!r) return (VREG *)-1; /* Stop if just checking. */ vrlowiden(r); code5(P_SETZ, VR2(r)); break; case castidx(TS_LNGDBL,TS_DOUBLE): case castidx(TS_DOUBLE,TS_LNGDBL): break; } break; case CAST_IT_FP: if (!r) return (VREG *)-1; /* Stop if just checking. */ r = gintwiden(r, tfrom, /* Ensure widened to int or unsigned */ tissigned(tfrom) ? inttype : uinttype, ln); switch (tto->Tspec) { case TS_FLOAT: /* Although FLTR and UFLTR are always supported by CCOUT, ** on KA-10s they are inefficient enough that it is worth ** checking for the opportunity to use a simple FSC, which ** is limited to integers of 27 bits or less. */ if (tissigned(tfrom) || tbitsize(tfrom) < TGSIZ_WORD) { /* Signed or known positive */ code0(P_FLTR, r, r); /* Use FLTR instr or macro */ break; } /* Ugh, unsigned full word value, must use hairy UFLTR. */ code0(P_UFLTR, r, r); /* Use UFLTR simulated op */ break; case TS_DOUBLE: case TS_LNGDBL: vrlowiden(r); /* Make into register pair */ code5(P_SETZ, VR2(r)); /* zero the next reg */ if (tissigned(tfrom) || tbitsize(tfrom) < TGSIZ_WORD) { code8(P_ASHC, r, -8); /* shift out mantissa*/ code8(P_TLC, r, 0243000L); /* put exponent in */ } else /* Unsigned conversion */ { code8(P_LSHC, r, -9); /* Shift unsigned */ code8(P_LSH, VR2(r), -1); /* Fix up lo wd */ code8(P_TLC, r, 0244000L); /* exp (note 1 bigger!) */ } code9(P_DFAD, r, 0.0, 1); /* Normalize the result */ break; } break; default: int_error("gcastr: bad cast %d", cop); return NULL; } /* Cast done, now set new type of object in virtual register! */ if (r) r->Vrtype = tto; return r; } /* GINTWIDEN and GUINTWIDEN - Auxiliaries for GCAST to widen integral values. ** Always widens to full word even if new type is smaller, because ** it's just as easy and makes no difference to handling of new type. ** NOTE: treats a VREG arg of NULL just as gcastr() does, i.e. only checks ** to see whether a conversion would be necessary or not. ** GUINTWIDEN is a subroutine just so gboolean() can invoke it to force ** an unsigned-type widen. */ static VREG * gintwiden(VREG *r, TYPE *tfrom, TYPE *tto, NODE *n) /* Node that R was generated from (if any) */ { if (tbitsize(tto) > tbitsize(tfrom)) { if (tisunsign(tfrom)) /* Handle unsigned. Easy, just mask off */ { r = guintwiden(r, tbitsize(tfrom), n); } else /* Handle signed. Harder, must test bit. */ { if (!r) return (VREG *)-1; /* Stop if just checking. */ if (tbitsize(tfrom) == TGSIZ_HALFWD) /* Special case */ { code0(P_HRRE, r, r); /* Extend sign of halfwd */ return r; } code8(P_TRN+POF_ISSKIP+POS_SKPE, r, (1<<(tbitsize(tfrom)-1))); code8(P_TRO+POF_ISSKIP+POS_SKPA, r, -(1 << tbitsize(tfrom))); code1(P_AND, r, (1 << tbitsize(tfrom))-1); /* Positive, zap! */ } } return r; } static VREG * guintwiden(VREG *r, int fbitsize, NODE *n) /* # bits of value in R and Node that R was generated from (if any) */ { /* Must zap high-order bits. Try to avoid doing this by ** seeing whether those bits are known to already be zero. ** Primary case is that of an LDB data fetch. */ if (!(n && (bptrref(n) > 0 /* Win if LDB fetch */ || (n->Nop == Q_ASGN /* Or if an assignment of a */ && bptrref(n->Nright) > 0 /* LDB also of safe size */ && tbitsize(n->Nright->Ntype) <= fbitsize))) ) { if (!r) return (VREG *)-1; /* Stop if just checking. */ code1(P_AND, r, ((INT) 1 << fbitsize)-1); /* Zap! */ } return r; } /* GINCDEC - Generate code for prefix/postfix increment/decrement. ** This is special-cased (instead of being handled by general ** arith code) both for efficiency and because the address is ** only supposed to be evaluated once. The code also checks ** for NF_DISCARD to see whether the result value is needed or not; ** if not, it forces the operation to be prefix instead of postfix, ** so that all fixup work can be avoided! */ static VREG * gincdec(NODE *n, int inc, int pre) /* The inc/dec expression node, +1 for increment, -1 for decrement, and * True if prefix, else postfix. */ { VREG *r, *ra, *r2; INT size = 1; /* Default size for most common case */ int savaddr; int volat; if (n->Nflag & NF_DISCARD) /* Will result be discarded? */ pre = 1; /* If so, prefix form is always better! */ n = n->Nleft; /* Mainly interested in operand */ if ((volat = tisvolatile(n->Ntype)) != 0) flushcode(); /* Barfo, avoid optimiz of volatile obj */ if (Register_Id(n)) { void codr1(int, int, INT); if (pre) /* r->Vrloc = n->Nid->Sreg, if preserve reg */ r_preserve = n->Nid->Sreg; switch(n->Ntype->Tspec) { case TS_FLOAT: r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code00(P_MOVE, r->Vrloc, n->Nid->Sreg); codr1(P_FADR, n->Nid->Sreg,(INT) ((inc > 0)? 1.0 : -1.0)); // FW KCC-NT break; #if 0 /* for next version of KCC regs */ case TS_DOUBLE: case TS_LNGDBL: r = vrdget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ if (!pre) { code00(P_DMOVE, r->Vrloc, n->Nid->Sreg); r_preserve = n->Nid->Sreg; ra = vrdget(); ra->Vrtype = n->Ntype;/* Set C type of object in reg */ code9(P_DFAD, ra, ((inc > 0)? 1.0 : -1.0), 1); vrfree(ra); } else code9(P_DFAD, r, ((inc > 0)? 1.0 : -1.0), 1); break; case TS_PTR: /* Hacking pointer? */ case TS_ENUM: case TS_BITF: case TS_UBITF: #endif case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: case TS_CHAR: case TS_UCHAR: case TS_SHORT: case TS_USHORT: r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code00(P_MOVE, r->Vrloc, n->Nid->Sreg); codr1(P_ADD, n->Nid->Sreg,((inc > 0)? 1 : -1)); break; default: int_error("gincdec: bad reg type %N", n); return NULL; } } else switch (n->Ntype->Tspec) { case TS_FLOAT: r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code9(P_MOVE, r, (inc > 0 ? 1.0 : -1.0), 0); code4(P_FADR+POF_BOTH, r, gaddress(n)); if (!pre) code9(P_FSBR, r, (inc > 0 ? 1.0 : -1.0), 0); break; case TS_DOUBLE: case TS_LNGDBL: r = vrdget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ if ((savaddr = sideffp(n)) != 0) /* See if lvalue has side effects */ { ra = gaddress(n); /* Yes, make address first */ code9(P_DMOVE, r, (inc > 0 ? 1.0 : -1.0), 1); codek4(P_DFAD, r, ra); /* Do op, keep address reg around */ code4(P_DMOVEM, r, ra); } else { code9(P_DMOVE, r, (inc > 0 ? 1.0 : -1.0), 1); code4(P_DFAD, r, gaddress(n)); code4(P_DMOVEM, r, gaddress(n)); } if (!pre) code9(P_DFSB, r, (inc > 0 ? 1.0 : -1.0), 1); break; case TS_PTR: /* Hacking pointer? */ size = sizeptobj(n->Ntype); /* Find size of obj */ if (!size) int_error("gincdec: 0-size obj %N", n); if (tisbytepointer(n->Ntype)) /* Special if a (char *) */ { if (inc < 0) size = -size; if ((savaddr = sideffp(n)) != 0) /* See addr has side effs */ ra = gaddress(n); /* Ugh, find & save it */ r = vrget(); r->Vrtype = n->Ntype; /* Set C type of obj in reg */ /* If doing post-increment, save orig pointer value */ if (!pre) { r2 = vrget(); r->Vrtype = n->Ntype; /* Set C type of obj in reg */ if (savaddr) codek4(P_MOVE, r2, ra); /* Save ptr */ else code4(P_MOVE, r2, gaddress(n)); } /* Now perform the increment. If the address of the pointer ** was saved in ra, it is released in this process. r has ** a copy of the new pointer value. */ if (size == 1) /* Special case */ { if (savaddr) codek4(P_IBP, 0, ra); else code4(P_IBP, (VREG *)NULL, gaddress(n)); if (pre) /* If will need val, get it. */ code4(P_MOVE, r, (savaddr ? ra : gaddress(n))); } else /* General case */ { code1(P_MOVE, r, size); /* get how much */ if (savaddr) codek4(P_ADJBP, r, ra); else code4(P_ADJBP, r, gaddress(n)); code4(P_MOVEM, r, /* store back in memory */ (savaddr ? ra : gaddress(n))); } /* Now, if doing postincrement, flush r and use r2 instead */ if (!pre) { vrfree(r); r = r2; } break; /* Break out to return R */ } /* Drop through to handle non-char pointer as integer */ case TS_ENUM: case TS_INT: case TS_UINT: case TS_LONG: case TS_ULONG: r = vrget(); r->Vrtype = n->Ntype; /* Set C type of obj in reg */ if (size == 1) code4((inc > 0 ? P_AOS : P_SOS), r, gaddress(n)); else /* inc/dec by non-1 integer */ { code1(P_MOVE, r, (inc > 0 ? size : -size)); code4(P_ADD+POF_BOTH, r, gaddress(n)); } if (!pre) /* For postincrement, undo reg */ code1((inc > 0 ? P_SUB : P_ADD), r, size); /* undo change */ break; case TS_BITF: case TS_UBITF: case TS_CHAR: case TS_UCHAR: case TS_SHORT: case TS_USHORT: if (inc < 0) size = -size; savaddr = sideffp(n); /* See if addr has side effs */ ra = gaddress(n); /* Ugh, find & save it */ /* Fetch byte, save addr if savaddr != 0 */ r = getmem(ra, n->Ntype, 1, savaddr); code1(P_ADD, r, size); /* Add inc/dec value */ /* Now store byte back */ if (!savaddr) ra = gaddress(n); /* else, re-use ra */ stomem(r, ra, 1, 1); if (!pre) /* For postfix, undo reg */ code1(P_SUB, r, size); /* undo change */ break; default: int_error("gincdec: bad type %N", n); return NULL; } if (volat) flushcode(); /* Finish up after volatile obj */ return r; } /* GPRIMARY - Generate primary expression. ** ** This handles all primary expressions, which are composed of node ops ** N_FNCALL, ** Q_DOT, (may be lvalue) ** Q_MEMBER, (always lvalue) ** Q_IDENT, (always lvalue) ** N_ICONST, N_FCONST, N_PCONST, N_SCONST, N_VCONST, Q_ASM. ** The first three of those are not terminal nodes and may have further ** sub-expressions. ** Note that array subscripting is done as pointer arithmetic rather than ** using a specific operator. Similarly, parenthesized expressions have ** no specific op since the parse tree structure reflects any parenthesizing. ** This is where array and function names are caught and turned into ** pointers instead. Arrays and functions are the only Q_IDENTs for which ** the node type (Ntype) is different from the symbol type (Stype)! The ** symbol type will have the actual type of the name, whereas the node type ** will be that of "pointer to ". ** Note special checking for fetching a value from "volatile"-qualified ** lvalues. There are only four nodes that can be lvalues -- the three above, ** plus N_PTR which is handled in gunary(). Storing into those lvalues is ** handled by gassign() and gincdec(). Because the peephole optimizer is ** such a mess, we can't easily tell it to avoid volatile objects; instead ** we simply flush out all peephole code before and after generating the ** fetch from (or store into) a volatile object! Crude, but should work. */ static VREG * gprimary(NODE *n) { VREG *q, *r; INT siz; int volat, t; switch (n->Nop) { case Q_IDENT: /* Variable name */ if ((t = n->Nid->Stype->Tspec) == TS_FUNCT || t == TS_ARRAY ) { /* Check for funct/array. Make sure Ntype is ptr */ if (n->Ntype->Tspec != TS_PTR) /* Later make this error again */ int_warn("gprimary: array/funct %N", n); return gaddress(n); /* Yup, just return ptr to object */ } /* Normal variable or structure/union */ if ((volat = tisanyvolat(n->Ntype)) != 0) flushcode(); /* If volatile, avoid optimization */ if (Register_Id(n)) /* approximate getmem() for registers */ #if 0 r = rgetmem(gaddress(n), n->Ntype, tisbyte(n->Ntype), 0); #else r = rgetmem(gaddress(n), n->Ntype, 0); #endif else r = getmem(gaddress(n), n->Ntype, tisbyte(n->Ntype), 0); if (volat) flushcode(); return r; case N_SCONST: /* Literal string - get char pointer to it */ n->Nsclab = newlabel(); n->Nscnext = litstrings; /* link on string stack */ litstrings = n; /* include this one */ r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ /* Get byte ptr to str, using given bytesize of type! */ code10(P_MOVE, r, n->Nsclab, elembsize(n->Ntype), 0); return r; case N_VCONST: /* Void "constant" */ return NULL; /* No register used! */ case N_ICONST: /* Integer constant */ case N_PCONST: /* Pointer constant uses same cell etc */ r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code1(P_MOVE, r, n->Niconst); return r; case N_FCONST: /* Floating-point constant */ switch (n->Ntype->Tspec) { case TS_FLOAT: r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code9(P_MOVE, r, n->Nfconst, 0); break; case TS_DOUBLE: case TS_LNGDBL: r = vrdget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ code9(P_DMOVE, r, n->Nfconst, 1); break; } return r; case Q_ASM: gasm(n); return NULL; /* Currently never returns anything */ case N_FNCALL: /* Function call */ return gcall(n); case Q_DOT: /* (). direct component selection */ if (!(n->Nleft->Nflag & NF_LVALUE)) break; /* Ugh, do hairy stuff if not lvalue! */ if ((debcsi == KCC_DBG_NULL) && (n->Nleft->Nop == Q_MEMBER)) { _chnl = n->Nleft->sfline; if (n->Nleft->Nleft->Nop == N_CAST) code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft->Nleft->Nleft)); else code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft->Nleft)); } /* OK, fall thru to handle like Q_MEMBER */ case Q_MEMBER: /* ()-> indirect component selection */ if ((volat = tisanyvolat(n->Ntype)) != 0) flushcode(); /* Ugh, avoid optimiz of volatile */ #if 0 /* KAR-1/92, leaving this as #if 0 in case I need it later */ if ((debcsi == KCC_DBG_NULL) && (n->Nop == Q_MEMBER)) { _chnl = n->sfline; code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft)); } #endif if (Register_Id(n)) /* approximate getmem() for registers */ #if 0 r = rgetmem(gaddress(n), n->Ntype, (n->Nxoff < 0) || tisbyte(n->Ntype), 0); #else r = rgetmem(gaddress(n), n->Ntype, 0); #endif else r = getmem(gaddress(n), n->Ntype, (n->Nxoff < 0) || tisbyte(n->Ntype), 0); if (volat) flushcode(); return r; case Q_MUUO: return gmuuo(n); default: int_error("gprimary: bad op %N", n); return NULL; } /* Hairy stuff for Q_DOT of something that isn't an lvalue. ** This can only happen for a struct returned from a function call. ** The structure resulting from the expression will either be ** completely contained in the registers (if size <= 2) or the register ** will contain the structure address. */ if ((siz = sizetype(n->Nleft->Ntype)) > 2) /* Find # wds in it */ /* Fake out gaddress into using genexpr instead of another gaddress ** when evaluating the structure expression, since result will ** be a pointer. */ { n->Nop = Q_MEMBER; if (Register_Id(n)) /* approximate getmem() for registers */ #if 0 return rgetmem(gaddress(n), n->Ntype, (n->Nxoff < 0) || tisbyte(n->Ntype), 0); #else return rgetmem(gaddress(n), n->Ntype, 0); #endif else return getmem(gaddress(n), n->Ntype, (n->Nxoff < 0) || tisbyte(n->Ntype), 0); } /* Pull component out of structure in 1- or 2-word register */ r = genexpr(n->Nleft); /* Get the structure */ switch (n->Nxoff) /* See which part of it we want */ { case 0: /* Want first word? */ if (siz == 2 && sizetype(n->Ntype) == 1) vrnarrow(r); /* Keep 1st word of a 2-word value */ return r; case 1: /* Want second word? */ vrnarrow(r = VR2(r)); /* Keep second word of a 2-word value */ return r; default: /* Bitfield of some kind */ /* NOTE: This generates a very uncommon use of PTA_BYTEPOINT ** wherein the E field of the byte pointer is actually a register ** address. This is why vrreal() is called, to get the ** actual register number. As long as this is one of the return-value ** registers as it should be, this usage is probably safe from ** the peephole optimizer. */ q = vrget(); /* Get another register */ q->Vrtype = n->Ntype; /* Set C type of object in reg */ (void) vrstoreal(q, r); /* Make sure both regs are active! */ codebp(P_LDB, vrreal(q), (unsigned)((- (n->Nxoff)) & 07777) << 6, 0, NULL, vrreal(r) + ((unsigned)(-(n->Nxoff)) >> 12)); vrfree(r); /* don't need rest of struct */ return q; } } /* GCALL - Generate function call */ static VREG* gcall (NODE* n) { NODE* l; INT narg, siz; VREG* r; SYMBOL* arg; if (n->Nleft->Ntype->Tspec != TS_FUNCT) int_error ("gcall: non-function %N", n); /* Check to see if OK to try for tail recursion */ if (!optgen /* Not optimizing? */ || stkgoto /* Function contains a setjmp call? */ || stackrefs) /* Fn makes addr refs to stack? */ n->Nflag &=~ NF_RETEXPR; /* If any of the above, forget it. */ /* Check for args in same order - if ok, can tail recurse */ l = n->Nright; siz = sizetype(n->Ntype); /* calculate size of return value */ if (n->Ntype->Tspec == TS_ARRAY) /* Someday flush this, I hope */ { int_error ("gcall: array type %N", n); siz = 0; } narg = -1; while ((n->Nflag & NF_RETEXPR) && l != NULL) { if (l->Nop == N_EXPRLIST) { arg = (l->Nright->Nop == Q_IDENT? l->Nright->Nid : NULL); l = l->Nleft; } else { arg = (l->Nop == Q_IDENT? l->Nid : NULL); l = NULL; } if (arg == NULL || (arg->Sclass != SC_ARG && arg->Sclass != SC_RARG)) n->Nflag &=~ NF_RETEXPR; else { if (narg == -1) narg = arg->Svalue; else if (narg != arg->Svalue) n->Nflag &=~ NF_RETEXPR; narg -= sizetype(arg->Stype); if (narg < 0) n->Nflag &=~ NF_RETEXPR; } } if (siz > 2) narg -= 1; /* account for retval (struct *) */ if (n->Nright == NULL) narg = 0; /* no args always matches */ /* * If we still think we can tail recurse, do it. * * NOTE: profiling precludes tail recursion: MVS 09/20/89 */ if (!profbliss) /* for BLISS profiler */ { if (narg == 0 && (n->Nflag & NF_RETEXPR)) { r = gaddress (n->Nleft); /* get address of function first */ code8(P_ADJSP, VR_SP, -stackoffset); /* before we lose marbles */ code4(P_JRST, (VREG *)NULL, r); /* now we can jump to it */ return NULL; /* can't want a return value */ } } if (n->Nleft->Nid->Sflags & TF_FORTRAN) /* FORTRAN fn */ XF4_call_spill = (char) ~0; /* spill preserved regs if XF4 call */ // FW KCC-NT vrallspill(); /* save active non-preserved regs */ XF4_call_spill = 0; /* * Next push function arguments */ l = n->Nright; narg = stackoffset; /* remember argument block start */ /* * Choose bliss, fortran, interrupt, or normal C function argument linkage */ if (n->Nleft->Nid->Sflags & TF_BLISS) emit_blissargs (l); /* bliss linkage */ else if (n->Nleft->Nid->Sflags & TF_FORTRAN) { /* fortran linkage */ code1(P_MOVS, (r = vrget()), (- sizeargs(l)) & 0777777L); code0(P_PUSH, VR_SP, r); /* Start with -<# arg wds>,,0 */ stackoffset++; emit_blissargs(l); /* now push args in BLISS order */ } else if (n->Nleft->Nid->Sflags & TF_INTERRUPT) { /* * FW 2A(52) * * This is an interrupt function. As such, it needs a special * prolog and epilog. */ } else /* ...No, it's a C fn */ while (l != NULL) { if (l->Nop == N_EXPRLIST) { gfnarg(l->Nright); l = l->Nleft; } else { gfnarg(l); break; } } if (siz > 2) /* Push struct addr on stack as 1st arg */ { r = vrget(); code13(P_MOVE, r, (n->Nretstruct->Svalue + 1) - stackoffset); code0(P_PUSH, VR_SP, r); stackoffset++; } narg -= stackoffset; /* calculate neg number of arg words */ if (n->Nleft->Nid->Sflags & TF_FORTRAN) /* for a FORTRAN fn */ { /* * Do FORTRAN call. Must get function address first, in case it is * an expression that might possibly clobber AC16, and then point * AC16 (R_FAP) to the start of our args on stack. */ r = gaddress(n->Nleft); /* Get function addr first */ code13(P_MOVE, VR_FAP, narg+2); /* Point to just after count */ code4(P_PUSHJ, VR_SP, r); /* Call function */ } else if (n->Nleft->Nop == Q_IDENT) code6(P_PUSHJ, VR_SP, n->Nleft->Nid); /* optimization */ else code4(P_PUSHJ, VR_SP, gaddress(n->Nleft)); /* call function or expr */ /* * flush args off stack */ if (narg) { code8 (P_ADJSP, VR_SP, narg); stackoffset += narg; } if (siz == 1) r = vrretget (); /* one return register */ else if (siz == 2) r = vrretdget (); /* two */ else if (siz > 2) { /* Can optimize better if we re-generate the addr we gave as arg. */ code13 (P_MOVE, (r = vrretget ()), (n->Nretstruct->Svalue + 1) - stackoffset); } else return NULL; /* Returning void */ if (n->Nleft->Nid->Sflags & TF_FORTRAN) /* for a FORTRAN fn */ { /* FORTRAN functions return values in regs 0+1 instead of 1+2 */ code0((siz == 2) ? P_DMOVE : P_MOVE, r, VR_ZERO); code5(P_SETZ, VR_ZERO); /* This may not be necessary */ } r->Vrtype = n->Ntype; /* Set C type of result obj */ return r; } /* * void emit_blissargs (NODE*) * * This little recursive function traverses a NODE tree * and generates calles to gfnarg () such as to emit pushes * for a function's arguments in the reverse of the usual order. */ static void emit_blissargs (NODE *l) { if (l) { if (l->Nop == N_EXPRLIST) { emit_blissargs(l->Nleft); gfnarg(l->Nright); } else gfnarg(l); } } /* Count # words needed by all args ahead of time, so FORTRAN linkage ** can use it without backpatching. */ static INT sizeargs(NODE *l) { INT size = 0; for (; l; l = l->Nleft) { if (l->Nop == N_EXPRLIST) size += sizetype(l->Nright->Ntype); else { size += sizetype(l->Ntype); break; } } return size; } /* GFNARG - generate function argument value and push on stack ** */ static void gfnarg(NODE *n) { VREG *reg; INT siz; siz = sizetype(n->Ntype); if (n->Ntype->Tspec == TS_ARRAY) { int_error("gfnarg: array type %N", n); siz = 0; } #if 0 /* Reg linkage */ if (Register_Id(n)) { if (siz == 1) { code00(P_PUSH, R_SP, n->Nid->Sreg); stackoffset++; } else int_error ("gfnarg: size of reg var %s > 1", n->Nid->Sname); return; } #endif switch (siz) { case 1: code0(P_PUSH, VR_SP, genexpr(n)); stackoffset++; break; case 2: reg = genexpr(n); code0(P_PUSH, VR_SP, reg); code0(P_PUSH, VR_SP, VR2(reg)); vrfree(reg); stackoffset += 2; break; default: reg = vrget(); code8(P_ADJSP, VR_SP, siz); /* Make space on the stack */ stackoffset += siz; /* remember where we are on stack */ code13(P_MOVE, reg, -(siz-1)); /* Get pointer to the space */ code4s(P_SMOVE, reg, genexpr(n), 0, siz); /* Copy, release reg */ vrfree(reg); } } /* GADDRESS - Generate address of object or function. ** Will set up as byte pointer if necessary */ static VREG * gaddress(NODE *n) { int boff, bsiz; INT offset; VREG *r, *p; SYMBOL *s; switch (n->Nop) { case Q_ASPLUS: /* ptr += &a[] */ case Q_PLUS: case Q_ASMINUS: /* ptr -= &a[] */ case Q_MINUS: case N_PTR: return genexpr(n->Nleft); case Q_DOT: case Q_MEMBER: if (n->Nop == Q_MEMBER) { if (debcsi == KCC_DBG_NULL) { _chnl = n->sfline; switch (n->Nleft->Nop) { case N_CAST: case N_ADDR: case Q_ASGN: case N_FNCALL: if (n->Nleft->Nleft->Nop == Q_IDENT) code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft->Nleft)); else _chnl = -1; break; default: code4 (P_NULPTR, (VREG *) NULL, gaddress (n->Nleft)); break; } } r = genexpr (n->Nleft); } else r = gaddress (n->Nleft); offset = n->Nxoff; /* calculate offset */ /* Check for attempt to get address of object within a word. */ if (offset < 0) /* bitfield or byte? */ { offset = -offset; /* Get back original encoding */ if (!tisbitf(n->Ntype)) /* If not bitfield, assume byte */ { bsiz = (int) (offset & 077); /* Get byte size of object */ boff = (int) (((TGSIZ_WORD /* Get offset in bytes */ - ((offset & 07700) >> 6) ) / bsiz) - 1); offset = (unsigned INT)offset >> 12; /* Get wd offset */ if (offset > 0) code1(P_ADD, r, offset); /* Do word offset */ pitopc(r, bsiz, boff, 1); /* Turn addr into byte ptr */ return r; } /* True bitfield. ** Note that although C does not allow pointers to bitfields, we ** still want to generate bitfield "addresses" for internal use ** so that the code generation can avoid lots of special-casing. */ p = vrget(); /* Need another reg */ p->Vrtype = n->Ntype; /* Set C type of object in reg */ (void) vrstoreal(r, p); /* Ensure both regs active! */ codebp(P_MOVE, vrreal(p), /* Construct local byte pointer */ (unsigned INT)(offset&07777) << 6, vrreal(r), NULL, (unsigned INT)offset >> 12); /* Now we release the struct address, even though it is still ** needed as index by the byte pointer we created! This is ** should be safe as long as the resulting address is used ** immediately -- for bitfields this should always be true since ** bitfield addresses cannot have an independent existence. */ vrfree(r); return p; } if (offset > 0) code1(P_ADD, r, offset); /* perform offset */ if (tisbytearray(n->Ntype)) /* If addr of byte array, */ pitopc(r, elembsize(n->Ntype), 0, 1); /* make BP to start */ else if (tisbyte(n->Ntype)) /* If addr of single byte, */ pitopc(r, tbitsize(n->Ntype), /* point to low byte */ (TGSIZ_WORD/tbitsize(n->Ntype))-1, 1); return r; case Q_IDENT: /* Note type checked is that of the symbol's, not that of the ** node's. This ensures we do the right thing when the ident ** is that of a function or array. */ r = vrget(); r->Vrtype = n->Ntype; /* Set C type of object in reg */ s = n->Nid; if (tisbytearray(s->Stype)) /* If ident is byte array, */ { bsiz = elembsize(s->Stype); /* set byte params */ offset = 0; /* with left-justified byte */ } else if (tisbyte(n->Ntype)) /* If it's a single byte, */ { bsiz = tbitsize(n->Ntype); /* also set them */ offset = (TGSIZ_WORD/bsiz)-1; /* with right-justified byte */ } else bsiz = 0; switch (s->Sclass) { case SC_RAUTO: /* value already moved to reg */ code00(P_MOVE, r->Vrloc, s->Sreg); return r; case SC_AUTO: /* Local variables */ code13(P_MOVE, r, (s->Svalue + 1) - stackoffset); break; case SC_RARG: /* Function parameters */ code00(P_MOVE, r->Vrloc, s->Sreg); return r; case SC_ARG: code13(P_MOVE, r, (- s->Svalue) - stackoffset); break; case SC_ENUM: int_error("gaddress: enum tag: %S %N", s, n); return r; case SC_ISTATIC: /* Internal static */ s = s->Ssym; /* uses internal label instead */ case SC_XEXTREF: case SC_EXLINK: /* Anything with linkage */ case SC_EXTDEF: case SC_EXTREF: case SC_INTDEF: case SC_INTREF: case SC_INLINK: if (bsiz) /* If byte pointer is addr, */ code10(P_MOVE, r, s, bsiz, offset); /* make BP */ else code3(P_MOVE, r, s); /* else just make addr */ return r; default: int_error("gaddress: bad Sclass %d %N", s->Sclass, n); return r; } if (bsiz) /* If addr is a byte addr, */ pitopc(r, bsiz, offset, 1); /* make BP */ return r; default: int_error("gaddress: bad op %N", n); return 0; } } /* GETMEM - Get object from memory, given address in register. ** Releases the register unless the "keep" flag is set. */ VREG * getmem(VREG *reg, TYPE *t, int byte, int keep) { VREG *q; switch (sizetype(t)) { case 1: q = vrget(); q->Vrtype = t; /* Set C type of object in reg */ if (byte) (keep ? codek0(P_LDB, q, reg) : code0(P_LDB, q, reg)); else (keep ? codek4(P_MOVE, q, reg) : code4(P_MOVE, q, reg)); return q; case 2: q = vrdget(); q->Vrtype = t; /* Set C type of object in reg */ (keep ? codek4(P_DMOVE, q, reg) : code4(P_DMOVE, q, reg)); return q; default: return reg; } } /* STOMEM - Store register into memory; inverse of GETMEM. ** Releases the address register, returns the value register ** for possible further processing. */ VREG * stomem(VREG *reg, VREG *ra, INT siz, int byteptr) /* Reg w/value to store (NULL if stacked struct), Reg w/address to * store into, Size of object in words, True if "address" is a byte pointer */ { switch (siz) { case 1: /* Store single word or byte */ if (byteptr) code0(P_DPB, reg, ra); else code4(P_MOVEM, reg, ra); break; case 2: /* Store doubleword */ code4(P_DMOVEM, reg, ra); break; default: /* Store a stacked structure */ /* ra has dest addr, reg has source addr */ code4s(P_SMOVE, ra, reg, 0, siz); /* Copy, release reg */ return ra; } return reg; } /* RGETMEM - Get object from register and do not release register, ** imitating GETMEM for registers. */ static VREG * #if 0 rgetmem(VREG *reg, TYPE *t, int byte, int keep) #else rgetmem(VREG *reg, TYPE *t, int keep) #endif { VREG *q; if (sizetype(t) == 1) { q = vrget(); q->Vrtype = t; /* Set C type of object in reg */ #if 0 if (byte) codek0(P_LDB, q, reg); else codek0(P_MOVE, q, reg); if (byte) (keep ? codek0(P_LDB, q, reg) : code0(P_LDB, q, reg)); else #else if (keep) codek0(P_MOVE, q, reg); else code0 (P_MOVE, q, reg); #endif return q; } else { int_error("rgetmem: register var of non-arithmetic type"); return reg; } } #if 0 /* RSTOMEM - Store register into register; inverse of RGETMEM. ** Do not release register, imitating STOMEM for registers. ** */ VREG * rstomem(VREG *reg, int ra, INT siz, int byteptr) /* Reg w/value to store (NULL if stacked struct), Real reg to * store into, Size of object in words, True if "address" is a byte pointer */ { if (siz == 1) code00(P_MOVE, ra, reg->Vrloc); else /* reg not defined for arrays or stacked structures */ { int_error("rstomem: register var of non-arithmetic type"); } return reg; } #endif /* PITOPC - Construct byte pointer from word pointer. ** Currently the only offsets used are either 0 (for left justified byte) ** or <# bytes-per-word>-1 (for right justified byte). Note that the latter ** can result in unused low-order bits if the byte size does not completely ** fill the word. ** ** Turn a word pointer into a byte pointer. So that our programs ** should run in extended addressing as well as in section 0, we ** must be able to create either local or global byte pointers, ** so we add in our P/S fields from a table instead of literally. ** ** Even if we know that the pointer will point to the same section ** that the code is in, we cannot use a local byte pointer, because ** pointers are local to where they are stored rather than to where ** the PC currently is. ** ** If the pointer is merely going to be used to load or deposit ** a byte, it will get turned into a local byte pointer later by ** the peepholer (see localbyte() in CCOPT). */ static void pitopc(VREG *r, int bsiz, int offset, int safe) /* byte size in bits, # bytes offset from start of word addr in R, * and Set if pointer known to be non-NULL, needn't test for 0. */ { if (!safe) /* Unless we already know not NULL */ code0(P_SKIP+POF_ISSKIP+POS_SKPE, r, r); /* NULL stays NULL */ code10(P_IOR, r, (SYMBOL *)NULL, bsiz, offset); /* Make it a pointer */ } /* BPTRREF - sees if expression value consists of a byte pointer ** reference. ** Returns: ** 1 if expression is a legal lvalue referenced via byte ptr. ** 0 if expression is a legal lvalue referenced via word address. ** -1 if expression is not an lvalue operand. */ static int bptrref(NODE *n) { switch (n->Nop) { case Q_DOT: case Q_MEMBER: return (n->Nxoff < 0 /* bitfield? */ || tisbyte(n->Ntype)); /* or char? */ case N_PTR: return tisbytepointer(n->Nleft->Ntype); /* byte pointer deposit */ case Q_IDENT: return tisbyte(n->Ntype); default: return -1; } } /* GASM - Generate direct assembly language constructs ** */ static void gasm(NODE *n) { NODE *arg; if ((arg = n->Nleft) == 0) { int_error("gasm: no arg %N", n); return; } if (arg->Nop != N_SCONST) { int_error("gasm: non-string arg %N", n); return; } /* Output the string, minus the terminating null char */ codestr(arg->Nsconst, arg->Nsclen-1); } /* KAR-6/91, Changed sentinal value for _chnl to -1 from 0 */ int _chnl = -1; #define mnem_param temp->Nleft->Nleft->Nleft #define ac_param temp->Nleft->Nleft #define ret_param temp->Nleft->Nright #define ea_param temp->Nright #define ch_sig n->Nleft /* --------------------------- */ /* imuuo statement */ /* -by KAR 1/91 */ /* --------------------------- */ VREG * gmuuo(n) NODE *n; { VREG *ac, *ret_ac; int p3_omitted = 0, p4_omitted = 0; NODE *temp = n->Nleft->Nleft; /* else, BC++ fails!? */ if ((ret_param->Nop == N_ICONST) && (ret_param->Niconst == 0)) p3_omitted = 1; if ((ea_param->Nop == N_ICONST) && (ea_param->Niconst == 0)) p4_omitted = 1; /* The following code is outputed: * MOVE ac,ac_contents * muuo ac, * TDZA 1,1 * MOVEI 1,1 * MOVEM ac,ret_val ; if a return address is specified */ ac = vrget(); if (ch_sig->Nright->Niconst == 0) code0(P_MOVE, ac, genexpr(ac_param->Nright)); else _chnl = ac_param->Nright->Niconst; if (p4_omitted != 1) code4m(P_MUUO, ac, genexpr(ea_param),mnem_param->Nright->Nsconst); else code5m(P_MUUO, ac, mnem_param->Nright->Nsconst); ret_ac = vrget(); code0(P_TDZ+POF_ISSKIP+POS_SKPA, ret_ac, ret_ac); code1(P_MOVE, ret_ac, 1); if (p3_omitted != 1) switch (ret_param->Nid->Sclass) { case SC_ARG: case SC_AUTO: code4(P_MOVEM, ac, gaddress(ret_param)); break; case SC_RARG: case SC_RAUTO: case SC_REGISTER: code0(P_MOVEM, ac, gaddress(ret_param)); break; default: code6(P_MOVEM, ac, ret_param->Nid); break; } /* switch */ vrfree(ac); return(ret_ac); }