Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1ffc9d1
feat(minivm): register VM bytecode pipeline + debugger + golden tests
cuzzo May 9, 2026
36f30e1
ci(minivm): gate PRs on register-VM allowlist baseline
cuzzo May 9, 2026
5fb8a58
feat(minivm): skip InlineZig equality assert lowering for :bc target
cuzzo May 9, 2026
baf94ad
feat(minivm): unwrap UnionSchema when collecting cross-file variants
cuzzo May 9, 2026
772adab
feat(minivm): skip FSM transform for :bc target (BgBlock unlock)
cuzzo May 9, 2026
c55f617
ci(minivm): bump register-VM baseline 215 -> 238
cuzzo May 9, 2026
e652332
Support runtime collection handles in register VM
cuzzo May 11, 2026
1eacd7a
chore(minivm): summarize register benchmark blockers
cuzzo May 11, 2026
d7f3bda
feat(minivm): support benchmark helper natives in register VM
cuzzo May 11, 2026
562f889
fix(minivm): emit string contains before map contains
cuzzo May 12, 2026
7cd1691
fix(minivm): allow discarded helper call results
cuzzo May 12, 2026
4d6837c
feat(minivm): support lowered pool slot iteration
cuzzo May 12, 2026
79a01e4
feat(minivm): support borrowed list iterator lowering
cuzzo May 12, 2026
8651a2d
feat(minivm): support float numeric maps in register VM
cuzzo May 12, 2026
d515cb0
feat(minivm): support pool id list lookups in register VM
cuzzo May 12, 2026
51ca851
feat(minivm): lower soa lists in register VM
cuzzo May 12, 2026
448fa63
feat(minivm): support weak rc graph lowering
cuzzo May 12, 2026
b6df76d
perf(minivm): lazily load struct-list fields
cuzzo May 12, 2026
f1c5236
fix(minivm): update mutable struct bindings in register VM
cuzzo May 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,37 @@ jobs:
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}

register-vm-allowlist:
name: Register-VM allowlist (transpile-tests via --vm=register)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ env.RUBY_VERSION }}
bundler-cache: true
- uses: mlugg/setup-zig@v2
with:
version: ${{ env.ZIG_VERSION }}
# The register VM compiles the runner template (vm.cht) once and
# caches it across tests, so warming clear-cache speeds it up
# significantly (5-10x). Share the cache key with transpile-tests
# since both run the same compile pipeline.
- uses: actions/cache@v4
with:
path: |
zig/.clear-cache
zig/.clear-transpile-cache
key: clear-build-${{ runner.os }}-zig${{ env.ZIG_VERSION }}-${{ hashFiles('src/**', 'zig/runtime/**', 'zig/lib/**', 'Gemfile.lock') }}
restore-keys: |
clear-build-${{ runner.os }}-zig${{ env.ZIG_VERSION }}-
# Baseline ratchet: the register VM is the primary stress-tester
# for the transpiler; new transpiler bugs surface as the test
# corpus the VM can run grows. Gate merge on no regression below
# the current pass count. Bump --min-pass=N as we land coverage
# work that crosses tests fail->pass.
- run: bundle exec ruby examples/minivm/run_tests.rb --vm=register --min-pass=238

module-integration:
name: transpile-tests/module-integration (zig build test)
runs-on: ubuntu-latest
Expand Down
9 changes: 9 additions & 0 deletions benchmarks/vm/01_fib.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
local function fib(n)
if n <= 1 then return n end
return fib(n - 1) + fib(n - 2)
end

local t0 = os.clock()
local r = fib(25)
print("fib(25) = " .. tostring(r))
print("BENCH_RESULT: " .. tostring(math.floor((os.clock() - t0) * 1000)) .. " ms")
7 changes: 7 additions & 0 deletions benchmarks/vm/02_loop_sum.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
local t0 = os.clock()
local total = 0
for i = 0, 999999 do
total = total + i
end
print("sum = " .. tostring(total))
print("BENCH_RESULT: " .. tostring(math.floor((os.clock() - t0) * 1000)) .. " ms")
18 changes: 18 additions & 0 deletions benchmarks/vm/03_hashmap.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local t0 = os.clock()
local m = {}
for i = 0, 99999 do
m[i] = i * 2
end
local insert_ms = (os.clock() - t0) * 1000

local t1 = os.clock()
local total = 0
for j = 0, 99999 do
total = total + (m[j] or 0)
end
local lookup_ms = (os.clock() - t1) * 1000
local total_ms = math.floor(insert_ms + lookup_ms)

