X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcoptind.c;h=b14b27ed941b4566d1497e60d7c4acf4ea68cd15;hb=f7dfcbcc3daf8426770842b5e6ed3634e0d50c82;hp=83311684744ccb587618ac676c6321ab3d9549b6;hpb=f42300ef62779856c7c5c88a9d84c9eb63da31ba;p=cc65 diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 833116847..b14b27ed9 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -6,7 +6,7 @@ /* */ /* */ /* */ -/* (C) 2001 Ullrich von Bassewitz */ +/* (C) 2001-2002 Ullrich von Bassewitz */ /* Wacholderweg 14 */ /* D-70597 Stuttgart */ /* EMail: uz@cc65.org */ @@ -33,7 +33,8 @@ -#include +/* common */ +#include "cpu.h" /* cc65 */ #include "codeent.h" @@ -45,12 +46,80 @@ /*****************************************************************************/ -/* Replace jumps to RTS by RTS */ +/* Helper functions */ /*****************************************************************************/ -unsigned OptRTSJumps (CodeSeg* S) +static int GetBranchDist (CodeSeg* S, unsigned From, CodeEntry* To) +/* Get the branch distance between the two entries and return it. The distance + * will be negative for backward jumps and positive for forward jumps. + */ +{ + /* Get the index of the branch target */ + unsigned TI = CS_GetEntryIndex (S, To); + + /* Determine the branch distance */ + int Distance = 0; + if (TI >= From) { + /* Forward branch, do not count the current insn */ + unsigned J = From+1; + while (J < TI) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance += N->Size; + } + } else { + /* Backward branch */ + unsigned J = TI; + while (J < From) { + CodeEntry* N = CS_GetEntry (S, J++); + Distance -= N->Size; + } + } + + /* Return the calculated distance */ + return Distance; +} + + + +static int IsShortDist (int Distance) +/* Return true if the given distance is a short branch distance */ +{ + return (Distance >= -125 && Distance <= 125); +} + + + +static short RegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given register */ +{ + if ((Use & REG_A) != 0) { + return RC->RegA; + } else if ((Use & REG_X) != 0) { + return RC->RegX; + } else if ((Use & REG_Y) != 0) { + return RC->RegY; + } else if ((Use & REG_TMP1) != 0) { + return RC->Tmp1; + } else if ((Use & REG_SREG_LO) != 0) { + return RC->SRegLo; + } else if ((Use & REG_SREG_HI) != 0) { + return RC->SRegHi; + } else { + return UNKNOWN_REGVAL; + } +} + + + +/*****************************************************************************/ +/* Replace jumps to RTS by RTS */ +/*****************************************************************************/ + + + +unsigned OptRTSJumps1 (CodeSeg* S) /* Replace jumps to RTS by RTS */ { unsigned Changes = 0; @@ -90,6 +159,64 @@ unsigned OptRTSJumps (CodeSeg* S) +unsigned OptRTSJumps2 (CodeSeg* S) +/* Replace long conditional jumps to RTS */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch to a local target */ + if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ + (E->Info & OF_LBRA) != 0 && /* Long branch */ + E->JumpTo != 0 && /* Local label */ + E->JumpTo->Owner->OPC == OP65_RTS && /* Target is an RTS */ + (N = CS_GetNextEntry (S, I)) != 0) { /* There is a next entry */ + + CodeEntry* X; + CodeLabel* LN; + opc_t NewBranch; + + /* We will create a jump around an RTS instead of the long branch */ + X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->JumpTo->Owner->LI); + CS_InsertEntry (S, X, I+1); + + /* Get the new branch opcode */ + NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); + + /* Get the label attached to N, create a new one if needed */ + LN = CS_GenLabel (S, N); + + /* Generate the branch */ + X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the long branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + /*****************************************************************************/ /* Remove dead jumps */ /*****************************************************************************/ @@ -100,33 +227,24 @@ unsigned OptDeadJumps (CodeSeg* S) /* Remove dead jumps (jumps to the next instruction) */ { unsigned Changes = 0; - CodeEntry* E; - unsigned I; - - /* Get the number of entries, bail out if we have less than two entries */ - unsigned Count = CS_GetEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries minus the last one */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get the next entry */ - E = CS_GetEntry (S, I); + CodeEntry* E = CS_GetEntry (S, I); /* Check if it's a branch, if it has a local target, and if the target * is the next instruction. */ - if (E->AM == AM65_BRA && E->JumpTo && E->JumpTo->Owner == CS_GetEntry (S, I+1)) { + if (E->AM == AM65_BRA && + E->JumpTo && + E->JumpTo->Owner == CS_GetNextEntry (S, I)) { /* Delete the dead jump */ CS_DelEntry (S, I); - /* Keep the number of entries updated */ - --Count; - /* Remember, we had changes */ ++Changes; @@ -145,7 +263,7 @@ unsigned OptDeadJumps (CodeSeg* S) /*****************************************************************************/ -/* Remove dead code */ +/* Remove dead code */ /*****************************************************************************/ @@ -156,36 +274,32 @@ unsigned OptDeadCode (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have less than two entries */ - unsigned Count = CS_GetEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries */ - I = 0; - while (I < Count) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; + CodeLabel* LN; /* Get this entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if it's an unconditional branch, and if the next entry has - * no labels attached + * no labels attached, or if the label is just used so that the insn + * can jump to itself. */ - if ((E->Info & OF_DEAD) != 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_HasLabel (N)) { + if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ + (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ + (!CE_HasLabel (N) || /* Don't has a label */ + ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ + (LN = N->JumpTo) != 0 && /* Jumps to known label */ + LN->Owner == N && /* Attached to insn */ + CL_GetRefCount (LN) == 1))) { /* Only reference */ /* Delete the next entry */ CS_DelEntry (S, I+1); - /* Keep the number of entries updated */ - --Count; - /* Remember, we had changes */ ++Changes; @@ -246,10 +360,28 @@ unsigned OptJumpCascades (CodeSeg* S) ((E->Info & OF_CBRA) != 0 && GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { - /* This is a jump cascade and we may jump to the final target. - * Insert a new instruction, then remove the old one + /* This is a jump cascade and we may jump to the final target, + * provided that the other insn does not jump to itself. If + * this is the case, we can also jump to ourselves, otherwise + * insert a jump to the new instruction and remove the old one. */ - CodeEntry* X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); + CodeEntry* X; + CodeLabel* LN = N->JumpTo; + + if (LN != 0 && LN->Owner == N) { + + /* We found a jump to a jump to itself. Replace our jump + * by a jump to itself. + */ + CodeLabel* LE = CS_GenLabel (S, E); + X = NewCodeEntry (E->OPC, E->AM, LE->Name, LE, E->LI); + + } else { + + /* Jump to the final jump target */ + X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo, E->LI); + + } /* Insert it behind E */ CS_InsertEntry (S, X, I+1); @@ -333,17 +465,10 @@ unsigned OptRTS (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have less than 2 entries */ - unsigned Count = CS_GetEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries minus the last one */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; @@ -392,37 +517,29 @@ unsigned OptJumpTarget (CodeSeg* S) CodeEntry* E1; /* Entry 1 */ CodeEntry* E2; /* Entry 2 */ CodeEntry* T1; /* Jump target entry 1 */ - CodeEntry* T2; /* Jump target entry 2 */ CodeLabel* TL1; /* Target label 1 */ - unsigned TI; /* Target index */ - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = CS_GetEntryCount (S); - if (Count < 3) { - return 0; - } /* Walk over the entries */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get next entry */ - E2 = CS_GetEntry (S, I+1); - - /* Check if we have a jump or branch, and a matching label */ - if ((E2->Info & OF_UBRA) != 0 && E2->JumpTo) { + E2 = CS_GetNextEntry (S, I); - /* Get the target instruction for the label */ - T2 = E2->JumpTo->Owner; - - /* Get the entry preceeding this one (if possible) */ - TI = CS_GetEntryIndex (S, T2); - if (TI == 0) { - /* There is no entry before this one */ + /* Check if we have a jump or branch, and a matching label, which + * is not attached to the jump itself + */ + if (E2 != 0 && + (E2->Info & OF_UBRA) != 0 && + E2->JumpTo && + E2->JumpTo->Owner != E2) { + + /* Get the entry preceeding the branch target */ + T1 = CS_GetPrevEntry (S, CS_GetEntryIndex (S, E2->JumpTo->Owner)); + if (T1 == 0) { + /* There is no such entry */ goto NextEntry; } - T1 = CS_GetEntry (S, TI-1); /* Get the entry preceeding the jump */ E1 = CS_GetEntry (S, I); @@ -451,7 +568,6 @@ unsigned OptJumpTarget (CodeSeg* S) /* Remove the entry preceeding the jump */ CS_DelEntry (S, I); - --Count; /* Remember, we had changes */ ++Changes; @@ -490,17 +606,10 @@ unsigned OptCondBranches (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = CS_GetEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over the entries */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; CodeLabel* L; @@ -527,7 +636,6 @@ unsigned OptCondBranches (CodeSeg* S) /* Remove the conditional branch */ CS_DelEntry (S, I+1); - --Count; /* Remember, we had changes */ ++Changes; @@ -560,7 +668,6 @@ unsigned OptCondBranches (CodeSeg* S) /* Remove the conditional branch */ CS_DelEntry (S, I); - --Count; /* Remember, we had changes */ ++Changes; @@ -579,7 +686,7 @@ unsigned OptCondBranches (CodeSeg* S) /*****************************************************************************/ -/* Remove unused loads */ +/* Remove unused loads and stores */ /*****************************************************************************/ @@ -599,27 +706,33 @@ unsigned OptUnusedLoads (CodeSeg* S) CodeEntry* E = CS_GetEntry (S, I); /* Check if it's a register load or transfer insn */ - if ((E->Info & (OF_LOAD | OF_XFR)) != 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_FBRA) == 0) { + if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { - case OP65_TXA: - case OP65_TYA: - case OP65_LDA: R = REG_A; break; - case OP65_TAX: - case OP65_LDX: R = REG_X; break; - case OP65_TAY: - case OP65_LDY: R = REG_Y; break; - default: goto NextEntry; /* OOPS */ + case OP65_DEA: + case OP65_INA: + case OP65_LDA: + case OP65_TXA: + case OP65_TYA: R = REG_A; break; + case OP65_DEX: + case OP65_INX: + case OP65_LDX: + case OP65_TAX: R = REG_X; break; + case OP65_DEY: + case OP65_INY: + case OP65_LDY: + case OP65_TAY: R = REG_Y; break; + default: goto NextEntry; /* OOPS */ } /* Get register usage and check if the register value is used later */ - if ((GetRegInfo (S, I+1) & R) == 0) { + if ((GetRegInfo (S, I+1, R) & R) == 0) { - /* Register value is not used, remove the load */ + /* Register value is not used, remove the load */ CS_DelEntry (S, I); /* Remember, we had changes */ @@ -628,6 +741,378 @@ unsigned OptUnusedLoads (CodeSeg* S) } } +NextEntry: + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptUnusedStores (CodeSeg* S) +/* Remove stores into zero page registers that aren't used later */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a register load or transfer insn */ + if ((E->Info & OF_STORE) != 0 && + E->AM == AM65_ZP && + (E->Chg & REG_ZP) != 0) { + + /* Check for the zero page location. We know that there cannot be + * more than one zero page location involved in the store. + */ + unsigned R = E->Chg & REG_ZP; + + /* Get register usage and check if the register value is used later */ + if ((GetRegInfo (S, I+1, R) & R) == 0) { + + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptDupLoads (CodeSeg* S) +/* Remove loads of registers where the value loaded is already in the register. */ +{ + unsigned Changes = 0; + unsigned I; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Assume we won't delete the entry */ + int Delete = 0; + + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + + /* Handle the different instructions */ + switch (E->OPC) { + + case OP65_LDA: + if (RegValIsKnown (In->RegA) && /* Value of A is known */ + CE_KnownImm (E) && /* Value to be loaded is known */ + In->RegA == (long) E->Num && /* Both are equal */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_LDX: + if (RegValIsKnown (In->RegX) && /* Value of X is known */ + CE_KnownImm (E) && /* Value to be loaded is known */ + In->RegX == (long) E->Num && /* Both are equal */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_LDY: + if (RegValIsKnown (In->RegY) && /* Value of Y is known */ + CE_KnownImm (E) && /* Value to be loaded is known */ + In->RegY == (long) E->Num && /* Both are equal */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_STA: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegA) && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegA == RegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + } + break; + + case OP65_STX: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegX) && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegX == RegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + + /* If the value in the X register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for X + * later. STX does support the zeropage,y addressing mode, + * so be sure to check for that. + */ + } else if (RegValIsKnown (In->RegX) && + In->RegX == In->RegA && + E->AM != AM65_ABSY && + E->AM != AM65_ZPY) { + /* Use the A register instead */ + CE_ReplaceOPC (E, OP65_STA); + } + break; + + case OP65_STY: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegY) && /* Value of Y is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegY == RegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + + /* If the value in the Y register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for Y + * later. If replacement by A is not possible try a + * replacement by X, but check for invalid addressing modes + * in this case. + */ + } else if (RegValIsKnown (In->RegY)) { + if (In->RegY == In->RegA) { + CE_ReplaceOPC (E, OP65_STA); + } else if (In->RegY == In->RegX && + E->AM != AM65_ABSX && + E->AM != AM65_ZPX) { + CE_ReplaceOPC (E, OP65_STX); + } + } + break; + + case OP65_STZ: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (CPU >= CPU_65C02 && E->AM == AM65_ZP) { + if (RegVal (E->Chg, In) == 0) { + Delete = 1; + } + } + break; + + case OP65_TAX: + if (RegValIsKnown (In->RegA) && + In->RegA == In->RegX && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TAY: + if (RegValIsKnown (In->RegA) && + In->RegA == In->RegY && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TXA: + if (RegValIsKnown (In->RegX) && + In->RegX == In->RegA && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TYA: + if (RegValIsKnown (In->RegY) && + In->RegY == In->RegA && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + default: + break; + + } + + /* Delete the entry if requested */ + if (Delete) { + + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptStoreLoad (CodeSeg* S) +/* Remove a store followed by a load from the same location. */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeEntry* X; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it is a store instruction followed by a load from the + * same address which is itself not followed by a conditional branch. + */ + if ((E->Info & OF_STORE) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (N) && + E->AM == N->AM && + ((E->OPC == OP65_STA && N->OPC == OP65_LDA) || + (E->OPC == OP65_STX && N->OPC == OP65_LDX) || + (E->OPC == OP65_STY && N->OPC == OP65_LDY)) && + strcmp (E->Arg, N->Arg) == 0 && + (X = CS_GetNextEntry (S, I+1)) != 0 && + !CE_UseLoadFlags (X)) { + + /* Register has already the correct value, remove the load */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptTransfers (CodeSeg* S) +/* Remove transfers from one register to another and back */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeEntry* X; + CodeEntry* P; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it is a store instruction followed by a load from the + * same address which is itself not followed by a conditional branch. + */ + if ((E->Info & OF_XFR) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (N) && + (N->Info & OF_XFR) != 0) { + + /* Check if it's a transfer and back */ + if ((E->OPC == OP65_TAX && N->OPC == OP65_TXA && !RegXUsed (S, I+2)) || + (E->OPC == OP65_TAY && N->OPC == OP65_TYA && !RegYUsed (S, I+2)) || + (E->OPC == OP65_TXA && N->OPC == OP65_TAX && !RegAUsed (S, I+2)) || + (E->OPC == OP65_TYA && N->OPC == OP65_TAY && !RegAUsed (S, I+2))) { + + /* If the next insn is a conditional branch, check if the insn + * preceeding the first xfr will set the flags right, otherwise we + * may not remove the sequence. + */ + if ((X = CS_GetNextEntry (S, I+1)) == 0) { + goto NextEntry; + } + if (CE_UseLoadFlags (X)) { + if (I == 0) { + /* No preceeding entry */ + goto NextEntry; + } + P = CS_GetEntry (S, I-1); + if ((P->Info & OF_SETF) == 0) { + /* Does not set the flags */ + goto NextEntry; + } + } + + /* Remove both transfers */ + CS_DelEntry (S, I+1); + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + } + } + NextEntry: /* Next entry */ ++I; @@ -640,6 +1125,87 @@ NextEntry: +unsigned OptPushPop (CodeSeg* S) +/* Remove a PHA/PLA sequence were A is not used later */ +{ + unsigned Changes = 0; + unsigned Push = 0; /* Index of push insn */ + unsigned Pop = 0; /* Index of pop insn */ + enum { + Searching, + FoundPush, + FoundPop + } State = Searching; + + /* Walk over the entries. Look for a push instruction that is followed by + * a pop later, where the pop is not followed by an conditional branch, + * and where the value of the A register is not used later on. + * Look out for the following problems: + * + * - There may be another PHA/PLA inside the sequence: Restart it. + * - If the PLA has a label, all jumps to this label must be inside + * the sequence, otherwise we cannot remove the PHA/PLA. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Searching: + if (E->OPC == OP65_PHA) { + /* Found start of sequence */ + Push = I; + State = FoundPush; + } + break; + + case FoundPush: + if (E->OPC == OP65_PHA) { + /* Inner push/pop, restart */ + Push = I; + } else if (E->OPC == OP65_PLA) { + /* Found a matching pop */ + Pop = I; + State = FoundPop; + } + break; + + case FoundPop: + /* Next insn, just check if it is no conditional branch and + * that A is not used later. Check also that the range we have + * found now is a basic block, which means that the PHA is the + * only entrance and the PLA the only exit. + */ + if ((E->Info & OF_CBRA) == 0 && + !RegAUsed (S, I) && + CS_IsBasicBlock (S, Push, Pop)) { + /* We can remove the PHA and PLA instructions */ + CS_DelEntry (S, Pop); + CS_DelEntry (S, Push); + /* Correct I so we continue with the next insn */ + I -= 2; + /* Remember we had changes */ + ++Changes; + } + /* Go into search mode again */ + State = Searching; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + /*****************************************************************************/ /* Optimize branch types */ /*****************************************************************************/ @@ -650,51 +1216,29 @@ unsigned OptBranchDist (CodeSeg* S) /* Change branches for the distance needed. */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = CS_GetEntryCount (S); /* Walk over the entries */ - I = 0; - while (I < Count) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if it's a conditional branch to a local label. */ - if ((E->Info & OF_CBRA) != 0) { + if (E->Info & OF_CBRA) { /* Is this a branch to a local symbol? */ if (E->JumpTo != 0) { - /* Get the index of the branch target */ - unsigned TI = CS_GetEntryIndex (S, E->JumpTo->Owner); - - /* Determine the branch distance */ - int Distance = 0; - if (TI >= I) { - /* Forward branch */ - unsigned J = I; - while (J < TI) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance += N->Size; - } - } else { - /* Backward branch */ - unsigned J = TI; - while (J < I) { - CodeEntry* N = CS_GetEntry (S, J++); - Distance += N->Size; - } - } + /* Check if the branch distance is short */ + int IsShort = IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner)); /* Make the branch short/long according to distance */ - if ((E->Info & OF_LBRA) == 0 && Distance > 120) { + if ((E->Info & OF_LBRA) == 0 && !IsShort) { /* Short branch but long distance */ CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); ++Changes; - } else if ((E->Info & OF_LBRA) != 0 && Distance < 120) { + } else if ((E->Info & OF_LBRA) != 0 && IsShort) { /* Long branch but short distance */ CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); ++Changes; @@ -707,6 +1251,15 @@ unsigned OptBranchDist (CodeSeg* S) ++Changes; } + + } else if (CPU == CPU_65C02 && + (E->Info & OF_UBRA) != 0 && + E->JumpTo != 0 && + IsShortDist (GetBranchDist (S, I, E->JumpTo->Owner))) { + + /* The jump is short and may be replaced by a BRA on the 65C02 CPU */ + CE_ReplaceOPC (E, OP65_BRA); + ++Changes; } /* Next entry */