From 6353f58170d28a142e3b012c2c86f684d50dea45 Mon Sep 17 00:00:00 2001 From: iximeow Date: Thu, 21 Apr 2022 02:35:38 -0700 Subject: support a fast path through the decoder for [rex-prefixed]opcode insts the overwhelming majority of x86 instructions are either a single-byte opcode or a single-byte opcode with a rex prefix. supporting these specially means that we don't have to length-check on every byte or go through the full decode loop while reading the most likely instructions. this is a significant improvement on typical x86 streams, but comes at a moderate penalty for crafted x86 instructions. the penalty is still not very bad, as the fast path is exited in favor of the full decode loop as soon as we see a non-rex prefix byte; this adds maybe a dozen instructions to the slow path. --- src/long_mode/mod.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 8ce4535..d22db30 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -6414,6 +6414,51 @@ impl fmt::Display for FieldDescription { } } +fn read_opc_hotpath< + T: Reader<::Address, ::Word>, + S: DescriptionSink, +>(mut b: u8, mut record: OpcodeRecord, nextb: &mut u8, next_rec: &mut OpcodeRecord, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result, DecodeError> { + if b >= 0x40 && b < 0x50 { + sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { + desc: InnerDescription::RexPrefix(b), + id: words.offset() as u32 * 8 - 8, + }); + b = words.next().ok().ok_or(DecodeError::ExhaustedInput)?; + record = unsafe { + core::ptr::read_volatile(&OPCODES[b as usize]) + }; + instruction.prefixes.rex_from(b); + } + + if let Interpretation::Instruction(opc) = record.0 { + if words.offset() > 1 { + sink.record( + words.offset() as u32 * 8 - 8 - 1, words.offset() as u32 * 8 - 8 - 1, + InnerDescription::Boundary("prefixes end") + .with_id(words.offset() as u32 * 8 - 9) + ); + } + if opc != Opcode::Invalid { + sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { + desc: InnerDescription::Opcode(opc), + id: words.offset() as u32 * 8 - 8, + }); + } + sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription { + desc: InnerDescription::OperandCode(OperandCodeWrapper { code: record.1 }), + id: words.offset() as u32 * 8 - 8 + 1, + }); + instruction.mem_size = 0; + instruction.operand_count = 2; + instruction.opcode = opc; + return Ok(Some(record.1)); + } else { + *nextb = b; + *next_rec = record; + return Ok(None); + } +} + #[inline(always)] fn read_with_annotations< T: Reader<::Address, ::Word>, @@ -6429,7 +6474,9 @@ fn read_with_annotations< // default operands to [RegRRR, Nothing, Nothing, Nothing] instruction.operands = unsafe { core::mem::transmute(0x00_00_00_01) }; - let record: OperandCode = { + let record: OperandCode = if let Some(rec) = read_opc_hotpath(nextb, next_rec, &mut nextb, &mut next_rec, words, instruction, sink)? { + rec + } else { let prefixes = &mut instruction.prefixes; let record = loop { let record = next_rec; -- cgit v1.1