{"record":{"id":"76adb76e4e109afd","repo":"ruby/ruby","slug":"invalid-operand-combination-to-subs-instruction","errorCode":null,"errorMessage":"Invalid operand combination to subs instruction.","messagePattern":"Invalid operand combination to subs instruction\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"zjit/src/asm/arm64/mod.rs","lineNumber":1120,"sourceCode":"            );\n\n            DataReg::subs(rd.reg_no, rn.reg_no, rm.reg_no, rd.num_bits).into()\n        },\n        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::UImm(uimm12)) => {\n            assert!(rd.num_bits == rn.num_bits, \"rd and rn must be of the same size.\");\n\n            DataImm::subs(rd.reg_no, rn.reg_no, uimm12.try_into().unwrap(), rd.num_bits).into()\n        },\n        (A64Opnd::Reg(rd), A64Opnd::Reg(rn), A64Opnd::Imm(imm12)) => {\n            assert!(rd.num_bits == rn.num_bits, \"rd and rn must be of the same size.\");\n\n            if imm12 < 0 {\n                DataImm::adds(rd.reg_no, rn.reg_no, (-imm12 as u64).try_into().unwrap(), rd.num_bits).into()\n            } else {\n                DataImm::subs(rd.reg_no, rn.reg_no, (imm12 as u64).try_into().unwrap(), rd.num_bits).into()\n            }\n        },\n        _ => panic!(\"Invalid operand combination to subs instruction.\"),\n    };\n\n    cb.write_bytes(&bytes);\n}\n\n/// SXTW - sign extend a 32-bit register into a 64-bit register\npub fn sxtw(cb: &mut CodeBlock, rd: A64Opnd, rn: A64Opnd) {\n    let bytes: [u8; 4] = match (rd, rn) {\n        (A64Opnd::Reg(rd), A64Opnd::Reg(rn)) => {\n            assert_eq!(rd.num_bits, 64, \"rd must be 64-bits wide.\");\n            assert_eq!(rn.num_bits, 32, \"rn must be 32-bits wide.\");\n\n            SBFM::sxtw(rd.reg_no, rn.reg_no).into()\n        },\n        _ => panic!(\"Invalid operand combination to sxtw instruction.\"),\n    };\n\n    cb.write_bytes(&bytes);","sourceCodeStart":1102,"sourceCodeEnd":1138,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/zjit/src/asm/arm64/mod.rs#L1102-L1138","documentation":"subs() is the flags-updating variant of sub in zjit's arm64 assembler and accepts the same three shapes: (Reg, Reg, Reg), (Reg, Reg, UImm), and (Reg, Reg, Imm with negative values lowered to ADDS). Any other operand combination reaches this panic. Like sub, there is no memory-operand form.","triggerScenarios":"Calling subs(cb, rd, rn, rm) with any Mem operand in any position, with an immediate in rn, or with A64Opnd::None. Separate asserts fire first for mismatched register widths; immediates wider than 12 bits fail the uimm12 conversion inside the matched arm rather than at this panic.","commonSituations":"Emitting comparison-style code (subs + b.cond) where the compared value is still in memory (spilled operand or struct field); lowering 'cmp and branch' patterns from a high-level IR where the IR allowed memory references as instruction operands.","solutions":["Load the memory operand into a register (ldr/ldur), then emit subs(cb, Reg, Reg, Reg).","Use cmp(), which zjit provides as SUBS with a zero destination, for pure comparisons.","For constants outside imm12 range, materialize them into a scratch register with movz/movk and use the register form."],"exampleFix":"// before: comparing against a spilled operand -> panic\nsubs(cb, X0, spilled_mem_opnd, X1);\n\n// after: reload then compare\nldr(cb, X9, spilled_mem_opnd);\nsubs(cb, X0, X9, X1);","handlingStrategy":"validation","validationCode":"fn can_subs(rd: &A64Opnd, rn: &A64Opnd, rm: &A64Opnd) -> bool {\n    matches!(\n        (rd, rn, rm),\n        (A64Opnd::Reg(_), A64Opnd::Reg(_), A64Opnd::Reg(_))\n            | (A64Opnd::Reg(_), A64Opnd::Reg(_), A64Opnd::UImm(_))\n            | (A64Opnd::Reg(_), A64Opnd::Reg(_), A64Opnd::Imm(_))\n    )\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use zjit's cmp() for pure comparisons instead of hand-building subs with a scratch destination.","Reload spilled (Mem) operands into registers before flags-setting instructions.","Test comparison lowering with both register and immediate operands in CI."],"tags":["arm64","assembler","operand-validation","jit","flags"],"backgroundTag":"invalid-instruction-operands","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}