Initial revision
[sbcl.git] / src / code / bit-bash.lisp
1 ;;;; functions to implement bitblt-ish operations
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
11
12 (in-package "SB!VM")
13
14 (file-comment
15   "$Header$")
16 \f
17 ;;;; constants and types
18
19 (eval-when (:compile-toplevel :load-toplevel :execute)
20
21 (defconstant unit-bits sb!vm:word-bits
22   #!+sb-doc
23   "The number of bits to process at a time.")
24
25 (defconstant max-bits (ash most-positive-fixnum -2)
26   #!+sb-doc
27   "The maximum number of bits that can be delt with during a single call.")
28
29 (deftype unit ()
30   `(unsigned-byte ,unit-bits))
31
32 (deftype offset ()
33   `(integer 0 ,max-bits))
34
35 (deftype bit-offset ()
36   `(integer 0 (,unit-bits)))
37
38 (deftype bit-count ()
39   `(integer 1 (,unit-bits)))
40
41 (deftype word-offset ()
42   `(integer 0 (,(ceiling max-bits unit-bits))))
43
44 ) ; EVAL-WHEN
45 \f
46 ;;;; support routines
47
48 ;;; A particular implementation must offer either VOPs to translate
49 ;;; these, or DEFTRANSFORMs to convert them into something supported
50 ;;; by the architecture.
51 (macrolet ((def-frob (name &rest args)
52              `(defun ,name ,args
53                 (,name ,@args))))
54   (def-frob 32bit-logical-not x)
55   (def-frob 32bit-logical-and x y)
56   (def-frob 32bit-logical-or x y)
57   (def-frob 32bit-logical-xor x y)
58   (def-frob 32bit-logical-nor x y)
59   (def-frob 32bit-logical-eqv x y)
60   (def-frob 32bit-logical-nand x y)
61   (def-frob 32bit-logical-andc1 x y)
62   (def-frob 32bit-logical-andc2 x y)
63   (def-frob 32bit-logical-orc1 x y)
64   (def-frob 32bit-logical-orc2 x y))
65
66 (defun shift-towards-start (number countoid)
67   #!+sb-doc
68   "Shift NUMBER by the low-order bits of COUNTOID, adding zero bits at
69   the ``end'' and removing bits from the ``start.''  On big-endian
70   machines this is a left-shift and on little-endian machines this is a
71   right-shift."
72   (declare (type unit number) (fixnum countoid))
73   (let ((count (ldb (byte (1- (integer-length unit-bits)) 0) countoid)))
74     (declare (type bit-offset count))
75     (if (zerop count)
76         number
77         (ecase sb!c:*backend-byte-order*
78           (:big-endian
79            (ash (ldb (byte (- unit-bits count) 0) number) count))
80           (:little-endian
81            (ash number (- count)))))))
82
83 (defun shift-towards-end (number count)
84   #!+sb-doc
85   "Shift NUMBER by COUNT bits, adding zero bits at the ``start'' and removing
86   bits from the ``end.''  On big-endian machines this is a right-shift and
87   on little-endian machines this is a left-shift."
88   (declare (type unit number) (fixnum count))
89   (let ((count (ldb (byte (1- (integer-length unit-bits)) 0) count)))
90     (declare (type bit-offset count))
91     (if (zerop count)
92         number
93         (ecase sb!c:*backend-byte-order*
94           (:big-endian
95            (ash number (- count)))
96           (:little-endian
97            (ash (ldb (byte (- unit-bits count) 0) number) count))))))
98
99 #!-sb-fluid (declaim (inline start-mask end-mask fix-sap-and-offset))
100 (defun start-mask (count)
101   #!+sb-doc
102   "Produce a mask that contains 1's for the COUNT ``start'' bits and 0's for
103   the remaining ``end'' bits. Only the lower 5 bits of COUNT are significant."
104   (declare (fixnum count))
105   (shift-towards-start (1- (ash 1 unit-bits)) (- count)))
106
107 (defun end-mask (count)
108   #!+sb-doc
109   "Produce a mask that contains 1's for the COUNT ``end'' bits and 0's for
110   the remaining ``start'' bits. Only the lower 5 bits of COUNT are
111   significant."
112   (declare (fixnum count))
113   (shift-towards-end (1- (ash 1 unit-bits)) (- count)))
114
115 (defun fix-sap-and-offset (sap offset)
116   #!+sb-doc
117   "Align the SAP to a word boundary, and update the offset accordingly."
118   (declare (type system-area-pointer sap)
119            (type index offset)
120            (values system-area-pointer index))
121   (let ((address (sap-int sap)))
122     (values (int-sap #!-alpha (32bit-logical-andc2 address 3)
123                      #!+alpha (ash (ash address -2) 2))
124             (+ (* (logand address 3) byte-bits) offset))))
125
126 #!-sb-fluid (declaim (inline word-sap-ref %set-word-sap-ref))
127 (defun word-sap-ref (sap offset)
128   (declare (type system-area-pointer sap)
129            (type index offset)
130            (values (unsigned-byte 32))
131            (optimize (speed 3) (safety 0) #-sb-xc-host (inhibit-warnings 3)))
132   (sap-ref-32 sap (the index (ash offset 2))))
133 (defun %set-word-sap-ref (sap offset value)
134   (declare (type system-area-pointer sap)
135            (type index offset)
136            (type (unsigned-byte 32) value)
137            (values (unsigned-byte 32))
138            (optimize (speed 3) (safety 0) (inhibit-warnings 3)))
139   (setf (sap-ref-32 sap (the index (ash offset 2))) value))
140 \f
141 ;;;; DO-CONSTANT-BIT-BASH
142
143 #!-sb-fluid (declaim (inline do-constant-bit-bash))
144 (defun do-constant-bit-bash (dst dst-offset length value dst-ref-fn dst-set-fn)
145   #!+sb-doc
146   "Fill DST with VALUE starting at DST-OFFSET and continuing for LENGTH bits."
147   (declare (type offset dst-offset) (type unit value)
148            (type function dst-ref-fn dst-set-fn))
149   (multiple-value-bind (dst-word-offset dst-bit-offset)
150       (floor dst-offset unit-bits)
151     (declare (type word-offset dst-word-offset)
152              (type bit-offset dst-bit-offset))
153     (multiple-value-bind (words final-bits)
154         (floor (+ dst-bit-offset length) unit-bits)
155       (declare (type word-offset words) (type bit-offset final-bits))
156       (if (zerop words)
157           (unless (zerop length)
158             (funcall dst-set-fn dst dst-word-offset
159                      (if (= length unit-bits)
160                          value
161                          (let ((mask (shift-towards-end (start-mask length)
162                                                         dst-bit-offset)))
163                            (declare (type unit mask))
164                            (32bit-logical-or
165                             (32bit-logical-and value mask)
166                             (32bit-logical-andc2
167                              (funcall dst-ref-fn dst dst-word-offset)
168                              mask))))))
169           (let ((interior (floor (- length final-bits) unit-bits)))
170             (unless (zerop dst-bit-offset)
171               (let ((mask (end-mask (- dst-bit-offset))))
172                 (declare (type unit mask))
173                 (funcall dst-set-fn dst dst-word-offset
174                          (32bit-logical-or
175                           (32bit-logical-and value mask)
176                           (32bit-logical-andc2
177                            (funcall dst-ref-fn dst dst-word-offset)
178                            mask))))
179               (incf dst-word-offset))
180             (dotimes (i interior)
181               (funcall dst-set-fn dst dst-word-offset value)
182               (incf dst-word-offset))
183             (unless (zerop final-bits)
184               (let ((mask (start-mask final-bits)))
185                 (declare (type unit mask))
186                 (funcall dst-set-fn dst dst-word-offset
187                          (32bit-logical-or
188                           (32bit-logical-and value mask)
189                           (32bit-logical-andc2
190                            (funcall dst-ref-fn dst dst-word-offset)
191                            mask)))))))))
192   (values))
193 \f
194 ;;;; DO-UNARY-BIT-BASH
195
196 #!-sb-fluid (declaim (inline do-unary-bit-bash))
197 (defun do-unary-bit-bash (src src-offset dst dst-offset length
198                               dst-ref-fn dst-set-fn src-ref-fn)
199   (declare (type offset src-offset dst-offset length)
200            (type function dst-ref-fn dst-set-fn src-ref-fn))
201   (multiple-value-bind (dst-word-offset dst-bit-offset)
202       (floor dst-offset unit-bits)
203     (declare (type word-offset dst-word-offset)
204              (type bit-offset dst-bit-offset))
205     (multiple-value-bind (src-word-offset src-bit-offset)
206         (floor src-offset unit-bits)
207       (declare (type word-offset src-word-offset)
208                (type bit-offset src-bit-offset))
209       (cond
210        ((<= (+ dst-bit-offset length) unit-bits)
211         ;; We are only writing one word, so it doesn't matter what order
212         ;; we do it in. But we might be reading from multiple words, so take
213         ;; care.
214         (cond
215          ((zerop length)
216           ;; Actually, we aren't even writing one word. This is real easy.
217           )
218          ((= length unit-bits)
219           ;; DST-BIT-OFFSET must be equal to zero, or we would be writing
220           ;; multiple words. If SRC-BIT-OFFSET is also zero, then we
221           ;; just transfer the single word. Otherwise we have to extract bits
222           ;; from two src words.
223           (funcall dst-set-fn dst dst-word-offset
224                    (if (zerop src-bit-offset)
225                        (funcall src-ref-fn src src-word-offset)
226                        (32bit-logical-or
227                         (shift-towards-start
228                          (funcall src-ref-fn src src-word-offset)
229                          src-bit-offset)
230                         (shift-towards-end
231                          (funcall src-ref-fn src (1+ src-word-offset))
232                          (- src-bit-offset))))))
233          (t
234           ;; We are only writing some portion of the dst word, so we need to
235           ;; preserve the extra bits. Also, we still don't know whether we need
236           ;; one or two source words.
237           (let ((mask (shift-towards-end (start-mask length) dst-bit-offset))
238                 (orig (funcall dst-ref-fn dst dst-word-offset))
239                 (value
240                  (if (> src-bit-offset dst-bit-offset)
241                      ;; The source starts further into the word than does
242                      ;; the dst, so the source could extend into the next
243                      ;; word. If it does, we have to merge the two words,
244                      ;; and if not, we can just shift the first word.
245                      (let ((src-bit-shift (- src-bit-offset dst-bit-offset)))
246                        (if (> (+ src-bit-offset length) unit-bits)
247                            (32bit-logical-or
248                             (shift-towards-start
249                              (funcall src-ref-fn src src-word-offset)
250                              src-bit-shift)
251                             (shift-towards-end
252                              (funcall src-ref-fn src (1+ src-word-offset))
253                              (- src-bit-shift)))
254                            (shift-towards-start
255                             (funcall src-ref-fn src src-word-offset)
256                             src-bit-shift)))
257                      ;; The dst starts further into the word than does the
258                      ;; source, so we know the source can not extend into
259                      ;; a second word (or else the dst would too, and we
260                      ;; wouldn't be in this branch.
261                      (shift-towards-end
262                       (funcall src-ref-fn src src-word-offset)
263                       (- dst-bit-offset src-bit-offset)))))
264             (declare (type unit mask orig value))
265             ;; Replace the dst word.
266             (funcall dst-set-fn dst dst-word-offset
267                      (32bit-logical-or
268                       (32bit-logical-and value mask)
269                       (32bit-logical-andc2 orig mask)))))))
270        ((= src-bit-offset dst-bit-offset)
271         ;; The source and dst are aligned, so we don't need to shift
272         ;; anything. But we have to pick the direction of the loop
273         ;; in case the source and dst are really the same thing.
274         (multiple-value-bind (words final-bits)
275             (floor (+ dst-bit-offset length) unit-bits)
276           (declare (type word-offset words) (type bit-offset final-bits))
277           (let ((interior (floor (- length final-bits) unit-bits)))
278             (declare (type word-offset interior))
279             (cond
280              ((<= dst-offset src-offset)
281               ;; We need to loop from left to right
282               (unless (zerop dst-bit-offset)
283                 ;; We are only writing part of the first word, so mask off the
284                 ;; bits we want to preserve.
285                 (let ((mask (end-mask (- dst-bit-offset)))
286                       (orig (funcall dst-ref-fn dst dst-word-offset))
287                       (value (funcall src-ref-fn src src-word-offset)))
288                   (declare (type unit mask orig value))
289                   (funcall dst-set-fn dst dst-word-offset
290                            (32bit-logical-or (32bit-logical-and value mask)
291                                              (32bit-logical-andc2 orig mask))))
292                 (incf src-word-offset)
293                 (incf dst-word-offset))
294               ;; Just copy the interior words.
295               (dotimes (i interior)
296                 (funcall dst-set-fn dst dst-word-offset
297                          (funcall src-ref-fn src src-word-offset))
298                 (incf src-word-offset)
299                 (incf dst-word-offset))
300               (unless (zerop final-bits)
301                 ;; We are only writing part of the last word.
302                 (let ((mask (start-mask final-bits))
303                       (orig (funcall dst-ref-fn dst dst-word-offset))
304                       (value (funcall src-ref-fn src src-word-offset)))
305                   (declare (type unit mask orig value))
306                   (funcall dst-set-fn dst dst-word-offset
307                            (32bit-logical-or
308                             (32bit-logical-and value mask)
309                             (32bit-logical-andc2 orig mask))))))
310              (t
311               ;; We need to loop from right to left.
312               (incf dst-word-offset words)
313               (incf src-word-offset words)
314               (unless (zerop final-bits)
315                 (let ((mask (start-mask final-bits))
316                       (orig (funcall dst-ref-fn dst dst-word-offset))
317                       (value (funcall src-ref-fn src src-word-offset)))
318                   (declare (type unit mask orig value))
319                   (funcall dst-set-fn dst dst-word-offset
320                            (32bit-logical-or
321                             (32bit-logical-and value mask)
322                             (32bit-logical-andc2 orig mask)))))
323               (dotimes (i interior)
324                 (decf src-word-offset)
325                 (decf dst-word-offset)
326                 (funcall dst-set-fn dst dst-word-offset
327                          (funcall src-ref-fn src src-word-offset)))
328               (unless (zerop dst-bit-offset)
329                 (decf src-word-offset)
330                 (decf dst-word-offset)
331                 (let ((mask (end-mask (- dst-bit-offset)))
332                       (orig (funcall dst-ref-fn dst dst-word-offset))
333                       (value (funcall src-ref-fn src src-word-offset)))
334                   (declare (type unit mask orig value))
335                   (funcall dst-set-fn dst dst-word-offset
336                            (32bit-logical-or
337                             (32bit-logical-and value mask)
338                             (32bit-logical-andc2 orig mask))))))))))
339        (t
340         ;; They aren't aligned.
341         (multiple-value-bind (words final-bits)
342             (floor (+ dst-bit-offset length) unit-bits)
343           (declare (type word-offset words) (type bit-offset final-bits))
344           (let ((src-shift (mod (- src-bit-offset dst-bit-offset) unit-bits))
345                 (interior (floor (- length final-bits) unit-bits)))
346             (declare (type bit-offset src-shift)
347                      (type word-offset interior))
348             (cond
349              ((<= dst-offset src-offset)
350               ;; We need to loop from left to right
351               (let ((prev 0)
352                     (next (funcall src-ref-fn src src-word-offset)))
353                 (declare (type unit prev next))
354                 (flet ((get-next-src ()
355                          (setf prev next)
356                          (setf next (funcall src-ref-fn src
357                                              (incf src-word-offset)))))
358                   (declare (inline get-next-src))
359                   (unless (zerop dst-bit-offset)
360                     (when (> src-bit-offset dst-bit-offset)
361                       (get-next-src))
362                     (let ((mask (end-mask (- dst-bit-offset)))
363                           (orig (funcall dst-ref-fn dst dst-word-offset))
364                           (value (32bit-logical-or
365                                   (shift-towards-start prev src-shift)
366                                   (shift-towards-end next (- src-shift)))))
367                       (declare (type unit mask orig value))
368                       (funcall dst-set-fn dst dst-word-offset
369                                (32bit-logical-or
370                                 (32bit-logical-and value mask)
371                                 (32bit-logical-andc2 orig mask)))
372                       (incf dst-word-offset)))
373                   (dotimes (i interior)
374                     (get-next-src)
375                     (let ((value (32bit-logical-or
376                                   (shift-towards-end next (- src-shift))
377                                   (shift-towards-start prev src-shift))))
378                       (declare (type unit value))
379                       (funcall dst-set-fn dst dst-word-offset value)
380                       (incf dst-word-offset)))
381                   (unless (zerop final-bits)
382                     (let ((value
383                            (if (> (+ final-bits src-shift) unit-bits)
384                                (progn
385                                  (get-next-src)
386                                  (32bit-logical-or
387                                   (shift-towards-end next (- src-shift))
388                                   (shift-towards-start prev src-shift)))
389                                (shift-towards-start next src-shift)))
390                           (mask (start-mask final-bits))
391                           (orig (funcall dst-ref-fn dst dst-word-offset)))
392                       (declare (type unit mask orig value))
393                       (funcall dst-set-fn dst dst-word-offset
394                                (32bit-logical-or
395                                 (32bit-logical-and value mask)
396                                 (32bit-logical-andc2 orig mask))))))))
397              (t
398               ;; We need to loop from right to left.
399               (incf dst-word-offset words)
400               (incf src-word-offset
401                     (1- (ceiling (+ src-bit-offset length) unit-bits)))
402               (let ((next 0)
403                     (prev (funcall src-ref-fn src src-word-offset)))
404                 (declare (type unit prev next))
405                 (flet ((get-next-src ()
406                          (setf next prev)
407                          (setf prev (funcall src-ref-fn src
408                                              (decf src-word-offset)))))
409                   (declare (inline get-next-src))
410                   (unless (zerop final-bits)
411                     (when (> final-bits (- unit-bits src-shift))
412                       (get-next-src))
413                     (let ((value (32bit-logical-or
414                                   (shift-towards-end next (- src-shift))
415                                   (shift-towards-start prev src-shift)))
416                           (mask (start-mask final-bits))
417                           (orig (funcall dst-ref-fn dst dst-word-offset)))
418                       (declare (type unit mask orig value))
419                       (funcall dst-set-fn dst dst-word-offset
420                                (32bit-logical-or
421                                 (32bit-logical-and value mask)
422                                 (32bit-logical-andc2 orig mask)))))
423                   (decf dst-word-offset)
424                   (dotimes (i interior)
425                     (get-next-src)
426                     (let ((value (32bit-logical-or
427                                   (shift-towards-end next (- src-shift))
428                                   (shift-towards-start prev src-shift))))
429                       (declare (type unit value))
430                       (funcall dst-set-fn dst dst-word-offset value)
431                       (decf dst-word-offset)))
432                   (unless (zerop dst-bit-offset)
433                     (if (> src-bit-offset dst-bit-offset)
434                         (get-next-src)
435                         (setf next prev prev 0))
436                     (let ((mask (end-mask (- dst-bit-offset)))
437                           (orig (funcall dst-ref-fn dst dst-word-offset))
438                           (value (32bit-logical-or
439                                   (shift-towards-start prev src-shift)
440                                   (shift-towards-end next (- src-shift)))))
441                       (declare (type unit mask orig value))
442                       (funcall dst-set-fn dst dst-word-offset
443                                (32bit-logical-or
444                                 (32bit-logical-and value mask)
445                                 (32bit-logical-andc2 orig mask)))))))))))))))
446   (values))
447 \f
448 ;;;; the actual bashers
449
450 (defun bit-bash-fill (value dst dst-offset length)
451   (declare (type unit value) (type offset dst-offset length))
452   (locally
453    (declare (optimize (speed 3) (safety 0)))
454    (do-constant-bit-bash dst dst-offset length value
455                          #'%raw-bits #'%set-raw-bits)))
456
457 (defun system-area-fill (value dst dst-offset length)
458   (declare (type unit value) (type offset dst-offset length))
459   (locally
460    (declare (optimize (speed 3) (safety 0)))
461    (multiple-value-bind (dst dst-offset) (fix-sap-and-offset dst dst-offset)
462      (do-constant-bit-bash dst dst-offset length value
463                            #'word-sap-ref #'%set-word-sap-ref))))
464
465 (defun bit-bash-copy (src src-offset dst dst-offset length)
466   (declare (type offset src-offset dst-offset length))
467   (locally
468    (declare (optimize (speed 3) (safety 0))
469             (inline do-unary-bit-bash))
470    (do-unary-bit-bash src src-offset dst dst-offset length
471                       #'%raw-bits #'%set-raw-bits #'%raw-bits)))
472
473 (defun system-area-copy (src src-offset dst dst-offset length)
474   (declare (type offset src-offset dst-offset length))
475   (locally
476    (declare (optimize (speed 3) (safety 0)))
477    (multiple-value-bind (src src-offset) (fix-sap-and-offset src src-offset)
478      (declare (type system-area-pointer src))
479      (multiple-value-bind (dst dst-offset) (fix-sap-and-offset dst dst-offset)
480        (declare (type system-area-pointer dst))
481        (do-unary-bit-bash src src-offset dst dst-offset length
482                           #'word-sap-ref #'%set-word-sap-ref
483                           #'word-sap-ref)))))
484
485 (defun copy-to-system-area (src src-offset dst dst-offset length)
486   (declare (type offset src-offset dst-offset length))
487   (locally
488    (declare (optimize (speed 3) (safety 0)))
489    (multiple-value-bind (dst dst-offset) (fix-sap-and-offset dst dst-offset)
490      (do-unary-bit-bash src src-offset dst dst-offset length
491                         #'word-sap-ref #'%set-word-sap-ref #'%raw-bits))))
492
493 (defun copy-from-system-area (src src-offset dst dst-offset length)
494   (declare (type offset src-offset dst-offset length))
495   (locally
496    (declare (optimize (speed 3) (safety 0)))
497    (multiple-value-bind (src src-offset) (fix-sap-and-offset src src-offset)
498      (do-unary-bit-bash src src-offset dst dst-offset length
499                         #'%raw-bits #'%set-raw-bits #'word-sap-ref))))
500
501 ;;; a common idiom for calling COPY-TO-SYSTEM-AREA
502 ;;;
503 ;;; Copy the entire contents of the vector V to memory starting at SAP+OFFSET.
504 (defun copy-byte-vector-to-system-area (bv sap &optional (offset 0))
505   ;; FIXME: There should be a type like SB!VM:BYTE so that we can write this
506   ;; type as (SIMPLE-ARRAY SB!VM:BYTE 1). Except BYTE is an external symbol of
507   ;; package CL; so maybe SB!VM:VM-BYTE?
508   (declare (type (simple-array (unsigned-byte 8) 1) bv))
509   (declare (type sap sap))
510   (declare (type fixnum offset))
511   ;; FIXME: Actually it looks as though this, and most other calls
512   ;; to COPY-TO-SYSTEM-AREA, could be written more concisely with BYTE-BLT.
513   ;; Except that the DST-END-DST-START convention for the length is confusing.
514   ;; Perhaps I could rename BYTE-BLT to BYTE-BLIT and replace the
515   ;; DST-END argument with an N-BYTES argument?
516   (copy-to-system-area bv
517                        (* sb!vm:vector-data-offset sb!vm:word-bits)
518                        sap
519                        offset
520                        (* (length bv) sb!vm:byte-bits)))