| author | Alan Dipert
<alan@dipert.org> 2026-11-11 04:32:05 UTC |
| committer | Alan Dipert
<alan@dipert.org> 2026-11-11 04:32:05 UTC |
| parent | ddf183af2f4a1b935aa1666fe5c8eaba65bb36b9 |
| doc/compiler.md | +2 | -2 |
| doc/tutorial.md | +2 | -2 |
| src/com/tailrecursion/blacktide/compiler.clj | +4 | -7 |
| src/com/tailrecursion/blacktide/compiler/languages.clj | +8 | -10 |
| src/com/tailrecursion/blacktide/compiler/passes/joins.clj | +7 | -7 |
| src/com/tailrecursion/blacktide/compiler/passes/structure.clj | +4 | -4 |
| test/com/tailrecursion/blacktide/backend_test.clj | +7 | -3 |
diff --git a/doc/compiler.md b/doc/compiler.md index 6bf5e9d..21b0ba8 100644 --- a/doc/compiler.md +++ b/doc/compiler.md @@ -36,11 +36,11 @@ The outer bindings enter as one lexical scope. `Native` contains analyzed subtre ```text Dispatcher: CFG → Machine → Code -Structured: CFG → Machine → Numeric → Analysis → Planned → Joined +Structured: CFG → Machine → Numeric → Planned → Joined → Represented → Lexical → Compact → Code ``` -`numeric` recognizes numeric runtime calls and implicit Java numeric conversions, retaining their original signatures. `value-facts` proves scalar values across assignments and backedges; a boxed result can still be known to contain a long. `storage` chooses representations after accounting for consumers, copies and observable identity. `lexical` makes loop arguments explicit. `numeric-lower` implements the proven equivalences using host operations and ordinary control flow. `compact` simplifies safe intermediates. Emission applies the Object result boundary at public exits and leaves join values internal. +`numeric` recognizes numeric runtime calls and implicit Java numeric conversions, retaining their original signatures. Control recovery uses that graph without numeric value proofs. Once it succeeds, `value-facts` analyzes the `Numeric` graph to produce `Analysis`: scalar facts across assignments and backedges, including long values held in boxes. `storage` combines those facts with the recovered tree to choose representations after accounting for consumers, copies and observable identity. Dispatcher fallback skips value inference. `lexical` makes loop arguments explicit. `numeric-lower` implements the proven equivalences using host operations and ordinary control flow. `compact` simplifies safe intermediates. Emission applies the Object result boundary at public exits and leaves join values internal. Inference and lowering share `numeric-rules`. Knowing the operands' values is insufficient: their original overload determines overflow, conversion and reference-return behavior. diff --git a/doc/tutorial.md b/doc/tutorial.md index a1cd658..eb86858 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -491,9 +491,9 @@ Two terms help here: a _join_ is a point where control-flow paths meet, and a _l | Pass group | Purpose | | --- | --- | -| Feasibility, numeric recognition and value facts | Select known branches, recognize audited numeric operations and prove scalar values. | +| Feasibility and numeric recognition | Select known branches and recognize audited numeric operations. | | Join planning and structured recovery | Arrange shared paths and recover conditionals and loops. | -| Invariants and storage | Remove loop parameters that only forward unchanged outer values; choose storage without changing object identity. | +| Value facts, invariants and storage | Prove scalar values, remove loop parameters that only forward unchanged outer values, and choose storage without changing object identity. | | Numeric lowering | Select proven primitive overloads; preserve other calls unchanged. | | Compaction and emission | Remove safe intermediates and emit Clojure with an Object boundary at public exits. | diff --git a/src/com/tailrecursion/blacktide/compiler.clj b/src/com/tailrecursion/blacktide/compiler.clj index 0e02da3..b7ea436 100644 --- a/src/com/tailrecursion/blacktide/compiler.clj +++ b/src/com/tailrecursion/blacktide/compiler.clj @@ -31,14 +31,11 @@ (np/defpass compile-program {:from [l/CFG Program] :to [l/Machine Code] :body true} [cfg] - (let [analysis (-> cfg - feasible/program - graph/allocate - numeric/program - value-facts/analyze) - decision (-> analysis joins/plan structure/recover)] + (let [machine (-> cfg feasible/program graph/allocate numeric/program) + decision (-> machine joins/plan structure/recover)] (if (= :structured (:op decision)) - (let [program (->> (:body decision) + (let [analysis (value-facts/analyze machine) + program (->> (:body decision) (invariants/expression #{}) (storage/select-storage analysis) lexical/program diff --git a/src/com/tailrecursion/blacktide/compiler/languages.clj b/src/com/tailrecursion/blacktide/compiler/languages.clj index a613837..5113cf1 100644 --- a/src/com/tailrecursion/blacktide/compiler/languages.clj +++ b/src/com/tailrecursion/blacktide/compiler/languages.clj @@ -186,7 +186,7 @@ Graph (t/HMap :mandatory {:entry Label :blocks (t/Map Label Block)} :optional {:slots (t/Vec t/Sym) :live (t/Map Label (t/Set t/Sym))} :complete? true)}}) -;; Facts and plans used by structured control recovery. +;; Numeric value facts and control plans are independent backend analyses. (np/deflanguage Analysis {Type (t/U ':bottom ':boxed java.lang.Class) Types (t/Map t/Sym Type) @@ -194,15 +194,13 @@ Program (t/HMap :mandatory {:machine l/Numeric-Program :types Types :reachable (t/Set l/Machine-Label)} :complete? true)}) -(np/derive-language Planned Analysis - {:replace - {Postdominators (t/Map l/Machine-Label (t/Set l/Machine-Label)) - Region (t/HMap :mandatory {:definite (t/Set t/Sym) :changed (t/Set t/Sym)} :complete? true) - Regions (t/Map l/Machine-Label Region) - Join (t/HMap :mandatory {:target l/Machine-Label :result t/Sym} :complete? true) - Joins (t/Map l/Machine-Label Join) - Program (t/HMap :mandatory {:machine l/Numeric-Program :types Types - :reachable (t/Set l/Machine-Label) :joins Joins} :complete? true)}}) +(np/deflanguage Planned + {Postdominators (t/Map l/Machine-Label (t/Set l/Machine-Label)) + Region (t/HMap :mandatory {:definite (t/Set t/Sym) :changed (t/Set t/Sym)} :complete? true) + Regions (t/Map l/Machine-Label Region) + Join (t/HMap :mandatory {:target l/Machine-Label :result t/Sym} :complete? true) + Joins (t/Map l/Machine-Label Join) + Program (t/HMap :mandatory {:machine l/Numeric-Program :joins Joins} :complete? true)}) ;; Structured recovery produces Joined expressions. Representation selection ;; produces Represented; lexical and numeric lowering follow it. diff --git a/src/com/tailrecursion/blacktide/compiler/passes/joins.clj b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj index 673a392..a864565 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/joins.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/joins.clj @@ -61,10 +61,10 @@ (when (and (<= (count outputs) 1) (symbol? result) (contains? (:definite proof) result)) {:target target :result result}))))))))) -(np/defpass plan {:from [l/Analysis Program] :to [l/Planned Program] :body true} - [analysis] - (let [machine (:machine analysis) known (postdominators machine)] - (assoc analysis :joins - (reduce (fn [^{::t/- l/Planned-Joins} out ^{::t/- l/Machine-Label} id] - (if-let [join (candidate machine known id)] (assoc out id join) out)) - {} (keys (:blocks machine)))))) +(np/defpass plan {:from [l/Numeric Program] :to [l/Planned Program] :body true} + [machine] + (let [known (postdominators machine)] + {:machine machine + :joins (reduce (fn [^{::t/- l/Planned-Joins} out ^{::t/- l/Machine-Label} id] + (if-let [join (candidate machine known id)] (assoc out id join) out)) + {} (keys (:blocks machine)))})) diff --git a/src/com/tailrecursion/blacktide/compiler/passes/structure.clj b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj index 284249d..dae9c77 100644 --- a/src/com/tailrecursion/blacktide/compiler/passes/structure.clj +++ b/src/com/tailrecursion/blacktide/compiler/passes/structure.clj @@ -141,9 +141,9 @@ :native-sites))) (np/defpass recover {:from [l/Planned Program] :to [l/Joined Decision] :body true} - [analysis] - (let [machine (:machine analysis) reachable (:reachable analysis) - [_ headers] (scan machine #{} [#{} #{}] (:entry machine)) + [plan] + (let [machine (:machine plan) + [reachable headers] (scan machine #{} [#{} #{}] (:entry machine)) dead (apply dissoc (:blocks machine) reachable) header (first headers)] (cond @@ -151,7 +151,7 @@ (not (every? harmless-block? (vals dead))) {:op :fallback :reason :validation} :else (let [slots (if header (vec (sort (get (:live machine) header #{}))) []) - ctx {:machine machine :header header :slots slots :joins (:joins analysis) :stop nil} + ctx {:machine machine :header header :slots slots :joins (:joins plan) :stop nil} budget (* 4 (reduce + 0 (map #(inc (count (:ops %))) (vals (select-keys (:blocks machine) reachable))))) result (edge ctx false budget (:entry machine))] (if (= :built (:op result)) diff --git a/test/com/tailrecursion/blacktide/backend_test.clj b/test/com/tailrecursion/blacktide/backend_test.clj index 0afcb6a..c5fdd1c 100644 --- a/test/com/tailrecursion/blacktide/backend_test.clj +++ b/test/com/tailrecursion/blacktide/backend_test.clj @@ -182,13 +182,17 @@ (do (psetq a b b (+ a b)) (setq i (inc i)) (goto again)) [a b]))] (is (= :structured (:op (capture #'structure/recover form)))) + (is (some? (capture #'facts/analyze form)) "Structured emission needs numeric value proofs") (doseq [mode [:default :dispatcher]] (let [events (atom [])] (is (= [8 13] ((compile-program form mode) events))) (is (= [[0 0 1] [1 1 1] [2 1 2] [3 2 3] [4 3 5] [5 5 8] [6 8 13]] @events))))) - (is (= :cycles (:reason (capture #'structure/recover '(loophole [i 0 j 0] - (while (< i 2) (setq i (inc i))) - (while (< j 3) (setq j (inc j))) [i j]))))) + (let [form '(loophole [i 0 j 0] + (while (< i 2) (setq i (inc i))) + (while (< j 3) (setq j (inc j))) [i j])] + (is (= :cycles (:reason (capture #'structure/recover form)))) + (is (nil? (capture #'facts/analyze form)) "Dispatcher fallback skips unused numeric value proofs") + (is (= [2 3] ((compile-program form :default) nil)))) (let [step '(if (odd? i) (setq x (inc x) y (inc y)) (setq x (+ x 2) y (+ y 2))) form (list 'loophole '[i 0 x 0 y 0] (list* 'while '(< i 3) (concat (repeat 8 step) ['(setq i (inc i))])) '[x y])]