diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj index d566c8bc1..1015b389f 100644 --- a/src/cc65.vcxproj +++ b/src/cc65.vcxproj @@ -93,6 +93,8 @@ + + @@ -167,6 +169,8 @@ + + diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 69ac35005..4a7722830 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -48,7 +48,6 @@ #include "xsprintf.h" /* cc65 */ -#include "asmlabel.h" #include "codeent.h" #include "codeinfo.h" #include "codeopt.h" @@ -56,6 +55,8 @@ #include "coptc02.h" #include "coptcmp.h" #include "coptind.h" +#include "coptjmp.h" +#include "coptmisc.h" #include "coptneg.h" #include "coptptrload.h" #include "coptptrstore.h" @@ -69,660 +70,8 @@ #include "error.h" #include "global.h" #include "output.h" -#include "symtab.h" -/*****************************************************************************/ -/* Optimize loads */ -/*****************************************************************************/ - - - -static unsigned OptLoad1 (CodeSeg* S) -/* Search for a call to ldaxysp where X is not used later and replace it by -** a load of just the A register. -*/ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* E; - - /* Get next entry */ - E = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (E, "ldaxysp") && - RegValIsKnown (E->RI->In.RegY) && - !RegXUsed (S, I+1)) { - - CodeEntry* X; - - /* Reload the Y register */ - const char* Arg = MakeHexArg (E->RI->In.RegY - 1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Load from stack */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); - CS_InsertEntry (S, X, I+2); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad2 (CodeSeg* S) -/* Replace calls to ldaxysp by inline code */ -{ - unsigned I; - unsigned Changes = 0; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[3]; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence */ - if (CE_IsCallTo (L[0], "ldaxysp")) { - - CodeEntry* X; - - /* Followed by sta abs/stx abs? */ - if (CS_GetEntries (S, L+1, I+1, 2) && - L[1]->OPC == OP65_STA && - L[2]->OPC == OP65_STX && - (L[1]->Arg == 0 || - L[2]->Arg == 0 || - strcmp (L[1]->Arg, L[2]->Arg) != 0) && - !CS_RangeHasLabel (S, I+1, 2) && - !RegXUsed (S, I+3)) { - - /* A/X are stored into memory somewhere and X is not used - ** later - */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); - CS_InsertEntry (S, X, I+4); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+5); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+6); - - /* sta abs */ - X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I+7); - - /* Now remove the call to the subroutine and the sta/stx */ - CS_DelEntries (S, I, 3); - - } else { - - /* Standard replacement */ - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+1); - - /* tax */ - X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+2); - - /* dey */ - X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); - CS_InsertEntry (S, X, I+3); - - /* lda (sp),y */ - X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); - CS_InsertEntry (S, X, I+4); - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - } - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -static unsigned OptLoad3 (CodeSeg* S) -/* Remove repeated loads from one and the same memory location */ -{ - unsigned Changes = 0; - CodeEntry* Load = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Forget a preceeding load if we have a label */ - if (Load && CE_HasLabel (E)) { - Load = 0; - } - - /* Check if this insn is a load */ - if (E->Info & OF_LOAD) { - - CodeEntry* N; - - /* If we had a preceeding load that is identical, remove this one. - ** If it is not identical, or we didn't have one, remember it. - */ - if (Load != 0 && - E->OPC == Load->OPC && - E->AM == Load->AM && - ((E->Arg == 0 && Load->Arg == 0) || - strcmp (E->Arg, Load->Arg) == 0) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_CBRA) == 0) { - - /* Now remove the call to the subroutine */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Next insn */ - continue; - - } else { - - Load = E; - - } - - } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { - /* Forget the first load on occurance of any insn we don't like */ - Load = 0; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Decouple operations */ -/*****************************************************************************/ - - - -static unsigned OptDecouple (CodeSeg* S) -/* Decouple operations, that is, do the following replacements: -** -** dex -> ldx #imm -** inx -> ldx #imm -** dey -> ldy #imm -** iny -> ldy #imm -** tax -> ldx #imm -** txa -> lda #imm -** tay -> ldy #imm -** tya -> lda #imm -** lda zp -> lda #imm -** ldx zp -> ldx #imm -** ldy zp -> ldy #imm -** -** Provided that the register values are known of course. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - const char* Arg; - - /* Get next entry and it's input register values */ - CodeEntry* E = CS_GetEntry (S, I); - const RegContents* In = &E->RI->In; - - /* Assume we have no replacement */ - CodeEntry* X = 0; - - /* Check the instruction */ - switch (E->OPC) { - - case OP65_DEA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA - 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX - 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_DEY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY - 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INA: - if (RegValIsKnown (In->RegA)) { - Arg = MakeHexArg ((In->RegA + 1) & 0xFF); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INX: - if (RegValIsKnown (In->RegX)) { - Arg = MakeHexArg ((In->RegX + 1) & 0xFF); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_INY: - if (RegValIsKnown (In->RegY)) { - Arg = MakeHexArg ((In->RegY + 1) & 0xFF); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_LDA: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDX: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use & REG_ZP, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_LDY: - if (E->AM == AM65_ZP) { - switch (GetKnownReg (E->Use, In)) { - case REG_TMP1: - Arg = MakeHexArg (In->Tmp1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_LO: - Arg = MakeHexArg (In->Ptr1Lo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_PTR1_HI: - Arg = MakeHexArg (In->Ptr1Hi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_LO: - Arg = MakeHexArg (In->SRegLo); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - - case REG_SREG_HI: - Arg = MakeHexArg (In->SRegHi); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - break; - } - } - break; - - case OP65_TAX: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TAY: - if (E->RI->In.RegA >= 0) { - Arg = MakeHexArg (In->RegA); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TXA: - if (E->RI->In.RegX >= 0) { - Arg = MakeHexArg (In->RegX); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - case OP65_TYA: - if (E->RI->In.RegY >= 0) { - Arg = MakeHexArg (In->RegY); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); - } - break; - - default: - /* Avoid gcc warnings */ - break; - - } - - /* Insert the replacement if we have one */ - if (X) { - CS_InsertEntry (S, X, I+1); - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize stack pointer ops */ -/*****************************************************************************/ - - - -static unsigned IsDecSP (const CodeEntry* E) -/* Check if this is an insn that decrements the stack pointer. If so, return -** the decrement. If not, return zero. -** The function expects E to be a subroutine call. -*/ -{ - if (strncmp (E->Arg, "decsp", 5) == 0) { - if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { - return (E->Arg[5] - '0'); - } - } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { - return E->RI->In.RegY; - } - - /* If we come here, it's not a decsp op */ - return 0; -} - - - -static unsigned OptStackPtrOps (CodeSeg* S) -/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all -** known cases! -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - unsigned Dec1; - unsigned Dec2; - const CodeEntry* N; - - /* Get the next entry */ - const CodeEntry* E = CS_GetEntry (S, I); - - /* Check for decspn or subysp */ - if (E->OPC == OP65_JSR && - (Dec1 = IsDecSP (E)) > 0 && - (N = CS_GetNextEntry (S, I)) != 0 && - (Dec2 = IsDecSP (N)) > 0 && - (Dec1 += Dec2) <= 255 && - !CE_HasLabel (N)) { - - CodeEntry* X; - char Buf[20]; - - /* We can combine the two */ - if (Dec1 <= 8) { - /* Insert a call to decsp */ - xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); - CS_InsertEntry (S, X, I+2); - } else { - /* Insert a call to subysp */ - const char* Arg = MakeHexArg (Dec1); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); - CS_InsertEntry (S, X, I+2); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); - CS_InsertEntry (S, X, I+3); - } - - /* Delete the old code */ - CS_DelEntries (S, I, 2); - - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} - -static unsigned OptGotoSPAdj (CodeSeg* S) -/* Optimize SP adjustment for forward 'goto' */ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* L[10], *X; - unsigned short adjustment; - const char* Arg; - - /* Get next entry */ - L[0] = CS_GetEntry (S, I); - - /* Check for the sequence generated by g_lateadjustSP */ - if (L[0]->OPC == OP65_PHA && - CS_GetEntries (S, L+1, I+1, 9) && - L[1]->OPC == OP65_LDA && - L[1]->AM == AM65_ABS && - L[2]->OPC == OP65_CLC && - L[3]->OPC == OP65_ADC && - strcmp (L[3]->Arg, "sp") == 0 && - L[6]->OPC == OP65_ADC && - strcmp (L[6]->Arg, "sp+1") == 0 && - L[9]->OPC == OP65_JMP) { - adjustment = FindSPAdjustment (L[1]->Arg); - - if (adjustment == 0) { - /* No SP adjustment needed, remove the whole sequence */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 8) { - /* If adjustment is in range [-8, 0) we use decsp* calls */ - char Buf[20]; - adjustment = 65536 - adjustment; - xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment >= 65536 - 255) { - /* For range [-255, -8) we have ldy #, jsr subysp */ - adjustment = 65536 - adjustment; - Arg = MakeHexArg (adjustment); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else if (adjustment > 255) { - /* For ranges [-32768, 255) and (255, 32767) the only modification - ** is to replace the absolute with immediate addressing - */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 1); - Arg = MakeHexArg (adjustment >> 8); - X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); - CS_InsertEntry (S, X, I + 6); - - /* Delete the old code */ - CS_DelEntry (S, I + 2); - CS_DelEntry (S, I + 6); - } - else if (adjustment > 8) { - /* For range (8, 255] we have ldy #, jsr addysp */ - Arg = MakeHexArg (adjustment & 0xff); - X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); - CS_InsertEntry (S, X, I + 10); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - else { - /* If adjustment is in range (0, 8] we use incsp* calls */ - char Buf[20]; - xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); - X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); - CS_InsertEntry (S, X, I + 9); - - /* Delete the old code */ - CS_DelEntries (S, I, 9); - } - /* Regenerate register info */ - CS_GenRegInfo (S); - - /* Remember we had changes */ - Changes++; - - } else { - - /* Next entry */ - ++I; - } - - } - - /* Return the number of changes made */ - return Changes; -} /*****************************************************************************/ /* struct OptFunc */ diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 811adff04..b506a21ca 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -124,46 +124,6 @@ static int MemAccess (CodeSeg* S, unsigned From, unsigned To, const CodeEntry* N -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 ZPRegVal (unsigned short Use, const RegContents* RC) /* Return the contents of the given zeropage register */ { @@ -184,838 +144,6 @@ static short ZPRegVal (unsigned short Use, const RegContents* RC) -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 { - return ZPRegVal (Use, RC); - } -} - - - -/*****************************************************************************/ -/* Replace jumps to RTS by RTS */ -/*****************************************************************************/ - - - -unsigned OptRTSJumps1 (CodeSeg* S) -/* Replace jumps to RTS by RTS */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* 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_UBRA) != 0 && - E->JumpTo != 0 && - E->JumpTo->Owner->OPC == OP65_RTS) { - - /* Insert an RTS instruction */ - CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Delete the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptRTSJumps2 (CodeSeg* S) -/* Replace long conditional jumps to RTS or to a final target */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S) - 1) { - - /* Get the next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's an conditional 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 */ - - - /* Get the jump target and the next entry. There's always a next - ** entry, because we don't cover the last entry in the loop. - */ - CodeEntry* X = 0; - CodeEntry* T = E->JumpTo->Owner; - CodeEntry* N = CS_GetNextEntry (S, I); - - /* Check if it's a jump to an RTS insn */ - if (T->OPC == OP65_RTS) { - - /* It's a jump to RTS. Create a conditional branch around an - ** RTS insn. - */ - X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); - - } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { - - /* It's a jump to a label outside the function. Create a - ** conditional branch around a jump to the external label. - */ - X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); - - } - - /* If we have a replacement insn, insert it */ - if (X) { - - CodeLabel* LN; - opc_t NewBranch; - - /* Insert the new insn */ - CS_InsertEntry (S, X, I+1); - - /* Create a conditional branch with the inverse condition - ** around the replacement insn - */ - - /* 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 */ -/*****************************************************************************/ - - - -unsigned OptDeadJumps (CodeSeg* S) -/* Remove dead jumps (jumps to the next instruction) */ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get the next entry */ - 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_GetNextEntry (S, I)) { - - /* Delete the dead jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Remove dead code */ -/*****************************************************************************/ - - - -unsigned OptDeadCode (CodeSeg* S) -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - 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, or if the label is just used so that the insn - ** can jump to itself. - */ - 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); - - /* Remember, we had changes */ - ++Changes; - - } else { - - /* Next entry */ - ++I; - - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump cascades */ -/*****************************************************************************/ - - - -unsigned OptJumpCascades (CodeSeg* S) -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* OldLabel; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check: - ** - if it's a branch, - ** - if it has a jump label, - ** - if this jump label is not attached to the instruction itself, - ** - if the target instruction is itself a branch, - ** - if either the first branch is unconditional or the target of - ** the second branch is internal to the function. - ** The latter condition will avoid conditional branches to targets - ** outside of the function (usually incspx), which won't simplify the - ** code, since conditional far branches are emulated by a short branch - ** around a jump. - */ - if ((E->Info & OF_BRA) != 0 && - (OldLabel = E->JumpTo) != 0 && - (N = OldLabel->Owner) != E && - (N->Info & OF_BRA) != 0 && - ((E->Info & OF_CBRA) == 0 || - N->JumpTo != 0)) { - - /* Check if we can use the final target label. That is the case, - ** if the target branch is an absolute branch; or, if it is a - ** conditional branch checking the same condition as the first one. - */ - if ((N->Info & OF_UBRA) != 0 || - ((E->Info & OF_CBRA) != 0 && - GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { - - /* 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; - 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); - - /* Remove E */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - /* Check if both are conditional branches, and the condition of - ** the second is the inverse of that of the first. In this case, - ** the second branch will never be taken, and we may jump directly - ** to the instruction behind this one. - */ - } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { - - CodeEntry* X; /* Instruction behind N */ - CodeLabel* LX; /* Label attached to X */ - - /* Get the branch conditions of both branches */ - bc_t BC1 = GetBranchCond (E->OPC); - bc_t BC2 = GetBranchCond (N->OPC); - - /* Check the branch conditions */ - if (BC1 != GetInverseCond (BC2)) { - /* Condition not met */ - goto NextEntry; - } - - /* We may jump behind this conditional branch. Get the - ** pointer to the next instruction - */ - if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { - /* N is the last entry, bail out */ - goto NextEntry; - } - - /* Get the label attached to X, create a new one if needed */ - LX = CS_GenLabel (S, X); - - /* Move the reference from E to the new label */ - CS_MoveLabelRef (S, E, LX); - - /* Remember, we had changes */ - ++Changes; - } - } - -NextEntry: - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jsr/rts */ -/*****************************************************************************/ - - - -unsigned OptRTS (CodeSeg* S) -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ -{ - unsigned Changes = 0; - - /* Walk over all entries minus the last one */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get this entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a subroutine call and if the following insn is RTS */ - if (E->OPC == OP65_JSR && - (N = CS_GetNextEntry (S, I)) != 0 && - N->OPC == OP65_RTS) { - - /* Change the jsr to a jmp and use the additional info for a jump */ - E->AM = AM65_BRA; - CE_ReplaceOPC (E, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize jump targets */ -/*****************************************************************************/ - - - -unsigned OptJumpTarget1 (CodeSeg* S) -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ -{ - unsigned Changes = 0; - CodeEntry* E1; /* Entry 1 */ - CodeEntry* E2; /* Entry 2 */ - CodeEntry* T1; /* Jump target entry 1 */ - CodeLabel* TL1; /* Target label 1 */ - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - E2 = CS_GetNextEntry (S, I); - - /* Check if we have a jump or branch without a label attached, and - ** a jump target, which is not attached to the jump itself - */ - if (E2 != 0 && - (E2->Info & OF_UBRA) != 0 && - !CE_HasLabel (E2) && - 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; - } - - /* The entry preceeding the branch target may not be the branch - ** insn. - */ - if (T1 == E2) { - goto NextEntry; - } - - /* Get the entry preceeding the jump */ - E1 = CS_GetEntry (S, I); - - /* Check if both preceeding instructions are identical */ - if (!CodeEntriesAreEqual (E1, T1)) { - /* Not equal, try next */ - goto NextEntry; - } - - /* Get the label for the instruction preceeding the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - TL1 = CS_GenLabel (S, T1); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E2, TL1); - - /* If the instruction preceeding the jump has labels attached, - ** move references to this label to the new label. - */ - if (CE_HasLabel (E1)) { - CS_MoveLabels (S, E1, T1); - } - - /* Remove the entry preceeding the jump */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } else { -NextEntry: - /* Next entry */ - ++I; - } - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget2 (CodeSeg* S) -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - /* OP that may be skipped */ - opc_t OPC; - - /* Jump target insn, old and new */ - CodeEntry* T; - CodeEntry* N; - - /* New jump label */ - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a bcc insn */ - if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { - OPC = OP65_CLC; - } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { - OPC = OP65_SEC; - } else { - /* Not what we're looking for */ - goto NextEntry; - } - - /* Must have a jump target */ - if (E->JumpTo == 0) { - goto NextEntry; - } - - /* Get the owner insn of the jump target and check if it's the one, we - ** will skip if present. - */ - T = E->JumpTo->Owner; - if (T->OPC != OPC) { - goto NextEntry; - } - - /* Get the entry following the branch target */ - N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); - if (N == 0) { - /* There is no such entry */ - goto NextEntry; - } - - /* Get the label for the instruction following the jump target. - ** This routine will create a new label if the instruction does - ** not already have one. - */ - L = CS_GenLabel (S, N); - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, E, L); - - /* Remember that we had changes */ - ++Changes; - -NextEntry: - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptJumpTarget3 (CodeSeg* S) -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if this is a load insn with a label and the next insn is not - ** a conditional branch that needs the flags from the load. - */ - if ((E->Info & OF_LOAD) != 0 && - CE_IsConstImm (E) && - CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - !CE_UseLoadFlags (N)) { - - unsigned J; - int K; - - /* New jump label */ - CodeLabel* LN = 0; - - /* Walk over all insn that jump here */ - for (J = 0; J < CE_GetLabelCount (E); ++J) { - - /* Get the label */ - CodeLabel* L = CE_GetLabel (E, J); - - /* Loop over all insn that reference this label. Since we may - ** eventually remove a reference in the loop, we must loop - ** from end down to start. - */ - for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { - - /* Get the entry that jumps here */ - CodeEntry* Jump = CL_GetRef (L, K); - - /* Get the register info from this insn */ - short Val = RegVal (E->Chg, &Jump->RI->Out2); - - /* Check if the outgoing value is the one thats's loaded */ - if (Val == (unsigned char) E->Num) { - - /* OK, skip the insn. First, generate a label for the - ** next insn after E. - */ - if (LN == 0) { - LN = CS_GenLabel (S, N); - } - - /* Change the jump target to point to this new label */ - CS_MoveLabelRef (S, Jump, LN); - - /* Remember that we had changes */ - ++Changes; - } - } - } - - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize conditional branches */ -/*****************************************************************************/ - - - -unsigned OptCondBranches1 (CodeSeg* S) -/* Performs several optimization steps: -** -** - If an immediate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -** - If the conditional branch is always taken because of the register load, -** replace it by a jmp. -** - If a conditional branch jumps around an unconditional branch, remove the -** conditional branch and make the jump a conditional branch with the -** inverse condition of the first one. -*/ -{ - unsigned Changes = 0; - - /* Walk over the entries */ - unsigned I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - CodeLabel* L; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a register load */ - if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ - E->AM == AM65_IMM && /* ..with immidiate addressing */ - (E->Flags & CEF_NUMARG) != 0 && /* ..and a numeric argument. */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ - !CE_HasLabel (N)) { /* ..and does not have a label */ - - /* Get the branch condition */ - bc_t BC = GetBranchCond (N->OPC); - - /* Check the argument against the branch condition */ - if ((BC == BC_EQ && E->Num != 0) || - (BC == BC_NE && E->Num == 0) || - (BC == BC_PL && (E->Num & 0x80) != 0) || - (BC == BC_MI && (E->Num & 0x80) == 0)) { - - /* Remove the conditional branch */ - CS_DelEntry (S, I+1); - - /* Remember, we had changes */ - ++Changes; - - } else if ((BC == BC_EQ && E->Num == 0) || - (BC == BC_NE && E->Num != 0) || - (BC == BC_PL && (E->Num & 0x80) == 0) || - (BC == BC_MI && (E->Num & 0x80) != 0)) { - - /* The branch is always taken, replace it by a jump */ - CE_ReplaceOPC (N, OP65_JMP); - - /* Remember, we had changes */ - ++Changes; - } - - } - - if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ - (L = E->JumpTo) != 0 && /* ..referencing a local label */ - (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ - !CE_HasLabel (N) && /* ..has no label attached */ - L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ - - /* Replace the jump by a conditional branch with the inverse branch - ** condition than the branch around it. - */ - CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); - - /* Remove the conditional branch */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptCondBranches2 (CodeSeg* S) -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry flag. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - CodeEntry* N; - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's a rol insn with A in accu and a branch follows */ - if (E->OPC == OP65_ROL && - E->AM == AM65_ACC && - E->RI->In.RegA == 0 && - !CE_HasLabel (E) && - (N = CS_GetNextEntry (S, I)) != 0 && - (N->Info & OF_ZBRA) != 0 && - !RegAUsed (S, I+1)) { - - /* Replace the branch condition */ - switch (GetBranchCond (N->OPC)) { - case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; - case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; - default: Internal ("Unknown branch condition in OptCondBranches2"); - } - - /* Delete the rol insn */ - CS_DelEntry (S, I); - - /* Remember, we had changes */ - ++Changes; - } - - /* Next entry */ - ++I; - } - - /* Return the number of changes made */ - return Changes; -} - - - /*****************************************************************************/ /* Remove unused loads and stores */ /*****************************************************************************/ @@ -1132,6 +260,70 @@ unsigned OptUnusedStores (CodeSeg* S) +unsigned OptLoad3 (CodeSeg* S) +/* Remove repeated loads from one and the same memory location */ +{ + unsigned Changes = 0; + CodeEntry* Load = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Forget a preceeding load if we have a label */ + if (Load && CE_HasLabel (E)) { + Load = 0; + } + + /* Check if this insn is a load */ + if (E->Info & OF_LOAD) { + + CodeEntry* N; + + /* If we had a preceeding load that is identical, remove this one. + ** If it is not identical, or we didn't have one, remember it. + */ + if (Load != 0 && + E->OPC == Load->OPC && + E->AM == Load->AM && + ((E->Arg == 0 && Load->Arg == 0) || + strcmp (E->Arg, Load->Arg) == 0) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_CBRA) == 0) { + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Next insn */ + continue; + + } else { + + Load = E; + + } + + } else if ((E->Info & OF_CMP) == 0 && (E->Info & OF_CBRA) == 0) { + /* Forget the first load on occurance of any insn we don't like */ + Load = 0; + } + + /* 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. */ { @@ -2138,172 +1330,3 @@ unsigned OptPrecalc (CodeSeg* S) /* Return the number of changes made */ return Changes; } - - - -/*****************************************************************************/ -/* Optimize branch types */ -/*****************************************************************************/ - - - -unsigned OptBranchDist (CodeSeg* S) -/* Change branches for the distance needed. */ -{ - 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 conditional branch to a local label. */ - if (E->Info & OF_CBRA) { - - /* Is this a branch to a local symbol? */ - if (E->JumpTo != 0) { - - /* 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 && !IsShort) { - /* Short branch but long distance */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - } else if ((E->Info & OF_LBRA) != 0 && IsShort) { - /* Long branch but short distance */ - CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); - ++Changes; - } - - } else if ((E->Info & OF_LBRA) == 0) { - - /* Short branch to external symbol - make it long */ - CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - - } - - } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && - (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 */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -/*****************************************************************************/ -/* Optimize indirect loads */ -/*****************************************************************************/ - - - -unsigned OptIndLoads1 (CodeSeg* S) -/* Change -** -** lda (zp),y -** -** into -** -** lda (zp,x) -** -** provided that x and y are both zero. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZP_INDY && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { - - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} - - - -unsigned OptIndLoads2 (CodeSeg* S) -/* Change -** -** lda (zp,x) -** -** into -** -** lda (zp),y -** -** provided that x and y are both zero. -*/ -{ - unsigned Changes = 0; - unsigned I; - - /* Walk over the entries */ - I = 0; - while (I < CS_GetEntryCount (S)) { - - /* Get next entry */ - CodeEntry* E = CS_GetEntry (S, I); - - /* Check if it's what we're looking for */ - if (E->OPC == OP65_LDA && - E->AM == AM65_ZPX_IND && - E->RI->In.RegY == 0 && - E->RI->In.RegX == 0) { - - /* Replace by the same insn with other addressing mode */ - CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); - CS_InsertEntry (S, X, I+1); - - /* Remove the old insn */ - CS_DelEntry (S, I); - ++Changes; - } - - /* Next entry */ - ++I; - - } - - /* Return the number of changes made */ - return Changes; -} diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 90e27d547..64acb10d8 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -49,69 +49,15 @@ -unsigned OptRTSJumps1 (CodeSeg* S); -/* Replace jumps to RTS by RTS */ - -unsigned OptRTSJumps2 (CodeSeg* S); -/* Replace long conditional jumps to RTS */ - -unsigned OptDeadJumps (CodeSeg* S); -/* Remove dead jumps (jumps to the next instruction) */ - -unsigned OptDeadCode (CodeSeg* S); -/* Remove dead code (code that follows an unconditional jump or an rts/rti -** and has no label) -*/ - -unsigned OptJumpCascades (CodeSeg* S); -/* Optimize jump cascades (jumps to jumps). In such a case, the jump is -** replaced by a jump to the final location. This will in some cases produce -** worse code, because some jump targets are no longer reachable by short -** branches, but this is quite rare, so there are more advantages than -** disadvantages. -*/ - -unsigned OptRTS (CodeSeg* S); -/* Optimize subroutine calls followed by an RTS. The subroutine call will get -** replaced by a jump. Don't bother to delete the RTS if it does not have a -** label, the dead code elimination should take care of it. -*/ - -unsigned OptJumpTarget1 (CodeSeg* S); -/* If the instruction preceeding an unconditional branch is the same as the -** instruction preceeding the jump target, the jump target may be moved -** one entry back. This is a size optimization, since the instruction before -** the branch gets removed. -*/ - -unsigned OptJumpTarget2 (CodeSeg* S); -/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since -** it's job is already done. -*/ - -unsigned OptJumpTarget3 (CodeSeg* S); -/* Jumps to load instructions of a register, that do already have the matching -** register contents may skip the load instruction, since it's job is already -** done. -*/ - -unsigned OptCondBranches1 (CodeSeg* S); -/* If an immidiate load of a register is followed by a conditional jump that -** is never taken because the load of the register sets the flags in such a -** manner, remove the conditional branch. -*/ - -unsigned OptCondBranches2 (CodeSeg* S); -/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, -** we can remove the rol and branch on the state of the carry. -*/ - unsigned OptUnusedLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is not used later. */ unsigned OptUnusedStores (CodeSeg* S); /* Remove stores into zero page registers that aren't used later */ +unsigned OptLoad3 (CodeSeg* S); +/* Remove repeated loads from one and the same memory location */ + unsigned OptDupLoads (CodeSeg* S); /* Remove loads of registers where the value loaded is already in the register. */ @@ -144,33 +90,6 @@ unsigned OptPrecalc (CodeSeg* S); ** known by a load of the final value. */ -unsigned OptBranchDist (CodeSeg* S); -/* Change branches for the distance needed. */ - -unsigned OptIndLoads1 (CodeSeg* S); -/* Change -** -** lda (zp),y -** -** into -** -** lda (zp,x) -** -** provided that x and y are both zero. -*/ - -unsigned OptIndLoads2 (CodeSeg* S); -/* Change -** -** lda (zp,x) -** -** into -** -** lda (zp),y -** -** provided that x and y are both zero. -*/ - /* End of coptind.h */ diff --git a/src/cc65/coptjmp.c b/src/cc65/coptjmp.c new file mode 100644 index 000000000..693c7eb79 --- /dev/null +++ b/src/cc65/coptjmp.c @@ -0,0 +1,1009 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.c */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +/* common */ +#include "cpu.h" + +/* cc65 */ +#include "codeent.h" +#include "coptjmp.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" + + + +/*****************************************************************************/ +/* Helper functions */ +/*****************************************************************************/ + + + +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 ZPRegVal (unsigned short Use, const RegContents* RC) +/* Return the contents of the given zeropage register */ +{ + if ((Use & REG_TMP1) != 0) { + return RC->Tmp1; + } else if ((Use & REG_PTR1_LO) != 0) { + return RC->Ptr1Lo; + } else if ((Use & REG_PTR1_HI) != 0) { + return RC->Ptr1Hi; + } else if ((Use & REG_SREG_LO) != 0) { + return RC->SRegLo; + } else if ((Use & REG_SREG_HI) != 0) { + return RC->SRegHi; + } else { + return UNKNOWN_REGVAL; + } +} + + + +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 { + return ZPRegVal (Use, RC); + } +} + + + +/*****************************************************************************/ +/* Optimize branch types */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S) +/* Change branches for the distance needed. */ +{ + 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 conditional branch to a local label. */ + if (E->Info & OF_CBRA) { + + /* Is this a branch to a local symbol? */ + if (E->JumpTo != 0) { + + /* 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 && !IsShort) { + /* Short branch but long distance */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + } else if ((E->Info & OF_LBRA) != 0 && IsShort) { + /* Long branch but short distance */ + CE_ReplaceOPC (E, MakeShortBranch (E->OPC)); + ++Changes; + } + + } else if ((E->Info & OF_LBRA) == 0) { + + /* Short branch to external symbol - make it long */ + CE_ReplaceOPC (E, MakeLongBranch (E->OPC)); + ++Changes; + + } + + } else if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && + (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 */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Replace jumps to RTS by RTS */ +/*****************************************************************************/ + + + +unsigned OptRTSJumps1 (CodeSeg* S) +/* Replace jumps to RTS by RTS */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* 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_UBRA) != 0 && + E->JumpTo != 0 && + E->JumpTo->Owner->OPC == OP65_RTS) { + + /* Insert an RTS instruction */ + CodeEntry* X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptRTSJumps2 (CodeSeg* S) +/* Replace long conditional jumps to RTS or to a final target */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S) - 1) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an conditional 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 */ + + + /* Get the jump target and the next entry. There's always a next + ** entry, because we don't cover the last entry in the loop. + */ + CodeEntry* X = 0; + CodeEntry* T = E->JumpTo->Owner; + CodeEntry* N = CS_GetNextEntry (S, I); + + /* Check if it's a jump to an RTS insn */ + if (T->OPC == OP65_RTS) { + + /* It's a jump to RTS. Create a conditional branch around an + ** RTS insn. + */ + X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, T->LI); + + } else if (T->OPC == OP65_JMP && T->JumpTo == 0) { + + /* It's a jump to a label outside the function. Create a + ** conditional branch around a jump to the external label. + */ + X = NewCodeEntry (OP65_JMP, AM65_ABS, T->Arg, T->JumpTo, T->LI); + + } + + /* If we have a replacement insn, insert it */ + if (X) { + + CodeLabel* LN; + opc_t NewBranch; + + /* Insert the new insn */ + CS_InsertEntry (S, X, I+1); + + /* Create a conditional branch with the inverse condition + ** around the replacement insn + */ + + /* 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 */ +/*****************************************************************************/ + + + +unsigned OptDeadJumps (CodeSeg* S) +/* Remove dead jumps (jumps to the next instruction) */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + 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_GetNextEntry (S, I)) { + + /* Delete the dead jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Remove dead code */ +/*****************************************************************************/ + + + +unsigned OptDeadCode (CodeSeg* S) +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + 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, or if the label is just used so that the insn + ** can jump to itself. + */ + 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); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump cascades */ +/*****************************************************************************/ + + + +unsigned OptJumpCascades (CodeSeg* S) +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* OldLabel; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check: + ** - if it's a branch, + ** - if it has a jump label, + ** - if this jump label is not attached to the instruction itself, + ** - if the target instruction is itself a branch, + ** - if either the first branch is unconditional or the target of + ** the second branch is internal to the function. + ** The latter condition will avoid conditional branches to targets + ** outside of the function (usually incspx), which won't simplify the + ** code, since conditional far branches are emulated by a short branch + ** around a jump. + */ + if ((E->Info & OF_BRA) != 0 && + (OldLabel = E->JumpTo) != 0 && + (N = OldLabel->Owner) != E && + (N->Info & OF_BRA) != 0 && + ((E->Info & OF_CBRA) == 0 || + N->JumpTo != 0)) { + + /* Check if we can use the final target label. That is the case, + ** if the target branch is an absolute branch; or, if it is a + ** conditional branch checking the same condition as the first one. + */ + if ((N->Info & OF_UBRA) != 0 || + ((E->Info & OF_CBRA) != 0 && + GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { + + /* 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; + 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); + + /* Remove E */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + /* Check if both are conditional branches, and the condition of + ** the second is the inverse of that of the first. In this case, + ** the second branch will never be taken, and we may jump directly + ** to the instruction behind this one. + */ + } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { + + CodeEntry* X; /* Instruction behind N */ + CodeLabel* LX; /* Label attached to X */ + + /* Get the branch conditions of both branches */ + bc_t BC1 = GetBranchCond (E->OPC); + bc_t BC2 = GetBranchCond (N->OPC); + + /* Check the branch conditions */ + if (BC1 != GetInverseCond (BC2)) { + /* Condition not met */ + goto NextEntry; + } + + /* We may jump behind this conditional branch. Get the + ** pointer to the next instruction + */ + if ((X = CS_GetNextEntry (S, CS_GetEntryIndex (S, N))) == 0) { + /* N is the last entry, bail out */ + goto NextEntry; + } + + /* Get the label attached to X, create a new one if needed */ + LX = CS_GenLabel (S, X); + + /* Move the reference from E to the new label */ + CS_MoveLabelRef (S, E, LX); + + /* Remember, we had changes */ + ++Changes; + } + } + +NextEntry: + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jsr/rts */ +/*****************************************************************************/ + + + +unsigned OptRTS (CodeSeg* S) +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get this entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a subroutine call and if the following insn is RTS */ + if (E->OPC == OP65_JSR && + (N = CS_GetNextEntry (S, I)) != 0 && + N->OPC == OP65_RTS) { + + /* Change the jsr to a jmp and use the additional info for a jump */ + E->AM = AM65_BRA; + CE_ReplaceOPC (E, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize jump targets */ +/*****************************************************************************/ + + + +unsigned OptJumpTarget1 (CodeSeg* S) +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ +{ + unsigned Changes = 0; + CodeEntry* E1; /* Entry 1 */ + CodeEntry* E2; /* Entry 2 */ + CodeEntry* T1; /* Jump target entry 1 */ + CodeLabel* TL1; /* Target label 1 */ + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + E2 = CS_GetNextEntry (S, I); + + /* Check if we have a jump or branch without a label attached, and + ** a jump target, which is not attached to the jump itself + */ + if (E2 != 0 && + (E2->Info & OF_UBRA) != 0 && + !CE_HasLabel (E2) && + 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; + } + + /* The entry preceeding the branch target may not be the branch + ** insn. + */ + if (T1 == E2) { + goto NextEntry; + } + + /* Get the entry preceeding the jump */ + E1 = CS_GetEntry (S, I); + + /* Check if both preceeding instructions are identical */ + if (!CodeEntriesAreEqual (E1, T1)) { + /* Not equal, try next */ + goto NextEntry; + } + + /* Get the label for the instruction preceeding the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + TL1 = CS_GenLabel (S, T1); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E2, TL1); + + /* If the instruction preceeding the jump has labels attached, + ** move references to this label to the new label. + */ + if (CE_HasLabel (E1)) { + CS_MoveLabels (S, E1, T1); + } + + /* Remove the entry preceeding the jump */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { +NextEntry: + /* Next entry */ + ++I; + } + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget2 (CodeSeg* S) +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* OP that may be skipped */ + opc_t OPC; + + /* Jump target insn, old and new */ + CodeEntry* T; + CodeEntry* N; + + /* New jump label */ + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a bcc insn */ + if (E->OPC == OP65_BCC || E->OPC == OP65_JCC) { + OPC = OP65_CLC; + } else if (E->OPC == OP65_BCS || E->OPC == OP65_JCS) { + OPC = OP65_SEC; + } else { + /* Not what we're looking for */ + goto NextEntry; + } + + /* Must have a jump target */ + if (E->JumpTo == 0) { + goto NextEntry; + } + + /* Get the owner insn of the jump target and check if it's the one, we + ** will skip if present. + */ + T = E->JumpTo->Owner; + if (T->OPC != OPC) { + goto NextEntry; + } + + /* Get the entry following the branch target */ + N = CS_GetNextEntry (S, CS_GetEntryIndex (S, T)); + if (N == 0) { + /* There is no such entry */ + goto NextEntry; + } + + /* Get the label for the instruction following the jump target. + ** This routine will create a new label if the instruction does + ** not already have one. + */ + L = CS_GenLabel (S, N); + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, E, L); + + /* Remember that we had changes */ + ++Changes; + +NextEntry: + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptJumpTarget3 (CodeSeg* S) +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a load insn with a label and the next insn is not + ** a conditional branch that needs the flags from the load. + */ + if ((E->Info & OF_LOAD) != 0 && + CE_IsConstImm (E) && + CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + + unsigned J; + int K; + + /* New jump label */ + CodeLabel* LN = 0; + + /* Walk over all insn that jump here */ + for (J = 0; J < CE_GetLabelCount (E); ++J) { + + /* Get the label */ + CodeLabel* L = CE_GetLabel (E, J); + + /* Loop over all insn that reference this label. Since we may + ** eventually remove a reference in the loop, we must loop + ** from end down to start. + */ + for (K = CL_GetRefCount (L) - 1; K >= 0; --K) { + + /* Get the entry that jumps here */ + CodeEntry* Jump = CL_GetRef (L, K); + + /* Get the register info from this insn */ + short Val = RegVal (E->Chg, &Jump->RI->Out2); + + /* Check if the outgoing value is the one thats's loaded */ + if (Val == (unsigned char) E->Num) { + + /* OK, skip the insn. First, generate a label for the + ** next insn after E. + */ + if (LN == 0) { + LN = CS_GenLabel (S, N); + } + + /* Change the jump target to point to this new label */ + CS_MoveLabelRef (S, Jump, LN); + + /* Remember that we had changes */ + ++Changes; + } + } + } + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize conditional branches */ +/*****************************************************************************/ + + + +unsigned OptCondBranches1 (CodeSeg* S) +/* Performs several optimization steps: +** +** - If an immediate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +** - If the conditional branch is always taken because of the register load, +** replace it by a jmp. +** - If a conditional branch jumps around an unconditional branch, remove the +** conditional branch and make the jump a conditional branch with the +** inverse condition of the first one. +*/ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeLabel* L; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a register load */ + if ((E->Info & OF_LOAD) != 0 && /* It's a load instruction */ + E->AM == AM65_IMM && /* ..with immidiate addressing */ + CE_HasNumArg (E) && /* ..and a numeric argument. */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ + !CE_HasLabel (N)) { /* ..and does not have a label */ + + /* Get the branch condition */ + bc_t BC = GetBranchCond (N->OPC); + + /* Check the argument against the branch condition */ + if ((BC == BC_EQ && E->Num != 0) || + (BC == BC_NE && E->Num == 0) || + (BC == BC_PL && (E->Num & 0x80) != 0) || + (BC == BC_MI && (E->Num & 0x80) == 0)) { + + /* Remove the conditional branch */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } else if ((BC == BC_EQ && E->Num == 0) || + (BC == BC_NE && E->Num != 0) || + (BC == BC_PL && (E->Num & 0x80) == 0) || + (BC == BC_MI && (E->Num & 0x80) != 0)) { + + /* The branch is always taken, replace it by a jump */ + CE_ReplaceOPC (N, OP65_JMP); + + /* Remember, we had changes */ + ++Changes; + } + + } + + if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ + (L = E->JumpTo) != 0 && /* ..referencing a local label */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a following entry */ + (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ + !CE_HasLabel (N) && /* ..has no label attached */ + L->Owner == CS_GetNextEntry (S, I+1)) { /* ..and jump target follows */ + + /* Replace the jump by a conditional branch with the inverse branch + ** condition than the branch around it. + */ + CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); + + /* Remove the conditional branch */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptCondBranches2 (CodeSeg* S) +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry flag. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's a rol insn with A in accu and a branch follows */ + if (E->OPC == OP65_ROL && + E->AM == AM65_ACC && + E->RI->In.RegA == 0 && + !CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 0 && + (N->Info & OF_ZBRA) != 0 && + !RegAUsed (S, I+1)) { + + /* Replace the branch condition */ + switch (GetBranchCond (N->OPC)) { + case BC_EQ: CE_ReplaceOPC (N, OP65_JCC); break; + case BC_NE: CE_ReplaceOPC (N, OP65_JCS); break; + default: Internal ("Unknown branch condition in OptCondBranches2"); + } + + /* Delete the rol insn */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptjmp.h b/src/cc65/coptjmp.h new file mode 100644 index 000000000..4cb7a2792 --- /dev/null +++ b/src/cc65/coptjmp.h @@ -0,0 +1,116 @@ +/*****************************************************************************/ +/* */ +/* coptjmp.h */ +/* */ +/* Low level optimizations regarding branches and jumps */ +/* */ +/* */ +/* */ +/* (C) 2001-2009, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTJMP_H +#define COPTJMP_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptBranchDist (CodeSeg* S); +/* Change branches for the distance needed. */ + +unsigned OptRTSJumps1 (CodeSeg* S); +/* Replace jumps to RTS by RTS */ + +unsigned OptRTSJumps2 (CodeSeg* S); +/* Replace long conditional jumps to RTS */ + +unsigned OptDeadJumps (CodeSeg* S); +/* Remove dead jumps (jumps to the next instruction) */ + +unsigned OptDeadCode (CodeSeg* S); +/* Remove dead code (code that follows an unconditional jump or an rts/rti +** and has no label) +*/ + +unsigned OptJumpCascades (CodeSeg* S); +/* Optimize jump cascades (jumps to jumps). In such a case, the jump is +** replaced by a jump to the final location. This will in some cases produce +** worse code, because some jump targets are no longer reachable by short +** branches, but this is quite rare, so there are more advantages than +** disadvantages. +*/ + +unsigned OptRTS (CodeSeg* S); +/* Optimize subroutine calls followed by an RTS. The subroutine call will get +** replaced by a jump. Don't bother to delete the RTS if it does not have a +** label, the dead code elimination should take care of it. +*/ + +unsigned OptJumpTarget1 (CodeSeg* S); +/* If the instruction preceeding an unconditional branch is the same as the +** instruction preceeding the jump target, the jump target may be moved +** one entry back. This is a size optimization, since the instruction before +** the branch gets removed. +*/ + +unsigned OptJumpTarget2 (CodeSeg* S); +/* If a bcs jumps to a sec insn or a bcc jumps to clc, skip this insn, since +** it's job is already done. +*/ + +unsigned OptJumpTarget3 (CodeSeg* S); +/* Jumps to load instructions of a register, that do already have the matching +** register contents may skip the load instruction, since it's job is already +** done. +*/ + +unsigned OptCondBranches1 (CodeSeg* S); +/* If an immidiate load of a register is followed by a conditional jump that +** is never taken because the load of the register sets the flags in such a +** manner, remove the conditional branch. +*/ + +unsigned OptCondBranches2 (CodeSeg* S); +/* If on entry to a "rol a" instruction the accu is zero, and a beq/bne follows, +** we can remove the rol and branch on the state of the carry. +*/ + + + +/* End of coptjmp.h */ + +#endif diff --git a/src/cc65/coptmisc.c b/src/cc65/coptmisc.c new file mode 100644 index 000000000..523fbf17c --- /dev/null +++ b/src/cc65/coptmisc.c @@ -0,0 +1,735 @@ +/*****************************************************************************/ +/* */ +/* codemisc.c */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#include + +/* common */ +#include "chartype.h" +#include "xsprintf.h" + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "coptmisc.h" +#include "error.h" +#include "symtab.h" + + + +/*****************************************************************************/ +/* Decouple operations */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S) +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + const char* Arg; + + /* Get next entry and it's input register values */ + CodeEntry* E = CS_GetEntry (S, I); + const RegContents* In = &E->RI->In; + + /* Assume we have no replacement */ + CodeEntry* X = 0; + + /* Check the instruction */ + switch (E->OPC) { + + case OP65_DEA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA - 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX - 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_DEY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY - 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INA: + if (RegValIsKnown (In->RegA)) { + Arg = MakeHexArg ((In->RegA + 1) & 0xFF); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INX: + if (RegValIsKnown (In->RegX)) { + Arg = MakeHexArg ((In->RegX + 1) & 0xFF); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_INY: + if (RegValIsKnown (In->RegY)) { + Arg = MakeHexArg ((In->RegY + 1) & 0xFF); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_LDA: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDX: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use & REG_ZP, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_LDY: + if (E->AM == AM65_ZP) { + switch (GetKnownReg (E->Use, In)) { + case REG_TMP1: + Arg = MakeHexArg (In->Tmp1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_LO: + Arg = MakeHexArg (In->Ptr1Lo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_PTR1_HI: + Arg = MakeHexArg (In->Ptr1Hi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_LO: + Arg = MakeHexArg (In->SRegLo); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + + case REG_SREG_HI: + Arg = MakeHexArg (In->SRegHi); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + break; + } + } + break; + + case OP65_TAX: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TAY: + if (E->RI->In.RegA >= 0) { + Arg = MakeHexArg (In->RegA); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TXA: + if (E->RI->In.RegX >= 0) { + Arg = MakeHexArg (In->RegX); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + case OP65_TYA: + if (E->RI->In.RegY >= 0) { + Arg = MakeHexArg (In->RegY); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, E->LI); + } + break; + + default: + /* Avoid gcc warnings */ + break; + + } + + /* Insert the replacement if we have one */ + if (X) { + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads1 (CodeSeg* S) +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZP_INDY && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZPX_IND, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptIndLoads2 (CodeSeg* S) +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's what we're looking for */ + if (E->OPC == OP65_LDA && + E->AM == AM65_ZPX_IND && + E->RI->In.RegY == 0 && + E->RI->In.RegX == 0) { + + /* Replace by the same insn with other addressing mode */ + CodeEntry* X = NewCodeEntry (E->OPC, AM65_ZP_INDY, E->Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Remove the old insn */ + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack pointer ops */ +/*****************************************************************************/ + + + +static unsigned IsDecSP (const CodeEntry* E) +/* Check if this is an insn that decrements the stack pointer. If so, return +** the decrement. If not, return zero. +** The function expects E to be a subroutine call. +*/ +{ + if (strncmp (E->Arg, "decsp", 5) == 0) { + if (E->Arg[5] >= '1' && E->Arg[5] <= '8') { + return (E->Arg[5] - '0'); + } + } else if (strcmp (E->Arg, "subysp") == 0 && RegValIsKnown (E->RI->In.RegY)) { + return E->RI->In.RegY; + } + + /* If we come here, it's not a decsp op */ + return 0; +} + + + +unsigned OptStackPtrOps (CodeSeg* S) +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + unsigned Dec1; + unsigned Dec2; + const CodeEntry* N; + + /* Get the next entry */ + const CodeEntry* E = CS_GetEntry (S, I); + + /* Check for decspn or subysp */ + if (E->OPC == OP65_JSR && + (Dec1 = IsDecSP (E)) > 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + (Dec2 = IsDecSP (N)) > 0 && + (Dec1 += Dec2) <= 255 && + !CE_HasLabel (N)) { + + CodeEntry* X; + char Buf[20]; + + /* We can combine the two */ + if (Dec1 <= 8) { + /* Insert a call to decsp */ + xsprintf (Buf, sizeof (Buf), "decsp%u", Dec1); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, N->LI); + CS_InsertEntry (S, X, I+2); + } else { + /* Insert a call to subysp */ + const char* Arg = MakeHexArg (Dec1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, N->LI); + CS_InsertEntry (S, X, I+2); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, N->LI); + CS_InsertEntry (S, X, I+3); + } + + /* Delete the old code */ + CS_DelEntries (S, I, 2); + + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptGotoSPAdj (CodeSeg* S) +/* Optimize SP adjustment for forward 'goto' */ +{ + unsigned Changes = 0; + unsigned I; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[10], *X; + unsigned short adjustment; + const char* Arg; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence generated by g_lateadjustSP */ + if (L[0]->OPC == OP65_PHA && + CS_GetEntries (S, L+1, I+1, 9) && + L[1]->OPC == OP65_LDA && + L[1]->AM == AM65_ABS && + L[2]->OPC == OP65_CLC && + L[3]->OPC == OP65_ADC && + strcmp (L[3]->Arg, "sp") == 0 && + L[6]->OPC == OP65_ADC && + strcmp (L[6]->Arg, "sp+1") == 0 && + L[9]->OPC == OP65_JMP) { + adjustment = FindSPAdjustment (L[1]->Arg); + + if (adjustment == 0) { + /* No SP adjustment needed, remove the whole sequence */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 8) { + /* If adjustment is in range [-8, 0) we use decsp* calls */ + char Buf[20]; + adjustment = 65536 - adjustment; + xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment >= 65536 - 255) { + /* For range [-255, -8) we have ldy #, jsr subysp */ + adjustment = 65536 - adjustment; + Arg = MakeHexArg (adjustment); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else if (adjustment > 255) { + /* For ranges [-32768, 255) and (255, 32767) the only modification + ** is to replace the absolute with immediate addressing + */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 1); + Arg = MakeHexArg (adjustment >> 8); + X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI); + CS_InsertEntry (S, X, I + 6); + + /* Delete the old code */ + CS_DelEntry (S, I + 2); + CS_DelEntry (S, I + 6); + } + else if (adjustment > 8) { + /* For range (8, 255] we have ldy #, jsr addysp */ + Arg = MakeHexArg (adjustment & 0xff); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI); + CS_InsertEntry (S, X, I + 10); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + else { + /* If adjustment is in range (0, 8] we use incsp* calls */ + char Buf[20]; + xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment); + X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI); + CS_InsertEntry (S, X, I + 9); + + /* Delete the old code */ + CS_DelEntries (S, I, 9); + } + /* Regenerate register info */ + CS_GenRegInfo (S); + + /* Remember we had changes */ + Changes++; + + } else { + + /* Next entry */ + ++I; + } + + } + + /* Return the number of changes made */ + return Changes; +} + + + +/*****************************************************************************/ +/* Optimize stack load ops */ +/*****************************************************************************/ + + + +unsigned OptLoad1 (CodeSeg* S) +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* E; + + /* Get next entry */ + E = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (E, "ldaxysp") && + RegValIsKnown (E->RI->In.RegY) && + !RegXUsed (S, I+1)) { + + CodeEntry* X; + + /* Reload the Y register */ + const char* Arg = MakeHexArg (E->RI->In.RegY - 1); + X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Load from stack */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, E->LI); + CS_InsertEntry (S, X, I+2); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptLoad2 (CodeSeg* S) +/* Replace calls to ldaxysp by inline code */ +{ + unsigned I; + unsigned Changes = 0; + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* L[3]; + + /* Get next entry */ + L[0] = CS_GetEntry (S, I); + + /* Check for the sequence */ + if (CE_IsCallTo (L[0], "ldaxysp")) { + + CodeEntry* X; + + /* Followed by sta abs/stx abs? */ + if (CS_GetEntries (S, L+1, I+1, 2) && + L[1]->OPC == OP65_STA && + L[2]->OPC == OP65_STX && + (L[1]->Arg == 0 || + L[2]->Arg == 0 || + strcmp (L[1]->Arg, L[2]->Arg) != 0) && + !CS_RangeHasLabel (S, I+1, 2) && + !RegXUsed (S, I+3)) { + + /* A/X are stored into memory somewhere and X is not used + ** later + */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[2]->AM, L[2]->Arg, 0, L[2]->LI); + CS_InsertEntry (S, X, I+4); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+5); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+6); + + /* sta abs */ + X = NewCodeEntry (OP65_STA, L[1]->AM, L[1]->Arg, 0, L[1]->LI); + CS_InsertEntry (S, X, I+7); + + /* Now remove the call to the subroutine and the sta/stx */ + CS_DelEntries (S, I, 3); + + } else { + + /* Standard replacement */ + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+1); + + /* tax */ + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+2); + + /* dey */ + X = NewCodeEntry (OP65_DEY, AM65_IMP, 0, 0, L[0]->LI); + CS_InsertEntry (S, X, I+3); + + /* lda (sp),y */ + X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI); + CS_InsertEntry (S, X, I+4); + + /* Now remove the call to the subroutine */ + CS_DelEntry (S, I); + + } + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} diff --git a/src/cc65/coptmisc.h b/src/cc65/coptmisc.h new file mode 100644 index 000000000..89242351c --- /dev/null +++ b/src/cc65/coptmisc.h @@ -0,0 +1,113 @@ +/*****************************************************************************/ +/* */ +/* codemisc.h */ +/* */ +/* Miscellaneous optimization operations */ +/* */ +/* */ +/* */ +/* (C) 2001-2012, Ullrich von Bassewitz */ +/* Roemerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ +/* */ +/* */ +/* This software is provided 'as-is', without any expressed or implied */ +/* warranty. In no event will the authors be held liable for any damages */ +/* arising from the use of this software. */ +/* */ +/* Permission is granted to anyone to use this software for any purpose, */ +/* including commercial applications, and to alter it and redistribute it */ +/* freely, subject to the following restrictions: */ +/* */ +/* 1. The origin of this software must not be misrepresented; you must not */ +/* claim that you wrote the original software. If you use this software */ +/* in a product, an acknowledgment in the product documentation would be */ +/* appreciated but is not required. */ +/* 2. Altered source versions must be plainly marked as such, and must not */ +/* be misrepresented as being the original software. */ +/* 3. This notice may not be removed or altered from any source */ +/* distribution. */ +/* */ +/*****************************************************************************/ + + + +#ifndef COPTMISC_H +#define COPTMISC_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptDecouple (CodeSeg* S); +/* Decouple operations, that is, do the following replacements: +** +** dex -> ldx #imm +** inx -> ldx #imm +** dey -> ldy #imm +** iny -> ldy #imm +** tax -> ldx #imm +** txa -> lda #imm +** tay -> ldy #imm +** tya -> lda #imm +** lda zp -> lda #imm +** ldx zp -> ldx #imm +** ldy zp -> ldy #imm +** +** Provided that the register values are known of course. +*/ + +unsigned OptIndLoads1 (CodeSeg* S); +/* Change +** +** lda (zp),y +** +** into +** +** lda (zp,x) +** +** provided that x and y are both zero. +*/ + +unsigned OptIndLoads2 (CodeSeg* S); +/* Change +** +** lda (zp,x) +** +** into +** +** lda (zp),y +** +** provided that x and y are both zero. +*/ + +unsigned OptStackPtrOps (CodeSeg* S); +/* Merge adjacent calls to decsp into one. NOTE: This function won't merge all +** known cases! +*/ + +unsigned OptGotoSPAdj (CodeSeg* S); +/* Optimize SP adjustment for forward 'goto' */ + +unsigned OptLoad1 (CodeSeg* S); +/* Search for a call to ldaxysp where X is not used later and replace it by +** a load of just the A register. +*/ + +unsigned OptLoad2 (CodeSeg* S); +/* Replace calls to ldaxysp by inline code */ + + +/* End of coptmisc.h */ + +#endif