diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c index 2c6624a5c..a81b2cf46 100644 --- a/src/cc65/codegen.c +++ b/src/cc65/codegen.c @@ -33,6 +33,7 @@ +#include #include #include #include @@ -4424,6 +4425,63 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size) +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + + + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth) +/* Test bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* If we need to do a test, then we avoid shifting (ASR only shifts one bit at a time, + ** so is slow) and just AND with the appropriate mask, then test the result of that. + */ + + /* Avoid overly large shift on host platform. */ + if (EndBit == sizeof (unsigned long) * CHAR_BIT) { + g_and (Flags | CF_CONST, (~0UL << BitOffs)); + } else { + g_and (Flags | CF_CONST, ((1UL << EndBit) - 1) & (~0UL << BitOffs)); + } + + /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes when + ** EndBit <= 24 is possible. + */ + g_test (Flags | CF_CONST); +} + + + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, + unsigned BitOffs, unsigned BitWidth) +/* Extract bits from bit-field in ax. */ +{ + unsigned EndBit = BitOffs + BitWidth; + + /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ + g_asr (Flags | CF_CONST, BitOffs); + + /* Since we have now shifted down, we could do char ops when the width fits in a char, but we + ** also need to clear the high byte since we've been using CF_FORCECHAR up to now. + */ + + /* And by the width if the field doesn't end on a char or int boundary. If it does end on + ** a boundary, then zeros have already been shifted in, but we need to clear the high byte + ** for char. g_and emits no code if the mask is all ones. + */ + if (EndBit == CHAR_BITS) { + /* We need to clear the high byte, since CF_FORCECHAR was set. */ + g_and (FullWidthFlags | CF_CONST, 0xFF); + } else if (EndBit != INT_BITS) { + g_and (FullWidthFlags | CF_CONST, (0x0001U << BitWidth) - 1U); + } +} + + + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h index c63fc5398..6581fc54e 100644 --- a/src/cc65/codegen.h +++ b/src/cc65/codegen.h @@ -471,6 +471,17 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size); +/*****************************************************************************/ +/* Bit-fields */ +/*****************************************************************************/ + +void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth); +/* Test bit-field in ax. */ + +void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, + unsigned BitOffs, unsigned BitWidth); +/* Extract bits from bit-field in ax. */ + /*****************************************************************************/ /* Switch statement */ /*****************************************************************************/ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index ba585d3e3..452d9a9a3 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -33,8 +33,6 @@ -#include - /* cc65 */ #include "codegen.h" #include "error.h" @@ -115,11 +113,10 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** field is completely contained in the lower byte, we will throw away ** the high byte anyway and may therefore load just the low byte. */ - unsigned EndBit = 0; /* End bit for bit-fields, or zero if non-bit-field. */ int AdjustBitField = 0; unsigned BitFieldFullWidthFlags = 0; if (ED_IsBitField (Expr)) { - EndBit = Expr->BitOffs + Expr->BitWidth; + unsigned EndBit = Expr->BitOffs + Expr->BitWidth; AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are @@ -226,50 +223,15 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) ** so be sure to always use unsigned ints for the operations. */ if (AdjustBitField) { - unsigned F = Flags | CF_CONST; - /* We always need to do something with the low byte, so there is no opportunity ** for optimization by skipping it. */ CHECK (Expr->BitOffs < CHAR_BITS); if (ED_NeedsTest (Expr)) { - /* If we need to do a test, then we avoid shifting (ASR only shifts one bit - ** at a time, so is slow) and just AND with the appropriate mask, then test - ** the result of that. - */ - - /* Avoid overly large shift on host platform. */ - if (EndBit == sizeof (unsigned long) * CHAR_BIT) { - g_and (F, (~0UL << Expr->BitOffs)); - } else { - g_and (F, ((1UL << EndBit) - 1) & (~0UL << Expr->BitOffs)); - } - - /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes - ** when EndBit <= 24 is possible. - */ - g_test (F); + g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); } else { - /* Shift right by the bit offset; no code is emitted if BitOffs is zero */ - g_asr (F, Expr->BitOffs); - - /* Since we have now shifted down, we could do char ops when the width fits in - ** a char, but we also need to clear the high byte since we've been using - ** CF_FORCECHAR up to now. - */ - - /* And by the width if the field doesn't end on a char or int boundary. - ** If it does end on a boundary, then zeros have already been shifted in, - ** but we need to clear the high byte for char. g_and emits no code if the mask - ** is all ones. - */ - if (EndBit == CHAR_BITS) { - /* We need to clear the high byte, since CF_FORCECHAR was set. */ - g_and (BitFieldFullWidthFlags | CF_CONST, 0xFF); - } else if (EndBit != INT_BITS) { - g_and (BitFieldFullWidthFlags | CF_CONST, (0x0001U << Expr->BitWidth) - 1U); - } + g_extractbitfield (Flags, BitFieldFullWidthFlags, Expr->BitOffs, Expr->BitWidth); } }