Skip to content

CONCEPT Cited by 1 source

Immediate encoding limit

Definition

Immediate encoding limit is the architectural constraint on how wide a value can be encoded directly inside a single instruction. On fixed-length ISAs (ARM64, RISC-V, etc.) the limit is strict — every instruction is exactly 4 bytes, and the opcode must share those 32 bits with the target register, source register, and any flags, leaving only a handful of bits for immediate operands.

Representative examples on ARM64:

Instruction Immediate bits Range
ADD (imm) 12 0..4095 (with optional LSL #12 → 24 bits effective)
MOV (wide imm) 16 0..65535 (with MOVK to combine 16-bit chunks)
LDR (imm offset) 12 typically 0..4095 × access size

Wider values must be built up across multiple instructions.

Consequences for compiler backends

A compiler targeting a fixed-length ISA must decide how to emit operations whose immediate exceeds the inline limit:

  1. Split into multiple opcodes. For ADD specifically, ARM64 reserves an LSL-#12 bit so any 24-bit addition can be expressed as two opcodes: ADD $low, dst, dst then ADD $(high << 12), dst, dst.
  2. Build in a scratch register, then apply. Load the immediate into a scratch register via MOV + MOVK pairs, then use the register form of the target operation. This is one logical operation expressed as multiple preparatory opcodes followed by one indivisible application opcode.

Option (1) is cheaper in instruction count when the register pressure is high; option (2) is necessary when the intermediate state after the first opcode must not be observable — i.e. when the target of the operation is runtime-observable state.

The stack pointer is a special case

When the target of a split-immediate operation is the stack pointer — or any other register the runtime watches during stack unwinding — option (1) is unsafe under async preemption because the intermediate state is a valid-looking but wrong stack pointer. The split-instruction race window becomes a reachable bug.

The Go toolchain pre-go1.23.12 used option (1) for epilogue SP adjustments on arm64. The fix (go1.23.12 / go1.24.6 / go1.25.0) switches to option (2): SP is now adjusted via a scratch-register + indivisible register-form ADD.

On variable-length ISAs (x86-64)

amd64 has variable-length instructions. ADD imm32, reg encodes a full 32-bit immediate directly in a single variable-length opcode. The immediate-encoding limit that forces split operations on arm64 does not arise — amd64 is not exposed to the split-instruction race window (for this specific class of operation). The Cloudflare 2025-10 bug was arm64-only for this reason.

Seen in

Last updated · 200 distilled / 1,178 read