Cause Division Overflow Error (x86)

I have a few questions about overflow overflow errors in x86 or x86_64 architecture. Recently, I read about integer overflow. Typically, when an arithmetic operation results in an integer overflow, the carry bit or overflow bit is set in the FLAGS register. But apparently, according to this article , overflow resulting from division operations does not set the overflow bit, but rather causes a hardware exception, similar to when you divide by zero.

Now whole overflows resulting from division are much less common than they say multiplication. There are only a few ways to even cause unit overflows. One way is to do something like:

int16_t a = -32768; int16_t b = -1; int16_t c = a / b; 

In this case, due to two additional representations of signed integers, you cannot represent a positive 32768 in a 16-bit integer value, therefore, the division operation is overflowed, which leads to an incorrect value of -32768.

A few questions:

1) Contrary to what is said in this article, the foregoing did not cause a hardware exception. I use an x86_64 machine running Linux, and when I divide by zero, the program ends with a Floating point exception . But when I cause the unit to overflow, the program continues, as usual, silently ignoring the erroneous attitude. So why does this not cause a hardware exception?

2) Why are separation errors heavily handled by hardware, unlike other arithmetic overflows? Why should overflow overflow (which is much more likely to happen by accident) be ignored by the hardware, but should a unit overflow trigger a fatal interrupt?

============ EDIT ===============

Ok, thanks everyone for the answers. I got answers saying basically that the above 16-bit integer division should not cause a hardware error, since the factor is still smaller than the register size. I do not understand this. In this case, the register that stores the factor is 16-bit, which is too small to hold a positive positive 32768. So why is there no hardware exception?

Ok, do it right in the GCC inline assembly and see what happens:

 int16_t a = -32768; int16_t b = -1; __asm__ ( "xorw %%dx, %%dx;" // Clear the DX register (upper-bits of dividend) "movw %1, %%ax;" // Load lower bits of dividend into AX "movw %2, %%bx;" // Load the divisor into BX "idivw %%bx;" // Divide a / b (quotient is stored in AX) "movw %%ax, %0;" // Copy the quotient into 'b' : "=rm"(b) // Output list :"ir"(a), "rm"(b) // Input list :"%ax", "%dx", "%bx" // Clobbered registers ); printf("%d\n", b); 

This simply outputs the erroneous value: -32768 . There is still no hardware exception, even if the register holding factor (AX) is too small to fit the quotient. Therefore, I do not understand why no hardware error occurs here.

+7
c ++ c assembly x86 integer-division
source share
7 answers

In C, arithmetic operations are never performed within types smaller than int . Each time you try to do arithmetic on smaller operands, they first undergo integral actions that convert them to int . If your platform int has, say, 32-bit width, then there is no way to force C to perform 16-bit division. Instead, the compiler will generate 32-bit division. This is probably why your C experiment does not cause the expected overflow on separation. If your platform really has a 32-bit int , then it would be best to try the same thing with 32-bit operands (i.e. divide INT_MIN by -1 ). I'm sure you can reproduce the overflow exception even in C code.


In your assembler, you are using 16-bit division, since you specified BX as the operand for idiv . 16-bit division by x86 divides the 32-bit dividend stored in the DX:AX pair by the idiv operand. This is what you do in your code. The DX:AX pair is interpreted as one composite 32-bit register, which means that the sign bit in this pair is now actually the highest order bit of DX . The highest order bit AX no longer a sign.

And what did you do with the DX ? You just cleaned it. You set it to 0. But if DX set to 0, your dividend is interpreted as positive! From a machine point of view, such a pair of DX:AX actually a positive value of +32768 . That is, in your assembler experiment, you divide +32768 by -1 . And the result is -32768 , as it should be. Nothing unusual here.

If you want to represent -32768 in a pair of DX:AX , you need to sign-renew it, i.e. you must fill the DX all-in-one bit pattern instead of zeros. Instead of xor DX, DX you should initialize AX with -32768 and then do cwd . This will have an extended AX to DX mark.

For example, in my experiment (not GCC) this code

 __asm { mov AX, -32768 cwd mov BX, -1 idiv BX } 

raises the expected exception because it really is trying to split -32768 by -1 .

+13
source share

When you get an integer overflow with the whole 2 complement add / subtract / multiply, you still have a valid result - it just skips some high order bits. This behavior is often useful, so it would be inappropriate to throw an exception for this.

With integer division, however, the result of division by zero is useless (because, unlike floating point, integers do not have the INF representation).

+2
source share

Contrary to what is said in this article, the above did not raise a hardware exception

The article does not say this. He speaks

... they generate a split error if the source operand (divisor) is zero or if the factor is too large for the specified register

Register size defined above 16 bits (32 || 64)

+1
source share

In the relevant section on integer overflow :

Unlike add, mul, and imul instructions, Intel div div and idiv instructions do not set an overflow flag; they generate divisions if the source operand (divisor) is zero or if the factor is too large for the assigned register.

The register size on a modern platform is 32 or 64 bits; 32768 will go into one of these registers. However, the following code will most likely result in an integer overflow (it works on my main Duo laptop on VC8):

 int x= INT_MIN; int y= -1; int z= x/y; 
+1
source share
  • The reason your example did not throw a hardware exception is due to the whole rules for moving forward C. Operands smaller than int are automatically updated to ints before the operation is performed.

  • Regarding why different types of overflows are handled differently, keep in mind that at the x86 machine level there is actually no such multiplication overflow. When you multiply AX by some other register, the result is a pair of DX: AX, so there is always room for the result, and therefore there is no need to signal an overflow exception. However, in C and other languages, the product of two ints must match int , so overflow occurs at the C level. X86 sometimes sets OF (overflow flag) to MUL s, but that just means that the high part of the result is nonzero.

0
source share

In an implementation with a 32-bit int your example will not lead to overflow of division . This results in a perfectly representable int , 32768, which is then converted to int16_t according to the implementation when the job is done. This is due to the default promotion specified in C, and as a result, the implementation that caused the exception would be inappropriate.

If you want to try to throw an exception (which may or may not happen, this is before implementation), try:

 int a = INT_MIN, b = -1, c = a/b; 

You may need to do some tricks so that the compiler cannot optimize it at compile time.

0
source share

I would suggest that on some older computers, trying to divide by zero would lead to serious problems (for example, put the equipment in an endless loop to subtract it so that the remainder is less than the dividend until the operator appears to fix things), and this started the tradition overflow divisions, which are considered more serious deficiencies than whole overflow divisions.

From a programming point of view, there is no reason why an unexpected gap overflow should be more or less serious than an unexpected integer overflow (signed or unsigned). Given the cost of dividing, the marginal cost of checking the overflow flag would be pretty small. Tradition is the only reason I see a hardware trap.

0
source share

All Articles