We have a code in production that in some situation may left-shift a 32-bit unsigned integer by more than 31 bits. I know this is considered undefined behavior. Unfortunately we can't fix this right now, but we can work this around, if only we can assume how it works in practice.
On x86/amd64 I know processor for shifts uses only the appropriate less-significant bits of the shift count operand. So that a << b
is in fact equivalent to a << (b & 31)
. From the hardware design this makes perfect sense.
My question is: how does this work in practice on modern popular platforms, such as arm, mips, RISC and etc. I mean those that are actually used in modern PCs and mobile devices, not outdated or esoteric.
Can we assume that those behave the same way?
EDIT:
The code I'm talking about currently runs in a blockchain. It's less important how exactly it works, but at the very least we want to be sure that it yields identical results on all the machines. This is the most important, otherwise this can be exploited to induce a so-called chain split.
Fixing this means hassles, because the fix should be applied simultaneously to all the running machines, otherwise we are yet again at risk of the chain split. But we will do this at some point in an organized (controlled) manner.
Lesser problem with the variety of compilers. We only use GCC. I looked at the code with my own eyes, there's a shl
instruction there. Frankly I don't expect it to be anything different given the context (shift operand comes from arbitrary source, can't be predicted at compile time).
Please don't remind me that I "can't assume". I know this. My question is 100% practical. As I said, I know that on x86/amd64 the 32-bit shift instruction only takes 5 least significant bits of the bit count operand.
How does this behave on current modern architectures? We can also restrict the question to little-endian processors.
It is UB in C. Here you have an example: https://godbolt.org/z/5h9f7W6rr
It does not have any practical use unless you use inline assembly. But if you want how particular platforms handle left shift assembly instructions you need to see their assembly language references:
ARM-Thumb
If n is 32 or more, then all the bits in the result are cleared to 0.
If n is 33 or more and the carry flag is updated, it is updated to 0.
x86 - shift is performed modulo 32.