Union of unsigned long long bit fields not starting at same memory location (undefined behavior?)

Running on a ADSP-21584, I noticed a variable not clearing correctly. Looking into it a bit more I noticed that I had a union whose members were not matching up (one of the members was offset 32 bits). 

The FaultCode starts at address ending 00, however member Type starts at address ending 04. This also corresponds length-wise: all members are 64 bits, and thus member Type ends above where we need it to. As far as I understand, all union entries should start at address ending 00.

I'll add the definition and declaration of FaultCode below the line. 

I did notice that an unsigned long long has a size of 0x8 in the memory map, and FaultCode has a size of 0x10 indicating that it is allocating more memory. When I remove the Type member I do see the size change to 0xC.

A couple things of note; I added the align 32 pragma but according to the manual I shouldn't need it (unions are guaranteed to be on valid boundaries). Does Sharc support 64 bit fields? I didn't see anything prohibiting it beyond MISRA advisories, so is this behavior (unsigned long long bit fields with unions) undefined in the SHARC?

============================================================

#pragma align 32
LOCATION union FaultCodeUnion FaultCode;
#define BOARD_TEST
#ifdef BOARD_TEST
#pragma align 32
LOCATION union FaultCodeUnion BoardTestFaults;
#endif

union FaultCodeUnion {
   struct { // Bit Position
      unsigned long long Fault1 : 1; // 0
      //... bits 1 - 30 follow the same pattern ....
      unsigned long long reserved7 : 1; // 31
      union {
         unsigned long long All : 8;
         struct {
            unsigned long long Fault33 : 1; // 32
            //... bits 33 - 38 follow the same pattern ...
            unsigned long long Fault40 : 1; // 39
         };
      } Fpga;
      unsigned long long Fault41 : 1; // 40
      //... bits 41 - 62 follow the same pattern ....
      unsigned long long Fault64 : 1; // 63
   } Bit;
   struct {
      unsigned long long TempFaults : 9;
      union {
         unsigned long long All :53;
         struct {
            unsigned long long PumpFaults :44;
            unsigned long long NA : 9;
         } EMP;
      } Active;
      unsigned long long PassiveFaults : 2;
   } Type;
   struct {
      unsigned short Short0;
      unsigned short Short1;
      unsigned short Short2;
      unsigned short Short3;
   } Short;
   struct {
      unsigned int IntL;
      unsigned int IntH;
   } Int;
   unsigned long long All;
};

  • Note that I see the same behavior when I swap Type and Bit: Type is in the original memory location with Short / Int / All and Bit is shifted by 32 bits. It seems like the first bit field is in the correct location where the second bit field is shifted... is this behavior undefined?

  • 0
    •  Analog Employees 
    on Sep 1, 2021 2:09 PM in reply to AnthonyR

    Hello,

    Please note that, the align pragma can be used before variable declarations and field declarations. It applies to the variable or field declaration that immediately follows the pragma.(only affects the next symbol or field defined)

    For example,
    struct s{
    #pragma align 8 /* field a aligned on 8-byte boundary */
    int a;
    int bar;
    #pragma align 16 /* field b aligned on 16-byte boundary */
    int b;
    } t[2];

    #pragma align 8
    int i1, i2, i3; // pragma only applies to i1

    The align pragma only applies to the immediately-following definition, even if that definition is part of a list.

    Bit-fields are aligned such that they do not cross a 32-bit word boundary (for bit-fields of type char, short, int or long) or a 64-bit boundary (for bit-fields of type long long). For example, a 24-bit bit-field can be placed immediately after an 8-bit bit-field, but a 25-bit bit-field member will be aligned on the next 32-bit boundary.

    Also, by default, the compiler inserts any necessary padding to ensure that elements are aligned on their required boundaries.

    In your case, we understood that,#pragma align 32 have effect only on first field of your union FaultCode and other members doesn't have pragma effect. Hence, other union members were not matching offset.

    Regards,
    Santhakumari.K

  • Thank you. Bit-fields being aligned like that explain what I'm seeing pretty well in the starting location of each union element. When I update the pragma in the actual union I see everything align correctly.

    -------

    Regarding the 32-bit word boundary and padding I believe that is what I am seeing here:

       struct { // Bit Position
          unsigned long long Fault1 : 1; // 0
          //... bits 1 - 30 follow the same pattern ....
          unsigned long long reserved7 : 1; // 31
          union {
             unsigned long long All : 8;
             struct {
                unsigned long long Fault33 : 1; // 32
                //... bits 33 - 38 follow the same pattern ...
                unsigned long long Fault40 : 1; // 39
             };
          } Fpga;
          unsigned long long Fault41 : 1; // 40
          //... bits 41 - 62 follow the same pattern ....
          unsigned long long Fault64 : 1; // 63
       } Bit;

    The FPGA field is on the next 32-bit boundary. If I move the FPGA field up I see that in the original 32-bit boundary and the remaining items are on the next 32-bit boundary.

    The below uses the Fpga structure above, but only has 17 bits above it, and the remaining bits (to a total of 64 total bits) below it. I assume adding the FPGA union in the middle of a bit-field like this causes it to be treated like a 32-bit structure, hence the padding to the next boundary despite being a 64-bit long long?