From 264b8dee4088b6a7f9cd20068e5fb8ea626dba46 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 30 Jun 2023 17:19:28 +1000 Subject: [PATCH] remove_noops --- .../copyCounting/arrayRef.ts | 3 +- .../copyCounting/iterator.ts | 0 valuescript_compiler/src/instruction.rs | 8 +- valuescript_compiler/src/optimization/mod.rs | 1 + .../src/optimization/optimize.rs | 2 + .../src/optimization/remove_noops.rs | 74 +++++++++++++++++++ .../src/optimization/simplify.rs | 20 ++--- 7 files changed, 95 insertions(+), 13 deletions(-) rename inputs/{failing => passing}/copyCounting/arrayRef.ts (92%) rename inputs/{failing => passing}/copyCounting/iterator.ts (100%) create mode 100644 valuescript_compiler/src/optimization/remove_noops.rs diff --git a/inputs/failing/copyCounting/arrayRef.ts b/inputs/passing/copyCounting/arrayRef.ts similarity index 92% rename from inputs/failing/copyCounting/arrayRef.ts rename to inputs/passing/copyCounting/arrayRef.ts index 39041a7..4860fb4 100644 --- a/inputs/failing/copyCounting/arrayRef.ts +++ b/inputs/passing/copyCounting/arrayRef.ts @@ -1,5 +1,4 @@ -//! test_output(1) -// Should be: 0 +//! test_output(0) /// diff --git a/inputs/failing/copyCounting/iterator.ts b/inputs/passing/copyCounting/iterator.ts similarity index 100% rename from inputs/failing/copyCounting/iterator.ts rename to inputs/passing/copyCounting/iterator.ts diff --git a/valuescript_compiler/src/instruction.rs b/valuescript_compiler/src/instruction.rs index 91fe2f5..799271e 100644 --- a/valuescript_compiler/src/instruction.rs +++ b/valuescript_compiler/src/instruction.rs @@ -326,7 +326,13 @@ impl Instruction { UnpackIterRes(iter_res, value_dst, done_dst) => { visit(RegisterVisitMut::write(done_dst)); visit(RegisterVisitMut::write(value_dst)); - visit(RegisterVisitMut::read_and_write(iter_res)); + + // This does a write in the sense that the register is consumed. However, the new value + // being written is not supposed to matter, `unpack_iter_res` should only be used once and + // the emptiness of the register isn't supposed to be then consumed the way a regular write + // would. In other words, well-constructed programs should be equivalent whether the VM + // decides to consume this memory or not. + visit(RegisterVisitMut::read(iter_res)); } UnsetCatch | RequireMutableThis => {} diff --git a/valuescript_compiler/src/optimization/mod.rs b/valuescript_compiler/src/optimization/mod.rs index 4ee8c23..1edf5c9 100644 --- a/valuescript_compiler/src/optimization/mod.rs +++ b/valuescript_compiler/src/optimization/mod.rs @@ -2,6 +2,7 @@ mod collapse_pointers_of_pointers; mod extract_constants; mod optimize; mod remove_meta_lines; +mod remove_noops; mod shake_tree; mod simplify; pub mod try_to_val; diff --git a/valuescript_compiler/src/optimization/optimize.rs b/valuescript_compiler/src/optimization/optimize.rs index b440a2e..db604e8 100644 --- a/valuescript_compiler/src/optimization/optimize.rs +++ b/valuescript_compiler/src/optimization/optimize.rs @@ -4,6 +4,7 @@ use crate::name_allocator::NameAllocator; use super::collapse_pointers_of_pointers::collapse_pointers_of_pointers; use super::extract_constants::extract_constants; use super::remove_meta_lines::remove_meta_lines; +use super::remove_noops::remove_noops; use super::shake_tree::shake_tree; use super::simplify::simplify; @@ -12,5 +13,6 @@ pub fn optimize(module: &mut Module, pointer_allocator: &mut NameAllocator) { extract_constants(module, pointer_allocator); shake_tree(module); simplify(module); + remove_noops(module); remove_meta_lines(module); } diff --git a/valuescript_compiler/src/optimization/remove_noops.rs b/valuescript_compiler/src/optimization/remove_noops.rs new file mode 100644 index 0000000..97dac5a --- /dev/null +++ b/valuescript_compiler/src/optimization/remove_noops.rs @@ -0,0 +1,74 @@ +use std::mem::take; + +use crate::{ + asm::{DefinitionContent, FnLine, Module, Value}, + instruction::Instruction, +}; + +pub fn remove_noops(module: &mut Module) { + for defn in &mut module.definitions { + if let DefinitionContent::Function(fn_) = &mut defn.content { + for line in take(&mut fn_.body) { + if let FnLine::Instruction(instr) = &line { + if is_noop(instr) { + continue; + } + } + + fn_.body.push(line); + } + } + } +} + +fn is_noop(instr: &Instruction) -> bool { + use Instruction::*; + + match instr { + End | OpInc(..) | OpDec(..) | Call(..) | Apply(..) | SubCall(..) | Jmp(..) | New(..) + | Throw(..) | SetCatch(..) | UnsetCatch | ConstSubCall(..) | RequireMutableThis + | ThisSubCall(..) | Next(..) | Yield(..) | YieldStar(..) => false, + + Mov(_, dst) + | OpPlus(_, _, dst) + | OpMinus(_, _, dst) + | OpMul(_, _, dst) + | OpDiv(_, _, dst) + | OpMod(_, _, dst) + | OpExp(_, _, dst) + | OpEq(_, _, dst) + | OpNe(_, _, dst) + | OpTripleEq(_, _, dst) + | OpTripleNe(_, _, dst) + | OpAnd(_, _, dst) + | OpOr(_, _, dst) + | OpNot(_, dst) + | OpLess(_, _, dst) + | OpLessEq(_, _, dst) + | OpGreater(_, _, dst) + | OpGreaterEq(_, _, dst) + | OpNullishCoalesce(_, _, dst) + | OpOptionalChain(_, _, dst) + | OpBitAnd(_, _, dst) + | OpBitOr(_, _, dst) + | OpBitNot(_, dst) + | OpBitXor(_, _, dst) + | OpLeftShift(_, _, dst) + | OpRightShift(_, _, dst) + | OpRightShiftUnsigned(_, _, dst) + | TypeOf(_, dst) + | InstanceOf(_, _, dst) + | In(_, _, dst) + | Bind(_, _, dst) + | Sub(_, _, dst) + | SubMov(_, _, dst) + | UnaryPlus(_, dst) + | UnaryMinus(_, dst) + | Import(_, dst) + | ImportStar(_, dst) + | Cat(_, dst) => dst.is_ignore(), + + JmpIf(cond, _) => *cond == Value::Bool(false), + UnpackIterRes(_, value_dst, done_dst) => value_dst.is_ignore() && done_dst.is_ignore(), + } +} diff --git a/valuescript_compiler/src/optimization/simplify.rs b/valuescript_compiler/src/optimization/simplify.rs index ce8630f..5741d63 100644 --- a/valuescript_compiler/src/optimization/simplify.rs +++ b/valuescript_compiler/src/optimization/simplify.rs @@ -347,26 +347,26 @@ impl FnState { if !taken { instr.visit_registers_mut_rev(&mut |rvm| { - if !taken && !rvm.write && rvm.register.name == released_reg.name { + if rvm.register.name != released_reg.name { + return; + } + + if !taken && !rvm.write { *rvm.register = rvm.register.take(); taken = true; } - if rvm.write && rvm.register.name == released_reg.name { + if !write_found && rvm.write { write_found = true; + + if !rvm.read && !taken { + *rvm.register = Register::ignore(); + } } }); } if write_found { - if !taken { - // TODO: Support removal of more instructions. - if let Instruction::Mov(..) = instr { - let line = &mut body[j]; - *line = FnLine::Comment(line.to_string()); - } - } - break; } }