Bug 92684 - bitfield behavior not matching the declared type
Summary: bitfield behavior not matching the declared type
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c (show other bugs)
Version: 9.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-11-27 02:29 UTC by Wu Xingbo
Modified: 2023-04-14 19:35 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Wu Xingbo 2019-11-27 02:29:44 UTC
found wrong data when compiled by gcc 9.2.0 (x86_64, -std=gnu11). Have not tried other gcc versions. The results are correct on clang 8.0.1.

Long story short: some bits are missing when using bitfields with uint64_t as the declared type. Maybe I should use a (u64) cast on every dereference to those fields. But the results are not always wrong. Maybe I should read some C11 specs. But consider the different behaviors between gcc and clang, I decide to report a bug here in hope to get some better help. Thanks.

This reproduces the results: gcc/clang -std=gnu11 main.c

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
typedef uint64_t u64;

struct st {
  union {
    struct {
      u64 v1:19;
      u64 v2:45;
    };
    void * ptr;
  };
};

struct bits {
  u64 bit1:1;
  u64 bitx:63;
};

  int
main(void)
{
  char * val = "0xffff0000fff8";
  // x has 48 valid bits, the three low bits are 0s
  volatile u64 x = strtoull(val, NULL, 16);
  printf("%lx\n", x);
  // st stores that 45 bits
  struct st st = {.v1 = 0, .v2 = (x >> 3)};
  printf("%p\n", st.ptr);
  // y should get the original bits; but the three high bits are gone.
  u64 y = st.v2 << 3;
  printf("%lx\n", y);


  // this is another (minor) related case.
  // some are correct and some are wrong.
  struct bits b = {.bit1 = 1, .bitx = 0};
  // this is more interesting. gcc shows "80000000", clang shows "1"
  printf("%lx %lx %lx\n", b.bit1, b.bit1 << 1, b.bit1 << 63);
  return 0;
}
Comment 1 Andrew Pinski 2019-11-27 02:35:10 UTC
-W -Wall:
t.c: In function ‘main’:
t.c:40:3: warning: left shift count >= width of type [enabled by default]
   printf("%lx %lx %lx\n", b.bit1, b.bit1 << 1, b.bit1 << 63);
   ^
t.c:40:3: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘int’ [-Wformat=]
t.c:40:3: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘int’ [-Wformat=]
t.c:40:3: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 4 has type ‘int’ [-Wformat=]
Comment 2 Andrew Pinski 2019-11-27 02:38:27 UTC
bit-fields in C is a type.
while in C++, it is not.

So:
st.v2 << 3

This is not ((long)st.v2) << 3 but rather (long:45)<<3.  So it gets truncated.

C11:
ffff0000fff8
0xffff0000fff80000
1fff0000fff8
1 2 ffffffff80000000

C++14:
ffff0000fff8
0xffff0000fff80000
ffff0000fff8
1 2 ffffffff80000000
Comment 3 Andrew Pinski 2019-11-27 02:40:28 UTC
There is a defect report dealing with this in C and such.
Both are correct for C11 but would be incorrect for C90 which defines bit-field types because of the defect report.  GCC decided to follow the C90 way because it is easier to implement one way for both.