1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-07 23:29:39 +00:00

Allow char bit-fields

These are not required to be supported (only int, signed int, and
unsigned int are required), but most compilers support it.

https://port70.net/~nsz/c/c89/c89-draft.html#3.5.2.1
https://port70.net/~nsz/c/c89/c89-draft.html#A.6.5.8

For consistency with other integral types, plain `char` bit-fields
are unsigned, regardless of the `--signed-chars` option.

Fixes #1047
This commit is contained in:
Jesse Rosenstock 2020-08-30 20:47:25 +02:00 committed by Oliver Schmidt
parent 41cee0eb44
commit 4e4e4c2d21
6 changed files with 401 additions and 5 deletions

View File

@ -807,6 +807,11 @@ This cc65 version has some extensions to the ISO C standard.
<tt/_Static_assert/ is also available as the macro <tt/static_assert/ in
<tt/assert.h/.
<item> cc65 supports bit-fields of any integral type that is int-sized or
smaller, and enumerated types with those types as their underlying
type. (Only <tt/int/, <tt/signed int/, and <tt/unsigned int/ are
required.)
<item> Computed gotos, a GCC extension, has limited support. With it you can
use fast jump tables from C. You can take the address of a label with
a double ampersand, putting them in a static const array of type void *.

View File

@ -764,9 +764,9 @@ static int ParseFieldWidth (Declaration* Decl)
/* TODO: This can be relaxed to be any integral type, but
** ParseStructInit currently only supports up to int.
*/
if (SizeOf (Decl->Type) != SizeOf (type_uint)) {
/* Only int sized types may be used for bit-fields for now */
Error ("cc65 currently only supports unsigned int bit-fields");
if (SizeOf (Decl->Type) > SizeOf (type_uint)) {
/* Only int-sized or smaller types may be used for bit-fields for now */
Error ("cc65 currently only supports char-sized and int-sized bit-fields");
return -1;
}

View File

@ -870,9 +870,13 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs,
if (!SignednessSpecified) {
/* int is treated as signed int everywhere except bit-fields; switch it to unsigned,
** since this is allowed for bit-fields and avoids sign-extension, so is much faster.
** enums set SignednessSpecified to 1 to avoid this adjustment.
** enums set SignednessSpecified to 1 to avoid this adjustment. Character types
** actually distinguish 3 types of char; char may either be signed or unsigned, which
** is controlled by `--signed-chars`. In bit-fields, however, we perform the same
** `char -> unsigned char` adjustment that is performed with other integral types.
*/
CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED);
CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED ||
IsTypeChar (Entry->Type));
Entry->Type->C &= ~T_MASK_SIGN;
Entry->Type->C |= T_SIGN_UNSIGNED;
}

280
test/val/char-bitfield.c Normal file
View File

