When I shift a bit field in an expression small bit fields behave
one way and large bit fields another. I'm using gcc 5.3.0 on Arch Linux:

$ gcc --version
gcc (GCC) 5.3.0
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


I've created the following example:

#include <stdio.h>

struct fields {
  long long unsigned f0:12;
  long long unsigned f1:52;
} __attribute__((__packed__));

struct fields x = { .f0 = 0xFFF, .f1 = 0xFFFFFFFFFFFFF };

long long unsigned g0;
long long unsigned g1;

void main(void) {
  g0 = x.f0 << 1;
  g1 = x.f1 << 1;

  printf("x.f0=0x%llx\n", x.f0);
  printf("  g0=0x%llx expect 0x1ffe\n", g0);
  printf("x.f1=0x%llx\n", x.f1);
  printf("  g1=0x%llx expect 0x1ffffffffffffe\n", g1);
}

The output is:

$ gcc -m64 main.c -o main ; ./main
x.f0=0xfff
  g0=0x1ffe expect 0x1ffe
x.f1=0xfffffffffffff
  g1=0xffffffffffffe expect 0x1ffffffffffffe

As you can see when shifting the f0, which is 12 bits, the
most significant bit it is seen in the result. But when shifting f1,
which is 52 bits, it is lost.

Looking at the generated code the difference in output is
because in g0 = x.f0 << 1 the and operation is done before the "shift left"
using add %eax, %eax. Where as with g1 = x.f1 << 1 the and operation
is done after the "shift left" using add %rax, %rax".

0000000000400506 <main>:
  400506: 55                   push   %rbp
  400507: 48 89 e5             mov    %rsp,%rbp
  40050a: 0f b7 05 f7 04 20 00 movzwl 0x2004f7(%rip),%eax        # 600a08 <x>
  400511: 66 25 ff 0f           and    $0xfff,%ax
  400515: 0f b7 c0             movzwl %ax,%eax
  400518: 01 c0                 add    %eax,%eax
  40051a: 48 98                 cltq
  40051c: 48 89 05 fd 04 20 00 mov    %rax,0x2004fd(%rip)        # 600a20 <g0>
  400523: 48 8b 05 de 04 20 00 mov    0x2004de(%rip),%rax        # 600a08 <x>
  40052a: 48 c1 e8 0c           shr    $0xc,%rax
  40052e: 48 01 c0             add    %rax,%rax
  400531: 48 ba ff ff ff ff ff movabs $0xfffffffffffff,%rdx
  400538: ff 0f 00
  40053b: 48 21 d0             and    %rdx,%rax
  40053e: 48 89 05 d3 04 20 00 mov    %rax,0x2004d3(%rip)        # 600a18 <g1>


It feels wrong that they would behave differently, is this really
correct behavior?

-- Wink

Reply via email to