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,
}
}