A compiler bug when a uint32_t is logical right shifted with 32 bit on RV32


(daiw) #1

In following code, when b=32, c should be 0, but the result from gcc for RV32 is 0xFFFFFFFF.
So I think it is a bug !

#include <stdint.h>

int checkShift(void)
{
uint32_t a, b, c, d;
d = 0;
a = 0xFFFFFFFF;
for (b=30; b<34; b++) {
c = a >> b;
d += c;
}

return (int)d;

}


(Bruce Hoult) #2

No, it’s correct.

According to the C standard, the result of shifting by more than the word size is undefined.

The RISC-V specification says that only the lowest 5 bits of the shift amount are used. In other words when you write “c = a >> b” the result is the same as if you wrote “c = a >> (b & 0x1f)”

You’ll get exactly the same results if you run your program on i386/i686 (32 bit x86).

ARM CPUs will give you the 0 result you seem to be looking for if you shift by 32 bits or more.

If you write your code as either “c = b < 32 ? a >> b : 0” or “c = a >> (b & 0x1f)” then you will avoid undefined behavior and get the same results on all CPU types. depending on which behavior you want.


(Jim Wilson) #3

GCC has a -fsanitize=undefined option you can use to find such problems. It inserts extra code to do run-time checks for undefined behavior. Modifying the example to have a main function that calls checkShift and compiling and running it on x86_64-linux I get

rohan:2016$ gcc -fsanitize=undefined -O tmp.c
rohan:2017$ ./a.out
tmp.c:9:7: runtime error: shift exponent 32 is too large for 32-bit type ‘unsigned int’
rohan:2018$