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 : require

Once 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 base operand can be a scalar or vector of int64_t or uint64_t.
  • The count operand 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 4UL

Out-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 & 0x3F or count & 63).
  • Shifting by 64 equates to 64 & 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_t shifts 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.