Menu

#2406 STM8 - Wrong Byte Order in Bitfield Elements

closed-rejected
nobody
None
STM8
5
2015-08-10
2015-08-03
Wolle K.
No

For bitfield elements > 8 bits vales are stored low part at low address.
Example:

// RTC_SHIFTR
volatile __at(ADR_RTC + 0x1A) union {
uint16_t r;
struct {
uint16_t
subfs: 15,
add1s: 1;
};
} rtc_shift;

//...
// code in some function:

rtc_shift.r = 0x1002;
/...
rtc_shift.subfs = 0x1002;

compiles to:
; rtc_shift.r = 0x1002;
ldw x, #_rtc_shift+0
ldw y, #0x1002
ldw (x), y ; (_rtc_shift + 0) = 0x10, (_rtc_shift + 1) = 0x02

; rtc_shift.subfs = 0x1002;
ldw x, #_rtc_shift+0
ld a, #0x02
ld (x), a ; (_rtc_shift + 0) = 0x02
ld a, #0x10
and a, #0x7f
push a
ld a, #0x80
and a, (0x1, x)
or a, (1, sp)
ld (0x1, x), a ; (_rtc_shift + 1) && 0x7F = 0x10
pop a

SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8 3.5.2 #9280 (MINGW32)

Discussion

  • Ben Shi

    Ben Shi - 2015-08-04

    I do not think this is a bug. Since endianess only defines the byte order within an int/long, not for a struct / union / array / bit_field. So
    struct {
    char a1;
    char b2;
    }
    can only result a1 is stored in lower byte and b2 in upper byte. And for your case, add1s is stored in the upper byte.

    I guess you expect rtc_shift.r = 0x1002 has the same effect with rtc_shift.subfs = 0x1002, right ? Unfortunately that is impossible given add1s is stored in the upper byte.

    Consider rtc_shift.r = 0x7FFF, you will actually get rtc_shift.subfs equals to 0x7F7F, if subfs is also in big endian.

     
  • Erik Petrich

    Erik Petrich - 2015-08-04

    I don't think this is a bug. I think you are assuming things that the standard leaves for the implementation to define.

    In the C standard (I have ISO/IEC 9899:1999 here; may be a different section in earlier or later versions) 6.7.2.1 paragraph 10:

    An implementation may allocate any addressable storage unit large enough to hold a bit-field. [...] The order of allocation of bit-fields within a unit (high-order to lower-order or low-order to high-order) is implementation-defined.

    And then 6.2.6.1 paragraph 4

    Values stored in non-bit-field objects of any other object type [not unsigned bit-fields or unsigned char] consist of n x CHAR_BIT bits, where n is the size of an object of that type, in bytes. The value may be copied into an object of type unsigned char [n] (e.g., by memcpy); the resulting set of bytes is called the object representation of the value. Values stored in bit-fields consist of m bits, where m is the size specified for the bit-field. The object representation is the set of m bits the bit-field comprises in the addressable storage unit hold it. The values (other than Nans) with the same object representation compare equal, but values that compare equal may have different object representations.

    Bolding is mine. My interpretation is that the bit-fields are not required to have the same representation as a non-bit-field element, even if they hold the same value.

    For the specific case of SDCC, the bit-fields are always allocated and processed in a little endian representation, regardless of any particular endianness of the underlying processor.

     
  • Philipp Klaus Krause

    I don't see a bug here either.
    Using big-endian might be more efficient for some-use cases (since we could use ldw instructions in more scenarios), but less efficient for others (since little-endian storage makes more bytes of each bitfield byte-aligned, reducing the number of shifts necessary, for the common case of a bigger bitfield variable being aligned to a byte boundary - the code in the original post is an example : little-endianness allows us to just sign the lower byte of subfs).
    Which one is better for which scenario might need more investigation, using bitfield-using code as benchmark. Unless sdcc users complain about bitfield-access efficiency in the stm8 port I would not consider that high priority for now though.

    Philipp

     
  • Maarten Brock

    Maarten Brock - 2015-08-04

    I agree this is not a bug against the standard. But it still feels weird that sdcc uses a different endianness for bitfields from what it uses for normal ints. And I fail to see the point. I also don't see how it requires less shifts. In this particular case I would expect (rtcshift + 0) = 0x10 in both cases. I wonder what happens when you change subfs to a 16 bits bitfield.

     
    • Philipp Klaus Krause

      According to my understanding:
      Store an n > 8 bit bitfield in a byte-aligned little-endian bitfield: All bytes except for the most significant can be just copied, most significant byte needs masking, no shifting necessary.
      Store an n > 8 bitfield in a byte-aligned big-ednidan bitfield: The most signifiant 8 bits need to go to the lowest address. The least significant byte needs masking. This means all bytes need some shifting.

      Philipp

      P.S.: I didn't think much about this when implementing bitfields for the stm8. I gues I was just too used to little-endian from working on the z80-related ports earlier.

       
      • Maarten Brock

        Maarten Brock - 2015-08-04

        Oops, I'm sorry. Shifting is indeed required in such a case. And thus in the bitfield case it would be (rtcshift + 0) = 0x20 following the big-endian format.

         
      • Ben Shi

        Ben Shi - 2015-08-04

        i would vote for keeping current situation, since it neither violate any standard nor generate inefficient assembly.

         
  • Ben Shi

    Ben Shi - 2015-08-10
    • status: open --> closed-rejected
     
  • Ben Shi

    Ben Shi - 2015-08-10

    Closed. Since

    1. it does not violate any standard,
    2. It generates efficient assembly. (both big-endian and little endian need masking, but BE need extra shifting, while LE does not.)
     

Log in to post a comment.