Implemented integer overflow class

C++ No Comments »

Last night, I had some extra time so I implemented my idea for using x86 assembly to catch integer overflow errors. I believe the experiment was quite successful. Here’s a quick post-mortem:

  • After running into some bizarre, hard-to-track-down compiler problems related to templates (and the Microsoft compiler makes them more readable than most!), I decided against using partial template specialization. I justified my decision after the fact by reasoning that it doesn’t make sense to use partial template specialization unless you are also going to use the generic code (which is an option, I suppose, but I personally wouldn’t choose it).
  • I implemented the binary operator+ in terms of the operator+= member operator (and likewise for operator-). This is a common C++ technique I had forgotten about, as I don’t use C++ often. The code looks something like:

    SafeInt& operator+=(const SafeInt& rhs)
    {
        // Do work...
        return *this;
    }
    
    SafeInt operator+(const SafeInt& lhs, const SafeInt& rhs)
    {
        SafeInt sum = lhs;
        sum += rhs;
        return sum;
    }
    
  • I implemented the postfix operator++ in terms of the prefix operator++ (and likewise for operator--) as I saw in a piece of example code somewhere. The code looks something like:

    SafeInt& SafeInt::operator++() // Prefix
    {
        // Do work...
        return *this;
    }
    
    SafeInt SafeInt::operator++(int) // Postfix
    {
        SafeInt temp = *this;
        ++*this;
        return temp;
    }
    
  • For signed integers, I needed to use the imul opcode (instead of mul), which is probably obvious to anyone who regularly works in x86 assembly.
  • The actual code for performing the operation was closer to:

    SafeInt& SafeInt::operator+=(const SafeInt& rhs)
    {
        int iLhs = m_val;
        int iRhs = rhs.m_val;
        int sum;
    
        __asm {
            mov eax, iLhs
            mov ecx, iRhs
            add eax, ecx
            jo overflow
            mov sum, eax
        }
    
        m_val = sum;
        return *this;
    
    overflow:
        throw OverflowException();
    }
    

    I used temporary variables within the function because I was not otherwise able to successfully retrieve them from the inline assembly. I used ecx instead of ebx because the former is always preserved across an inline assembly section, while the latter will require the compiler to emit an extra push and pop.

    I stepped through the generated assembly in both the debug and release builds to see what it looked like. Unfortunately, it doesn’t seem as if the function was inlined, nor did the compiler optimize away some of the extra integer copies as I had hoped it would. Perhaps a few extra ns of performance can be squeezed out of this function by working on these problems, making it effectively costless, as the jo branch would presumably be correctly predicted. If only all methods of making code secure were as costless!

    Update 2006-10-04 12:33 PM: This assembly code is slow and needs to be optimized. See the below comment.

I did a quick test, and it seems that C# has retained C’s overflow semantics for addition. Personally, if I were designing a new language, I think I would seriously consider having addition throw an exception upon overflow. This has led me to the following thoughts:

  • How would one handle a language in which an operation as simple and basic as addition can throw an exception? Would you be try/catching addition operations frequently? Are there any situations nowadays where addition can throw an exception?
  • What if there were no operations which could be guaranteed to never throw an exception? Imagine a network-transparent programming language where underneath the operations were being sent out to multiple computers across the network — any operation could easily fail due to network failures — how would this affect your style of programming? Can you create a completely ’safe’ program if no operations were guaranteed to never throw an exception? (My hunch is no, for the same reason you can’t create a safe multithreaded program without atomic operations)
  • There seems to be some kind of fundamental impedence mismatch between out-of-bound exceptions and linear, imperative programming. For example, if a function fails but the caller is expecting and will immediately handle the failure, a return value denoting failure will simultaneously be cleaner code to write and much more efficient. Naturally, there are many compelling reasons to use exceptions, but the general, tautological rule of thumb has become Only use exceptions in exceptional situations. I will discuss error and exception handling in a future post.

Update 2006-10-04 12:26 PM: I see that Sree Kotay has demonstrated that my assembly version of the integer overflow handler is slower than the “tricky” pure-C MSDN implementation, and provides an even faster pure-C version (although I find it even more tricky!). That’s fine, as I didn’t spend any time working on the performance of my assembly version, I just wanted to illustrate that it was possible. Optimization would have come later. Presumably the assembly version can always be made at least as fast as the pure-C version — after all, you could use the compiled C code as a starting point.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Log in