Liam White
Avoiding signed overflow with MSVC is unnecessarily inefficient

GCC and clang offer a number of special intrinsics for avoiding signed overflow, which can be conveniently used like so:

bool CanAddWithoutOverflow(int32_t a, int32_t b) {
    int32_t dest;
    return !__builtin_add_overflow(a, b, &dest);
}

Since this does not cause overflow if the operation would not overflow, this operation is safe to perform with any values of a and b. On x86 this also results in very simple code generation:

CanAddWithoutOverflow(int, int):
        add     edi, esi
        setno   al
        ret

MSVC offers many intrinsics, but none of them can be used to determine if an operation overflowed. This means that all of the cases where overflow could occur have to be explicitly checked. The cleanest way I found to do this was to do the operation using unsigned integers and then check if the result makes sense when reinterpreted as signed:

int32_t WrappingAdd(int32_t a, int32_t b) {
    const uint32_t a_u = std::bit_cast<uint32_t>(a);
    const uint32_t b_u = std::bit_cast<uint32_t>(b);

    return std::bit_cast<int32_t>(a_u + b_u);
}

bool CanAddWithoutOverflow(int32_t a, int32_t b) {
    if (a >= 0 && b >= 0) {
        return WrappingAdd(a, b) >= std::max(a, b);
    } else if (a < 0 && b < 0) {
        return WrappingAdd(a, b) <= std::min(a, b);
    } else {
        return true;
    }
}

Predictably, MSVC cannot optimize this, so you end up with a rather large pile of code to handle this scenario correctly.

a$ = 8
b$ = 16
bool CanAddWithoutOverflow(int,int) PROC             ; CanAddWithoutOverflow, COMDAT
        mov     eax, ecx
        test    ecx, ecx
        js      SHORT $LN17@CanAddWith
        test    edx, edx
        js      SHORT $LN4@CanAddWith
        cmp     ecx, edx
        mov     r8d, ecx
        cmovl   r8d, edx
        add     eax, edx
        cmp     eax, r8d
        setge   al
        ret     0
$LN17@CanAddWith:
        test    edx, edx
        jns     SHORT $LN4@CanAddWith
        cmp     edx, eax
        cmovl   ecx, edx
        add     eax, edx
        cmp     eax, ecx
        setle   al
        ret     0
$LN4@CanAddWith:
        mov     al, 1
        ret     0