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: Anoutparameter 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:
- Difference: The returned value wraps around according to two's complement modulo arithmetic, producing \((x - y) \pmod{2^{32}}\).
- Borrow Flag: The
borrowoutput parameter is explicitly assigned:0uif \(x \ge y\) (no borrow needed).1uif \(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.