From 4018eb87ed54c8f71bbf20fd4b0d0435adde844d Mon Sep 17 00:00:00 2001 From: Yogthos Date: Sun, 14 Jun 2026 11:03:28 -0400 Subject: [PATCH] Const-link stable vars under whole-program; direct-link cfunction roots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- src/jolt/backend.janet | 28 +++++++++++++++++++++-- test/integration/whole-program-test.janet | 7 +++++- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/jolt/backend.janet b/src/jolt/backend.janet index c2abfb6..157f784 100644 --- a/src/jolt/backend.janet +++ b/src/jolt/backend.janet @@ -123,7 +123,24 @@ (and (get (ctx :env) :direct-linking?) (not (cell :dynamic)) (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 # 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))] (if (direct-var? ctx cell) (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 # (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 @@ -597,7 +621,7 @@ (let [qcell (tuple 'quote cell)] ['if ['in qcell :dynamic] (tuple var-get qcell) - ['in qcell :root]]))) + ['in qcell :root]])))) # (var x): the var object itself (not its value) — the embedded cell, by # reference. binding keys its thread-binding frame on this exact cell. :the-var (tuple 'quote (cell-for ctx (node :ns) (node :name))) diff --git a/test/integration/whole-program-test.janet b/test/integration/whole-program-test.janet index ba8aea9..3839f65 100644 --- a/test/integration/whole-program-test.janet +++ b/test/integration/whole-program-test.janet @@ -13,10 +13,15 @@ (spit (string dir "/wputil.clj") (string "(ns wputil)\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 "(defn dot [a b n]\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") (string "(ns wpmain (:require [wputil :as v]))\n" "(defn -main []\n"