Browse Source

Fix panics and refactor code

tco
Haled Odat 12 months ago committed by RageKnify
parent
commit
a7da92ab71
No known key found for this signature in database
  1. 29
      core/engine/src/bytecompiler/mod.rs
  2. 67
      core/engine/src/vm/code_block.rs
  3. 46
      core/engine/src/vm/opcode/call/mod.rs

29
core/engine/src/bytecompiler/mod.rs

@ -1512,24 +1512,25 @@ impl<'ctx> ByteCompiler<'ctx> {
fn optimize(bytecode: &mut Vec<u8>, flags: CodeBlockFlags) { fn optimize(bytecode: &mut Vec<u8>, flags: CodeBlockFlags) {
// only perform Tail Call Optimisation in strict mode // only perform Tail Call Optimisation in strict mode
if flags.contains(CodeBlockFlags::STRICT) { if flags.contains(CodeBlockFlags::STRICT) && flags.is_ordinary() {
// if a sequence of Call, SetReturnValue, CheckReturn, Return is found // if a sequence of Call, SetReturnValue, CheckReturn, Return is found
// replace Call with TailCall to allow frame elimination // replace Call with TailCall to allow frame elimination
for i in 0..bytecode.len() { for i in 0..bytecode.len() {
let opcode: Opcode = bytecode[i].into(); let opcode: Opcode = bytecode[i].into();
if matches!(opcode, Opcode::Call) { if !matches!(opcode, Opcode::Call) {
if bytecode.len() > i + 5 { continue;
let second = bytecode[i + 2].into(); }
let third = bytecode[i + 3].into(); if bytecode.len() <= i + 5 {
let fourth = bytecode[i + 4].into(); return;
if let (Opcode::SetReturnValue, Opcode::CheckReturn, Opcode::Return) = }
(second, third, fourth)
{ let second = bytecode[i + 2].into();
bytecode[i] = Opcode::TailCall as u8; let third = bytecode[i + 3].into();
} let fourth = bytecode[i + 4].into();
} else { if let (Opcode::SetReturnValue, Opcode::CheckReturn, Opcode::Return) =
return; (second, third, fourth)
} {
bytecode[i] = Opcode::TailCall as u8;
} }
} }
} }

67
core/engine/src/vm/code_block.rs

@ -76,6 +76,47 @@ bitflags! {
} }
} }
impl CodeBlockFlags {
/// Check if the function is traced.
#[cfg(feature = "trace")]
pub(crate) fn traceable(&self) -> bool {
self.contains(CodeBlockFlags::TRACEABLE)
}
/// Check if the function is a class constructor.
pub(crate) fn is_class_constructor(&self) -> bool {
self.contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
}
/// Check if the function is in strict mode.
pub(crate) fn strict(&self) -> bool {
self.contains(CodeBlockFlags::STRICT)
}
/// Indicates if the function is an expression and has a binding identifier.
pub(crate) fn has_binding_identifier(&self) -> bool {
self.contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
}
/// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.
pub(crate) fn in_class_field_initializer(&self) -> bool {
self.contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
}
/// Returns true if this function is a derived constructor.
pub(crate) fn is_derived_constructor(&self) -> bool {
self.contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
}
/// Returns true if this function an async function.
pub(crate) fn is_async(self) -> bool {
self.contains(CodeBlockFlags::IS_ASYNC)
}
/// Returns true if this function an generator function.
pub(crate) fn is_generator(self) -> bool {
self.contains(CodeBlockFlags::IS_GENERATOR)
}
/// Returns true if this function an async function.
pub(crate) fn is_ordinary(self) -> bool {
!self.is_async() && !self.is_generator()
}
}
// SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe. // SAFETY: Nothing in CodeBlockFlags needs tracing, so this is safe.
unsafe impl Trace for CodeBlockFlags { unsafe impl Trace for CodeBlockFlags {
empty_trace!(); empty_trace!();
@ -232,7 +273,7 @@ impl CodeBlock {
/// Check if the function is traced. /// Check if the function is traced.
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
pub(crate) fn traceable(&self) -> bool { pub(crate) fn traceable(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::TRACEABLE) self.flags.get().traceable()
} }
/// Enable or disable instruction tracing to `stdout`. /// Enable or disable instruction tracing to `stdout`.
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
@ -245,50 +286,42 @@ impl CodeBlock {
/// Check if the function is a class constructor. /// Check if the function is a class constructor.
pub(crate) fn is_class_constructor(&self) -> bool { pub(crate) fn is_class_constructor(&self) -> bool {
self.flags self.flags.get().is_class_constructor()
.get()
.contains(CodeBlockFlags::IS_CLASS_CONSTRUCTOR)
} }
/// Check if the function is in strict mode. /// Check if the function is in strict mode.
pub(crate) fn strict(&self) -> bool { pub(crate) fn strict(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::STRICT) self.flags.get().strict()
} }
/// Indicates if the function is an expression and has a binding identifier. /// Indicates if the function is an expression and has a binding identifier.
pub(crate) fn has_binding_identifier(&self) -> bool { pub(crate) fn has_binding_identifier(&self) -> bool {
self.flags self.flags.get().has_binding_identifier()
.get()
.contains(CodeBlockFlags::HAS_BINDING_IDENTIFIER)
} }
/// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value. /// Does this function have the `[[ClassFieldInitializerName]]` internal slot set to non-empty value.
pub(crate) fn in_class_field_initializer(&self) -> bool { pub(crate) fn in_class_field_initializer(&self) -> bool {
self.flags self.flags.get().in_class_field_initializer()
.get()
.contains(CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER)
} }
/// Returns true if this function is a derived constructor. /// Returns true if this function is a derived constructor.
pub(crate) fn is_derived_constructor(&self) -> bool { pub(crate) fn is_derived_constructor(&self) -> bool {
self.flags self.flags.get().is_derived_constructor()
.get()
.contains(CodeBlockFlags::IS_DERIVED_CONSTRUCTOR)
} }
/// Returns true if this function an async function. /// Returns true if this function an async function.
pub(crate) fn is_async(&self) -> bool { pub(crate) fn is_async(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::IS_ASYNC) self.flags.get().is_async()
} }
/// Returns true if this function an generator function. /// Returns true if this function an generator function.
pub(crate) fn is_generator(&self) -> bool { pub(crate) fn is_generator(&self) -> bool {
self.flags.get().contains(CodeBlockFlags::IS_GENERATOR) self.flags.get().is_generator()
} }
/// Returns true if this function an async function. /// Returns true if this function an async function.
pub(crate) fn is_ordinary(&self) -> bool { pub(crate) fn is_ordinary(&self) -> bool {
!self.is_async() && !self.is_generator() self.flags.get().is_ordinary()
} }
/// Returns true if this function has the `"prototype"` property when function object is created. /// Returns true if this function has the `"prototype"` property when function object is created.

46
core/engine/src/vm/opcode/call/mod.rs

@ -3,7 +3,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
module::{ModuleKind, Referrer}, module::{ModuleKind, Referrer},
object::FunctionObjectBuilder, object::FunctionObjectBuilder,
vm::{opcode::Operation, CompletionType}, vm::{opcode::Operation, CallFrame, CompletionType},
Context, JsObject, JsResult, JsValue, NativeFunction, Context, JsObject, JsResult, JsValue, NativeFunction,
}; };
@ -210,7 +210,7 @@ pub(crate) struct TailCall;
impl TailCall { impl TailCall {
fn operation(context: &mut Context, argument_count: usize) -> JsResult<CompletionType> { fn operation(context: &mut Context, argument_count: usize) -> JsResult<CompletionType> {
let at = context.vm.stack.len() - argument_count; let at = context.vm.stack.len() - argument_count;
let func = &context.vm.stack[at - 1]; let func = context.vm.stack[at - 1].clone();
let Some(object) = func.as_object() else { let Some(object) = func.as_object() else {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
@ -218,39 +218,25 @@ impl TailCall {
.into()); .into());
}; };
let is_ordinary_function = object.is::<OrdinaryFunction>(); let mut early_exit = false;
object.__call__(argument_count).resolve(context)?; if object.is::<OrdinaryFunction>() {
if let Some(caller) = context.vm.pop_frame() {
// only tail call for ordinary functions let fp = caller.fp as usize;
// don't tail call on the main script
// TODO: the 3 needs to be reviewed
if is_ordinary_function && context.vm.frames.len() > 3 {
// check that caller is also ordinary function
let frames = &context.vm.frames;
let caller_frame = &frames[frames.len() - 2];
let caller_function = caller_frame
.function(&context.vm)
.expect("there must be a caller function");
if caller_function.is::<OrdinaryFunction>() {
// remove caller's CallFrame
let frames = &mut context.vm.frames;
let caller_frame = frames.swap_remove(frames.len() - 2);
// remove caller's prologue from stack
// this + func + arguments
let to_remove = 1 + 1 + caller_frame.argument_count as usize;
context context
.vm .vm
.stack .stack
.drain((caller_frame.fp as usize)..(caller_frame.fp as usize + to_remove)); .drain(fp..(at - CallFrame::FUNCTION_PROLOGUE));
early_exit = caller.exit_early();
// update invoked function's fp
let frames = &mut context.vm.frames;
let invoked_frame = frames.last_mut().expect("invoked frame must exist");
invoked_frame.set_exit_early(caller_frame.exit_early());
invoked_frame.fp -= to_remove as u32;
} }
} }
object.__call__(argument_count).resolve(context)?;
// If the caller is early exit, since it has been poped from the frames
// we have to mark the current frame as early exit.
if early_exit {
context.vm.frame_mut().set_exit_early(true);
}
Ok(CompletionType::Normal) Ok(CompletionType::Normal)
} }
} }

Loading…
Cancel
Save