Calls compile direct (target value embedded) iff the compiling unit has direct-linking on, the target isn't ^:redef/^:dynamic, and it's an already-defined fn; otherwise indirect (live var deref, redefinable). Direct emission is guarded to function values only — embedding a non-function would make Janet evaluate it as code. :aot-core? (init opt, default true; JOLT_AOT_CORE=0 disables) compiles the core tiers with direct-linking on so core->core calls are direct. User/REPL code compiles indirect by default, so redefining functions in the REPL works with no annotation, exactly like Clojure. :aot-core? false makes core indirect too — the whole language becomes redefinable. ^:redef / ^:dynamic on a target force it indirect even inside a direct-linked unit. Also fixes a pre-existing gap this surfaced: compiled def dropped ALL var metadata (so ^:dynamic was silently lost under compilation, and ^:redef couldn't work). def metadata now flows analyzer -> IR -> back end (new form-sym-meta host fn, def-node :meta, var-setter-meta) and is applied to the cell. Matrix test in test/integration/direct-linking-test.janet. Conformance 218/218 in all three modes under both :aot-core? true and false; battery holds 3916; binary rebuilt.
50 lines
2 KiB
Clojure
50 lines
2 KiB
Clojure
(ns jolt.ir
|
|
"Host-neutral intermediate representation for the Jolt compiler.
|
|
|
|
The analyzer (jolt.analyzer) produces IR; a host back end consumes it. IR nodes
|
|
are plain maps tagged with :op — no host values embedded. Globals reference vars
|
|
by name (:ns/:name), never by a host var cell, so the IR is portable and
|
|
AOT-safe. This namespace is pure Clojure (portable jolt-core): it depends on
|
|
nothing host-specific.")
|
|
|
|
;; Node constructors. Kept as data so any back end can pattern-match on :op.
|
|
|
|
(defn const [v] {:op :const :val v})
|
|
|
|
(defn local [name] {:op :local :name name})
|
|
|
|
;; A global var reference, by name. The back end resolves it to a host var.
|
|
(defn var-ref [ns name] {:op :var :ns ns :name name})
|
|
|
|
;; A runtime primitive (cons, +, get, apply, …) the back end maps to the host RT.
|
|
(defn rt [name] {:op :rt :name name})
|
|
|
|
;; A name that resolves only via the host's own environment (e.g. + or int? on
|
|
;; Janet) — the back end emits a host-appropriate reference.
|
|
(defn host-ref [name] {:op :host :name name})
|
|
|
|
(defn if-node [test then else] {:op :if :test test :then then :else else})
|
|
|
|
(defn do-node [statements ret] {:op :do :statements statements :ret ret})
|
|
|
|
(defn invoke [f args] {:op :invoke :fn f :args args})
|
|
|
|
;; meta is the var metadata (e.g. {:dynamic true} / {:redef true}) the back end
|
|
;; applies to the cell; nil when the def name carried none.
|
|
(defn def-node
|
|
([ns name init] {:op :def :ns ns :name name :init init :meta nil})
|
|
([ns name init meta] {:op :def :ns ns :name name :init init :meta meta}))
|
|
|
|
(defn let-node [bindings body] {:op :let :bindings bindings :body body})
|
|
|
|
;; A fn is one or more arities. Each arity: {:params [..] :rest name|nil :body ir}.
|
|
(defn fn-node [name arities] {:op :fn :name name :arities arities})
|
|
|
|
(defn vector-node [items] {:op :vector :items items})
|
|
(defn map-node [pairs] {:op :map :pairs pairs})
|
|
(defn set-node [items] {:op :set :items items})
|
|
|
|
(defn quote-node [form] {:op :quote :form form})
|
|
(defn throw-node [expr] {:op :throw :expr expr})
|
|
|
|
(defn op [node] (:op node))
|