From 842ad083b528d1366710de90588ebd0a29a95728 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Thu, 29 Jun 2023 18:39:02 +1000 Subject: [PATCH] wip --- Cargo.lock | 1 + valuescript_compiler/Cargo.toml | 1 + valuescript_compiler/src/lib.rs | 1 + valuescript_compiler/src/optimization/mod.rs | 2 + .../src/optimization/simplify.rs | 335 +++++++++++++++++- .../src/optimization/try_to_val.rs | 52 +++ .../src/optimization/try_to_value.rs | 66 ++++ valuescript_vm/src/lib.rs | 1 + valuescript_wasm/src/lib.rs | 56 +-- 9 files changed, 452 insertions(+), 63 deletions(-) create mode 100644 valuescript_compiler/src/optimization/try_to_val.rs create mode 100644 valuescript_compiler/src/optimization/try_to_value.rs diff --git a/Cargo.lock b/Cargo.lock index c4807a9..358adb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2243,6 +2243,7 @@ dependencies = [ "swc_ecma_ast", "swc_ecma_parser", "valuescript_common", + "valuescript_vm", ] [[package]] diff --git a/valuescript_compiler/Cargo.toml b/valuescript_compiler/Cargo.toml index ec2ecfa..1805c32 100644 --- a/valuescript_compiler/Cargo.toml +++ b/valuescript_compiler/Cargo.toml @@ -18,3 +18,4 @@ swc_ecma_ast = "0.76.0" queues = "1.0.2" valuescript_common = { path = "../valuescript_common" } +valuescript_vm = { path = "../valuescript_vm" } diff --git a/valuescript_compiler/src/lib.rs b/valuescript_compiler/src/lib.rs index 7306ab5..7dd38f1 100644 --- a/valuescript_compiler/src/lib.rs +++ b/valuescript_compiler/src/lib.rs @@ -30,5 +30,6 @@ pub use gather_modules::gather_modules; pub use link_module::link_module; pub use module_compiler::compile_module; pub use module_compiler::CompilerOutput; +pub use optimization::try_to_val::TryToVal; pub use resolve_path::resolve_path; pub use resolve_path::ResolvedPath; diff --git a/valuescript_compiler/src/optimization/mod.rs b/valuescript_compiler/src/optimization/mod.rs index ce8727d..6b0b028 100644 --- a/valuescript_compiler/src/optimization/mod.rs +++ b/valuescript_compiler/src/optimization/mod.rs @@ -3,5 +3,7 @@ mod extract_constants; mod optimize; mod shake_tree; mod simplify; +pub mod try_to_val; +pub mod try_to_value; pub use optimize::optimize; diff --git a/valuescript_compiler/src/optimization/simplify.rs b/valuescript_compiler/src/optimization/simplify.rs index de75d20..fbfe300 100644 --- a/valuescript_compiler/src/optimization/simplify.rs +++ b/valuescript_compiler/src/optimization/simplify.rs @@ -1,4 +1,14 @@ -use crate::asm::{DefinitionContent, FnLine, Function, Instruction, Module}; +use std::collections::{HashMap, HashSet}; + +use num_bigint::BigInt; +use valuescript_vm::{operations, vs_value::Val}; + +use crate::{ + asm::{DefinitionContent, FnLine, Function, Instruction, Module, Number, Register, Value}, + TryToVal, +}; + +use super::try_to_value::TryToValue; pub fn simplify(module: &mut Module) { for defn in &mut module.definitions { @@ -14,7 +24,7 @@ pub fn simplify(module: &mut Module) { #[derive(Default)] struct FnState { mutable_this_established: bool, - // registers: HashMap, + registers: HashMap, } impl FnState { @@ -24,28 +34,333 @@ impl FnState { fn simplify_line(&self, line: &mut FnLine) { match line { - FnLine::Instruction(instr) => { - if let Instruction::RequireMutableThis = instr { + FnLine::Instruction(instr) => match instr { + Instruction::End => {} + Instruction::Mov(a1, _) => { + self.simplify_arg(a1); + } + + Instruction::OpInc(_) => {} + Instruction::OpDec(_) => {} + + Instruction::OpNot(a1, _) + | Instruction::OpBitNot(a1, _) + | Instruction::TypeOf(a1, _) + | Instruction::UnaryPlus(a1, _) + | Instruction::UnaryMinus(a1, _) + | Instruction::Throw(a1) + | Instruction::Import(a1, _) + | Instruction::ImportStar(a1, _) + | Instruction::Cat(a1, _) + | Instruction::Yield(a1, _) + | Instruction::YieldStar(a1, _) => { + self.simplify_arg(a1); + } + + Instruction::OpPlus(a1, a2, _) + | Instruction::OpMinus(a1, a2, _) + | Instruction::OpMul(a1, a2, _) + | Instruction::OpDiv(a1, a2, _) + | Instruction::OpMod(a1, a2, _) + | Instruction::OpExp(a1, a2, _) + | Instruction::OpEq(a1, a2, _) + | Instruction::OpNe(a1, a2, _) + | Instruction::OpTripleEq(a1, a2, _) + | Instruction::OpTripleNe(a1, a2, _) + | Instruction::OpAnd(a1, a2, _) + | Instruction::OpOr(a1, a2, _) + | Instruction::OpLess(a1, a2, _) + | Instruction::OpLessEq(a1, a2, _) + | Instruction::OpGreater(a1, a2, _) + | Instruction::OpGreaterEq(a1, a2, _) + | Instruction::OpNullishCoalesce(a1, a2, _) + | Instruction::OpOptionalChain(a1, a2, _) + | Instruction::OpBitAnd(a1, a2, _) + | Instruction::OpBitOr(a1, a2, _) + | Instruction::OpBitXor(a1, a2, _) + | Instruction::OpLeftShift(a1, a2, _) + | Instruction::OpRightShift(a1, a2, _) + | Instruction::OpRightShiftUnsigned(a1, a2, _) + | Instruction::InstanceOf(a1, a2, _) + | Instruction::In(a1, a2, _) + | Instruction::Call(a1, a2, _) + | Instruction::Bind(a1, a2, _) + | Instruction::Sub(a1, a2, _) + | Instruction::SubMov(a1, a2, _) + | Instruction::New(a1, a2, _) => { + self.simplify_arg(a1); + self.simplify_arg(a2); + } + + Instruction::Apply(a1, _this, a3, _) => { + self.simplify_arg(a1); + self.simplify_arg(a3); + } + + Instruction::SubCall(_this, a2, a3, _) | Instruction::ThisSubCall(_this, a2, a3, _) => { + self.simplify_arg(a2); + self.simplify_arg(a3); + } + + Instruction::ConstSubCall(a1, a2, a3, _) => { + self.simplify_arg(a1); + self.simplify_arg(a2); + self.simplify_arg(a3); + } + + Instruction::JmpIf(a1, _) => { + self.simplify_arg(a1); + } + + Instruction::Jmp(_) => {} + Instruction::SetCatch(_, _) => {} + Instruction::UnsetCatch => {} + Instruction::RequireMutableThis => { if self.mutable_this_established { *line = FnLine::Comment(line.to_string()); } } - } + Instruction::Next(_, _) => {} + Instruction::UnpackIterRes(_, _, _) => {} + }, FnLine::Label(..) | FnLine::Empty | FnLine::Comment(..) => {} } } - fn apply_line(&mut self, line: &FnLine) { - match line { - FnLine::Instruction(instr) => { - if let Instruction::RequireMutableThis = instr { - self.mutable_this_established = true; + fn simplify_arg(&self, arg: &mut Value) { + arg.visit_values_mut(&mut |value| { + if let Value::Register(reg) = value { + if let Some(new_value) = self.registers.get(reg) { + *value = new_value.clone(); } } + }); + } + + fn apply_line(&mut self, line: &FnLine) { + match line { + FnLine::Instruction(instr) => match instr { + Instruction::End => {} + Instruction::Mov(a1, dst) => { + self.set_register(dst, Some(a1.clone())); + } + + Instruction::OpInc(reg) => { + // TODO: Use apply_binary_op? + + let new_value = match self.registers.get(reg) { + Some(Value::Number(Number(x))) => Some(Value::Number(Number(x + 1.0))), + Some(Value::BigInt(x)) => Some(Value::BigInt(x + BigInt::from(1))), + Some(_) => None, + None => return, + }; + + self.set_register(reg, new_value); + } + Instruction::OpDec(reg) => { + let new_value = match self.registers.get(reg) { + Some(Value::Number(Number(x))) => Some(Value::Number(Number(x - 1.0))), + Some(Value::BigInt(x)) => Some(Value::BigInt(x - BigInt::from(1))), + Some(_) => None, + None => return, + }; + + self.set_register(reg, new_value); + } + + Instruction::OpNot(a1, dst) => self.apply_unary_op(a1, dst, operations::op_not), + Instruction::OpBitNot(a1, dst) => self.apply_unary_op(a1, dst, operations::op_bit_not), + Instruction::TypeOf(a1, dst) => self.apply_unary_op(a1, dst, operations::op_typeof), + Instruction::UnaryPlus(a1, dst) => self.apply_unary_op(a1, dst, operations::op_unary_plus), + Instruction::UnaryMinus(a1, dst) => { + self.apply_unary_op(a1, dst, operations::op_unary_minus) + } + Instruction::Import(_a1, dst) + | Instruction::ImportStar(_a1, dst) + | Instruction::Cat(_a1, dst) => { + // TODO: cat + self.set_register(dst, None); + } + + Instruction::Yield(_a1, dst) | Instruction::YieldStar(_a1, dst) => { + self.set_register(dst, None) + } + + Instruction::Throw(_a1) => {} + + Instruction::OpPlus(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_plus), + Instruction::OpMinus(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_minus) + } + Instruction::OpMul(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_mul), + Instruction::OpDiv(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_div), + Instruction::OpMod(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_mod), + Instruction::OpExp(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_exp), + Instruction::OpEq(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_eq), + Instruction::OpNe(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_ne), + Instruction::OpTripleEq(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_triple_eq) + } + Instruction::OpTripleNe(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_triple_ne) + } + Instruction::OpAnd(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_and), + Instruction::OpOr(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_or), + Instruction::OpLess(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_less), + Instruction::OpLessEq(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_less_eq) + } + Instruction::OpGreater(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_greater) + } + Instruction::OpGreaterEq(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_greater_eq) + } + Instruction::OpNullishCoalesce(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_nullish_coalesce) + } + Instruction::OpOptionalChain(_a1, _a2, dst) => { + // self.apply_binary_op(a1, a2, dst, operations::op_optional_chain) + // TODO: op_optional_chain takes mut lhs to optimize, but breaks this pattern + self.set_register(dst, None); + } + Instruction::OpBitAnd(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_bit_and) + } + Instruction::OpBitOr(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_bit_or) + } + Instruction::OpBitXor(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_bit_xor) + } + Instruction::OpLeftShift(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_left_shift) + } + Instruction::OpRightShift(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_right_shift) + } + Instruction::OpRightShiftUnsigned(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_right_shift_unsigned) + } + Instruction::InstanceOf(a1, a2, dst) => { + self.apply_binary_op(a1, a2, dst, operations::op_instance_of) + } + Instruction::In(a1, a2, dst) => self.apply_binary_op(a1, a2, dst, operations::op_in), + + Instruction::Call(_a1, _a2, dst) + | Instruction::Bind(_a1, _a2, dst) + | Instruction::Sub(_a1, _a2, dst) + | Instruction::SubMov(_a1, _a2, dst) + | Instruction::New(_a1, _a2, dst) => { + self.set_register(dst, None); + } + + Instruction::Apply(_a1, _this, _a3, dst) => self.set_register(dst, None), + + Instruction::SubCall(_this, _a2, _a3, dst) + | Instruction::ThisSubCall(_this, _a2, _a3, dst) => self.set_register(dst, None), + + Instruction::ConstSubCall(_a1, _a2, _a3, dst) => self.set_register(dst, None), + + Instruction::JmpIf(_a1, _) => {} + + Instruction::Jmp(_) => {} + Instruction::SetCatch(_, _) => {} + Instruction::UnsetCatch => {} + Instruction::RequireMutableThis => { + self.mutable_this_established = true; + } + Instruction::Next(iter, dst) => { + self.set_register(iter, None); + self.set_register(dst, None); + } + Instruction::UnpackIterRes(iter_res, value_reg, done) => { + self.set_register(iter_res, None); + self.set_register(value_reg, None); + self.set_register(done, None); + } + }, FnLine::Label(..) => self.clear(), FnLine::Empty | FnLine::Comment(..) => {} } } + + fn set_register(&mut self, reg: &Register, value: Option) { + let mut registers_to_clear = HashSet::::new(); + + for (k, v) in &mut self.registers { + v.visit_values_mut(&mut |value| { + if let Value::Register(reg_value) = value { + if reg_value.name == reg.name { + registers_to_clear.insert(k.clone()); + } + } + }); + } + + for reg_to_clear in registers_to_clear { + self.registers.remove(®_to_clear); + } + + match value { + Some(value) => self.registers.insert(reg.clone(), value), + None => self.registers.remove(reg), + }; + } + + fn apply_unary_op(&mut self, arg: &Value, dst: &Register, op: fn(input: &Val) -> Val) { + match self.apply_unary_op_impl(arg, dst, op) { + Ok(_) => {} + Err(_) => { + self.set_register(dst, None); + } + } + } + + fn apply_unary_op_impl( + &mut self, + arg: &Value, + dst: &Register, + op: fn(input: &Val) -> Val, + ) -> Result<(), Val> { + let arg = arg.clone().try_to_val()?; + let value = op(&arg).try_to_value()?; + + self.set_register(dst, Some(value)); + + Ok(()) + } + + fn apply_binary_op( + &mut self, + left: &Value, + right: &Value, + dst: &Register, + op: fn(left: &Val, right: &Val) -> Result, + ) { + match self.apply_binary_op_impl(left, right, dst, op) { + Ok(_) => {} + Err(_) => { + self.set_register(dst, None); + } + } + } + + fn apply_binary_op_impl( + &mut self, + left: &Value, + right: &Value, + dst: &Register, + op: fn(left: &Val, right: &Val) -> Result, + ) -> Result<(), Val> { + let left = left.clone().try_to_val()?; + let right = right.clone().try_to_val()?; + let value = op(&left, &right)?.try_to_value()?; + + self.set_register(dst, Some(value)); + + Ok(()) + } } fn simplify_fn(mut state: FnState, fn_: &mut Function) { diff --git a/valuescript_compiler/src/optimization/try_to_val.rs b/valuescript_compiler/src/optimization/try_to_val.rs new file mode 100644 index 0000000..0848efb --- /dev/null +++ b/valuescript_compiler/src/optimization/try_to_val.rs @@ -0,0 +1,52 @@ +use std::collections::BTreeMap; + +use valuescript_vm::{ + vs_object::VsObject, + vs_value::{ToVal, Val}, +}; + +use crate::asm::{Number, Value}; + +pub trait TryToVal { + fn try_to_val(self) -> Result; +} + +impl TryToVal for Value { + fn try_to_val(self) -> Result { + Ok(match self { + Value::Undefined => Val::Undefined, + Value::Null => Val::Null, + Value::Bool(b) => b.to_val(), + Value::Number(Number(n)) => n.to_val(), + Value::BigInt(n) => n.to_val(), + Value::String(s) => s.to_val(), + Value::Array(arr) => { + let mut result = Vec::::new(); + + for value in arr.values { + result.push(value.try_to_val()?); + } + + result.to_val() + } + Value::Object(obj) => { + let mut string_map = BTreeMap::::new(); + + for (key, value) in obj.properties { + string_map.insert(key.try_to_val()?.to_string(), value.try_to_val()?); + } + + VsObject { + string_map, + symbol_map: Default::default(), + prototype: None, + } + .to_val() + } + + Value::Void | Value::Register(..) | Value::Pointer(..) | Value::Builtin(..) => { + return Err("Invalid argument".to_val()); + } + }) + } +} diff --git a/valuescript_compiler/src/optimization/try_to_value.rs b/valuescript_compiler/src/optimization/try_to_value.rs new file mode 100644 index 0000000..81a0733 --- /dev/null +++ b/valuescript_compiler/src/optimization/try_to_value.rs @@ -0,0 +1,66 @@ +use valuescript_vm::{ + vs_value::{ToVal, Val}, + VsSymbol, +}; + +use crate::asm::{Array, Builtin, Number, Object, Value}; + +pub trait TryToValue { + fn try_to_value(&self) -> Result; +} + +impl TryToValue for Val { + fn try_to_value(&self) -> Result { + Ok(match self { + Val::Void => Value::Undefined, + Val::Undefined => Value::Undefined, + Val::Null => Value::Null, + Val::Bool(b) => Value::Bool(*b), + Val::Number(n) => Value::Number(Number(*n)), + Val::BigInt(n) => Value::BigInt(n.clone()), + Val::Symbol(sym) => match sym { + VsSymbol::ITERATOR => Value::Builtin(Builtin { + name: "SymbolIterator".to_string(), + }), + }, + Val::String(s) => Value::String(s.to_string()), + Val::Array(arr) => { + let mut values = Vec::::new(); + + for value in &arr.elements { + values.push(value.try_to_value()?); + } + + Value::Array(Box::new(Array { values })) + } + Val::Object(obj) => { + if obj.prototype.is_some() { + return Err("can't (yet?) convert object with prototype to Value".to_val()); + } + + let mut properties = Vec::<(Value, Value)>::new(); + + for (k, v) in &obj.symbol_map { + let k = match k { + VsSymbol::ITERATOR => Value::Builtin(Builtin { + name: "SymbolIterator".to_string(), + }), + }; + + properties.push((k, v.try_to_value()?)); + } + + for (k, v) in &obj.string_map { + properties.push((Value::String(k.clone()), v.try_to_value()?)); + } + + Value::Object(Box::new(Object { properties })) + } + Val::Function(..) + | Val::Class(..) + | Val::Static(..) + | Val::Dynamic(..) + | Val::CopyCounter(..) => return Err("TODO: support more of these".to_val()), + }) + } +} diff --git a/valuescript_vm/src/lib.rs b/valuescript_vm/src/lib.rs index 8678ffc..7a622c2 100644 --- a/valuescript_vm/src/lib.rs +++ b/valuescript_vm/src/lib.rs @@ -29,4 +29,5 @@ pub mod vs_value; pub use bytecode::Bytecode; pub use virtual_machine::VirtualMachine; +pub use vs_symbol::VsSymbol; pub use vs_value::{LoadFunctionResult, ValTrait}; diff --git a/valuescript_wasm/src/lib.rs b/valuescript_wasm/src/lib.rs index 696e8c1..2756f36 100644 --- a/valuescript_wasm/src/lib.rs +++ b/valuescript_wasm/src/lib.rs @@ -1,18 +1,12 @@ -use std::{ - collections::{BTreeMap, HashMap}, - rc::Rc, -}; +use std::{collections::HashMap, rc::Rc}; use wasm_bindgen::prelude::*; use valuescript_compiler::{ - asm::{Number, Value}, - assemble, - assembly_parser::AssemblyParser, - compile as compile_internal, CompileResult, Diagnostic, ResolvedPath, + asm::Value, assemble, assembly_parser::AssemblyParser, compile as compile_internal, + CompileResult, Diagnostic, ResolvedPath, TryToVal, }; use valuescript_vm::{ - vs_object::VsObject, vs_value::{ToVal, Val}, Bytecode, LoadFunctionResult, ValTrait, VirtualMachine, }; @@ -175,47 +169,3 @@ fn parse_args(args: &str) -> Result, Val> { Ok(result) } - -pub trait TryToVal { - fn try_to_val(self) -> Result; -} - -impl TryToVal for Value { - fn try_to_val(self) -> Result { - Ok(match self { - Value::Undefined => Val::Undefined, - Value::Null => Val::Null, - Value::Bool(b) => b.to_val(), - Value::Number(Number(n)) => n.to_val(), - Value::BigInt(n) => n.to_val(), - Value::String(s) => s.to_val(), - Value::Array(arr) => { - let mut result = Vec::::new(); - - for value in arr.values { - result.push(value.try_to_val()?); - } - - result.to_val() - } - Value::Object(obj) => { - let mut string_map = BTreeMap::::new(); - - for (key, value) in obj.properties { - string_map.insert(key.try_to_val()?.to_string(), value.try_to_val()?); - } - - VsObject { - string_map, - symbol_map: Default::default(), - prototype: None, - } - .to_val() - } - - Value::Void | Value::Register(..) | Value::Pointer(..) | Value::Builtin(..) => { - return Err("Invalid argument".to_val()); - } - }) - } -}