64-Bit Integer Bit-Shifts in GLSL Compute Shaders
Modern GLSL compute shaders support 64-bit integer bit-shift
operations through explicit 64-bit scalar extensions, allowing
developers to execute high-precision data packing, hashing, and
cryptography directly on the GPU. This article examines how
int64_t handles bit-shifts, covering required extensions,
syntax rules, hardware execution details, sign extension behavior, and
undefined behavior boundaries.
Enabling 64-Bit Integers in GLSL
Native 64-bit integer types are not available by default in base
GLSL. To utilize int64_t and uint64_t, you
must target an appropriate OpenGL or Vulkan environment and explicitly
declare the required extension at the top of your compute shader:
#version 450
#extension GL_ARB_gpu_shader_int64 : require
// Alternatively for SPIR-V/Vulkan:
// #extension GL_EXT_shader_explicit_arithmetic_types_int64 : requireOnce enabled, int64_t represents a two's complement
64-bit signed integer, and uint64_t represents an unsigned
64-bit integer.
Shift Operators and Operand Compatibility
GLSL supports standard C-style shift operators: left shift
(<<) and right shift (>>).
In bit-shift expressions (base << count or
base >> count):
- The
baseoperand can be a scalar or vector ofint64_toruint64_t. - The
countoperand does not need to match the bit-width of the base operand. It can be a 32-bit signed or unsigned integer (int,uint), or a 64-bit integer (int64_t,uint64_t). - If operating on vector types, the operation is performed component-wise.
Left Shift (<<)
Behavior
Left-shifting an int64_t or uint64_t shifts
all bits to the left, filling vacated low-order bits with zeros.
High-order bits shifted beyond bit position 63 are discarded.
For signed int64_t, shifting bits into the sign bit
position modifies the sign of the value without throwing an overflow
exception, adhering to standard two's complement binary
representation.
Right Shift
(>>): Arithmetic vs. Logical
The GLSL specification defines right shifts based on the signedness of the base operand:
- Logical Right Shift (
uint64_t): When shifting an unsigned 64-bit integer, vacant high-order bits are filled with zeros. - Arithmetic Right Shift (
int64_t): When shifting a signed 64-bit integer, the sign bit is duplicated into the vacated high-order bits. If the original integer is negative, the vacant positions are populated with ones; if positive, they are populated with zeros.
int64_t s_val = -16L; // Signed negative
uint64_t u_val = 16UL; // Unsigned
int64_t s_shifted = s_val >> 2; // Arithmetic: result is -4L (sign preserved)
uint64_t u_shifted = u_val >> 2; // Logical: result is 4ULOut-of-Range Shift Counts and Hardware Execution
According to the core GLSL and SPIR-V specifications, shifting by a
count that is negative or greater than or equal to the total bit-width
of the base operand (i.e., count < 0 or
count >= 64) produces undefined behavior.
In practice, modern GPU hardware (AMD, NVIDIA, Intel) typically evaluates 64-bit shift counts using a bitwise mask:
- The GPU masks the shift count to the lower 6 bits
(
count & 0x3Forcount & 63). - Shifting by
64equates to64 & 63 = 0, leaving the integer unchanged rather than clearing it to zero. - Shifting by negative numbers interprets the sign bits through the same 6-bit mask, leading to unexpected shift magnitudes.
To ensure deterministic cross-vendor execution, you must clamp or mask the shift count manually if the count cannot be guaranteed at compile time:
int shiftCount = ...;
int64_t safeShift = base << (shiftCount & 63);Performance and Hardware Considerations
Native support for 64-bit integer ALUs varies significantly across GPU architectures:
- Data Center / HPC GPUs: Typically feature dedicated 64-bit integer execution units, executing 64-bit shifts with single-cycle or low-latency throughput.
- Consumer / Gaming GPUs: Often lack wide 64-bit
integer ALUs. Drivers compile
int64_tshifts into multiple 32-bit operations (using instructions such as multi-word shifts with carry/funnel shifts). While completely transparent in GLSL code, wide shifts on these architectures consume more register space and ALU cycles than their 32-bit counterparts.