This project is read-only.

Why no assembly?

Jun 15, 2010 at 1:40 AM

I recently encountered the need to check for int overflows in order to speed up the computation in an application we are developing that involves big numbers. We basically use the same trick with extending the size of the operands and checking if the result fits. It is unusual to have to do this when the processors do offer overflow flags and we can access them (although not in a nice way). Since you definitely have more experience in developing with overflows, I was wondering why you are not doing something like this

#include <iostream>
#include <limits> 

using namespace std;

#undef max

#define CHECK_OVERFLOW(code, label) \
    code; \
    _asm { jo label } \ 

int main(int argc, char* argv[])
{
    int x, y = 0;
    x = numeric_limits<int>::max();
    CHECK_OVERFLOW(y = x + 1, overflow_label);
    CHECK_OVERFLOW(x = y + 1, overflow_label);   
    cout << "no overflow" << endl;
    return 0;
overflow_label:
    cout << "overflow" << endl;
}

Are there conditions where this would fail (like compiler optimizations), or is there other reasons that I'm not seeing?

 

Jun 15, 2010 at 9:02 AM

Also, after some hacking, I got an equivalent in gcc

#include <iostream>
#include <limits> 

using namespace std;

int main(int argc, char* argv[])
{    
    int x, y;
    bool overflow = false;
    x = numeric_limits<int>::max();
    y = x + 1;
    asm("jno no_overflow\n"
	"mov\t$1, %0\n"
        "no_overflow:\n"
       :"=r"(overflow)
       :"r"(overflow) 
       );
    if (overflow) cout << "overflow" << endl;
    else cout << "no overflow" << endl;
    return 0;
}
Cheers, Dejan

Jun 15, 2010 at 6:08 PM

The problem is two-fold. First, the overflow flag doesn't get set for everything. For example, assigning a 32-bit int to a 16-bit int is dangerous, but the CPU doesn't see an overflow. Adding two 16-bit ints won't set the overflow flag, either. There's a bunch of cases that don't set the overflow flag. I haven't checked, but I suspect that you could well run into the same thing on a 64-bit CPU where a 32-bit multiplication doesn't set the overflow flag, though it might because of different assembly instructions.

The second part of the problem is that as soon as you start using assembly, the compiler decides that you must know what you're doing and disables optimizations. Michael Howard and I tried a test case with multiplication, and the inline assembly came in as _much_ slower than optimized SafeInt.

We've recently been dinking with what produces the best assembly for a test case like uint32 * uint32, and it is all around upcasting to 64-bit, and then you get some very different numbers of branches depending on how you detect an overflow. If you _really_ want to get hard core about perf, what I'd suggest would be to take the approach I use in SafeInt for the specific operation you want, and then manipulate the logic until the optimizer gives you something you like.