Skip to content

Commit 437a77c

Browse files
perf: optimize nightly tail dispatch
Similar to #55 but with only safe code. LLVM seems so optimize the discriminant conversion away. Signed-off-by: Henry <mail@henrygressmann.de>
1 parent 795ec87 commit 437a77c

2 files changed

Lines changed: 59 additions & 26 deletions

File tree

crates/tinywasm/src/interpreter/executor/dispatch_become.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ fn instruction_handler_mismatch() -> ! {
1515
macro_rules! define_unbudgeted_tail_dispatch {
1616
($executor:ident, $instr_ptr:ident, $dispatch_next:ident, $dispatch_flow:ident;
1717
$($variant:ident $(($($arg:pat),*))? $({ $($field:ident),* })? => $body:expr),* $(,)?) => {
18-
fn handler_for(instruction: &Instruction) -> UnbudgetedHandler {
19-
use tinywasm_types::Instruction::*;
20-
21-
match instruction {
22-
$($variant { .. } => Self::$variant,)*
23-
}
18+
#[inline(always)]
19+
fn handler_for(opcode: InstructionOpcode) -> UnbudgetedHandler {
20+
static HANDLERS: [UnbudgetedHandler; InstructionOpcode::COUNT] = {
21+
let mut handlers = [Unbudgeted::Unreachable as UnbudgetedHandler; InstructionOpcode::COUNT];
22+
$(handlers[InstructionOpcode::$variant as usize] = Unbudgeted::$variant;)*
23+
handlers
24+
};
25+
HANDLERS[opcode as usize]
2426
}
2527

2628
$(
@@ -34,7 +36,7 @@ macro_rules! define_unbudgeted_tail_dispatch {
3436
($next_instr_ptr:expr) => {{
3537
let next_instr_ptr = $next_instr_ptr;
3638
let instruction = $executor.func.instructions[next_instr_ptr];
37-
let handler = Self::handler_for(&instruction);
39+
let handler = Self::handler_for(instruction.opcode());
3840
become handler($executor, next_instr_ptr, instruction);
3941
}};
4042
}
@@ -63,12 +65,14 @@ macro_rules! define_unbudgeted_tail_dispatch {
6365
macro_rules! define_bounded_tail_dispatch {
6466
($executor:ident, $instr_ptr:ident, $dispatch_next:ident, $dispatch_flow:ident;
6567
$($variant:ident $(($($arg:pat),*))? $({ $($field:ident),* })? => $body:expr),* $(,)?) => {
66-
fn handler_for(instruction: &Instruction) -> BoundedHandler {
67-
use tinywasm_types::Instruction::*;
68-
69-
match instruction {
70-
$($variant { .. } => Self::$variant,)*
71-
}
68+
#[inline(always)]
69+
fn handler_for(opcode: InstructionOpcode) -> BoundedHandler {
70+
static HANDLERS: [BoundedHandler; InstructionOpcode::COUNT] = {
71+
let mut handlers = [Bounded::Unreachable as BoundedHandler; InstructionOpcode::COUNT];
72+
$(handlers[InstructionOpcode::$variant as usize] = Bounded::$variant;)*
73+
handlers
74+
};
75+
HANDLERS[opcode as usize]
7276
}
7377

7478
$(
@@ -90,7 +94,7 @@ macro_rules! define_bounded_tail_dispatch {
9094
}
9195

9296
let instruction = $executor.func.instructions[next_instr_ptr];
93-
let handler = Self::handler_for(&instruction);
97+
let handler = Self::handler_for(instruction.opcode());
9498
become handler($executor, next_instr_ptr, instruction, instructions_until_checkpoint - 1);
9599
}};
96100
}
@@ -130,7 +134,7 @@ impl Bounded {
130134
fn run(executor: &mut Executor<'_>) -> ExecResult<()> {
131135
let instr_ptr = executor.cf.instr_ptr;
132136
let instruction = executor.func.instructions[instr_ptr];
133-
let handler = Self::handler_for(&instruction);
137+
let handler = Self::handler_for(instruction.opcode());
134138
handler(executor, instr_ptr, instruction, CHECKPOINT_INTERVAL - 1)
135139
}
136140
}
@@ -140,7 +144,7 @@ impl<'store> Executor<'store> {
140144
pub(crate) fn run_to_completion(mut self) -> Result<()> {
141145
let instr_ptr = self.cf.instr_ptr;
142146
let instruction = self.func.instructions[instr_ptr];
143-
let handler = Unbudgeted::handler_for(&instruction);
147+
let handler = Unbudgeted::handler_for(instruction.opcode());
144148
Ok(handler(&mut self, instr_ptr, instruction)?)
145149
}
146150

crates/types/src/instructions.rs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -500,16 +500,45 @@ pub enum BinOp128 {
500500
I64x2Mul,
501501
}
502502

503-
/// A TinyWasm bytecode instruction.
504-
///
505-
/// These instructions are an internal, version-specific representation and do not
506-
/// map one-to-one to WebAssembly instructions. Their variants and serialized form
507-
/// may change between TinyWasm releases.
508-
#[rustfmt::skip]
509-
#[derive(Clone, Copy, PartialEq)]
510-
#[cfg_attr(feature = "debug", derive(Debug))]
511-
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
512-
pub enum Instruction {
503+
macro_rules! define_instructions {
504+
($($variant:ident $(($($field:ty),* $(,)?))? $({ $($name:ident: $named_field:ty),* $(,)? })?),* $(,)?) => {
505+
/// A TinyWasm bytecode instruction.
506+
///
507+
/// These instructions are an internal, version-specific representation and do not
508+
/// map one-to-one to WebAssembly instructions. Their variants and serialized form
509+
/// may change between TinyWasm releases.
510+
#[derive(Clone, Copy, PartialEq)]
511+
#[cfg_attr(feature = "debug", derive(Debug))]
512+
#[cfg_attr(feature = "archive", derive(serde::Serialize, serde::Deserialize))]
513+
pub enum Instruction {
514+
$($variant $(($($field),*))? $({ $($name: $named_field),* })?),*
515+
}
516+
517+
#[doc(hidden)]
518+
#[repr(u16)]
519+
#[derive(Clone, Copy)]
520+
pub enum InstructionOpcode {
521+
$($variant,)*
522+
}
523+
524+
impl InstructionOpcode {
525+
#[doc(hidden)]
526+
pub const COUNT: usize = [$(Self::$variant),*].len();
527+
}
528+
529+
impl Instruction {
530+
/// Returns the compact opcode used to dispatch this instruction.
531+
#[inline(always)]
532+
pub const fn opcode(&self) -> InstructionOpcode {
533+
match self {
534+
$(Self::$variant { .. } => InstructionOpcode::$variant,)*
535+
}
536+
}
537+
}
538+
};
539+
}
540+
541+
define_instructions! {
513542
LocalCopy32(LocalAddr, LocalAddr), LocalCopy64(LocalAddr, LocalAddr), LocalCopy128(LocalAddr, LocalAddr),
514543
AddConst32(i32), AndConst32(i32), XorConst32(i32), ShrUConst32(i32), AddConst64(Operand64Idx<i64>),
515544
IncLocal32(I32LocalArg), IncLocal64(PackedOp64<LocalAddr, i64>),

0 commit comments

Comments
 (0)