0

In a C++ function, I have the following variables:

uint32_t buf = 229;  //Member variable
int bufSize = 0;     //Member variable
static constexpr const uint32_t all1 = ~((uint32_t)0);

And this line of code:

uint32_t result = buf & (all1 >> (32-bufSize ));

Then the value of result is 229, both by console output, or by debugging with gdb. The expected value is of course 0, and any trial to make a minimal example reproducing the problem failed.

So I went to the disassembly and executed it step by step. The bit shift operation is here:

0x44c89e  <+0x00b4>        41 d3 e8                 shr    %cl,%r8d

The debugger registers show that before the instruction, rcx=0x20 and r8=0xFFFFFFFF

After the instruction, we still have r8=0xFFFFFFFF

I have a very poor knowledge of x86-64 assembly, but this instruction is supposed to be an unsigned shift, so why the heck isn't the result 0?

I am using mingw-gcc x86-64 4.9.1

galinette
  • 8,896
  • 2
  • 36
  • 87
  • 6
    "The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand". (5.8, Shift operators.) – molbdnilo Dec 14 '16 at 10:40
  • 2
    For an x86, the hardware only uses the lower 5 bits of cl for the shift count. Once upon a time that reduced the interrupt latency. – Bo Persson Dec 14 '16 at 10:47
  • Invoke -Wall on compiler and you will have `-Wshift-count-overflow` problem. – Shiv Dec 14 '16 at 10:49
  • Look up [SHR in Intel's insn set ref](http://www.felixcloutier.com/x86/SAL:SAR:SHL:SHR.html). It masks the shift count. Some architectures saturate the shift count, or mask with a different limit, which is why C chooses to leave it undefined. Related: [a safe rotate in C with no undefined behaviour, that compiles to a single `ror`](http://stackoverflow.com/questions/776508/best-practices-for-circular-shift-rotate-operations-in-c) because the compiler knows that the masking in the C source matches what x86's ROR instruction does. – Peter Cordes Dec 14 '16 at 20:50

1 Answers1

2

Invoke -Wall on compiler and you will have -Wshift-count-overflow problem as you are using 32 for shifting which is size of unsigned int. Now you can do one thing just for knowing about it. Change 32 to 31 and compile. Then compare the assembly generated and you will know what went wrong.

The easiest fix for you would be use long data type for all1 and result.

Shiv
  • 1,912
  • 1
  • 15
  • 21