Browse Source

Register post and pre increment and decrement

refactor/register-vm
Haled Odat 3 weeks ago
parent
commit
7351c87097
  1. 234
      core/engine/src/bytecompiler/expression/update.rs
  2. 27
      core/engine/src/bytecompiler/mod.rs
  3. 15
      core/engine/src/vm/code_block.rs
  4. 12
      core/engine/src/vm/flowgraph/mod.rs
  5. 38
      core/engine/src/vm/opcode/get/property.rs
  6. 30
      core/engine/src/vm/opcode/mod.rs
  7. 75
      core/engine/src/vm/opcode/unary_ops/decrement.rs
  8. 80
      core/engine/src/vm/opcode/unary_ops/increment.rs

234
core/engine/src/bytecompiler/expression/update.rs

@ -1,5 +1,5 @@
use crate::{
bytecompiler::{Access, ByteCompiler, Operand, ToJsString},
bytecompiler::{Access, ByteCompiler, 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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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::Varying(dst.index()),
Operand2::Varying(src.index()),
],
);
self.emit2(
opcode,
&[
Operand2::Varying(src.index()),
Operand2::Varying(dst.index()),
],
);
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);
}
},
},

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

@ -698,6 +698,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));
@ -706,7 +723,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::Varying(dst.index()),
Operand2::Varying(receiver.index()),
Operand2::Varying(value.index()),
Operand2::Varying(ic_index),
],
);
}
fn emit_set_property_by_name(&mut self, ident: Sym) {

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

@ -393,7 +393,10 @@ impl CodeBlock {
| Instruction::InstanceOf { .. }
| Instruction::StrictNotEq { .. }
| Instruction::StrictEq { .. }
| Instruction::InPrivate { .. } => "TODO: fix".to_string(),
| 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 { dst: r1, src: r2 } => {
@ -527,7 +530,8 @@ impl CodeBlock {
.to_std_string_escaped(),
)
}
Instruction::GetPropertyByName { index } | Instruction::SetPropertyByName { index } => {
Instruction::GetPropertyByName { index, .. }
| Instruction::SetPropertyByName { index } => {
let ic = &self.ic[index.value() as usize];
let slot = ic.slot();
format!(
@ -588,10 +592,6 @@ impl CodeBlock {
| Instruction::LogicalNot
| Instruction::Pos
| Instruction::Neg
| Instruction::Inc
| Instruction::IncPost
| Instruction::Dec
| Instruction::DecPost
| Instruction::GetPropertyByValue
| Instruction::GetPropertyByValuePush
| Instruction::SetPropertyByValue
@ -722,7 +722,8 @@ impl CodeBlock {
| Instruction::Reserved51
| Instruction::Reserved52
| Instruction::Reserved53
| Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved54
| Instruction::Reserved55 => unreachable!("Reserved opcodes are unrechable"),
}
}
}

12
core/engine/src/vm/flowgraph/mod.rs

@ -66,7 +66,10 @@ impl CodeBlock {
| Instruction::LessThanOrEq { .. }
| Instruction::InstanceOf { .. }
| Instruction::SetAccumulator { .. }
| Instruction::SetFunctionName { .. } => {
| 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);
}
@ -380,10 +383,6 @@ impl CodeBlock {
| Instruction::LogicalNot
| Instruction::Pos
| Instruction::Neg
| Instruction::Inc
| Instruction::IncPost
| Instruction::Dec
| Instruction::DecPost
| Instruction::GetPropertyByValue
| Instruction::GetPropertyByValuePush
| Instruction::SetPropertyByValue
@ -516,7 +515,8 @@ impl CodeBlock {
| Instruction::Reserved51
| Instruction::Reserved52
| Instruction::Reserved53
| Instruction::Reserved54 => unreachable!("Reserved opcodes are unrechable"),
| Instruction::Reserved54
| Instruction::Reserved55 => unreachable!("Reserved opcodes are unrechable"),
}
}

38
core/engine/src/vm/opcode/get/property.rs

@ -13,13 +13,20 @@ use crate::{
pub(crate) struct GetPropertyByName;
impl GetPropertyByName {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let receiver = context.vm.pop();
let value = context.vm.pop();
fn operation(
dst: u32,
receiver: u32,
value: u32,
index: usize,
context: &mut Context,
) -> JsResult<CompletionType> {
let rp = context.vm.frame().rp;
let receiver = context.vm.stack[(rp + receiver) as usize].clone();
let value = &context.vm.stack[(rp + value) as usize];
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 +48,7 @@ impl GetPropertyByName {
context,
)?;
}
context.vm.push(result);
context.vm.stack[(rp + dst) as usize] = result;
return Ok(CompletionType::Normal);
}
@ -61,7 +68,7 @@ impl GetPropertyByName {
ic.set(shape, slot);
}
context.vm.push(result);
context.vm.stack[(rp + dst) as usize] = result;
Ok(CompletionType::Normal)
}
}
@ -72,18 +79,27 @@ impl Operation for GetPropertyByName {
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>();
Self::operation(context, index as usize)
let dst = context.vm.read::<u8>().into();
let receiver = context.vm.read::<u8>().into();
let value = context.vm.read::<u8>().into();
let index = context.vm.read::<u8>() as usize;
Self::operation(dst, receiver, value, index, context)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst = context.vm.read::<u16>().into();
let receiver = context.vm.read::<u16>().into();
let value = context.vm.read::<u16>().into();
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
Self::operation(dst, receiver, value, index, context)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>();
Self::operation(context, index as usize)
let dst = context.vm.read::<u32>();
let receiver = context.vm.read::<u32>();
let value = context.vm.read::<u32>();
let index = context.vm.read::<u32>() as usize;
Self::operation(dst, receiver, value, index, context)
}
}

30
core/engine/src/vm/opcode/mod.rs

@ -1029,33 +1029,22 @@ generate_opcodes! {
/// Stack: value **=>** (-value)
Neg,
/// Convert value at register `src` to numeric and puts it in register `dst`.
ToNumeric { dst: VaryingOperand, src: VaryingOperand },
/// Unary `++` operator.
///
/// Operands:
///
/// Stack: value **=>** (value + 1)
Inc,
/// Unary postfix `++` operator.
///
/// Operands:
///
/// Stack: value **=>** (ToNumeric(value)), (value + 1)
IncPost,
Inc { dst: VaryingOperand, src: VaryingOperand },
/// Unary `--` operator.
///
/// Operands:
///
/// Stack: value **=>** (value - 1)
Dec,
/// Unary postfix `--` operator.
///
/// Operands:
///
/// Stack: value **=>** (ToNumeric(value)), (value - 1)
DecPost,
Dec { dst: VaryingOperand, src: VaryingOperand },
/// Declare `var` type variable.
///
@ -1149,7 +1138,12 @@ generate_opcodes! {
/// Operands: index: `u32`
///
/// Stack: object, receiver **=>** value
GetPropertyByName { index: VaryingOperand },
GetPropertyByName {
dst: VaryingOperand,
receiver: VaryingOperand,
value: VaryingOperand,
index: VaryingOperand
},
/// Get a property by value from an object an push it on the stack.
///
@ -2235,6 +2229,8 @@ generate_opcodes! {
Reserved53 => Reserved,
/// Reserved [`Opcode`].
Reserved54 => Reserved,
/// Reserved [`Opcode`].
Reserved55 => Reserved,
}
/// Specific opcodes for bindings.

75
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,41 @@ 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, context: &mut Context) -> JsResult<CompletionType> {
let rp = context.vm.frame().rp;
let value = &context.vm.stack[(rp + src) as usize];
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<CompletionType> {
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<CompletionType> {
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 dst: u32 = context.vm.read::<u8>().into();
let src: u32 = context.vm.read::<u8>().into();
Self::operation(src, dst, context)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u16>().into();
let src: u32 = context.vm.read::<u16>().into();
Self::operation(src, dst, context)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u32>();
let src: u32 = context.vm.read::<u32>();
Self::operation(src, dst, context)
}
}

80
core/engine/src/vm/opcode/unary_ops/increment.rs

@ -4,6 +4,43 @@ use crate::{
Context, JsBigInt, JsResult,
};
/// `ToNumeric` implements the Opcode Operation for `Opcode::ToNumeric`
#[derive(Debug, Clone, Copy)]
pub(crate) struct ToNumeric;
impl ToNumeric {
fn operation(src: u32, dst: u32, context: &mut Context) -> JsResult<CompletionType> {
let rp = context.vm.frame().rp;
let value = context.vm.stack[(rp + src) as usize]
.clone()
.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<CompletionType> {
let dst: u32 = context.vm.read::<u8>().into();
let src: u32 = context.vm.read::<u8>().into();
Self::operation(src, dst, context)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u16>().into();
let src: u32 = context.vm.read::<u16>().into();
Self::operation(src, dst, context)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u32>();
let src: u32 = context.vm.read::<u32>();
Self::operation(src, dst, context)
}
}
/// `Inc` implements the Opcode Operation for `Opcode::Inc`
///
/// Operation:
@ -11,25 +48,42 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub(crate) struct Inc;
impl Inc {
#[allow(clippy::unnecessary_wraps)]
fn operation(src: u32, dst: u32, context: &mut Context) -> JsResult<CompletionType> {
let rp = context.vm.frame().rp;
let value = &context.vm.stack[(rp + src) as usize];
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 Inc {
const NAME: &'static str = "Inc";
const INSTRUCTION: &'static str = "INST - Inc";
const COST: u8 = 3;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
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 dst: u32 = context.vm.read::<u8>().into();
let src: u32 = context.vm.read::<u8>().into();
Self::operation(src, dst, context)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u16>().into();
let src: u32 = context.vm.read::<u16>().into();
Self::operation(src, dst, context)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let dst: u32 = context.vm.read::<u32>();
let src: u32 = context.vm.read::<u32>();
Self::operation(src, dst, context)
}
}

Loading…
Cancel
Save