diff --git a/core/engine/src/builtins/array/mod.rs b/core/engine/src/builtins/array/mod.rs index 6fb6fa8118a..042e66b2f5c 100644 --- a/core/engine/src/builtins/array/mod.rs +++ b/core/engine/src/builtins/array/mod.rs @@ -385,7 +385,7 @@ impl Array { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-createarrayfromlist - pub(crate) fn create_array_from_list(elements: I, context: &mut Context) -> JsObject + pub(crate) fn create_array_from_list(elements: I, context: &Context) -> JsObject where I: IntoIterator, { diff --git a/core/engine/src/builtins/eval/mod.rs b/core/engine/src/builtins/eval/mod.rs index 7c592d6cfb6..b55de3cbd16 100644 --- a/core/engine/src/builtins/eval/mod.rs +++ b/core/engine/src/builtins/eval/mod.rs @@ -254,6 +254,8 @@ impl Eval { false, var_env.clone(), lex_env.clone(), + false, + false, context.interner_mut(), in_with, ); diff --git a/core/engine/src/builtins/json/mod.rs b/core/engine/src/builtins/json/mod.rs index 06e047ea7c8..08dcdd7d549 100644 --- a/core/engine/src/builtins/json/mod.rs +++ b/core/engine/src/builtins/json/mod.rs @@ -120,6 +120,8 @@ impl Json { true, context.realm().environment().compile_env(), context.realm().environment().compile_env(), + false, + false, context.interner_mut(), in_with, ); diff --git a/core/engine/src/bytecompiler/class.rs b/core/engine/src/bytecompiler/class.rs index f18b9f6f9e0..0b67e8cb060 100644 --- a/core/engine/src/bytecompiler/class.rs +++ b/core/engine/src/bytecompiler/class.rs @@ -1,4 +1,4 @@ -use super::{ByteCompiler, Literal, Operand, ToJsString}; +use super::{ByteCompiler, InstructionOperand, Literal, Operand, Operand2, ToJsString}; use crate::{ js_string, vm::{BindingOpcode, CodeBlock, CodeBlockFlags, Opcode}, @@ -54,6 +54,8 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), + false, + false, self.interner, self.in_with, ); @@ -84,7 +86,7 @@ impl ByteCompiler<'_> { } else { compiler.emit_opcode(Opcode::PushUndefined); } - compiler.emit_opcode(Opcode::SetReturnValue); + compiler.emit_opcode(Opcode::SetAccumulatorFromStack); // 17. If ClassHeritageopt is present, set F.[[ConstructorKind]] to derived. compiler.code_block_flags.set( @@ -94,19 +96,43 @@ impl ByteCompiler<'_> { let code = Gc::new(compiler.finish()); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); - self.emit_opcode(Opcode::Dup); + let class_register = self.register_allocator.alloc(); + self.emit_get_function(&class_register, index); + + let prototype_register = self.register_allocator.alloc(); + if let Some(node) = class.super_ref() { self.compile_expr(node, true); - self.emit_opcode(Opcode::PushClassPrototype); + self.pop_into_register(&prototype_register); + + self.emit2( + Opcode::PushClassPrototype, + &[ + Operand2::Register(&prototype_register), + Operand2::Operand(InstructionOperand::Register(&class_register)), + Operand2::Operand(InstructionOperand::Register(&prototype_register)), + ], + ); } else { self.emit_opcode(Opcode::PushUndefined); + self.pop_into_register(&prototype_register); } - self.emit_opcode(Opcode::SetClassPrototype); - self.emit_opcode(Opcode::Swap); - let count_label = self.emit_opcode_with_operand(Opcode::PushPrivateEnvironment); + let proto_register = self.register_allocator.alloc(); + + self.emit2( + Opcode::SetClassPrototype, + &[ + Operand2::Register(&proto_register), + Operand2::Operand(InstructionOperand::Register(&prototype_register)), + Operand2::Operand(InstructionOperand::Register(&class_register)), + ], + ); + self.register_allocator.dealloc(prototype_register); + + let count_label = + self.emit_push_private_environment(InstructionOperand::Register(&class_register)); let mut count = 0; for element in class.elements() { match element { @@ -127,10 +153,16 @@ impl ByteCompiler<'_> { let mut static_field_name_count = 0; if old_lex_env.is_some() { - self.emit_opcode(Opcode::Dup); + self.push_from_register(&class_register); self.emit_binding(BindingOpcode::InitLexical, class_name.clone()); } + self.push_from_register(&proto_register); + self.push_from_register(&class_register); + + self.register_allocator.dealloc(proto_register); + self.register_allocator.dealloc(class_register); + // TODO: set function name for getter and setters for element in class.elements() { match element { @@ -288,6 +320,8 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), + false, + false, self.interner, self.in_with, ); @@ -299,13 +333,18 @@ impl ByteCompiler<'_> { } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - field_compiler.emit_opcode(Opcode::SetReturnValue); + field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; let code = Gc::new(field_compiler.finish()); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); + self.emit_opcode(Opcode::PushClassField); } ClassElement::PrivateFieldDefinition(name, field) => { @@ -317,6 +356,8 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), + false, + false, self.interner, self.in_with, ); @@ -326,13 +367,16 @@ impl ByteCompiler<'_> { } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - field_compiler.emit_opcode(Opcode::SetReturnValue); + field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; let code = Gc::new(field_compiler.finish()); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); self.emit_with_varying_operand(Opcode::PushClassFieldPrivate, name_index); } ClassElement::StaticFieldDefinition(name, field) => { @@ -356,6 +400,8 @@ impl ByteCompiler<'_> { self.json_parse, self.variable_environment.clone(), self.lexical_environment.clone(), + false, + false, self.interner, self.in_with, ); @@ -365,7 +411,7 @@ impl ByteCompiler<'_> { } else { field_compiler.emit_opcode(Opcode::PushUndefined); } - field_compiler.emit_opcode(Opcode::SetReturnValue); + field_compiler.emit_opcode(Opcode::SetAccumulatorFromStack); field_compiler.code_block_flags |= CodeBlockFlags::IN_CLASS_FIELD_INITIALIZER; @@ -391,6 +437,8 @@ impl ByteCompiler<'_> { false, self.variable_environment.clone(), self.lexical_environment.clone(), + false, + false, self.interner, self.in_with, ); @@ -555,7 +603,10 @@ impl ByteCompiler<'_> { StaticElement::StaticBlock(code) => { self.emit_opcode(Opcode::Dup); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); self.emit_opcode(Opcode::SetHomeObject); self.emit_with_varying_operand(Opcode::Call, 0); self.emit_opcode(Opcode::Pop); @@ -564,7 +615,10 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::Dup); self.emit_opcode(Opcode::Dup); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); self.emit_opcode(Opcode::SetHomeObject); self.emit_with_varying_operand(Opcode::Call, 0); if let Some(name_index) = name_index { diff --git a/core/engine/src/bytecompiler/declarations.rs b/core/engine/src/bytecompiler/declarations.rs index 941e349f44f..ad03b6d58dc 100644 --- a/core/engine/src/bytecompiler/declarations.rs +++ b/core/engine/src/bytecompiler/declarations.rs @@ -597,10 +597,13 @@ impl ByteCompiler<'_> { ); // Ensures global functions are printed when generating the global flowgraph. - let function_index = self.push_function_to_constants(code.clone()); + let function_index = self.push_function_to_constants(code); // b. Let fo be InstantiateFunctionObject of f with arguments env and privateEnv. - self.emit_with_varying_operand(Opcode::GetFunction, function_index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, function_index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); // c. Perform ? env.CreateGlobalFunctionBinding(fn, fo, false). let name_index = self.get_or_insert_name(name); @@ -971,7 +974,10 @@ impl ByteCompiler<'_> { let index = self.push_function_to_constants(code.clone()); // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); // i. Perform ? varEnv.CreateGlobalFunctionBinding(fn, fo, true). let name_index = self.get_or_insert_name(name); @@ -984,7 +990,10 @@ impl ByteCompiler<'_> { else { // b. Let fo be InstantiateFunctionObject of f with arguments lexEnv and privateEnv. let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); let name = name.to_js_string(self.interner()); diff --git a/core/engine/src/bytecompiler/expression/assign.rs b/core/engine/src/bytecompiler/expression/assign.rs index 12d3644dc79..bb8bd465525 100644 --- a/core/engine/src/bytecompiler/expression/assign.rs +++ b/core/engine/src/bytecompiler/expression/assign.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Operand, ToJsString}, + bytecompiler::{Access, ByteCompiler, InstructionOperand, Operand, Operand2, ToJsString}, environments::BindingLocatorError, vm::{BindingOpcode, Opcode}, }; @@ -53,6 +53,25 @@ impl ByteCompiler<'_> { let mut pop_count = 0; let mut early_exit = None; + let lhs = self.register_allocator.alloc(); + + let emit_stack_opcode = |this: &mut ByteCompiler<'_>| { + let rhs = this.register_allocator.alloc(); + this.pop_into_register(&rhs); + this.pop_into_register(&lhs); + this.emit2( + opcode, + &[ + Operand2::Register(&lhs), + Operand2::Operand(InstructionOperand::Register(&lhs)), + Operand2::Operand(InstructionOperand::Register(&rhs)), + ], + ); + this.register_allocator.dealloc(rhs); + + this.push_from_register(&lhs); + }; + match access { Access::Variable { name } => { let name = name.to_js_string(self.interner()); @@ -69,15 +88,25 @@ impl ByteCompiler<'_> { } if short_circuit { - early_exit = Some(self.emit_opcode_with_operand(opcode)); + self.pop_into_register(&lhs); + early_exit = + Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } + if use_expr { self.emit_opcode(Opcode::Dup); } + if binding.is_lexical() { match self.lexical_environment.set_mutable_binding(name.clone()) { Ok(binding) => { @@ -105,16 +134,24 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::Dup); self.emit_get_property_by_name(*name); + if short_circuit { pop_count = 2; - early_exit = Some(self.emit_opcode_with_operand(opcode)); + self.pop_into_register(&lhs); + early_exit = Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } self.emit_set_property_by_name(*name); + if !use_expr { self.emit_opcode(Opcode::Pop); } @@ -129,11 +166,18 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::GetPropertyByValuePush); if short_circuit { pop_count = 3; - early_exit = Some(self.emit_opcode_with_operand(opcode)); + + self.pop_into_register(&lhs); + early_exit = Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } self.emit_opcode(Opcode::SetPropertyByValue); @@ -150,11 +194,18 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::GetPrivateField, index); if short_circuit { pop_count = 1; - early_exit = Some(self.emit_opcode_with_operand(opcode)); + + self.pop_into_register(&lhs); + early_exit = Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } self.emit_with_varying_operand(Opcode::SetPrivateField, index); @@ -173,11 +224,18 @@ impl ByteCompiler<'_> { self.emit_get_property_by_name(*name); if short_circuit { pop_count = 2; - early_exit = Some(self.emit_opcode_with_operand(opcode)); + + self.pop_into_register(&lhs); + early_exit = Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } self.emit_set_property_by_name(*name); @@ -194,11 +252,18 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::GetPropertyByValuePush); if short_circuit { pop_count = 2; - early_exit = Some(self.emit_opcode_with_operand(opcode)); + + self.pop_into_register(&lhs); + early_exit = Some(self.emit_opcode_with_operand2( + opcode, + InstructionOperand::Register(&lhs), + )); self.compile_expr(assign.rhs(), true); + self.pop_into_register(&lhs); + self.push_from_register(&lhs); } else { self.compile_expr(assign.rhs(), true); - self.emit_opcode(opcode); + emit_stack_opcode(self); } self.emit_opcode(Opcode::This); @@ -215,18 +280,19 @@ impl ByteCompiler<'_> { } if let Some(early_exit) = early_exit { - if pop_count == 0 { - self.patch_jump(early_exit); - } else { - let exit = self.emit_opcode_with_operand(Opcode::Jump); - self.patch_jump(early_exit); - for _ in 0..pop_count { - self.emit_opcode(Opcode::Swap); - self.emit_opcode(Opcode::Pop); - } - self.patch_jump(exit); + let exit = self.emit_opcode_with_operand(Opcode::Jump); + + self.patch_jump(early_exit); + self.push_from_register(&lhs); + + for _ in 0..pop_count { + self.emit_opcode(Opcode::Swap); + self.emit_opcode(Opcode::Pop); } + self.patch_jump(exit); } + + self.register_allocator.dealloc(lhs); } } } diff --git a/core/engine/src/bytecompiler/expression/binary.rs b/core/engine/src/bytecompiler/expression/binary.rs index 04388e7c6dc..89774acc76e 100644 --- a/core/engine/src/bytecompiler/expression/binary.rs +++ b/core/engine/src/bytecompiler/expression/binary.rs @@ -3,104 +3,138 @@ use boa_ast::expression::operator::{ Binary, BinaryInPrivate, }; -use crate::{bytecompiler::ByteCompiler, vm::Opcode}; +use crate::{ + bytecompiler::{ByteCompiler, InstructionOperand, Operand2, Reg}, + vm::Opcode, +}; impl ByteCompiler<'_> { - pub(crate) fn compile_binary(&mut self, binary: &Binary, use_expr: bool) { + pub(crate) fn compile_binary(&mut self, binary: &Binary, dst: &Reg) { self.compile_expr(binary.lhs(), true); + match binary.op() { BinaryOp::Arithmetic(op) => { self.compile_expr(binary.rhs(), true); - match op { - ArithmeticOp::Add => self.emit_opcode(Opcode::Add), - ArithmeticOp::Sub => self.emit_opcode(Opcode::Sub), - ArithmeticOp::Div => self.emit_opcode(Opcode::Div), - ArithmeticOp::Mul => self.emit_opcode(Opcode::Mul), - ArithmeticOp::Exp => self.emit_opcode(Opcode::Pow), - ArithmeticOp::Mod => self.emit_opcode(Opcode::Mod), - } - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let rhs = self.register_allocator.alloc(); + let lhs = self.register_allocator.alloc(); + + self.pop_into_register(&rhs); + self.pop_into_register(&lhs); + let opcode = match op { + ArithmeticOp::Add => Opcode::Add, + ArithmeticOp::Sub => Opcode::Sub, + ArithmeticOp::Div => Opcode::Div, + ArithmeticOp::Mul => Opcode::Mul, + ArithmeticOp::Exp => Opcode::Pow, + ArithmeticOp::Mod => Opcode::Mod, + }; + + self.emit2( + opcode, + &[ + Operand2::Register(dst), + Operand2::Operand(InstructionOperand::Register(&lhs)), + Operand2::Operand(InstructionOperand::Register(&rhs)), + ], + ); + self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Bitwise(op) => { self.compile_expr(binary.rhs(), true); - match op { - BitwiseOp::And => self.emit_opcode(Opcode::BitAnd), - BitwiseOp::Or => self.emit_opcode(Opcode::BitOr), - BitwiseOp::Xor => self.emit_opcode(Opcode::BitXor), - BitwiseOp::Shl => self.emit_opcode(Opcode::ShiftLeft), - BitwiseOp::Shr => self.emit_opcode(Opcode::ShiftRight), - BitwiseOp::UShr => self.emit_opcode(Opcode::UnsignedShiftRight), - } - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let rhs = self.register_allocator.alloc(); + let lhs = self.register_allocator.alloc(); + + self.pop_into_register(&rhs); + self.pop_into_register(&lhs); + let opcode = match op { + BitwiseOp::And => Opcode::BitAnd, + BitwiseOp::Or => Opcode::BitOr, + BitwiseOp::Xor => Opcode::BitXor, + BitwiseOp::Shl => Opcode::ShiftLeft, + BitwiseOp::Shr => Opcode::ShiftRight, + BitwiseOp::UShr => Opcode::UnsignedShiftRight, + }; + + self.emit2( + opcode, + &[ + Operand2::Register(dst), + Operand2::Operand(InstructionOperand::Register(&lhs)), + Operand2::Operand(InstructionOperand::Register(&rhs)), + ], + ); + self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Relational(op) => { self.compile_expr(binary.rhs(), true); - match op { - RelationalOp::Equal => self.emit_opcode(Opcode::Eq), - RelationalOp::NotEqual => self.emit_opcode(Opcode::NotEq), - RelationalOp::StrictEqual => self.emit_opcode(Opcode::StrictEq), - RelationalOp::StrictNotEqual => self.emit_opcode(Opcode::StrictNotEq), - RelationalOp::GreaterThan => self.emit_opcode(Opcode::GreaterThan), - RelationalOp::GreaterThanOrEqual => { - self.emit_opcode(Opcode::GreaterThanOrEq); - } - RelationalOp::LessThan => self.emit_opcode(Opcode::LessThan), - RelationalOp::LessThanOrEqual => self.emit_opcode(Opcode::LessThanOrEq), - RelationalOp::In => self.emit_opcode(Opcode::In), - RelationalOp::InstanceOf => self.emit_opcode(Opcode::InstanceOf), - } - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let rhs = self.register_allocator.alloc(); + let lhs = self.register_allocator.alloc(); + + self.pop_into_register(&rhs); + self.pop_into_register(&lhs); + let opcode = match op { + RelationalOp::Equal => Opcode::Eq, + RelationalOp::NotEqual => Opcode::NotEq, + RelationalOp::StrictEqual => Opcode::StrictEq, + RelationalOp::StrictNotEqual => Opcode::StrictNotEq, + RelationalOp::GreaterThan => Opcode::GreaterThan, + RelationalOp::GreaterThanOrEqual => Opcode::GreaterThanOrEq, + RelationalOp::LessThan => Opcode::LessThan, + RelationalOp::LessThanOrEqual => Opcode::LessThanOrEq, + RelationalOp::In => Opcode::In, + RelationalOp::InstanceOf => Opcode::InstanceOf, + }; + + self.emit2( + opcode, + &[ + Operand2::Register(dst), + Operand2::Operand(InstructionOperand::Register(&lhs)), + Operand2::Operand(InstructionOperand::Register(&rhs)), + ], + ); + self.register_allocator.dealloc(lhs); + self.register_allocator.dealloc(rhs); } BinaryOp::Logical(op) => { - match op { - LogicalOp::And => { - let exit = self.emit_opcode_with_operand(Opcode::LogicalAnd); - self.compile_expr(binary.rhs(), true); - self.patch_jump(exit); - } - LogicalOp::Or => { - let exit = self.emit_opcode_with_operand(Opcode::LogicalOr); - self.compile_expr(binary.rhs(), true); - self.patch_jump(exit); - } - LogicalOp::Coalesce => { - let exit = self.emit_opcode_with_operand(Opcode::Coalesce); - self.compile_expr(binary.rhs(), true); - self.patch_jump(exit); - } + self.pop_into_register(dst); + + let opcode = match op { + LogicalOp::And => Opcode::LogicalAnd, + LogicalOp::Or => Opcode::LogicalOr, + LogicalOp::Coalesce => Opcode::Coalesce, }; - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + let exit = + self.emit_opcode_with_operand2(opcode, InstructionOperand::Register(dst)); + self.compile_expr(binary.rhs(), true); + self.pop_into_register(dst); + self.patch_jump(exit); } BinaryOp::Comma => { self.emit_opcode(Opcode::Pop); self.compile_expr(binary.rhs(), true); - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.pop_into_register(dst); } - }; + } } - pub(crate) fn compile_binary_in_private(&mut self, binary: &BinaryInPrivate, use_expr: bool) { + pub(crate) fn compile_binary_in_private(&mut self, binary: &BinaryInPrivate, dst: &Reg) { let index = self.get_or_insert_private_name(*binary.lhs()); self.compile_expr(binary.rhs(), true); - self.emit_with_varying_operand(Opcode::InPrivate, index); - - if !use_expr { - self.emit_opcode(Opcode::Pop); - } + self.pop_into_register(dst); + self.emit2( + Opcode::InPrivate, + &[ + Operand2::Register(dst), + Operand2::Varying(index), + Operand2::Operand(InstructionOperand::Register(dst)), + ], + ); } } diff --git a/core/engine/src/bytecompiler/expression/mod.rs b/core/engine/src/bytecompiler/expression/mod.rs index fb229b41c60..00aaf83a97e 100644 --- a/core/engine/src/bytecompiler/expression/mod.rs +++ b/core/engine/src/bytecompiler/expression/mod.rs @@ -93,9 +93,21 @@ impl ByteCompiler<'_> { } Expression::Unary(unary) => self.compile_unary(unary, use_expr), Expression::Update(update) => self.compile_update(update, use_expr), - Expression::Binary(binary) => self.compile_binary(binary, use_expr), + Expression::Binary(binary) => { + let reg = self.register_allocator.alloc(); + self.compile_binary(binary, ®); + if use_expr { + self.push_from_register(®); + } + self.register_allocator.dealloc(reg); + } Expression::BinaryInPrivate(binary) => { - self.compile_binary_in_private(binary, use_expr); + let reg = self.register_allocator.alloc(); + self.compile_binary_in_private(binary, ®); + if use_expr { + self.push_from_register(®); + } + self.register_allocator.dealloc(reg); } Expression::Assign(assign) => self.compile_assign(assign, use_expr), Expression::ObjectLiteral(object) => { diff --git a/core/engine/src/bytecompiler/expression/object_literal.rs b/core/engine/src/bytecompiler/expression/object_literal.rs index 3fcabb0a3e8..fd91cd9985e 100644 --- a/core/engine/src/bytecompiler/expression/object_literal.rs +++ b/core/engine/src/bytecompiler/expression/object_literal.rs @@ -161,19 +161,19 @@ impl ByteCompiler<'_> { function: FunctionSpec<'_>, kind: MethodKind, ) { - // stack: object, object + // stack: object self.compile_expr(name, true); - // stack: object, object, name + // stack: object, name self.emit_opcode(Opcode::ToPropertyKey); - // stack: object, object, ToPropertyKey(name) + // stack: object, ToPropertyKey(name) self.emit_opcode(Opcode::Dup); - // stack: object, object, ToPropertyKey(name), ToPropertyKey(name) + // stack: object, ToPropertyKey(name), ToPropertyKey(name) self.object_method(function); - // stack: object, object, ToPropertyKey(name), ToPropertyKey(name), method + // stack: object, ToPropertyKey(name), ToPropertyKey(name), method let value = match kind { MethodKind::Get => 1, MethodKind::Set => 2, @@ -181,27 +181,29 @@ impl ByteCompiler<'_> { }; self.emit(Opcode::SetFunctionName, &[Operand::U8(value)]); - // stack: object, object, ToPropertyKey(name), method + // stack: object, ToPropertyKey(name), method self.emit(Opcode::RotateLeft, &[Operand::U8(3)]); - // stack: object, ToPropertyKey(name), method, object + // stack: ToPropertyKey(name), method, object self.emit_opcode(Opcode::Swap); - // stack: object, ToPropertyKey(name), object, method + // stack: ToPropertyKey(name), object, method self.emit_opcode(Opcode::SetHomeObject); - // stack: object, ToPropertyKey(name), object, method + // stack: ToPropertyKey(name), object, method self.emit_opcode(Opcode::Swap); - // stack: object, ToPropertyKey(name), method, object + // stack: ToPropertyKey(name), method, object self.emit(Opcode::RotateRight, &[Operand::U8(3)]); - // stack: object, object, ToPropertyKey(name), method + // stack: object, ToPropertyKey(name), method match kind { MethodKind::Get => self.emit_opcode(Opcode::SetPropertyGetterByValue), MethodKind::Set => self.emit_opcode(Opcode::SetPropertySetterByValue), MethodKind::Ordinary => self.emit_opcode(Opcode::DefineOwnPropertyByValue), } + + // stack: } } diff --git a/core/engine/src/bytecompiler/expression/update.rs b/core/engine/src/bytecompiler/expression/update.rs index 7fd0b986cae..aa8cd971950 100644 --- a/core/engine/src/bytecompiler/expression/update.rs +++ b/core/engine/src/bytecompiler/expression/update.rs @@ -1,5 +1,5 @@ use crate::{ - bytecompiler::{Access, ByteCompiler, Operand, ToJsString}, + bytecompiler::{Access, ByteCompiler, InstructionOperand, Operand2, ToJsString}, environments::BindingLocatorError, vm::Opcode, }; @@ -11,10 +11,8 @@ use boa_ast::expression::{ impl ByteCompiler<'_> { pub(crate) fn compile_update(&mut self, update: &Update, use_expr: bool) { let opcode = match update.op() { - UpdateOp::IncrementPre => Opcode::Inc, - UpdateOp::DecrementPre => Opcode::Dec, - UpdateOp::IncrementPost => Opcode::IncPost, - UpdateOp::DecrementPost => Opcode::DecPost, + UpdateOp::IncrementPost | UpdateOp::IncrementPre => Opcode::Inc, + UpdateOp::DecrementPre | UpdateOp::DecrementPost => Opcode::Dec, }; let post = matches!( update.op(), @@ -35,12 +33,26 @@ impl ByteCompiler<'_> { self.emit_with_varying_operand(Opcode::GetNameAndLocator, index); } - self.emit_opcode(opcode); - if post { - self.emit_opcode(Opcode::Swap); - } else { - self.emit_opcode(Opcode::Dup); - } + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); + self.push_from_register(&src); if binding.is_lexical() { match self.lexical_environment.set_mutable_binding(name.clone()) { @@ -59,60 +71,134 @@ impl ByteCompiler<'_> { } else { self.emit_opcode(Opcode::SetNameByLocator); } + if post { + self.push_from_register(&dst); + } else { + self.push_from_register(&src); + } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } Access::Property { access } => match access { - PropertyAccess::Simple(access) => match access.field() { - PropertyAccessField::Const(name) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); + PropertyAccess::Simple(access) => { + self.compile_expr(access.target(), true); - self.emit_get_property_by_name(*name); - self.emit_opcode(opcode); - if post { - self.emit(Opcode::RotateRight, &[Operand::U8(4)]); - } + self.emit_opcode(Opcode::Dup); + self.emit_opcode(Opcode::Dup); + self.emit_opcode(Opcode::Dup); - self.emit_set_property_by_name(*name); - if post { - self.emit_opcode(Opcode::Pop); - } - } - PropertyAccessField::Expr(expr) => { - self.compile_expr(access.target(), true); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.emit_opcode(Opcode::Dup); - self.compile_expr(expr, true); + // Stack: value, value, value, value + match access.field() { + PropertyAccessField::Const(name) => { + self.emit_get_property_by_name(*name); - self.emit_opcode(Opcode::GetPropertyByValuePush); - self.emit_opcode(opcode); - if post { - self.emit(Opcode::RotateRight, &[Operand::U8(5)]); + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); + + self.push_from_register(&src); + + self.emit_set_property_by_name(*name); + + if post { + self.emit_opcode(Opcode::Pop); + self.push_from_register(&dst); + } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } + PropertyAccessField::Expr(expr) => { + self.compile_expr(expr, true); - self.emit_opcode(Opcode::SetPropertyByValue); - if post { - self.emit_opcode(Opcode::Pop); + self.emit_opcode(Opcode::GetPropertyByValuePush); + + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); + + self.push_from_register(&src); + + self.emit_opcode(Opcode::SetPropertyByValue); + + if post { + self.emit_opcode(Opcode::Pop); + self.push_from_register(&dst); + } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } } - }, + } PropertyAccess::Private(access) => { let index = self.get_or_insert_private_name(access.field()); self.compile_expr(access.target(), true); + self.emit_opcode(Opcode::Dup); self.emit_with_varying_operand(Opcode::GetPrivateField, index); - self.emit_opcode(opcode); - if post { - self.emit(Opcode::RotateRight, &[Operand::U8(3)]); - } + + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); + + self.push_from_register(&src); self.emit_with_varying_operand(Opcode::SetPrivateField, index); if post { self.emit_opcode(Opcode::Pop); + self.push_from_register(&dst); } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } PropertyAccess::Super(access) => match access.field() { PropertyAccessField::Const(name) => { @@ -123,15 +209,36 @@ impl ByteCompiler<'_> { self.emit_opcode(Opcode::This); self.emit_get_property_by_name(*name); - self.emit_opcode(opcode); - if post { - self.emit(Opcode::RotateRight, &[Operand::U8(3)]); - } + + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); + + self.push_from_register(&src); self.emit_set_property_by_name(*name); if post { self.emit_opcode(Opcode::Pop); + self.push_from_register(&dst); } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } PropertyAccessField::Expr(expr) => { self.emit_opcode(Opcode::Super); @@ -140,18 +247,37 @@ impl ByteCompiler<'_> { self.compile_expr(expr, true); self.emit_opcode(Opcode::GetPropertyByValuePush); - self.emit_opcode(opcode); - if post { - self.emit(Opcode::RotateRight, &[Operand::U8(2)]); - } + + let src = self.register_allocator.alloc(); + let dst = self.register_allocator.alloc(); + self.pop_into_register(&src); + + self.emit2( + Opcode::ToNumeric, + &[ + Operand2::Register(&dst), + Operand2::Operand(InstructionOperand::Register(&src)), + ], + ); + self.emit2( + opcode, + &[ + Operand2::Register(&src), + Operand2::Operand(InstructionOperand::Register(&dst)), + ], + ); self.emit_opcode(Opcode::This); - self.emit(Opcode::RotateRight, &[Operand::U8(2)]); + self.push_from_register(&src); self.emit_opcode(Opcode::SetPropertyByValue); if post { self.emit_opcode(Opcode::Pop); + self.push_from_register(&dst); } + + self.register_allocator.dealloc(src); + self.register_allocator.dealloc(dst); } }, }, diff --git a/core/engine/src/bytecompiler/function.rs b/core/engine/src/bytecompiler/function.rs index fceb90ce131..b06fcf70066 100644 --- a/core/engine/src/bytecompiler/function.rs +++ b/core/engine/src/bytecompiler/function.rs @@ -112,16 +112,12 @@ impl FunctionCompiler { false, variable_environment, lexical_environment, + self.r#async, + self.generator, interner, self.in_with, ); compiler.length = length; - compiler - .code_block_flags - .set(CodeBlockFlags::IS_ASYNC, self.r#async); - compiler - .code_block_flags - .set(CodeBlockFlags::IS_GENERATOR, self.generator); compiler.code_block_flags.set( CodeBlockFlags::HAS_PROTOTYPE_PROPERTY, !self.arrow && !self.method && !self.r#async && !self.generator, diff --git a/core/engine/src/bytecompiler/jump_control.rs b/core/engine/src/bytecompiler/jump_control.rs index 50a9bb09eba..3736534b6e9 100644 --- a/core/engine/src/bytecompiler/jump_control.rs +++ b/core/engine/src/bytecompiler/jump_control.rs @@ -122,7 +122,7 @@ impl JumpRecord { return_value_on_stack, } => { if return_value_on_stack { - compiler.emit_opcode(Opcode::SetReturnValue); + compiler.emit_opcode(Opcode::SetAccumulatorFromStack); } match (compiler.is_async(), compiler.is_generator()) { diff --git a/core/engine/src/bytecompiler/mod.rs b/core/engine/src/bytecompiler/mod.rs index 94082e90e0b..d4e328d2276 100644 --- a/core/engine/src/bytecompiler/mod.rs +++ b/core/engine/src/bytecompiler/mod.rs @@ -8,6 +8,7 @@ mod expression; mod function; mod jump_control; mod module; +mod register; mod statement; mod utils; @@ -18,8 +19,8 @@ use crate::{ environments::{BindingLocator, BindingLocatorError, CompileTimeEnvironment}, js_string, vm::{ - BindingOpcode, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, Handler, - InlineCache, Opcode, VaryingOperandKind, + BindingOpcode, CallFrame, CodeBlock, CodeBlockFlags, Constant, GeneratorResumeKind, + Handler, InlineCache, Opcode, VaryingOperandKind, }, JsBigInt, JsStr, JsString, }; @@ -48,6 +49,7 @@ pub(crate) use declarations::{ }; pub(crate) use function::FunctionCompiler; pub(crate) use jump_control::JumpControlInfo; +pub(crate) use register::*; pub(crate) trait ToJsString { fn to_js_string(&self, interner: &Interner) -> JsString; @@ -242,7 +244,7 @@ impl Access<'_> { /// An opcode operand. #[derive(Debug, Clone, Copy)] -#[allow(unused)] +#[allow(dead_code)] pub(crate) enum Operand { Bool(bool), I8(i8), @@ -256,6 +258,39 @@ pub(crate) enum Operand { Varying(u32), } +/// An opcode operand. +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub(crate) enum Operand2<'a> { + Bool(bool), + I8(i8), + U8(u8), + I16(i16), + U16(u16), + I32(i32), + U32(u32), + I64(i64), + U64(u64), + + Varying(u32), + Register(&'a Reg), + Operand(InstructionOperand<'a>), +} + +/// An opcode operand. +/// +// | 00 Register +// | 01 Argements +// | 10 Immediate +// | 11 ??? +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub(crate) enum InstructionOperand<'a> { + Register(&'a Reg), + Argument(u32), + Constant(u32), +} + /// The [`ByteCompiler`] is used to compile ECMAScript AST from [`boa_ast`] to bytecode. #[derive(Debug)] #[allow(clippy::struct_excessive_bools)] @@ -266,7 +301,7 @@ pub struct ByteCompiler<'ctx> { /// The number of arguments expected. pub(crate) length: u32, - pub(crate) register_count: u32, + pub(crate) register_allocator: RegisterAllocator, /// `[[ThisMode]]` pub(crate) this_mode: ThisMode, @@ -290,7 +325,7 @@ pub struct ByteCompiler<'ctx> { pub(crate) current_open_environments_count: u32, current_stack_value_count: u32, - pub(crate) code_block_flags: CodeBlockFlags, + code_block_flags: CodeBlockFlags, handlers: ThinVec, pub(crate) ic: Vec, literals_map: FxHashMap, @@ -323,18 +358,53 @@ impl<'ctx> ByteCompiler<'ctx> { /// Creates a new [`ByteCompiler`]. #[inline] + #[allow(clippy::too_many_arguments)] + #[allow(clippy::fn_params_excessive_bools)] pub(crate) fn new( name: JsString, strict: bool, json_parse: bool, variable_environment: Rc, lexical_environment: Rc, + is_async: bool, + is_generator: bool, interner: &'ctx mut Interner, in_with: bool, ) -> ByteCompiler<'ctx> { let mut code_block_flags = CodeBlockFlags::empty(); code_block_flags.set(CodeBlockFlags::STRICT, strict); + code_block_flags.set(CodeBlockFlags::IS_ASYNC, is_async); + code_block_flags.set(CodeBlockFlags::IS_GENERATOR, is_generator); code_block_flags |= CodeBlockFlags::HAS_PROTOTYPE_PROPERTY; + + let mut register_allocator = RegisterAllocator::default(); + if is_async { + let promise_register = register_allocator.alloc_persistent(); + let resolve_register = register_allocator.alloc_persistent(); + let reject_register = register_allocator.alloc_persistent(); + + debug_assert_eq!( + promise_register.index(), + CallFrame::PROMISE_CAPABILITY_PROMISE_REGISTER_INDEX + ); + debug_assert_eq!( + resolve_register.index(), + CallFrame::PROMISE_CAPABILITY_RESOLVE_REGISTER_INDEX + ); + debug_assert_eq!( + reject_register.index(), + CallFrame::PROMISE_CAPABILITY_REJECT_REGISTER_INDEX + ); + + if is_generator { + let async_function_object_register = register_allocator.alloc_persistent(); + debug_assert_eq!( + async_function_object_register.index(), + CallFrame::ASYNC_GENERATOR_OBJECT_REGISTER_INDEX + ); + } + } + Self { function_name: name, length: 0, @@ -345,7 +415,7 @@ impl<'ctx> ByteCompiler<'ctx> { params: FormalParameterList::default(), current_open_environments_count: 0, - register_count: 0, + register_allocator, current_stack_value_count: 0, code_block_flags, handlers: ThinVec::default(), @@ -522,6 +592,121 @@ impl<'ctx> ByteCompiler<'ctx> { } } + pub(crate) fn emit2(&mut self, opcode: Opcode, operands: &[Operand2<'_>]) { + let mut varying_kind = VaryingOperandKind::U8; + let mut operand_types = 0; + let mut has_operand_types = false; + for operand in operands { + let operand = match *operand { + Operand2::Register(operand) => { + if u8::try_from(operand.index()).is_ok() { + } else if u16::try_from(operand.index()).is_ok() { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); + } else { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); + } + None + } + Operand2::Varying(operand) => { + if u8::try_from(operand).is_ok() { + } else if u16::try_from(operand).is_ok() { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); + } else { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); + } + None + } + Operand2::Operand(operand) => Some(operand), + _ => None, + }; + + let Some(operand) = operand else { + continue; + }; + + has_operand_types = true; + + let (value, type_) = match operand { + InstructionOperand::Register(reg) => (reg.index(), 0b0000_0000), + InstructionOperand::Argument(index) => (index, 0b0000_0001), + InstructionOperand::Constant(value) => (value, 0b0000_0010), + }; + + if u8::try_from(value).is_ok() { + } else if u16::try_from(value).is_ok() { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U16); + } else { + varying_kind = std::cmp::max(varying_kind, VaryingOperandKind::U32); + } + + operand_types <<= 2; + operand_types |= type_; + } + + match varying_kind { + VaryingOperandKind::U8 => {} + VaryingOperandKind::U16 => self.emit_opcode(Opcode::U16Operands), + VaryingOperandKind::U32 => self.emit_opcode(Opcode::U32Operands), + } + self.emit_opcode(opcode); + if has_operand_types { + self.emit_u8(operand_types); + } + for operand in operands { + self.emit_operand2(*operand, varying_kind); + } + } + + pub(crate) fn emit_operand2( + &mut self, + operand: Operand2<'_>, + varying_kind: VaryingOperandKind, + ) { + match operand { + Operand2::Bool(v) => self.emit_u8(v.into()), + Operand2::I8(v) => self.emit_i8(v), + Operand2::U8(v) => self.emit_u8(v), + Operand2::I16(v) => self.emit_i16(v), + Operand2::U16(v) => self.emit_u16(v), + Operand2::I32(v) => self.emit_i32(v), + Operand2::U32(v) => self.emit_u32(v), + Operand2::I64(v) => self.emit_i64(v), + Operand2::U64(v) => self.emit_u64(v), + Operand2::Varying(v) + | Operand2::Operand( + InstructionOperand::Argument(v) | InstructionOperand::Constant(v), + ) => match varying_kind { + VaryingOperandKind::U8 => self.emit_u8(v as u8), + VaryingOperandKind::U16 => self.emit_u16(v as u16), + VaryingOperandKind::U32 => self.emit_u32(v), + }, + Operand2::Register(reg) | Operand2::Operand(InstructionOperand::Register(reg)) => { + let v = reg.index(); + match varying_kind { + VaryingOperandKind::U8 => self.emit_u8(v as u8), + VaryingOperandKind::U16 => self.emit_u16(v as u16), + VaryingOperandKind::U32 => self.emit_u32(v), + } + } + } + } + + pub(crate) fn emit_get_function(&mut self, dst: &Reg, index: u32) { + self.emit2( + Opcode::GetFunction, + &[Operand2::Register(dst), Operand2::Varying(index)], + ); + } + + /// TODO: Temporary function, remove once transition is complete. + fn pop_into_register(&mut self, dst: &Reg) { + self.emit2(Opcode::PopIntoRegister, &[Operand2::Register(dst)]); + } + /// TODO: Temporary function, remove once transition is complete. + fn push_from_register(&mut self, src: &Reg) { + self.emit2(Opcode::PushFromRegister, &[Operand2::Register(src)]); + } + /// Emits an opcode with one varying operand. /// /// Simpler version of [`ByteCompiler::emit()`]. @@ -563,6 +748,23 @@ impl<'ctx> ByteCompiler<'ctx> { self.emit_u64(value as u64); } fn emit_get_property_by_name(&mut self, ident: Sym) { + let dst = self.register_allocator.alloc(); + let receiver = self.register_allocator.alloc(); + let value = self.register_allocator.alloc(); + + self.pop_into_register(&receiver); + self.pop_into_register(&value); + + self.emit_get_property_by_name2(&dst, &receiver, &value, ident); + + self.push_from_register(&dst); + + self.register_allocator.dealloc(dst); + self.register_allocator.dealloc(receiver); + self.register_allocator.dealloc(value); + } + + fn emit_get_property_by_name2(&mut self, dst: &Reg, receiver: &Reg, value: &Reg, ident: Sym) { let ic_index = self.ic.len() as u32; let name_index = self.get_or_insert_name(Identifier::new(ident)); @@ -571,7 +773,15 @@ impl<'ctx> ByteCompiler<'ctx> { }; self.ic.push(InlineCache::new(name.clone())); - self.emit_with_varying_operand(Opcode::GetPropertyByName, ic_index); + self.emit2( + Opcode::GetPropertyByName, + &[ + Operand2::Register(dst), + Operand2::Operand(InstructionOperand::Register(receiver)), + Operand2::Operand(InstructionOperand::Register(value)), + Operand2::Varying(ic_index), + ], + ); } fn emit_set_property_by_name(&mut self, ident: Sym) { @@ -673,6 +883,14 @@ impl<'ctx> ByteCompiler<'ctx> { } } + #[allow(dead_code)] + fn emit_move(&mut self, dst: &Reg, src: InstructionOperand<'_>) { + self.emit2( + Opcode::Move, + &[Operand2::Register(dst), Operand2::Operand(src)], + ); + } + fn jump(&mut self) -> Label { self.emit_opcode_with_operand(Opcode::Jump) } @@ -728,6 +946,27 @@ impl<'ctx> ByteCompiler<'ctx> { Label { index } } + pub(crate) fn emit_opcode_with_operand2( + &mut self, + opcode: Opcode, + src: InstructionOperand<'_>, + ) -> Label { + let index = self.next_opcode_location(); + self.emit2( + opcode, + &[Operand2::U32(Self::DUMMY_ADDRESS), Operand2::Operand(src)], + ); + // NOTE: Plus one because the `operand_types` is emited + Label { index: index + 1 } + } + + pub(crate) fn emit_push_private_environment(&mut self, class: InstructionOperand<'_>) -> Label { + self.emit2(Opcode::PushPrivateEnvironment, &[Operand2::Operand(class)]); + let index = self.next_opcode_location(); + self.emit_u32(Self::DUMMY_ADDRESS); + Label { index: index - 1 } + } + /// Emit an opcode with two dummy operands. /// Return the `Label`s of the two operands. pub(crate) fn emit_opcode_with_two_operands(&mut self, opcode: Opcode) -> (Label, Label) { @@ -1353,7 +1592,10 @@ impl<'ctx> ByteCompiler<'ctx> { let name = function.name; let index = self.function(function); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); match node_kind { NodeKind::Declaration => { @@ -1416,7 +1658,10 @@ impl<'ctx> ByteCompiler<'ctx> { ); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); } /// Compile a class method AST Node into bytecode. @@ -1464,7 +1709,10 @@ impl<'ctx> ByteCompiler<'ctx> { ); let index = self.push_function_to_constants(code); - self.emit_with_varying_operand(Opcode::GetFunction, index); + let dst = self.register_allocator.alloc(); + self.emit_get_function(&dst, index); + self.push_from_register(&dst); + self.register_allocator.dealloc(dst); } fn call(&mut self, callable: Callable<'_>, use_expr: bool) { @@ -1564,19 +1812,9 @@ impl<'ctx> ByteCompiler<'ctx> { } self.r#return(false); - if self.is_async() { - // NOTE: +3 for the promise capability - self.register_count += 3; - if self.is_generator() { - // NOTE: +1 for the async generator function - self.register_count += 1; - } - } - - // NOTE: Offset the handlers stack count so we don't pop the registers - // when a exception is thrown. + let register_count = self.register_allocator.finish(); for handler in &mut self.handlers { - handler.stack_count += self.register_count; + handler.stack_count += register_count; } let mapped_arguments_binding_indices = if self.emitted_mapped_arguments_object_opcode { @@ -1588,7 +1826,7 @@ impl<'ctx> ByteCompiler<'ctx> { CodeBlock { name: self.function_name, length: self.length, - register_count: self.register_count, + register_count, this_mode: self.this_mode, parameter_length: self.params.as_ref().len() as u32, mapped_arguments_binding_indices, diff --git a/core/engine/src/bytecompiler/register.rs b/core/engine/src/bytecompiler/register.rs new file mode 100644 index 00000000000..9a0684a9129 --- /dev/null +++ b/core/engine/src/bytecompiler/register.rs @@ -0,0 +1,115 @@ +use std::mem::forget; + +bitflags::bitflags! { + #[derive(Debug, Default, Clone, Copy)] + struct RegisterFlags: u8 { + const USED = 0b0000_0001; + const PERSISTENT = 0b0000_0010; + } +} + +impl RegisterFlags { + fn is_used(self) -> bool { + self.contains(Self::USED) + } + fn is_persistent(self) -> bool { + self.contains(Self::PERSISTENT) + } +} + +#[derive(Debug, Default)] +pub(crate) struct Register { + flags: RegisterFlags, +} + +#[derive(Debug)] +pub(crate) struct Reg { + index: u32, + flags: RegisterFlags, +} + +impl Reg { + pub(crate) fn index(&self) -> u32 { + self.index + } +} + +impl Drop for Reg { + fn drop(&mut self) { + if !self.flags.is_persistent() { + unreachable!("forgot to deallocate a register!") + } + } +} + +#[derive(Debug, Default)] +pub(crate) struct RegisterAllocator { + registers: Vec, +} + +impl RegisterAllocator { + pub(crate) fn alloc(&mut self) -> Reg { + for (i, register) in self.registers.iter_mut().enumerate() { + if register.flags.is_used() { + continue; + } + + assert!(!register.flags.is_persistent()); + + register.flags |= RegisterFlags::USED; + return Reg { + index: i as u32, + flags: register.flags, + }; + } + + let flags = RegisterFlags::USED; + + let index = self.registers.len() as u32; + self.registers.push(Register { flags }); + + Reg { index, flags } + } + + pub(crate) fn alloc_persistent(&mut self) -> Reg { + let mut reg = self.alloc(); + + let index = reg.index(); + + let register = &mut self.registers[index as usize]; + + register.flags |= RegisterFlags::PERSISTENT; + + reg.flags = register.flags; + reg + } + + pub(crate) fn dealloc(&mut self, reg: Reg) { + assert!( + !reg.flags.is_persistent(), + "Trying to deallocate a persistent register" + ); + + let register = &mut self.registers[reg.index as usize]; + + assert!( + register.flags.is_used(), + "Cannot deallocate unused variable" + ); + register.flags.set(RegisterFlags::USED, false); + + // NOTE: We should not drop it since, dropping it used to detect bugs. + forget(reg); + } + + pub(crate) fn finish(self) -> u32 { + for register in &self.registers { + debug_assert!( + !register.flags.is_used() + || (register.flags.is_used() && register.flags.is_persistent()) + ); + } + + self.registers.len() as u32 + } +} diff --git a/core/engine/src/bytecompiler/statement/mod.rs b/core/engine/src/bytecompiler/statement/mod.rs index 1abce62e11d..17ca6631dfd 100644 --- a/core/engine/src/bytecompiler/statement/mod.rs +++ b/core/engine/src/bytecompiler/statement/mod.rs @@ -44,14 +44,14 @@ impl ByteCompiler<'_> { Statement::Continue(node) => { if root_statement && (use_expr || self.jump_control_info_has_use_expr()) { self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetReturnValue); + self.emit_opcode(Opcode::SetAccumulatorFromStack); } self.compile_continue(*node, use_expr); } Statement::Break(node) => { if root_statement && (use_expr || self.jump_control_info_has_use_expr()) { self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetReturnValue); + self.emit_opcode(Opcode::SetAccumulatorFromStack); } self.compile_break(*node, use_expr); } @@ -79,7 +79,7 @@ impl ByteCompiler<'_> { Statement::Expression(expr) => { self.compile_expr(expr, use_expr); if use_expr { - self.emit_opcode(Opcode::SetReturnValue); + self.emit_opcode(Opcode::SetAccumulatorFromStack); } } Statement::With(with) => self.compile_with(with, use_expr), diff --git a/core/engine/src/bytecompiler/statement/try.rs b/core/engine/src/bytecompiler/statement/try.rs index c2030207fb8..b415c8e6f6a 100644 --- a/core/engine/src/bytecompiler/statement/try.rs +++ b/core/engine/src/bytecompiler/statement/try.rs @@ -143,11 +143,11 @@ impl ByteCompiler<'_> { } pub(crate) fn compile_finally_stmt(&mut self, finally: &Finally, has_catch: bool) { - // TODO: We could probably remove the Get/SetReturnValue if we check that there is no break/continues statements. + // TODO: We could probably remove the Get/SetAccumulatorFromStack if we check that there is no break/continues statements. self.current_stack_value_count += 1; - self.emit_opcode(Opcode::GetReturnValue); + self.emit_opcode(Opcode::GetAccumulator); self.compile_catch_finally_block(finally.block(), true); - self.emit_opcode(Opcode::SetReturnValue); + self.emit_opcode(Opcode::SetAccumulatorFromStack); self.current_stack_value_count -= 1; // Rethrow error if error happend! @@ -184,7 +184,7 @@ impl ByteCompiler<'_> { Statement::Break(_) | Statement::Continue(_), )) => { self.emit_opcode(Opcode::PushUndefined); - self.emit_opcode(Opcode::SetReturnValue); + self.emit_opcode(Opcode::SetAccumulatorFromStack); break; } Some(StatementListItem::Statement(Statement::Block(block))) => { diff --git a/core/engine/src/module/source.rs b/core/engine/src/module/source.rs index ceadec0c037..7d4b01074ec 100644 --- a/core/engine/src/module/source.rs +++ b/core/engine/src/module/source.rs @@ -28,7 +28,7 @@ use crate::{ realm::Realm, vm::{ create_function_object_fast, ActiveRunnable, CallFrame, CallFrameFlags, CodeBlock, - CodeBlockFlags, CompletionRecord, Opcode, + CompletionRecord, Opcode, }, Context, JsArgs, JsError, JsNativeError, JsObject, JsResult, JsString, JsValue, NativeFunction, }; @@ -1432,11 +1432,12 @@ impl SourceTextModule { false, env.clone(), env.clone(), + true, + false, context.interner_mut(), false, ); - compiler.code_block_flags |= CodeBlockFlags::IS_ASYNC; compiler.async_handler = Some(compiler.push_handler()); let mut imports = Vec::new(); diff --git a/core/engine/src/module/synthetic.rs b/core/engine/src/module/synthetic.rs index 330b57995c5..89451f687ca 100644 --- a/core/engine/src/module/synthetic.rs +++ b/core/engine/src/module/synthetic.rs @@ -286,6 +286,8 @@ impl SyntheticModule { false, module_compile_env.clone(), module_compile_env.clone(), + false, + false, context.interner_mut(), false, ); diff --git a/core/engine/src/script.rs b/core/engine/src/script.rs index 31152ee8894..17c76a45505 100644 --- a/core/engine/src/script.rs +++ b/core/engine/src/script.rs @@ -134,6 +134,8 @@ impl Script { false, self.inner.realm.environment().compile_env(), self.inner.realm.environment().compile_env(), + false, + false, context.interner_mut(), false, ); diff --git a/core/engine/src/vm/call_frame/mod.rs b/core/engine/src/vm/call_frame/mod.rs index 92ee7f13c07..7a9600c469c 100644 --- a/core/engine/src/vm/call_frame/mod.rs +++ b/core/engine/src/vm/call_frame/mod.rs @@ -43,6 +43,7 @@ pub struct CallFrame { pub(crate) code_block: Gc, pub(crate) pc: u32, /// The register pointer, points to the first register in the stack. + /// // TODO: Check if storing the frame pointer instead of argument count and computing the // argument count based on the pointers would be better for accessing the arguments // and the elements before the register pointer. @@ -119,10 +120,10 @@ impl CallFrame { /// caller prologue caller arguments callee prologue callee arguments /// ┌─────────────────┐ ┌─────────┐ ┌─────────────────┐ ┌──────┐ /// ▼ ▼ ▼ ▼ │ ▼ ▼ ▼ - /// | 0: undefined | 1: y | 2: 1 | 3: 2 | 4: undefined | 5: x | 6: 3 | - /// ▲ ▲ ▲ - /// │ caller register pointer ────┤ │ - /// │ │ callee register pointer + /// | 0: undefined | 1: y | 2: 1 | 3: 2 | 4: undefined | 5: x | 6: 3 | + /// ▲ ▲ ▲ + /// │ caller register pointer ────┤ │ + /// │ │ callee register pointer /// │ callee frame pointer /// │ /// └───── caller frame pointer @@ -333,6 +334,26 @@ impl CallFrame { pub(crate) fn has_this_value_cached(&self) -> bool { self.flags.contains(CallFrameFlags::THIS_VALUE_CACHED) } + + pub(crate) fn read_value( + &self, + operand_types: u8, + operand: u32, + vm: &Vm, + ) -> JsValue { + assert!(N <= 4, "operand type index ({N}) must be less than 4"); + + let type_ = (operand_types >> (N * 2)) & 0x0000_0011; + match type_ { + 0 => vm.stack[(self.rp + operand) as usize].clone(), + 1 => self + .argument(operand as usize, vm) + .expect("should be argument") + .clone(), + 2 => operand.into(), + _ => unreachable!(), + } + } } /// ---- `CallFrame` stack methods ---- diff --git a/core/engine/src/vm/code_block.rs b/core/engine/src/vm/code_block.rs index b3fbb40a78b..1d97824ff85 100644 --- a/core/engine/src/vm/code_block.rs +++ b/core/engine/src/vm/code_block.rs @@ -370,6 +370,48 @@ impl CodeBlock { /// Returns an empty `String` if no operands are present. pub(crate) fn instruction_operands(&self, instruction: &Instruction) -> String { match instruction { + Instruction::SetRegisterFromAccumulator { register } + | Instruction::SetAccumulator { register } => format!("R{}", register.value()), + Instruction::Add { .. } + | Instruction::Sub { .. } + | Instruction::Div { .. } + | Instruction::Mul { .. } + | Instruction::Mod { .. } + | Instruction::Pow { .. } + | Instruction::ShiftRight { .. } + | Instruction::ShiftLeft { .. } + | Instruction::UnsignedShiftRight { .. } + | Instruction::BitOr { .. } + | Instruction::BitAnd { .. } + | Instruction::BitXor { .. } + | Instruction::BitNot { .. } + | Instruction::In { .. } + | Instruction::Eq { .. } + | Instruction::NotEq { .. } + | Instruction::GreaterThan { .. } + | Instruction::GreaterThanOrEq { .. } + | Instruction::LessThan { .. } + | Instruction::LessThanOrEq { .. } + | Instruction::InstanceOf { .. } + | Instruction::StrictNotEq { .. } + | Instruction::StrictEq { .. } + | Instruction::InPrivate { .. } + | Instruction::Inc { .. } + | Instruction::Dec { .. } + | Instruction::ToNumeric { .. } => "TODO: fix".to_string(), + Instruction::PopIntoRegister { dst } => format!("R{}", dst.value()), + Instruction::PushFromRegister { src } => format!("R{}", src.value()), + Instruction::Move { + operand_types, + dst: r1, + src: r2, + } => { + format!( + "dst:reg{}, src:{}", + r1.value(), + r2.to_string::<0>(*operand_types) + ) + } Instruction::SetFunctionName { prefix } => match prefix { 0 => "prefix: none", 1 => "prefix: get", @@ -410,10 +452,24 @@ impl CodeBlock { | Instruction::JumpIfNotUndefined { address: value } | Instruction::JumpIfNullOrUndefined { address: value } | Instruction::Case { address: value } - | Instruction::Default { address: value } - | Instruction::LogicalAnd { exit: value } - | Instruction::LogicalOr { exit: value } - | Instruction::Coalesce { exit: value } => value.to_string(), + | Instruction::Default { address: value } => value.to_string(), + Instruction::LogicalAnd { + exit, + lhs, + operand_types, + } + | Instruction::LogicalOr { + exit, + lhs, + operand_types, + } + | Instruction::Coalesce { + exit, + lhs, + operand_types, + } => { + format!("lhs:{} exit:{exit}", lhs.to_string::<0>(*operand_types)) + } Instruction::CallEval { argument_count: value, } @@ -449,10 +505,11 @@ impl CodeBlock { Instruction::TemplateCreate { count, site } => { format!("{}, {site}", count.value()) } - Instruction::GetFunction { index } => { + Instruction::GetFunction { dst, index } => { let index = index.value() as usize; format!( - "{index:04}: '{}' (length: {})", + "R{} = {index:04}: '{}' (length: {})", + dst.value(), self.constant_function(index).name().to_std_string_escaped(), self.constant_function(index).length ) @@ -483,7 +540,6 @@ impl CodeBlock { | Instruction::SetPropertySetterByName { index } | Instruction::DefineClassStaticSetterByName { index } | Instruction::DefineClassSetterByName { index } - | Instruction::InPrivate { index } | Instruction::ThrowMutateImmutable { index } | Instruction::DeletePropertyByName { index } | Instruction::SetPrivateField { index } @@ -503,7 +559,28 @@ impl CodeBlock { .to_std_string_escaped(), ) } - Instruction::GetPropertyByName { index } | Instruction::SetPropertyByName { index } => { + Instruction::GetPropertyByName { + operand_types, + dst, + receiver, + value, + index, + } => { + let ic = &self.ic[index.value() as usize]; + let slot = ic.slot(); + format!( + "dst:reg{}, receiver:{}, value:{}, {:04}: '{}', Shape: 0x{:x}, Slot: index: {}, attributes {:?}", + dst.value(), + receiver.to_string::<0>(*operand_types), + value.to_string::<1>(*operand_types), + index.value(), + ic.name.to_std_string_escaped(), + ic.shape.borrow().to_addr_usize(), + slot.index, + slot.attributes, + ) + } + Instruction::SetPropertyByName { index } => { let ic = &self.ic[index.value() as usize]; let slot = ic.slot(); format!( @@ -515,8 +592,15 @@ impl CodeBlock { slot.attributes, ) } - Instruction::PushPrivateEnvironment { name_indices } => { - format!("{name_indices:?}") + Instruction::PushPrivateEnvironment { + class, + name_indices, + operand_types, + } => { + format!( + "class:{}, names:{name_indices:?}", + class.to_string::<0>(*operand_types) + ) } Instruction::JumpTable { default, addresses } => { let mut operands = format!("#{}: Default: {default:4}", addresses.len()); @@ -544,6 +628,32 @@ impl CodeBlock { .to_std_string_escaped(); format!("name: {name}, configurable: {configurable}") } + Instruction::PushClassPrototype { + operand_types, + dst, + class, + superclass, + } => { + format!( + "dst:reg{}, class:{}, superclass:{}", + dst.value(), + class.to_string::<0>(*operand_types), + superclass.to_string::<1>(*operand_types) + ) + } + Instruction::SetClassPrototype { + operand_types, + dst, + prototype, + class, + } => { + format!( + "dst:reg{}, prototype:{}, class:{}", + dst.value(), + prototype.to_string::<0>(*operand_types), + class.to_string::<1>(*operand_types) + ) + } Instruction::Pop | Instruction::Dup | Instruction::Swap @@ -557,41 +667,12 @@ impl CodeBlock { | Instruction::PushFalse | Instruction::PushUndefined | Instruction::PushEmptyObject - | Instruction::PushClassPrototype - | Instruction::SetClassPrototype | Instruction::SetHomeObject - | Instruction::Add - | Instruction::Sub - | Instruction::Div - | Instruction::Mul - | Instruction::Mod - | Instruction::Pow - | Instruction::ShiftRight - | Instruction::ShiftLeft - | Instruction::UnsignedShiftRight - | Instruction::BitOr - | Instruction::BitAnd - | Instruction::BitXor - | Instruction::BitNot - | Instruction::In - | Instruction::Eq - | Instruction::StrictEq - | Instruction::NotEq - | Instruction::StrictNotEq - | Instruction::GreaterThan - | Instruction::GreaterThanOrEq - | Instruction::LessThan - | Instruction::LessThanOrEq - | Instruction::InstanceOf | Instruction::TypeOf | Instruction::Void | Instruction::LogicalNot | Instruction::Pos | Instruction::Neg - | Instruction::Inc - | Instruction::IncPost - | Instruction::Dec - | Instruction::DecPost | Instruction::GetPropertyByValue | Instruction::GetPropertyByValuePush | Instruction::SetPropertyByValue @@ -661,8 +742,8 @@ impl CodeBlock { | Instruction::SetNameByLocator | Instruction::PopPrivateEnvironment | Instruction::ImportCall - | Instruction::GetReturnValue - | Instruction::SetReturnValue + | Instruction::GetAccumulator + | Instruction::SetAccumulatorFromStack | Instruction::BindThisValue | Instruction::CreateMappedArgumentsObject | Instruction::CreateUnmappedArgumentsObject @@ -718,11 +799,7 @@ impl CodeBlock { | Instruction::Reserved46 | Instruction::Reserved47 | Instruction::Reserved48 - | Instruction::Reserved49 - | Instruction::Reserved50 - | Instruction::Reserved51 - | Instruction::Reserved52 - | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved49 => unreachable!("Reserved opcodes are unrechable"), } } } diff --git a/core/engine/src/vm/flowgraph/mod.rs b/core/engine/src/vm/flowgraph/mod.rs index 5ed0326f591..192f6d0d2a6 100644 --- a/core/engine/src/vm/flowgraph/mod.rs +++ b/core/engine/src/vm/flowgraph/mod.rs @@ -38,7 +38,38 @@ impl CodeBlock { let pc = iterator.pc(); match instruction { - Instruction::SetFunctionName { .. } => { + Instruction::StrictEq { .. } + | Instruction::StrictNotEq { .. } + | Instruction::SetRegisterFromAccumulator { .. } + | Instruction::Move { .. } + | Instruction::PopIntoRegister { .. } + | Instruction::PushFromRegister { .. } + | Instruction::Add { .. } + | Instruction::Sub { .. } + | Instruction::Div { .. } + | Instruction::Mul { .. } + | Instruction::Mod { .. } + | Instruction::Pow { .. } + | Instruction::ShiftRight { .. } + | Instruction::ShiftLeft { .. } + | Instruction::UnsignedShiftRight { .. } + | Instruction::BitOr { .. } + | Instruction::BitAnd { .. } + | Instruction::BitXor { .. } + | Instruction::BitNot { .. } + | Instruction::In { .. } + | Instruction::Eq { .. } + | Instruction::NotEq { .. } + | Instruction::GreaterThan { .. } + | Instruction::GreaterThanOrEq { .. } + | Instruction::LessThan { .. } + | Instruction::LessThanOrEq { .. } + | Instruction::InstanceOf { .. } + | Instruction::SetAccumulator { .. } + | Instruction::SetFunctionName { .. } + | Instruction::ToNumeric { .. } + | Instruction::Inc { .. } + | Instruction::Dec { .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } @@ -114,9 +145,9 @@ impl CodeBlock { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::Red); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); } - Instruction::LogicalAnd { exit } - | Instruction::LogicalOr { exit } - | Instruction::Coalesce { exit } => { + Instruction::LogicalAnd { exit, .. } + | Instruction::LogicalOr { exit, .. } + | Instruction::Coalesce { exit, .. } => { graph.add_node(previous_pc, NodeShape::None, label.into(), Color::None); graph.add_edge(previous_pc, pc, None, Color::None, EdgeStyle::Line); graph.add_edge( @@ -348,41 +379,14 @@ impl CodeBlock { | Instruction::PushFalse | Instruction::PushUndefined | Instruction::PushEmptyObject - | Instruction::PushClassPrototype - | Instruction::SetClassPrototype + | Instruction::PushClassPrototype { .. } + | Instruction::SetClassPrototype { .. } | Instruction::SetHomeObject - | Instruction::Add - | Instruction::Sub - | Instruction::Div - | Instruction::Mul - | Instruction::Mod - | Instruction::Pow - | Instruction::ShiftRight - | Instruction::ShiftLeft - | Instruction::UnsignedShiftRight - | Instruction::BitOr - | Instruction::BitAnd - | Instruction::BitXor - | Instruction::BitNot - | Instruction::In - | Instruction::Eq - | Instruction::StrictEq - | Instruction::NotEq - | Instruction::StrictNotEq - | Instruction::GreaterThan - | Instruction::GreaterThanOrEq - | Instruction::LessThan - | Instruction::LessThanOrEq - | Instruction::InstanceOf | Instruction::TypeOf | Instruction::Void | Instruction::LogicalNot | Instruction::Pos | Instruction::Neg - | Instruction::Inc - | Instruction::IncPost - | Instruction::Dec - | Instruction::DecPost | Instruction::GetPropertyByValue | Instruction::GetPropertyByValuePush | Instruction::SetPropertyByValue @@ -445,8 +449,8 @@ impl CodeBlock { | Instruction::PushObjectEnvironment | Instruction::PopPrivateEnvironment | Instruction::ImportCall - | Instruction::GetReturnValue - | Instruction::SetReturnValue + | Instruction::GetAccumulator + | Instruction::SetAccumulatorFromStack | Instruction::Exception | Instruction::MaybeException | Instruction::CheckReturn @@ -512,11 +516,7 @@ impl CodeBlock { | Instruction::Reserved46 | Instruction::Reserved47 | Instruction::Reserved48 - | Instruction::Reserved49 - | Instruction::Reserved50 - | Instruction::Reserved51 - | Instruction::Reserved52 - | Instruction::Reserved53 => unreachable!("Reserved opcodes are unrechable"), + | Instruction::Reserved49 => unreachable!("Reserved opcodes are unrechable"), } } diff --git a/core/engine/src/vm/opcode/binary_ops/logical.rs b/core/engine/src/vm/opcode/binary_ops/logical.rs index 547a68a0413..b0892ec34ec 100644 --- a/core/engine/src/vm/opcode/binary_ops/logical.rs +++ b/core/engine/src/vm/opcode/binary_ops/logical.rs @@ -10,19 +10,49 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct LogicalAnd; +impl LogicalAnd { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + exit: u32, + lhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + + if !lhs.to_boolean() { + context.vm.frame_mut().pc = exit; + } + Ok(CompletionType::Normal) + } +} + impl Operation for LogicalAnd { const NAME: &'static str = "LogicalAnd"; const INSTRUCTION: &'static str = "INST - LogicalAnd"; const COST: u8 = 1; fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); let exit = context.vm.read::(); - let lhs = context.vm.pop(); - if !lhs.to_boolean() { - context.vm.frame_mut().pc = exit; - context.vm.push(lhs); - } - Ok(CompletionType::Normal) + let lhs = context.vm.read::().into(); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = context.vm.read::().into(); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = context.vm.read::(); + Self::operation(exit, lhs, operand_types, context) } } @@ -33,19 +63,49 @@ impl Operation for LogicalAnd { #[derive(Debug, Clone, Copy)] pub(crate) struct LogicalOr; +impl LogicalOr { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + exit: u32, + lhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + + if lhs.to_boolean() { + context.vm.frame_mut().pc = exit; + } + Ok(CompletionType::Normal) + } +} + impl Operation for LogicalOr { const NAME: &'static str = "LogicalOr"; const INSTRUCTION: &'static str = "INST - LogicalOr"; const COST: u8 = 1; fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); let exit = context.vm.read::(); - let lhs = context.vm.pop(); - if lhs.to_boolean() { - context.vm.frame_mut().pc = exit; - context.vm.push(lhs); - } - Ok(CompletionType::Normal) + let lhs = u32::from(context.vm.read::()); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = u32::from(context.vm.read::()); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = context.vm.read::(); + Self::operation(exit, lhs, operand_types, context) } } @@ -56,18 +116,48 @@ impl Operation for LogicalOr { #[derive(Debug, Clone, Copy)] pub(crate) struct Coalesce; +impl Coalesce { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + exit: u32, + lhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + + if !lhs.is_null_or_undefined() { + context.vm.frame_mut().pc = exit; + } + Ok(CompletionType::Normal) + } +} + impl Operation for Coalesce { const NAME: &'static str = "Coalesce"; const INSTRUCTION: &'static str = "INST - Coalesce"; const COST: u8 = 1; fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); let exit = context.vm.read::(); - let lhs = context.vm.pop(); - if !lhs.is_null_or_undefined() { - context.vm.frame_mut().pc = exit; - context.vm.push(lhs); - } - Ok(CompletionType::Normal) + let lhs = u32::from(context.vm.read::()); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = u32::from(context.vm.read::()); + Self::operation(exit, lhs, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let exit = context.vm.read::(); + let lhs = context.vm.read::(); + Self::operation(exit, lhs, operand_types, context) } } diff --git a/core/engine/src/vm/opcode/binary_ops/macro_defined.rs b/core/engine/src/vm/opcode/binary_ops/macro_defined.rs index c36dea2b8f9..75e416505dc 100644 --- a/core/engine/src/vm/opcode/binary_ops/macro_defined.rs +++ b/core/engine/src/vm/opcode/binary_ops/macro_defined.rs @@ -1,6 +1,6 @@ use crate::{ vm::{opcode::Operation, CompletionType}, - Context, JsResult, + Context, JsResult, JsValue, }; macro_rules! implement_bin_ops { @@ -12,17 +12,54 @@ macro_rules! implement_bin_ops { #[derive(Debug, Clone, Copy)] pub(crate) struct $name; + impl $name { + #[allow(clippy::needless_pass_by_value)] + fn operation( + output: u32, + lhs: u32, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + + let lhs = context.vm.frame().read_value::<0>(operand_types, lhs, &context.vm); + let rhs = context.vm.frame().read_value::<1>(operand_types, rhs, &context.vm); + + let value = lhs.$op(&rhs, context)?; + + context.vm.stack[(rp + output) as usize] = JsValue::from(value); + Ok(CompletionType::Normal) + } + } + impl Operation for $name { const NAME: &'static str = stringify!($name); const INSTRUCTION: &'static str = stringify!("INST - " + $name); const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { - let rhs = context.vm.pop(); - let lhs = context.vm.pop(); - let value = lhs.$op(&rhs, context)?; - context.vm.push(value); - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let output = context.vm.read::().into(); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::().into(); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::(); + let lhs = context.vm.read::(); + let rhs = context.vm.read::(); + Self::operation(output, lhs, rhs, operand_types, context) } } }; @@ -45,3 +82,4 @@ implement_bin_ops!(GreaterThan, gt, "Binary `>` operator."); implement_bin_ops!(GreaterThanOrEq, ge, "Binary `>=` operator."); implement_bin_ops!(LessThan, lt, "Binary `<` operator."); implement_bin_ops!(LessThanOrEq, le, "Binary `<=` operator."); +implement_bin_ops!(InstanceOf, instance_of, "Binary `<=` operator."); diff --git a/core/engine/src/vm/opcode/binary_ops/mod.rs b/core/engine/src/vm/opcode/binary_ops/mod.rs index 2ecf80d4fe3..947cd47194a 100644 --- a/core/engine/src/vm/opcode/binary_ops/mod.rs +++ b/core/engine/src/vm/opcode/binary_ops/mod.rs @@ -1,7 +1,7 @@ use crate::{ error::JsNativeError, vm::{opcode::Operation, CompletionType}, - Context, JsResult, + Context, JsResult, JsValue, }; pub(crate) mod logical; @@ -17,17 +17,60 @@ pub(crate) use macro_defined::*; #[derive(Debug, Clone, Copy)] pub(crate) struct NotEq; +impl NotEq { + #[allow(clippy::needless_pass_by_value)] + fn operation( + output: u32, + lhs: u32, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + let rhs = context + .vm + .frame() + .read_value::<1>(operand_types, rhs, &context.vm); + + let value = !lhs.equals(&rhs, context)?; + + context.vm.stack[(rp + output) as usize] = JsValue::from(value); + Ok(CompletionType::Normal) + } +} + impl Operation for NotEq { const NAME: &'static str = "NotEq"; const INSTRUCTION: &'static str = "INST - NotEq"; const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { - let rhs = context.vm.pop(); - let lhs = context.vm.pop(); - let value = !lhs.equals(&rhs, context)?; - context.vm.push(value); - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let output = context.vm.read::().into(); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::().into(); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::(); + let lhs = context.vm.read::(); + let rhs = context.vm.read::(); + Self::operation(output, lhs, rhs, operand_types, context) } } @@ -38,16 +81,61 @@ impl Operation for NotEq { #[derive(Debug, Clone, Copy)] pub(crate) struct StrictEq; +impl StrictEq { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + output: u32, + lhs: u32, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + let rhs = context + .vm + .frame() + .read_value::<1>(operand_types, rhs, &context.vm); + + let value = lhs.strict_equals(&rhs); + + context.vm.stack[(rp + output) as usize] = JsValue::from(value); + Ok(CompletionType::Normal) + } +} + impl Operation for StrictEq { const NAME: &'static str = "StrictEq"; const INSTRUCTION: &'static str = "INST - StrictEq"; const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { - let rhs = context.vm.pop(); - let lhs = context.vm.pop(); - context.vm.push(lhs.strict_equals(&rhs)); - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::(); + let lhs = context.vm.read::(); + let rhs = context.vm.read::(); + Self::operation(output, lhs, rhs, operand_types, context) } } @@ -58,16 +146,61 @@ impl Operation for StrictEq { #[derive(Debug, Clone, Copy)] pub(crate) struct StrictNotEq; +impl StrictNotEq { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + output: u32, + lhs: u32, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); + let rhs = context + .vm + .frame() + .read_value::<1>(operand_types, rhs, &context.vm); + + let value = !lhs.strict_equals(&rhs); + + context.vm.stack[(rp + output) as usize] = JsValue::from(value); + Ok(CompletionType::Normal) + } +} + impl Operation for StrictNotEq { const NAME: &'static str = "StrictNotEq"; const INSTRUCTION: &'static str = "INST - StrictNotEq"; const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { - let rhs = context.vm.pop(); - let lhs = context.vm.pop(); - context.vm.push(!lhs.strict_equals(&rhs)); - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::(); + let lhs = context.vm.read::(); + let rhs = context.vm.read::(); + Self::operation(output, lhs, rhs, operand_types, context) } } @@ -78,14 +211,20 @@ impl Operation for StrictNotEq { #[derive(Debug, Clone, Copy)] pub(crate) struct In; -impl Operation for In { - const NAME: &'static str = "In"; - const INSTRUCTION: &'static str = "INST - In"; - const COST: u8 = 3; - - fn execute(context: &mut Context) -> JsResult { - let rhs = context.vm.pop(); - let lhs = context.vm.pop(); +impl In { + #[allow(clippy::needless_pass_by_value)] + fn operation( + output: u32, + lhs: u32, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let rhs = context + .vm + .frame() + .read_value::<1>(operand_types, rhs, &context.vm); let Some(rhs) = rhs.as_object() else { return Err(JsNativeError::typ() @@ -95,13 +234,48 @@ impl Operation for In { )) .into()); }; + + let lhs = context + .vm + .frame() + .read_value::<0>(operand_types, lhs, &context.vm); let key = lhs.to_property_key(context)?; let value = rhs.has_property(key, context)?; - context.vm.push(value); + context.vm.stack[(rp + output) as usize] = JsValue::from(value); Ok(CompletionType::Normal) } } +impl Operation for In { + const NAME: &'static str = "In"; + const INSTRUCTION: &'static str = "INST - In"; + const COST: u8 = 3; + + fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = u32::from(context.vm.read::()); + let lhs = context.vm.read::().into(); + let rhs = context.vm.read::().into(); + Self::operation(output, lhs, rhs, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let output = context.vm.read::(); + let lhs = context.vm.read::(); + let rhs = context.vm.read::(); + Self::operation(output, lhs, rhs, operand_types, context) + } +} + /// `InPrivate` implements the Opcode Operation for `Opcode::InPrivate` /// /// Operation: @@ -110,9 +284,20 @@ impl Operation for In { pub(crate) struct InPrivate; impl InPrivate { - fn operation(context: &mut Context, index: usize) -> JsResult { + #[allow(clippy::needless_pass_by_value)] + fn operation( + dst: u32, + index: usize, + rhs: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { let name = context.vm.frame().code_block().constant_string(index); - let rhs = context.vm.pop(); + let rp = context.vm.frame().rp; + let rhs = context + .vm + .frame() + .read_value::<0>(operand_types, rhs, &context.vm); let Some(rhs) = rhs.as_object() else { return Err(JsNativeError::typ() @@ -129,11 +314,9 @@ impl InPrivate { .resolve_private_identifier(name) .expect("private name must be in environment"); - if rhs.private_element_find(&name, true, true).is_some() { - context.vm.push(true); - } else { - context.vm.push(false); - } + let value = rhs.private_element_find(&name, true, true).is_some(); + + context.vm.stack[(rp + dst) as usize] = JsValue::from(value); Ok(CompletionType::Normal) } } @@ -144,39 +327,26 @@ impl Operation for InPrivate { const COST: u8 = 4; fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, index) + let rhs = context.vm.read::().into(); + Self::operation(dst, index, rhs, operand_types, context) } fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, index) + let rhs = context.vm.read::().into(); + Self::operation(dst, index, rhs, operand_types, context) } fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) - } -} - -/// `InstanceOf` implements the Opcode Operation for `Opcode::InstanceOf` -/// -/// Operation: -/// - Binary `instanceof` operation -#[derive(Debug, Clone, Copy)] -pub(crate) struct InstanceOf; - -impl Operation for InstanceOf { - const NAME: &'static str = "InstanceOf"; - const INSTRUCTION: &'static str = "INST - InstanceOf"; - const COST: u8 = 4; - - fn execute(context: &mut Context) -> JsResult { - let target = context.vm.pop(); - let v = context.vm.pop(); - let value = v.instance_of(&target, context)?; - - context.vm.push(value); - Ok(CompletionType::Normal) + let rhs = context.vm.read::(); + Self::operation(dst, index, rhs, operand_types, context) } } diff --git a/core/engine/src/vm/opcode/control_flow/return.rs b/core/engine/src/vm/opcode/control_flow/return.rs index 93e502b8321..c6e0eae3c23 100644 --- a/core/engine/src/vm/opcode/control_flow/return.rs +++ b/core/engine/src/vm/opcode/control_flow/return.rs @@ -77,16 +77,16 @@ impl Operation for CheckReturn { } } -/// `GetReturnValue` implements the Opcode Operation for `Opcode::GetReturnValue` +/// `GetAccumulator` implements the Opcode Operation for `Opcode::GetAccumulator` /// /// Operation: -/// - Gets the return value of a function. +/// - Gets the accumulator value, which is the implicit return value of a function. #[derive(Debug, Clone, Copy)] -pub(crate) struct GetReturnValue; +pub(crate) struct GetAccumulator; -impl Operation for GetReturnValue { - const NAME: &'static str = "GetReturnValue"; - const INSTRUCTION: &'static str = "INST - GetReturnValue"; +impl Operation for GetAccumulator { + const NAME: &'static str = "GetAccumulator"; + const INSTRUCTION: &'static str = "INST - GetAccumulator"; const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { @@ -96,16 +96,16 @@ impl Operation for GetReturnValue { } } -/// `SetReturnValue` implements the Opcode Operation for `Opcode::SetReturnValue` +/// `SetAccumulatorFromStack` implements the Opcode Operation for `Opcode::SetAccumulatorFromStack` /// /// Operation: -/// - Sets the return value of a function. +/// - Sets the accumulator value, which is the implicit return value of a function. #[derive(Debug, Clone, Copy)] -pub(crate) struct SetReturnValue; +pub(crate) struct SetAccumulatorFromStack; -impl Operation for SetReturnValue { - const NAME: &'static str = "SetReturnValue"; - const INSTRUCTION: &'static str = "INST - SetReturnValue"; +impl Operation for SetAccumulatorFromStack { + const NAME: &'static str = "SetAccumulatorFromStack"; + const INSTRUCTION: &'static str = "INST - SetAccumulatorFromStack"; const COST: u8 = 2; fn execute(context: &mut Context) -> JsResult { @@ -114,3 +114,208 @@ impl Operation for SetReturnValue { Ok(CompletionType::Normal) } } + +/// `SetAccumulator` implements the Opcode Operation for `Opcode::SetAccumulator` +/// +/// Operation: +/// - Sets the accumulator value, which is the implicit return value of a function. +#[derive(Debug, Clone, Copy)] +pub(crate) struct SetAccumulator; + +impl SetAccumulator { + #[allow(clippy::unnecessary_wraps)] + fn operation(register: u32, context: &mut Context) -> JsResult { + let value = context + .vm + .frame() + .register(register, &context.vm.stack) + .clone(); + context.vm.set_return_value(value); + Ok(CompletionType::Normal) + } +} + +impl Operation for SetAccumulator { + const NAME: &'static str = "SetAccumulator"; + const INSTRUCTION: &'static str = "INST - SetAccumulator"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let register = u32::from(context.vm.read::()); + Self::operation(register, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let register = u32::from(context.vm.read::()); + Self::operation(register, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let register = context.vm.read::(); + Self::operation(register, context) + } +} + +/// `Move` implements the Opcode Operation for `Opcode::Move` +/// +/// Operation: +/// - Sets the accumulator value, which is the implicit return value of a function. +#[derive(Debug, Clone, Copy)] +pub(crate) struct Move; + +impl Move { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation( + dst: u32, + src: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let value = context + .vm + .frame() + .read_value::<0>(operand_types, src, &context.vm); + + context.vm.stack[(rp + dst) as usize] = value; + Ok(CompletionType::Normal) + } +} + +impl Operation for Move { + const NAME: &'static str = "Move"; + const INSTRUCTION: &'static str = "INST - Move"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(dst, src, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(dst, src, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let src = context.vm.read::(); + Self::operation(dst, src, operand_types, context) + } +} + +/// TODO: doc +#[derive(Debug, Clone, Copy)] +pub(crate) struct PopIntoRegister; + +impl PopIntoRegister { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation(dst: u32, context: &mut Context) -> JsResult { + let value = context.vm.pop(); + + let rp = context.vm.frame().rp; + context.vm.stack[(rp + dst) as usize] = value; + Ok(CompletionType::Normal) + } +} + +impl Operation for PopIntoRegister { + const NAME: &'static str = "PopIntoRegister"; + const INSTRUCTION: &'static str = "INST - PopIntoRegister"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); + Self::operation(dst, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); + Self::operation(dst, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, context) + } +} + +/// TODO: doc +#[derive(Debug, Clone, Copy)] +pub(crate) struct PushFromRegister; + +impl PushFromRegister { + #[allow(clippy::unnecessary_wraps)] + #[allow(clippy::needless_pass_by_value)] + fn operation(dst: u32, context: &mut Context) -> JsResult { + let rp = context.vm.frame().rp; + let value = context.vm.stack[(rp + dst) as usize].clone(); + context.vm.push(value); + Ok(CompletionType::Normal) + } +} + +impl Operation for PushFromRegister { + const NAME: &'static str = "PushFromRegister"; + const INSTRUCTION: &'static str = "INST - PushFromRegister"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); + Self::operation(dst, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); + Self::operation(dst, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let dst = context.vm.read::(); + Self::operation(dst, context) + } +} + +/// `SetRegisterFromAccumulator` implements the Opcode Operation for `Opcode::SetRegisterFromAccumulator` +/// +/// Operation: +/// - Sets the accumulator value, which is the implicit return value of a function. +#[derive(Debug, Clone, Copy)] +pub(crate) struct SetRegisterFromAccumulator; + +impl SetRegisterFromAccumulator { + #[allow(clippy::unnecessary_wraps)] + fn operation(register: u32, context: &mut Context) -> JsResult { + let rp = context.vm.frame().rp; + context.vm.stack[(rp + register) as usize] = context.vm.get_return_value(); + Ok(CompletionType::Normal) + } +} + +impl Operation for SetRegisterFromAccumulator { + const NAME: &'static str = "SetRegisterFromAccumulator"; + const INSTRUCTION: &'static str = "INST - SetRegisterFromAccumulator"; + const COST: u8 = 2; + + fn execute(context: &mut Context) -> JsResult { + let register = u32::from(context.vm.read::()); + Self::operation(register, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let register = u32::from(context.vm.read::()); + Self::operation(register, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let register = context.vm.read::(); + Self::operation(register, context) + } +} diff --git a/core/engine/src/vm/opcode/generator/mod.rs b/core/engine/src/vm/opcode/generator/mod.rs index d5a47e22a05..de4ba79f863 100644 --- a/core/engine/src/vm/opcode/generator/mod.rs +++ b/core/engine/src/vm/opcode/generator/mod.rs @@ -20,7 +20,7 @@ use crate::{ pub(crate) use yield_stm::*; -use super::SetReturnValue; +use super::SetAccumulatorFromStack; /// `Generator` implements the Opcode Operation for `Opcode::Generator` /// @@ -172,7 +172,7 @@ impl Operation for GeneratorNext { GeneratorResumeKind::Return => { assert!(context.vm.pending_exception.is_none()); - SetReturnValue::execute(context)?; + SetAccumulatorFromStack::execute(context)?; ReThrow::execute(context) } } diff --git a/core/engine/src/vm/opcode/get/function.rs b/core/engine/src/vm/opcode/get/function.rs index bd19632532f..cbd680151bf 100644 --- a/core/engine/src/vm/opcode/get/function.rs +++ b/core/engine/src/vm/opcode/get/function.rs @@ -12,10 +12,11 @@ pub(crate) struct GetFunction; impl GetFunction { #[allow(clippy::unnecessary_wraps)] - fn operation(context: &mut Context, index: usize) -> JsResult { + fn operation(context: &mut Context, dst: u32, index: usize) -> JsResult { + let rp = context.vm.frame().rp; let code = context.vm.frame().code_block().constant_function(index); let function = create_function_object_fast(code, context); - context.vm.push(function); + context.vm.stack[(rp + dst) as usize] = function.into(); Ok(CompletionType::Normal) } } @@ -26,17 +27,20 @@ impl Operation for GetFunction { const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(context, dst, index) } fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let dst = u32::from(context.vm.read::()); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(context, dst, index) } fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let dst = context.vm.read::(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(context, dst, index) } } diff --git a/core/engine/src/vm/opcode/get/property.rs b/core/engine/src/vm/opcode/get/property.rs index 395ea12045e..f486a0536d9 100644 --- a/core/engine/src/vm/opcode/get/property.rs +++ b/core/engine/src/vm/opcode/get/property.rs @@ -13,13 +13,29 @@ use crate::{ pub(crate) struct GetPropertyByName; impl GetPropertyByName { - fn operation(context: &mut Context, index: usize) -> JsResult { - let receiver = context.vm.pop(); - let value = context.vm.pop(); + fn operation( + dst: u32, + receiver: u32, + value: u32, + index: usize, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + // println!("operand_types: {operand_types:08b}, reciever: {receiver}, value: {value}"); + let receiver = context + .vm + .frame() + .read_value::<0>(operand_types, receiver, &context.vm); + let value = context + .vm + .frame() + .read_value::<1>(operand_types, value, &context.vm); + + let rp = context.vm.frame().rp; let object = if let Some(object) = value.as_object() { object.clone() } else { - value.to_object(context)? + value.clone().to_object(context)? }; let ic = &context.vm.frame().code_block().ic[index]; @@ -41,7 +57,7 @@ impl GetPropertyByName { context, )?; } - context.vm.push(result); + context.vm.stack[(rp + dst) as usize] = result; return Ok(CompletionType::Normal); } @@ -61,7 +77,7 @@ impl GetPropertyByName { ic.set(shape, slot); } - context.vm.push(result); + context.vm.stack[(rp + dst) as usize] = result; Ok(CompletionType::Normal) } } @@ -72,18 +88,30 @@ impl Operation for GetPropertyByName { const COST: u8 = 4; fn execute(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let value = context.vm.read::().into(); + let index = context.vm.read::() as usize; + Self::operation(dst, receiver, value, index, operand_types, context) } fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let receiver = context.vm.read::().into(); + let value = context.vm.read::().into(); let index = context.vm.read::() as usize; - Self::operation(context, index) + Self::operation(dst, receiver, value, index, operand_types, context) } fn execute_with_u32_operands(context: &mut Context) -> JsResult { - let index = context.vm.read::(); - Self::operation(context, index as usize) + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let receiver = context.vm.read::(); + let value = context.vm.read::(); + let index = context.vm.read::() as usize; + Self::operation(dst, receiver, value, index, operand_types, context) } } diff --git a/core/engine/src/vm/opcode/mod.rs b/core/engine/src/vm/opcode/mod.rs index 15a362159dc..377546f09ea 100644 --- a/core/engine/src/vm/opcode/mod.rs +++ b/core/engine/src/vm/opcode/mod.rs @@ -131,7 +131,8 @@ where } /// Represents a varying operand kind. -#[derive(Default, Debug, Clone, Copy)] +#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u8)] pub(crate) enum VaryingOperandKind { #[default] U8, @@ -181,6 +182,16 @@ impl VaryingOperand { pub(crate) const fn kind(self) -> VaryingOperandKind { self.kind } + #[must_use] + pub(crate) fn to_string(self, operand_types: u8) -> String { + let type_ = (operand_types >> (N * 2)) & 0x0000_0011; + match type_ { + 0b0000_0000 => format!("reg{}", self.value), + 0b0000_0001 => format!("arg{}", self.value), + 0b0000_0010 => format!("{}", self.value), + _ => unreachable!("unknown operand kind"), + } + } } trait BytecodeConversion: Sized { @@ -758,14 +769,24 @@ generate_opcodes! { /// Operands: /// /// Stack: class, superclass **=>** class, superclass.prototype - PushClassPrototype, + PushClassPrototype { + operand_types: u8, + dst: VaryingOperand, + class: VaryingOperand, + superclass: VaryingOperand + }, /// Set the prototype of a class object. /// /// Operands: /// /// Stack: class, prototype **=>** class.prototype - SetClassPrototype, + SetClassPrototype { + operand_types: u8, + dst: VaryingOperand, + prototype: VaryingOperand, + class: VaryingOperand + }, /// Set home object internal slot of an object literal method. /// @@ -814,168 +835,160 @@ generate_opcodes! { /// Operands: /// /// Stack: lhs, rhs **=>** (lhs + rhs) - Add, + Add { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `-` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs - rhs) - Sub, + Sub { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `/` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs / rhs) - Div, + Div { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `*` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs * rhs) - Mul, + Mul { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `%` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs % rhs) - Mod, + Mod { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `**` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs ** rhs) - Pow, + Pow { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>>` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs >> rhs) - ShiftRight, + ShiftRight { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<<` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** `(lhs << rhs)` - ShiftLeft, + ShiftLeft { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>>>` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs >>> rhs) - UnsignedShiftRight, + UnsignedShiftRight { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `|` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs | rhs) - BitOr, + BitOr { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `&` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs & rhs) - BitAnd, + BitAnd { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary bitwise `^` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs ^ rhs) - BitXor, + BitXor { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Unary bitwise `~` operator. /// /// Operands: /// /// Stack: value **=>** ~value - BitNot, + BitNot { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `in` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs `in` rhs) - In, + In { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `in` operator for private names. /// /// Operands: index: `u32` /// /// Stack: rhs **=>** (private_name `in` rhs) - InPrivate { index: VaryingOperand }, + InPrivate { operand_types: u8, dst: VaryingOperand, index: VaryingOperand, rhs: VaryingOperand }, /// Binary `==` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs `==` rhs) - Eq, + Eq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `===` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs `===` rhs) - StrictEq, + StrictEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `!=` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `!=` rhs) - NotEq, + NotEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `!==` operator. - /// - /// Operands: - /// - /// Stack: lhs, rhs **=>** (lhs `!==` rhs) - StrictNotEq, + StrictNotEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs > rhs) - GreaterThan, + GreaterThan { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `>=` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs >= rhs) - GreaterThanOrEq, + GreaterThanOrEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** `(lhs < rhs)` - LessThan, + LessThan { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `<=` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** `(lhs <= rhs)` - LessThanOrEq, + LessThanOrEq { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary `instanceof` operator. /// /// Operands: /// /// Stack: lhs, rhs **=>** (lhs instanceof rhs) - InstanceOf, + InstanceOf { operand_types: u8, dst: VaryingOperand, lhs: VaryingOperand, rhs: VaryingOperand }, /// Binary logical `&&` operator. /// @@ -984,7 +997,7 @@ generate_opcodes! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs && rhs) - LogicalAnd { exit: u32 }, + LogicalAnd { operand_types: u8, exit: u32, lhs: VaryingOperand }, /// Binary logical `||` operator. /// @@ -993,7 +1006,7 @@ generate_opcodes! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs || rhs) - LogicalOr { exit: u32 }, + LogicalOr { operand_types: u8, exit: u32, lhs: VaryingOperand }, /// Binary `??` operator. /// @@ -1003,7 +1016,7 @@ generate_opcodes! { /// Operands: exit: `u32` /// /// Stack: lhs, rhs **=>** (lhs ?? rhs) - Coalesce { exit: u32 }, + Coalesce { operand_types: u8, exit: u32, lhs: VaryingOperand }, /// Unary `typeof` operator. /// @@ -1040,33 +1053,22 @@ generate_opcodes! { /// Stack: value **=>** (-value) Neg, + /// Convert value at register `src` to numeric and puts it in register `dst`. + ToNumeric { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + /// Unary `++` operator. /// /// Operands: /// /// Stack: value **=>** (value + 1) - Inc, - - /// Unary postfix `++` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (ToNumeric(value)), (value + 1) - IncPost, + Inc { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, /// Unary `--` operator. /// /// Operands: /// /// Stack: value **=>** (value - 1) - Dec, - - /// Unary postfix `--` operator. - /// - /// Operands: - /// - /// Stack: value **=>** (ToNumeric(value)), (value - 1) - DecPost, + Dec { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, /// Declare `var` type variable. /// @@ -1160,7 +1162,13 @@ generate_opcodes! { /// Operands: index: `u32` /// /// Stack: object, receiver **=>** value - GetPropertyByName { index: VaryingOperand }, + GetPropertyByName { + operand_types: u8, + dst: VaryingOperand, + receiver: VaryingOperand, + value: VaryingOperand, + index: VaryingOperand + }, /// Get a property by value from an object an push it on the stack. /// @@ -1694,7 +1702,7 @@ generate_opcodes! { /// Operands: index: `VaryingOperand` /// /// Stack: **=>** func - GetFunction { index: VaryingOperand }, + GetFunction { dst: VaryingOperand, index: VaryingOperand }, /// Call a function named "eval". /// @@ -1771,14 +1779,49 @@ generate_opcodes! { /// Operands: /// /// Stack: **=>** value - GetReturnValue, + GetAccumulator, + + /// Set return value of a function. + /// + /// Operands: + /// + /// Stack: value **=>** + SetAccumulatorFromStack, /// Set return value of a function. /// /// Operands: /// + /// Stack: **=>** + SetAccumulator { register: VaryingOperand }, + + // Set return value of a function. + /// + /// Operands: + /// + /// Stack: **=>** + SetRegisterFromAccumulator { register: VaryingOperand }, + + /// Move value of operand `src` to register `dst`. + /// + /// Operands: + /// + /// Stack: **=>** + Move { operand_types: u8, dst: VaryingOperand, src: VaryingOperand }, + + /// Pop value from the stack and push to register `dst` + /// + /// Operands: + /// /// Stack: value **=>** - SetReturnValue, + PopIntoRegister { dst: VaryingOperand }, + + /// Copy value at register `src` and push it into the stack. + /// + /// Operands: + /// + /// Stack: **=>** value + PushFromRegister { src: VaryingOperand }, /// Push a declarative environment. /// @@ -2058,7 +2101,7 @@ generate_opcodes! { /// Operands: count: `u32`, count * name_index: `u32` /// /// Stack: class **=>** class - PushPrivateEnvironment { name_indices: ThinVec }, + PushPrivateEnvironment { operand_types: u8, class: VaryingOperand, name_indices: ThinVec }, /// Pop a private environment. /// @@ -2251,14 +2294,6 @@ generate_opcodes! { Reserved48 => Reserved, /// Reserved [`Opcode`]. Reserved49 => Reserved, - /// Reserved [`Opcode`]. - Reserved50 => Reserved, - /// Reserved [`Opcode`]. - Reserved51 => Reserved, - /// Reserved [`Opcode`]. - Reserved52 => Reserved, - /// Reserved [`Opcode`]. - Reserved53 => Reserved, } /// Specific opcodes for bindings. diff --git a/core/engine/src/vm/opcode/push/class/mod.rs b/core/engine/src/vm/opcode/push/class/mod.rs index 60f8c8f8852..f7492cdb366 100644 --- a/core/engine/src/vm/opcode/push/class/mod.rs +++ b/core/engine/src/vm/opcode/push/class/mod.rs @@ -18,14 +18,23 @@ pub(crate) use private::*; #[derive(Debug, Clone, Copy)] pub(crate) struct PushClassPrototype; -impl Operation for PushClassPrototype { - const NAME: &'static str = "PushClassPrototype"; - const INSTRUCTION: &'static str = "INST - PushClassPrototype"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let superclass = context.vm.pop(); - let class = context.vm.pop(); +impl PushClassPrototype { + fn operation( + dst: u32, + class: u32, + superclass: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let class = context + .vm + .frame() + .read_value::<0>(operand_types, class, &context.vm); + let superclass = context + .vm + .frame() + .read_value::<1>(operand_types, superclass, &context.vm); // // Taken from `15.7.14 Runtime Semantics: ClassDefinitionEvaluation`: // @@ -68,9 +77,37 @@ impl Operation for PushClassPrototype { class_object.set_prototype(Some(constructor_parent)); } - context.vm.push(class); - context.vm.push(proto_parent); - + context.vm.stack[(rp + dst) as usize] = proto_parent; Ok(CompletionType::Normal) } } + +impl Operation for PushClassPrototype { + const NAME: &'static str = "PushClassPrototype"; + const INSTRUCTION: &'static str = "INST - PushClassPrototype"; + const COST: u8 = 6; + + fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); + let class = u32::from(context.vm.read::()); + let superclass = u32::from(context.vm.read::()); + Self::operation(dst, class, superclass, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); + let class = u32::from(context.vm.read::()); + let superclass = u32::from(context.vm.read::()); + Self::operation(dst, class, superclass, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let class = context.vm.read::(); + let superclass = context.vm.read::(); + Self::operation(dst, class, superclass, operand_types, context) + } +} diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index ca2a5e2e643..20e99934f46 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -2,7 +2,7 @@ use crate::{ builtins::function::OrdinaryFunction, environments::PrivateEnvironment, vm::{opcode::Operation, CompletionType}, - Context, JsResult, + Context, JsResult, JsString, }; use boa_gc::Gc; @@ -78,15 +78,41 @@ impl Operation for PushObjectEnvironment { #[derive(Debug, Clone, Copy)] pub(crate) struct PushPrivateEnvironment; +impl PushPrivateEnvironment { + #[allow(clippy::unnecessary_wraps)] + fn operation( + class: u32, + names: Vec, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let class_value = context + .vm + .frame() + .read_value::<0>(operand_types, class, &context.vm); + let class = class_value.as_object().expect("should be a object"); + + let ptr: *const _ = class.as_ref(); + let environment = Gc::new(PrivateEnvironment::new(ptr.cast::<()>() as usize, names)); + + class + .downcast_mut::() + .expect("class object must be function") + .push_private_environment(environment.clone()); + context.vm.environments.push_private(environment); + + Ok(CompletionType::Normal) + } +} + impl Operation for PushPrivateEnvironment { const NAME: &'static str = "PushPrivateEnvironment"; const INSTRUCTION: &'static str = "INST - PushPrivateEnvironment"; const COST: u8 = 5; fn execute(context: &mut Context) -> JsResult { - let class_value = context.vm.pop(); - let class = class_value.to_object(context)?; - + let operand_types = context.vm.read::(); + let class = u32::from(context.vm.read::()); let count = context.vm.read::(); let mut names = Vec::with_capacity(count as usize); for _ in 0..count { @@ -98,19 +124,41 @@ impl Operation for PushPrivateEnvironment { .constant_string(index as usize); names.push(name); } + Self::operation(class, names, operand_types, context) + } - let ptr: *const _ = class.as_ref(); - let environment = Gc::new(PrivateEnvironment::new(ptr.cast::<()>() as usize, names)); - - class - .downcast_mut::() - .expect("class object must be function") - .push_private_environment(environment.clone()); - context.vm.environments.push_private(environment); - - context.vm.push(class_value); + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let class = u32::from(context.vm.read::()); + let count = context.vm.read::(); + let mut names = Vec::with_capacity(count as usize); + for _ in 0..count { + let index = context.vm.read::(); + let name = context + .vm + .frame() + .code_block() + .constant_string(index as usize); + names.push(name); + } + Self::operation(class, names, operand_types, context) + } - Ok(CompletionType::Normal) + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let class = context.vm.read::(); + let count = context.vm.read::(); + let mut names = Vec::with_capacity(count as usize); + for _ in 0..count { + let index = context.vm.read::(); + let name = context + .vm + .frame() + .code_block() + .constant_string(index as usize); + names.push(name); + } + Self::operation(class, names, operand_types, context) } } diff --git a/core/engine/src/vm/opcode/rest_parameter/mod.rs b/core/engine/src/vm/opcode/rest_parameter/mod.rs index 59db486fb4b..a009ecc8e2a 100644 --- a/core/engine/src/vm/opcode/rest_parameter/mod.rs +++ b/core/engine/src/vm/opcode/rest_parameter/mod.rs @@ -20,16 +20,30 @@ impl Operation for RestParameterInit { let frame = context.vm.frame(); let argument_count = frame.argument_count; let param_count = frame.code_block().parameter_length; + let register_count = frame.code_block().register_count; - let array = if argument_count >= param_count { + if argument_count >= param_count { let rest_count = argument_count - param_count + 1; - let args = context.vm.pop_n_values(rest_count as usize); - Array::create_array_from_list(args, context) + + let len = context.vm.stack.len() as u32; + let start = (len - rest_count - register_count) as usize; + let end = (len - register_count) as usize; + + let args = &context.vm.stack[start..end]; + + let array = Array::create_array_from_list(args.iter().cloned(), context); + context.vm.stack.splice(start..end, [array.clone().into()]); + + context.vm.frame_mut().rp -= (start..end).len() as u32; + context.vm.frame_mut().argument_count -= (start..end).len() as u32; + + context.vm.push(array); } else { - Array::array_create(0, None, context).expect("could not create an empty array") - }; + let array = + Array::array_create(0, None, context).expect("could not create an empty array"); + context.vm.push(array); + } - context.vm.push(array); Ok(CompletionType::Normal) } } diff --git a/core/engine/src/vm/opcode/set/class_prototype.rs b/core/engine/src/vm/opcode/set/class_prototype.rs index be0d89c2054..1384826d224 100644 --- a/core/engine/src/vm/opcode/set/class_prototype.rs +++ b/core/engine/src/vm/opcode/set/class_prototype.rs @@ -13,13 +13,21 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct SetClassPrototype; -impl Operation for SetClassPrototype { - const NAME: &'static str = "SetClassPrototype"; - const INSTRUCTION: &'static str = "INST - SetClassPrototype"; - const COST: u8 = 6; - - fn execute(context: &mut Context) -> JsResult { - let prototype_value = context.vm.pop(); +impl SetClassPrototype { + #[allow(clippy::unnecessary_wraps)] + fn operation( + dst: u32, + prototype: u32, + class: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let prototype_value = + context + .vm + .frame() + .read_value::<0>(operand_types, prototype, &context.vm); let prototype = match &prototype_value { JsValue::Object(proto) => Some(proto.clone()), JsValue::Null => None, @@ -33,7 +41,10 @@ impl Operation for SetClassPrototype { prototype, OrdinaryObject, ); - let class = context.vm.pop(); + let class = context + .vm + .frame() + .read_value::<1>(operand_types, class, &context.vm); { let class_object = class.as_object().expect("class must be object"); @@ -67,7 +78,37 @@ impl Operation for SetClassPrototype { ) .expect("cannot fail per spec"); - context.vm.push(proto); + context.vm.stack[(rp + dst) as usize] = proto.into(); Ok(CompletionType::Normal) } } + +impl Operation for SetClassPrototype { + const NAME: &'static str = "SetClassPrototype"; + const INSTRUCTION: &'static str = "INST - SetClassPrototype"; + const COST: u8 = 6; + + fn execute(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); + let prototype = u32::from(context.vm.read::()); + let class = u32::from(context.vm.read::()); + Self::operation(dst, prototype, class, operand_types, context) + } + + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = u32::from(context.vm.read::()); + let prototype = u32::from(context.vm.read::()); + let class = u32::from(context.vm.read::()); + Self::operation(dst, prototype, class, operand_types, context) + } + + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let prototype = context.vm.read::(); + let class = context.vm.read::(); + Self::operation(dst, prototype, class, operand_types, context) + } +} diff --git a/core/engine/src/vm/opcode/unary_ops/decrement.rs b/core/engine/src/vm/opcode/unary_ops/decrement.rs index 978b10607fc..6a5f7adf874 100644 --- a/core/engine/src/vm/opcode/unary_ops/decrement.rs +++ b/core/engine/src/vm/opcode/unary_ops/decrement.rs @@ -1,5 +1,5 @@ use crate::{ - value::{JsValue, Numeric}, + value::JsValue, vm::{opcode::Operation, CompletionType}, Context, JsBigInt, JsResult, }; @@ -11,58 +11,52 @@ use crate::{ #[derive(Debug, Clone, Copy)] pub(crate) struct Dec; -impl Operation for Dec { - const NAME: &'static str = "Dec"; - const INSTRUCTION: &'static str = "INST - Dec"; - const COST: u8 = 3; +impl Dec { + #[allow(clippy::unnecessary_wraps)] + fn operation( + src: u32, + dst: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let value = context + .vm + .frame() + .read_value::<0>(operand_types, src, &context.vm); + let value = match value { + JsValue::Integer(number) if number > i32::MIN => JsValue::from(number - 1), + JsValue::Rational(value) => JsValue::from(value - 1f64), + JsValue::BigInt(bigint) => JsBigInt::sub(&bigint, &JsBigInt::one()).into(), + _ => unreachable!("there is always a call to ToNumeric before Inc"), + }; - fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number > i32::MIN => { - context.vm.push(number - 1); - } - _ => match value.to_numeric(context)? { - Numeric::Number(number) => context.vm.push(number - 1f64), - Numeric::BigInt(bigint) => { - context.vm.push(JsBigInt::sub(&bigint, &JsBigInt::one())); - } - }, - } + context.vm.stack[(rp + dst) as usize] = value; Ok(CompletionType::Normal) } } -/// `DecPost` implements the Opcode Operation for `Opcode::DecPost` -/// -/// Operation: -/// - Unary postfix `--` operator. -#[derive(Debug, Clone, Copy)] -pub(crate) struct DecPost; - -impl Operation for DecPost { - const NAME: &'static str = "DecPost"; - const INSTRUCTION: &'static str = "INST - DecPost"; +impl Operation for Dec { + const NAME: &'static str = "Dec"; + const INSTRUCTION: &'static str = "INST - Dec"; const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number > i32::MIN => { - context.vm.push(number - 1); - context.vm.push(value); - } - _ => { - let value = value.to_numeric(context)?; - match value { - Numeric::Number(number) => context.vm.push(number - 1f64), - Numeric::BigInt(ref bigint) => { - context.vm.push(JsBigInt::sub(bigint, &JsBigInt::one())); - } - } - context.vm.push(value); - } - } - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let dst: u32 = context.vm.read::().into(); + let src: u32 = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst: u32 = context.vm.read::().into(); + let src: u32 = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst: u32 = context.vm.read::(); + let src: u32 = context.vm.read::(); + Self::operation(src, dst, operand_types, context) } } diff --git a/core/engine/src/vm/opcode/unary_ops/increment.rs b/core/engine/src/vm/opcode/unary_ops/increment.rs index 87fefb21934..f72bd7cc487 100644 --- a/core/engine/src/vm/opcode/unary_ops/increment.rs +++ b/core/engine/src/vm/opcode/unary_ops/increment.rs @@ -1,68 +1,109 @@ use crate::{ - value::{JsValue, Numeric}, + value::JsValue, vm::{opcode::Operation, CompletionType}, Context, JsBigInt, JsResult, }; -/// `Inc` implements the Opcode Operation for `Opcode::Inc` -/// -/// Operation: -/// - Unary `++` operator. +/// `ToNumeric` implements the Opcode Operation for `Opcode::ToNumeric` #[derive(Debug, Clone, Copy)] -pub(crate) struct Inc; +pub(crate) struct ToNumeric; -impl Operation for Inc { - const NAME: &'static str = "Inc"; - const INSTRUCTION: &'static str = "INST - Inc"; +impl ToNumeric { + fn operation( + src: u32, + dst: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let value = context + .vm + .frame() + .read_value::<0>(operand_types, src, &context.vm) + .to_numeric(context)?; + context.vm.stack[(rp + dst) as usize] = value.into(); + Ok(CompletionType::Normal) + } +} + +impl Operation for ToNumeric { + const NAME: &'static str = "ToNumeric"; + const INSTRUCTION: &'static str = "INST - ToNumeric"; const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number < i32::MAX => { - context.vm.push(number + 1); - } - _ => match value.to_numeric(context)? { - Numeric::Number(number) => context.vm.push(number + 1f64), - Numeric::BigInt(bigint) => { - context.vm.push(JsBigInt::add(&bigint, &JsBigInt::one())); - } - }, - } - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let src = context.vm.read::(); + Self::operation(src, dst, operand_types, context) } } /// `Inc` implements the Opcode Operation for `Opcode::Inc` /// /// Operation: -/// - Unary postfix `++` operator. +/// - Unary `++` operator. #[derive(Debug, Clone, Copy)] -pub(crate) struct IncPost; +pub(crate) struct Inc; + +impl Inc { + #[allow(clippy::unnecessary_wraps)] + fn operation( + src: u32, + dst: u32, + operand_types: u8, + context: &mut Context, + ) -> JsResult { + let rp = context.vm.frame().rp; + let value = context + .vm + .frame() + .read_value::<0>(operand_types, src, &context.vm); + let value = match value { + JsValue::Integer(number) if number < i32::MAX => JsValue::from(number + 1), + JsValue::Rational(value) => JsValue::from(value + 1f64), + JsValue::BigInt(bigint) => JsBigInt::add(&bigint, &JsBigInt::one()).into(), + _ => unreachable!("there is always a call to ToNumeric before Inc"), + }; + + context.vm.stack[(rp + dst) as usize] = value; + Ok(CompletionType::Normal) + } +} -impl Operation for IncPost { - const NAME: &'static str = "IncPost"; - const INSTRUCTION: &'static str = "INST - IncPost"; +impl Operation for Inc { + const NAME: &'static str = "Inc"; + const INSTRUCTION: &'static str = "INST - Inc"; const COST: u8 = 3; fn execute(context: &mut Context) -> JsResult { - let value = context.vm.pop(); - match value { - JsValue::Integer(number) if number < i32::MAX => { - context.vm.push(number + 1); - context.vm.push(value); - } - _ => { - let value = value.to_numeric(context)?; - match value { - Numeric::Number(number) => context.vm.push(number + 1f64), - Numeric::BigInt(ref bigint) => { - context.vm.push(JsBigInt::add(bigint, &JsBigInt::one())); - } - } - context.vm.push(value); - } - } - Ok(CompletionType::Normal) + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u16_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::().into(); + let src = context.vm.read::().into(); + Self::operation(src, dst, operand_types, context) + } + fn execute_with_u32_operands(context: &mut Context) -> JsResult { + let operand_types = context.vm.read::(); + let dst = context.vm.read::(); + let src = context.vm.read::(); + Self::operation(src, dst, operand_types, context) } } diff --git a/docs/boa_object.md b/docs/boa_object.md index b3fa7098a39..f5771ff42d1 100644 --- a/docs/boa_object.md +++ b/docs/boa_object.md @@ -42,7 +42,7 @@ Location Count Handler Opcode Operands 000016 0007 none GetName 0000: 'x' 000018 0008 none GetName 0001: 'y' 000020 0009 none Add -000021 0010 none SetReturnValue +000021 0010 none SetAccumulatorFromStack 000022 0011 none CheckReturn 000023 0012 none Return 000024 0013 none CheckReturn