]> git.sur5r.net Git - cc65/blobdiff - src/cc65/coptstop.c
The check for illegal storage classes on globals was wrong
[cc65] / src / cc65 / coptstop.c
index cca49c871eb0db701d70351214f5385160362ff5..5a512cb206c202348dae6d746da32fce0e384dda 100644 (file)
 
 
 #include <stdlib.h>
+#include <ctype.h>
 
 /* cc65 */
 #include "codeent.h"
 #include "codeinfo.h"
-#include "codeopt.h"
-#include "error.h"
 #include "coptstop.h"
 
 
@@ -68,7 +67,7 @@ struct StackOpData {
 
 /* Flags returned by DirectOp */
 #define OP_DIRECT       0x01            /* Direct op may be used */
-#define OP_ONSTACK      0x02            /* Operand is on stack */
+#define OP_RELOAD_Y     0x02            /* Must reload index register Y */
 
 
 
@@ -94,14 +93,27 @@ static unsigned AdjustStackOffset (CodeSeg* S, unsigned Start, unsigned Stop,
 
        CodeEntry* E = CS_GetEntry (S, I);
 
-       if (E->Use & REG_SP) {
-
-           CodeEntry* P;
+        int NeedCorrection = 0;
+       if ((E->Use & REG_SP) != 0) {
 
            /* Check for some things that should not happen */
            CHECK (E->AM == AM65_ZP_INDY || E->RI->In.RegY >= (short) Offs);
            CHECK (strcmp (E->Arg, "sp") == 0);
 
+            /* We need to correct this one */
+            NeedCorrection = 1;
+
+        } else if (CE_IsCallTo (E, "ldaxysp")) {
+
+            /* We need to correct this one */
+            NeedCorrection = 1;
+
+        }
+
+        if (NeedCorrection) {
+
+           CodeEntry* P;
+
            /* Get the code entry before this one. If it's a LDY, adjust the
             * value.
             */
@@ -196,11 +208,13 @@ static void CheckDirectOp (StackOpData* D)
         if (E->AM == AM65_IMM || E->AM == AM65_ZP || E->AM == AM65_ABS) {
             /* These insns are all ok and replaceable */
             D->Flags |= OP_DIRECT;
-        } else if (E->AM == AM65_ZP_INDY &&
-                   E->RI->In.RegY >= 0   &&
-                          (E->Use & REG_SP) != 0) {
-            /* Load from stack with known offset is also ok */
-            D->Flags |= (OP_DIRECT | OP_ONSTACK);
+        } else if (E->AM == AM65_ZP_INDY && RegValIsKnown (E->RI->In.RegY) &&
+                   strcmp (E->Arg, D->ZPLo) != 0 && strcmp (E->Arg, D->ZPHi) != 0) {
+            /* Load indirect with known offset is also ok, provided that
+             * the zeropage location used is not the same as the one we're
+             * using for the temp storage.
+             */
+            D->Flags |= (OP_DIRECT | OP_RELOAD_Y);
         }
     }
 }
@@ -211,7 +225,7 @@ static void ReplacePushByStore (StackOpData* D)
 /* Replace the call to the push subroutine by a store into the zero page
  * location (actually, the push is not replaced, because we need it for
  * later, but the name is still ok since the push will get removed at the
- * end of each routine.
+ * end of each routine).
  */
 {
     CodeEntry* X;
@@ -229,7 +243,7 @@ static void ReplacePushByStore (StackOpData* D)
 
 static void AddOpLow (StackOpData* D, opc_t OPC)
 /* Add an op for the low byte of an operator. This function honours the
- * OP_DIRECT and OP_ONSTACK flags and generates the necessary instructions.
+ * OP_DIRECT and OP_RELOAD_Y flags and generates the necessary instructions.
  * All code is inserted at the current insertion point.
  */
 {
@@ -239,7 +253,7 @@ static void AddOpLow (StackOpData* D, opc_t OPC)
                /* Op with a variable location. If the location is on the stack, we
          * need to reload the Y register.
          */
-        if ((D->Flags & OP_ONSTACK) != 0) {
+        if ((D->Flags & OP_RELOAD_Y) != 0) {
             const char* Arg = MakeHexArg (D->PrevEntry->RI->In.RegY);
             X = NewCodeEntry (OP65_LDY, AM65_IMM, Arg, 0, D->OpEntry->LI);
             InsertEntry (D, X, D->IP++);
@@ -286,6 +300,33 @@ static void RemovePushAndOp (StackOpData* D)
 
 
 
+static const char* IsRegVar (const StackOpData* D)
+/* If the value pushed is that of a register variable, return the name of the
+ * entry in the register bank. Otherwise return NULL.
+ */
+{
+    CodeEntry* P;
+
+    if (D->PushIndex >= 2                                &&
+        (P = D->PrevEntry) != 0                          &&
+        P->OPC == OP65_LDX                               &&
+        P->AM == AM65_ZP                                 &&
+        strncmp (P->Arg, "regbank+", 7) == 0             &&
+        isdigit (P->Arg[8])                              &&
+        (P = CS_GetEntry (D->Code, D->PushIndex-2)) != 0 &&
+        P->OPC == OP65_LDA                               &&
+        P->AM == AM65_ZP                                 &&
+        strncmp (P->Arg, "regbank+", 7) == 0             &&
+        isdigit (P->Arg[8])) {
+        /* Ok, it loads the register variable */
+        return P->Arg;
+    } else {
+        return 0;
+    }
+}
+
+
+
 /*****************************************************************************/
 /*                      Actual optimization functions                       */
 /*****************************************************************************/
@@ -296,12 +337,20 @@ static unsigned Opt_staspidx (StackOpData* D)
 /* Optimize the staspidx sequence if possible */
 {
     CodeEntry* X;
+    const char* ZPLo;
 
-    /* Store the value into the zeropage instead of pushing it */
-    ReplacePushByStore (D);
+    /* Check if we're using a register variable */
+    if ((ZPLo = IsRegVar (D)) == 0) {
+
+        /* Store the value into the zeropage instead of pushing it */
+        ReplacePushByStore (D);
+
+        /* Use the given zero page loc */
+        ZPLo = D->ZPLo;
+    }
 
     /* Replace the store subroutine call by a direct op */
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
     InsertEntry (D, X, D->OpIndex+1);
 
     /* Remove the push and the call to the staspidx function */
@@ -317,16 +366,24 @@ static unsigned Opt_staxspidx (StackOpData* D)
 /* Optimize the staxspidx sequence if possible */
 {
     CodeEntry* X;
+    const char* ZPLo;
 
-    /* Store the value into the zeropage instead of pushing it */
-    ReplacePushByStore (D);
+    /* Check if we're using a register variable */
+    if ((ZPLo = IsRegVar (D)) == 0) {
+
+        /* Store the value into the zeropage instead of pushing it */
+        ReplacePushByStore (D);
+
+        /* Use the given zero page loc */
+        ZPLo = D->ZPLo;
+    }
 
     /* Inline the store */
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
     InsertEntry (D, X, D->OpIndex+1);
     X = NewCodeEntry (OP65_INY, AM65_IMP, 0, 0, D->OpEntry->LI);
     InsertEntry (D, X, D->OpIndex+2);
-    if (D->OpEntry->RI->In.RegX >= 0) {
+    if (RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Value of X is known */
        const char* Arg = MakeHexArg (D->OpEntry->RI->In.RegX);
                X = NewCodeEntry (OP65_LDA, AM65_IMM, Arg, 0, D->OpEntry->LI);
@@ -335,7 +392,7 @@ static unsigned Opt_staxspidx (StackOpData* D)
        X = NewCodeEntry (OP65_TXA, AM65_IMP, 0, 0, D->OpEntry->LI);
     }
     InsertEntry (D, X, D->OpIndex+3);
-    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, D->ZPLo, 0, D->OpEntry->LI);
+    X = NewCodeEntry (OP65_STA, AM65_ZP_INDY, ZPLo, 0, D->OpEntry->LI);
     InsertEntry (D, X, D->OpIndex+4);
 
     /* Remove the push and the call to the staspidx function */
@@ -385,7 +442,7 @@ static unsigned Opt_tosaddax (StackOpData* D)
     } else if (D->OpEntry->RI->In.RegX == 0) {
                /* The high byte is that of the first operand plus carry */
        CodeLabel* L;
-       if (D->PushEntry->RI->In.RegX >= 0) {
+       if (RegValIsKnown (D->PushEntry->RI->In.RegX)) {
            /* Value of first op high byte is known */
            const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX);
            X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
@@ -471,7 +528,8 @@ static unsigned Opt_tosorax (StackOpData* D)
     AddOpLow (D, OP65_ORA);
 
     /* High byte */
-    if (D->PushEntry->RI->In.RegX >= 0 && D->OpEntry->RI->In.RegX >= 0) {
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
        const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX | D->OpEntry->RI->In.RegX);
                X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
@@ -510,7 +568,8 @@ static unsigned Opt_tosxorax (StackOpData* D)
     AddOpLow (D, OP65_EOR);
 
     /* High byte */
-    if (D->PushEntry->RI->In.RegX >= 0 && D->OpEntry->RI->In.RegX >= 0) {
+    if (RegValIsKnown (D->PushEntry->RI->In.RegX) &&
+        RegValIsKnown (D->OpEntry->RI->In.RegX)) {
        /* Both values known, precalculate the result */
        const char* Arg = MakeHexArg (D->PushEntry->RI->In.RegX ^ D->OpEntry->RI->In.RegX);
                X = NewCodeEntry (OP65_LDX, AM65_IMM, Arg, 0, D->OpEntry->LI);
@@ -594,7 +653,9 @@ static int HarmlessCall (const char* Name)
  */
 {
     static const char* Tab[] = {
+        "ldaxidx",
         "ldaxysp",
+        "negax",
     };
 
     void* R = bsearch (Name,
@@ -632,8 +693,7 @@ unsigned OptStackOps (CodeSeg* S)
      * 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).
+     *  - the range must be a basic block (one entry, one exit)
      *  - there may not be accesses to local variables with unknown
      *    offsets (because we have to adjust these offsets).
      *  - no subroutine calls
@@ -651,10 +711,16 @@ unsigned OptStackOps (CodeSeg* S)
        /* Handling depends if we're inside a sequence or not */
        if (InSeq) {
 
-           if ((E->Info & OF_BRA) != 0                              ||
-               ((E->Use & REG_SP) != 0                         &&
-                (E->AM != AM65_ZP_INDY || E->RI->In.RegY < 0))      ||
-               CE_HasLabel (E)) {
+            /* If we are using the stack, and we don't have "indirect Y"
+             * addressing mode, or the value of Y is unknown, or less than
+             * two, we cannot cope with this piece of code. Having an unknown
+             * value of Y means that we cannot correct the stack offset, while
+             * having an offset less than two means that the code works with
+             * the value on stack which is to be removed.
+             */
+                   if ((E->Use & REG_SP) != 0 &&
+               (E->AM != AM65_ZP_INDY || RegValIsUnknown (E->RI->In.RegY) ||
+                 E->RI->In.RegY < 2)) {
 
                /* All this stuff is not allowed in a sequence */
                InSeq = 0;
@@ -688,7 +754,7 @@ unsigned OptStackOps (CodeSeg* S)
                            Data.ZPLo = "ptr1";
                            Data.ZPHi = "ptr1+1";
                        } else if ((UsedRegs & REG_PTR2) == REG_NONE) {
-                           Data.ZPLo = "ptr2";
+                           Data.ZPLo = "ptr2";
                            Data.ZPHi = "ptr2+1";
                        } else {
                            /* No registers available */
@@ -696,6 +762,11 @@ unsigned OptStackOps (CodeSeg* S)
                        }
                    }
 
+                    /* Determine if we have a basic block */
+                    if (PreCondOk) {
+                        PreCondOk = CS_IsBasicBlock (S, Push, I);
+                    }
+
                    /* If preconditions are ok, call the optimizer function */
                    if (PreCondOk) {
 
@@ -725,19 +796,20 @@ unsigned OptStackOps (CodeSeg* S)
                    /* Restart the sequence */
                    Push     = I;
                    UsedRegs = REG_NONE;
-               } else if (!HarmlessCall (E->Arg)) {
+               } else if (HarmlessCall (E->Arg)) {
+                    /* Track zeropage register usage */
+                    UsedRegs |= (E->Use | E->Chg);
+                } else {
                    /* A call to an unkown subroutine ends the sequence */
                    InSeq = 0;
                }
 
            } else {
-
                /* Other stuff: Track zeropage register usage */
                UsedRegs |= (E->Use | E->Chg);
-
            }
 
-       } else if (CE_IsCall (E, "pushax")) {
+       } else if (CE_IsCallTo (E, "pushax")) {
 
            /* This starts a sequence */
            Push     = I;