Reputation: 635
What is an easy way to check after the SHL EAX, CL
instruction whether the theoretical result (EAX times (2 power CL)) fits into EAX? The question is about unsigned integers.
I hoped for something like checking the carry flag in the case of ADD
instructions or checking EDX in the case of MUL
instructions.
Upvotes: 3
Views: 467
Reputation: 4572
You have not defined in what sense a method should be "easy", but if you afford two registers for temporary values, the check is trivial - just shift back (reverse direction) by the same width with the same shift kind ("logical" vs. "arithmetic"), and compare with the original value. This would look like:
; esi, edi - arbitrary choice
mov esi, eax ; save the original value to later comparison
shl eax, cl ; <-- the shift to check
mov edi, eax
shr edi, cl ; <-- the opposite action
cmp esi, edi ; equal to the original value?
jne overflow_happened
This method is universal for processors that have cheap shifts (99.9%, with barrel shifter), and among all four variants of single linear shifts (sal, shl, sar, shr - for x86). It automatically "adjusts" its logic for the shift type. Also, you don't need to calculate a mask, test against two different signs (for signed values), etc.
For ISAs where shifts donʼt set condition codes at all (even if they are present, like in AArch64), or condition codes donʼt reflect full-width number interpretation (like in x86), the described method is the only really easy. So universal compilers tend to use it over different architectures. A few architectures (example: SystemZ) reflect arithmetic result in condition codes, but legacy - and easiness of reverse check - resulted in absence of this beside rare cases.
Upvotes: 3
Reputation: 58548
You can't do this by checking after the instruction. It does not record information about unsigned overflow, i.e. whether any 1 bits were shifted out. The carry flag only contains the last bit that was shifted out.
For instance, if EAX = 0xF0000000 and CL = 7, then SHL EAX, CL
will leave EAX = 0 and the carry flag clear. You would get exactly the same architectural result, including all the other flag values, if the input was EAX = 0x00000000. So even though one has an overflow and the other does not, there is no way to distinguish them after the fact.
You can test before the instruction whether it will overflow, by checking if the top CL bits of EAX are zero. For instance (untested):
MOV EBX, 0x80000000
SAR EBX, CL ; now top CL+1 bits of EBX are 1
SHL EBX, 1 ; now top CL bits of EBX are 1
TEST EBX, EAX ; mask off all lower bits
JNZ will_overflow
Some other possible algorithms (maybe better) were suggested in the comments, such as SHLD
.
Just because I happened to be thinking about it, here's a check for signed overflow, which occurs if and only if the top CL+1 bits are not all equal.
MOV EBX, 0x80000000
SAR EBX, CL ; now top CL+1 bits of EBX are 1
MOV EDX, EAX ; copy the input to be shifted
AND EDX, EBX ; mask off all but top CL+1 bits
JZ no_overflow ; top bits all 0
CMP EDX, EBX
JE no_overflow ; top bits all 1
; else handle overflow
For the one-bit shift instruction SHL EAX, 1
, then the carry flag does exactly indicate whether unsigned overflow occurs. Also, the overflow flag indicates whether signed overflow occurs. In fact, all of CF, OF, ZF, SF, PF are set exactly the same as they would be for the mathematically equivalent ADD EAX, EAX
. So if you are optimizing for space over speed, then instead of SHL EAX, CL
, you could consider a loop, iterating CL times and executing SHL EAX, 1
followed by JC overflow_occurred
on each iteration.
Upvotes: 2