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> {
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(
context
.interner()
.resolve_expect(frame.code.name)
.to_owned(),
);
prev_frame = frame.prev.as_ref();
}
stack_trace

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

@ -263,7 +263,7 @@ impl Generator {
let result = context.run();
generator_context.call_frame = *context
generator_context.call_frame = context
.vm
.pop_frame()
.expect("generator call frame must exist");
@ -384,7 +384,7 @@ impl Generator {
context.run()
}
};
generator_context.call_frame = *context
generator_context.call_frame = context
.vm
.pop_frame()
.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");
self.vm.push_frame(CallFrame {
prev: None,
code: code_block,
pc: 0,
catch: Vec::new(),
@ -833,7 +832,7 @@ impl ContextBuilder {
console: Console::default(),
intrinsics: Intrinsics::default(),
vm: Vm {
frame: None,
frames: Vec::with_capacity(16),
stack: Vec::with_capacity(1024),
trace: false,
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)]
pub struct CallFrame {
pub(crate) prev: Option<Box<Self>>,
pub(crate) code: Gc<CodeBlock>,
pub(crate) pc: usize,
#[unsafe_ignore_trace]

6
boa_engine/src/vm/code_block.rs

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

24
boa_engine/src/vm/mod.rs

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

2
test262

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