Merge pull request #1677 from acqn/LongBitField

[cc65] Added support for long-type bit-fields
This commit is contained in:
Bob Andrews 2022-04-18 10:01:18 +02:00 committed by GitHub
commit 35153a6dca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 2335 additions and 932 deletions

View File

@ -93,6 +93,7 @@
<ClInclude Include="cc65\hexval.h" />
<ClInclude Include="cc65\ident.h" />
<ClInclude Include="cc65\incpath.h" />
<ClInclude Include="cc65\initdata.h" />
<ClInclude Include="cc65\input.h" />
<ClInclude Include="cc65\lineinfo.h" />
<ClInclude Include="cc65\litpool.h" />
@ -170,6 +171,7 @@
<ClCompile Include="cc65\hexval.c" />
<ClCompile Include="cc65\ident.c" />
<ClCompile Include="cc65\incpath.c" />
<ClCompile Include="cc65\initdata.c" />
<ClCompile Include="cc65\input.c" />
<ClCompile Include="cc65\lineinfo.c" />
<ClCompile Include="cc65\litpool.c" />

View File

@ -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=' */

View File

@ -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) {

View File

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

View File

@ -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"

View File

@ -551,6 +551,24 @@ unsigned long GetIntegerTypeMax (const Type* Type)
static unsigned GetBitFieldMinimalTypeSize (unsigned BitWidth)
/* Return the size of the smallest integer type that may have BitWidth bits */
{
/* Since all integer types supported in cc65 for bit-fields have sizes that
** are powers of 2, we can just use this bit-twiddling trick.
*/
unsigned V = (int)(BitWidth - 1U) / (int)CHAR_BITS;
V |= V >> 1;
V |= V >> 2;
V |= V >> 4;
V |= V >> 8;
V |= V >> 16;
/* Return the result size */
return V + 1U;
}
static unsigned TypeOfBySize (unsigned Size)
/* Get the code generator replacement type of the object by its size */
{
@ -591,8 +609,7 @@ const Type* GetUnderlyingType (const Type* Type)
** bit-field, instead of the type used in the declaration, the truly
** underlying of the bit-field.
*/
unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
switch (Size) {
switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break;
case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break;
case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break;
@ -646,8 +663,7 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
** bit-field, instead of the type used in the declaration, the truly
** underlying of the bit-field.
*/
unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1;
switch (Size) {
switch (GetBitFieldMinimalTypeSize (Type->A.B.Width)) {
case SIZEOF_CHAR: Underlying = T_CHAR; break;
case SIZEOF_INT: Underlying = T_INT; break;
case SIZEOF_LONG: Underlying = T_LONG; break;
@ -663,6 +679,39 @@ TypeCode GetUnderlyingTypeCode (const Type* Type)
const Type* GetBitFieldChunkType (const Type* Type)
/* Get the type needed to operate on the byte chunk containing the bit-field */
{
unsigned ChunkSize;
if ((Type->A.B.Width - 1U) / CHAR_BITS ==
(Type->A.B.Offs + Type->A.B.Width - 1U) / CHAR_BITS) {
/* T bit-field fits within its underlying type */
return GetUnderlyingType (Type);
}
ChunkSize = GetBitFieldMinimalTypeSize (Type->A.B.Offs + Type->A.B.Width);
if (ChunkSize < SizeOf (Type + 1)) {
/* The end of the bit-field is offset by some bits so that it requires
** more bytes to be accessed as a whole than its underlying type does.
** Note: In cc65 the bit offset is always less than CHAR_BITS.
*/
switch (ChunkSize) {
case SIZEOF_CHAR: return IsSignSigned (Type) ? type_schar : type_uchar;
case SIZEOF_INT: return IsSignSigned (Type) ? type_int : type_uint;
case SIZEOF_LONG: return IsSignSigned (Type) ? type_long : type_ulong;
default: return IsSignSigned (Type) ? type_int : type_uint;
}
}
/* We can always use the declarartion integer type as the chunk type.
** Note: A bit-field will not occupy bits located in bytes more than that
** of its declaration type in cc65. So this is OK.
*/
return Type + 1;
}
unsigned SizeOf (const Type* T)
/* Compute size of object represented by type array. */
{
@ -967,9 +1016,18 @@ const Type* IntPromotion (const Type* T)
*/
if (IsTypeBitField (T)) {
/* The standard rule is OK for now as we don't support bit-fields with widths > 16.
/* As we now support long bit-fields, we need modified rules for them:
** - If an int can represent all values of the bit-field, the bit-field is converted
** to an int;
** - Otherwise, if an unsigned int can represent all values of the bit-field, the
** bit-field is converted to an unsigned int;
** - Otherwise, the bit-field will have its declared integer type.
** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's.
*/
return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
if (T->A.B.Width > INT_BITS) {
return IsSignUnsigned (T) ? type_ulong : type_long;
}
return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int;
} else if (IsTypeChar (T)) {
/* An integer can represent all values from either signed or unsigned char, so convert
** chars to int.
@ -1127,6 +1185,15 @@ Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth)
int IsTypeFragBitField (const Type* T)
/* Return true if this is a bit-field that shares byte space with other fields */
{
return IsTypeBitField (T) &&
(T->A.B.Offs != 0 || T->A.B.Width != CHAR_BITS * SizeOf (T));
}
int IsClassObject (const Type* T)
/* Return true if this is a fully described object type */
{

View File

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

View File

@ -66,22 +66,6 @@
/*****************************************************************************/
/* Data */
/*****************************************************************************/
typedef struct StructInitData StructInitData;
struct StructInitData {
unsigned Size; /* Size of struct */
unsigned Offs; /* Current offset in struct */
unsigned BitVal; /* Summed up bit-field value */
unsigned ValBits; /* Valid bits in Val */
};
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
@ -92,9 +76,6 @@ static void ParseTypeSpec (DeclSpec* D, long Default, TypeCode Qualifiers,
int* SignednessSpecified);
/* Parse a type specifier */
static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers);
/* Parse initialization of variables. Return the number of data bytes. */
/*****************************************************************************/
@ -765,12 +746,10 @@ static int ParseFieldWidth (Declaration* D)
D->Type[0].C = T_INT;
}
/* TODO: This can be relaxed to be any integral type, but
** ParseStructInit currently supports only up to int.
*/
if (SizeOf (D->Type) > SizeOf (type_uint)) {
/* Only int-sized or smaller types may be used for bit-fields, for now */
Error ("cc65 currently supports only char-sized and int-sized bit-field types");
/* We currently support integral types up to long */
if (SizeOf (D->Type) > SizeOf (type_ulong)) {
/* Only long-sized or smaller types may be used for bit-fields, for now */
Error ("cc65 currently supports only long-sized and smaller bit-field types");
/* Avoid a diagnostic storm */
D->Type[0].C = T_INT;
@ -2121,704 +2100,3 @@ void CheckEmptyDecl (const DeclSpec* D)
Warning ("Useless declaration");
}
}
static void SkipInitializer (int BracesExpected)
/* Skip the remainder of an initializer in case of errors. Try to be somewhat
** smart so we don't have too many following errors.
*/
{
while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) {
switch (CurTok.Tok) {
case TOK_RCURLY: --BracesExpected; break;
case TOK_LCURLY: ++BracesExpected; break;
default: break;
}
if (BracesExpected >= 0) {
NextToken ();
}
}
}
static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
/* Accept any number of opening curly braces around an initialization, skip
** them and return the number. If the number of curly braces is less than
** BracesNeeded, issue a warning.
*/
{
unsigned BraceCount = 0;
while (CurTok.Tok == TOK_LCURLY) {
++BraceCount;
NextToken ();
}
if (BraceCount < BracesNeeded) {
Error ("'{' expected");
}
return BraceCount;
}
static void ClosingCurlyBraces (unsigned BracesExpected)
/* Accept and skip the given number of closing curly braces together with
** an optional comma. Output an error messages, if the input does not contain
** the expected number of braces.
*/
{
while (BracesExpected) {
/* TODO: Skip all excess initializers until next closing curly brace */
if (CurTok.Tok == TOK_RCURLY) {
NextToken ();
} else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
NextToken ();
NextToken ();
} else {
Error ("'}' expected");
return;
}
--BracesExpected;
}
}
static void DefineData (ExprDesc* Expr)
/* Output a data definition for the given expression */
{
switch (ED_GetLoc (Expr)) {
case E_LOC_NONE:
/* Immediate numeric value with no storage */
g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_ABS:
/* Absolute numeric address */
g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal);
break;
case E_LOC_STATIC:
/* Static variable */
g_defdata (CF_STATIC, Expr->Name, Expr->IVal);
break;
case E_LOC_LITERAL:
/* Literal in the literal pool */
g_defdata (CF_LITERAL, Expr->Name, Expr->IVal);
break;
case E_LOC_REGISTER:
/* Register variable. Taking the address is usually not
** allowed.
*/
if (IS_Get (&AllowRegVarAddr) == 0) {
Error ("Cannot take the address of a register variable");
}
g_defdata (CF_REGVAR, Expr->Name, Expr->IVal);
break;
case E_LOC_CODE:
/* Code label location */
g_defdata (CF_CODE, Expr->Name, Expr->IVal);
break;
case E_LOC_STACK:
case E_LOC_PRIMARY:
case E_LOC_EXPR:
Error ("Non constant initializer");
break;
default:
Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr));
}
}
static void DefineBitFieldData (StructInitData* SI)
/* Output bit field data */
{
/* Ignore if we have no data */
if (SI->ValBits > 0) {
/* Output the data */
g_defdata (CF_CHAR | CF_UNSIGNED | CF_CONST, SI->BitVal, 0);
/* Update the data from SI and account for the size */
if (SI->ValBits >= CHAR_BITS) {
SI->BitVal >>= CHAR_BITS;
SI->ValBits -= CHAR_BITS;
} else {
SI->BitVal = 0;
SI->ValBits = 0;
}
SI->Offs += SIZEOF_CHAR;
}
}
static void DefineStrData (Literal* Lit, unsigned Count)
{
/* Translate into target charset */
TranslateLiteral (Lit);
/* Output the data */
g_defbytes (GetLiteralStr (Lit), Count);
}
static ExprDesc ParseScalarInitInternal (const Type* T)
/* Parse initializaton for scalar data types. This function will not output the
** data but return it in ED.
*/
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* We warn if an initializer for a scalar contains braces, because this is
** quite unusual and often a sign for some problem in the input.
*/
if (BraceCount > 0) {
Warning ("Braces around scalar initializer");
}
/* Get the expression and convert it to the target type */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
return ED;
}
static unsigned ParseScalarInit (const Type* T)
/* Parse initializaton for scalar data types. Return the number of data bytes. */
{
/* Parse initialization */
ExprDesc ED = ParseScalarInitInternal (T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Done */
return SizeOf (T);
}
static unsigned ParsePointerInit (const Type* T)
/* Parse initializaton for pointer data types. Return the number of data bytes. */
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* Expression */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
/* Done */
return SIZEOF_PTR;
}
static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initializaton for arrays. Return the number of data bytes. */
{
int Count;
int HasCurly = 0;
/* Get the array data */
Type* ElementType = IndirectModifiable (T);
unsigned ElementSize = SizeOf (ElementType);
long ElementCount = GetElementCount (T);
/* Special handling for a character array initialized by a literal */
if (IsClassChar (ElementType) &&
(CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST ||
(CurTok.Tok == TOK_LCURLY &&
(NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
/* Char array initialized by string constant */
int NeedParen;
/* If we initializer is enclosed in brackets, remember this fact and
** skip the opening bracket.
*/
NeedParen = (CurTok.Tok == TOK_LCURLY);
if (NeedParen) {
NextToken ();
}
/* If the array is one too small for the string literal, omit the
** trailing zero.
*/
Count = GetLiteralSize (CurTok.SVal);
if (ElementCount != UNSPECIFIED &&
ElementCount != FLEXIBLE &&
Count == ElementCount + 1) {
/* Omit the trailing zero */
--Count;
}
/* Output the data */
DefineStrData (CurTok.SVal, Count);
/* Skip the string */
NextToken ();
/* If the initializer was enclosed in curly braces, we need a closing
** one.
*/
if (NeedParen) {
ConsumeRCurly ();
}
} else {
/* Arrays can be initialized without a pair of curly braces */
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Initialize the array members */
Count = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Flexible array members may not be initialized within
** an array (because the size of each element may differ
** otherwise).
*/
ParseInitInternal (ElementType, Braces, 0);
++Count;
if (CurTok.Tok != TOK_COMMA)
break;
NextToken ();
}
if (HasCurly) {
/* Closing curly braces */
ConsumeRCurly ();
}
}
/* Size of 'void' elements are determined after initialization */
if (ElementSize == 0) {
ElementSize = SizeOf (ElementType);
}
if (ElementCount == UNSPECIFIED) {
/* Number of elements determined by initializer */
SetElementCount (T, Count);
ElementCount = Count;
} else if (ElementCount == FLEXIBLE) {
if (AllowFlexibleMembers) {
/* In non ANSI mode, allow initialization of flexible array
** members.
*/
ElementCount = Count;
} else {
/* Forbid */
Error ("Initializing flexible array member is forbidden");
ElementCount = Count;
}
} else if (Count < ElementCount) {
g_zerobytes ((ElementCount - Count) * ElementSize);
} else if (Count > ElementCount && HasCurly) {
Error ("Excess elements in array initializer");
}
return ElementCount * ElementSize;
}
static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initialization of a struct or union. Return the number of data bytes. */
{
SymEntry* Sym;
SymTable* Tab;
StructInitData SI;
int HasCurly = 0;
int SkipComma = 0;
/* Fields can be initialized without a pair of curly braces */
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Get a pointer to the struct entry from the type */
Sym = GetESUSymEntry (T);
/* Get the size of the struct from the symbol table entry */
SI.Size = Sym->V.S.Size;
/* Check if this struct definition has a field table. If it doesn't, it
** is an incomplete definition.
*/
Tab = Sym->V.S.SymTab;
if (Tab == 0) {
Error ("Cannot initialize variables with incomplete type");
/* Try error recovery */
SkipInitializer (HasCurly);
/* Nothing initialized */
return 0;
}
/* Get a pointer to the list of symbols */
Sym = Tab->SymHead;
/* Initialize fields */
SI.Offs = 0;
SI.BitVal = 0;
SI.ValBits = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Check for excess elements */
if (Sym == 0) {
/* Is there just one trailing comma before a closing curly? */
if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
/* Skip comma and exit scope */
NextToken ();
break;
}
if (HasCurly) {
Error ("Excess elements in %s initializer", GetBasicTypeName (T));
SkipInitializer (HasCurly);
}
return SI.Offs;
}
/* Check for special members that don't consume the initializer */
if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) {
/* Just skip */
goto NextMember;
}
/* This may be an anonymous bit-field, in which case it doesn't
** have an initializer.
*/
if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) {
/* Account for the data and output it if we have at least a full
** word. We may have more if there was storage unit overlap, for
** example two consecutive 10 bit fields. These will be packed
** into 3 bytes.
*/
SI.ValBits += Sym->Type->A.B.Width;
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
/* Avoid consuming the comma if any */
goto NextMember;
}
/* Skip comma this round */
if (SkipComma) {
NextToken ();
SkipComma = 0;
}
if (SymIsBitField (Sym)) {
/* Parse initialization of one field. Bit-fields need a special
** handling.
*/
ExprDesc ED;
ED_Init (&ED);
unsigned Val;
unsigned Shift;
/* Calculate the bitmask from the bit-field data */
unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U;
/* Safety ... */
CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
SI.Offs * CHAR_BITS + SI.ValBits);
/* Read the data, check for a constant integer, do a range check */
ED = ParseScalarInitInternal (IntPromotion (Sym->Type));
if (!ED_IsConstAbsInt (&ED)) {
Error ("Constant initializer expected");
ED_MakeConstAbsInt (&ED, 1);
}
/* Truncate the initializer value to the width of the bit-field and check if we lost
** any useful bits.
*/
Val = (unsigned) ED.IVal & Mask;
if (IsSignUnsigned (Sym->Type)) {
if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) {
Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %u",
GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
Sym->Type->A.B.Width, ED.IVal, Val);
}
} else {
/* Sign extend back to full width of host long. */
unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width;
long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits);
if (ED.IVal != RestoredVal) {
Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer "
"changes value from %ld to %ld",
GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type),
Sym->Type->A.B.Width, ED.IVal, RestoredVal);
}
}
/* Add the value to the currently stored bit-field value */
Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs;
SI.BitVal |= (Val << Shift);
/* Account for the data and output any full bytes we have. */
SI.ValBits += Sym->Type->A.B.Width;
/* Make sure unsigned is big enough to hold the value, 22 bits.
** This is 22 bits because the most we can have is 7 bits left
** over from the previous OutputBitField call, plus 15 bits
** from this field. A 16-bit bit-field will always be byte
** aligned, so will have padding before it.
*/
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
} else {
/* Standard member. We should never have stuff from a
** bit-field left because an anonymous member was added
** for padding by ParseStructDecl.
*/
CHECK (SI.ValBits == 0);
/* Flexible array members may only be initialized if they are
** the last field (or part of the last struct field).
*/
SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0);
}
/* More initializers? */
if (CurTok.Tok != TOK_COMMA) {
break;
}
/* Skip the comma next round */
SkipComma = 1;
NextMember:
/* Next member. For unions, only the first one can be initialized */
if (IsTypeUnion (T)) {
/* Union */
Sym = 0;
} else {
/* Struct */
Sym = Sym->NextSym;
}
}
if (HasCurly) {
/* Consume the closing curly brace */
ConsumeRCurly ();
}
/* If we have data from a bit-field left, output it now */
CHECK (SI.ValBits < CHAR_BITS);
DefineBitFieldData (&SI);
/* If there are struct fields left, reserve additional storage */
if (SI.Offs < SI.Size) {
g_zerobytes (SI.Size - SI.Offs);
SI.Offs = SI.Size;
}
/* Return the actual number of bytes initialized. This number may be
** larger than sizeof (Struct) if flexible array members are present and
** were initialized (possible in non ANSI mode).
*/
return SI.Offs;
}
static unsigned ParseVoidInit (Type* T)
/* Parse an initialization of a void variable (special cc65 extension).
** Return the number of bytes initialized.
*/
{
unsigned Size;
/* Opening brace */
ConsumeLCurly ();
/* Allow an arbitrary list of values */
Size = 0;
do {
ExprDesc Expr = NoCodeConstExpr (hie1);
switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
case T_SCHAR:
case T_UCHAR:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it byte sized */
Expr.IVal &= 0xFF;
}
DefineData (&Expr);
Size += SIZEOF_CHAR;
break;
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_PTR:
case T_ARRAY:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it word sized */
Expr.IVal &= 0xFFFF;
}
DefineData (&Expr);
Size += SIZEOF_INT;
break;
case T_LONG:
case T_ULONG:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it dword sized */
Expr.IVal &= 0xFFFFFFFF;
}
DefineData (&Expr);
Size += SIZEOF_LONG;
break;
default:
Error ("Illegal type in initialization");
break;
}
if (CurTok.Tok != TOK_COMMA) {
break;
}
NextToken ();
} while (CurTok.Tok != TOK_RCURLY);
/* Closing brace */
ConsumeRCurly ();
/* Number of bytes determined by initializer */
if (T->A.U != 0 && T->A.U != Size) {
Error ("'void' array initialized with elements of variant sizes");
} else {
T->A.U = Size;
}
/* Return the number of bytes initialized */
return Size;
}
static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers)
/* Parse initialization of variables. Return the number of data bytes. */
{
switch (GetUnderlyingTypeCode (T)) {
case T_SCHAR:
case T_UCHAR:
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_LONG:
case T_ULONG:
case T_FLOAT:
case T_DOUBLE:
return ParseScalarInit (T);
case T_PTR:
return ParsePointerInit (T);
case T_ARRAY:
return ParseArrayInit (T, Braces, AllowFlexibleMembers);
case T_STRUCT:
case T_UNION:
return ParseStructInit (T, Braces, AllowFlexibleMembers);
case T_ENUM:
/* Incomplete enum type must have already raised errors.
** Just proceed to consume the value.
*/
return ParseScalarInit (T);
case T_VOID:
if (IS_Get (&Standard) == STD_CC65) {
/* Special cc65 extension in non-ANSI mode */
return ParseVoidInit (T);
}
/* FALLTHROUGH */
default:
Error ("Illegal type");
return SIZEOF_CHAR;
}
}
unsigned ParseInit (Type* T)
/* Parse initialization of variables. Return the number of data bytes. */
{
/* Current curly braces layers */
int Braces = 0;
/* Parse the initialization. Flexible array members can only be initialized
** in cc65 mode.
*/
unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65);
/* The initialization may not generate code on global level, because code
** outside function scope will never get executed.
*/
if (HaveGlobalCode ()) {
Error ("Non constant initializers");
RemoveGlobalCode ();
}
/* Return the size needed for the initialization */
return Size;
}

