diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index bb7c40476..e43af238e 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -1016,9 +1016,18 @@ const Type* IntPromotion (const Type* T) */ if (IsTypeBitField (T)) { - /* The standard rule is OK for now as we don't support bit-fields with widths > 16. + /* As we now support long bit-fields, we need modified rules for them: + ** - If an int can represent all values of the bit-field, the bit-field is converted + ** to an int; + ** - Otherwise, if an unsigned int can represent all values of the bit-field, the + ** bit-field is converted to an unsigned int; + ** - Otherwise, the bit-field will have its declared integer type. + ** These rules are borrowed from C++ and seem to be consistent with GCC/Clang's. */ - return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; + if (T->A.B.Width > INT_BITS) { + return IsSignUnsigned (T) ? type_ulong : type_long; + } + return T->A.B.Width == INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; } else if (IsTypeChar (T)) { /* An integer can represent all values from either signed or unsigned char, so convert ** chars to int. diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 67e9a1783..7cc7444b6 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -746,12 +746,10 @@ static int ParseFieldWidth (Declaration* D) D->Type[0].C = T_INT; } - /* TODO: This can be relaxed to be any integral type, but - ** ParseStructInit currently supports only up to int. - */ - if (SizeOf (D->Type) > SizeOf (type_uint)) { - /* Only int-sized or smaller types may be used for bit-fields, for now */ - Error ("cc65 currently supports only char-sized and int-sized bit-field types"); + /* We currently support integral types up to long */ + if (SizeOf (D->Type) > SizeOf (type_ulong)) { + /* Only long-sized or smaller types may be used for bit-fields, for now */ + Error ("cc65 currently supports only long-sized and smaller bit-field types"); /* Avoid a diagnostic storm */ D->Type[0].C = T_INT; diff --git a/src/cc65/initdata.c b/src/cc65/initdata.c index 1f8b35ce4..99dacdca9 100644 --- a/src/cc65/initdata.c +++ b/src/cc65/initdata.c @@ -505,13 +505,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) */ if (SymIsBitField (Sym) && (IsAnonName (Sym->Name))) { /* Account for the data and output it if we have at least a full - ** word. We may have more if there was storage unit overlap, for - ** example two consecutive 10 bit fields. These will be packed - ** into 3 bytes. + ** byte. We may have more if there was storage unit overlap, for + ** example two consecutive 7 bit fields. Those would be packed + ** into 2 bytes. */ SI.ValBits += Sym->Type->A.B.Width; + CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); /* TODO: Generalize this so any type can be used. */ - CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); + CHECK (SI.ValBits <= LONG_BITS); while (SI.ValBits >= CHAR_BITS) { DefineBitFieldData (&SI); } @@ -530,45 +531,51 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Parse initialization of one field. Bit-fields need a special ** handling. */ - ExprDesc ED; - ED_Init (&ED); - unsigned Val; + ExprDesc Field; + ED_Init (&Field); + unsigned long Val; unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; + unsigned long Mask = shl_l (1UL, Sym->Type->A.B.Width) - 1UL; /* Safety ... */ CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); - if (!ED_IsConstAbsInt (&ED)) { + Field = ParseScalarInitInternal (IntPromotion (Sym->Type)); + if (!ED_IsConstAbsInt (&Field)) { Error ("Constant initializer expected"); - ED_MakeConstAbsInt (&ED, 1); + ED_MakeConstAbsInt (&Field, 1); } /* Truncate the initializer value to the width of the bit-field and check if we lost ** any useful bits. */ - Val = (unsigned) ED.IVal & Mask; + Val = (unsigned long) Field.IVal & Mask; if (IsSignUnsigned (Sym->Type)) { - if (ED.IVal < 0 || (unsigned long) ED.IVal != Val) { - Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" - " changes value from %ld to %u", - GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->Type->A.B.Width, ED.IVal, Val); + if (Field.IVal < 0 || (unsigned long) Field.IVal != Val) { + Warning (IsSignUnsigned (Field.Type) ? + "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %lu to %lu" : + "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %ld to %lu", + GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, Field.IVal, Val); } } else { /* Sign extend back to full width of host long. */ unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; - long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); - if (ED.IVal != RestoredVal) { - Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " - "changes value from %ld to %ld", - GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->Type->A.B.Width, ED.IVal, RestoredVal); + long RestoredVal = asr_l (asl_l (Val, ShiftBits), ShiftBits); + if (Field.IVal != RestoredVal) { + Warning (IsSignUnsigned (Field.Type) ? + "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %lu to %ld" : + "Implicit truncation from '%s' to '%s : %u' in bit-field initializer" + " changes value from %ld to %ld", + GetFullTypeName (Field.Type), GetFullTypeName (Sym->Type), + Sym->Type->A.B.Width, Field.IVal, RestoredVal); } } @@ -578,15 +585,15 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) /* Account for the data and output any full bytes we have. */ SI.ValBits += Sym->Type->A.B.Width; - /* Make sure unsigned is big enough to hold the value, 22 bits. - ** This is 22 bits because the most we can have is 7 bits left - ** over from the previous OutputBitField call, plus 15 bits - ** from this field. A 16-bit bit-field will always be byte - ** aligned, so will have padding before it. + /* Make sure unsigned is big enough to hold the value, 32 bits. + ** This cannot be more than 32 bits because a 16-bit or 32-bit + ** bit-field will always be byte-aligned with padding before it + ** if there are bits from prior fields that haven't been output + ** yet. */ CHECK (SI.ValBits <= CHAR_BIT * sizeof(SI.BitVal)); /* TODO: Generalize this so any type can be used. */ - CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); + CHECK (SI.ValBits <= LONG_BITS); while (SI.ValBits >= CHAR_BITS) { DefineBitFieldData (&SI); }