]> git.sur5r.net Git - cc65/blobdiff - src/cc65/codeopt.c
added optimization for indexed 16-bit array load of form (array[i & 0x7f])
[cc65] / src / cc65 / codeopt.c
index 566fdf954dfbfa92ffe02c2d313dfdf62a24eacf..d920f001a6134488cad838e84aa3302a56e45db3 100644 (file)
@@ -69,7 +69,7 @@
 #include "error.h"
 #include "global.h"
 #include "output.h"
-
+#include "symtab.h"
 
 
 /*****************************************************************************/
@@ -80,8 +80,8 @@
 
 static unsigned OptLoad1 (CodeSeg* S)
 /* Search for a call to ldaxysp where X is not used later and replace it by
- * a load of just the A register.
- */
+** a load of just the A register.
+*/
 {
     unsigned I;
     unsigned Changes = 0;
@@ -161,8 +161,8 @@ static unsigned OptLoad2 (CodeSeg* S)
                 !RegXUsed (S, I+3)) {
 
                 /* A/X are stored into memory somewhere and X is not used
-                 * later
-                 */
+                ** later
+                */
 
                 /* lda (sp),y */
                 X = NewCodeEntry (OP65_LDA, AM65_ZP_INDY, "sp", 0, L[0]->LI);
@@ -251,8 +251,8 @@ static unsigned OptLoad3 (CodeSeg* S)
             CodeEntry* N;
 
             /* If we had a preceeding load that is identical, remove this one.
-             * If it is not identical, or we didn't have one, remember it.
-             */
+            ** If it is not identical, or we didn't have one, remember it.
+            */
             if (Load != 0                               &&
                 E->OPC == Load->OPC                     &&
                 E->AM == Load->AM                       &&
@@ -299,21 +299,21 @@ static unsigned OptLoad3 (CodeSeg* S)
 
 static unsigned OptDecouple (CodeSeg* S)
 /* Decouple operations, that is, do the following replacements:
- *
- *   dex        -> ldx #imm
- *   inx        -> ldx #imm
- *   dey        -> ldy #imm
- *   iny        -> ldy #imm
- *   tax        -> ldx #imm
- *   txa        -> lda #imm
- *   tay        -> ldy #imm
- *   tya        -> lda #imm
- *   lda zp     -> lda #imm
- *   ldx zp     -> ldx #imm
- *   ldy zp     -> ldy #imm
- *
- * Provided that the register values are known of course.
- */
+**
+**   dex        -> ldx #imm
+**   inx        -> ldx #imm
+**   dey        -> ldy #imm
+**   iny        -> ldy #imm
+**   tax        -> ldx #imm
+**   txa        -> lda #imm
+**   tay        -> ldy #imm
+**   tya        -> lda #imm
+**   lda zp     -> lda #imm
+**   ldx zp     -> ldx #imm
+**   ldy zp     -> ldy #imm
+**
+** Provided that the register values are known of course.
+*/
 {
     unsigned Changes = 0;
     unsigned I;
@@ -529,9 +529,9 @@ static unsigned OptDecouple (CodeSeg* S)
 
 static unsigned IsDecSP (const CodeEntry* E)
 /* Check if this is an insn that decrements the stack pointer. If so, return
- * the decrement. If not, return zero.
- * The function expects E to be a subroutine call.
- */
+** the decrement. If not, return zero.
+** The function expects E to be a subroutine call.
+*/
 {
     if (strncmp (E->Arg, "decsp", 5) == 0) {
         if (E->Arg[5] >= '1' && E->Arg[5] <= '8') {
@@ -549,8 +549,8 @@ static unsigned IsDecSP (const CodeEntry* E)
 
 static unsigned OptStackPtrOps (CodeSeg* S)
 /* Merge adjacent calls to decsp into one. NOTE: This function won't merge all
- * known cases!
- */
+** known cases!
+*/
 {
     unsigned Changes = 0;
     unsigned I;
@@ -613,7 +613,116 @@ static unsigned OptStackPtrOps (CodeSeg* S)
     return Changes;
 }
 
+static unsigned OptGotoSPAdj (CodeSeg* S)
+/* Optimize SP adjustment for forward 'goto' */
+{
+    unsigned Changes = 0;
+    unsigned I;
+
+    /* Walk over the entries */
+    I = 0;
+    while (I < CS_GetEntryCount (S)) {
+
+        CodeEntry* L[10], *X;
+        unsigned short adjustment;
+        const char* Arg;
 
+        /* Get next entry */
+        L[0] = CS_GetEntry (S, I);
+
+        /* Check for the sequence generated by g_lateadjustSP */
+        if (L[0]->OPC == OP65_PHA            &&
+            CS_GetEntries (S, L+1, I+1, 9)   &&
+            L[1]->OPC == OP65_LDA            &&
+            L[1]->AM == AM65_ABS             &&
+            L[2]->OPC == OP65_CLC            &&
+            L[3]->OPC == OP65_ADC            &&
+            strcmp (L[3]->Arg, "sp") == 0    &&
+            L[6]->OPC == OP65_ADC            &&
+            strcmp (L[6]->Arg, "sp+1") == 0  &&
+            L[9]->OPC == OP65_JMP) {
+            adjustment = FindSPAdjustment (L[1]->Arg);
+
+            if (adjustment == 0) {
+                /* No SP adjustment needed, remove the whole sequence */
+                CS_DelEntries (S, I, 9);
+            }
+            else if (adjustment >= 65536 - 8) {
+                /* If adjustment is in range [-8, 0) we use decsp* calls */
+                char Buf[20];
+                adjustment = 65536 - adjustment;
+                xsprintf (Buf, sizeof (Buf), "decsp%u", adjustment);
+                X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 9);
+
+                /* Delete the old code */
+                CS_DelEntries (S, I, 9);
+            }
+            else if (adjustment >= 65536 - 255) {
+                /* For range [-255, -8) we have ldy #, jsr subysp */
+                adjustment = 65536 - adjustment;
+                Arg = MakeHexArg (adjustment);
+                X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 9);
+                X = NewCodeEntry (OP65_JSR, AM65_ABS, "subysp", 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 10);
+
+                /* Delete the old code */
+                CS_DelEntries (S, I, 9);
+            }
+            else if (adjustment > 255) {
+                /* For ranges [-32768, 255) and (255, 32767) the only modification
+                ** is to replace the absolute with immediate addressing
+                */
+                Arg = MakeHexArg (adjustment & 0xff);
+                X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 1);
+                Arg = MakeHexArg (adjustment >> 8);
+                X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, L[5]->LI);
+                CS_InsertEntry (S, X, I + 6);
+
+                /* Delete the old code */
+                CS_DelEntry (S, I + 2);
+                CS_DelEntry (S, I + 6);
+            }
+            else if (adjustment > 8) {
+                /* For range (8, 255] we have ldy #, jsr addysp */
+                Arg = MakeHexArg (adjustment & 0xff);
+                X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 9);
+                X = NewCodeEntry (OP65_JSR, AM65_ABS, "addysp", 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 10);
+
+                /* Delete the old code */
+                CS_DelEntries (S, I, 9);
+            }
+            else {
+                /* If adjustment is in range (0, 8] we use incsp* calls */
+                char Buf[20];
+                xsprintf (Buf, sizeof (Buf), "incsp%u", adjustment);
+                X = NewCodeEntry (OP65_JSR, AM65_ABS, Buf, 0, L[1]->LI);
+                CS_InsertEntry (S, X, I + 9);
+
+                /* Delete the old code */
+                CS_DelEntries (S, I, 9);
+            }
+            /* Regenerate register info */
+            CS_GenRegInfo (S);
+
+            /* Remember we had changes */
+            Changes++;
+
+        } else {
+
+            /* Next entry */
+            ++I;
+        }
+
+    }
+
+    /* Return the number of changes made */
+    return Changes;
+}
 
 /*****************************************************************************/
 /*                              struct OptFunc                               */
@@ -675,6 +784,7 @@ static OptFunc DOptDeadCode     = { OptDeadCode,     "OptDeadCode",     100, 0,
 static OptFunc DOptDeadJumps    = { OptDeadJumps,    "OptDeadJumps",    100, 0, 0, 0, 0, 0 };
 static OptFunc DOptDecouple     = { OptDecouple,     "OptDecouple",     100, 0, 0, 0, 0, 0 };
 static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",       0, 0, 0, 0, 0, 0 };
+static OptFunc DOptGotoSPAdj    = { OptGotoSPAdj,    "OptGotoSPAdj",      0, 0, 0, 0, 0, 0 };
 static OptFunc DOptIndLoads1    = { OptIndLoads1,    "OptIndLoads1",      0, 0, 0, 0, 0, 0 };
 static OptFunc DOptIndLoads2    = { OptIndLoads2,    "OptIndLoads2",      0, 0, 0, 0, 0, 0 };
 static OptFunc DOptJumpCascades = { OptJumpCascades, "OptJumpCascades", 100, 0, 0, 0, 0, 0 };
@@ -704,6 +814,8 @@ static OptFunc DOptPtrLoad14    = { OptPtrLoad14,    "OptPtrLoad14",    108, 0,
 static OptFunc DOptPtrLoad15    = { OptPtrLoad15,    "OptPtrLoad15",     86, 0, 0, 0, 0, 0 };
 static OptFunc DOptPtrLoad16    = { OptPtrLoad16,    "OptPtrLoad16",    100, 0, 0, 0, 0, 0 };
 static OptFunc DOptPtrLoad17    = { OptPtrLoad17,    "OptPtrLoad17",    190, 0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad18    = { OptPtrLoad18,    "OptPtrLoad18",    100, 0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad19    = { OptPtrLoad19,    "OptPtrLoad19",     65, 0, 0, 0, 0, 0 };
 static OptFunc DOptPtrStore1    = { OptPtrStore1,    "OptPtrStore1",     65, 0, 0, 0, 0, 0 };
 static OptFunc DOptPtrStore2    = { OptPtrStore2,    "OptPtrStore2",     65, 0, 0, 0, 0, 0 };
 static OptFunc DOptPtrStore3    = { OptPtrStore3,    "OptPtrStore3",    100, 0, 0, 0, 0, 0 };
@@ -774,6 +886,7 @@ static OptFunc* OptFuncs[] = {
     &DOptDeadJumps,
     &DOptDecouple,
     &DOptDupLoads,
+    &DOptGotoSPAdj,
     &DOptIndLoads1,
     &DOptIndLoads2,
     &DOptJumpCascades,
@@ -794,6 +907,8 @@ static OptFunc* OptFuncs[] = {
     &DOptPtrLoad15,
     &DOptPtrLoad16,
     &DOptPtrLoad17,
+    &DOptPtrLoad18,
+    &DOptPtrLoad19,
     &DOptPtrLoad2,
     &DOptPtrLoad3,
     &DOptPtrLoad4,
@@ -851,8 +966,8 @@ static int CmpOptStep (const void* Key, const void* Func)
 
 static OptFunc* FindOptFunc (const char* Name)
 /* Find an optimizer step by name in the table and return a pointer. Return
- * NULL if no such step is found.
- */
+** NULL if no such step is found.
+*/
 {
     /* Search for the function in the list */
     OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
@@ -863,14 +978,14 @@ static OptFunc* FindOptFunc (const char* Name)
 
 static OptFunc* GetOptFunc (const char* Name)
 /* Find an optimizer step by name in the table and return a pointer. Print an
- * error and call AbEnd if not found.
- */
+** error and call AbEnd if not found.
+*/
 {
     /* Search for the function in the list */
     OptFunc* F = FindOptFunc (Name);
     if (F == 0) {
         /* Not found */
-        AbEnd ("Optimization step `%s' not found", Name);
+        AbEnd ("Optimization step '%s' not found", Name);
     }
     return F;
 }
@@ -911,6 +1026,8 @@ void ListOptSteps (FILE* F)
 /* List all optimization steps */
 {
     unsigned I;
+    
+    fprintf (F, "any\n");
     for (I = 0; I < OPTFUNC_COUNT; ++I) {
         fprintf (F, "%s\n", OptFuncs[I]->Name);
     }
@@ -1055,10 +1172,10 @@ static void WriteDebugOutput (CodeSeg* S, const char* Step)
         /* Output a header line */
         if (Step == 0) {
             /* Initial output */
-            WriteOutput ("Initial code for function `%s':\n",
+            WriteOutput ("Initial code for function '%s':\n",
                          S->Func? S->Func->Name : "<global>");
         } else {
-            WriteOutput ("Code after applying `%s':\n", Step);
+            WriteOutput ("Code after applying '%s':\n", Step);
         }
 
         /* Output the code segment */
@@ -1074,8 +1191,8 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
     unsigned Changes, C;
 
     /* Don't run the function if it is disabled or if it is prohibited by the
-     * code size factor
-     */
+    ** code size factor
+    */
     if (F->Disabled || F->CodeSizeFactor > S->CodeSizeFactor) {
         return 0;
     }
@@ -1113,13 +1230,14 @@ static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
 
 static unsigned RunOptGroup1 (CodeSeg* S)
 /* Run the first group of optimization steps. These steps translate known
- * patterns emitted by the code generator into more optimal patterns. Order
- * of the steps is important, because some of the steps done earlier cover
- * the same patterns as later steps as subpatterns.
- */
+** patterns emitted by the code generator into more optimal patterns. Order
+** of the steps is important, because some of the steps done earlier cover
+** the same patterns as later steps as subpatterns.
+*/
 {
     unsigned Changes = 0;
 
+    Changes += RunOptFunc (S, &DOptGotoSPAdj, 1);
     Changes += RunOptFunc (S, &DOptStackPtrOps, 5);
     Changes += RunOptFunc (S, &DOptPtrStore1, 1);
     Changes += RunOptFunc (S, &DOptPtrStore2, 1);
@@ -1132,6 +1250,7 @@ static unsigned RunOptGroup1 (CodeSeg* S)
     Changes += RunOptFunc (S, &DOptPtrLoad5, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad7, 1);
+    Changes += RunOptFunc (S, &DOptPtrLoad18, 1); /* Before OptPtrLoad11 */
     Changes += RunOptFunc (S, &DOptPtrLoad11, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad12, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad13, 1);
@@ -1139,6 +1258,7 @@ static unsigned RunOptGroup1 (CodeSeg* S)
     Changes += RunOptFunc (S, &DOptPtrLoad15, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad16, 1);
     Changes += RunOptFunc (S, &DOptPtrLoad17, 1);
+    Changes += RunOptFunc (S, &DOptPtrLoad19, 1);
     Changes += RunOptFunc (S, &DOptBNegAX1, 1);
     Changes += RunOptFunc (S, &DOptBNegAX2, 1);
     Changes += RunOptFunc (S, &DOptBNegAX3, 1);
@@ -1168,10 +1288,10 @@ static unsigned RunOptGroup1 (CodeSeg* S)
 
 static unsigned RunOptGroup2 (CodeSeg* S)
 /* Run one group of optimization steps. This step involves just decoupling
- * instructions by replacing them by instructions that do not depend on
- * previous instructions. This makes it easier to find instructions that
- * aren't used.
- */
+** instructions by replacing them by instructions that do not depend on
+** previous instructions. This makes it easier to find instructions that
+** aren't used.
+*/
 {
     unsigned Changes = 0;
 
@@ -1185,9 +1305,9 @@ static unsigned RunOptGroup2 (CodeSeg* S)
 
 static unsigned RunOptGroup3 (CodeSeg* S)
 /* Run one group of optimization steps. These steps depend on each other,
- * that means that one step may allow another step to do additional work,
- * so we will repeat the steps as long as we see any changes.
- */
+** that means that one step may allow another step to do additional work,
+** so we will repeat the steps as long as we see any changes.
+*/
 {
     unsigned Changes, C;
 
@@ -1254,8 +1374,8 @@ static unsigned RunOptGroup3 (CodeSeg* S)
 
 static unsigned RunOptGroup4 (CodeSeg* S)
 /* Run another round of pattern replacements. These are done late, since there
- * may be better replacements before.
- */
+** may be better replacements before.
+*/
 {
     unsigned Changes = 0;
 
@@ -1287,9 +1407,9 @@ static unsigned RunOptGroup5 (CodeSeg* S)
         Changes += RunOptFunc (S, &DOpt65C02Stores, 1);
         if (Changes) {
             /* The 65C02 replacement codes do often make the use of a register
-             * value unnecessary, so if we have changes, run another load
-             * removal pass.
-             */
+            ** value unnecessary, so if we have changes, run another load
+            ** removal pass.
+            */
             Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
         }
     }
@@ -1302,18 +1422,18 @@ static unsigned RunOptGroup5 (CodeSeg* S)
 
 static unsigned RunOptGroup6 (CodeSeg* S)
 /* This one is quite special. It tries to replace "lda (sp),y" by "lda (sp,x)".
- * The latter is ony cycle slower, but if we're able to remove the necessary
- * load of the Y register, because X is zero anyway, we gain 1 cycle and
- * shorten the code by one (transfer) or two bytes (load). So what we do is
- * to replace the insns, remove unused loads, and then change back all insns
- * where Y is still zero (meaning that the load has not been removed).
- */
+** The latter is ony cycle slower, but if we're able to remove the necessary
+** load of the Y register, because X is zero anyway, we gain 1 cycle and
+** shorten the code by one (transfer) or two bytes (load). So what we do is
+** to replace the insns, remove unused loads, and then change back all insns
+** where Y is still zero (meaning that the load has not been removed).
+*/
 {
     unsigned Changes = 0;
 
     /* This group will only run for a standard 6502, because the 65C02 has a
-     * better addressing mode that covers this case.
-     */
+    ** better addressing mode that covers this case.
+    */
     if ((CPUIsets[CPU] & CPU_ISET_65SC02) == 0) {
         Changes += RunOptFunc (S, &DOptIndLoads1, 1);
         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
@@ -1328,21 +1448,21 @@ static unsigned RunOptGroup6 (CodeSeg* S)
 
 static unsigned RunOptGroup7 (CodeSeg* S)
 /* The last group of optimization steps. Adjust branches, do size optimizations.
- */
+*/
 {
     unsigned Changes = 0;
     unsigned C;
 
     /* Optimize for size, that is replace operations by shorter ones, even
-     * if this does hinder further optimizations (no problem since we're
-     * done soon).
-     */
+    ** if this does hinder further optimizations (no problem since we're
+    ** done soon).
+    */
     C = RunOptFunc (S, &DOptSize1, 1);
     if (C) {
         Changes += C;
         /* Run some optimization passes again, since the size optimizations
-         * may have opened new oportunities.
-         */
+        ** may have opened new oportunities.
+        */
         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
         Changes += RunOptFunc (S, &DOptUnusedStores, 1);
         Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
@@ -1353,8 +1473,8 @@ static unsigned RunOptGroup7 (CodeSeg* S)
     if (C) {
         Changes += C;
         /* Run some optimization passes again, since the size optimizations
-         * may have opened new oportunities.
-         */
+        ** may have opened new oportunities.
+        */
         Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
         Changes += RunOptFunc (S, &DOptJumpTarget1, 5);
         Changes += RunOptFunc (S, &DOptStore5, 1);
@@ -1366,8 +1486,8 @@ static unsigned RunOptGroup7 (CodeSeg* S)
     Changes += RunOptFunc (S, &DOptBranchDist, 3);
 
     /* Replace conditional branches to RTS. If we had changes, we must run dead
-     * code elimination again, since the change may have introduced dead code.
-     */
+    ** code elimination again, since the change may have introduced dead code.
+    */
     C = RunOptFunc (S, &DOptRTSJumps2, 1);
     Changes += C;
     if (C) {
@@ -1398,7 +1518,7 @@ void RunOpt (CodeSeg* S)
 
     /* 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);
+        Print (stdout, 1, "Running optimizer for function '%s'\n", S->Func->Name);
     } else {
         Print (stdout, 1, "Running optimizer for global code segment\n");
     }