From dc678e8dcb4ff577c008434f796689aa0e57cda8 Mon Sep 17 00:00:00 2001 From: uz Date: Mon, 31 Aug 2009 14:59:49 +0000 Subject: [PATCH] Improved the code generated for bit-fields. git-svn-id: svn://svn.cc65.org/cc65/trunk@4096 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- src/cc65/asmcode.c | 19 ++++++++++ src/cc65/asmcode.h | 3 ++ src/cc65/assignment.c | 46 ++++++++++++++++++++----- src/cc65/codeseg.c | 80 +++++++++++++++++++++++++++++++++++++++++++ src/cc65/codeseg.h | 7 ++++ 5 files changed, 146 insertions(+), 9 deletions(-) diff --git a/src/cc65/asmcode.c b/src/cc65/asmcode.c index a09b00dfa..e5fff9af0 100644 --- a/src/cc65/asmcode.c +++ b/src/cc65/asmcode.c @@ -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 */ { diff --git a/src/cc65/asmcode.h b/src/cc65/asmcode.h index b43cfce8d..b91b892d0 100644 --- a/src/cc65/asmcode.h +++ b/src/cc65/asmcode.h @@ -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 */ diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index af414ff50..eed097272 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -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); diff --git a/src/cc65/codeseg.c b/src/cc65/codeseg.c index f47e4dd50..36fdc05b6 100644 --- a/src/cc65/codeseg.c +++ b/src/cc65/codeseg.c @@ -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 */ { diff --git a/src/cc65/codeseg.h b/src/cc65/codeseg.h index 453dd7a52..e7f0ebc9f 100644 --- a/src/cc65/codeseg.h +++ b/src/cc65/codeseg.h @@ -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 */ -- 2.39.5