Skip to content

Commit 91f8560

Browse files
committed
Merge branch 'main' into trampolines
2 parents 7e163db + a35d167 commit 91f8560

6 files changed

Lines changed: 167 additions & 9 deletions

File tree

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Version 3.1.0 (LLVM15-20, 22) Pending
2+
3+
## Optimized
4+
* Comparisons (`eq`, `<`, etc.) are optimized out based on type information.
5+
16
# Version 3.0.1 (LLVM15-20, 22) 2026-06-24
27

38
## Fixed

src/lisp/kernel/cleavir/bir-to-bmir.lisp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@
187187
(deftransform-f error (constantly 'type-error) (constantly t)
188188
(4 2) t (eql type-error) (eql :expected-type) t (eql :datum) t)
189189

190+
(deftransform eq eq t t)
191+
190192
(deftransform core:to-single-float core::double-to-single double-float)
191193
(deftransform core:to-single-float core::fixnum-to-single fixnum)
192194
(deftransform core:to-double-float core::single-to-double single-float)

src/lisp/kernel/cleavir/interval.lisp

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
(low nil :type (or null real (cons real null)))
66
(high nil :type (or null real (cons real null))))
77

8+
(defun make-unbounded-interval () (make-interval nil nil))
9+
(defun make-empty-interval () (make-interval '(0) '(0)))
10+
811
(defun bound-parts (finite-bound)
912
(if (consp finite-bound)
1013
(values (car finite-bound) t)
@@ -21,6 +24,27 @@
2124
(let ((r (funcall unop b)))
2225
(if bxp (list r) r))))
2326

