diff options
| author | iximeow <me@iximeow.net> | 2026-02-25 09:22:14 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-05-25 00:59:27 +0000 |
| commit | 63582532b8014d59117a8929afd1f7725bc1b469 (patch) | |
| tree | 90230c5e3979a6722fdb8fd053381dd2e3c8c41c /src | |
| parent | 6b2b3e82ccf9fb2b726ec819a0af7892bbb5d21a (diff) | |
add initial stats for disasm stats in all modes
Diffstat (limited to 'src')
| -rw-r--r-- | src/long_mode/mod.rs | 63 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 58 | ||||
| -rw-r--r-- | src/real_mode/mod.rs | 88 |
3 files changed, 206 insertions, 3 deletions
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 1e1d387..0ffc9f5 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -1102,9 +1102,6 @@ const XSAVE: [Opcode; 10] = [ Opcode::XSETBV, ]; -//struct OpcodeBuilder { -// variant_specifier: OpcodeVariantInfo - /// an `x86_64` opcode. there sure are a lot of these. #[allow(non_camel_case_types)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] @@ -2788,6 +2785,49 @@ impl LengthedInstruction for Instruction { } } +#[cfg(feature="_debug_internal_disasm_stats")] +mod disasm_stats { +} +#[cfg(not(feature="_debug_internal_disasm_stats"))] +mod disasm_stats { + macro_rules! shim_impl { + ($name:ident) => { + #[inline(always)] + pub fn $name() { } + } + } + + #[inline(always)] + pub fn record_opcode(_opc: super::Opcode) { + } + + #[inline(always)] + pub(crate) fn record_operand_case(_oper: super::OperandCase) { + } + + shim_impl!(record_opc_0f); + shim_impl!(record_opc_rep_0f); + shim_impl!(record_opc_repnz_0f); + shim_impl!(record_opc_op_size_0f); + shim_impl!(record_opc_0f38); + shim_impl!(record_opc_0f3a); + + shim_impl!(check_lock); + shim_impl!(read_operands); + shim_impl!(is_only_modrm_operands); + shim_impl!(instr_done); + + shim_impl!(prefix_rex); + shim_impl!(read_opc_hotpath_hit); + shim_impl!(read_opc_hotpath_miss); + shim_impl!(indirect_branch_prefixes_slow); +} + +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DisasmStats; +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DISASM_STATS; + /// an `x86_64` instruction decoder. /// /// fundamentally this is one or two primitives with no additional state kept during decoding. it @@ -5260,6 +5300,7 @@ fn record_opcode_record_found< T: Reader<Address<Arch>, Word<Arch>>, S: DescriptionSink<FieldDescription>, >(words: &mut T, sink: &mut S, opc: Opcode, code: OperandCode, opc_length: u32) { + disasm_stats::record_opcode(opc); let offset = words.offset() as u32; let opcode_start_bit = (offset - opc_length) * 8; let opcode_end_bit = offset * 8 - 1; @@ -5386,12 +5427,15 @@ fn read_with_annotations< instruction.regs[2] = RegSpec::rax(); let record: OperandCode = if self.read_opc_hotpath(nextb, &mut nextb, &mut next_rec, words, instruction, sink)? { + disasm_stats::read_opc_hotpath_hit(); next_rec.operand() } else { + disasm_stats::read_opc_hotpath_miss(); let prefixes = &mut instruction.prefixes; let record = loop { let mut record = next_rec; if nextb >= 0x40 && nextb < 0x50 { + disasm_stats::prefix_rex(); let b = nextb; sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { desc: InnerDescription::RexPrefix(b), @@ -5455,6 +5499,7 @@ fn read_with_annotations< }); prefixes.set_rep(); } else { + disasm_stats::indirect_branch_prefixes_slow(); match b { 0x26 | 0x2e | @@ -5524,11 +5569,14 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { + disasm_stats::check_lock(); if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } + disasm_stats::instr_done(); + Ok(()) } @@ -5598,11 +5646,13 @@ fn read_operands< InnerDescription::Boundary("opcode ends/operands begin (typically)") .with_id(words.offset() as u32 * 8 - 1) ); + disasm_stats::read_operands(); let operand_code = OperandCodeBuilder::from_bits(operand_code as u16); let modrm_start = words.offset() as u32 * 8; let opcode_start = modrm_start - 8; if operand_code.is_only_modrm_operands() { + disasm_stats::is_only_modrm_operands(); let bank; let modrm = read_modrm(words)?; let rrr = (modrm >> 3) & 7; @@ -5942,6 +5992,7 @@ fn read_operands< } instruction.operand_count = 2; + disasm_stats::record_operand_case(operand_code.operand_case_handler_index()); // match operand_code { match operand_code.operand_case_handler_index() { // these operand cases are all `only_*`, and are unreachable here.. @@ -9023,18 +9074,23 @@ fn read_0f_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecor // invalid prefix is in fact an invalid instruction. so just duplicate for the four kinds of // opcode lists. if prefixes.repnz() { + disasm_stats::record_opc_repnz_0f(); REPNZ_0F_CODES[opcode as usize] } else if prefixes.rep() { + disasm_stats::record_opc_rep_0f(); REP_0F_CODES[opcode as usize] } else if prefixes.operand_size() { + disasm_stats::record_opc_op_size_0f(); OPERAND_SIZE_0F_CODES[opcode as usize] } else { + disasm_stats::record_opc_0f(); NORMAL_0F_CODES[opcode as usize] } } #[inline(always)] fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<OpcodeRecord, DecodeError> { + disasm_stats::record_opc_0f38(); if prefixes.rep() { const TBL: [OpcodeRecord; 0x28] = [ OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30f38d8), @@ -9155,6 +9211,7 @@ fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<Op #[inline(always)] fn read_0f3a_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<OpcodeRecord, DecodeError> { + disasm_stats::record_opc_0f3a(); if prefixes.rep() { if prefixes != &Prefixes::new(0x10) { return Err(DecodeError::InvalidOpcode); diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 615efe6..6f052c6 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -2702,6 +2702,48 @@ impl LengthedInstruction for Instruction { } } +#[cfg(feature="_debug_internal_disasm_stats")] +mod disasm_stats { +} +#[cfg(not(feature="_debug_internal_disasm_stats"))] +mod disasm_stats { + macro_rules! shim_impl { + ($name:ident) => { + #[inline(always)] + pub fn $name() { } + } + } + + #[inline(always)] + pub fn record_opcode(_opc: super::Opcode) { + } + + #[inline(always)] + pub(crate) fn record_operand_case(_oper: super::OperandCase) { + } + + shim_impl!(record_opc_0f); + shim_impl!(record_opc_rep_0f); + shim_impl!(record_opc_repnz_0f); + shim_impl!(record_opc_op_size_0f); + shim_impl!(record_opc_0f38); + shim_impl!(record_opc_0f3a); + + shim_impl!(check_lock); + shim_impl!(read_operands); + shim_impl!(is_only_modrm_operands); + shim_impl!(instr_done); + + shim_impl!(read_opc_hotpath_hit); + shim_impl!(read_opc_hotpath_miss); + shim_impl!(indirect_branch_prefixes_slow); +} + +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DisasmStats; +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DISASM_STATS; + /// an `x86` instruction decoder. /// /// fundamentally this is one or two primitives with no additional state kept during decoding. it @@ -5285,6 +5327,7 @@ fn record_opcode_record_found< T: Reader<Address<Arch>, Word<Arch>>, S: DescriptionSink<FieldDescription>, >(words: &mut T, sink: &mut S, opc: Opcode, code: OperandCode, opc_length: u32) { + disasm_stats::record_opcode(opc); let offset = words.offset() as u32; let opcode_start_bit = (offset - opc_length) * 8; let opcode_end_bit = offset * 8 - 1; @@ -5392,8 +5435,10 @@ fn read_with_annotations< instruction.regs[2] = RegSpec::eax(); let record: OperandCode = if self.read_opc_hotpath(nextb, &mut nextb, &mut next_rec, words, instruction, sink)? { + disasm_stats::read_opc_hotpath_hit(); next_rec.operand() } else { + disasm_stats::read_opc_hotpath_miss(); let prefixes = &mut instruction.prefixes; let record = loop { let record = next_rec; @@ -5450,6 +5495,7 @@ fn read_with_annotations< }); prefixes.set_rep(); } else { + disasm_stats::indirect_branch_prefixes_slow(); match b { 0x26 => { sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { @@ -5527,11 +5573,14 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { + disasm_stats::check_lock(); if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } + disasm_stats::instr_done(); + Ok(()) } @@ -5546,11 +5595,13 @@ fn read_operands< InnerDescription::Boundary("opcode ends/operands begin (typically)") .with_id(words.offset() as u32 * 8 - 1) ); + disasm_stats::read_operands(); let operand_code = OperandCodeBuilder::from_bits(operand_code as u16); let modrm_start = words.offset() as u32 * 8; let opcode_start = modrm_start - 8; if operand_code.is_only_modrm_operands() { + disasm_stats::is_only_modrm_operands(); let bank; // cool! we can precompute width and know we need to read_E. if !operand_code.has_byte_operands() { @@ -5817,6 +5868,7 @@ fn read_operands< } instruction.operand_count = 2; + disasm_stats::record_operand_case(operand_code.operand_case_handler_index()); // match operand_code { match operand_code.operand_case_handler_index() { // these operand cases are all `only_*`, and are unreachable here.. @@ -8873,17 +8925,22 @@ fn read_0f_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecor // invalid prefix is in fact an invalid instruction. so just duplicate for the four kinds of // opcode lists. if prefixes.repnz() { + disasm_stats::record_opc_repnz_0f(); REPNZ_0F_CODES[opcode as usize] } else if prefixes.rep() { + disasm_stats::record_opc_rep_0f(); REP_0F_CODES[opcode as usize] } else if prefixes.operand_size() { + disasm_stats::record_opc_op_size_0f(); OPERAND_SIZE_0F_CODES[opcode as usize] } else { + disasm_stats::record_opc_0f(); NORMAL_0F_CODES[opcode as usize] } } fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord { + disasm_stats::record_opc_0f38(); if prefixes.rep() { return match opcode { 0xd8 => OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30f38d8), @@ -9016,6 +9073,7 @@ fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRec } fn read_0f3a_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord { + disasm_stats::record_opc_0f3a(); if prefixes.rep() { if prefixes != &Prefixes::new(0x10) { return OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing); diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs index 709a301..42a0b03 100644 --- a/src/real_mode/mod.rs +++ b/src/real_mode/mod.rs @@ -2702,6 +2702,78 @@ impl LengthedInstruction for Instruction { } } +#[cfg(feature="_debug_internal_disasm_stats")] +mod disasm_stats { + macro_rules! shim_impl { + ($name:ident) => { + #[inline(always)] + pub fn $name() { } + } + } + + #[inline(always)] + pub fn record_opcode(_opc: super::Opcode) { + } + + #[inline(always)] + pub(crate) fn record_operand_case(_oper: super::OperandCase) { + } + + shim_impl!(record_opc_0f); + shim_impl!(record_opc_rep_0f); + shim_impl!(record_opc_repnz_0f); + shim_impl!(record_opc_op_size_0f); + shim_impl!(record_opc_0f38); + shim_impl!(record_opc_0f3a); + + shim_impl!(check_lock); + shim_impl!(read_operands); + shim_impl!(is_only_modrm_operands); + shim_impl!(instr_done); + + shim_impl!(read_opc_hotpath_hit); + shim_impl!(read_opc_hotpath_miss); + shim_impl!(indirect_branch_prefixes_slow); +} +#[cfg(not(feature="_debug_internal_disasm_stats"))] +mod disasm_stats { + macro_rules! shim_impl { + ($name:ident) => { + #[inline(always)] + pub fn $name() { } + } + } + + #[inline(always)] + pub fn record_opcode(_opc: super::Opcode) { + } + + #[inline(always)] + pub(crate) fn record_operand_case(_oper: super::OperandCase) { + } + + shim_impl!(record_opc_0f); + shim_impl!(record_opc_rep_0f); + shim_impl!(record_opc_repnz_0f); + shim_impl!(record_opc_op_size_0f); + shim_impl!(record_opc_0f38); + shim_impl!(record_opc_0f3a); + + shim_impl!(check_lock); + shim_impl!(read_operands); + shim_impl!(is_only_modrm_operands); + shim_impl!(instr_done); + + shim_impl!(read_opc_hotpath_hit); + shim_impl!(read_opc_hotpath_miss); + shim_impl!(indirect_branch_prefixes_slow); +} + +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DisasmStats; +#[cfg(feature="_debug_internal_disasm_stats")] +pub use disasm_stats::DISASM_STATS; + /// an `x86` instruction decoder. /// /// fundamentally this is one or two primitives with no additional state kept during decoding. it @@ -5286,6 +5358,7 @@ fn record_opcode_record_found< T: Reader<Address<Arch>, Word<Arch>>, S: DescriptionSink<FieldDescription>, >(words: &mut T, sink: &mut S, opc: Opcode, code: OperandCode, opc_length: u32) { + disasm_stats::record_opcode(opc); let offset = words.offset() as u32; let opcode_start_bit = (offset - opc_length) * 8; let opcode_end_bit = offset * 8 - 1; @@ -5393,8 +5466,10 @@ fn read_with_annotations< instruction.regs[2] = RegSpec::ax(); let record: OperandCode = if self.read_opc_hotpath(nextb, &mut nextb, &mut next_rec, words, instruction, sink)? { + disasm_stats::read_opc_hotpath_hit(); next_rec.operand() } else { + disasm_stats::read_opc_hotpath_miss(); let prefixes = &mut instruction.prefixes; let record = loop { let record = next_rec; @@ -5451,6 +5526,7 @@ fn read_with_annotations< }); prefixes.set_rep(); } else { + disasm_stats::indirect_branch_prefixes_slow(); match b { 0x26 => { sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { @@ -5528,11 +5604,14 @@ fn read_with_annotations< self.read_operands(decoder, words, instruction, record, sink)?; if self.check_lock { + disasm_stats::check_lock(); if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() { return Err(DecodeError::InvalidPrefixes); } } + disasm_stats::instr_done(); + Ok(()) } @@ -5542,6 +5621,7 @@ fn read_operands< T: Reader<Address<Arch>, Word<Arch>>, S: DescriptionSink<FieldDescription> >(&mut self, decoder: &InstDecoder, words: &mut T, instruction: &mut Instruction, operand_code: OperandCode, sink: &mut S) -> Result<(), DecodeError> { + disasm_stats::read_operands(); sink.record( words.offset() as u32 * 8 - 1, words.offset() as u32 * 8 - 1, InnerDescription::Boundary("opcode ends/operands begin (typically)") @@ -5552,6 +5632,7 @@ fn read_operands< let opcode_start = modrm_start - 8; if operand_code.is_only_modrm_operands() { + disasm_stats::is_only_modrm_operands(); let bank; // cool! we can precompute width and know we need to read_E. if !operand_code.has_byte_operands() { @@ -5818,6 +5899,7 @@ fn read_operands< } instruction.operand_count = 2; + disasm_stats::record_operand_case(operand_code.operand_case_handler_index()); // match operand_code { match operand_code.operand_case_handler_index() { // these operand cases are all `only_*`, and are unreachable here.. @@ -8888,17 +8970,22 @@ fn read_0f_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecor // invalid prefix is in fact an invalid instruction. so just duplicate for the four kinds of // opcode lists. if prefixes.repnz() { + disasm_stats::record_opc_repnz_0f(); REPNZ_0F_CODES[opcode as usize] } else if prefixes.rep() { + disasm_stats::record_opc_rep_0f(); REP_0F_CODES[opcode as usize] } else if prefixes.operand_size() { + disasm_stats::record_opc_op_size_0f(); OPERAND_SIZE_0F_CODES[opcode as usize] } else { + disasm_stats::record_opc_0f(); NORMAL_0F_CODES[opcode as usize] } } fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord { + disasm_stats::record_opc_0f38(); if prefixes.rep() { return match opcode { 0xd8 => OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30f38d8), @@ -9031,6 +9118,7 @@ fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRec } fn read_0f3a_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecord { + disasm_stats::record_opc_0f3a(); if prefixes.rep() { if prefixes != &Prefixes::new(0x10) { return OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing); |
