/* EPC Imp to C Translation Release 4 Version Apr 95 */ #include "imptoc.h" #undef PI /** 02Sep97 - p5procs06.5.2 */ /** Correction in Improve Compares for Byte conparison */ /** Changes in wrapping to help keep stack 8 byte aligned */ /** and to support microsofts STDCALL */ /** 16Jul97 - p5procs06.5.1 */ /** Two minor bug fixes in loopopt (Hoisting from conde sections */ /** and cleanupfrag (Care with nextinstr being NULL */ /** More care in tail recursion */ /** Care in Doubledefs over dead instrs which change FSTP */ /** 08Jul97 - p5procs06.5 */ /** Don't make the instruction generated by makefpop potentially */ /** deleteable since FP stack must *always* be tidied up. */ /** Use Breconvergeliveness not Bupdate.. in InproveCondEstablish */ /** since several frags have been changed. */ /** Watch for FISTPd in peepholer since this instr is notstandard */ /** Also avoid popping params into edx if a LONGINT is returned */ /** 10Jun97 - p5rocs06.4 */ /** More effort in Cvalidate jumps to get correct jump size */ /** 09May97 - p5procs06.3.2 */ /** Moved one assembler optimisation to Cestablishca from finaltidy*/ /** so that Calums exceptions tables keep the right offset with -g */ /** Also take more care when putting out .aligns in the code */ /** 24Mar97 - p5procs06.3.1 */ /** Checked for redundant test instructions in cleanupfrag */ /** Added the AND by 2**N CMP 2**N optimisation for GPT */ /** 20Nov96 - p5procs06.3 */ /** Removed BARRIER FLAG from exit sequence for better schedules */ /** 24Oct96 - Flattening for translation to c p5procs06.2 */ /** 16Oct96 - PIC used via external var. extensions for GPT&DG */ /** 02Jly96 - P5procs06 */ /** More work on loopopt */ /** 18Jun96 - p5procs05.1 */ /** Constrain loopopt so as not to expand the wrappers(pds) */ /** Handle LEAs correctly in 'improve leafentry' */ /** Minor changes to Assembler for Lynxos */ /** 22Feb95 - p5procs05 */ /** Changes to improve performace for DG and others including */ /** use of the Beta registers to model AGI delays(pds) */ /** 31Jan95 - p5procs04.5.i */ /** Changes to exit wrapper not to rely on ESP */ /** 24Nov95 - p5procs04.4.i */ /** Additional optimisations for bits and bytes using movb */ /** 10Nov95 - p5procs04.3.i */ /** Used TargetABI to get minor differences for DG-UX operating sys*/ /** 28Jly95 - Added peepholing to delete redundant real truncates */ /** 31May95 - P5reinitialise added and asstext changed for GNU as p5rprocs04.1.i*/ /** 09Sep94 - Changes for Pentium Specific optimisations p5procs04.i */ /** 25Mar94 - Changes for new GIS bprocs p5procs02.i */ /***/ /***/ /** 27/10/91 - base version p5procs01.i (derived loosely from mipsprocs9) */ /***/ #define modulename ("targetprocs") /***/ #include "cgtarget.h" /***/ #include "cgconsts.h" /***/ #include "archdefs.h" #include "boconsts.h" /***/ /***/ #define optimtrace 1 /***/ /***/ /**************************************************************************/ /** **/ /** Imports **/ /** **/ /**************************************************************************/ /***/ /***/ #include"prototypes.h" /***/ extern int RegClaimMaskA; extern int RegClaimMaskB; /* %EXTERNALINTEGERSPEC lowfreg,lowreg, saveparams, paramssaved */ extern int diagnostics; extern int UnkCntrlFlow; /***/ /***/ /***/ /********************************************************************************/ /** **/ /** CONSTANTS FOR FUNCTIONAL INTERFACE TO GLOBAL SCHEDULER **/ /** **/ /********************************************************************************/ /***/ /** Bit values for bit vector information passed to global scheduler */ /***/ /** Values for GetAccessInfo */ #define AILITOFFSET (1<<0) /*access has a literal offset */ #define AIFIXOFFSET (1<<1) /*access has a fixup displacement */ #define AISCALED (1<<2) /*access has scaled register offset */ #define AIVOLATILE (1<<3) /*access is to volatile location */ #define AITSAACCESS (1<<4) /*access is to TSA */ #define AIDBLIND (1<<5) /*access is doubly indirect */ #define AIDOMEXCL (1<<6) /*access is domain exclusive */ /***/ /** Values for GetFragProps */ #define FPFALLSTHROUGH (1<<0) /*frag falls through to successor */ #define FPFALLENINTO (1<<1) /*frag fallen into from predecessor */ #define FPNOGIS (1<<2) /*frag should not be globally sched */ #define FPRETURNS (1<<3) /*frag does a function return */ #define FPRETBRANCH (1<<4) /*frag does a branch to a return */ /***/ /***/ /** Values for GetOperands */ /***/ /** Definition of operand types: 00 - single register */ /** 01 - exempt register */ /** 10 - register pair */ /** 11 - register quad */ /***/ #define OPTYPE0 (1<<0) /*operand type LSB */ #define OPTYPE1 (1<<1) /*operand type MSB */ #define OPFIXED (1<<2) /*operand has fixed allocation */ #define OPALIGN (1<<3) /*operand requires size alignment */ #define OPTARGET (1<<4) /*operand is part of destination */ #define OPSPECIAL (1<<5) /*operand has a beta alias or undefs*/ /*other half of a register pair */ #define OPREAD (1<<6) /*operand is read */ #define OPWRITE (1<<7) /*operand is written */ /***/ /** Constant to represent unchanged operands */ #define NONEWREG 255 /***/ /***/ /**************************************************************************/ /** **/ /** Global data **/ /** **/ /**************************************************************************/ /***/ #if(optimtrace!=0) #define bannerline ("------------------------------------------------------------------------------") #endif; /***/ static unsigned char codebuff [1059+1]; static int bbschedule; static int gschedule; static int leafopt; static int peepopt; static int ProcNum; static int firstentry; static int buffptr; static int baseca; static int latestlineno; static int setdbx; static int miscreport; static int hazardreport; static int peepreport; /* controls local diagnostic printing */ static int globreport; /* controls global diagnostic printing */ static int asmlist; /* controls production of address-less asm list */ extern int Pic; /*0 => defaults/ >0 => generate pic */ static int lastlocalcallra=0; /* Point from where switch access calculated */ static int wrappediregs=0; /* Defines regs saved in wrappers */ static int wrappertype; /* Defines wrappers as follows :- */ /* 0=normal, 1=uses esp as ebp, 2=no frame*/ static struct procfmt *PI; static struct procfmt *outerPI; /***/ #define alloworphans 1 /* change to 0 to prevent orphaned relocations */ /***/ #define STDOUT 1 #define HT 9 /* Horizontal Tab*/ #define SENSIBLEITER 1000 #define Normalfix 0 #define Gotrelfix 7 #define dcallfix 3 #define idcallfix 5 #define GOTfix 6 #define GOTofffix 4 /***/ #if((TargetABI==DGUXABI)||(TargetABI==LynxOS)) #define commentmark (" #") #else #define commentmark (" /") #endif; /***/ static const unsigned char InverseJump [JG-(JO)+1] = { JNO, JO,JNB,JB,JNE,JE,JA,0, JBE,JNS,JS,JNP,JP,JGE,JL,JG, JLE}; static const unsigned char setjump [JG-(JO)+1] = { SETO, SETNO,SETB,SETNB,SETE,SETNEQ,SETBE,0, SETA,SETS,SETNS,SETP,SETNP,SETL,SETGE,SETLEQ, SETG}; /***/ /***/ /********************************************************/ /** Arrays for returning results from p5 GetOperands **/ /********************************************************/ /***/ /***/ /***/ /********************************************************/ /** Globals for Stack Frame Store Instructions **/ /********************************************************/ /***/ extern unsigned char StackSTflags [256+1]; /*Records how the stack or parameter locations have been accessed:*/ /* Bit 0 - store flag */ /* Bit 1 - load flag */ /* Bit 2 - address taken flag */ /***/ /***/ /********************************************************/ /** Globals for Recording Parameter Store Instructions **/ /********************************************************/ /***/ int ParamSTMmarkers [(((unsigned)Regparamsize>>2)-1)+1]; /*records the location of the STore instruction*/ /* for each parameter register */ unsigned char paramSTflags [(((unsigned)Regparamsize>>2)-1)+1]; /*Records how the parameters have been accessed:*/ /* Bit 0 - store flag */ /* Bit 1 - load flag */ /* Bit 2 - address taken flag */ /***/ /***/ /***/ void reintegrateregmasks( struct fragfmt * ); static int DoLeafOpt( struct fragfmt * ); /***/ /***/ /**************************************************************************/ /** **/ /** Initialisation **/ /** **/ /**************************************************************************/ /***/ /***/ static void phex(int val){ printf("%8X",val); } static char * itos(int val){ static char sp[20]; sprintf(sp,"%d",val); return sp; } void p5init(int options) { /***************************************************************************/ /** called by MSetOptions **/ /** options have bit value significance **/ /** x'00000001' enable miscellaneous architecture specific tracing **/ /** x'00000002' generate assembly listing for each frag **/ /** x'00000004' enable peephole optimisation tracing **/ /** x'00000008' parameters are independent (FORTRAN only) **/ /** x'00000010' enable put memory allocation tracing **/ /** x'00000080' enable put file monitoring **/ /** x'00000100' enable global scheduling **/ /** x'00000200' enable leaf routine optimisation **/ /** x'00000400' enable peephole optimisation **/ /** x'00010000' profiling **/ /** x'00020000' enable DBX/SDB information generation **/ /** x'00040000' minimum diags preparation **/ /** x'00080000' dynamic line number updating **/ /** x'00100000' prepare static line no table (later) **/ /** x'00200000' support overflow checking **/ /** x'00400000' allow compiler allocation of registers (FORTRAN only) **/ /** x'00800000' set FPU traps if compiling a main program **/ /** x'01000000' enable basic block instruction scheduling **/ /** x'02000000' prepare for FORTRAN argument checking **/ /** x'04000000' generate assembly listing without addresses **/ /** x'08000000' RNDSNGL machine dependent change for DG **/ /** x'10000000' Target dependent unused on pentium **/ /** x'20000000' line profiling **/ /** x'40000000' leave external symbol names in mixed case **/ /** **/ /** Additionally, bits 12-15 give the architecture variant for which **/ /** backend optimisation should be performed **/ /***************************************************************************/ int archvariant; miscreport=options&0x1; setdbx=options&0x20000; globreport=options&0x4; bbschedule=options&0x1000000; asmlist=options&0x4000000; gschedule=options&0x100; leafopt=options&0x200; peepopt=options&0x400; archvariant=((unsigned)options>>12)&0xF; BSetArchVariant(archvariant); peepreport=globreport; hazardreport=globreport; ProcNum=0; firstentry=1; } /* sparc init */ /***/ void p5reinit() { /*************************************************************************/ /** For completeness but nouthing seems to be needed **/ /*************************************************************************/ ProcNum=0; firstentry=1; } /***/ void p5setpi(struct procfmt * tpi) { if (tpi->proclevel==2 && PI->proclevel==1) outerPI=PI; PI=tpi; } /*p5 setPI*/ /***/ /***/ void puthex(int n,int d) { static const unsigned char hex [15+1] = { '0','1', '2','3','4','5','6','7', '8','9','A','B','C','D','E','F' }; int i,j,k; j=0; if (d==0) { printf("0x"); d=1; } for (i=7; i>=0; i--) { k=((unsigned)n>>(i<<2))&15; if (d>i) j=1; if (k!=0) { printf("%c",hex [k]); j=1; } else if (j!=0) { printf("%c",hex [k]); } } } /*puthex*/ /***/ static void p5Error(char * ErrStr) { /* ****call on untranslateable imp support routine**** */; printf("\n***** P5 CODE OPTIMISER INTERNAL ERROR -\n"); printf(" %s\n",ErrStr); exit(2); } /*p5 Error*/ /***/ /***/ void CPrintLine(int line,int prefix) { /*************************************************************************/ /** Prints the given line number with the given line prefix **/ /*************************************************************************/ struct LinePrefixFmt *LinePrefix; int prefixtable [50+1]; int position,i; position=0; while (prefix!=0) { LinePrefix=(struct LinePrefixFmt*)(prefix); position++; prefixtable [position]=LinePrefix->line; prefix=LinePrefix->parent; } for (i=position; i>=1; i--) { printf("%1d:",prefixtable [i]); } printf("%1d",line); } /*CPrintLine*/ /***/ void Ccountparams(int *aparamprops,int sideentry) { /*************************************************************************/ /** Examine the properties of the arguments of a procedure **/ /** declaration and set register management data as appropriate - **/ /** returns the number of registers containing an argument in low reg. **/ /** Also handles the transfer of incoming parameter FregVars in %I **/ /** registers to their allocated %F registers. **/ /** **/ /** Structure of parameter info for C and C++ is as follows (One 32 **/ /** bit word for each parameter): **/ /** Bits 28-31: The alignment of the parameter (1,2,4 or 8 bytes) **/ /** Bits 24-27: The type of the parameter **/ /** Bit 23: Set if the parameter is a register parameter **/ /** Bit 22: Set if the parameter is not to be saved on entry **/ /** Bits 16-21: The logical number of any remapped register parameter, **/ /** else 0 **/ /** Bits 0-15: The size of the parameter in bytes **/ /** **/ /** Structure of parameter info for FORTRAN, PASCAL and MODULA is as **/ /** follows (One 32 bit word for each parameter): **/ /** Bits 30-31: The alignment of the parameter **/ /** Bits 24-29: The type of the parameter **/ /** Bits 16-23: Set to 1 if the parameter is unused or used only in the **/ /** prologue **/ /** Bits 0-15: The size of the parameter in bytes **/ /** **/ /** On RS6000 only record parameter information in a funny compact way **/ /** suitable for insertion into IBM's Tracebacktable produced at the **/ /** procedure end by "tidy frags". this info goes into the PI record **/ /** for storage until needed **/ /*************************************************************************/ unsigned char StoreFlags [REGCEILING+1]; int i,paramtype,PBase,paramprop; int hidden,elipsis,j,n; int TBTbits,nTbit,ipcnt,fpcnt,code /* decs for IBM traceback*/; #if (AllowReverseEndianData!=0 && Language==CCOMP && LanguageVariant==USLC) int paramlen,cumoffset; #endif for (i=0; i<=REGPARAMNUM-1; i++) { StoreFlags [i]=0; } if (sideentry==0) { for (i=0; i<=256; i++) StackSTflags [i]=0; for (i=0; i<=(((unsigned)Regparamsize>>2)-1); i++) { paramSTflags [i]=0; ParamSTMmarkers [i]=0; } } TBTbits=0; ipcnt=0; fpcnt=0; nTbit=31; elipsis=0; n=0; i=0; PI->inparsize=0; if (aparamprops!=NULL) { n=aparamprops[0]; /* no of param entries */ i=aparamprops[1]; /* no of byte of params passed */ if (i<0) elipsis=1; /* variable no of params */ if (i>=0) { PI->inparsize=i; } else { PI->inparsize=n<<2; } /* Set the register variable masks */ #if((Language==CCOMP)&&(LanguageVariant==USLC)) RegClaimMaskA=aparamprops[2]; RegClaimMaskB=aparamprops[3]; PBase=4; #else RegClaimMaskA=0; RegClaimMaskB=0; PBase=2; #endif; } else { RegClaimMaskA=0; RegClaimMaskB=0; /* lowreg=0 */ } /**/ /* Check for hidden parameters -- should be set in procprops but */ /*also check the table. Check can not be made if there is an elipsis. CCOMP */ /* must set the procprops bit */ /**/ hidden=((unsigned)PI->procprops>>9)&1; if (hidden!=0) PI->inparsize+=4; if (elipsis==0) { for (j=0; j<=n-1; j++) { paramprop=aparamprops[PBase+j]; if (paramprop==-1) continue ; paramtype=((unsigned)paramprop>>24)&15; if (paramtype==ResAddrval) hidden=1; if (paramtype==Realval) { nTbit-=2; if ((paramprop&0xFFFF)==8) code=3; else code=2; fpcnt++; TBTbits|=code<procprops|=(hidden<<9); #endif; /* PASCAL is careless here */ PI->ipcnt=ipcnt; PI->fpcnt=fpcnt; /**/ /* All params are store by ABI convention. There is mothing else to do */ /* except do byte swapping of wrong endian data that has arrived */ /**/ /* fix by rtr: parameters have at least word size */ /* bytes do not need to be swapped */ /* halfwords need to be shifted after swapping */ /**/ #if((AllowReverseEndianData!=0)&&(Language==CCOMP)&&(LanguageVariant==USLC)) cumoffset=(4*hidden)+8; for (j=0; j<=n-1; j++) { paramprop=aparamprops[PBase+j]; if (paramprop==-1) { cumoffset+=4; continue ; } paramtype=((unsigned)paramprop>>24)&15; paramlen=paramprop&0xFFFF; /**/ /* don't swap return addresses, */ /* structure value parameters, and byte size params */ if (paramtype!=ResAddrval && paramtype!=Structval && paramlen>1 && (paramprop&0x800000)!=0) /* swapping needed */{ if (paramtype==Realval && paramlen>=12) { ARCH(floadri(FLDq,EBP,cumoffset,currentFSP-1); ARCH(fstoreri(FSTPd,currentFSP-1,EBP,cumoffset)); } ARCH(loadri(LW,EBP,cumoffset,EAX)); ARCH(opr(BSWAP,EAX)); if (paramtype==Realval && paramlen>=8) { ARCH(loadri(LW,EBP,cumoffset+4,EDX)); ARCH(opr(BSWAP,EDX)); ARCH(storeri(ST,EAX,EBP,cumoffset+4)); ARCH(storeri(ST,EDX,EBP,cumoffset)); } else { if (paramlen==2) ARCH(rlit(SAR,EAX,16,EAX)); ARCH(storeri(ST,EAX,EBP,cumoffset)); } } cumoffset+=(paramlen+3)&(-4); /* bump to next mltpl of 4 */ } #endif; } /* Ccountparams */ /***/ static void ProcessSparse() { /************************************************************************/ /** This deals with sparse switch tables. It searches for fragments **/ /** containing a sparse switch table, and then replaces the instruction**/ /** with a set of IF THEN GOTOs. **/ /** Assumptions made : **/ /** PI_ProcFrag points at the first fragment of the proc being **/ /** processed **/ /** PI_curfrag points at the fragment into which new instructions **/ /** are placed. This is assumed to be empty (ie last user **/ /** has done an BBreakFrag) **/ /** There is only one SWITCHTAB in a fragment, and it is the last **/ /** instruction (safe to assume as BBreakFrag was introduced **/ /** into CSwitch) **/ /************************************************************************/ struct fragfmt *CurrentFragment; /* frag being processed */ struct fragfmt *NextFragment; /* the one after the Switch */ struct fragfmt *FirstNewFragment,*LastNewFragment /* where our new code goes */; struct fragfmt *EndFragment; /* the last USED fragment in proc */ struct fragfmt *EmptyFragment; /* The new last empty Fragmentof proc */ struct Swtabfmt *Tab; /* the switch table itself */ struct fragfmt * NextInList; /* so we can cycle through whole */ /* list even after altering links */ int I,CaseLabel,CaseLabelx; CurrentFragment=PI->procfrag; while (CurrentFragment!=NULL) { NextInList=CurrentFragment->nextfrag; if (CurrentFragment->switchtab!=0) { Tab=(struct Swtabfmt*)(CurrentFragment->switchtab); if (Tab->Mode==SPARSESWITCH) { /* start to plant the IF THEN GOTOs for the sparse switch */ CurrentFragment->switchtab=0; /**/ FirstNewFragment=PI->curfrag; for (I=0; I<=Tab->SlotsUsed-1; I++) { CaseLabel=Tab->u0.S [I].Label; CaseLabelx=BLocateLabel(CaseLabel,0); if (I==(Tab->SlotsUsed-1)) /* Last case */{ if (Tab->Rangelab>0) /* There is an error lab */{ ARCH(rlit(CMP,RETURNREG,Tab->u0.S [I].Entry,RETURNREG)); ARCH(bcctf(JE<<16,CaseLabelx,0)) /* otherwise we have */; } else { ARCH(bcctf(JMP<<16,CaseLabelx,0)) /* NO default unconditionally to last */; } /* a branch in a branch's */ } else { ARCH(rlit(CMP,RETURNREG,Tab->u0.S [I].Entry,RETURNREG)); ARCH(bcctf(JE<<16,CaseLabelx,0)) /* delay slot at the end */; } /* cycling through each case */ } /* Now we have the GOTO default, reached only if no IF succeeds */ if (Tab->Rangelab>0) ARCH(bcc(JMP<<16,Tab->Rangelab,0)); /* At this stage, the new instructions are at the end of the list */ /* of fragments, so they have to be reordered */ LastNewFragment=PI->curfrag; /* where our new code ends */ /* Now we have to make a new fragment for future code */ if (Tab->Rangelab>0) BTerminateFrag(Tab->Rangelab,0); else BBreakFrag(0); /* first join the switch fragment with our new code */ EndFragment=FirstNewFragment->prevfrag; NextFragment=CurrentFragment->nextfrag; CurrentFragment->nextfrag=FirstNewFragment; FirstNewFragment->prevfrag=CurrentFragment; /* now join the new code with that which followed the switch */ LastNewFragment->nextfrag=NextFragment; NextFragment->prevfrag=LastNewFragment; /* and finally point the end of the code at the new empty fragment */ EmptyFragment=PI->curfrag; /* the new, empty fragment */ EmptyFragment->prevfrag=EndFragment; EndFragment->nextfrag=PI->curfrag; } } CurrentFragment=NextInList; } /* While CurrentFragment # NULL */ } /*ProcessSparse*/ static void deleteunusedstackstores() { /****************************************************************/ /** remove the STore instruction of any stack location which **/ /** is not otherwise referenced **/ /** Bits signify as follows in StackSTflags **/ /** 2**3 allocated as a temp **/ /** 2**2 has its address taken **/ /** 2**1 is loaded **/ /** 2**0 is stored into **/ /****************************************************************/ struct fragfmt *frag; struct instrfmt *instr; int i,adtaken; /* If the address is taken of any stack location then we cannot delete */ adtaken=0; if ((PI->privprops&FRAMEADDRTAKEN)!=0) adtaken=1; else { for (i=0; i<=256; i++) { if ((StackSTflags [i]&0xC)==4) adtaken=1; } } StackSTflags [0]=StackSTflags [0]|(adtaken<<2); /* for recheck stack stores*/ frag=PI->procfrag; while (frag!=NULL) /* through all frags in this procedure */{ instr=frag->firstinstr; while (instr!=NULL) /*through all instructions in this frag*/{ if (INTSTORE<=instr->group && instr->group<=FPSTORE) { if ((instr->privprops&FRAMEACCESS)!=0) { if (-1024<=instr->u0.offset && instr->u0.offset<0) { i=(unsigned)(-instr->u0.offset+3)>>2; if ((StackSTflags [i]==1 && adtaken==0) || StackSTflags [i]==9/*temp $store*/) { if ((instr->datasize<=4 || StackSTflags [i-1]==StackSTflags [i]) && instr->opcode!=FISTP && instr->opcode!=FSTPm && instr->opcode!=FSTPd) BDeleteInstr(instr,0); /*store is dead since it is never loaded or addressed*/ } } } } instr=instr->nextinstr; } frag=frag->nextfrag; } } /* delete unused stack stores */ static int tailparams(struct instrfmt *callinstr) { /*************************************************************************/ /** callinstr is a recursive call. try and transfer parameters to input **/ /** site to avoid a recursive call. We take a very cautious line and **/ /** give up on floats, parameters involing a call or on anything like **/ /** a local address being passed. **/ /** Particularly difficult is the case when an undefined reg is pushed **/ /** This may be a regvar containing a pointer to a local so must fail **/ /** returns 1 if done. 0 if unsafe to change the call **/ /*************************************************************************/ int creepoffset,state,ptr,pushedregs; static const int dangerregs=(1<inparsize==0) return 1; /* No params at all */ if (PI->inparsize>16) { state=1; goto fail; } curinstr=BMachineInstr(callinstr->previnstr,PREVCHAIN); while (curinstr!=NULL) { if ((curinstr->props&ANYCALL)!=0) { state=2; goto fail; } if (curinstr->opcode==LEA && (curinstr->privprops&FRAMEACCESS)!=0) { state=3; goto fail; } if ((curinstr->rdef&(1<opcode!=PUSH) { state=4; goto fail; } if (curinstr->group==FPSTORE) { state=5; goto fail; } if ((curinstr->props&(LOADINSTR|STOREINSTR))!=0 && curinstr->area==PARAMS && (curinstr->u0.offset>=creepoffset || curinstr->u0.offset==-1)) { state=6; goto fail; } if (curinstr->opcode==PUSH) { if (curinstr->variant!=OPPLUSR && curinstr->variant!=OPMEMLIT) { state=7; goto fail; } if (curinstr->variant==OPPLUSR) pushedregs|=curinstr->rref; if ((curinstr->privprops&FIXEDLIT)!=0) { state=8; goto fail; } iads [ptr]=curinstr; ptr++; creepoffset+=4; } pushedregs&=(~curinstr->rdef); if (creepoffset==PI->inparsize && ((pushedregs&dangerregs)==0 || (PI->privprops&FRAMEADDRTAKEN)==0)) goto cando; curinstr=BMachineInstr(curinstr->previnstr,PREVCHAIN); } state=10; goto fail; cando: /* Now change the instrs */ ptr--; while (ptr>=0) { curinstr=(struct instrfmt*)(iads [ptr]); if (curinstr->variant==OPPLUSR) /* PUSH reg => ST reg,x(%EBP)*/{ curinstr->opcode=ST; curinstr->rs1=EBP; curinstr->disp=8+(ptr<<2); curinstr->group=INTSTORE; curinstr->props=STOREINSTR|DESTROYABLE; curinstr->area=PARAMS; curinstr->u0.offset=ptr<<2; curinstr->datasize=4; curinstr->rref=(256<rd); curinstr->privprops=FRAMEACCESS; curinstr->csize=3; curinstr->variant=OPRMR; curinstr->rmbyte=0x40; /* 8 bit disp */ curinstr->rdef=0; } else if (curinstr->variant==OPMEMLIT) { /* PUSH lit => mov LIT */ curinstr->opcode=MOV; curinstr->rs1=EBP; curinstr->disp=8+(ptr<<2); curinstr->group=INTSTORE; curinstr->props=STOREINSTR|DESTROYABLE; curinstr->area=PARAMS; curinstr->u0.offset=ptr<<2; curinstr->datasize=4; curinstr->rref=256<privprops=FRAMEACCESS; curinstr->variant=OPMEMLIT; curinstr->datasize=4; curinstr->csize=7; curinstr->rmbyte=0x40; /* 8 bit disp */ curinstr->rdef=0; } else Mabort(628); ptr--; } return 1; fail: if (optimtrace!=0&&peepreport!=0) { printf("Tail PARAM move fails state=%2d instrad = %.8X\n",state,(unsigned)curinstr); } return 0; } /* tail params */ /***/ static void tailrecursion() { /*************************************************************************/ /** Looks for a call followed by a return or jump to return and **/ /** if the call is to the current procedure substitues a jump **/ /** to the special tail recursion label (PI_returnlab+2) **/ /** Abandon optimisation unless all params in registers **/ /*************************************************************************/ struct fragfmt *frag,*prevfrag; struct instrfmt *instr,*jinstr,*stinstr; int id,mode; id=-1; frag=PI->procfrag; if (frag->entryid!=0) id=frag->entryid; while (frag->nextfrag!=NULL) { frag=frag->nextfrag; if ((frag->props&RETURNBRANCH)!=0 && frag->labrefinstr!=NULL) { jinstr=frag->labrefinstr; instr=BMachineInstr(jinstr->previnstr,PREVCHAIN); mode=0; if (instr==NULL && frag->prevfrag!=0) { prevfrag=frag->prevfrag; instr=BMachineInstr(prevfrag->lastinstr,PREVCHAIN); mode=1; } if (instr!=NULL && instr->opcode==ADD && instr->rd==ESP && instr->variant==OPRSLIT) { stinstr=instr; instr=BMachineInstr(stinstr->previnstr,PREVCHAIN); } else stinstr=NULL; if (instr!=NULL && (instr->privprops&DIRECTCALL)!=0 && instr->u1.immval==id && (jinstr->props&BRANCH)!=0 && (jinstr->props&CONDITIONALBRANCH)==0) { if (tailparams(instr)!=0) { if (optimtrace!=0&&peepreport!=0) { printf("PHOLE: Tail recursion removed\n"); p5printinstr(instr); } if (stinstr!=NULL) BDeleteInstr(stinstr,0); if (mode==0) /* can overwrite jump */{ BDeleteInstr(instr,0); jinstr->u1.immval=BLocateLabel((PI->returnlab+2)+PI->labadjust,0); frag->props=frag->props^RETURNBRANCH; frag->labref=jinstr->u1.immval; } else { instr->u1.immval=BLocateLabel((PI->returnlab+2)+PI->labadjust,0); prevfrag->labref=instr->u1.immval; prevfrag->labrefinstr=instr; instr->opcode=JMP; instr->group=instrgroup [JMP]; instr->props=BRANCH; instr->privprops=0; instr->variant=OPPCREL; instr->csize=5; instr->rref=0; instr->cref=0; instr->fref=0; instr->rdef=0; instr->cdef=0; instr->fdef=0; } } } } } } /*Tail recursion*/ /***/ static int congruentfrags(struct fragfmt *frag1,struct fragfmt *frag2){ /* ********************************************************************* * Looks for two frags that are the same and end in an unconditional * * jump to the same place. Only looking for 'return TRUE' type stmst * * but could be made a lot more elaborate * *********************************************************************/ struct instrfmt * instr11,*instr12,*instr13,*instr21,*instr22,*instr23; if (frag1->labref==0 || frag2->labref==0 || frag1->labref!=frag2->labref) return 0; instr12=frag1->labrefinstr; instr11=BMachineInstr(instr12->previnstr,PREVCHAIN); instr13=BMachineInstr(instr12->nextinstr,NEXTCHAIN); instr22=frag2->labrefinstr; instr21=BMachineInstr(instr22->previnstr,PREVCHAIN); instr23=BMachineInstr(instr22->nextinstr,NEXTCHAIN); if (instr13!=NULL || instr23!=NULL) return 0; if (instr11==NULL || instr21==NULL) return 0; if ((instr12->props&(BRANCH|CONDITIONALBRANCH))!=BRANCH) return 0; if ((instr22->props&(BRANCH|CONDITIONALBRANCH))!=BRANCH) return 0; if (instr12->u1.immval!=instr22->u1.immval) return 0; if (instr11->opcode!=instr21->opcode || instr11->variant!=instr21->variant) return 0; if ((instr11->privprops&FIXEDLIT)!=(instr21->privprops&FIXEDLIT)) return 0; if ((instr11->privprops&FIXEDLIT) && (instr11->area!=instr21->area || instr11->u0.offset!=instr21->u0.offset)) return 0; if ( instr11->variant==OPPLUSRLIT && instr11->opcode==MOV && instr11->rd==instr21->rd && instr11->u1.immval==instr21->u1.immval) return 1; return 0; } /* congruentfrags* */ static struct fragfmt * tailmerge(struct fragfmt *frag1,struct fragfmt *frag2){ /* ****************************************************************** * frag2 & frag1 are congruent. change frag2 to jump to frag1 * ******************************************************************/ struct instrfmt *instr,*instr1,*instr2; struct fragfmt *newfrag; int lab,labx; struct lrecfmt *lrec; /* had frag1 got more instructions than the common tail. If so we need to insert another frag and move instructions */ instr1=frag1->labrefinstr; instr2=BMachineInstr(instr1->previnstr,PREVCHAIN); if (BMachineInstr(instr2->previnstr,PREVCHAIN)!=NULL) { newfrag=BInsertFrag(frag1->nextfrag); BMoveBefore(frag1,newfrag,instr2,NULL); newfrag->labrefinstr=instr1; frag1->labrefinstr=NULL; BMoveBefore(frag1,newfrag,instr1,NULL); newfrag->props=RETURNBRANCH; frag1->props&=(~RETURNBRANCH); frag1->props|=COMMONTAIL; newfrag->labref=frag1->labref; frag1->labref=NULL; } else { newfrag=frag1; } if (newfrag->labindex==0) { lab=Mprivatelabel(); labx=BLocateLabel(lab+PI->labadjust,0); lrec=BLabRecAd(labx); lrec->frag=newfrag; newfrag->labindex=labx; } labx=newfrag->labindex; frag2->labref=labx; instr=frag2->labrefinstr; instr->u1.immval=labx; frag2->props&=(~RETURNBRANCH); frag2->props|=COMMONTAIL; instr=BMachineInstr(instr->previnstr,PREVCHAIN); BDeleteInstr(instr,0); return newfrag; } /*tailmerge */ static void commontails() { /* ****************************************************************** * Checks all return jump frag for simple commonality and coalesce* * into a single frag * * must be done before frag reorganisation * ******************************************************************/ struct fragfmt *frag1,*frag2; frag1=PI->curfrag; while (frag1!=NULL) { if ((frag1->props&RETURNBRANCH)!=0) { frag2=frag1->prevfrag; while (frag2!=NULL) { if ((frag2->props&RETURNBRANCH)!=0) { if (congruentfrags(frag1,frag2)) frag1=tailmerge(frag1,frag2); } frag2=frag2->prevfrag; } } frag1=frag1->prevfrag; } } /* commontails */ void Cprocessproc() { /********************************************************************/ /** process the current procedure and perform expansion of sparse **/ /** switches for C and C++ **/ /********************************************************************/ int traceproc; traceproc=BGetTraceProc(); if (traceproc==0 || traceproc==ProcNum) { peepreport=globreport; } else { peepreport=0; } if (optimtrace!=0 && peepreport!=0) { BPrintFrags("PHOLE: FRAGMENTS BEFORE PROCESSING"); } ProcessSparse(); if (peepopt!=0) tailrecursion(); if (((Language==FORTRAN && LanguageVariant!=FORTRAN90) || Language==CCOMP || (PI->procprops&PP_NOINTERNAL)!=0) && leafopt!=0) { /* for block strucutured procs we can not tell what is going on so do */ /* not attemp any deletions unless there are no inner blocks (props&32#0) */ deleteunusedstackstores(); } if (peepopt!=0) commontails(); if (optimtrace!=0 && peepreport!=0) { BPrintFrags("PHOLE: FRAGMENTS AFTER PROCESSING"); } peepreport=globreport; } /* Cprocessproc */ /***/ static struct instrfmt * followinginstr(struct fragfmt *frag,struct instrfmt * instrad) { /*************************************************************************/ /** Gives the address of the instruction that executes after the one **/ /** at instrad. Chases into subsequent frags but not via jumps **/ /*************************************************************************/ struct instrfmt * i; struct fragfmt *nextfrag; i=BMachineInstr(instrad,NEXTCHAIN); if (i!=NULL || (frag->props&FALLSTHROUGH)==0 || frag->nextfrag==NULL) return i; /**/ nextfrag=frag; do /* chase through following frags */{ nextfrag=nextfrag->nextfrag; nextfrag=nextfrag->final; i=BMachineInstr(nextfrag->firstinstr,NEXTCHAIN); if (i!=NULL || (nextfrag->props&FALLSTHROUGH)==0 || nextfrag->nextfrag==NULL) return i; } while (1) /* FOR EVER */; } /*following instr */ /***/ static struct instrfmt * followinginstr2(int *fragaddr,struct instrfmt*instrad,int mode) { /*************************************************************************/ /** Gives the address of the instruction that executes after the one **/ /** at instrad. Chases into subsequent frags but not via jumps **/ /** B reorganise fragmants may not have been called so that _props **/ /** will not be valid. Can test _final field for this condition **/ /** mode=0 continue to end or unconditional jump **/ /** mode=1 dont continue past a label **/ /*************************************************************************/ struct instrfmt * i; struct fragfmt *nextfrag,*frag; frag=(struct fragfmt*)(*fragaddr); i=BMachineInstr(instrad,NEXTCHAIN); if (i!=NULL || frag->nextfrag==0 || ((frag->props&FALLSTHROUGH)==0 && frag->final!=0)) return i; /**/ nextfrag=frag; do /* chase through following frags */{ *fragaddr=(int)nextfrag->nextfrag; nextfrag=(struct fragfmt*)(*fragaddr); if (nextfrag->labindex!=0 && mode!=0) return 0; i=BMachineInstr(nextfrag->firstinstr,NEXTCHAIN); if (i!=NULL || nextfrag->nextfrag==0 || ((nextfrag->props&FALLSTHROUGH)==0 && nextfrag->final!=0 && nextfrag->uses>0)) return i; } while (1) /* FOR EVER */; } /*following instr2 */ /***/ static struct fragfmt * followingfrag(struct fragfmt *frag,struct instrfmt *instrad) { /*************************************************************************/ /** Gives the address of the fragment holding the instruction that **/ /** executes after the one at instrad. Chases into subsequent frags but **/ /** not via jumps **/ /*************************************************************************/ struct instrfmt * i; struct fragfmt *nextfrag; i=BMachineInstr(instrad,NEXTCHAIN); if (i!=NULL || (frag->props&FALLSTHROUGH)==0 || frag->nextfrag==0) return frag; /**/ nextfrag=frag; do /* chase through following frags */{ nextfrag=nextfrag->nextfrag; nextfrag=nextfrag->final; i=BMachineInstr(nextfrag->firstinstr,NEXTCHAIN); if (i!=NULL || (nextfrag->props&FALLSTHROUGH)==0 || nextfrag->nextfrag==0) return nextfrag; } while (1) /* FOR EVER */; } /*following instr */ /***/ void Cestablishca(struct fragfmt *frag,int *ca) { /****************************************************************/ /** process the current fragment and determine total code size **/ /****************************************************************/ int op; struct instrfmt *instr,*nextinstr; struct instrvariants *ivar; struct Exdatafmt *exdata; #if (MINCODESIZE==0) if ((bbschedule|gschedule)!=0 && ((frag==frag->final && (frag->props&(FALLENINTO|DSWITCHCASEFRAG))==0) || frag->prevfrag==0)) { *ca=(*ca+15)&(-16); } #endif frag->address=*ca; nextinstr=frag->firstinstr; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; op=instr->opcode; if (Pic==0 && (op==LW || op==ST || op==SB || op==LBNOX) && instr->rd==EAX && (instr->privprops&(FIXEDINSTR|SIBREQD|EXTENINSTRFORM))==FIXEDINSTR) { instr->csize--; instr->privprops|=EXTENINSTRFORM; if (optimtrace!=0 && peepreport!=0) { printf("ESTBLSH: Instruction shortened %08X\n",(unsigned)instr); } } if (instr->variant==OPRLIT && instr->rd==EAX) { ivar=(struct instrvariants*)(&instrops [instr->opcode*instrvarels]); if ((ivar->valid&opeaxlitval)!=0) { instr->variant=OPEAXLIT; instr->csize--; if (optimtrace!=0 && peepreport!=0) { printf("ESTBLSH: EAXLIT form used %.8X\n",(unsigned)instr); } } } if (instr->variant==OPBRMLIT && instr->rd==EAX) { ivar=(struct instrvariants*)(&instrops [instr->opcode*instrvarels]); if ((ivar->valid&opallitval)!=0) { instr->variant=OPALLIT; instr->csize--; if (optimtrace!=0 && peepreport!=0) { printf("ESTBLSH: ALLIT form used %.8X\n",(unsigned)instr); } } } /***/ switch (instr->group) { default: #if(Language!=PASCAL && Language!=MODULA) Mabort(40); /* Invalid instruction class */ #endif; /***/ case BCC: case JUMPLINK: *ca+=instr->csize; if (instrform [instr->opcode]==JFORM && (instr->privprops&DIRECTCALL)!=0) { exdata=exrecad(instr->u1.immval); if (exdata->Ca<0) /* Entry as yet unknown*/{ #if (OutputASSEMBLER!=Positive) if (exdata->PutId<=0) { if (strcmp(exdata->Name,"")==0 || (exdata->flags&4)!=0) { exdata->PutId=pnextsymbol(); } else { exdata->PutId=pxname(exdata->flags,exdata->Name); /* Reserve an id if referencing a private procedure which */ /* will be declared later else generate an external reference */ /* for all others */ } } #endif } } continue ; case Unimp: /* Store access groups */ case INTLOAD: case FPLOAD: case INTSTORE: case FPSTORE: case RWMEM: case INTOPS: case INTOPSu: if ((instr->privprops&PROCADFIX)!=0) { exdata=exrecad(instr->u0.offset); /*id in offset fixcode*/ if (exdata->Ca<0 && exdata->PutId<=0) { #if(OutputASSEMBLER!=Positive) if (strcmp(exdata->Name,"")==0 || (exdata->flags&4)!=0) { exdata->PutId=pnextsymbol(); } else { exdata->PutId=pxname(exdata->flags,exdata->Name); /* Reserve an id if referencing a private procedure which */ /* will be declared later else generate an external reference*/ /* for all others */ } #endif; } } case SHIFTS: case SPECREGrw: case FPOPS1: case FPOPS2: case FPCOMPARE: case HICODEFIX: case COPYASM: case STROP: *ca+=instr->csize; continue ; /***/ case LINENUMBER: case MARKER: case LINEDEBUG: case DBXINFO: case FIXUPPOINT: case DSWITCHNOTE: case DELETED: case CXTCHANGE: case REGIONINFO: continue ; /***/ } /* end of case based on instr->group */ } /* end of while loop */ if (frag->switchtab!=0) *ca=((*ca+3)&(-4)); /* word align */ } /* Cestablishca */ /***/ /**/ /* Copyright (c) 1993 Edinburgh Portable Compilers Ltd. All Rights Reserved.*/ /**/ /***/ void p5abiinit(int *fixedca) { } /*p5 abiinit*/ static void addprefixes(unsigned char *actcode,struct instrfmt *instr) { /*************************************************************************/ /** Prefixes (up to 4) go at the start of the instruction. **/ /*************************************************************************/ int i,l; l=0; for (i=0; i<=3; i++) { if (instr->u2.prefixes [i]!=0) { l++; actcode [l]=instr->u2.prefixes [i]; } } actcode [0]=l; } /*addprefixes*/ /***/ static void addopcode(unsigned char *actcode,int opcode) { /*************************************************************************/ /** The opcode is 1 or 2 bytes and goes after the prefixes **/ /*************************************************************************/ int l; l=actcode [0]+1; if ((opcode&0xFF00)!=0) { actcode [l]=(unsigned)opcode>>8; l++; } actcode [l]=opcode; actcode [0]=l; } /*addopcode*/ /***/ static void adddisp(unsigned char *actcode,int disp,int size) { /*************************************************************************/ /** Displacements are normally 1 or 4 bytes as determined by modrm **/ /** Some intsructions allow only one size of disp(eg call shl) **/ /** 2 byte displacements are possible in prefixed instructions **/ /** if size =0 this routine chooses between 1 & 4 **/ /*************************************************************************/ int l,i; if (size>4) p5Error("Strange Displacement/Literal"); if (size==0) { if (disp<-128 || disp>127) { size=4; } else { size=1; } } l=actcode [0]; for (i=0; i<=size-1; i++) { l++; actcode [l]=(unsigned)disp>>(8*i); } actcode [0]=l; } /*adddsip*/ /***/ static void addrmsubopreg(unsigned char *actcode,struct instrvariants *invar,int reg) { /*************************************************************************/ /** Set up a rm byte with a subopcode and register **/ /*************************************************************************/ int l; l=actcode [0]+1; actcode [0]=l; actcode [l]=(0xC0|(invar->subop<<3))|reg; } /*addrmsubopreg */ /***/ static void addsibmem(unsigned char *actcode,struct instrfmt *instr,int mode,int midfield,int ca) { /*************************************************************************/ /** Add an indexed memory refrence which may be fixed up **/ /*************************************************************************/ int l,area; l=actcode [0]+2; actcode [0]=l; if ((instr->privprops&FIXEDINSTR)!=0) { if (instr->fixtype==GOTfix) { actcode [l-1]=0x84|(midfield<<3); /* rm byte */ actcode [l]=((instr->sib&0xC0)|(instr->rs2<<3))|instr->rs1; } else { actcode [l-1]=4|(midfield<<3); /* rm byte */ actcode [l]=((instr->sib&0xC0)|(instr->rs2<<3))|5; } adddisp(actcode,0,4); #if (OutputASSEMBLER!=Positive) if (mode!=0) { area=instr->area; if (area>CNST) area=egiveareaid(area); pfix2(CODE,ca+l,area,instr->u0.offset,instr->fixtype); } #endif } else if (instr->disp==0 && instr->rs1!=EBP) { actcode [l-1]=(midfield<<3)|4; actcode [l]=((instr->sib&0xC0)|(instr->rs2<<3))|instr->rs1; } else if (instr->disp>=-128 && instr->disp<=127) { actcode [l-1]=(0x40|(midfield<<3))|4; actcode [l]=((instr->sib&0xC0)|(instr->rs2<<3))|instr->rs1; adddisp(actcode,instr->disp,1); } else { actcode [l-1]=(0x80|(midfield<<3))|4; actcode [l]=((instr->sib&0xC0)|(instr->rs2<<3))|instr->rs1; adddisp(actcode,instr->disp,4); } } /***/ static void addmem(unsigned char *actcode,struct instrfmt *instr,int mode,int midfield,int ca) { /*************************************************************************/ /** Add a simple memory refrence which may be fixed up **/ /*************************************************************************/ int l,rm,dsize,area; if ((instr->privprops&SIBREQD)!=0) { addsibmem(actcode,instr,mode,midfield,ca); return; } l=actcode [0]+1; actcode [0]=l; if ((instr->privprops&FIXEDINSTR)!=0) { actcode [l]=5|(midfield<<3); if ((instr->fixtype==GOTfix)||(instr->fixtype==GOTofffix)) actcode [l]=(EBX|(midfield<<3))|0x80; adddisp(actcode,0,4); area=instr->area; if (area>CNST) area=egiveareaid(area); #if (OutputASSEMBLER!=Positive) if (mode!=0) pfix2(CODE,ca+l,area,instr->u0.offset,instr->fixtype); #endif return; } if (instr->disp==0 && instr->rs1!=EBP) { rm=0; dsize=0; } else if (instr->disp>=-128 && instr->disp<=127) { rm=0x40; dsize=1; } else { rm=0x80; dsize=4; } actcode [l]=(rm|(midfield<<3))|instr->rs1; if (instr->rs1==ESP) /* Refs via sf are no slower */{ actcode [l+1]=(4<<3)|ESP; /* but need a sib byte */ actcode [0]=l+1; } if (dsize>0) adddisp(actcode,instr->disp,dsize); } /*addmem */ /***/ static int Cgenerateinstr(int mode,struct instrfmt *instr,int ca,unsigned char *actcode) { /****************************************************************/ /** process the current instruction and generate code **/ /** mode = 0 form instruction only **/ /** 1 form instruction and generate red tape **/ /** result = 0 no instruction generated **/ /** 1 instruction generated (value in actcode) **/ /** 2 instruction generated (value in actcode) **/ /** but there is more to come **/ /** ca is the current address, updated if instr generated **/ /****************************************************************/ struct lrecfmt *lrec; struct Exdatafmt *exdata; struct fragfmt *addrfrag; int form,tail,area,group,privprops,j,opcode,l,litsize; struct instrvariants *invar; opcode=instr->opcode; form=instrform [opcode]; invar=(struct instrvariants*)((int)&instrops [opcode*instrvarels]); actcode [0]=0; group=instr->group; privprops=instr->privprops; if (group>=MINPSEUDOGROUP) switch (group) { case LINENUMBER: latestlineno=instr->u1.immval; #if (OutputASSEMBLER!=Positive) if (mode!=0 && setdbx!=0) plinestart(latestlineno,ca); #endif return 0; /***/ case MARKER: case LINEDEBUG: case DSWITCHNOTE: case REGIONINFO: case DELETED: case COPYASM: case CXTCHANGE: return 0; case DBXINFO: #if (OutputASSEMBLER!=Positive) if (mode!=0) { if (instr->opcode==0) (*(int *)(instr->u1.immval))=ca; /*type 0 = give ca*/ if (instr->opcode==1) ppushfile(); if (instr->opcode==2) (*(int *)(instr->u1.immval))=ppopfile(); } #endif return 0; /* fix area&offset to point here plus or minus immedval */ case FIXUPPOINT: #if (OutputASSEMBLER!=Positive) if (mode!=0) { pfix2(instr->area,instr->u0.offset,CODE,ca+instr->u1.immval,2); } #endif return 0; default: BADSWITCH(group,__LINE__,__FILE__); } /*end case for control operation not instruction*/ addprefixes(actcode,instr); /***/ if (form==NOFORM) { addopcode(actcode,invar->u0.onlyop); if (instr->opcode==ENTER) { adddisp(actcode,(unsigned)instr->u1.immval>>8,2); adddisp(actcode,instr->u1.immval&255,1); } if (instr->opcode==RETN) { adddisp(actcode,instr->u1.immval,2); } return 1; } if (form==JFORM) { /* treat jumps by form not variant */ if ((instr->props&ANYCALL)==0) /* pc rel jumps */{ if ((privprops&KNOWNOFFSET)!=0) { tail=instr->u1.immval; lastlocalcallra=ca+instr->csize; /* Remeber for adjusting switches */ } else { tail=0; lrec=BLabRecAd(instr->u1.immval); if ((int)lrec->frag!=0) { addrfrag=lrec->frag; if (addrfrag->final!=NULL) addrfrag=addrfrag->final; tail=addrfrag->address-(ca+instr->csize); /*Base is next instr addr*/ } } if (instr->csize<=2) { addopcode(actcode,invar->other /* short form of jump here*/); if (ca>0 && (tail<-128 || tail>127)) Mabort(960); adddisp(actcode,tail,1); return 1; } addopcode(actcode,invar->oppcrel /* Long form of branch */); adddisp(actcode,tail,4); return 1; } /* If here it is a call */ exdata=exrecad(instr->u1.immval); addopcode(actcode,invar->oppcrel /* Long form of call)*/); /**/ /* We can fill known calls here BUT in PIC mode Globals can exist in .so */ /* and in the a.out. The choice of which is made by the linker under */ /* control -Bsymbolic option. Can onl;y fill local calls in PIC mode */ /**/ if (exdata->Ca>=0 && ((exdata->flags&4)!=0 || Pic==0)) /*known local routine*/{ tail=exdata->Ca-(ca+5); /*Base is next instr addr*/ adddisp(actcode,tail,4); return 1; } adddisp(actcode,0,4); #if (OutputASSEMBLER!=Positive) if (mode!=0) { /**/ /* id assigned by Cestablish ca */ /**/ pfix2(CODE,ca+1,exdata->PutId,-4,dcallfix); /* code relative branch fixup */ } #endif return 1; } if (form==SLITFORM) { /* treat shift-by-lit as a special */ addopcode(actcode,invar->oprmslit); if (instr->variant==OPRSLIT) /* There is a special for immval=1 currently ignored*/{ addrmsubopreg(actcode,invar,instr->rd); } else { addmem(actcode,instr,mode,invar->subop,ca); } adddisp(actcode,instr->u1.immval,1); return 1; } switch (instr->variant) { case OPONLY: case FOPONLY: addopcode(actcode,invar->u0.onlyop); if (instr->opcode==ENTER) { adddisp(actcode,(unsigned)instr->u1.immval>>8,2); adddisp(actcode,instr->u1.immval&255,1); } if (instr->opcode==RETN) { adddisp(actcode,instr->u1.immval,2); } return 1; case FOPPLUSR: if ((instr->rs1rd)||(invar->opplusreg==0)) { printf("%6d %6d\n",instr->rd,instr->rs1); p5Error("Funny float instr"); } addopcode(actcode,(invar->opplusreg+instr->rs1)-instr->rd); return 1; case OPPLUSR: addopcode(actcode,invar->opplusreg+instr->rd); return 1; case OPR: addopcode(actcode,invar->oprm); addrmsubopreg(actcode,invar,instr->rd); return 1; case OPRR: addopcode(actcode,invar->u0.oprrm); l=actcode [0]+1; actcode [0]=l; actcode [l]=(0xC0|(instr->rd<<3))|instr->rs1; return 1; case OPRRM: if ((instr->privprops&EXTENINSTRFORM)!=0) { j=actcode [0]+1; addmem(actcode,instr,mode,0,ca); actcode [j]=invar->other; /* overwite rm byte with special opcode */ return 1; } addopcode(actcode,invar->u0.oprrm); if ((instr->privprops&DSWITCHLOADINSTR)!=0) { tail=instr->disp; /* May have to adjust disp due to aligning table */ j=lastlocalcallra+instr->disp; /* Is ca of table before alignmeny */ j=(j+3)&(-4); /* Now aligned */ instr->disp=j-lastlocalcallra; /* disp now adjusted */ addmem(actcode,instr,mode,instr->rd,ca) /* code planted */; instr->disp=tail; /* Back to original vale in case just monitoring*/ /* However we may have altered instr size */ /* All offsets should be within byte size */ /* even allowing for max alignment (see Cswitch */ /* but disp can become 0 */ if ((mode==1)&&(instr->csize!=actcode [0])) { if (instr->csize>actcode [0]) { actcode [0]=actcode [0]+1; actcode [actcode [0]]=0x90; /* harmless NOP */ } else Mabort(959); } } else addmem(actcode,instr,mode,instr->rd,ca); return 1; case OPRMR: if ((instr->privprops&EXTENINSTRFORM)!=0) { j=actcode [0]+1; addmem(actcode,instr,mode,0,ca); actcode [j]=invar->other; /* overwite rm byte with special opcode */ return 1; } addopcode(actcode,invar->oprmr); addmem(actcode,instr,mode,instr->rd,ca); return 1; case OPRSLIT: addopcode(actcode,invar->oprmslit); if (opcode==IMUL) { l=actcode [0]+1; actcode [0]=l; actcode [l]=(0xC0|(instr->rd<<3))|instr->rs1; } else addrmsubopreg(actcode,invar,instr->rd); adddisp(actcode,instr->u1.immval,1); return 1; case OPBRMLIT: addopcode(actcode,invar->opbrmlit); addrmsubopreg(actcode,invar,instr->rd); adddisp(actcode,instr->u1.immval,1); return 1; case OPRLIT: addopcode(actcode,invar->oprmlit); if (opcode==IMUL) { l=actcode [0]+1; actcode [0]=l; actcode [l]=(0xC0|(instr->rd<<3))|instr->rs1; } else addrmsubopreg(actcode,invar,instr->rd); #if (OutputASSEMBLER!=Positive) if ( mode!=0 && (instr->privprops&FIXEDLIT)!=0) { area=instr->area; if (area>CNST) area=egiveareaid(area); pfix2(CODE,ca+actcode [0],area,instr->u0.offset,instr->fixtype); } #endif adddisp(actcode,instr->u1.immval,instr->datasize); return 1; case OPMEM: case FOPRM: addopcode(actcode,invar->oprm); addmem(actcode,instr,mode,invar->subop,ca); return 1; case OPALLIT: addopcode(actcode,invar->allit); adddisp(actcode,instr->u1.immval,1); return 1; case OPEAXLIT: if (instr->rd!=EAX) Mabort(958); addopcode(actcode,invar->eaxlit); #if (OutputASSEMBLER!=Positive) if ( mode!=0 && (instr->privprops&FIXEDLIT)!=0) { area=instr->area; if (area>CNST) area=egiveareaid(area); pfix2(CODE,ca+actcode [0],area,instr->u0.offset,instr->fixtype); } #endif adddisp(actcode,instr->u1.immval,4); return 1; case OPBMEMLIT: addopcode(actcode,invar->other); addmem(actcode,instr,mode,invar->subop,ca); adddisp(actcode,instr->u1.immval,1); return 1; case OPMEMLIT: if ((instr->datasize==1) || (((invar->valid&oprmslitval)!=0) && ((-128<=instr->u1.immval)&&(instr->u1.immval<=127) && ((instr->privprops&FIXEDLIT)==0)))) litsize=1; else litsize=instr->datasize; if (litsize==1) addopcode(actcode,invar->oprmslit); else addopcode(actcode,invar->oprmlit); if (opcode!=PUSH) addmem(actcode,instr,mode,invar->subop,ca); #if (OutputASSEMBLER!=Positive) if (mode!=0 && (instr->privprops&FIXEDLIT)!=0) { area=instr->area; if (area>CNST) area=egiveareaid(area); pfix2(CODE,ca+actcode [0],area,instr->u0.offset,instr->fixtype); } #endif adddisp(actcode,instr->u1.immval,litsize); return 1; case OPPLUSRLIT: addopcode(actcode,invar->opplusreg+instr->rd); #if (OutputASSEMBLER!=Positive) if (mode!=0 && (instr->privprops&FIXEDLIT)!=0) { area=instr->area; if (area>CNST) area=egiveareaid(area); pfix2(CODE,ca+actcode [0],area,instr->u0.offset,instr->fixtype); } #endif adddisp(actcode,instr->u1.immval,4); /* only mov */; return 1; /***/ } /* end of case on instr->variant */ Mabort(878); return 0; } /* Cgenerateinstr */ /***/ /**/ /* Copyright (c) 1993 Edinburgh Portable Compilers Ltd. All Rights Reserved.*/ /**/ /***/ void Cgeneratefrag(struct fragfmt *frag) { /****************************************************************/ /** process the current fragment and generate code **/ /****************************************************************/ static unsigned char initseq [15+1] = { 0xE8,0x0,0x0,0x0,0x0, /* call */ 0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90}; /* Will be 16 byte aligned */ static unsigned char nops[16]= {0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90, 0x90,0x90,0x90,0x90,0x90,0x90}; static int next_code_byte=0; int ca,putprops,putid,tabentry,tabval,area,I,J,base; unsigned char p5code [32]; char errmess [64]; struct instrfmt *instr,*nextinstr; struct fragfmt *addrfrag; struct lrecfmt *lrec; struct Exdatafmt *exdata; struct Swtabfmt *tab; #if(OutputASSEMBLER!=Positive) buffptr=0; ca=frag->address; baseca=ca; if (ca!=next_code_byte) { pcbytes(next_code_byte,ca-next_code_byte,nops); } if (frag->entryid!=0) { exdata=exrecad(frag->entryid); exdata->Ca=ca; if ((exdata->Props&2)!=0) /*main entry*/{ putprops=0x80000001; } else { putprops=exdata->Props&1; } if (exdata->PutId<=0) putid=-1; else putid=exdata->PutId; if (frag->prevfrag!=NULL) { putid=pentry2(putprops,ca,putid,exdata->Name); } else { pproc(exdata->Name,putprops,ca,&putid,latestlineno+1); } if (exdata->PutId<=0) exdata->PutId=putid; } nextinstr=frag->firstinstr; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; do { I=Cgenerateinstr(1,instr,ca,&p5code[0] ); if (I!=0) { if (p5code [0]!=instr->csize) { Cdisasfrag(frag); sprintf(errmess,"Generated instr size %s calc %d/gen %d", instrmnem [instr->opcode],instr->csize,p5code [0]); p5Error(errmess); } for (J=1; J<=(int)p5code [0]; J++) { codebuff [buffptr]=p5code [J]; ca++; buffptr++; if (buffptr>1023) { pcbytes(baseca,1024,codebuff); buffptr=0; baseca=ca; } } } /*more info to generate for this pseudo-op*/ } while (I>1) ; } if (frag->switchtab!=0) { tab=(struct Swtabfmt*)(frag->switchtab); while ((ca&3)!=0) /* align switch table */{ codebuff [buffptr]=0x90; /*NOP*/ ca++; buffptr++; } for (tabentry=0; tabentry<=tab->Entries-1; tabentry++) { tabval=tab->u0.R [tabentry]; if (tabval!=-1) { if (tabval<0) { p5Error("Illegal switch table entry"); } lrec=BLabRecAd(tabval&0xFFFFFFF); addrfrag=lrec->frag; if (addrfrag->final!=NULL) addrfrag=addrfrag->final; if (Pic==0) pfix2(CODE,ca,CODE,addrfrag->address,0) /*non Pic to abs address*/; } if ((Pic==0)||(tabval==-1)) J=0; else J=addrfrag->address-lastlocalcallra; codebuff [buffptr]=J; codebuff [buffptr+1]=(unsigned)J>>8; codebuff [buffptr+2]=(unsigned)J>>16; codebuff [buffptr+3]=(unsigned)J>>24; ca+=4; buffptr+=4; if (buffptr>1023) { pcbytes(baseca,buffptr,codebuff); buffptr=0; baseca=ca; } } } if (buffptr>0) { pcbytes(baseca,buffptr,codebuff); } next_code_byte=ca; if (frag->nextfrag==0 && PI->procfrag->entryid!=0) { pprocend(ca,PI->framesize,PI->saveareasize); area=((unsigned)PI->procprops>>11)&3; if (area!=0) /* This is .init or .fini proc */{ base=pgivecurrentsize(area+10); if (base<0) base=0; exdata=exrecad(PI->procfrag->entryid); pdbytes(area+10,base,16,initseq) /* Code to init section */; pfix2(area+10,base+1,CODE,exdata->Ca-4,dcallfix) /* Fix the call */; } } #endif; } /* Cgeneratefrag */ /***/ int Cvalidatejumps(struct fragfmt *frag) { /*************************************************************************/ /** This routine is called after address have been assigned **/ /** Jumps can be shortened if the offset is 1 signed byte **/ /** This routine checks jumps and shortens if appropiate **/ /*************************************************************************/ struct instrfmt *instr,*nextinstr; struct lrecfmt *lrec; struct fragfmt *tfrag; char S[32]; int ca,count,j; unsigned reachable; ca=frag->address; /*starting ca */ count=0; nextinstr=frag->firstinstr; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; if (instr->group==BCC && instr->opcode!=JMPPTR) { lrec=BLabRecAd(instr->u1.immval); if (lrec->frag==NULL) { sprintf(S,"Unset Lab referenced %d",lrec->labnum-PI->labadjust); p5Error(S); } tfrag=lrec->frag; if (tfrag->final!=NULL) tfrag=tfrag->final; j=tfrag->address-(ca+2); /* base is next instr address */ reachable=(j>=-128 && j<=127); if (reachable && instr->csize>2 && (instr->privprops&OVERLONG)==0) { count++; instr->csize=2; } if ((!reachable) && instr->csize==2) { count++; instr->csize=6; if (instr->opcode==JMP) instr->csize=5; instr->privprops|=OVERLONG; /* needed to prevent looping */ } } if (instr->groupcsize; } return count; /* count of overlong jumps */ } /*Cvalidate jumps */ /***/ /**/ /* Copyright (c) 1993 Edinburgh Portable Compilers Ltd. All Rights Reserved.*/ /**/ /***/ void Cdisasfrag(struct fragfmt *frag) { /****************************************************************/ /** process the current fragment and list instructions **/ /****************************************************************/ struct fragfmt *xfrag,*addrfrag,*tempfrag; struct instrfmt *instr,*tempinstr,*poststart,*previnstr,*nextinstr; struct lrecfmt *lrec; struct Swtabfmt *tab; struct Exdatafmt *exdata; int ca,tabval,val,area,i; /* print procedure start information for the first fragment */ #if (MINCODESIZE==0) if ((bbschedule|gschedule)!=0 && ((frag==frag->final && (frag->props&(FALLENINTO|DSWITCHCASEFRAG))==0 && frag->uses!=0) || frag->prevfrag==0)) { printf("\t.text\n\t.align 16\n"); } #endif if (frag->entryid!=0) { if (firstentry!=0) printf("\nepctext:\n"); if (frag->prevfrag==0) { printf("\n%s PROCEDURE: ",commentmark); } else { printf("\n%s ENTRY: ",commentmark); } exdata=exrecad(frag->entryid); printf("%s\n",exdata->Name); if (frag->prevfrag==0) { #if OutputASSEMBLER==Positive int mask,fmask,i,offset; char name [256] ; strcpy(name,exdata->Name); if (strcmp(name,"s#go")==0) strcpy(name,"MAIN_"); if (name[0]=='s' && name[1]=='#') name[1]='_'; printf("%c%c.text\n",HT,HT); if (firstentry!=0) firstentry=0; if ((PI->procprops&PP_EXTERNAL)!=0) printf("%c%c.globl %s\n",HT,HT,name); printf("%s:\n",name); #endif } else { firstentry=0; } #if((Language==CCOMP)&&(LanguageVariant==USLC)&&(TargetABI==LynxOS)) outputstabstrings(); #endif; } /* print any region structure information at the start of the fragment */ ca=frag->address; previnstr=NULL; nextinstr=frag->firstinstr; if (nextinstr!=NULL && frag->labindex!=0) { instr=nextinstr; if (instr->group==REGIONINFO) { if (instr->previnstr!=0) { p5Error("Cdisasfrag: Bad instruction chain"); } p5disasinstr(instr,&ca); previnstr=nextinstr; nextinstr=instr->nextinstr; } } poststart=frag->lastinstr; if (poststart!=NULL) { while (poststart!=NULL) { instr=poststart; if (instr->group!=REGIONINFO) break ; poststart=instr->previnstr; } if (poststart==NULL) { poststart=instr; } else { poststart=instr->nextinstr; } } /* output any label associated with the start of the fragment */ if (frag->labindex!=0) { lrec=BLabRecAd(frag->labindex); printf("\n"); printf(".L%s:\t\t%s %x\n",itos(lrec->labnum),commentmark,frag->address); } /* disassemble the instructions in the fragment */ while (nextinstr!=NULL && nextinstr!=poststart) { instr=nextinstr; if (instr->previnstr!=previnstr) { p5Error("Cdisasfrag: Bad instruction chain"); } if (instr->group==LINENUMBER && instr->nextinstr!=0) { tempinstr=instr->nextinstr; if (tempinstr->group!=LINENUMBER) { p5disasinstr(instr,&ca); } } else { p5disasinstr(instr,&ca); } previnstr=nextinstr; nextinstr=instr->nextinstr; } /* allow assembler reorganisations again at the end of a user assembly */ /* fragment */ /* output any switch table associated with the fragment */ if (frag->switchtab!=0) { tab=(struct Swtabfmt*)(frag->switchtab); if (tab->Mode==SPARSESWITCH) p5Error("Unexpanded switch"); printf("%c%c.align 4\n",HT,HT); printf(".SW%s:",itos(tab->Labbase)); for (i=0; i<=tab->Entries-1; i++) { tabval=tab->u0.R [i]; val=(unsigned)tabval>>28; if (val==1) { /* IMP switch */ xfrag=(struct fragfmt*)(tabval&0xFFFFFFF); tabval=xfrag->address; } else { if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); addrfrag=lrec->frag; if (addrfrag->final!=NULL) addrfrag=addrfrag->final; tabval=addrfrag->address; } } printf("%c%c.long ",HT,HT); if (val==1) { printf("X%08X",tabval); } else { printf(".L%s",itos(lrec->labnum)); if (Pic!=0) { printf("-.Sbase%s+1 ",itos(tab->Labbase)); } } printf("\n"); ca+=4; } } /* output any region information pseudo instructions after any */ /* dense switch information */ while (nextinstr!=NULL) { instr=nextinstr; if (instr->previnstr!=previnstr) { p5Error("Cdisasfrag: Bad instruction chain"); } p5disasinstr(instr,&ca); previnstr=nextinstr; nextinstr=instr->nextinstr; } /* print information after the last fragment*/ #if (OutputASSEMBLER!=Positive) if (frag->nextfrag==0) { tempfrag=PI->procfrag; if (tempfrag->entryid!=0) { printf("\n"); } area=((unsigned)PI->procprops>>11)&3; if (area!=0) /* This is .init or .fini proc */{ xfrag=PI->procfrag; exdata=exrecad(xfrag->entryid); printf("%c%c",HT,HT); if (area==1) { #if (TargetABI==DGUXABI) printf(".section\t.init"); #else printf(".init"); #endif } else { #if (TargetABI==DGUXABI) printf(".section\t.fini"); #else printf(".fini"); #endif } printf("\n%c%ccall %s\n%c%c.text\n",HT,HT,exdata->Name,HT,HT); } } #endif } /*Cdisasfrag*/ /***/ void p5frlabel(int labx,int count,int curfragaddr) { /*************************************************************************/ /** The label whose index is labx has been used recently count times **/ /** for a forward reference only. Obtain all registers defined since **/ /** first use and clear their register memory. Better than clearing **/ /** everything. Count normally one **/ /*************************************************************************/ int gdef,cdef,fdef,i,j,g; struct instrfmt *instr; struct fragfmt *frag; gdef=0; cdef=0; fdef=0; frag=PI->curfrag; /*Curfrag has only the label as yet */ frag=frag->prevfrag; while (frag!=NULL) { if (frag->labref==labx) { count--; if (count==0) goto fragfound; } frag=frag->prevfrag; } Mabort(950); /* Reach procstart without finding count jumps */ /**/ fragfound: frag=frag->nextfrag; do { if (frag==NULL) break ; instr=frag->firstinstr; while (instr!=NULL) { gdef|=instr->rdef; fdef|=instr->fdef; cdef|=instr->cdef; if ((instr->props&ANYCALL)!=0) { gdef|=~CALLEESAVEDREGS; fdef|=~CALLEESAVEDFREGS; } g=instr->group; /* can also define reguse by a store */ if (g==INTSTORE) gdef|=1<rd; if (g==FPSTORE) fdef|=1<<(instr->rd&7); instr=instr->nextinstr; } frag=frag->nextfrag; } while (1) /* FOR EVER */; /**/ for (i=0; i<=7; i++) { j=1<curfrag; PI->curfrag=frag; /* There is a breakfrag in Mstart at right point */ /**/ if ((PI->proclevel<=1 && (PI->procprops&PP_NODISPLAY)!=0) || rtl>0 || (PI->privprops&(CALLSINTERNAL|GLOBALACCESS))==0) /* Avoid enter with no display */{ if (rtl==0) { ARCH(opr(PUSH,EBP)); ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); ARCH(rr(MOV,ESP,-1,EBP)); ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); } if ((rtl<=1)&&(localsize!=0)) { ARCH(oplit(SUB,ESP,localsize,ESP)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY|BARRIERINSTR,0,0)); } } else if (PI->proclevel==1) { /* emulate an enter size,1 */ ARCH(opr(PUSH,EBP)); ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); ARCH(rr(MOV,ESP,-1,EBP)); ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); ARCH(opr(PUSH,ESP)) /* Only component of display at level 1*/; ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); ARCH(oplit(SUB,ESP,localsize,ESP)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY|BARRIERINSTR,0,0)); } else { /* use enter dealing with offsets > 12 bits specially */ if (localsize<4095) { ARCH(enter(localsize,PI->proclevel)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY|BARRIERINSTR,0,0)); } else { ARCH(enter(localsize&0xFFF,PI->proclevel)); ARCH(modinstrprops(0,WRAPPERINSTR,0,0)); ARCH(rlit(SUB,ESP,((unsigned)localsize>>12)<<12,ESP)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY|BARRIERINSTR,0,0)); } }; if ((imask&(1<curfrag=savefrag; } /*entry wrapper*/ /***/ static void exitwrapper(int imask,int fmask,int initoffset,int localsize,int rtl) { /*************************************************************************/ /** Restore the callee saved registers on exit and reset STACKPOINTER **/ /*************************************************************************/ struct fragfmt *frag; struct instrfmt *instr; int i,offset; frag=PI->curfrag; frag->props|=RETURNFRAG; /*!NO COALESCE TAIL pds sees no need to set this */ rclearregs(1); if ((PI->proclevel<=1 && (PI->procprops&PP_NODISPLAY)!=0) || rtl>0 || (PI->privprops&(CALLSINTERNAL|GLOBALACCESS))==0) /* Nodisplay */{ offset=-localsize; } else { offset=(-localsize)-(4*PI->proclevel); } if ((imask&(1<rdef|=257<props|=WRAPPERINSTR; ARCH(opr(POP,EBP)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY,0,0)); #endif; } else if (rtl==1) { ARCH(oplit(ADD,ESP,localsize,ESP)); ARCH(modinstrprops(0,WRAPPERINSTR|FRAMEMODIFY,0,0)); } i=0; if ((PI->privprops&RETURNSINT)!=0) i=1<privprops&RETURNSLONG)!=0) i=(1<procprops&PP_RETURNSAG)!=0) /* returns agregate*/{ instr=BCurInstr(); instr->opcode=RETN; instr->u1.immval=4; instr->csize=3; } if ((PI->procprops&PP_ALTCALLCONV) && PI->inparsize!=0) { instr=BCurInstr(); instr->opcode=RETN; instr->u1.immval=PI->inparsize; instr->csize=3; } } /*exit wrapper*/ /***/ static void InvestigateGOTLoad() { /****************************************************************/ /** Chop GOTbase load at start of fn **/ /****************************************************************/ int cnt; #define MAXGOTREGREFS 10 struct fragfmt *frag; struct instrfmt *curinstr; struct instrfmt *iad [MAXGOTREGREFS+1]; cnt=0; frag=PI->procfrag; while (frag!=NULL) { curinstr=frag->firstinstr; while (curinstr!=NULL) { if ((curinstr->rref|curinstr->rdef)&(1<rdef)) goto nogo; /* genuine use found*/ cnt++; if (cnt>MAXGOTREGREFS) goto nogo; /* too many side entries */ iad [cnt]=curinstr; } curinstr=curinstr->nextinstr; } frag=frag->nextfrag; } for (cnt=cnt; cnt>=1; cnt--) BDeleteInstr(iad [cnt],1); PI->rdef&=~(1<rdef&CALLEESAVEDREGS; fmask=PI->fdef&CALLEESAVEDFREGS; wrappediregs=imask; rtl=0; PI->saveareasize=0; for (i=0; i<=7; i++) { if ((1<saveareasize+=4; } /* We want the adjustment in total to be a multiple of 8 bytes */ totsize=localsize+PI->saveareasize+4*PI->proclevel; if ((PI->proclevel<=1 && (PI->procprops&PP_NODISPLAY)==0) || (PI->privprops&(CALLSINTERNAL|GLOBALACCESS))==0) totsize-=4; if ((totsize&7)==4) { localsize+=4; PI->localsize=localsize; } /* perform leaf routine optimisation */ if ((PI->privprops&(NOTLEAFRT|GLOBALACCESS))==0 && (PI->procprops&PP_HASSIDEE)==0 /*no side entries*/ && leafopt!=0){ rtl=DoLeafOpt(PI->procfrag); } wrappertype=rtl; /* remember sort of wrappers used */ if (rtl==0) wrappediregs|=1<procfrag,imask,fmask,PI->saveareasize,PI->localsize,rtl); if ((PI->procprops&PP_HASSIDEE)!=0) /* has side entries)*/{ nextfrag=PI->procfrag; while (nextfrag!=NULL) { frag=nextfrag; if ((frag->entryid!=0)&&(frag->prevfrag!=0)) { entrywrapper(nextfrag,imask,fmask,PI->saveareasize,PI->localsize,rtl); } nextfrag=frag->nextfrag; } } /**/ exitwrapper(imask,fmask,PI->saveareasize,PI->localsize,rtl); } /* p5 do wrapping*/ /***/ /***/ /***/ /********************************************************************************/ /** **/ /** Fragment cleanup **/ /** **/ /********************************************************************************/ static void transform(unsigned char *reg,struct instrfmt *instr,int *regmap) { /*************************************************************************/ /** Chnages a single register reference to its new value **/ /*************************************************************************/ int oldbit,newbit,newreg,oldreg; oldreg=*reg; newreg=regmap [oldreg]; if (oldreg==newreg) return; *reg=newreg; oldbit=1<<(oldreg&31); newbit=1<<(newreg&31); if (oldreg>f0REG) { Mabort(961); /* can not transform fregs on stacked acc m-c*/ } else { if ((oldbit&instr->rref)!=0) instr->rref=(instr->rref&(~oldbit))|newbit; if ((oldbit<<8)&instr->rref) instr->rref=(instr->rref&(~(oldbit<<8)))|(newbit<<8); if ((oldbit&instr->rdef)!=0) instr->rdef=(instr->rdef&(~(oldbit|(oldbit<<8))))|(257<nextfrag; nextinstr=frag->firstinstr; cdef1=0; cref1=0; rref1=0; rdef1=0; fref1=0; fdef1=0; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; if (instr->group<=MAXCODEGROUP) { cdef1|=instr->cdef; cref1|=instr->cref; rdef1|=instr->rdef; rref1|=instr->rref; fref1|=instr->fref; fdef1|=instr->fdef; } } frag->cuse=cref1; cref|=cref1; frag->cdef=cdef1; cdef|=cdef1; frag->ruse=rref1; rref|=rref1; frag->rdef=rdef1; rdef|=rdef1; frag->fuse=fref1; fref|=fref1; frag->fdef=fdef1; fdef|=fdef1; } PI->cdef=cdef; PI->cref=cref; PI->rdef=rdef; PI->rref=rref; PI->fdef=fdef; PI->fref=fref; } /***/ static int DoLeafOpt(struct fragfmt * procfrag) { /*************************************************************************/ /** This procedure attempts to remove the stack frame allocation of a **/ /** leaf procedure. We do this by counting the references to SF **/ /** Result signifies as follows:- **/ /** =0 no changes made **/ /** =1 REfs changes to ESP frame still needed but not EBP **/ /** =2 Routine runs with ESP unchanged **/ /*************************************************************************/ struct instrfmt *curinstr; struct fragfmt *frag,*nextfrag; int adj,pcount,fcount,origdisp; #if(MINCODESIZE!=0) static const int SENSIBLE=5; #else static const int SENSIBLE=12; /* ESP refs are bigger so dont optimise*/ #endif; fcount=0; pcount=0; nextfrag=procfrag; while (nextfrag!=NULL) { frag=nextfrag; nextfrag=frag->nextfrag; curinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (curinstr!=NULL) { if ((curinstr->rdef&(1<privprops&FRAMEACCESS)!=0) { if (curinstr->disp<0) fcount++; if (curinstr->disp>0) pcount++; } curinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); } } if (targetvariant==0 && (fcount+pcount)>SENSIBLE) return 0; if (fcount==0) PI->localsize=0; /* No frame reqd*/ adj=PI->localsize+PI->saveareasize; nextfrag=procfrag; while (nextfrag!=NULL) { frag=nextfrag; nextfrag=frag->nextfrag; curinstr=frag->firstinstr; while (curinstr!=NULL) { if ((curinstr->privprops&FRAMEACCESS)!=0) { origdisp=curinstr->disp; if (curinstr->disp>0) curinstr->disp+=adj-4; if (curinstr->disp<0) curinstr->disp+=adj; if ((-128<=origdisp && origdisp<=127)&& !(((-128<=curinstr->disp)&&(curinstr->disp<=127)))) curinstr->csize+=3; if ((-128<=curinstr->disp && curinstr->disp<=127) && !((-128<=origdisp && origdisp<=127))) curinstr->csize-=3; if ((curinstr->privprops&SIBREQD)==0) curinstr->csize++; if (curinstr->disp==0) { if (adj<=128) curinstr->csize--; else curinstr->csize-=3; /* can happen */ } curinstr->rs1=STACKPOINTER; curinstr->rref=(curinstr->rref&(~(257<nextinstr; } } if (fcount==0) return 2; return 1; } /*DoLeafOpt*/ /***/ static void improveleafentry() { /*************************************************************************/ /** Check out a type 1 leaf routine to see if the stack frame is still */ /** needed. If not delete it otherwise try the optimisations for a */ /** normal routine entry amended as necessary */ /*************************************************************************/ struct fragfmt *frag; struct instrfmt *instr,*subinstr,*addinstr,*nextinstr; #define FVOLS (3<<7) #define VOLS ((1<procfrag; while (frag!=NULL) { nextinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (nextinstr!=NULL) { instr=nextinstr; nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if ((instr->props&WRAPPERINSTR)!=0) { if (instr->opcode==SUB && instr->rd==ESP && (instr->variant==OPRLIT || instr->variant==OPRSLIT)) { framesize=instr->u1.immval; if (subinstr!=NULL) return; subinstr=instr; } if (instr->opcode==ADD && instr->rd==ESP && (instr->variant==OPRLIT || instr->variant==OPRSLIT)) { if (addinstr!=NULL) return; addinstr=instr; } } if ((instr->props&(LOADINSTR|STOREINSTR))!=0) { if (instr->area==STACK || (instr->rs1==ESP && instr->dispopcode==LEA && (instr->rref&(257<rs1==ESP && (instr->privprops&SIBREQD)==0 && instr->disp>framesize)) stkaccess=1; /* addr of params only*/ } } frag=frag->nextfrag; } if (stkaccess==0 && subinstr!=NULL && addinstr!=NULL) { if (addinstr->u1.immval!=framesize) { BPrintFrags("At point of failure"); Mabort(982); } BDeleteInstr(subinstr,0); BDeleteInstr(addinstr,0); frag=PI->procfrag; while (frag!=NULL) { nextinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (nextinstr!=NULL) { instr=nextinstr; nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if ((instr->props&(LOADINSTR|STOREINSTR))!=0 || instr->opcode==LEA) /* unsafe LEAs excluded above*/{ if (instr->rs1==ESP) { instr->disp-=framesize; if (instr->disp<127 && (instr->disp+framesize)>127) instr->csize-=3; } } } frag=frag->nextfrag; } if (optimtrace!=0 && peepreport!=0) { printf("Leaf routine improved further"); printf("\n"); } return; } #undef VOLS #undef FVOLS } /*improve leafentry*/ /***/ static void improveprocentry() { /*************************************************************************/ /** Attempt to move parameter loads in front of frame generation **/ /** this avoides the AGIs and gives the scheduler more scope **/ /*************************************************************************/ struct fragfmt *frag; struct instrfmt *instr,*nextinstr; static const int FVOLS=3<<7; static const int VOLS=((1<procfrag; nextinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (nextinstr!=NULL) { instr=nextinstr; nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if ((instr->props&LOADINSTR)!=0 && instr->area==PARAMS && (instr->variant==OPRRM || instr->variant==FOPRM) && instr->rs1==EBP && (instr->rdef&deffed)==0 && (instr->fdef&fdeffed)==0 && ((instr->rdef&VOLS)!=0 || (instr->fdef&FVOLS)!=0)) { BMoveAfter(frag,frag,instr,NULL); instr->csize++; instr->disp-=4; /* push ebp not done */ instr->rref=(instr->rref&(~(257<rs1=ESP; if (optimtrace!=0 && peepreport!=0) { printf("Param hoisted at procentry\n"); } } if ((instr->props&STOREINSTR)!=0 && instr->area!=STACK) return; deffed|=instr->rdef; reffed|=instr->rref; fdeffed|=instr->fdef; freffed|=instr->fref; if ((deffed&VOLS)==VOLS && freffed!=0) return; } } /*improve procentry*/ /***/ static void improvereturns(struct fragfmt *jumpfrag,struct fragfmt *retfrag,struct instrfmt *retinstr ) { /*************************************************************************/ /** Jumpfrag is marked as a retun junmp and retfrag is the destintn **/ /** which has at most 2 instructions. Copy the return over the jump **/ /** with some care as delay slots have been filled **/ /*************************************************************************/ struct instrfmt *jumpinstr; jumpinstr=(struct instrfmt*)(jumpfrag->labrefinstr); /* Should be jump to return */ if (optimtrace!=0 && peepreport!=0) { printf("PHOLE: improve return %08X %08X %3d %5d\n",(unsigned)jumpfrag,(unsigned)retfrag, jumpfrag->labref,retfrag->labindex); } if (!((jumpfrag->labref==retfrag->labindex && retfrag->labindex!=0) && jumpinstr!=NULL && (jumpinstr->props&(BRANCH|CONDITIONALBRANCH))==BRANCH)) return; BCopyInstr(retinstr,jumpinstr) /* Overwrite jump with first ret instr */; do { retinstr=BMachineInstr(retinstr->nextinstr,NEXTCHAIN); if (retinstr==NULL) { BUpdateLiveRegs(jumpfrag); return; } jumpinstr=BInsertInstr(jumpfrag,NULL) /* at end */; BCopyInstr(retinstr,jumpinstr); } while (1) /* FOR EVER */; } /***/ /***/ /***/ void FinalTidy(struct fragfmt *procfrag) { /**************************************************************************/ /** do procedure tidy-ups which must be performed late in the cycle **/ /**************************************************************************/ struct fragfmt *f,*frag,*prevfrag,*retfrag,*nextfrag,*targetfrag; struct instrfmt *instr,*lastinstr,*specinstr,*curinstr; struct lrecfmt *lrec; int rlive,flive,clive,alive; static const int fmask=~(1<nextfrag; if ((frag->props&RETURNFRAG)!=0) retfrag=frag; curinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (curinstr!=NULL) /* regroup certain special pairs not scheduled correctly */{ instr=curinstr; curinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if (curinstr!=NULL&& targetvariant==0 && ((instr->issueprops&curinstr->issueprops)&TPIGSTART)!=0) { if ((instr->opcode==POP || instr->opcode==PUSH) && curinstr->opcode==instr->opcode) curinstr->issueprops&=~TPIGSTART; if ((curinstr->props&CONDITIONALBRANCH)!=0 && (instr->opcode==CMP || instr->opcode==CMPB || instr->opcode==TEST)) curinstr->issueprops&=~TPIGSTART; } #if (MINCODESIZE!=0) if ((instr->variant==OPRLIT || instr->variant==OPEAXLIT) && instr->rd<=3) { if (((instr->u1.immval&0xFFFF00FF)==0 && (instr->opcode==OR || instr->opcode==XOR || instr->opcode==TEST)) || (instr->opcode==AND && (instr->u1.immval&0xFFFF00FF)==0xFFFF00FF)) { instr->rd|=4; instr->u1.immval=((unsigned)instr->u1.immval>>8)&0xff; instr->csize=3; instr->variant=OPBRMLIT; if (optimtrace!=0 && peepreport!=0) { printf("FINAL: AH type form used %08x\n",instr); } } else if ((instr->u1.immval&0xFFFF00FF)==0xff && instr->opcode==AND) { lastinstr=BMachineInstr(instr->previnstr,PREVCHAIN); do { if (lastinstr==NULL) break; if ((lastinstr->rdef&instr->rdef)!=0) break; lastinstr=BMachineInstr(lastinstr->previnstr,PREVCHAIN); } while(1); if (lastinstr!=NULL && lastinstr->opcode==LHU && lastinstr->rd==instr->rd) { instr->rd|=4; instr->u1.immval=((unsigned)instr->u1.immval>>8)&0xff; instr->csize=3; instr->variant=OPBRMLIT; if (optimtrace!=0 && peepreport!=0) { printf("FINAL: AH type form used %08x\n",instr); } } } } #endif } } if (retfrag==NULL) goto specexec; instr=BMachineInstr(retfrag->firstinstr,NEXTCHAIN); if (instr==NULL) goto specexec; again: /**/ /* if we have a simple return with fall thro then the stack reclaim */ /* after the previous call can probably be reclaimed */ /**/ if (!((instr->opcode==RET) || ((instr->opcode==MOV || instr->opcode==ADD) && instr->rdef==(1<props&FALLENINTO)!=0 && (PI->privprops&NOTLEAFRT)!=0) { prevfrag=retfrag->prevfrag; while (prevfrag->lastinstr==NULL) prevfrag=prevfrag->prevfrag; lastinstr=prevfrag->lastinstr; while (((((lastinstr->rref|lastinstr->rdef)&(257<props&((ANYCALL|JUMPINSTR)|BRANCH))==0) && (lastinstr->previnstr!=0)) lastinstr=lastinstr->previnstr; if ((lastinstr->privprops&PARAMRECLAIM)!=0) { BDeleteInstr(lastinstr,1); goto again; } } /**/ f=procfrag; while ((f!=NULL)) { frag=f; f=frag->nextfrag; if (MINCODESIZE==0 && (frag->props&RETURNBRANCH)!=0) { improvereturns(frag,retfrag,instr); } } /**/ /* Since we have no GIS try a little speculative instruction execution */ /* we look for a frag ending in a conditional jump where th next frag */ /* has no label. We then try to hoist an instruction from this frag into */ /* a bubble before the conditional jump. The optimisation is important */ /* because of the delays in fomp...bcc etc */ /* The hoisted instruction must not alter store or define anything */ /* that is live on entry to the jump target frag */ /**/ specexec: f=procfrag; roundagain: while ((f!=NULL)) { frag=f->final; f=frag->nextfrag; if (frag->labref==0 || frag->labrefinstr==0 || (frag->props&(FALLSTHROUGH|USERASSEMFRAG))!=FALLSTHROUGH) continue ; nextfrag=f->final; if (nextfrag->uses!=1 || (nextfrag->props&FALLENINTO)==0 || (nextfrag->props&USERASSEMFRAG)==0) continue ; instr=BMachineInstr(frag->lastinstr,PREVCHAIN); if (instr->group!=BCC || instr->opcode==JMPPTR) continue ; lrec=BLabRecAd(frag->labref); targetfrag=lrec->frag->final; specinstr=BMachineInstr(nextfrag->firstinstr,NEXTCHAIN); if (specinstr==NULL || (specinstr->privprops&SLOWRESULTINSTR)!=0) continue ; if (specinstr->fdef!=0 || (specinstr->rdef&targetfrag->rin)!=0 || (specinstr->cdef&targetfrag->cin)!=0 || (specinstr->props&(STOREINSTR|BRANCH|CONDITIONALBRANCH|JUMPINSTR|ANYCALL))!=0 || ((specinstr->props&LOADINSTR)!=0 && (specinstr->area|specinstr->u0.offset)==-1)) continue ; if ((specinstr->cdef&(1<rdef&instr->rref)!=0) || (((specinstr->cdef&instr->cref)&fmask)!=0) || ((specinstr->rref&instr->rdef)!=0) || (((specinstr->cref&instr->cdef)&fmask)!=0)) break ; if ((instr->props&(ANYCALL|JUMPINSTR|WRAPPERINSTR))!=0) break ; if (((specinstr->props&LOADINSTR)!=0) && ((instr->props&STOREINSTR)!=0) && (p5memoryoverlap(instr,specinstr,0)!=MONO)) break ; if (instr->bubbles<=1 || instr->bubbles==255) { instr=BMachineInstr(instr->previnstr,PREVCHAIN); continue ; } if ((specinstr->cdef&(1<previnstr,PREVCHAIN); if (lastinstr==NULL) break ; BLiveSetAfterInstr(frag,lastinstr,&rlive,&flive,&clive,&alive); if (((clive&(1<opcode==FSTSW)) { instr=lastinstr; continue ; } /* Best earlier here*/ } instr->bubbles--; BMoveBefore(nextfrag,frag,specinstr,instr); instr->bubbles--; specinstr->issueprops|=TPCONTSPEC; BUpdateLiveRegs(nextfrag); BUpdateLiveRegs(frag); if (optimtrace!=0 && peepreport!=0) { printf("PHOLE: Instr executed speculatively %.8X\n",(unsigned)specinstr); } f=frag; goto roundagain; } } } /*FinalTidy*/ /***/ /***/ static void improvecompares(struct fragfmt *frag) { /*************************************************************************/ /** Inspects fragments ending with a compare and branch. If the operands**/ /** are bytes or halfs then try to amend the comparision to avoid costly**/ /** sign extension. Other improvements may be possible **/ /** This routine must be called before 'cleanup frag' to which it **/ /** logically belongs. The caller must ensure there is a labrefinstr **/ /*************************************************************************/ int reg1,reg2,l1op,l2op,cmpop,jop,rlive,flive,clive,alive,state; struct instrfmt *jinstr,*cmpinstr,*l1instr,*l2instr,*iinstr; jinstr=frag->labrefinstr; if (jinstr==NULL) return; jop=jinstr->opcode; cmpinstr=BMachineInstr(jinstr->previnstr,PREVCHAIN); if (cmpinstr==NULL) return; cmpop=cmpinstr->opcode; if (cmpop!=CMP || cmpinstr->variant!=OPRR) { state=1; goto fail; } l2instr=BMachineInstr(cmpinstr->previnstr,PREVCHAIN); if (l2instr==NULL) { state=2; goto fail; } l2op=l2instr->opcode; if ((l2instr->props&LOADINSTR)==0 || (l2instr->rdef&cmpinstr->rref)==0) { state=3; goto fail; } /* wrong load */ l1instr=BMachineInstr(l2instr->previnstr,PREVCHAIN); if (l1instr==NULL) { state=4; goto fail; } l1op=l1instr->opcode; /**/ /* there may be a base of index load between the two relevant loads */ /* check for this and skip it if it is harmless */ /**/ if ((l1instr->rdef&cmpinstr->rref)==0) { iinstr=l1instr; /* Hang onto intermediate load */ l1instr=BMachineInstr(l1instr->previnstr,PREVCHAIN); if (l1instr==NULL) { state=5; goto fail; } l1op=l1instr->opcode; if ((l1instr->rdef&iinstr->rref)!=0) { state=6; goto fail; } /* interactions !*/ } reg1=l1instr->rd; reg2=l2instr->rd; if (l1op==l2op && (l1op==LB || l1op==LBU) && reg1<=3 && reg2<=3 && reg1!=reg2 && cmpinstr->rref==((1<rdef&l2instr->rref)==0) /*opnd also used as index !*/ { if (l1op==LB && (cmpop==JA || cmpop==JAE || cmpop==JB || cmpop==JBE)) { state=7; goto fail; } BLiveSetAfterInstr(frag,cmpinstr,&rlive,&flive,&clive,&alive); if (l1op==LBU) { if (jop==JGE) jop=JAE; if (jop==JG) jop=JA; if (jop==JLE) jop=JBE; if (jop==JL) jop=JB; } if ((l1instr->rdef&rlive)==0) { l1instr->opcode=LBNOX; l1instr->privprops|=BYTEREGOP; l1instr->csize--; } if ((l2instr->rdef&rlive)==0) { l2instr->opcode=LBNOX; l2instr->privprops|=BYTEREGOP; l2instr->csize--; } jinstr->opcode=jop; cmpinstr->opcode=CMPB; cmpinstr->privprops|=BYTEREGOP; if (optimtrace!=0 && peepreport!=0) { printf("\nImprove CMP: Sign Extn avoided at %08X\n",(unsigned)cmpinstr); } return; } state=8; fail: if (optimtrace!=0 && peepreport!=0) { printf("\nImprove CMP: Exit state=%4d\n",state); } } /***/ static void improvereclaims(struct fragfmt *frag) { /*************************************************************************/ /** Look for parameter space reclaims and attempt to merge them **/ /** or substitute faster sequences. Suitable (ie non nested) instrs **/ /** have been marked already by routine ccall. **/ /*************************************************************************/ struct fragfmt *nextfrag; int reg,rlive,flive,clive,alive,lindex,size; struct Exdatafmt *exdata; struct instrfmt *instr,*master,*previnstr; char x [256] ,y [256] ; previnstr=NULL; master=BMachineInstr(frag->lastinstr,PREVCHAIN); do { if (master==NULL) return; if ((master->privprops&PARAMRECLAIM)!=0 && master->opcode==ADD && master->rd==ESP) goto found; if (master->opcode==MOV && master->rd==ESP && master->rs1==EBP && (master->props&WRAPPERINSTR)!=0) goto found; previnstr=master; master=BMachineInstr(master->previnstr,PREVCHAIN); } while (1) /* FOR EVER */; found: /**/ /* alloca is a naughty routine which tampers with the stackpointer */ /* check for calls before proceding further with this optimisation */ /**/ instr=BMachineInstr(master->previnstr,PREVCHAIN); while (instr!=NULL) { if ((instr->props&ANYCALL)!=0 && (instr->privprops&KNOWNOFFSET)==0) { exdata=exrecad(instr->u1.immval); /* the use of imp_resolve handles all cases of underscores */ if (imp_resolve(exdata->Name,x,"alloca",y)==0) goto popopt; } instr=BMachineInstr(instr->previnstr,PREVCHAIN); } instr=BMachineInstr(master->previnstr,PREVCHAIN); while (instr!=NULL) { if (instr->opcode==ADD && instr->rd==ESP) { if ((instr->privprops&PARAMRECLAIM)==0) goto popopt; /* Other stack operation found*/ if (master->opcode==ADD) master->u1.immval+=instr->u1.immval; if (optimtrace!=0 && peepreport!=0) { printf("\nParam reclaim deleted at %08X\n",(unsigned)instr); } BDeleteInstr(instr,0) /* Mark as deleted */; } instr=BMachineInstr(instr->previnstr,PREVCHAIN); } if (master->variant==OPRSLIT && master->u1.immval>=128) { /* instruction now needs a larger literal value */ master->variant=OPRLIT; master->datasize=4; master->csize+=3; } nextfrag=frag; lindex=BLocateLabel(PI->returnlab+PI->labadjust,0); while ((nextfrag->props&(RETURNFRAG|RETURNBRANCH|COMMONTAIL))==0 && nextfrag->nextfrag!=0 && (nextfrag->props&FALLSTHROUGH)!=0 && (nextfrag->labrefinstr==0 || nextfrag->labref==lindex)) { nextfrag=nextfrag->nextfrag->final; } if ((nextfrag->props&(RETURNFRAG|RETURNBRANCH|COMMONTAIL))!=0 && (master->props&WRAPPERINSTR)==0) { BDeleteInstr(master,0); if (optimtrace!=0 && peepreport!=0) { printf("\nFinal Param reclaim deleted at %08X\n",(unsigned)master); } return; } popopt: size=master->u1.immval; if (master->opcode==ADD && (size==4 || (size==8 && (MINCODESIZE!=0 || targetvariant==0)))) /* Change to pop */{ reg=EDX; BLiveSetAfterInstr(frag,master,&rlive,&flive,&clive,&alive); if ( size==4 && previnstr!=NULL && (previnstr->rdef&(1<opcode=POP; master->group=instrgroup [POP]; master->rd=reg; master->datasize=4; master->csize=1; master->variant=OPPLUSR; master->rdef=(257<rref=1<props|=LOADINSTR; master->cdef=0; if (size==8 ) { instr=BInsertInstr(frag,master); BCopyInstr(master,instr); instr->rd=ECX; instr->rdef=(257<labref==0 || frag->labrefinstr==0 || (frag->props&FALLSTHROUGH)==0 || (frag->props&USERASSEMFRAG)==0) return; branchinstr=frag->labrefinstr; branchop=branchinstr->opcode; if (branchinstr->group!=BCC || frag->nextfrag==NULL) return; thenfrag=frag->nextfrag; thenfrag=thenfrag->final; if (thenfrag->preds!=0 || thenfrag->uses!=1 || (thenfrag->props&FALLENINTO)==0 || (thenfrag->props&FALLSTHROUGH)!=0 || (thenfrag->props&USERASSEMFRAG)!=0 || BMachineInstr(thenfrag->firstinstr,NEXTCHAIN)==NULL) return; estinstrA=BMachineInstr(thenfrag->firstinstr,NEXTCHAIN); if (BMachineInstr(estinstrA->nextinstr,NEXTCHAIN)==NULL) return; if (estinstrA->variant!=OPPLUSRLIT || estinstrA->rd>3) return; if (estinstrA->u1.immval!=0 && estinstrA->u1.immval!=1) return; if (estinstrA->opcode!=MOV) return; value=estinstrA->u1.immval; uncondinstr=BMachineInstr(estinstrA->nextinstr,NEXTCHAIN); if ((uncondinstr->group!=BCC) || (uncondinstr->opcode!=JMP) || (thenfrag->labref==0) || (thenfrag->labrefinstr!=uncondinstr) || (thenfrag->nextfrag==0)) return; elsefrag=thenfrag->nextfrag; elsefrag=elsefrag->final; lrec=BLabRecAd(frag->labref); if (lrec->frag==elsefrag && BMachineInstr(elsefrag->firstinstr,NEXTCHAIN)!=NULL && (elsefrag->props&FALLENINTO)==0 && (elsefrag->props&USERASSEMFRAG)==0 && (elsefrag->uses==1)) { estinstrB=BMachineInstr(elsefrag->firstinstr,NEXTCHAIN); postestinstr=BMachineInstr(estinstrB->nextinstr,NEXTCHAIN); if (estinstrB->variant==OPPLUSRLIT && estinstrB->rd==estinstrA->rd && estinstrB->opcode==MOV && estinstrB->u1.immval==(1-value)) { lrec=BLabRecAd(thenfrag->labref); if (postestinstr==NULL && (elsefrag->props&FALLSTHROUGH)!=0 && elsefrag->nextfrag!=NULL) { postfrag=elsefrag->nextfrag; } else postfrag=lrec->frag; postfrag=postfrag->final; if (postestinstr==NULL && lrec->frag==postfrag && (postfrag->props&FALLENINTO)!=0) { if (peepreport!=0) { printf("IMPROVING LOGICAL ESTABLISH AT %08X\n",(unsigned)frag); } thenfrag->labref=0; thenfrag->labrefinstr=0; thenfrag->props|=FALLSTHROUGH; elsefrag->props|=FALLENINTO; elsefrag->preds=0; elsefrag->labindex=0; frag->labref=0; frag->labrefinstr=0; postfrag->uses--; if (postfrag->uses<=1 && (postfrag->props&FALLENINTO)!=0) postfrag->labindex=0; /**/ /* now overwrite the jump of the then frag with the correct SETcc */ /* and remove other jumps */ /**/ if (value!=0) branchop=InverseJump [branchop-(JO)]; uncondinstr->opcode=setjump [branchop-(JO)]; uncondinstr->group=INTOPSu; uncondinstr->rs1=0; uncondinstr->variant=OPR; uncondinstr->rd=estinstrA->rd; uncondinstr->u1.immval=0; uncondinstr->rref=1<rd; uncondinstr->rdef=257<rd; uncondinstr->cref=1<props=DESTROYABLE; uncondinstr->privprops=BYTEREGOP; uncondinstr->csize=3; uncondinstr->rmbyte=0xC0; if ((((1<rd)&RegClaimMaskA))!=0) { uncondinstr->privprops|=REGVARASSN; } BDeleteInstr(branchinstr,1); prevpred=0; nextpred=postfrag->preds; while (nextpred!=0) { preddesc=(struct preddescfmt*)(nextpred); if (preddesc->frag==(int)thenfrag) { if (prevpred==0) { postfrag->preds=preddesc->next; } else { tempdesc=(struct preddescfmt*)(prevpred); tempdesc->next=preddesc->next; } } prevpred=nextpred; nextpred=preddesc->next; } BDeleteInstr(estinstrB,1); BReconvergeLiveness(); /* Updates not good enough here (learnt the hard way)*/ } } } } /*ImproveCondEstablish*/ /***/ /***/ static void DelRedunCompares(struct fragfmt *frag) { /*******************************************************************************/ /** Finds repeated compare operations and deletes the second, redundant, **/ /** compare **/ /*******************************************************************************/ struct instrfmt *instr,*sinstr; if ((frag->props&FALLSTHROUGH)==0 || frag->nextfrag==NULL || frag->labref==NULL || frag->labrefinstr==NULL) return; instr=frag->labrefinstr; if (instr->rdef!=0) return; instr=BMachineInstr(instr->previnstr,PREVCHAIN); if (instr==NULL) return; if (instr->opcode!=CMP && instr->opcode!=CMPB) return; if ((instr->privprops&(FIXEDINSTR|FIXEDLIT))!=0) return; frag=frag->nextfrag; frag=frag->final; if (frag->labindex!=0 || frag->uses!=1) return; sinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); if (sinstr==NULL) return; if (sinstr->opcode!=CMP && sinstr->opcode!=CMPB) return; if ((sinstr->privprops&(FIXEDINSTR|FIXEDLIT))!=0) return; if (instr->opcode==sinstr->opcode && instr->variant==sinstr->variant && instr->u1.immval==sinstr->u1.immval && instr->rd==sinstr->rd && instr->rs1==sinstr->rs1) { if (!(instr->variant==OPRR || instr->variant==OPRRM || instr->variant==OPRMR || instr->variant==OPRLIT || instr->variant==OPRSLIT)) return; if (instr->variant==OPRRM || instr->variant==OPRMR) { if (instr->datasize!=sinstr->datasize) return; if (p5memoryoverlap(instr,sinstr,1)!=MOEXACT) return; } BDeleteInstr(sinstr,0); BUpdateLiveRegs(frag); if (optimtrace!=0 && peepreport!=0) { printf("DELETED REDUNDANT COMPARE INSTR AT %08X\n",(unsigned)sinstr); } } } /*DelRedunCompares*/ /***/ static void increasedataoffsetby(struct instrfmt *instr,int amount) { /* ******************************************************************* * We are increasing the data offset by not more than 8 bytes * * however even a small changes can change the size of the offsets * * past one of the critical thresholds. Hence need this RT to do it* *******************************************************************/ int prev,new; prev=instr->disp; new=prev+amount; instr->u0.offset+=amount; if (instr->privprops&FIXEDINSTR) return; if (prev==0) { instr->csize++; instr->disp=new; } else if (prev<=127 && new>127) { instr->disp=new; instr->csize+=3; } else if (prev<-128 && new >=-128) { instr->disp=new; instr->csize-=3; } else if (new==0) { instr->disp=0; instr->csize--; } else instr->disp=new; } static void Cleanupfrag(struct fragfmt *frag,int final) { /****************************************************************/ /** perform peepholing cleanups within frag **/ /** Some optimisations are left to the final pass as they **/ /** can intefere with other peep hole routines **/ /****************************************************************/ int curi,previ,op,prevop,group; int FirstPass,changes,anychanged,changed,rlive,flive,clive,alive, rdef,rref,j,rd,rs1,fragaddr,anyref; struct instrfmt* fpads [21]; struct instrfmt *curinstr,*previnstr,*nextinstr,*thirdinstr; again: changes=0; curi=(int)BMachineInstr(frag->firstinstr,NEXTCHAIN); FirstPass=1; while (curi!=0) { curinstr=(struct instrfmt*)(curi); op=curinstr->opcode; group=curinstr->group; previ=(int)BMachineInstr(curinstr->previnstr,PREVCHAIN); previnstr=(struct instrfmt*)(previ); if (previ!=0) { prevop=previnstr->opcode; } /* Look for TEST register instructions that are redundant since */ /* the previous instruction which defined the register also set */ /* the flags register in a STANDARD way */ if (op==TEST && curinstr->variant==OPRR && curinstr->rd==curinstr->rs1 && previ!=0) { while (((previnstr->rdef&curinstr->rref)==0) && ((previnstr->cdef&(1<props&ANYCALL)==0)) { previ=(int)BMachineInstr(previnstr->previnstr,PREVCHAIN); if (previ==0) break ; previnstr=(struct instrfmt*)(previ); } if (previnstr!=NULL) prevop=previnstr->opcode; if (((previnstr->rdef&255/*alphas only*/)==curinstr->rref) && ((previnstr->cdef&(1<u2.prefixes[osprefix]!=0 && previnstr->u2.prefixes[osprefix]==0) { previnstr->u2.prefixes[osprefix]=SHORTOPND; previnstr->csize++; } BDeleteInstr(curinstr,0) /* Mark as deleted*/; BUpdateLiveRegs(frag); goto NextPass; } /*INC & DEC set the flags in a nonstandard way that is */ /* Good enough for the signed comparisions only */ thirdinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if ((thirdinstr!=NULL) && (prevop==INC||prevop==DEC)) { j=thirdinstr->opcode; if (j==JG||j==JGE||j==JL||j==JLE||j==JE||j==JNE||j==SETG || j==SETGE||j==SETL||j==SETLEQ||j==SETE||j==SETNEQ) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant TEST(2) deleted at %08X\n",(unsigned)curinstr); } if (curinstr->u2.prefixes[osprefix]!=0 && previnstr->u2.prefixes[osprefix]==0) { previnstr->u2.prefixes[osprefix]=SHORTOPND; previnstr->csize++; } BDeleteInstr(curinstr,0) /* Mark as deleted*/; BUpdateLiveRegs(frag); goto NextPass; } } } /* Is the flag register actually Used ? */ BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((clive&(1<rd==previnstr->rs1 && previnstr->variant==OPRR && previnstr->rd==curinstr->rd) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { curinstr->u2.prefixes[osprefix]=SHORTOPND; curinstr->csize++; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Extend & TEST elided at %08X\n",(unsigned)curinstr); } BDeleteInstr(previnstr,1); } } } previ=(int)BMachineInstr(curinstr->previnstr,PREVCHAIN); previnstr=(struct instrfmt*)(previ); if (previ!=0) { prevop=previnstr->opcode; } /* Look for an LEA which can be reduced to and INC or DEC */ /* This saves space rather than time since thew are both 1 bt*/ /* instructions. However an AGI stall may be avoided */ if (op==LEA && abs(curinstr->disp)==1 && curinstr->rd==curinstr->rs1 && (curinstr->privprops&SIBREQD)==0) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((clive&(1<disp>0) curinstr->opcode=INC; else curinstr->opcode=DEC; curinstr->group=INTOPS; curinstr->datasize=4; curinstr->csize=1; curinstr->variant=OPPLUSR; curinstr->rref=1<rd; curinstr->cdef=1<rs1=0; if (optimtrace!=0 && peepreport!=0) { printf("\n"); printf("\nPHOLE: LEA transformed at %08X\n",curi); } changes++; op=LEA; } } /* Various optimisations can follow an AND with literal */ /* Bit tests can be improved by eliding the previous shift */ /* and sign extension-truncations can sometimes be merged */ if (op==AND && (curinstr->variant==OPRLIT || curinstr->variant==OPRSLIT)) { thirdinstr=NULL; nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (nextinstr!=NULL ) thirdinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); if (nextinstr!=NULL && (nextinstr->props&CONDITIONALBRANCH)!=0 && ((frag->rout&curinstr->rdef)==0) && (previ!=0) && (previnstr->rd==curinstr->rd) && (prevop==SHR||prevop==SAR) && (previnstr->variant==OPRSLIT)) { curinstr->u1.immval=curinstr->u1.immval<u1.immval; if (curinstr->variant==OPRSLIT && (curinstr->u1.immval<-128||curinstr->u1.immval>127)) { curinstr->variant=OPRLIT; curinstr->csize+=3; curinstr->datasize=4; } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Rshift elided at %08X\n",curi); p5printinstr(curinstr); } changes++; BDeleteInstr(previnstr,0); goto NextPass; } #if (MINCODESIZE==0) if (thirdinstr!=NULL && nextinstr->opcode==CMP && (nextinstr->variant==OPRLIT || nextinstr->variant==OPRSLIT) && (thirdinstr->props&CONDITIONALBRANCH)!=0 && (thirdinstr->opcode==JE || thirdinstr->opcode==JNE) && ((frag->rout&curinstr->rdef)==0) && (previ!=0) && previnstr->rd==curinstr->rd && nextinstr->rd==curinstr->rd && (prevop==SHR||prevop==SAR) && (previnstr->variant==OPRSLIT)) { curinstr->u1.immval=curinstr->u1.immval<u1.immval; if (curinstr->variant==OPRSLIT && (curinstr->u1.immval<-128||curinstr->u1.immval>127)) { curinstr->variant=OPRLIT; curinstr->csize+=3; curinstr->datasize=4; } nextinstr->u1.immval=nextinstr->u1.immval<u1.immval; if (nextinstr->variant==OPRSLIT && (nextinstr->u1.immval<-128||nextinstr->u1.immval>127)) { nextinstr->variant=OPRLIT; nextinstr->csize+=3; nextinstr->datasize=4; } if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: Rshift elided at %08X\n",curi); changes++; BDeleteInstr(previnstr,0); goto NextPass; } #endif while (nextinstr!=NULL && ((curinstr->rdef&(nextinstr->rdef|nextinstr->rref))==0)) { nextinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); } if (nextinstr!=NULL) { if (nextinstr->rd==curinstr->rd && nextinstr->opcode==AND && (nextinstr->variant==OPRLIT || nextinstr->variant==OPRSLIT)) { nextinstr->u1.immval&=curinstr->u1.immval; if (nextinstr->variant==OPRLIT && nextinstr->u1.immval>-128 && (nextinstr->u1.immval<=127)) { nextinstr->variant=OPRSLIT; nextinstr->csize+=(-3); nextinstr->datasize=1; } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: ANDs combined at %08X\n",curi); } changes++; BDeleteInstr(curinstr,0); goto NextPass; } if (nextinstr->rd==curinstr->rd && nextinstr->opcode==MOVSX && nextinstr->rd==nextinstr->rs1 && (curinstr->u1.immval&0x8000)==0) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Sign-Ext(4) deleted at %08X\n",(unsigned) nextinstr); } changes++; BDeleteInstr(nextinstr,0); goto NextPass; } if (nextinstr->rd==curinstr->rd && nextinstr->opcode==SHL && previ!=0 ){ thirdinstr=previnstr; do { if (thirdinstr==NULL) break; if (((thirdinstr->rref|thirdinstr->rdef)&curinstr->rref)!=0) break; thirdinstr=BMachineInstr(thirdinstr->previnstr,PREVCHAIN); } while (1); if (thirdinstr!=NULL && thirdinstr->rd==curinstr->rd && (thirdinstr->opcode==SHR || thirdinstr->opcode==SAR) && (thirdinstr->variant==OPRSLIT && OPRSLIT==nextinstr->variant)) { if (thirdinstr->u1.immval>nextinstr->u1.immval) { thirdinstr->u1.immval-=nextinstr->u1.immval; curinstr->u1.immval=curinstr->u1.immval<u1.immval; BDeleteInstr(nextinstr,0); } else if (nextinstr->u1.immval>thirdinstr->u1.immval) { nextinstr->u1.immval-=thirdinstr->u1.immval; curinstr->u1.immval=curinstr->u1.immval<u1.immval; BDeleteInstr(thirdinstr,0); } else { curinstr->u1.immval=curinstr->u1.immval<u1.immval; BDeleteInstr(nextinstr,0); BDeleteInstr(thirdinstr,0); } if (curinstr->variant==OPRSLIT && (curinstr->u1.immval<-128 || curinstr->u1.immval>127)) { curinstr->variant=OPRLIT; curinstr->csize+=3; } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: SH-AND-SH improved at %08X\n",curi); } changes++; goto NextPass; } } } /* This next peephole must follow the others since we need to */ /* remove redundant shifts and sign extends before complicating */ /* everything by changing somes ands to tests */ nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (nextinstr!=NULL && (nextinstr->props&CONDITIONALBRANCH)!=0 && (frag->rout&curinstr->rdef)==0) { curinstr->opcode=TEST; if (curinstr->variant==OPRSLIT) /* no such variant for TEST */{ if (targetvariant!=0 || curinstr->rd>3) { curinstr->csize+=3; curinstr->datasize=4; curinstr->variant=OPRLIT; } else { curinstr->variant=OPBRMLIT; curinstr->privprops|=BYTEREGOP; } } /* Because of sign bit propagation an ANDL x'80' will */ /* remain a TESTL so extra code is needed here to reduce */ /* this case to a TESTB */ if (curinstr->variant==OPRLIT && ((unsigned)curinstr->u1.immval>>8)==0 && curinstr->rd<=3) { curinstr->variant=OPBRMLIT; curinstr->csize=3; curinstr->privprops|=BYTEREGOP; } curinstr->rdef=0; if (curinstr->variant==OPBRMLIT && previ!=0 && curinstr->rs1==previnstr->rd && (prevop==LB || prevop==LBU ||prevop==LBNOX)) { /* No change to csize as LB & LBU are 2 byte opcodes */ if (prevop==LBNOX) previnstr->csize++; previnstr->opcode=TEST; previnstr->variant=OPBMEMLIT; previnstr->u1.immval=curinstr->u1.immval; previnstr->rdef=0; previnstr->cdef|=(1<variant==OPRLIT && previ!=0 && (prevop==LH || prevop==LHU) && curinstr->rs1==previnstr->rd && (curinstr->u1.immval&0xffff00ff)==0) { previnstr->opcode=TEST; previnstr->variant=OPBMEMLIT; previnstr->datasize=1; /* previnstr->csize-=3;*/ increasedataoffsetby(previnstr,1); previnstr->u1.immval=curinstr->u1.immval>>8; previnstr->rdef=0; previnstr->cdef|=(1<u2.prefixes[osprefix]=0; BDeleteInstr(curinstr,0); } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: AND->TEST change at %08X\n",curi); } changes++; goto NextPass; } /* Finally look for the following not very commom sequence */ /* and reg,2**N */ /* cmp reg,2**n */ /* jcc Label */ /* the cmp can be deleted and the jcc changed to use the flag */ /* settings of the and. This should be changed into a TEST */ /* during the next pass */ /* the JLT case is a branch never but deleting a branch at */ /* this time makes the topology of the program inconsistent */ /* with enormous complications. If this (Silly) case is found */ /* we skip this optimisation */ if (nextinstr!=NULL && nextinstr->opcode==CMP && (nextinstr->variant==OPRSLIT || nextinstr->variant==OPRLIT) && nextinstr->rd==curinstr->rd && nextinstr->u1.immval==curinstr->u1.immval && curinstr->u1.immval!=0x80000000 && (curinstr->u1.immval&(curinstr->u1.immval-1))==0) { thirdinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); if (thirdinstr!=NULL && (thirdinstr->props&CONDITIONALBRANCH)!=0 && thirdinstr->opcode!=JL) { if (thirdinstr->opcode==JE || thirdinstr->opcode==JLE || thirdinstr->opcode==JGE) thirdinstr->opcode=JNZ; else thirdinstr->opcode=JZ; BDeleteInstr(nextinstr,0); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: CMP 2**N change at %8x\n ",curi); } changes++; goto NextPass; } } } /* Look for a SIB byte that can be enhanced to avoid a */ /* shift occurrs when picking up every second array element */ if ((curinstr->privprops&SIBREQD)!=0 && previ!=0 && prevop==SHL && previnstr->variant==OPRSLIT && (((unsigned)curinstr->sib>>6)+previnstr->u1.immval)<=3 && curinstr->rs2==previnstr->rd) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rs2))==0) { curinstr->sib+=(previnstr->u1.immval<<6); BDeleteInstr(previnstr,0); /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: SIB use enhanced at %08X\n",curi); } changes++; goto NextPass; } } /* Look for a byte load followed by a shift or and such that */ /* the register does not need to be sign or zero extended */ if (targetvariant==0 && final>0 && previ!=0 && (prevop==LB || prevop==LBU) && (curinstr->variant==OPRLIT || curinstr->variant==OPRSLIT || curinstr->variant==OPBRMLIT)) { if (((op==SHL && 24<=curinstr->u1.immval && curinstr->u1.immval<=31) || ((op==AND || op==TEST) && 0u1.immval && curinstr->u1.immval<=255)) && curinstr->rd==previnstr->rd && curinstr->rd<=3) { prevop=LBNOX; previnstr->opcode=LBNOX; previnstr->privprops|=BYTEREGOP; previnstr->csize-=1; /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: redundant Sign-Ext(1) deleted at %08X\n",previ); changes++; goto NextPass; } else { thirdinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (thirdinstr!=NULL && thirdinstr->opcode==AND && thirdinstr->variant==OPRSLIT && thirdinstr->rd==curinstr->rd && curinstr->rd==previnstr->rd && curinstr->rd<=3 && (op==SHR || op==SAR) && 1<=curinstr->u1.immval && curinstr->u1.immval<=7 && (thirdinstr->u1.immval<u1.immval)<=255) { prevop=LBNOX; previnstr->opcode=LBNOX; previnstr->privprops|=BYTEREGOP; previnstr->csize+=-1; if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: redundant Sign-Ext(1A) deleted at %08X\n",previ); changes++; /* Live regs are not changed by this optimisation */ goto NextPass; } if (thirdinstr!=NULL && thirdinstr->opcode==SB && thirdinstr->rd==curinstr->rd && curinstr->rd==previnstr->rd && curinstr->rd<=3 && (op==AND || op==OR || op==XOR || op==SHL || op==ADD || op==SUB )) { BLiveSetAfterInstr(frag,thirdinstr,&rlive,&flive,&clive,&alive); if ((clive&(1<rd))==0) { prevop=LBNOX; previnstr->opcode=LBNOX; previnstr->privprops|=BYTEREGOP; previnstr->csize+=-1; if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: redundant Sign-Ext(1B) deleted at %08XC\n",previ); changes++; goto NextPass; } } } } /* Repeat the simpler forms of the above with the LB not adjacent */ if (targetvariant==0 && final>0 && previ!=0 && (curinstr->variant==OPRLIT || curinstr->variant==OPRSLIT || curinstr->variant==OPBRMLIT) && (op==AND || op==TEST) && 0u1.immval && curinstr->u1.immval<=255 && curinstr->rd<=3) { thirdinstr=previnstr; do { if (thirdinstr==NULL) break; if (((thirdinstr->rref|thirdinstr->rdef)&curinstr->rref)!=0) break; thirdinstr=BMachineInstr(thirdinstr->previnstr,PREVCHAIN); } while (1); if (thirdinstr!=NULL && (thirdinstr->opcode==LB || thirdinstr->opcode==LBU) && thirdinstr->rd==curinstr->rd) { thirdinstr->opcode=LBNOX; thirdinstr->privprops|=BYTEREGOP; previnstr->csize--; if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: redundant Sign-Ext(1C) deleted at %08X\n",(unsigned)thirdinstr); changes++; goto NextPass; } } /* Repeat above optimisation for 16 bit operands */ if (targetvariant==0 && final>0 && previ!=0 && (prevop==LH || prevop==LHU) && (curinstr->variant==OPRLIT || curinstr->variant==OPRSLIT)) { if (((op==SHL && 16<=curinstr->u1.immval && curinstr->u1.immval<=31) || ((op==AND ||op==TEST) && (0u1.immval && curinstr->u1.immval<=0xFFFF))) && curinstr->rd==previnstr->rd) { /* this operation does not save space but does save time and may well rule out the and Sx..,%ah instructions which saves a lot of space. Duck out in the case */ #if (MINCODESIZE!=0) if (prevop=LHU && op==AND && curinstr->rd<=3 && (curinstr->u1.immval&255)==255) goto NextPass; #endif previnstr->opcode=MOV; previnstr->u2.prefixes [osprefix]=SHORTOPND; /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Sign-Ext(2) deleted at %08X\n",previ); } changes++; } else if (op==SHR && curinstr->u1.immval==8) { previnstr->opcode=LBU; /* previnstr->csize--; */ previnstr->datasize=1; previnstr->u2.prefixes[osprefix]=0; increasedataoffsetby(previnstr,1); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant SHR elided at %08X\n",(unsigned)curinstr); p5printinstr(previnstr); } BDeleteInstr(curinstr,0); changes++; } else { thirdinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (thirdinstr!=NULL && thirdinstr->opcode==AND && (thirdinstr->variant==OPRLIT || thirdinstr->variant==OPRSLIT) && (thirdinstr->rd==curinstr->rd && curinstr->rd==previnstr->rd) && (op==SHR || op==SAR) && 1<=curinstr->u1.immval && curinstr->u1.immval<=15 && (thirdinstr->u1.immval<u1.immval)<=0xFFFF) { previnstr->opcode=MOV; previnstr->u2.prefixes [osprefix]=SHORTOPND; /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Sign-Ext(2A) deleted at %08X\n",previ); } } } } /* Look for a byte load followed by a byte store where*/ /* the loaded register is dead after th store. */ /* Remove any unnecessary sign extension */ if (final>0 && previ!=0 && (prevop==LB || prevop==LBU) && op==SB && curinstr->rd==previnstr->rd && previnstr->rd<=3 && ((curinstr->privprops&FIXEDINSTR)==0 || curinstr->rs1!=curinstr->rd) && ((curinstr->privprops&SIBREQD)==0 || curinstr->rs2!=curinstr->rd)) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { previnstr->opcode=LBNOX; previnstr->privprops=previnstr->privprops|BYTEREGOP; previnstr->csize=previnstr->csize-1; /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Sign-Ext(3) deleted at "); phex(previ); printf("\n"); p5printinstr(previnstr); } changes++; goto NextPass; } } /* Repeat above optimisation for 16 bit operands */ if (final>0 && previ!=0 && (prevop==LH || prevop==LHU) && (op==SH) && ((curinstr->privprops&FIXEDINSTR)==0 || curinstr->rs1!=curinstr->rd) && ((curinstr->privprops&SIBREQD)==0 || curinstr->rs2!=curinstr->rd)) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { previnstr->opcode=MOV; previnstr->u2.prefixes [osprefix]=SHORTOPND; /* Live regs are not changed by this optimisation */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Zero-Ext improved at %08X\n",(unsigned)curinstr); } } } /* Finally look for LBUs which can become XOR &MOVB */ /* which is quicker and more likely to pair but not on P6*/ if (targetvariant==0 && MINCODESIZE==0 && final>0 && previ!=0 && prevop==LBU && previnstr->rd<=3 && (previnstr->rdef&previnstr->rref)==0) { thirdinstr=BInsertInstr(frag,previnstr); thirdinstr->rs1=previnstr->rd; thirdinstr->rd=previnstr->rd; thirdinstr->opcode=XOR; thirdinstr->group=instrgroup [XOR]; thirdinstr->props=DESTROYABLE; thirdinstr->csize=2; thirdinstr->rmbyte=0xC0; thirdinstr->variant=OPRR; thirdinstr->rdef=257<rd; thirdinstr->cdef=1<opcode=LBNOX; previnstr->privprops|=BYTEREGOP; previnstr->csize--; previnstr->rref|=(previnstr->rdef&255); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Sign-Ext(5) deleted at %08X\n",previ); p5printinstr(previnstr); } } /* Look for pointless ops involving zero such ops can */ /* arise out of MAX&MIN eops and are best dealt with */ /* in the peepholer */ if (previ!=0 && prevop==FLDZ && (op==FADDP || op==FSUBP)) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant FLDZ deleted at %08X\n",(unsigned)curinstr); } BDeleteInstr(curinstr,0) /* Mark as deleted*/; BDeleteInstr(previnstr,0) /* Mark as deleted*/; BUpdateLiveRegs(frag); changes++; goto NextPass; } /* Look for repeated initialisations and save a store access*/ if (curinstr->opcode==FLDZ || curinstr->opcode==FLD1) { nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); j=0; while (nextinstr!=NULL) { if ((nextinstr->props&(BRANCH|ANYCALL|JUMPINSTR|WRAPPERINSTR|UNKNOWNFLOW))!=0) break; if (((nextinstr->cdef&(1<fdef)!=0 && j<=20) { fpads [j]=nextinstr; j++; } nextinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); } if (j>=2) { nextinstr=fpads [0]; thirdinstr=fpads [1]; if (thirdinstr->opcode==curinstr->opcode && thirdinstr->fdef==curinstr->fdef && nextinstr->group==FPSTORE && instrform [nextinstr->opcode]==F2pFORM) { nextinstr->opcode+=3; nextinstr->cdef&=(~(1<privprops&PRECISIONREDN)!=0 && (curinstr->privprops&PRECISIONREDN)!=0) { nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); while (nextinstr!=NULL) { if ((nextinstr->fdef|nextinstr->fref)!=0 || ((nextinstr->props&(BRANCH|ANYCALL|JUMPINSTR|WRAPPERINSTR|UNKNOWNFLOW))!=0)) break ; nextinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); } if (nextinstr!=0 && nextinstr->opcode==FSTPm) { BDeleteInstr(curinstr,0) /* Mark as deleted*/; BDeleteInstr(previnstr,0) /* Mark as deleted*/; changes++; goto NextPass; } } /* Look for an integer load followed by a store back into */ /* the same location. This is commonly a result of a C smt*/ /* x=MACRO(x) where MACRO has a null expansion */ if (curinstr->group==INTSTORE && previ!=0 && previnstr->group==INTLOAD && curinstr->datasize==previnstr->datasize && curinstr->rd==previnstr->rd && (previnstr->rdef&previnstr->rref)==0) { if (p5memoryoverlap(curinstr,previnstr,1)==MOEXACT) { BDeleteInstr(curinstr,0); /* Mark as deleted */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant Store deleted at %08X\n",(unsigned)curinstr); } goto NextPass; } } /* Look for pairs of BSWAPs that operate on the same */ /* integer register and delete both */ if (op==BSWAP && previ!=0) { thirdinstr=previnstr; while (thirdinstr!=NULL && (thirdinstr->props&ANYCALL)==0 && (((thirdinstr->rdef|thirdinstr->rref)&curinstr->rdef))==0) { thirdinstr=BMachineInstr(thirdinstr->previnstr,PREVCHAIN); } if (thirdinstr!=NULL && thirdinstr->opcode==BSWAP && curinstr->rd==thirdinstr->rd) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant BSWAP deleted at %08X\n",(unsigned)curinstr); } BDeleteInstr(curinstr,0) /* Mark as deleted*/; BDeleteInstr(thirdinstr,0) /* Mark as deleted*/; changes++; goto NextPass; } } /* Look for unnecessary FXCHs which may be put in by the */ /* next optimisation. They often take 0 beats on Pentium */ /* but they cost on 486 and are inelegant */ if (previ!=0 && prevop==FXCH) { if (op==FXCH && previnstr->fdef==curinstr->fdef) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant FXCH deleted at %08X\n",(unsigned)curinstr); } BDeleteInstr(curinstr,0) /* Mark as deleted*/; BDeleteInstr(previnstr,0) /* Mark as deleted*/; changes++; goto NextPass; } if ((op==FADDP || op==FSUBP || op==FMULP) && previnstr->fdef==curinstr->fref) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: redundant FXCH deleted at %08X\n",(unsigned)previnstr); } BDeleteInstr(previnstr,0) /* Mark as deleted*/; if (op==FSUBP) curinstr->opcode=FSUBRP; /* Reverse the subtract */ changes++; goto NextPass; } } /* Look for simple 4 byte assign done via int regs which */ /* would be more efficient done via FP stack */ if (previ!=0 && previnstr->group==INTSTORE && curinstr->group==FPLOAD && curinstr->datasize==4 && curinstr->opcode!=FILD && p5memoryoverlap(previnstr,curinstr,1)==MOEXACT) { BLiveSetAfterInstr(frag,previnstr,&rlive,&flive,&clive,&alive); if ((rlive&(1<rd))==0) { thirdinstr=BMachineInstr(previnstr->previnstr,PREVCHAIN); while (thirdinstr!=NULL && (thirdinstr->rref|(thirdinstr->rdef&(257<rd)))==0 && (thirdinstr->fref|thirdinstr->fdef)==0) { thirdinstr=BMachineInstr(thirdinstr->previnstr,PREVCHAIN); } if (thirdinstr!=NULL && thirdinstr->group==INTLOAD && p5memoryoverlap(thirdinstr,curinstr,0)==MOEXACT) { thirdinstr->opcode=FLDm; thirdinstr->rd=curinstr->rd; thirdinstr->rdef=0; thirdinstr->fdef=curinstr->fdef; thirdinstr->cdef=1<cref=(1<variant=FOPRM; thirdinstr->group=FPLOAD; previnstr->opcode=FSTm; previnstr->rref&=(~(1<rd)); previnstr->rd=curinstr->rd; previnstr->fref=curinstr->fdef; previnstr->cref=(1<variant=FOPRM; previnstr->group=FPSTORE; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Store changed to use FSTK at %08X\n",(unsigned)curinstr); } BDeleteInstr(curinstr,0); changes++; goto NextPass; } } } /* Look for Floating store and pop following a FDUPL */ /* (is FLD %st(0) and delete the duplicate by changing the */ /* store and pop to a store */ if (previ!=0 && previnstr->variant==FOPPLUSR && prevop==FLDd && previnstr->rd==previnstr->rs1 && group==FPSTORE && instrform [op]==F2pFORM && op!=FSTPq && op!=FISTPd) { curinstr->opcode=op+3; /* non pop form */ curinstr->cdef&=(~(1<rd++; curinstr->fref=curinstr->fref<<1; BDeleteInstr(previnstr,0); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: FP dup deleted at %08X\n",curi); } changes++; goto NextPass; } /* Look for Floating store and pop followed by fetch back */ /* and change into store without pop and delete fetch */ /* Can only do this if both instructions marked DELETABLE */ /* since store & fetch back is used in place of round to */ /* single precision which is not implemented ! */ /* Also if there have been exact comparisons of 4 byte */ /* quanities in this routine skip this section since */ /* numeric differences can occur as a result of GREATER */ /* accuracy caused by this optimisation. */ /* A further hazard is load-stores within the FP-stack */ /* These must be eliminated by checking the prop bits */ /* This optimisation cant work with extended reals */ /* as there is no store extended without popping */ if (previ!=0 && curinstr->group==FPLOAD && (curinstr->props&(LOADINSTR|DESTROYABLE))==(LOADINSTR|DESTROYABLE)) { rdef=0; while (previnstr->fdef==0 && (previnstr->cdef&(1<group!=FPSTORE && (previnstr->props&ANYCALL)==0 && prevop!=FXCH && ((previnstr->props&STOREINSTR)==0 || p5memoryoverlap(curinstr,previnstr,0)==MONO)) { rdef|=previnstr->rdef; previ=(int)BMachineInstr(previnstr->previnstr,PREVCHAIN); if (previ==0) break ; previnstr=(struct instrfmt*)(previ); } prevop=previnstr->opcode; /**/ /* Check that the location referenced by previ is not accessed by an integer */ /* instruction before we reach curinstr. Unlikely but alas possible */ /**/ if ((previnstr->props&STOREINSTR)!=0) { j=0; thirdinstr=BMachineInstr(previnstr->nextinstr,NEXTCHAIN); while (thirdinstr!=curinstr) { if ((thirdinstr->props&(LOADINSTR|STOREINSTR))!=0 && p5memoryoverlap(thirdinstr,previnstr,0)!=MONO) j=1; thirdinstr=BMachineInstr(thirdinstr->nextinstr,NEXTCHAIN); } } if (previnstr->group==FPSTORE && instrform [prevop]==F2pFORM && prevop!=FISTPd && (previnstr->props&STOREINSTR)!=0 && j==0 && (previnstr->rref&rdef)==0) { rdef&=curinstr->rref|previnstr->rref; /* changed base regs */ if (rdef==0) j=1; else j=0; if ((curinstr->datasize==previnstr->datasize && previnstr->datasize==8) && (previnstr->props&DESTROYABLE)!=0) { if (MOEXACT==p5memoryoverlap(curinstr,previnstr,j)) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE:: FP reload(8) deleted at %08X %d\n",(unsigned)curinstr,rdef); } previnstr->opcode+=3; /* To non pop version */ previnstr->cdef&=(~(1<nextinstr,NEXTCHAIN); thirdinstr=BMachineInstr(previnstr->previnstr,PREVCHAIN); while (thirdinstr!=NULL && (thirdinstr->fref|thirdinstr->fdef)==0 && (thirdinstr->props&ANYCALL)==0) { thirdinstr=BMachineInstr(thirdinstr->previnstr,PREVCHAIN); } if (curinstr->datasize==previnstr->datasize && previnstr->datasize==4 && (previnstr->props&DESTROYABLE)!=0 && ((thirdinstr!=NULL && thirdinstr->group==FPLOAD && thirdinstr->datasize==4) || (nextinstr!=NULL && nextinstr->group==FPSTORE && nextinstr->datasize==4))) { if (MOEXACT==p5memoryoverlap(curinstr,previnstr,j)) { if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE:: FP reload(4) deleted at %08X %d\n",(unsigned)curinstr,rdef); } previnstr->opcode+=3; /* To non pop version */ previnstr->cdef&=(~(1<0 && p5memoryoverlap(curinstr,previnstr,j)==MONO && curinstr->rd>=13 /*dont go too deep */) { if (nextinstr!=NULL && fp0popop(nextinstr->opcode)==1) { if ((nextinstr->props&LOADINSTR)==0 || p5memoryoverlap(nextinstr,previnstr,j)==MONO) { thirdinstr=ARCH(appendfxch(frag,nextinstr->nextinstr,previnstr->rd-1)); BMoveAfter(frag,frag,previnstr,thirdinstr); curinstr->rd--; curinstr->fdef=(unsigned)curinstr->fdef>>1; previnstr->rd--; previnstr->fdef=(unsigned)previnstr->fdef>>1; previnstr->fref=(unsigned)previnstr->fref>>1; nextinstr->rd--; if (nextinstr->rs1>FRBASE) nextinstr->rs1--; nextinstr->fdef=(unsigned)nextinstr->fdef>>1; nextinstr->fref=(unsigned)nextinstr->fref>>1; changes++; } } else if (nextinstr!=NULL && fp1popop(nextinstr->opcode)==1 && nextinstr->group==FPCOMPARE/* nothing pushed back*/) { if ((nextinstr->props&LOADINSTR)==0 || p5memoryoverlap(nextinstr,previnstr,j)==MONO) { BMoveAfter(frag,frag,previnstr,nextinstr); curinstr->rd--; curinstr->fdef=(unsigned)curinstr->fdef>>1; nextinstr->rd--; if (nextinstr->rs1>FRBASE) nextinstr->rs1--; nextinstr->fdef=(unsigned)nextinstr->fdef>>1; nextinstr->fref=(unsigned)nextinstr->fref>>1; changes++; } } else { /* Just swop the load and store */ thirdinstr=ARCH(appendfxch(frag,curinstr->nextinstr,previnstr->rd-1)); BMoveAfter(frag,frag,previnstr,thirdinstr); curinstr->rd--; curinstr->fdef=(unsigned)curinstr->fdef>>1; previnstr->rd--; previnstr->fdef=(unsigned)previnstr->fdef>>1; previnstr->fref=(unsigned)previnstr->fref>>1; changes++; } } #endif } /* if previnstr_group=FPSTORE */ } /* if curinstr_group=FPload */ previ=(int)BMachineInstr(curinstr->previnstr,PREVCHAIN); previnstr=(struct instrfmt*)(previ); if (previ!=0) { prevop=previnstr->opcode; } /* Look for mov immediates of 1 or 0 and replace */ /* provided the FLAGS are not live at this point */ if (op==MOV && curinstr->variant==OPPLUSRLIT && (curinstr->privprops&(FIXEDINSTR|FIXEDLIT))==0 && (0<=curinstr->u1.immval && curinstr->u1.immval<=1)) { j=curinstr->u1.immval; BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((clive&(1<opcode=XOR; /* But XOR does */ curinstr->u1.immval=0; curinstr->csize=2; curinstr->group=instrgroup [XOR]; curinstr->rs1=curinstr->rd; curinstr->rmbyte=0xC0; curinstr->variant=OPRR; curinstr->cdef=1<nextinstr); thirdinstr->opcode=INC; thirdinstr->rd=curinstr->rd; thirdinstr->group=instrgroup [INC]; thirdinstr->props=DESTROYABLE; thirdinstr->rref=1<rd; thirdinstr->rdef=257<rd; thirdinstr->cdef=1<variant=OPPLUSR; thirdinstr->csize=1; } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: MOV-Literal improved at %08X\n",(unsigned)curinstr); } goto NextPass; } } /* look for the complex sequence mov safereg,wkreg */ /* op wkreg */ /* mov wkreg,safereg */ /* and if wkreg dead then reduce to a single instr */ nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (previ!=0 && (previnstr->props©INSTR)!=0 && previnstr->rd==curinstr->rd && (curinstr->rdef&0xFFFF)==(257<rd) && (group==INTOPS || group==INTOPSu)) { if (nextinstr!=NULL && (nextinstr->props©INSTR)!=0 && nextinstr->rs1==curinstr->rd && nextinstr->rd==previnstr->rs1) { BLiveSetAfterInstr(frag,nextinstr,&rlive,&flive,&clive,&alive); /* printstring("double copy stage 2"); newline */ /* p5 PrintInstr(previ); p5 PrintInstr(curi); p5 PrintInstr(nexti); newline*/ if ((rlive&(257<rd))==0 && ((curinstr->privprops&SIBREQD)==0 || curinstr->rd!=curinstr->rs2)) { if (curinstr->rs1==curinstr->rd) curinstr->rs1=nextinstr->rd; curinstr->rd=nextinstr->rd; curinstr->rdef=257<rd; curinstr->rref=(curinstr->rref&(~(1<rd)))|(1<rd); if (curinstr->variant==OPEAXLIT) { curinstr->variant=OPRLIT; curinstr->csize++; } if (curinstr->variant==OPALLIT) { curinstr->variant=OPBRMLIT; curinstr->csize++; } BDeleteInstr(previnstr,0); BDeleteInstr(nextinstr,0); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: double copy removed at %08X",curi); } goto NextPass; } } } #if (MINCODESIZE!=0) /* look for the complex sequence lbnox var,%xl */ /* orb $0x..,%xl */ /* sb %xl,var */ /* and if xh dead reduce to single lit-store instr */ /* this saves a lot of space but is slow due to bus lock */ if (previ!=0 && prevop==LBNOX && (previnstr->rref&previnstr->rdef)==0 && (op==OR || op==AND || op==XOR) && (curinstr->variant==OPBRMLIT || curinstr->variant==OPRSLIT || curinstr->variant==OPRLIT) && curinstr->rd==previnstr->rd && nextinstr!=NULL && nextinstr->opcode==SB && nextinstr->rd==curinstr->rd && p5memoryoverlap(previnstr,nextinstr,1)==MOEXACT) { BLiveSetAfterInstr(frag,nextinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { previnstr->variant=OPBMEMLIT; previnstr->props|=STOREINSTR; previnstr->group=RWMEM; previnstr->opcode=op; previnstr->rdef=0; previnstr->csize++; previnstr->u1.immval=curinstr->u1.immval; BDeleteInstr(curinstr,0); BDeleteInstr(nextinstr,0); if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE:Op on Store byte issued at %08X",previnstr); changes++; goto NextPass; } } /* look for the Vcomplex sequence lbnox var,%xl */ /* andb $0x..,xl */ /* orb $0x..,%xl */ /* sb %xl,var */ /* and if xh dead reduce to two lit-store instrs */ /* this saves a lot of space but is slow due to bus lock */ thirdinstr=NULL; if (previ!=0 && prevop==LBNOX && op==AND && nextinstr!=NULL) { thirdinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); } if (thirdinstr!=NULL && (previnstr->rref&previnstr->rdef)==0 && (nextinstr->opcode==OR || nextinstr->opcode==XOR) && (curinstr->variant==OPBRMLIT || curinstr->variant==OPRSLIT || curinstr->variant==OPRLIT) && (nextinstr->variant==OPBRMLIT || nextinstr->variant==OPRSLIT || nextinstr->variant==OPRLIT) && curinstr->rd==previnstr->rd && nextinstr->rd==curinstr->rd && thirdinstr->opcode==SB && thirdinstr->rd==curinstr->rd && p5memoryoverlap(previnstr,thirdinstr,1)==MOEXACT) { BLiveSetAfterInstr(frag,thirdinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { previnstr->variant=OPBMEMLIT; previnstr->props|=STOREINSTR; previnstr->group=RWMEM; previnstr->opcode=op; previnstr->rdef=0; previnstr->csize++; previnstr->u1.immval=curinstr->u1.immval; thirdinstr->variant=OPBMEMLIT; thirdinstr->props|=LOADINSTR; thirdinstr->group=RWMEM; thirdinstr->opcode=nextinstr->opcode; thirdinstr->rdef=0; thirdinstr->rref&=(~(1<rd)); thirdinstr->csize++; thirdinstr->u1.immval=nextinstr->u1.immval; BDeleteInstr(curinstr,0); BDeleteInstr(nextinstr,0); if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: Two ops on store bytes issued at %08X",previnstr); changes++; goto NextPass; } } #endif /* Look for a register copy following a simple operation */ /* where the two instructions can be merged into one LEA */ /* The source reg must be dead after the move of course */ /* as must the flags reg which is not set by lea */ /* This can occur out of LOOP OPT reallocating locals to */ /* integer registers. It is not common but can occur in */ /* 'hot' SPEC loops. */ if (op==MOV && curinstr->variant==OPRR && previ!=0 && previnstr->rd==curinstr->rs1 && previnstr->u1.immval!=0x80000000 && (prevop==INC||prevop==DEC|| ((prevop==ADD||prevop==SUB) && (previnstr->variant==OPRSLIT||previnstr->variant==OPRLIT||previnstr->variant==OPEAXLIT)))) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0 && (clive&(1<disp=previnstr->u1.immval; if (prevop==INC ||prevop==DEC) curinstr->disp=1; if (prevop==DEC ||prevop==SUB) curinstr->disp=-curinstr->disp; curinstr->opcode=LEA; curinstr->variant=OPRRM; if (curinstr->disp==0) { curinstr->csize=2; curinstr->rmbyte=0; } else if (-128<=curinstr->disp && curinstr->disp<=127) { curinstr->csize=3; curinstr->rmbyte=0x40; } else { curinstr->csize=6; curinstr->rmbyte=0x80; } curinstr->rref=256<rs1; curinstr->props&=(~COPYINSTR); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Two instrs merged into LEA at %08X\n",(unsigned)curinstr); } BDeleteInstr(previnstr,0); changes++; goto NextPass; } } /* Look for a register copy followed by a simple operatn */ /* where the two instructions can be merged into one LEA */ /* The flags reg must be dead after the move of course */ /* This can occur out of LOOP OPT reallocating locals to */ /* integer registers. It is not common but can occur in */ /* 'hot' SPEC loops. */ if (previ!=0 && prevop == MOV && previnstr->variant==OPRR && previnstr->rd==curinstr->rd && curinstr->u1.immval!=0x80000000 && (op ==INC || op ==DEC || ((op ==ADD || op ==SUB) && (curinstr->variant==OPRSLIT || curinstr->variant==OPRLIT || curinstr->variant==OPEAXLIT))) ) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((clive&(1<disp=curinstr->u1.immval; curinstr->u1.immval=0; if (op ==INC || op ==DEC ) curinstr->disp=1; if (op ==DEC || op ==SUB ) curinstr->disp=-curinstr->disp; curinstr->opcode=LEA; curinstr->rs1=previnstr->rs1; curinstr->variant=OPRRM; if (curinstr->disp==0 && curinstr->rs1!=EBP ) { curinstr->csize=2; curinstr->rmbyte=0; } else if (-128<=curinstr->disp && curinstr->disp<=127 ) { curinstr->csize=3; curinstr->rmbyte=0x40; } else { curinstr->csize=6; curinstr->rmbyte=0x80; } curinstr->rref=256<rs1; if (optimtrace!=0 && peepreport!=0 ) { printf("\nPHOLE: Two instrs coalesced into LEA at %08X\n",(unsigned)curinstr); } BDeleteInstr(previnstr,0); changes++; goto NextPass; } } /* look for LEA followed by a move and retatget the LEA */ if (op==MOV && curinstr->variant==OPRR && previ!=0 && previnstr->rd==curinstr->rs1 && prevop==LEA) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rd))==0) { previnstr->rd=curinstr->rd; previnstr->rdef=curinstr->rdef; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: LEA amendend at %08X\n",(unsigned)previnstr); } BDeleteInstr(curinstr,0); changes++; goto NextPass; } } /* look for LEA without a SIB following and add and try to combine into a single LEA */ if (op==LEA && (curinstr->privprops&SIBREQD)==0 && previnstr!=NULL && previnstr->opcode==ADD && previnstr->variant==OPRR && previnstr->rd==curinstr->rs1) { BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&alive); if ((rlive&(257<rs1))==0 || curinstr->rs1==curinstr->rd) { curinstr->csize++; curinstr->rs2=previnstr->rs1; curinstr->privprops|=SIBREQD; curinstr->sib=0; curinstr->rref|=(257<rs2); BDeleteInstr(previnstr,0); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: LEA expanded at %08X\n",(unsigned)curinstr); } changes++; goto NextPass; } } /* Look for Integer results in the wrong register */ /* Happens in FORTRAN because of epilogues */ if (op==MOV && curinstr->variant==OPRR) { nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); if (nextinstr!=NULL && (nextinstr->props&WRAPPERINSTR)!=0) { rref=curinstr->rref; rdef=curinstr->rdef; previnstr=BMachineInstr(curinstr->previnstr,PREVCHAIN); while (previnstr!=NULL) { if (previnstr->rdef==curinstr->rref) { previnstr->rd=curinstr->rd; previnstr->rdef=curinstr->rdef; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: PEEPHOLED INT RESULT GENERATOR AT %08X\n",(unsigned)previnstr); printf(" : %08X\n",(unsigned)curinstr); } BDeleteInstr(curinstr,0); BUpdateLiveRegs(frag); goto NextPass; } if ((((previnstr->rref|previnstr->rdef)&(rref|rdef)))!=0) break ; previnstr=BMachineInstr(previnstr->previnstr,PREVCHAIN); } } /**/ /* carry on from MOV substituting source for dest as long as it is safe */ /* this reduces dependencies and give scheduler more scope. However if */ /* we are assigning to a RegVar (including temporary ones allocated */ /* by loopopt) substitute dest for source since the regvar can be assumed */ /* to have a long life. */ /**/ anychanged=0; rd=curinstr->rd; rs1=curinstr->rs1; if (rd==rs1) { BDeleteInstr(curinstr,0); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Silly move deleted at %08X\n",(unsigned)curinstr); } goto NextPass; } if ((curinstr->privprops®VARASSN)!=0) { rd=rs1; rs1=curinstr->rd; } #if (OutputASSEMBLER==Negative) /* ESP may require changes in csize which are messy so only do this if ASSEMBLER can handle the problem */ if (rd==ESP || rs1==ESP) goto NextPass; #endif rref=1<rref&rdef)!=0) /*%and nextinstr_rdef&(anyref)=0*/{ changed=0; if (nextinstr->rd==rd && (nextinstr->rdef&anyref)==0 && nextinstr->variant!=OPONLY && nextinstr->variant!=OPMEM && nextinstr->variant!=OPMEMLIT && ((nextinstr->privprops&BYTEREGOP)==0 || rs1<=3)) { /* byte loads and stores can only use first 4 regs */ nextinstr->rd=rs1; changed=1; } if (nextinstr->rs1==rd && nextinstr->variant!=OPONLY && nextinstr->variant!=OPR && nextinstr->variant!=OPPLUSR && ((nextinstr->rdef&nextinstr->rref)==0 || nextinstr->opcode==LEA) && ((nextinstr->privprops&BYTEREGOP)==0 || rs1<=3)) { nextinstr->rs1=rs1; if (rs1==EBP && ((unsigned)nextinstr->rmbyte>>6)==0 && nextinstr->disp==0) { nextinstr->csize++; nextinstr->rmbyte|=0x40; } changed=1; } if (nextinstr->rs2==rd && (nextinstr->privprops&SIBREQD)!=0) { nextinstr->rs2=rs1; changed=1; } if (changed!=0) { if (((unsigned)nextinstr->rref>>8)!=0) nextinstr->rref=(nextinstr->rref&(~rdef))|(rref<<8); else nextinstr->rref=(nextinstr->rref&(~rdef))|rref; anychanged=1; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: PEEPHOLED INT RESULT USER AT %08X\n",(unsigned)nextinstr); } } } /**/ /* Check we have not set up a no-op */ /**/ if (nextinstr->rd==nextinstr->rs1 && nextinstr->opcode==MOV && nextinstr->variant==OPRR) { BDeleteInstr(nextinstr,0); anychanged=1; } if (((nextinstr->rdef&anyref))!=0 || (nextinstr->props&ANYCALL)!=0) break ; nextinstr=followinginstr2(&fragaddr,nextinstr->nextinstr,1); } if (anychanged!=0) { BUpdateLiveRegs(frag); if ((int)frag!=fragaddr) BUpdateLiveRegs((struct fragfmt*)fragaddr); changes++; } } /**/ /**/ NextPass: FirstPass=0; previnstr=curinstr; curi=(int)BMachineInstr(curinstr->nextinstr,NEXTCHAIN); } if (optimtrace!=0 && peepreport!=0 && changes>0) { curinstr=frag->firstinstr; printf("\nFragment before repeat of cleanup\n"); while (curinstr!=0) { p5printinstr(curinstr); curinstr=curinstr->nextinstr; } } if (changes>0) goto again; } /* Cleanupfrag*/ /***/ static void CombineLitOps(struct fragfmt *frag) { /****************************************************************************/ /** Search frag for sequences of ADD instructions with literal operands, **/ /** the same destination register and no intervening uses except in these **/ /** instructions. [ A bit unlikely, except in benchmarks. ] **/ /** Combine them into one instruction, assuming the combined literal still **/ /** fits into 16 bits. **/ /****************************************************************************/ int r,rd,newval,op,opp; struct instrfmt * uses [32]; /* 0 or addr of instr which is reg's next use */ struct instrfmt *instr,*use; for (r=0; r<=31; r++) { uses [r]=0; } instr=BMachineInstr(frag->lastinstr,PREVCHAIN); /* loop backwards through frag looking for ADD/SUB with literal opnd */ while (instr!=NULL) { op=instr->opcode; if ((op==INC || op==DEC) || ((op==ADD || op==SUB) && (instr->variant==OPRLIT || instr->variant==OPRSLIT))) { rd=instr->rd; if (uses [rd]!=NULL) { use=uses [rd]; opp=use->opcode; if (op==INC) {op=ADD; instr->u1.immval=1;} if (op==DEC) {op=SUB; instr->u1.immval=1;} if (opp==INC) {opp=ADD; use->u1.immval=1;} if (opp==DEC) {opp=SUB; use->u1.immval=1;} if (op==opp) newval=instr->u1.immval+use->u1.immval; if (op==SUB && opp==ADD) newval=-(use->u1.immval-instr->u1.immval); if (op==ADD && opp==SUB) newval=-(use->u1.immval-instr->u1.immval); if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Combining two litops:"); p5printinstr(instr); p5printinstr(use); } instr->privprops&=(~PARAMRECLAIM); instr->u1.immval=newval; instr->opcode=op; if ((-128<=newval)&&(newval<=127)) { instr->csize=3; instr->variant=OPRSLIT; instr->datasize=1; } else { instr->csize=6; instr->variant=OPRLIT; instr->datasize=4; } if (newval==1) { if (instr->opcode==ADD) instr->opcode=INC; else instr->opcode=DEC; instr->csize=1; instr->variant=OPPLUSR; instr->datasize=4; } if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: Combined litop="); if (newval==0) printf("ZERO"); else p5printinstr(instr); printf("\n"); } if (newval==0) BDeleteInstr(instr,0); BDeleteInstr(uses [rd],0); BUpdateLiveRegs(frag); } if (instr->rs1==rd) uses [rd]=instr; else { uses [rd]=NULL; uses [instr->rs1]=NULL; } } else { for (r=0; r<=7; r++) { if (((instr->rref|instr->rdef)&(257<props&JUMPINSTR)!=0) { for (r=0; r<=7; r++) { uses [r]=NULL; } } } instr=BMachineInstr(instr->previnstr,PREVCHAIN); } } /*CombineLitOps*/ /***/ static void RemoveLoads(struct fragfmt *frag) { /***************************************************************************/ /** Attempt to match load/store instructions with loads from same location**/ /** Such loads may be turned into copies in suitable circumstances. **/ /***************************************************************************/ struct fragfmt *curfrag; struct instrfmt *instr,*nextinstr,*nextiterinstr,*previnstr; int overlap,movenow,movethen,curfragaddr; int stlen,streg1,ldreg1,rch,rused,xch,xused,bregs,var; static const int NOTSIMPLE=(((ANYCALL|JUMPINSTR)|LOADINSTR)|STOREINSTR)|BRANCH; static const int MEMORYINSTR=LOADINSTR|STOREINSTR; /* work backwards through fragment looking for memory instructions */ nextiterinstr=BMachineInstr(frag->lastinstr,PREVCHAIN); while (nextiterinstr!=NULL) { instr=nextiterinstr; nextiterinstr=BMachineInstr(instr->previnstr,PREVCHAIN) /* for next iteration */; if ((instr->props&MEMORYINSTR)==0) goto outerrepeat; if (optimtrace!=0 && peepreport!=0 && miscreport!=0) { printf("PHOLE: Testing"); p5printinstr(instr); } /* have found a memory instruction: work forwards from here seeing */ /* whether there is a load from the same location */ nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if ((instr->rdef&instr->rref)!=0) continue ; /* avoid things like ld [%l2 +%l3], %l3 */ if (instr->group!=INTLOAD && instr->group!=INTSTORE) continue ; rused=0; rch=0; /* record if any regs change */ xused=0; xch=0; bregs=1<rs1; if ((instr->privprops&SIBREQD)!=0) bregs|=1<rs2; /* mask of baseregs*/ curfrag=frag; while (nextinstr!=NULL) { if ((optimtrace!=0)&&(peepreport!=0)&&(miscreport!=0)) { printf("\nPHOLE: checking against"); p5printinstr(nextinstr); } /* first update regs used or changed by instruction */ if ((rch&bregs)!=0) break ; /* base reg updated give up */ if ((nextinstr->props&NOTSIMPLE)==0) goto CheckNext; /* check for subsequent stores to same location */ if ((nextinstr->props&STOREINSTR)!=0) { overlap=p5memoryoverlap(instr,nextinstr,1); if (overlap==MONO) goto CheckNext; /* no memory overlap: can continue looking for loads */ break ; /* destinations may overlap: assume memory changed */ } /* check for load now : can possibly modify or remove the load */ if ((nextinstr->props&LOADINSTR)!=0) /*%and nextinstr_group=INT LOAD*/{ overlap=p5memoryoverlap(instr,nextinstr,1); if (overlap==MOMAYBE) break ; /* don't know what's happening: assume the worst */ if (overlap==MONO) goto CheckNext; /* if no register interference then carry on looking */ /* memory interference: see whether load can be modified */ if (overlap!=MOEXACT || instr->datasize!=nextinstr->datasize) break ; /* load is of different size : too complicated */ stlen=instr->datasize; movenow=0; movethen=0; /**/ /* move now #0 if the current load can be replaced by a move */ /* move then # 0 if a move can be inserted after the load-store */ /* held in instr */ /* there are intermediate case where a move can be inserted (one day ?) */ /**/ if (stlen>=4 || (instr->opcode==nextinstr->opcode && (instr->opcode==LBU || instr->opcode==LB || instr->opcode==LHU|| instr->opcode==LH))) { /* deal with whole-register cases */ if (nextinstr->group!=INTLOAD) /* add to reg etc*/{ if ((((1<rd)&rch))==0 && (nextinstr->privprops&VOLATILELOC)==0 && nextinstr->variant==OPRRM) { nextinstr->variant=OPRR; nextinstr->csize=1+ARCH(opcodesize(nextinstr->opcode,OPRR)); nextinstr->area=0; nextinstr->u0.offset=0; nextinstr->rs1=instr->rd; nextinstr->rref=(1<rd)|(1<rs1); nextinstr->privprops&=(~FIXEDINSTR); nextinstr->props&=~LOADINSTR; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: using register copy:"); p5printinstr(nextinstr); printf("\n"); } BUpdateLiveRegs(curfrag); } } else if (instr->rd==nextinstr->rd) { if (instr->rd<=7 && ((1<rd)&rch)!=0) break ; /* reloading same register(s): delete the load instr */ /* since the old copy has not been updated */ if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: DELETING REDUNDANT LOAD INSTRUCTION:"); p5printinstr(nextinstr); printf("\n"); } BDeleteInstr(nextinstr,0); BUpdateLiveRegs(frag); if (frag!=curfrag) BUpdateLiveRegs(curfrag); nextinstr->rref=0; nextinstr->rdef=0; nextinstr->fref=0; nextinstr->fdef=0; goto CheckNext; } else { /* loading different registers: change load to copy */ streg1=instr->rd; ldreg1=nextinstr->rd; if (streg1<=7 && ((1<rout)==0)) movethen=1; if ((movenow|movethen)==0) break ; /* change load instruction to a register copy */ nextinstr->opcode=MOV; nextinstr->rd=ldreg1; nextinstr->rs1=streg1; nextinstr->rs2=0; nextinstr->u1.immval=0; nextinstr->datasize=0; nextinstr->rref=1<rdef=257<props=((nextinstr->props&(~LOADINSTR))|COPYINSTR)|DESTROYABLE; nextinstr->privprops=nextinstr->privprops&(~(SYMBOLICACCESS|FRAMEACCESS)); nextinstr->area=-1; nextinstr->variant=OPRR; nextinstr->csize=2; nextinstr->domain=0; nextinstr->group=instrgroup [nextinstr->opcode]; if (optimtrace!=0 && peepreport!=0) { printf("\n"); printf(bannerline); printf("\nPHOLE: CHANGING LOAD TO REGISTER COPY Of:"); phex((int)instr); p5printinstr(nextinstr); printf("\n"); printf("\n"); } rch|=nextinstr->rdef; rused|=nextinstr->rref; xch|=nextinstr->fdef; xused|=nextinstr->fref; /* if no register interference can carry on */ if ((movethen!=0)&&(movenow==0)) { previnstr=nextinstr->previnstr; BMoveAfter(curfrag,frag,nextinstr,instr); if (previnstr==NULL) previnstr=frag->lastinstr; nextinstr=previnstr; } BUpdateLiveRegs(frag); if (frag!=curfrag) BUpdateLiveRegs(curfrag); if (movethen!=0) break ; } } else { /* part-register cases */ /* maybe someday ... */ var=nextinstr->variant; if (nextinstr->opcode!=CMP && nextinstr->opcode!=TEST) break ; if (var!=OPMEMLIT && var!=OPRRM) break ; /**/ /* adjust instruction size */ /**/ if ((nextinstr->privprops&SIBREQD)!=0) nextinstr->csize=nextinstr->csize- 1; if (((nextinstr->privprops&FIXEDINSTR)!=0)||((nextinstr->disp<-128)||(nextinstr->disp>127))) { nextinstr->csize=nextinstr->csize-4; } else { if (nextinstr->disp!=0) nextinstr->csize=nextinstr->csize-1; } /**/ /* adjust variant and flags */ /**/ if (var==OPMEMLIT) { nextinstr->variant=OPRLIT; if ((nextinstr->opcode==CMP)&&((-128<=nextinstr->u1.immval)&&(nextinstr->u1.immval<=127))) nextinstr ->variant=OPRSLIT; nextinstr->rd=instr->rd; nextinstr->rref=1<rd; } else { nextinstr->variant=OPRR; nextinstr->rs1=instr->rd; nextinstr->rref=(1<rd)|(1<rs1); } nextinstr->privprops=nextinstr->privprops&(~(FIXEDINSTR|SIBREQD)); nextinstr->props=nextinstr->props&(~LOADINSTR); nextinstr->area=-1; nextinstr->disp=-1; if (optimtrace!=0 && peepreport!=0) { printf("\nPHOLE: CHANGING LOAD TO USE REGISTER at:"); p5printinstr(nextinstr); printf("\n"); } } } if ((nextinstr==NULL)||((nextinstr->props&(ANYCALL|JUMPINSTR))!=0)) break ; /* can't look beyon d calls, etc */ CheckNext: rch|=nextinstr->rdef; rused|=nextinstr->rref; xch|=nextinstr->fdef; xused|=nextinstr->fref; nextinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); if (nextinstr==NULL) { curfragaddr=(int)curfrag; nextinstr=followinginstr2(&curfragaddr,nextinstr,1) /* into next frag if safe)*/; if (nextinstr!=NULL) curfrag=(struct fragfmt*)(curfragaddr); } } outerrepeat:; } } /* RemoveLoads */ /***/ static void RemoveDeadStores(struct fragfmt *frag) { /***************************************************************************/ /** Attempt to remove stores into stack frames on exit paths **/ /***************************************************************************/ struct instrfmt *instr,*nextinstr,*followinstr; int overlap; /* work backwards through fragment looking for memory instructions */ followinstr=BMachineInstr(frag->lastinstr,PREVCHAIN); while (followinstr!=NULL) { instr=followinstr; followinstr=BMachineInstr(instr->previnstr,PREVCHAIN) /* for next iteration */; if ((instr->props&ANYCALL)!=0) return; if ((instr->props&STOREINSTR)!=0 && (instr->props&WRAPPERINSTR)==0 && instr->area==STACK && !instr->cdef) { if (optimtrace!=0 && peepreport!=0 && miscreport!=0) { printf("PHOLE: Deadstore Testing "); p5printinstr(instr); } /* have found a store instruction: work forwards from here seeing */ /* whether there is a load from the same location */ nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); while (nextinstr!=NULL) { if ((nextinstr->props&LOADINSTR)==0) goto CheckNext; if (optimtrace!=0 && peepreport!=0 && miscreport!=0) { printf("\nPHOLE: checking against "); p5printinstr(nextinstr); } /* check for load now : can possibly modify or remove the load */ overlap=p5memoryoverlap(instr,nextinstr,0); if (overlap==MONO) { /* if no register interference then carry on looking */ goto CheckNext; } if (optimtrace!=0 && peepreport!=0 && miscreport!=0) printf("\nPHOLE: memory interference %2d %2d", instr->datasize,nextinstr->datasize); goto NextStore; CheckNext: nextinstr=BMachineInstr(nextinstr->nextinstr,NEXTCHAIN); } /**/ /* Have checked all loads after "instr" no intereference found */ /* can delete the store */ /**/ if (optimtrace!=0 && peepreport!=0) printf("\nPHOLE: Dead store deleted %08X\n",(unsigned)instr); BDeleteInstr(instr,1); BUpdateLiveRegs(frag); } NextStore:; } } /* RemoveDeadStores */ /***/ /***/ static void Doubledefs(struct fragfmt *frag) { /*************************************************************************/ /** Looks for registers which are redfined without being used **/ /** If the first definition has no side effects (ie sets CC) then **/ /** delete the first definition. At the frag end delete all defns **/ /** of unused registers that are not 'live' in subsequent frags. **/ /*************************************************************************/ int rref,rdef,fref,fdef,cref,cdef; int anychanged; int changes,opcode,rd,rlive,flive,clive,dlive; struct instrfmt *curinstr,*nextinstr; static const int IPARAMREGS=1<firstinstr,NEXTCHAIN); while (nextinstr!=NULL) { curinstr=nextinstr; nextinstr=BMachineInstr(curinstr->nextinstr,NEXTCHAIN); opcode=curinstr->opcode; rdef=curinstr->rdef&0xFFFF; fdef=curinstr->fdef; rref=curinstr->rref&0xFFFF; fref=curinstr->fref; cdef=curinstr->cdef; cref=curinstr->cref; rd=curinstr->rd; if ((curinstr->props&ANYCALL)!=0) { /**/ /* a call with no delay slot effectively read any param regs and defines all volatile regs*/ /* A D-S is more compilcated but this is first called before D-S filling */ /**/ rref=IPARAMREGS|curinstr->rref; /* rdefs as defined in instruction */ /**/ /* The stackpointer will be reset so the value will not be lost even tho it is not*/ /* a callee save reg */ /* Hence amendmments to CALLEE SAVED REGS above */ /**/ fref=FPARAMREGS; fdef=~CALLEESAVEDFREGS; } /**/ /**/ /* step3 record any definitions for later reference avoiding delay slot instrs*/ /* which might not be executed and any instruction that defines more than a */ /* single register or regpair */ /**/ if ((curinstr->props&(ANYCALL|JUMPINSTR|FRAMEMODIFY|WRAPPERINSTR|STOREINSTR))!=0) continue ; if ((curinstr->props&DESTROYABLE)==0) continue ; if (fdef!=0) continue ; if (nextinstr!=NULL && ((nextinstr->rref&rdef)!=0 || (nextinstr->cref&cdef)!=0)) continue; BLiveSetAfterInstr(frag,curinstr,&rlive,&flive,&clive,&dlive); if ((rdef&rlive)==0 && (cdef&clive)==0 ) { if (optimtrace!=0 && peepreport!=0) printf("DDEFS: Instruction has no live oputs %08X\n",(unsigned)curinstr); /* FSTP is dead on returns but this leaves rubbish on stack!*/ if ((cdef&(1<nextfrag; nextinstr=BMachineInstr(frag->firstinstr,NEXTCHAIN); while (nextinstr!=NULL) { instr=nextinstr; nextinstr=BMachineInstr(instr->nextinstr,NEXTCHAIN); if ((instr->props&WRAPPERINSTR)==0 && (instr->props&(LOADINSTR|STOREINSTR))!=0 && instr->area==STACK) { offset=(unsigned)((-instr->u0.offset)+3)>>2; if (1<=offset && offset<=256) { j=instr->datasize; do { if ((instr->props&LOADINSTR)!=0 && instr->opcode!=POP) StackSTflags [offset]|=2; if ((instr->props&STOREINSTR)!=0 && instr->opcode!=PUSH) StackSTflags[offset]|=1; offset--; j-=4; } while (j>0) ; } } } } for (i=1; i<=256; i++) { if (StackSTflags [i]==1) goto part2; } return; /* no deletable stores */ part2: nextfrag=procfrag; while (nextfrag!=NULL) { frag=nextfrag; nextfrag=frag->nextfrag; nextinstr=frag->firstinstr; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; if ((instr->props&WRAPPERINSTR)==0 && (instr->props&STOREINSTR)!=0 && instr->area==STACK) { offset=(unsigned)((-instr->u0.offset)+3)>>2; if (1<=offset && offset<=256) { if (StackSTflags [offset]==1 && (instr->datasize<=4 || (instr->datasize<=8 && StackSTflags[offset-1]==1)) && instr->opcode!=FISTP && instr->opcode!=FSTPm && instr->opcode!=FSTPd) { if (optimtrace!=0 && peepreport!=0) { printf("PHOLE: Store deleted after rechk %08X\n",(unsigned)instr); } BDeleteInstr(instr,1); BUpdateLiveRegs(frag); } } } } } } /* rechech stack stores */ /***/ static void makepreamble(struct fragfmt *prevfrag,int preptr,struct instrfmt *preinstrs) { /*************************************************************************/ /** Add the instrs in the preamble to the back of prevfrag **/ /*************************************************************************/ struct fragfmt *tempfrag,*firstfrag,*i,*j; struct instrfmt *newinstr; int k; /**/ /* if the preamble is to go into an empty frag there is red tape work */ /**/ firstfrag=prevfrag->final; if (firstfrag!=prevfrag) { prevfrag->props=(prevfrag->props|FALLENINTO)|FALLSTHROUGH; prevfrag->uses=1; prevfrag->rin=firstfrag->rin; prevfrag->rout=firstfrag->rout; prevfrag->fin=firstfrag->fin; prevfrag->fout=firstfrag->fout; prevfrag->cin=firstfrag->cin; prevfrag->cout=firstfrag->cout; i=prevfrag; j=prevfrag->final; prevfrag->final=i; tempfrag=prevfrag; do { tempfrag=tempfrag->prevfrag; if (tempfrag==NULL) break ; if (tempfrag->final==j) tempfrag->final=i; } while (1) /* FOR EVER */; } for (k=0; k<= /* At endof frag */preptr-1; k++) { newinstr=BInsertInstr(prevfrag,NULL); BCopyInstr(&preinstrs [k],newinstr); } } /*makepreamble */ static void makepostamble(struct fragfmt *frag,int postptr,struct instrfmt *postinstrs) { /*************************************************************************/ /** Add the instrs in the postamble to the front of frag **/ /*************************************************************************/ int k; struct fragfmt *final; struct instrfmt *newinstr; final=frag->final; if (final!=frag) { frag->props=(final->props&FALLENINTO)|FALLSTHROUGH; frag->final->props|=FALLENINTO; frag->uses=1; frag->rin=final->rin; frag->rout=final->rout; frag->fin=final->fin; frag->fout=final->fout; frag->cin=final->cin; frag->cout=final->cout; frag->final=frag; } for (k=0; k<=postptr-1; k++) /* at front of frag*/ { newinstr=BInsertInstr(frag,frag->firstinstr); BCopyInstr(&postinstrs [k],newinstr); } } /* makepostamble*/ /***/ static void makepush(struct instrfmt *instr,int rd) { /*************************************************************************/ /** generate a PUSH for the preamble **/ /*************************************************************************/ memset(instr,0,sizeof( struct instrfmt)); instr->opcode=PUSH; instr->rd=rd; instr->group=SPECIAL; instr->datasize=4; instr->csize=1; instr->props=DESTROYABLE|STOREINSTR; instr->variant=OPPLUSR; instr->rref=(1<rdef=257<opcode=FSTPd; instr->rd=LASTFREG; instr->rs1=LASTFREG; instr->group=FPSTORE; instr->csize=2; /* instr->props=DESTROYABLE; */ /* FP stack must always be tidied */ instr->variant=FOPPLUSR; instr->fref=(1<fdef=instr->fref; instr->cref=(1<cdef=1<opcode=POP; instr->rd=rd; instr->group=SPECIAL; instr->datasize=4; instr->csize=1; instr->props=DESTROYABLE|LOADINSTR; instr->variant=OPPLUSR; instr->rref=1<rdef=(257<group!=INTLOAD) continue ; /* First one must be a load */ if (instr->rref&defmask) continue; count [i]=1; for (j=i+1; j<=nloads; j++) { nextinstr=(struct instrfmt*)(loadads [j]); res=p5memoryoverlap(instr,nextinstr,0); if (res==MONO) continue ; if ((res==MOEXACT)&&((nextinstr->fref|nextinstr->fdef)==0)) { count [i]=count [i]+1; lmasks [i]=lmasks [i]|(1<0)&&((instr->privprops&BYTEREGOP)==0)&&(instr->datasize!=1)&&((nextinstr ->fref|nextinstr->fdef)==0)) { count [i]=count [i]+1; smasks [i]=smasks [i]|(1<k) { j=i; k=count [i]; } } if (k<=1) { *lmask=0; *smask=0; return; } *lmask=lmasks [j]|(1<variant; group=instr->group; opcode=instr->opcode; switch (variant) { default: Mabort(707); case OPRRM: /* rem& memeory to reg-reg */ if (group==INTLOAD) { instr->variant=OPRR; instr->csize=2; instr->group=INTOPSu; instr->rs1=xtrareg; instr->opcode=MOV; instr->props=(instr->props&(~LOADINSTR))|COPYINSTR; instr->privprops&=~(BYTEREGOP|FIXEDINSTR); instr->datasize=4; instr->u2.prefixes [osprefix]=0; instr->rmbyte=0xC0; instr->rref=1<variant=OPRR; instr->csize=2; if (opcode==IMUL) instr->csize=3; instr->rs1=xtrareg; instr->props&=~LOADINSTR; instr->privprops&=~FIXEDINSTR; instr->rmbyte=0xC0; instr->rref=(1<rd); instr->datasize=4; instr->u2.prefixes [osprefix]=0; return; } Mabort(727); case OPRMR: /* Store instrs and add to stores */ if (group==INTSTORE) { instr->variant=OPRR; instr->csize=2; instr->group=INTOPSu; instr->opcode=MOV; instr->props=(instr->props&(~STOREINSTR))|COPYINSTR; instr->privprops=(instr->privprops&(~FIXEDINSTR))|REGVARASSN; instr->rmbyte=0xC0; instr->rs1=instr->rd; instr->rd=xtrareg; instr->rdef=257<rref=1<rs1; instr->datasize=4; instr->u2.prefixes [osprefix]=0; return; } Mabort(737); case OPMEMLIT: /* move lit to store or cmp lit to store */ instr->rmbyte=0xC0; if (instr->opcode==MOV) { instr->csize=6; instr->variant=OPRLIT; } else { if ((-128<=instr->u1.immval)&&(instr->u1.immval<=127)) { instr->csize=3; instr->variant=OPRSLIT; } else { instr->csize=6; instr->variant=OPRLIT; } } instr->rd=xtrareg; instr->props&=~(LOADINSTR|STOREINSTR); instr->privprops=(instr->privprops&(~FIXEDINSTR))|REGVARASSN; if (instr->opcode==MOV) { instr->rdef=257<rd; instr->rref=0; } else { instr->rdef=0; instr->rref=1<rd; } instr->u2.prefixes [osprefix]=0; instr->datasize=4; return; } /* end of case */ } /*update instr*/ /***/ static void arrangeloads(int *loadads,int *storeads,int lmask,int smask,int xtrareg,struct instrfmt *preinstrs,struct instrfmt *postinstrs,int *preptr,int *postptr) { /*************************************************************************/ /** Arrange to Load a variable into xtrareg and change all itsreferences**/ /** from r-m to rform. The load goes into the preamble and it may need **/ /** a corresponding store int the postamble **/ /*************************************************************************/ int i; struct instrfmt *ldinstr,*instr,*stinstr; /**/ /* the first instruction must currently be a load */ /**/ i=1; while (((1<rd=xtrareg; instr->rdef=257<rref=stinstr->rref|(1<rdef=0; stinstr->variant=OPRMR; stinstr->props=(stinstr->props&(~LOADINSTR))|STOREINSTR; if (stinstr->datasize==1) stinstr->opcode=SB; else if (stinstr->datasize==2) stinstr->opcode=SH; else stinstr->opcode=ST; stinstr->csize=(instr->csize-ARCH(opcodesize(instr->opcode,instr->variant)))+ARCH(opcodesize(stinstr->opcode ,stinstr->variant)); } for (i=1; i<=31; i++) { if (((1<procfrag; while (frag!=NULL) { if (labx==frag->labref) n++; frag=frag->nextfrag; } if (optimtrace!=0 && peepreport!=0) { printf("Countjumps reports %2d to lab with index %5d\n",n,labx); } return n; } /***/ static int unsafeoverlaps(struct instrfmt *instr,int nstores,int *storeads) { static const int MAXSTORES=31; int i,res; if (nstores>=MAXSTORES) return MOYES; for (i=1; i<=nstores; i++) { res=p5memoryoverlap(instr,(struct instrfmt *)(storeads [i]),1); if (res!=MONO) return res; } return MONO; } /***/ static void loopopt(struct fragfmt *firstfrag,struct fragfmt *lastfrag,int exitlabx) { /*************************************************************************/ /** Attempts to move loop invariant register loads to preamble. **/ /** Can allow for a single conditional path in parts of the loop. **/ /*************************************************************************/ struct instrfmt *curinstr,*nextinstr,*newinstr; struct fragfmt *frag,*prevfrag,*tempfrag,*sideexitfrag,*postfrag; int usemask,usebeforedef,validdef,defmask,fdefmask,storeseen,nfpops,i,k,deadregs,moved,op,curi,nexti ,singledef,condpath,bit,nstores,nloads,validaccess,xtrareg,liveexitr,dumpableregs,lmask,smask; struct lrecfmt *lrec; #define MAXHOISTS 30 struct instrfmt preinstrs [MAXHOISTS+1]; struct instrfmt postinstrs [MAXHOISTS+1]; int preptr,postptr,postvalid; static const int maxstores=31; static const int usableregs=((((((1<nextfrag; postfrag=postfrag->final; if (exitlabx!=0) { lrec=BLabRecAd(exitlabx); sideexitfrag=lrec->frag; sideexitfrag=sideexitfrag->final; if (!((sideexitfrag->uses==1)||((sideexitfrag->uses==2)&&((int)postfrag==(int)sideexitfrag)&&((sideexitfrag ->props&FALLENINTO)!=0)))) postvalid=0; /*can not add a postamble*/ } if (!(((postfrag->props&FALLENINTO)==0)||(((postfrag->props&FALLENINTO)!=0)&&(postfrag->uses==1))||(((postfrag ->props&FALLENINTO)!=0)&&(postfrag->uses==2)&&(exitlabx!=0)&&((int)postfrag==(int)sideexitfrag)))) postvalid=0; /**/ /* there is one more possibility. There might be an empty frag right after */ /* the final frag */ /**/ if ((postvalid==0)&&(exitlabx!=0)&&(lrec->frag==lastfrag->nextfrag)) { tempfrag=lrec->frag; if ((tempfrag->final!=tempfrag)&&(countjumpstolab(exitlabx)==1)) { sideexitfrag=tempfrag; postfrag=tempfrag; postvalid=1; } } /* If there is no exit label we can establish a valid postamble frag by brute force */ if (postvalid==0 && exitlabx==0) { tempfrag=BObtainFrag(); tempfrag->prevfrag=lastfrag->final; tempfrag->nextfrag=lastfrag->final->nextfrag; tempfrag->nextfrag->prevfrag=tempfrag; tempfrag->prevfrag->nextfrag=tempfrag; tempfrag->final=tempfrag->nextfrag->final; tempfrag->rout=tempfrag->final->rout; tempfrag->fout=tempfrag->final->fout; tempfrag->cout=tempfrag->final->cout; tempfrag->rin=tempfrag->rout; tempfrag->fin=tempfrag->fout; tempfrag->cin=tempfrag->cout; postfrag=tempfrag; postvalid=1; } prevfrag=firstfrag->prevfrag; frag=firstfrag; do { curi=(int)BMachineInstr(frag->firstinstr,NEXTCHAIN); while (curi!=0) { curinstr=(struct instrfmt*)(curi); nexti=(int)BMachineInstr(curinstr->nextinstr,NEXTCHAIN); nextinstr=(struct instrfmt*)(nexti); op=curinstr->opcode; if ((curinstr->props&ANYCALL)!=0) return; if ((curinstr->props&STOREINSTR)!=0) { if (curinstr->area==-1) bit=-1; else if (curinstr->area>30) bit=0x80000000; else bit=1<area; storeseen|=bit; /* storeseen a mask of areas */ if (nstores>=maxstores) return; nstores++; storeads [nstores]=curi; } if ((curinstr->props&LOADINSTR)!=0) { if (nloads>=maxstores) return; nloads++; loadads [nloads]=curi; } if ((curinstr->fref|curinstr->fdef)!=0) { nfpops++; if (nfpops<=maxstores) fpops [nfpops]=curi; } usemask|=curinstr->rref; usebeforedef|=curinstr->rref&(~defmask); defmask|=curinstr->rdef; fdefmask|=curinstr->fdef; if (PI->privprops&NOTLEAFRT) { i=~(257<rref&i)|curinstr->fref|(curinstr->cref&(1<rdef; if (curinstr!=0) { for (i=0; i<=7; i++) { if (((1<rdef)!=0) defs [i]=defs [i]+1; } } curi=nexti; } if ((int)frag==(int)lastfrag) break ; frag=frag->nextfrag; } while (1) /* FOR EVER */; storeads [0]=nstores; loadads [0]=nloads; /**/ /* If there are are any registers not defined in the loop and 'dead' on */ /* loop entry these can be used. Attempt to use them by looking for */ /* register & store ops loading the stoire item and substituting a RR op */ /**/ k=~((firstfrag->rin&255)|((unsigned)firstfrag->rin>>8)); deadregs=(((~defmask)&255)&k)&usableregs; /* until there is a revise wrappers routine we must not use unsaved NONVOLS */ /**/ deadregs&=wrappediregs|(~((1<>8; dumpableregs=(dumpableregs|(1<firstinstr,NEXTCHAIN); while (curi!=0) { curinstr=(struct instrfmt*)(curi); nexti=(int)BMachineInstr(curinstr->nextinstr,NEXTCHAIN); nextinstr=(struct instrfmt*)(nexti); op=curinstr->opcode; if (((INTOPS<=curinstr->group)&&(curinstr->group<=INTOPSu))&&((curinstr->props&LOADINSTR)!=0)&&(curinstr ->datasize==4)&&(curinstr->variant==OPRRM)&&((curinstr->privprops&SIBREQD)==0)&&(unsafeoverlaps(curinstr ,nstores,&storeads[0] )==MONO)) { if (((curinstr->privprops&FIXEDINSTR)==0)&&(((1<rs1)&defmask)!=0)) { curi=nexti; continue ; } newinstr=BInsertInstr(frag,curinstr); newinstr->opcode=LW; newinstr->group=INTLOAD; newinstr->rd=xtrareg; newinstr->rs1=curinstr->rs1; newinstr->props=LOADINSTR|DESTROYABLE; newinstr->rdef=257<rref=curinstr->rref&(~(257<rd)); newinstr->privprops=curinstr->privprops|DEADREGUTIL; newinstr->disp=curinstr->disp; newinstr->datasize=4; newinstr->area=curinstr->area; newinstr->domain=curinstr->domain; newinstr->u0.offset=curinstr->u0.offset; newinstr->rmbyte=curinstr->rmbyte; newinstr->variant=curinstr->variant; newinstr->csize=(curinstr->csize-ARCH(opcodesize(curinstr->opcode,curinstr->variant)))+ ARCH(opcodesize(LW ,OPRRM)); curinstr->variant=OPRR; curinstr->rs1=xtrareg; curinstr->rref=(1<rd)|(1<props=curinstr->props&(~LOADINSTR); curinstr->privprops=curinstr->privprops&(~FIXEDINSTR); curinstr->area=-1; curinstr->domain=0; curinstr->csize=1+ARCH(opcodesize(curinstr->opcode,OPRR)); curinstr->rmbyte=0xC0; frag->rdef=frag->rdef|(257<ruse=frag->ruse|(1<nextfrag; } while (1) /* FOR EVER */; } /**/ /* any register defined only once and which is not used before defn */ /* and is valid (loop invariant) can be moved. If there are no stores */ /* in the loop any register defined once only in terms of regs */ /* not defined in the loop can be moved (mainly loads from frame or GLA) */ /**/ singledef=0; for (i=0; i<=7; i++) { if (defs [i]==1) singledef|=1<rin; if ((lastfrag->props&FALLSTHROUGH)!=0) { liveexitr|=postfrag->rin; } liveexitr=(liveexitr&255)|((unsigned)liveexitr>>8); if (optimtrace!=0 && peepreport!=0) { printf("\n"); printf("PHOLE: Loop optimised "); phex((int)firstfrag); printf(" "); phex(singledef); printf(" "); phex(usebeforedef); printf(" "); phex(usemask); printf(" "); phex(defmask); printf(" "); phex(validdef); printf(" "); phex(storeseen); phex(liveexitr); printf("\n"); for (i=0; i<=7; i++) printf("%3d",defs [i]); printf("\n"); } if (singledef==0) goto repeep; moved=0; condpath=0; frag=firstfrag; do { curi=(int)BMachineInstr(frag->firstinstr,NEXTCHAIN); while (curi!=0) { curinstr=(struct instrfmt*)(curi); nexti=(int)BMachineInstr(curinstr->nextinstr,NEXTCHAIN); nextinstr=(struct instrfmt*)(nexti); op=curinstr->opcode; if ((condpath!=0)&&((curinstr->rdef&(liveexitr|firstfrag->rin))!=0)) { curi=nexti; continue ; } /* Care needed in condpath re regvars*/ if (optimtrace!=0 && peepreport!=0) { printf("\n"); printf("LOOP:Testing"); phex(curi); printf(" "); phex(curinstr->cdef); printf(" "); phex(curinstr->rdef); } if (((curinstr->cdef&(1<cref&(1<fref))==0)&&((curinstr ->rdef&255)==(1<rd))&&(((curinstr->rdef&255)&singledef)==(curinstr->rdef&255))) { if (curinstr->area==-1) bit=-1; else if (curinstr->area>30) bit=0x80000000; else bit=1<area; if ((curinstr->props&LOADINSTR)!=0) { validaccess=0; if (((storeseen&bit)==0)||(unsafeoverlaps(curinstr,nstores,&storeads[0] )==MONO)) { validaccess=1; } } else { validaccess=1; } if (optimtrace!=0 && peepreport!=0) { if (validaccess!=0) printf("V"); else printf("NV"); printf("\n"); } if ((validaccess!=0)&&((((curinstr->rdef&validdef)==curinstr->rdef)&&(curinstr->rdef!=0))&&((curinstr ->rref&defmask)==0))) { if (optimtrace!=0 && peepreport!=0) { printf("LOOP: Loop HOIST "); p5printinstr((struct instrfmt *)curi); printf("\n"); } curinstr->issueprops=curinstr->issueprops|TPHOISTED; BCopyInstr(curinstr,&preinstrs [preptr]); BDeleteInstr((struct instrfmt*)curi,0); preptr++; moved++; } } curi=nexti; } if ((int)frag==(int)lastfrag) break ; if (frag->labref!=0) condpath=1; frag=frag->nextfrag; if (frag->labindex!=0) condpath=0; /* end of conditional path */ } while (1) /* FOR EVER */; /**/ /* now re peephole any blocks which have had instructions moved into them */ /**/ repeep: if (moved>0) { if (preptr>0) { /**/ /* if prevfrag end with a jump a new frag must be obtained and linked in */ /* otherwise the delat slot filling is wrecked */ /**/ if (prevfrag->labref!=0) { frag=BObtainFrag(); frag->prevfrag=prevfrag; frag->nextfrag=firstfrag; frag->final=firstfrag; /* In case frag stays empty */ prevfrag->nextfrag=frag; firstfrag->prevfrag=frag; prevfrag=frag; } makepreamble(prevfrag,preptr,&preinstrs[0] ); } if (postptr>0) { if (exitlabx!=0) makepostamble(sideexitfrag,postptr,&postinstrs[0] ); if (((lastfrag->props&FALLSTHROUGH)!=0)&&((exitlabx==0)||((int)postfrag!=(int)sideexitfrag))) makepostamble (postfrag,postptr,&postinstrs[0] ); } reintegrateregmasks(PI->procfrag); BReconvergeLiveness(); RemoveLoads(prevfrag); Cleanupfrag(prevfrag,0); frag=firstfrag; do { RemoveLoads(frag); Cleanupfrag(frag,0); Doubledefs(frag); if ((int)frag==(int)lastfrag) break ; frag=frag->nextfrag; } while (1) /* FOR EVER */; /**/ /* if anything has moved out it is possible that some operations that */ /* appeared to depend on loop variables no longer do so. Notlikely */ /* but worth another shot */ /**/ if (optimtrace!=0 && peepreport!=0) { BPrintFrags("Before making a further attempt at loop"); } goto again; } /**/ /* can we do any fp instr hoisting */ /**/ curinstr=(struct instrfmt*)(fpops [1]); /* printstring("Fop checks"); write(firstfrag_fin,2); write(nfpops,2) */ /* write(postvalid,2); write(curinstr_opcode,2); newline */ if ((firstfrag->fin!=0)||(nfpops<2)||(postvalid==0)||(nfpops>maxstores)||((fdefmask&(1<<8))!=0)) return; /*Fstk full*/ if (((curinstr->rref&defmask)==0)&&((curinstr->opcode==FLDZ)||(curinstr->opcode==FLD1)||((curinstr->group ==FPLOAD)&&(unsafeoverlaps(curinstr,nstores,&storeads[0] )==MONO)))) { for (k=2; k<=nfpops; k++) { nextinstr=(struct instrfmt*)(fpops [k]); if (!((nextinstr->fdef==0)&&((nextinstr->cdef&(1<opcode; if (((FISTP<=op)&&(op<=FSTPm))||(op==FCOMPm)||(op==FCOMPd)) { if (optimtrace!=0 && peepreport!=0) { printf("LOOP: Loop FHOIST "); p5printinstr(curinstr); printf("\n"); } curinstr->issueprops=curinstr->issueprops|TPHOISTED; BCopyInstr(curinstr,&preinstrs [preptr]); BDeleteInstr(curinstr,0); preptr++; moved++; if ((FISTP<=op)&&(op<=FSTPm)) { nextinstr->opcode=op+3; } else { if (op==FCOMPm) nextinstr->opcode=FCOMm; else nextinstr->opcode=FCOMd; } nextinstr->cdef=nextinstr->cdef&(~(1<rd=nextinstr->rd-1; if (nextinstr->variant==FOPPLUSR) nextinstr->rs1=nextinstr->rs1-1; nextinstr->fdef=(unsigned)nextinstr->fdef>>1; nextinstr->fref=(unsigned)nextinstr->fref>>1; if ((nextinstr->group==FPLOAD)&&(p5memoryoverlap(curinstr,nextinstr,0)==MOEXACT)) { nextinstr->opcode=FLDd; nextinstr->rs1=14; /* To give FLD %st(0) if only held value present */ nextinstr->variant=FOPPLUSR; nextinstr->fref=0x8000; nextinstr->props=nextinstr->props&(~LOADINSTR); nextinstr->csize=2; nextinstr->area=-1; } } makefpop(&postinstrs [postptr]); postptr++; goto repeep; #undef MAXHOISTS } static void looptest(struct fragfmt *frag) { /*************************************************************************/ /** Is this frag the start of a single entry loop ? **/ /*************************************************************************/ int rtlab,cplab; struct fragfmt *lfrag; if (!((frag->labindex>0)&&(frag->uses==2)&&((frag->props&FALLENINTO)!=0)&&(frag->switchtab==0))) return; rtlab=frag->labindex; if (frag->labref==rtlab) { loopopt(frag,frag,0); return; } lfrag=frag; cplab=0; while ((int)lfrag!=0) { if ((lfrag->labindex>0)&&(lfrag->labindex!=rtlab)) { if (!((lfrag->labindex==cplab)&&((lfrag->uses==1)||((lfrag->uses==2)&&((lfrag->props&FALLENINTO)!=0) )))) return; cplab=0; } if (lfrag->labref==rtlab) { loopopt(frag,lfrag,cplab); return; } if (lfrag->labref!=0) { if (cplab!=0) return; cplab=lfrag->labref; } lfrag=lfrag->nextfrag; } } /***/ static void integratecontexts(struct fragfmt *procfrag) { /*************************************************************************/ /** Add info from set contexts into instructions **/ /*************************************************************************/ struct fragfmt *frag,*nextfrag; struct instrfmt *instr,*nextinstr; int noalias,unroll; noalias=0; unroll=0; nextfrag=procfrag; while (nextfrag!=NULL) { frag=nextfrag; nextfrag=frag->nextfrag; nextinstr=frag->firstinstr; while (nextinstr!=NULL) { instr=nextinstr; nextinstr=instr->nextinstr; if (instr->group==CXTCHANGE) { if ((instr->u0.CXTset&CXTnoalias)!=0) noalias=NOINTERFERENCE; if ((instr->u1.CXTunset&CXTnoalias)!=0) noalias=0; if ((instr->u0.CXTset&CXToverlap)!=0) unroll=ADJACENTLOOPS; if ((instr->u1.CXTunset&CXToverlap)!=0) unroll=0; } else { instr->privprops|=noalias|unroll; } } } } /* integrate contexts */ /***/ void COptimiseProc() { /*******************************************************************************/ /** perform instruction scheduling and peephole optimisation within procedure **/ /*******************************************************************************/ struct fragfmt *frag,*destfrag; struct instrfmt *newinstr; struct lrecfmt *lrec; struct Swtabfmt *swtab; int nextfrag,foundcode,traceproc,tabval,i; hazardreport=globreport; if ((bbschedule|gschedule|leafopt|peepopt)==0) return; /* check that the procedure actually contains some real code */ foundcode=0; nextfrag=(int)PI->procfrag; while ((foundcode==0)&&(nextfrag!=0)) { frag=(struct fragfmt*)nextfrag; if ((int)BMachineInstr(frag->firstinstr,NEXTCHAIN)!=0) foundcode=1; nextfrag=(int)frag->nextfrag; } if (foundcode==0) return; integratecontexts(PI->procfrag); /* determine if tracing should be generated or not */ traceproc=BGetTraceProc(); if ((traceproc==0)||(traceproc==ProcNum)) { peepreport=globreport; } else { peepreport=0; } hazardreport=peepreport; /* find any dense switch tables and go through them and mark the */ /* possible destination fragments with a switch note */ nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; if (((frag->props&DSWITCHJUMPFRAG)!=0)&&(frag->nextfrag!=0)) { if (frag->switchtab!=0) { swtab=(struct Swtabfmt*)(frag->switchtab); for (i=0; i<=swtab->Entries-1; i++) { tabval=swtab->u0.R [i]; if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); destfrag=lrec->frag; destfrag->props=destfrag->props|DSWITCHCASEFRAG; newinstr=BInsertInstr(destfrag,destfrag->firstinstr); newinstr->group=DSWITCHNOTE; newinstr->u1.immval=(int)frag; } } } } nextfrag=(int)frag->nextfrag; } /* do fragment reorganisation */ BReorganiseFragments(); /* peephole optimise the procedure */ if ((diagnostics==0)&&(peepopt!=0)) /* RemoveLoads & stores can remove diags markers */{ nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; nextfrag=(int)frag->nextfrag; RemoveLoads(frag); } if ((optimtrace!=0)&&(peepreport!=0)&&(miscreport!=0)) { BPrintFrags("PHOLE: FRAGMENTS AFTER LOAD REMOVAL"); } nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; nextfrag=(int)frag->nextfrag; CombineLitOps(frag); DelRedunCompares(frag); if (((frag->props&RETURNFRAG)!=0)||((frag->props&(RETURNBRANCH|FALLSTHROUGH))==RETURNBRANCH)) RemoveDeadStores (frag); if (((frag->props&FALLSTHROUGH)!=0)&&(frag->labrefinstr!=0)) improvecompares(frag); if ((PI->privprops&NONVIRTUALFP)==0) improvereclaims(frag); ImproveCondEstablish(frag); Cleanupfrag(frag,0); } if ((optimtrace!=0)&&(peepreport!=0)&&(miscreport!=0)) { BPrintFrags("PHOLE: FRAGMENTS AFTER CLEANUP"); } nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; nextfrag=(int)frag->nextfrag; Doubledefs(frag); looptest(frag); } if ((optimtrace!=0)&&(peepreport!=0)&&(miscreport!=0)) { BPrintFrags("PHOLE: FRAGMENTS AFTER DOUBLE-DEF AND LOOP-OPT"); } nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; nextfrag=(int)frag->nextfrag; Cleanupfrag(frag,1); Doubledefs(frag); } if (((Language==FORTRAN)&&(LanguageVariant!=FORTRAN90))||(Language==CCOMP)||((PI->procprops&PP_NOINTERNAL)!=0) ) { if ((StackSTflags [0]&4)==0) RecheckStackStores(PI->procfrag); /* can only recheck if no pointers and no inner blks */ } } if (wrappertype==0) improveprocentry(); if ((wrappertype==1)&&((PI->privprops&FRAMEADDRTAKEN)==0)) improveleafentry(); /* now do the basic block or global scheduling of the procedure - this */ /* is not possible if the procedure has side entries */ #if BUILDWITHGS!=0 if (gschedule!=0 && (PI->procprops&PP_HASSIDEE)==0) { /* do the global scheduling on the procedure */ int oldsavesize=PI->saveareasize; BGlobalSchProc(ProcNum,PI->labadjust,UnkCntrlFlow); /* global scheduling may change the register save area size on */ /* the stack frame - in which case frame accessing instructions */ /* have to be modified appropriately */ if (((PI->privprops&(NESTEDCALLS|NONVIRTUALFP))==0)&&(PI->saveareasize!=oldsavesize)) { if (optimtrace!=0 && peepreport!=0) { printf("PHOLE: READJUSTING STACK FRAME ACCESSES BY "); printf("%1d",PI->saveareasize-oldsavesize); printf(" OLDSAVESIZE:"); printf("%1d",oldsavesize); printf(" NEWSAVESIZE:"); printf("%1d",PI->saveareasize); printf("\n"); } ReadjustStackAccesses(PI->saveareasize-oldsavesize); } /* recreate the register liveness after global scheduling - */ /* this involves recreating the psuedo instructions for dense */ /* switches */ nextfrag=(int)PI->procfrag; while (nextfrag!=0) { frag=(struct fragfmt*)nextfrag; if (((frag->props&DSWITCHJUMPFRAG)!=0)&&(frag->nextfrag!=0)) { if (frag->switchtab!=0) { swtab=(struct Swtabfmt*)(frag->switchtab); for (i=0; i<=swtab->Entries-1; i++) { tabval=swtab->u0.R [i]; if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); destfrag=lrec->frag; destfrag->props=destfrag->props|DSWITCHCASEFRAG; newinstr=BInsertInstr(destfrag,destfrag->firstinstr); newinstr->group=DSWITCHNOTE; newinstr->u1.immval=(int)frag; } } } } nextfrag=(int)frag->nextfrag; } BReconvergeLiveness(); } else #endif { if (bbschedule!=0) BBasicBlockSchProc(0); } /* do final titivation of code */ FinalTidy(PI->procfrag); /* print the final state of the fragments if required */ if (optimtrace!=0 && peepreport!=0) { BPrintFrags("PHOLE: FRAGMENTS AFTER FINAL TITIVATION"); } peepreport=globreport; } /*COptimiseProc*/ /***/ /***/ /********************************************************************************/ /** **/ /** BPROCS INTERFACE FUNCTIONS **/ /** **/ /********************************************************************************/ /***/ int p5malloc(int size) { /**************************************************************************/ /** This is EPC's interface to the system malloc **/ /**************************************************************************/ int addr; #if (OutputASSEMBLER==Positive) addr=(int)malloc(size); #else addr=getspace(size); #endif if (addr==0) { if ((((bbschedule|gschedule)|leafopt)|peepopt)!=0) { Mabort(50); } else { Mabort(39); } } return addr; } /*p5 Malloc*/ /***/ void p5free(void * addr) { /**************************************************************************/ /** This is EPC's interface to the system free **/ /**************************************************************************/ #if (OutputASSEMBLER==Positive) free((char *)addr); #else freespace((int)addr); #endif } /*p5 Free*/ /***/ static void outregtext(int reg,struct instrfmt *instr) { /*************************************************************************/ /** OUTPUTS THE REGISTER MNEMONIC FOR REG. There are 3 forms **/ /** depending on context so the whole instr record is passed in **/ /** to enable special cases to be detected **/ /*************************************************************************/ if (((instr->privprops&BYTEREGOP)!=0)||((instr->opcode==SB)&&(reg==instr->rd))) { printf("%s",bregtext [reg]); return; } if (((instr->opcode==LH)||(instr->opcode==LHU))&&(reg==instr->rd)) { printf("%s",regtext [reg]); return; } if ((instr->u2.prefixes [osprefix]==102)||(instr->datasize==2)) { printf("%s",wregtext [reg]); return; } printf("%s",regtext [reg]); } /* outregtext*/ /***/ /***/ static void printprops(struct instrfmt *instr,int diagassem,int ca) { struct lrecfmt *lrec; struct fragfmt *addrfrag; int iprops,prcomma,x; iprops=(instr->issueprops&(~TPGLOBSCHED))&0xFFFF; prcomma=0; if ((miscreport==0)&&(BIsAnnotated()==0)) return; if ((ca>0)&&((instr->props&(CONDITIONALBRANCH|PREDICTED))==(CONDITIONALBRANCH|PREDICTED))) { lrec=BLabRecAd(instr->u1.immval); addrfrag=lrec->frag; x=0; if (lrec->frag!=NULL) x=addrfrag->address-ca; /* rel jump displcmnt*/ if ((x>0 && (instr->props&PREDICTEDTAKEN)) || (x<0 && (instr->props&PREDICTEDTAKEN)!=0)) printf("%s[predicion honoured]",commentmark); if ((x<0 && !(instr->props&PREDICTEDTAKEN)) || (x>0 && (instr->props&PREDICTEDTAKEN))) printf("%s[predicion dishonoured]",commentmark); } if (iprops!=0 || instr->bubbles==255 || ((instr->props&(DELAYSLOTEXISTS|DELAYSLOTINSTR)) && diagassem) || (instr->domain!=0 && diagassem!=0)) { printf("\t%s[",commentmark); if (diagassem!=0 && instr->domain!=0) { if (instr->domain>0) { printf("DOM %d",instr->domain); } else { printf("ATD %d",-instr->domain); } prcomma=1; } if ((iprops&TPIGSTART)!=0) { if (prcomma!=0) printf(", "); printf("IGS"); prcomma=1; } if ((iprops&TPQUEUED)!=0) { if (prcomma!=0) printf(", "); printf("QUEUED"); prcomma=1; } if ((iprops&TPIFTHEN)!=0) { if (prcomma!=0) printf(", "); printf("IT"); prcomma=1; } if ((iprops&TPIFTHENELSE)!=0) { if (prcomma!=0) printf(", "); printf("ITE"); prcomma=1; } if ((iprops&TPITECOLLAPSE)!=0) { if (prcomma!=0) printf(", "); printf("ITE_COLLAPSE"); } if ((iprops&TPLOOPBRANCH)!=0) { if (prcomma!=0) printf(", "); printf("LOOP"); } if ((iprops&TPMIGRATED)!=0) { if (prcomma!=0) printf(", "); printf("MIGRATED"); prcomma=1; } if ((iprops&TPCONTSPEC)!=0) { if (prcomma!=0) printf(", "); printf("SPECULATIVE"); prcomma=1; } if ((iprops&TPGLUED)!=0) { if (prcomma!=0) printf(", "); printf("GLUED"); prcomma=1; } if ((iprops&TPCASCADED)!=0) { if (prcomma!=0) printf(", "); printf("CASCADED"); prcomma=1; } if ((iprops&TPCOPYBYPASS)!=0) { if (prcomma!=0) printf(", "); printf("COPY_BYPASS"); prcomma=1; } if ((iprops&TPEARLY)!=0) { if (prcomma!=0) printf(", "); printf("EARLY"); prcomma=1; } if ((iprops&TPHOISTED)!=0) { if (prcomma!=0) printf(", "); printf("HOISTED"); prcomma=1; } if ((iprops&TPREFRESH)!=0) { if (prcomma!=0) printf(", "); printf("REFRESH"); prcomma=1; } if ((iprops&TPOPTACCESS)!=0) { if (prcomma!=0) printf(", "); printf("OPT_ACCESS"); prcomma=1; } if (instr->bubbles==255) { if (prcomma!=0) printf(", "); printf("POST_FILL"); } printf("]"); } else { printf("\t%s",commentmark); } if (instr->rref|instr->fref|instr->cref) { printf(" REF:"); p5printregisters(instr->rref,instr->fref,instr->cref); } if (instr->rdef|instr->fdef|instr->cdef) { printf(" DEF:"); p5printregisters(instr->rdef /*&255*/,instr->fdef,instr->cdef); } printf(" P/PP:%X/%X A/O/S%2d/%2d/%2d",instr->props, instr->privprops,instr->area,instr->u0.offset,instr->datasize); printf(" csize=%d",instr->csize); } /*printprops*/ /***/ static void outfixedlit(struct instrfmt *instr) { int offset; printf("$%s",egivename(instr->area)); offset=instr->u0.offset-AreaBaseOffset; if (offset>0) printf("+"); if (offset!=0) printf("%1d",offset); } /* outfixedlit */ /***/ static void address(struct instrfmt *instr) { int Base,Index,scale,offset; if ((instr->privprops&SIBREQD)!=0) { scale=(unsigned)instr->sib>>6; Base=instr->rs1; Index=instr->rs2; } if ((instr->privprops&FIXEDINSTR)==0) { if (instr->disp!=0) printf(itos(instr->disp)); if ((instr->privprops&SIBREQD)==0) { printf("(%s)",regtext [instr->rs1]); } } else { printf(egivename(instr->area)); offset=instr->u0.offset-AreaBaseOffset; if (instr->fixtype==GOTofffix) printf("@GOT"); else { if (instr->fixtype==GOTfix) printf("@GOTOFF"); if (offset>0) printf("+%d",offset); if (offset<0) printf("%1d",offset); } if (((instr->fixtype==GOTfix)||(instr->fixtype==GOTofffix))&&((instr->privprops&SIBREQD)==0)) printf("(%%ebx)"); } if ((instr->privprops&SIBREQD)!=0) { printf("("); if ((instr->privprops&FIXEDINSTR)==0 || instr->fixtype==GOTfix) printf("%s",regtext [Base]); printf(",%s,%d)",regtext [Index],1<bubbles!=0)&&(instr->bubbles!=255)&&(*ca!=-2)&&(*ca!=-3)&&(instr->groupbubbles-1; if (bubbles!=0) { if (DiagAssem==0) printf(commentmark); printf("%c",HT); if (OutputASSEMBLER==Positive && instr->groupgroup; op=instr->opcode; if ((OutputASSEMBLER==Positive)&&(*ca>=0)) { if (groupcsize) printf("!***!"); } }; /***/ form=instrform [op]; strcpy(mnem,instrmnem [op]); /***/ /***/ if ((instr->u2.prefixes [osprefix]==102)&&(imp_resolve(mnem,mnem,"l ",S)==0)) { strcat(mnem,"w "); strcat(mnem,S); } J=instr->u2.prefixes [iprefix]; if (J==242) { printf("repnz \n%c",HT); } if (J==243) { printf("rep \n%c",HT); } if (group>=31) switch (group) { case MARKER: if (DiagAssem!=0) printf("%cMARKER\n",HT); /*will usually have been processed by now*/ return; case LINENUMBER: printf("%s line ",commentmark); CPrintLine(instr->u1.immval,instr->u0.offset); printf("\n"); return; case REGIONINFO: printf(commentmark); if (OutputASSEMBLER==Negative&&DiagAssem==0&&asmlist==0) printf("%c%c%c",HT,HT,HT); if ((instr->issueprops&RPUNROLLED)!=0) { printf("UNROLLED REGION"); } else { printf("REGION"); } printf("%1d",instr->u1.immval); if ((instr->issueprops&RPINCELLAR)!=0) printf(" CELLAR"); if ((instr->issueprops&RPTOPLEVEL)!=0) { printf(" [PROCEDURE"); printf("%1d",ProcNum); printf("]"); } if ((instr->issueprops&RPSTART)!=0) { printf(" START "); } else { printf(" END "); } if ((instr->issueprops&RPCONTAINER)!=0) { printf("(CONTAINER)"); } else if ((instr->issueprops&RPGISRESTRICTED)!=0) { printf("(GIS RESTRICTED)"); } else if ((instr->issueprops&RPBBSCHEDULED)!=0) { printf("(BASIC BLOCK SCHEDULED)"); } else { printf("(GLOBALLY SCHEDULED)"); } printf("\n"); return; case LINEDEBUG: /* line number map */ #if(OutputASSEMBLER==Positive) #if(!((Language==CCOMP)&&(LanguageVariant==USLC)&&(TargetABI==LynxOS))) printf("%c.section%c.text\n",HT,HT); printf(".LN%s:",itos(instr->u1.immval)); printf("\n%c.section",HT); printf("%c.line,\"\",@progbits\n",HT); printf("%c.align 1\n%c",HT,HT); printf(".4byte %s;",itos(instr->u1.immval)); printf(" .2byte 0xffff; .4byte .LN%s-..text.b",itos(instr->u1.immval)); printf("\n%c.previous\n",HT); #endif; #endif; return; case DBXINFO: if (DiagAssem!=0) printf("%cDBXINFO %1d 0x%X\n",HT,instr->opcode,instr->u1.immval); #if (OutputASSEMBLER==Positive) if (DiagAssem==0) { if (instr->opcode==0) (*(int *)(instr->u1.immval))=*ca; /*type 0 = give ca*/ } #endif return; case DSWITCHNOTE: if (DiagAssem!=0) printf("%cDSWITCHNOTE 0x%X\n",HT,instr->u1.immval); return; case DELETED: if (DiagAssem!=0) printf("%cDELETED\n",HT); return; case CXTCHANGE: if (DiagAssem!=0) printf("%cCXTCHANGE 0x%x/0x%x\n",HT,instr->u0.CXTset,instr->u1.CXTunset); return; case FIXUPPOINT: if (DiagAssem!=0) printf("%cFIXUPPOINT%3d/%3d\n",HT,instr->area,instr->u0.offset); return; case COPYASM: printf("%c%c%s\n",HT,HT,(char *)instr->u1.immval); printf("\n"); return; /***/ default: BADSWITCH(group,__LINE__,__FILE__); } /*end of switch on control operation */ if (ca!=NULL) *ca+=instr->csize; switch (form) { /***/ case NOFORM: Oponly: printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if (op==ENTER) { printf("%3d,%3d",(unsigned)instr->u1.immval>>8,instr->u1.immval&255); } if (op==RETN) { printf("$"); puthex(instr->u1.immval,0); } if (op==FSTSW) printf("%%ax"); /* GNU AS fussy */ printprops(instr,DiagAssem,*ca); printf("\n"); return; case JFORM: printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if ((instr->props&ANYCALL)==0) { if ((instr->opcode==CALL)&&((instr->privprops&KNOWNOFFSET)!=0)) { printf(".+%1d",instr->u1.immval+5); } else { lrec=BLabRecAd(instr->u1.immval); printf(".L%s",itos(lrec->labnum)); printprops(instr,DiagAssem,*ca); } printf("\n"); return; } if ((instr->privprops&DIRECTCALL)!=0) { exdata=exrecad(instr->u1.immval); if ((OutputASSEMBLER==Negative)&&(exdata->Ca>0)) phex(exdata->Ca); else if (strcmp(exdata->Name,"") ==0) { printf(""); } else { strcpy(S,exdata->Name); if (S[1]=='#') S[1]='_'; if (Pic!=0 && exdata->Ca<=0 && (exdata->flags&4)==0) strcat(S,"@PLT"); printf(S); } } else { lrec=BLabRecAd(instr->u1.immval); printf(".L%s",itos(lrec->labnum)); } printprops(instr,DiagAssem,*ca); printf("\n"); return; } /* end of case based on instr_from */ /***/ switch (instr->variant) { case OPONLY: case FOPONLY: goto Oponly; case OPPLUSR: case OPR: printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if (group==SHIFTS) printf("%%cl,"); if ((instr->opcode==CALLPTR)||(instr->opcode==JMPPTR)) printf("*"); outregtext(instr->rd,instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPRR: printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if (op==LB) printf("%s",bregtext[instr->rs1]); else if (op==MOVSX) printf("%s",wregtext [instr->rs1]); else outregtext(instr->rs1,instr); printf(","); outregtext(instr->rd,instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPRLIT: case OPRSLIT: case OPEAXLIT: case OPPLUSRLIT: printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if ((instr->privprops&FIXEDLIT)!=0) { outfixedlit(instr); } else { printf("$"); if (instr->u1.immval<0 && instr->u1.immval >=-128) { printf("%d",instr->u1.immval); /* assembler treats hex as unsigned */ /* so instruction is bigger than reqd*/ } else { puthex(instr->u1.immval,0); } } printf(","); if (op==IMUL) { outregtext(instr->rs1,instr); printf(","); } outregtext(instr->rd,instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPALLIT: case OPBRMLIT: if (imp_resolve(mnem,mnem,"l ",S)==0) { strcat(mnem,"b "); strcat(mnem,S); } printf("%c%s",HT,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if ((instr->privprops&FIXEDLIT)!=0) { outfixedlit(instr); } else { printf("$0x%X",instr->u1.immval); } printf(",%s",bregtext [instr->rd]); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPRRM: if ((instr->privprops&DSWITCHLOADINSTR)!=0) { printf("\n.Sbase%d:\n\t\t%s",instr->u0.offset,mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); printf(".SW%d-.",instr->u0.offset); J=((instr->disp-7)&(-4))+1; if (J<0) printf("%2d",J); if (J>0) printf("+%d",J); printf("(%s,%s,4),",regtext [instr->rs1],regtext [instr->rs2]); } else { printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); address(instr); printf(","); } outregtext(instr->rd,instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPRMR: printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); outregtext(instr->rd,instr); printf(","); address(instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPMEM: case FOPRM: if ((instr->privprops&DSWITCHLOADINSTR)!=0) { printf("\n.Sbase%s:\n%c",itos(instr->u0.offset),HT); printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); printf("*.SW%d",instr->u0.offset); J=instr->disp; if (J!=0) printf("+%d",J); printf("(,%s,4)",regtext [instr->rs2]); } else { printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); address(instr); } printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPBMEMLIT: if (imp_resolve(mnem,mnem,"l ",S)==0) { strcat(mnem,"b "); strcat(mnem,S); } printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); printf("$%1d,",instr->u1.immval); address(instr); printprops(instr,DiagAssem,*ca); printf("\n"); return; case OPMEMLIT: printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if ((instr->privprops&FIXEDLIT)!=0) { outfixedlit(instr); } else { printf("$"); if (instr->datasize==2) printf("%1d",instr->u1.immval); else puthex(instr->u1.immval,0); } if (op!=PUSH) { printf(","); address(instr); } printprops(instr,DiagAssem,*ca); printf("\n"); return; case FOPPLUSR: if ((imp_resolve(mnem,mnem,"l ",S)==0)||(imp_resolve(mnem,mnem,"s ",S)==0)) { strcat(mnem," "); strcat(mnem,S); } printf("\t%s",mnem); for (jj=1; jj<=12-(int)strlen(mnem); jj++) printf(" "); if (instr->opcode==FCOMPP) { if ((instr->rs1-instr->rd)!=1) Mabort(975); } else if ((instr->opcode==FXCH)||(instr->opcode==FLDd)||(instr->opcode==FSTPd)) { printf("%%st(%c)",(instr->rs1-instr->rd)+'0'); } else if ((FCMOVB<=instr->opcode) && (instr->opcode<=FUCOMIP)) { printf("%%st(%c),%%st",(instr->rs1-instr->rd)+'0'); } else { printf("%%st,%%st(%c)",(instr->rs1-instr->rd)+'0'); } printprops(instr,DiagAssem,*ca); printf("\n"); return; } /*end of switch based on instr_variant */ } /*p5 DisasInstr*/ /***/ void p5printinstr(struct instrfmt * instr) { /******************************************************************/ /** Prints the given instruction with its associated information **/ /******************************************************************/ int ca; ca=-2; printf("%08X ",(unsigned)instr); p5disasinstr(instr,&ca); if (instr->groupprevinstr,PREVCHAIN)); printf("/%08X",(unsigned)BMachineInstr(instr->nextinstr,NEXTCHAIN)); if (((instr->rref|instr->fref)|instr->cref)!=0) { printf(" REF:"); p5printregisters(instr->rref,instr->fref,instr->cref); } if (((instr->rdef|instr->fdef)|instr->cdef)!=0) { printf(" DEF:"); p5printregisters(instr->rdef /*&255*/,instr->fdef,instr->cdef); } printf(" P/PP:%X/%X",instr->props,instr->privprops); if (instr->area!=-1) { printf(" A/O/S:%d/%d/%d",instr->area,instr->u0.offset,instr->datasize); } if (instr->domain!=0) { printf(" DOM:%d",instr->domain); } printf(" csize=%d\n",instr->csize); } else if (instr->group==LINENUMBER) { printf("#line %2d\n",instr->u1.immval); } else if (instr->group==DELETED) { printf("DELETED\n"); } } /*p5 PrintInstr*/ /***/ int p5memoryoverlap(struct instrfmt *instr1,struct instrfmt *instr2,int constbase) { /*****************************************************************************/ /** 'instr1' and 'instr2' are two memory referencing instructions. **/ /** Return MO NO if the memory references definitely do not overla **/ /** Return MO YES if the memory references definitely do overlap **/ /** Return MO MAYBE otherwise. **/ /** Err on the side of MAYBE. **/ /** if constbase is non-zero then any base registers are constant between **/ /** the two accesses **/ /*****************************************************************************/ int start1,start2,end1,end2; /* see if aliasing can be determined from the domain information */ /* printstring("p5memoverlap"); phex(addr(instr1)); space */ /* phex(addr(instr2)); newline */ /* write(instr1_area,2); write(instr1_offset,2); write(instr1_domain,2) */ /* spaces(10) */ /* write(instr2_area,2); write(instr2_offset,2); write(instr2_domain,2) */ /* newline */ if (instr1->domain!=0 || instr2->domain!=0) { if ((instr1->domain!=0)&&(instr2->domain!=0)) { if (instr1->domain!=instr2->domain) return MONO; } else if (instr1->domain<0) { return MONO; } else if (instr2->domain<0) { return MONO; } } /* see if aliasing can be determined from the area information */ if (instr1->area!=instr2->area) { /* differing ordinary areas indicate that the accesses can't overlap */ if (instr1->area>=0 && instr2->area>=0) return MONO; /* Nothing can overlap stack unless frame address has been taken */ if ((PI->privprops&FRAMEADDRTAKEN)==0 && (instr1->area==STACK || instr2->area==STACK)) return MONO; /* Nothing can overlap PARAMS unless param address has been taken */ if ((paramSTflags [0]&4)==0 && (instr1->area==PARAMS || instr2->area==PARAMS)) return MONO; /* FORTRAN scalars are in GLA and arrays are in GST so these */ /* can not overlap */ #if(Language==FORTRAN) if (instr1->area==GLA && (instr2->privprops&SIBREQD)!=0) return MONO; if (instr2->area==GLA && (instr1->privprops&SIBREQD)!=0) return MONO; #endif; } else if (instr1->area>=0) { /* the areas are the same so we may be able to use offsets to */ /* determine if the accesses are aliased */ start1=instr1->u0.offset; start2=instr2->u0.offset; if (instr1->fixtype!=instr2->fixtype) return MOMAYBE; if (start1!=-1 && start2!=-1 && ((instr1->privprops|instr2->privprops)&SIBREQD)==0) { end1=start1+instr1->datasize-1; end2=start2+instr2->datasize-1; if (start1==start2 && end1==end2) return MOEXACT; if ((start2<=start1 && start1<=end2) || (start1<=start2 && start2<=end1)) return MOYES; return MONO; } }; /**/ /* A push or pop that does not have a memory operand can only clash with */ /* a store reference via ESP and then only when ESP is not being used for */ /* Frame access in a leaf routine. (NB only pushed in a leaf routime are */ /* those to save and restore ESI,EDI,EBX */ /**/ if (instr1->variant!=OPMEM && (instr1->opcode==POP || instr1->opcode==PUSH) && ((instr2->rref&(1<privprops&FRAMEACCESS)!=0)) return MONO; if (instr2->variant!=OPMEM && (instr2->opcode==POP || instr2->opcode==PUSH) && ((instr1->rref&(1<privprops&FRAMEACCESS)!=0)) return MONO; if ((((instr1->privprops|instr2->privprops)&(SYMBOLICACCESS|FIXEDINSTR)))!=0) return MOMAYBE; /* base register invalid for symbolic access */ if (instr1->group==STROP || instr2->group==STROP) return MOMAYBE; if (instr1->rs1==instr2->rs1 && (constbase!=0 || instr1->rs1==FRAMEPOINTER) && ((((instr1->privprops|instr2->privprops)&SIBREQD)==0) || (constbase!=0 && (instr1->privprops&instr2->privprops&SIBREQD)!=0 && instr1->sib==instr2->sib && instr1->rs2==instr2->rs2))) { /* base reg plus constant offset */ start1=instr1->disp; start2=instr2->disp; end1=(start1+instr1->datasize)-1; end2=(start2+instr2->datasize)-1; if ((start1==start2)&&(end1==end2)) return MOEXACT; if (((start2<=start1)&&(start1<=end2))||((start1<=start2)&&(start2<=end1))) return MOYES; return MONO; } else { /* base regs not the same: we do not know if the accesses overlap */ return MOMAYBE; } } /*p5 MemoryOverlap*/ /***/ int p5hasannullableslots() { /*************************************************************************/ /** Returns a non-zero value if annullable delay slots are available. **/ /** This is always false on 486 and Pentium. **/ /*************************************************************************/ return 0; } /*p5 HasAnnullableSlots*/ /***/ void p5filldelayslot(struct fragfmt *frag,struct instrfmt *instr) { /*****************************************************************************/ /** If the delay slot is empty for a call or jump attempt to fill it from **/ /** the previous instruction stream **/ /** No delayslots on pentium but bprocs reference must be satisfied **/ /*****************************************************************************/ } /*p5 FillDelaySlot*/ /***/ void p5printarchinfo(int archinfo) { /*******************************************************************************/ /** Prints architecture specific information which is associated with a **/ /** fragment. **/ /*******************************************************************************/ if (archinfo!=0) { printf("ARCHINFO: "); puthex(archinfo,0); printf("\n"); } } /*p5 PrintArchInfo*/ /***/ void p5deleteswitch(struct fragfmt *frag) { /**************************************************************************/ /** Given that the contents of a dense switch jump fragment has been **/ /** deleted, goes ahead and deletes any associated switch table. On **/ /** p5 the switch table is always associated with the same fragment. **/ /**************************************************************************/ if (frag->switchtab==0) { p5Error("p5 DeleteSwitch: Switch table not found"); } frag->switchtab=0; } /*p5 DeleteSwitch*/ /***/ int p5improveswitch(struct fragfmt *frag) { /**************************************************************************/ /** For switch tables check each destination to see whether it is itself **/ /** an unconditional branch -- if so, change the table entry to point **/ /** directly to destination's destination; apply repeatedly. Returns a **/ /** non-zero value if any change to the table was made. **/ /**************************************************************************/ struct fragfmt *destfrag,*notefrag; struct instrfmt *destinstr,*oldnote,*nexti; struct lrecfmt *lrec; struct Swtabfmt *swtab; int j,prevchanges,changes,tabval; changes=0; swtab=(struct Swtabfmt*)(frag->switchtab); if (swtab->Mode!=SPARSESWITCH) { /* check that the fragment is marked correctly */ if ((frag->props&DSWITCHJUMPFRAG)==0) p5Error("p5 ImproveSwitch: Cannot find jump fragment"); do { if (changes>SENSIBLEITER) p5Error("p5 ImproveSwitch: Too many iterations"); prevchanges=changes; for (j=0; j<=swtab->Entries-1; j++) { tabval=swtab->u0.R [j]; if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); tabval=lrec->labnum; } if (tabval!=-1) { notefrag=lrec->frag; destfrag=notefrag->final; destinstr=BMachineInstr(destfrag->firstinstr,NEXTCHAIN); if (destinstr==NULL) continue ; if ((destinstr->props&BRANCH)==0 || (destinstr->props&CONDITIONALBRANCH)!=0 || destinstr->opcode!=JMP || ((destinstr->props&DELAYSLOTEXISTS)!=0 && (destinstr->props&DELAYSLOTUNUSED)==0)) { continue ; } lrec=BLabRecAd(destinstr->u1.immval); swtab->u0.R [j]=BLocateLabel(lrec->labnum,0); /* locate the existing dense switch note */ nexti=notefrag->firstinstr; while (nexti!=NULL) { oldnote=nexti; if (oldnote->group==DSWITCHNOTE && oldnote->u1.immval==(int)frag) break; nexti=oldnote->nextinstr; } if (nexti==NULL) p5Error("p5 ImproveSwitch: Switch note missing"); /* update the fragment use counts and properties */ destfrag->uses--; destfrag=lrec->frag; destfrag->props|=DSWITCHCASEFRAG; destfrag=destfrag->final; destfrag->uses++; /* move the dense switch note to the new destination frag */ destfrag=lrec->frag; BMoveBefore(notefrag,destfrag,oldnote,destfrag->firstinstr); changes++; if (optimtrace!=0 && peepreport!=0) printf("PHOLE: FRAGMENT: %08X IMPROVED DENSE SWITCH TO %08X\n", (unsigned)frag,(unsigned)notefrag); } } } while (changes!=prevchanges) ; } return changes; } /*p5 ImproveSwitch*/ /***/ int p5improvereturn(struct fragfmt *frag) { /**************************************************************************/ /** Modifies unconditional branches to returns into direct return **/ /** instructions. This optimisation is not performed on MIPS. **/ /**************************************************************************/ return 0; } /*p5 ImproveReturn*/ /***/ void p5printswitch(struct fragfmt *frag) { /**************************************************************************/ /** Prints the labels used in a dense switch table **/ /**************************************************************************/ struct Swtabfmt *swtab; struct lrecfmt *lrec; int tabval,i; swtab=(struct Swtabfmt*)(frag->switchtab); if (swtab->Mode!=SPARSESWITCH) { printf("SWITCH TABLE LABELS:"); for (i=0; i<=swtab->Entries-1; i++) { tabval=swtab->u0.R [i]; if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); printf(" .L%d",lrec->labnum); } } printf("\n"); } } /*p5 PrintSwitch*/ /***/ void p5printregisters(int gvector,int fvector,int cvector) { /*******************************************************************************/ /** Prints the set of registers represented by the given bit vectors **/ /*******************************************************************************/ int reg; if ((gvector==-1)&&(fvector==-1)&&(cvector==-1)) { printf("ALL"); } else { reg=0; while (reg<16) { if ((gvector&(1<=8) printf("-Beta "); else printf(" "); reg++; } else { reg++; } } reg=0; while (reg<8) { if ((fvector&(1<Name); } /*p5 PrintEntryName*/ /***/ int p5createuncond(int labindex) { /********************************************************************************/ /** Creates an unconditional jump instruction which is not linked in any **/ /** instruction chain. The branch is to the given label index. The **/ /** DELAY SLOT UNUSED property for the instruction is set to indicate that it **/ /** requires a delay slot instruction. **/ /********************************************************************************/ struct instrfmt *instr; instr=BObtainInstr(); instr->group=instrgroup [JMP]; instr->opcode=JMP; instr->rs1=0; instr->rs2=0; instr->u1.immval=labindex; instr->props=(((instr->props|BRANCH)|DELAYSLOTUNUSED)|DELAYSLOTEXISTS)|DSMUSTEXIST; return (int)instr; } /*p5 CreateUncond*/ /***/ int p5askforlabelnum() { /********************************************************************************/ /** Returns a new unique label number that is not currently used in the code **/ /********************************************************************************/ return Mprivatelabel()+PI->labadjust; } /*p5 AskForLabelNum*/ /***/ void p5flushoutput(int selstdout) { /********************************************************************************/ /** Flushes any output streams - this allows C and IMP output to be mixed for **/ /** languages in which the IMP library routines are not implemented in C. If **/ /** selstdout is non-zero then the standard output stream is selected for IMP. **/ /********************************************************************************/ #if((Language!=CCOMP)||(LanguageVariant!=USLC)) #endif; } /*p5 FlushOutput*/ /***/ void p5setlabelrefs(struct fragfmt *frag) { /*******************************************************************************/ /** Updates fragment use counts for any label references (other than **/ /** branches) made in the given fragment. If these references are no taken **/ /** into account then the label might look dead to the fragment reorganiser **/ /** and will thus be deleted. On p5 the possible destination fragments of **/ /** a dense switch have to handled by this routine. **/ /*******************************************************************************/ struct fragfmt *destfrag; struct Swtabfmt *swtab; struct lrecfmt *lrec; int tabval,i; if (frag->switchtab!=0) { swtab=(struct Swtabfmt*)(frag->switchtab); if (swtab->Mode!=SPARSESWITCH) { for (i=0; i<=swtab->Entries-1; i++) { tabval=swtab->u0.R [i]; if (tabval!=-1) { if (tabval<0) tabval=-tabval; lrec=BLabRecAd(tabval&0xFFFFFFF); destfrag=lrec->frag; destfrag=destfrag->final; destfrag->uses=destfrag->uses+1; } } } } } /*p5 SetLabelRefs*/ /***/ int p5conditionmatch(struct instrfmt *instr1,struct instrfmt *instr2) { /*****************************************************************************/ /** 'instraddr1' is the address of a jump instruction. **/ /** If 'instraddr2 is the address of another jump instruction whose 'true' **/ /** condition includes that of the first jump, then return 1 else return 0 **/ /** **/ /** The return value will be used to decide whether to replace the first **/ /** jump's destination with the second's destination if appropriate - thus **/ /** other checks may be applicable in this routine. **/ /*****************************************************************************/ int op1,op2,cond1,cond2,ca; if (instr2==NULL) return 0; /* don't think this can happen */ op1=instr1->opcode; cond1=(instr1->rs1<<8)|instr1->rs2; op2=instr2->opcode; cond2=(instr2->rs1<<8)|instr2->rs2; if ((instr1->group!=BCC)||(instr2->group!=BCC)) return 0; /* %RESULT = 0 %IF cond1 = NEVER %OR cond2 = NEVER */ /*a jump never happens*/ if ((instr2->props&(BRANCH|CONDITIONALBRANCH))==BRANCH) goto match; /*the condition of the 2nd jump is a superset of the 1st*/ /* test for inclusive conditions */ if (((cond1==cond2)&&(op1==op2))) goto match; /* identical operations */ return 0; match: if (optimtrace!=0 && peepreport!=0) { printf("PHOLE: Condition match :%08X %08X\n",(unsigned)instr1,(unsigned)instr2); ca=-1; p5disasinstr(instr1,&ca); p5disasinstr(instr2,&ca); } return 1; } /*p5 ConditionMatch*/ /***/ int p5isaddrlabel(int label) { /*******************************************************************************/ /** Returns a non-zero value if the given label has been addressed. **/ /*******************************************************************************/ return Maddressedlabel(label); } /*p5 IsAddrLabel*/ /***/ /***/ /********************************************************************************/ /** **/ /** p5 GIS DELTA AND BPROCS SUPPORT FUNCTIONS **/ /** **/ /********************************************************************************/ /***/ /* to save paper */ int p5invertbranch(struct instrfmt *instr,int predtaken,int istest) { /*****************************************************************************/ /** 'instr' is a conditional jump : invert the condition for which it jumps **/ /** eg **/ /** bge L123 becomes blt L123 **/ /** **/ /** 'predtaken' does not apply to p5 **/ /*****************************************************************************/ if (instr->opcode==JMP) { p5Error("p5 InvertBranch: Cannot invert jump"); } if (istest==0) instr->opcode=InverseJump [instr->opcode-(JO)]; return 0; /* no branch prediction */ } /*p5 InvertBranch*/ /***/ int p5createnop() { /********************************************************************************/ /** Creates a NOP instruction which is not linked in any instruction chain **/ /********************************************************************************/ struct instrfmt *instr; instr=BObtainInstr(); instr->group=instrgroup [NOP]; instr->opcode=NOP; instr->u1.immval=0; return (int)instr; } /*p5 CreateNOP*/ /***/ /***/ /********************************************************************************/ /** **/ /** p5 GIS DELTA SUPPORT FUNCTIONS **/ /** **/ /********************************************************************************/ /***/ #if(BUILDWITHGS>0) static unsigned char RetOpRegs [9+1]; static unsigned char RetOpAliases [9+1]; static unsigned char RetOpMasks [9+1]; static unsigned char RetOpProps [9+1]; static int fp0popop(int opcode) { /*************************************************************************/ /** returns 0 unless opcode is a FPOP that does not pop the FSTACK **/ /*************************************************************************/ if (opcode==FIADDm || opcode==FADDm || opcode==FADDd) return 1; if (opcode==FIMULm || opcode==FMULm || opcode==FMULd) return 1; if (opcode==FISUBm || opcode==FSUBm || opcode==FSUBd) return 1; if (opcode==FISUBRm || opcode==FSUBRm || opcode==FSUBRd) return 1; if (opcode==FIDIVm || opcode==FDIVm || opcode==FDIVd) return 1; if (opcode==FIDIVRm || opcode==FDIVRm || opcode==FDIVRd) return 1; if (opcode==FICOMm || opcode==FCOMm || opcode==FCOMd) return 1; if (opcode==FABS || opcode==FCHS || opcode==FTST || opcode==FRNDINT) return 1; return 0; } /* of fp0popop*/ /***/ static int fp1popop(int opcode) { /*************************************************************************/ /** returns 0 unless opcode is a FPOP that pops the FSTACK once **/ /*************************************************************************/ if (opcode==FADDP || opcode==FSUBP || opcode==FMULP || opcode==FDIVP || opcode==FSUBRP || opcode==FDIVRP) return 1; if (opcode==FICOMPm || opcode==FCOMPm || opcode==FCOMPd || opcode==FCOMP) return 1; return 0; } /* of fp1popop*/ static void ReadjustStackAccesses(int adjustment) { /************************************************************************/ /** Adjusts accesses to the stack frame via the stack pointer after **/ /** global scheduling has been performed if the size of the register **/ /** save area changes **/ /************************************************************************/ struct fragfmt *curfrag,*frag; struct instrfmt *instr,*curinstr; /* update any offsets for accesses via the stack pointer */ curfrag=PI->procfrag; while (curfrag!=NULL) /*through all frags in this procedure*/{ frag=curfrag; curinstr=frag->firstinstr; while (curinstr!=NULL) /*through all instructions in this frag*/{ instr=curinstr; if (((instr->privprops&FRAMEACCESS)!=0)&&((instr->rref&(1<disp>127) instr->csize+=-3; instr->disp+=adjustment; if (instr->disp>127) instr->csize+=3; /* In case crossint 127 changes lit size*/ } curinstr=instr->nextinstr; } curfrag=frag->nextfrag; } /* modify the prologue stack size */ frag=PI->procfrag; curinstr=(struct instrfmt *)(int)BMachineInstr(frag->firstinstr,NEXTCHAIN); while (curinstr!=NULL) { instr=curinstr; if (((instr->rdef&(1<props&FRAMEMODIFY)!=0)) { if (instr->opcode==SUB) { instr->u1.immval+=adjustment; break ; } if (instr->opcode==ENTER) { instr->u1.immval+=(adjustment<<8); break ; } p5Error("Illegal frame modify instruction"); } curinstr=(struct instrfmt *)(int)BMachineInstr(instr->nextinstr,NEXTCHAIN); } /* modify the epilogue stack size */ curfrag=PI->procfrag; while (curfrag!=NULL) { frag=curfrag; if ((frag->props&RETURNFRAG)!=0) { curinstr=(struct instrfmt *)(int)BMachineInstr(frag->lastinstr,PREVCHAIN); while (curinstr!=NULL) { instr=curinstr; if (((instr->rdef&(1<props&FRAMEMODIFY)!=0)) { if (instr->opcode==ADD) { instr->u1.immval+=adjustment; break ; } p5Error("Illegal frame modify instruction"); } curinstr=(struct instrfmt *)(int)BMachineInstr(instr->previnstr,PREVCHAIN); } } curfrag=frag->nextfrag; } } /*ReadjustStackAccesses*/ /***/ int p5CanRewriteFixInstr(struct instrfmt *instr,int area,int offset) { /********************************************************************************/ /** Determines if the given instruction can be rewritten to use a fixed up **/ /** value with the given area and offset. This is not possible for any **/ /** instructions on MIPS. **/ /********************************************************************************/ return 0; } /*p5 CanRewriteFixInstr*/ /***/ int p5CreateNewLabel() { /********************************************************************************/ /** Creates a new unique label that is not currently used in the code. The **/ /** label definition point must be later set using a B SetEntryLabel call. **/ /********************************************************************************/ struct lrecfmt *lrec; int labnum,labindex; labnum=Mprivatelabel()+PI->labadjust; labindex=BLocateLabel(labnum,0); lrec=BLabRecAd(labindex); return (int)lrec; } /*p5 CreateNewLabel*/ /***/ int p5DuplicateFrag(struct fragfmt *frag) { /********************************************************************************/ /** Generates a duplicate of the given fragment and returns a pointer to the **/ /** duplicate which is not linked into any fragment chain. **/ /********************************************************************************/ struct fragfmt *newfrag; newfrag=BObtainFrag(); BCopyFrag(frag,newfrag); newfrag->prevfrag=0; newfrag->nextfrag=0; newfrag->final=(int)newfrag; return (int)newfrag; } /*p5 DuplicateFrag*/ /***/ int p5DuplicateInstr(struct instrfmt *instr) { /********************************************************************************/ /** Generates a duplicate of the given instruction and returns a pointer to **/ /** the duplicate which is not linked into any instruction chain **/ /********************************************************************************/ struct instrfmt *newinstr; newinstr=BObtainInstr(); BCopyInstr(instr,newinstr); newinstr->previnstr=0; newinstr->nextinstr=0; newinstr->issueprops=0; newinstr->bubbles=0; return (int)newinstr; } /*p5 DuplicateInstr*/ /***/ void p5GenRestoreWrapper(struct fragfmt *exit,int regsa,int regsb,int regsc,int regsd) { /********************************************************************************/ /** Generates code to restore the given callee preserved registers at the end **/ /** of the given exit fragment. Wrapper instructions will already exist so **/ /** we make use of these were possible, deleting existing ones or inserting **/ /** new ones as required. **/ /********************************************************************************/ struct instrfmt *instr,*retinstr; int newregsa,newregsb,redunregsa,redunregsb,nexti,reg,offset,previ; int movestackset,foundspdef; /* if no register expansion was allowed then we don't have to do anything */ if ((PI->privprops&NOREGEXPAN)!=0) return; } /*p5 GenRestoreWrapper*/ /***/ void p5GenSaveWrapper(struct fragfmt *entry,int regsa,int regsb,int regsc,int regsd) { /********************************************************************************/ /** Generates code to store the given callee preserved registers at the start **/ /** of the given entry fragment. Wrapper instructions will already exist so **/ /** we make use of these were possible, deleting existing ones or inserting **/ /** new ones as required. **/ /********************************************************************************/ struct instrfmt *instr,*tempinstr; int newregsa,newregsb,redunregsa,redunregsb,nexti,reg,offset; int foundspdef,movestackset,tempi; /* if no register expansion was allowed then we don't have to do anything */ if ((PI->privprops&NOREGEXPAN)!=0) return; } /*p5 GenSaveWrapper*/ /***/ void p5GetAccessInfo(struct instrfmt *instr,int *basereg,int *offset,int *size,int *props) { /********************************************************************************/ /** Obtains information about the given memory access instruction. Basereg is **/ /** set to the number of the base register. Offset is set to a byte offset if **/ /** the AI LIT OFFSET property is set or is undefined if the AI FIX OFFSET **/ /** property is set. Size is set to the width of the access in bytes and props **/ /** is set to give a properties vector for the access. **/ /********************************************************************************/ if ((instr->props&(LOADINSTR|STOREINSTR))==0) { p5Error("p5 GetAccessInfo: Instruction is not a memory access"); } if ((instr->privprops&VOLATILELOC)!=0) { *props=AIVOLATILE; } else { *props=0; } *basereg=instr->rs1; *size=instr->datasize; if ((instr->privprops&(SYMBOLICACCESS|FIXEDINSTR))!=0) { *props|=AIFIXOFFSET; } else { *props|=AILITOFFSET; *offset=instr->u1.immval; } #if(Language==FORTRAN) if (instr->area==STACK) *props|=AIDOMEXCL; #endif; } /*p5 GetAccessInfo*/ /***/ void p5GetFixupInfo(struct instrfmt *instr,int *base,int *offset,int *ispartial,int *isupper) { /********************************************************************************/ /** Obtains fixup information for an instruction which has a fixed up operand. **/ /** Base is set to the area of the fixup and offset to the byte offset. **/ /** Ispartial is set to non-zero if the fixup is only partial and isupper is **/ /** set to non-zero if the fixup is to be upper part of the word. This **/ /** function should only be applied to instructions which return a positive **/ /** response to p5 IsFixupInstr. **/ /********************************************************************************/ *base=instr->area; *offset=instr->u0.offset; if ((instr->privprops&SYMBOLICACCESS)!=0) { *ispartial=0; } else { *ispartial=1; *isupper=0; } } /*p5 GetFixupInfo*/ /***/ int p5GetFragProps(struct fragfmt *frag) { /********************************************************************************/ /** Returns the properties associated with the given fragment in the form of a **/ /** bit vector **/ /********************************************************************************/ int props; if ((frag->props&FALLSTHROUGH)!=0) { props=FPFALLSTHROUGH; } else { props=0; } if ((frag->props&FALLENINTO)!=0) { props|=FPFALLENINTO; } if (((frag->props&((USERASSEMFRAG|DSWITCHJUMPFRAG)|NOGISFRAG))!=0)||(frag->switchtab!=0)) { props|=FPNOGIS; } if ((frag->props&RETURNFRAG)!=0) { props|=FPRETURNS; } if ((frag->props&RETURNBRANCH)!=0) { props|=FPRETBRANCH; } return props; } /*p5 GetFragProps*/ /***/ int p5GetInstrProps(struct instrfmt *instr) { /********************************************************************************/ /** Obtains a bit vector giving the properties for the given instruction **/ /********************************************************************************/ return instr->props; } /*p5 GetInstrProps*/ /***/ int p5GetLitOp(struct instrfmt *instr) { /********************************************************************************/ /** Gets the immediate operand of the given instruction. This function should **/ /** only be called for instructions which return a positive response to the **/ /** p5 HasLitOp function. **/ /********************************************************************************/ int form; form=instrform [instr->opcode]; return instr->u1.immval; } /*p5 GetLitOp*/ /***/ void p5GetOperands(struct instrfmt *instr,int *numops,int *opregs,int *opaliases,int *opmasks,int *opprops ) { /********************************************************************************/ /** Obtains information regarding the operands of the given instruction. **/ /** Numops is set to the number of operands which the instruction has. The **/ /** opregs, opaliases, opmasks and opprops are set to point to the base of **/ /** byte arrays holding information about the operands. These arrays are only **/ /** temporary and may be reused by the next call of this function. opregs **/ /** gives the registers (or first register in a pair) for each operand or -1 **/ /** if the operand uses an immediate operand. Opaliases, opmasks and opprops **/ /** are defined for each register operand. Register aliases are not used on **/ /** the p5 architecture. Opprops gives the properties for each register **/ /** operand. **/ /********************************************************************************/ int a_value; int curreg,form,op; *opregs=(int)&RetOpRegs [0]; *opaliases=(int)&RetOpAliases [0]; *opmasks=(int)&RetOpMasks [0]; *opprops=(int)&RetOpProps [0]; form=instrform [instr->opcode]; goto a_skip; a_despatch: switch (a_value) { default: BADSWITCH(a_value,__LINE__,__FILE__); } a_skip: ; } /*p5 GetOperands*/ /***/ void p5GetReadRegs(struct instrfmt *instr,int *regsa,int *regsb,int *regsc,int *regsd) { /********************************************************************************/ /** Sets regsa, regsb, regsc and regsd to the set of registers referenced by **/ /** the given instruction. Exempt registers referenced by the instruction are **/ /** ignored. **/ /********************************************************************************/ *regsa=instr->rref&0xFFFFFFFE; *regsb=instr->fref; *regsc=instr->cref; *regsd=0; } /*p5 GetReadRegs*/ /***/ int p5GetRegDomain(struct fragfmt *frag,int regnum) { /********************************************************************************/ /** Determines if the given register holds an address pointing to a particular **/ /** domain on exit from the given fragment. The number of the domain is **/ /** returned or 0 if there is no domain information for the register. Negative **/ /** values represent exclusive domains and positive values represent **/ /** inclusive domains. **/ /********************************************************************************/ struct instrfmt *instr; int previ,searchreg; previ=(int)BMachineInstr(frag->lastinstr,PREVCHAIN); while (previ!=0) { instr=(struct instrfmt*)(previ); if ((instr->rdef&(1<rd==regnum)&&(instr->domain!=0)&&((instr->props&DELAYSLOTINSTR)==0)) { return instr->domain; } return 0; } previ=(int)BMachineInstr(instr->previnstr,PREVCHAIN); } return 0; } /*p5 GetRegDomain*/ /***/ void p5GetRegFixup(struct fragfmt *frag,int regnum,int *base,int *offset,int *ispartial,int *isupper ) { /********************************************************************************/ /** Determines if the given register holds a fixed up address on exit from the **/ /** given fragment. Base is set to the area of the fixup or -1 if there is no **/ /** fixup associated with the register. Offset is set to the displacement **/ /** within the area. Ispartial is set to a non-zero value if the fixup is only **/ /** to part of the register and isupper is set to a non-zero value if it is **/ /** the upper part of the register which is fixed up. **/ /********************************************************************************/ struct instrfmt *instr; int previ,searchreg; } /*p5 GetRegFixup*/ /***/ void p5GetWrapperInfo(int *regsa,int *regsb,int *regsc,int *regsd) { /********************************************************************************/ /** Obtains wrapper information for the function being globally scheduled. **/ /** Regsa - regsd are set to the set of callee preserved registers that may be **/ /** used by the scheduler. **/ /********************************************************************************/ if ((PI->privprops&NOREGEXPAN)!=0) { *regsa=PI->rdef|(~CALLEESAVEDREGS); *regsb=PI->fdef|(~CALLEESAVEDFREGS); } else { *regsa=-1; *regsb=-1; } *regsc=-1; *regsd=-1; } /*p5 GetWrapperInfo*/ /***/ void p5GetWriteRegs(struct instrfmt *instr,int *regsa,int *regsb,int *regsc,int *regsd,int incwindow ) { /********************************************************************************/ /** Gets the set of registers which are modified by the given instruction. **/ /** Exempt registers written by the instruction are ignored. Incwindow is **/ /** ignored on MIPS. **/ /********************************************************************************/ *regsa=instr->rdef&0xFFFFFFFE; *regsb=instr->fdef; *regsc=instr->cdef; *regsd=0; } /*p5 GetWriteRegs*/ /***/ int p5HasAssociate(struct instrfmt *instr) { /********************************************************************************/ /** Returns a non-zero result if the given instruction has an associate. There **/ /** are no associate instructions on MIPS. **/ /********************************************************************************/ return 0; } /*p5 HasAssociate*/ /***/ int p5HasFixedDS(struct instrfmt *instr) { /********************************************************************************/ /** Returns a non-zero value if the given instruction is a delay slot owner **/ /** with a fixed delay slot instruction. There are no fixed delay slot **/ /** instructions on MIPS. **/ /********************************************************************************/ return 0; } /*p5 HasFixedDS*/ /***/ int p5HasLitOp(struct instrfmt *instr) { /********************************************************************************/ /** Returns a non-zero value if the given instruction has an immediate **/ /** operand. The operand itself may be obtained with p5 GetLitOp. **/ /********************************************************************************/ int form; form=instrform [instr->opcode]; /* %IF form # DForm %AND form # DUForm %AND form # SAForm %THEN %RESULT = 0*/ return 1; } /*p5 HasLitOp*/ /***/ int p5IsFixupInstr(struct instrfmt *instr) { /********************************************************************************/ /** Returns a non-zero value if the given instruction has an operand which **/ /** is fixed up. Information may be obtained about the fixup using the **/ /** p5 GetFixupInfo function. **/ /********************************************************************************/ if ((instr->privprops&SYMBOLICACCESS)!=0) { return 1; } else if ((instr->privprops&FIXEDINSTR)!=0) { return 1; } return 0; } /*p5 IsFixupInstr*/ /***/ int p5IsNotSelfAlias(int area) { /********************************************************************************/ /** Returns a non-zero value if the given area cannot be self-aliased. That **/ /** is, there will be no pointers in the area which point to other parts of **/ /** same area. This is currently hard-wired to the library array '__iob' which **/ /** holds character I/O information. **/ /********************************************************************************/ char S [41] ; #if(Language==CCOMP) strcpy(S,egivename(area)); if (strcmp(S,"__iob")==0) return 1; #endif; return 0; } /*p5 IsNotSelfAlias*/ /***/ int p5IsUnlikelyCall(struct instrfmt *instr) { /********************************************************************************/ /** Returns a non-zero value if the given instruction is a call to a function **/ /** which is unlikely to be called. This is currently hard-wired to the **/ /** library functions '__flsbuf' and '__filbuf'. These are used to flush or **/ /** fill, respectively, the character I/O buffers. These are much less likely **/ /** to be called than the alternative of putting a character into or removing **/ /** a character from the buffer. **/ /********************************************************************************/ char S [41] ; return 0; } /*p5 IsUnlikelyCall*/ /***/ void p5RewriteFixInstr(struct instrfmt *instr,int offset) { /********************************************************************************/ /** Rewrites an instruction which uses a fixup to use another register fixed **/ /** up to a potentially different offset in the same area. This is not **/ /** possible on the p5 architecture. **/ /********************************************************************************/ } /*p5 RewriteFixInstr*/ /***/ void p5SetOperands(struct instrfmt *instr,int numops,int opregs,int issueprops,int bubbles) { /********************************************************************************/ /** Sets the operand registers for the given instruction. The instruction has **/ /** numops operands (obtained from a previous p5 GetOperands call). Opregs **/ /** is a byte array giving the registers allocated to each operand. Operands **/ /** which represent an immediate value or exempt register have a register **/ /** allocation of NO NEW REG. Issueprops gives information about the issue of **/ /** the instruction from GIS and bubbles gives the estimated number of **/ /** pipeline bubbles preceeding the issue of the instruction. **/ /********************************************************************************/ int a_value; int form,op; /* check that the instruction has been committed */ if ((instr->bubbles==0)&&((instr->previnstr!=0)||(instr->nextinstr!=0))) { p5Error("p5 SetOperands: Cannot modify uncommitted instruction"); } /* set the GIS timing information for the instruction */ if (instr->bubbles!=0) { if (bubbles>=254) bubbles=253; instr->bubbles=bubbles+1; } instr->issueprops=instr->issueprops|issueprops; /* set the operand information */ form=instrform [instr->opcode]; goto a_skip; a_despatch: switch (a_value) { default: BADSWITCH(a_value,__LINE__,__FILE__); } a_skip: ; } /*p5 SetOperands*/ /***/ int p5TraceReg(int regnum) { /********************************************************************************/ /** Prints the symbolic form of the given register and returns the number of **/ /** characters printed **/ /********************************************************************************/ int junk,len; } /*p5 TraceReg*/ /***/ #endif; /* end of automatic translation */