aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2022-04-21 02:35:38 -0700
committeriximeow <git@iximeow.net>2022-12-03 15:11:09 -0800
commite1b3633e9595abdd4714a18bae9d5b0aeaed8a80 (patch)
tree7b8f99118156fa9a4ba35e27bc8d51da50be1a7b
parentc15999d4a5afa1e2e3e6d6c227135dfd5cf28780 (diff)
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.
-rw-r--r--src/long_mode/mod.rs49
1 files changed, 48 insertions, 1 deletions
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index bc14a50..afa6642 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<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::Word>,
+ S: DescriptionSink<FieldDescription>,
+>(mut b: u8, mut record: OpcodeRecord, nextb: &mut u8, next_rec: &mut OpcodeRecord, words: &mut T, instruction: &mut Instruction, sink: &mut S) -> Result<Option<OperandCode>, 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<<Arch as yaxpeax_arch::Arch>::Address, <Arch as yaxpeax_arch::Arch>::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;