From f985698724bb5218a3af4bac6d0c8a1f0c9a559c Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Sat, 1 Jul 2023 08:24:42 +1000 Subject: [PATCH] Release registers upon return --- inputs/failing/copyCounting/externalMethod.ts | 8 ++++++-- .../failing/exceptions/finallyWithoutThrow.ts | 17 +++++++++++++++++ inputs/failing/exceptions/throwTypeError.ts | 11 +++++++++++ .../passing/exceptions/finallyWithoutThrow.ts | 12 ------------ inputs/passing/exceptions/throwTypeError.ts | 6 ------ valuescript_compiler/src/asm.rs | 4 ++++ valuescript_compiler/src/function_compiler.rs | 11 +++++++++++ valuescript_compiler/src/name_allocator.rs | 14 ++++++++++++-- 8 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 inputs/failing/exceptions/finallyWithoutThrow.ts create mode 100644 inputs/failing/exceptions/throwTypeError.ts delete mode 100644 inputs/passing/exceptions/finallyWithoutThrow.ts delete mode 100644 inputs/passing/exceptions/throwTypeError.ts diff --git a/inputs/failing/copyCounting/externalMethod.ts b/inputs/failing/copyCounting/externalMethod.ts index 2a6fa429..34235118 100644 --- a/inputs/failing/copyCounting/externalMethod.ts +++ b/inputs/failing/copyCounting/externalMethod.ts @@ -1,4 +1,4 @@ -//! test_output(1) +//! test_output(2) // Should be: 0 /// @@ -16,7 +16,7 @@ function measure(doPush: boolean) { arr = push(arr, "y"); } - return x.count; + return len(arr) + x.count; } function push(x: T[], value: T) { @@ -27,3 +27,7 @@ function push(x: T[], value: T) { function echo(x: T) { return x; } + +function len(arr: T[]) { + return arr.length; +} diff --git a/inputs/failing/exceptions/finallyWithoutThrow.ts b/inputs/failing/exceptions/finallyWithoutThrow.ts new file mode 100644 index 00000000..8d74c6a5 --- /dev/null +++ b/inputs/failing/exceptions/finallyWithoutThrow.ts @@ -0,0 +1,17 @@ +//! test_output(E: undefined) +// Should be: "Ok" + +// FIXME: This is failing because the optimizer learned to move out of the exception variable. This +// is resulting in a void->undefined conversion (I think), which breaks because we rely on +// throw(void) to not actually throw. + +export default function () { + let result: string; + + try { + } finally { + result = "Ok"; + } + + return result; +} diff --git a/inputs/failing/exceptions/throwTypeError.ts b/inputs/failing/exceptions/throwTypeError.ts new file mode 100644 index 00000000..1842360f --- /dev/null +++ b/inputs/failing/exceptions/throwTypeError.ts @@ -0,0 +1,11 @@ +//! test_output(undefined) +// Should be: E: TypeError{"message":"Cannot subscript undefined"} + +// FIXME: This is failing because the optimizer is removing the subscript instruction because the +// result is unused. We need to implement throw detection in the optimizer and only remove +// instructions that can throw when we know that they won't throw. + +export default function () { + const arr = undefined; + const len = arr.length; +} diff --git a/inputs/passing/exceptions/finallyWithoutThrow.ts b/inputs/passing/exceptions/finallyWithoutThrow.ts deleted file mode 100644 index f20ea309..00000000 --- a/inputs/passing/exceptions/finallyWithoutThrow.ts +++ /dev/null @@ -1,12 +0,0 @@ -//! test_output("Ok") - -export default function () { - let result: string; - - try { - } finally { - result = "Ok"; - } - - return result; -} diff --git a/inputs/passing/exceptions/throwTypeError.ts b/inputs/passing/exceptions/throwTypeError.ts deleted file mode 100644 index 33a738cf..00000000 --- a/inputs/passing/exceptions/throwTypeError.ts +++ /dev/null @@ -1,6 +0,0 @@ -//! test_output(E: TypeError{"message":"Cannot subscript undefined"}) - -export default function () { - const arr = undefined; - const len = arr.length; -} diff --git a/valuescript_compiler/src/asm.rs b/valuescript_compiler/src/asm.rs index f7c7a5f4..1632bb70 100644 --- a/valuescript_compiler/src/asm.rs +++ b/valuescript_compiler/src/asm.rs @@ -274,6 +274,10 @@ impl Register { return self.name == "ignore"; } + pub fn is_special(&self) -> bool { + return self.is_return() || self.is_this() || self.is_ignore(); + } + pub fn value_type(&self) -> ValueType { if self.take { ValueType::TakeRegister diff --git a/valuescript_compiler/src/function_compiler.rs b/valuescript_compiler/src/function_compiler.rs index ce9ce9bd..a5d30711 100644 --- a/valuescript_compiler/src/function_compiler.rs +++ b/valuescript_compiler/src/function_compiler.rs @@ -218,6 +218,14 @@ impl FunctionCompiler { self.current.body.push(FnLine::Release(reg.clone())); } + pub fn insert_all_releases(&mut self) { + for reg in self.reg_allocator.all_used() { + if !reg.is_special() { + self.current.body.push(FnLine::Release(reg)); + } + } + } + pub fn compile( definition_pointer: Pointer, fn_name: Option, @@ -341,6 +349,8 @@ impl FunctionCompiler { } }; + self.insert_all_releases(); + if let Some(end_label) = self.end_label.as_ref() { self.current.body.push(FnLine::Label(end_label.clone())); @@ -493,6 +503,7 @@ impl FunctionCompiler { self.push(Instruction::Mov(Value::Bool(true), is_returning.clone())); self.push(Instruction::Jmp(finally_label.ref_())); } else { + self.insert_all_releases(); self.push(Instruction::End); } } diff --git a/valuescript_compiler/src/name_allocator.rs b/valuescript_compiler/src/name_allocator.rs index 961b7997..84abb010 100644 --- a/valuescript_compiler/src/name_allocator.rs +++ b/valuescript_compiler/src/name_allocator.rs @@ -1,10 +1,10 @@ -use std::collections::HashSet; +use std::collections::BTreeSet; use crate::asm::{Pointer, Register}; #[derive(Default, Clone)] pub struct NameAllocator { - used_names: HashSet, + used_names: BTreeSet, released_names: Vec, } @@ -149,6 +149,16 @@ impl RegAllocator { false => panic!("Can't release non-named register"), } } + + pub fn all_used(&self) -> Vec { + let mut res = Vec::::new(); + + for name in &self.alloc.used_names { + res.push(Register::named(name.clone())); + } + + res + } } impl Default for RegAllocator {