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

  1. Use a general-purpose register (R0-R30, or R31 where valid) as the base of the shifted operand.
  2. If the operation genuinely targets a vector register, use the correct vector instruction form without a GPR-style shift (e.g., SVE shift instructions).
  3. Double-check register name spelling in the .s file — a typo can cause a non-R decode.
  4. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/1cd241ad637a638c. Report an issue: GitHub.