diff --git a/src/backend/cgcod.c b/src/backend/cgcod.c index a844de5a1c5f..d56103872f12 100644 --- a/src/backend/cgcod.c +++ b/src/backend/cgcod.c @@ -307,23 +307,7 @@ void codgen() coffset = Coffset; if (eecontext.EEelem) - { regm_t retregs; - code *c; - - eecontext.EEin++; - regcon.immed.mval = 0; - retregs = 0; //regmask(eecontext.EEelem->Ety); - assert(EEoffset >= REGSIZE); - c = genc2(NULL,0x81,modregrm(3,5,SP),EEoffset - REGSIZE); // SUB ESP,EEoffset - gen1(c,0x50 + SI); // PUSH ESI - genadjesp(c,EEoffset); - c = gencodelem(c,eecontext.EEelem,&retregs, FALSE); - assignaddrc(c); - pinholeopt(c,NULL); - jmpaddr(c); - eecontext.EEcode = gen1(c,0xCC); // INT 3 - eecontext.EEin--; - } + genEEcode(); for (b = startblock; b; b = b->Bnext) { @@ -814,12 +798,8 @@ void stackoffsets(int flags) */ STATIC void blcodgen(block *bl) -{ regm_t retregs; - bool jcond; - elem *e; +{ code *c; - block *nextb; - block *bs1,*bs2; list_t bpl; int refparamsave; regm_t mfuncregsave = mfuncreg; @@ -927,394 +907,13 @@ STATIC void blcodgen(block *bl) regcon.indexregs &= regcon.indexregs - 1; } - e = bl->Belem; regsave.idx = 0; - retregs = 0; reflocal = 0; refparamsave = refparam; refparam = 0; assert((regcon.cse.mops & regcon.cse.mval) == regcon.cse.mops); - switch (bl->BC) /* block exit condition */ - { - case BCiftrue: - jcond = TRUE; - bs1 = list_block(bl->Bsucc); - bs2 = list_block(list_next(bl->Bsucc)); - if (bs1 == bl->Bnext) - { // Swap bs1 and bs2 - block *btmp; - - jcond ^= 1; - btmp = bs1; - bs1 = bs2; - bs2 = btmp; - } - c = cat(c,logexp(e,jcond,FLblock,(code *) bs1)); - nextb = bs2; - bl->Bcode = NULL; - L2: - if (nextb != bl->Bnext) - { if (configv.addlinenumbers && bl->Bsrcpos.Slinnum && - !(funcsym_p->ty() & mTYnaked)) - cgen_linnum(&c,bl->Bsrcpos); - assert(!(bl->Bflags & BFLepilog)); - c = cat(c,genjmp(CNIL,JMP,FLblock,nextb)); - } - bl->Bcode = cat(bl->Bcode,c); - break; - case BCjmptab: - case BCifthen: - case BCswitch: - assert(!(bl->Bflags & BFLepilog)); - doswitch(bl); /* hide messy details */ - bl->Bcode = cat(c,bl->Bcode); - break; -#if MARS - case BCjcatch: - // Mark all registers as destroyed. This will prevent - // register assignments to variables used in catch blocks. - c = cat(c,getregs((I32 | I64) ? allregs : (ALLREGS | mES))); -#if 0 && TARGET_LINUX - if (config.flags3 & CFG3pic && !(allregs & mBX)) - { - c = cat(c, cod3_load_got()); - } -#endif - goto case_goto; -#endif -#if SCPP - case BCcatch: - // Mark all registers as destroyed. This will prevent - // register assignments to variables used in catch blocks. - c = cat(c,getregs(allregs | mES)); -#if 0 && TARGET_LINUX - if (config.flags3 & CFG3pic && !(allregs & mBX)) - { - c = cat(c, cod3_load_got()); - } -#endif - goto case_goto; - - case BCtry: - usednteh |= EHtry; - if (config.flags2 & CFG2seh) - usednteh |= NTEHtry; - goto case_goto; -#endif - case BCgoto: - nextb = list_block(bl->Bsucc); - if ((funcsym_p->Sfunc->Fflags3 & Fnteh || - (MARS /*&& config.flags2 & CFG2seh*/)) && - bl->Btry != nextb->Btry && - nextb->BC != BC_finally) - { int toindex; - int fromindex; - - bl->Bcode = NULL; - c = gencodelem(c,e,&retregs,TRUE); - toindex = nextb->Btry ? nextb->Btry->Bscope_index : -1; - assert(bl->Btry); - fromindex = bl->Btry->Bscope_index; -#if MARS - if (toindex + 1 == fromindex) - { // Simply call __finally - if (bl->Btry && - list_block(list_next(bl->Btry->Bsucc))->BC == BCjcatch) - { - goto L2; - } - } -#endif - if (config.flags2 & CFG2seh) - c = cat(c,nteh_unwind(0,toindex)); -#if MARS && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) - else if (toindex + 1 <= fromindex) - { - //c = cat(c, linux_unwind(0, toindex)); - block *bt; - - //printf("B%d: fromindex = %d, toindex = %d\n", bl->Bdfoidx, fromindex, toindex); - bt = bl; - while ((bt = bt->Btry) != NULL && bt->Bscope_index != toindex) - { block *bf; - - //printf("\tbt->Bscope_index = %d, bt->Blast_index = %d\n", bt->Bscope_index, bt->Blast_index); - bf = list_block(list_next(bt->Bsucc)); - // Only look at try-finally blocks - if (bf->BC == BCjcatch) - continue; - - if (bf == nextb) - continue; - //printf("\tbf = B%d, nextb = B%d\n", bf->Bdfoidx, nextb->Bdfoidx); - if (nextb->BC == BCgoto && - !nextb->Belem && - bf == list_block(nextb->Bsucc)) - continue; - - // call __finally - code *cs; - code *cr; - int nalign = 0; - - gensaverestore(retregs,&cs,&cr); - if (STACKALIGN == 16) - { int npush = (numbitsset(retregs) + 1) * REGSIZE; - if (npush & (STACKALIGN - 1)) - { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); - cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign - if (I64) - code_orrex(cs, REX_W); - } - } - cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); - if (nalign) - { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign - if (I64) - code_orrex(cs, REX_W); - } - c = cat3(c,cs,cr); - } - } -#endif - goto L2; - } - case_goto: - c = gencodelem(c,e,&retregs,TRUE); - if (anyspill) - { // Add in the epilog code - code *cstore = NULL; - code *cload = NULL; - - for (int i = 0; i < anyspill; i++) - { symbol *s = globsym.tab[i]; - - if (s->Sflags & SFLspill && - vec_testbit(dfoidx,s->Srange)) - { - s->Sfl = sflsave[i]; // undo block register assignments - cgreg_spillreg_epilog(bl,s,&cstore,&cload); - } - } - c = cat3(c,cstore,cload); - } - - L3: - bl->Bcode = NULL; - nextb = list_block(bl->Bsucc); - goto L2; - - case BC_try: - if (config.flags2 & CFG2seh) - { usednteh |= NTEH_try; - nteh_usevars(); - } - else - usednteh |= EHtry; - goto case_goto; - - case BC_finally: - // Mark all registers as destroyed. This will prevent - // register assignments to variables used in finally blocks. - assert(!getregs(allregs)); - assert(!e); - assert(!bl->Bcode); -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS - if (config.flags3 & CFG3pic) - { - int nalign = 0; - if (STACKALIGN == 16) - { nalign = STACKALIGN - REGSIZE; - c = genc2(c,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign - if (I64) - code_orrex(c, REX_W); - } - // CALL bl->Bsucc - c = genc(c,0xE8,0,0,0,FLblock,(long)list_block(bl->Bsucc)); - if (nalign) - { c = genc2(c,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign - if (I64) - code_orrex(c, REX_W); - } - // JMP list_next(bl->Bsucc) - nextb = list_block(list_next(bl->Bsucc)); - goto L2; - } - else -#endif - { - // Generate a PUSH of the address of the successor to the - // corresponding BC_ret - //assert(list_block(list_next(bl->Bsucc))->BC == BC_ret); - // PUSH &succ - c = genc(c,0x68,0,0,0,FLblock,(long)list_block(list_next(bl->Bsucc))); - nextb = list_block(bl->Bsucc); - goto L2; - } - - case BC_ret: - c = gencodelem(c,e,&retregs,TRUE); - bl->Bcode = gen1(c,0xC3); // RET - break; - -#if NTEXCEPTIONS - case BC_except: - assert(!e); - usednteh |= NTEH_except; - c = cat(c,nteh_setsp(0x8B)); - getregs(allregs); - goto L3; - - case BC_filter: - c = cat(c,nteh_filter(bl)); - // Mark all registers as destroyed. This will prevent - // register assignments to variables used in filter blocks. - getregs(allregs); - retregs = regmask(e->Ety, TYnfunc); - c = gencodelem(c,e,&retregs,TRUE); - bl->Bcode = gen1(c,0xC3); // RET - break; -#endif - - case BCretexp: - retregs = regmask(e->Ety, funcsym_p->ty()); - - // For the final load into the return regs, don't set regcon.used, - // so that the optimizer can potentially use retregs for register - // variable assignments. - - if (config.flags4 & CFG4optimized) - { regm_t usedsave; - - c = cat(c,docommas(&e)); - usedsave = regcon.used; - if (EOP(e)) - c = gencodelem(c,e,&retregs,TRUE); - else - { - if (e->Eoper == OPconst) - regcon.mvar = 0; - c = gencodelem(c,e,&retregs,TRUE); - regcon.used = usedsave; - if (e->Eoper == OPvar) - { symbol *s = e->EV.sp.Vsym; - - if (s->Sfl == FLreg && s->Sregm != mAX) - retsym = s; - } - } - } - else - { - case BCret: - case BCexit: - c = gencodelem(c,e,&retregs,TRUE); - } - bl->Bcode = c; - if (retregs == mST0) - { assert(stackused == 1); - pop87(); // account for return value - } - else if (retregs == mST01) - { assert(stackused == 2); - pop87(); - pop87(); // account for return value - } - if (bl->BC == BCexit && config.flags4 & CFG4optimized) - mfuncreg = mfuncregsave; - if (MARS || usednteh & NTEH_try) - { block *bt; - - bt = bl; - while ((bt = bt->Btry) != NULL) - { block *bf; - - bf = list_block(list_next(bt->Bsucc)); -#if MARS - // Only look at try-finally blocks - if (bf->BC == BCjcatch) - { - continue; - } -#endif - if (config.flags2 & CFG2seh) - { - if (bt->Bscope_index == 0) - { - // call __finally - code *cs; - code *cr; - - c = cat(c,nteh_gensindex(-1)); - gensaverestore(retregs,&cs,&cr); - cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); - bl->Bcode = cat3(c,cs,cr); - } - else - bl->Bcode = cat(c,nteh_unwind(retregs,~0)); - break; - } - else - { - // call __finally - code *cs; - code *cr; - int nalign = 0; - - gensaverestore(retregs,&cs,&cr); - if (STACKALIGN == 16) - { int npush = (numbitsset(retregs) + 1) * REGSIZE; - if (npush & (STACKALIGN - 1)) - { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); - cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign - if (I64) - code_orrex(cs, REX_W); - } - } - // CALL bf->Bsucc - cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); - if (nalign) - { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign - if (I64) - code_orrex(cs, REX_W); - } - bl->Bcode = c = cat3(c,cs,cr); - } - } - } - break; -#if SCPP || MARS - case BCasm: - assert(!e); - // Mark destroyed registers - assert(!c); - c = cat(c,getregs(iasm_regs(bl))); - if (bl->Bsucc) - { nextb = list_block(bl->Bsucc); - if (!bl->Bnext) - goto L2; - if (nextb != bl->Bnext && - bl->Bnext && - !(bl->Bnext->BC == BCgoto && - !bl->Bnext->Belem && - nextb == list_block(bl->Bnext->Bsucc))) - { code *cl; - - // See if already have JMP at end of block - cl = code_last(bl->Bcode); - if (!cl || cl->Iop != JMP) - goto L2; // add JMP at end of block - } - } - break; -#endif - default: -#ifdef DEBUG - printf("bl->BC = %d\n",bl->BC); -#endif - assert(0); - } + outblkexitcode(bl, c, anyspill, sflsave, &retsym, mfuncregsave); for (int i = 0; i < anyspill; i++) { symbol *s = globsym.tab[i]; @@ -1522,99 +1121,6 @@ STATIC void cgcod_eh() #endif -/***************************** - * Given a type, return a mask of - * registers to hold that type. - * Input: - * tyf function type - */ - -regm_t regmask(tym_t tym, tym_t tyf) -{ - switch (tybasic(tym)) - { - case TYvoid: - case TYstruct: - return 0; - case TYbool: - case TYwchar_t: - case TYchar16: - case TYchar: - case TYschar: - case TYuchar: - case TYshort: - case TYushort: - case TYint: - case TYuint: -#if JHANDLE - case TYjhandle: -#endif - case TYnullptr: - case TYnptr: - case TYsptr: - case TYcptr: - return mAX; - - case TYfloat: - case TYifloat: - if (I64) - return mXMM0; - if (config.exe & EX_flat) - return mST0; - case TYlong: - case TYulong: - case TYdchar: - if (!I16) - return mAX; - case TYfptr: - case TYhptr: - return mDX | mAX; - - case TYcent: - case TYucent: - assert(I64); - return mDX | mAX; - - case TYvptr: - return mDX | mBX; - - case TYdouble: - case TYdouble_alias: - case TYidouble: - if (I64) - return mXMM0; - if (config.exe & EX_flat) - return mST0; - return DOUBLEREGS; - - case TYllong: - case TYullong: - return I64 ? mAX : (I32 ? mDX | mAX : DOUBLEREGS); - - case TYldouble: - case TYildouble: - return mST0; - - case TYcfloat: -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS - if (I32 && tybasic(tyf) == TYnfunc) - return mDX | mAX; -#endif - case TYcdouble: - if (I64) - return mXMM0 | mXMM1; - case TYcldouble: - return mST01; - - default: -#if DEBUG - WRTYxx(tym); -#endif - assert(0); - return 0; - } -} - /****************************** * Count the number of bits set in a register mask. */ @@ -2102,15 +1608,7 @@ STATIC code * cse_save(regm_t ms) // If we can simply reload the CSE, we don't need to save it if (!cse_simple(csextab[i].e,i)) { - // MOV i[BP],reg - op = 0x89; // normal mov - if (reg == ES) - { reg = 0; // the real reg number - op = 0x8C; // segment reg mov - } - c = genc1(c,op,modregxrm(2, reg, BPRM),FLcs,(targ_uns) i); - if (I64) - code_orrex(c, REX_W); + c = cat(c, gensavereg(reg, i)); reflocal = TRUE; } } @@ -2757,6 +2255,212 @@ code *codelem(elem *e,regm_t *pretregs,bool constflag) return c; } +/******************************* + * Same as codelem(), but do not destroy the registers in keepmsk. + * Use scratch registers as much as possible, then use stack. + * Input: + * constflag TRUE if user of result will not modify the + * registers returned in *pretregs. + */ + +code *scodelem(elem *e,regm_t *pretregs,regm_t keepmsk,bool constflag) +{ code *c,*cs1,*cs2,*cs3; + unsigned i,j; + regm_t oldmfuncreg,oldregcon,oldregimmed,overlap,tosave,touse; + int adjesp; + unsigned stackpushsave; + char calledafuncsave; + +#ifdef DEBUG + if (debugw) + printf("+scodelem(e=%p *pretregs=%s keepmsk=%s constflag=%d\n", + e,regm_str(*pretregs),regm_str(keepmsk),constflag); +#endif + elem_debug(e); + if (constflag) + { regm_t regm; + unsigned reg; + + if (isregvar(e,®m,®) && // if e is a register variable + (regm & *pretregs) == regm && // in one of the right regs + e->EV.sp.Voffset == 0 + ) + { + unsigned sz1 = tysize(e->Ety); + unsigned sz2 = tysize(e->EV.sp.Vsym->Stype->Tty); + if (sz1 <= REGSIZE && sz2 > REGSIZE) + regm &= mLSW; + c = fixresult(e,regm,pretregs); + cssave(e,regm,0); + freenode(e); +#ifdef DEBUG + if (debugw) + printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", + e,*pretregs,keepmsk,constflag); +#endif + return c; + } + } + overlap = msavereg & keepmsk; + msavereg |= keepmsk; /* add to mask of regs to save */ + oldregcon = regcon.cse.mval; + oldregimmed = regcon.immed.mval; + oldmfuncreg = mfuncreg; /* remember old one */ + mfuncreg = (mBP | mES | ALLREGS) & ~regcon.mvar; + stackpushsave = stackpush; +#if 0 + if (keepmsk) + stackpush++; /* assume we'll have to save stuff on stack */ +#endif + calledafuncsave = calledafunc; + calledafunc = 0; + c = codelem(e,pretregs,constflag); /* generate code for the elem */ +#if 0 + if (keepmsk) + stackpush--; +#endif + + tosave = keepmsk & ~msavereg; /* registers to save */ + if (tosave) + { cgstate.stackclean++; + c = genstackclean(c,stackpush - stackpushsave,*pretregs | msavereg); + cgstate.stackclean--; + } + + /* Assert that no new CSEs are generated that are not reflected */ + /* in mfuncreg. */ +#ifdef DEBUG + if ((mfuncreg & (regcon.cse.mval & ~oldregcon)) != 0) + printf("mfuncreg x%x, regcon.cse.mval x%x, oldregcon x%x, regcon.mvar x%x\n", + mfuncreg,regcon.cse.mval,oldregcon,regcon.mvar); +#endif + assert((mfuncreg & (regcon.cse.mval & ~oldregcon)) == 0); + + /* bugzilla 3521 + * The problem is: + * reg op (reg = exp) + * where reg must be preserved (in keepregs) while the expression to be evaluated + * must change it. + * The only solution is to make this variable not a register. + */ + if (regcon.mvar & tosave) + { + //elem_print(e); + //printf("test1: regcon.mvar x%x tosave x%x\n", regcon.mvar, tosave); + cgreg_unregister(regcon.mvar & tosave); + } + + /* which registers can we use to save other registers in? */ + if (config.flags4 & CFG4space || // if optimize for space + config.target_cpu >= TARGET_80486) // PUSH/POP ops are 1 cycle + touse = 0; // PUSH/POP pairs are always shorter + else + { touse = mfuncreg & allregs & ~(msavereg | oldregcon | regcon.cse.mval); + /* Don't use registers we'll have to save/restore */ + touse &= ~(fregsaved & oldmfuncreg); + /* Don't use registers that have constant values in them, since + the code generated might have used the value. + */ + touse &= ~oldregimmed; + } + + cs1 = cs2 = cs3 = NULL; + adjesp = 0; + + for (i = 0; tosave; i++) + { regm_t mi = mask[i]; + + assert(i < REGMAX); + if (mi & tosave) /* i = register to save */ + { + if (touse) /* if any scratch registers */ + { for (j = 0; j < 8; j++) + { regm_t mj = mask[j]; + + if (touse & mj) + { cs1 = genmovreg(cs1,j,i); + cs2 = cat(genmovreg(CNIL,i,j),cs2); + touse &= ~mj; + mfuncreg &= ~mj; + regcon.used |= mj; + break; + } + } + assert(j < 8); + } + else /* else use stack */ +#if 0 + { int push,pop; + + stackchanged = 1; + adjesp += REGSIZE; + if (i == ES) + { push = 0x06; + pop = 0x07; + } + else + { push = 0x50 + i; + pop = push | 8; + } + cs1 = gen1(cs1,push); /* PUSH i */ + cs2 = cat(gen1(CNIL,pop),cs2); /* POP i */ + } +#else + { + stackchanged = 1; + adjesp += REGSIZE; + gensaverestore2(mask[i], &cs1, &cs2); + } +#endif + cs3 = cat(getregs(mi),cs3); + tosave &= ~mi; + } + } + if (adjesp) + { + // If this is done an odd number of times, it + // will throw off the 8 byte stack alignment. + // We should *only* worry about this if a function + // was called in the code generation by codelem(). + int sz; + if (STACKALIGN == 16) + sz = -(adjesp & (STACKALIGN - 1)) & (STACKALIGN - 1); + else + sz = -(adjesp & 7) & 7; + if (calledafunc && !I16 && sz && (STACKALIGN == 16 || config.flags4 & CFG4stackalign)) + { + unsigned grex = I64 ? REX_W << 16 : 0; + regm_t mval_save = regcon.immed.mval; + regcon.immed.mval = 0; // prevent reghasvalue() optimizations + // because c hasn't been executed yet + cs1 = genc2(cs1,0x81,grex | modregrm(3,5,SP),sz); // SUB ESP,sz + if (I64) + code_orrex(cs1, REX_W); + regcon.immed.mval = mval_save; + cs1 = genadjesp(cs1, sz); + + code *cx = genc2(CNIL,0x81,grex | modregrm(3,0,SP),sz); // ADD ESP,sz + if (I64) + code_orrex(cx, REX_W); + cx = genadjesp(cx, -sz); + cs2 = cat(cx, cs2); + } + + cs1 = genadjesp(cs1,adjesp); + cs2 = genadjesp(cs2,-adjesp); + } + + calledafunc |= calledafuncsave; + msavereg &= ~keepmsk | overlap; /* remove from mask of regs to save */ + mfuncreg &= oldmfuncreg; /* update original */ +#ifdef DEBUG + if (debugw) + printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", + e,*pretregs,keepmsk,constflag); +#endif + return cat4(cs1,c,cs3,cs2); +} + /********************************************* * Turn register mask into a string suitable for printing. */ diff --git a/src/backend/cgen.c b/src/backend/cgen.c index 976d0e1affb7..05a3f186d9bf 100644 --- a/src/backend/cgen.c +++ b/src/backend/cgen.c @@ -247,32 +247,6 @@ code *gen2sib(code *c,unsigned op,unsigned rm,unsigned sib) return cstart; } -code *genregs(code *c,unsigned op,unsigned dstreg,unsigned srcreg) -{ return gen2(c,op,modregxrmx(3,dstreg,srcreg)); } - -code *gentstreg(code *c,unsigned t) -{ - c = gen2(c,0x85,modregxrmx(3,t,t)); // TEST t,t - code_orflag(c,CFpsw); - return c; -} - -code *genpush(code *c, unsigned reg) -{ - c = gen1(c, 0x50 + (reg & 7)); - if (reg & 8) - code_orrex(c, REX_B); - return c; -} - -code *genpop(code *c, unsigned reg) -{ - c = gen1(c, 0x58 + (reg & 7)); - if (reg & 8) - code_orrex(c, REX_B); - return c; -} - /******************************** * Generate an ASM sequence. */ @@ -289,81 +263,6 @@ code *genasm(code *c,char *s,unsigned slen) return cat(c,ce); } -/************************** - * Generate a MOV to,from register instruction. - * Smart enough to dump redundant register moves, and segment - * register moves. - */ - -code *genmovreg(code *c,unsigned to,unsigned from) -{ -#if DEBUG - if (to > ES || from > ES) - printf("genmovreg(c = %p, to = %d, from = %d)\n",c,to,from); -#endif - assert(to <= ES && from <= ES); - if (to != from) - { - if (to == ES) - c = genregs(c,0x8E,0,from); - else if (from == ES) - c = genregs(c,0x8C,0,to); - else - c = genregs(c,0x89,from,to); - if (I64) - code_orrex(c, REX_W); - } - return c; -} - -/************************** - * Generate a jump instruction. - */ - -code *genjmp(code *c,unsigned op,unsigned fltarg,block *targ) -{ code cs; - code *cj; - code *cnop; - - cs.Iop = op & 0xFF; - cs.Iflags = 0; - cs.Irex = 0; - if (op != JMP && op != 0xE8) // if not already long branch - cs.Iflags = CFjmp16; /* assume long branch for op = 0x7x */ - cs.IFL2 = fltarg; /* FLblock (or FLcode) */ - cs.IEV2.Vblock = targ; /* target block (or code) */ - if (fltarg == FLcode) - ((code *)targ)->Iflags |= CFtarg; - - if (config.flags4 & CFG4fastfloat) // if fast floating point - return gen(c,&cs); - - cj = gen(CNIL,&cs); - switch (op & 0xFF00) /* look at second jump opcode */ - { - /* The JP and JNP come from floating point comparisons */ - case JP << 8: - cs.Iop = JP; - gen(cj,&cs); - break; - case JNP << 8: - /* Do a JP around the jump instruction */ - cnop = gennop(CNIL); - c = genjmp(c,JP,FLcode,(block *) cnop); - cat(cj,cnop); - break; - case 1 << 8: /* toggled no jump */ - case 0 << 8: - break; - default: -#ifdef DEBUG - printf("jop = x%x\n",op); -#endif - assert(0); - } - return cat(c,cj); -} - code *gencs(code *c,unsigned op,unsigned ea,unsigned FL2,symbol *s) { code cs; @@ -427,34 +326,6 @@ code *genc(code *c,unsigned op,unsigned ea,unsigned FL1,targ_size_t EV1,unsigned return gen(c,&cs); } -/*************************************** - * Generate immediate multiply instruction for r1=r2*imm. - * Optimize it into LEA's if we can. - */ - -code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm) -{ code cs; - - // These optimizations should probably be put into pinholeopt() - switch (imm) - { case 1: - c = genmovreg(c,r1,r2); - break; - case 5: - cs.Iop = LEA; - cs.Iflags = 0; - cs.Irex = 0; - buildEA(&cs,r2,r2,4,0); - cs.orReg(r1); - c = gen(c,&cs); - break; - default: - c = genc2(c,0x69,modregxrmx(3,r1,r2),imm); // IMUL r1,r2,imm - break; - } - return c; -} - /******************************** * Generate 'instruction' which is actually a line number. */ @@ -541,306 +412,25 @@ code *gennop(code *c) return gen1(c,NOP); } -/****************************** - * Load CX with the value of _AHSHIFT. + +/**************************************** + * Clean stack after call to codelem(). */ -code *genshift(code *c) +code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag) { -#if SCPP && TX86 - code *c1; - - // Set up ahshift to trick ourselves into giving the right fixup, - // which must be seg-relative, external frame, external target. - c1 = gencs(CNIL,0xC7,modregrm(3,0,CX),FLfunc,rtlsym[RTLSYM_AHSHIFT]); - c1->Iflags |= CFoff; - return cat(c,c1); -#else - assert(0); - return 0; -#endif -} - -/****************************** - * Move constant value into reg. - * Take advantage of existing values in registers. - * If flags & mPSW - * set flags based on result - * Else if flags & 8 - * do not disturb flags - * Else - * don't care about flags - * If flags & 1 then byte move - * If flags & 2 then short move (for I32 and I64) - * If flags & 4 then don't disturb unused portion of register - * If flags & 16 then reg is a byte register AL..BH - * If flags & 64 (0x40) then 64 bit move (I64 only) - * Returns: - * code (if any) generated - */ - -code *movregconst(code *c,unsigned reg,targ_size_t value,regm_t flags) -{ unsigned r; - regm_t mreg; - - //printf("movregconst(reg=%s, value= %lld (%llx), flags=%x)\n", regm_str(mask[reg]), value, value, flags); -#define genclrreg(a,r) genregs(a,0x31,r,r) - - regm_t regm = regcon.immed.mval & mask[reg]; - targ_size_t regv = regcon.immed.value[reg]; - - if (flags & 1) // 8 bits - { - value &= 0xFF; - regm &= BYTEREGS; - - // If we already have the right value in the right register - if (regm && (regv & 0xFF) == value) - goto L2; - - if (flags & 16 && reg & 4 && // if an H byte register - regcon.immed.mval & mask[reg & 3] && - (((regv = regcon.immed.value[reg & 3]) >> 8) & 0xFF) == value) - goto L2; - - /* Avoid byte register loads on Pentium Pro and Pentium II - * to avoid dependency stalls. - */ - if (config.flags4 & CFG4speed && - config.target_cpu >= TARGET_PentiumPro && !(flags & 4)) - goto L3; - - // See if another register has the right value - r = 0; - for (mreg = (regcon.immed.mval & BYTEREGS); mreg; mreg >>= 1) - { - if (mreg & 1) - { - if ((regcon.immed.value[r] & 0xFF) == value) - { c = genregs(c,0x8A,reg,r); // MOV regL,rL - if (I64 && reg >= 4 || r >= 4) - code_orrex(c, REX); - goto L2; - } - if (!(I64 && reg >= 4) && - r < 4 && ((regcon.immed.value[r] >> 8) & 0xFF) == value) - { c = genregs(c,0x8A,reg,r | 4); // MOV regL,rH - goto L2; - } - } - r++; - } - - if (value == 0 && !(flags & 8)) - { - if (!(flags & 4) && // if we can set the whole register - !(flags & 16 && reg & 4)) // and reg is not an H register - { c = genregs(c,0x31,reg,reg); // XOR reg,reg - regimmed_set(reg,value); - regv = 0; - } - else - c = genregs(c,0x30,reg,reg); // XOR regL,regL - flags &= ~mPSW; // flags already set by XOR - } - else - { c = genc2(c,0xC6,modregrmx(3,0,reg),value); /* MOV regL,value */ - if (reg >= 4 && I64) - { - code_orrex(c, REX); - } - } - L2: - if (flags & mPSW) - genregs(c,0x84,reg,reg); // TEST regL,regL - - if (regm) - // Set just the 'L' part of the register value - regimmed_set(reg,(regv & ~(targ_size_t)0xFF) | value); - else if (flags & 16 && reg & 4 && regcon.immed.mval & mask[reg & 3]) - // Set just the 'H' part of the register value - regimmed_set((reg & 3),(regv & ~(targ_size_t)0xFF00) | (value << 8)); - return c; - } -L3: - if (I16) - value = (targ_short) value; /* sign-extend MSW */ - else if (I32) - value = (targ_int) value; - - if (!I16 && flags & 2) // load 16 bit value + if (e) { - value &= 0xFFFF; - if (value == 0) - goto L1; - else - { - if (flags & mPSW) - goto L1; - code *c1 = genc2(CNIL,0xC7,modregrmx(3,0,reg),value); // MOV reg,value - c1->Iflags |= CFopsize; // yes, even for I64 - c = cat(c,c1); - if (regm) - // High bits of register are not affected by 16 bit load - regimmed_set(reg,(regv & ~(targ_size_t)0xFFFF) | value); - } - return c; - } -L1: - - /* If we already have the right value in the right register */ - if (regm && (regv & 0xFFFFFFFF) == (value & 0xFFFFFFFF) && !(flags & 64)) - { if (flags & mPSW) - c = gentstreg(c,reg); - } - else if (flags & 64 && regm && regv == value) - { // Look at the full 64 bits - if (flags & mPSW) - { - c = gentstreg(c,reg); - code_orrex(c, REX_W); - } - } - else - { - if (flags & mPSW) - { - switch (value) - { case 0: - c = genclrreg(c,reg); - if (flags & 64) - code_orrex(c, REX_W); - break; - case 1: - if (I64) - goto L4; - c = genclrreg(c,reg); - goto inc; - case -1: - if (I64) - goto L4; - c = genclrreg(c,reg); - goto dec; - default: - L4: - if (flags & 64) - { - c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 - gentstreg(c,reg); - code_orrex(c, REX_W); - } - else - { c = genc2(c,0xC7,modregrmx(3,0,reg),value); /* MOV reg,value */ - gentstreg(c,reg); - } - break; - } - } - else - { - /* Look for single byte conversion */ - if (regcon.immed.mval & mAX) - { - if (I32) - { if (reg == AX && value == (targ_short) regv) - { c = gen1(c,0x98); /* CWDE */ - goto done; - } - if (reg == DX && - value == (regcon.immed.value[AX] & 0x80000000 ? 0xFFFFFFFF : 0) && - !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) - ) - { c = gen1(c,0x99); /* CDQ */ - goto done; - } - } - else if (I16) - { - if (reg == AX && - (targ_short) value == (signed char) regv) - { c = gen1(c,0x98); /* CBW */ - goto done; - } - - if (reg == DX && - (targ_short) value == (regcon.immed.value[AX] & 0x8000 ? (targ_short) 0xFFFF : (targ_short) 0) && - !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) - ) - { c = gen1(c,0x99); /* CWD */ - goto done; - } - } - } - if (value == 0 && !(flags & 8) && config.target_cpu >= TARGET_80486) - { c = genclrreg(c,reg); // CLR reg - if (flags & 64) - code_orrex(c, REX_W); - goto done; - } - - if (!I64 && regm && !(flags & 8)) - { if (regv + 1 == value || - /* Catch case of (0xFFFF+1 == 0) for 16 bit compiles */ - (I16 && (targ_short)(regv + 1) == (targ_short)value)) - { - inc: - c = gen1(c,0x40 + reg); /* INC reg */ - goto done; - } - if (regv - 1 == value) - { - dec: - c = gen1(c,0x48 + reg); /* DEC reg */ - goto done; - } - } - - /* See if another register has the right value */ - r = 0; - for (mreg = regcon.immed.mval; mreg; mreg >>= 1) - { -#ifdef DEBUG - assert(!I16 || regcon.immed.value[r] == (targ_short)regcon.immed.value[r]); -#endif - if (mreg & 1 && regcon.immed.value[r] == value) - { c = genmovreg(c,reg,r); - if (flags & 64) - code_orrex(c, REX_W); - goto done; - } - r++; - } - - if (value == 0 && !(flags & 8)) - { c = genclrreg(c,reg); // CLR reg - if (flags & 64) - code_orrex(c, REX_W); - } - else - { /* See if we can just load a byte */ - if (regm & BYTEREGS && - !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_PentiumPro) - ) - { - if ((regv & ~(targ_size_t)0xFF) == (value & ~(targ_size_t)0xFF)) - { c = movregconst(c,reg,value,(flags & 8) |4|1); // load regL - return c; - } - if (regm & (mAX|mBX|mCX|mDX) && - (regv & ~(targ_size_t)0xFF00) == (value & ~(targ_size_t)0xFF00) && - !I64) - { c = movregconst(c,4|reg,value >> 8,(flags & 8) |4|1|16); // load regH - return c; - } - } - if (flags & 64) - c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 - else - c = genc2(c,0xC7,modregrmx(3,0,reg),value); // MOV reg,value - } - } - done: - regimmed_set(reg,value); + unsigned stackpushsave; + int stackcleansave; + + stackpushsave = stackpush; + stackcleansave = cgstate.stackclean; + cgstate.stackclean = 0; // defer cleaning of stack + c = cat(c,codelem(e,pretregs,constflag)); + assert(cgstate.stackclean == 0); + cgstate.stackclean = stackcleansave; + c = genstackclean(c,stackpush - stackpushsave,*pretregs); // do defered cleaning } return c; } diff --git a/src/backend/cod1.c b/src/backend/cod1.c index 1c7a4d3bd070..15ea38101a6e 100644 --- a/src/backend/cod1.c +++ b/src/backend/cod1.c @@ -298,6 +298,29 @@ void andregcon(con_t *pregconsave) regcon.cse.mops &= regcon.cse.mval; } +/**************************************** + * Generate code for eecontext + */ + +void genEEcode() +{ regm_t retregs; + code *c; + + eecontext.EEin++; + regcon.immed.mval = 0; + retregs = 0; //regmask(eecontext.EEelem->Ety); + assert(EEoffset >= REGSIZE); + c = genc2(NULL,0x81,modregrm(3,5,SP),EEoffset - REGSIZE); // SUB ESP,EEoffset + gen1(c,0x50 + SI); // PUSH ESI + genadjesp(c,EEoffset); + c = gencodelem(c,eecontext.EEelem,&retregs, FALSE); + assignaddrc(c); + pinholeopt(c,NULL); + jmpaddr(c); + eecontext.EEcode = gen1(c,0xCC); // INT 3 + eecontext.EEin--; +} + /********************************* * Scan down comma-expressions. * Output: @@ -340,28 +363,6 @@ code *docommas(elem **pe) return cc; } -/**************************************** - * Clean stack after call to codelem(). - */ - -code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag) -{ - if (e) - { - unsigned stackpushsave; - int stackcleansave; - - stackpushsave = stackpush; - stackcleansave = cgstate.stackclean; - cgstate.stackclean = 0; // defer cleaning of stack - c = cat(c,codelem(e,pretregs,constflag)); - assert(cgstate.stackclean == 0); - cgstate.stackclean = stackcleansave; - c = genstackclean(c,stackpush - stackpushsave,*pretregs); // do defered cleaning - } - return c; -} - /******************************************** * Gen a save/restore sequence for mask of registers. */ @@ -1485,213 +1486,6 @@ code *getlvalue(code *pcs,elem *e,regm_t keepmsk) return c; } -/******************************* - * Same as codelem(), but do not destroy the registers in keepmsk. - * Use scratch registers as much as possible, then use stack. - * Input: - * constflag TRUE if user of result will not modify the - * registers returned in *pretregs. - */ - -code *scodelem(elem *e,regm_t *pretregs,regm_t keepmsk,bool constflag) -{ code *c,*cs1,*cs2,*cs3; - unsigned i,j; - regm_t oldmfuncreg,oldregcon,oldregimmed,overlap,tosave,touse; - int adjesp; - unsigned stackpushsave; - char calledafuncsave; - -#ifdef DEBUG - if (debugw) - printf("+scodelem(e=%p *pretregs=%s keepmsk=%s constflag=%d\n", - e,regm_str(*pretregs),regm_str(keepmsk),constflag); -#endif - elem_debug(e); - if (constflag) - { regm_t regm; - unsigned reg; - - if (isregvar(e,®m,®) && // if e is a register variable - (regm & *pretregs) == regm && // in one of the right regs - e->EV.sp.Voffset == 0 - ) - { - unsigned sz1 = tysize(e->Ety); - unsigned sz2 = tysize(e->EV.sp.Vsym->Stype->Tty); - if (sz1 <= REGSIZE && sz2 > REGSIZE) - regm &= mLSW; - c = fixresult(e,regm,pretregs); - cssave(e,regm,0); - freenode(e); -#ifdef DEBUG - if (debugw) - printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", - e,*pretregs,keepmsk,constflag); -#endif - return c; - } - } - overlap = msavereg & keepmsk; - msavereg |= keepmsk; /* add to mask of regs to save */ - oldregcon = regcon.cse.mval; - oldregimmed = regcon.immed.mval; - oldmfuncreg = mfuncreg; /* remember old one */ - mfuncreg = (mBP | mES | ALLREGS) & ~regcon.mvar; - stackpushsave = stackpush; -#if 0 - if (keepmsk) - stackpush++; /* assume we'll have to save stuff on stack */ -#endif - calledafuncsave = calledafunc; - calledafunc = 0; - c = codelem(e,pretregs,constflag); /* generate code for the elem */ -#if 0 - if (keepmsk) - stackpush--; -#endif - - tosave = keepmsk & ~msavereg; /* registers to save */ - if (tosave) - { cgstate.stackclean++; - c = genstackclean(c,stackpush - stackpushsave,*pretregs | msavereg); - cgstate.stackclean--; - } - - /* Assert that no new CSEs are generated that are not reflected */ - /* in mfuncreg. */ -#ifdef DEBUG - if ((mfuncreg & (regcon.cse.mval & ~oldregcon)) != 0) - printf("mfuncreg x%x, regcon.cse.mval x%x, oldregcon x%x, regcon.mvar x%x\n", - mfuncreg,regcon.cse.mval,oldregcon,regcon.mvar); -#endif - assert((mfuncreg & (regcon.cse.mval & ~oldregcon)) == 0); - - /* bugzilla 3521 - * The problem is: - * reg op (reg = exp) - * where reg must be preserved (in keepregs) while the expression to be evaluated - * must change it. - * The only solution is to make this variable not a register. - */ - if (regcon.mvar & tosave) - { - //elem_print(e); - //printf("test1: regcon.mvar x%x tosave x%x\n", regcon.mvar, tosave); - cgreg_unregister(regcon.mvar & tosave); - } - - /* which registers can we use to save other registers in? */ - if (config.flags4 & CFG4space || // if optimize for space - config.target_cpu >= TARGET_80486) // PUSH/POP ops are 1 cycle - touse = 0; // PUSH/POP pairs are always shorter - else - { touse = mfuncreg & allregs & ~(msavereg | oldregcon | regcon.cse.mval); - /* Don't use registers we'll have to save/restore */ - touse &= ~(fregsaved & oldmfuncreg); - /* Don't use registers that have constant values in them, since - the code generated might have used the value. - */ - touse &= ~oldregimmed; - } - - cs1 = cs2 = cs3 = NULL; - adjesp = 0; - - for (i = 0; tosave; i++) - { regm_t mi = mask[i]; - - assert(i < REGMAX); - if (mi & tosave) /* i = register to save */ - { - if (touse) /* if any scratch registers */ - { for (j = 0; j < 8; j++) - { regm_t mj = mask[j]; - - if (touse & mj) - { cs1 = genmovreg(cs1,j,i); - cs2 = cat(genmovreg(CNIL,i,j),cs2); - touse &= ~mj; - mfuncreg &= ~mj; - regcon.used |= mj; - break; - } - } - assert(j < 8); - } - else /* else use stack */ -#if 0 - { int push,pop; - - stackchanged = 1; - adjesp += REGSIZE; - if (i == ES) - { push = 0x06; - pop = 0x07; - } - else - { push = 0x50 + i; - pop = push | 8; - } - cs1 = gen1(cs1,push); /* PUSH i */ - cs2 = cat(gen1(CNIL,pop),cs2); /* POP i */ - } -#else - { - stackchanged = 1; - adjesp += REGSIZE; - gensaverestore2(mask[i], &cs1, &cs2); - } -#endif - cs3 = cat(getregs(mi),cs3); - tosave &= ~mi; - } - } - if (adjesp) - { - // If this is done an odd number of times, it - // will throw off the 8 byte stack alignment. - // We should *only* worry about this if a function - // was called in the code generation by codelem(). - int sz; - if (STACKALIGN == 16) - sz = -(adjesp & (STACKALIGN - 1)) & (STACKALIGN - 1); - else - sz = -(adjesp & 7) & 7; - if (calledafunc && !I16 && sz && (STACKALIGN == 16 || config.flags4 & CFG4stackalign)) - { - unsigned grex = I64 ? REX_W << 16 : 0; - regm_t mval_save = regcon.immed.mval; - regcon.immed.mval = 0; // prevent reghasvalue() optimizations - // because c hasn't been executed yet - cs1 = genc2(cs1,0x81,grex | modregrm(3,5,SP),sz); // SUB ESP,sz - if (I64) - code_orrex(cs1, REX_W); - regcon.immed.mval = mval_save; - cs1 = genadjesp(cs1, sz); - - code *cx = genc2(CNIL,0x81,grex | modregrm(3,0,SP),sz); // ADD ESP,sz - if (I64) - code_orrex(cx, REX_W); - cx = genadjesp(cx, -sz); - cs2 = cat(cx, cs2); - } - - cs1 = genadjesp(cs1,adjesp); - cs2 = genadjesp(cs2,-adjesp); - } - - calledafunc |= calledafuncsave; - msavereg &= ~keepmsk | overlap; /* remove from mask of regs to save */ - mfuncreg &= oldmfuncreg; /* update original */ -#ifdef DEBUG - if (debugw) - printf("-scodelem(e=%p *pretregs=x%x keepmsk=x%x constflag=%d\n", - e,*pretregs,keepmsk,constflag); -#endif - return cat4(cs1,c,cs3,cs2); -} - - /***************************** * Given an opcode and EA in cs, generate code * for each floating register in turn. diff --git a/src/backend/cod3.c b/src/backend/cod3.c index 41740c597409..e515d1b4eb42 100644 --- a/src/backend/cod3.c +++ b/src/backend/cod3.c @@ -314,6 +314,493 @@ void cod3_align() #endif } +/***************************** + * Given a type, return a mask of + * registers to hold that type. + * Input: + * tyf function type + */ + +regm_t regmask(tym_t tym, tym_t tyf) +{ + switch (tybasic(tym)) + { + case TYvoid: + case TYstruct: + return 0; + case TYbool: + case TYwchar_t: + case TYchar16: + case TYchar: + case TYschar: + case TYuchar: + case TYshort: + case TYushort: + case TYint: + case TYuint: +#if JHANDLE + case TYjhandle: +#endif + case TYnullptr: + case TYnptr: + case TYsptr: + case TYcptr: + return mAX; + + case TYfloat: + case TYifloat: + if (I64) + return mXMM0; + if (config.exe & EX_flat) + return mST0; + case TYlong: + case TYulong: + case TYdchar: + if (!I16) + return mAX; + case TYfptr: + case TYhptr: + return mDX | mAX; + + case TYcent: + case TYucent: + assert(I64); + return mDX | mAX; + + case TYvptr: + return mDX | mBX; + + case TYdouble: + case TYdouble_alias: + case TYidouble: + if (I64) + return mXMM0; + if (config.exe & EX_flat) + return mST0; + return DOUBLEREGS; + + case TYllong: + case TYullong: + return I64 ? mAX : (I32 ? mDX | mAX : DOUBLEREGS); + + case TYldouble: + case TYildouble: + return mST0; + + case TYcfloat: +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (I32 && tybasic(tyf) == TYnfunc) + return mDX | mAX; +#endif + case TYcdouble: + if (I64) + return mXMM0 | mXMM1; + case TYcldouble: + return mST01; + + default: +#if DEBUG + WRTYxx(tym); +#endif + assert(0); + return 0; + } +} + +/******************************* + * Generate block exit code + */ +void outblkexitcode(block *bl, code*& c, int& anyspill, const char* sflsave, symbol** retsym, const regm_t mfuncregsave) +{ + elem *e = bl->Belem; + block *nextb; + block *bs1,*bs2; + regm_t retregs = 0; + bool jcond; + + switch (bl->BC) /* block exit condition */ + { + case BCiftrue: + jcond = TRUE; + bs1 = list_block(bl->Bsucc); + bs2 = list_block(list_next(bl->Bsucc)); + if (bs1 == bl->Bnext) + { // Swap bs1 and bs2 + block *btmp; + + jcond ^= 1; + btmp = bs1; + bs1 = bs2; + bs2 = btmp; + } + c = cat(c,logexp(e,jcond,FLblock,(code *) bs1)); + nextb = bs2; + bl->Bcode = NULL; + L2: + if (nextb != bl->Bnext) + { if (configv.addlinenumbers && bl->Bsrcpos.Slinnum && + !(funcsym_p->ty() & mTYnaked)) + cgen_linnum(&c,bl->Bsrcpos); + assert(!(bl->Bflags & BFLepilog)); + c = cat(c,genjmp(CNIL,JMP,FLblock,nextb)); + } + bl->Bcode = cat(bl->Bcode,c); + break; + case BCjmptab: + case BCifthen: + case BCswitch: + assert(!(bl->Bflags & BFLepilog)); + doswitch(bl); /* hide messy details */ + bl->Bcode = cat(c,bl->Bcode); + break; +#if MARS + case BCjcatch: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in catch blocks. + c = cat(c,getregs((I32 | I64) ? allregs : (ALLREGS | mES))); +#if 0 && TARGET_LINUX + if (config.flags3 & CFG3pic && !(allregs & mBX)) + { + c = cat(c, cod3_load_got()); + } +#endif + goto case_goto; +#endif +#if SCPP + case BCcatch: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in catch blocks. + c = cat(c,getregs(allregs | mES)); +#if 0 && TARGET_LINUX + if (config.flags3 & CFG3pic && !(allregs & mBX)) + { + c = cat(c, cod3_load_got()); + } +#endif + goto case_goto; + + case BCtry: + usednteh |= EHtry; + if (config.flags2 & CFG2seh) + usednteh |= NTEHtry; + goto case_goto; +#endif + case BCgoto: + nextb = list_block(bl->Bsucc); + if ((funcsym_p->Sfunc->Fflags3 & Fnteh || + (MARS /*&& config.flags2 & CFG2seh*/)) && + bl->Btry != nextb->Btry && + nextb->BC != BC_finally) + { int toindex; + int fromindex; + + bl->Bcode = NULL; + c = gencodelem(c,e,&retregs,TRUE); + toindex = nextb->Btry ? nextb->Btry->Bscope_index : -1; + assert(bl->Btry); + fromindex = bl->Btry->Bscope_index; +#if MARS + if (toindex + 1 == fromindex) + { // Simply call __finally + if (bl->Btry && + list_block(list_next(bl->Btry->Bsucc))->BC == BCjcatch) + { + goto L2; + } + } +#endif + if (config.flags2 & CFG2seh) + c = cat(c,nteh_unwind(0,toindex)); +#if MARS && (TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS) + else if (toindex + 1 <= fromindex) + { + //c = cat(c, linux_unwind(0, toindex)); + block *bt; + + //printf("B%d: fromindex = %d, toindex = %d\n", bl->Bdfoidx, fromindex, toindex); + bt = bl; + while ((bt = bt->Btry) != NULL && bt->Bscope_index != toindex) + { block *bf; + + //printf("\tbt->Bscope_index = %d, bt->Blast_index = %d\n", bt->Bscope_index, bt->Blast_index); + bf = list_block(list_next(bt->Bsucc)); + // Only look at try-finally blocks + if (bf->BC == BCjcatch) + continue; + + if (bf == nextb) + continue; + //printf("\tbf = B%d, nextb = B%d\n", bf->Bdfoidx, nextb->Bdfoidx); + if (nextb->BC == BCgoto && + !nextb->Belem && + bf == list_block(nextb->Bsucc)) + continue; + + // call __finally + code *cs; + code *cr; + int nalign = 0; + + gensaverestore(retregs,&cs,&cr); + if (STACKALIGN == 16) + { int npush = (numbitsset(retregs) + 1) * REGSIZE; + if (npush & (STACKALIGN - 1)) + { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); + cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + } + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + if (nalign) + { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + c = cat3(c,cs,cr); + } + } +#endif + goto L2; + } + case_goto: + c = gencodelem(c,e,&retregs,TRUE); + if (anyspill) + { // Add in the epilog code + code *cstore = NULL; + code *cload = NULL; + + for (int i = 0; i < anyspill; i++) + { symbol *s = globsym.tab[i]; + + if (s->Sflags & SFLspill && + vec_testbit(dfoidx,s->Srange)) + { + s->Sfl = sflsave[i]; // undo block register assignments + cgreg_spillreg_epilog(bl,s,&cstore,&cload); + } + } + c = cat3(c,cstore,cload); + } + + L3: + bl->Bcode = NULL; + nextb = list_block(bl->Bsucc); + goto L2; + + case BC_try: + if (config.flags2 & CFG2seh) + { usednteh |= NTEH_try; + nteh_usevars(); + } + else + usednteh |= EHtry; + goto case_goto; + + case BC_finally: + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in finally blocks. + assert(!getregs(allregs)); + assert(!e); + assert(!bl->Bcode); +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + if (config.flags3 & CFG3pic) + { + int nalign = 0; + if (STACKALIGN == 16) + { nalign = STACKALIGN - REGSIZE; + c = genc2(c,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + // CALL bl->Bsucc + c = genc(c,0xE8,0,0,0,FLblock,(long)list_block(bl->Bsucc)); + if (nalign) + { c = genc2(c,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(c, REX_W); + } + // JMP list_next(bl->Bsucc) + nextb = list_block(list_next(bl->Bsucc)); + goto L2; + } + else +#endif + { + // Generate a PUSH of the address of the successor to the + // corresponding BC_ret + //assert(list_block(list_next(bl->Bsucc))->BC == BC_ret); + // PUSH &succ + c = genc(c,0x68,0,0,0,FLblock,(long)list_block(list_next(bl->Bsucc))); + nextb = list_block(bl->Bsucc); + goto L2; + } + + case BC_ret: + c = gencodelem(c,e,&retregs,TRUE); + bl->Bcode = gen1(c,0xC3); // RET + break; + +#if NTEXCEPTIONS + case BC_except: + assert(!e); + usednteh |= NTEH_except; + c = cat(c,nteh_setsp(0x8B)); + getregs(allregs); + goto L3; + + case BC_filter: + c = cat(c,nteh_filter(bl)); + // Mark all registers as destroyed. This will prevent + // register assignments to variables used in filter blocks. + getregs(allregs); + retregs = regmask(e->Ety, TYnfunc); + c = gencodelem(c,e,&retregs,TRUE); + bl->Bcode = gen1(c,0xC3); // RET + break; +#endif + + case BCretexp: + retregs = regmask(e->Ety, funcsym_p->ty()); + + // For the final load into the return regs, don't set regcon.used, + // so that the optimizer can potentially use retregs for register + // variable assignments. + + if (config.flags4 & CFG4optimized) + { regm_t usedsave; + + c = cat(c,docommas(&e)); + usedsave = regcon.used; + if (EOP(e)) + c = gencodelem(c,e,&retregs,TRUE); + else + { + if (e->Eoper == OPconst) + regcon.mvar = 0; + c = gencodelem(c,e,&retregs,TRUE); + regcon.used = usedsave; + if (e->Eoper == OPvar) + { symbol *s = e->EV.sp.Vsym; + + if (s->Sfl == FLreg && s->Sregm != mAX) + *retsym = s; + } + } + } + else + { + case BCret: + case BCexit: + c = gencodelem(c,e,&retregs,TRUE); + } + bl->Bcode = c; + if (retregs == mST0) + { assert(stackused == 1); + pop87(); // account for return value + } + else if (retregs == mST01) + { assert(stackused == 2); + pop87(); + pop87(); // account for return value + } + if (bl->BC == BCexit && config.flags4 & CFG4optimized) + mfuncreg = mfuncregsave; + if (MARS || usednteh & NTEH_try) + { block *bt; + + bt = bl; + while ((bt = bt->Btry) != NULL) + { block *bf; + + bf = list_block(list_next(bt->Bsucc)); +#if MARS + // Only look at try-finally blocks + if (bf->BC == BCjcatch) + { + continue; + } +#endif + if (config.flags2 & CFG2seh) + { + if (bt->Bscope_index == 0) + { + // call __finally + code *cs; + code *cr; + + c = cat(c,nteh_gensindex(-1)); + gensaverestore(retregs,&cs,&cr); + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + bl->Bcode = cat3(c,cs,cr); + } + else + bl->Bcode = cat(c,nteh_unwind(retregs,~0)); + break; + } + else + { + // call __finally + code *cs; + code *cr; + int nalign = 0; + + gensaverestore(retregs,&cs,&cr); + if (STACKALIGN == 16) + { int npush = (numbitsset(retregs) + 1) * REGSIZE; + if (npush & (STACKALIGN - 1)) + { nalign = STACKALIGN - (npush & (STACKALIGN - 1)); + cs = genc2(cs,0x81,modregrm(3,5,SP),nalign); // SUB ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + } + // CALL bf->Bsucc + cs = genc(cs,0xE8,0,0,0,FLblock,(long)list_block(bf->Bsucc)); + if (nalign) + { cs = genc2(cs,0x81,modregrm(3,0,SP),nalign); // ADD ESP,nalign + if (I64) + code_orrex(cs, REX_W); + } + bl->Bcode = c = cat3(c,cs,cr); + } + } + } + break; + +#if SCPP || MARS + case BCasm: + assert(!e); + // Mark destroyed registers + assert(!c); + c = cat(c,getregs(iasm_regs(bl))); + if (bl->Bsucc) + { nextb = list_block(bl->Bsucc); + if (!bl->Bnext) + goto L2; + if (nextb != bl->Bnext && + bl->Bnext && + !(bl->Bnext->BC == BCgoto && + !bl->Bnext->Belem && + nextb == list_block(bl->Bnext->Bsucc))) + { code *cl; + + // See if already have JMP at end of block + cl = code_last(bl->Bcode); + if (!cl || cl->Iop != JMP) + goto L2; // add JMP at end of block + } + } + break; +#endif + default: +#ifdef DEBUG + printf("bl->BC = %d\n",bl->BC); +#endif + assert(0); + } +} + /******************************* * Generate code for blocks ending in a switch statement. * Take BCswitch and decide on @@ -1176,6 +1663,457 @@ STATIC int obj_namestring(char *p,const char *name) } #endif +code *genregs(code *c,unsigned op,unsigned dstreg,unsigned srcreg) +{ return gen2(c,op,modregxrmx(3,dstreg,srcreg)); } + +code *gentstreg(code *c,unsigned t) +{ + c = gen2(c,0x85,modregxrmx(3,t,t)); // TEST t,t + code_orflag(c,CFpsw); + return c; +} + +code *genpush(code *c, unsigned reg) +{ + c = gen1(c, 0x50 + (reg & 7)); + if (reg & 8) + code_orrex(c, REX_B); + return c; +} + +code *genpop(code *c, unsigned reg) +{ + c = gen1(c, 0x58 + (reg & 7)); + if (reg & 8) + code_orrex(c, REX_B); + return c; +} + +/************************** + * Generate a MOV to save a register to a stack slot + */ +code *gensavereg(unsigned& reg, targ_uns slot) +{ + // MOV i[BP],reg + unsigned op = 0x89; // normal mov + if (reg == ES) + { reg = 0; // the real reg number + op = 0x8C; // segment reg mov + } + code *c = genc1(NULL,op,modregxrm(2, reg, BPRM),FLcs,slot); + if (I64) + code_orrex(c, REX_W); + + return c; +} + +/************************** + * Generate a MOV to,from register instruction. + * Smart enough to dump redundant register moves, and segment + * register moves. + */ + +code *genmovreg(code *c,unsigned to,unsigned from) +{ +#if DEBUG + if (to > ES || from > ES) + printf("genmovreg(c = %p, to = %d, from = %d)\n",c,to,from); +#endif + assert(to <= ES && from <= ES); + if (to != from) + { + if (to == ES) + c = genregs(c,0x8E,0,from); + else if (from == ES) + c = genregs(c,0x8C,0,to); + else + c = genregs(c,0x89,from,to); + if (I64) + code_orrex(c, REX_W); + } + return c; +} + +/*************************************** + * Generate immediate multiply instruction for r1=r2*imm. + * Optimize it into LEA's if we can. + */ + +code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm) +{ code cs; + + // These optimizations should probably be put into pinholeopt() + switch (imm) + { case 1: + c = genmovreg(c,r1,r2); + break; + case 5: + cs.Iop = LEA; + cs.Iflags = 0; + cs.Irex = 0; + buildEA(&cs,r2,r2,4,0); + cs.orReg(r1); + c = gen(c,&cs); + break; + default: + c = genc2(c,0x69,modregxrmx(3,r1,r2),imm); // IMUL r1,r2,imm + break; + } + return c; +} + +/****************************** + * Load CX with the value of _AHSHIFT. + */ + +code *genshift(code *c) +{ +#if SCPP && TX86 + code *c1; + + // Set up ahshift to trick ourselves into giving the right fixup, + // which must be seg-relative, external frame, external target. + c1 = gencs(CNIL,0xC7,modregrm(3,0,CX),FLfunc,rtlsym[RTLSYM_AHSHIFT]); + c1->Iflags |= CFoff; + return cat(c,c1); +#else + assert(0); + return 0; +#endif +} + +/****************************** + * Move constant value into reg. + * Take advantage of existing values in registers. + * If flags & mPSW + * set flags based on result + * Else if flags & 8 + * do not disturb flags + * Else + * don't care about flags + * If flags & 1 then byte move + * If flags & 2 then short move (for I32 and I64) + * If flags & 4 then don't disturb unused portion of register + * If flags & 16 then reg is a byte register AL..BH + * If flags & 64 (0x40) then 64 bit move (I64 only) + * Returns: + * code (if any) generated + */ + +code *movregconst(code *c,unsigned reg,targ_size_t value,regm_t flags) +{ unsigned r; + regm_t mreg; + + //printf("movregconst(reg=%s, value= %lld (%llx), flags=%x)\n", regm_str(mask[reg]), value, value, flags); +#define genclrreg(a,r) genregs(a,0x31,r,r) + + regm_t regm = regcon.immed.mval & mask[reg]; + targ_size_t regv = regcon.immed.value[reg]; + + if (flags & 1) // 8 bits + { + value &= 0xFF; + regm &= BYTEREGS; + + // If we already have the right value in the right register + if (regm && (regv & 0xFF) == value) + goto L2; + + if (flags & 16 && reg & 4 && // if an H byte register + regcon.immed.mval & mask[reg & 3] && + (((regv = regcon.immed.value[reg & 3]) >> 8) & 0xFF) == value) + goto L2; + + /* Avoid byte register loads on Pentium Pro and Pentium II + * to avoid dependency stalls. + */ + if (config.flags4 & CFG4speed && + config.target_cpu >= TARGET_PentiumPro && !(flags & 4)) + goto L3; + + // See if another register has the right value + r = 0; + for (mreg = (regcon.immed.mval & BYTEREGS); mreg; mreg >>= 1) + { + if (mreg & 1) + { + if ((regcon.immed.value[r] & 0xFF) == value) + { c = genregs(c,0x8A,reg,r); // MOV regL,rL + if (I64 && reg >= 4 || r >= 4) + code_orrex(c, REX); + goto L2; + } + if (!(I64 && reg >= 4) && + r < 4 && ((regcon.immed.value[r] >> 8) & 0xFF) == value) + { c = genregs(c,0x8A,reg,r | 4); // MOV regL,rH + goto L2; + } + } + r++; + } + + if (value == 0 && !(flags & 8)) + { + if (!(flags & 4) && // if we can set the whole register + !(flags & 16 && reg & 4)) // and reg is not an H register + { c = genregs(c,0x31,reg,reg); // XOR reg,reg + regimmed_set(reg,value); + regv = 0; + } + else + c = genregs(c,0x30,reg,reg); // XOR regL,regL + flags &= ~mPSW; // flags already set by XOR + } + else + { c = genc2(c,0xC6,modregrmx(3,0,reg),value); /* MOV regL,value */ + if (reg >= 4 && I64) + { + code_orrex(c, REX); + } + } + L2: + if (flags & mPSW) + genregs(c,0x84,reg,reg); // TEST regL,regL + + if (regm) + // Set just the 'L' part of the register value + regimmed_set(reg,(regv & ~(targ_size_t)0xFF) | value); + else if (flags & 16 && reg & 4 && regcon.immed.mval & mask[reg & 3]) + // Set just the 'H' part of the register value + regimmed_set((reg & 3),(regv & ~(targ_size_t)0xFF00) | (value << 8)); + return c; + } +L3: + if (I16) + value = (targ_short) value; /* sign-extend MSW */ + else if (I32) + value = (targ_int) value; + + if (!I16 && flags & 2) // load 16 bit value + { + value &= 0xFFFF; + if (value == 0) + goto L1; + else + { + if (flags & mPSW) + goto L1; + code *c1 = genc2(CNIL,0xC7,modregrmx(3,0,reg),value); // MOV reg,value + c1->Iflags |= CFopsize; // yes, even for I64 + c = cat(c,c1); + if (regm) + // High bits of register are not affected by 16 bit load + regimmed_set(reg,(regv & ~(targ_size_t)0xFFFF) | value); + } + return c; + } +L1: + + /* If we already have the right value in the right register */ + if (regm && (regv & 0xFFFFFFFF) == (value & 0xFFFFFFFF) && !(flags & 64)) + { if (flags & mPSW) + c = gentstreg(c,reg); + } + else if (flags & 64 && regm && regv == value) + { // Look at the full 64 bits + if (flags & mPSW) + { + c = gentstreg(c,reg); + code_orrex(c, REX_W); + } + } + else + { + if (flags & mPSW) + { + switch (value) + { case 0: + c = genclrreg(c,reg); + if (flags & 64) + code_orrex(c, REX_W); + break; + case 1: + if (I64) + goto L4; + c = genclrreg(c,reg); + goto inc; + case -1: + if (I64) + goto L4; + c = genclrreg(c,reg); + goto dec; + default: + L4: + if (flags & 64) + { + c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 + gentstreg(c,reg); + code_orrex(c, REX_W); + } + else + { c = genc2(c,0xC7,modregrmx(3,0,reg),value); /* MOV reg,value */ + gentstreg(c,reg); + } + break; + } + } + else + { + /* Look for single byte conversion */ + if (regcon.immed.mval & mAX) + { + if (I32) + { if (reg == AX && value == (targ_short) regv) + { c = gen1(c,0x98); /* CWDE */ + goto done; + } + if (reg == DX && + value == (regcon.immed.value[AX] & 0x80000000 ? 0xFFFFFFFF : 0) && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) + ) + { c = gen1(c,0x99); /* CDQ */ + goto done; + } + } + else if (I16) + { + if (reg == AX && + (targ_short) value == (signed char) regv) + { c = gen1(c,0x98); /* CBW */ + goto done; + } + + if (reg == DX && + (targ_short) value == (regcon.immed.value[AX] & 0x8000 ? (targ_short) 0xFFFF : (targ_short) 0) && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_Pentium) + ) + { c = gen1(c,0x99); /* CWD */ + goto done; + } + } + } + if (value == 0 && !(flags & 8) && config.target_cpu >= TARGET_80486) + { c = genclrreg(c,reg); // CLR reg + if (flags & 64) + code_orrex(c, REX_W); + goto done; + } + + if (!I64 && regm && !(flags & 8)) + { if (regv + 1 == value || + /* Catch case of (0xFFFF+1 == 0) for 16 bit compiles */ + (I16 && (targ_short)(regv + 1) == (targ_short)value)) + { + inc: + c = gen1(c,0x40 + reg); /* INC reg */ + goto done; + } + if (regv - 1 == value) + { + dec: + c = gen1(c,0x48 + reg); /* DEC reg */ + goto done; + } + } + + /* See if another register has the right value */ + r = 0; + for (mreg = regcon.immed.mval; mreg; mreg >>= 1) + { +#ifdef DEBUG + assert(!I16 || regcon.immed.value[r] == (targ_short)regcon.immed.value[r]); +#endif + if (mreg & 1 && regcon.immed.value[r] == value) + { c = genmovreg(c,reg,r); + if (flags & 64) + code_orrex(c, REX_W); + goto done; + } + r++; + } + + if (value == 0 && !(flags & 8)) + { c = genclrreg(c,reg); // CLR reg + if (flags & 64) + code_orrex(c, REX_W); + } + else + { /* See if we can just load a byte */ + if (regm & BYTEREGS && + !(config.flags4 & CFG4speed && config.target_cpu >= TARGET_PentiumPro) + ) + { + if ((regv & ~(targ_size_t)0xFF) == (value & ~(targ_size_t)0xFF)) + { c = movregconst(c,reg,value,(flags & 8) |4|1); // load regL + return c; + } + if (regm & (mAX|mBX|mCX|mDX) && + (regv & ~(targ_size_t)0xFF00) == (value & ~(targ_size_t)0xFF00) && + !I64) + { c = movregconst(c,4|reg,value >> 8,(flags & 8) |4|1|16); // load regH + return c; + } + } + if (flags & 64) + c = genc2(c,0xC7,(REX_W << 16) | modregrmx(3,0,reg),value); // MOV reg,value64 + else + c = genc2(c,0xC7,modregrmx(3,0,reg),value); // MOV reg,value + } + } + done: + regimmed_set(reg,value); + } + return c; +} + +/************************** + * Generate a jump instruction. + */ + +code *genjmp(code *c,unsigned op,unsigned fltarg,block *targ) +{ code cs; + code *cj; + code *cnop; + + cs.Iop = op & 0xFF; + cs.Iflags = 0; + cs.Irex = 0; + if (op != JMP && op != 0xE8) // if not already long branch + cs.Iflags = CFjmp16; /* assume long branch for op = 0x7x */ + cs.IFL2 = fltarg; /* FLblock (or FLcode) */ + cs.IEV2.Vblock = targ; /* target block (or code) */ + if (fltarg == FLcode) + ((code *)targ)->Iflags |= CFtarg; + + if (config.flags4 & CFG4fastfloat) // if fast floating point + return gen(c,&cs); + + cj = gen(CNIL,&cs); + switch (op & 0xFF00) /* look at second jump opcode */ + { + /* The JP and JNP come from floating point comparisons */ + case JP << 8: + cs.Iop = JP; + gen(cj,&cs); + break; + case JNP << 8: + /* Do a JP around the jump instruction */ + cnop = gennop(CNIL); + c = genjmp(c,JP,FLcode,(block *) cnop); + cat(cj,cnop); + break; + case 1 << 8: /* toggled no jump */ + case 0 << 8: + break; + default: +#ifdef DEBUG + printf("jop = x%x\n",op); +#endif + assert(0); + } + return cat(c,cj); +} + /******************************* * Generate code for a function start. * Input: diff --git a/src/backend/code.h b/src/backend/code.h index 3682ddd4470b..c63f9e30c466 100644 --- a/src/backend/code.h +++ b/src/backend/code.h @@ -623,7 +623,6 @@ extern code *(*cdxxx[])(elem *,regm_t *); void stackoffsets(int); void codgen (void ); -regm_t regmask (tym_t tym, tym_t tyf ); #ifdef DEBUG unsigned findreg (regm_t regm , int line , const char *file ); #define findreg(regm) findreg((regm),__LINE__,__FILE__) @@ -648,6 +647,7 @@ void cssave (elem *e , regm_t regm , unsigned opsflag ); bool evalinregister (elem *e ); regm_t getscratch(); code *codelem (elem *e , regm_t *pretregs , bool constflag ); +code *scodelem (elem *e , regm_t *pretregs , regm_t keepmsk , bool constflag ); const char *regm_str(regm_t rm); int numbitsset(regm_t); @@ -659,9 +659,10 @@ int ssindex(int op,targ_uns product); void buildEA(code *c,int base,int index,int scale,targ_size_t disp); unsigned buildModregrm(int mod, int reg, int rm); void andregcon (con_t *pregconsave); +void genEEcode(); code *docommas (elem **pe ); -code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag); void gensaverestore(regm_t, code **, code **); +void gensaverestore2(regm_t regm,code **csave,code **crestore); code *genstackclean(code *c,unsigned numpara,regm_t keepmsk); code *logexp (elem *e , int jcond , unsigned fltarg , code *targ ); code *loadea (elem *e , code *cs , unsigned op , unsigned reg , targ_size_t offset , regm_t keepmsk , regm_t desmsk ); @@ -670,7 +671,6 @@ void setaddrmode(code *c, regm_t idxregs); void getlvalue_msw(code *); void getlvalue_lsw(code *); code *getlvalue (code *pcs , elem *e , regm_t keepmsk ); -code *scodelem (elem *e , regm_t *pretregs , regm_t keepmsk , bool constflag ); code *fltregs (code *pcs , tym_t tym ); code *tstresult (regm_t regm , tym_t tym , unsigned saveflag ); code *fixresult (elem *e , regm_t retregs , regm_t *pretregs ); @@ -731,11 +731,23 @@ regm_t cod3_useBP(); void cod3_set32 (void ); void cod3_set64 (void ); void cod3_align (void ); +regm_t regmask(tym_t tym, tym_t tyf); +void outblkexitcode(block *bl, code*& c, int& anyspill, const char* sflsave, symbol** retsym, const regm_t mfuncregsave ); void doswitch (block *b ); void outjmptab (block *b ); void outswitab (block *b ); int jmpopcode (elem *e ); void cod3_ptrchk(code **pc,code *pcs,regm_t keepmsk); +code *genregs (code *c , unsigned op , unsigned dstreg , unsigned srcreg ); +code *gentstreg (code *c , unsigned reg ); +code *genpush (code *c , unsigned reg ); +code *genpop (code *c , unsigned reg ); +code* gensavereg(unsigned& reg, targ_uns slot); +code *genmovreg (code *c , unsigned to , unsigned from ); +code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm); +code *genshift(code *); +code *movregconst (code *c , unsigned reg , targ_size_t value , regm_t flags ); +code *genjmp (code *c , unsigned op , unsigned fltarg , block *targ ); code *prolog (void ); void epilog (block *b); cd_t cdframeptr; @@ -860,27 +872,19 @@ code *gen (code *c , code *cs ); code *gen1 (code *c , unsigned op ); code *gen2 (code *c , unsigned op , unsigned rm ); code *gen2sib(code *c,unsigned op,unsigned rm,unsigned sib); -code *genregs (code *c , unsigned op , unsigned dstreg , unsigned srcreg ); -code *genpush (code *c , unsigned reg ); -code *genpop (code *c , unsigned reg ); -code *gentstreg (code *c , unsigned reg ); code *genasm (code *c , char *s , unsigned slen ); -code *genmovreg (code *c , unsigned to , unsigned from ); -code *genjmp (code *c , unsigned op , unsigned fltarg , block *targ ); code *gencsi (code *c , unsigned op , unsigned rm , unsigned FL2 , SYMIDX si ); code *gencs (code *c , unsigned op , unsigned rm , unsigned FL2 , symbol *s ); code *genc2 (code *c , unsigned op , unsigned rm , targ_size_t EV2 ); code *genc1 (code *c , unsigned op , unsigned rm , unsigned FL1 , targ_size_t EV1 ); code *genc (code *c , unsigned op , unsigned rm , unsigned FL1 , targ_size_t EV1 , unsigned FL2 , targ_size_t EV2 ); -code *genmulimm(code *c,unsigned r1,unsigned r2,targ_int imm); code *genlinnum(code *,Srcpos); void cgen_linnum(code **pc,Srcpos srcpos); void cgen_prelinnum(code **pc,Srcpos srcpos); code *genadjesp(code *c, int offset); code *genadjfpu(code *c, int offset); code *gennop(code *); -code *genshift(code *); -code *movregconst (code *c , unsigned reg , targ_size_t value , regm_t flags ); +code *gencodelem(code *c,elem *e,regm_t *pretregs,bool constflag); bool reghasvalue (regm_t regm , targ_size_t value , unsigned *preg ); code *regwithvalue (code *c , regm_t regm , targ_size_t value , unsigned *preg , regm_t flags );