`(lambda (x y z)
(make-array '3 :initial-contents `(,x ,y ,z))))))))
+;;; optimizing array-in-bounds-p
+(with-test (:name :optimize-array-in-bounds-p)
+ (locally
+ (macrolet ((find-callees (&body body)
+ `(ctu:find-named-callees
+ (compile nil
+ '(lambda ()
+ ,@body))
+ :name 'array-in-bounds-p))
+ (must-optimize (&body exprs)
+ `(progn
+ ,@(loop for expr in exprs
+ collect `(assert (not (find-callees
+ ,expr))))))
+ (must-not-optimize (&body exprs)
+ `(progn
+ ,@(loop for expr in exprs
+ collect `(assert (find-callees
+ ,expr))))))
+ (must-optimize
+ ;; in bounds
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a 0))
+ ;; exceeds upper bound (constant)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a 1))
+ ;; exceeds upper bound (interval)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a (+ 1 (random 2))))
+ ;; negative lower bound (constant)
+ (let ((a (make-array '(1))))
+ (array-in-bounds-p a -1))
+ ;; negative lower bound (interval)
+ (let ((a (make-array 3))
+ (i (- (random 1) 20)))
+ (array-in-bounds-p a i))
+ ;; multiple known dimensions
+ (let ((a (make-array '(1 1))))
+ (array-in-bounds-p a 0 0))
+ ;; union types
+ (let ((s (the (simple-string 10) (eval "0123456789"))))
+ (array-in-bounds-p s 9)))
+ (must-not-optimize
+ ;; don't trust non-simple array length in safety=1
+ (let ((a (the (array * (10)) (make-array 10 :adjustable t))))
+ (eval `(adjust-array ,a 0))
+ (array-in-bounds-p a 9))
+ ;; same for a union type
+ (let ((s (the (string 10) (make-array 10
+ :element-type 'character
+ :adjustable t))))
+ (eval `(adjust-array ,s 0))
+ (array-in-bounds-p s 9))
+ ;; single unknown dimension
+ (let ((a (make-array (random 20))))
+ (array-in-bounds-p a 10))
+ ;; multiple unknown dimensions
+ (let ((a (make-array (list (random 20) (random 5)))))
+ (array-in-bounds-p a 5 2))
+ ;; some other known dimensions
+ (let ((a (make-array (list 1 (random 5)))))
+ (array-in-bounds-p a 0 2))
+ ;; subscript might be negative
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (- (random 3) 2)))
+ ;; subscript might be too large
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (random 6)))
+ ;; unknown upper bound
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (get-universal-time)))
+ ;; unknown lower bound
+ (let ((a (make-array 5)))
+ (array-in-bounds-p a (- (get-universal-time))))
+ ;; in theory we should be able to optimize
+ ;; the following but the current implementation
+ ;; doesn't cut it because the array type's
+ ;; dimensions get reported as (* *).
+ (let ((a (make-array (list (random 20) 1))))
+ (array-in-bounds-p a 5 2))))))
+
;;; optimizing (EXPT -1 INTEGER)
(test-util:with-test (:name (expt minus-one integer))
(dolist (x '(-1 -1.0 -1.0d0))
(let ((* "fooo"))
(test '(integer 4 4) '(length *) t))))
-(with-test (:name :float-division-by-one)
- (flet ((test (lambda-form arg &optional (result arg))
- (let* ((fun1 (compile nil lambda-form))
- (fun2 (funcall (compile nil `(lambda ()
- (declare (optimize (sb-c::float-accuracy 0)))
- ,lambda-form))))
- (disassembly1 (with-output-to-string (s)
- (disassemble fun1 :stream s)))
- (disassembly2 (with-output-to-string (s)
- (disassemble fun2 :stream s))))
+(with-test (:name :float-division-using-exact-reciprocal)
+ (flet ((test (lambda-form arg res &key (check-insts t))
+ (let* ((fun (compile nil lambda-form))
+ (disassembly (with-output-to-string (s)
+ (disassemble fun :stream s))))
;; Let's make sure there is no division at runtime: for x86 and
;; x86-64 that implies an FDIV, DIVSS, or DIVSD instruction, so
;; look for DIV in the disassembly. It's a terrible KLUDGE, but
;; it works.
#+(or x86 x86-64)
- (assert (and (not (search "DIV" disassembly1))
- (not (search "DIV" disassembly2))))
- (assert (eql result (funcall fun1 arg)))
- (assert (eql result (funcall fun2 arg))))))
- (test `(lambda (x) (declare (single-float x)) (/ x 1)) 123.45)
- (test `(lambda (x) (declare (single-float x)) (/ x -1)) 123.45 -123.45)
- (test `(lambda (x) (declare (single-float x)) (/ x 1.0)) 543.21)
- (test `(lambda (x) (declare (single-float x)) (/ x -1.0)) 543.21 -543.21)
- (test `(lambda (x) (declare (single-float x)) (/ x 1.0d0)) 42.00 42.d0)
- (test `(lambda (x) (declare (single-float x)) (/ x -1.0d0)) 42.00 -42.d0)
- (test `(lambda (x) (declare (double-float x)) (/ x 1)) 123.45d0)
- (test `(lambda (x) (declare (double-float x)) (/ x -1)) 123.45d0 -123.45d0)
- (test `(lambda (x) (declare (double-float x)) (/ x 1.0)) 543.21d0)
- (test `(lambda (x) (declare (double-float x)) (/ x -1.0)) 543.21d0 -543.21d0)
- (test `(lambda (x) (declare (double-float x)) (/ x 1.0d0)) 42.d0)
- (test `(lambda (x) (declare (double-float x)) (/ x -1.0d0)) 42.d0 -42.0d0)))
+ (when check-insts
+ (assert (not (search "DIV" disassembly))))
+ ;; No generic arithmetic!
+ (assert (not (search "GENERIC" disassembly)))
+ (assert (eql res (funcall fun arg))))))
+ (dolist (c '(128 64 32 16 8 4 2 1 1/2 1/4 1/8 1/16 1/32 1/64))
+ (dolist (type '(single-float double-float))
+ (let* ((cf (coerce c type))
+ (arg (- (random (* 2 cf)) cf))
+ (r1 (eval `(/ ,arg ,cf)))
+ (r2 (eval `(/ ,arg ,(- cf)))))
+ (test `(lambda (x) (declare (,type x)) (/ x ,cf)) arg r1)
+ (test `(lambda (x) (declare (,type x)) (/ x ,(- cf))) arg r2)
+ ;; rational args should get optimized as well
+ (test `(lambda (x) (declare (,type x)) (/ x ,c)) arg r1)
+ (test `(lambda (x) (declare (,type x)) (/ x ,(- c))) arg r2))))
+ ;; Also check that inexact reciprocals (1) are not used by default (2) are
+ ;; used with FLOAT-ACCURACY=0.
+ (dolist (type '(single-float double-float))
+ (let ((trey (coerce 3 type))
+ (one (coerce 1 type)))
+ (test `(lambda (x) (declare (,type x)) (/ x 3)) trey one
+ :check-insts nil)
+ (test `(lambda (x)
+ (declare (,type x)
+ (optimize (sb-c::float-accuracy 0)))
+ (/ x 3))
+ trey (eval `(* ,trey (/ ,trey))))))))
(with-test (:name :float-multiplication-by-one)
(flet ((test (lambda-form arg &optional (result arg))
(disassemble fun1 :stream s)))
(disassembly2 (with-output-to-string (s)
(disassemble fun2 :stream s))))
- ;; Let's make sure there is no multiplication at runtime: for x86
- ;; and x86-64 that implies an FMUL, MULSS, or MULSD instruction,
- ;; so look for MUL in the disassembly. It's a terrible KLUDGE,
- ;; but it works.
+ ;; Multiplication at runtime should be eliminated only with
+ ;; FLOAT-ACCURACY=0. (To catch SNaNs.)
#+(or x86 x86-64)
- (assert (and (not (search "MUL" disassembly1))
+ (assert (and (search "MUL" disassembly1)
(not (search "MUL" disassembly2))))
+ ;; Not generic arithmetic, please!
+ (assert (and (not (search "GENERIC" disassembly1))
+ (not (search "GENERIC" disassembly2))))
(assert (eql result (funcall fun1 arg)))
(assert (eql result (funcall fun2 arg))))))
- (test `(lambda (x) (declare (single-float x)) (* x 1)) 123.45)
- (test `(lambda (x) (declare (single-float x)) (* x -1)) 123.45 -123.45)
- (test `(lambda (x) (declare (single-float x)) (* x 1.0)) 543.21)
- (test `(lambda (x) (declare (single-float x)) (* x -1.0)) 543.21 -543.21)
- (test `(lambda (x) (declare (single-float x)) (* x 1.0d0)) 42.00 42.d0)
- (test `(lambda (x) (declare (single-float x)) (* x -1.0d0)) 42.00 -42.d0)
- (test `(lambda (x) (declare (double-float x)) (* x 1)) 123.45d0)
- (test `(lambda (x) (declare (double-float x)) (* x -1)) 123.45d0 -123.45d0)
- (test `(lambda (x) (declare (double-float x)) (* x 1.0)) 543.21d0)
- (test `(lambda (x) (declare (double-float x)) (* x -1.0)) 543.21d0 -543.21d0)
- (test `(lambda (x) (declare (double-float x)) (* x 1.0d0)) 42.d0)
- (test `(lambda (x) (declare (double-float x)) (* x -1.0d0)) 42.d0 -42.0d0)))
+ (dolist (type '(single-float double-float))
+ (let* ((one (coerce 1 type))
+ (arg (random (* 2 one)))
+ (-r (- arg)))
+ (test `(lambda (x) (declare (,type x)) (* x 1)) arg)
+ (test `(lambda (x) (declare (,type x)) (* x -1)) arg -r)
+ (test `(lambda (x) (declare (,type x)) (* x ,one)) arg)
+ (test `(lambda (x) (declare (,type x)) (* x ,(- one))) arg -r)))))
(with-test (:name :float-addition-of-zero)
(flet ((test (lambda-form arg &optional (result arg))
(test `(lambda (x) (declare (double-float x)) (* x 2)) 123.45d0 246.9d0)
(test `(lambda (x) (declare (double-float x)) (* x 2.0)) 543.21d0 1086.42d0)
(test `(lambda (x) (declare (double-float x)) (* x 2.0d0)) 42.0d0 84.0d0)))
+
+(with-test (:name :bug-392203)
+ ;; Used to hit an AVER in COMVERT-MV-CALL.
+ (assert (zerop
+ (funcall
+ (compile nil
+ `(lambda ()
+ (flet ((k (&rest x) (declare (ignore x)) 0))
+ (multiple-value-call #'k #'k))))))))
+
+(with-test (:name :allocate-closures-failing-aver)
+ (let ((f (compile nil `(lambda ()
+ (labels ((k (&optional x) #'k)))))))
+ (assert (null (funcall f)))))
+
+(with-test (:name :flush-vector-creation)
+ (let ((f (compile nil `(lambda ()
+ (dotimes (i 1024)
+ (vector i i i))
+ t))))
+ (ctu:assert-no-consing (funcall f))))
+
+(with-test (:name :array-type-predicates)
+ (dolist (et sb-kernel::*specialized-array-element-types*)
+ (when et
+ (let* ((v (make-array 3 :element-type et))
+ (fun (compile nil `(lambda ()
+ (list
+ (if (typep ,v '(simple-array ,et (*)))
+ :good
+ :bad)
+ (if (typep (elt ,v 0) '(simple-array ,et (*)))
+ :bad
+ :good))))))
+ (assert (equal '(:good :good) (funcall fun)))))))
+
+(with-test (:name :truncate-float)
+ (let ((s (compile nil `(lambda (x)
+ (declare (single-float x))
+ (truncate x))))
+ (d (compile nil `(lambda (x)
+ (declare (double-float x))
+ (truncate x)))))
+ ;; Check that there is no generic arithmetic
+ (assert (not (search "GENERIC"
+ (with-output-to-string (out)
+ (disassemble s :stream out)))))
+ (assert (not (search "GENERIC"
+ (with-output-to-string (out)
+ (disassemble d :stream out)))))))
+
+(with-test (:name :make-array-unnamed-dimension-leaf)
+ (let ((fun (compile nil `(lambda (stuff)
+ (make-array (map 'list 'length stuff))))))
+ (assert (equalp #2A((0 0 0) (0 0 0))
+ (funcall fun '((1 2) (1 2 3)))))))
+
+(with-test (:name :fp-decoding-funs-not-flushable-in-safe-code)
+ (dolist (name '(float-sign float-radix float-digits float-precision decode-float
+ integer-decode-float))
+ (let ((fun (compile nil `(lambda (x)
+ (declare (optimize safety))
+ (,name x)
+ nil))))
+ (flet ((test (arg)
+ (unless (eq :error
+ (handler-case
+ (funcall fun arg)
+ (error () :error)))
+ (error "(~S ~S) did not error"
+ name arg))))
+ ;; No error
+ (funcall fun 1.0)
+ ;; Error
+ (test 'not-a-float)
+ (when (member name '(decode-float integer-decode-float))
+ (test sb-ext:single-float-positive-infinity))))))
+
+(with-test (:name :sap-ref-16)
+ (let* ((fun (compile nil `(lambda (x y)
+ (declare (type sb-sys:system-area-pointer x)
+ (type (integer 0 100) y))
+ (sb-sys:sap-ref-16 x (+ 4 y)))))
+ (vector (coerce '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
+ '(simple-array (unsigned-byte 8) (*))))
+ (sap (sb-sys:vector-sap vector))
+ (ret (funcall fun sap 0)))
+ ;; test for either endianness
+ (assert (or (= ret (+ (* 5 256) 4)) (= ret (+ (* 4 256) 5))))))
+
+(with-test (:name :coerce-type-warning)
+ (dolist (type '(t (unsigned-byte 8) (unsigned-byte 16) (unsigned-byte 32)
+ (signed-byte 8) (signed-byte 16) (signed-byte 32)))
+ (multiple-value-bind (fun warningsp failurep)
+ (compile nil `(lambda (x)
+ (declare (type simple-vector x))
+ (coerce x '(vector ,type))))
+ (assert (null warningsp))
+ (assert (null failurep))
+ (assert (typep (funcall fun #(1)) `(simple-array ,type (*)))))))
+
+(with-test (:name :truncate-double-float)
+ (let ((fun (compile nil `(lambda (x)
+ (multiple-value-bind (q r)
+ (truncate (coerce x 'double-float))
+ (declare (type unsigned-byte q)
+ (type double-float r))
+ (list q r))))))
+ (assert (equal (funcall fun 1.0d0) '(1 0.0d0)))))
+
+(with-test (:name :set-slot-value-no-warning)
+ (let ((notes 0))
+ (handler-bind ((warning #'error)
+ (sb-ext:compiler-note (lambda (c)
+ (declare (ignore c))
+ (incf notes))))
+ (compile nil `(lambda (x y)
+ (declare (optimize speed safety))
+ (setf (slot-value x 'bar) y))))
+ (assert (= 1 notes))))
+
+(with-test (:name :concatenate-string-opt)
+ (flet ((test (type grep)
+ (let* ((fun (compile nil `(lambda (a b c d e)
+ (concatenate ',type a b c d e))))
+ (args '("foo" #(#\.) "bar" (#\-) "quux"))
+ (res (apply fun args)))
+ (assert (search grep (with-output-to-string (out)
+ (disassemble fun :stream out))))
+ (assert (equal (apply #'concatenate type args)
+ res))
+ (assert (typep res type)))))
+ (test 'string "%CONCATENATE-TO-STRING")
+ (test 'simple-string "%CONCATENATE-TO-STRING")
+ (test 'base-string "%CONCATENATE-TO-BASE-STRING")
+ (test 'simple-base-string "%CONCATENATE-TO-BASE-STRING")))