@tishlang/tish 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/tish +0 -0
- package/crates/tish/Cargo.toml +2 -2
- package/crates/tish/src/cli_help.rs +504 -0
- package/crates/tish/src/main.rs +76 -90
- package/crates/tish/src/repl_completion.rs +1 -1
- package/crates/tish/tests/cargo_example_compile.rs +65 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
- package/crates/tish/tests/integration_test.rs +48 -0
- package/crates/tish_build_utils/src/lib.rs +204 -1
- package/crates/tish_builtins/src/string.rs +248 -0
- package/crates/tish_bytecode/Cargo.toml +1 -0
- package/crates/tish_bytecode/src/compiler.rs +289 -66
- package/crates/tish_bytecode/src/opcode.rs +13 -3
- package/crates/tish_bytecode/src/peephole.rs +21 -16
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +277 -93
- package/crates/tish_compile/src/lib.rs +7 -4
- package/crates/tish_compile/src/resolve.rs +418 -40
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +1 -0
- package/crates/tish_core/src/value.rs +1 -0
- package/crates/tish_eval/src/eval.rs +49 -1
- package/crates/tish_eval/src/lib.rs +1 -1
- package/crates/tish_native/src/build.rs +86 -17
- package/crates/tish_native/src/lib.rs +36 -16
- package/crates/tish_runtime/src/lib.rs +4 -0
- package/crates/tish_vm/src/lib.rs +1 -1
- package/crates/tish_vm/src/vm.rs +165 -19
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
//! Peephole optimizations on bytecode (post-emission).
|
|
2
2
|
//! B2 from optimization plan: jump chaining, etc.
|
|
3
3
|
|
|
4
|
+
use std::collections::BTreeSet;
|
|
5
|
+
|
|
4
6
|
use crate::opcode::Opcode;
|
|
5
7
|
use crate::Chunk;
|
|
6
8
|
|
|
@@ -90,6 +92,18 @@ fn final_jump_target(code: &[u8], jump_ip: usize) -> Option<usize> {
|
|
|
90
92
|
skip_unconditional_jump_chain(code, first_target)
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
/// Instruction boundaries from a linear scan (aligned bytecode from the compiler).
|
|
96
|
+
fn collect_insn_starts(code: &[u8]) -> BTreeSet<usize> {
|
|
97
|
+
let mut out = BTreeSet::new();
|
|
98
|
+
let mut ip = 0usize;
|
|
99
|
+
while ip < code.len() {
|
|
100
|
+
out.insert(ip);
|
|
101
|
+
let sz = instruction_size(code, ip).unwrap_or(1);
|
|
102
|
+
ip += sz;
|
|
103
|
+
}
|
|
104
|
+
out
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
/// Replace instruction at [ip..ip+len) with Nops (preserves length, no offset updates).
|
|
94
108
|
fn nop_out(code: &mut [u8], ip: usize, len: usize) {
|
|
95
109
|
for i in 0..len {
|
|
@@ -112,19 +126,6 @@ fn remove_dup_pop(code: &mut [u8]) {
|
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
|
|
115
|
-
/// Remove redundant LoadConst + Pop (load constant then discard = no-op).
|
|
116
|
-
fn remove_loadconst_pop(code: &mut [u8]) {
|
|
117
|
-
let mut ip = 0;
|
|
118
|
-
while ip + 4 <= code.len() {
|
|
119
|
-
if Opcode::from_u8(code[ip]) == Some(Opcode::LoadConst)
|
|
120
|
-
&& Opcode::from_u8(code[ip + 3]) == Some(Opcode::Pop)
|
|
121
|
-
{
|
|
122
|
-
nop_out(code, ip, 4);
|
|
123
|
-
}
|
|
124
|
-
ip += instruction_size(code, ip).unwrap_or(1);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
129
|
/// Replace no-op jumps (Jump with offset 0) with Nops.
|
|
129
130
|
fn remove_noop_jumps(code: &mut [u8]) {
|
|
130
131
|
let mut ip = 0;
|
|
@@ -142,6 +143,7 @@ fn remove_noop_jumps(code: &mut [u8]) {
|
|
|
142
143
|
/// Apply jump chaining: if Jump/JumpIfFalse targets another jump, update to
|
|
143
144
|
/// jump directly to the final target.
|
|
144
145
|
fn chain_jumps(code: &mut [u8]) {
|
|
146
|
+
let insn_starts = collect_insn_starts(code);
|
|
145
147
|
let mut ip = 0;
|
|
146
148
|
while ip < code.len() {
|
|
147
149
|
let op = match Opcode::from_u8(code[ip]) {
|
|
@@ -160,9 +162,13 @@ fn chain_jumps(code: &mut [u8]) {
|
|
|
160
162
|
let current_offset = read_i16(code, ip + 1) as isize;
|
|
161
163
|
let current_target = (ip as isize + 3 + current_offset).max(0) as usize;
|
|
162
164
|
if let Some(final_target) = final_jump_target(code, ip) {
|
|
163
|
-
|
|
165
|
+
let target_ok = final_target == code.len()
|
|
166
|
+
|| insn_starts.contains(&final_target);
|
|
167
|
+
if final_target != current_target && target_ok {
|
|
164
168
|
let new_offset = final_target as i32 - (ip + 3) as i32;
|
|
165
|
-
|
|
169
|
+
if (i16::MIN as i32..=i16::MAX as i32).contains(&new_offset) {
|
|
170
|
+
write_u16(code, ip + 1, (new_offset as i16) as u16);
|
|
171
|
+
}
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
}
|
|
@@ -174,7 +180,6 @@ fn chain_jumps(code: &mut [u8]) {
|
|
|
174
180
|
|
|
175
181
|
/// Run peephole optimizations on a chunk (and nested chunks).
|
|
176
182
|
pub fn optimize(chunk: &mut Chunk) {
|
|
177
|
-
remove_loadconst_pop(&mut chunk.code);
|
|
178
183
|
remove_dup_pop(&mut chunk.code);
|
|
179
184
|
remove_noop_jumps(&mut chunk.code);
|
|
180
185
|
chain_jumps(&mut chunk.code);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//! Regression: C-style `for` `continue` must jump forward to the update clause (not JumpBack).
|
|
2
|
+
|
|
3
|
+
use std::fs;
|
|
4
|
+
|
|
5
|
+
use tishlang_bytecode::{compile, compile_unoptimized};
|
|
6
|
+
use tishlang_vm::run;
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
fn break_continue_fixture_runs_on_vm() {
|
|
10
|
+
let path = format!(
|
|
11
|
+
"{}/../../tests/core/break_continue.tish",
|
|
12
|
+
env!("CARGO_MANIFEST_DIR")
|
|
13
|
+
);
|
|
14
|
+
let src = fs::read_to_string(&path).unwrap();
|
|
15
|
+
let prog = tishlang_parser::parse(&src).expect("parse");
|
|
16
|
+
let chunk = compile_unoptimized(&prog).expect("compile");
|
|
17
|
+
run(&chunk).expect("VM run");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[test]
|
|
21
|
+
fn mutation_vm_ast_opt_without_peephole() {
|
|
22
|
+
let path = format!(
|
|
23
|
+
"{}/../../tests/core/mutation.tish",
|
|
24
|
+
env!("CARGO_MANIFEST_DIR")
|
|
25
|
+
);
|
|
26
|
+
let src = fs::read_to_string(&path).unwrap();
|
|
27
|
+
let mut prog = tishlang_parser::parse(&src).expect("parse");
|
|
28
|
+
tishlang_opt::optimize(&mut prog);
|
|
29
|
+
let chunk = compile_unoptimized(&prog).expect("compile");
|
|
30
|
+
run(&chunk).expect("VM");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn mutation_vm_ast_opt_with_peephole() {
|
|
35
|
+
let path = format!(
|
|
36
|
+
"{}/../../tests/core/mutation.tish",
|
|
37
|
+
env!("CARGO_MANIFEST_DIR")
|
|
38
|
+
);
|
|
39
|
+
let src = fs::read_to_string(&path).unwrap();
|
|
40
|
+
let mut prog = tishlang_parser::parse(&src).expect("parse");
|
|
41
|
+
tishlang_opt::optimize(&mut prog);
|
|
42
|
+
let chunk = compile(&prog).expect("compile");
|
|
43
|
+
run(&chunk).expect("VM");
|
|
44
|
+
}
|