GLSL usubBorrow: Handling Subtraction Borrows

The usubBorrow function in the OpenGL Shading Language (GLSL) is an intrinsic operation designed for unsigned integer subtraction with explicit borrow detection. This article explains how usubBorrow works, details its syntax and behavior, and demonstrates how it facilitates multi-precision (extended-precision) integer arithmetic on the GPU.

Function Overview and Syntax

Introduced in GLSL 4.00 (and via the GL_ARB_gpu_shader5 extension), usubBorrow executes standard 32-bit unsigned subtraction while simultaneously tracking whether the operation required a borrow from a higher-order bit.

The function signature is defined as:

genUType usubBorrow(genUType x, genUType y, out genUType borrow);
  • x: The unsigned integer (or vector of unsigned integers) minuend.
  • y: The unsigned integer (or vector of unsigned integers) subtrahend.
  • borrow: An out parameter that receives the borrow value.
  • Return Value: Returns the lower 32 bits of the difference x - y (effectively modulo \(2^{32}\)).

How the Borrow Mechanism Works

In unsigned integer arithmetic, if \(x \ge y\), the subtraction completes without underflow. However, if \(x < y\), an underflow occurs because the result cannot be represented as a positive unsigned integer within the word limit.

usubBorrow handles this scenario predictably:

  1. Difference: The returned value wraps around according to two's complement modulo arithmetic, producing \((x - y) \pmod{2^{32}}\).
  2. Borrow Flag: The borrow output parameter is explicitly assigned:
    • 0u if \(x \ge y\) (no borrow needed).
    • 1u if \(x < y\) (a borrow was required).

Standard subtraction using the - operator yields the identical wrapped difference but discards the borrow information, forcing developers to implement conditional checks (e.g., borrow = x < y ? 1u : 0u;) to determine underflow. The usubBorrow intrinsic exposes hardware-level carry/borrow flags directly, resulting in faster and branchless execution on the GPU.

Primary Use Case: Multi-Precision Arithmetic

The primary function of usubBorrow is constructing multi-word integer arithmetic, such as 64-bit, 128-bit, or arbitrary-precision (bignum) operations within shaders, cryptography, or hashing algorithms.

When subtracting multi-word integers stored as arrays of uint values, the borrow generated from lower-order words must cascade into higher-order words:

// Subtracting a 64-bit integer (low, high) from another (x - y)
uint x_low, x_high;
uint y_low, y_high;

uint diff_low, diff_high;
uint borrow;

// Subtract lower 32 bits and capture borrow
diff_low = usubBorrow(x_low, y_low, borrow);

// Subtract borrow and upper 32 bits
diff_high = x_high - y_high - borrow;

In longer multi-word subtractions, usubBorrow is used iteratively along with addition checks to propagate borrows across each limb of the integer. By providing both the wrapped difference and the exact borrow state in a single instruction, usubBorrow ensures that multi-precision calculations remain both accurate and efficient.