Browse Source

Use opcode table rather than match (#2501)

`execute_instruction` is heavily used. After decoding an opcode, `match` is used to find a proper `execute` function for the opcode. But, the `match` may not be able to be optimized into a table jump by rust compiler, so it may use multiple branches to find the function. When I tested with a toy program, only `enum -> &'static str` case was optimized to use a table while `enum -> function call` uses multiple branches. ([gotbolt](https://rust.godbolt.org/z/1rzK5vj6f))

This change makes the opcode to use a table explicitly. It improves the benchmark score of Richards by 1-2% (22.8 -> 23.2).
pull/2509/head
Choongwoo Han 2 years ago
parent
commit
ab2e6e1ab3
  1. 36
      boa_engine/src/vm/opcode/mod.rs

36
boa_engine/src/vm/opcode/mod.rs

@ -98,7 +98,7 @@ macro_rules! generate_impl {
pub enum $Type:ident { pub enum $Type:ident {
$( $(
$(#[$inner:ident $($args:tt)*])* $(#[$inner:ident $($args:tt)*])*
$Variant:ident $Variant:ident $(= $index:expr)*
),* ),*
$(,)? $(,)?
} }
@ -108,7 +108,7 @@ macro_rules! generate_impl {
pub enum $Type { pub enum $Type {
$( $(
$(#[$inner $($args)*])* $(#[$inner $($args)*])*
$Variant $Variant $(= $index)*
),* ),*
} }
@ -126,32 +126,32 @@ macro_rules! generate_impl {
unsafe { std::mem::transmute(value) } unsafe { std::mem::transmute(value) }
} }
const NAMES: &[&'static str] = &[
$($Variant::NAME),*
];
/// Name of this opcode. /// Name of this opcode.
#[must_use] #[must_use]
pub const fn as_str(self) -> &'static str { pub const fn as_str(self) -> &'static str {
match self { Self::NAMES[self as usize]
$(
Self::$Variant => $Variant::NAME
),*
}
} }
const INSTRUCTIONS: &[&'static str] = &[
$($Variant::INSTRUCTION),*
];
/// Name of the profiler event for this opcode. /// Name of the profiler event for this opcode.
#[must_use] #[must_use]
pub const fn as_instruction_str(self) -> &'static str { pub const fn as_instruction_str(self) -> &'static str {
match self { Self::INSTRUCTIONS[self as usize]
$(
Self::$Variant => $Variant::INSTRUCTION
),*
}
} }
const EXECUTE_FNS: &[fn(&mut Context) -> JsResult<ShouldExit>] = &[
$($Variant::execute),*
];
pub(super) fn execute(self, context: &mut Context) -> JsResult<ShouldExit> { pub(super) fn execute(self, context: &mut Context) -> JsResult<ShouldExit> {
match self { Self::EXECUTE_FNS[self as usize](context)
$(
Self::$Variant => $Variant::execute(context)
),*
}
} }
} }
}; };
@ -179,7 +179,7 @@ generate_impl! {
/// Operands: /// Operands:
/// ///
/// Stack: value **=>** /// Stack: value **=>**
Pop, Pop = 0,
/// Pop the top value from the stack if the last try block has thrown a value. /// Pop the top value from the stack if the last try block has thrown a value.
/// ///

Loading…
Cancel
Save