X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcoptind.c;h=80d6c772c65a56647da04e0bc68c03f65915e487;hb=c2b03e02088d2d459ababaef366ae73a6ab8adf4;hp=2f8951ec25dcc9149d941f64efd0f888c27d3143;hpb=3c04d5777f95a6df47f9ea617e03aab6153fa9cc;p=cc65 diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index 2f8951ec2..80d6c772c 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2001 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@cc65.org */ +/* (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 */ @@ -33,45 +33,260 @@ -#include +/* common */ +#include "cpu.h" /* cc65 */ #include "codeent.h" +#include "coptind.h" #include "codeinfo.h" #include "codeopt.h" #include "error.h" -#include "coptind.h" /*****************************************************************************/ -/* Replace jumps to RTS by RTS */ +/* Helper functions */ +/*****************************************************************************/ + + + +static int MemAccess (CodeSeg* S, unsigned From, unsigned To, const CodeEntry* N) +/* Checks a range of code entries if there are any memory accesses to N->Arg */ +{ + /* Get the length of the argument */ + unsigned NLen = strlen (N->Arg); + + /* What to check for? */ + enum { + None = 0x00, + Base = 0x01, /* Check for location without "+1" */ + Word = 0x02, /* Check for location with "+1" added */ + } What = None; + + + /* If the argument of N is a zero page location that ends with "+1", we + * must also check for word accesses to the location without +1. + */ + if (N->AM == AM65_ZP && NLen > 2 && strcmp (N->Arg + NLen - 2, "+1") == 0) { + What |= Base; + } + + /* If the argument is zero page indirect, we must also check for accesses + * to "arg+1" + */ + if (N->AM == AM65_ZP_INDY || N->AM == AM65_ZPX_IND || N->AM == AM65_ZP_IND) { + What |= Word; + } + + /* Walk over all code entries */ + while (From <= To) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, From); + + /* Check if there is an argument and if this argument equals Arg in + * some variants. + */ + if (E->Arg[0] != '\0') { + + unsigned ELen; + + if (strcmp (E->Arg, N->Arg) == 0) { + /* Found an access */ + return 1; + } + + ELen = strlen (E->Arg); + if ((What & Base) != 0) { + if (ELen == NLen - 2 && strncmp (E->Arg, N->Arg, NLen-2) == 0) { + /* Found an access */ + return 1; + } + } + + if ((What & Word) != 0) { + if (ELen == NLen + 2 && strncmp (E->Arg, N->Arg, NLen) == 0 && + E->Arg[NLen] == '+' && E->Arg[NLen+1] == '1') { + /* Found an access */ + return 1; + } + } + } + + /* Next entry */ + ++From; + } + + /* Nothing found */ + return 0; +} + + + +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); + } +} + + + +/*****************************************************************************/ +/* Replace jumps to RTS by RTS */ /*****************************************************************************/ -unsigned OptRTSJumps (CodeSeg* S) +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 < GetCodeEntryCount (S)) { + while (I < CS_GetEntryCount (S)) { /* Get the next entry */ - CodeEntry* E = GetCodeEntry (S, I); + 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 == OPC_RTS) { + 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 */ - DelCodeEntry (S, I); + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + - /* Insert an RTS instruction instead */ - InsertCodeEntry (S, NewCodeEntry (OPC_RTS, AM_IMP, 0, 0), I); +unsigned OptRTSJumps2 (CodeSeg* S) +/* Replace long conditional jumps to RTS */ +{ + unsigned Changes = 0; + + /* Walk over all entries minus the last one */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it's an unconditional branch to a local target */ + if ((E->Info & OF_CBRA) != 0 && /* Conditional branch */ + (E->Info & OF_LBRA) != 0 && /* Long branch */ + E->JumpTo != 0 && /* Local label */ + E->JumpTo->Owner->OPC == OP65_RTS && /* Target is an RTS */ + (N = CS_GetNextEntry (S, I)) != 0) { /* There is a next entry */ + + CodeEntry* X; + CodeLabel* LN; + opc_t NewBranch; + + /* We will create a jump around an RTS instead of the long branch */ + X = NewCodeEntry (OP65_RTS, AM65_IMP, 0, 0, E->JumpTo->Owner->LI); + CS_InsertEntry (S, X, I+1); + + /* Get the new branch opcode */ + NewBranch = MakeShortBranch (GetInverseBranch (E->OPC)); + + /* Get the label attached to N, create a new one if needed */ + LN = CS_GenLabel (S, N); + + /* Generate the branch */ + X = NewCodeEntry (NewBranch, AM65_BRA, LN->Name, LN, E->LI); + CS_InsertEntry (S, X, I+1); + + /* Delete the long branch */ + CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; @@ -99,32 +314,23 @@ unsigned OptDeadJumps (CodeSeg* S) /* Remove dead jumps (jumps to the next instruction) */ { unsigned Changes = 0; - CodeEntry* E; - unsigned I; - - /* Get the number of entries, bail out if we have less than two entries */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries minus the last one */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get the next entry */ - E = GetCodeEntry (S, I); + CodeEntry* E = CS_GetEntry (S, I); /* Check if it's a branch, if it has a local target, and if the target * is the next instruction. */ - if (E->AM == AM_BRA && E->JumpTo && E->JumpTo->Owner == GetCodeEntry (S, I+1)) { + if (E->AM == AM65_BRA && + E->JumpTo && + E->JumpTo->Owner == CS_GetNextEntry (S, I)) { /* Delete the dead jump */ - DelCodeEntry (S, I); - - /* Keep the number of entries updated */ - --Count; + CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; @@ -144,7 +350,7 @@ unsigned OptDeadJumps (CodeSeg* S) /*****************************************************************************/ -/* Remove dead code */ +/* Remove dead code */ /*****************************************************************************/ @@ -155,35 +361,31 @@ unsigned OptDeadCode (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have less than two entries */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries */ - I = 0; - while (I < Count) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; + CodeLabel* LN; /* Get this entry */ - CodeEntry* E = GetCodeEntry (S, I); + CodeEntry* E = CS_GetEntry (S, I); /* Check if it's an unconditional branch, and if the next entry has - * no labels attached + * no labels attached, or if the label is just used so that the insn + * can jump to itself. */ - if ((E->Info & OF_DEAD) != 0 && - (N = GetNextCodeEntry (S, I)) != 0 && - !CodeEntryHasLabel (N)) { + if ((E->Info & OF_DEAD) != 0 && /* Dead code follows */ + (N = CS_GetNextEntry (S, I)) != 0 && /* Has next entry */ + (!CE_HasLabel (N) || /* Don't has a label */ + ((N->Info & OF_UBRA) != 0 && /* Uncond branch */ + (LN = N->JumpTo) != 0 && /* Jumps to known label */ + LN->Owner == N && /* Attached to insn */ + CL_GetRefCount (LN) == 1))) { /* Only reference */ /* Delete the next entry */ - DelCodeEntry (S, I+1); - - /* Keep the number of entries updated */ - --Count; + CS_DelEntry (S, I+1); /* Remember, we had changes */ ++Changes; @@ -220,13 +422,13 @@ unsigned OptJumpCascades (CodeSeg* S) /* Walk over all entries */ unsigned I = 0; - while (I < GetCodeEntryCount (S)) { + while (I < CS_GetEntryCount (S)) { CodeEntry* N; CodeLabel* OldLabel; /* Get this entry */ - CodeEntry* E = GetCodeEntry (S, I); + 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, and if the @@ -243,67 +445,76 @@ unsigned OptJumpCascades (CodeSeg* S) */ if ((N->Info & OF_UBRA) != 0 || ((E->Info & OF_CBRA) != 0 && - GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { + GetBranchCond (E->OPC) == GetBranchCond (N->OPC))) { - /* This is a jump cascade and we may jump to the final target. - * Insert a new instruction, then remove the old one + /* This is a jump cascade and we may jump to the final target, + * provided that the other insn does not jump to itself. If + * this is the case, we can also jump to ourselves, otherwise + * insert a jump to the new instruction and remove the old one. */ - CodeEntry* X = NewCodeEntry (E->OPC, E->AM, N->Arg, N->JumpTo); + CodeEntry* X; + CodeLabel* LN = N->JumpTo; - /* Insert it behind E */ - InsertCodeEntry (S, X, I+1); + if (LN != 0 && LN->Owner == N) { - /* Remove E */ - DelCodeEntry (S, I); + /* 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); - /* Remember, we had changes */ - ++Changes; + } else { - /* Done */ - continue; + /* 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. */ - if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { + } else if ((E->Info & OF_CBRA) != 0 && (N->Info & OF_CBRA) != 0) { - CodeEntry* X; /* Instruction behind N */ - CodeLabel* LX; /* Label attached to X */ + 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); + /* 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; - } + /* 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 = GetNextCodeEntry (S, GetCodeEntryIndex (S, N))) == 0) { - /* N is the last entry, bail out */ - 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 = GenCodeLabel (S, X); + /* 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 */ - MoveCodeLabelRef (S, E, LX); + /* Move the reference from E to the new label */ + CS_MoveLabelRef (S, E, LX); /* Remember, we had changes */ ++Changes; - - /* Done */ - continue; - } } @@ -332,31 +543,24 @@ unsigned OptRTS (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have less than 2 entries */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over all entries minus the last one */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; /* Get this entry */ - CodeEntry* E = GetCodeEntry (S, I); + CodeEntry* E = CS_GetEntry (S, I); /* Check if it's a subroutine call and if the following insn is RTS */ - if (E->OPC == OPC_JSR && - (N = GetNextCodeEntry (S, I)) != 0 && - N->OPC == OPC_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 = AM_BRA; - ReplaceOPC (E, OPC_JMP); + E->AM = AM65_BRA; + CE_ReplaceOPC (E, OP65_JMP); /* Remember, we had changes */ ++Changes; @@ -380,7 +584,7 @@ unsigned OptRTS (CodeSeg* S) -unsigned OptJumpTarget (CodeSeg* S) +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 @@ -391,78 +595,229 @@ unsigned OptJumpTarget (CodeSeg* S) CodeEntry* E1; /* Entry 1 */ CodeEntry* E2; /* Entry 2 */ CodeEntry* T1; /* Jump target entry 1 */ - CodeEntry* T2; /* Jump target entry 2 */ CodeLabel* TL1; /* Target label 1 */ - unsigned TI; /* Target index */ - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 3) { - return 0; - } /* Walk over the entries */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get next entry */ - E2 = GetCodeEntry (S, I+1); - - /* Check if we have a jump or branch, and a matching label */ - if ((E2->Info & OF_UBRA) != 0 && E2->JumpTo) { - - /* Get the target instruction for the label */ - T2 = E2->JumpTo->Owner; + E2 = CS_GetNextEntry (S, I); - /* Get the entry preceeding this one (if possible) */ - TI = GetCodeEntryIndex (S, T2); - if (TI == 0) { - /* There is no entry before this one */ + /* 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; } - T1 = GetCodeEntry (S, TI-1); + + /* The entry preceeding the branch target may not be the branch + * insn. + */ + if (T1 == E2) { + goto NextEntry; + } /* Get the entry preceeding the jump */ - E1 = GetCodeEntry (S, I); + E1 = CS_GetEntry (S, I); /* Check if both preceeding instructions are identical */ if (!CodeEntriesAreEqual (E1, T1)) { /* Not equal, try next */ - goto NextEntry; + 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 = GenCodeLabel (S, T1); + TL1 = CS_GenLabel (S, T1); /* Change the jump target to point to this new label */ - MoveCodeLabelRef (S, E2, TL1); + CS_MoveLabelRef (S, E2, TL1); /* If the instruction preceeding the jump has labels attached, * move references to this label to the new label. */ - if (CodeEntryHasLabel (E1)) { - MoveCodeLabels (S, E1, T1); + if (CE_HasLabel (E1)) { + CS_MoveLabels (S, E1, T1); } /* Remove the entry preceeding the jump */ - DelCodeEntry (S, I); - --Count; + 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; + /* 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; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + unsigned J, K; + CodeEntry* N; + + /* New jump label */ + CodeLabel* LN = 0; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if this is a load insn with a label */ + if ((E->Info & OF_LOAD) != 0 && + CE_IsConstImm (E) && + CE_HasLabel (E) && + (N = CS_GetNextEntry (S, I)) != 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); + for (K = 0; K < CL_GetRefCount (L); ++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 thatr's loaded */ + if (Val == (unsigned char) E->Num) { + + /* Ok, skip the insn. First, generate a label */ + 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; } + /* Free register info */ + CS_FreeRegInfo (S); + /* Return the number of changes made */ return Changes; } @@ -470,12 +825,12 @@ NextEntry: /*****************************************************************************/ -/* Optimize conditional branches */ +/* Optimize conditional branches */ /*****************************************************************************/ -unsigned OptCondBranches (CodeSeg* S) +unsigned OptCondBranches1 (CodeSeg* S) /* Performs several optimization steps: * * - If an immidiate load of a register is followed by a conditional jump that @@ -489,55 +844,47 @@ unsigned OptCondBranches (CodeSeg* S) */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over the entries */ - I = 0; - while (I < Count-1) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { CodeEntry* N; CodeLabel* L; /* Get next entry */ - CodeEntry* E = GetCodeEntry (S, I); + 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 == AM_IMM && /* ..with immidiate addressing */ - (E->Flags & CEF_NUMARG) != 0 && /* ..and a numeric argument. */ - (N = GetNextCodeEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_CBRA) != 0 && /* ..which is a conditional branch */ - !CodeEntryHasLabel (N)) { /* ..and does not have a label */ + 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) || + 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 */ - DelCodeEntry (S, I+1); - --Count; + CS_DelEntry (S, I+1); /* Remember, we had changes */ - ++Changes; + ++Changes; - } else if ((BC == BC_EQ && E->Num == 0) || - (BC == BC_NE && E->Num != 0) || + } 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 */ - ReplaceOPC (N, OPC_JMP); + CE_ReplaceOPC (N, OP65_JMP); /* Remember, we had changes */ ++Changes; @@ -545,21 +892,20 @@ unsigned OptCondBranches (CodeSeg* S) } - if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ - (L = E->JumpTo) != 0 && /* ..referencing a local label */ - (N = GetNextCodeEntry (S, I)) != 0 && /* There is a following entry */ - (N->Info & OF_UBRA) != 0 && /* ..which is an uncond branch, */ - !CodeEntryHasLabel (N) && /* ..has no label attached */ - L->Owner == GetNextCodeEntry (S, I+1)) {/* ..and jump target follows */ + 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. */ - ReplaceOPC (N, GetInverseBranch (E->OPC)); + CE_ReplaceOPC (N, GetInverseBranch (E->OPC)); /* Remove the conditional branch */ - DelCodeEntry (S, I); - --Count; + CS_DelEntry (S, I); /* Remember, we had changes */ ++Changes; @@ -577,8 +923,64 @@ unsigned OptCondBranches (CodeSeg* S) +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; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* 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; + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* Return the number of changes made */ + return Changes; +} + + + /*****************************************************************************/ -/* Remove unused loads */ +/* Remove unused loads and stores */ /*****************************************************************************/ @@ -587,56 +989,56 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Remove loads of registers where the value loaded is not used later. */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = GetCodeEntryCount (S); - if (Count < 2) { - return 0; - } /* Walk over the entries */ - I = 0; - while (I < Count-1) { - - /* Get next entry */ - CodeEntry* E = GetCodeEntry (S, I); + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { - /* Check if it's a register load */ - if ((E->Info & OF_LOAD) != 0) { + CodeEntry* N; - unsigned char R; + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); - /* Get the next instruction, it must not be a conditional branch */ - CodeEntry* N = GetCodeEntry (S, I+1); - if ((N->Info & OF_CBRA) != 0) { - goto NextEntry; - } + /* Check if it's a register load or transfer insn */ + if ((E->Info & (OF_LOAD | OF_XFR | OF_REG_INCDEC)) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { - /* Check which sort of load it is */ + /* Check which sort of load or transfer it is */ + unsigned R; switch (E->OPC) { - case OPC_LDA: R = REG_A; break; - case OPC_LDX: R = REG_X; break; - case OPC_LDY: R = REG_Y; break; - default: goto NextEntry; /* OOPS */ + case OP65_DEA: + case OP65_INA: + case OP65_LDA: + case OP65_TXA: + case OP65_TYA: R = REG_A; break; + case OP65_DEX: + case OP65_INX: + case OP65_LDX: + case OP65_TAX: R = REG_X; break; + case OP65_DEY: + case OP65_INY: + case OP65_LDY: + case OP65_TAY: R = REG_Y; break; + default: goto NextEntry; /* OOPS */ } /* Get register usage and check if the register value is used later */ - if ((GetRegInfo (S, I+1) & R) == 0) { + if ((GetRegInfo (S, I+1, R) & R) == 0) { - /* Register value is not used, remove the load */ - DelCodeEntry (S, I); - --Count; + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); - /* Remember, we had changes */ - ++Changes; + /* Remember, we had changes. Account the deleted entry in I. */ + ++Changes; + --I; } } NextEntry: - /* Next entry */ - ++I; + /* Next entry */ + ++I; } @@ -646,77 +1048,44 @@ NextEntry: -/*****************************************************************************/ -/* Optimize branch types */ -/*****************************************************************************/ - - - -unsigned OptBranchDist (CodeSeg* S) -/* Change branches for the distance needed. */ +unsigned OptUnusedStores (CodeSeg* S) +/* Remove stores into zero page registers that aren't used later */ { unsigned Changes = 0; - unsigned I; - - /* Get the number of entries, bail out if we have not enough */ - unsigned Count = GetCodeEntryCount (S); /* Walk over the entries */ - I = 0; - while (I < Count) { + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { /* Get next entry */ - CodeEntry* E = GetCodeEntry (S, I); + CodeEntry* E = CS_GetEntry (S, I); - /* Check if it's a conditional branch to a local label. */ - if ((E->Info & OF_CBRA) != 0) { - - /* Is this a branch to a local symbol? */ - if (E->JumpTo != 0) { + /* Check if it's a register load or transfer insn */ + if ((E->Info & OF_STORE) != 0 && + E->AM == AM65_ZP && + (E->Chg & REG_ZP) != 0) { - /* Get the index of the branch target */ - unsigned TI = GetCodeEntryIndex (S, E->JumpTo->Owner); - - /* Determine the branch distance */ - int Distance = 0; - if (TI >= I) { - /* Forward branch */ - unsigned J = I; - while (J < TI) { - CodeEntry* N = GetCodeEntry (S, J++); - Distance += N->Size; - } - } else { - /* Backward branch */ - unsigned J = TI; - while (J < I) { - CodeEntry* N = GetCodeEntry (S, J++); - Distance += N->Size; - } - } + /* Check for the zero page location. We know that there cannot be + * more than one zero page location involved in the store. + */ + unsigned R = E->Chg & REG_ZP; - /* Make the branch short/long according to distance */ - if ((E->Info & OF_LBRA) == 0 && Distance > 120) { - /* Short branch but long distance */ - ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; - } else if ((E->Info & OF_LBRA) != 0 && Distance < 120) { - /* Long branch but short distance */ - ReplaceOPC (E, MakeShortBranch (E->OPC)); - ++Changes; - } + /* Get register usage and check if the register value is used later */ + if ((GetRegInfo (S, I+1, R) & R) == 0) { - } else if ((E->Info & OF_LBRA) == 0) { + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); - /* Short branch to external symbol - make it long */ - ReplaceOPC (E, MakeLongBranch (E->OPC)); - ++Changes; + /* Remember, we had changes */ + ++Changes; + /* Continue with next insn */ + continue; } } - /* Next entry */ - ++I; + /* Next entry */ + ++I; } @@ -726,7 +1095,1183 @@ unsigned OptBranchDist (CodeSeg* S) +unsigned OptDupLoads (CodeSeg* S) +/* Remove loads of registers where the value loaded is already in the register. */ +{ + unsigned Changes = 0; + unsigned I; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Assume we won't delete the entry */ + int Delete = 0; + + /* Get a pointer to the input registers of the insn */ + const RegContents* In = &E->RI->In; + + /* Handle the different instructions */ + switch (E->OPC) { + + case OP65_LDA: + if (RegValIsKnown (In->RegA) && /* Value of A is known */ + CE_IsKnownImm (E, In->RegA) && /* Value to be loaded is known */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_LDX: + if (RegValIsKnown (In->RegX) && /* Value of X is known */ + CE_IsKnownImm (E, In->RegX) && /* Value to be loaded is known */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_LDY: + if (RegValIsKnown (In->RegY) && /* Value of Y is known */ + CE_IsKnownImm (E, In->RegY) && /* Value to be loaded is known */ + (N = CS_GetNextEntry (S, I)) != 0 && /* There is a next entry */ + !CE_UseLoadFlags (N)) { /* Which does not use the flags */ + Delete = 1; + } + break; + + case OP65_STA: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegA) && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegA == ZPRegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + } + break; + + case OP65_STX: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegX) && /* Value of A is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegX == ZPRegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + + /* If the value in the X register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for X + * later. STX does support the zeropage,y addressing mode, + * so be sure to check for that. + */ + } else if (RegValIsKnown (In->RegX) && + In->RegX == In->RegA && + E->AM != AM65_ABSY && + E->AM != AM65_ZPY) { + /* Use the A register instead */ + CE_ReplaceOPC (E, OP65_STA); + } + break; + + case OP65_STY: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if (RegValIsKnown (In->RegY) && /* Value of Y is known */ + E->AM == AM65_ZP && /* Store into zp */ + In->RegY == ZPRegVal (E->Chg, In)) { /* Value identical */ + + Delete = 1; + + /* If the value in the Y register is known and the same as + * that in the A register, replace the store by a STA. The + * optimizer will then remove the load instruction for Y + * later. If replacement by A is not possible try a + * replacement by X, but check for invalid addressing modes + * in this case. + */ + } else if (RegValIsKnown (In->RegY)) { + if (In->RegY == In->RegA) { + CE_ReplaceOPC (E, OP65_STA); + } else if (In->RegY == In->RegX && + E->AM != AM65_ABSX && + E->AM != AM65_ZPX) { + CE_ReplaceOPC (E, OP65_STX); + } + } + break; + + case OP65_STZ: + /* If we store into a known zero page location, and this + * location does already contain the value to be stored, + * remove the store. + */ + if ((CPUIsets[CPU] & CPU_ISET_65SC02) != 0 && E->AM == AM65_ZP) { + if (ZPRegVal (E->Chg, In) == 0) { + Delete = 1; + } + } + break; + + case OP65_TAX: + if (RegValIsKnown (In->RegA) && + In->RegA == In->RegX && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TAY: + if (RegValIsKnown (In->RegA) && + In->RegA == In->RegY && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TXA: + if (RegValIsKnown (In->RegX) && + In->RegX == In->RegA && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + case OP65_TYA: + if (RegValIsKnown (In->RegY) && + In->RegY == In->RegA && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_UseLoadFlags (N)) { + /* Value is identical and not followed by a branch */ + Delete = 1; + } + break; + + default: + break; + + } + + /* Delete the entry if requested */ + if (Delete) { + + /* Register value is not used, remove the load */ + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + + } else { + + /* Next entry */ + ++I; + + } + + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptStoreLoad (CodeSeg* S) +/* Remove a store followed by a load from the same location. */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeEntry* X; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if it is a store instruction followed by a load from the + * same address which is itself not followed by a conditional branch. + */ + if ((E->Info & OF_STORE) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (N) && + E->AM == N->AM && + ((E->OPC == OP65_STA && N->OPC == OP65_LDA) || + (E->OPC == OP65_STX && N->OPC == OP65_LDX) || + (E->OPC == OP65_STY && N->OPC == OP65_LDY)) && + strcmp (E->Arg, N->Arg) == 0 && + (X = CS_GetNextEntry (S, I+1)) != 0 && + !CE_UseLoadFlags (X)) { + + /* Register has already the correct value, remove the load */ + CS_DelEntry (S, I+1); + + /* Remember, we had changes */ + ++Changes; + + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptTransfers1 (CodeSeg* S) +/* Remove transfers from one register to another and back */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + CodeEntry* X; + CodeEntry* P; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if we have two transfer instructions */ + if ((E->Info & OF_XFR) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (N) && + (N->Info & OF_XFR) != 0) { + + /* Check if it's a transfer and back */ + if ((E->OPC == OP65_TAX && N->OPC == OP65_TXA && !RegXUsed (S, I+2)) || + (E->OPC == OP65_TAY && N->OPC == OP65_TYA && !RegYUsed (S, I+2)) || + (E->OPC == OP65_TXA && N->OPC == OP65_TAX && !RegAUsed (S, I+2)) || + (E->OPC == OP65_TYA && N->OPC == OP65_TAY && !RegAUsed (S, I+2))) { + + /* If the next insn is a conditional branch, check if the insn + * preceeding the first xfr will set the flags right, otherwise we + * may not remove the sequence. + */ + if ((X = CS_GetNextEntry (S, I+1)) == 0) { + goto NextEntry; + } + if (CE_UseLoadFlags (X)) { + if (I == 0) { + /* No preceeding entry */ + goto NextEntry; + } + P = CS_GetEntry (S, I-1); + if ((P->Info & OF_SETF) == 0) { + /* Does not set the flags */ + goto NextEntry; + } + } + + /* Remove both transfers */ + CS_DelEntry (S, I+1); + CS_DelEntry (S, I); + + /* Remember, we had changes */ + ++Changes; + } + } + +NextEntry: + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptTransfers2 (CodeSeg* S) +/* Replace loads followed by a register transfer by a load with the second + * register if possible. + */ +{ + unsigned Changes = 0; + + /* Walk over the entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* N; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check if we have a load followed by a transfer where the loaded + * register is not used later. + */ + if ((E->Info & OF_LOAD) != 0 && + (N = CS_GetNextEntry (S, I)) != 0 && + !CE_HasLabel (N) && + (N->Info & OF_XFR) != 0 && + GetRegInfo (S, I+2, E->Chg) != E->Chg) { + + CodeEntry* X = 0; + + if (E->OPC == OP65_LDA && N->OPC == OP65_TAX) { + /* LDA/TAX - check for the right addressing modes */ + if (E->AM == AM65_IMM || + E->AM == AM65_ZP || + E->AM == AM65_ABS || + E->AM == AM65_ABSY) { + /* Replace */ + X = NewCodeEntry (OP65_LDX, E->AM, E->Arg, 0, N->LI); + } + } else if (E->OPC == OP65_LDA && N->OPC == OP65_TAY) { + /* LDA/TAY - check for the right addressing modes */ + if (E->AM == AM65_IMM || + E->AM == AM65_ZP || + E->AM == AM65_ZPX || + E->AM == AM65_ABS || + E->AM == AM65_ABSX) { + /* Replace */ + X = NewCodeEntry (OP65_LDY, E->AM, E->Arg, 0, N->LI); + } + } else if (E->OPC == OP65_LDY && N->OPC == OP65_TYA) { + /* LDY/TYA. LDA supports all addressing modes LDY does */ + X = NewCodeEntry (OP65_LDA, E->AM, E->Arg, 0, N->LI); + } else if (E->OPC == OP65_LDX && N->OPC == OP65_TXA) { + /* LDX/TXA. LDA doesn't support zp,y, so we must map it to + * abs,y instead. + */ + am_t AM = (E->AM == AM65_ZPY)? AM65_ABSY : E->AM; + X = NewCodeEntry (OP65_LDA, AM, E->Arg, 0, N->LI); + } + + /* If we have a load entry, add it and remove the old stuff */ + if (X) { + CS_InsertEntry (S, X, I+2); + CS_DelEntries (S, I, 2); + ++Changes; + --I; /* Correct for one entry less */ + } + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptTransfers3 (CodeSeg* S) +/* Replace a register transfer followed by a store of the second register by a + * store of the first register if this is possible. + */ +{ + unsigned Changes = 0; + unsigned UsedRegs = REG_NONE; /* Track used registers */ + unsigned Xfer = 0; /* Index of transfer insn */ + unsigned Store = 0; /* Index of store insn */ + CodeEntry* XferEntry = 0; /* Pointer to xfer insn */ + CodeEntry* StoreEntry = 0; /* Pointer to store insn */ + + enum { + Initialize, + Search, + FoundXfer, + FoundStore + } State = Initialize; + + /* Walk over the entries. Look for a xfer instruction that is followed by + * a store later, where the value of the register is not used later. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Initialize: + /* Clear the list of used registers */ + UsedRegs = REG_NONE; + /* FALLTHROUGH */ + + case Search: + if (E->Info & OF_XFR) { + /* Found start of sequence */ + Xfer = I; + XferEntry = E; + State = FoundXfer; + } + break; + + case FoundXfer: + /* If we find a conditional jump, abort the sequence, since + * handling them makes things really complicated. + */ + if (E->Info & OF_CBRA) { + + /* Switch back to searching */ + I = Xfer; + State = Initialize; + + /* Does this insn use the target register of the transfer? */ + } else if ((E->Use & XferEntry->Chg) != 0) { + + /* It it's a store instruction, and the block is a basic + * block, proceed. Otherwise restart + */ + if ((E->Info & OF_STORE) != 0 && + CS_IsBasicBlock (S, Xfer, I)) { + Store = I; + StoreEntry = E; + State = FoundStore; + } else { + I = Xfer; + State = Initialize; + } + + /* Does this insn change the target register of the transfer? */ + } else if (E->Chg & XferEntry->Chg) { + + /* We *may* add code here to remove the transfer, but I'm + * currently not sure about the consequences, so I won't + * do that and bail out instead. + */ + I = Xfer; + State = Initialize; + + /* Does this insn have a label? */ + } else if (CE_HasLabel (E)) { + + /* Too complex to handle - bail out */ + I = Xfer; + State = Initialize; + + } else { + /* Track used registers */ + UsedRegs |= E->Use; + } + break; + + case FoundStore: + /* We are at the instruction behind the store. If the register + * isn't used later, and we have an address mode match, we can + * replace the transfer by a store and remove the store here. + */ + if ((GetRegInfo (S, I, XferEntry->Chg) & XferEntry->Chg) == 0 && + (StoreEntry->AM == AM65_ABS || + StoreEntry->AM == AM65_ZP) && + (StoreEntry->AM != AM65_ZP || + (StoreEntry->Chg & UsedRegs) == 0) && + !MemAccess (S, Xfer+1, Store-1, StoreEntry)) { + + /* Generate the replacement store insn */ + CodeEntry* X = 0; + switch (XferEntry->OPC) { + + case OP65_TXA: + X = NewCodeEntry (OP65_STX, + StoreEntry->AM, + StoreEntry->Arg, + 0, + StoreEntry->LI); + break; + + case OP65_TAX: + X = NewCodeEntry (OP65_STA, + StoreEntry->AM, + StoreEntry->Arg, + 0, + StoreEntry->LI); + break; + + case OP65_TYA: + X = NewCodeEntry (OP65_STY, + StoreEntry->AM, + StoreEntry->Arg, + 0, + StoreEntry->LI); + break; + + case OP65_TAY: + X = NewCodeEntry (OP65_STA, + StoreEntry->AM, + StoreEntry->Arg, + 0, + StoreEntry->LI); + break; + + default: + break; + } + + /* If we have a replacement store, change the code */ + if (X) { + /* Insert after the xfer insn */ + CS_InsertEntry (S, X, Xfer+1); + + /* Remove the xfer instead */ + CS_DelEntry (S, Xfer); + + /* Remove the final store */ + CS_DelEntry (S, Store); + + /* Correct I so we continue with the next insn */ + I -= 2; + + /* Remember we had changes */ + ++Changes; + } else { + /* Restart after last xfer insn */ + I = Xfer; + } + } else { + /* Restart after last xfer insn */ + I = Xfer; + } + State = Initialize; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptTransfers4 (CodeSeg* S) +/* Replace a load of a register followed by a transfer insn of the same register + * by a load of the second register if possible. + */ +{ + unsigned Changes = 0; + unsigned Load = 0; /* Index of load insn */ + unsigned Xfer = 0; /* Index of transfer insn */ + CodeEntry* LoadEntry = 0; /* Pointer to load insn */ + CodeEntry* XferEntry = 0; /* Pointer to xfer insn */ + + enum { + Search, + FoundLoad, + FoundXfer + } State = Search; + + /* Walk over the entries. Look for a load instruction that is followed by + * a load later. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Search: + if (E->Info & OF_LOAD) { + /* Found start of sequence */ + Load = I; + LoadEntry = E; + State = FoundLoad; + } + break; + + case FoundLoad: + /* If we find a conditional jump, abort the sequence, since + * handling them makes things really complicated. + */ + if (E->Info & OF_CBRA) { + + /* Switch back to searching */ + I = Load; + State = Search; + + /* Does this insn use the target register of the load? */ + } else if ((E->Use & LoadEntry->Chg) != 0) { + + /* It it's a xfer instruction, and the block is a basic + * block, proceed. Otherwise restart + */ + if ((E->Info & OF_XFR) != 0 && + CS_IsBasicBlock (S, Load, I)) { + Xfer = I; + XferEntry = E; + State = FoundXfer; + } else { + I = Load; + State = Search; + } + + /* Does this insn change the target register of the load? */ + } else if (E->Chg & LoadEntry->Chg) { + + /* We *may* add code here to remove the load, but I'm + * currently not sure about the consequences, so I won't + * do that and bail out instead. + */ + I = Load; + State = Search; + } + break; + + case FoundXfer: + /* We are at the instruction behind the xfer. If the register + * isn't used later, and we have an address mode match, we can + * replace the transfer by a load and remove the initial load. + */ + if ((GetRegInfo (S, I, LoadEntry->Chg) & LoadEntry->Chg) == 0 && + (LoadEntry->AM == AM65_ABS || + LoadEntry->AM == AM65_ZP || + LoadEntry->AM == AM65_IMM) && + !MemAccess (S, Load+1, Xfer-1, LoadEntry)) { + + /* Generate the replacement load insn */ + CodeEntry* X = 0; + switch (XferEntry->OPC) { + + case OP65_TXA: + case OP65_TYA: + X = NewCodeEntry (OP65_LDA, + LoadEntry->AM, + LoadEntry->Arg, + 0, + LoadEntry->LI); + break; + + case OP65_TAX: + X = NewCodeEntry (OP65_LDX, + LoadEntry->AM, + LoadEntry->Arg, + 0, + LoadEntry->LI); + break; + + case OP65_TAY: + X = NewCodeEntry (OP65_LDY, + LoadEntry->AM, + LoadEntry->Arg, + 0, + LoadEntry->LI); + break; + + default: + break; + } + + /* If we have a replacement load, change the code */ + if (X) { + /* Insert after the xfer insn */ + CS_InsertEntry (S, X, Xfer+1); + + /* Remove the xfer instead */ + CS_DelEntry (S, Xfer); + + /* Remove the initial load */ + CS_DelEntry (S, Load); + + /* Correct I so we continue with the next insn */ + I -= 2; + + /* Remember we had changes */ + ++Changes; + } else { + /* Restart after last xfer insn */ + I = Xfer; + } + } else { + /* Restart after last xfer insn */ + I = Xfer; + } + State = Search; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptPushPop (CodeSeg* S) +/* Remove a PHA/PLA sequence were A is not used later */ +{ + unsigned Changes = 0; + unsigned Push = 0; /* Index of push insn */ + unsigned Pop = 0; /* Index of pop insn */ + unsigned ChgA = 0; /* Flag for A changed */ + enum { + Searching, + FoundPush, + FoundPop + } State = Searching; + + /* Walk over the entries. Look for a push instruction that is followed by + * a pop later, where the pop is not followed by an conditional branch, + * and where the value of the A register is not used later on. + * Look out for the following problems: + * + * - There may be another PHA/PLA inside the sequence: Restart it. + * - If the PLA has a label, all jumps to this label must be inside + * the sequence, otherwise we cannot remove the PHA/PLA. + */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + CodeEntry* X; + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + switch (State) { + + case Searching: + if (E->OPC == OP65_PHA) { + /* Found start of sequence */ + Push = I; + ChgA = 0; + State = FoundPush; + } + break; + + case FoundPush: + if (E->OPC == OP65_PHA) { + /* Inner push/pop, restart */ + Push = I; + ChgA = 0; + } else if (E->OPC == OP65_PLA) { + /* Found a matching pop */ + Pop = I; + /* Check that the block between Push and Pop is a basic + * block (one entry, one exit). Otherwise ignore it. + */ + if (CS_IsBasicBlock (S, Push, Pop)) { + State = FoundPop; + } else { + /* Go into searching mode again */ + State = Searching; + } + } else if (E->Chg & REG_A) { + ChgA = 1; + } + break; + + case FoundPop: + /* We're at the instruction after the PLA. + * Check for the following conditions: + * - If this instruction is a store of A, and A is not used + * later, we may replace the PHA by the store and remove + * pla if several other conditions are met. + * - If this instruction is not a conditional branch, and A + * is either unused later, or not changed by the code + * between push and pop, we may remove PHA and PLA. + */ + if (E->OPC == OP65_STA && + !RegAUsed (S, I+1) && + !MemAccess (S, Push+1, Pop-1, E)) { + + /* Insert a STA after the PHA */ + X = NewCodeEntry (E->OPC, E->AM, E->Arg, E->JumpTo, E->LI); + CS_InsertEntry (S, X, Push+1); + + /* Remove the PHA instead */ + CS_DelEntry (S, Push); + + /* Remove the PLA/STA sequence */ + CS_DelEntries (S, Pop, 2); + + /* Correct I so we continue with the next insn */ + I -= 2; + + /* Remember we had changes */ + ++Changes; + + } else if ((E->Info & OF_CBRA) == 0 && + (!RegAUsed (S, I) || !ChgA)) { + + /* We can remove the PHA and PLA instructions */ + CS_DelEntry (S, Pop); + CS_DelEntry (S, Push); + + /* Correct I so we continue with the next insn */ + I -= 2; + + /* Remember we had changes */ + ++Changes; + + } + /* Go into search mode again */ + State = Searching; + break; + + } + + /* Next entry */ + ++I; + } + + /* Return the number of changes made */ + return Changes; +} + + + +unsigned OptPrecalc (CodeSeg* S) +/* Replace immediate operations with the accu where the current contents are + * known by a load of the final value. + */ +{ + unsigned Changes = 0; + unsigned I; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* Walk over the entries */ + I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Get pointers to the input and output registers of the insn */ + const RegContents* Out = &E->RI->Out; + const RegContents* In = &E->RI->In; + + /* Argument for LDn and flag */ + const char* Arg = 0; + opc_t OPC = OP65_LDA; + + /* Handle the different instructions */ + switch (E->OPC) { + + case OP65_LDA: + if (E->AM != AM65_IMM && RegValIsKnown (Out->RegA)) { + /* Result of load is known */ + Arg = MakeHexArg (Out->RegA); + } + break; + + case OP65_LDX: + if (E->AM != AM65_IMM && RegValIsKnown (Out->RegX)) { + /* Result of load is known but register is X */ + Arg = MakeHexArg (Out->RegX); + OPC = OP65_LDX; + } + break; + + case OP65_LDY: + if (E->AM != AM65_IMM && RegValIsKnown (Out->RegY)) { + /* Result of load is known but register is Y */ + Arg = MakeHexArg (Out->RegY); + OPC = OP65_LDY; + } + break; + + case OP65_EOR: + if (RegValIsKnown (Out->RegA)) { + /* Accu op zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } + break; + + case OP65_ADC: + case OP65_SBC: + /* If this is an operation with an immediate operand of zero, + * and the register is zero, the operation won't give us any + * results we don't already have (including the flags), so + * remove it. Something like this is generated as a result of + * a compare where parts of the values are known to be zero. + */ + if (In->RegA == 0 && CE_IsKnownImm (E, 0x00)) { + /* 0-0 or 0+0 -> remove */ + CS_DelEntry (S, I); + ++Changes; + } + break; + + case OP65_AND: + if (CE_IsKnownImm (E, 0xFF)) { + /* AND with 0xFF, remove */ + CS_DelEntry (S, I); + ++Changes; + } else if (CE_IsKnownImm (E, 0x00)) { + /* AND with 0x00, replace by lda #$00 */ + Arg = MakeHexArg (0x00); + } else if (RegValIsKnown (Out->RegA)) { + /* Accu AND zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } else if (In->RegA == 0xFF) { + /* AND but A contains 0xFF - replace by lda */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } + break; + + case OP65_ORA: + if (CE_IsKnownImm (E, 0x00)) { + /* ORA with zero, remove */ + CS_DelEntry (S, I); + ++Changes; + } else if (CE_IsKnownImm (E, 0xFF)) { + /* ORA with 0xFF, replace by lda #$ff */ + Arg = MakeHexArg (0xFF); + } else if (RegValIsKnown (Out->RegA)) { + /* Accu AND zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } else if (In->RegA == 0) { + /* ORA but A contains 0x00 - replace by lda */ + CE_ReplaceOPC (E, OP65_LDA); + ++Changes; + } + break; + + default: + break; + + } + + /* Check if we have to replace the insn by LDA */ + if (Arg) { + CodeEntry* X = NewCodeEntry (OPC, AM65_IMM, Arg, 0, E->LI); + CS_InsertEntry (S, X, I+1); + CS_DelEntry (S, I); + ++Changes; + } + + /* Next entry */ + ++I; + } + + /* Free register info */ + CS_FreeRegInfo (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; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* 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; + + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* 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; + + /* Generate register info for this step */ + CS_GenRegInfo (S); + + /* 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; + + } + + /* Free register info */ + CS_FreeRegInfo (S); + + /* Return the number of changes made */ + return Changes; +}