@ -0,0 +1,280 @@
/*
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 of char bit-fields; see https://github.com/cc65/cc65/issues/1047
*/
#include <stdio.h>
static unsigned char failures = 0;
static struct four_bits {
unsigned char 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++;
}
}
static struct four_bits_signed {
signed char x : 4;
} fbs = {1};
static void test_four_bits_signed (void)
{
if (sizeof (struct four_bits_signed) != 1) {
printf ("Got sizeof (struct four_bits_signed) = %zu, expected 1.\n",
sizeof (struct four_bits));
failures++;
}
if (fbs.x != 1) {
printf ("Got fbs.x = %d, expected 1.\n", fbs.x);
failures++;
}
fbs.x = 3;
if (fbs.x != 3) {
printf ("Got fbs.x = %d, expected 3.\n", fbs.x);
failures++;
}
}
static struct four_bits_plain {
char x : 4;
} fbp = {1};
static void test_four_bits_plain (void)
{
if (sizeof (struct four_bits_plain) != 1) {
printf ("Got sizeof (struct four_bits_plain) = %zu, expected 1.\n",
sizeof (struct four_bits));
failures++;
}
if (fbp.x != 1) {
printf ("Got fbp.x = %d, expected 1.\n", fbp.x);
failures++;
}
fbp.x = 3;
if (fbp.x != 3) {
printf ("Got fbp.x = %d, expected 3.\n", fbp.x);
failures++;
}
}
/*
Logic is somewhat diferent for bit-fields that end a struct vs
having additional fields.
*/
static struct four_bits_with_char {
unsigned char x : 4;
unsigned char y;
} fbi = {1, 2};
static void test_four_bits_with_char (void)
{
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 two_chars {
unsigned char x : 4;
unsigned char y : 4;
} o = {11, 7};
/* Tests that bit-fields can share allocation units. */
static void test_two_chars (void)
{
if (sizeof (struct two_chars) != 1) {
printf ("Got sizeof (struct two_chars) = %zu, expected 1.\n",
sizeof (struct two_chars));
failures++;
}
if (o.x != 11) {
printf ("Got o.x = %u, expected 11.\n", o.x);
failures++;
}
if (o.y != 7) {
printf ("Got o.y = %u, expected 7.\n", o.y);
failures++;
}
o.x = 3;
o.y = 4;
if (o.x != 3) {
printf ("Got o.x = %u, expected 3.\n", o.x);
failures++;
}
if (o.y != 4) {
printf ("Got o.y = %u, expected 4.\n", o.y);
failures++;
}
}
static struct full_width {
unsigned char x : 8;
} fw = {255};
static void test_full_width (void)
{
if (sizeof (struct full_width) != 1) {
printf ("Got sizeof (struct full_width) = %zu, expected 1.\n",
sizeof (struct full_width));
failures++;
}
if (fw.x != 255) {
printf ("Got fw.x = %u, expected 255.\n", fw.x);
failures++;
}
fw.x = 42;
if (fw.x != 42) {
printf ("Got fw.x = %u, expected 42.\n", fw.x);
failures++;
}
}
static struct aligned_end {
unsigned char : 2;
unsigned char x : 6;
unsigned char : 3;
unsigned char y : 5;
} ae = {63, 17};
static void test_aligned_end (void)
{
if (sizeof (struct aligned_end) != 2) {
printf ("Got sizeof (struct aligned_end) = %zu, expected 2.\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 = 15;
if (ae.x != 42) {
printf ("Got ae.x = %u, expected 42.\n", ae.x);
failures++;
}
if (ae.y != 15) {
printf ("Got ae.y = %u, expected 15.\n", ae.y);
failures++;
}
}
struct { signed char x : 1; } sc = {-1};
struct { unsigned char x : 1; } uc = {1};
struct { char x : 1; } pc = {1};
static void test_signedness (void)
{
if (sc.x != -1) {
printf ("Got sc.x = %d, expected -1.\n", sc.x);
failures++;
}
if (uc.x != 1) {
printf ("Got uc.x = %u, expected 1.\n", uc.x);
failures++;
}
if (pc.x != 1) {
printf ("Got pc.x = %u, expected 1.\n", pc.x);
failures++;
}
}
int main (void)
{
test_four_bits ();
test_four_bits_with_char ();
test_two_chars ();
test_full_width ();
test_aligned_end ();
test_signedness ();
printf ("failures: %u\n", failures);
return failures;
}

View File

@ -149,10 +149,117 @@ static void test_enum_bitfield_int(void)
}
}
/* Enum with underlying type unsigned char. */
enum e7uc {
E7UC_100 = 100,
};
static struct enum_bitfield_uchar {
enum e7uc x : 1;
enum e7uc y : 4;
enum e7uc z : 8;
} e7ucbf = {0, 10, E7UC_100};
static void test_enum_bitfield_uchar(void)
{
if (sizeof (struct enum_bitfield_uchar) != 2) {
printf ("Got sizeof(struct enum_bitfield_uchar) = %zu, expected 2.\n",
sizeof(struct enum_bitfield_uchar));
failures++;
}
if (e7ucbf.x != 0) {
printf ("Got e7ucbf.x = %u, expected 0.\n", e7ucbf.x);
failures++;
}
if (e7ucbf.y != 10) {
printf ("Got e7ucbf.y = %u, expected 10.\n", e7ucbf.y);
failures++;
}
if (e7ucbf.z != 100) {
printf ("Got e7ucbf.z = %u, expected 100.\n", e7ucbf.z);
failures++;
}
e7ucbf.x = -1; /* Will store 1. */
e7ucbf.y = -1; /* Will store 15. */
e7ucbf.z = 127;
/* Both signed char and unsigned char are converted to int in arithmetic expressions,
** so we write this test differently to enum_bitfield_int.
*/
if (e7ucbf.x != 1) {
printf ("Got e7ucbf.x = %u, expected 1.\n", e7ucbf.x);
failures++;
}
if (e7ucbf.y != 15) {
printf ("Got e7ucbf.y = %u, expected 15.\n", e7ucbf.y);
failures++;
}
if (e7ucbf.z != 127) {
printf ("Got e7ucbf.z = %u, expected 127.\n", e7ucbf.z);
failures++;
}
}
/* Enum with underlying type signed char. */
enum e8sc {
E8SC_M1 = -1,
E8SC_100 = 100,
};
static struct enum_bitfield_char {
enum e8sc x : 1;
enum e8sc y : 4;
enum e8sc z : 8;
} e8scbf = {0, 5, E8SC_100};
static void test_enum_bitfield_char(void)
{
if (sizeof (struct enum_bitfield_char) != 2) {
printf ("Got sizeof(struct enum_bitfield_char) = %zu, expected 2.\n",
sizeof(struct enum_bitfield_char));
failures++;
}
if (e8scbf.x != 0) {
printf ("Got e8scbf.x = %d, expected 0.\n", e8scbf.x);
failures++;
}
if (e8scbf.y != 5) {
printf ("Got e8scbf.y = %d, expected 10.\n", e8scbf.y);
failures++;
}
if (e8scbf.z != 100) {
printf ("Got e8scbf.z = %d, expected 100.\n", e8scbf.z);
failures++;
}
e8scbf.x = -1;
e8scbf.y = -3;
e8scbf.z = 127;
if (e8scbf.x != -1) {
printf ("Got e8scbf.x = %d, expected -1.\n", e8scbf.x);
failures++;
}
if (e8scbf.y != -3) {
printf ("Got e8scbf.y = %d, expected -3.\n", e8scbf.y);
failures++;
}
if (e8scbf.z != 127) {
printf ("Got e8scbf.z = %d, expected 127.\n", e8scbf.z);
failures++;
}
}
int main(void)
{
test_enum_bitfield_uint();
test_enum_bitfield_int();
test_enum_bitfield_uchar();
test_enum_bitfield_char();
printf("failures: %u\n", failures);
return failures;
}