Bare-index field reads for statically-known records (#228)
When the inference types a keyword-lookup receiver as a record — it carries the field-order :shape and :hint :struct from the whole-program fixpoint — the back end reads the field by its declared slot via jrec-field-at instead of jolt-get. That skips the jolt-get case-lambda, the dispatch fn, and the field-key hashtable lookup, leaving a jrec? check + a static-index vector-ref. jrec-field-at falls back to jolt-get when the receiver isn't the expected record (a map downgraded by dissoc, or a value the inference mistyped), so it stays correct if the static type is wrong. Only the no-default form takes the bare path (a declared field is always present). Sound only for non-nil records: a self-recursive param that can be nil (e.g. binary-trees check-tree, whose untagged child is nil at leaves) types :any and keeps jolt-get — the whole-program fixpoint demotes it. The target is non-nil record params, like a Vec3 dot product (~5% there; boxed-flonum arithmetic dominates the rest, a separate numeric lever). run-fieldread.ss gate: emitted form uses jrec-field-at at the right slot and matches jolt-get for each declared field; a non-field key and a default-arg form keep the generic path. make test / shakesmoke green, 0 new divergences. Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
af11aaa7ff
commit
de31221573
5 changed files with 188 additions and 80 deletions
|
|
@ -482,6 +482,14 @@
|
|||
(let [op (case kind :double (dbl-ops nm) :long (lng-ops nm) :bigdec (bd-ops nm))]
|
||||
(order-args (fn [as] (str "(" op " " (str/join " " as) ")"))))))
|
||||
|
||||
;; slot of a declared field key in a record's field-order shape, or nil.
|
||||
(defn- struct-field-index [shape kw]
|
||||
(when shape
|
||||
(loop [i 0]
|
||||
(cond (>= i (count shape)) nil
|
||||
(= (nth shape i) kw) i
|
||||
:else (recur (inc i))))))
|
||||
|
||||
(defn- emit-invoke [node]
|
||||
(let [fnode (:fn node)
|
||||
arg-nodes (:args node)
|
||||
|
|
@ -519,9 +527,18 @@
|
|||
(and nop (= 1 (count args)) (cmp1-ops nop)) (str "(begin " (first args) " #t)")
|
||||
nop (order-args (fn [as] (str "(" nop " " (str/join " " as) ")")))
|
||||
;; (:k coll [default]) -> (jolt-get coll :k [default]) — the key (fnode) is a
|
||||
;; const, so only the coll/default args carry order.
|
||||
;; const, so only the coll/default args carry order. When the inference typed
|
||||
;; the receiver as a record whose declared fields include :k (it carries the
|
||||
;; field-order :shape), read the field by its static slot — no field-key
|
||||
;; lookup, no jolt-get dispatch. Only the no-default form (a declared field is
|
||||
;; always present, so a default is never taken).
|
||||
(= kind :keyword)
|
||||
(order-args (fn [as] (str "(jolt-get " (first as) " " (emit fnode) (defstr as) ")")))
|
||||
(let [recv (first arg-nodes)
|
||||
idx (when (and (= :struct (:hint recv)) (= 1 (count arg-nodes)))
|
||||
(struct-field-index (:shape recv) (:val fnode)))]
|
||||
(if idx
|
||||
(order-args (fn [as] (str "(jrec-field-at " (first as) " " idx " " (emit fnode) ")")))
|
||||
(order-args (fn [as] (str "(jolt-get " (first as) " " (emit fnode) (defstr as) ")")))))
|
||||
;; (coll k [default]) -> (jolt-get coll k [default]) — coll (fnode) is the
|
||||
;; callee, evaluated before the key/default args.
|
||||
(= kind :coll)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue