]> git.sur5r.net Git - cc65/commitdiff
More stack op optimizations
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 1 Oct 2001 19:40:01 +0000 (19:40 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 1 Oct 2001 19:40:01 +0000 (19:40 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@991 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/cc65/coptstop.c

index b1cca1ada4e06576c32007d42fac84aa4f6dab53..6123b945abb19c4c9af7eadceba8df0b92062f4b 100644 (file)
 
 
 
+#include <stdlib.h>
+
+/* common */
+#include "xsprintf.h"
+
 /* cc65 */
 #include "codeent.h"
 #include "codeinfo.h"
 
 
 
-static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add)
+static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store,
+                             const char* ZPLo, const char* ZPHi)
+/* Optimize the staspidx sequence if possible */
+{
+    CodeEntry* X;
+    CodeEntry* PushEntry;
+    CodeEntry* StoreEntry;
+
+    /* Generate register info */
+    CS_GenRegInfo (S);
+
+    /* Get the push entry */
+    PushEntry = CS_GetEntry (S, Push);
+
+    /* Store the value into the zeropage 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;
+}
+
+
+
+static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add,
+                             const char* ZPLo, const char* ZPHi)
 /* 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->OPC == OP65_JSR      ||
-           (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) {
@@ -107,7 +115,7 @@ static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add)
     /* Get the push entry */
     PushEntry = CS_GetEntry (S, Push);
 
-    /* Store the value into sreg instead of pushing it */
+    /* Store the value into the zeropage 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);
@@ -156,58 +164,71 @@ static unsigned Opt_tosaddax (CodeSeg* S, unsigned Push, unsigned Add)
 
 
 
-static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
-/* Optimize the staspidx sequence if possible */
+static unsigned Opt_tosandax (CodeSeg* S, unsigned Push, unsigned And,
+                             const char* ZPLo, const char* ZPHi)
+/* Optimize the tosandax sequence if possible */
 {
-    unsigned I;
-    CodeEntry* N;
     CodeEntry* X;
     CodeEntry* PushEntry;
-    CodeEntry* StoreEntry;
-    const char* ZPLo;
-    const char* ZPHi;
+    CodeEntry* AndEntry;
 
-    /* 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->OPC == OP65_JSR      ||
-           (E->Use & REG_SP) != 0  ||
-           CE_HasLabel (E)) {
-           /* A jump or stack pointer usage - bail out */
-           return 0;
-       }
-       UsedRegs |= (E->Use | E->Chg);
-    }
+    /* Generate register info */
+    CS_GenRegInfo (S);
 
-    /* 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";
+    /* Get the push entry */
+    PushEntry = CS_GetEntry (S, Push);
+
+    /* Store the value into the zeropage 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 */
+    And += 2;
+    AndEntry = CS_GetEntry (S, And);
+
+    /* Inline the and */
+    X = NewCodeEntry (OP65_AND, AM65_ZP, ZPLo, 0, AndEntry->LI);
+    CS_InsertEntry (S, X, And+1);
+    if (PushEntry->RI->In.RegX == 0 || AndEntry->RI->In.RegX == 0) {
+       /* The high byte is zero */
+               X = NewCodeEntry (OP65_LDX, AM65_IMM, "$00", 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+2);
     } else {
-       /* No registers available */
-       return 0;
+       /* High byte is unknown */
+               X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+2);
+       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+3);
+       X = NewCodeEntry (OP65_AND, AM65_ZP, ZPHi, 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+4);
+       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+5);
+       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, AndEntry->LI);
+       CS_InsertEntry (S, X, And+6);
     }
 
-    /* We need the entry behind the store */
-    if ((N = CS_GetNextEntry (S, Store)) == 0) {
-       /* Unavailable */
-       return 0;
-    }
+    /* Remove the push and the call to the tosandax function */
+    CS_DelEntry (S, And);
+    CS_DelEntry (S, Push);
+
+    /* Free the register info */
+    CS_FreeRegInfo (S);
+
+    /* We changed the sequence */
+    return 1;
+}
+
+
+
+static unsigned Opt_tosorax (CodeSeg* S, unsigned Push, unsigned Or,
+                            const char* ZPLo, const char* ZPHi)
+/* Optimize the tosorax sequence if possible */
+{
+    CodeEntry* X;
+    CodeEntry* PushEntry;
+    CodeEntry* OrEntry;
 
     /* Generate register info */
     CS_GenRegInfo (S);
@@ -215,22 +236,41 @@ static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
     /* Get the push entry */
     PushEntry = CS_GetEntry (S, Push);
 
-    /* Store the value into sreg instead of pushing it */
+    /* Store the value into the zeropage 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);
+    /* Correct the index of the add and get a pointer to the entry */
+    Or += 2;
+    OrEntry = CS_GetEntry (S, Or);
+
+    /* Inline the or */
+    X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPLo, 0, OrEntry->LI);
+    CS_InsertEntry (S, X, Or+1);
+    if (PushEntry->RI->In.RegX >= 0 && OrEntry->RI->In.RegX == 0) {
+       /* Value of X will be that of the first operand */
+       char Buf [16];
+       xsprintf (Buf, sizeof (Buf), "$%02X", PushEntry->RI->In.RegX);
+               X = NewCodeEntry (OP65_LDX, AM65_IMM, Buf, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+2);
+    } else if (PushEntry->RI->In.RegX != 0) {
+       /* High byte is unknown */
+               X = NewCodeEntry (OP65_STA, AM65_ZP, ZPLo, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+2);
+       X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+3);
+               X = NewCodeEntry (OP65_ORA, AM65_ZP, ZPHi, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+4);
+       X = NewCodeEntry (OP65_TAX, AM65_IMP, 0, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+5);
+       X = NewCodeEntry (OP65_LDA, AM65_ZP, ZPLo, 0, OrEntry->LI);
+       CS_InsertEntry (S, X, Or+6);
+    }
 
