]> git.sur5r.net Git - cc65/commitdiff
Added backend optimizations
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Wed, 9 May 2001 21:56:03 +0000 (21:56 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Wed, 9 May 2001 21:56:03 +0000 (21:56 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@722 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/cc65/codeent.c
src/cc65/codeent.h
src/cc65/codeopt.c
src/cc65/codeopt.h
src/cc65/main.c
src/cc65/opcodes.c
src/cc65/opcodes.h

index f2661e1e5e9379b93ca48a52f6653d60d1ceaa7d..2a0c6164f30434fadad8ce9aff3e30b296c38110 100644 (file)
 
 
 
+#include <stdlib.h>
 #include <string.h>
 
 /* common */
+#include "chartype.h"
 #include "check.h"
 #include "xmalloc.h"
 
@@ -90,6 +92,47 @@ static char* GetArgCopy (const char* Arg)
 
 
 
+static int NumArg (const char* Arg, unsigned long* Num)
+/* If the given argument is numerical, convert it and return true. Otherwise
+ * set Num to zero and return false.
+ */
+{
+    char* End;
+    unsigned long Val;
+
+    /* Determine the base */
+    int Base = 10;
+    if (*Arg == '$') {
+       ++Arg;
+       Base = 16;
+    } else if (*Arg == '%') {
+       ++Arg;
+       Base = 2;
+    }
+
+    /* Convert the value. strtol is not exactly what we want here, but it's
+     * cheap and may be replaced by something fancier later.
+     */
+    Val = strtoul (Arg, &End, Base);
+
+    /* Check if the conversion was successful */
+    if (*End != '\0') {
+
+       /* Could not convert */
+       *Num = 0;
+       return 0;
+
+    } else {
+
+       /* Conversion ok */
+       *Num = Val;
+       return 1;
+
+    }
+}
+
+
+
 /*****************************************************************************/
 /*                                          Code                                    */
 /*****************************************************************************/
@@ -108,8 +151,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
     E->Size    = GetInsnSize (E->OPC, E->AM);
     E->Hints   = 0;
     E->Arg             = GetArgCopy (Arg);
-    E->Num     = 0;
-    E->Flags   = 0;
+    if (NumArg (E->Arg, &E->Num)) {
+       E-> Flags = CEF_NUMARG;
+    } else {
+               E->Flags  = 0;
+    }
     E->Info    = D->Info;
     E->Use     = D->Use;
     E->Chg     = D->Chg;
@@ -118,7 +164,7 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
        GetFuncInfo (E->Arg, &E->Use, &E->Chg);
     } else {
        /* Some other instruction */
-       E->Use |= GetAMUseInfo (AM);
+       E->Use |= GetAMUseInfo (E->AM);
     }
     E->JumpTo  = JumpTo;
     InitCollection (&E->Labels);
@@ -149,6 +195,31 @@ void FreeCodeEntry (CodeEntry* E)
 
 
 
+void ReplaceOPC (CodeEntry* E, opc_t OPC)
+/* Replace the opcode of the instruction. This will also replace related info,
+ * Size, Use and Chg, but it will NOT update any arguments or labels.
+ */
+{
+    /* Get the opcode descriptor */
+    const OPCDesc* D = GetOPCDesc (OPC);
+
+    /* Replace the opcode */
+    E->OPC     = OPC;
+    E->Size    = GetInsnSize (E->OPC, E->AM);
+    E->Info    = D->Info;
+    E->Use     = D->Use;
+    E->Chg     = D->Chg;
+    if (E->OPC == OPC_JSR) {
+       /* A subroutine call */
+       GetFuncInfo (E->Arg, &E->Use, &E->Chg);
+    } else {
+       /* Some other instruction */
+       E->Use |= GetAMUseInfo (E->AM);
+    }
+}
+
+
+
 int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2)
 /* Check if both code entries are equal */
 {
index ba704eb2836ffb08f9f47b37485a29a3934e2151..e862a197a4cb3cf0aab433a86b45f584812d3166 100644 (file)
@@ -67,7 +67,7 @@ struct CodeEntry {
     unsigned char      Size;           /* Estimated size */
     unsigned char      Hints;          /* Hints for this entry */
     char*                      Arg;            /* Argument as string */
-    unsigned           Num;            /* Numeric argument */
+    unsigned long      Num;            /* Numeric argument */
     unsigned short     Flags;          /* Flags */
     unsigned char      Info;           /* Additional code info */
     unsigned char      Use;            /* Registers used */
@@ -90,6 +90,11 @@ CodeEntry* NewCodeEntry (const OPCDesc* D, am_t AM, const char* Arg, CodeLabel*
 void FreeCodeEntry (CodeEntry* E);
 /* Free the given code entry */
 
+void ReplaceOPC (CodeEntry* E, opc_t OPC);
+/* Replace the opcode of the instruction. This will also replace related info,
+ * Size, Use and Chg, but it will NOT update any arguments or labels.
+ */
+
 int CodeEntriesAreEqual (const CodeEntry* E1, const CodeEntry* E2);
 /* Check if both code entries are equal */
 
index 90417a53734e7343516455c2f34f15776b255e28..b52212dbbd9ebda5b96bd8b4dfd72b32b0684d03 100644 (file)
 
 
 
+#include <string.h>
+
 /* common */
+#include "abend.h"
 #include "print.h"
 
 /* cc65 */
 #include "asmlabel.h"
 #include "codeent.h"
 #include "codeinfo.h"
+#include "error.h"
 #include "global.h"
 #include "codeopt.h"
 
  */
 static unsigned OptChanges;
 
+/* Defines for the conditions in a compare */
+typedef enum {
+    CMP_INV = -1,
+    CMP_EQ,
+    CMP_NE,
+    CMP_GT,
+    CMP_GE,
+    CMP_LT,
+    CMP_LE,
+    CMP_UGT,
+    CMP_UGE,
+    CMP_ULT,
+    CMP_ULE
+} cmp_t;
+
+/* Table with the compare suffixes */
+static const char CmpSuffixTab [][4] = {
+    "eq", "ne", "gt", "ge", "lt", "le", "ugt", "uge", "ult", "ule"
+};
+
+/* Table used to invert a condition, indexed by condition */
+static const unsigned char CmpInvertTab [] = {
+    CMP_NE, CMP_EQ,
+    CMP_LE, CMP_LT, CMP_GE, CMP_GT,
+    CMP_ULE, CMP_ULT, CMP_UGE, CMP_UGT
+};
+
+/* Table to show which compares are signed (use the N flag) */
+static const char CmpSignedTab [] = {
+    0, 0, 1, 1, 1, 1, 0, 0, 0, 0
+};
+
 
 
 /*****************************************************************************/
-/*                            Remove dead jumps                             */
+/*                            Helper functions                              */
+/*****************************************************************************/
+
+
+
+static cmp_t FindCmpCond (const char* Suffix)
+/* Map a condition suffix to a code. Return the code or CMP_INV on failure */
+{
+    int I;
+
+    /* Linear search */
+    for (I = 0; I < sizeof (CmpSuffixTab) / sizeof (CmpSuffixTab [0]); ++I) {
+               if (strcmp (Suffix, CmpSuffixTab [I]) == 0) {
+           /* Found */
+           return I;
+       }
+    }
+
+    /* Not found */
+    return CMP_INV;
+}
+
+
+
+/*****************************************************************************/
+/*                            Remove dead jumps                             */
 /*****************************************************************************/
 
 
@@ -109,7 +170,7 @@ static void OptDeadJumps (CodeSeg* S)
 
 
 /*****************************************************************************/
-/*                            Remove dead code                              */
+/*                            Remove dead code                              */
 /*****************************************************************************/
 
 
@@ -237,7 +298,7 @@ static void OptJumpCascades (CodeSeg* S)
                ++OptChanges;
 
                /* Done */
-               goto NextEntry;
+               continue;
 
            }
 
@@ -260,7 +321,7 @@ NextEntry:
 
 
 /*****************************************************************************/
-/*                            Optimize jsr/rts                              */
+/*                            Optimize jsr/rts                              */
 /*****************************************************************************/
 
 
@@ -377,7 +438,7 @@ static void OptJumpTarget (CodeSeg* S)
             * move references to this label to the new label.
             */
            if (CodeEntryHasLabel (E1)) {
-               MoveCodeLabels (S, E1, T1);
+               MoveCodeLabels (S, E1, T1);
            }
 
            /* Remove the entry preceeding the jump */
@@ -398,44 +459,315 @@ NextEntry:
 
 
 
+/*****************************************************************************/
+/*                  Remove conditional jumps never taken                    */
+/*****************************************************************************/
+
+
+
+static void OptDeadCondBranches (CodeSeg* S)
+/* If an immidiate load of a register is followed by a conditional jump that
+ * is never taken because the load of the register sets the flags in such a
+ * manner, remove the conditional branch.
+ */
+{
+    unsigned I;
+
+    /* Get the number of entries, bail out if we have not enough */
+    unsigned Count = GetCodeEntryCount (S);
+    if (Count < 2) {
+       return;
+    }
+
+    /* Walk over the entries */
+    I = 0;
+    while (I < Count-1) {
+
+       /* Get next entry */
+               CodeEntry* E = GetCodeEntry (S, I);
+
+       /* Check if it's a register load */
+               if ((E->Info & OF_LOAD) != 0 && E->AM == AM_IMM && (E->Flags & CEF_NUMARG) != 0) {
+
+           bc_t BC;
+
+           /* Immidiate register load, get next instruction */
+           CodeEntry* N = GetCodeEntry (S, I+1);
+
+           /* Check if the following insn is a conditional branch or if it
+            * has a label attached.
+            */
+           if ((N->Info & OF_CBRA) == 0 || CodeEntryHasLabel (E)) {
+               /* No conditional jump or label attached, bail out */
+               goto NextEntry;
+           }
+
+           /* Get the branch condition */
+           BC = GetBranchCond (N->OPC);
+
+           /* Check the argument against the branch condition */
+                   if ((BC == BC_EQ && E->Num != 0)            ||
+               (BC == BC_NE && E->Num == 0)            ||
+               (BC == BC_PL && (E->Num & 0x80) != 0)   ||
+               (BC == BC_MI && (E->Num & 0x80) == 0)) {
+
+               /* Remove the conditional branch */
+               DelCodeEntry (S, I+1);
+               --Count;
+
+               /* Remember, we had changes */
+               ++OptChanges;
+
+           }
+       }
+
+NextEntry:
+       /* Next entry */
+       ++I;
+
+    }
+}
+
+
+
+/*****************************************************************************/
+/*            Remove calls to the bool transformer subroutines              */
+/*****************************************************************************/
+
+
+
+static void OptBoolTransforms (CodeSeg* S)
+/* Try to remove the call to boolean transformer routines where the call is
+ * not really needed.
+ */
+{
+    unsigned I;
+
+    /* Get the number of entries, bail out if we have not enough */
+    unsigned Count = GetCodeEntryCount (S);
+    if (Count < 2) {
+       return;
+    }
+
+    /* Walk over the entries */
+    I = 0;
+    while (I < Count-1) {
+
+       /* Get next entry */
+               CodeEntry* E = GetCodeEntry (S, I);
+
+       /* Check for a boolean transformer */
+       if (E->OPC == OPC_JSR && strncmp (E->Arg, "bool", 4) == 0) {
+
+           cmp_t Cond;
+
+           /* Get the next entry */
+           CodeEntry* N = GetCodeEntry (S, I+1);
+
+           /* Check if this is a conditional branch */
+           if ((N->Info & OF_CBRA) == 0) {
+               /* No conditional branch, bail out */
+               goto NextEntry;
+           }
+
+           /* Make the boolean transformer unnecessary by changing the
+            * the conditional jump to evaluate the condition flags that
+            * are set after the compare directly. Note: jeq jumps if
+            * the condition is not met, jne jumps if the condition is met.
+            */
+           Cond = FindCmpCond (E->Arg + 4);
+           if (Cond == CMP_INV) {
+               /* Unknown function */
+               goto NextEntry;
+           }
+
+           /* Invert the code if we jump on condition not met. */
+                   if (GetBranchCond (N->OPC) == BC_EQ) {
+               /* Jumps if condition false, invert condition */
+               Cond = CmpInvertTab [Cond];
+           }
+
+           /* Check if we can replace the code by something better */
+           switch (Cond) {
+
+               case CMP_EQ:
+                   ReplaceOPC (N, OPC_JEQ);
+                   break;
+
+               case CMP_NE:
+                   ReplaceOPC (N, OPC_JNE);
+                   break;
+
+               case CMP_GT:
+                   /* Not now ### */
+                   goto NextEntry;
+
+               case CMP_GE:
+                   ReplaceOPC (N, OPC_JPL);
+                   break;
+
+               case CMP_LT:
+                   ReplaceOPC (N, OPC_JMI);
+                   break;
+
+               case CMP_LE:
+                   /* Not now ### */
+                   goto NextEntry;
+
+               case CMP_UGT:
+                   /* Not now ### */
+                   goto NextEntry;
+
+               case CMP_UGE:
+                           ReplaceOPC (N, OPC_JCS);
+                   break;
+
+               case CMP_ULT:
+                   ReplaceOPC (N, OPC_JCC);
+                   break;
+
+               case CMP_ULE:
+                   /* Not now ### */
+                   goto NextEntry;
+
+               default:
+                   Internal ("Unknown jump condition: %d", Cond);
+
+           }
+
+           /* Remove the call to the bool transformer */
+           DelCodeEntry (S, I);
+           --Count;
+
+           /* Remember, we had changes */
+           ++OptChanges;
+
+       }
+
+NextEntry:
+       /* Next entry */
+       ++I;
+
+    }
+}
+
+
+
 /*****************************************************************************/
 /*                                          Code                                    */
 /*****************************************************************************/
 
 
 
+/* Table with all the optimization functions */
+typedef struct OptFunc OptFunc;
+struct OptFunc {
+    void (*Func) (CodeSeg*);   /* Optimizer function */
+    const char*        Name;           /* Name of optimizer step */
+    char       Disabled;       /* True if pass disabled */
+};
+
+
+
+/* Table with optimizer steps -  are called in this order */
+static OptFunc OptFuncs [] = {
+    /* Optimize jump cascades */
+    { OptJumpCascades,             "OptJumpCascades",          0       },
+    /* Remove dead jumps */
+    { OptDeadJumps,                "OptDeadJumps",             0       },
+    /* Change jsr/rts to jmp */
+    { OptRTS,                      "OptRTS",                   0       },
+    /* Remove dead code */
+    { OptDeadCode,                 "OptDeadCode",              0       },
+    /* Optimize jump targets */
+    { OptJumpTarget,               "OptJumpTarget",            0       },
+    /* Remove dead conditional branches */
+    { OptDeadCondBranches,  "OptDeadCondBranches",     0       },
+    /* Remove calls to the bool transformer subroutines        */
+    { OptBoolTransforms,    "OptBoolTransforms",       0       },
+};
+
+
+
+static OptFunc* FindOptStep (const char* Name)
+/* Find an optimizer step by name in the table and return a pointer. Print an
+ * error and cann AbEnd if not found.
+ */
+{
+    unsigned I;
+
+    /* Run all optimization steps */
+    for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
+       if (strcmp (OptFuncs[I].Name, Name) == 0) {
+           /* Found */
+           return OptFuncs+I;
+       }
+    }
+
+    /* Not found */
+    AbEnd ("Optimization step `%s' not found", Name);
+    return 0;
+}
+
+
+
+void DisableOpt (const char* Name)
+/* Disable the optimization with the given name */
+{
+    OptFunc* F  = FindOptStep (Name);
+    F->Disabled = 1;
+}
+
+
+
+void EnableOpt (const char* Name)
+/* Enable the optimization with the given name */
+{
+    OptFunc* F  = FindOptStep (Name);
+    F->Disabled = 0;
+}
+
+
+
 void RunOpt (CodeSeg* S)
 /* Run the optimizer */
 {
-    typedef void (*OptFunc) (CodeSeg*);
+    unsigned Pass = 0;
 
-    /* Table with optimizer steps -  are called in this order */
-    static const OptFunc OptFuncs [] = {
-       OptJumpCascades,        /* Optimize jump cascades */
-               OptDeadJumps,           /* Remove dead jumps */
-       OptDeadCode,            /* Remove dead code */
-       OptRTS,                 /* Change jsr/rts to jmp */
-       OptJumpTarget,          /* Optimize jump targets */
-    };
+    /* Print the name of the function we are working on */
+    if (S->Func) {
+       Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
+    } else {
+       Print (stdout, 1, "Running optimizer for global code segment\n");
+    }
 
     /* Repeat all steps until there are no more changes */
     do {
 
-       unsigned long Flags;
-       unsigned      I;
+               unsigned I;
 
        /* Reset the number of changes */
        OptChanges = 0;
 
+       /* Keep the user hapy */
+       Print (stdout, 1, "  Optimizer pass %u:\n", ++Pass);
+
                /* Run all optimization steps */
-       Flags = 1UL;
                for (I = 0; I < sizeof(OptFuncs)/sizeof(OptFuncs[0]); ++I) {
-           if ((OptDisable & Flags) == 0) {
-               OptFuncs[I] (S);
-           } else if (Verbosity > 0 || Debug) {
-               printf ("Optimizer pass %u skipped\n", I);
+
+           /* Print the name of the following optimizer step */
+           Print (stdout, 1, "    %s:%*s", OptFuncs[I].Name,
+                  (int) (30-strlen(OptFuncs[I].Name)), "");
+
+           /* Check if the step is disabled */
+                   if (OptFuncs[I].Disabled) {
+               Print (stdout, 1, "Disabled\n");
+           } else {
+               unsigned Changes = OptChanges;
+               OptFuncs[I].Func (S);
+               Changes = OptChanges - Changes;
+               Print (stdout, 1, "%u Changes\n", Changes);
            }
-           Flags <<= 1;
        }
 
     } while (OptChanges > 0);
index 15960a99fb555488e6741021582a029daf6a3971..72f9c44bb999ca389249881c2365388a96df0e37 100644 (file)
 
 
 
+void DisableOpt (const char* Name);
+/* Disable the optimization with the given name */
+
+void EnableOpt (const char* Name);
+/* Enable the optimization with the given name */
+
 void RunOpt (CodeSeg* S);
 /* Run the optimizer */
 
index 65346db94952aab495956730016891d1da9b9d97..874e9b4b17d81478fda0c8a0d26efb0491885eed 100644 (file)
@@ -52,6 +52,7 @@
 /* cc65 */
 #include "asmcode.h"
 #include "compile.h"
+#include "codeopt.h"
 #include "cpu.h"
 #include "error.h"
 #include "global.h"
@@ -103,6 +104,8 @@ static void Usage (void)
                     "  --data-name seg\tSet the name of the DATA segment\n"
                     "  --debug\t\tDebug mode\n"
                     "  --debug-info\t\tAdd debug info to object file\n"
+            "  --disable-opt name\tDisable an optimization step\n"
+                    "  --enable-opt name\tEnable an optimization step\n"
             "  --help\t\tHelp (this text)\n"
                     "  --include-dir dir\tSet an include directory search path\n"
                     "  --rodata-name seg\tSet the name of the RODATA segment\n"
@@ -377,6 +380,22 @@ static void OptDebugInfo (const char* Opt, const char* Arg)
 
 
 
+static void OptDisableOpt (const char* Opt, const char* Arg)
+/* Disable an optimization step */
+{
+    DisableOpt (Arg);
+}
+
+
+
+static void OptEnableOpt (const char* Opt, const char* Arg)
+/* Enable an optimization step */
+{
+    EnableOpt (Arg);
+}
+
+
+
 static void OptHelp (const char* Opt, const char* Arg)
 /* Print usage information and exit */
 {
@@ -463,6 +482,8 @@ int main (int argc, char* argv[])
        { "--data-name",        1,      OptDataName             },
                { "--debug",            0,      OptDebug                },
        { "--debug-info",       0,      OptDebugInfo            },
+       { "--disable-opt",      1,      OptDisableOpt           },
+       { "--enable-opt",       1,      OptEnableOpt,           },
        { "--help",             0,      OptHelp                 },
        { "--include-dir",      1,      OptIncludeDir           },
        { "--rodata-name",      1,      OptRodataName           },
index 79f6658f5672c7e54a78a25e5fac8d2adb9647d7..fb765c296005af7560d6df76d0dbf0a5dda3f75d 100644 (file)
@@ -96,9 +96,9 @@ static const OPCDesc OPCTable[OPC_COUNT] = {
     { OPC_JSR, "jsr", 3, REG_NONE, REG_NONE, OF_NONE   },
     { OPC_JVC, "jvc", 5, REG_NONE, REG_NONE, OF_CBRA   },
     { OPC_JVS, "jvs", 5, REG_NONE, REG_NONE, OF_CBRA   },
-    { OPC_LDA, "lda", 0, REG_NONE, REG_A,    OF_NONE   },
-    { OPC_LDX, "ldx", 0, REG_NONE, REG_X,    OF_NONE   },
-    { OPC_LDY, "ldy", 0, REG_NONE, REG_Y,    OF_NONE   },
+    { OPC_LDA, "lda", 0, REG_NONE, REG_A,    OF_LOAD           },
+    { OPC_LDX, "ldx", 0, REG_NONE, REG_X,    OF_LOAD   },
+    { OPC_LDY, "ldy", 0, REG_NONE, REG_Y,    OF_LOAD   },
     { OPC_LSR, "lsr", 0, REG_A,    REG_A,    OF_NONE   },
     { OPC_NOP, "nop", 1, REG_NONE, REG_NONE, OF_NONE   },
     { OPC_ORA, "ora", 0, REG_A,    REG_A,    OF_NONE   },
index df590fd147341d5eec44f1b2a1f93113dd1f12f1..fcd4aa4444f5365026e678fe3e13924a81de11d6 100644 (file)
@@ -156,6 +156,7 @@ typedef enum {
 #define OF_UBRA        0x0001U                 /* Unconditional branch */
 #define OF_CBRA        0x0002U                 /* Conditional branch */
 #define OF_RET         0x0004U                 /* Return from function */
+#define OF_LOAD 0x0008U                        /* Register load */
 #define OF_BRA         (OF_UBRA|OF_CBRA)       /* Operation is a jump/branch */
 #define OF_DEAD        (OF_UBRA|OF_RET)        /* Dead end - no exec behind this point */