print("total = " .. tostring(total))
print("Insert: " .. tostring(math.floor(insert_ms)) .. " ms | Lookup: " .. tostring(math.floor(lookup_ms)) .. " ms")
print("BENCH_RESULT: " .. tostring(total_ms) .. " ms")
19 changes: 19 additions & 0 deletions benchmarks/vm/04_list_sum.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FN main() RETURNS Void ->
t0 = timestampMs();
MUTABLE values: Int64[]@list = [];
FOR i IN (0_i64 ..< 10000_i64) DO
values.append(i * 2_i64);
END
appendMs = timestampMs() - t0;
t1 = timestampMs();
MUTABLE total: Int64 = 0_i64;
FOR j IN (0_i64 ..< values.length()) DO
total = total + values[j];
END
sumMs = timestampMs() - t1;
totalMs = appendMs + sumMs;
print("total = ${total.toString()}");
print("Append: ${appendMs.toString()} ms | Sum: ${sumMs.toString()} ms");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
18 changes: 18 additions & 0 deletions benchmarks/vm/04_list_sum.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
local t0 = os.clock()
local values = {}
for i = 0, 9999 do
values[#values + 1] = i * 2
end
local append_ms = (os.clock() - t0) * 1000

local t1 = os.clock()
local total = 0
for j = 1, #values do
total = total + values[j]
end
local sum_ms = (os.clock() - t1) * 1000
local total_ms = append_ms + sum_ms

print("total = " .. tostring(total))
print(string.format("Append: %.3f ms | Sum: %.3f ms", append_ms, sum_ms))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
16 changes: 16 additions & 0 deletions benchmarks/vm/04_list_sum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import time

t0 = time.monotonic()
values = []
for i in range(10000):
values.append(i * 2)
append_ms = (time.monotonic() - t0) * 1000
t1 = time.monotonic()
total = 0
for j in range(len(values)):
total += values[j]
sum_ms = (time.monotonic() - t1) * 1000
total_ms = append_ms + sum_ms
print(f"total = {total}")
print(f"Append: {append_ms:.3f} ms | Sum: {sum_ms:.3f} ms")
print(f"BENCH_RESULT: {total_ms:.3f} ms")
12 changes: 12 additions & 0 deletions benchmarks/vm/04_list_sum.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
values = []
10_000.times { |i| values << i * 2 }
append_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000
t1 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
total = 0
values.length.times { |j| total += values[j] }
sum_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t1) * 1000
total_ms = append_ms + sum_ms
puts "total = #{total}"
puts format("Append: %.3f ms | Sum: %.3f ms", append_ms, sum_ms)
puts format("BENCH_RESULT: %.3f ms", total_ms)
15 changes: 15 additions & 0 deletions benchmarks/vm/05_call_loop.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FN mix(x: Int64) RETURNS Int64 ->
RETURN (x * 3_i64) + 1_i64;
END

FN main() RETURNS Void ->
t0 = timestampMs();
MUTABLE total: Int64 = 0_i64;
FOR i IN (0_i64 ..< 100000_i64) DO
total = total + mix(i);
END
totalMs = timestampMs() - t0;
print("total = ${total.toString()}");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
12 changes: 12 additions & 0 deletions benchmarks/vm/05_call_loop.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
local function mix(x)
return (x * 3) + 1
end

local t0 = os.clock()
local total = 0
for i = 0, 99999 do
total = total + mix(i)
end
local total_ms = (os.clock() - t0) * 1000
print("total = " .. tostring(total))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
14 changes: 14 additions & 0 deletions benchmarks/vm/05_call_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import time


def mix(x):
return (x * 3) + 1


t0 = time.monotonic()
total = 0
for i in range(100000):
total += mix(i)
total_ms = (time.monotonic() - t0) * 1000
print(f"total = {total}")
print(f"BENCH_RESULT: {total_ms:.3f} ms")
10 changes: 10 additions & 0 deletions benchmarks/vm/05_call_loop.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
def mix(x)
(x * 3) + 1
end

t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
total = 0
100_000.times { |i| total += mix(i) }
total_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000
puts "total = #{total}"
puts format("BENCH_RESULT: %.3f ms", total_ms)
12 changes: 12 additions & 0 deletions benchmarks/vm/06_float_loop.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FN main() RETURNS Void ->
t0 = timestampMs();
MUTABLE x: Float64 = 1.0;
FOR i IN (0_i64 ..< 100000_i64) DO
x = (x + 1.25) * 1.000001 - 0.25;
END
totalMs = timestampMs() - t0;
whole = toInt(x);
print("x = ${whole.toString()}");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
8 changes: 8 additions & 0 deletions benchmarks/vm/06_float_loop.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local t0 = os.clock()
local x = 1.0
for _ = 0, 99999 do
x = (x + 1.25) * 1.000001 - 0.25
end
local total_ms = (os.clock() - t0) * 1000
print("x = " .. tostring(math.floor(x)))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
9 changes: 9 additions & 0 deletions benchmarks/vm/06_float_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import time