-    /* Remove the push and the call to the staspidx function */
-    CS_DelEntry (S, Store);
+    /* Remove the push and the call to the tosandax function */
+    CS_DelEntry (S, Or);
     CS_DelEntry (S, Push);
 
     /* Free the register info */
@@ -248,42 +288,147 @@ static unsigned Opt_staspidx (CodeSeg* S, unsigned Push, unsigned Store)
 
 
 
+typedef unsigned (*OptFunc) (CodeSeg* S, unsigned Push, unsigned Store,
+                            const char* ZPLo, const char* ZPHi);
+typedef struct OptFuncDesc OptFuncDesc;
+struct OptFuncDesc {
+    const char*         Name;   /* Name of the replaced runtime function */
+    OptFunc             Func;   /* Function pointer */
+};
+
+static const OptFuncDesc FuncTable[] = {
+    { "staspidx",       Opt_staspidx    },
+    { "tosaddax",       Opt_tosaddax    },
+    { "tosandax",       Opt_tosandax    },
+    { "tosorax",        Opt_tosorax     },
+};
+#define FUNC_COUNT (sizeof(FuncTable) / sizeof(FuncTable[0]))
+
+
+
+static int CmpFunc (const void* Key, const void* Func)
+/* Compare function for bsearch */
+{
+    return strcmp (Key, ((const        OptFuncDesc*) Func)->Name);
+}
+
+
+
+static const OptFuncDesc* FindFunc (const char* Name)
+/* Find the function with the given name. Return a pointer to the table entry
+ * or NULL if the function was not found.
+ */
+{
+    return bsearch (Name, FuncTable, FUNC_COUNT, sizeof(OptFuncDesc), CmpFunc);
+}
+
+
+
+/*****************************************************************************/
+/*                                  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 Changes = 0;     /* Number of changes in one run */
+    int      InSeq = 0;       /* Inside a sequence */
+    unsigned Push = 0;               /* Index of pushax */
+    unsigned UsedRegs = 0;    /* Zeropage registers used in sequence */
+
+
+    /* Look for a call to pushax followed by a call to some other function
+     * that takes it's first argument on the stack, and the second argument
+     * in the primary register.
+     * It depends on the code between the two if we can handle/transform the
+     * sequence, so check this code for the following list of things:
+     *
+     *  - there must not be a jump or conditional branch (this may
+     *    get relaxed later).
+     *  - there may not be accesses to local variables (may also be
+     *    relaxed later)
+     *  - no subroutine calls
+     *  - no jump labels
+     *
+     * Since we need a zero page register later, do also check the
+     * intermediate code for zero page use.
+     */
     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) {
+       /* Handling depends if we're inside a sequence or not */
+       if (InSeq) {
+
+           /* Subroutine call? */
+           if (E->OPC == OP65_JSR) {
+
+               /* Check if this is one of our functions */
+               const OptFuncDesc* F = FindFunc (E->Arg);
+               if (F) {
+
+                   /* Determine the register to use */
+                   const char* ZPLo;
+                   const char* ZPHi;
+                           UsedRegs |= GetRegInfo (S, I+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 */
+                       ZPLo = 0;
+                       ZPHi = 0;
+                   }
+
+                   /* If we have a register, call the optimizer function */
+                   if (ZPLo && ZPHi) {
+                       Changes += F->Func (S, Push, I, ZPLo, ZPHi);
+                   }
+
+                   /* End of sequence */
+                   InSeq = 0;
+
+               } else if (strcmp (E->Arg, "pushax") == 0) {
+                   /* Restart the sequence */
+                   Push     = I;
+                   UsedRegs = REG_NONE;
+               } else {
+                   /* A call to an unkown subroutine ends the sequence */
+                   InSeq = 0;
+               }
 
-           /* 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) {
+           } else if ((E->Info & OF_BRA) != 0 ||
+                      (E->Use & REG_SP) != 0  ||
+                      CE_HasLabel (E)) {
 
-               /* Just remember it */
-               LastPush = I;
+               /* All this stuff is not allowed in a sequence */
+               InSeq = 0;
 
-           } else if (LastPush >= 0) {
+           } else {
 
-               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;
-               }
+               /* Other stuff: Track zeropage register usage */
+               UsedRegs |= (E->Use | E->Chg);
 
            }
+
+       } else if (CE_IsCall (E, "pushax")) {
+
+           /* This starts a sequence */
+           Push     = I;
+           UsedRegs = REG_NONE;
+           InSeq    = 1;
+
        }
 
        /* Next entry */