compiler: IFn collections held in a var/local are callable (jolt-vh5)
The back end emitted a direct Janet call (f args) for every :var/:local callee, but Janet calling a pvec-table does get (no integer key -> nil), a keyword does the wrong thing, etc. — so (let [v [1 2 3]] (v 0)), a keyword/meta-vector in a binding, all returned nil in compile mode (interpret was fine). Now a direct call is emitted only when the callee is provably a function: :fn / :host always, and a :var whose current root is a function; everything else routes through jolt-call, which dispatches IFn correctly (function fast-path first). User/core fn calls stay direct, so no hot-path regression — fixpoint stage1==stage2==stage3 holds. Conformance 218/218 all modes; full suite green. Spec cases added.
This commit is contained in:
parent
d189ee2ded
commit
0878c803d7
2 changed files with 22 additions and 4 deletions
|
|
@ -156,8 +156,21 @@
|
||||||
['do ['var s nil] ['set s body] s])
|
['do ['var s nil] ['set s body] s])
|
||||||
body))
|
body))
|
||||||
|
|
||||||
(defn- direct-call? [fnode]
|
# A direct Janet call (f args) is only correct when the callee is definitely a
|
||||||
(case (fnode :op) :var true :local true :fn true :host true false))
|
# function: Janet calling a pvec/keyword/etc. does get (or the wrong thing), not
|
||||||
|
# IFn dispatch. So only emit a direct call for :fn / :host (always functions) and
|
||||||
|
# a :var whose CURRENT root is a function (the common user/core-fn case). A :var
|
||||||
|
# holding an IFn COLLECTION (vector/keyword/set used as a fn) or a :local of
|
||||||
|
# unknown value falls through to jolt-call, which dispatches IFn correctly
|
||||||
|
# (function fast-path first). Trade-off, like direct-linking: a fn-var redefined
|
||||||
|
# to a collection after this call was compiled would still emit a direct call.
|
||||||
|
(defn- direct-call? [ctx fnode]
|
||||||
|
(case (fnode :op)
|
||||||
|
:fn true
|
||||||
|
:host true
|
||||||
|
:var (let [r (get (cell-for ctx (fnode :ns) (fnode :name)) :root)]
|
||||||
|
(or (function? r) (cfunction? r)))
|
||||||
|
false))
|
||||||
|
|
||||||
# Hot primitives emitted as native Janet ops (host-specific optimization): a
|
# Hot primitives emitted as native Janet ops (host-specific optimization): a
|
||||||
# call to clojure.core/+ etc. becomes (+ …) rather than a var deref + variadic
|
# call to clojure.core/+ etc. becomes (+ …) rather than a var deref + variadic
|
||||||
|
|
@ -188,7 +201,7 @@
|
||||||
'++ ['+ (in args 0) 1]
|
'++ ['+ (in args 0) 1]
|
||||||
'-- ['- (in args 0) 1]
|
'-- ['- (in args 0) 1]
|
||||||
(tuple nop ;args))
|
(tuple nop ;args))
|
||||||
(direct-call? (node :fn)) (tuple (emit ctx (node :fn)) ;args)
|
(direct-call? ctx (node :fn)) (tuple (emit ctx (node :fn)) ;args)
|
||||||
(tuple jolt-call (emit ctx (node :fn)) ;args)))
|
(tuple jolt-call (emit ctx (node :fn)) ;args)))
|
||||||
|
|
||||||
(defn- emit-vector [ctx node]
|
(defn- emit-vector [ctx node]
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,12 @@
|
||||||
["count" "3" "(count [1 2 3])"]
|
["count" "3" "(count [1 2 3])"]
|
||||||
["contains? index" "true" "(contains? [:a :b] 1)"]
|
["contains? index" "true" "(contains? [:a :b] 1)"]
|
||||||
["contains? past end" "false" "(contains? [:a] 3)"]
|
["contains? past end" "false" "(contains? [:a] 3)"]
|
||||||
["vector as fn" ":b" "([:a :b :c] 1)"])
|
["vector as fn" ":b" "([:a :b :c] 1)"]
|
||||||
|
# An IFn collection held in a binding (not just a literal) must dispatch as IFn,
|
||||||
|
# not as a host call: applies to vectors, keywords, and meta-bearing vectors.
|
||||||
|
["vector-in-local as fn" "20" "(let [v [10 20 30]] (v 1))"]
|
||||||
|
["keyword-in-local as fn" "7" "(let [k :a] (k {:a 7}))"]
|
||||||
|
["meta vector as fn" "10" "((with-meta [10 20] {:k 1}) 0)"])
|
||||||
|
|
||||||
(defspec "vector / update (persistent)"
|
(defspec "vector / update (persistent)"
|
||||||
["conj appends" "[1 2 3]" "(conj [1 2] 3)"]
|
["conj appends" "[1 2 3]" "(conj [1 2] 3)"]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue