Tree-shake: count #'x references; multi-app soundness smoke

Two things, both from studying Stalin's closed-world DCE:

1. Soundness fix: dce-collect-refs now counts a :the-var (#'x / (var x)) reference,
   not just a :var. Stalin's rule is that ANY reference — value position, not only a
   direct call — keeps the target live; a var referenced as #'x would otherwise be
   wrongly dropped. (My :var collection already covered value-position refs; this
   closes the the-var hole.)

2. host/chez/tree-shake-smoke.sh (make shakesmoke): builds example apps default vs
   --tree-shake and requires identical output — the real risk is shaking a binary
   that pulled libraries via deps.edn. Covers markdown/malli/commonmark/hiccup
   (git-lib apps). All produce byte-identical output shaken vs not, and drop
   ~0.8-1MB (malli 10.0MB -> 9.0MB) from the compiler-drop. Slow; not in the default
   gate. Skips without the examples repo.
This commit is contained in:
Yogthos 2026-06-23 20:27:21 -04:00
parent 709d152a1e
commit e0c1564ff9
3 changed files with 80 additions and 6 deletions

View file

@ -93,17 +93,22 @@
;; only the re-emitted namespaces are shaken.
(define dce-kw-op (keyword #f "op"))
(define dce-kw-var (keyword #f "var"))
(define dce-kw-the-var (keyword #f "the-var"))
(define dce-kw-def (keyword #f "def"))
(define dce-kw-ns (keyword #f "ns"))
(define dce-kw-name (keyword #f "name"))
(define dce-reduce-children (var-deref "jolt.ir" "reduce-ir-children"))
;; "ns/name" of every :var reference anywhere in node, prepended to acc. Arg order
;; (acc node) matches reduce-ir-children's fold fn, so it nests directly.
;; "ns/name" of every var reference anywhere in node, prepended to acc. Counts BOTH
;; a :var (call head or value) and a :the-var (#'x / (var x)) — per Stalin's rule,
;; any reference (not just a direct call) keeps the target live, so a var passed as a
;; value or referenced as #'x is reachable. Arg order (acc node) matches
;; reduce-ir-children's fold fn, so it nests directly.
(define (dce-collect-refs acc node)
(if (eq? (jolt-get node dce-kw-op) dce-kw-var)
(cons (string-append (jolt-get node dce-kw-ns) "/" (jolt-get node dce-kw-name)) acc)
(dce-reduce-children dce-collect-refs acc node)))
(let ((op (jolt-get node dce-kw-op)))
(if (or (eq? op dce-kw-var) (eq? op dce-kw-the-var))
(cons (string-append (jolt-get node dce-kw-ns) "/" (jolt-get node dce-kw-name)) acc)
(dce-reduce-children dce-collect-refs acc node))))
;; The fqn of a bare top-level def (the only prunable form), else #f.
(define (dce-def-fqn node)