GLSL uaddCarry: Unsigned Addition with Carry

The GLSL built-in function uaddCarry adds two 32-bit unsigned integers or vectors while simultaneously calculating whether an arithmetic overflow occurred. Instead of discarding the excess high-order bit, uaddCarry outputs the standard modulo \(2^{32}\) sum as its return value and writes the resulting overflow (the carry-out bit) into an out parameter. This mechanism enables precise multi-word arithmetic on GPUs, allowing shaders to execute extended-precision integer calculations that exceed standard 32-bit register capacities.

Function Signature and Mechanism

The standard declaration for uaddCarry in GLSL supports both scalar and vector types:

genUType uaddCarry(genUType x, genUType y, out genUType carry);

Here, genUType represents uint, uvec2, uvec3, or uvec4.

When the function executes, it performs addition on the unsigned arguments x and y:

  1. Sum Evaluation: The primary operation calculates \((x + y) \pmod{2^{32}}\). This 32-bit truncated sum is returned directly by the function.
  2. Carry Evaluation: If the mathematical sum of \(x\) and \(y\) is greater than or equal to \(2^{32}\) (equivalent to 0x100000000u), an arithmetic overflow has occurred. The function assigns 1u to the carry variable. If no overflow occurs, carry is set to 0u.

Component-Wise Processing

When used with vector types (uvec2, uvec3, uvec4), uaddCarry operates strictly component-wise. The addition and carry generation for each vector lane occur independently:

uvec2 a = uvec2(0xFFFFFFFFu, 5u);
uvec2 b = uvec2(1u, 10u);
uvec2 carry;

uvec2 result = uaddCarry(a, b, carry);
// result is uvec2(0u, 15u)
// carry  is uvec2(1u, 0u)

In the first lane, 0xFFFFFFFFu + 1u overflows, producing a sum of 0u and setting the first carry component to 1u. In the second lane, 5u + 10u yields 15u without overflowing, setting the second carry component to 0u.

Emulating Multi-Word Precision

The primary practical application of uaddCarry is emulating 64-bit or larger arithmetic in environments without native 64-bit integer support, such as cryptographic hashing algorithms or high-precision counters.

To add two 64-bit values represented by two uvec2 structures (where the x component is the low 32 bits and the y component is the high 32 bits), uaddCarry chains the lower-half overflow into the upper-half calculation:

uvec2 add64(uvec2 numA, uvec2 numB) {
    uint carry;
    uvec2 sum;
    
    // Add lower 32-bit words and extract carry
    sum.x = uaddCarry(numA.x, numB.x, carry);
    
    // Add upper 32-bit words along with the carry from the lower addition
    sum.y = numA.y + numB.y + carry;
    
    return sum;
}

Because modern graphics hardware includes dedicated carry-generation logic in its arithmetic logic units (ALUs), uaddCarry typically compiles into single, native machine instructions rather than multi-step branches or software comparisons, maintaining high shader execution performance.