From 2d96f79bc732bf5ba36f9dc05cf425aa0e54a9da Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 1 Dec 2021 09:45:17 +0800 Subject: [PATCH 1/5] Added and used new utility type functions for bit-fields. Fixed GetUnderlyingTypeCode() for bit-fields with widths > 16. --- src/cc65/assignment.c | 34 ++++------------------ src/cc65/datatype.c | 66 ++++++++++++++++++++++++++++++++++++++++--- src/cc65/datatype.h | 6 ++++ src/cc65/expr.c | 4 +-- src/cc65/loadexpr.c | 36 ++++++++++------------- src/cc65/loadexpr.h | 5 +++- 6 files changed, 95 insertions(+), 56 deletions(-) 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/datatype.c b/src/cc65/datatype.c index cc313bd21..bb7c40476 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. */ { @@ -1127,6 +1176,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/expr.c b/src/cc65/expr.c index 7343702ea..3b3754665 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -398,7 +398,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 +485,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/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. +*/ From 970607cde5c0afc96fd8af35b4fdf27173a2a986 Mon Sep 17 00:00:00 2001 From: acqn Date: Sun, 5 Dec 2021 12:21:01 +0800 Subject: [PATCH 2/5] Optimized g_testbitfield() and g_extractbitfield() with enhanced support for long bit-fields. --- src/cc65/codegen.c | 295 ++++++++++++++++++++++++++++++++++----------- src/cc65/codegen.h | 4 +- 2 files changed, 229 insertions(+), 70 deletions(-) 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 */ From 21858b52e7a7b1574c9bc68a441be1ed983a31c4 Mon Sep 17 00:00:00 2001 From: acqn Date: Mon, 3 Jan 2022 13:10:32 +0800 Subject: [PATCH 3/5] Separated data initializer stuff from declaration stuff. --- src/cc65.vcxproj | 2 + src/cc65/compile.c | 1 + src/cc65/declare.c | 720 --------------------------------------- src/cc65/declare.h | 5 - src/cc65/expr.c | 1 + src/cc65/initdata.c | 800 ++++++++++++++++++++++++++++++++++++++++++++ src/cc65/initdata.h | 61 ++++ src/cc65/locals.c | 1 + 8 files changed, 866 insertions(+), 725 deletions(-) create mode 100644 src/cc65/initdata.c create mode 100644 src/cc65/initdata.h 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/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/declare.c b/src/cc65/declare.c index 017a69874..67e9a1783 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. */ - /*****************************************************************************/ @@ -2121,704 +2102,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 3b3754665..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" diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c new file mode 100644 index 000000000..1f8b35ce4 --- /dev/null +++ b/src/cc65/initdata.c @@ -0,0 +1,800 @@ +/*****************************************************************************/ +/* */ +/* 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 + ** 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/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/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" From 4f4487cb032a26478a0ac27fcaf0d29812118d7f Mon Sep 17 00:00:00 2001 From: acqn Date: Tue, 4 Jan 2022 18:23:04 +0800 Subject: [PATCH 4/5] Added supports for long bit-fields. --- src/cc65/datatype.c | 13 +++++++-- src/cc65/declare.c | 10 +++---- src/cc65/initdata.c | 65 +++++++++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index bb7c40476..e43af238e 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1016,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. diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 67e9a1783..7cc7444b6 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -746,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; diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 1f8b35ce4..99dacdca9 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -505,13 +505,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) */ 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. + ** 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 <= CHAR_BITS + INT_BITS - 2); + CHECK (SI.ValBits <= LONG_BITS); while (SI.ValBits >= CHAR_BITS) { DefineBitFieldData (&SI); } @@ -530,45 +531,51 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of one field. Bit-fields need a special ** handling. */ - ExprDesc ED; - ED_Init (&ED); - unsigned Val; + ExprDesc Field; + ED_Init (&Field); + unsigned long Val; unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; + 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 */ - ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); - if (!ED_IsConstAbsInt (&ED)) { + Field = ParseScalarInitInternal (IntPromotion (Sym->Type)); + if (!ED_IsConstAbsInt (&Field)) { Error ("Constant initializer expected"); - ED_MakeConstAbsInt (&ED, 1); + 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) ED.IVal & Mask; + Val = (unsigned long) Field.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); + 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 (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); + 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); } } @@ -578,15 +585,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* 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. + /* 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 <= CHAR_BITS + INT_BITS - 2); + CHECK (SI.ValBits <= LONG_BITS); while (SI.ValBits >= CHAR_BITS) { DefineBitFieldData (&SI); } From 67594cca70e21cf29caf16b1cda065d2b5858814 Mon Sep 17 00:00:00 2001 From: acqn Date: Thu, 3 Mar 2022 16:14:26 +0800 Subject: [PATCH 5/5] Testcases for long bit-fields. --- test/val/{enum-bitfield.c => bitfield-enum.c} | 162 ++++++++- test/val/bitfield-packing-char.c | 278 ++++++++++++++++ test/val/bitfield-packing-long.c | 315 ++++++++++++++++++ test/val/{bitfield.c => bitfield-packing.c} | 18 +- test/val/bitfield-plain.c | 180 ++++++++++ test/val/bitfield-signed.c | 180 ++++++++++ test/val/plain-int-bitfield.c | 63 ---- 7 files changed, 1123 insertions(+), 73 deletions(-) rename test/val/{enum-bitfield.c => bitfield-enum.c} (61%) create mode 100644 test/val/bitfield-packing-char.c create mode 100644 test/val/bitfield-packing-long.c rename test/val/{bitfield.c => bitfield-packing.c} (93%) create mode 100644 test/val/bitfield-plain.c create mode 100644 test/val/bitfield-signed.c delete mode 100644 test/val/plain-int-bitfield.c 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; -}