diff --git a/src/cc65/codeinfo.c b/src/cc65/codeinfo.c index 9dd6e9b95..8592e32c8 100644 --- a/src/cc65/codeinfo.c +++ b/src/cc65/codeinfo.c @@ -144,6 +144,8 @@ static const FuncInfo FuncInfoTable[] = { { "staspidx", REG_A | REG_Y, REG_Y | REG_TMP1 | REG_PTR1 }, { "stax0sp", REG_AX, REG_Y }, { "staxysp", REG_AXY, REG_Y }, + { "tosadda0", REG_A, REG_AXY }, + { "tosaddax", REG_AX, REG_AXY }, { "tosicmp", REG_AX, REG_AXY | REG_SREG }, { "tosdiva0", REG_AX, REG_ALL }, { "tosdivax", REG_AX, REG_ALL }, @@ -164,22 +166,24 @@ static const ZPInfo ZPInfoTable[] = { { 0, "ptr1+1", REG_PTR1_HI, REG_PTR1 }, { 0, "ptr2", REG_PTR2_LO, REG_PTR2 }, { 0, "ptr2+1", REG_PTR2_HI, REG_PTR2 }, - { 4, "ptr3", REG_NONE, REG_NONE }, - { 4, "ptr4", REG_NONE, REG_NONE }, - { 7, "regbank", REG_NONE, REG_NONE }, + { 4, "ptr3", REG_NONE, REG_NONE }, + { 4, "ptr4", REG_NONE, REG_NONE }, + { 7, "regbank", REG_NONE, REG_NONE }, { 0, "regsave", REG_SAVE_LO, REG_SAVE }, { 0, "regsave+1", REG_SAVE_HI, REG_SAVE }, - { 2, "sp", REG_NONE, REG_NONE }, + { 0, "sp", REG_SP_LO, REG_SP }, + { 0, "sp+1", REG_SP_HI, REG_SP }, { 0, "sreg", REG_SREG_LO, REG_SREG }, { 0, "sreg+1", REG_SREG_HI, REG_SREG }, - { 0, "tmp1", REG_TMP1, REG_TMP1 }, - { 0, "tmp2", REG_NONE, REG_NONE }, - { 0, "tmp3", REG_NONE, REG_NONE }, - { 0, "tmp4", REG_NONE, REG_NONE }, + { 0, "tmp1", REG_TMP1, REG_TMP1 }, + { 0, "tmp2", REG_NONE, REG_NONE }, + { 0, "tmp3", REG_NONE, REG_NONE }, + { 0, "tmp4", REG_NONE, REG_NONE }, }; #define ZPInfoCount (sizeof(ZPInfoTable) / sizeof(ZPInfoTable[0])) + /*****************************************************************************/ /* Code */ /*****************************************************************************/ @@ -395,7 +399,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, unsigned U2; U1 = GetRegInfo2 (S, E->JumpTo->Owner, -1, Visited, Used, Unused, Wanted); - if (U1 == REG_AXY) { + if (U1 == REG_ALL) { /* All registers used, no need for second call */ return REG_AXY; } @@ -434,7 +438,7 @@ static unsigned GetRegInfo2 (CodeSeg* S, } - + static unsigned GetRegInfo1 (CodeSeg* S, CodeEntry* E, int Index, diff --git a/src/cc65/codeinfo.h b/src/cc65/codeinfo.h index c445bd431..435e49884 100644 --- a/src/cc65/codeinfo.h +++ b/src/cc65/codeinfo.h @@ -68,17 +68,21 @@ struct CodeSeg; #define REG_SREG_HI 0x0200U #define REG_SAVE_LO 0x0400U #define REG_SAVE_HI 0x0800U +#define REG_SP_LO 0x1000U +#define REG_SP_HI 0x2000U + /* Combined register defines */ #define REG_PTR1 (REG_PTR1_LO | REG_PTR1_HI) #define REG_PTR2 (REG_PTR2_LO | REG_PTR2_HI) #define REG_SREG (REG_SREG_LO | REG_SREG_HI) #define REG_SAVE (REG_SAVE_LO | REG_SAVE_HI) +#define REG_SP (REG_SP_LO | REG_SP_HI) #define REG_AX (REG_A | REG_X) #define REG_AY (REG_A | REG_Y) #define REG_XY (REG_X | REG_Y) #define REG_AXY (REG_AX | REG_Y) -#define REG_EAX (REG_AX | REG_SREG) +#define REG_EAX (REG_AX | REG_SREG) #define REG_EAXY (REG_EAX | REG_Y) #define REG_ZP 0xFFF8U #define REG_ALL 0xFFFFU diff --git a/src/cc65/codeopt.c b/src/cc65/codeopt.c index 789c177d1..0e8012864 100644 --- a/src/cc65/codeopt.c +++ b/src/cc65/codeopt.c @@ -47,6 +47,7 @@ #include "codeent.h" #include "codeinfo.h" #include "coptind.h" +#include "coptstop.h" #include "error.h" #include "global.h" #include "codeopt.h" @@ -1005,7 +1006,7 @@ static unsigned OptCmp2 (CodeSeg* S) /* Return the number of changes made */ return Changes; -} +} @@ -1301,25 +1302,25 @@ static unsigned OptCmp7 (CodeSeg* S) /* Evaluate the branch condition */ int Cond; - switch (GetBranchCond (N->OPC)) { - case BC_CC: - Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num); - break; + switch (GetBranchCond (N->OPC)) { + case BC_CC: + Cond = ((unsigned char)RegVal) < ((unsigned char)E->Num); + break; - case BC_CS: - Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num); - break; + case BC_CS: + Cond = ((unsigned char)RegVal) >= ((unsigned char)E->Num); + break; - case BC_EQ: - Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num); - break; + case BC_EQ: + Cond = ((unsigned char)RegVal) == ((unsigned char)E->Num); + break; - case BC_MI: + case BC_MI: Cond = ((signed char)RegVal) < ((signed char)E->Num); - break; + break; - case BC_NE: - Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num); + case BC_NE: + Cond = ((unsigned char)RegVal) != ((unsigned char)E->Num); break; case BC_PL: @@ -1328,7 +1329,17 @@ static unsigned OptCmp7 (CodeSeg* S) case BC_VC: case BC_VS: - } + /* Not set by the compare operation, bail out (Note: + * Just skipping anything here is rather stupid, but + * the sequence is never generated by the compiler, + * so it's quite safe to skip). + */ + goto NextEntry; + + default: + Internal ("Unknown branch condition"); + + } /* If the condition is false, we may remove the jump. Otherwise * the branch will always be taken, so we may replace it by a @@ -1355,7 +1366,8 @@ static unsigned OptCmp7 (CodeSeg* S) } - /* Next entry */ +NextEntry: + /* Next entry */ ++I; } @@ -2709,6 +2721,8 @@ static OptFunc OptFuncs [] = { OptEntry (OptDuplicateLoads, optMain), OptEntry (OptStoreLoad, optMain), OptEntry (OptTransfers, optMain), + /* Optimize operations that use the stack to pass operands */ + OptEntry (OptStackOps, optMain), /* Optimize branch distance */ OptEntry (OptBranchDist, optPost), }; diff --git a/src/cc65/coptind.h b/src/cc65/coptind.h index 746637092..ae6bb3720 100644 --- a/src/cc65/coptind.h +++ b/src/cc65/coptind.h @@ -38,12 +38,13 @@ +/* cc65 */ #include "codeseg.h" /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ diff --git a/src/cc65/coptstop.c b/src/cc65/coptstop.c new file mode 100644 index 000000000..40d6354a7 --- /dev/null +++ b/src/cc65/coptstop.c @@ -0,0 +1,299 @@ +/*****************************************************************************/ +/* */ +/* coptstop.c */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* 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 + +/* cc65 */ +#include "codeent.h" +#include "codeinfo.h" +#include "codeopt.h" +#include "error.h" +#include "coptstop.h" + + + +/*****************************************************************************/ +/* Helpers */ +/*****************************************************************************/ + + + +static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add) +/* Optimize the tosaddax sequence if possible */ +{ + unsigned I; + CodeEntry* N; + CodeEntry* X; + CodeEntry* PushEntry; + CodeEntry* AddEntry; + const char* ZPLo; + const char* ZPHi; + + /* Check if the sequence is safe. This means that there may not be any + * jumps between the two data points, and no usage of the stack. Handling + * these conditions is possible and may be done later. + */ + unsigned UsedRegs = REG_NONE; + for (I = Push + 1; I < Add; ++I) { + CodeEntry* E = CS_GetEntry (S, I); + if ((E->Info & OF_BRA) != 0 || + (E->Use & REG_SP) != 0 || + CE_HasLabel (E)) { + /* A jump or stack pointer usage - bail out */ + return 0; + } + UsedRegs |= (E->Use | E->Chg); + } + + /* We prefer usage of sreg for the intermediate value, since sreg is + * tracked and optimized. + */ + UsedRegs |= GetRegInfo (S, Push+1, REG_ALL); + if ((UsedRegs & REG_SREG) == REG_NONE) { + /* SREG is available */ + ZPLo = "sreg"; + ZPHi = "sreg+1"; + } else if ((UsedRegs & REG_PTR1) == REG_NONE) { + ZPLo = "ptr1"; + ZPHi = "ptr1+1"; + } else if ((UsedRegs & REG_PTR2) == REG_NONE) { + ZPLo = "ptr2"; + ZPHi = "ptr2+1"; + } else { + /* No registers available */ + return 0; + } + + /* We need the entry behind the add */ + if ((N = CS_GetNextEntry (S, Add)) == 0) { + /* Unavailable */ + return 0; + } + + /* Generate register info */ + CS_GenRegInfo (S); + + /* Get the push entry */ + PushEntry = CS_GetEntry (S, Push); + + /* Store the value into sreg instead of pushing it */ + X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); + CS_InsertEntry (S, X, Push+1); + X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); + CS_InsertEntry (S, X, Push+2); + + /* Correct the index of the add and get a pointer to the entry */ + Add += 2; + AddEntry = CS_GetEntry (S, Add); + + /* Inline the add */ + X = NewCodeEntry (OP65_CLC, AM65_IMP, 0, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+1); + X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPLo, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+2); + if (PushEntry->RI->In.RegX == 0 && AddEntry->RI->In.RegX == 0) { + /* The high byte is zero on entry */ + CodeLabel* L = CS_GenLabel (S, N); + X = NewCodeEntry (OP65_BCC, AM65_BRA, L->Name, L, AddEntry->LI); + CS_InsertEntry (S, X, Add+3); + X = NewCodeEntry (OP65_INX, AM65_IMP, 0, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+4); + } else { + /* High byte is unknown */ + X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+3); + X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+4); + X = NewCodeEntry (OP65_ADC, AM65_ZP, ZPHi, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+5); + X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+6); + X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AddEntry->LI); + CS_InsertEntry (S, X, Add+7); + } + + /* Remove the push and the call to the tosaddax function */ + CS_DelEntry (S, Add); + CS_DelEntry (S, Push); + + /* Free the register info */ + CS_FreeRegInfo (S); + + /* We changed the sequence */ + return 1; +} + + + +static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store) +/* Optimize the staspidx sequence if possible */ +{ + unsigned I; + CodeEntry* N; + CodeEntry* X; + CodeEntry* PushEntry; + CodeEntry* StoreEntry; + const char* ZPLo; + const char* ZPHi; + + /* Check if the sequence is safe. This means that there may not be any + * jumps between the two data points, and no usage of the stack. Handling + * these conditions is possible and may be done later. + */ + unsigned UsedRegs = REG_NONE; + for (I = Push + 1; I < Store; ++I) { + CodeEntry* E = CS_GetEntry (S, I); + if ((E->Info & OF_BRA) != 0 || + (E->Use & REG_SP) != 0 || + CE_HasLabel (E)) { + /* A jump or stack pointer usage - bail out */ + return 0; + } + UsedRegs |= (E->Use | E->Chg); + } + + /* We prefer usage of sreg for the intermediate value, since sreg is + * tracked and optimized. + */ + UsedRegs |= GetRegInfo (S, Push+1, REG_SREG | REG_PTR1 | REG_PTR2); + if ((UsedRegs & REG_SREG) == REG_NONE) { + /* SREG is available */ + ZPLo = "sreg"; + ZPHi = "sreg+1"; + } else if ((UsedRegs & REG_PTR1) == REG_NONE) { + ZPLo = "ptr1"; + ZPHi = "ptr1+1"; + } else if ((UsedRegs & REG_PTR2) == REG_NONE) { + ZPLo = "ptr2"; + ZPHi = "ptr2+1"; + } else { + /* No registers available */ + return 0; + } + + /* We need the entry behind the store */ + if ((N = CS_GetNextEntry (S, Store)) == 0) { + /* Unavailable */ + return 0; + } + + /* Generate register info */ + CS_GenRegInfo (S); + + /* Get the push entry */ + PushEntry = CS_GetEntry (S, Push); + + /* Store the value into sreg instead of pushing it */ + X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, PushEntry->LI); + CS_InsertEntry (S, X, Push+1); + X = NewCodeEntry (OP65_STX, AM65_ZP, ZPHi, 0, PushEntry->LI); + CS_InsertEntry (S, X, Push+2); + + /* Correct the index of the store and get a pointer to the entry */ + Store += 2; + StoreEntry = CS_GetEntry (S, Store); + + /* Inline the store */ + X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, StoreEntry->LI); + CS_InsertEntry (S, X, Store+1); + + /* Remove the push and the call to the staspidx function */ + CS_DelEntry (S, Store); + CS_DelEntry (S, Push); + + /* Free the register info */ + CS_FreeRegInfo (S); + + /* We changed the sequence */ + return 1; +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptStackOps (CodeSeg* S) +/* Optimize operations that take operands via the stack */ +{ + unsigned Changes = 0; /* Number of changes in one run */ + int LastPush = -1; /* Last call to pushax */ + + /* Walk over all entries */ + unsigned I = 0; + while (I < CS_GetEntryCount (S)) { + + /* Get the next entry */ + CodeEntry* E = CS_GetEntry (S, I); + + /* Check for a subroutine call */ + if (E->OPC == OP65_JSR) { + + /* We look for two things: A call to pushax, and a call to one + * of the known functions we're going to replace. We're only + * interested in the latter ones, if we had a push before. + */ + if (strcmp (E->Arg, "pushax") == 0) { + + /* Just remember it */ + LastPush = I; + + } else if (LastPush >= 0) { + + if (strcmp (E->Arg, "tosaddax") == 0) { + Changes += Opt_tosaddax (S, LastPush, I); + LastPush = -1; + } else if (strcmp (E->Arg, "staspidx") == 0) { + Changes += Opt_staspidx (S, LastPush, I); + LastPush = -1; + } + + } + } + + /* Next entry */ + ++I; + + } + + /* Return the number of changes made */ + return Changes; +} + + + diff --git a/src/cc65/coptstop.h b/src/cc65/coptstop.h new file mode 100644 index 000000000..786158e32 --- /dev/null +++ b/src/cc65/coptstop.h @@ -0,0 +1,61 @@ +/*****************************************************************************/ +/* */ +/* coptstop.h */ +/* */ +/* Optimize operations that take operands via the stack */ +/* */ +/* */ +/* */ +/* (C) 2001 Ullrich von Bassewitz */ +/* Wacholderweg 14 */ +/* D-70597 Stuttgart */ +/* 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 COPTSTOP_H +#define COPTSTOP_H + + + +/* cc65 */ +#include "codeseg.h" + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +unsigned OptStackOps (CodeSeg* S); +/* Optimize operations that take operands via the stack */ + + + +/* End of coptstop.h */ +#endif + + + diff --git a/src/cc65/make/gcc.mak b/src/cc65/make/gcc.mak index fb5478529..de707c806 100644 --- a/src/cc65/make/gcc.mak +++ b/src/cc65/make/gcc.mak @@ -34,6 +34,7 @@ OBJS = anonname.o \ codeseg.o \ compile.o \ coptind.o \ + coptstop.o \ cpu.o \ dataseg.o \ datatype.o \ @@ -105,3 +106,4 @@ depend dep: $(OBJS:.o=.c) $(CC) -I$(COMMON) -MM $^ > .depend + diff --git a/src/cc65/make/watcom.mak b/src/cc65/make/watcom.mak index ad6585172..2d21b640a 100644 --- a/src/cc65/make/watcom.mak +++ b/src/cc65/make/watcom.mak @@ -79,6 +79,7 @@ OBJS = anonname.obj \ codeseg.obj \ compile.obj \ coptind.obj \ + coptstop.obj \ cpu.obj \ dataseg.obj \ datatype.obj \ @@ -148,6 +149,7 @@ FILE codeopt.obj FILE codeseg.obj FILE compile.obj FILE coptind.obj +FILE coptstop.obj FILE cpu.obj FILE dataseg.obj FILE datatype.obj