GLSL Signed 64-Bit Integer Addition Overflow

This article examines the arithmetic overflow behavior of signed 64-bit integer (int64_t) addition within GLSL compute kernels. While the formal OpenGL Shading Language (GLSL) specification defines signed integer overflow as undefined, the underlying hardware implementation and modern intermediate representations like SPIR-V enforce two's complement modular wrap-around. Below, we break down the distinction between the language specification, SPIR-V execution rules, hardware reality, and best practices for writing predictable compute kernels.

The GLSL Specification: Undefined Behavior

According to the official GLSL core specification (as well as the GL_ARB_gpu_shader_int64 and GL_EXT_shader_explicit_arithmetic_types_int64 extensions), signed integer arithmetic overflow is strictly undefined.

Unlike unsigned integer types (uint64_t), which are explicitly specified to wrap around using modulo \(2^{64}\), the language does not formally guarantee what happens when a signed operation exceeds INT64_MAX (\(2^{63}-1\)) or drops below INT64_MIN (\(-2^{63}\)). In pure theoretical GLSL, the compiler is not obligated to generate code that wraps, clamps, or produces consistent results when signed overflow occurs.

The SPIR-V and Vulkan Reality: Two's Complement Wrap-Around

Most modern GLSL compute kernels are compiled ahead-of-time or just-in-time into SPIR-V for execution in Vulkan or modern OpenGL environments. In the SPIR-V specification, the rules for signed integer addition are explicitly defined:

  • Addition operations on integers map to the OpIAdd instruction.
  • The SPIR-V specification explicitly dictates that signed integer overflow for OpIAdd evaluates via standard two's complement wrap-around.

Because the translation from GLSL to SPIR-V converts a standard + operator on an int64_t into an OpIAdd instruction, compute kernels running via Vulkan or modern SPIR-V consumption pipelines will reliably exhibit two's complement wrapping. An overflow past 9223372036854775807 wraps around to -9223372036854775808.

GPU Hardware Architecture

Modern GPU Arithmetic Logic Units (ALUs) handle signed and unsigned integer addition using the exact same binary circuitry. At the silicon level, there is no distinct "signed add" instruction versus an "unsigned add" instruction in the hardware instruction sets of vendors like NVIDIA, AMD, or Intel.

Both operations perform identical bitwise additions discard the carry bit past the 64th bit. Consequently, native hardware execution naturally results in two's complement wrap-around regardless of the theoretical undefined behavior specified in the GLSL language manual.

Risks and Compiler Optimizations

Although the physical hardware wraps around, relying on signed overflow in high-level GLSL source code carries subtle risks:

  1. Undefined Behavior Optimizations: While GPU shader compilers historically perform fewer aggressive undefined-behavior-based optimizations than CPU compilers (such as Clang or GCC), a GLSL compiler assuming undefined behavior could theoretically eliminate overflow checks or assume that a + 1 > a is always true.
  2. Portability Across Drivers: Pure OpenGL drivers that compile high-level GLSL text directly to internal assembly could, in rare cases, produce non-wrapping behavior if specialized emulation routines or specific instruction sets are invoked.

To ensure fully defined, portable, and predictable behavior in compute kernels:

  • Use Unsigned Arithmetic for Modular Operations: If intentional wrap-around behavior is required, cast the operands to uint64_t prior to the addition and cast the result back to int64_t:

    int64_t safe_add(int64_t a, int64_t b) {
        return int64_t(uint64_t(a) + uint64_t(b));
    }
  • Target SPIR-V Directly: When working in Vulkan or using tools like glslangValidator, the generated OpIAdd guarantees that two's complement wrapping is respected by the driver's backend compiler.