mirror of
https://github.com/cc65/cc65.git
synced 2025-04-21 12:40:03 +00:00
Merge pull request #1677 from acqn/LongBitField
[cc65] Added support for long-type bit-fields
This commit is contained in:
commit
35153a6dca
@ -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" />
|
||||
|
@ -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=' */
|
||||
|
@ -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) {
|
||||
|
@ -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 */
|
||||
|
@ -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"
|
||||
|
@ -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 */
|
||||
{
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
807
src/cc65/initdata.c
Normal 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
61
src/cc65/initdata.h
Normal 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
|
@ -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)) {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
278
test/val/bitfield-packing-char.c
Normal file
278
test/val/bitfield-packing-char.c
Normal 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;
|
||||
}
|
315
test/val/bitfield-packing-long.c
Normal file
315
test/val/bitfield-packing-long.c
Normal 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;
|
||||
}
|
@ -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
180
test/val/bitfield-plain.c
Normal 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
180
test/val/bitfield-signed.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user