Improved the code generated for bit-fields.

git-svn-id: svn://svn.cc65.org/cc65/trunk@4096 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
uz 2009-08-31 14:59:49 +00:00
parent 0a9c7484ad
commit dc678e8dcb
5 changed files with 146 additions and 9 deletions

View 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 */
{

View 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 */

View 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)) {
/* Apply the mask */
g_and (Flags | CF_CONST, Mask);
/* Get the value and apply the mask */
unsigned Val = (unsigned) (Expr2.IVal & Mask);
/* Shift it into the right position */
g_asl (Flags | CF_CONST, Expr->BitOffs);
/* Since we will do the OR with a constant, we can remove the push */
RemoveCode (&PushPos);
/* Or both values */
g_or (Flags, 0);
/* 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);
}
/* Generate the or operation */
g_or (Flags | CF_CONST, Val << Expr->BitOffs);
} 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);

View 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 */
{

View 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 */