diff --git a/src/cc65.vcxproj b/src/cc65.vcxproj
index 14500296d..5cddc1862 100644
--- a/src/cc65.vcxproj
+++ b/src/cc65.vcxproj
@@ -93,6 +93,7 @@
+
@@ -170,6 +171,7 @@
+
diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c
index 05a6d9a96..e6d1e4526 100644
--- a/src/cc65/assignment.c
+++ b/src/cc65/assignment.c
@@ -156,19 +156,8 @@ void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult)
unsigned ChunkFlags;
const Type* ChunkType;
- /* If the bit-field fits within one byte, do the following operations
- ** with bytes.
- */
- if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS ==
- (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) {
- ChunkType = GetUnderlyingType (Expr->Type);
- } else {
- /* We use the declarartion integer type as the chunk type.
- ** Note: A bit-field will not occupy bits located in bytes more than
- ** that of its declaration type in cc65. So this is OK.
- */
- ChunkType = Expr->Type + 1;
- }
+ /* Determine the type to operate on the whole byte chunk containing the bit-field */
+ ChunkType = GetBitFieldChunkType (Expr->Type);
/* Determine code generator flags */
Flags = TypeOf (Expr->Type) | CF_FORCECHAR;
@@ -254,19 +243,8 @@ static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
- /* If the bit-field fits within one byte, do the following operations
- ** with bytes.
- */
- if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS ==
- (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) {
- ChunkType = GetUnderlyingType (Expr->Type);
- } else {
- /* We use the declarartion integer type as the chunk type.
- ** Note: A bit-field will not occupy bits located in bytes more than
- ** that of its declaration type in cc65. So this is OK.
- */
- ChunkType = Expr->Type + 1;
- }
+ /* Determine the type to operate on the whole byte chunk containing the bit-field */
+ ChunkType = GetBitFieldChunkType (Expr->Type);
/* Determine code generator flags */
Flags = TypeOf (Expr->Type) | CF_FORCECHAR;
@@ -620,8 +598,8 @@ void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
if (IsClassStruct (ltype)) {
/* Copy the struct or union by value */
CopyStruct (Expr, &Expr2);
- } else if (IsTypeBitField (ltype)) {
- /* Special care is needed for bit-field 'op=' */
+ } else if (IsTypeFragBitField (ltype)) {
+ /* Special care is needed for bit-fields if they don't fit in full bytes */
OpAssignBitField (Gen, Expr, Op);
} else {
/* Normal straight 'op=' */
diff --git a/src/cc65/codegen.c b/src/cc65/codegen.c
index 5bfc6696b..c79863d0e 100644
--- a/src/cc65/codegen.c
+++ b/src/cc65/codegen.c
@@ -43,6 +43,7 @@
#include "addrsize.h"
#include "check.h"
#include "cpu.h"
+#include "shift.h"
#include "strbuf.h"
#include "xmalloc.h"
#include "xsprintf.h"
@@ -4560,110 +4561,268 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size)
void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth)
-/* Test bit-field in ax. */
+/* Test bit-field in primary. */
{
- unsigned EndBit = BitOffs + BitWidth;
+ /* Since the end is inclusive and cannot be negative here, we subtract 1 from the sum */
+ unsigned MSBit = BitOffs + BitWidth - 1U;
+ unsigned Bytes = MSBit / CHAR_BITS + 1U - BitOffs / CHAR_BITS;
+ unsigned HeadMask = (0xFF << (BitOffs % CHAR_BITS)) & 0xFF;
+ unsigned TailMask = ((1U << (MSBit % CHAR_BITS + 1U)) - 1U) & 0xFF;
+ unsigned UntestedBytes = ((1U << Bytes) - 1U) << (BitOffs / CHAR_BITS);
+
+ /* We don't use these flags for now. Could CF_NOKEEP be potentially interesting? */
+ Flags &= ~CF_STYPEMASK;
/* 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.
+ ** so is slow) and just AND the head and tail bytes with the appropriate mask, then
+ ** OR the results with the rest bytes.
*/
-
- /* 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));
+ if (Bytes == 1) {
+ HeadMask = TailMask = HeadMask & TailMask;
}
- /* TODO: When long bit-fields are supported, an optimization to test only 3 bytes when
- ** EndBit <= 24 is possible.
- */
- g_test (Flags | CF_CONST);
+ /* Get the head byte */
+ switch (BitOffs / CHAR_BITS) {
+ case 0:
+ if (HeadMask == 0xFF && Bytes == 1) {
+ AddCodeLine ("tax");
+ UntestedBytes &= ~0x1;
+ }
+ break;
+ case 1:
+ if (HeadMask != 0xFF || TailMask == 0xFF) {
+ AddCodeLine ("txa");
+ UntestedBytes &= ~0x2;
+ }
+ break;
+ case 2:
+ if (HeadMask != 0xFF || TailMask == 0xFF) {
+ AddCodeLine ("lda sreg");
+ UntestedBytes &= ~0x4;
+ }
+ break;
+ case 3:
+ /* In this case we'd have HeadMask == TailMask and only 1 byte, but anyways... */
+ if (HeadMask != 0xFF || TailMask == 0xFF) {
+ AddCodeLine ("lda sreg+1");
+ UntestedBytes &= ~0x8;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Keep in mind that the head is NOT always "Byte 0" */
+ if (HeadMask != 0xFF) {
+ AddCodeLine ("and #$%02X", HeadMask);
+ /* Abuse the "Byte 0" flag so that this head content will be saved by the routine */
+ UntestedBytes |= 0x1;
+ }
+
+ /* If there is only 1 byte to test, we have done with it */
+ if (Bytes == 1) {
+ return;
+ }
+
+ /* Handle the tail byte */
+ if (TailMask != 0xFF) {
+ /* If we have to do any more masking operation, register A will be used for that,
+ ** and its current content in it must be saved.
+ */
+ if (UntestedBytes & 0x1) {
+ AddCodeLine ("sta tmp1");
+ }
+
+ /* Test the tail byte */
+ switch (MSBit / CHAR_BITS) {
+ case 1:
+ AddCodeLine ("txa");
+ UntestedBytes &= ~0x2;
+ break;
+ case 2:
+ AddCodeLine ("lda sreg");
+ UntestedBytes &= ~0x4;
+ break;
+ case 3:
+ AddCodeLine ("lda sreg+1");
+ UntestedBytes &= ~0x8;
+ break;
+ default:
+ break;
+ }
+ AddCodeLine ("and #$%02X", TailMask);
+
+ if (UntestedBytes & 0x1) {
+ AddCodeLine ("ora tmp1");
+ }
+ }
+
+ /* OR the rest bytes together, which could never need masking */
+ if (UntestedBytes & 0x2) {
+ AddCodeLine ("stx tmp1");
+ AddCodeLine ("ora tmp1");
+ }
+ if (UntestedBytes & 0x4) {
+ AddCodeLine ("ora sreg");
+ }
+ if (UntestedBytes & 0x8) {
+ AddCodeLine ("ora sreg+1");
+ }
}
void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned,
unsigned BitOffs, unsigned BitWidth)
-/* Extract bits from bit-field in ax. */
+/* Extract bits from bit-field in primary. */
{
unsigned EndBit = BitOffs + BitWidth;
+ unsigned long ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */
/* 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 (or set) the high byte since we've been using CF_FORCECHAR up to now.
- */
- unsigned Mask = (1U << BitWidth) - 1;
-
/* To zero-extend, we will 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 will 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.
** This is here so the signed and unsigned branches can use it.
*/
- unsigned ZeroExtendMask = 0; /* Zero if we don't need to zero-extend. */
if (EndBit == CHAR_BITS) {
/* We need to clear the high byte, since CF_FORCECHAR was set. */
ZeroExtendMask = 0xFF;
- } else if (EndBit != INT_BITS) {
- ZeroExtendMask = (1U << BitWidth) - 1;
+ } else if (EndBit != INT_BITS && EndBit != LONG_BITS) {
+ ZeroExtendMask = shl_l (1UL, BitWidth) - 1UL;
}
/* Handle signed bit-fields. */
if (IsSigned) {
- /* Save .A because the sign-bit test will destroy it. */
- AddCodeLine ("tay");
-
- /* Check sign bit */
unsigned SignBitPos = BitWidth - 1U;
unsigned SignBitByte = SignBitPos / CHAR_BITS;
unsigned SignBitPosInByte = SignBitPos % CHAR_BITS;
- unsigned SignBitMask = 1U << SignBitPosInByte;
- /* Move the correct byte to .A. This can be only .X for now,
- ** but more cases will be needed to support long.
- */
- switch (SignBitByte) {
- case 0:
- break;
- case 1:
- AddCodeLine ("txa");
- break;
- default:
- FAIL ("Invalid Byte for sign bit");
- }
-
- /* Test the sign bit */
- AddCodeLine ("and #$%02X", SignBitMask);
- unsigned ZeroExtendLabel = GetLocalLabel ();
- AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel));
-
- /* Get back .A and sign-extend if required; operating on the full result needs
- ** to sign-extend into the high byte, too.
- */
- AddCodeLine ("tya");
- g_or (FullWidthFlags | CF_CONST, ~Mask);
-
- /* We can generate a branch, instead of a jump, here because we know
- ** that only a few instructions will be put between here and where
- ** DoneLabel will be defined.
- */
- unsigned DoneLabel = GetLocalLabel ();
- g_branch (DoneLabel);
-
- /* Get back .A, then zero-extend. We need to duplicate the TYA, rather than move it before
- ** the branch to share with the other label, because TYA changes some condition codes.
- */
- g_defcodelabel (ZeroExtendLabel);
- AddCodeLine ("tya");
-
- /* Zero the upper bits, the same as the unsigned path. */
if (ZeroExtendMask != 0) {
- g_and (FullWidthFlags | CF_CONST, ZeroExtendMask);
- }
+ /* The universal trick is:
+ ** x = bits & bit_mask
+ ** m = 1 << (bit_width - 1)
+ ** r = (x ^ m) - m
+ ** which works for long as well.
+ */
- g_defcodelabel (DoneLabel);
+ if (SignBitByte + 1U == sizeofarg (FullWidthFlags)) {
+ /* We can just sign-extend on the high byte if it is the only affected one */
+ unsigned char SignBitMask = (1UL << SignBitPosInByte) & 0xFF;
+ unsigned char Mask = ((2UL << (SignBitPos % CHAR_BITS)) - 1UL) & 0xFF;
+
+ /* Move the correct byte to .A */
+ switch (SignBitByte) {
+ case 0:
+ break;
+ case 1:
+ AddCodeLine ("tay");
+ AddCodeLine ("txa");
+ break;
+ case 3:
+ AddCodeLine ("tay");
+ AddCodeLine ("lda sreg+1");
+ break;
+ default:
+ FAIL ("Invalid Byte for sign bit");
+ }
+
+ /* Use .A to do the ops on the correct byte */
+ AddCodeLine ("and #$%02X", Mask);
+ AddCodeLine ("eor #$%02X", SignBitMask);
+ AddCodeLine ("sec");
+ AddCodeLine ("sbc #$%02X", SignBitMask);
+
+ /* Move the correct byte from .A */
+ switch (SignBitByte) {
+ case 0:
+ break;
+ case 1:
+ AddCodeLine ("tax");
+ AddCodeLine ("tya");
+ break;
+ case 3:
+ AddCodeLine ("sta sreg+1");
+ AddCodeLine ("tya");
+ break;
+ default:
+ FAIL ("Invalid Byte for sign bit");
+ }
+ } else {
+ unsigned long SignBitMask = 1UL << SignBitPos;
+ unsigned long Mask = (2UL << SignBitPos) - 1UL;
+ g_and (FullWidthFlags | CF_CONST, Mask);
+ g_xor (FullWidthFlags | CF_CONST, SignBitMask);
+ g_dec (FullWidthFlags | CF_CONST, SignBitMask);
+ }
+ } else {
+ unsigned char SignBitMask = (1UL << SignBitPosInByte) & 0xFF;
+ unsigned ZeroExtendLabel = GetLocalLabel ();
+
+ /* Save .A because the sign-bit test will destroy it. */
+ AddCodeLine ("tay");
+
+ /* Move the correct byte to .A */
+ switch (SignBitByte) {
+ case 0:
+ break;
+ case 1:
+ AddCodeLine ("txa");
+ break;
+ case 3:
+ AddCodeLine ("lda sreg+1");
+ break;
+ default:
+ FAIL ("Invalid Byte for sign bit");
+ }
+
+ /* Test the sign bit */
+ AddCodeLine ("and #$%02X", SignBitMask);
+ AddCodeLine ("beq %s", LocalLabelName (ZeroExtendLabel));
+
+ if (SignBitByte + 1U == sizeofarg (FullWidthFlags)) {
+ /* We can just sign-extend on the high byte if it is the only affected one */
+ unsigned char Mask = ~((2UL << (SignBitPos % CHAR_BITS)) - 1UL) & 0xFF;
+
+ /* Use .A to do the ops on the correct byte */
+ switch (SignBitByte) {
+ case 0:
+ AddCodeLine ("tya");
+ AddCodeLine ("ora #$%02X", Mask);
+ /* We could jump over the following tya instead, but that wouldn't be faster
+ ** than taking this extra tay and then the tya.
+ */
+ AddCodeLine ("tay");
+ break;
+ case 1:
+ AddCodeLine ("txa");
+ AddCodeLine ("ora #$%02X", Mask);
+ AddCodeLine ("tax");
+ break;
+ case 3:
+ AddCodeLine ("lda sreg+1");
+ AddCodeLine ("ora #$%02X", Mask);
+ AddCodeLine ("sta sreg+1");
+ break;
+ default:
+ FAIL ("Invalid Byte for sign bit");
+ }
+ } else {
+ /* Since we are going to get back .A later anyways, we may just do the op on the
+ ** higher bytes with whatever content currently in it.
+ */
+ unsigned long Mask = ~((2UL << SignBitPos) - 1UL);
+ g_or (FullWidthFlags | CF_CONST, Mask);
+ }
+
+ /* Get back .A. We need to duplicate the TYA, rather than move it before
+ ** the branch to share with the other label, because TYA changes some condition codes.
+ */
+ g_defcodelabel (ZeroExtendLabel);
+ AddCodeLine ("tya");
+ }
} else {
/* Unsigned bit-field, needs only zero-extension. */
if (ZeroExtendMask != 0) {
diff --git a/src/cc65/codegen.h b/src/cc65/codegen.h
index 1de71e7d3..cb62d78bd 100644
--- a/src/cc65/codegen.h
+++ b/src/cc65/codegen.h
@@ -486,11 +486,11 @@ void g_initstatic (unsigned InitLabel, unsigned VarLabel, unsigned Size);
/*****************************************************************************/
void g_testbitfield (unsigned Flags, unsigned BitOffs, unsigned BitWidth);
-/* Test bit-field in ax. */
+/* Test bit-field in primary. */
void g_extractbitfield (unsigned Flags, unsigned FullWidthFlags, int IsSigned,
unsigned BitOffs, unsigned BitWidth);
-/* Extract bits from bit-field in ax. */
+/* Extract bits from bit-field in primary. */
/*****************************************************************************/
/* Switch statement */
diff --git a/src/cc65/compile.c b/src/cc65/compile.c
index 94dfc3ffb..85c9bd5a4 100644
--- a/src/cc65/compile.c
+++ b/src/cc65/compile.c
@@ -56,6 +56,7 @@
#include "funcdesc.h"
#include "function.h"
#include "global.h"
+#include "initdata.h"
#include "input.h"
#include "litpool.h"
#include "macrotab.h"
diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c
index cc313bd21..e43af238e 100644
--- a/src/cc65/datatype.c
+++ b/src/cc65/datatype.c
@@ -551,6 +551,24 @@ unsigned long GetIntegerTypeMax (const Type* Type)
+static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth)
+/* Return the size of the smallest integer type that may have BitWidth bits */
+{
+ /* Since all integer types supported in cc65 for bit-fields have sizes that
+ ** are powers of 2, we can just use this bit-twiddling trick.
+ */
+ unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS;
+ V |= V >> 1;
+ V |= V >> 2;
+ V |= V >> 4;
+ V |= V >> 8;
+ V |= V >> 16;
+
+ /* Return the result size */
+ return V + 1U;
+}
+
+
static unsigned TypeOfBySize (unsigned Size)
/* Get the code generator replacement type of the object by its size */
{
@@ -591,8 +609,7 @@ const Type* GetUnderlyingType (const Type* Type)
** bit-field, instead of the type used in the declaration, the truly
** underlying of the bit-field.
*/
- unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
- switch (Size) {
+ switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break;
case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break;
case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break;
@@ -646,8 +663,7 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
** bit-field, instead of the type used in the declaration, the truly
** underlying of the bit-field.
*/
- unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
- switch (Size) {
+ switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
case SIZEOF_CHAR: Underlying = T_CHAR; break;
case SIZEOF_INT: Underlying = T_INT; break;
case SIZEOF_LONG: Underlying = T_LONG; break;
@@ -663,6 +679,39 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
+const Type* GetBitFieldChunkType (const Type* Type)
+/* Get the type needed to operate on the byte chunk containing the bit-field */
+{
+ unsigned ChunkSize;
+ if ((Type->A.B.Width - 1U) / CHAR_BITS ==
+ (Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) {
+ /* T bit-field fits within its underlying type */
+ return GetUnderlyingType (Type);
+ }
+
+ ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width);
+ if (ChunkSize < SizeOf (Type + 1)) {
+ /* The end of the bit-field is offset by some bits so that it requires
+ ** more bytes to be accessed as a whole than its underlying type does.
+ ** Note: In cc65 the bit offset is always less than CHAR_BITS.
+ */
+ switch (ChunkSize) {
+ case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar;
+ case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint;
+ case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong;
+ default: return IsSignSigned (Type) ? type_int : type_uint;
+ }
+ }
+
+ /* We can always use the declarartion integer type as the chunk type.
+ ** Note: A bit-field will not occupy bits located in bytes more than that
+ ** of its declaration type in cc65. So this is OK.
+ */
+ return Type + 1;
+}
+
+
+
unsigned SizeOf (const Type* T)
/* Compute size of object represented by type array. */
{
@@ -967,9 +1016,18 @@ const Type* IntPromotion (const Type* T)
*/
if (IsTypeBitField (T)) {
- /* The standard rule is OK for now as we don't support bit-fields with widths > 16.
+ /* As we now support long bit-fields, we need modified rules for them:
+ ** - If an int can represent all values of the bit-field, the bit-field is converted
+ ** to an int;
+ ** - Otherwise, if an unsigned int can represent all values of the bit-field, the
+ ** bit-field is converted to an unsigned int;
+ ** - Otherwise, the bit-field will have its declared integer type.
+ ** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's.
*/
- return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
+ if (T->A.B.Width > INT_BITS) {
+ return IsSignUnsigned (T) ? type_ulong : type_long;
+ }
+ return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
} else if (IsTypeChar (T)) {
/* An integer can represent all values from either signed or unsigned char, so convert
** chars to int.
@@ -1127,6 +1185,15 @@ Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth)
+int IsTypeFragBitField (const Type* T)
+/* Return true if this is a bit-field that shares byte space with other fields */
+{
+ return IsTypeBitField (T) &&
+ (T->A.B.Offs != 0 || T->A.B.Width != CHAR_BITS * SizeOf (T));
+}
+
+
+
int IsClassObject (const Type* T)
/* Return true if this is a fully described object type */
{
diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h
index e36d7c82e..c60023944 100644
--- a/src/cc65/datatype.h
+++ b/src/cc65/datatype.h
@@ -313,6 +313,9 @@ TypeCode GetUnderlyingTypeCode (const Type* Type);
** Return TCode if it is not scalar.
*/
+const Type* GetBitFieldChunkType (const Type* Type);
+/* Get the type needed to operate on the byte chunk containing the bit-field */
+
unsigned SizeOf (const Type* T);
/* Compute size of object represented by type array. */
@@ -556,6 +559,9 @@ INLINE int IsTypeBitField (const Type* T)
# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T))
#endif
+int IsTypeFragBitField (const Type* T);
+/* Return true if this is a bit-field that shares byte space with other fields */
+
#if defined(HAVE_INLINE)
INLINE int IsTypeStruct (const Type* T)
/* Return true if this is a struct type */
diff --git a/src/cc65/declare.c b/src/cc65/declare.c
index 017a69874..7cc7444b6 100644
--- a/src/cc65/declare.c
+++ b/src/cc65/declare.c
@@ -66,22 +66,6 @@
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-typedef struct StructInitData StructInitData;
-struct StructInitData {
- unsigned Size; /* Size of struct */
- unsigned Offs; /* Current offset in struct */
- unsigned BitVal; /* Summed up bit-field value */
- unsigned ValBits; /* Valid bits in Val */
-};
-
-
-
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
@@ -92,9 +76,6 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
int* SignednessSpecified);
/* Parse a type specifier */
-static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers);
-/* Parse initialization of variables. Return the number of data bytes. */
-
/*****************************************************************************/
@@ -765,12 +746,10 @@ static int ParseFieldWidth (Declaration* D)
D->Type[0].C = T_INT;
}
- /* TODO: This can be relaxed to be any integral type, but
- ** ParseStructInit currently supports only up to int.
- */
- if (SizeOf (D->Type) > SizeOf (type_uint)) {
- /* Only int-sized or smaller types may be used for bit-fields, for now */
- Error ("cc65 currently supports only char-sized and int-sized bit-field types");
+ /* We currently support integral types up to long */
+ if (SizeOf (D->Type) > SizeOf (type_ulong)) {
+ /* Only long-sized or smaller types may be used for bit-fields, for now */
+ Error ("cc65 currently supports only long-sized and smaller bit-field types");
/* Avoid a diagnostic storm */
D->Type[0].C = T_INT;
@@ -2121,704 +2100,3 @@ void CheckEmptyDecl (const DeclSpec* D)
Warning ("Useless declaration");
}
}
-
-
-
-static void SkipInitializer (int BracesExpected)
-/* Skip the remainder of an initializer in case of errors. Try to be somewhat
-** smart so we don't have too many following errors.
-*/
-{
- while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) {
- switch (CurTok.Tok) {
- case TOK_RCURLY: --BracesExpected; break;
- case TOK_LCURLY: ++BracesExpected; break;
- default: break;
- }
- if (BracesExpected >= 0) {
- NextToken ();
- }
- }
-}
-
-
-
-static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
-/* Accept any number of opening curly braces around an initialization, skip
-** them and return the number. If the number of curly braces is less than
-** BracesNeeded, issue a warning.
-*/
-{
- unsigned BraceCount = 0;
- while (CurTok.Tok == TOK_LCURLY) {
- ++BraceCount;
- NextToken ();
- }
- if (BraceCount < BracesNeeded) {
- Error ("'{' expected");
- }
- return BraceCount;
-}
-
-
-
-static void ClosingCurlyBraces (unsigned BracesExpected)
-/* Accept and skip the given number of closing curly braces together with
-** an optional comma. Output an error messages, if the input does not contain
-** the expected number of braces.
-*/
-{
- while (BracesExpected) {
- /* TODO: Skip all excess initializers until next closing curly brace */
- if (CurTok.Tok == TOK_RCURLY) {
- NextToken ();
- } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
- NextToken ();
- NextToken ();
- } else {
- Error ("'}' expected");
- return;
- }
- --BracesExpected;
- }
-}
-
-
-
-static void DefineData (ExprDesc* Expr)
-/* Output a data definition for the given expression */
-{
- switch (ED_GetLoc (Expr)) {
-
- case E_LOC_NONE:
- /* Immediate numeric value with no storage */
- g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
- break;
-
- case E_LOC_ABS:
- /* Absolute numeric address */
- g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
- break;
-
- case E_LOC_GLOBAL:
- /* Global variable */
- g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal);
- break;
-
- case E_LOC_STATIC:
- /* Static variable */
- g_defdata (CF_STATIC, Expr->Name, Expr->IVal);
- break;
-
- case E_LOC_LITERAL:
- /* Literal in the literal pool */
- g_defdata (CF_LITERAL, Expr->Name, Expr->IVal);
- break;
-
- case E_LOC_REGISTER:
- /* Register variable. Taking the address is usually not
- ** allowed.
- */
- if (IS_Get (&AllowRegVarAddr) == 0) {
- Error ("Cannot take the address of a register variable");
- }
- g_defdata (CF_REGVAR, Expr->Name, Expr->IVal);
- break;
-
- case E_LOC_CODE:
- /* Code label location */
- g_defdata (CF_CODE, Expr->Name, Expr->IVal);
- break;
-
- case E_LOC_STACK:
- case E_LOC_PRIMARY:
- case E_LOC_EXPR:
- Error ("Non constant initializer");
- break;
-
- default:
- Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr));
- }
-}
-
-
-
-static void DefineBitFieldData (StructInitData* SI)
-/* Output bit field data */
-{
- /* Ignore if we have no data */
- if (SI->ValBits > 0) {
-
- /* Output the data */
- g_defdata (CF_CHAR | CF_UNSIGNED | CF_CONST, SI->BitVal, 0);
-
- /* Update the data from SI and account for the size */
- if (SI->ValBits >= CHAR_BITS) {
- SI->BitVal >>= CHAR_BITS;
- SI->ValBits -= CHAR_BITS;
- } else {
- SI->BitVal = 0;
- SI->ValBits = 0;
- }
- SI->Offs += SIZEOF_CHAR;
- }
-}
-
-
-
-static void DefineStrData (Literal* Lit, unsigned Count)
-{
- /* Translate into target charset */
- TranslateLiteral (Lit);
-
- /* Output the data */
- g_defbytes (GetLiteralStr (Lit), Count);
-}
-
-
-
-static ExprDesc ParseScalarInitInternal (const Type* T)
-/* Parse initializaton for scalar data types. This function will not output the
-** data but return it in ED.
-*/
-{
- /* Optional opening brace */
- unsigned BraceCount = OpeningCurlyBraces (0);
-
- /* We warn if an initializer for a scalar contains braces, because this is
- ** quite unusual and often a sign for some problem in the input.
- */
- if (BraceCount > 0) {
- Warning ("Braces around scalar initializer");
- }
-
- /* Get the expression and convert it to the target type */
- ExprDesc ED = NoCodeConstExpr (hie1);
- TypeConversion (&ED, T);
-
- /* Close eventually opening braces */
- ClosingCurlyBraces (BraceCount);
-
- return ED;
-}
-
-
-
-static unsigned ParseScalarInit (const Type* T)
-/* Parse initializaton for scalar data types. Return the number of data bytes. */
-{
- /* Parse initialization */
- ExprDesc ED = ParseScalarInitInternal (T);
-
- /* Output the data */
- DefineData (&ED);
-
- /* Do this anyways for safety */
- DoDeferred (SQP_KEEP_NONE, &ED);
-
- /* Done */
- return SizeOf (T);
-}
-
-
-
-static unsigned ParsePointerInit (const Type* T)
-/* Parse initializaton for pointer data types. Return the number of data bytes. */
-{
- /* Optional opening brace */
- unsigned BraceCount = OpeningCurlyBraces (0);
-
- /* Expression */
- ExprDesc ED = NoCodeConstExpr (hie1);
- TypeConversion (&ED, T);
-
- /* Output the data */
- DefineData (&ED);
-
- /* Do this anyways for safety */
- DoDeferred (SQP_KEEP_NONE, &ED);
-
- /* Close eventually opening braces */
- ClosingCurlyBraces (BraceCount);
-
- /* Done */
- return SIZEOF_PTR;
-}
-
-
-
-static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
-/* Parse initializaton for arrays. Return the number of data bytes. */
-{
- int Count;
- int HasCurly = 0;
-
- /* Get the array data */
- Type* ElementType = IndirectModifiable (T);
- unsigned ElementSize = SizeOf (ElementType);
- long ElementCount = GetElementCount (T);
-
- /* Special handling for a character array initialized by a literal */
- if (IsClassChar (ElementType) &&
- (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST ||
- (CurTok.Tok == TOK_LCURLY &&
- (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
-
- /* Char array initialized by string constant */
- int NeedParen;
-
- /* If we initializer is enclosed in brackets, remember this fact and
- ** skip the opening bracket.
- */
- NeedParen = (CurTok.Tok == TOK_LCURLY);
- if (NeedParen) {
- NextToken ();
- }
-
- /* If the array is one too small for the string literal, omit the
- ** trailing zero.
- */
- Count = GetLiteralSize (CurTok.SVal);
- if (ElementCount != UNSPECIFIED &&
- ElementCount != FLEXIBLE &&
- Count == ElementCount + 1) {
- /* Omit the trailing zero */
- --Count;
- }
-
- /* Output the data */
- DefineStrData (CurTok.SVal, Count);
-
- /* Skip the string */
- NextToken ();
-
- /* If the initializer was enclosed in curly braces, we need a closing
- ** one.
- */
- if (NeedParen) {
- ConsumeRCurly ();
- }
-
- } else {
-
- /* Arrays can be initialized without a pair of curly braces */
- if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
- /* Consume the opening curly brace */
- HasCurly = ConsumeLCurly ();
- *Braces += HasCurly;
- }
-
- /* Initialize the array members */
- Count = 0;
- while (CurTok.Tok != TOK_RCURLY) {
- /* Flexible array members may not be initialized within
- ** an array (because the size of each element may differ
- ** otherwise).
- */
- ParseInitInternal (ElementType, Braces, 0);
- ++Count;
- if (CurTok.Tok != TOK_COMMA)
- break;
- NextToken ();
- }
-
- if (HasCurly) {
- /* Closing curly braces */
- ConsumeRCurly ();
- }
- }
-
- /* Size of 'void' elements are determined after initialization */
- if (ElementSize == 0) {
- ElementSize = SizeOf (ElementType);
- }
-
- if (ElementCount == UNSPECIFIED) {
- /* Number of elements determined by initializer */
- SetElementCount (T, Count);
- ElementCount = Count;
- } else if (ElementCount == FLEXIBLE) {
- if (AllowFlexibleMembers) {
- /* In non ANSI mode, allow initialization of flexible array
- ** members.
- */
- ElementCount = Count;
- } else {
- /* Forbid */
- Error ("Initializing flexible array member is forbidden");
- ElementCount = Count;
- }
- } else if (Count < ElementCount) {
- g_zerobytes ((ElementCount - Count) * ElementSize);
- } else if (Count > ElementCount && HasCurly) {
- Error ("Excess elements in array initializer");
- }
- return ElementCount * ElementSize;
-}
-
-
-
-static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
-/* Parse initialization of a struct or union. Return the number of data bytes. */
-{
- SymEntry* Sym;
- SymTable* Tab;
- StructInitData SI;
- int HasCurly = 0;
- int SkipComma = 0;
-
-
- /* Fields can be initialized without a pair of curly braces */
- if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
- /* Consume the opening curly brace */
- HasCurly = ConsumeLCurly ();
- *Braces += HasCurly;
- }
-
- /* Get a pointer to the struct entry from the type */
- Sym = GetESUSymEntry (T);
-
- /* Get the size of the struct from the symbol table entry */
- SI.Size = Sym->V.S.Size;
-
- /* Check if this struct definition has a field table. If it doesn't, it
- ** is an incomplete definition.
- */
- Tab = Sym->V.S.SymTab;
- if (Tab == 0) {
- Error ("Cannot initialize variables with incomplete type");
- /* Try error recovery */
- SkipInitializer (HasCurly);
- /* Nothing initialized */
- return 0;
- }
-
- /* Get a pointer to the list of symbols */
- Sym = Tab->SymHead;
-
- /* Initialize fields */
- SI.Offs = 0;
- SI.BitVal = 0;
- SI.ValBits = 0;
- while (CurTok.Tok != TOK_RCURLY) {
-
- /* Check for excess elements */
- if (Sym == 0) {
- /* Is there just one trailing comma before a closing curly? */
- if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
- /* Skip comma and exit scope */
- NextToken ();
- break;
- }
-
- if (HasCurly) {
- Error ("Excess elements in %s initializer", GetBasicTypeName (T));
- SkipInitializer (HasCurly);
- }
- return SI.Offs;
- }
-
- /* Check for special members that don't consume the initializer */
- if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) {
- /* Just skip */
- goto NextMember;
- }
-
- /* This may be an anonymous bit-field, in which case it doesn't
- ** have an initializer.
- */
- if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) {
- /* Account for the data and output it if we have at least a full
- ** word. We may have more if there was storage unit overlap, for
- ** example two consecutive 10 bit fields. These will be packed
- ** into 3 bytes.
- */
- SI.ValBits += Sym->Type->A.B.Width;
- /* TODO: Generalize this so any type can be used. */
- CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
- while (SI.ValBits >= CHAR_BITS) {
- DefineBitFieldData (&SI);
- }
- /* Avoid consuming the comma if any */
- goto NextMember;
- }
-
- /* Skip comma this round */
- if (SkipComma) {
- NextToken ();
- SkipComma = 0;
- }
-
- if (SymIsBitField (Sym)) {
-
- /* Parse initialization of one field. Bit-fields need a special
- ** handling.
- */
- ExprDesc ED;
- ED_Init (&ED);
- unsigned Val;
- unsigned Shift;
-
- /* Calculate the bitmask from the bit-field data */
- unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U;
-
- /* Safety ... */
- CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
- SI.Offs * CHAR_BITS + SI.ValBits);
-
- /* Read the data, check for a constant integer, do a range check */
- ED = ParseScalarInitInternal (IntPromotion (Sym->Type));
- if (!ED_IsConstAbsInt (&ED)) {
- Error ("Constant initializer expected");
- ED_MakeConstAbsInt (&ED, 1);
- }
-
- /* Truncate the initializer value to the width of the bit-field and check if we lost
- ** any useful bits.
- */
- Val = (unsigned) ED.IVal & Mask;
- if (IsSignUnsigned (Sym->Type)) {
- if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) {
- Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
- " changes value from %ld to %u",
- GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
- Sym->Type->A.B.Width, ED.IVal, Val);
- }
- } else {
- /* Sign extend back to full width of host long. */
- unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width;
- long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits);
- if (ED.IVal != RestoredVal) {
- Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer "
- "changes value from %ld to %ld",
- GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
- Sym->Type->A.B.Width, ED.IVal, RestoredVal);
- }
- }
-
- /* Add the value to the currently stored bit-field value */
- Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs;
- SI.BitVal |= (Val << Shift);
-
- /* Account for the data and output any full bytes we have. */
- SI.ValBits += Sym->Type->A.B.Width;
- /* Make sure unsigned is big enough to hold the value, 22 bits.
- ** This is 22 bits because the most we can have is 7 bits left
- ** over from the previous OutputBitField call, plus 15 bits
- ** from this field. A 16-bit bit-field will always be byte
- ** aligned, so will have padding before it.
- */
- CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
- /* TODO: Generalize this so any type can be used. */
- CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
- while (SI.ValBits >= CHAR_BITS) {
- DefineBitFieldData (&SI);
- }
-
- } else {
-
- /* Standard member. We should never have stuff from a
- ** bit-field left because an anonymous member was added
- ** for padding by ParseStructDecl.
- */
- CHECK (SI.ValBits == 0);
-
- /* Flexible array members may only be initialized if they are
- ** the last field (or part of the last struct field).
- */
- SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0);
- }
-
- /* More initializers? */
- if (CurTok.Tok != TOK_COMMA) {
- break;
- }
-
- /* Skip the comma next round */
- SkipComma = 1;
-
-NextMember:
- /* Next member. For unions, only the first one can be initialized */
- if (IsTypeUnion (T)) {
- /* Union */
- Sym = 0;
- } else {
- /* Struct */
- Sym = Sym->NextSym;
- }
- }
-
- if (HasCurly) {
- /* Consume the closing curly brace */
- ConsumeRCurly ();
- }
-
- /* If we have data from a bit-field left, output it now */
- CHECK (SI.ValBits < CHAR_BITS);
- DefineBitFieldData (&SI);
-
- /* If there are struct fields left, reserve additional storage */
- if (SI.Offs < SI.Size) {
- g_zerobytes (SI.Size - SI.Offs);
- SI.Offs = SI.Size;
- }
-
- /* Return the actual number of bytes initialized. This number may be
- ** larger than sizeof (Struct) if flexible array members are present and
- ** were initialized (possible in non ANSI mode).
- */
- return SI.Offs;
-}
-
-
-
-static unsigned ParseVoidInit (Type* T)
-/* Parse an initialization of a void variable (special cc65 extension).
-** Return the number of bytes initialized.
-*/
-{
- unsigned Size;
-
- /* Opening brace */
- ConsumeLCurly ();
-
- /* Allow an arbitrary list of values */
- Size = 0;
- do {
- ExprDesc Expr = NoCodeConstExpr (hie1);
- switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
-
- case T_SCHAR:
- case T_UCHAR:
- if (ED_IsConstAbsInt (&Expr)) {
- /* Make it byte sized */
- Expr.IVal &= 0xFF;
- }
- DefineData (&Expr);
- Size += SIZEOF_CHAR;
- break;
-
- case T_SHORT:
- case T_USHORT:
- case T_INT:
- case T_UINT:
- case T_PTR:
- case T_ARRAY:
- if (ED_IsConstAbsInt (&Expr)) {
- /* Make it word sized */
- Expr.IVal &= 0xFFFF;
- }
- DefineData (&Expr);
- Size += SIZEOF_INT;
- break;
-
- case T_LONG:
- case T_ULONG:
- if (ED_IsConstAbsInt (&Expr)) {
- /* Make it dword sized */
- Expr.IVal &= 0xFFFFFFFF;
- }
- DefineData (&Expr);
- Size += SIZEOF_LONG;
- break;
-
- default:
- Error ("Illegal type in initialization");
- break;
-
- }
-
- if (CurTok.Tok != TOK_COMMA) {
- break;
- }
- NextToken ();
-
- } while (CurTok.Tok != TOK_RCURLY);
-
- /* Closing brace */
- ConsumeRCurly ();
-
- /* Number of bytes determined by initializer */
- if (T->A.U != 0 && T->A.U != Size) {
- Error ("'void' array initialized with elements of variant sizes");
- } else {
- T->A.U = Size;
- }
-
- /* Return the number of bytes initialized */
- return Size;
-}
-
-
-
-static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers)
-/* Parse initialization of variables. Return the number of data bytes. */
-{
- switch (GetUnderlyingTypeCode (T)) {
-
- case T_SCHAR:
- case T_UCHAR:
- case T_SHORT:
- case T_USHORT:
- case T_INT:
- case T_UINT:
- case T_LONG:
- case T_ULONG:
- case T_FLOAT:
- case T_DOUBLE:
- return ParseScalarInit (T);
-
- case T_PTR:
- return ParsePointerInit (T);
-
- case T_ARRAY:
- return ParseArrayInit (T, Braces, AllowFlexibleMembers);
-
- case T_STRUCT:
- case T_UNION:
- return ParseStructInit (T, Braces, AllowFlexibleMembers);
-
- case T_ENUM:
- /* Incomplete enum type must have already raised errors.
- ** Just proceed to consume the value.
- */
- return ParseScalarInit (T);
-
- case T_VOID:
- if (IS_Get (&Standard) == STD_CC65) {
- /* Special cc65 extension in non-ANSI mode */
- return ParseVoidInit (T);
- }
- /* FALLTHROUGH */
-
- default:
- Error ("Illegal type");
- return SIZEOF_CHAR;
-
- }
-}
-
-
-
-unsigned ParseInit (Type* T)
-/* Parse initialization of variables. Return the number of data bytes. */
-{
- /* Current curly braces layers */
- int Braces = 0;
-
- /* Parse the initialization. Flexible array members can only be initialized
- ** in cc65 mode.
- */
- unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65);
-
- /* The initialization may not generate code on global level, because code
- ** outside function scope will never get executed.
- */
- if (HaveGlobalCode ()) {
- Error ("Non constant initializers");
- RemoveGlobalCode ();
- }
-
- /* Return the size needed for the initialization */
- return Size;
-}
diff --git a/src/cc65/declare.h b/src/cc65/declare.h
index 3293a0dcb..2b8b36f1c 100644
--- a/src/cc65/declare.h
+++ b/src/cc65/declare.h
@@ -114,11 +114,6 @@ void CheckEmptyDecl (const DeclSpec* D);
** warning if not.
*/
-unsigned ParseInit (Type* T);
-/* Parse initialization of variables. Return the number of initialized data
-** bytes.
-*/
-
/* End of declare.h */
diff --git a/src/cc65/expr.c b/src/cc65/expr.c
index 7343702ea..0275e61a3 100644
--- a/src/cc65/expr.c
+++ b/src/cc65/expr.c
@@ -25,6 +25,7 @@
#include "funcdesc.h"
#include "function.h"
#include "global.h"
+#include "initdata.h"
#include "litpool.h"
#include "loadexpr.h"
#include "macrotab.h"
@@ -398,7 +399,7 @@ static void DoInc (ExprDesc* Expr, unsigned KeepResult)
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
- if (IsTypeBitField (Expr->Type)) {
+ if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, Val, KeepResult);
return;
}
@@ -485,7 +486,7 @@ static void DoDec (ExprDesc* Expr, unsigned KeepResult)
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
- if (IsTypeBitField (Expr->Type)) {
+ if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, -Val, KeepResult);
return;
}
diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c
new file mode 100644
index 000000000..99dacdca9
--- /dev/null
+++ b/src/cc65/initdata.c
@@ -0,0 +1,807 @@
+/*****************************************************************************/
+/* */
+/* initdata.c */
+/* */
+/* Parse and generate initializer data */
+/* */
+/* */
+/* */
+/* (C) 1998-2015, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
+/* */
+/* */
+/* This software is provided 'as-is', without any expressed or implied */
+/* warranty. In no event will the authors be held liable for any damages */
+/* arising from the use of this software. */
+/* */
+/* Permission is granted to anyone to use this software for any purpose, */
+/* including commercial applications, and to alter it and redistribute it */
+/* freely, subject to the following restrictions: */
+/* */
+/* 1. The origin of this software must not be misrepresented; you must not */
+/* claim that you wrote the original software. If you use this software */
+/* in a product, an acknowledgment in the product documentation would be */
+/* appreciated but is not required. */
+/* 2. Altered source versions must be plainly marked as such, and must not */
+/* be misrepresented as being the original software. */
+/* 3. This notice may not be removed or altered from any source */
+/* distribution. */
+/* */
+/*****************************************************************************/
+
+
+
+#include
+#include
+#include
+#include
+
+/* common */
+#include "addrsize.h"
+#include "mmodel.h"
+#include "shift.h"
+#include "xmalloc.h"
+
+/* cc65 */
+#include "anonname.h"
+#include "codegen.h"
+#include "datatype.h"
+#include "declattr.h"
+#include "error.h"
+#include "expr.h"
+#include "exprdesc.h"
+#include "funcdesc.h"
+#include "function.h"
+#include "global.h"
+#include "litpool.h"
+#include "pragma.h"
+#include "scanner.h"
+#include "shift.h"
+#include "standard.h"
+#include "symtab.h"
+#include "wrappedcall.h"
+#include "typeconv.h"
+#include "initdata.h"
+
+
+
+/*****************************************************************************/
+/* Data */
+/*****************************************************************************/
+
+
+
+typedef struct StructInitData StructInitData;
+struct StructInitData {
+ unsigned Size; /* Size of struct */
+ unsigned Offs; /* Current offset in struct */
+ unsigned BitVal; /* Summed up bit-field value */
+ unsigned ValBits; /* Valid bits in Val */
+};
+
+
+
+/*****************************************************************************/
+/* Forwards */
+/*****************************************************************************/
+
+
+
+static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers);
+/* Parse initialization of variables. Return the number of data bytes. */
+
+
+
+/*****************************************************************************/
+/* code */
+/*****************************************************************************/
+
+
+
+static void SkipInitializer (int BracesExpected)
+/* Skip the remainder of an initializer in case of errors. Try to be somewhat
+** smart so we don't have too many following errors.
+*/
+{
+ while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) {
+ switch (CurTok.Tok) {
+ case TOK_RCURLY: --BracesExpected; break;
+ case TOK_LCURLY: ++BracesExpected; break;
+ default: break;
+ }
+ if (BracesExpected >= 0) {
+ NextToken ();
+ }
+ }
+}
+
+
+
+static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
+/* Accept any number of opening curly braces around an initialization, skip
+** them and return the number. If the number of curly braces is less than
+** BracesNeeded, issue a warning.
+*/
+{
+ unsigned BraceCount = 0;
+ while (CurTok.Tok == TOK_LCURLY) {
+ ++BraceCount;
+ NextToken ();
+ }
+ if (BraceCount < BracesNeeded) {
+ Error ("'{' expected");
+ }
+ return BraceCount;
+}
+
+
+
+static void ClosingCurlyBraces (unsigned BracesExpected)
+/* Accept and skip the given number of closing curly braces together with
+** an optional comma. Output an error messages, if the input does not contain
+** the expected number of braces.
+*/
+{
+ while (BracesExpected) {
+ /* TODO: Skip all excess initializers until next closing curly brace */
+ if (CurTok.Tok == TOK_RCURLY) {
+ NextToken ();
+ } else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
+ NextToken ();
+ NextToken ();
+ } else {
+ Error ("'}' expected");
+ return;
+ }
+ --BracesExpected;
+ }
+}
+
+
+
+static void DefineData (ExprDesc* Expr)
+/* Output a data definition for the given expression */
+{
+ switch (ED_GetLoc (Expr)) {
+
+ case E_LOC_NONE:
+ /* Immediate numeric value with no storage */
+ g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
+ break;
+
+ case E_LOC_ABS:
+ /* Absolute numeric address */
+ g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
+ break;
+
+ case E_LOC_GLOBAL:
+ /* Global variable */
+ g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal);
+ break;
+
+ case E_LOC_STATIC:
+ /* Static variable */
+ g_defdata (CF_STATIC, Expr->Name, Expr->IVal);
+ break;
+
+ case E_LOC_LITERAL:
+ /* Literal in the literal pool */
+ g_defdata (CF_LITERAL, Expr->Name, Expr->IVal);
+ break;
+
+ case E_LOC_REGISTER:
+ /* Register variable. Taking the address is usually not
+ ** allowed.
+ */
+ if (IS_Get (&AllowRegVarAddr) == 0) {
+ Error ("Cannot take the address of a register variable");
+ }
+ g_defdata (CF_REGVAR, Expr->Name, Expr->IVal);
+ break;
+
+ case E_LOC_CODE:
+ /* Code label location */
+ g_defdata (CF_CODE, Expr->Name, Expr->IVal);
+ break;
+
+ case E_LOC_STACK:
+ case E_LOC_PRIMARY:
+ case E_LOC_EXPR:
+ Error ("Non constant initializer");
+ break;
+
+ default:
+ Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr));
+ }
+}
+
+
+
+static void DefineBitFieldData (StructInitData* SI)
+/* Output bit field data */
+{
+ /* Ignore if we have no data */
+ if (SI->ValBits > 0) {
+
+ /* Output the data */
+ g_defdata (CF_CHAR | CF_UNSIGNED | CF_CONST, SI->BitVal, 0);
+
+ /* Update the data from SI and account for the size */
+ if (SI->ValBits >= CHAR_BITS) {
+ SI->BitVal >>= CHAR_BITS;
+ SI->ValBits -= CHAR_BITS;
+ } else {
+ SI->BitVal = 0;
+ SI->ValBits = 0;
+ }
+ SI->Offs += SIZEOF_CHAR;
+ }
+}
+
+
+
+static void DefineStrData (Literal* Lit, unsigned Count)
+{
+ /* Translate into target charset */
+ TranslateLiteral (Lit);
+
+ /* Output the data */
+ g_defbytes (GetLiteralStr (Lit), Count);
+}
+
+
+
+static ExprDesc ParseScalarInitInternal (const Type* T)
+/* Parse initializaton for scalar data types. This function will not output the
+** data but return it in ED.
+*/
+{
+ /* Optional opening brace */
+ unsigned BraceCount = OpeningCurlyBraces (0);
+
+ /* We warn if an initializer for a scalar contains braces, because this is
+ ** quite unusual and often a sign for some problem in the input.
+ */
+ if (BraceCount > 0) {
+ Warning ("Braces around scalar initializer");
+ }
+
+ /* Get the expression and convert it to the target type */
+ ExprDesc ED = NoCodeConstExpr (hie1);
+ TypeConversion (&ED, T);
+
+ /* Close eventually opening braces */
+ ClosingCurlyBraces (BraceCount);
+
+ return ED;
+}
+
+
+
+static unsigned ParseScalarInit (const Type* T)
+/* Parse initializaton for scalar data types. Return the number of data bytes. */
+{
+ /* Parse initialization */
+ ExprDesc ED = ParseScalarInitInternal (T);
+
+ /* Output the data */
+ DefineData (&ED);
+
+ /* Do this anyways for safety */
+ DoDeferred (SQP_KEEP_NONE, &ED);
+
+ /* Done */
+ return SizeOf (T);
+}
+
+
+
+static unsigned ParsePointerInit (const Type* T)
+/* Parse initializaton for pointer data types. Return the number of data bytes. */
+{
+ /* Optional opening brace */
+ unsigned BraceCount = OpeningCurlyBraces (0);
+
+ /* Expression */
+ ExprDesc ED = NoCodeConstExpr (hie1);
+ TypeConversion (&ED, T);
+
+ /* Output the data */
+ DefineData (&ED);
+
+ /* Do this anyways for safety */
+ DoDeferred (SQP_KEEP_NONE, &ED);
+
+ /* Close eventually opening braces */
+ ClosingCurlyBraces (BraceCount);
+
+ /* Done */
+ return SIZEOF_PTR;
+}
+
+
+
+static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
+/* Parse initializaton for arrays. Return the number of data bytes. */
+{
+ int Count;
+ int HasCurly = 0;
+
+ /* Get the array data */
+ Type* ElementType = IndirectModifiable (T);
+ unsigned ElementSize = SizeOf (ElementType);
+ long ElementCount = GetElementCount (T);
+
+ /* Special handling for a character array initialized by a literal */
+ if (IsClassChar (ElementType) &&
+ (CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST ||
+ (CurTok.Tok == TOK_LCURLY &&
+ (NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
+
+ /* Char array initialized by string constant */
+ int NeedParen;
+
+ /* If we initializer is enclosed in brackets, remember this fact and
+ ** skip the opening bracket.
+ */
+ NeedParen = (CurTok.Tok == TOK_LCURLY);
+ if (NeedParen) {
+ NextToken ();
+ }
+
+ /* If the array is one too small for the string literal, omit the
+ ** trailing zero.
+ */
+ Count = GetLiteralSize (CurTok.SVal);
+ if (ElementCount != UNSPECIFIED &&
+ ElementCount != FLEXIBLE &&
+ Count == ElementCount + 1) {
+ /* Omit the trailing zero */
+ --Count;
+ }
+
+ /* Output the data */
+ DefineStrData (CurTok.SVal, Count);
+
+ /* Skip the string */
+ NextToken ();
+
+ /* If the initializer was enclosed in curly braces, we need a closing
+ ** one.
+ */
+ if (NeedParen) {
+ ConsumeRCurly ();
+ }
+
+ } else {
+
+ /* Arrays can be initialized without a pair of curly braces */
+ if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
+ /* Consume the opening curly brace */
+ HasCurly = ConsumeLCurly ();
+ *Braces += HasCurly;
+ }
+
+ /* Initialize the array members */
+ Count = 0;
+ while (CurTok.Tok != TOK_RCURLY) {
+ /* Flexible array members may not be initialized within
+ ** an array (because the size of each element may differ
+ ** otherwise).
+ */
+ ParseInitInternal (ElementType, Braces, 0);
+ ++Count;
+ if (CurTok.Tok != TOK_COMMA)
+ break;
+ NextToken ();
+ }
+
+ if (HasCurly) {
+ /* Closing curly braces */
+ ConsumeRCurly ();
+ }
+ }
+
+ /* Size of 'void' elements are determined after initialization */
+ if (ElementSize == 0) {
+ ElementSize = SizeOf (ElementType);
+ }
+
+ if (ElementCount == UNSPECIFIED) {
+ /* Number of elements determined by initializer */
+ SetElementCount (T, Count);
+ ElementCount = Count;
+ } else if (ElementCount == FLEXIBLE) {
+ if (AllowFlexibleMembers) {
+ /* In non ANSI mode, allow initialization of flexible array
+ ** members.
+ */
+ ElementCount = Count;
+ } else {
+ /* Forbid */
+ Error ("Initializing flexible array member is forbidden");
+ ElementCount = Count;
+ }
+ } else if (Count < ElementCount) {
+ g_zerobytes ((ElementCount - Count) * ElementSize);
+ } else if (Count > ElementCount && HasCurly) {
+ Error ("Excess elements in array initializer");
+ }
+ return ElementCount * ElementSize;
+}
+
+
+
+static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
+/* Parse initialization of a struct or union. Return the number of data bytes. */
+{
+ SymEntry* Sym;
+ SymTable* Tab;
+ StructInitData SI;
+ int HasCurly = 0;
+ int SkipComma = 0;
+
+
+ /* Fields can be initialized without a pair of curly braces */
+ if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
+ /* Consume the opening curly brace */
+ HasCurly = ConsumeLCurly ();
+ *Braces += HasCurly;
+ }
+
+ /* Get a pointer to the struct entry from the type */
+ Sym = GetESUSymEntry (T);
+
+ /* Get the size of the struct from the symbol table entry */
+ SI.Size = Sym->V.S.Size;
+
+ /* Check if this struct definition has a field table. If it doesn't, it
+ ** is an incomplete definition.
+ */
+ Tab = Sym->V.S.SymTab;
+ if (Tab == 0) {
+ Error ("Cannot initialize variables with incomplete type");
+ /* Try error recovery */
+ SkipInitializer (HasCurly);
+ /* Nothing initialized */
+ return 0;
+ }
+
+ /* Get a pointer to the list of symbols */
+ Sym = Tab->SymHead;
+
+ /* Initialize fields */
+ SI.Offs = 0;
+ SI.BitVal = 0;
+ SI.ValBits = 0;
+ while (CurTok.Tok != TOK_RCURLY) {
+
+ /* Check for excess elements */
+ if (Sym == 0) {
+ /* Is there just one trailing comma before a closing curly? */
+ if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
+ /* Skip comma and exit scope */
+ NextToken ();
+ break;
+ }
+
+ if (HasCurly) {
+ Error ("Excess elements in %s initializer", GetBasicTypeName (T));
+ SkipInitializer (HasCurly);
+ }
+ return SI.Offs;
+ }
+
+ /* Check for special members that don't consume the initializer */
+ if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) {
+ /* Just skip */
+ goto NextMember;
+ }
+
+ /* This may be an anonymous bit-field, in which case it doesn't
+ ** have an initializer.
+ */
+ if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) {
+ /* Account for the data and output it if we have at least a full
+ ** byte. We may have more if there was storage unit overlap, for
+ ** example two consecutive 7 bit fields. Those would be packed
+ ** into 2 bytes.
+ */
+ SI.ValBits += Sym->Type->A.B.Width;
+ CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
+ /* TODO: Generalize this so any type can be used. */
+ CHECK (SI.ValBits <= LONG_BITS);
+ while (SI.ValBits >= CHAR_BITS) {
+ DefineBitFieldData (&SI);
+ }
+ /* Avoid consuming the comma if any */
+ goto NextMember;
+ }
+
+ /* Skip comma this round */
+ if (SkipComma) {
+ NextToken ();
+ SkipComma = 0;
+ }
+
+ if (SymIsBitField (Sym)) {
+
+ /* Parse initialization of one field. Bit-fields need a special
+ ** handling.
+ */
+ ExprDesc Field;
+ ED_Init (&Field);
+ unsigned long Val;
+ unsigned Shift;
+
+ /* Calculate the bitmask from the bit-field data */
+ unsigned long Mask = shl_l (1UL, Sym->Type->A.B.Width) - 1UL;
+
+ /* Safety ... */
+ CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
+ SI.Offs * CHAR_BITS + SI.ValBits);
+
+ /* Read the data, check for a constant integer, do a range check */
+ Field = ParseScalarInitInternal (IntPromotion (Sym->Type));
+ if (!ED_IsConstAbsInt (&Field)) {
+ Error ("Constant initializer expected");
+ ED_MakeConstAbsInt (&Field, 1);
+ }
+
+ /* Truncate the initializer value to the width of the bit-field and check if we lost
+ ** any useful bits.
+ */
+ Val = (unsigned long) Field.IVal & Mask;
+ if (IsSignUnsigned (Sym->Type)) {
+ if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) {
+ Warning (IsSignUnsigned (Field.Type) ?
+ "Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
+ " changes value from %lu to %lu" :
+ "Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
+ " changes value from %ld to %lu",
+ GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
+ Sym->Type->A.B.Width, Field.IVal, Val);
+ }
+ } else {
+ /* Sign extend back to full width of host long. */
+ unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width;
+ long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits);
+ if (Field.IVal != RestoredVal) {
+ Warning (IsSignUnsigned (Field.Type) ?
+ "Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
+ " changes value from %lu to %ld" :
+ "Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
+ " changes value from %ld to %ld",
+ GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
+ Sym->Type->A.B.Width, Field.IVal, RestoredVal);
+ }
+ }
+
+ /* Add the value to the currently stored bit-field value */
+ Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs;
+ SI.BitVal |= (Val << Shift);
+
+ /* Account for the data and output any full bytes we have. */
+ SI.ValBits += Sym->Type->A.B.Width;
+ /* Make sure unsigned is big enough to hold the value, 32 bits.
+ ** This cannot be more than 32 bits because a 16-bit or 32-bit
+ ** bit-field will always be byte-aligned with padding before it
+ ** if there are bits from prior fields that haven't been output
+ ** yet.
+ */
+ CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
+ /* TODO: Generalize this so any type can be used. */
+ CHECK (SI.ValBits <= LONG_BITS);
+ while (SI.ValBits >= CHAR_BITS) {
+ DefineBitFieldData (&SI);
+ }
+
+ } else {
+
+ /* Standard member. We should never have stuff from a
+ ** bit-field left because an anonymous member was added
+ ** for padding by ParseStructDecl.
+ */
+ CHECK (SI.ValBits == 0);
+
+ /* Flexible array members may only be initialized if they are
+ ** the last field (or part of the last struct field).
+ */
+ SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0);
+ }
+
+ /* More initializers? */
+ if (CurTok.Tok != TOK_COMMA) {
+ break;
+ }
+
+ /* Skip the comma next round */
+ SkipComma = 1;
+
+NextMember:
+ /* Next member. For unions, only the first one can be initialized */
+ if (IsTypeUnion (T)) {
+ /* Union */
+ Sym = 0;
+ } else {
+ /* Struct */
+ Sym = Sym->NextSym;
+ }
+ }
+
+ if (HasCurly) {
+ /* Consume the closing curly brace */
+ ConsumeRCurly ();
+ }
+
+ /* If we have data from a bit-field left, output it now */
+ CHECK (SI.ValBits < CHAR_BITS);
+ DefineBitFieldData (&SI);
+
+ /* If there are struct fields left, reserve additional storage */
+ if (SI.Offs < SI.Size) {
+ g_zerobytes (SI.Size - SI.Offs);
+ SI.Offs = SI.Size;
+ }
+
+ /* Return the actual number of bytes initialized. This number may be
+ ** larger than sizeof (Struct) if flexible array members are present and
+ ** were initialized (possible in non ANSI mode).
+ */
+ return SI.Offs;
+}
+
+
+
+static unsigned ParseVoidInit (Type* T)
+/* Parse an initialization of a void variable (special cc65 extension).
+** Return the number of bytes initialized.
+*/
+{
+ unsigned Size;
+
+ /* Opening brace */
+ ConsumeLCurly ();
+
+ /* Allow an arbitrary list of values */
+ Size = 0;
+ do {
+ ExprDesc Expr = NoCodeConstExpr (hie1);
+ switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
+
+ case T_SCHAR:
+ case T_UCHAR:
+ if (ED_IsConstAbsInt (&Expr)) {
+ /* Make it byte sized */
+ Expr.IVal &= 0xFF;
+ }
+ DefineData (&Expr);
+ Size += SIZEOF_CHAR;
+ break;
+
+ case T_SHORT:
+ case T_USHORT:
+ case T_INT:
+ case T_UINT:
+ case T_PTR:
+ case T_ARRAY:
+ if (ED_IsConstAbsInt (&Expr)) {
+ /* Make it word sized */
+ Expr.IVal &= 0xFFFF;
+ }
+ DefineData (&Expr);
+ Size += SIZEOF_INT;
+ break;
+
+ case T_LONG:
+ case T_ULONG:
+ if (ED_IsConstAbsInt (&Expr)) {
+ /* Make it dword sized */
+ Expr.IVal &= 0xFFFFFFFF;
+ }
+ DefineData (&Expr);
+ Size += SIZEOF_LONG;
+ break;
+
+ default:
+ Error ("Illegal type in initialization");
+ break;
+
+ }
+
+ if (CurTok.Tok != TOK_COMMA) {
+ break;
+ }
+ NextToken ();
+
+ } while (CurTok.Tok != TOK_RCURLY);
+
+ /* Closing brace */
+ ConsumeRCurly ();
+
+ /* Number of bytes determined by initializer */
+ if (T->A.U != 0 && T->A.U != Size) {
+ Error ("'void' array initialized with elements of variant sizes");
+ } else {
+ T->A.U = Size;
+ }
+
+ /* Return the number of bytes initialized */
+ return Size;
+}
+
+
+
+static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers)
+/* Parse initialization of variables. Return the number of data bytes. */
+{
+ switch (GetUnderlyingTypeCode (T)) {
+
+ case T_SCHAR:
+ case T_UCHAR:
+ case T_SHORT:
+ case T_USHORT:
+ case T_INT:
+ case T_UINT:
+ case T_LONG:
+ case T_ULONG:
+ case T_FLOAT:
+ case T_DOUBLE:
+ return ParseScalarInit (T);
+
+ case T_PTR:
+ return ParsePointerInit (T);
+
+ case T_ARRAY:
+ return ParseArrayInit (T, Braces, AllowFlexibleMembers);
+
+ case T_STRUCT:
+ case T_UNION:
+ return ParseStructInit (T, Braces, AllowFlexibleMembers);
+
+ case T_ENUM:
+ /* Incomplete enum type must have already raised errors.
+ ** Just proceed to consume the value.
+ */
+ return ParseScalarInit (T);
+
+ case T_VOID:
+ if (IS_Get (&Standard) == STD_CC65) {
+ /* Special cc65 extension in non-ANSI mode */
+ return ParseVoidInit (T);
+ }
+ /* FALLTHROUGH */
+
+ default:
+ Error ("Illegal type");
+ return SIZEOF_CHAR;
+
+ }
+}
+
+
+
+unsigned ParseInit (Type* T)
+/* Parse initialization of variables. Return the number of data bytes. */
+{
+ /* Current curly braces layers */
+ int Braces = 0;
+
+ /* Parse the initialization. Flexible array members can only be initialized
+ ** in cc65 mode.
+ */
+ unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65);
+
+ /* The initialization may not generate code on global level, because code
+ ** outside function scope will never get executed.
+ */
+ if (HaveGlobalCode ()) {
+ Error ("Non constant initializers");
+ RemoveGlobalCode ();
+ }
+
+ /* Return the size needed for the initialization */
+ return Size;
+}
diff --git a/src/cc65/initdata.h b/src/cc65/initdata.h
new file mode 100644
index 000000000..6fa3f20b3
--- /dev/null
+++ b/src/cc65/initdata.h
@@ -0,0 +1,61 @@
+/*****************************************************************************/
+/* */
+/* initdata.h */
+/* */
+/* Parse and generate initializer data */
+/* */
+/* */
+/* */
+/* (C) 1998-2009, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
+/* */
+/* */
+/* This software is provided 'as-is', without any expressed or implied */
+/* warranty. In no event will the authors be held liable for any damages */
+/* arising from the use of this software. */
+/* */
+/* Permission is granted to anyone to use this software for any purpose, */
+/* including commercial applications, and to alter it and redistribute it */
+/* freely, subject to the following restrictions: */
+/* */
+/* 1. The origin of this software must not be misrepresented; you must not */
+/* claim that you wrote the original software. If you use this software */
+/* in a product, an acknowledgment in the product documentation would be */
+/* appreciated but is not required. */
+/* 2. Altered source versions must be plainly marked as such, and must not */
+/* be misrepresented as being the original software. */
+/* 3. This notice may not be removed or altered from any source */
+/* distribution. */
+/* */
+/*****************************************************************************/
+
+
+
+#ifndef INITDATA_H
+#define INITDATA_H
+
+
+
+/* cc65 */
+#include "datatype.h"
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+unsigned ParseInit (Type* T);
+/* Parse initialization of variables. Return the number of initialized data
+** bytes.
+*/
+
+
+
+/* End of initdata.h */
+
+#endif
diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c
index a742087b7..4b7f8e279 100644
--- a/src/cc65/loadexpr.c
+++ b/src/cc65/loadexpr.c
@@ -110,6 +110,8 @@ static void LoadAddress (unsigned Flags, ExprDesc* Expr)
void LoadExpr (unsigned Flags, struct ExprDesc* Expr)
/* Load an expression into the primary register if it is not already there.
+** If Flags contains any CF_TYPEMASK bits, it then overrides the codegen type
+** info that would be otherwise taken from the expression type.
** Note: This function can't modify the content in Expr since there are many
** instances of the "GetCodePos + LoadExpr (maybe indirectly) + RemoveCode"
** code pattern here and there which assumes that Expr should be unchanged,
@@ -125,32 +127,24 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr)
int AdjustBitField = 0;
unsigned BitFieldFullWidthFlags = 0;
if ((Flags & CF_TYPEMASK) == 0) {
- if (IsTypeBitField (Expr->Type)) {
- unsigned EndBit = Expr->Type->A.B.Offs + Expr->Type->A.B.Width;
- AdjustBitField = Expr->Type->A.B.Offs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS);
-
- /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are
- ** supported.
- */
- Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT;
- if (IsSignUnsigned (Expr->Type)) {
- Flags |= CF_UNSIGNED;
- }
+ if (IsTypeFragBitField (Expr->Type)) {
+ /* We need to adjust the bits in this case. */
+ AdjustBitField = 1;
/* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */
- BitFieldFullWidthFlags = Flags;
+ BitFieldFullWidthFlags = Flags | TypeOf (Expr->Type);
+
+ /* Flags we need operate on the whole chunk containing the bit-field. */
+ Flags |= TypeOf (GetBitFieldChunkType (Expr->Type));
/* If we're adjusting, then only load a char (not an int) and do only char ops;
- ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the
- ** type is not CF_CHAR.
+ ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if
+ ** the type is not CF_CHAR;
+ ** If adjusting, then we're sign extending manually, so do everything unsigned
+ ** to make shifts faster.
*/
- if (AdjustBitField) {
- /* If adjusting, then we're sign extending manually, so do everything unsigned
- ** to make shifts faster.
- */
- Flags |= CF_UNSIGNED | CF_FORCECHAR;
- BitFieldFullWidthFlags |= CF_UNSIGNED;
- }
+ Flags |= CF_UNSIGNED | CF_FORCECHAR;
+ BitFieldFullWidthFlags |= CF_UNSIGNED;
} else {
/* If Expr is an incomplete ESY type, bail out */
if (IsIncompleteESUType (Expr->Type)) {
diff --git a/src/cc65/loadexpr.h b/src/cc65/loadexpr.h
index c9e70e1f6..90862e33a 100644
--- a/src/cc65/loadexpr.h
+++ b/src/cc65/loadexpr.h
@@ -55,7 +55,10 @@ struct ExprDesc;
void LoadExpr (unsigned Flags, struct ExprDesc* Expr);
-/* Load an expression into the primary register if it is not already there. */
+/* Load an expression into the primary register if it is not already there.
+** If Flags contains any CF_TYPEMASK bits, it then overrides the codegen type
+** info that would be otherwise taken from the expression type.
+*/
diff --git a/src/cc65/locals.c b/src/cc65/locals.c
index d3902f329..297994455 100644
--- a/src/cc65/locals.c
+++ b/src/cc65/locals.c
@@ -46,6 +46,7 @@
#include "expr.h"
#include "function.h"
#include "global.h"
+#include "initdata.h"
#include "loadexpr.h"
#include "locals.h"
#include "stackptr.h"
diff --git a/test/val/enum-bitfield.c b/test/val/bitfield-enum.c
similarity index 61%
rename from test/val/enum-bitfield.c
rename to test/val/bitfield-enum.c
index 5669978c9..ce74b062e 100644
--- a/test/val/enum-bitfield.c
+++ b/test/val/bitfield-enum.c
@@ -1,5 +1,5 @@
/*
- Copyright 2020 The cc65 Authors
+ Copyright 2020-2022 The cc65 Authors
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -247,7 +247,7 @@ static void test_enum_bitfield_char(void)
failures++;
}
if (e8scbf.y != 5) {
- printf ("Got e8scbf.y = %d, expected 10.\n", e8scbf.y);
+ printf ("Got e8scbf.y = %d, expected 5.\n", e8scbf.y);
failures++;
}
if (e8scbf.z != 100) {
@@ -273,12 +273,170 @@ static void test_enum_bitfield_char(void)
}
}
+/* Enum with underlying type unsigned long. */
+enum e20ul {
+ E20UL_10 = 10,
+ E20UL_1000 = 1000,
+ E20UL_1000000000 = 1000000000L,
+};
+
+static struct enum_bitfield_ulong {
+ enum e20ul x : 4;
+ enum e20ul y : 16;
+ enum e20ul z : CHAR_BIT * sizeof (enum e20ul);
+} e20ulbf = {E20UL_10, E20UL_1000, E20UL_1000000000};
+
+static void test_enum_bitfield_ulong(void)
+{
+ if (sizeof (struct enum_bitfield_ulong) != 7) {
+ printf ("Got sizeof(struct enum_bitfield_ulong) = %zu, expected 7.\n",
+ sizeof(struct enum_bitfield_ulong));
+ failures++;
+ }
+
+ if (e20ulbf.x != 10) {
+ printf ("Got e20ulbf.x = %u, expected 10.\n", e20ulbf.x);
+ failures++;
+ }
+ if (e20ulbf.y != 1000) {
+ printf ("Got e20ulbf.y = %u, expected 1000.\n", e20ulbf.y);
+ failures++;
+ }
+ if (e20ulbf.z != 1000000000L) {
+ printf ("Got e20ulbf.z = %ul, expected 1000000000.\n", e20ulbf.z);
+ failures++;
+ }
+
+ e20ulbf.x = 8;
+ e20ulbf.y = -1; /* Will store 65535. */
+ e20ulbf.z = 1048575L;
+
+ if (e20ulbf.x != 8) {
+ printf ("Got e20ulbf.x = %ld, expected 8.\n", (long)e20ulbf.x);
+ failures++;
+ }
+
+ /* Check signedness, should be signed. */
+ {
+ if (e20ulbf.x - 9 >= 0) {
+ printf ("Got non-negative e20ulbf.x - 9 = %lu, expected negative.\n", (unsigned long)(e20ulbf.x - 9));
+ failures++;
+ }
+ }
+
+ if (e20ulbf.y != 65535L) {
+ printf ("Got e20ulbf.y = %ld, expected 65535.\n", (long)e20ulbf.y);
+ failures++;
+ }
+
+ /* Check signedness, should be signed. */
+ {
+ if (e20ulbf.y - 65536L >= 0) {
+ printf ("Got non-negative e20ulbf.y - 65536L = %lu, expected negative.\n", (unsigned long)(e20ulbf.y - 65536L));
+ failures++;
+ }
+ }
+
+ if (e20ulbf.z != 1048575L) {
+ printf ("Got e20ulbf.z = %lu, expected 1048575.\n", (unsigned long)e20ulbf.z);
+ failures++;
+ }
+
+ /* Check signedness, should be unsigned. */
+ {
+ if (e20ulbf.z - 1048576L < 0) {
+ printf ("Got negative e20ulbf.z - 1048576 = %ld, expected positive.\n", (long)(e20ulbf.z - 1048576L));
+ failures++;
+ }
+ }
+}
+
+/* Enum with underlying type signed long. */
+enum e20sl {
+ E20SL_M1 = -1,
+ E20SL_1000 = 1000,
+ E20SL_1000000000 = 1000000000L,
+};
+
+static struct enum_bitfield_long {
+ enum e20sl x : 2;
+ enum e20sl y : 16;
+ enum e20sl z : CHAR_BIT * sizeof (enum e20sl);
+} e20slbf = {E20SL_M1, E20SL_1000, E20SL_1000000000};
+
+static void test_enum_bitfield_long(void)
+{
+ if (sizeof (struct enum_bitfield_long) != 7) {
+ printf ("Got sizeof(struct enum_bitfield_long) = %zu, expected 8.\n",
+ sizeof(struct enum_bitfield_long));
+ failures++;
+ }
+
+ if (e20slbf.x != -1) {
+ printf ("Got e20slbf.x = %ld, expected -1.\n", (long)e20slbf.x);
+ failures++;
+ }
+ if (e20slbf.y != 1000) {
+ printf ("Got e20slbf.y = %ld, expected 1000.\n", (long)e20slbf.y);
+ failures++;
+ }
+ if (e20slbf.z != 1000000000L) {
+ printf ("Got e20slbf.z = %ld, expected 1000000000.\n", (long)e20slbf.z);
+ failures++;
+ }
+
+ e20slbf.x = 1;
+ e20slbf.y = 257;
+ e20slbf.z = 1048575L;
+
+ if (e20slbf.x != 1) {
+ printf ("Got e20slbf.x = %d, expected 1.\n", e20slbf.x);
+ failures++;
+ }
+
+ /* Check signedness, should be signed. */
+ {
+ if (e20slbf.x - 2 >= 0) {
+ printf ("Got non-negative e20slbf.x - 2 = %lu, expected negative.\n", (unsigned long)(e20slbf.x - 2));
+ failures++;
+ }
+ }
+
+ if (e20slbf.y != 257) {
+ printf ("Got e20slbf.y = %ld, expected 257.\n", (long)e20slbf.y);
+ failures++;
+ }
+
+ /* Check signedness, should be signed. */
+ {
+ if (e20slbf.y - 258 >= 0) {
+ printf ("Got non-negative e20slbf.y - 258 = %lu, expected negative.\n", (unsigned long)(e20slbf.y - 258));
+ failures++;
+ }
+ }
+
+ if (e20slbf.z != 1048575L) {
+ printf ("Got e20slbf.z = %ld, expected 1048575.\n", (long)e20slbf.z);
+ failures++;
+ }
+
+ /* Check signedness, should be signed. */
+ {
+ if (e20slbf.z - 1048576L >= 0) {
+ printf ("Got non-negative e20slbf.z - 1048576L = %ld, expected negative.\n", (long)(e20slbf.z - 1048576L));
+ failures++;
+ }
+ }
+}
+
int main(void)
{
test_enum_bitfield_uint();
test_enum_bitfield_int();
test_enum_bitfield_uchar();
test_enum_bitfield_char();
+ test_enum_bitfield_ulong();
+ test_enum_bitfield_long();
printf("failures: %u\n", failures);
return failures;
}
diff --git a/test/val/bitfield-packing-char.c b/test/val/bitfield-packing-char.c
new file mode 100644
index 000000000..18621e0eb
--- /dev/null
+++ b/test/val/bitfield-packing-char.c
@@ -0,0 +1,278 @@
+/*
+ Copyright 2020-2022 The cc65 Authors
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ Tests of char bit-field packing and typedef works with them; see issues below
+ - packing issue: https://github.com/cc65/cc65/issues/1054
+ - typedef issue: https://github.com/cc65/cc65/pull/1662
+ - char bit-field support: https://github.com/cc65/cc65/issues/1047
+*/
+
+#include
+
+static unsigned char failures = 0;
+
+typedef unsigned char field_type;
+
+static struct four_bits {
+ field_type x : 4;
+} fb = {1};
+
+static void test_four_bits(void)
+{
+ if (sizeof(struct four_bits) != 1) {
+ printf("Got sizeof(struct four_bits) = %zu, expected 1.\n",
+ sizeof(struct four_bits));
+ failures++;
+ }
+
+ if (fb.x != 1) {
+ printf("Got fb.x = %u, expected 1.\n", fb.x);
+ failures++;
+ }
+
+ fb.x = 3;
+
+ if (fb.x != 3) {
+ printf("Got fb.x = %u, expected 3.\n", fb.x);
+ failures++;
+ }
+}
+
+/*
+ Logic is somewhat diferent for bit-fields that end a struct vs
+ having additional fields.
+*/
+
+static struct four_bits_with_char {
+ field_type x : 4;
+ field_type y;
+} fbi = {1, 2};
+
+static void test_four_bits_with_char(void)
+{
+ /* The first 4-bit bit-field just takes one byte, so the size is 2. */
+ if (sizeof(struct four_bits_with_char) != 2) {
+ printf("Got sizeof(struct four_bits_with_char) = %zu, expected 2.\n",
+ sizeof(struct four_bits_with_char));
+ failures++;
+ }
+
+ if (fbi.x != 1) {
+ printf("Got fbi.x = %u, expected 1.\n", fbi.x);
+ failures++;
+ }
+
+ if (fbi.y != 2) {
+ printf("Got fbi.y = %u, expected 2.\n", fbi.y);
+ failures++;
+ }
+
+ fbi.x = 3;
+ fbi.y = 17;
+
+ if (fbi.x != 3) {
+ printf("Got fbi.x = %u, expected 3.\n", fbi.x);
+ failures++;
+ }
+
+ if (fbi.y != 17) {
+ printf("Got fbi.y = %u, expected 17.\n", fbi.y);
+ failures++;
+ }
+}
+
+static struct overlap {
+ field_type x : 6;
+ field_type y : 6;
+} o = {11, 22};
+
+/* Tests that bit-fields can share allocation units. */
+static void test_overlap(void)
+{
+ if (sizeof(struct overlap) != 2) {
+ printf("Got sizeof(struct overlap) = %zu, expected 2.\n",
+ sizeof(struct overlap));
+ failures++;
+ }
+
+ if (o.x != 11) {
+ printf("Got o.x = %u, expected 11.\n", o.x);
+ failures++;
+ }
+
+ if (o.y != 22) {
+ printf("Got o.y = %u, expected 22.\n", o.y);
+ failures++;
+ }
+
+ o.x = 33;
+ o.y = 44;
+
+ if (o.x != 33) {
+ printf("Got o.x = %u, expected 33.\n", o.x);
+ failures++;
+ }
+
+ if (o.y != 44) {
+ printf("Got o.y = %u, expected 44.\n", o.y);
+ failures++;
+ }
+}
+
+static struct overlap_with_char {
+ field_type x : 6;
+ field_type y : 6;
+ field_type z;
+} oi = {11, 22, 33};
+
+static void test_overlap_with_char(void)
+{
+ /* First two fields in 2 bytes, then another 1 byte. */
+ if (sizeof(struct overlap_with_char) != 3) {
+ printf("Got sizeof(struct overlap_with_char) = %zu, expected 3.\n",
+ sizeof(struct overlap_with_char));
+ failures++;
+ }
+
+ if (oi.x != 11) {
+ printf("Got oi.x = %u, expected 11.\n", oi.x);
+ failures++;
+ }
+
+ if (oi.y != 22) {
+ printf("Got oi.y = %u, expected 22.\n", oi.y);
+ failures++;
+ }
+
+ if (oi.z != 33) {
+ printf("Got oi.z = %u, expected 33.\n", oi.z);
+ failures++;
+ }
+
+ oi.x = 44;
+ oi.y = 55;
+ oi.z = 66;
+
+ if (oi.x != 44) {
+ printf("Got oi.x = %u, expected 44.\n", oi.x);
+ failures++;
+ }
+
+ if (oi.y != 55) {
+ printf("Got oi.y = %u, expected 55.\n", oi.y);
+ failures++;
+ }
+
+ if (oi.z != 66) {
+ printf("Got oi.z = %u, expected 66.\n", oi.z);
+ failures++;
+ }
+}
+
+static struct full_width {
+ field_type x : 8;
+ field_type y : 8;
+} fw = {255, 17};
+
+static void test_full_width(void)
+{
+ if (sizeof(struct full_width) != 2) {
+ printf("Got sizeof(struct full_width) = %zu, expected 2.\n",
+ sizeof(struct full_width));
+ failures++;
+ }
+
+ if (fw.x != 255) {
+ printf("Got fw.x = %u, expected 255.\n", fw.x);
+ failures++;
+ }
+
+ if (fw.y != 17) {
+ printf("Got fw.y = %u, expected 17.\n", fw.y);
+ failures++;
+ }
+
+ fw.x = 42;
+ fw.y = 255;
+
+ if (fw.x != 42) {
+ printf("Got fw.x = %u, expected 42.\n", fw.x);
+ failures++;
+ }
+
+ if (fw.y != 255) {
+ printf("Got fw.y = %u, expected 255.\n", fw.y);
+ failures++;
+ }
+}
+
+static struct aligned_end {
+ field_type : 2;
+ field_type x : 6;
+ /* y crosses a byte boundary, but fits in a byte when shifted. */
+ field_type : 6;
+ field_type y : 7;
+} ae = {63, 17};
+
+static void test_aligned_end(void)
+{
+ if (sizeof(struct aligned_end) != 3) {
+ printf("Got sizeof(struct aligned_end) = %zu, expected 3.\n",
+ sizeof(struct aligned_end));
+ failures++;
+ }
+
+ if (ae.x != 63) {
+ printf("Got ae.x = %u, expected 63.\n", ae.x);
+ failures++;
+ }
+
+ if (ae.y != 17) {
+ printf("Got ae.y = %u, expected 17.\n", ae.y);
+ failures++;
+ }
+
+ ae.x = 42;
+ ae.y = 127;
+
+ if (ae.x != 42) {
+ printf("Got ae.x = %u, expected 42.\n", ae.x);
+ failures++;
+ }
+
+ if (ae.y != 127) {
+ printf("Got ae.y = %u, expected 127.\n", ae.y);
+ failures++;
+ }
+
+}
+
+int main(void)
+{
+ test_four_bits();
+ test_four_bits_with_char();
+ test_overlap();
+ test_overlap_with_char();
+ test_full_width();
+ test_aligned_end();
+ printf("failures: %u\n", failures);
+ return failures;
+}
diff --git a/test/val/bitfield-packing-long.c b/test/val/bitfield-packing-long.c
new file mode 100644
index 000000000..fcc8eb7fe
--- /dev/null
+++ b/test/val/bitfield-packing-long.c
@@ -0,0 +1,315 @@
+/*
+ Copyright 2020-2022 The cc65 Authors
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ Tests of long bit-field packing and typedef works with them; see issues below
+ - packing: https://github.com/cc65/cc65/issues/1054
+ - typedef: https://github.com/cc65/cc65/pull/1662
+ - long bit-field support: https://github.com/cc65/cc65/issues/1131
+*/
+
+#include
+
+static unsigned char failures = 0;
+
+typedef unsigned long field_type;
+
+static struct four_bits {
+ field_type x : 4;
+} fb = {1};
+
+static void test_four_bits(void)
+{
+ if (sizeof(struct four_bits) != 1) {
+ printf("Got sizeof(struct four_bits) = %zu, expected 1.\n",
+ sizeof(struct four_bits));
+ failures++;
+ }
+
+ if (fb.x != 1) {
+ printf("Got fb.x = %u, expected 1.\n", fb.x);
+ failures++;
+ }
+
+ fb.x = 3;
+
+ if (fb.x != 3) {
+ printf("Got fb.x = %u, expected 3.\n", fb.x);
+ failures++;
+ }
+}
+
+/*
+ Logic is somewhat diferent for bit-fields that end a struct vs
+ having additional fields.
+*/
+
+static struct four_bits_with_long {
+ field_type x : 4;
+ field_type y;
+} fbi = {1, 2};
+
+static void test_four_bits_with_long(void)
+{
+ /* The first 4-bit bit-field just takes one byte, so the size is 5. */
+ if (sizeof(struct four_bits_with_long) != 5) {
+ printf("Got sizeof(struct four_bits_with_long) = %zu, expected 5.\n",
+ sizeof(struct four_bits_with_long));
+ failures++;
+ }
+
+ if (fbi.x != 1) {
+ printf("Got fbi.x = %u, expected 1.\n", fbi.x);
+ failures++;
+ }
+
+ if (fbi.y != 2) {
+ printf("Got fbi.y = %lu, expected 2.\n", fbi.y);
+ failures++;
+ }
+
+ fbi.x = 3;
+ fbi.y = 65537;
+
+ if (fbi.x != 3) {
+ printf("Got fbi.x = %u, expected 3.\n", fbi.x);
+ failures++;
+ }
+
+ if (fbi.y != 65537) {
+ printf("Got fbi.y = %lu, expected 65537.\n", fbi.y);
+ failures++;
+ }
+}
+
+static struct overlap {
+ field_type x : 10;
+ field_type y : 10;
+} o = {11, 22};
+
+/* Tests that bit-fields can share allocation units. */
+static void test_overlap(void)
+{
+ if (sizeof(struct overlap) != 3) {
+ printf("Got sizeof(struct overlap) = %zu, expected 3.\n",
+ sizeof(struct overlap));
+ failures++;
+ }
+
+ if (o.x != 11) {
+ printf("Got o.x = %u, expected 11.\n", o.x);
+ failures++;
+ }
+
+ if (o.y != 22) {
+ printf("Got o.y = %u, expected 22.\n", o.y);
+ failures++;
+ }
+
+ o.x = 33;
+ o.y = 44;
+
+ if (o.x != 33) {
+ printf("Got o.x = %u, expected 33.\n", o.x);
+ failures++;
+ }
+
+ if (o.y != 44) {
+ printf("Got o.y = %u, expected 44.\n", o.y);
+ failures++;
+ }
+}
+
+static struct overlap_with_long {
+ field_type x : 10;
+ field_type y : 10;
+ field_type z;
+} oi = {111, 222, 333};
+
+static void test_overlap_with_long(void)
+{
+ /* First two fields in 3 bytes, then another 4 bytes. */
+ if (sizeof(struct overlap_with_long) != 7) {
+ printf("Got sizeof(struct overlap_with_long) = %zu, expected 7.\n",
+ sizeof(struct overlap_with_long));
+ failures++;
+ }
+
+ if (oi.x != 111) {
+ printf("Got oi.x = %u, expected 111.\n", oi.x);
+ failures++;
+ }
+
+ if (oi.y != 222) {
+ printf("Got oi.y = %u, expected 222.\n", oi.y);
+ failures++;
+ }
+
+ if (oi.z != 333) {
+ printf("Got oi.z = %u, expected 333.\n", oi.z);
+ failures++;
+ }
+
+ oi.x = 444;
+ oi.y = 555;
+ oi.z = 4294967295;
+
+ if (oi.x != 444) {
+ printf("Got oi.x = %u, expected 444.\n", oi.x);
+ failures++;
+ }
+
+ if (oi.y != 555) {
+ printf("Got oi.y = %u, expected 555.\n", oi.y);
+ failures++;
+ }
+
+ if (oi.z != 4294967295) {
+ printf("Got oi.z = %lu, expected 4294967295.\n", oi.z);
+ failures++;
+ }
+}
+
+static struct full_width {
+ field_type x : 8;
+ field_type y : 16;
+ field_type z : 32;
+} fw = {255, 17, 1};
+
+static void test_full_width(void)
+{
+ if (sizeof(struct full_width) != 7) {
+ printf("Got sizeof(struct full_width) = %zu, expected 7.\n",
+ sizeof(struct full_width));
+ failures++;
+ }
+
+ if (fw.x != 255) {
+ printf("Got fw.x = %u, expected 255.\n", fw.x);
+ failures++;
+ }
+
+ if (fw.y != 17) {
+ printf("Got fw.y = %u, expected 17.\n", fw.y);
+ failures++;
+ }
+
+ if (fw.z != 1) {
+ printf("Got fw.z = %lu, expected 1.\n", fw.z);
+ failures++;
+ }
+
+ fw.x = 42;
+ fw.y = 1023;
+ fw.z = 65537;
+
+ if (fw.x != 42) {
+ printf("Got fw.x = %u, expected 42.\n", fw.x);
+ failures++;
+ }
+
+ if (fw.y != 1023) {
+ printf("Got fw.y = %u, expected 1023.\n", fw.y);
+ failures++;
+ }
+
+ if (fw.z != 65537) {
+ printf("Got fw.z = %lu, expected 65537.\n", fw.z);
+ failures++;
+ }
+}
+
+static struct aligned_end {
+ field_type : 2;
+ field_type x : 6;
+ field_type : 3;
+ field_type y : 13;
+ field_type : 14;
+ field_type z : 18;
+ /* w crosses a byte boundary, but fits in a byte when shifted. */
+ field_type : 6;
+ field_type w : 7;
+} ae = {63, 17, 1, 100};
+
+static void test_aligned_end(void)
+{
+ if (sizeof(struct aligned_end) != 9) {
+ printf("Got sizeof(struct aligned_end) = %zu, expected 9.\n",
+ sizeof(struct aligned_end));
+ failures++;
+ }
+
+ if (ae.x != 63) {
+ printf("Got ae.x = %u, expected 63.\n", ae.x);
+ failures++;
+ }
+
+ if (ae.y != 17) {
+ printf("Got ae.y = %u, expected 17.\n", ae.y);
+ failures++;
+ }
+
+ if (ae.z != 1) {
+ printf("Got ae.z = %lu, expected 1.\n", ae.z);
+ failures++;
+ }
+
+ if (ae.w != 100) {
+ printf("Got ae.w = %u, expected 100.\n", ae.w);
+ failures++;
+ }
+
+ ae.x = 42;
+ ae.y = 1023;
+ ae.z = 262143;
+ ae.w = 66;
+
+ if (ae.x != 42) {
+ printf("Got ae.x = %u, expected 42.\n", ae.x);
+ failures++;
+ }
+
+ if (ae.y != 1023) {
+ printf("Got ae.y = %u, expected 1023.\n", ae.y);
+ failures++;
+ }
+
+ if (ae.z != 262143) {
+ printf("Got ae.z = %lu, expected 262143.\n", ae.z);
+ failures++;
+ }
+
+ if (ae.w != 66) {
+ printf("Got ae.w = %u, expected 66.\n", ae.w);
+ failures++;
+ }
+}
+
+int main(void)
+{
+ test_four_bits();
+ test_four_bits_with_long();
+ test_overlap();
+ test_overlap_with_long();
+ test_full_width();
+ test_aligned_end();
+ printf("failures: %u\n", failures);
+ return failures;
+}
diff --git a/test/val/bitfield.c b/test/val/bitfield-packing.c
similarity index 93%
rename from test/val/bitfield.c
rename to test/val/bitfield-packing.c
index 1de19777a..5786d6906 100644
--- a/test/val/bitfield.c
+++ b/test/val/bitfield-packing.c
@@ -1,5 +1,5 @@
/*
- Copyright 2020 The cc65 Authors
+ Copyright 2020-2022 The cc65 Authors
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -19,7 +19,9 @@
*/
/*
- Tests of bit-field packing; see https://github.com/cc65/cc65/issues/1054
+ Tests of int bit-field packing and typedef works with them; see issues below
+ - packing issue: https://github.com/cc65/cc65/issues/1054
+ - typedef issue: https://github.com/cc65/cc65/pull/1662
*/
#include
@@ -83,15 +85,15 @@ static void test_four_bits_with_int(void)
}
fbi.x = 3;
- fbi.y = 17;
+ fbi.y = 257;
if (fbi.x != 3) {
printf("Got fbi.x = %u, expected 3.\n", fbi.x);
failures++;
}
- if (fbi.y != 17) {
- printf("Got fbi.y = %u, expected 17.\n", fbi.y);
+ if (fbi.y != 257) {
+ printf("Got fbi.y = %u, expected 257.\n", fbi.y);
failures++;
}
}
@@ -166,7 +168,7 @@ static void test_overlap_with_int(void)
oi.x = 444;
oi.y = 555;
- oi.z = 666;
+ oi.z = 65535;
if (oi.x != 444) {
printf("Got oi.x = %u, expected 444.\n", oi.x);
@@ -178,8 +180,8 @@ static void test_overlap_with_int(void)
failures++;
}
- if (oi.z != 666) {
- printf("Got oi.z = %u, expected 666.\n", oi.z);
+ if (oi.z != 65535) {
+ printf("Got oi.z = %u, expected 65535.\n", oi.z);
failures++;
}
}
diff --git a/test/val/bitfield-plain.c b/test/val/bitfield-plain.c
new file mode 100644
index 000000000..735f3dc87
--- /dev/null
+++ b/test/val/bitfield-plain.c
@@ -0,0 +1,180 @@
+/*
+ Copyright 2020-2022 The cc65 Authors
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ Tests that plain bit-fields are unsigned; see issues below
+ - unsigned integer types by default: https://github.com/cc65/cc65/issues/1095
+ - char bit-field support: https://github.com/cc65/cc65/issues/1047
+ - long bit-field support: https://github.com/cc65/cc65/issues/1131
+*/
+
+#include
+
+static unsigned char failures = 0;
+
+static struct plain_ints {
+ int x : 4;
+ int y : 10;
+} pi = {15, 700};
+
+static void test_plain_int_bitfields (void)
+{
+ if (pi.x != 15) {
+ printf ("Got pi.x = %ld, expected 15.\n", (long)pi.x);
+ failures++;
+ }
+ if (pi.y != 700) {
+ printf ("Got pi.y = %ld, expected 700.\n", (long)pi.y);
+ failures++;
+ }
+
+ pi.x = 3;
+ pi.y = 1023;
+
+ if (pi.x != 3) {
+ printf ("Got pi.x = %ld, expected 3.\n", (long)pi.x);
+ failures++;
+ }
+ if (pi.y != 1023) {
+ printf ("Got pi.y = %ld, expected 1023.\n", (long)pi.y);
+ failures++;
+ }
+}
+
+static struct plain_shorts {
+ short x : 4;
+ short y : 10;
+} ps = {15, 700};
+
+static void test_plain_short_bitfields (void)
+{
+ if (ps.x != 15) {
+ printf ("Got ps.x = %ld, expected 15.\n", (long)ps.x);
+ failures++;
+ }
+ if (ps.y != 700) {
+ printf ("Got ps.y = %ld, expected 700.\n", (long)ps.y);
+ failures++;
+ }
+
+ ps.x = 3;
+ ps.y = 1023;
+
+ if (ps.x != 3) {
+ printf ("Got ps.x = %ld, expected 3.\n", (long)ps.x);
+ failures++;
+ }
+ if (ps.y != 1023) {
+ printf ("Got ps.y = %ld, expected 1023.\n", (long)ps.y);
+ failures++;
+ }
+}
+
+static struct plain_chars {
+ char x : 4;
+} pc = {15};
+
+static void test_plain_char_bitfields (void)
+{
+ if (pc.x != 15) {
+ printf ("Got pc.x = %ld, expected 15.\n", (long)pc.x);
+ failures++;
+ }
+
+ pc.x = 3;
+
+ if (pc.x != 3) {
+ printf ("Got pc.x = %ld, expected 3.\n", (long)pc.x);
+ failures++;
+ }
+}
+
+static struct plain_longs {
+ long x : 4;
+ long y : 10;
+ long z : 18;
+} pl = {15, 700, 200000};
+
+static void test_plain_long_bitfields (void)
+{
+ if (pl.x != 15) {
+ if (pl.x < 0) {
+ printf ("Got pl.x = %ld, expected 15.\n", (long)pl.x);
+ } else {
+ printf ("Got pl.x = %lu, expected 15.\n", (unsigned long)pl.x);
+ }
+ failures++;
+ }
+ if (pl.y != 700) {
+ if (pl.y < 0) {
+ printf ("Got pl.y = %ld, expected 700.\n", (long)pl.y);
+ } else {
+ printf ("Got pl.y = %lu, expected 700.\n", (unsigned long)pl.y);
+ }
+ failures++;
+ }
+ if (pl.z != 200000) {
+ if (pl.z < 0) {
+ printf ("Got pl.z = %ld, expected 200000.\n", (long)pl.z);
+ } else {
+ printf ("Got pl.z = %lu, expected 200000.\n", (unsigned long)pl.z);
+ }
+ failures++;
+ }
+
+ pl.x = 3;
+ pl.y = 1023;
+ pl.z = 262143;
+
+ if (pl.x != 3) {
+ if (pl.x < 0) {
+ printf ("Got pl.x = %ld, expected 3.\n", (long)pl.x);
+ } else {
+ printf ("Got pl.x = %lu, expected 3.\n", (unsigned long)pl.x);
+ }
+ failures++;
+ }
+ if (pl.y != 1023) {
+ if (pl.y < 0) {
+ printf ("Got pl.y = %ld, expected 1023.\n", (long)pl.y);
+ } else {
+ printf ("Got pl.y = %lu, expected 1023.\n", (unsigned long)pl.y);
+ }
+ failures++;
+ }
+ if (pl.z != 262143) {
+ if (pl.z < 0) {
+ printf ("Got pl.z = %ld, expected 262143.\n", (long)pl.z);
+ } else {
+ printf ("Got pl.z = %lu, expected 262143.\n", (unsigned long)pl.z);
+ }
+ failures++;
+ }
+}
+
+int main (void)
+{
+ test_plain_int_bitfields ();
+ test_plain_short_bitfields ();
+ test_plain_char_bitfields ();
+ test_plain_long_bitfields ();
+ printf ("failures: %u\n", failures);
+ return failures;
+}
diff --git a/test/val/bitfield-signed.c b/test/val/bitfield-signed.c
new file mode 100644
index 000000000..68f36f92a
--- /dev/null
+++ b/test/val/bitfield-signed.c
@@ -0,0 +1,180 @@
+/*
+ Copyright 2020-2022 The cc65 Authors
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be signedly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+/*
+ Tests that signed bit-fields are indeed signed; see issues below
+ - unsigned integer types by default: https://github.com/cc65/cc65/issues/1095
+ - char bit-field support: https://github.com/cc65/cc65/issues/1047
+ - long bit-field support: https://github.com/cc65/cc65/issues/1131
+*/
+
+#include
+
+static unsigned char failures = 0;
+
+static struct signed_ints {
+ signed int x : 4;
+ signed int y : 10;
+} pi = {-8, -500};
+
+static void test_signed_int_bitfields (void)
+{
+ if (pi.x != -8) {
+ printf ("Got pi.x = %ld, expected -8.\n", (long)pi.x);
+ failures++;
+ }
+ if (pi.y != -500) {
+ printf ("Got pi.y = %ld, expected -500.\n", (long)pi.y);
+ failures++;
+ }
+
+ pi.x = -3;
+ pi.y = -512;
+
+ if (pi.x != -3) {
+ printf ("Got pi.x = %ld, expected -3.\n", (long)pi.x);
+ failures++;
+ }
+ if (pi.y != -512) {
+ printf ("Got pi.y = %ld, expected -512.\n", (long)pi.y);
+ failures++;
+ }
+}
+
+static struct signed_shorts {
+ signed short x : 4;
+ signed short y : 10;
+} ps = {-8, -500};
+
+static void test_signed_short_bitfields (void)
+{
+ if (ps.x != -8) {
+ printf ("Got ps.x = %ld, expected -8.\n", (long)ps.x);
+ failures++;
+ }
+ if (ps.y != -500) {
+ printf ("Got ps.y = %ld, expected -500.\n", (long)ps.y);
+ failures++;
+ }
+
+ ps.x = -3;
+ ps.y = -512;
+
+ if (ps.x != -3) {
+ printf ("Got ps.x = %ld, expected -3.\n", (long)ps.x);
+ failures++;
+ }
+ if (ps.y != -512) {
+ printf ("Got ps.y = %ld, expected -512.\n", (long)ps.y);
+ failures++;
+ }
+}
+
+static struct signed_chars {
+ signed char x : 4;
+} pc = {-8};
+
+static void test_signed_char_bitfields (void)
+{
+ if (pc.x != -8) {
+ printf ("Got pc.x = %ld, expected -8.\n", (long)pc.x);
+ failures++;
+ }
+
+ pc.x = -3;
+
+ if (pc.x != -3) {
+ printf ("Got pc.x = %ld, expected -3.\n", (long)pc.x);
+ failures++;
+ }
+}
+
+static struct signed_longs {
+ signed long x : 4;
+ signed long y : 10;
+ signed long z : 18;
+} pl = {-8, -500, -70000};
+
+static void test_signed_long_bitfields (void)
+{
+ if (pl.x != -8) {
+ if (pl.x < 0) {
+ printf ("Got pl.x = %ld, expected -8.\n", (long)pl.x);
+ } else {
+ printf ("Got pl.x = %lu, expected -8.\n", (unsigned long)pl.x);
+ }
+ failures++;
+ }
+ if (pl.y != -500) {
+ if (pl.y < 0) {
+ printf ("Got pl.y = %ld, expected -500.\n", (long)pl.y);
+ } else {
+ printf ("Got pl.y = %lu, expected -500.\n", (unsigned long)pl.y);
+ }
+ failures++;
+ }
+ if (pl.z != -70000) {
+ if (pl.z < 0) {
+ printf ("Got pl.z = %ld, expected -70000.\n", (long)pl.z);
+ } else {
+ printf ("Got pl.z = %lu, expected -70000.\n", (unsigned long)pl.z);
+ }
+ failures++;
+ }
+
+ pl.x = -3;
+ pl.y = -512;
+ pl.z = -131072;
+
+ if (pl.x != -3) {
+ if (pl.x < 0) {
+ printf ("Got pl.x = %ld, expected -3.\n", (long)pl.x);
+ } else {
+ printf ("Got pl.x = %lu, expected -3.\n", (unsigned long)pl.x);
+ }
+ failures++;
+ }
+ if (pl.y != -512) {
+ if (pl.y < 0) {
+ printf ("Got pl.y = %ld, expected -512.\n", (long)pl.y);
+ } else {
+ printf ("Got pl.y = %lu, expected -512.\n", (unsigned long)pl.y);
+ }
+ failures++;
+ }
+ if (pl.z != -131072) {
+ if (pl.z < 0) {
+ printf ("Got pl.z = %ld, expected -131072.\n", (long)pl.z);
+ } else {
+ printf ("Got pl.z = %lu, expected -131072.\n", (unsigned long)pl.z);
+ }
+ failures++;
+ }
+}
+
+int main (void)
+{
+ test_signed_int_bitfields ();
+ test_signed_short_bitfields ();
+ test_signed_char_bitfields ();
+ test_signed_long_bitfields ();
+ printf ("failures: %u\n", failures);
+ return failures;
+}
diff --git a/test/val/plain-int-bitfield.c b/test/val/plain-int-bitfield.c
deleted file mode 100644
index 4d158eca9..000000000
--- a/test/val/plain-int-bitfield.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- Copyright 2020 The cc65 Authors
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-*/
-
-/*
- Tests that plain int bit-fields are unsigned.
-*/
-
-#include
-
-static unsigned char failures = 0;
-
-static struct plain_ints {
- int x : 4;
- int y : 10;
-} pi = {15, 700};
-
-static void test_plain_int_bitfields (void)
-{
- if (pi.x != 15) {
- printf ("Got pi.x = %u, expected 15.\n", pi.x);
- failures++;
- }
- if (pi.y != 700) {
- printf ("Got pi.y = %u, expected 700.\n", pi.y);
- failures++;
- }
-
- pi.x = 3;
- pi.y = 1023;
-
- if (pi.x != 3) {
- printf ("Got pi.x = %u, expected 3.\n", pi.x);
- failures++;
- }
- if (pi.y != 1023) {
- printf ("Got pi.y = %u, expected 1023.\n", pi.y);
- failures++;
- }
-}
-
-int main (void)
-{
- test_plain_int_bitfields ();
- printf ("failures: %u\n", failures);
- return failures;
-}