How __builtin_add_overflow Detects C Overflows

Compiler built-ins like __builtin_add_overflow allow developers to perform arithmetic operations while reliably catching integer overflows and calculation wraps. In the C programming language, signed integer overflow results in undefined behavior, which often causes standard manual safety checks to be optimized away by modern compilers. Built-in functions resolve this issue portably by emitting hardware-level instruction flags when available or falling back to safe, standards-compliant arithmetic logic that prevents undefined behavior.

The Problem with Manual Overflow Detection

In standard C, checking for overflow after performing an operation is inherently problematic:

// Flawed signed overflow check
int c = a + b;
if (c < a) {
    // Handle overflow
}

Because signed overflow invokes undefined behavior according to the C standard, the optimizer assumes a + b never overflows. Consequently, it treats a + b < a as mathematically impossible for positive values and strips the condition entirely.

While unsigned arithmetic wraps around reliably according to modulo \(2^N\) rules, manually checking for wraps requires writing extra branches before performing the operation, which introduces runtime overhead and boilerplate code.

Direct Access to Hardware Condition Flags

Modern CPU architectures—including x86-64, ARM, and PowerPC—automatically evaluate arithmetic operations and update status register flags in hardware during the execution cycle:

  • Carry Flag (CF): Tracks unsigned arithmetic overflow.
  • Overflow Flag (OF): Tracks signed arithmetic overflow when the sign bit changes unexpectedly.

When you use __builtin_add_overflow(a, b, &result), the compiler does not generate separate comparison checks. Instead, it emits the native addition instruction (such as add on x86-64 or adds on ARM) and directly queries the condition flag via conditional set or conditional jump instructions (such as seto/setc or jo/jc). This performs the addition and the overflow check in minimal CPU cycles.

Portable Software Fallbacks

When compiling for architectures that lack dedicated condition code registers (such as basic RISC architectures), the compiler substitutes the intrinsic with safe, platform-agnostic lowering logic during code generation.

Instead of generating code that risks undefined behavior, the compiler generates branchless bitwise operations or pre-condition checks:

  • Unsigned Operations: The compiler verifies if the computed sum is strictly less than either operand (sum < a).
  • Signed Operations: The compiler uses sign-extension or bitwise masks to evaluate whether the operands share the same sign while the resulting sum produces a different sign: ((a ^ sum) & (b ^ sum)) < 0.

Because the compiler controls this lowering step directly inside the intermediate representation (IR), it guarantees that the checks are not discarded during dead-code elimination.

Type Genericity and C23 Adoption

A significant advantage of __builtin_add_overflow is its type-generic interface:

bool __builtin_add_overflow(type1 a, type2 b, type3 *res);

The arguments a, b, and *res can be any integer type, signed or unsigned, and can even differ in width. The compiler determines the target width and signedness at compile time, applies appropriate sign or zero extensions, and flags an overflow if the mathematically correct result cannot fit into *res.

Due to the success and portability of these compiler intrinsics across GCC and Clang, this mechanism was formally standardized in C23 under the <stdckdint.h> header using ckd_add(), bringing portable hardware-level wrap detection natively into the standard C language.