t0 = time.monotonic()
x = 1.0
for _ in range(100000):
x = (x + 1.25) * 1.000001 - 0.25
total_ms = (time.monotonic() - t0) * 1000
print(f"x = {int(x)}")
print(f"BENCH_RESULT: {total_ms:.3f} ms")
8 changes: 8 additions & 0 deletions benchmarks/vm/06_float_loop.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
x = 1.0
100_000.times do
x = (x + 1.25) * 1.000001 - 0.25
end
total_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000
puts "x = #{x.to_i}"
puts format("BENCH_RESULT: %.3f ms", total_ms)
16 changes: 16 additions & 0 deletions benchmarks/vm/07_string_scan.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FN main() RETURNS Void ->
line = "SET:12345:payload";
t0 = timestampMs();
MUTABLE total: Int64 = 0_i64;
FOR i IN (0_i64 ..< 50000_i64) DO
IF startsWith?(line, "SET:") THEN
IF contains?(line, "payload") THEN
total = total + substr(line, 4_i64, 5_i64).length();
END
END
END
totalMs = timestampMs() - t0;
print("total = ${total.toString()}");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
11 changes: 11 additions & 0 deletions benchmarks/vm/07_string_scan.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local line = "SET:12345:payload"
local t0 = os.clock()
local total = 0
for _ = 0, 49999 do
if string.sub(line, 1, 4) == "SET:" and string.find(line, "payload", 1, true) ~= nil then
total = total + string.len(string.sub(line, 5, 9))
end
end
local total_ms = (os.clock() - t0) * 1000
print("total = " .. tostring(total))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
11 changes: 11 additions & 0 deletions benchmarks/vm/07_string_scan.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import time

line = "SET:12345:payload"
t0 = time.monotonic()
total = 0
for _ in range(50000):
if line.startswith("SET:") and "payload" in line:
total += len(line[4:9])
total_ms = (time.monotonic() - t0) * 1000
print(f"total = {total}")
print(f"BENCH_RESULT: {total_ms:.3f} ms")
11 changes: 11 additions & 0 deletions benchmarks/vm/07_string_scan.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
line = "SET:12345:payload"
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
total = 0
50_000.times do
if line.start_with?("SET:") && line.include?("payload")
total += line[4, 5].length
end
end
total_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000
puts "total = #{total}"
puts format("BENCH_RESULT: %.3f ms", total_ms)
20 changes: 20 additions & 0 deletions benchmarks/vm/08_branch_loop.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FN main() RETURNS Void ->
t0 = timestampMs();
MUTABLE total: Int64 = 0_i64;
FOR i IN (0_i64 ..< 200000_i64) DO
r = i MOD 5_i64;
IF r == 0_i64 THEN
total = total + i;
ELSE_IF r == 1_i64 THEN
total = total - i;
ELSE_IF r == 2_i64 THEN
total = total + (i * 2_i64);
ELSE
total = total + 1_i64;
END
END
totalMs = timestampMs() - t0;
print("total = ${total.toString()}");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
17 changes: 17 additions & 0 deletions benchmarks/vm/08_branch_loop.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
local t0 = os.clock()
local total = 0
for i = 0, 199999 do
local r = i % 5
if r == 0 then
total = total + i
elseif r == 1 then
total = total - i
elseif r == 2 then
total = total + (i * 2)
else
total = total + 1
end
end
local total_ms = (os.clock() - t0) * 1000
print("total = " .. tostring(total))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
17 changes: 17 additions & 0 deletions benchmarks/vm/08_branch_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import time

t0 = time.monotonic()
total = 0
for i in range(200000):
r = i % 5
if r == 0:
total += i
elif r == 1:
total -= i
elif r == 2:
total += i * 2
else:
total += 1
total_ms = (time.monotonic() - t0) * 1000
print(f"total = {total}")
print(f"BENCH_RESULT: {total_ms:.3f} ms")
17 changes: 17 additions & 0 deletions benchmarks/vm/08_branch_loop.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
total = 0
200_000.times do |i|
r = i % 5
if r == 0
total += i
elsif r == 1
total -= i
elsif r == 2
total += i * 2
else
total += 1
end
end
total_ms = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0) * 1000
puts "total = #{total}"
puts format("BENCH_RESULT: %.3f ms", total_ms)
15 changes: 15 additions & 0 deletions benchmarks/vm/09_struct_loop.cht
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
STRUCT Acc { a: Int64, b: Int64 }

FN main() RETURNS Void ->
t0 = timestampMs();
MUTABLE acc = Acc{ a: 1_i64, b: 2_i64 };
FOR i IN (0_i64 ..< 200000_i64) DO
acc.a = acc.a + i;
acc.b = acc.b + acc.a;
END
total = acc.a + acc.b;
totalMs = timestampMs() - t0;
print("total = ${total.toString()}");
print("BENCH_RESULT: ${totalMs.toString()} ms");
RETURN;
END
10 changes: 10 additions & 0 deletions benchmarks/vm/09_struct_loop.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
local t0 = os.clock()
local acc = { a = 1, b = 2 }
for i = 0, 199999 do
acc.a = acc.a + i
acc.b = acc.b + acc.a
end
local total = acc.a + acc.b
local total_ms = (os.clock() - t0) * 1000
print("total = " .. tostring(total))
print(string.format("BENCH_RESULT: %.3f ms", total_ms))
Loading
Loading