ruby/ruby · error
Invalid operand combination to msr instruction
Error message
Invalid operand combination to msr instruction
What it means
Panic from the msr helper in zjit's ARM64 assembler (zjit/src/asm/arm64/mod.rs:776). msr (write general-purpose register to a system register) matches only A64Opnd::Reg(rt); any other rt variant hits the wildcard arm and panics 'Invalid operand combination to msr instruction'. The first parameter is the SystemRegister enum itself, so the failure is always the register-side operand: it must be Reg, not Imm/UImm/Mem/None.
Source
Thrown at zjit/src/asm/arm64/mod.rs:776
/// MRS - move a system register into a general-purpose register
pub fn mrs(cb: &mut CodeBlock, rt: A64Opnd, systemregister: SystemRegister) {
let bytes: [u8; 4] = match rt {
A64Opnd::Reg(rt) => {
SysReg::mrs(rt.reg_no, systemregister).into()
},
_ => panic!("Invalid operand combination to mrs instruction")
};
cb.write_bytes(&bytes);
}
/// MSR - move a general-purpose register into a system register
pub fn msr(cb: &mut CodeBlock, systemregister: SystemRegister, rt: A64Opnd) {
let bytes: [u8; 4] = match rt {
A64Opnd::Reg(rt) => {
SysReg::msr(systemregister, rt.reg_no).into()
},
_ => panic!("Invalid operand combination to msr instruction")
};
cb.write_bytes(&bytes);
}
/// MUL - multiply two registers, put the result in a third register
pub fn mul(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd, rm: A64Opnd) {
let bytes: [u8; 4] = match (rd, rn, rm) {
(A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {
assert!(rd.num_bits == rn.num_bits && rn.num_bits == rm.num_bits, "Expected registers to be the same size");
MAdd::mul(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into()
},
_ => panic!("Invalid operand combination to mul instruction")
};
cb.write_bytes(&bytes);
}View on GitHub (pinned to 0e5b888e1c)
Solutions
- Load the constant into a register first, then msr with a Reg operand: movz(cb, x0, A64Opnd::new_uimm(0), 0); msr(cb, SystemRegister::FPCR, x0).
- Ensure rt is A64Opnd::Reg — check with is_reg() when the operand is computed.
- Verify argument order: msr(cb, SystemRegister, rt), the reverse of mrs(cb, rt, SystemRegister).
Example fix
// before msr(cb, SystemRegister::FPCR, A64Opnd::new_uimm(0)); // panics: UImm rt // after movz(cb, x0, A64Opnd::new_uimm(0), 0); msr(cb, SystemRegister::FPCR, x0);
Defensive patterns
Strategy: type-guard
Validate before calling
assert!(rt.is_reg(), "msr value must be a register operand"); msr(cb, SystemRegister::FPCR, rt);
Type guard
fn msr_operand_ok(rt: A64Opnd) -> bool { rt.is_reg() } Prevention
- No immediate-to-system-register form exists — always materialize constants into a register first.
- Guard with is_reg() when the value operand comes from a generic lowering path.
- Keep mrs/msr call sites adjacent and cross-check their mirrored argument order.
When it happens
Trigger: Calling msr(cb, SystemRegister::FPCR, A64Opnd::UImm(0)) hoping to write a constant directly; passing a Mem operand as the value; passing None.
Common situations: Setting floating-point or debug flags (FPCR, DACR) from a JIT; assuming an immediate-to-system-register form exists like some macro-assembler shorthands; swapping mrs/msr argument orders during refactors.
Related errors
- Invalid operand combination to mrs instruction
- Invalid operand combination to movk instruction.
- Invalid operand combination to movn instruction.
- Invalid operand combination to movz instruction.
- Invalid operand combination to mul instruction
AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21).
Data as JSON: /api/errors/746db49f15f41c31.
Report an issue: GitHub.