X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fcoptind.c;h=63351677a44d24add188ecf4ade44c475dfad685;hb=67d64e67d590c75c30e48f41fdbdccce0c04fcb7;hp=ae77b6f87bb4feedac440b2bb10ccddefe884524;hpb=b391929acb8d1c320b86c68c1a812ecc06f31ba7;p=cc65 diff --git a/src/cc65/coptind.c b/src/cc65/coptind.c index ae77b6f87..63351677a 100644 --- a/src/cc65/coptind.c +++ b/src/cc65/coptind.c @@ -38,10 +38,10 @@ /* cc65 */ #include "codeent.h" +#include "coptind.h" #include "codeinfo.h" #include "codeopt.h" #include "error.h" -#include "coptind.h" @@ -544,7 +544,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 @@ -623,13 +623,90 @@ NextEntry: +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; +} + + + /*****************************************************************************/ /* 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 @@ -675,7 +752,7 @@ unsigned OptCondBranches (CodeSeg* S) 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) || @@ -692,7 +769,7 @@ unsigned OptCondBranches (CodeSeg* S) } if ((E->Info & OF_CBRA) != 0 && /* It's a conditional branch */ - (L = E->JumpTo) != 0 && /* ..referencing a local label */ + (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 */ @@ -722,6 +799,62 @@ 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 and stores */ /*****************************************************************************/ @@ -750,17 +883,17 @@ unsigned OptUnusedLoads (CodeSeg* S) /* Check which sort of load or transfer it is */ unsigned R; switch (E->OPC) { - case OP65_DEA: - case OP65_INA: - case OP65_LDA: + 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_DEX: + case OP65_INX: + case OP65_LDX: case OP65_TAX: R = REG_X; break; - case OP65_DEY: - case OP65_INY: + case OP65_DEY: + case OP65_INY: case OP65_LDY: case OP65_TAY: R = REG_Y; break; default: goto NextEntry; /* OOPS */ @@ -770,11 +903,11 @@ unsigned OptUnusedLoads (CodeSeg* S) if ((GetRegInfo (S, I+1, R) & R) == 0) { /* Register value is not used, remove the load */ - CS_DelEntry (S, I); + CS_DelEntry (S, I); - /* Remember, we had changes. Account the deleted entry in I. */ - ++Changes; - --I; + /* Remember, we had changes. Account the deleted entry in I. */ + ++Changes; + --I; } } @@ -822,6 +955,8 @@ unsigned OptUnusedStores (CodeSeg* S) /* Remember, we had changes */ ++Changes; + /* Continue with next insn */ + continue; } } @@ -896,7 +1031,7 @@ unsigned OptDupLoads (CodeSeg* S) * remove the store. */ if (RegValIsKnown (In->RegA) && /* Value of A is known */ - E->AM == AM65_ZP && /* Store into zp */ + E->AM == AM65_ZP && /* Store into zp */ In->RegA == ZPRegVal (E->Chg, In)) { /* Value identical */ Delete = 1; @@ -949,7 +1084,7 @@ unsigned OptDupLoads (CodeSeg* S) */ } else if (RegValIsKnown (In->RegY)) { if (In->RegY == In->RegA) { - CE_ReplaceOPC (E, OP65_STA); + CE_ReplaceOPC (E, OP65_STA); } else if (In->RegY == In->RegX && E->AM != AM65_ABSX && E->AM != AM65_ZPX) { @@ -1002,7 +1137,7 @@ unsigned OptDupLoads (CodeSeg* S) case OP65_TYA: if (RegValIsKnown (In->RegY) && - In->RegY == In->RegA && + In->RegY == In->RegA && (N = CS_GetNextEntry (S, I)) != 0 && !CE_UseLoadFlags (N)) { /* Value is identical and not followed by a branch */ @@ -1051,10 +1186,10 @@ unsigned OptStoreLoad (CodeSeg* S) unsigned I = 0; while (I < CS_GetEntryCount (S)) { - CodeEntry* N; - CodeEntry* X; + CodeEntry* N; + CodeEntry* X; - /* Get next entry */ + /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); /* Check if it is a store instruction followed by a load from the @@ -1240,16 +1375,18 @@ unsigned OptTransfers3 (CodeSeg* S) */ { unsigned Changes = 0; - 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 */ + 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 { - Searching, + Initialize, + Search, FoundXfer, FoundStore - } State = Searching; + } 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. @@ -1262,7 +1399,12 @@ unsigned OptTransfers3 (CodeSeg* S) switch (State) { - case Searching: + 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; @@ -1279,7 +1421,7 @@ unsigned OptTransfers3 (CodeSeg* S) /* Switch back to searching */ I = Xfer; - State = Searching; + State = Initialize; /* Does this insn use the target register of the transfer? */ } else if ((E->Use & XferEntry->Chg) != 0) { @@ -1294,7 +1436,7 @@ unsigned OptTransfers3 (CodeSeg* S) State = FoundStore; } else { I = Xfer; - State = Searching; + State = Initialize; } /* Does this insn change the target register of the transfer? */ @@ -1305,7 +1447,18 @@ unsigned OptTransfers3 (CodeSeg* S) * do that and bail out instead. */ I = Xfer; - State = Searching; + 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; @@ -1315,8 +1468,11 @@ unsigned OptTransfers3 (CodeSeg* S) * 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) && - !MemAccess (S, Xfer+1, Store-1, E->Arg)) { + (StoreEntry->AM == AM65_ABS || + StoreEntry->AM == AM65_ZP) && + (StoreEntry->AM != AM65_ZP || + (StoreEntry->Chg & UsedRegs) == 0) && + !MemAccess (S, Xfer+1, Store-1, StoreEntry->Arg)) { /* Generate the replacement store insn */ CodeEntry* X = 0; @@ -1360,11 +1516,11 @@ unsigned OptTransfers3 (CodeSeg* S) /* If we have a replacement store, change the code */ if (X) { - /* Insert before the xfer insn */ - CS_InsertEntry (S, X, Xfer); + /* Insert after the xfer insn */ + CS_InsertEntry (S, X, Xfer+1); /* Remove the xfer instead */ - CS_DelEntry (S, Xfer+1); + CS_DelEntry (S, Xfer); /* Remove the final store */ CS_DelEntry (S, Store); @@ -1382,7 +1538,7 @@ unsigned OptTransfers3 (CodeSeg* S) /* Restart after last xfer insn */ I = Xfer; } - State = Searching; + State = Initialize; break; } @@ -1409,10 +1565,10 @@ unsigned OptTransfers4 (CodeSeg* S) CodeEntry* XferEntry = 0; /* Pointer to xfer insn */ enum { - Searching, + Search, FoundLoad, FoundXfer - } State = Searching; + } State = Search; /* Walk over the entries. Look for a load instruction that is followed by * a load later. @@ -1425,7 +1581,7 @@ unsigned OptTransfers4 (CodeSeg* S) switch (State) { - case Searching: + case Search: if (E->Info & OF_LOAD) { /* Found start of sequence */ Load = I; @@ -1442,7 +1598,7 @@ unsigned OptTransfers4 (CodeSeg* S) /* Switch back to searching */ I = Load; - State = Searching; + State = Search; /* Does this insn use the target register of the load? */ } else if ((E->Use & LoadEntry->Chg) != 0) { @@ -1457,7 +1613,7 @@ unsigned OptTransfers4 (CodeSeg* S) State = FoundXfer; } else { I = Load; - State = Searching; + State = Search; } /* Does this insn change the target register of the load? */ @@ -1468,7 +1624,7 @@ unsigned OptTransfers4 (CodeSeg* S) * do that and bail out instead. */ I = Load; - State = Searching; + State = Search; } break; @@ -1478,10 +1634,10 @@ unsigned OptTransfers4 (CodeSeg* S) * 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_ABS || LoadEntry->AM == AM65_ZP || LoadEntry->AM == AM65_IMM) && - !MemAccess (S, Load+1, Xfer-1, E->Arg)) { + !MemAccess (S, Load+1, Xfer-1, LoadEntry->Arg)) { /* Generate the replacement load insn */ CodeEntry* X = 0; @@ -1518,11 +1674,11 @@ unsigned OptTransfers4 (CodeSeg* S) /* If we have a replacement load, change the code */ if (X) { - /* Insert before the xfer insn */ - CS_InsertEntry (S, X, Xfer); + /* Insert after the xfer insn */ + CS_InsertEntry (S, X, Xfer+1); /* Remove the xfer instead */ - CS_DelEntry (S, Xfer+1); + CS_DelEntry (S, Xfer); /* Remove the initial load */ CS_DelEntry (S, Load); @@ -1540,7 +1696,7 @@ unsigned OptTransfers4 (CodeSeg* S) /* Restart after last xfer insn */ I = Xfer; } - State = Searching; + State = Search; break; } @@ -1626,12 +1782,12 @@ unsigned OptPushPop (CodeSeg* S) !RegAUsed (S, I+1) && !MemAccess (S, Push+1, Pop-1, E->Arg)) { - /* Insert a STA before the PHA */ + /* Insert a STA after the PHA */ X = NewCodeEntry (E->OPC, E->AM, E->Arg, E->JumpTo, E->LI); - CS_InsertEntry (S, X, Push); + CS_InsertEntry (S, X, Push+1); /* Remove the PHA instead */ - CS_DelEntry (S, Push+1); + CS_DelEntry (S, Push); /* Remove the PLA/STA sequence */ CS_DelEntries (S, Pop, 2); @@ -1690,8 +1846,9 @@ unsigned OptPrecalc (CodeSeg* S) /* Get next entry */ CodeEntry* E = CS_GetEntry (S, I); - /* Get a pointer to the output registers of the insn */ + /* 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; @@ -1723,25 +1880,48 @@ unsigned OptPrecalc (CodeSeg* S) } break; - case OP65_ADC: case OP65_ASL: case OP65_EOR: case OP65_LSR: - case OP65_SBC: 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; + } else if (RegValIsKnown (Out->RegA)) { + /* Accu op zp with known contents */ + Arg = MakeHexArg (Out->RegA); + } + 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; @@ -1750,9 +1930,16 @@ unsigned OptPrecalc (CodeSeg* S) /* 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; @@ -1849,3 +2036,117 @@ unsigned OptBranchDist (CodeSeg* S) +/*****************************************************************************/ +/* 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; +} + + +