{"record":{"id":"d9777403dcd87cf0","repo":"ruby/ruby","slug":"invalid-operand-combination-to-tst-instruction","errorCode":null,"errorMessage":"Invalid operand combination to tst instruction.","messagePattern":"Invalid operand combination to tst instruction\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"zjit/src/asm/arm64/mod.rs","lineNumber":1193,"sourceCode":"\n/// TST - test the bits of a register against a mask, then update flags\npub fn tst(cb: &mut CodeBlock, rn: A64Opnd, rm: A64Opnd) {\n    let bytes: [u8; 4] = match (rn, rm) {\n        (A64Opnd::Reg(rn), A64Opnd::Reg(rm)) => {\n            assert!(rn.num_bits == rm.num_bits, \"All operands must be of the same size.\");\n\n            LogicalReg::tst(rn.reg_no, rm.reg_no, rn.num_bits).into()\n        },\n        (A64Opnd::Reg(rn), A64Opnd::UImm(imm)) => {\n            let bitmask_imm = if rn.num_bits == 32 {\n                BitmaskImmediate::new_32b_reg(imm.try_into().unwrap())\n            } else {\n                imm.try_into()\n            }.unwrap();\n\n            LogicalImm::tst(rn.reg_no, bitmask_imm, rn.num_bits).into()\n        },\n        _ => panic!(\"Invalid operand combination to tst instruction.\"),\n    };\n\n    cb.write_bytes(&bytes);\n}\n\n/// CBZ - branch if a register is zero\npub fn cbz(cb: &mut CodeBlock, rt: A64Opnd, offset: InstructionOffset) {\n    assert!(imm_fits_bits(offset.into(), 19), \"jump offset for cbz must fit in 19 bits\");\n    let bytes: [u8; 4] = if let A64Opnd::Reg(rt) = rt {\n        cbz_cbnz(rt.num_bits, false, offset, rt.reg_no)\n    } else {\n        panic!(\"Invalid operand combination to cbz instruction.\")\n    };\n\n    cb.write_bytes(&bytes);\n}\n\n/// CBNZ - branch if a register is non-zero","sourceCodeStart":1175,"sourceCodeEnd":1211,"githubUrl":"https://github.com/ruby/ruby/blob/0e5b888e1c355f3f728f2659f085820937dada48/zjit/src/asm/arm64/mod.rs#L1175-L1211","documentation":"zjit's tst() accepts (Reg, Reg) register masks and (Reg, UImm) immediate masks; any other pair — signed Imm, Mem, None — panics here. For the UImm path the immediate must additionally be a legal A64 logical bitmask immediate (a repeating one-bit pattern); an unencodable value fails inside BitmaskImmediate::new_32b_reg(...).unwrap() with a different panic.","triggerScenarios":"Calling tst(cb, rn, A64Opnd::new_imm(0xFF)) with a signed Imm instead of new_uimm(0xFF); passing a Mem operand as the mask; testing a memory operand directly. For 32-bit registers the mask must be encodable via new_32b_reg; masks like 0x80 (single bit) are fine, arbitrary values like 0x101 are not and fail the unwrap instead.","commonSituations":"Lowering 'x & mask == 0' checks from an IR whose constant nodes default to signed i64 (new_imm); reusing x86 'test' idioms where the mask came from a computation that produced i64 -1 (0xFFFFFFFF...) rather than u64 !0; testing flag words before they were loaded into a register.","solutions":["Build the mask as unsigned: A64Opnd::new_uimm(mask).","For masks that are not a valid bitmask immediate (e.g. 0x101), materialize the mask into a register and use tst(cb, Reg, Reg).","Load the tested value into a register first if it is in memory."],"exampleFix":"// before: signed Imm mask -> panic\ntst(cb, W0, A64Opnd::new_imm(0xFF));\n\n// after: unsigned mask, or register form for irregular masks\ntst(cb, W0, A64Opnd::new_uimm(0xFF));\n// or: movz w1, #0x101-ish irregular mask into w1, then tst(cb, W0, W1);","handlingStrategy":"validation","validationCode":"fn can_tst(rn: &A64Opnd, rm: &A64Opnd) -> bool {\n    matches!((rn, rm), (A64Opnd::Reg(_), A64Opnd::Reg(_)) | (A64Opnd::Reg(_), A64Opnd::UImm(_)))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Convert i64 mask constants to u64 (new_uimm(mask as u64)) before tst.","For non-repeating masks, prefer the register form: materialize mask, then tst(Reg, Reg).","Keep a can_encode_bitmask(mask, width) check next to your mask-producing code."],"tags":["arm64","assembler","operand-validation","bitmask-immediate","flags"],"backgroundTag":"invalid-instruction-operands","analyzedSha":"0e5b888e1c355f3f728f2659f085820937dada48","analyzedAt":"2026-08-21T14:25:43.473Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}