See through or/and in truthy-elision
A loop test like (or (>= i cap) (> ... 4.0)) desugars to (let* [g (>= i cap)] (if (truthy? g) g (> ... 4.0))) and the whole thing was wrapped in jolt-truthy? because returns-scheme-bool? only looked at :const and :invoke nodes, not the let*/if an or/and expands to. The wrapper defeats Chez's branch inlining on the hot loop edge. Make returns-scheme-bool? recursive over :if (both branches bool), :let (body bool, tracking which bound locals hold a Scheme boolean), and :local (in that set). or/and over bool-returning ops then read as Scheme booleans and the outer wrapper drops. Still sound: eliding only when the value is provably #t/#f — a jolt-nil is a truthy record in Chez, so a false positive would be a real bug, and the recursion only proves bool-ness through ops already known to return one. No bench regression; the win lands on hinted float loops where the branch, not boxed arithmetic, is the cost.
This commit is contained in:
parent
0a7e818700
commit
5d0ba28128
3 changed files with 131 additions and 113 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -566,14 +566,32 @@
|
|||
core)))
|
||||
|
||||
;; Does this IR node emit to an expression that yields a Scheme boolean? Used to
|
||||
;; drop the redundant jolt-truthy? on an :if test.
|
||||
(defn- returns-scheme-bool? [node]
|
||||
(cond
|
||||
(and (= :const (:op node)) (boolean? (:val node))) true
|
||||
(= :invoke (:op node))
|
||||
(let [nop (native-op (:fn node) (count (:args node)))]
|
||||
(if (and nop (bool-returning-ops nop)) true false))
|
||||
:else false))
|
||||
;; drop the redundant jolt-truthy? on an :if test. Sees through the let*/if an
|
||||
;; (or ...)/(and ...) of bool-returning ops desugars to: `or` is
|
||||
;; (let* [g E1] (if (truthy? g) g E2)), `and` is (let* [g E1] (if (truthy? g) E2 g))
|
||||
;; — both return a Scheme boolean when E1/E2 are bool ops, since the value yielded
|
||||
;; is always one of the (boolean) operand results. `bools` tracks let-bound locals
|
||||
;; proven to hold a Scheme boolean.
|
||||
(defn- returns-scheme-bool?
|
||||
([node] (returns-scheme-bool? node #{}))
|
||||
([node bools]
|
||||
(cond
|
||||
(and (= :const (:op node)) (boolean? (:val node))) true
|
||||
(= :invoke (:op node))
|
||||
(let [nop (native-op (:fn node) (count (:args node)))]
|
||||
(boolean (and nop (bool-returning-ops nop))))
|
||||
(= :local (:op node)) (contains? bools (:name node))
|
||||
(= :if (:op node))
|
||||
(and (returns-scheme-bool? (:then node) bools)
|
||||
(returns-scheme-bool? (:else node) bools))
|
||||
(= :let (:op node))
|
||||
(let [bools' (reduce (fn [s b]
|
||||
(if (returns-scheme-bool? (nth b 1) s)
|
||||
(conj s (nth b 0))
|
||||
(disj s (nth b 0))))
|
||||
bools (:bindings node))]
|
||||
(returns-scheme-bool? (:body node) bools'))
|
||||
:else false)))
|
||||
|
||||
(defn emit [node]
|
||||
(case (:op node)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue