mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
2648 lines
87 KiB
Common Lisp
2648 lines
87 KiB
Common Lisp
;;;; tests that dynamic-extent functionality works.
|
||
|
||
;;;; This software is part of the SBCL system. See the README file for
|
||
;;;; more information.
|
||
;;;;
|
||
;;;; While most of SBCL is derived from the CMU CL system, the test
|
||
;;;; files (like this one) were written from scratch after the fork
|
||
;;;; from CMU CL.
|
||
;;;;
|
||
;;;; This software is in the public domain and is provided with
|
||
;;;; absolutely no warranty. See the COPYING and CREDITS files for
|
||
;;;; more information.
|
||
|
||
(when (eq sb-ext:*evaluator-mode* :interpret)
|
||
(invoke-restart 'run-tests::skip-file))
|
||
|
||
(use-package :ctu)
|
||
|
||
(setq sb-c::*check-consistency* t
|
||
sb-ext:*stack-allocate-dynamic-extent* t)
|
||
|
||
(defun crashme (a)
|
||
(declare (optimize (speed 3) (safety 0) (space 0)))
|
||
(declare (muffle-conditions style-warning)) ; re F1 and F2
|
||
(restart-case
|
||
(progn (ignore-errors (error "Foo")) (write-char #\.))
|
||
(retry () (f1 a))
|
||
(use-value (new) (f2 new))))
|
||
|
||
;; lp#1530390
|
||
(with-test (:name :do-not-dxify-restarts)
|
||
(let ((a (make-array 100))) (unwind-protect (crashme 'bork) (fill a 0)))
|
||
(let ((a (make-array 100))) (unwind-protect (crashme 'bork) (fill a 0)))
|
||
(let ((a (make-array 100))) (unwind-protect (crashme 'bork) (fill a 0)))
|
||
(let ((a (make-array 100))) (unwind-protect (crashme 'bork) (fill a 0)))
|
||
)
|
||
|
||
(defmacro defun-with-dx (name arglist &body body)
|
||
(let ((debug-name (sb-int:symbolicate name "-HIGH-DEBUG"))
|
||
(default-name (sb-int:symbolicate name "-DEFAULT")))
|
||
`(progn
|
||
(defun ,debug-name ,arglist
|
||
(declare (optimize debug))
|
||
,@body)
|
||
(defun ,default-name ,arglist
|
||
,@body)
|
||
(defun ,name (&rest args)
|
||
(apply #',debug-name args)
|
||
(apply #',default-name args)))))
|
||
|
||
;;; &REST lists
|
||
|
||
(defun-with-dx dxlength (&rest rest)
|
||
(declare (dynamic-extent rest))
|
||
(length rest))
|
||
|
||
(with-test (:name (:dx-&rest :basics))
|
||
(assert (= (dxlength 1 2 3) 3))
|
||
(assert (= (dxlength t t t t t t) 6))
|
||
(assert (= (dxlength) 0)))
|
||
|
||
(defun callee (list)
|
||
(destructuring-bind (a b c d e f &rest g) list
|
||
(+ a b c d e f (length g))))
|
||
|
||
(defun-with-dx dxcaller (&rest rest)
|
||
(declare (dynamic-extent rest))
|
||
(callee rest))
|
||
|
||
(with-test (:name (:dx-&rest :pass-down-to-callee :tail-call))
|
||
(assert (= (dxcaller 1 2 3 4 5 6 7) 22)))
|
||
|
||
(defun-with-dx dxcaller-align-1 (x &rest rest)
|
||
(declare (dynamic-extent rest))
|
||
(+ x (callee rest)))
|
||
|
||
(with-test (:name (:dx-&rest :pass-down-to-callee :non-tail-call))
|
||
(assert (= (dxcaller-align-1 17 1 2 3 4 5 6 7) 39))
|
||
(assert (= (dxcaller-align-1 17 1 2 3 4 5 6 7 8) 40)))
|
||
|
||
;;; %NIP-VALUES
|
||
|
||
(defun-with-dx test-nip-values ()
|
||
(flet ((bar (x &rest y)
|
||
(declare (dynamic-extent y))
|
||
(if (> x 0)
|
||
(values x (length y))
|
||
(values (car y)))))
|
||
(multiple-value-call #'values
|
||
(bar 1 2 3 4 5 6)
|
||
(bar -1 'a 'b))))
|
||
|
||
(with-test (:name (:nip-values))
|
||
(assert (equal (multiple-value-list (test-nip-values)) '(1 5 a))))
|
||
|
||
;;; LET-variable substitution
|
||
|
||
(defun-with-dx test-let-var-subst1 (x)
|
||
(let ((y (list x (1- x))))
|
||
(opaque-identity :foo)
|
||
(let ((z (the list y)))
|
||
(declare (dynamic-extent z))
|
||
(length z))))
|
||
|
||
(with-test (:name (:let-variable-substitution))
|
||
(assert (eql (test-let-var-subst1 17) 2)))
|
||
|
||
(defun-with-dx test-let-var-subst2 (x)
|
||
(let ((y (list x (1- x))))
|
||
(declare (dynamic-extent y))
|
||
(opaque-identity :foo)
|
||
(let ((z (the list y)))
|
||
(length z))))
|
||
|
||
(with-test (:name (:let-variable-substitution-2))
|
||
(assert (eql (test-let-var-subst2 17) 2)))
|
||
|
||
|
||
;;; DX propagation through LET-return.
|
||
|
||
(defun-with-dx test-lvar-subst (x)
|
||
(let ((y (list x (1- x))))
|
||
(declare (dynamic-extent y))
|
||
(second (let ((z (the list y)))
|
||
(opaque-identity :foo)
|
||
z))))
|
||
|
||
(with-test (:name (:dx-propagation-through-let-return))
|
||
(assert (eql (test-lvar-subst 11) 10)))
|
||
|
||
;;; this code is incorrect, but the compiler should not fail
|
||
(defun-with-dx test-let-var-subst-incorrect (x)
|
||
(let ((y (list x (1- x))))
|
||
(opaque-identity :foo)
|
||
(let ((z (the list y)))
|
||
(declare (dynamic-extent z))
|
||
(opaque-identity :bar)
|
||
z)))
|
||
|
||
;;; alignment
|
||
|
||
(defvar *x*)
|
||
(defun-with-dx test-alignment-dx-list (form)
|
||
(multiple-value-prog1 (eval form)
|
||
(let ((l (list 1 2 3 4)))
|
||
(declare (dynamic-extent l))
|
||
(setq *x* (copy-list l)))))
|
||
|
||
(with-test (:name (:dx-list :alignment))
|
||
(dotimes (n 64)
|
||
(let* ((res (loop for i below n collect i))
|
||
(form `(values ,@res)))
|
||
(assert (equal (multiple-value-list (test-alignment-dx-list form)) res))
|
||
(assert (equal *x* '(1 2 3 4))))))
|
||
|
||
;;; closure
|
||
|
||
(declaim (notinline true))
|
||
(defun true (x)
|
||
(declare (ignore x))
|
||
t)
|
||
|
||
(defun-with-dx dxclosure (x)
|
||
(flet ((f (y)
|
||
(+ y x)))
|
||
(declare (dynamic-extent #'f))
|
||
(true #'f)))
|
||
|
||
(with-test (:name (:dx-closure))
|
||
(assert (eq t (dxclosure 13))))
|
||
|
||
;;; value-cells
|
||
|
||
(defun-with-dx dx-value-cell (x)
|
||
(let ((cell x))
|
||
(flet ((f ()
|
||
(incf cell)))
|
||
(declare (dynamic-extent #'f))
|
||
(true #'f))))
|
||
|
||
;;; CONS
|
||
|
||
(defun-with-dx cons-on-stack (x)
|
||
(let ((cons (cons x x)))
|
||
(declare (dynamic-extent cons))
|
||
(true cons)
|
||
nil))
|
||
|
||
;;; MAKE-ARRAY
|
||
|
||
(defun force-make-array-on-stack (n)
|
||
(declare (optimize safety))
|
||
(let ((v (make-array (min n 1))))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-1 ()
|
||
(let ((v (make-array '(42) :element-type 'single-float)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-2 (n x)
|
||
(declare (integer n))
|
||
(let ((v (make-array n :initial-contents x)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-3 (x y z)
|
||
(let ((v (make-array 3
|
||
:element-type 'fixnum :initial-contents (list x y z)
|
||
:element-type t :initial-contents x)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-4 ()
|
||
(let ((v (make-array 3 :initial-contents '(1 2 3))))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-5 ()
|
||
(let ((v (make-array 3 :initial-element 12 :element-type t)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-6 ()
|
||
(let ((v (make-array 3 :initial-element 12 :element-type '(unsigned-byte 8))))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-7 ()
|
||
(let ((v (make-array 3 :initial-element 12 :element-type '(signed-byte 8))))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-8 ()
|
||
(let ((v (make-array 3 :initial-element 12 :element-type 'word)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-9 ()
|
||
(let ((v (make-array 3 :initial-element 12.0 :element-type 'single-float)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-10 ()
|
||
(let ((v (make-array 3 :initial-element 12.0d0 :element-type 'double-float)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-array-on-stack-11 ()
|
||
(let ((v (make-array (the integer (opaque-identity 3)) :initial-element 12.0d0 :element-type 'double-float)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx vector-on-stack (x y)
|
||
(let ((v (vector 1 x 2 y 3)))
|
||
(declare (dynamic-extent v))
|
||
(true v)
|
||
nil))
|
||
|
||
(defun-with-dx make-3d-fixed-array-on-stack-1 ()
|
||
(let ((a (make-array '(4 8 3) :initial-element 12 :element-type t)))
|
||
(declare (dynamic-extent a))
|
||
(true a)
|
||
(true a)
|
||
nil))
|
||
(defun-with-dx make-3d-fixed-array-on-stack-2 (a b c d)
|
||
(sb-int:dx-let ((a (make-array '(2 2 1)
|
||
:element-type 'bit
|
||
:initial-contents `(#((,a) (,b)) (#(,c) (,d))))))
|
||
(true a)
|
||
(true a)
|
||
nil))
|
||
|
||
(defun-with-dx make-2d-variable-array-on-stack ()
|
||
(let* ((n (opaque-identity 5))
|
||
(a (make-array `(,n 2) :initial-element 12 :element-type t)))
|
||
(declare (dynamic-extent a))
|
||
(true a)
|
||
(true a)
|
||
nil))
|
||
|
||
(defun 2d-array-initializer (n)
|
||
(ecase n
|
||
(1 '((a)))
|
||
(2 '((a b) (c d)))
|
||
(3 '((a b c) (c d e) (f g h)))))
|
||
|
||
(defun-with-dx make-2d-array-function-initializer (n)
|
||
(let* ((x (opaque-identity n))
|
||
(y (opaque-identity x))
|
||
(a (make-array `(,x ,y) :initial-contents (2d-array-initializer x))))
|
||
(declare (dynamic-extent a))
|
||
(true a)
|
||
(true a)
|
||
nil))
|
||
|
||
;;; MAKE-LIST
|
||
|
||
(declaim (inline make-list-container))
|
||
(defstruct list-container listy-slot)
|
||
(defun make-var-length-dx-list (n thunk)
|
||
(sb-int:dx-let ((s (make-list-container :listy-slot (make-list n))))
|
||
(values (funcall (the function thunk) s))))
|
||
;; stack-allocatable lists are necessary but not sufficient
|
||
(with-test (:name (:dx-list :make-list) :fails-on (not :x86-64)
|
||
:skipped-on :debug-gc-barriers)
|
||
(let ((calls (ctu:asm-search "CALL" #'make-var-length-dx-list)))
|
||
;; Call nothing but the funarg
|
||
(assert (eql (length calls) 1)))
|
||
(assert-no-consing (make-var-length-dx-list
|
||
50 (lambda (x) (declare (ignore x))))))
|
||
|
||
;;; MAKE-STRUCTURE
|
||
|
||
;; stack-allocatable fixed-size objects are necessary but not sufficient
|
||
(with-test (:name :copy-structure-dx :fails-on (not (or :x86 :x86-64)))
|
||
(let ((thing sb-c::*backend-parsed-vops*))
|
||
;; check some preconditions
|
||
(assert (typep thing 'hash-table))
|
||
(assert (/= (sb-kernel:layout-bitmap (sb-kernel:%instance-layout thing))
|
||
sb-kernel:+layout-all-tagged+))
|
||
(assert-no-consing
|
||
(sb-int:dx-let ((x (copy-structure thing)))
|
||
(opaque-identity x)
|
||
0))))
|
||
|
||
(declaim (inline make-fp-struct-1))
|
||
(defstruct fp-struct-1
|
||
(s 0.0 :type single-float)
|
||
(d 0.0d0 :type double-float))
|
||
|
||
(defun-with-dx test-fp-struct-1.1 (s d)
|
||
d
|
||
(let ((fp (make-fp-struct-1 :s s)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-1-s fp)))
|
||
(assert (eql 0.0d0 (fp-struct-1-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-1.2 (s d)
|
||
s
|
||
(let ((fp (make-fp-struct-1 :d d)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql 0.0 (fp-struct-1-s fp)))
|
||
(assert (eql d (fp-struct-1-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-1.3 (s d)
|
||
(let ((fp (make-fp-struct-1 :d d :s s)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-1-s fp)))
|
||
(assert (eql d (fp-struct-1-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-1.4 (s d)
|
||
(let ((fp (make-fp-struct-1 :s s :d d)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-1-s fp)))
|
||
(assert (eql d (fp-struct-1-d fp)))))
|
||
|
||
(with-test (:name (:test-fp-struct-1.1))
|
||
(test-fp-struct-1.1 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-1.2))
|
||
(test-fp-struct-1.2 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-1.3))
|
||
(test-fp-struct-1.3 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-1.4))
|
||
(test-fp-struct-1.4 123.456 876.243d0))
|
||
|
||
(declaim (inline make-fp-struct-2))
|
||
(defstruct fp-struct-2
|
||
(d 0.0d0 :type double-float)
|
||
(s 0.0 :type single-float))
|
||
|
||
(defun-with-dx test-fp-struct-2.1 (s d)
|
||
d
|
||
(let ((fp (make-fp-struct-2 :s s)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-2-s fp)))
|
||
(assert (eql 0.0d0 (fp-struct-2-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-2.2 (s d)
|
||
s
|
||
(let ((fp (make-fp-struct-2 :d d)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql 0.0 (fp-struct-2-s fp)))
|
||
(assert (eql d (fp-struct-2-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-2.3 (s d)
|
||
(let ((fp (make-fp-struct-2 :d d :s s)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-2-s fp)))
|
||
(assert (eql d (fp-struct-2-d fp)))))
|
||
|
||
(defun-with-dx test-fp-struct-2.4 (s d)
|
||
(let ((fp (make-fp-struct-2 :s s :d d)))
|
||
(declare (dynamic-extent fp))
|
||
(assert (eql s (fp-struct-2-s fp)))
|
||
(assert (eql d (fp-struct-2-d fp)))))
|
||
|
||
(with-test (:name (:test-fp-struct-2.1))
|
||
(test-fp-struct-2.1 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-2.2))
|
||
(test-fp-struct-2.2 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-2.3))
|
||
(test-fp-struct-2.3 123.456 876.243d0))
|
||
(with-test (:name (:test-fp-struct-2.4))
|
||
(test-fp-struct-2.4 123.456 876.243d0))
|
||
|
||
(declaim (inline make-cfp-struct-1))
|
||
(defstruct cfp-struct-1
|
||
(s (complex 0.0) :type (complex single-float))
|
||
(d (complex 0.0d0) :type (complex double-float)))
|
||
|
||
(defun-with-dx test-cfp-struct-1.1 (s d)
|
||
d
|
||
(let ((cfp (make-cfp-struct-1 :s s)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-1-s cfp)))
|
||
(assert (eql (complex 0.0d0) (cfp-struct-1-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-1.2 (s d)
|
||
s
|
||
(let ((cfp (make-cfp-struct-1 :d d)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql (complex 0.0) (cfp-struct-1-s cfp)))
|
||
(assert (eql d (cfp-struct-1-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-1.3 (s d)
|
||
(let ((cfp (make-cfp-struct-1 :d d :s s)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-1-s cfp)))
|
||
(assert (eql d (cfp-struct-1-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-1.4 (s d)
|
||
(let ((cfp (make-cfp-struct-1 :s s :d d)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-1-s cfp)))
|
||
(assert (eql d (cfp-struct-1-d cfp)))))
|
||
|
||
(with-test (:name (:test-cfp-struct-1.1))
|
||
(test-cfp-struct-1.1 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-1.2))
|
||
(test-cfp-struct-1.2 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-1.3))
|
||
(test-cfp-struct-1.3 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-1.4))
|
||
(test-cfp-struct-1.4 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
|
||
(declaim (inline make-cfp-struct-2))
|
||
(defstruct cfp-struct-2
|
||
(d (complex 0.0d0) :type (complex double-float))
|
||
(s (complex 0.0) :type (complex single-float)))
|
||
|
||
(defun-with-dx test-cfp-struct-2.1 (s d)
|
||
d
|
||
(let ((cfp (make-cfp-struct-2 :s s)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-2-s cfp)))
|
||
(assert (eql (complex 0.0d0) (cfp-struct-2-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-2.2 (s d)
|
||
s
|
||
(let ((cfp (make-cfp-struct-2 :d d)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql (complex 0.0) (cfp-struct-2-s cfp)))
|
||
(assert (eql d (cfp-struct-2-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-2.3 (s d)
|
||
(let ((cfp (make-cfp-struct-2 :d d :s s)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-2-s cfp)))
|
||
(assert (eql d (cfp-struct-2-d cfp)))))
|
||
|
||
(defun-with-dx test-cfp-struct-2.4 (s d)
|
||
(let ((cfp (make-cfp-struct-2 :s s :d d)))
|
||
(declare (dynamic-extent cfp))
|
||
(assert (eql s (cfp-struct-2-s cfp)))
|
||
(assert (eql d (cfp-struct-2-d cfp)))))
|
||
|
||
(with-test (:name (:test-cfp-struct-2.1))
|
||
(test-cfp-struct-2.1 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-2.2))
|
||
(test-cfp-struct-2.2 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-2.3))
|
||
(test-cfp-struct-2.3 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
(with-test (:name (:test-cfp-struct-2.4))
|
||
(test-cfp-struct-2.4 (complex 0.123 123.456) (complex 908132.41d0 876.243d0)))
|
||
|
||
;; It works to declare a structure constructor INLINE after the DEFSTRUCT
|
||
;; was processed, as long as it was in the null lexical environment.
|
||
;; In a perfect world, we'd figure out that a variable declared DX which
|
||
;; receives the value of a structure constructor defined in the same file
|
||
;; and neither expressly INLINE nor NOTINLINE should be locally inlined.
|
||
;; But at least this shows that a declaration at the intended use point
|
||
;; is sufficient, without also a bracketing INLINE/NOTINLINE at the DEFSTRUCT.
|
||
|
||
;; Verify the precondition for the assertions that claim that DXifying works
|
||
;; even though the MAKE- function was not expressly INLINE when its DEFSTRUCT
|
||
;; was compiled.
|
||
(dolist (s '(make-foo1 make-foo2 make-foo3))
|
||
(assert (null (sb-int:info :function :inlining-data s))))
|
||
|
||
(defstruct foo1 x)
|
||
|
||
(defun-with-dx make-foo1-on-stack (x)
|
||
(declare (inline make-foo1))
|
||
(let ((foo (make-foo1 :x x)))
|
||
(declare (dynamic-extent foo))
|
||
(assert (eql x (foo1-x foo)))))
|
||
|
||
(defstruct foo2
|
||
(x 0.0 :type single-float)
|
||
(y 0.0d0 :type double-float)
|
||
a
|
||
b
|
||
c)
|
||
|
||
(defun-with-dx make-foo2-on-stack (x y)
|
||
(declare (inline make-foo2))
|
||
x
|
||
(let ((foo (make-foo2 :y y :c 'c)))
|
||
(declare (dynamic-extent foo))
|
||
(assert (eql 0.0 (foo2-x foo)))
|
||
(assert (eql y (foo2-y foo)))
|
||
(assert (eql 'c (foo2-c foo)))
|
||
(assert (eql nil (foo2-b foo)))))
|
||
|
||
;;; Check that constants work out as argument for all relevant
|
||
;;; slot types.
|
||
(defstruct foo3
|
||
(a 0 :type t)
|
||
(b 1 :type fixnum)
|
||
(c 2 :type sb-vm:word)
|
||
(d 3.0 :type single-float)
|
||
(e 4.0d0 :type double-float))
|
||
|
||
(defun-with-dx make-foo3-on-stack ()
|
||
(declare (inline make-foo3))
|
||
(let ((foo (make-foo3)))
|
||
(declare (dynamic-extent foo))
|
||
(assert (eql 0 (foo3-a foo)))
|
||
(assert (eql 1 (foo3-b foo)))
|
||
(assert (eql 2 (foo3-c foo)))
|
||
(assert (eql 3.0 (foo3-d foo)))
|
||
(assert (eql 4.0d0 (foo3-e foo)))))
|
||
|
||
;;; Nested DX
|
||
|
||
(defun-with-dx nested-dx-lists ()
|
||
(let ((dx (list (list 1 2) (list 3 4))))
|
||
(declare (dynamic-extent dx))
|
||
(true dx)
|
||
nil))
|
||
|
||
(defun-with-dx nested-dx-conses ()
|
||
(let ((dx (cons 1 (cons 2 (cons 3 (cons (cons t t) nil))))))
|
||
(declare (dynamic-extent dx))
|
||
(true dx)
|
||
nil))
|
||
|
||
(defun-with-dx nested-dx-not-used (x)
|
||
(declare (list x))
|
||
(let ((l (setf (car x) (list x x x))))
|
||
(declare (dynamic-extent l))
|
||
(true l)
|
||
(true (length l))
|
||
nil))
|
||
|
||
(defun-with-dx nested-evil-dx-used (x)
|
||
(declare (list x))
|
||
(let ((l (list x x x)))
|
||
(declare (dynamic-extent l))
|
||
(unwind-protect
|
||
(progn
|
||
(setf (car x) l)
|
||
(true l))
|
||
(setf (car x) nil))
|
||
nil))
|
||
|
||
(defparameter *bar* nil)
|
||
(declaim (inline make-nested-bad make-nested-good))
|
||
(defstruct (nested (:constructor make-nested-bad (&key bar &aux (bar (setf *bar* bar))))
|
||
(:constructor make-nested-good (&key bar)))
|
||
bar)
|
||
|
||
(defun-with-dx nested-good (y)
|
||
(let ((x (list (list (make-nested-good :bar (list (list (make-nested-good :bar (list y)))))))))
|
||
(declare (dynamic-extent x))
|
||
(true x)))
|
||
|
||
(defun-with-dx nested-bad (y)
|
||
(let ((x (list (list (make-nested-bad :bar (list (list (make-nested-bad :bar (list y)))))))))
|
||
(declare (dynamic-extent x))
|
||
(unless (equalp (caar x) (make-nested-good :bar *bar*))
|
||
(error "got ~S, wanted ~S" (caar x) (make-nested-good :bar *bar*)))
|
||
;; the NESTED instance itself *should* be DX!
|
||
(copy-nested (caar x))))
|
||
|
||
(with-test (:name :conservative-nested-dx)
|
||
;; NESTED-BAD should not stack-allocate :BAR due to the SETF.
|
||
(assert (equalp (nested-bad 42) (make-nested-good :bar *bar*)))
|
||
(assert (equalp *bar* (list (list (make-nested-bad :bar (list 42)))))))
|
||
|
||
;;; Conditional nested DX
|
||
|
||
;; These two test cases prompted a substantial redesign of the STACK
|
||
;; phase of the compiler to handle their particular permutation of
|
||
;; nested DX.
|
||
|
||
(with-test (:name (:bug-1044465 :reduced))
|
||
;; Test case from Stas Boukarev
|
||
(checked-compile '(lambda (x)
|
||
(let ((a (if x
|
||
(list (list x))
|
||
(list (list x)))))
|
||
(declare (dynamic-extent a))
|
||
(prin1 a)
|
||
1))))
|
||
|
||
(with-test (:name (:bug-1044465 :nasty))
|
||
;; Test case from Alastair Bridgewater
|
||
(checked-compile '(lambda (x y)
|
||
(dotimes (i 2)
|
||
(block bar
|
||
(let ((a (if x
|
||
(list (list x))
|
||
(list (list x))))
|
||
(b (if y x (return-from bar x))))
|
||
(declare (dynamic-extent a))
|
||
(prin1 a)
|
||
b))))))
|
||
|
||
;;; multiple uses for dx lvar
|
||
|
||
(defun-with-dx multiple-dx-uses ()
|
||
(let ((dx (if (true t)
|
||
(list 1 2 3)
|
||
(list 2 3 4))))
|
||
(declare (dynamic-extent dx))
|
||
(true dx)
|
||
nil))
|
||
|
||
;;; mapfoo should make the initial cons dx
|
||
|
||
(defun loop-collect-negate (x) (loop for item in x collect (- x)))
|
||
(defun mapcar-negate (x) (mapcar #'- x))
|
||
(defun mapcan-reverse (x) (mapcan #'reverse x))
|
||
|
||
;;; handler-case and handler-bind should use DX internally
|
||
|
||
(defun dx-handler-bind (x)
|
||
(let ((y 3))
|
||
(macrolet ((fool () `(lambda (c) (print (list c (incf y))))))
|
||
(handler-bind ((error
|
||
#'(lambda (c)
|
||
(break "OOPS: ~S caused ~S" x c)))
|
||
(warning (fool))
|
||
((and serious-condition (not error))
|
||
#'(lambda (c)
|
||
(break "OOPS2: ~S did ~S" x c))))
|
||
(/ 2 x)))))
|
||
|
||
(defun dx-handler-case (x)
|
||
(assert (zerop (handler-case (/ 2 x)
|
||
(error (c)
|
||
(break "OOPS: ~S caused ~S" x c)
|
||
-1)
|
||
(:no-error (res)
|
||
(1- res))))))
|
||
|
||
(defun list-delete-some-stuff ()
|
||
;; opaque-identity hides the fact that we are calling a destructive function
|
||
;; on a constant, which is technically illegal. But no deletion occurs,
|
||
;; so it's innocuous. Also these aren't tests of DX, but oh well...
|
||
(declare (muffle-conditions style-warning))
|
||
(delete 'a (opaque-identity '(x y)))
|
||
(delete 'a (opaque-identity '(x y)) :from-end t)
|
||
(delete-duplicates (opaque-identity '(x y))))
|
||
|
||
(defvar *a-cons* (cons nil nil))
|
||
|
||
(with-test (:name (:no-consing :dx-closures))
|
||
(assert-no-consing (dxclosure 42)))
|
||
|
||
(with-test (:name (:no-consing :dx-lists))
|
||
(assert-no-consing (dxlength 1 2 3))
|
||
(assert-no-consing (dxlength t t t t t t))
|
||
(assert-no-consing (dxlength))
|
||
(assert-no-consing (dxcaller 1 2 3 4 5 6 7))
|
||
(assert-no-consing (test-nip-values))
|
||
(assert-no-consing (test-let-var-subst2 17))
|
||
(assert-no-consing (test-lvar-subst 11))
|
||
(assert-no-consing (nested-dx-lists))
|
||
(assert-consing (nested-dx-not-used *a-cons*))
|
||
(assert-no-consing (nested-evil-dx-used *a-cons*))
|
||
(assert-no-consing (loop-collect-negate nil))
|
||
(assert-no-consing (mapcar-negate nil))
|
||
(assert-no-consing (mapcan-reverse nil))
|
||
(assert-no-consing (list-delete-some-stuff))
|
||
(assert-no-consing (multiple-dx-uses)))
|
||
|
||
(with-test (:name (:no-consing :dx-value-cell))
|
||
(assert-no-consing (dx-value-cell 13)))
|
||
|
||
(with-test (:name (:no-consing :dx-fixed-objects))
|
||
(assert-no-consing (cons-on-stack 42))
|
||
(assert-no-consing (make-foo1-on-stack 123))
|
||
(assert-no-consing (nested-good 42))
|
||
(assert-no-consing (nested-dx-conses))
|
||
(assert-no-consing (dx-handler-bind 2))
|
||
(assert-no-consing (dx-handler-case 2)))
|
||
|
||
(with-test (:name (:no-consing :dx-vectors))
|
||
(assert-no-consing (force-make-array-on-stack 128))
|
||
(assert-no-consing (make-array-on-stack-2 5 '(1 2.0 3 4.0 5)))
|
||
(assert-no-consing (make-array-on-stack-3 9 8 7))
|
||
(assert-no-consing (make-array-on-stack-4))
|
||
(assert-no-consing (make-array-on-stack-5))
|
||
(assert-no-consing (vector-on-stack :x :y)))
|
||
|
||
(with-test (:name (:no-consing :dx-arrays))
|
||
(assert-no-consing (make-3d-fixed-array-on-stack-1))
|
||
(assert-no-consing (make-2d-variable-array-on-stack))
|
||
(assert-no-consing (make-2d-array-function-initializer 1))
|
||
(assert-no-consing (make-2d-array-function-initializer 2))
|
||
(assert-no-consing (make-2d-array-function-initializer 3)))
|
||
|
||
(with-test (:name (:no-consing :dx-specialized-arrays)
|
||
:skipped-on (not :c-stack-is-control-stack))
|
||
(assert-no-consing (make-3d-fixed-array-on-stack-2 0 0 1 1)))
|
||
|
||
(with-test (:name (:no-consing :specialized-dx-vectors)
|
||
:skipped-on (not :c-stack-is-control-stack))
|
||
(assert-no-consing (make-array-on-stack-1))
|
||
(assert-no-consing (make-array-on-stack-6))
|
||
(assert-no-consing (make-array-on-stack-7))
|
||
(assert-no-consing (make-array-on-stack-8))
|
||
(assert-no-consing (make-array-on-stack-9))
|
||
(assert-no-consing (make-array-on-stack-10))
|
||
(assert-no-consing (make-array-on-stack-11)))
|
||
|
||
(with-test (:name (:no-consing :dx-raw-instances)
|
||
:skipped-on (not (and :generational :c-stack-is-control-stack)))
|
||
(let (a b)
|
||
(setf a 1.24 b 1.23d0)
|
||
(assert-no-consing (make-foo2-on-stack a b)))
|
||
(assert-no-consing (make-foo3-on-stack)))
|
||
|
||
;;; not really DX, but GETHASH and (SETF GETHASH) should not cons
|
||
|
||
(defvar *table* (make-hash-table))
|
||
|
||
(defun test-hash-table ()
|
||
(setf (gethash 5 *table*) 13)
|
||
(setf (gethash 6 *table*) 14)
|
||
(values (gethash 5 *table*)
|
||
(gethash 6 *table*)))
|
||
|
||
(with-test (:name (:no-consing :hash-tables))
|
||
(test-hash-table) ;; initialize all the vectors first
|
||
(assert-no-consing (test-hash-table)))
|
||
|
||
;;; Both with-pinned-objects and without-gcing should not cons
|
||
|
||
(defun call-without-gcing (fun)
|
||
(sb-sys:without-gcing (funcall fun)))
|
||
|
||
(defun call-with-pinned-object (fun obj)
|
||
(sb-sys:with-pinned-objects (obj)
|
||
(funcall fun obj)))
|
||
|
||
(with-test (:name (:no-consing :without-gcing))
|
||
(assert-no-consing (call-without-gcing (lambda ()))))
|
||
|
||
(with-test (:name (:no-consing :with-pinned-objects))
|
||
(assert-no-consing (call-with-pinned-object #'identity 42)))
|
||
|
||
;;; with-mutex should use DX and not cons
|
||
|
||
(defvar *mutex* (sb-thread::make-mutex :name "mutexlock"))
|
||
|
||
(defun test-mutex ()
|
||
(sb-thread:with-mutex (*mutex*)
|
||
(true *mutex*)))
|
||
|
||
(with-test (:name (:no-consing :mutex) :skipped-on (not :sb-thread))
|
||
(assert-no-consing (test-mutex)))
|
||
|
||
|
||
;;; Bugs found by Paul F. Dietz
|
||
|
||
(with-test (:name (:dx-bug-misc :pfdietz))
|
||
(checked-compile-and-assert (:optimize :all)
|
||
'(lambda (a b)
|
||
(let* ((v5 (cons b b)))
|
||
(declare (dynamic-extent v5))
|
||
v5
|
||
a))
|
||
(('x 'y) 'x)))
|
||
|
||
(with-test (:name :bug-1738095)
|
||
;; STACK analysis wasn't marking UVL or DX LVARs that are held live
|
||
;; by a DX allocation as being live in blocks along the path from
|
||
;; the allocation to the ENTRY node, causing problems when there is
|
||
;; a block that is outside the lexical environment of the "earlier"
|
||
;; DX allocation (in the case below, such a block would be the first
|
||
;; (outermost) use of LIST).
|
||
(checked-compile '(lambda ()
|
||
(let ((* (list (let ((* (list nil)))
|
||
(declare (dynamic-extent *))
|
||
(list 1)))))
|
||
(declare (dynamic-extent *))))))
|
||
|
||
(with-test (:name :bug-1739308)
|
||
;; STACK analysis wasn't propagating DX LVARs back from ENTRY to
|
||
;; allocation through non-local-entry environments (below, the CATCH
|
||
;; entry point), causing problems when such is the ONLY live path
|
||
;; back to the allocation.
|
||
(checked-compile '(lambda (x y)
|
||
(let ((s
|
||
(multiple-value-prog1
|
||
(list x)
|
||
(catch 'ct1
|
||
(throw 'ct8 30)))))
|
||
(declare (dynamic-extent s))
|
||
(funcall (the function y) s)))))
|
||
|
||
;;; bug reported by Svein Ove Aas
|
||
(defun svein-2005-ii-07 (x y)
|
||
(declare (optimize (speed 3) (space 2) (safety 0) (debug 0)))
|
||
(let ((args (list* y 1 2 x)))
|
||
(declare (dynamic-extent args))
|
||
(apply #'aref args)))
|
||
|
||
(with-test (:name (:dx-bugs-misc :svein-2005-ii-07))
|
||
(assert (eql
|
||
(svein-2005-ii-07
|
||
'(0)
|
||
#3A(((1 1 1) (1 1 1) (1 1 1))
|
||
((1 1 1) (1 1 1) (4 1 1))
|
||
((1 1 1) (1 1 1) (1 1 1))))
|
||
4)))
|
||
|
||
;;; bug reported by Brian Downing: stack-allocated arrays were not
|
||
;;; filled with zeroes.
|
||
(defun-with-dx bdowning-2005-iv-16 ()
|
||
(let ((a (make-array 11 :initial-element 0)))
|
||
(declare (dynamic-extent a))
|
||
(assert (every (lambda (x) (eql x 0)) a))))
|
||
|
||
(with-test (:name (:dx-bug-misc :bdowning-2005-iv-16))
|
||
(assert-no-consing (bdowning-2005-iv-16))
|
||
(bdowning-2005-iv-16))
|
||
|
||
(declaim (inline my-nconc))
|
||
(defun my-nconc (&rest lists)
|
||
(declare (dynamic-extent lists))
|
||
(apply #'nconc lists))
|
||
(defun-with-dx my-nconc-caller (a b c)
|
||
(let ((l1 (list a b c))
|
||
(l2 (list a b c)))
|
||
(my-nconc l1 l2)))
|
||
(with-test (:name :rest-stops-the-buck)
|
||
(let ((list1 (my-nconc-caller 1 2 3))
|
||
(list2 (my-nconc-caller 9 8 7)))
|
||
(assert (equal list1 '(1 2 3 1 2 3)))
|
||
(assert (equal list2 '(9 8 7 9 8 7)))))
|
||
|
||
(defun-with-dx let-converted-vars-dx-allocated-bug (x y z)
|
||
(let* ((a (list x y z))
|
||
(b (list x y z))
|
||
(c (list a b)))
|
||
(declare (dynamic-extent c))
|
||
(values a b)))
|
||
(with-test (:name :let-converted-vars-dx-allocated-bug)
|
||
(multiple-value-bind (i j) (let-converted-vars-dx-allocated-bug-default 1 2 3)
|
||
(assert (equal i j))
|
||
(assert (equal i (list 1 2 3)))))
|
||
|
||
;;; workaround for bug 419 -- real issue remains, but check that the
|
||
;;; bandaid holds.
|
||
(defun-with-dx bug419 (x)
|
||
(multiple-value-call #'list
|
||
(eval '(values 1 2 3))
|
||
(let ((x x))
|
||
(declare (dynamic-extent x))
|
||
(flet ((mget (y)
|
||
(+ x y))
|
||
(mset (z)
|
||
(incf x z)))
|
||
(declare (dynamic-extent #'mget #'mset))
|
||
((lambda (f g) (eval `(progn ,f ,g (values 4 5 6)))) #'mget #'mset)))))
|
||
|
||
(with-test (:name (:dx-bug-misc :bug419))
|
||
(assert (equal (bug419 42) '(1 2 3 4 5 6))))
|
||
|
||
;;; Multiple DX arguments in a local function call
|
||
(defun test-dx-flet-test (fun n f1 f2 f3)
|
||
(let ((res (with-output-to-string (s)
|
||
(assert (eql n (ignore-errors (funcall fun s)))))))
|
||
(multiple-value-bind (x pos) (read-from-string res nil)
|
||
(assert (equalp f1 x))
|
||
(multiple-value-bind (y pos2) (read-from-string res nil nil :start pos)
|
||
(assert (equalp f2 y))
|
||
(assert (equalp f3 (read-from-string res nil nil :start pos2))))))
|
||
(assert-no-consing (assert (eql n (funcall fun nil))))
|
||
(assert (eql n (funcall fun nil))))
|
||
|
||
(macrolet ((def (n f1 f2 f3)
|
||
(let ((name (sb-int:package-symbolicate "DX-FLET-TEST." n)))
|
||
`(progn
|
||
(defun-with-dx ,name (s)
|
||
(flet ((f (x)
|
||
(declare (dynamic-extent x))
|
||
(when s
|
||
(print x s)
|
||
(finish-output s))
|
||
nil))
|
||
(f ,f1)
|
||
(f ,f2)
|
||
(f ,f3)
|
||
,n))
|
||
(with-test (:name (:dx-flet-test ,n))
|
||
(test-dx-flet-test #',name ,n ,f1 ,f2 ,f3))))))
|
||
(def 0 (list :one) (list :two) (list :three))
|
||
(def 1 (make-array 128 :initial-element nil) (list 1 2 3 4 5 6 7 8) (list 'list))
|
||
(def 2 (list 1) (list 2 3) (list 4 5 6 7)))
|
||
|
||
;;; Test that unknown-values coming after a DX value won't mess up the
|
||
;;; stack analysis
|
||
(defun test-update-uvl-live-sets (x y z)
|
||
(declare (optimize speed (safety 0)))
|
||
(flet ((bar (a b)
|
||
(declare (dynamic-extent a))
|
||
(eval `(list (length ',a) ',b))))
|
||
(list (bar x y)
|
||
(bar (list x y z) ; dx push
|
||
(list
|
||
(multiple-value-call 'list
|
||
(eval '(values 1 2 3)) ; uv push
|
||
(max y z)
|
||
) ; uv pop
|
||
14)
|
||
))))
|
||
|
||
(with-test (:name (:update-uvl-live-sets))
|
||
(assert (equal '((0 4) (3 ((1 2 3 5) 14)))
|
||
(test-update-uvl-live-sets #() 4 5))))
|
||
|
||
(with-test (:name :regression-1.0.23.38)
|
||
(checked-compile '(lambda ()
|
||
(declare (muffle-conditions compiler-note))
|
||
(flet ((make (x y)
|
||
(let ((res (cons x x)))
|
||
(setf (cdr res) y)
|
||
res)))
|
||
(declare (inline make))
|
||
(let ((z (make 1 2)))
|
||
(declare (dynamic-extent z))
|
||
(print z)
|
||
t))))
|
||
(checked-compile '(lambda ()
|
||
(declare (muffle-conditions compiler-note))
|
||
(flet ((make (x y)
|
||
(let ((res (cons x x)))
|
||
(setf (cdr res) y)
|
||
(if x res y))))
|
||
(declare (inline make))
|
||
(let ((z (make 1 2)))
|
||
(declare (dynamic-extent z))
|
||
(print z)
|
||
t)))))
|
||
|
||
;;; On x86 and x86-64 upto 1.0.28.16 LENGTH and WORDS argument
|
||
;;; tns to ALLOCATE-VECTOR-ON-STACK could be packed in the same
|
||
;;; location, leading to all manner of badness. ...reproducing this
|
||
;;; reliably is hard, but this it at least used to break on x86-64.
|
||
(defun length-and-words-packed-in-same-tn (m)
|
||
(declare (optimize speed (safety 0) (debug 0) (space 0)))
|
||
(let ((array (make-array (max 1 m) :element-type 'fixnum)))
|
||
(declare (dynamic-extent array))
|
||
(array-total-size array)))
|
||
(with-test (:name :length-and-words-packed-in-same-tn)
|
||
(assert (= 1 (length-and-words-packed-in-same-tn -3))))
|
||
|
||
(with-test (:name :handler-case-bogus-compiler-note)
|
||
;; Taken from SWANK, used to signal a bogus stack allocation
|
||
;; failure note.
|
||
(checked-compile
|
||
`(lambda (files fasl-dir load)
|
||
(declare (muffle-conditions style-warning))
|
||
(let ((needs-recompile nil))
|
||
(dolist (src files)
|
||
(let ((dest (binary-pathname src fasl-dir)))
|
||
(handler-case
|
||
(progn
|
||
(when (or needs-recompile
|
||
(not (probe-file dest))
|
||
(file-newer-p src dest))
|
||
(setq needs-recompile t)
|
||
(ensure-directories-exist dest)
|
||
(compile-file src :output-file dest :print nil :verbose t))
|
||
(when load
|
||
(load dest :verbose t)))
|
||
(serious-condition (c)
|
||
(handle-loadtime-error c dest)))))))
|
||
:allow-notes nil))
|
||
|
||
(declaim (inline barvector))
|
||
(defun barvector (x y z)
|
||
(make-array 3 :initial-contents (list x y z)))
|
||
(with-test (:name :dx-compiler-notes)
|
||
(flet ((assert-notes (j lambda)
|
||
(let ((notes (nth 4 (multiple-value-list (checked-compile lambda))))) ; TODO
|
||
(unless (= (length notes) j)
|
||
(error "Wanted ~S notes, got ~S for~% ~S"
|
||
j (length notes) lambda)))))
|
||
;; These ones should not complain.
|
||
(assert-notes 0 `(lambda (name)
|
||
(with-alien
|
||
((posix-getenv (function c-string c-string)
|
||
:EXTERN "getenv"))
|
||
(values
|
||
(alien-funcall posix-getenv name)))))
|
||
(assert-notes 0 `(lambda (x)
|
||
(let ((y (barvector x x x)))
|
||
(declare (dynamic-extent y))
|
||
(print y)
|
||
nil)))
|
||
(assert-notes 0 `(lambda (list)
|
||
(declare (optimize (space 0)))
|
||
(sort list (lambda (x y) ; shut unrelated notes up
|
||
(< (truly-the fixnum x)
|
||
(truly-the fixnum y))))))
|
||
(assert-notes 0 `(lambda (other)
|
||
#'(lambda (s c n)
|
||
(ignore-errors (funcall other s c n)))))))
|
||
|
||
;;; Stack allocating a value cell in HANDLER-CASE would blow up stack
|
||
;;; in an unfortunate loop.
|
||
(defun handler-case-eating-stack ()
|
||
(declare (muffle-conditions warning)) ; "dead code detected ... IR1-PHASES"
|
||
(let ((sp nil))
|
||
(do ((n 0 (logand most-positive-fixnum (1+ n))))
|
||
((>= n 1024))
|
||
(multiple-value-bind (value error) (ignore-errors)
|
||
(when (and value error) nil))
|
||
(if sp
|
||
(assert (= sp (sb-c::%primitive sb-c:current-stack-pointer)))
|
||
(setf sp (sb-c::%primitive sb-c:current-stack-pointer))))))
|
||
(with-test (:name :handler-case-eating-stack)
|
||
(assert-no-consing (handler-case-eating-stack)))
|
||
|
||
;;; A nasty bug where RECHECK-DYNAMIC-EXTENT-LVARS thought something was going
|
||
;;; to be stack allocated when it was not, leading to a bogus %NIP-VALUES.
|
||
;;; Fixed by making RECHECK-DYNAMIC-EXTENT-LVARS deal properly with nested DX.
|
||
(deftype vec ()
|
||
`(simple-array t (3)))
|
||
(declaim (ftype (function (t t t) vec) vec))
|
||
(declaim (inline vec))
|
||
(defun vec (a b c)
|
||
(make-array 3 :initial-contents (list a b c)))
|
||
(defun bad-boy (vec)
|
||
(declare (type vec vec))
|
||
(lambda (fun)
|
||
(let ((vec (vec (aref vec 0) (aref vec 1) (aref vec 2))))
|
||
(declare (dynamic-extent vec))
|
||
(funcall fun vec))))
|
||
(with-test (:name :recheck-nested-dx-bug)
|
||
(assert (funcall (bad-boy (vec 1.0 2.0 3.3))
|
||
(lambda (vec) (equalp vec (vec 1.0 2.0 3.3)))))
|
||
(flet ((foo (x) (declare (ignore x))))
|
||
(let ((bad-boy (bad-boy (vec 2.0 3.0 4.0))))
|
||
(assert-no-consing (funcall bad-boy #'foo)))))
|
||
|
||
(with-test (:name :bug-497321)
|
||
(flet ((test (lambda &rest args)
|
||
(multiple-value-bind (fun failure-p warnings style-warnings notes)
|
||
(apply #'checked-compile lambda args)
|
||
(declare (ignore fun failure-p))
|
||
(assert (= (length (append warnings style-warnings notes)) 1)))))
|
||
(test `(lambda () (declare (dynamic-extent #'bar)))
|
||
:allow-style-warnings 'style-warning)
|
||
(test `(lambda () (declare (dynamic-extent bar)))
|
||
:allow-warnings 'warning)
|
||
(test `(lambda (bar) (cons bar (lambda () (declare (dynamic-extent bar)))))
|
||
:allow-notes 'sb-ext:compiler-note)
|
||
(test `(lambda ()
|
||
(flet ((bar () t))
|
||
(cons #'bar (lambda () (declare (dynamic-extent #'bar))))))
|
||
:allow-notes 'sb-ext:compiler-note)))
|
||
|
||
(with-test (:name :bug-586105)
|
||
(flet ((test (x)
|
||
(let ((vec1 (make-array 1 :initial-contents (list (list x))))
|
||
(vec2 (make-array 1 :initial-contents `((,x))))
|
||
(vec3 (make-array 1 :initial-contents `#((,x))))
|
||
(vec4 (make-array 1 :initial-contents `(#(,x)))))
|
||
(declare (dynamic-extent vec1 vec2 vec3 vec4))
|
||
(assert (eql x (car (aref vec1 0))))
|
||
(assert (eql x (car (aref vec2 0))))
|
||
(assert (eql x (car (aref vec3 0))))
|
||
(assert (eql x (elt (aref vec4 0) 0))))))
|
||
(assert-no-consing (test 42))))
|
||
|
||
(defun bug-681092 ()
|
||
(declare (optimize speed))
|
||
(let ((c 0))
|
||
(flet ((bar () c))
|
||
(declare (dynamic-extent #'bar))
|
||
(do () ((list) (bar))
|
||
(setf c 10)
|
||
(return (bar))))))
|
||
(with-test (:name :bug-681092)
|
||
(assert (= 10 (bug-681092))))
|
||
|
||
;;;; Including a loop in the flow graph between a DX-allocation and
|
||
;;;; the start of its environment would, for a while, cause executing
|
||
;;;; any of the code in the loop to discard the value. Found via
|
||
;;;; attempting to DX-allocate an array, which triggered the FILL
|
||
;;;; transform, which inserted such a loop.
|
||
(defun bug-1472785 (x)
|
||
(let ((y (let ((z (cons nil nil)))
|
||
(let ((i 0))
|
||
(tagbody
|
||
b1
|
||
(when (= x i) (go b2))
|
||
(incf i)
|
||
(go b1)
|
||
b2))
|
||
z))
|
||
(w (cons t t)))
|
||
(declare (dynamic-extent y w))
|
||
(eq y w)))
|
||
(with-test (:name :bug-1472785)
|
||
(assert (null (bug-1472785 1))))
|
||
|
||
;;;; &REST lists should stop DX propagation -- not required by ANSI,
|
||
;;;; but required by sanity.
|
||
|
||
(declaim (inline rest-stops-dx))
|
||
(defun-with-dx rest-stops-dx (&rest args)
|
||
(declare (dynamic-extent args))
|
||
(apply #'opaque-identity args))
|
||
|
||
(defun-with-dx rest-stops-dx-ok ()
|
||
(equal '(:foo) (rest-stops-dx (list :foo))))
|
||
|
||
(with-test (:name :rest-stops-dynamic-extent)
|
||
(assert (rest-stops-dx-ok)))
|
||
|
||
;;;; These tests aren't strictly speaking DX, but rather &REST -> &MORE
|
||
;;;; conversion.
|
||
(with-test (:name :rest-to-more-conversion)
|
||
(let ((f1 (checked-compile `(lambda (f &rest args)
|
||
(apply f args)))))
|
||
(assert-no-consing (assert (eql f1 (funcall f1 #'identity f1)))))
|
||
(let ((f2 (checked-compile `(lambda (f1 f2 &rest args)
|
||
(values (apply f1 args) (apply f2 args))))))
|
||
(assert-no-consing (multiple-value-bind (a b)
|
||
(funcall f2 (lambda (x y z) (+ x y z)) (lambda (x y z) (- x y z))
|
||
1 2 3)
|
||
(assert (and (eql 6 a) (eql -4 b))))))
|
||
(let ((f3 (checked-compile `(lambda (f &optional x &rest args)
|
||
(when x
|
||
(apply f x args))))))
|
||
(assert-no-consing (assert (eql 42 (funcall f3
|
||
(lambda (a b c) (+ a b c))
|
||
11
|
||
10
|
||
21)))))
|
||
(let ((f4
|
||
(checked-compile `(lambda (f &optional x &rest args
|
||
&key y &allow-other-keys)
|
||
(apply f y x args))
|
||
:allow-style-warnings t)))
|
||
(assert-no-consing (funcall f4 (lambda (y x yk y2 b c)
|
||
(assert (eq y 'y))
|
||
(assert (= x 2))
|
||
(assert (eq :y yk))
|
||
(assert (eq y2 'y))
|
||
(assert (eq b 'b))
|
||
(assert (eq c 'c)))
|
||
2 :y 'y 'b 'c)))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (a b c &rest args)
|
||
(apply #'list* a b c args))
|
||
((1 2 3 4 5 6 '(7)) '(1 2 3 4 5 6 7)))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (x y)
|
||
(concatenate 'string x y))
|
||
(("foo" "bar") "foobar"))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (&rest args)
|
||
(lambda (f)
|
||
(apply f args)))
|
||
(('a 'b 'c 'd 'e 'f) '(a b c d e f)
|
||
:test (lambda (values expected)
|
||
(equal (multiple-value-list
|
||
(funcall (first values) 'list))
|
||
expected))))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (&rest args)
|
||
(flet ((foo (f)
|
||
(apply f args)))
|
||
#'foo))
|
||
(('a 'b 'c 'd 'e 'f) '(a b c d e f)
|
||
:test (lambda (values expected)
|
||
(equal (multiple-value-list
|
||
(funcall (first values) 'list))
|
||
expected))))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (f &rest args)
|
||
(flet ((foo (g)
|
||
(apply g args)))
|
||
(declare (dynamic-extent #'foo))
|
||
(funcall f #'foo)))
|
||
(((lambda (f) (funcall f 'list)) 'a 'b 'c 'd 'e 'f)
|
||
'(a b c d e f)))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (f &rest args)
|
||
(flet ((foo (g)
|
||
(apply g args)))
|
||
(funcall f #'foo)))
|
||
(((lambda (f) (funcall f 'list)) 'a 'b 'c 'd 'e 'f)
|
||
'(a b c d e f)))
|
||
(checked-compile-and-assert ()
|
||
`(lambda (x y z)
|
||
(block out
|
||
(labels ((foo (x &rest rest)
|
||
(apply (lambda (&rest rest2)
|
||
(return-from out (values-list rest2)))
|
||
x rest)))
|
||
(if x
|
||
(foo x y z)
|
||
(foo y z x)))))
|
||
((1 2 3) (values 1 2 3))))
|
||
|
||
(defun opaque-funcall (function &rest arguments)
|
||
(apply function arguments))
|
||
|
||
(with-test (:name :implicit-value-cells)
|
||
(flet ((test-it (type input output)
|
||
(checked-compile-and-assert ()
|
||
`(lambda (x)
|
||
(declare (type ,type x))
|
||
(flet ((inc ()
|
||
(incf x)))
|
||
(declare (dynamic-extent #'inc))
|
||
(list (opaque-funcall #'inc) x)))
|
||
((input) (list output output)))))
|
||
(let ((width sb-vm:n-word-bits))
|
||
(test-it t (1- most-positive-fixnum) most-positive-fixnum)
|
||
(test-it `(unsigned-byte ,(1- width)) (ash 1 (- width 2)) (1+ (ash 1 (- width 2))))
|
||
(test-it `(signed-byte ,width) (ash -1 (- width 2)) (1+ (ash -1 (- width 2))))
|
||
(test-it `(unsigned-byte ,width) (ash 1 (1- width)) (1+ (ash 1 (1- width))))
|
||
(test-it 'single-float 3f0 4f0)
|
||
(test-it 'double-float 3d0 4d0)
|
||
(test-it '(complex single-float) #c(3f0 4f0) #c(4f0 4f0))
|
||
(test-it '(complex double-float) #c(3d0 4d0) #c(4d0 4d0)))))
|
||
|
||
(with-test (:name :sap-implicit-value-cells)
|
||
(let ((f (checked-compile `(lambda (x)
|
||
(declare (type system-area-pointer x))
|
||
(flet ((inc ()
|
||
(setf x (sb-sys:sap+ x 16))))
|
||
(declare (dynamic-extent #'inc))
|
||
(list (opaque-funcall #'inc) x)))))
|
||
(width sb-vm:n-machine-word-bits))
|
||
(assert (every (lambda (x)
|
||
(sb-sys:sap= x (sb-sys:int-sap (+ 16 (ash 1 (1- width))))))
|
||
(funcall f (sb-sys:int-sap (ash 1 (1- width))))))))
|
||
|
||
(with-test (:name (:&more-bounds :lp-1154946))
|
||
(checked-compile-and-assert () '(lambda (&rest args) (car args)) (() nil))
|
||
(checked-compile-and-assert () '(lambda (&rest args) (nth 6 args)) (() nil))
|
||
(checked-compile-and-assert () '(lambda (&rest args) (cadr args)) (() nil))
|
||
(checked-compile-and-assert () '(lambda (&rest args) (third args)) (() nil)))
|
||
|
||
(with-test (:name :local-notinline-functions)
|
||
(multiple-value-bind (start result end)
|
||
(funcall (checked-compile
|
||
`(lambda ()
|
||
(values (sb-kernel:current-sp)
|
||
(flet ((x () (cons 1 2)))
|
||
(declare (notinline x))
|
||
(let ((x (x)))
|
||
(declare (dynamic-extent x))
|
||
(true x))
|
||
(true 10))
|
||
(sb-kernel:current-sp)))))
|
||
(assert (sb-sys:sap= start end))
|
||
(assert result))
|
||
(multiple-value-bind (start result end)
|
||
(funcall (checked-compile
|
||
`(lambda ()
|
||
(declare (optimize speed))
|
||
(values (sb-kernel:current-sp)
|
||
(flet ((x () (cons 1 2)))
|
||
(let ((x (x))
|
||
(y (x)))
|
||
(declare (dynamic-extent x y))
|
||
(true x)
|
||
(true y))
|
||
(true 10))
|
||
(sb-kernel:current-sp)))))
|
||
(assert (sb-sys:sap= start end))
|
||
(assert result)))
|
||
|
||
(with-test (:name :unused-paremeters-of-an-inlined-function)
|
||
(let ((name (gensym "fun")))
|
||
(proclaim `(inline ,name))
|
||
(eval `(defun ,name (a b &optional c d)
|
||
(declare (ignore c d))
|
||
(cons a b)))
|
||
(checked-compile-and-assert ()
|
||
`(lambda ()
|
||
(let ((x (cons
|
||
(,name 1 2)
|
||
(,name 2 3))))
|
||
(declare (dynamic-extent x))
|
||
(true x))
|
||
#',name)
|
||
(() '(2 . 3) :test (lambda (values expected)
|
||
(equal (multiple-value-list
|
||
(funcall (first values) 2 3))
|
||
expected))))))
|
||
|
||
(with-test (:name :nested-multiple-use-vars)
|
||
(let ((fun (checked-compile
|
||
`(lambda ()
|
||
(sb-int:dx-let ((x (let ((x (make-array 3)))
|
||
(setf (aref x 0) 22)
|
||
x)))
|
||
(opaque-identity x)
|
||
10)))))
|
||
(assert-no-consing (funcall fun))))
|
||
|
||
(with-test (:name :nested-multiple-use-vars-vector-fill)
|
||
(let ((fun (checked-compile
|
||
`(lambda ()
|
||
(declare (optimize speed))
|
||
(sb-int:dx-let ((x (make-array 3 :initial-element 123)))
|
||
(opaque-identity x)
|
||
10)))))
|
||
(assert-no-consing (funcall fun))))
|
||
|
||
(defun trythisfun (test arg &key key)
|
||
(declare (dynamic-extent test key))
|
||
(funcall test (funcall key arg)))
|
||
(declaim (maybe-inline sortasort))
|
||
(defun sortasort (seq pred)
|
||
(declare (dynamic-extent pred))
|
||
(funcall pred (elt seq 0) (elt seq 1))
|
||
seq)
|
||
(eval-when (:compile-toplevel :load-toplevel :execute)
|
||
(import 'sb-c::fun-name-dx-args))
|
||
(with-test (:name :store-dx-arglist)
|
||
;; Positional argument 0 and keyword argument :KEY
|
||
(assert (equal (fun-name-dx-args 'trythisfun) '(0 :key)))
|
||
;; Positional argument 1
|
||
(assert (equal (fun-name-dx-args 'sortasort) '(1)))
|
||
;; And also an inline expansion
|
||
(assert (sb-c::fun-name-inline-expansion 'sortasort)))
|
||
(with-test (:name :store-dx-arglist-std-functions)
|
||
;; You might think this would go in the SB-C::FUN-INFO,
|
||
;; but we want user-defined functions to have this bit of info
|
||
;; as well, potentially.
|
||
(assert (equal (fun-name-dx-args 'remove) '(:test :test-not :key)))
|
||
(assert (equal (fun-name-dx-args 'remove-if) '(0 :key))))
|
||
|
||
(defun trivial-hof (fun arg)
|
||
(declare (dynamic-extent fun))
|
||
(funcall fun 3 arg))
|
||
|
||
(declaim (ftype (function (function t &key (:key function)) *)
|
||
fancy-hof))
|
||
(defun fancy-hof (pred arg &key key)
|
||
(declare (dynamic-extent pred key))
|
||
(funcall pred (funcall key arg)))
|
||
|
||
(defun autodxclosure1 (&optional (x 4))
|
||
;; Calling a higher-order function will only implicitly
|
||
;; stack-allocate a funarg if the callee is trusted (a CL: function)
|
||
;; or the caller is unsafe.
|
||
(declare (optimize speed (safety 0) (debug 0)))
|
||
(trivial-hof (lambda (a b) (+ a b x)) 92))
|
||
|
||
(defun autodxclosure2 (&aux (i 0) (j 0))
|
||
(declare (optimize speed (safety 0) (debug 0)))
|
||
(assert (eq (fancy-hof (lambda (x) (incf i) (symbolp x))
|
||
'(a b)
|
||
:key (lambda (x) (incf j) (car x)))
|
||
t))
|
||
(assert (and (= i 1) (= j 1))))
|
||
|
||
(with-test (:name (:no-consing :auto-dx-closures))
|
||
(assert-no-consing (autodxclosure1 42))
|
||
(assert-no-consing (autodxclosure2)))
|
||
|
||
(with-test (:name (:no-consing :more-auto-dx-closures))
|
||
(assert-no-consing
|
||
(let ((ct 0))
|
||
(sb-vm:map-allocated-objects
|
||
(lambda (obj type size)
|
||
(declare (ignore obj type size))
|
||
(incf ct))
|
||
:static)
|
||
;; Static-space has some small number of objects
|
||
(assert (<= 1 ct 100)))))
|
||
|
||
(with-test (:name :cast-dx-funarg-no-spurious-warn)
|
||
(checked-compile
|
||
'(lambda (&key (test #'eql) key)
|
||
(declare (function test key))
|
||
(declare (dynamic-extent test key))
|
||
test key
|
||
1)
|
||
:allow-notes nil))
|
||
|
||
(with-test (:name :stack-alloc-p :skipped-on (not :sb-thread))
|
||
(let* ((sem1 (sb-thread:make-semaphore))
|
||
(sem2 (sb-thread:make-semaphore))
|
||
(blah nil)
|
||
(thread
|
||
(sb-thread:make-thread
|
||
(lambda ()
|
||
(sb-int:dx-let ((a (cons 1 2)))
|
||
(setq blah a)
|
||
(sb-thread:signal-semaphore sem1)
|
||
(sb-thread:wait-on-semaphore sem2))
|
||
'yay))))
|
||
;; thread1 will assign something on its stack into BLAH
|
||
(sb-thread:wait-on-semaphore sem1)
|
||
(assert blah)
|
||
;; not on my stack
|
||
(assert (not (sb-ext:stack-allocated-p blah)))
|
||
;; but on their stack
|
||
(assert (eq (sb-ext:stack-allocated-p blah t) thread))
|
||
(setq blah nil) ; be safe, don't look at other stacks
|
||
(sb-thread:signal-semaphore sem2)
|
||
(sb-thread:join-thread thread)))
|
||
|
||
(with-test (:name :back-propagation-losing-blocks)
|
||
(checked-compile-and-assert ()
|
||
`(lambda ()
|
||
(let ((v (list (list :good)
|
||
(labels ((r (v)
|
||
(or v
|
||
(r (not v)))))
|
||
(r nil)))))
|
||
(declare (dynamic-extent v))
|
||
(caar v)))
|
||
(() :good)))
|
||
|
||
(with-test (:name :back-propagation-losing-blocks.2)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (b c)
|
||
(let ((v
|
||
(vector
|
||
(list 10)
|
||
(BLOCK NIL
|
||
(let ((loop-repeat-550 1)
|
||
(loop-sum-551 0))
|
||
(tagbody
|
||
next-loop
|
||
(if (<= loop-repeat-550 0)
|
||
(go end-loop)
|
||
(decf loop-repeat-550))
|
||
(setq loop-sum-551
|
||
(if (not c)
|
||
(return-from nil 0)
|
||
(block nil
|
||
(let ((loop-repeat-552 1)
|
||
(loop-sum-553 0))
|
||
(tagbody
|
||
next-loop
|
||
(if (<= loop-repeat-552 0)
|
||
(go end-loop)
|
||
(decf loop-repeat-552))
|
||
(setq loop-sum-553
|
||
(if b
|
||
1
|
||
2))
|
||
(go next-loop)
|
||
end-loop
|
||
(return-from nil loop-sum-553))))))
|
||
(go next-loop)
|
||
end-loop)
|
||
loop-sum-551)))))
|
||
(declare (dynamic-extent v))
|
||
(car (elt v 0))))
|
||
((t t) 10)))
|
||
|
||
(with-test (:name :back-propagate-one-dx-lvar-nlx)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (c)
|
||
(catch 'c
|
||
(let ((v (list (vector 0 c 0 0) (catch 'ct5 (throw 'ct5 0)) 0)))
|
||
(declare (dynamic-extent v))
|
||
(elt (elt v 0) 1))))
|
||
((33) 33)))
|
||
|
||
(with-test (:name :dominators-recomputation)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (x)
|
||
(let ((m (if x
|
||
(make-array 2 :initial-element 1)
|
||
(make-array 2 :initial-element 2))))
|
||
(declare (dynamic-extent m))
|
||
(elt m 0)))
|
||
((t) 1)
|
||
((nil) 2)))
|
||
|
||
(with-test (:name :notes-in-deleted-code)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(labels ((z ()
|
||
(list 1))
|
||
(fn (&key)
|
||
(let ((x (z)))
|
||
(declare (dynamic-extent x))
|
||
(print x)
|
||
1)))
|
||
(declare (ignorable #'fn))))
|
||
:allow-notes nil))
|
||
|
||
(with-test (:name :list+fill+make-array)
|
||
(let ((fun (checked-compile
|
||
`(lambda ()
|
||
(declare (optimize speed))
|
||
(let* ((v (make-array 8 :initial-element nil))
|
||
(node (let ((m v))
|
||
(list m m))))
|
||
(declare (simple-vector v)
|
||
(dynamic-extent v node))
|
||
(opaque-identity node)
|
||
0))
|
||
:allow-notes nil)))
|
||
(assert-no-consing (funcall fun))))
|
||
|
||
(with-test (:name :with-output-to-string)
|
||
(let ((s (make-array 20000 :fill-pointer 0 :element-type 'character)))
|
||
(assert-no-consing
|
||
(with-output-to-string (*standard-output* s)
|
||
(write-char #\x)))))
|
||
|
||
(with-test (:name :cycles-without-dx-lvars)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (f x z)
|
||
(let ((l (if x
|
||
(loop while z)
|
||
(list (list 1)))))
|
||
(declare (dynamic-extent l))
|
||
(funcall f l)))
|
||
(((lambda (l) (equal l '((1)))) nil nil) t)
|
||
(((lambda (l) (equal l nil)) t nil) t)))
|
||
|
||
;;; Test that we don't preserve UVLs too long because of DX in stack
|
||
;;; analysis.
|
||
(with-test (:name :uvl-preserved-by-dx-too-long)
|
||
(checked-compile
|
||
'(lambda (f1 test args)
|
||
(flet ((f3 (&rest m) (apply test m)))
|
||
(declare (dynamic-extent #'f3))
|
||
(apply f1 #'f3 args)))))
|
||
|
||
;;; Test that we don't preserve LVARs that just happen to be in the
|
||
;;; same block.
|
||
(with-test (:name :uvl-preserved-incidentally)
|
||
(checked-compile
|
||
'(lambda (b)
|
||
(catch 'ct2
|
||
(multiple-value-prog1 (multiple-value-prog1 (restart-case 0))
|
||
(restart-case 0)
|
||
b
|
||
0)))))
|
||
|
||
(with-test (:name :uvl-preserved-incidentally.2)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(multiple-value-prog1
|
||
(multiple-value-prog1 (catch 'ct2 0))
|
||
(restart-bind nil
|
||
(flet ((%f () 0))
|
||
(declare (dynamic-extent (function %f)))
|
||
(%f)))))))
|
||
|
||
;;; After dx entries no longer started delimiting their blocks, there
|
||
;;; was no reason for propagate-dx to end its own blocks either. In
|
||
;;; fact propagate-dx started causing problems instead.
|
||
(with-test (:name :propagate-dx-ended-block)
|
||
(checked-compile
|
||
'(lambda (c)
|
||
(declare (notinline - svref))
|
||
(let* ((v1 (svref #(1 3 4 6) 0)))
|
||
(declare (dynamic-extent v1))
|
||
(- c v1)))))
|
||
|
||
(with-test (:name :dx-for-optional-entries)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (a b c)
|
||
(declare (notinline funcall max))
|
||
(labels ((%f14
|
||
(f14-1 f14-2
|
||
&optional (f14-3 (setq a (min 13 (max 1 0))))
|
||
(f14-4 (setq c 0)))
|
||
(declare (ignore f14-1 f14-2 f14-3 f14-4))
|
||
0))
|
||
(declare (dynamic-extent (function %f14)))
|
||
(funcall #'%f14
|
||
(%f14
|
||
(min 11 (max 0 c))
|
||
(funcall #'%f14 a b 1)
|
||
1
|
||
b)
|
||
0)))
|
||
((1 2 3) 0)))
|
||
|
||
(with-test (:name :dx-for-optional-entries.2)
|
||
(checked-compile-and-assert
|
||
(:optimize '(:speed 3))
|
||
`(lambda (b)
|
||
(declare (notinline funcall))
|
||
(labels ((%f1 (&optional f1-1)
|
||
(declare (ignore f1-1))
|
||
(shiftf b 0)))
|
||
(declare (dynamic-extent (function %f1)))
|
||
(funcall #'%f1 :bad)))
|
||
((10) 10)))
|
||
|
||
(with-test (:name :no-stack-cleanup-before-return)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda ()
|
||
(let ((identity (eval '#'identity))
|
||
(continuation (eval '(lambda (&rest args)
|
||
args))))
|
||
(flet ((f (n fn)
|
||
(funcall fn n (vector))))
|
||
(f 1
|
||
(lambda (j &rest points)
|
||
(apply continuation j (mapcar identity points)))))))
|
||
(() '(1 #()) :test #'equalp)))
|
||
|
||
(with-test (:name :special-bind-removal)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (a)
|
||
(multiple-value-prog1 (eval a)
|
||
(let ((* (let ((x (list 1)))
|
||
(setf (car x) 1)
|
||
x)))
|
||
(declare (dynamic-extent *)))))
|
||
((1) 1)))
|
||
|
||
(with-test (:name :let-setf-aref)
|
||
(checked-compile
|
||
`(lambda ()
|
||
(declare (optimize debug))
|
||
(let ((x (let ((z (make-array 3)))
|
||
(setf (aref z 0) 10)
|
||
(setf (aref z 0) 20)
|
||
z)))
|
||
(declare (dynamic-extent x))
|
||
(aref x 0)))
|
||
:allow-notes nil))
|
||
|
||
(with-test (:name :dx-multi-use-with-cast)
|
||
(checked-compile '(lambda (bar)
|
||
(declare (inline make-foo1))
|
||
(let ((a (if bar
|
||
(make-foo1 :x 2)
|
||
(cons 1 2))))
|
||
(declare (dynamic-extent a))
|
||
(print a)))))
|
||
|
||
(with-test (:name :dx-propagation-dx-already-exists)
|
||
(checked-compile '(lambda ()
|
||
(let ((x (let ((m (make-array 3)))
|
||
(declare (dynamic-extent m))
|
||
(fill m 0)
|
||
m)))
|
||
(declare (dynamic-extent x))
|
||
(print x)))))
|
||
|
||
(with-test (:name :nested-var)
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda ()
|
||
(multiple-value-bind (v4 c)
|
||
(values (vector 1) 2)
|
||
(let ((m v4))
|
||
(declare (dynamic-extent m))
|
||
(eval m))
|
||
(values (sb-ext:heap-allocated-p v4) c)))
|
||
(() (values :dynamic 2)))
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda ()
|
||
(flet ((m (&key v4 c)
|
||
(let ((m v4))
|
||
(declare (dynamic-extent m))
|
||
(eval m))
|
||
(values v4 c)))
|
||
(let ((m (vector 1)))
|
||
(m :v4 m)
|
||
(sb-ext:heap-allocated-p m))))
|
||
(() :dynamic)))
|
||
|
||
;;; The point of PRINTing seems to be to force a use of the DX value
|
||
;;; and also show that it's not garbage. But assuming it works,
|
||
;;; we don't need to actually see the output
|
||
(defun print-nothing (x) (print x (make-broadcast-stream)))
|
||
|
||
(with-test (:name :dx-do-propagate-let-var)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (z)
|
||
(let ((x (cons nil (let ((y (list z z z)))
|
||
(declare (optimize debug))
|
||
y))))
|
||
(declare (dynamic-extent x))
|
||
(print-nothing x)
|
||
(print-nothing x)
|
||
(assert (sb-ext:stack-allocated-p x))
|
||
(assert (sb-ext:stack-allocated-p (cdr x))))
|
||
nil)
|
||
((3) NIL)))
|
||
|
||
(with-test (:name :dx-anonymous-closure)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (z)
|
||
(let ((x (lambda () (print-nothing z))))
|
||
(declare (dynamic-extent x))
|
||
(funcall x)
|
||
(funcall x)
|
||
(assert (sb-ext:stack-allocated-p x))
|
||
(funcall x)))
|
||
((3) 3)))
|
||
|
||
(with-test (:name :dx-flet-substitution-multiple-refs)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (z)
|
||
(flet ((y (a)
|
||
(incf z a)))
|
||
(let ((x #'y))
|
||
(declare (dynamic-extent x))
|
||
(funcall x 5)
|
||
(funcall x 6)
|
||
(assert (sb-ext:stack-allocated-p x))
|
||
(funcall x 7))
|
||
(map nil #'y '(1 2 3))
|
||
z))
|
||
((3) 27)))
|
||
|
||
(with-test (:name :dx-flet-substitution-different-frame)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (z)
|
||
(labels ((y (a)
|
||
(incf z a))
|
||
(g ()
|
||
(let ((x #'y))
|
||
(declare (dynamic-extent x))
|
||
(funcall x 5)
|
||
(print-nothing x)
|
||
(funcall x 6))))
|
||
(map nil #'y '(1 2 3))
|
||
(g)
|
||
(g)))
|
||
((3) 31)))
|
||
|
||
(with-test (:name :dx-anonymous-closure-otherwise-inaccessible)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((y (cons (lambda () (print-nothing x))
|
||
(lambda () (print-nothing x)))))
|
||
(declare (dynamic-extent y))
|
||
(print-nothing y)
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(assert (sb-ext:stack-allocated-p (car y)))
|
||
(assert (sb-ext:stack-allocated-p (cdr y)))
|
||
(print-nothing y)
|
||
(funcall (car y))))
|
||
((3) 3)))
|
||
|
||
(with-test (:name :dx-anonymous-closure-otherwise-inaccessible.flet)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((y (cons (flet ((g () (print-nothing x)))
|
||
#'g)
|
||
(lambda () (print-nothing x)))))
|
||
(declare (dynamic-extent y))
|
||
(print-nothing y)
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(assert (sb-ext:stack-allocated-p (car y)))
|
||
(assert (sb-ext:stack-allocated-p (cdr y)))
|
||
(print-nothing y)
|
||
(funcall (car y))))
|
||
((3) 3)))
|
||
|
||
(defun known-function-autodx (x thing list)
|
||
(subst-if x (lambda (y) (eq y thing)) list))
|
||
|
||
(with-test (:name :auto-dx-known-functions-too)
|
||
(assert (equal (known-function-autodx 3 4 (list 1 2 3 4)) '(1 2 3 3)))
|
||
(assert-no-consing (known-function-autodx 3 4 nil)))
|
||
|
||
(defun known-function-autodx-transform (x list)
|
||
(map nil (lambda (y) (+ x y)) list))
|
||
|
||
(with-test (:name :auto-dx-known-functions-too.transform)
|
||
(assert-no-consing (known-function-autodx-transform 3 '(1 2 3 4))))
|
||
|
||
(defun known-function-autodx-transform-2 (array x list)
|
||
(map-into array (lambda (y) (+ x y)) list)
|
||
array)
|
||
|
||
(with-test (:name :auto-dx-known-functions-too.transform-2)
|
||
(let ((array (make-array 4)))
|
||
(declare (dynamic-extent array))
|
||
(assert (equalp (known-function-autodx-transform-2 array 3 '(1 2 3 4)) #(4 5 6 7)))
|
||
(assert-no-consing (known-function-autodx-transform-2 array 3 '(1 2 3 4)))))
|
||
|
||
(defun auto-dx-cleaned-up-too-many-times (off array)
|
||
(let ((acc 0))
|
||
(flet ((positivep (num) (plusp (+ num off))))
|
||
(dotimes (i 10)
|
||
(incf acc (position-if #'positivep array))))
|
||
acc))
|
||
|
||
(with-test (:name :auto-dx-cleaned-up-too-many-times)
|
||
(assert (= (auto-dx-cleaned-up-too-many-times 1 #(-1 2 3)) 10)))
|
||
|
||
(with-test (:name :auto-dx-with-single-ref-flet)
|
||
(assert-no-consing (auto-dx-cleaned-up-too-many-times 1 #(-1 2 3))))
|
||
|
||
(with-test (:name :auto-dx-correct-mess-up)
|
||
(checked-compile-and-assert
|
||
(:allow-notes nil
|
||
:optimize '(:speed 1))
|
||
'(lambda (x y z)
|
||
(map 'list
|
||
(lambda (a b)
|
||
(+ z a b))
|
||
x
|
||
y))
|
||
(('(1 2 3 4) '(1 2 3 4) 1) '(3 5 7 9) :test #'equal)))
|
||
|
||
(defun dxf (s i)
|
||
(elt s i))
|
||
(declaim (notinline dxf))
|
||
|
||
(with-test (:name :dynamic-extent-lp2031224)
|
||
(checked-compile
|
||
'(lambda (a b)
|
||
(let ((v
|
||
(list
|
||
(list (vector 0 0) a
|
||
(restart-bind nil a))
|
||
(block b2 b))))
|
||
(declare (dynamic-extent v))
|
||
(dxf (dxf v 0) 2)))))
|
||
|
||
(with-test (:name :dynamic-extent-lp2031399)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(declare (notinline funcall not))
|
||
(labels ((%f1 ()
|
||
(labels ((%f2 (&key (key1
|
||
(if (not t)
|
||
(ignore-errors 0)
|
||
0)))
|
||
(declare (ignore key1))
|
||
0))
|
||
(funcall #'%f2))))
|
||
(%f1)))))
|
||
|
||
(with-test (:name :dynamic-extent-conditional-allocation.partial)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (test y z)
|
||
(let ((x (if test
|
||
(cons (cons y y) z)
|
||
nil)))
|
||
(declare (dynamic-extent x))
|
||
(when test
|
||
(assert (sb-ext:stack-allocated-p x)))
|
||
(and x
|
||
(* (car (car x)) (cdr x)))))
|
||
((t 4 5) 20)
|
||
((nil 4 5) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-conditional-allocation.partial.2)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (test y z)
|
||
(let (res)
|
||
(dotimes (i 3)
|
||
(let ((x (if test
|
||
(cons (cons y y) z)
|
||
nil)))
|
||
(declare (dynamic-extent x))
|
||
(when test
|
||
(assert (sb-ext:stack-allocated-p x)))
|
||
(setq res (and x
|
||
(* (car (car x)) (cdr x))))))
|
||
res))
|
||
((t 4 5) 20)
|
||
((nil 4 5) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-mess-up)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((y (list (cons 1 2)
|
||
(progn
|
||
(if x
|
||
(print-nothing 2)
|
||
(print-nothing 3))
|
||
(cons 3 4))
|
||
(progn
|
||
(if x
|
||
(print-nothing 2)
|
||
(print-nothing 3))
|
||
(cons 3 4)))))
|
||
(declare (dynamic-extent y))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(copy-tree y)))
|
||
((t) '((1 . 2) (3 . 4) (3 . 4)) :test #'tree-equal)))
|
||
|
||
(with-test (:name :stack-analysis-graph-walk-nlx)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(let ((+++ 3))
|
||
(multiple-value-bind (result error)
|
||
(ignore-errors (eval 1))
|
||
(declare (ignore result error)))
|
||
(catch 'foo
|
||
(error "bar"))
|
||
(multiple-value-bind (result error)
|
||
(ignore-errors (eval 1))
|
||
(declare (ignore result error)))))))
|
||
|
||
(with-test (:name :stack-analysis-graph-walk-nlx.2)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(declare (optimize (speed 0) (debug 1)))
|
||
(tagbody
|
||
a
|
||
(flet ((f ()
|
||
(go tag)))
|
||
(f))
|
||
tag
|
||
(let ((m (list 1)))
|
||
(declare (dynamic-extent m))
|
||
(eval m)
|
||
(let ((m (list 2)))
|
||
(declare (dynamic-extent m))
|
||
(eval m)))))))
|
||
|
||
(defstruct thing
|
||
(times-v nil :type simple-vector :read-only t)
|
||
(fringe nil))
|
||
|
||
(with-test (:name :dx-propagation-existing-dynamic-extent)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda ()
|
||
(declare (inline make-thing))
|
||
(let ((times-v (make-array 2))
|
||
(result nil))
|
||
(declare (dynamic-extent times-v))
|
||
(dolist (portion '(1 2 3))
|
||
(let ((scratchpad (make-thing :times-v times-v)))
|
||
(declare (dynamic-extent scratchpad))
|
||
(setq result (nconc result (thing-fringe scratchpad)))))
|
||
result))
|
||
(() nil)))
|
||
|
||
(with-test (:name :dynamic-extent-setq-let)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(dotimes (i 2)
|
||
(let ((y (cons x x)))
|
||
(declare (dynamic-extent y))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(setq y (list x x x))
|
||
(assert (equal y (list x x x)))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(setq y (make-array 20 :initial-element nil))
|
||
(assert (equalp y (make-array 20 :initial-element nil)))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(setq y (list (cons x i) (cons x i)))
|
||
(assert (tree-equal y (list (cons x i) (cons x i))))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(assert (sb-ext:stack-allocated-p (first y) (second y))))))
|
||
((6) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-setq-local-calls)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x y)
|
||
(labels ((f (a b)
|
||
(declare (dynamic-extent a b))
|
||
(setq a (cons x y))
|
||
(assert (sb-ext:stack-allocated-p a))
|
||
(setq b (vector x y))
|
||
(assert (sb-ext:stack-allocated-p b))
|
||
(assert (equal a (cons x y)))
|
||
(assert (equalp b (vector x y)))))
|
||
(dotimes (i 4)
|
||
(cond (x
|
||
(f nil nil)
|
||
(print-nothing x))
|
||
(t
|
||
(f t t)
|
||
(print-nothing y))))))
|
||
((nil 2) nil)
|
||
((t 2) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-setq-local-calls.interleave)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x y)
|
||
(labels ((f (a b)
|
||
(declare (dynamic-extent a b))
|
||
(let ((z (cons a b)))
|
||
(declare (dynamic-extent z))
|
||
(setq a (cons x y))
|
||
(print-nothing z))
|
||
(let ((z (cons a b)))
|
||
(declare (dynamic-extent z))
|
||
(print-nothing z))
|
||
(assert (sb-ext:stack-allocated-p a))
|
||
(assert (equal a (cons x y)))
|
||
(setq b (vector x y))
|
||
(assert (sb-ext:stack-allocated-p b))
|
||
(assert (equal a (cons x y)))
|
||
(assert (equalp b (vector x y)))))
|
||
(dotimes (i 4)
|
||
(cond (x
|
||
(f nil nil)
|
||
(print-nothing x))
|
||
(t
|
||
(f t t)
|
||
(print-nothing y))))))
|
||
((nil 2) nil)
|
||
((t 2) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-setq-different-environments)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (test)
|
||
(let ((list nil))
|
||
(declare (dynamic-extent list))
|
||
(flet ((body ()
|
||
(setq list (cons nil list))))
|
||
(if test
|
||
(progn
|
||
(body)
|
||
(print-nothing 'foo))
|
||
(body)))))
|
||
((t) 'foo)
|
||
((nil) '(nil))))
|
||
|
||
(with-test (:name :dynamic-extent-nested)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a)
|
||
(let ((v (list (vector 0 0)
|
||
(let ((x (cons 1 2)))
|
||
(declare (dynamic-extent x))
|
||
(print-nothing x)
|
||
(list 1 2)))))
|
||
(declare (dynamic-extent v))
|
||
(copy-list (elt v a))))
|
||
((1) '(1 2))))
|
||
|
||
(with-test (:name :dynamic-extent-setq-nested)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda ()
|
||
(dotimes (i 2)
|
||
(let ((y (cons 1 2)))
|
||
(declare (dynamic-extent y))
|
||
(assert (equal y (cons 1 2)))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(let ((z (cons 3 4)))
|
||
(declare (dynamic-extent z))
|
||
(assert (equal z (cons 3 4)))
|
||
(assert (sb-ext:stack-allocated-p z))
|
||
(setq y (cons 2 1))
|
||
(assert (equal y (cons 2 1)))
|
||
(assert (sb-ext:stack-allocated-p y)))
|
||
(let ((z (list 9 9 9 9 9)))
|
||
(declare (dynamic-extent z))
|
||
(assert (equal z (list 9 9 9 9 9)))
|
||
(assert (sb-ext:stack-allocated-p z))
|
||
(assert (equal y (cons 2 1)))
|
||
(assert (sb-ext:stack-allocated-p y))))))
|
||
(() nil)))
|
||
|
||
(with-test (:name :dynamic-extent-start-later)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((bar))
|
||
(declare (dynamic-extent bar))
|
||
(print-nothing (if x 1 2))
|
||
(push 12 bar)
|
||
(assert (sb-ext:stack-allocated-p bar))
|
||
(assert (equal bar '(12)))
|
||
nil))
|
||
((t) nil)
|
||
((nil) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-preserve.unreferenced-tn)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (b)
|
||
(let ((v nil))
|
||
(declare (dynamic-extent v))
|
||
(case b
|
||
(0 (setq v (vector 3)))
|
||
(1 (setq v (vector 0 (let ((x (cons 1 2)))
|
||
(declare (dynamic-extent x))
|
||
(print-nothing x)
|
||
2)))))
|
||
(elt v 0)))
|
||
((0) 3)
|
||
((1) 0)))
|
||
|
||
(with-test (:name :dynamic-extent-interleave.bogus-unused)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (as xs)
|
||
(flet ((pred (a)
|
||
(print-nothing xs)
|
||
a))
|
||
(mapcar #'print-nothing (remove-if #'pred as))))
|
||
(('(1 2 3) '(3 4 5)) nil)))
|
||
|
||
(with-test (:name :dynamic-extent-preserve.heap-exhaustion)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a b c)
|
||
(let ((v nil))
|
||
(declare (dynamic-extent v))
|
||
(case a
|
||
(0 (setq v (list (prog2 b (restart-bind nil -17607) c) 17632849286127)))
|
||
(1 (setq v (list -17532189700714087747 b c))))
|
||
(case a (0 (elt v 1)) (1 (elt v 2)))))
|
||
((1 2 3) 3)))
|
||
|
||
(with-test (:name :dynamic-extent-setq-already-existing)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a)
|
||
(let ((x (eval a)))
|
||
(declare (dynamic-extent x))
|
||
(let ((y 0))
|
||
(declare (dynamic-extent y))
|
||
(let ((z y))
|
||
(setq y x)
|
||
(values z)))))
|
||
((1) 0)))
|
||
|
||
(with-test (:name :stack-allocated-vector-checks-overflow)
|
||
(checked-compile-and-assert
|
||
(:optimize :safe)
|
||
'(lambda ()
|
||
(let ((size
|
||
;; guaranteed to overflow the stack.
|
||
(abs (- (sb-sys:sap-int
|
||
(sb-vm::current-thread-offset-sap sb-vm::thread-control-stack-start-slot))
|
||
(sb-sys:sap-int
|
||
(sb-int:descriptor-sap sb-vm:*control-stack-end*))))))
|
||
(block nil
|
||
(handler-bind ((sb-kernel::stack-allocated-object-overflows-stack
|
||
(lambda (c)
|
||
(assert (= (sb-kernel::stack-allocated-object-overflows-stack-size c)
|
||
(sb-vm::primitive-object-size (make-array size))))
|
||
(return :good)))
|
||
(condition
|
||
(lambda (c) (return c))))
|
||
(let ((x (make-array size)))
|
||
(declare (dynamic-extent x))
|
||
(dotimes (i (length x))
|
||
(setf (aref x i) i))
|
||
(aref x 0))))))
|
||
(() :good)))
|
||
|
||
(with-test (:name :stack-allocated-vector-integer-size-arg)
|
||
(checked-compile-and-assert
|
||
(:optimize :safe)
|
||
'(lambda ()
|
||
(let ((buffer (make-array (* 128 1024) :element-type '(unsigned-byte 8))))
|
||
(declare (dynamic-extent buffer))
|
||
(dotimes (i (length buffer))
|
||
(setf (aref buffer i) (mod i 32)))
|
||
(print-nothing (aref buffer 0))
|
||
nil))
|
||
(() nil)))
|
||
|
||
(with-test (:name :stack-analysis-preserve.setq-loop)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda ()
|
||
(let ((digits '()))
|
||
(declare (dynamic-extent digits))
|
||
(let ((result (make-array 128 :element-type 'base-char)))
|
||
(declare (dynamic-extent result))
|
||
(dotimes (i 2)
|
||
(setq digits (cons 0 digits)))
|
||
(dotimes (i 3)
|
||
(setf (aref result i) #\0)))
|
||
;; can't actually deallocate RESULT yet because we have to keep
|
||
;; DIGITS on the stack.
|
||
(let ((another-result (make-array 128 :element-type 'base-char))
|
||
(another (list 1 2 3 4 5 6 7 8 9 10)))
|
||
(declare (dynamic-extent another another-result))
|
||
(dotimes (i 3)
|
||
(setf (aref another-result i) #\0))
|
||
(print-nothing (car another)))
|
||
(car digits)))
|
||
(() 0)))
|
||
|
||
(with-test (:name :stack-analysis-preserve.setq-nested-loop)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda ()
|
||
(let (y)
|
||
(declare (dynamic-extent y))
|
||
(loop for i to 10
|
||
do (let ((j (list 1 2 3)))
|
||
(declare (dynamic-extent j))
|
||
(print-nothing j)
|
||
(assert (sb-ext:stack-allocated-p j))
|
||
(push (- i) y)))
|
||
(assert (sb-ext:stack-allocated-p y))
|
||
(list-length y)))
|
||
(() 11)))
|
||
|
||
(declaim (inline nested.make))
|
||
(defun nested.make (&optional (x nil))
|
||
(let* ((name (if (stringp x)
|
||
nil
|
||
(print x)))
|
||
(set (list 1)))
|
||
(declare (ignore name))
|
||
(flet ((add (element)
|
||
(setq element set)))
|
||
(map nil #'add x))
|
||
set))
|
||
|
||
(with-test (:name :stack-analysis-preserve.nested-inline)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(let* ((v (make-array 3))
|
||
(set (nested.make)))
|
||
(declare (dynamic-extent v set))
|
||
(print (list v set (lambda ())))
|
||
3))))
|
||
|
||
(with-test (:name :stack-analysis-preserve.dx-node)
|
||
(checked-compile
|
||
'(lambda (b c d)
|
||
(restart-bind nil
|
||
(case (catch 'ct7 55)
|
||
((2258)
|
||
(reduce
|
||
(lambda (x y) y (progv nil b x))
|
||
(list
|
||
(block b5
|
||
(restart-bind nil
|
||
(return-from b5 (- d c))))))))))))
|
||
|
||
(with-test (:name :stack-analysis-preserve.dx-node-1)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(let* ((x (vector))
|
||
(y (vector))
|
||
(z (prog1 (vector)
|
||
(mapcar #'identity nil))))
|
||
(declare (dynamic-extent x y z))
|
||
(print-nothing (list x y z))
|
||
nil))))
|
||
|
||
(with-test (:name :stack-analysis-preserve.functional-ref)
|
||
(checked-compile
|
||
'(lambda (v)
|
||
(loop repeat 2
|
||
collect
|
||
(flet ((l (x)
|
||
x
|
||
v))
|
||
(mapcar #'l v)
|
||
(map nil #'l v))))))
|
||
|
||
(with-test (:name :encode-error-break-large-immediate)
|
||
(disassemble '(lambda ()
|
||
(sb-int:dx-let ((v (make-array 65536
|
||
:element-type '(unsigned-byte 8))))
|
||
(opaque-identity v)))
|
||
:stream (make-broadcast-stream)))
|
||
|
||
(with-test (:name :make-list-large-immediate)
|
||
(checked-compile
|
||
'(lambda ()
|
||
(let ((x (make-list (1- (ash most-positive-fixnum (- (+ sb-vm:word-shift 1)))))))
|
||
(declare (dynamic-extent x))
|
||
(car x))))
|
||
(checked-compile
|
||
'(lambda ()
|
||
(let ((x (make-list 100000 :initial-element 1)))
|
||
(declare (dynamic-extent x))
|
||
(reduce #'+ x)))))
|
||
|
||
(with-test (:name :dynamic-extent-constantly-function-branch)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((function (if x
|
||
(constantly x)
|
||
(constantly t))))
|
||
(declare (dynamic-extent function))
|
||
(funcall function)))
|
||
((1) 1)
|
||
((nil) t)))
|
||
|
||
(with-test (:name :dynamic-extent-function-branch)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (x)
|
||
(let ((function (if x
|
||
(lambda () x)
|
||
(lambda () t))))
|
||
(declare (dynamic-extent function))
|
||
(when x
|
||
(assert (sb-ext:stack-allocated-p function)))
|
||
(funcall function)))
|
||
((1) 1)
|
||
((nil) t)))
|
||
|
||
(with-test (:name :dynamic-extent-lp2043242.reduced)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a b)
|
||
(let ((v (block block258
|
||
(let ((z (lambda ()
|
||
(return-from block258))))
|
||
(declare (dynamic-extent z))
|
||
(print-nothing z)
|
||
(cons a b)))))
|
||
(declare (dynamic-extent v))
|
||
(copy-tree v)))
|
||
((1 2) '(1 . 2) :test #'equal)))
|
||
|
||
(with-test (:name :dynamic-extent-lp2043242.reduced.2)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a b)
|
||
(let ((v (if a
|
||
(cons a b)
|
||
(let ((z (cons b b)))
|
||
(declare (dynamic-extent z))
|
||
(print-nothing z)
|
||
(cons a b)))))
|
||
(declare (dynamic-extent v))
|
||
(copy-tree v)))
|
||
((1 2) '(1 . 2) :test #'equal)))
|
||
|
||
(with-test (:name :dynamic-extent-lp2043242)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (a b)
|
||
(let* ((v (ignore-errors (cons a b))))
|
||
(declare (dynamic-extent v))
|
||
(assert (sb-ext:stack-allocated-p v))
|
||
(case (if t 450336 v)
|
||
(t (copy-tree v)))))
|
||
((1 2) '(1 . 2) :test #'equal)))
|
||
|
||
(defun auto-dx-flet-several-ref (off array)
|
||
(let ((acc 0))
|
||
(flet ((positivep (num) (plusp (+ num off))))
|
||
(dotimes (i 10)
|
||
(incf acc (position-if #'positivep array)))
|
||
(if (plusp off)
|
||
(incf acc (if (positivep acc) 10 3))
|
||
(incf acc (position-if #'positivep array))))
|
||
acc))
|
||
|
||
(with-test (:name :auto-dx-flet-several-ref.correct)
|
||
(assert (= (auto-dx-flet-several-ref 1 #(-1 2 3)) 20))
|
||
(assert (= (auto-dx-flet-several-ref 0 #(-1 2 3)) 11)))
|
||
|
||
(with-test (:name :auto-dx-flet-several-ref.stack-allocates)
|
||
(assert-no-consing (auto-dx-flet-several-ref 1 #(-1 2 3)))
|
||
(assert-no-consing (auto-dx-flet-several-ref 0 #(-1 2 3))))
|
||
|
||
(defun auto-dx-xep-and-local-call (set)
|
||
(let ((elt nil))
|
||
(labels ((add (e)
|
||
(setf elt e))
|
||
(visit (item)
|
||
(add item)))
|
||
(map nil #'add set)
|
||
(map nil #'visit set)
|
||
elt)))
|
||
|
||
(with-test (:name :auto-dx-xep-and-local-call.correct)
|
||
(assert (equal (auto-dx-xep-and-local-call '(1)) 1)))
|
||
|
||
(with-test (:name :auto-dx-xep-and-local-call.stack-allocates)
|
||
(assert-no-consing (auto-dx-xep-and-local-call '(1))))
|
||
|
||
(defun auto-dx-xep-and-local-call-escape (set)
|
||
(let ((seq nil))
|
||
(labels ((add (e)
|
||
(push e seq))
|
||
(visit (item)
|
||
(add item)))
|
||
(declare (dynamic-extent #'visit))
|
||
(map nil #'add set)
|
||
(map nil #'visit set)
|
||
(values seq
|
||
#'add))))
|
||
|
||
(with-test (:name :auto-dx-xep-and-local-call-escape.correct)
|
||
(multiple-value-bind (value fun)
|
||
(auto-dx-xep-and-local-call-escape '(1))
|
||
(assert (equal value '(1 1)))
|
||
(assert (not (sb-ext:stack-allocated-p fun)))))
|
||
|
||
(defun auto-dx-xep-and-local-call.shared-flet-cleanup (set)
|
||
(let ((elt nil))
|
||
(flet ((add (e)
|
||
(setf elt e))
|
||
(visit (n)
|
||
n
|
||
elt))
|
||
(map nil #'add set)
|
||
(map nil #'visit set))
|
||
elt))
|
||
|
||
(with-test (:name :auto-dx-xep-and-local-call.shared-flet-cleanup.correct)
|
||
(assert (equal (auto-dx-xep-and-local-call.shared-flet-cleanup '(1)) 1)))
|
||
|
||
(with-test (:name :auto-dx-xep-and-local-call.shared-flet-cleanup.stack-allocates)
|
||
(assert-no-consing (auto-dx-xep-and-local-call.shared-flet-cleanup '(1))))
|
||
|
||
(defun auto-dx-monster (x seq)
|
||
(map nil
|
||
(if x
|
||
(flet ((f (e)
|
||
(setq x e)))
|
||
(map nil (if x
|
||
#'f
|
||
(lambda (e)
|
||
(setq x e)))
|
||
seq)
|
||
#'f)
|
||
(flet ((g (e)
|
||
(setq x e)))
|
||
(map nil #'g seq)
|
||
#'g))
|
||
seq)
|
||
x)
|
||
|
||
(with-test (:name :auto-dx-monster)
|
||
(assert (equal (auto-dx-monster 3 #(1 2 3)) 3))
|
||
(assert-no-consing (auto-dx-monster 3 #(1 2 3))))
|
||
|
||
(defun auto-dx-recursive-ref (seq)
|
||
(labels ((f (x)
|
||
(when (member x seq)
|
||
(return-from f))
|
||
(map nil #'f (cdr seq))))
|
||
(f seq)))
|
||
|
||
(with-test (:name :auto-dx-recursive-ref.correct)
|
||
(assert (equal (auto-dx-recursive-ref '(1 2 3 4)) nil)))
|
||
|
||
(with-test (:name :auto-dx-recursive-ref.stack-allocates
|
||
:fails-on :sbcl)
|
||
(assert-no-consing (auto-dx-recursive-ref '(1 2 3 4))))
|
||
|
||
(defun auto-dx-recursive-ref-2 (seq)
|
||
(labels ((f (x)
|
||
(when (member x seq)
|
||
(return-from f))
|
||
(map nil #'f (cdr seq))))
|
||
(map nil #'f seq)
|
||
(f seq)))
|
||
|
||
(with-test (:name :auto-dx-recursive-ref-2.correct)
|
||
(assert (equal (auto-dx-recursive-ref-2 '(1 2 3 4)) nil)))
|
||
|
||
(with-test (:name :auto-dx-recursive-ref-2.stack-allocates
|
||
:fails-on :sbcl)
|
||
(assert-no-consing (auto-dx-recursive-ref-2 '(1 2 3 4))))
|
||
|
||
;;; Even though #'f's direct references do not escape, it is closed
|
||
;;; over by #'g which does escape, counting as an implicit escaping
|
||
;;; reference, so #'f must not be stack allocated.
|
||
(defun auto-dx-recursive-closes-over (seq)
|
||
(let ((z (car seq)))
|
||
(labels ((f (x)
|
||
(when (member x seq)
|
||
(return-from f z))
|
||
(map nil #'f (cdr seq)))
|
||
(g ()
|
||
(map nil #'f (cdr seq))))
|
||
(values (f seq) #'g))))
|
||
|
||
(with-test (:name :auto-dx-recursive-closes-over)
|
||
(multiple-value-bind (val g)
|
||
(auto-dx-recursive-closes-over '(1 2 3 4))
|
||
(assert (eq val nil))
|
||
(assert (eq (funcall g) nil))))
|
||
|
||
(defun auto-dx-anonymous-closure-single-ref (x)
|
||
(let ((y (lambda (a) (incf x a))))
|
||
(map nil y '(1 2 3))
|
||
x))
|
||
|
||
(with-test (:name :auto-dx-anonymous-closure-single-ref.correct)
|
||
(assert (= (auto-dx-anonymous-closure-single-ref 5) 11)))
|
||
|
||
(with-test (:name :auto-dx-anonymous-closure-single-ref.stack-allocates)
|
||
(assert-no-consing (auto-dx-anonymous-closure-single-ref 5)))
|
||
|
||
(defun auto-dx-anonymous-closure-multiple-ref (x)
|
||
(let ((y (lambda (a) (incf x a))))
|
||
(map nil y '(1 2 3))
|
||
(map nil y '(2 3 4))
|
||
x))
|
||
|
||
(with-test (:name :auto-dx-anonymous-closure-multiple-ref.correct)
|
||
(assert (= (auto-dx-anonymous-closure-multiple-ref 5) 20)))
|
||
|
||
(with-test (:name :auto-dx-anonymous-closure-multiple-ref.stack-allocates)
|
||
(assert-no-consing (auto-dx-anonymous-closure-multiple-ref 5)))
|
||
|
||
(with-test (:name :push+dx+mv-call
|
||
:broken-on :sbcl)
|
||
(checked-compile-and-assert
|
||
()
|
||
'(lambda (f)
|
||
(let (j)
|
||
(declare (dynamic-extent j))
|
||
(print (multiple-value-call #'list (funcall f)
|
||
(progn (push 1 j)
|
||
(funcall f))))
|
||
(copy-list j)))
|
||
(((constantly t)) '(1) :test #'equal)))
|
||
|
||
(with-test (:name :dynamic-extent-lp2113935)
|
||
(checked-compile-and-assert
|
||
(:optimize '(:debug 2))
|
||
'(lambda (a)
|
||
(let ((x (cons nil a)))
|
||
(declare (dynamic-extent x))
|
||
(print-nothing x)
|
||
(let* ((g
|
||
(cons
|
||
(let ((v3
|
||
(let ((q 0))
|
||
(declare (special q))
|
||
2)))
|
||
(declare (dynamic-extent v3))
|
||
v3)
|
||
(progn
|
||
(let ((g (lambda () x)))
|
||
(declare (dynamic-extent g))
|
||
(print-nothing g))
|
||
0))))
|
||
(declare (dynamic-extent g))
|
||
(print-nothing g)
|
||
0)))
|
||
((2) 0)))
|
||
|
||
(with-test (:name :lambda-ref-substitution)
|
||
(assert (funcall (checked-compile `(lambda (x)
|
||
(let ((a (lambda () x)))
|
||
(let ((x (list a)))
|
||
(declare (dynamic-extent x))
|
||
(not (sb-ext:stack-allocated-p (car x)))))))
|
||
1))
|
||
(assert (funcall (checked-compile `(lambda (x)
|
||
(let ((a (lambda () x)))
|
||
(let ((x a))
|
||
(declare (dynamic-extent x))
|
||
(sb-ext:stack-allocated-p x)))))
|
||
1))
|
||
(assert (funcall (checked-compile `(lambda (x)
|
||
(flet ((f (&rest args)
|
||
(declare (dynamic-extent args))
|
||
(not (sb-ext:stack-allocated-p (car args)))))
|
||
(declare (notinline f))
|
||
(f (lambda () x)))))
|
||
1)))
|
||
|
||
(with-test (:name :reverse-remove-dx)
|
||
(declare (optimize (debug 1)))
|
||
(checked-compile-and-assert
|
||
()
|
||
`(lambda (a b)
|
||
(sb-int:dx-let ((a (list a b)))
|
||
(stack-allocated-p (reverse a))))
|
||
((1 2) nil)))
|