| author | Alan Dipert
<alan@dipert.org> 2026-11-10 22:11:45 UTC |
| committer | Alan Dipert
<alan@dipert.org> 2026-11-10 22:11:45 UTC |
| parent | e46e6b37181c1da11b381b02bc0c708f36ad93e1 |
| md/_MutableLocals.md | +15 | -13 |
diff --git a/md/_MutableLocals.md b/md/_MutableLocals.md index 6ab5626..fa53c40 100644 --- a/md/_MutableLocals.md +++ b/md/_MutableLocals.md @@ -163,23 +163,25 @@ This is where the earlier compiler work pays off. A later pass can inspect the g The compiler makes this choice before emitting Clojure, keeping the dispatcher when it can't safely reconstruct the control flow. -For our sum with the early exit, it emits the loop below, shown with shorter local names. `loop*` and `let*` are Clojure's underlying loop and binding forms. The annotated [`RT.box(Object)`](https://github.com/clojure/clojure/blob/clojure-1.12.6/src/jvm/clojure/lang/RT.java#L1037-L1039) calls return their arguments unchanged, keeping `sum` typed as an object; arithmetic can still allocate boxed values. A surrounding `let*` initializes `sum` to zero and sets `limit` to ten: +For our sum with the early exit, it emits this, with shorter local names. `loop*` and `let*` are Clojure's underlying loop and binding forms: ```clojure -(loop* [sum (^{:param-tags [java.lang.Object]} clojure.lang.RT/box sum) - i '0] - (if (clojure.lang.Numbers/lt i limit) - (let* [done? (clojure.lang.Util/equiv i '5)] - (if done? - sum - (let* [next-sum - (^{:param-tags [java.lang.Object]} clojure.lang.RT/box - (clojure.lang.Numbers/add ^java.lang.Object sum i))] - (recur next-sum (clojure.lang.Numbers/unchecked_inc i))))) - sum)) +(loop* [sum '0 i '0] + (if (^{:param-tags [long long]} clojure.lang.Numbers/lt i '10) + (if (^{:param-tags [long long]} clojure.lang.Util/equiv i '5) + (^{:param-tags [java.lang.Object]} clojure.lang.RT/box sum) + (let* [next-sum + (^{:param-tags [long long]} clojure.lang.Numbers/add sum i)] + (recur next-sum + (^{:param-tags [long]} clojure.lang.Numbers/unchecked_inc i)))) + (^{:param-tags [java.lang.Object]} clojure.lang.RT/box sum))) ``` -When the index reaches five, the `done?` branch returns the sum: 10. That was our `goto`. Having gone to considerable trouble to accommodate it, we have compiled it out of existence. +Both loop variables are primitive longs. A value-analysis pass proves that each addition produces a long or throws on overflow; a separate storage pass checks that no use observes the intermediate boxes' identities. Only then does numeric lowering select the primitive overloads, spelled out here by `:param-tags`. The emitter gets those decisions already made. + +The [`RT.box(Object)`](https://github.com/clojure/clojure/blob/clojure-1.12.6/src/jvm/clojure/lang/RT.java#L1037-L1039) calls are at the exits: Clojure boxes the final long, and `RT.box(Object)` returns that object unchanged. The loop's arithmetic stays unboxed. Putting this boundary around the whole loop would make Clojure generate an extra function to evaluate it. Apparently even the parentheses need supervision. + +When the index reaches five, the inner `if` returns the sum: 10. That was our `goto`. Having gone to considerable trouble to accommodate it, we have compiled it out of existence. ## Back to the inversion counter