Chez Phase 1 (increment 1): IR -> Scheme emitter, real IR shapes
New back end half: host/chez/emit.janet consumes the host-neutral jolt IR (ir.clj shapes) and emits Scheme, reusing the existing front-end (Option-2 backend swap). Covers the pure-functional subset: const/local/var/rt/if/do/let/ fn/invoke/def/loop/recur. Tested by hand-built IR run on Chez: (+ 1 2)=3, fib(30)=832040, loop/recur sum=15 (4/4). Finding: correct emit wraps every if-test in jolt-truthy?, costing ~3x on fib (15.8ms vs hand-Scheme 5ms). Eliding the wrapper for known-boolean tests recovers the ceiling (Phase-4 type-driven opt). Remaining Phase 1: wire the live analyzer, var-cell late binding, RT module, broader op coverage for mandelbrot.
This commit is contained in:
parent
b3d0a91e3e
commit
874e3c7cf2
2 changed files with 179 additions and 0 deletions
73
test/chez/emit-test.janet
Normal file
73
test/chez/emit-test.janet
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Phase 1 — IR -> Scheme emitter tests. Hand-built IR in the real ir.clj shapes,
|
||||
# emitted to Scheme, compiled+run on Chez, results + fib speed checked.
|
||||
# janet test/chez/emit-test.janet (from repo root)
|
||||
(import ../../host/chez/emit :as e)
|
||||
|
||||
(defn run-chez [src]
|
||||
(spit "/tmp/emit-prog.ss" src)
|
||||
(def proc (os/spawn ["chez" "--script" "/tmp/emit-prog.ss"] :p {:out :pipe :err :pipe}))
|
||||
(def out (ev/read (proc :out) 0x100000))
|
||||
(def err (ev/read (proc :err) 0x100000))
|
||||
(def code (os/proc-wait proc))
|
||||
[code (string/trim (if out (string out) "")) (string/trim (if err (string err) ""))])
|
||||
|
||||
(var total 0) (var fails 0)
|
||||
(defn ok [name pred] (++ total) (unless pred (++ fails) (printf "FAIL: %s" name)))
|
||||
|
||||
# --- IR builders (ir.clj shapes) ---
|
||||
(defn rt [name & args] {:op :invoke :fn {:op :rt :name name} :args args})
|
||||
(defn lcl [n] {:op :local :name n})
|
||||
(defn k [v] {:op :const :val v})
|
||||
|
||||
# 1) (+ 1 2)
|
||||
(def add-ir (rt "+" (k 1) (k 2)))
|
||||
(let [[code out err] (run-chez (e/program [] (e/emit add-ir)))]
|
||||
(ok "(+ 1 2) = 3" (and (= code 0) (= out "3")))
|
||||
(when (not= code 0) (printf " err: %s" err)))
|
||||
|
||||
# 2) fib def + (fib 30)
|
||||
(defn fib-call [arg] {:op :invoke :fn {:op :var :ns "user" :name "fib"} :args [arg]})
|
||||
(def fib-def
|
||||
{:op :def :ns "user" :name "fib"
|
||||
:init {:op :fn :name "fib"
|
||||
:arities [{:params ["n"]
|
||||
:body {:op :if
|
||||
:test (rt "<" (lcl "n") (k 2))
|
||||
:then (lcl "n")
|
||||
:else (rt "+" (fib-call (rt "-" (lcl "n") (k 1)))
|
||||
(fib-call (rt "-" (lcl "n") (k 2))))}}]}})
|
||||
(let [prog (e/program [(e/emit fib-def)] (e/emit (fib-call (k 30))))
|
||||
[code out err] (run-chez prog)]
|
||||
(ok "(fib 30) = 832040" (and (= code 0) (= out "832040")))
|
||||
(when (not= code 0) (printf " err: %s" err)))
|
||||
|
||||
# 3) loop/recur sum 1..5 = 15
|
||||
(def loop-ir
|
||||
{:op :loop
|
||||
:bindings [["i" (k 1)] ["acc" (k 0)]]
|
||||
:body {:op :if
|
||||
:test (rt ">" (lcl "i") (k 5))
|
||||
:then (lcl "acc")
|
||||
:else {:op :recur :args [(rt "inc" (lcl "i")) (rt "+" (lcl "acc") (lcl "i"))]}}})
|
||||
(let [[code out err] (run-chez (e/program [] (e/emit loop-ir)))]
|
||||
(ok "loop/recur sum = 15" (and (= code 0) (= out "15")))
|
||||
(when (not= code 0) (printf " err: %s" err)))
|
||||
|
||||
# 4) speed: emitted fib(30) should hit ~the spike ceiling (hand-Scheme ~5ms),
|
||||
# proving the IR->Scheme path adds no overhead vs hand-written Scheme.
|
||||
(def timed-fib
|
||||
(string (e/emit fib-def) "\n"
|
||||
"(define (now-ns) (let ((t (current-time 'time-monotonic))) (+ (* (time-second t) 1000000000) (time-nanosecond t))))\n"
|
||||
"(fib 24)(fib 24)\n"
|
||||
"(let* ((t0 (now-ns)) (r (fib 30)) (ms (/ (- (now-ns) t0) 1000000.0)))\n"
|
||||
" (printf \"~a ~a\\n\" r (exact->inexact ms)))"))
|
||||
(let [[code out err] (run-chez (string "(import (chezscheme))\n(load \"host/chez/values.ss\")\n(define (jolt-inc x) (+ x 1))\n" timed-fib))]
|
||||
(def parts (string/split " " out))
|
||||
(def result (get parts 0))
|
||||
(def ms (scan-number (or (get parts 1) "999")))
|
||||
(ok "emitted fib(30) correct + fast" (and (= code 0) (= result "832040") (< ms 40)))
|
||||
(printf " emitted fib(30): %s in %.2f ms (hand-Scheme spike ~5ms)" result ms)
|
||||
(when (not= code 0) (printf " err: %s" err)))
|
||||
|
||||
(printf "\nemit-test: %d/%d passed" (- total fails) total)
|
||||
(os/exit (if (> fails 0) 1 0))
|
||||
Loading…
Add table
Add a link
Reference in a new issue