]> git.sur5r.net Git - cc65/commitdiff
Improved the code generated for bit-fields.
authoruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 31 Aug 2009 14:59:49 +0000 (14:59 +0000)
committeruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Mon, 31 Aug 2009 14:59:49 +0000 (14:59 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@4096 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/cc65/asmcode.c
src/cc65/asmcode.h
src/cc65/assignment.c
src/cc65/codeseg.c
src/cc65/codeseg.h

index a09b00dfaa5047e61fc1751a7883759095b52ca5..e5fff9af082179853e4fa5041da916bffa4493ac 100644 (file)
@@ -62,6 +62,25 @@ void GetCodePos (CodeMark* M)
 
 
 
+void RemoveCodeRange (const CodeMark* Start, const CodeMark* End)
+/* Remove all code between two code markers */
+{
+    /* Nothing to do if the range is empty */
+    if (Start->Pos == End->Pos) {
+        return;
+    }
+
+    /* We can only delete the range if End is the end of the code segment or
+     * if both SP values are identical.
+     */
+    CHECK (Start->SP == End->SP || End->Pos == CS_GetEntryCount (CS->Code));
+
+    /* Delete the range */
+    CS_DelCodeRange (CS->Code, Start->Pos, End->Pos-1);
+}
+
+
+
 void RemoveCode (const CodeMark* M)
 /* Remove all code after the given code marker */
 {
index b43cfce8d095851aaeaa784a9207c5c4eac957e9..b91b892d0afa0e01615adf614f669797e6bbab19 100644 (file)
@@ -68,6 +68,9 @@ typedef struct {
 void GetCodePos (CodeMark* M);
 /* Get a marker pointing to the current output position */
 
+void RemoveCodeRange (const CodeMark* Start, const CodeMark* End);
+/* Remove all code between two code markers */
+
 void RemoveCode (const CodeMark* M);
 /* Remove all code after the given code marker */
 
index af414ff50e76e2902c7b4c40e567a462125271c9..eed097272b4602deaa747b30ef15da9bbc7893bd 100644 (file)
@@ -34,6 +34,7 @@
 
 
 /* cc65 */
+#include "asmcode.h"
 #include "assignment.h"
 #include "codegen.h"
 #include "datatype.h"
@@ -160,6 +161,9 @@ void Assignment (ExprDesc* Expr)
 
     } else if (ED_IsBitField (Expr)) {
 
+        CodeMark AndPos;
+        CodeMark PushPos;
+
         unsigned Mask;
         unsigned Flags;
 
@@ -182,30 +186,54 @@ void Assignment (ExprDesc* Expr)
 
         /* Mask unwanted bits */
         Mask = (0x0001U << Expr->BitWidth) - 1U;
+        GetCodePos (&AndPos);
         g_and (Flags | CF_CONST, ~(Mask << Expr->BitOffs));
 
         /* Push it on stack */
+        GetCodePos (&PushPos);
         g_push (Flags, 0);
 
        /* Read the expression on the right side of the '=' */
        hie1 (&Expr2);
 
-       /* Do type conversion if necessary. Beware: Do not use char type 
+       /* Do type conversion if necessary. Beware: Do not use char type
          * here!
          */
        TypeConversion (&Expr2, ltype);
 
-       /* If necessary, load the value into the primary register */
-       LoadExpr (CF_NONE, &Expr2);
+        /* Special treatment if the value is constant */
+        if (ED_IsConstAbsInt (&Expr2)) {
+
+            /* Get the value and apply the mask */
+            unsigned Val = (unsigned) (Expr2.IVal & Mask);
 
-        /* Apply the mask */
-        g_and (Flags | CF_CONST, Mask);
+            /* Since we will do the OR with a constant, we can remove the push */
+            RemoveCode (&PushPos);
+
+            /* If the value is equal to the mask now, all bits are one, and we
+             * can remove the mask operation from above.
+             */
+            if (Val == Mask) {
+                RemoveCode (&AndPos);
+            }
 
-        /* Shift it into the right position */
-        g_asl (Flags | CF_CONST, Expr->BitOffs);
+            /* Generate the or operation */
+            g_or (Flags | CF_CONST, Val << Expr->BitOffs);
 
-        /* Or both values */
-        g_or (Flags, 0);
+        } else {
+
+            /* If necessary, load the value into the primary register */
+            LoadExpr (CF_NONE, &Expr2);
+
+            /* Apply the mask */
+            g_and (Flags | CF_CONST, Mask);
+
+            /* Shift it into the right position */
+            g_asl (Flags | CF_CONST, Expr->BitOffs);
+
+            /* Or both values */
+            g_or (Flags, 0);
+        }
 
        /* Generate a store instruction */
        Store (Expr, 0);
index f47e4dd5095d1ced0e32f495438edcd8858874ec..36fdc05b6e853d537434ccd00b0434f9dd4703e2 100644 (file)
@@ -1050,6 +1050,86 @@ void CS_MoveLabelRef (CodeSeg* S, struct CodeEntry* E, CodeLabel* L)
 
 
 
+void CS_DelCodeRange (CodeSeg* S, unsigned First, unsigned Last)
+/* Delete all entries between first and last, both inclusive. The function
+ * can only handle basic blocks (First is the only entry, Last the only exit)
+ * and no open labels. It will call FAIL if any of these preconditions are
+ * violated.
+ */
+{
+    unsigned   I;
+    CodeEntry* FirstEntry;
+
+    /* Do some sanity checks */
+    CHECK (First <= Last && Last < CS_GetEntryCount (S));
+
+    /* If Last is actually the last insn, call CS_DelCodeAfter instead, which
+     * is more flexible in this case.
+     */
+    if (Last == CS_GetEntryCount (S) - 1) {
+        CS_DelCodeAfter (S, First);
+        return;
+    }
+
+    /* Get the first entry and check if it has any labels. If it has, move
+     * them to the insn following Last. If Last is the last insn of the code
+     * segment, make them ownerless and move them to the label pool.
+     */
+    FirstEntry = CS_GetEntry (S, First);
+    if (CE_HasLabel (FirstEntry)) {
+        /* Get the entry following last */
+        CodeEntry* FollowingEntry = CS_GetNextEntry (S, Last);
+        if (FollowingEntry) {       
+            /* There is an entry after Last - move the labels */
+            CS_MoveLabels (S, FirstEntry, FollowingEntry);
+        } else {
+           /* Move the labels to the pool and clear the owner pointer */
+           CS_MoveLabelsToPool (S, FirstEntry);
+        }
+    }
+
+    /* First pass: Delete all references to labels. If the reference count
+     * for a label drops to zero, delete it.
+     */
+    for (I = Last; I >= First; --I) {
+
+               /* Get the next entry */
+               CodeEntry* E = CS_GetEntry (S, I);
+
+               /* Check if this entry has a label reference */
+               if (E->JumpTo) {
+
+                   /* If the label is a label in the label pool, this is an error */
+                   CodeLabel* L = E->JumpTo;
+                   CHECK (CollIndex (&S->Labels, L) < 0);
+
+           /* Remove the reference to the label */
+           CS_RemoveLabelRef (S, E);
+       }
+    }
+
+    /* Second pass: Delete the instructions. If a label attached to an
+     * instruction still has references, it must be references from outside
+     * the deleted area, which is an error.
+     */
+    for (I = Last; I >= First; --I) {
+
+       /* Get the next entry */
+       CodeEntry* E = CS_GetEntry (S, I);
+
+               /* Check if this entry has a label attached */
+       CHECK (!CE_HasLabel (E));
+
+       /* Delete the pointer to the entry */
+       CollDelete (&S->Entries, I);
+
+       /* Delete the entry itself */
+       FreeCodeEntry (E);
+    }
+}
+
+
+
 void CS_DelCodeAfter (CodeSeg* S, unsigned Last)
 /* Delete all entries including the given one */
 {
index 453dd7a52e3497951e08b62b8f8e3a1b2cfd56af..e7f0ebc9f3a2f0054617bbbc2a80190ddff85f74 100644 (file)
@@ -240,6 +240,13 @@ void CS_MoveLabelRef (CodeSeg* S, struct CodeEntry* E, CodeLabel* L);
  * deleted.
  */
 
+void CS_DelCodeRange (CodeSeg* S, unsigned First, unsigned Last);
+/* Delete all entries between first and last, both inclusive. The function
+ * can only handle basic blocks (First is the only entry, Last the only exit)
+ * and no open labels. It will call FAIL if any of these preconditions are
+ * violated.
+ */
+
 void CS_DelCodeAfter (CodeSeg* S, unsigned Last);
 /* Delete all entries including the given one */