View File

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

View File

@ -25,6 +25,7 @@
#include "funcdesc.h"
#include "function.h"
#include "global.h"
#include "initdata.h"
#include "litpool.h"
#include "loadexpr.h"
#include "macrotab.h"
@ -398,7 +399,7 @@ static void DoInc (ExprDesc* Expr, unsigned KeepResult)
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
if (IsTypeBitField (Expr->Type)) {
if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, Val, KeepResult);
return;
}
@ -485,7 +486,7 @@ static void DoDec (ExprDesc* Expr, unsigned KeepResult)
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
if (IsTypeBitField (Expr->Type)) {
if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, -Val, KeepResult);
return;
}

807
src/cc65/initdata.c Normal file
View File

@ -0,0 +1,807 @@
/*****************************************************************************/
/* */
/* initdata.c */
/* */
/* Parse and generate initializer data */
/* */
/* */
/* */
/* (C) 1998-2015, Ullrich von Bassewitz */
/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
/* common */
#include "addrsize.h"
#include "mmodel.h"
#include "shift.h"
#include "xmalloc.h"
/* cc65 */
#include "anonname.h"
#include "codegen.h"
#include "datatype.h"
#include "declattr.h"
#include "error.h"
#include "expr.h"
#include "exprdesc.h"
#include "funcdesc.h"
#include "function.h"
#include "global.h"
#include "litpool.h"
#include "pragma.h"
#include "scanner.h"
#include "shift.h"
#include "standard.h"
#include "symtab.h"
#include "wrappedcall.h"
#include "typeconv.h"
#include "initdata.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
typedef struct StructInitData StructInitData;
struct StructInitData {
unsigned Size; /* Size of struct */
unsigned Offs; /* Current offset in struct */
unsigned BitVal; /* Summed up bit-field value */
unsigned ValBits; /* Valid bits in Val */
};
/*****************************************************************************/
/* Forwards */
/*****************************************************************************/
static unsigned ParseInitInternal (Type* T, int* Braces, int AllowFlexibleMembers);
/* Parse initialization of variables. Return the number of data bytes. */
/*****************************************************************************/
/* code */
/*****************************************************************************/
static void SkipInitializer (int BracesExpected)
/* Skip the remainder of an initializer in case of errors. Try to be somewhat
** smart so we don't have too many following errors.
*/
{
while (CurTok.Tok != TOK_CEOF && CurTok.Tok != TOK_SEMI && BracesExpected >= 0) {
switch (CurTok.Tok) {
case TOK_RCURLY: --BracesExpected; break;
case TOK_LCURLY: ++BracesExpected; break;
default: break;
}
if (BracesExpected >= 0) {
NextToken ();
}
}
}
static unsigned OpeningCurlyBraces (unsigned BracesNeeded)
/* Accept any number of opening curly braces around an initialization, skip
** them and return the number. If the number of curly braces is less than
** BracesNeeded, issue a warning.
*/
{
unsigned BraceCount = 0;
while (CurTok.Tok == TOK_LCURLY) {
++BraceCount;
NextToken ();
}
if (BraceCount < BracesNeeded) {
Error ("'{' expected");
}
return BraceCount;
}
static void ClosingCurlyBraces (unsigned BracesExpected)
/* Accept and skip the given number of closing curly braces together with
** an optional comma. Output an error messages, if the input does not contain
** the expected number of braces.
*/
{
while (BracesExpected) {
/* TODO: Skip all excess initializers until next closing curly brace */
if (CurTok.Tok == TOK_RCURLY) {
NextToken ();
} else if (CurTok.Tok == TOK_COMMA && NextTok.Tok == TOK_RCURLY) {
NextToken ();
NextToken ();
} else {
Error ("'}' expected");
return;
}
--BracesExpected;
}
}
static void DefineData (ExprDesc* Expr)
/* Output a data definition for the given expression */
{
switch (ED_GetLoc (Expr)) {
case E_LOC_NONE:
/* Immediate numeric value with no storage */
g_defdata (CF_IMM | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_ABS:
/* Absolute numeric address */
g_defdata (CF_ABSOLUTE | TypeOf (Expr->Type) | CF_CONST, Expr->IVal, 0);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_defdata (CF_EXTERNAL, Expr->Name, Expr->IVal);
break;
case E_LOC_STATIC:
/* Static variable */
g_defdata (CF_STATIC, Expr->Name, Expr->IVal);
break;
case E_LOC_LITERAL:
/* Literal in the literal pool */
g_defdata (CF_LITERAL, Expr->Name, Expr->IVal);
break;
case E_LOC_REGISTER:
/* Register variable. Taking the address is usually not
** allowed.
*/
if (IS_Get (&AllowRegVarAddr) == 0) {
Error ("Cannot take the address of a register variable");
}
g_defdata (CF_REGVAR, Expr->Name, Expr->IVal);
break;
case E_LOC_CODE:
/* Code label location */
g_defdata (CF_CODE, Expr->Name, Expr->IVal);
break;
case E_LOC_STACK:
case E_LOC_PRIMARY:
case E_LOC_EXPR:
Error ("Non constant initializer");
break;
default:
Internal ("Unknown constant type: 0x%04X", ED_GetLoc (Expr));
}
}
static void DefineBitFieldData (StructInitData* SI)
/* Output bit field data */
{
/* Ignore if we have no data */
if (SI->ValBits > 0) {
/* Output the data */
g_defdata (CF_CHAR | CF_UNSIGNED | CF_CONST, SI->BitVal, 0);
/* Update the data from SI and account for the size */
if (SI->ValBits >= CHAR_BITS) {
SI->BitVal >>= CHAR_BITS;
SI->ValBits -= CHAR_BITS;
} else {
SI->BitVal = 0;
SI->ValBits = 0;
}
SI->Offs += SIZEOF_CHAR;
}
}
static void DefineStrData (Literal* Lit, unsigned Count)
{
/* Translate into target charset */
TranslateLiteral (Lit);
/* Output the data */
g_defbytes (GetLiteralStr (Lit), Count);
}
static ExprDesc ParseScalarInitInternal (const Type* T)
/* Parse initializaton for scalar data types. This function will not output the
** data but return it in ED.
*/
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* We warn if an initializer for a scalar contains braces, because this is
** quite unusual and often a sign for some problem in the input.
*/
if (BraceCount > 0) {
Warning ("Braces around scalar initializer");
}
/* Get the expression and convert it to the target type */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
return ED;
}
static unsigned ParseScalarInit (const Type* T)
/* Parse initializaton for scalar data types. Return the number of data bytes. */
{
/* Parse initialization */
ExprDesc ED = ParseScalarInitInternal (T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Done */
return SizeOf (T);
}
static unsigned ParsePointerInit (const Type* T)
/* Parse initializaton for pointer data types. Return the number of data bytes. */
{
/* Optional opening brace */
unsigned BraceCount = OpeningCurlyBraces (0);
/* Expression */
ExprDesc ED = NoCodeConstExpr (hie1);
TypeConversion (&ED, T);
/* Output the data */
DefineData (&ED);
/* Do this anyways for safety */
DoDeferred (SQP_KEEP_NONE, &ED);
/* Close eventually opening braces */
ClosingCurlyBraces (BraceCount);
/* Done */
return SIZEOF_PTR;
}
static unsigned ParseArrayInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initializaton for arrays. Return the number of data bytes. */
{
int Count;
int HasCurly = 0;
/* Get the array data */
Type* ElementType = IndirectModifiable (T);
unsigned ElementSize = SizeOf (ElementType);
long ElementCount = GetElementCount (T);
/* Special handling for a character array initialized by a literal */
if (IsClassChar (ElementType) &&
(CurTok.Tok == TOK_SCONST || CurTok.Tok == TOK_WCSCONST ||
(CurTok.Tok == TOK_LCURLY &&
(NextTok.Tok == TOK_SCONST || NextTok.Tok == TOK_WCSCONST)))) {
/* Char array initialized by string constant */
int NeedParen;
/* If we initializer is enclosed in brackets, remember this fact and
** skip the opening bracket.
*/
NeedParen = (CurTok.Tok == TOK_LCURLY);
if (NeedParen) {
NextToken ();
}
/* If the array is one too small for the string literal, omit the
** trailing zero.
*/
Count = GetLiteralSize (CurTok.SVal);
if (ElementCount != UNSPECIFIED &&
ElementCount != FLEXIBLE &&
Count == ElementCount + 1) {
/* Omit the trailing zero */
--Count;
}
/* Output the data */
DefineStrData (CurTok.SVal, Count);
/* Skip the string */
NextToken ();
/* If the initializer was enclosed in curly braces, we need a closing
** one.
*/
if (NeedParen) {
ConsumeRCurly ();
}
} else {
/* Arrays can be initialized without a pair of curly braces */
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Initialize the array members */
Count = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Flexible array members may not be initialized within
** an array (because the size of each element may differ
** otherwise).
*/
ParseInitInternal (ElementType, Braces, 0);
++Count;
if (CurTok.Tok != TOK_COMMA)
break;
NextToken ();
}
if (HasCurly) {
/* Closing curly braces */
ConsumeRCurly ();
}
}
/* Size of 'void' elements are determined after initialization */
if (ElementSize == 0) {
ElementSize = SizeOf (ElementType);
}
if (ElementCount == UNSPECIFIED) {
/* Number of elements determined by initializer */
SetElementCount (T, Count);
ElementCount = Count;
} else if (ElementCount == FLEXIBLE) {
if (AllowFlexibleMembers) {
/* In non ANSI mode, allow initialization of flexible array
** members.
*/
ElementCount = Count;
} else {
/* Forbid */
Error ("Initializing flexible array member is forbidden");
ElementCount = Count;
}
} else if (Count < ElementCount) {
g_zerobytes ((ElementCount - Count) * ElementSize);
} else if (Count > ElementCount && HasCurly) {
Error ("Excess elements in array initializer");
}
return ElementCount * ElementSize;
}
static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers)
/* Parse initialization of a struct or union. Return the number of data bytes. */
{
SymEntry* Sym;
SymTable* Tab;
StructInitData SI;
int HasCurly = 0;
int SkipComma = 0;
/* Fields can be initialized without a pair of curly braces */
if (*Braces == 0 || CurTok.Tok == TOK_LCURLY) {
/* Consume the opening curly brace */
HasCurly = ConsumeLCurly ();
*Braces += HasCurly;
}
/* Get a pointer to the struct entry from the type */
Sym = GetESUSymEntry (T);
/* Get the size of the struct from the symbol table entry */
SI.Size = Sym->V.S.Size;
/* Check if this struct definition has a field table. If it doesn't, it
** is an incomplete definition.
*/
Tab = Sym->V.S.SymTab;
if (Tab == 0) {
Error ("Cannot initialize variables with incomplete type");
/* Try error recovery */
SkipInitializer (HasCurly);
/* Nothing initialized */
return 0;
}
/* Get a pointer to the list of symbols */
Sym = Tab->SymHead;
/* Initialize fields */
SI.Offs = 0;
SI.BitVal = 0;
SI.ValBits = 0;
while (CurTok.Tok != TOK_RCURLY) {
/* Check for excess elements */
if (Sym == 0) {
/* Is there just one trailing comma before a closing curly? */
if (NextTok.Tok == TOK_RCURLY && CurTok.Tok == TOK_COMMA) {
/* Skip comma and exit scope */
NextToken ();
break;
}
if (HasCurly) {
Error ("Excess elements in %s initializer", GetBasicTypeName (T));
SkipInitializer (HasCurly);
}
return SI.Offs;
}
/* Check for special members that don't consume the initializer */
if ((Sym->Flags & SC_ALIAS) == SC_ALIAS) {
/* Just skip */
goto NextMember;
}
/* This may be an anonymous bit-field, in which case it doesn't
** have an initializer.
*/
if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) {
/* Account for the data and output it if we have at least a full
** byte. We may have more if there was storage unit overlap, for
** example two consecutive 7 bit fields. Those would be packed
** into 2 bytes.
*/
SI.ValBits += Sym->Type->A.B.Width;
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= LONG_BITS);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
/* Avoid consuming the comma if any */
goto NextMember;
}
/* Skip comma this round */
if (SkipComma) {
NextToken ();
SkipComma = 0;
}
if (SymIsBitField (Sym)) {
/* Parse initialization of one field. Bit-fields need a special
** handling.
*/
ExprDesc Field;
ED_Init (&Field);
unsigned long Val;
unsigned Shift;
/* Calculate the bitmask from the bit-field data */
unsigned long Mask = shl_l (1UL, Sym->Type->A.B.Width) - 1UL;
/* Safety ... */
CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs ==
SI.Offs * CHAR_BITS + SI.ValBits);
/* Read the data, check for a constant integer, do a range check */
Field = ParseScalarInitInternal (IntPromotion (Sym->Type));
if (!ED_IsConstAbsInt (&Field)) {
Error ("Constant initializer expected");
ED_MakeConstAbsInt (&Field, 1);
}
/* Truncate the initializer value to the width of the bit-field and check if we lost
** any useful bits.
*/
Val = (unsigned long) Field.IVal & Mask;
if (IsSignUnsigned (Sym->Type)) {
if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) {
Warning (IsSignUnsigned (Field.Type) ?
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %lu to %lu" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %lu",
GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
Sym->Type->A.B.Width, Field.IVal, Val);
}
} else {
/* Sign extend back to full width of host long. */
unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width;
long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits);
if (Field.IVal != RestoredVal) {
Warning (IsSignUnsigned (Field.Type) ?
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %lu to %ld" :
"Implicit truncation from '%s' to '%s : %u' in bit-field initializer"
" changes value from %ld to %ld",
GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type),
Sym->Type->A.B.Width, Field.IVal, RestoredVal);
}
}
/* Add the value to the currently stored bit-field value */
Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs;
SI.BitVal |= (Val << Shift);
/* Account for the data and output any full bytes we have. */
SI.ValBits += Sym->Type->A.B.Width;
/* Make sure unsigned is big enough to hold the value, 32 bits.
** This cannot be more than 32 bits because a 16-bit or 32-bit
** bit-field will always be byte-aligned with padding before it
** if there are bits from prior fields that haven't been output
** yet.
*/
CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal));
/* TODO: Generalize this so any type can be used. */
CHECK (SI.ValBits <= LONG_BITS);
while (SI.ValBits >= CHAR_BITS) {
DefineBitFieldData (&SI);
}
} else {
/* Standard member. We should never have stuff from a
** bit-field left because an anonymous member was added
** for padding by ParseStructDecl.
*/
CHECK (SI.ValBits == 0);
/* Flexible array members may only be initialized if they are
** the last field (or part of the last struct field).
*/
SI.Offs += ParseInitInternal (Sym->Type, Braces, AllowFlexibleMembers && Sym->NextSym == 0);
}
/* More initializers? */
if (CurTok.Tok != TOK_COMMA) {
break;
}
/* Skip the comma next round */
SkipComma = 1;
NextMember:
/* Next member. For unions, only the first one can be initialized */
if (IsTypeUnion (T)) {
/* Union */
Sym = 0;
} else {
/* Struct */
Sym = Sym->NextSym;
}
}
if (HasCurly) {
/* Consume the closing curly brace */
ConsumeRCurly ();
}
/* If we have data from a bit-field left, output it now */
CHECK (SI.ValBits < CHAR_BITS);
DefineBitFieldData (&SI);
/* If there are struct fields left, reserve additional storage */
if (SI.Offs < SI.Size) {
g_zerobytes (SI.Size - SI.Offs);
SI.Offs = SI.Size;
}
/* Return the actual number of bytes initialized. This number may be
** larger than sizeof (Struct) if flexible array members are present and
** were initialized (possible in non ANSI mode).
*/
return SI.Offs;
}
static unsigned ParseVoidInit (Type* T)
/* Parse an initialization of a void variable (special cc65 extension).
** Return the number of bytes initialized.
*/
{
unsigned Size;
/* Opening brace */
ConsumeLCurly ();
/* Allow an arbitrary list of values */
Size = 0;
do {
ExprDesc Expr = NoCodeConstExpr (hie1);
switch (GetUnderlyingTypeCode (&Expr.Type[0])) {
case T_SCHAR:
case T_UCHAR:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it byte sized */
Expr.IVal &= 0xFF;
}
DefineData (&Expr);
Size += SIZEOF_CHAR;
break;
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_PTR:
case T_ARRAY:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it word sized */
Expr.IVal &= 0xFFFF;
}
DefineData (&Expr);
Size += SIZEOF_INT;
break;
case T_LONG:
case T_ULONG:
if (ED_IsConstAbsInt (&Expr)) {
/* Make it dword sized */
Expr.IVal &= 0xFFFFFFFF;
}
DefineData (&Expr);
Size += SIZEOF_LONG;
break;
default:
Error ("Illegal type in initialization");
break;
}
if (CurTok.Tok != TOK_COMMA) {
break;
}
NextToken ();
} while (CurTok.Tok != TOK_RCURLY);
/* Closing brace */
ConsumeRCurly ();
/* Number of bytes determined by initializer */
if (T->A.U != 0 && T->A.U != Size) {
Error ("'void' array initialized with elements of variant sizes");
} else {
T->A.U = Size;
}
/* Return the number of bytes initialized */
return Size;
}
static unsigned ParseInitInternal (Type* T, int *Braces, int AllowFlexibleMembers)
/* Parse initialization of variables. Return the number of data bytes. */
{
switch (GetUnderlyingTypeCode (T)) {
case T_SCHAR:
case T_UCHAR:
case T_SHORT:
case T_USHORT:
case T_INT:
case T_UINT:
case T_LONG:
case T_ULONG:
case T_FLOAT:
case T_DOUBLE:
return ParseScalarInit (T);
case T_PTR:
return ParsePointerInit (T);
case T_ARRAY:
return ParseArrayInit (T, Braces, AllowFlexibleMembers);
case T_STRUCT:
case T_UNION:
return ParseStructInit (T, Braces, AllowFlexibleMembers);
case T_ENUM:
/* Incomplete enum type must have already raised errors.
** Just proceed to consume the value.
*/
return ParseScalarInit (T);
case T_VOID:
if (IS_Get (&Standard) == STD_CC65) {
/* Special cc65 extension in non-ANSI mode */
return ParseVoidInit (T);
}
/* FALLTHROUGH */
default:
Error ("Illegal type");
return SIZEOF_CHAR;
}
}
unsigned ParseInit (Type* T)
/* Parse initialization of variables. Return the number of data bytes. */
{
/* Current curly braces layers */
int Braces = 0;
/* Parse the initialization. Flexible array members can only be initialized
** in cc65 mode.
*/
unsigned Size = ParseInitInternal (T, &Braces, IS_Get (&Standard) == STD_CC65);
/* The initialization may not generate code on global level, because code
** outside function scope will never get executed.
*/
if (HaveGlobalCode ()) {
Error ("Non constant initializers");
RemoveGlobalCode ();
}
/* Return the size needed for the initialization */
return Size;
}

61
src/cc65/initdata.h Normal file
View File

@ -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

View File

@ -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)) {

View File

@ -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.
*/

View File

@ -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"

View File

@ -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;
}

View File

@ -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 <stdio.h>
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;
}

View File

@ -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 <stdio.h>
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;
}

View File

@ -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 <stdio.h>
@ -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++;
}
}

180
test/val/bitfield-plain.c Normal file
View File

@ -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 <stdio.h>
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;
}

180
test/val/bitfield-signed.c Normal file
View File

@ -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 <stdio.h>
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;
}

View File

@ -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 <stdio.h>
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;
}