27+
(defun empty-interval-p (interval)
28+
(let ((low (interval-low interval)) (high (interval-high interval)))
29+
(and low high
30+
(multiple-value-bind (low lxp) (bound-parts low)
31+
(multiple-value-bind (high hxp) (bound-parts high)
32+
(or (> low high) (and (= low high) (or lxp hxp))))))))
33+
34+
(defun intervals-disjoint-p (i1 i2)
35+
(let ((i1l (interval-low i1)) (i2l (interval-low i2))
36+
(i1h (interval-high i1)) (i2h (interval-high i2)))
37+
(or (and i1h i2l
38+
(multiple-value-bind (i1high i1hxp) (bound-parts i1h)
39+
(multiple-value-bind (i2low i2lxp) (bound-parts i2l)
40+
(or (< i1high i2low)
41+
(and (= i1high i2low) (or i1hxp i2lxp))))))
42+
(and i2h i1l
43+
(multiple-value-bind (i2high i2hxp) (bound-parts i2h)
44+
(multiple-value-bind (i1low i1lxp) (bound-parts i1l)
45+
(or (< i2high i1low)
46+
(and (= i2high i1low) (or i2hxp i1lxp)))))))))
47+
2448
(defun interval+ (i1 i2)
2549
(make-interval
2650
(let ((l1 (interval-low i1)) (l2 (interval-low i2)))
@@ -35,6 +59,27 @@
3559
(let ((low (interval-low interval)))
3660
(if low (finite-bound-unop #'- low) nil))))
3761

62+
(defun interval-= (i1 i2)
63+
(let ((i1l (interval-low i1)) (i2l (interval-low i2))
64+
(i1h (interval-high i1)) (i2h (interval-high i2)))
65+
(and (realp i1l) (realp i2l) (realp i1h) (realp i2h)
66+
(= i1l i2l i1h i2h))))
67+
(defun interval-/= (i1 i2) (intervals-disjoint-p i1 i2))
68+
69+
(defun interval-< (i1 i2)
70+
(let ((i1h (interval-high i1)) (i2l (interval-low i2)))
71+
(and i1h i2l
72+
(multiple-value-bind (i1high i1hxp) (bound-parts i1h)
73+
(multiple-value-bind (i2low i2lxp) (bound-parts i2l)
74+
(or (< i1high i2low)
75+
(and (= i1high i2low) (or i1hxp i2lxp))))))))
76+
(defun interval-<= (i1 i2)
77+
(let ((i1h (interval-high i1)) (i2l (interval-low i2)))
78+
(and i1h i2l (<= (bound-parts i1h) (bound-parts i2l)))))
79+
80+
(defun interval-> (i1 i2) (interval-< i2 i1))
81+
(defun interval->= (i1 i2) (interval-<= i2 i1))
82+
3883
;; Return -1 if the number is below the interval, 1 if above, 0 if in the interval.
3984
(defun interval-num-compare (interval num)
4085
(let ((low (interval-low interval)) (high (interval-high interval)))
@@ -64,16 +109,34 @@
64109
(defun interval-merge (i1 i2)
65110
;; Return the smallest interval including both input intervals.
66111
;; If the inputs do not intersect, this will not be a strict join.
112+
(labels ((lbmin (b1 b2)
113+
(cond ((not b1) b1)
114+
((not b2) b2)
115+
(t (multiple-value-bind (b1 xp1) (bound-parts b1)
116+
(multiple-value-bind (b2 xp2) (bound-parts b2)
117+
(if (and xp1 xp2) (list (min b1 b2)) (min b1 b2)))))))
118+
(hbmax (b1 b2)
119+
(cond ((not b1) b1)
120+
((not b2) b2)
121+
(t (multiple-value-bind (b1 xp1) (bound-parts b1)
122+
(multiple-value-bind (b2 xp2) (bound-parts b2)
123+
(if (and xp1 xp2) (list (max b1 b2)) (max b1 b2))))))))
124+
(make-interval (lbmin (interval-low i1) (interval-low i2))
125+
(hbmax (interval-high i1) (interval-high i2)))))
126+
127+
(defun interval-intersect (i1 i2)
128+
;; Return the smallest interval included by both input intervals.
129+
;; If the inputs do not intersect, this will be empty.
67130
(labels ((fb< (b1 b2)
68131
(multiple-value-bind (b1 xp1) (bound-parts b1)
69132
(multiple-value-bind (b2 xp2) (bound-parts b2)
70133
(or (< b1 b2) (and (= b1 b2) xp1 (not xp2))))))
71-
(lbmin (b1 b2)
72-
(if (and b2 (or (not b1) (fb< b1 b2))) b1 b2))
73-
(hbmax (b1 b2)
74-
(if (and b1 (or (not b2) (fb< b1 b2))) b2 b1)))
75-
(make-interval (lbmin (interval-low i1) (interval-low i2))
76-
(hbmax (interval-high i1) (interval-high i2)))))
134+
(lbmax (b1 b2)
135+
(if (and b2 (or (not b1) (fb< b1 b2))) b2 b1))
136+
(hbmin (b1 b2)
137+
(if (and b1 (or (not b2) (fb< b1 b2))) b1 b2)))
138+
(make-interval (lbmax (interval-low i1) (interval-low i2))
139+
(hbmin (interval-high i1) (interval-high i2)))))
77140

78141
;; Multiply a positive interval by any interval.
79142
(defun interval*-1-pos (i1 i2)

src/lisp/kernel/cleavir/primop.lisp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@
112112
(defvprimop-intrinsic core::etypecase-error ((:object) :object :object)
113113
"cc_etypecase_error")
114114

115+
(defvprimop (eq :flags (:flushable))
116+
((:boolean) :object :object) (inst)
117+
(assert (= 2 (length (bir:inputs inst))))
118+
(cmp:irc-icmp-eq
119+
(in (first (bir:inputs inst))) (in (second (bir:inputs inst)))))
120+
115121
(macrolet ((def-float-compare (sfname dfname op reversep)
116122
`(progn
117123
(deftprimop ,sfname (:single-float :single-float)

src/lisp/kernel/cleavir/transform.lisp

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,11 +374,47 @@ Optimizations are available for any of:
374374

375375
(deftransform-type-predicate compiled-function-p compiled-function)
376376

377-
(deftransform equal (((x number) (y t))) '(eql x y))
378-
(deftransform equal (((x t) (y number))) '(eql x y))
377+
;;; If the types of the arguments are disjoint, EQ (and EQL) are false.
378+
;;; (This is not generally true of EQUAL and EQUALP; consider e.g.
379+
;;; the type (EQL "foo") compared to a distinct string "foo")
380+
(deftransform eq (((x t) (y t)) :argstype args)
381+
(with-transformer-types (x y) args
382+
(if (ctype:disjointp x y *clasp-system*)
383+
'nil
384+
(decline-transform "types not disjoint"))))
385+
386+
(deftransform eql (((x t) (y t)) :argstype args)
387+
(with-transformer-types (x y) args
388+
(if (ctype:disjointp x y *clasp-system*)
389+
'nil
390+
(decline-transform "types not disjoint"))))
391+
;;; on clasp, eq of fixnums and singles and characters is ok
392+
;;; (i.e. is eql)
393+
;;; And of course pointers are tagged differently from fixnums and
394+
;;; characters, so raw comparison can't have any false positives.
395+
;;; Doubles and longs and bignums are boxed.
396+
;;; FIXME: conditionalize on compiler parameters?
397+
(deftransform eql (((x (or fixnum short-float single-float (not number)))
398+
(y t)))
399+
'(eq x y))
400+
(deftransform eql (((x t)
401+
(y (or fixnum short-float single-float (not number)))))
402+
'(eq x y))
403+
404+
(deftransform equal (((x (not (or cons string bit-vector pathname
405+
number character)))
406+
(y t)))
407+
'(eq x y))
408+
(deftransform equal (((x t) (y (not (or cons string bit-vector pathname
409+
number character)))))
410+
'(eq x y))
411+
(deftransform equal (((x (not (or cons string bit-vector pathname))) (y t)))
412+
'(eql x y))
413+
(deftransform equal (((x t) (y (not (or cons string bit-vector pathname)))))
414+
'(eql x y))
379415
(deftransform equalp (((x number) (y number))) '(= x y))
380416

381-
(deftransform equal (((x character) (y character))) '(char= x y))
417+
(deftransform equal (((x character) (y character))) '(eq x y))
382418
(deftransform equalp (((x character) (y character))) '(char-equal x y))
383419

384420
#+(or) ; string= is actually slower atm due to keyword etc processing
@@ -413,6 +449,20 @@ Optimizations are available for any of:
413449

414450
(deftransform-type-predicate random-state-p random-state)
415451

452+
(macrolet ((def (comparator yes no)
453+
`(deftransform ,comparator (((a1 real) (a2 real)) :argstype args)
454+
(with-transformer-types (a1 a2) args
455+
(let ((interval1 (type-approximate-interval a1 *clasp-system*))
456+
(interval2 (type-approximate-interval a2 *clasp-system*)))
457+
(cond ((,yes interval1 interval2) 't)
458+
((,no interval1 interval2) 'nil)
459+
(t (decline-transform "unable to fold by intervals"))))))))
460+
(def core:two-arg-= interval-= interval-/=)
461+
(def core:two-arg-< interval-< interval->=)
462+
(def core:two-arg-<= interval-<= interval->)
463+
(def core:two-arg-> interval-> interval-<=)
464+
(def core:two-arg->= interval->= interval-<))
465+
416466
(macrolet ((define-two-arg-f (name)
417467
`(progn
418468
(deftransform ,name (((a1 single-float) (a2 double-float)))

src/lisp/kernel/cleavir/type.lisp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,24 @@
214214
(values nil nil)))
215215
(values nil nil)))
216216

217+
;;; Reduce a type to a single interval. This is general worse than the original
218+
;;; type, e.g. (or (integer 0 4) (integer 12 19)) would become (integer 0 19),
219+
;;; but single intervals are way easier to work with.
220+
(defun type-approximate-interval (type sys)
221+
(cond ((ctype:rangep type sys)
222+
(range->interval type sys))
223+
((ctype:disjunctionp type sys)
224+
(reduce #'interval-merge
225+
(loop for st in (ctype:disjunction-ctypes type sys)
226+
collect (type-approximate-interval st sys))
227+
:initial-value (make-empty-interval)))
228+
((ctype:conjunctionp type sys)
229+
(reduce #'interval-intersect
230+
(loop for st in (ctype:conjunction-ctypes type sys)
231+
collect (type-approximate-interval st sys))
232+
:initial-value (make-unbounded-interval)))
233+
(t (make-unbounded-interval)))) ; unknown
234+
217235
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
218236
;;;
219237
;;; (4) TYPES AND CLASSES
@@ -383,6 +401,20 @@
383401
(define-deriver random-state-p (object)
384402
(derive-type-predicate object 'random-state *clasp-system*))
385403

404+
(macrolet ((def (comparator yes no)
405+
`(define-deriver ,comparator (num1 num2)
406+
(sv (let* ((sys *clasp-system*)
407+
(interval1 (type-approximate-interval num1 sys))
408+
(interval2 (type-approximate-interval num2 sys)))
409+
(cond ((,yes interval1 interval2) (ctype:member sys t))
410+
((,no interval1 interval2) (ctype:member sys nil))
411+
(t (ctype:member sys t nil))))))))
412+
(def core:two-arg-= interval-= interval-/=)
413+
(def core:two-arg-< interval-< interval->=)
414+
(def core:two-arg-<= interval-<= interval->)
415+
(def core:two-arg-> interval-> interval-<=)
416+
(def core:two-arg->= interval->= interval-<))
417+
386418
(defun contagion (ty1 ty2)
387419
(ecase ty1
388420
((integer)

0 commit comments

Comments
 (0)