Const-link stable vars under whole-program; direct-link cfunction roots
direct-var? now treats a cfunction root the same as a function root, so a call/ref to a native fn (clojure.math/sqrt et al.) embeds the value instead of a per-call cell deref. This was the hot indirection in the ray tracer — sqrt runs every bounce — and it applies in every direct-link build, not just whole-program. const-link? is new and whole-program-only: in a closed world every non-dynamic var has a stable root, so embed it as a constant (quoted unless it's already callable) rather than reading the cell each reference. Covers what direct-var? can't — ^:redef vars (reloading is off under the flag), data defs, and record type/ctor roots. Dynamic vars stay indirect; a nil (not-yet-defined) root stays indirect and the whole-program re-emit picks it up once the root is in place. Measured on the records ray tracer: hot-path indirect refs (sqrt + data vars) gone; the only indirect refs left are cold defrecord self-references. whole- program-test now also checks a ^:redef fn and a data def so the per-ns vs whole-program comparison guards const-link soundness.
This commit is contained in:
parent
840f699f54
commit
4018eb87ed
2 changed files with 32 additions and 3 deletions
|
|
@ -123,7 +123,24 @@
|
||||||
(and (get (ctx :env) :direct-linking?)
|
(and (get (ctx :env) :direct-linking?)
|
||||||
(not (cell :dynamic))
|
(not (cell :dynamic))
|
||||||
(not (let [m (cell :meta)] (and m (get m :redef))))
|
(not (let [m (cell :meta)] (and m (get m :redef))))
|
||||||
(function? (cell :root))))
|
(let [r (cell :root)] (or (function? r) (cfunction? r)))))
|
||||||
|
|
||||||
|
# Whole-program constant-linking (closed world): under JOLT_WHOLE_PROGRAM every
|
||||||
|
# non-dynamic var has a stable root we can embed as a CONSTANT, eliminating the
|
||||||
|
# per-reference cell deref the indirect path pays. This covers what direct-var?
|
||||||
|
# can't: ^:redef vars (no reloading under the flag, so redef is moot), data vars
|
||||||
|
# (def of a number/vector/etc.), and record-type / non-fn callable roots. The
|
||||||
|
# value is quoted at the emit site unless it's callable (a function/cfunction is
|
||||||
|
# valid in head AND value position as-is). Dynamic vars stay indirect — thread
|
||||||
|
# binding is a runtime mechanism, not redefinition. A nil root (a not-yet-run
|
||||||
|
# forward def) stays indirect so the live deref picks the value up; the
|
||||||
|
# whole-program re-emit (infer-program!, callee-first after full load) then
|
||||||
|
# const-links it once its root is in place.
|
||||||
|
(defn- const-link? [ctx cell]
|
||||||
|
(and (get (ctx :env) :whole-program?)
|
||||||
|
(get (ctx :env) :direct-linking?)
|
||||||
|
(not (cell :dynamic))
|
||||||
|
(not (nil? (cell :root)))))
|
||||||
|
|
||||||
# Fresh Janet symbol for back-end-introduced bindings (arity dispatch). NOT
|
# Fresh Janet symbol for back-end-introduced bindings (arity dispatch). NOT
|
||||||
# Janet's `gensym` — `(use ./core)` shadows it with Jolt's, which returns a jolt
|
# Janet's `gensym` — `(use ./core)` shadows it with Jolt's, which returns a jolt
|
||||||
|
|
@ -579,6 +596,13 @@
|
||||||
:var (let [cell (cell-for ctx (node :ns) (node :name))]
|
:var (let [cell (cell-for ctx (node :ns) (node :name))]
|
||||||
(if (direct-var? ctx cell)
|
(if (direct-var? ctx cell)
|
||||||
(cell :root) # direct link: embed the fn value
|
(cell :root) # direct link: embed the fn value
|
||||||
|
(if (const-link? ctx cell)
|
||||||
|
# whole-program closed world: embed the stable root as a constant.
|
||||||
|
# Callable roots go in bare (valid in head/value position); any
|
||||||
|
# other value is quoted so Janet returns it rather than evaluating
|
||||||
|
# it as code (a bare tuple/struct in code position would be called).
|
||||||
|
(let [r (cell :root)]
|
||||||
|
(if (or (function? r) (cfunction? r)) r (tuple 'quote r)))
|
||||||
# Indirect: live deref, with the var-get FN CALL inlined away
|
# Indirect: live deref, with the var-get FN CALL inlined away
|
||||||
# (jolt-8sq): a non-dynamic var's value is always its root, so
|
# (jolt-8sq): a non-dynamic var's value is always its root, so
|
||||||
# the common case is two native table ops + a branch instead of
|
# the common case is two native table ops + a branch instead of
|
||||||
|
|
@ -597,7 +621,7 @@
|
||||||
(let [qcell (tuple 'quote cell)]
|
(let [qcell (tuple 'quote cell)]
|
||||||
['if ['in qcell :dynamic]
|
['if ['in qcell :dynamic]
|
||||||
(tuple var-get qcell)
|
(tuple var-get qcell)
|
||||||
['in qcell :root]])))
|
['in qcell :root]]))))
|
||||||
# (var x): the var object itself (not its value) — the embedded cell, by
|
# (var x): the var object itself (not its value) — the embedded cell, by
|
||||||
# reference. binding keys its thread-binding frame on this exact cell.
|
# reference. binding keys its thread-binding frame on this exact cell.
|
||||||
:the-var (tuple 'quote (cell-for ctx (node :ns) (node :name)))
|
:the-var (tuple 'quote (cell-for ctx (node :ns) (node :name)))
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,15 @@
|
||||||
(spit (string dir "/wputil.clj")
|
(spit (string dir "/wputil.clj")
|
||||||
(string "(ns wputil)\n"
|
(string "(ns wputil)\n"
|
||||||
"(defrecord V [x y z])\n"
|
"(defrecord V [x y z])\n"
|
||||||
|
# const-link targets (jolt-rvt): a data def and a ^:redef fn are
|
||||||
|
# indirect (cell deref) per-ns but embedded as constants under
|
||||||
|
# whole-program. Soundness => both modes must give the same answer.
|
||||||
|
"(def scale 2.0)\n"
|
||||||
|
"(defn ^:redef bump [x] (+ x 1.0))\n"
|
||||||
# recursive => never inlined; params proven only whole-program
|
# recursive => never inlined; params proven only whole-program
|
||||||
"(defn dot [a b n]\n"
|
"(defn dot [a b n]\n"
|
||||||
" (if (<= n 0) 0.0\n"
|
" (if (<= n 0) 0.0\n"
|
||||||
" (+ (* (:x a) (:x b)) (* (:y a) (:y b)) (* (:z a) (:z b)) (dot a b (dec n)))))\n"))
|
" (+ (* (:x a) (:x b)) (* (:y a) (:y b)) (* (:z a) (:z b)) (bump (* scale (dot a b (dec n)))))))\n"))
|
||||||
(spit (string dir "/wpmain.clj")
|
(spit (string dir "/wpmain.clj")
|
||||||
(string "(ns wpmain (:require [wputil :as v]))\n"
|
(string "(ns wpmain (:require [wputil :as v]))\n"
|
||||||
"(defn -main []\n"
|
"(defn -main []\n"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue