1.0.2.14: Speed up constraint propagation
[sbcl.git] / src / compiler / sset.lisp
1 ;;;; This file implements a sparse set abstraction, represented as a
2 ;;;; custom lightweight hash-table. We don't use bit-vectors to
3 ;;;; represent sets in flow analysis, since the universe may be quite
4 ;;;; large but the average number of elements is small. We also don't
5 ;;;; use sorted lists like in the original CMUCL code, since it had
6 ;;;; bad worst-case performance (on some real-life programs the
7 ;;;; hash-based sset gives a 20% compilation speedup). A custom
8 ;;;; hash-table is used since the standard one is too heavy (locking,
9 ;;;; memory use) for this use.
10
11 ;;;; This software is part of the SBCL system. See the README file for
12 ;;;; more information.
13 ;;;;
14 ;;;; This software is derived from the CMU CL system, which was
15 ;;;; written at Carnegie Mellon University and released into the
16 ;;;; public domain. The software is in the public domain and is
17 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
18 ;;;; files for more information. (This file no)
19
20 (in-package "SB!C")
21
22 ;;; Each structure that may be placed in a SSET must include the
23 ;;; SSET-ELEMENT structure. We allow an initial value of NIL to mean
24 ;;; that no ordering has been assigned yet (although an ordering must
25 ;;; be assigned before doing set operations.)
26 (def!struct (sset-element (:constructor nil)
27                          (:copier nil))
28   (number nil :type (or index null)))
29
30 (defstruct (sset (:copier nil))
31   ;; Vector containing the set values. 0 is used for empty (since
32   ;; initializing a vector with 0 is cheaper than with NIL), +DELETED+
33   ;; is used to mark buckets that used to contain an element, but no
34   ;; longer do.
35   (vector #() :type simple-vector)
36   ;; How many buckets currently contain or used to contain an element.
37   (free 0 :type index)
38   ;; How many elements are currently members of the set.
39   (count 0 :type index))
40 (defprinter (sset) vector)
41
42 ;;; Iterate over the elements in SSET, binding VAR to each element in
43 ;;; turn.
44 (defmacro do-sset-elements ((var sset &optional result) &body body)
45   `(loop for ,var across (sset-vector ,sset)
46          do (unless (member ,var '(0 +deleted+))
47               ,@body)
48          finally (return ,result)))
49
50 ;;; Primary hash.
51 (declaim (inline sset-hash1))
52 (defun sset-hash1 (element)
53   #+sb-xc-host
54   (let ((result (sset-element-number element)))
55     ;; This is performance critical, and it's not certain that the host
56     ;; compiler does modular arithmetic optimization. Instad use
57     ;; something that most CL implementations will do efficiently.
58     (the fixnum (logxor (the fixnum result)
59                         (the fixnum (ash result -9))
60                         (the fixnum (ash result -5)))))
61   #-sb-xc-host
62   (let ((result (sset-element-number element)))
63     (declare (type sb!vm:word result))
64     ;; We only use the low-order bits.
65     (macrolet ((set-result (form)
66                  `(setf result (ldb (byte #.sb!vm:n-word-bits 0) ,form))))
67       (set-result (+ result (ash result -19)))
68       (set-result (logxor result (ash result -13)))
69       (set-result (+ result (ash result -9)))
70       (set-result (logxor result (ash result -5)))
71       (set-result (+ result (ash result -2)))
72       (logand sb!xc:most-positive-fixnum result))))
73
74 ;;; Secondary hash (for double hash probing). Needs to return an odd
75 ;;; number.
76 (declaim (inline sset-hash2))
77 (defun sset-hash2 (element)
78   (let ((number (sset-element-number element)))
79     (declare (fixnum number))
80     (logior 1 number)))
81
82 ;;; Double the size of the hash vector of SET.
83 (defun sset-grow (set)
84   (let* ((vector (sset-vector set))
85          (new-vector (make-array (if (zerop (length vector))
86                                      2
87                                      (* (length vector) 2)))))
88     (setf (sset-vector set) new-vector
89           (sset-free set) (length new-vector)
90           (sset-count set) 0)
91     (loop for element across vector
92           do (unless (member element '(0 +deleted+))
93                (sset-adjoin element set)))))
94
95 ;;; Rehash the sset when the proportion of free cells in the set is
96 ;;; lower than this.
97 (defconstant +sset-rehash-threshold+ 1/4)
98
99 ;;; Destructively add ELEMENT to SET. If ELEMENT was not in the set,
100 ;;; then we return true, otherwise we return false.
101 (declaim (ftype (sfunction (sset-element sset) boolean) sset-adjoin))
102 (defun sset-adjoin (element set)
103   (declare (optimize (speed 2)))
104   (when (<= (sset-free set)
105             (max 1 (truncate (length (sset-vector set))
106                              #.(round (/ +sset-rehash-threshold+)))))
107     (sset-grow set))
108   (loop with vector = (sset-vector set)
109         with mask of-type fixnum = (1- (length vector))
110         with secondary-hash = (sset-hash2 element)
111         for hash of-type index = (logand mask (sset-hash1 element)) then
112           (logand mask (+ hash secondary-hash))
113         for current = (aref vector hash)
114         for deleted-index = nil
115         do (cond ((eql current 0)
116                   (incf (sset-count set))
117                   (cond (deleted-index
118                          (setf (aref vector deleted-index) element))
119                         (t
120                          (decf (sset-free set))
121                          (setf (aref vector hash) element)))
122                   (return t))
123                  ((and (eql current '+deleted+)
124                        (not deleted-index))
125                   (setf deleted-index hash))
126                  ((eq current element)
127                   (return nil)))))
128
129 ;;; Destructively remove ELEMENT from SET. If element was in the set,
130 ;;; then return true, otherwise return false.
131 (declaim (ftype (sfunction (sset-element sset) boolean) sset-delete))
132 (defun sset-delete (element set)
133   (when (zerop (length (sset-vector set)))
134     (return-from sset-delete nil))
135   (loop with vector = (sset-vector set)
136         with mask fixnum = (1- (length vector))
137         with secondary-hash = (sset-hash2 element)
138         for hash of-type index = (logand mask (sset-hash1 element)) then
139           (logand mask (+ hash secondary-hash))
140         for current = (aref vector hash)
141         do (cond ((eql current 0)
142                   (return nil))
143                  ((eq current element)
144                   (decf (sset-count set))
145                   (setf (aref vector hash) '+deleted+)
146                   (return t)))))
147
148 ;;; Return true if ELEMENT is in SET, false otherwise.
149 (declaim (ftype (sfunction (sset-element sset) boolean) sset-member))
150 (defun sset-member (element set)
151   (when (zerop (length (sset-vector set)))
152     (return-from sset-member nil))
153   (loop with vector = (sset-vector set)
154         with mask fixnum = (1- (length vector))
155         with secondary-hash = (sset-hash2 element)
156         for hash of-type index = (logand mask (sset-hash1 element)) then
157           (logand mask (+ hash secondary-hash))
158         for current = (aref vector hash)
159         do (cond ((eql current 0)
160                   (return nil))
161                  ((eq current element)
162                   (return t)))))
163
164 (declaim (ftype (sfunction (sset sset) boolean) sset=))
165 (defun sset= (set1 set2)
166   (unless (eql (sset-count set1)
167                (sset-count set2))
168     (return-from sset= nil))
169   (do-sset-elements (element set1)
170     (unless (sset-member element set2)
171       (return-from sset= nil)))
172   t)
173
174 ;;; Return true if SET contains no elements, false otherwise.
175 (declaim (ftype (sfunction (sset) boolean) sset-empty))
176 (defun sset-empty (set)
177   (zerop (sset-count set)))
178
179 ;;; Return a new copy of SET.
180 (declaim (ftype (sfunction (sset) sset) copy-sset))
181 (defun copy-sset (set)
182   (make-sset :vector (let* ((vector (sset-vector set))
183                             (new-vector (make-array (length vector))))
184                        (declare (type simple-vector vector new-vector)
185                                 (optimize speed (safety 0)))
186                        ;; There's no REPLACE deftransform for simple-vectors.
187                        (dotimes (i (length vector))
188                          (setf (aref new-vector i)
189                                (aref vector i)))
190                        new-vector)
191              :count (sset-count set)
192              :free (sset-free set)))
193
194 ;;; Perform the appropriate set operation on SET1 and SET2 by
195 ;;; destructively modifying SET1. We return true if SET1 was modified,
196 ;;; false otherwise.
197 (declaim (ftype (sfunction (sset sset) boolean) sset-union sset-intersection
198                 sset-difference))
199 (defun sset-union (set1 set2)
200   (loop with modified = nil
201         for element across (sset-vector set2)
202         do (unless (member element '(0 +deleted+))
203              (when (sset-adjoin element set1)
204                (setf modified t)))
205         finally (return modified)))
206 (defun sset-intersection (set1 set2)
207   (loop with modified = nil
208         for element across (sset-vector set1)
209         for index of-type index from 0
210         do (unless (member element '(0 +deleted+))
211              (unless (sset-member element set2)
212                (decf (sset-count set1))
213                (setf (aref (sset-vector set1) index) '+deleted+
214                      modified t)))
215         finally (return modified)))
216 (defun sset-difference (set1 set2)
217   (loop with modified = nil
218         for element across (sset-vector set1)
219         for index of-type index from 0
220         do (unless (member element '(0 +deleted+))
221              (when (sset-member element set2)
222                (decf (sset-count set1))
223                (setf (aref (sset-vector set1) index) '+deleted+
224                      modified t)))
225         finally (return modified)))
226
227 ;;; Destructively modify SET1 to include its union with the difference
228 ;;; of SET2 and SET3. We return true if SET1 was modified, false
229 ;;; otherwise.
230 (declaim (ftype (sfunction (sset sset sset) boolean) sset-union-of-difference))
231 (defun sset-union-of-difference (set1 set2 set3)
232   (loop with modified = nil
233         for element across (sset-vector set2)
234         do (unless (member element '(0 +deleted+))
235              (unless (sset-member element set3)
236                (when (sset-adjoin element set1)
237                  (setf modified t))))
238         finally (return modified)))