golang/go · error
invalid register for shift operation
Error message
invalid register for shift operation
What it means
The Go assembler's ARM64 arch helper ARM64RegisterShift returns this error when the base register of a shift operation is outside the general-purpose register range [REG_R0, REG_R31] (arm64.go:207-209). Only general-purpose registers (R0-R31, where R31 is NZR/SP) are valid as the base of a shifted operand; vector/SVE/predicate registers are not allowed, so the assembler rejects them with this message at assembly time.
Source
Thrown at src/cmd/asm/internal/arch/arm64.go:208
return arm64.REG_Z0 + n, true
}
case "P":
if 0 <= n && n <= 15 {
return arm64.REG_P0 + n, true
}
case "PN":
if 0 <= n && n <= 15 {
return arm64.REG_PN0 + n, true
}
}
return 0, false
}
// ARM64RegisterShift constructs an ARM64 register with shift operation.
func ARM64RegisterShift(reg, op, count int16) (int64, error) {
// the base register of shift operations must be general register.
if reg > arm64.REG_R31 || reg < arm64.REG_R0 {
return 0, errors.New("invalid register for shift operation")
}
return int64(reg&31)<<16 | int64(op)<<22 | int64(uint16(count)), nil
}
// ARM64RegisterArrangement constructs an ARM64 vector register arrangement.
func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
var curQ, curSize, prefix uint16
if name[0] != 'V' && name[0] != 'Z' && name[0] != 'P' {
return 0, errors.New("expect V0-V31, Z0-Z31, or P0-P15; found: " + name)
}
switch name[0] {
case 'V':
prefix = 0
case 'Z':
prefix = 1
case 'P':
prefix = 2
}View on GitHub (pinned to b6b368adc5)
Solutions
- Use a general-purpose register (R0-R30, or R31 where valid) as the base of the shifted operand.
- If the operation genuinely targets a vector register, use the correct vector instruction form without a GPR-style shift (e.g., SVE shift instructions).
- Double-check register name spelling in the .s file — a typo can cause a non-R decode.
- Consult the ARM Architecture Reference Manual for the instruction's permitted operand types.
Example fix
; before — shift on a vector register (invalid) TEXT foo(SB), $0 ADD V0.LSL#1, V1, V2 ; after — use a general-purpose register TEXT foo(SB), $0 ADD R0.LSL#1, R1, R2
Defensive patterns
Strategy: validation
Validate before calling
// Validate register class before constructing a shift operand (assembler tooling).
func validShiftBase(reg int16) bool {
return reg >= arm64.REG_R0 && reg <= arm64.REG_R31
}
if !validShiftBase(reg) { return fmt.Errorf("shift base must be R0-R31") } Prevention
- Use only general-purpose registers (R0-R30/R31) as the base of ARM64 shift operands.
- Reserve vector/SVE shift operations for their dedicated instruction forms.
- Double-check register prefix spelling in .s files.
When it happens
Trigger: Triggered by writing an ARM64 assembly instruction with a shift applied to a non-GPR operand, e.g., `ADD V0.LSL#1, V1, V2` or `ADD Z0.LSL#2, ...`. The check `reg > arm64.REG_R31 || reg < arm64.REG_R0` catches vector (V), SVE (Z), and predicate (P) registers, as well as any out-of-range encoding.
Common situations: Hand-written ARM64 assembly (`.s` files assembled by `go tool asm`) using shift modifiers (LSL, LSR, ASR, ROR) on vector or SVE registers. Migrating AArch64 code that used a shift on an operand the Go assembler classifies as non-GPR. Typos in register names that decode to a non-R register.
Related errors
- expect V0-V31, Z0-Z31, or P0-P15; found: {name}
- invalid register number: {name}
- invalid arrangement in ARM64 register list
- Loong64 extension: invalid LSX/LASX register: {reg}
- globalThis.crypto is not available, polyfill required (crypt
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/1cd241ad637a638c.
Report an issue: GitHub.