Derive that m is integer after (integerp (* m m)).
[sbcl.git] / src / compiler / dump.lisp
blob72f9ac16bc8103b11c242686893ef4dcbe432c84
1 ;;;; stuff that knows about dumping FASL files
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.
12 (in-package "SB-C")
14 ;;;; fasl dumper state
16 ;;; The FASL-OUTPUT structure represents everything we need to
17 ;;; know about dumping to a fasl file. (We need to objectify the
18 ;;; state because the fasdumper must be reentrant.)
19 (defstruct (fasl-output
20 (:constructor make-fasl-output (stream))
21 (:print-object (lambda (x s)
22 (print-unreadable-object (x s :type t)
23 (prin1 (namestring (fasl-output-stream x))
24 s))))
25 (:copier nil))
26 ;; the stream we dump to
27 (stream (missing-arg) :type stream)
28 ;; scratch space for computing varint encodings
29 ;; FIXME: can't use the theoretical max of 10 bytes
30 ;; due to constraint in WRITE-VAR-INTEGER.
31 (varint-buf (make-array 10 :element-type '(unsigned-byte 8) :fill-pointer t))
32 ;; hashtables we use to keep track of dumped constants so that we
33 ;; can get them from the table rather than dumping them again. The
34 ;; SIMILAR-TABLE is used for lists and strings, and the EQ-TABLE is
35 ;; used for everything else. We use a separate EQ table to avoid
36 ;; performance pathologies with objects for which SIMILAR
37 ;; degenerates to EQL. Everything entered in the SIMILAR table is
38 ;; also entered in the EQ table.
39 (similar-table (make-similarity-table) :type hash-table :read-only t)
40 (eq-table (make-hash-table :test 'eq) :type hash-table :read-only t)
41 ;; the INSTANCE table maps dumpable instances to unique IDs for calculating
42 ;; a similarity hash of composite objects that contain instances.
43 ;; A user-defined hash function can not use address-based hashing, and it is
44 ;; better not to add a new lazy stable hash slot to instances as a
45 ;; side-effect of compiling.
46 (instance-id-table (make-hash-table :test 'eq) :type hash-table :read-only t)
47 ;; Hashtable mapping a string to a list of fop-table indices of
48 ;; symbols whose name is that string. For any name as compared
49 ;; by STRING= there can be a symbol whose name is a base string
50 ;; and/or a symbol whose name is not a base string.
51 (string=-table (make-hash-table :test 'equal) :type hash-table)
52 ;; the fasloader table's current free pointer: the next offset to be used
53 (table-free 0 :type index)
54 ;; an alist (PACKAGE . OFFSET) of the table offsets for each package
55 ;; we have currently located.
56 (packages () :type list)
57 ;; a table mapping from the ENTRY-INFO structures for dumped XEPs to
58 ;; the table offsets of the corresponding code pointers
59 (entry-table (make-hash-table :test 'eq) :type hash-table)
60 ;; a table holding back-patching info for forward references to XEPs.
61 ;; The key is the ENTRY-INFO structure for the XEP, and the value is
62 ;; a list of conses (<code-handle> . <offset>), where <code-handle>
63 ;; is the offset in the table of the code object needing to be
64 ;; patched, and <offset> is the offset that must be patched.
65 (patch-table (make-hash-table :test 'eq) :type hash-table)
66 ;; a list of the table handles for all of the DEBUG-INFO structures
67 ;; dumped in this file. These structures must be back-patched with
68 ;; source location information when the compilation is complete.
69 (debug-info () :type list)
70 ;; This is used to keep track of objects that we are in the process
71 ;; of dumping so that circularities can be preserved. The key is the
72 ;; object that we have previously seen, and the value is the object
73 ;; that we reference in the table to find this previously seen
74 ;; object. (The value is never NIL.)
76 ;; Except with list objects, the key and the value are always the
77 ;; same. In a list, the key will be some tail of the value.
78 (circularity-table (make-hash-table :test 'eq) :type hash-table)
79 ;; a hash table of structures that are allowed to be dumped. If we
80 ;; try to dump a structure that isn't in this hash table, we lose.
81 (valid-structures (make-hash-table :test 'eq) :type hash-table)
82 ;; a hash table of slots to be saved when dumping an instance.
83 (saved-slot-names (make-hash-table :test 'eq) :type hash-table))
84 (declaim (freeze-type fasl-output))
86 ;;; Similarity hash table logic.
87 ;;; It really seems bogus to me that we do similarity checking in both
88 ;;; the IR1 namespace and the fasl dumper, even going so far as to use
89 ;;; two different ways to decide that an object is "harmless" to look up
90 ;;; (i.e. has no cyclic references).
92 #-sb-xc-host
93 (progn
94 (defun similarp (x y)
95 ;; Do almost the same thing that EQUAL does, but:
96 ;; - consider strings to be dissimilar if their element types differ.
97 ;; - scan elements of all specialized numeric vectors (BIT is done by EQUAL)
98 (named-let recurse ((x x) (y y))
99 (or (%eql x y)
100 (typecase x
101 (cons
102 (and (consp y)
103 (recurse (car x) (car y))
104 (recurse (cdr x) (cdr y))))
105 (string
106 ;; Incidentally, if we want to preserve non-simpleness of dumped arrays
107 ;; (which is permissible but not required), this case (and below for arrays)
108 ;; would be where to do it by returning non-similar.
109 (and (stringp y)
110 ;; (= (widetag-of ...)) would be too strict, because a simple string
111 ;; can be be similar to a non-simple string.
112 (eq (array-element-type x)
113 (array-element-type y))
114 (string= x y)))
115 ((or pathname bit-vector) ; fall back to EQUAL
116 ;; This could be slightly wrong, but so it always was, because we use
117 ;; (and have used) EQUAL for PATHNAME in FIND-CONSTANT, but:
118 ;; "Two pathnames S and C are similar if all corresponding pathname components are similar."
119 ;; and we readily admit that similarity of strings requires equal element types.
120 ;; So this is slightly dubious:
121 ;; (EQUAL (MAKE-PATHNAME :NAME (COERCE "A" 'SB-KERNEL:SIMPLE-CHARACTER-STRING))
122 ;; (MAKE-PATHNAME :NAME (COERCE "A" 'BASE-STRING))) => T
123 ;; On the other hand, nothing says that the pathname constructors such as
124 ;; MAKE-PATHNAME and MERGE-PATHNAMES don't convert to a canonical representation
125 ;; which renders them EQUAL when all strings are STRING=.
126 ;; This area of the language spec seems to have been a clusterfsck.
127 (equal x y))
128 ;; We would need to enhance COALESCE-TREE-P to detect cycles involving
129 ;; SIMPLE-VECTOR before recursing, otherwise this could exhaust stack.
130 ((unboxed-array (*))
131 (and (sb-xc:typep y '(simple-array * 1))
132 (= (length x) (length y))
133 (equal (array-element-type x) (array-element-type y))
134 (or (typep x '(array nil (*)))
135 (dotimes (i (length x) t)
136 (unless (= (aref x i) (aref y i)) (return nil))))))
137 ;; How do SIMPLE-VECTOR and other array types get here?
138 ;; Answer: COALESCE-TREE-P is "weaker than" the the local COALESCE-P function in FIND-CONSTANT,
139 ;; so it may return T on trees that contain atoms that COALESCE-P would have returned NIL on.
140 ;; Therefore DUMP-NON-IMMEDIATE-OBJECT may call SIMILARP on an object for which COALESCE-P
141 ;; would have said NIL.
142 ;; As mentioned at the top of this file, this seems incredibly bad,
143 ;; But users do not tend to have object cycles involving SIMPLE-VECTOR and such, I guess?
144 ;; Anyway, the answer has to be "no" for everything else: un-EQL objects are not similar.
145 (t nil)))))
146 ;; This hash function is an amalgam of SXHASH and PSHASH with the following properties:
147 ;; - numbers must have the same type to be similar (same as SXHASH)
148 ;; - instances must be EQ to be similar (same as SXHASH)
149 ;; - strings and characters are compared case-sensitively (same as SXHASH)
150 ;; - arrays must have the same type to be similar
151 ;; Unlike EQUAL-HASH, we never call EQ-HASH, because there is generally no reason
152 ;; to try to look up an object that lacks a content-based hash value.
153 (defun similar-hash (x)
154 (declare (special *compile-object*))
155 (named-let recurse ((x x))
156 ;; There is no depth cutoff - X must not be circular,
157 ;; which was already determined as a precondition to calling this,
158 ;; except that as pointed out, we must not descend into simple-vector
159 ;; because there was no circularity checking done for arrays.
160 (typecase x
161 (list (do ((hash 0))
162 ((atom x) (mix (if x (recurse x) #xD00F) hash))
163 ;; mix the hash of the CARs only, without consuming stack
164 ;; proportional to list length.
165 (setf hash (mix (recurse (car x)) hash)
166 x (cdr x))))
167 (symbol (sxhash x))
168 (number (sb-impl::number-sxhash x))
169 (pathname (sb-impl::pathname-sxhash x))
170 ((or instance simple-vector)
171 (let ((idmap (fasl-output-instance-id-table *compile-object*)))
172 (values (ensure-gethash x idmap
173 (let ((c (1+ (hash-table-count idmap))))
174 (mix c c))))))
175 ;; Arrays disregard simplicity.
176 ((array nil (*)) #xdead) ; don't access the data in these bastards
177 ((unboxed-array (*))
178 (let* ((simple-array (coerce x '(simple-array * (*))))
179 (widetag (%other-pointer-widetag simple-array))
180 (saetp (find widetag sb-vm:*specialized-array-element-type-properties*
181 :key #'sb-vm:saetp-typecode))
182 (n-data-words (ceiling (sb-vm::vector-n-data-octets simple-array saetp)
183 sb-vm:n-word-bytes))
184 (hash (word-mix (length x) widetag)))
185 (declare (word hash))
186 (dotimes (i n-data-words (logand hash most-positive-fixnum))
187 ;; FIXME: the last word of {1,2,4}-bit-per-element vectors
188 ;; needs to be masked. At worst, this fails to coalesce
189 ;; similar vectors, so it's not fatal.
190 (setq hash (word-mix hash (%vector-raw-bits x i))))))
191 (character (char-code x))
192 (t 0))))
193 (defun make-similarity-table ()
194 (make-hash-table :hash-function #'similar-hash :test #'similarp))
195 ) ; end PROGN
197 ;;; When cross-compiling, it's good enough to approximate similarity as EQUAL.
198 #+sb-xc-host
199 (defun make-similarity-table () (make-hash-table :test 'equal))
201 (defmacro get-similar (key table) `(gethash ,key ,table))
203 ;;; This structure holds information about a circularity.
204 (defstruct (circularity (:copier nil))
205 ;; the kind of modification to make to create circularity
206 (type (missing-arg) :type (member :rplaca :rplacd :svset :struct-set :slot-set))
207 ;; the object containing circularity
208 object
209 ;; index in object for circularity
210 (index (missing-arg) :type index)
211 ;; slot name in object for circularity
212 (slot-name nil :type symbol)
213 ;; the object to be stored at INDEX in OBJECT. This is that the key
214 ;; that we were using when we discovered the circularity.
215 value
216 ;; the value that was associated with VALUE in the
217 ;; CIRCULARITY-TABLE. This is the object that we look up in the
218 ;; EQ-TABLE to locate VALUE.
219 enclosing-object)
221 ;;; a list of the CIRCULARITY structures for all of the circularities
222 ;;; detected in the current top level call to DUMP-OBJECT. Setting
223 ;;; this lobotomizes circularity detection as well, since circular
224 ;;; dumping uses the table.
225 (defvar *circularities-detected*)
227 ;;;; utilities
229 ;;; Write the byte B to the specified FASL-OUTPUT stream.
230 (defun dump-byte (b fasl-output)
231 (declare (type (unsigned-byte 8) b) (type fasl-output fasl-output))
232 (write-byte b (fasl-output-stream fasl-output)))
234 ;; Dump a word-sized integer.
235 (defun dump-word (num fasl-output)
236 (declare (type sb-vm:word num) (type fasl-output fasl-output))
237 (let ((stream (fasl-output-stream fasl-output)))
238 (dotimes (i sb-vm:n-word-bytes)
239 (write-byte (ldb (byte 8 (* 8 i)) num) stream))))
241 ;; Dump a 32-bit integer.
242 (defun dump-unsigned-byte-32 (num fasl-output)
243 (declare (type (unsigned-byte 32) num) (type fasl-output fasl-output))
244 (let ((stream (fasl-output-stream fasl-output)))
245 (dotimes (i 4)
246 (write-byte (ldb (byte 8 (* 8 i)) num) stream))))
248 ;;; Dump NUM to the fasl stream, represented by N bytes. This works
249 ;;; for either signed or unsigned integers. There's no range checking
250 ;;; -- if you don't specify enough bytes for the number to fit, this
251 ;;; function cheerfully outputs the low bytes.
252 ;;; Multi-byte integers written by this function are always little-endian.
253 (defun dump-integer-as-n-bytes (num bytes fasl-output)
254 (declare (integer num) (type index bytes))
255 (declare (type fasl-output fasl-output))
256 (do ((n num (ash n -8))
257 (i bytes (1- i)))
258 ((= i 0))
259 (declare (type index i))
260 (dump-byte (logand n #xff) fasl-output))
261 (values))
263 (defun dump-varint (n fasl-output)
264 (let ((buf (fasl-output-varint-buf fasl-output)))
265 (setf (fill-pointer buf) 0)
266 (write-var-integer n buf)
267 (write-sequence buf (fasl-output-stream fasl-output))))
269 (defun dump-fop+operands (fasl-output opcode arg1
270 &optional (arg2 0 arg2p) (arg3 0 arg3p))
271 (declare (type (unsigned-byte 8) opcode) (type word arg1 arg2 arg3))
272 (dump-byte opcode fasl-output)
273 (dump-varint arg1 fasl-output)
274 (when arg2p (dump-varint arg2 fasl-output))
275 (when arg3p (dump-varint arg3 fasl-output)))
277 ;;; Dump the FOP code for the named FOP to the specified FASL-OUTPUT.
278 ;;; This macro is supposed to look functional in that it evals its
279 ;;; args, but it wants to evaluate the first arg at compile-time. For
280 ;;; this reason it should really not be a quoted symbol, but I think
281 ;;; this used to actually be a function which had to look up the fop's
282 ;;; opcode every time called. The named FOP is also treated as a string
283 ;;; designator which is interned in the package defining the FOPs.
284 (defmacro dump-fop (fop-symbol file &rest args)
285 (let* ((fop-symbol
286 ;; EVAL is too much. Just ascertain we have a quoted symbol
287 (if (typep fop-symbol '(cons (eql quote) (cons symbol null)))
288 (cadr fop-symbol)
289 (error "Bad 1st arg to DUMP-FOP: ~S" fop-symbol)))
290 (val (or (gethash (intern (symbol-name fop-symbol) #.(find-package "SB-FASL"))
291 *fop-name-to-opcode*)
292 (error "compiler bug: ~S is not a legal fasload operator."
293 fop-symbol)))
294 (fop-argc (aref (car **fop-signatures**) val)))
295 (cond
296 ((not (eql (length args) fop-argc))
297 (error "~S takes ~D argument~:P" fop-symbol fop-argc))
298 ((eql fop-argc 0)
299 `(dump-byte ,val ,file))
301 `(dump-fop+operands ,file ,val ,@args)))))
303 ;;; Push the object at table offset Handle on the fasl stack.
304 (defun dump-push (handle fasl-output)
305 (declare (type index handle) (type fasl-output fasl-output))
306 (dump-fop 'fop-push fasl-output handle)
307 (values))
309 ;;; Pop the object currently on the fasl stack top into the table, and
310 ;;; return the table index, incrementing the free pointer.
311 (defun dump-pop (fasl-output)
312 (prog1
313 (fasl-output-table-free fasl-output)
314 (dump-fop 'fop-pop fasl-output)
315 (incf (fasl-output-table-free fasl-output))))
317 (defun dump-to-table (fasl-output)
318 (prog1
319 (fasl-output-table-free fasl-output)
320 (dump-fop 'fop-move-to-table fasl-output)
321 (incf (fasl-output-table-free fasl-output))))
323 (defun cdr-similarity-p (index fasl-output)
324 (when (consp index)
325 (destructuring-bind (list . nthcdr) index
326 (let ((index (gethash list (fasl-output-eq-table fasl-output))))
327 (when (fixnump index)
328 (dump-push index fasl-output)
329 (dump-fop 'fop-nthcdr fasl-output nthcdr)
330 t)))))
332 ;;; If X is in File's SIMILAR-TABLE, then push the object and return T,
333 ;;; otherwise NIL.
334 (defun similar-check-table (x fasl-output)
335 (declare (type fasl-output fasl-output))
336 (let ((index (get-similar x (fasl-output-similar-table fasl-output))))
337 (cond ((fixnump index)
338 (dump-push index fasl-output)
340 ((cdr-similarity-p index fasl-output)))))
342 ;;; These functions are called after dumping an object to save the
343 ;;; object in the table. The object (also passed in as X) must already
344 ;;; be on the top of the FOP stack.
345 (defun eq-save-object (x fasl-output)
346 (declare (type fasl-output fasl-output))
347 (setf (gethash x (fasl-output-eq-table fasl-output))
348 (dump-to-table fasl-output))
349 (values))
350 (defun similar-save-object (x fasl-output)
351 (declare (type fasl-output fasl-output))
352 (let ((handle (dump-to-table fasl-output)))
353 (setf (get-similar x (fasl-output-similar-table fasl-output)) handle)
354 (setf (gethash x (fasl-output-eq-table fasl-output)) handle))
355 (values))
356 ;;; Record X in File's CIRCULARITY-TABLE. This is called on objects
357 ;;; that we are about to dump might have a circular path through them.
359 ;;; The object must not currently be in this table, since the dumper
360 ;;; should never be recursively called on a circular reference.
361 ;;; Instead, the dumping function must detect the circularity and
362 ;;; arrange for the dumped object to be patched.
363 (defun note-potential-circularity (x fasl-output)
364 (let ((circ (fasl-output-circularity-table fasl-output)))
365 (aver (not (gethash x circ)))
366 (setf (gethash x circ) x))
367 (values))
369 ;;;; opening and closing fasl files
371 ;;; Open a fasl file, write its header, and return a FASL-OUTPUT
372 ;;; object for dumping to it. Some human-readable information about
373 ;;; the source code is given by the string WHERE.
374 (defun open-fasl-output (name where)
375 (declare (type pathname name))
376 (flet ((fasl-write-string (string stream)
377 ;; UTF-8 is safe to use, because +FASL-HEADER-STRING-STOP-CHAR-CODE+
378 ;; may not appear in UTF-8 encoded bytes
379 (write-sequence (string-to-octets string :external-format :utf-8)
380 stream)))
381 (let* ((stream (open name
382 :direction :output
383 :if-exists :supersede
384 :element-type 'sb-assem:assembly-unit))
385 (res (make-fasl-output stream)))
386 ;; Before the actual FASL header, write a shebang line using the current
387 ;; runtime path, so our fasls can be executed directly from the shell.
388 #-sb-xc-host ; cross-compiled fasls are not directly executable
389 (when *runtime-pathname*
390 (fasl-write-string
391 (format nil "#!~A --script~%"
392 (native-namestring *runtime-pathname* :as-file t))
393 stream))
394 ;; Begin the header with the constant machine-readable (and
395 ;; semi-human-readable) string which is used to identify fasl files.
396 (fasl-write-string *fasl-header-string-start-string* stream)
397 ;; The constant string which begins the header is followed by
398 ;; arbitrary human-readable text, terminated by
399 ;; +FASL-HEADER-STRING-STOP-CHAR-CODE+.
400 (fasl-write-string
401 (with-standard-io-syntax
402 (let ((*print-readably* nil)
403 (*print-pretty* nil))
404 (format nil
405 "~% ~
406 compiled from ~S~% ~
407 using ~A version ~A~%"
408 where
409 (lisp-implementation-type)
410 (lisp-implementation-version))))
411 stream)
412 (dump-byte +fasl-header-string-stop-char-code+ res)
413 ;; Finish the header by outputting fasl file implementation,
414 ;; version, and key *FEATURES*.
415 (flet ((dump-counted-string (string)
416 ;; The count is dumped as a 32-bit unsigned-byte even on 64-bit
417 ;; platforms. This ensures that a x86-64 SBCL can gracefully
418 ;; detect an error when trying to read a x86 fasl, instead
419 ;; of choking on a ridiculously long counted string.
420 ;; -- JES, 2005-12-30
421 (dump-unsigned-byte-32 (length string) res)
422 (dotimes (i (length string))
423 (dump-byte (char-code (aref string i)) res))))
424 (dump-counted-string (symbol-name +backend-fasl-file-implementation+))
425 (dump-word +fasl-file-version+ res)
426 (dump-counted-string (lisp-implementation-version))
427 (dump-counted-string (compute-features-affecting-fasl-format)))
428 res)))
430 ;;; Close the specified FASL-OUTPUT, aborting the write if ABORT-P.
431 (defun close-fasl-output (fasl-output abort-p)
432 (declare (type fasl-output fasl-output))
434 (unless abort-p
435 ;; sanity checks
436 (aver (zerop (hash-table-count (fasl-output-patch-table fasl-output))))
437 ;; End the group.
438 (dump-fop 'fop-end-group fasl-output (fasl-output-table-free fasl-output)))
440 ;; That's all, folks.
441 (close (fasl-output-stream fasl-output) :abort abort-p)
442 (values))
444 ;;;; main entries to object dumping
446 ;;; This function deals with dumping objects that are complex enough
447 ;;; so that we want to cache them in the table, rather than repeatedly
448 ;;; dumping them. If the object is in the EQ-TABLE, then we push it,
449 ;;; otherwise, we do a type dispatch to a type specific dumping
450 ;;; function. The type specific branches do any appropriate
451 ;;; SIMILAR-TABLE check and table entry.
453 ;;; When we go to dump the object, we enter it in the CIRCULARITY-TABLE.
454 (defun dump-non-immediate-object (x file)
455 (let ((index (gethash x (fasl-output-eq-table file))))
456 (cond ((fixnump index)
457 (dump-push index file))
458 ((cdr-similarity-p index file))
460 (typecase x
461 (symbol (dump-symbol x file))
462 (list
463 (cond ((not (coalesce-tree-p x))
464 (dump-list x file)
465 (eq-save-object x file))
466 ((not (similar-check-table x file))
467 (dump-list x file t)
468 (similar-save-object x file))))
469 (layout
470 (dump-layout x file)
471 (eq-save-object x file))
472 #+sb-xc-host
473 (ctype
474 (aver (not (classoid-p x)))
475 (dump-object 'values-specifier-type file)
476 (dump-object (type-specifier x) file)
477 (dump-fop 'fop-funcall file 1))
478 (instance
479 (multiple-value-bind (slot-names slot-names-p)
480 (gethash x (fasl-output-saved-slot-names file))
481 (if slot-names-p
482 (dump-instance-saving-slots x slot-names file)
483 (dump-structure x file)))
484 (eq-save-object x file))
485 (array
486 ;; DUMP-ARRAY (and its callees) are responsible for
487 ;; updating the EQ and SIMILAR hash tables.
488 (dump-array x file))
489 (number
490 (unless (similar-check-table x file)
491 (etypecase x
492 (ratio (dump-ratio x file))
493 (complex (dump-complex x file))
494 (float (dump-float x file))
495 (integer (dump-integer x file)))
496 (similar-save-object x file)))
497 #+(and (not sb-xc-host) sb-simd-pack)
498 (simd-pack
499 (unless (similar-check-table x file)
500 (dump-fop 'fop-simd-pack file)
501 (dump-integer-as-n-bytes (%simd-pack-tag x) 8 file)
502 (dump-integer-as-n-bytes (%simd-pack-low x) 8 file)
503 (dump-integer-as-n-bytes (%simd-pack-high x) 8 file)
504 (similar-save-object x file)))
505 #+(and (not sb-xc-host) sb-simd-pack-256)
506 (simd-pack-256
507 (unless (similar-check-table x file)
508 (dump-simd-pack-256 x file)
509 (similar-save-object x file)))
511 ;; This probably never happens, since bad things tend to
512 ;; be detected during IR1 conversion.
513 (error "This object cannot be dumped into a fasl file:~% ~S"
514 x))))))
515 (values))
517 #+(and (not sb-xc-host) sb-simd-pack-256)
518 (defun dump-simd-pack-256 (x file)
519 (dump-fop 'fop-simd-pack file)
520 (dump-integer-as-n-bytes (logior (%simd-pack-256-tag x) (ash 1 6)) 8 file)
521 (dump-integer-as-n-bytes (%simd-pack-256-0 x) 8 file)
522 (dump-integer-as-n-bytes (%simd-pack-256-1 x) 8 file)
523 (dump-integer-as-n-bytes (%simd-pack-256-2 x) 8 file)
524 (dump-integer-as-n-bytes (%simd-pack-256-3 x) 8 file))
526 ;;; Dump an object of any type by dispatching to the correct
527 ;;; type-specific dumping function. We pick off immediate objects,
528 ;;; symbols and magic lists here. Other objects are handled by
529 ;;; DUMP-NON-IMMEDIATE-OBJECT.
531 ;;; This is the function used for recursive calls to the fasl dumper.
532 ;;; We don't worry about creating circularities here, since it is
533 ;;; assumed that there is a top level call to DUMP-OBJECT.
534 (defun sub-dump-object (x file)
535 (cond ((listp x)
536 (if x
537 (dump-non-immediate-object x file)
538 (dump-fop 'fop-empty-list file)))
539 ((symbolp x)
540 (if (eq x t)
541 (dump-fop 'fop-truth file)
542 (dump-non-immediate-object x file)))
543 ((fixnump x) (dump-integer x file))
544 ((characterp x)
545 (dump-fop 'fop-character file (char-code x)))
546 ((packagep x)
547 (dump-push (dump-package x file) file))
548 #-sb-xc-host
549 ((system-area-pointer-p x)
550 (dump-fop 'fop-word-pointer file)
551 (dump-integer-as-n-bytes (sap-int x) sb-vm:n-word-bytes file))
553 (dump-non-immediate-object x file))))
555 ;;; Dump stuff to backpatch already dumped objects. INFOS is the list
556 ;;; of CIRCULARITY structures describing what to do. The patching FOPs
557 ;;; take the value to store on the stack. We compute this value by
558 ;;; fetching the enclosing object from the table, and then CDR'ing it
559 ;;; if necessary.
560 (defun dump-circularities (infos file)
561 (let ((table (fasl-output-eq-table file)))
562 (dolist (info infos)
564 (let* ((value (circularity-value info))
565 (enclosing (circularity-enclosing-object info)))
566 (dump-push (gethash enclosing table) file)
567 (unless (eq enclosing value)
568 (do ((current enclosing (cdr current))
569 (i 0 (1+ i)))
570 ((eq current value)
571 (dump-fop 'fop-nthcdr file i))
572 (declare (type index i)))))
574 (macrolet ((fop-op (symbol)
575 (gethash (intern (symbol-name symbol) "SB-FASL")
576 *fop-name-to-opcode*)))
577 (dump-byte (ecase (circularity-type info)
578 (:rplaca (fop-op fop-rplaca))
579 (:rplacd (fop-op fop-rplacd))
580 (:svset (fop-op fop-svset))
581 (:struct-set (fop-op fop-structset))
582 (:slot-set
583 (dump-object (circularity-slot-name info) file)
584 (fop-op fop-slotset)))
585 file))
586 (dump-varint (gethash (circularity-object info) table) file)
587 (dump-varint (circularity-index info) file))))
589 ;;; Set up stuff for circularity detection, then dump an object. All
590 ;;; shared and circular structure will be exactly preserved within a
591 ;;; single call to DUMP-OBJECT. Sharing between objects dumped by
592 ;;; separate calls is only preserved when convenient.
594 ;;; We peek at the object type so that we only pay the circular
595 ;;; detection overhead on types of objects that might be circular.
596 (defun dump-object (x file)
597 (if (compound-object-p x)
598 (let ((*circularities-detected* ())
599 (circ (fasl-output-circularity-table file)))
600 (clrhash circ)
601 (sub-dump-object x file)
602 (when *circularities-detected*
603 (dump-circularities *circularities-detected* file)
604 (clrhash circ)))
605 (sub-dump-object x file)))
607 ;;;; LOAD-TIME-VALUE and MAKE-LOAD-FORM support
609 ;;; Emit a funcall of the function and return the handle for the
610 ;;; result.
611 (defun fasl-dump-load-time-value-lambda (fun file)
612 (declare (type clambda fun) (type fasl-output file))
613 (let ((handle (gethash (leaf-info fun)
614 (fasl-output-entry-table file))))
615 (aver handle)
616 (dump-push handle file)
617 (dump-fop 'fop-funcall file 0))
618 (dump-pop file))
620 ;;; Return T iff CONSTANT has already been dumped. It's been dumped if
621 ;;; it's in the EQ table.
623 ;;; Note: historically (1) the above comment was "T iff ... has not been dumped",
624 ;;; (2) the test was was also true if the constant had been validated / was in
625 ;;; the valid objects table. This led to substructures occasionally skipping the
626 ;;; validation, and hence failing the "must have been validated" test.
627 (defun fasl-constant-already-dumped-p (constant file)
628 (and (gethash constant (fasl-output-eq-table file)) t))
630 ;;; Use HANDLE whenever we try to dump CONSTANT. HANDLE should have been
631 ;;; returned earlier by FASL-DUMP-LOAD-TIME-VALUE-LAMBDA.
632 (defun fasl-note-handle-for-constant (constant handle file)
633 (let ((table (fasl-output-eq-table file)))
634 (when (gethash constant table)
635 (error "~S already dumped?" constant))
636 (setf (gethash constant table) handle))
637 (values))
639 ;;; Note that the specified structure can just be dumped by
640 ;;; enumerating the slots.
641 (defun fasl-validate-structure (structure file)
642 (setf (gethash structure (fasl-output-valid-structures file)) t)
643 (values))
645 ;;; Note that the specified standard object can just be dumped by
646 ;;; saving its slot values.
647 (defun fasl-note-instance-saves-slots (instance slot-names file)
648 (setf (gethash instance (fasl-output-saved-slot-names file)) slot-names)
649 (values))
651 ;;;; number dumping
653 (defun dump-ratio (x file)
654 (sub-dump-object (numerator x) file)
655 (sub-dump-object (denominator x) file)
656 (dump-fop 'fop-ratio file))
658 #+(and long-float x86)
659 (defun dump-long-float (float file)
660 (declare (long-float float))
661 (let ((exp-bits (long-float-exp-bits float))
662 (high-bits (long-float-high-bits float))
663 (low-bits (long-float-low-bits float)))
664 ;; We could get away with DUMP-WORD here, since the x86 has 4-byte words,
665 ;; but we prefer to make things as explicit as possible.
666 ;; --njf, 2004-08-16
667 (dump-integer-as-n-bytes low-bits 4 file)
668 (dump-integer-as-n-bytes high-bits 4 file)
669 (dump-integer-as-n-bytes exp-bits 2 file)))
671 #+(and long-float sparc)
672 (defun dump-long-float (float file)
673 (declare (long-float float))
674 (let ((exp-bits (long-float-exp-bits float))
675 (high-bits (long-float-high-bits float))
676 (mid-bits (long-float-mid-bits float))
677 (low-bits (long-float-low-bits float)))
678 ;; We could get away with DUMP-WORD here, since the sparc has 4-byte
679 ;; words, but we prefer to make things as explicit as possible.
680 ;; --njf, 2004-08-16
681 (dump-integer-as-n-bytes low-bits 4 file)
682 (dump-integer-as-n-bytes mid-bits 4 file)
683 (dump-integer-as-n-bytes high-bits 4 file)
684 (dump-integer-as-n-bytes exp-bits 4 file)))
686 (defun dump-integer (n file)
687 (typecase n
688 ((signed-byte 8)
689 (case n
690 (0 (dump-fop 'fop-int-const0 file))
691 (1 (dump-fop 'fop-int-const1 file))
692 (2 (dump-fop 'fop-int-const2 file))
693 (-1 (dump-fop 'fop-int-const-neg1 file))
694 (t (dump-fop 'fop-byte-integer file)
695 (dump-byte (logand #xFF n) file))))
696 ((unsigned-byte #.(1- sb-vm:n-word-bits))
697 (dump-fop 'fop-word-integer file)
698 (dump-word n file))
699 (sb-vm:signed-word
700 (dump-fop 'fop-word-integer file)
701 (dump-integer-as-n-bytes n sb-vm:n-word-bytes file))
703 (let ((bytes (ceiling (1+ (integer-length n)) 8)))
704 (dump-fop 'fop-integer file bytes)
705 (dump-integer-as-n-bytes n bytes file)))))
707 (defun dump-float (x file)
708 (etypecase x
709 (single-float
710 (dump-fop 'fop-single-float file)
711 (dump-integer-as-n-bytes (single-float-bits x) 4 file))
712 (double-float
713 (dump-fop 'fop-double-float file)
714 (dump-integer-as-n-bytes (double-float-low-bits x) 4 file)
715 (dump-integer-as-n-bytes (double-float-high-bits x) 4 file))
716 #+long-float
717 (long-float
718 (dump-fop 'fop-long-float file)
719 (dump-long-float x file))))
721 (defun dump-complex (x file)
722 (let ((re (realpart x))
723 (im (imagpart x)))
724 (typecase x
725 ((complex single-float)
726 (dump-fop 'fop-complex-single-float file)
727 (dump-integer-as-n-bytes (single-float-bits re) 4 file)
728 (dump-integer-as-n-bytes (single-float-bits im) 4 file))
729 ((complex double-float)
730 (dump-fop 'fop-complex-double-float file)
731 (dump-integer-as-n-bytes (double-float-low-bits re) 4 file)
732 (dump-integer-as-n-bytes (double-float-high-bits re) 4 file)
733 (dump-integer-as-n-bytes (double-float-low-bits im) 4 file)
734 (dump-integer-as-n-bytes (double-float-high-bits im) 4 file))
735 #+long-float
736 ((complex long-float)
737 (dump-fop 'fop-complex-long-float file)
738 (dump-long-float re file)
739 (dump-long-float im file))
741 (sub-dump-object re file)
742 (sub-dump-object im file)
743 (dump-fop 'fop-complex file)))))
745 ;;;; symbol dumping
747 ;;; Return the table index of PKG, adding the package to the table if
748 ;;; necessary. During cold load, we read the string as a normal string
749 ;;; so that we can do the package lookup at cold load time.
751 ;;; FIXME: Despite the parallelism in names, the functionality of
752 ;;; this function is not parallel to other functions DUMP-FOO, e.g.
753 ;;; DUMP-SYMBOL and DUMP-LIST. The mapping between names and behavior
754 ;;; should be made more consistent.
755 (declaim (ftype (function (package fasl-output) index) dump-package))
756 (defun dump-package (pkg file)
757 (declare (inline assoc))
758 (cond ((cdr (assoc pkg (fasl-output-packages file) :test #'eq)))
760 (let ((s (sb-xc:package-name pkg)))
761 (dump-fop 'fop-named-package-save file (length s))
762 ;; Package names are always dumped as varint-encoded character strings
763 ;; except on non-unicode builds.
764 (dump-chars (coerce s '(simple-array character (*))) file nil))
765 (let ((entry (fasl-output-table-free file)))
766 (incf (fasl-output-table-free file))
767 (push (cons pkg entry) (fasl-output-packages file))
768 entry))))
770 ;;; dumper for lists
772 ;;; Dump a list, setting up patching information when there are
773 ;;; circularities. We scan down the list, checking for CDR and CAR
774 ;;; circularities.
776 ;;; If there is a CDR circularity, we terminate the list with NIL and
777 ;;; make a CIRCULARITY notation for the CDR of the previous cons.
779 ;;; If there is no CDR circularity, then we mark the current cons and
780 ;;; check for a CAR circularity. When there is a CAR circularity, we
781 ;;; make the CAR NIL initially, arranging for the current cons to be
782 ;;; patched later.
784 ;;; Otherwise, we recursively call the dumper to dump the current
785 ;;; element.
786 (defun dump-list (list file &optional coalesce)
787 (aver (and list
788 (not (gethash list (fasl-output-circularity-table file)))))
789 (let ((circ (fasl-output-circularity-table file)))
790 (flet ((cdr-circularity (obj n)
791 ;; COALESCE means there's no cycles
792 (let ((ref (gethash obj circ)))
793 (when ref
794 (push (make-circularity :type :rplacd
795 :object list
796 :index (1- n)
797 :value obj
798 :enclosing-object ref)
799 *circularities-detected*)
800 (terminate-undotted-list n file)
801 t))))
802 (do* ((l list (cdr l))
803 (n 0 (1+ n)))
804 ((atom l)
805 (cond ((null l)
806 (terminate-undotted-list n file))
808 (cond ((cdr-circularity l n))
810 (sub-dump-object l file)
811 (terminate-dotted-list n file))))))
812 (declare (type index n))
813 (when (cdr-circularity l n)
814 (return))
816 ;; if this CONS is EQ to some other object we have already
817 ;; dumped, dump a reference to that instead.
818 (let* ((table (if coalesce
819 (fasl-output-similar-table file)
820 (fasl-output-eq-table file)))
821 (index (gethash l table)))
822 (cond ((fixnump index)
823 (dump-push index file)
824 (terminate-dotted-list n file)
825 (return))
826 ((cdr-similarity-p index file)
827 (when (> n 0)
828 (terminate-dotted-list n file))
829 (return)))
831 ;; put an entry for this cons into the fasl output cons table,
832 ;; for the benefit of dumping later constants
833 (let ((index (cons list n)))
834 (setf (gethash l (fasl-output-eq-table file)) index)
835 (when coalesce
836 (setf (gethash l (fasl-output-similar-table file)) index))))
838 (setf (gethash l circ) list)
840 (let* ((obj (car l))
841 (ref (gethash obj circ)))
842 (cond (ref
843 (push (make-circularity :type :rplaca
844 :object list
845 :index n
846 :value obj
847 :enclosing-object ref)
848 *circularities-detected*)
849 (sub-dump-object nil file))
850 ;; Avoid coalescing if COALESCE-TREE-P decided not to
851 ((consp obj)
852 ;; This is the same as DUMP-NON-IMMEDIATE-OBJECT but
853 ;; without calling COALESCE-TREE-P again.
854 (let ((index (gethash obj (fasl-output-eq-table file))))
855 (cond ((fixnump index)
856 (dump-push index file))
857 ((cdr-similarity-p index file))
858 ((not coalesce)
859 (dump-list obj file)
860 (eq-save-object obj file))
861 ((not (similar-check-table obj file))
862 (dump-list obj file t)
863 (similar-save-object obj file)))))
865 (sub-dump-object obj file))))))))
867 (defconstant fop-list-base-opcode 128)
869 (defun terminate-dotted-list (n file)
870 (declare (type index n) (type fasl-output file))
871 (aver (plusp n))
872 (cond ((< n 16)
873 (dump-byte (logior fop-list-base-opcode #b10000 n) file))
875 (dump-byte (logior fop-list-base-opcode #b10000) file)
876 (dump-varint (- n 16) file))))
878 (defun terminate-undotted-list (n file)
879 (declare (type index n) (type fasl-output file))
880 (aver (plusp n))
881 (cond ((< n 16)
882 (dump-byte (logior fop-list-base-opcode n) file))
884 (dump-byte (logior fop-list-base-opcode) file)
885 (dump-varint (- n 16) file))))
887 ;;;; array dumping
889 ;;; Dump the array thing.
890 (defun dump-array (x file)
891 (if (vectorp x)
892 (dump-vector x file)
893 (dump-multi-dim-array x file)))
895 ;;; Dump the vector object. If it's not simple, then actually dump a
896 ;;; simple realization of it. But we enter the original in the EQ or SIMILAR
897 ;;; tables.
898 (defun dump-vector (x file)
899 (let ((simple-version (if (array-header-p x)
900 (coerce x `(simple-array
901 ,(array-element-type x)
902 (*)))
903 x)))
904 (typecase simple-version
905 ;; On the host, take all strings to be simple-base-string.
906 ;; In the target, really test for simple-base-string.
907 (#+sb-xc-host simple-string #-sb-xc-host simple-base-string
908 (unless (similar-check-table x file)
909 (dump-fop 'fop-base-string file (length simple-version))
910 (dump-chars simple-version file t)
911 (similar-save-object x file)))
912 #-sb-xc-host
913 ((simple-array character (*))
914 #-sb-unicode (bug "how did we get here?")
915 (unless (similar-check-table x file)
916 (dump-fop 'fop-character-string file (length simple-version))
917 (dump-chars simple-version file nil)
918 (similar-save-object x file)))
919 ;; SB-XC:SIMPLE-VECTOR will not match an array whose element type
920 ;; the host upgraded to T but whose expressed type was not T.
921 (sb-xc:simple-vector
922 (dump-simple-vector simple-version file)
923 (eq-save-object x file)
924 (unless (eq x simple-version)
925 ;; In case it has circularities that need to be patched
926 ;; later.
927 (setf (gethash simple-version (fasl-output-eq-table file))
928 (gethash x (fasl-output-eq-table file)))))
930 (unless (similar-check-table x file)
931 (dump-specialized-vector simple-version file)
932 (similar-save-object x file))))))
934 ;;; Dump a SIMPLE-VECTOR, handling any circularities.
935 (defun dump-simple-vector (v file)
936 (declare (type simple-vector v) (type fasl-output file))
937 (note-potential-circularity v file)
938 (do ((index 0 (1+ index))
939 (length (length v))
940 (circ (fasl-output-circularity-table file)))
941 ((= index length)
942 (dump-fop 'fop-vector file length))
943 (let* ((obj (aref v index))
944 (ref (gethash obj circ)))
945 (cond (ref
946 (push (make-circularity :type :svset
947 :object v
948 :index index
949 :value obj
950 :enclosing-object ref)
951 *circularities-detected*)
952 (sub-dump-object nil file))
954 (sub-dump-object obj file))))))
956 (defun dump-specialized-vector (vector file &key data-only)
957 ;; The DATA-ONLY option was for the now-obsolete trace-table,
958 ;; but it seems like a good option to keep around.
959 #-sb-xc-host (declare (type (simple-unboxed-array (*)) vector))
960 (let* ((length (length vector))
961 (widetag (%other-pointer-widetag vector))
962 (bits-per-elt (sb-vm::simple-array-widetag->bits-per-elt widetag)))
963 (unless data-only
964 ;; fop-spec-vector doesn't grok trailing #\null convention.
965 (aver (and (/= widetag sb-vm:simple-base-string-widetag)
966 (/= widetag sb-vm:simple-vector-widetag)))
967 (dump-fop 'fop-spec-vector file length)
968 (dump-byte widetag file))
970 #+sb-xc-host
971 (when (or (= widetag sb-vm:simple-array-fixnum-widetag)
972 (= widetag sb-vm:simple-array-unsigned-fixnum-widetag))
973 ;; Fixnum vector contents are tagged numbers. Make a copy.
974 (setq vector (map 'vector (lambda (x) (ash x sb-vm:n-fixnum-tag-bits))
975 vector)))
977 ;; cross-io doesn't know about fasl streams, so use actual stream.
978 (sb-impl::buffer-output (fasl-output-stream file)
979 vector
981 (ceiling (* length bits-per-elt) sb-vm:n-byte-bits)
982 #+sb-xc-host bits-per-elt)))
984 ;;; Dump a multi-dimensional array. Note: any displacements are folded out.
985 (defun dump-multi-dim-array (array file)
986 (note-potential-circularity array file)
987 (let ((rank (array-rank array)))
988 (dotimes (i rank)
989 (dump-integer (array-dimension array i) file))
990 (with-array-data ((vector array) (start) (end))
991 (if (and (= start 0) (= end (length vector)))
992 (sub-dump-object vector file)
993 (sub-dump-object (subseq vector start end) file)))
994 (dump-fop 'fop-array file rank)
995 (eq-save-object array file)))
998 ;;; Dump string-ish things.
1000 ;;; Dump a SIMPLE-STRING.
1001 (defun dump-chars (s fasl-output base-string-p)
1002 (declare (type simple-string s))
1003 (if (or base-string-p #-sb-unicode t) ; if non-unicode, every char is 1 byte
1004 (dovector (c s)
1005 (dump-byte (char-code c) fasl-output))
1006 (dovector (c s) ; varint (a/k/a LEB128) is better for this than UTF-8.
1007 (dump-varint (char-code c) fasl-output))))
1009 ;;; If we get here, it is assumed that the symbol isn't in the table,
1010 ;;; but we are responsible for putting it there when appropriate.
1011 (defun dump-symbol (s file)
1012 (declare (type fasl-output file))
1013 (let* ((pname (symbol-name s))
1014 (pname-length (length pname))
1015 ;; If no unicode, then all strings are base-string-p.
1016 ;; On the host, everything is base-string-p.
1017 (base-string-p (and #-sb-xc-host (typep pname 'base-string)))
1018 (length+flag (logior (ash pname-length 1) (if base-string-p 1 0)))
1019 (dumped-as-copy nil)
1020 (pkg (sb-xc:symbol-package s)))
1021 (cond ((null pkg)
1022 (let ((this-base-p base-string-p))
1023 (dolist (lookalike (gethash pname (fasl-output-string=-table file))
1024 (dump-fop 'fop-uninterned-symbol-save
1025 file length+flag))
1026 ;; Find the right kind of lookalike symbol.
1027 ;; [what about a symbol whose name is a (simple-array nil (0))?]
1028 (let ((that-base-p
1029 (and #-sb-xc-host (typep (symbol-name lookalike) 'base-string))))
1030 (when (or (and this-base-p that-base-p)
1031 (and (not this-base-p) (not that-base-p)))
1032 (dump-fop 'fop-copy-symbol-save file
1033 (gethash lookalike (fasl-output-eq-table file)))
1034 (return (setq dumped-as-copy t)))))))
1035 ((eq pkg *cl-package*)
1036 (dump-fop 'fop-lisp-symbol-save file length+flag))
1037 ((eq pkg *keyword-package*)
1038 (dump-fop 'fop-keyword-symbol-save file length+flag))
1040 (let ((pkg-index (dump-package pkg file)))
1041 (if (eq (find-symbol pname pkg) :inherited)
1042 (dump-fop 'fop-symbol-in-package-save file length+flag pkg-index)
1043 (dump-fop 'fop-symbol-in-package-internal-save file length+flag pkg-index)))))
1045 (unless dumped-as-copy
1046 (dump-chars pname file base-string-p)
1047 (push s (gethash (symbol-name s) (fasl-output-string=-table file))))
1049 (setf (gethash s (fasl-output-eq-table file))
1050 (fasl-output-table-free file))
1052 (incf (fasl-output-table-free file)))
1054 (values))
1056 ;;;; component (function) dumping
1058 (defun dump-segment (segment code-length fasl-output)
1059 (declare (type sb-assem:segment segment)
1060 (type fasl-output fasl-output))
1061 (let* ((stream (fasl-output-stream fasl-output))
1062 (n-written (write-segment-contents segment stream)))
1063 ;; In CMU CL there was no enforced connection between the CODE-LENGTH
1064 ;; argument and the number of bytes actually written. I added this
1065 ;; assertion while trying to debug portable genesis. -- WHN 19990902
1066 (unless (= code-length n-written)
1067 (bug "code-length=~W, n-written=~W" code-length n-written)))
1068 (values))
1070 (eval-when (:compile-toplevel)
1071 (assert (<= (length +fixup-kinds+) 16))) ; fixup-kind fits in 4 bits
1073 (defconstant-eqx +fixup-flavors+
1074 #(:assembly-routine
1075 :card-table-index-mask :symbol-tls-index
1076 :alien-code-linkage-index :alien-data-linkage-index
1077 :foreign :foreign-dataref
1078 :code-object
1079 :layout :immobile-symbol :linkage-cell
1080 :symbol-value
1081 :layout-id)
1082 #'equalp)
1084 (eval-when (:compile-toplevel :load-toplevel :execute)
1085 (defun encoded-fixup-flavor (flavor)
1086 (or (position flavor +fixup-flavors+)
1087 (error "Bad fixup flavor ~s" flavor))))
1089 ;;; Pack the aspects of a fixup into an integer.
1090 ;;; DATA is for asm routine fixups. The routine can be represented in 8 bits,
1091 ;;; so the fixup can be reduced to one word instead of an integer and a symbol.
1092 (declaim (inline !pack-fixup-info))
1093 (defun !pack-fixup-info (offset kind flavor data)
1094 (logior ;; 4 bits
1095 (the (mod 16) (or (position kind +fixup-kinds+)
1096 (error "Bad fixup kind ~s" kind)))
1097 ;; 4 bits
1098 (ash (the (mod 16) (encoded-fixup-flavor flavor)) 4)
1099 ;; 8 bits
1100 (ash (the (mod 256) data) 8)
1101 ;; whatever it needs
1102 (ash offset 16)))
1104 #-(or x86 x86-64) ; these two architectures provide an overriding definition
1105 (defun pack-fixups-for-reapplication (fixup-notes)
1106 (let (result)
1107 (dolist (note fixup-notes (pack-code-fixup-locs result))
1108 (let ((fixup (fixup-note-fixup note)))
1109 (when (eq (fixup-flavor fixup) :card-table-index-mask)
1110 (push (fixup-note-position note) result))))))
1112 ;;; Fasl files encode <flavor,kind> in a packed integer. Dispatching on the integer
1113 ;;; is simple, but the case keys still want to be symbols.
1114 (defmacro fixup-flavor-case (flavor-id &rest clauses)
1115 (declare (notinline position))
1116 `(case ,flavor-id
1117 ,@(mapcar (lambda (clause)
1118 (if (eq (car clause) t)
1119 clause
1120 (cons (mapcar (lambda (kwd) (encoded-fixup-flavor kwd))
1121 (ensure-list (car clause)))
1122 (cdr clause))))
1123 clauses)))
1125 ;;; Dump all the fixups. The two CASE statements below check that each
1126 ;;; fixnum has a NAME of the type appropriate to the flavor.
1127 (defun dump-fixups (fixup-notes alloc-points fasl-output &aux (nelements 2))
1128 (declare (type list fixup-notes) (type fasl-output fasl-output))
1129 ;; "retained" fixups are those whose offset in the code needs to be
1130 ;; remembered for subsequent reapplication by the garbage collector,
1131 ;; or in some cases, on core startup.
1132 (dump-object (pack-fixups-for-reapplication fixup-notes) fasl-output)
1133 (dump-object alloc-points fasl-output)
1134 (dolist (note fixup-notes nelements)
1135 (let* ((fixup (fixup-note-fixup note))
1136 (name (fixup-name fixup))
1137 (flavor (fixup-flavor fixup))
1138 (flavor-id (encoded-fixup-flavor flavor))
1139 (numeric-operand
1140 (fixup-flavor-case flavor-id
1141 ((:code-object :card-table-index-mask)
1142 (the null name)
1143 1) ; avoid dumping a general operand
1144 #-sb-xc-host ; ASM routine indices aren't known to the cross-compiler
1145 (:assembly-routine
1146 (the (integer 1 *) ; must not be nonzero. 0 decodes as no numeric operand
1147 (cddr (gethash name (%asm-routine-table *assembler-routines*)))))))
1148 (info
1149 (!pack-fixup-info (fixup-note-position note) (fixup-note-kind note)
1150 flavor (or numeric-operand 0))))
1151 (dump-object info fasl-output)
1152 (if numeric-operand
1153 (incf nelements) ; used 1 element of the fasl stack
1154 (let ((operand
1155 (fixup-flavor-case flavor-id
1156 ((:alien-code-linkage-index :alien-data-linkage-index
1157 :foreign :foreign-dataref) (the string name))
1158 (:layout
1159 (if (symbolp name)
1160 name
1161 (layout-classoid-name name)))
1162 (:layout-id (the layout name))
1163 ((:assembly-routine
1164 :symbol-tls-index
1165 ;; Only #+immobile-space can use the following two flavors.
1166 ;; An :IMMOBILE-SYMBOL fixup references the symbol itself,
1167 ;; whereas a :SYMBOL-VALUE fixup references the value of the symbol.
1168 ;; In the latter case, the symbol's address doesn't matter,
1169 ;; but its global value must be an immobile object.
1170 :immobile-symbol :symbol-value)
1171 (the symbol name))
1172 (t name)))) ; function name
1173 (dump-object operand fasl-output)
1174 (incf nelements 2))))))
1176 ;;; Dump out the constant pool and code-vector for component, push the
1177 ;;; result in the table, and return the offset.
1179 ;;; The only tricky thing is handling constant-pool references to
1180 ;;; functions. If we have already dumped the function, then we just
1181 ;;; push the code pointer. Otherwise, we must create back-patching
1182 ;;; information so that the constant will be set when the function is
1183 ;;; eventually dumped. This is a bit awkward, since we don't have the
1184 ;;; handle for the code object being dumped while we are dumping its
1185 ;;; constants.
1187 ;;; We dump trap objects in any unused slots or forward referenced slots.
1188 (defun dump-code-object (component code-segment code-length fixups alloc-points fasl-output)
1189 (declare (type component component)
1190 (type index code-length)
1191 (type fasl-output fasl-output))
1192 (let* ((2comp (component-info component))
1193 (constants (ir2-component-constants 2comp))
1194 (header-length (length constants)))
1195 (collect ((patches)
1196 (named-constants))
1197 ;; Dump the constants, noting any :ENTRY constants that have to
1198 ;; be patched.
1199 (loop for i from sb-vm:code-constants-offset below header-length do
1200 (binding* ((entry (aref constants i))
1201 ((kind payload)
1202 (if (listp entry) (values (car entry) (cadr entry)))))
1203 (etypecase entry
1204 (constant
1205 (cond ((leaf-has-source-name-p entry)
1206 (named-constants (cons (leaf-source-name entry) i))
1207 (dump-fop 'fop-misc-trap fasl-output))
1209 (dump-object (constant-value entry) fasl-output))))
1210 (null
1211 (dump-fop 'fop-misc-trap fasl-output))
1212 (list
1213 (ecase kind
1214 (:entry
1215 (let* ((info (leaf-info payload))
1216 (handle (gethash info
1217 (fasl-output-entry-table fasl-output))))
1218 (declare (type entry-info info))
1219 (cond (handle (dump-push handle fasl-output))
1221 (patches (cons info i))
1222 (dump-fop 'fop-misc-trap fasl-output)))))
1223 (:load-time-value
1224 (dump-push payload fasl-output))
1225 (:fdefinition
1226 ;; It's possible for other fdefns to be found in the header not resulting
1227 ;; from IR2-CONVERT-GLOBAL-VAR, for example (L-T-V (find-or-create-fdefn ...)).
1228 ;; Those fdefns would not use FOP-FDEFN.
1229 (dump-object payload fasl-output)
1230 (dump-fop 'fop-fdefn fasl-output))
1231 (:known-fun
1232 (dump-object payload fasl-output)
1233 (dump-fop 'fop-known-fun fasl-output))
1234 (:coverage-marks
1235 ;; Avoid the coalescence done by DUMP-VECTOR
1236 (dump-specialized-vector (make-array (cdr entry)
1237 :element-type '(unsigned-byte 8)
1238 :initial-element #xFF)
1239 fasl-output)))))))
1241 ;; Dump the debug info.
1242 (let ((info (debug-info-for-component component)))
1243 (fasl-validate-structure info fasl-output)
1244 (dump-object info fasl-output)
1245 (push (dump-to-table fasl-output)
1246 (fasl-output-debug-info fasl-output)))
1248 (let ((n-fixup-elts (dump-fixups fixups alloc-points fasl-output)))
1249 (dump-fop 'fop-load-code fasl-output
1250 (logior (ash header-length 1)
1251 (if (code-immobile-p component) 1 0))
1252 code-length
1253 n-fixup-elts))
1254 ;; Fasl dumper/loader convention allows at most 3 integer args.
1255 ;; Others have to be written with explicit calls.
1256 (dump-integer-as-n-bytes (length (ir2-component-entries 2comp))
1257 4 ; output 4 bytes
1258 fasl-output)
1259 (dump-segment code-segment code-length fasl-output)
1261 (let ((handle (dump-pop fasl-output)))
1262 (dolist (patch (patches))
1263 (push (cons handle (cdr patch))
1264 (gethash (car patch)
1265 (fasl-output-patch-table fasl-output))))
1266 (dolist (named-constant (named-constants))
1267 (dump-object (car named-constant) fasl-output)
1268 (dump-push handle fasl-output)
1269 (dump-fop 'fop-named-constant-set fasl-output (cdr named-constant)))
1270 handle))))
1272 ;;; This is only called from assemfile, which doesn't exist in the target.
1273 #+sb-xc-host
1274 (defun dump-assembler-routines (code-segment octets fixups alloc-points routines file)
1275 (let ((n-fixup-elts (dump-fixups fixups alloc-points file)))
1276 ;; The name -> address table has to be created before applying fixups
1277 ;; because a fixup may refer to an entry point in the same code component.
1278 ;; So these go on the stack last, i.e. nearest the top.
1279 ;; Reversing sorts the entry points in ascending address order
1280 ;; except possibly when there are multiple entry points to one routine
1281 (unless (= (length (remove-duplicates (mapcar 'car routines)))
1282 (length routines))
1283 (error "Duplicated asm routine name"))
1284 (dolist (routine (reverse routines))
1285 (dump-object (car routine) file)
1286 (dump-integer (+ (label-position (cadr routine)) (caddr routine))
1287 file))
1288 (dump-fop 'fop-assembler-code file)
1289 (dump-word (length routines) file)
1290 (dump-word (length octets) file)
1291 (dump-word n-fixup-elts file)
1292 (write-segment-contents code-segment (fasl-output-stream file))
1293 (dump-pop file)))
1295 ;;; Alter the code object referenced by CODE-HANDLE at the specified
1296 ;;; OFFSET, storing the object referenced by ENTRY-HANDLE.
1297 (defun dump-alter-code-object (code-handle offset entry-handle file)
1298 (declare (type index code-handle entry-handle offset))
1299 (declare (type fasl-output file))
1300 (dump-push code-handle file)
1301 (dump-push entry-handle file)
1302 (dump-fop 'fop-alter-code file offset)
1303 (values))
1305 ;;; Dump the code, constants, etc. for component. We pass in the
1306 ;;; assembler fixups, code vector and node info.
1307 (defun fasl-dump-component (component code-segment code-length fixups alloc-points file)
1308 (declare (type component component))
1309 (declare (type fasl-output file))
1311 #+sb-dyncount
1312 (let ((info (ir2-component-dyncount-info (component-info component))))
1313 (when info
1314 (fasl-validate-structure info file)))
1316 (let* ((2comp (component-info component))
1317 (entries (ir2-component-entries 2comp))
1318 (nfuns (length entries))
1319 (code-handle
1320 (dump-code-object component code-segment code-length fixups
1321 alloc-points file))
1322 (fun-index nfuns))
1324 (dolist (entry entries)
1325 (dump-push code-handle file)
1326 (dump-fop 'fop-fun-entry file (decf fun-index))
1327 (let ((entry-handle (dump-pop file)))
1328 ;; When cross compiling, if the entry is a DEFUN, then we also
1329 ;; dump a FOP-FSET so that the cold loader can instantiate the
1330 ;; definition at cold-load time, allowing forward references
1331 ;; to functions in top-level forms. If the entry is a
1332 ;; DEFMETHOD, we dump a FOP-MSET so that the cold loader
1333 ;; recognizes the method definition.
1334 #+sb-xc-host
1335 (let ((name (entry-info-name entry)))
1336 (cond ((legal-fun-name-p name)
1337 (dump-object name file)
1338 (dump-push entry-handle file)
1339 (dump-fop 'fop-fset file))
1340 ((and (listp name)
1341 (eq (first name) 'sb-pcl::fast-method))
1342 (let ((method (second name))
1343 (qualifiers (butlast (cddr name)))
1344 (specializers (first (last name))))
1345 (dump-object method file)
1346 (dump-object qualifiers file)
1347 (dump-object specializers file)
1348 (dump-push entry-handle file)
1349 (dump-fop 'fop-mset file)))))
1350 (setf (gethash entry (fasl-output-entry-table file)) entry-handle)
1351 (let ((old (gethash entry (fasl-output-patch-table file))))
1352 (when old
1353 (dolist (patch old)
1354 (dump-alter-code-object (car patch)
1355 (cdr patch)
1356 entry-handle
1357 file))
1358 (remhash entry (fasl-output-patch-table file)))))))
1359 (values))
1361 ;;; Dump a FOP-FUNCALL to call an already-dumped top level lambda at
1362 ;;; load time.
1363 (defun fasl-dump-toplevel-lambda-call (fun fasl-output)
1364 (declare (type clambda fun) (type fasl-output fasl-output))
1365 (let ((handle (gethash (leaf-info fun)
1366 (fasl-output-entry-table fasl-output))))
1367 (aver handle)
1368 (dump-push handle fasl-output)
1369 (dump-fop 'fop-funcall-for-effect fasl-output 0))
1370 (values))
1372 ;;; Dump some information to allow partial reconstruction of the
1373 ;;; DEBUG-SOURCE structure.
1374 (defun fasl-dump-partial-source-info (info file)
1375 (declare (type source-info info) (type fasl-output file))
1376 (let ((partial (debug-source-for-info info)))
1377 (dump-object (debug-source-namestring partial) file)
1378 (dump-object (debug-source-created partial) file)
1379 (dump-object (debug-source-plist partial) file)
1380 (dump-fop 'fop-note-partial-source-info file)))
1382 ;;; Compute the correct list of DEBUG-SOURCE structures and backpatch
1383 ;;; all of the dumped DEBUG-INFO structures. We clear the
1384 ;;; FASL-OUTPUT-DEBUG-INFO, so that subsequent components with
1385 ;;; different source info may be dumped.
1386 (defun fasl-dump-source-info (info file)
1387 (declare (type source-info info) (type fasl-output file))
1388 (let ((res (debug-source-for-info info)))
1389 (fasl-validate-structure res file)
1390 (dump-object res file)
1391 (let ((res-handle (dump-pop file)))
1392 (dolist (info-handle (fasl-output-debug-info file))
1393 (dump-push res-handle file)
1394 (dump-fop 'fop-structset file info-handle (get-dsd-index debug-info source)))))
1396 (setf (fasl-output-debug-info file) nil)
1397 (values))
1399 ;;;; dumping structures
1401 (defun dump-structure (struct file)
1402 (unless (gethash struct (fasl-output-valid-structures file))
1403 (error "attempt to dump invalid structure:~% ~S~%How did this happen?"
1404 struct))
1405 (note-potential-circularity struct file)
1406 (do* ((length (%instance-length struct))
1407 (layout (%instance-layout struct))
1408 (bitmap (layout-bitmap layout))
1409 (circ (fasl-output-circularity-table file))
1410 (index sb-vm:instance-data-start (1+ index)))
1411 ((>= index length)
1412 (dump-non-immediate-object layout file)
1413 (dump-fop 'fop-struct file length))
1414 (let* ((obj (if (logbitp index bitmap)
1415 (%instance-ref struct index)
1416 (%raw-instance-ref/word struct index)))
1417 (ref (gethash obj circ)))
1418 (sub-dump-object (cond (ref
1419 (push (make-circularity :type :struct-set
1420 :object struct
1421 :index index
1422 :value obj
1423 :enclosing-object ref)
1424 *circularities-detected*)
1425 nil)
1426 (t obj))
1427 file))))
1429 (defun dump-layout (obj file)
1430 (when (layout-invalid obj)
1431 (compiler-error "attempt to dump reference to obsolete class: ~S"
1432 (layout-classoid obj)))
1433 ;; STANDARD-OBJECT could in theory be dumpable, but nothing else,
1434 ;; because all its subclasses can evolve to have new layouts.
1435 (aver (not (logtest (layout-flags obj) +pcl-object-layout-flag+)))
1436 (let ((name (layout-classoid-name obj)))
1437 ;; Q: Shouldn't we aver that NAME is the proper name for its classoid?
1438 (unless name
1439 (compiler-error "dumping anonymous layout: ~S" obj))
1440 ;; The target lisp can save some space in fasls (sometimes),
1441 ;; but the cross-compiler can't because we need to construct the
1442 ;; cold representation of all layouts, not reference host layouts.
1443 #-sb-xc-host
1444 (let ((fop (known-layout-fop name)))
1445 (when fop
1446 (return-from dump-layout (dump-byte fop file))))
1447 (dump-object name file))
1448 (sub-dump-object (layout-bitmap obj) file)
1449 (sub-dump-object (layout-inherits obj) file)
1450 (dump-fop 'fop-layout file
1451 (1+ (layout-depthoid obj)) ; non-stack args can't be negative
1452 (logand (layout-flags obj) sb-kernel::layout-flags-mask)
1453 (layout-length obj)))
1455 ;;;; dumping instances which just save their slots
1457 (defun dump-instance-saving-slots (object slot-names file)
1458 (note-potential-circularity object file)
1459 (let ((circ (fasl-output-circularity-table file)))
1460 (dolist (slot-name slot-names)
1461 (if (slot-boundp object slot-name)
1462 (let* ((value (slot-value object slot-name))
1463 (ref (gethash value circ)))
1464 (cond (ref
1465 (push (make-circularity :type :slot-set
1466 :object object
1467 :index 0
1468 :slot-name slot-name
1469 :value value
1470 :enclosing-object ref)
1471 *circularities-detected*)
1472 (sub-dump-object nil file))
1474 (sub-dump-object value file))))
1475 (dump-fop 'fop-misc-trap file))
1476 (sub-dump-object slot-name file)))
1477 (sub-dump-object (class-name (class-of object)) file)
1478 (dump-fop 'fop-instance file (length slot-names)))
1480 ;;;; code coverage
1482 (defun dump-code-coverage-records (cc file)
1483 (declare (type list cc))
1484 (dump-object cc file)
1485 (dump-fop 'fop-record-code-coverage file))
1487 ;;; NOTE: this is unused at present and may never have been necessary-
1488 ;;; full-calls can be inferred at load-time by tracking :LINKAGE-CELL fixups or FOP-FDEFN.
1489 (defun dump-emitted-full-calls (hash-table fasl)
1490 (let ((list (%hash-table-alist hash-table)))
1491 #+sb-xc-host ; enforce host-insensitive reproducible ordering
1492 (labels ((symbol< (a b)
1493 (cond ((string< a b) t)
1494 ((string= a b)
1495 ;; this does find a few pairs of lookalikes
1496 (string< (cl:package-name (sb-xc:symbol-package a))
1497 (cl:package-name (sb-xc:symbol-package b))))))
1498 (fname< (a b)
1499 (cond ((and (atom a) (atom b)) (symbol< a b))
1500 ((atom a) t) ; symbol < list
1501 ((atom b) nil) ; opposite
1502 ((symbol< (cadr a) (cadr b))))))
1503 (setq list (sort list #'fname< :key #'car)))
1504 (dump-object list fasl)
1505 (dump-fop 'fop-note-full-calls fasl)))