Arithmetic and comparisons lowered to raw Chez ops, so an operand outside Chez's tower (BigDecimal) crashed with a raw condition, and Chez contagion leaked: (* 1.0 0) gave exact 0 where the JVM gives 0.0, (* ##Inf 0) gave 0 instead of ##NaN, (/ 1 0) raised an untyped error. One seam now (host/chez/seq.ss): call position emits jolt-n* macros with the both-Chez-numbers fast path open-coded; value position folds through the same binary ops. Anything outside the tower falls to per-op slow hooks that java/bigdec.ss extends, so bigdec arithmetic works in every position (the old static-only :bigdec typing limitation is gone). JVM rules patched into the fast path: a double operand wins, an exact zero divisor throws ArithmeticException while a double zero divisor yields Inf/NaN, quot/rem/mod cover ratios and doubles, min/max return the original operand with NaN winning, a nil operand is NPE and a non-number CCE, zero-arg -// throw ArityException at runtime instead of failing expansion. Also: with-precision now binds *math-context* and bigdec results round with real RoundingMode semantics (UNNECESSARY throws; division rounds to precision instead of throwing); rationalize goes through the shortest decimal print like BigDecimal.valueOf (the identity stub is gone); ratios coerce to bigdec like Numbers.toBigDecimal; min/max int-literal operands no longer coerce to flonum in the numeric pass. Perf neutral: fib and seq benches unchanged (the fast path is two type checks the optimizer folds); hinted fl/fx paths untouched. 19 JVM-certified corpus rows; cts baseline 5614->5730 pass, 192->88 errors, 84->79 baselined namespaces.
67 lines
3.4 KiB
Scheme
67 lines
3.4 KiB
Scheme
;; IR inlining (jolt.passes.inline), enabled under optimization. A small
|
|
;; single-arity defn is stashed and spliced at its call sites, removing the call.
|
|
;; A ^double/^long fn's param-entry and return coercions travel with the splice
|
|
;; (via :coerce nodes) so an inlined call matches the called one — incl. coercing a
|
|
;; non-double arg — and the body's fl*/fx* fast path still fires. Run:
|
|
;; chez --script test/chez/inline-test.ss
|
|
|
|
(import (chezscheme))
|
|
(load "host/chez/rt.ss")
|
|
(set-chez-ns! "clojure.core")
|
|
(load "host/chez/seed/prelude.ss")
|
|
(load "host/chez/post-prelude.ss")
|
|
(set-chez-ns! "user")
|
|
(load "host/chez/host-contract.ss")
|
|
(load "host/chez/seed/image.ss")
|
|
(load "host/chez/compile-eval.ss")
|
|
|
|
(define total 0) (define fails 0)
|
|
(define (ok name pred) (set! total (+ total 1)) (unless pred (set! fails (+ fails 1)) (printf "FAIL: ~a\n" name)))
|
|
(define (has? s sub)
|
|
(let ((ns (string-length s)) (nsub (string-length sub)))
|
|
(let loop ((i 0))
|
|
(cond ((> (+ i nsub) ns) #f)
|
|
((string=? (substring s i (+ i nsub)) sub) #t)
|
|
(else (loop (+ i 1)))))))
|
|
(define (emitf ns str) ; analyze + run-passes (optimize on) + emit
|
|
(let-values (((f j) (rdr-read-form str 0 (string-length str))))
|
|
(let ((ctx (make-analyze-ctx ns)))
|
|
(jolt-ce-emit (jolt-ce-run-passes (jolt-ce-analyze ctx f) ctx)))))
|
|
(define (ev s) (jolt-compile-eval s "u"))
|
|
|
|
;; inlining is a closed-world optimization — only under optimize.
|
|
(set-optimize! #t)
|
|
|
|
;; a small plain fn is spliced; the call to it disappears.
|
|
(ev "(def add1 (fn* ([x] (+ x 1))))")
|
|
(let ((e (emitf "u" "(fn* ([y] (add1 y)))")))
|
|
(ok "plain fn is inlined (call to add1 gone)" (not (has? e "add1")))
|
|
(ok "inlined body present (jolt-n+ ... 1)" (has? e "(jolt-n+")))
|
|
(ok "inlined plain fn runtime: (add1 41) = 42" (= 42 (jnum->exact (ev "((fn* ([y] (add1 y))) 41)"))))
|
|
|
|
;; a ^double fn: body fl-ops fire after inlining, and the call is gone.
|
|
(ev "(def ^double dwork (fn* ([^double a ^double b] (+ (* a a) (* b b)))))")
|
|
(let ((e (emitf "u" "(fn* ([] (dwork 3.0 4.0)))")))
|
|
(ok "inlined ^double fn body uses fl*" (has? e "(fl*"))
|
|
(ok "inlined ^double fn call to dwork is gone" (not (has? e "dwork"))))
|
|
(ok "inlined ^double call: 3^2+4^2 = 25" (= 25 (jnum->exact (ev "((fn* ([] (dwork 3.0 4.0))))"))))
|
|
;; coercion travels with the splice: int args become doubles, so the result is a
|
|
;; flonum 25.0 — matching the called fn, not an exact 25.
|
|
(ok "inlined ^double with int args still returns a flonum" (flonum? (ev "((fn* ([] (dwork 3 4))))")))
|
|
|
|
;; a ^long fn inlines with fixnum coercion + fx ops.
|
|
(ev "(def ^long lsum (fn* ([^long a ^long b] (+ a b))))")
|
|
(let ((e (emitf "u" "(fn* ([] (lsum 3 4)))")))
|
|
(ok "inlined ^long fn body uses fx+" (has? e "(fx+")))
|
|
(ok "inlined ^long call: 3+4 = 7 (fixnum)" (let ((r (ev "((fn* ([] (lsum 3 4))))"))) (and (fixnum? r) (= r 7))))
|
|
|
|
;; an accumulator over an inlined ^double call: the whole loop body fuses to fl-ops.
|
|
(ev "(def ^double sq (fn* ([^double x] (* x x))))")
|
|
(let ((e (emitf "u" "(fn* ([] (loop [acc 0.0 i 0] (if (< i 3) (recur (+ acc (sq 2.0)) (inc i)) acc))))")))
|
|
(ok "accumulator over inlined ^double call lowers to fl+" (has? e "(fl+"))
|
|
(ok "the sq call is inlined away" (not (has? e "sq"))))
|
|
(ok "accumulator over inlined ^double call: 3*4.0 = 12" (= 12 (jnum->exact (ev "((fn* ([] (loop [acc 0.0 i 0] (if (< i 3) (recur (+ acc (sq 2.0)) (inc i)) acc)))))"))))
|
|
|
|
(set-optimize! #f)
|
|
(printf "~a/~a passed~n" (- total fails) total)
|
|
(exit (if (zero? fails) 0 1))
|