Skip to content

Commit 083fac8

Browse files
committed
CLJS JIT: compile keyword calls instead of escaping
1 parent 3d88386 commit 083fac8

2 files changed

Lines changed: 38 additions & 7 deletions

File tree

src/sci/impl/analyzer.cljc

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,19 +2130,27 @@
21302130
(let [stack (utils/stack-frame m f-meta)]
21312131
(sci.impl.types/->Node nil stack)))))))
21322132
(keyword? f*)
2133+
;; a cljs Keyword's -invoke is (get coll kw [not-found]), so the
2134+
;; jit compiles it as that call instead of escaping. Without an
2135+
;; ast one keyword lookup drops the whole enclosing fn out of
2136+
;; locals mode.
21332137
(let [children (analyze-children ctx (rest expr))
21342138
ccount (count children)]
21352139
(case ccount
21362140
1 (let [arg (nth children 0)]
2137-
(sci.impl.types/->Node
2138-
(f* (t/eval arg ctx bindings))
2139-
nil))
2141+
(t/attach-ast
2142+
(sci.impl.types/->Node
2143+
(f* (t/eval arg ctx bindings))
2144+
nil)
2145+
[:call-direct get [arg f*] nil]))
21402146
2 (let [arg0 (nth children 0)
21412147
arg1 (nth children 1)]
2142-
(sci.impl.types/->Node
2143-
(f* (t/eval arg0 ctx bindings)
2144-
(t/eval arg1 ctx bindings))
2145-
nil))
2148+
(t/attach-ast
2149+
(sci.impl.types/->Node
2150+
(f* (t/eval arg0 ctx bindings)
2151+
(t/eval arg1 ctx bindings))
2152+
nil)
2153+
[:call-direct get [arg0 f* arg1] nil]))
21462154
(throw-error-with-location (str "Wrong number of args (" ccount ") passed to: " f*) expr)))
21472155
:else
21482156
(let [f (analyze ctx f*)

test/sci/jit_test.cljs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,29 @@
151151
;; stacks table, one get, 4 keywords: 9 without dedup
152152
(is (= 6 (count (re-seq #"c\d+=C\[" preamble))) preamble))))))
153153

154+
(deftest jit-keyword-call-parity-test
155+
(testing "(:k coll) and (:k coll default) agree with the interpreter"
156+
(doseq [kw [":a" ":missing" ":a/b"]
157+
coll ["{:a 1}" "{}" "nil" "[1 2]" "\"s\"" "#{:a}" "(list 1)"
158+
"(js-obj \"a\" 1)" "1" ":a" "(sorted-map :a 1)"]
159+
dflt [nil ":dflt"]]
160+
(let [src (str "((fn [c] (" kw " c" (when dflt (str " " dflt)) ")) " coll ")")
161+
[interp jitted] (eval-both src)]
162+
(is (= interp jitted) src)))))
163+
164+
(deftest jit-keyword-call-compiles-test
165+
(testing "(:k m) compiles instead of escaping to the interpreter"
166+
(when (do (jit/enable!) (jit/enabled?))
167+
(doseq [src ["(fn [m] (:a m))" "(fn [m] (:a m :dflt))"]]
168+
(vreset! jit/last-srcs [])
169+
(vreset! jit/collect-srcs? true)
170+
((sci/eval-string* (sci/init {}) src) {:a 1})
171+
(vreset! jit/collect-srcs? false)
172+
(let [js (apply str @jit/last-srcs)]
173+
(is (not (str/includes? js "H.ev")) (str src " escaped: " js))
174+
;; escape-free means locals mode, no invocation array
175+
(is (not (str/includes? js "new Array")) (str src " left locals mode: " js)))))))
176+
154177
(deftest jit-var-mutation-visibility-test
155178
;; BOTH modes cache var derefs keyed on sci.impl.vars/var-epoch, so
156179
;; interp/jit agreement alone can't catch a missed bump — each case

0 commit comments

Comments
 (0)