From 6ee59518cf021a8f3be073cf77ada2115bb1fbe4 Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 30 Jun 2023 14:12:27 +1000 Subject: [PATCH] Take registers and remove unused movs --- inputs/failing/copyCounting/arrayRef.ts | 2 +- inputs/failing/copyCounting/arraySwap.ts | 2 +- .../copyCounting/return.ts | 3 +- .../copyCounting/subscript.ts | 3 +- .../src/expression_compiler.rs | 8 +- .../src/optimization/simplify.rs | 152 +++++++++++++++++- 6 files changed, 157 insertions(+), 13 deletions(-) rename inputs/{failing => passing}/copyCounting/return.ts (83%) rename inputs/{failing => passing}/copyCounting/subscript.ts (79%) diff --git a/inputs/failing/copyCounting/arrayRef.ts b/inputs/failing/copyCounting/arrayRef.ts index d5ca2ff..39041a7 100644 --- a/inputs/failing/copyCounting/arrayRef.ts +++ b/inputs/failing/copyCounting/arrayRef.ts @@ -1,4 +1,4 @@ -//! test_output(2) +//! test_output(1) // Should be: 0 /// diff --git a/inputs/failing/copyCounting/arraySwap.ts b/inputs/failing/copyCounting/arraySwap.ts index a291af3..8abe734 100644 --- a/inputs/failing/copyCounting/arraySwap.ts +++ b/inputs/failing/copyCounting/arraySwap.ts @@ -1,4 +1,4 @@ -//! test_output(4) +//! test_output(2) // Should be: 0 /// diff --git a/inputs/failing/copyCounting/return.ts b/inputs/passing/copyCounting/return.ts similarity index 83% rename from inputs/failing/copyCounting/return.ts rename to inputs/passing/copyCounting/return.ts index bce5232..cf1ca50 100644 --- a/inputs/failing/copyCounting/return.ts +++ b/inputs/passing/copyCounting/return.ts @@ -1,5 +1,4 @@ -//! test_output(1) -// Should be: 0 +//! test_output(0) /// diff --git a/inputs/failing/copyCounting/subscript.ts b/inputs/passing/copyCounting/subscript.ts similarity index 79% rename from inputs/failing/copyCounting/subscript.ts rename to inputs/passing/copyCounting/subscript.ts index ca31b43..f356140 100644 --- a/inputs/failing/copyCounting/subscript.ts +++ b/inputs/passing/copyCounting/subscript.ts @@ -1,5 +1,4 @@ -//! test_output(1) -// Should be: 0 +//! test_output(0) /// diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index 1366ae5..2d7552d 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -1586,10 +1586,6 @@ impl<'a> ExpressionCompiler<'a> { }; } - for reg in sub_nested_registers { - self.fnc.release_reg(®); - } - if !current.is_empty() { segments.push(Value::Array(Box::new(Array { values: current }))); } @@ -1610,6 +1606,10 @@ impl<'a> ExpressionCompiler<'a> { res_reg.clone(), )); + for reg in sub_nested_registers { + self.fnc.release_reg(®); + } + CompiledExpression::new(Value::Register(res_reg), nested_registers) } } diff --git a/valuescript_compiler/src/optimization/simplify.rs b/valuescript_compiler/src/optimization/simplify.rs index d047a78..ce8630f 100644 --- a/valuescript_compiler/src/optimization/simplify.rs +++ b/valuescript_compiler/src/optimization/simplify.rs @@ -208,8 +208,8 @@ impl FnState { }, FnLine::Label(..) => self.clear(), FnLine::Empty | FnLine::Comment(..) => {} - FnLine::Release(_reg) => { - // TODO + FnLine::Release(reg) => { + self.set_register(reg, None); } } } @@ -290,11 +290,157 @@ impl FnState { Ok(()) } + + fn handle_releases(&self, body: &mut Vec, i: usize) { + let released_reg = match &body[i] { + FnLine::Instruction(_instr) => { + // TODO + return; + } + FnLine::Release(released_reg) => released_reg.clone(), + FnLine::Label(_) | FnLine::Empty | FnLine::Comment(_) => return, + }; + + // Search backwards to find where this register was last written. If a jump instruction occurs, + // then we don't know for sure whether the release point will be hit, and we can't apply our + // analysis. + let mut j = i; + while j > 0 { + j -= 1; + + let instr = match &mut body[j] { + FnLine::Instruction(instr) => instr, + _ => continue, + }; + + if is_jmp_instr(instr) { + return; + } + + let mut write_found = false; + + instr.visit_registers_mut_rev(&mut |rvm| { + if rvm.write && rvm.register.name == released_reg.name { + write_found = true; + } + }); + + if write_found { + break; + } + } + + // Now that we've established that the last write always hits the release point, find the last + // read and use .take() instead of copying. Also, if this .take() never occurs, it means the + // value was never used, and comment out the instruction that writes the value, if possible. + let mut j = i; + let mut taken = false; + while j > 0 { + j -= 1; + + let instr = match &mut body[j] { + FnLine::Instruction(instr) => instr, + _ => continue, + }; + + let mut write_found = false; + + if !taken { + instr.visit_registers_mut_rev(&mut |rvm| { + if !taken && !rvm.write && rvm.register.name == released_reg.name { + *rvm.register = rvm.register.take(); + taken = true; + } + + if rvm.write && rvm.register.name == released_reg.name { + write_found = true; + } + }); + } + + 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; + } + } + } } fn simplify_fn(mut state: FnState, fn_: &mut Function) { - for line in &mut fn_.body { + for i in 0..fn_.body.len() { + let line = &mut fn_.body[i]; + state.simplify_line(line); state.apply_line(line); + + state.handle_releases(&mut fn_.body, i); + } +} + +fn is_jmp_instr(instr: &Instruction) -> bool { + match instr { + Instruction::Jmp(..) | Instruction::JmpIf(..) => true, + Instruction::End + | Instruction::Mov(..) + | Instruction::OpInc(..) + | Instruction::OpDec(..) + | Instruction::OpPlus(..) + | Instruction::OpMinus(..) + | Instruction::OpMul(..) + | Instruction::OpDiv(..) + | Instruction::OpMod(..) + | Instruction::OpExp(..) + | Instruction::OpEq(..) + | Instruction::OpNe(..) + | Instruction::OpTripleEq(..) + | Instruction::OpTripleNe(..) + | Instruction::OpAnd(..) + | Instruction::OpOr(..) + | Instruction::OpNot(..) + | Instruction::OpLess(..) + | Instruction::OpLessEq(..) + | Instruction::OpGreater(..) + | Instruction::OpGreaterEq(..) + | Instruction::OpNullishCoalesce(..) + | Instruction::OpOptionalChain(..) + | Instruction::OpBitAnd(..) + | Instruction::OpBitOr(..) + | Instruction::OpBitNot(..) + | Instruction::OpBitXor(..) + | Instruction::OpLeftShift(..) + | Instruction::OpRightShift(..) + | Instruction::OpRightShiftUnsigned(..) + | Instruction::TypeOf(..) + | Instruction::InstanceOf(..) + | Instruction::In(..) + | Instruction::Call(..) + | Instruction::Apply(..) + | Instruction::Bind(..) + | Instruction::Sub(..) + | Instruction::SubMov(..) + | Instruction::SubCall(..) + | Instruction::UnaryPlus(..) + | Instruction::UnaryMinus(..) + | Instruction::New(..) + | Instruction::Throw(..) + | Instruction::Import(..) + | Instruction::ImportStar(..) + | Instruction::SetCatch(..) + | Instruction::UnsetCatch + | Instruction::ConstSubCall(..) + | Instruction::RequireMutableThis + | Instruction::ThisSubCall(..) + | Instruction::Next(..) + | Instruction::UnpackIterRes(..) + | Instruction::Cat(..) + | Instruction::Yield(..) + | Instruction::YieldStar(..) => false, } }