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:
parent
0a9c7484ad
commit
dc678e8dcb
5 changed files with 146 additions and 9 deletions
|
@ -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 */
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
{
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue