Skip to main content

IAR Embedded Workbench for RL78 5.20

Bitfields

In this section:

In Standard C, int, signed int, and unsignedint can be used as the base type for integer bitfields. In standard C++, and in C when language extensions are enabled in the compiler, any integer or enumeration type can be used as the base type. It is implementation-defined whether a plain integer type (char, short, int, etc) results in a signed or unsigned bitfield.

In the IAR C/C++ Compiler for RL78, plain integer types are treated as signed.

Bitfields in expressions are treated as int if int can represent all values of the bitfield. Otherwise, they are treated as the bitfield base type.

If you are using the V2 calling convention as the default calling convention for the project, each bitfield is placed in the next suitably aligned container of its base type that has enough available bits to accommodate the bitfield. Within each container, the bitfield is placed in the first available byte or bytes, taking the byte order into account. Note that containers can overlap if needed, as long as they are suitably aligned for their type.

In addition, the compiler supports an alternative bitfield allocation strategy (disjoint types), where bitfield containers of different types are not allowed to overlap. Using this allocation strategy, each bitfield is placed in a new container if its type is different from that of the previous bitfield, or if the bitfield does not fit in the same container as the previous bitfield. Within each container, the bitfield is placed from the least significant bit to the most significant bit (disjoint types) or from the most significant bit to the least significant bit (reverse disjoint types). This allocation strategy will never use less space than the default allocation strategy (joined types), and can use significantly more space when mixing bitfield types.

Note

If you are using the V1 calling convention as the default calling convention for the project, disjoint types is the only available allocation strategy.

If you are using the V2 calling convention as the default calling convention for the project, you can use the #pragma bitfields directive to choose which bitfield allocation strategy to use, see bitfields. If you use the disjoint types allocation strategy, you can also use the directive #pragma bitfields to place bitfields from the most significant bit to the least significant bit in each container.

Assume this example:

struct BitfieldExample
{
  uint32_t a : 12;
  uint16_t b : 3;
  uint16_t c : 7;
  uint8_t  d;
};
The example in the joined types bitfield allocation strategy

To place the first bitfield, a, the compiler allocates a 32-bit container at offset 0 and puts a into the first and second bytes of the container.

For the second bitfield, b, a 16-bit container is needed and because there are still four bits free at offset 0, the bitfield is placed there.

For the third bitfield, c, as there is now only one bit left in the first 16-bit container, a new container is allocated at offset 2, and c is placed in the first byte of this container.

The fourth member, d, can be placed in the next available full byte, which is the byte at offset 3.

Each bitfield is allocated starting from the least significant free bit of its container to ensure that it is placed into bytes from left to right.

LayoutBitfieldJoinedLEmode_100percent.png
The example in the disjoint types bitfield allocation strategy

To place the first bitfield, a, the compiler allocates a 32-bit container at offset 0 and puts a into the least significant 12 bits of the container.

To place the second bitfield, b, a new container is allocated at offset 4, because the type of the bitfield is not the same as that of the previous one. b is placed into the least significant three bits of this container.

The third bitfield, c, has the same type as b and fits into the same container.

The fourth member, d, is allocated into the byte at offset 6. d cannot be placed into the same container as b and c because it is not a bitfield, it is not of the same type, and it would not fit.

When using reverse order (reverse disjoint types), each bitfield is instead placed starting from the most significant bit of its container.

This is the layout of bitfield_example:

LayoutBitfieldDisjointLittleEmode 100percent.png
Padding

Padding is usually added to the end of structures to accommodate reading/writing an entire bitfield container when accessing bitfields, as shown above. However, when bits are allocated from low to high addresses, padding is only added if it is needed to accommodate the alignment of the field.

Example:

struct X { uint32_t x1 : 5; };

When the alignment of the uint32_t bitfield is 4, the size of struct X is 4, to enable reading/writing the entire bitfield container (uint32_t) at its natural alignment. However, if the alignment of the field is lower (for example, by using #pragma pack), and bits are allocated from low addresses, the size of struct X is also correspondingly less.