Hint-directed fast arithmetic: fl*/fx* from ^double/^long (round 1)
A ^double/^long param hint (or a float literal) now drives Chez flonum/fixnum ops instead of generic arithmetic — JVM-style primitive hints, available in every build and at -e (not gated on direct-linking or whole-program inference). New pass jolt.passes.numeric: a local forward type-flow seeded from ^double/^long fn-param hints (analyzer attaches :nhints per arity) and float literals, propagated through let inits / arithmetic / if / do. It tags an arithmetic invoke :num-kind :double|:long when every operand is that kind (an integer literal is a wildcard, coerced to a flonum in a double op). The back end lowers a tagged node to fl+/fl-/fl*/fl//fl<?/... or fx+/fx*/fx1+/fxquotient/... (unchecked-add etc. join the fixnum path; == too). Runs last in run-passes, both branches. Soundness: :long is seeded ONLY from an explicit ^long hint, never a bare integer literal, so un-hinted integer code keeps jolt's arbitrary-precision numbers — no fixnum-overflow surprise, no corpus divergence. :double comes from ^double hints and float literals (flonum arithmetic is always flonum, matching the generic result). A ^long hint is a promise the value is a fixnum: fx+ raises on overflow, like a JVM fixed-width long. Numeric-hinted params coerce at fn entry (exact->inexact / jolt->fx), the way the JVM coerces a primitive parameter — so the body's fl*/fx* ops can rely on the type even when a caller passes an exact int (e.g. Chez's (* 0 1.0) => exact 0). Round 1 specializes hinted straight-line / fn-body arithmetic. fl-ops are ~4x generic in a tight Chez loop, but realizing that on loop-carried accumulators needs loop-var typing — round 2. Sound foundation, gated by test/chez/numeric-test.ss.
This commit is contained in:
parent
2c18fcdc61
commit
59905a71fd
9 changed files with 567 additions and 236 deletions
|
|
@ -80,6 +80,15 @@
|
|||
(let [m (form-sym-meta sym)]
|
||||
(when m (let [t (get m :tag)] (when t (record-ctor-key ctx t))))))
|
||||
|
||||
;; A primitive numeric hint (^long / ^double) on a binding symbol -> :long /
|
||||
;; :double, else nil. Drives the fl*/fx* fast path (jolt.passes.numeric). The tag
|
||||
;; is a string on the data reader; tolerate a symbol from macroexpansion too.
|
||||
(defn- nhint-of [ctx sym]
|
||||
(let [m (form-sym-meta sym)
|
||||
t (when m (get m :tag))
|
||||
s (cond (form-sym? t) (form-sym-name t) (string? t) t :else nil)]
|
||||
(cond (= s "double") :double (= s "long") :long :else nil)))
|
||||
|
||||
(defn- analyze-seq [ctx forms env]
|
||||
(let [v (mapv #(analyze ctx % env) forms)
|
||||
n (count v)]
|
||||
|
|
@ -104,19 +113,20 @@
|
|||
;; folds it with a plain reduce — no reduce-over-map in the kernel subset).
|
||||
;; :phints is the parallel vector of [name ctor-key] for record param hints,
|
||||
;; carrying the specific type for the inference to seed.
|
||||
(loop [i 0 fixed [] rest-name nil hints [] phints []]
|
||||
(loop [i 0 fixed [] rest-name nil hints [] phints [] nhints []]
|
||||
(if (< i (count pvec))
|
||||
(let [p (nth pvec i)]
|
||||
(when-not (form-sym? p) (uncompilable "destructuring fn param"))
|
||||
(if (= "&" (form-sym-name p))
|
||||
(let [r (nth pvec (inc i))]
|
||||
(when-not (form-sym? r) (uncompilable "destructuring fn rest"))
|
||||
(recur (+ i 2) fixed (form-sym-name r) hints phints))
|
||||
(let [nm (form-sym-name p) h (hint-of ctx p) ph (phint-of ctx p)]
|
||||
(recur (+ i 2) fixed (form-sym-name r) hints phints nhints))
|
||||
(let [nm (form-sym-name p) h (hint-of ctx p) ph (phint-of ctx p) nh (nhint-of ctx p)]
|
||||
(recur (inc i) (conj fixed nm) rest-name
|
||||
(if h (conj hints [nm h]) hints)
|
||||
(if ph (conj phints [nm ph]) phints)))))
|
||||
{:fixed fixed :rest rest-name :hints hints :phints phints})))
|
||||
(if ph (conj phints [nm ph]) phints)
|
||||
(if nh (conj nhints [nm nh]) nhints)))))
|
||||
{:fixed fixed :rest rest-name :hints hints :phints phints :nhints nhints})))
|
||||
|
||||
;; Clojure lets a later param shadow an earlier same-named one (a macro expander
|
||||
;; uses _ for both its &form and &env slots, so its param list is (_ _ …)); the
|
||||
|
|
@ -154,7 +164,9 @@
|
|||
:body (analyze-seq ctx body env*)}
|
||||
;; carry record param hints (name -> ctor-key) for the inference to seed
|
||||
;; the param type; only when present so a hintless arity stays a struct.
|
||||
arity (if (seq (:phints pp)) (assoc arity :phints (:phints pp)) arity)]
|
||||
arity (if (seq (:phints pp)) (assoc arity :phints (:phints pp)) arity)
|
||||
;; numeric param hints (name -> :long/:double) for jolt.passes.numeric.
|
||||
arity (if (seq (:nhints pp)) (assoc arity :nhints (:nhints pp)) arity)]
|
||||
;; :rest only when variadic — an absent :rest reads back nil, same as before,
|
||||
;; but keeps a fixed arity a nil-free struct rather than a phm.
|
||||
(if rst (assoc arity :rest rst) arity)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue