Browse Source

Store call frames in `Vec` instead of singly-linked list (#2164)

This storage method should be more cache friendly since we store in contiguous memory, besides that it should make #2157 a bit easier. Will work on that next after this gets merged :)

It changes the following:
- Remove the unneeded `prev` field in `CallFrame`
- Preallocate some space for future calls (16 slots)
pull/2160/head
Halid Odat 2 years ago
parent
commit
64f59ddacb
  1. 4
      boa_engine/src/builtins/console/mod.rs
  2. 4
      boa_engine/src/builtins/generator/mod.rs
  3. 3
      boa_engine/src/context/mod.rs
  4. 1
      boa_engine/src/vm/call_frame.rs
  5. 6
      boa_engine/src/vm/code_block.rs
  6. 24
      boa_engine/src/vm/mod.rs
  7. 2
      test262

4
boa_engine/src/builtins/console/mod.rs

@ -294,16 +294,14 @@ impl Console {
fn get_stack_trace(context: &mut Context) -> Vec<String> { fn get_stack_trace(context: &mut Context) -> Vec<String> {
let mut stack_trace: Vec<String> = vec![]; let mut stack_trace: Vec<String> = vec![];
let mut prev_frame = context.vm.frame.as_ref();
while let Some(frame) = prev_frame { for frame in context.vm.frames.iter().rev() {
stack_trace.push( stack_trace.push(
context context
.interner() .interner()
.resolve_expect(frame.code.name) .resolve_expect(frame.code.name)
.to_owned(), .to_owned(),
); );
prev_frame = frame.prev.as_ref();
} }
stack_trace stack_trace

4
boa_engine/src/builtins/generator/mod.rs

@ -263,7 +263,7 @@ impl Generator {
let result = context.run(); let result = context.run();
generator_context.call_frame = *context generator_context.call_frame = context
.vm .vm
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
@ -384,7 +384,7 @@ impl Generator {
context.run() context.run()
} }
}; };
generator_context.call_frame = *context generator_context.call_frame = context
.vm .vm
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");

3
boa_engine/src/context/mod.rs

@ -714,7 +714,6 @@ impl Context {
let _timer = Profiler::global().start_event("Execution", "Main"); let _timer = Profiler::global().start_event("Execution", "Main");
self.vm.push_frame(CallFrame { self.vm.push_frame(CallFrame {
prev: None,
code: code_block, code: code_block,
pc: 0, pc: 0,
catch: Vec::new(), catch: Vec::new(),
@ -833,7 +832,7 @@ impl ContextBuilder {
console: Console::default(), console: Console::default(),
intrinsics: Intrinsics::default(), intrinsics: Intrinsics::default(),
vm: Vm { vm: Vm {
frame: None, frames: Vec::with_capacity(16),
stack: Vec::with_capacity(1024), stack: Vec::with_capacity(1024),
trace: false, trace: false,
stack_size_limit: 1024, stack_size_limit: 1024,

1
boa_engine/src/vm/call_frame.rs

@ -7,7 +7,6 @@ use boa_gc::{Finalize, Gc, Trace};
#[derive(Clone, Debug, Finalize, Trace)] #[derive(Clone, Debug, Finalize, Trace)]
pub struct CallFrame { pub struct CallFrame {
pub(crate) prev: Option<Box<Self>>,
pub(crate) code: Gc<CodeBlock>, pub(crate) code: Gc<CodeBlock>,
pub(crate) pc: usize, pub(crate) pc: usize,
#[unsafe_ignore_trace] #[unsafe_ignore_trace]

6
boa_engine/src/vm/code_block.rs

@ -723,7 +723,6 @@ impl JsObject {
let has_expressions = code.params.has_expressions(); let has_expressions = code.params.has_expressions();
context.vm.push_frame(CallFrame { context.vm.push_frame(CallFrame {
prev: None,
code, code,
pc: 0, pc: 0,
catch: Vec::new(), catch: Vec::new(),
@ -844,7 +843,6 @@ impl JsObject {
let has_expressions = code.params.has_expressions(); let has_expressions = code.params.has_expressions();
context.vm.push_frame(CallFrame { context.vm.push_frame(CallFrame {
prev: None,
code, code,
pc: 0, pc: 0,
catch: Vec::new(), catch: Vec::new(),
@ -955,7 +953,6 @@ impl JsObject {
let param_count = code.params.parameters.len(); let param_count = code.params.parameters.len();
let call_frame = CallFrame { let call_frame = CallFrame {
prev: None,
code, code,
pc: 0, pc: 0,
catch: Vec::new(), catch: Vec::new(),
@ -999,7 +996,7 @@ impl JsObject {
state: GeneratorState::SuspendedStart, state: GeneratorState::SuspendedStart,
context: Some(Gc::new(Cell::new(GeneratorContext { context: Some(Gc::new(Cell::new(GeneratorContext {
environments, environments,
call_frame: *call_frame, call_frame,
stack, stack,
}))), }))),
}), }),
@ -1189,7 +1186,6 @@ impl JsObject {
let param_count = code.params.parameters.len(); let param_count = code.params.parameters.len();
context.vm.push_frame(CallFrame { context.vm.push_frame(CallFrame {
prev: None,
code, code,
pc: 0, pc: 0,
catch: Vec::new(), catch: Vec::new(),

24
boa_engine/src/vm/mod.rs

@ -39,7 +39,7 @@ mod tests;
/// Virtual Machine. /// Virtual Machine.
#[derive(Debug)] #[derive(Debug)]
pub struct Vm { pub struct Vm {
pub(crate) frame: Option<Box<CallFrame>>, pub(crate) frames: Vec<CallFrame>,
pub(crate) stack: Vec<JsValue>, pub(crate) stack: Vec<JsValue>,
pub(crate) trace: bool, pub(crate) trace: bool,
pub(crate) stack_size_limit: usize, pub(crate) stack_size_limit: usize,
@ -81,7 +81,7 @@ impl Vm {
/// If there is no frame, then this will panic. /// If there is no frame, then this will panic.
#[inline] #[inline]
pub(crate) fn frame(&self) -> &CallFrame { pub(crate) fn frame(&self) -> &CallFrame {
self.frame.as_ref().expect("no frame found") self.frames.last().expect("no frame found")
} }
/// Retrieves the VM frame mutably /// Retrieves the VM frame mutably
@ -91,21 +91,17 @@ impl Vm {
/// If there is no frame, then this will panic. /// If there is no frame, then this will panic.
#[inline] #[inline]
pub(crate) fn frame_mut(&mut self) -> &mut CallFrame { pub(crate) fn frame_mut(&mut self) -> &mut CallFrame {
self.frame.as_mut().expect("no frame found") self.frames.last_mut().expect("no frame found")
} }
#[inline] #[inline]
pub(crate) fn push_frame(&mut self, mut frame: CallFrame) { pub(crate) fn push_frame(&mut self, frame: CallFrame) {
let prev = self.frame.take(); self.frames.push(frame);
frame.prev = prev;
self.frame = Some(Box::new(frame));
} }
#[inline] #[inline]
pub(crate) fn pop_frame(&mut self) -> Option<Box<CallFrame>> { pub(crate) fn pop_frame(&mut self) -> Option<CallFrame> {
let mut current = self.frame.take()?; self.frames.pop()
self.frame = current.prev.take();
Some(current)
} }
} }
@ -2307,7 +2303,7 @@ impl Context {
context.vm.push(args.get_or_undefined(0)); context.vm.push(args.get_or_undefined(0));
context.run()?; context.run()?;
*frame = *context *frame = context
.vm .vm
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
@ -2346,7 +2342,7 @@ impl Context {
context.vm.push(args.get_or_undefined(0)); context.vm.push(args.get_or_undefined(0));
context.run()?; context.run()?;
*frame = *context *frame = context
.vm .vm
.pop_frame() .pop_frame()
.expect("generator call frame must exist"); .expect("generator call frame must exist");
@ -2392,7 +2388,7 @@ impl Context {
let _timer = Profiler::global().start_event("run", "vm"); let _timer = Profiler::global().start_event("run", "vm");
if self.vm.trace { if self.vm.trace {
let msg = if self.vm.frame().prev.is_some() { let msg = if self.vm.frames.get(self.vm.frames.len() - 2).is_some() {
" Call Frame " " Call Frame "
} else { } else {
" VM Start " " VM Start "

2
test262

@ -1 +1 @@
Subproject commit 1b3bddbeff6b1de63d38250c283e212fe27ffb2d Subproject commit f0bf5dfcea7bce15eb4f7e0b0e97b2a5f3e830c5
Loading…
Cancel
Save