Make sure quantifiers don't cons
authorPaul Khuong <pvk@pvk.ca>
Sat, 26 Oct 2013 17:18:23 +0000 (13:18 -0400)
committerPaul Khuong <pvk@pvk.ca>
Sat, 26 Oct 2013 17:47:53 +0000 (13:47 -0400)
Quantifiers like SOME and EVERY are implemented in terms of (MAP NIL)
of a wrapper function with early RETURN. If type information does not
enable MAP to be open coded, declarations are necessary to avoid
consing up a closure and a value cell for the return.

DX functions really shouldn't cause value cells for return blocks.

Also, revert d0f65b07a30adc989e36a82ddc0ed54d135d638e which is
now mostly redundant.

NEWS
src/code/seq.lisp
src/pcl/std-class.lisp
tests/compiler.pure.lisp

diff --git a/NEWS b/NEWS
index 978021c..2951b1b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,8 @@ changes relative to sbcl-1.1.12:
     COMPUTE-RESTARTS, making it faster and cons less (lp#769615)
   * enhancement: FIND-RESTART and COMPUTE-RESTARTS handle huge restart
     clusters better in some cases
+  * enhancement: SOME/ANY/other quantification higher-order functions no
+    longer cons. (lp#1070635)
   * bug fix: forward references to classes in fasls can now be loaded.
     (lp#746132)
   * bug fix: don't warn on a interpreted->compiled function redefinition
index 54c7c02..e6abb66 100644 (file)
@@ -1207,17 +1207,20 @@ many elements are copied."
                 ;; from the old seq.lisp into target-seq.lisp.
                 (define-compiler-macro ,name (pred first-seq &rest more-seqs)
                   (let ((elements (make-gensym-list (1+ (length more-seqs))))
-                        (blockname (sb!xc:gensym "BLOCK")))
+                        (blockname (sb!xc:gensym "BLOCK"))
+                        (wrapper (sb!xc:gensym "WRAPPER")))
                     (once-only ((pred pred))
                       `(block ,blockname
-                         (map nil
-                              (lambda (,@elements)
-                                (let ((pred-value (funcall ,pred ,@elements)))
-                                  (,',found-test pred-value
-                                    (return-from ,blockname
-                                      ,',found-result))))
-                              ,first-seq
-                              ,@more-seqs)
+                         (flet ((,wrapper (,@elements)
+                                  (declare (optimize (sb!c::check-tag-existence 0)))
+                                  (let ((pred-value (funcall ,pred ,@elements)))
+                                    (,',found-test pred-value
+                                                   (return-from ,blockname
+                                                     ,',found-result)))))
+                           (declare (inline ,wrapper)
+                                    (dynamic-extent #',wrapper))
+                           (map nil #',wrapper ,first-seq
+                                ,@more-seqs))
                          ,',unfound-result)))))))
   (defquantifier some when pred-value :unfound-result nil :doc
   "Apply PREDICATE to the 0-indexed elements of the sequences, then
index 37e3c75..975acc4 100644 (file)
   (or (when (forward-referenced-class-p class)
         class)
       (some #'class-has-a-forward-referenced-superclass-p
-            ;; KLUDGE: SOME conses without knowing the type
-            (the list (class-direct-superclasses class)))))
+            (class-direct-superclasses class))))
 
 ;;; This is called by :after shared-initialize whenever a class is initialized
 ;;; or reinitialized. The class may or may not be finalized.
index 92079e7..21adaf5 100644 (file)
     (declare (ignore warnings-p))
     (assert (functionp fun))
     (assert failure-p)))
+
+;; quantifiers shouldn't cons themselves.
+(with-test (:name :quantifiers-no-consing)
+  (let ((constantly-t (lambda (x) x t))
+        (constantly-nil (lambda (x) x nil))
+        (list (make-list 1000 :initial-element nil))
+        (vector (make-array 1000 :initial-element nil)))
+    (macrolet ((test (quantifier)
+                 (let ((function (make-symbol (format nil "TEST-~A" quantifier))))
+                   `(flet ((,function (function sequence)
+                             (,quantifier function sequence)))
+                      (ctu:assert-no-consing (,function constantly-t list))
+                      (ctu:assert-no-consing (,function constantly-nil vector))))))
+      (test some)
+      (test every)
+      (test notany)
+      (test notevery))))