1 ;;;; stuff which is not specific to any particular build phase, but
2 ;;;; used by most of them
4 ;;;; Note: It's specifically not used when bootstrapping PCL, because
5 ;;;; we do SAVE-LISP after that, and we don't want to save extraneous
6 ;;;; bootstrapping machinery into the frozen image which will
7 ;;;; subsequently be used as the mother of all Lisp sessions.
9 ;;;; This software is part of the SBCL system. See the README file for
10 ;;;; more information.
12 ;;;; This software is derived from the CMU CL system, which was
13 ;;;; written at Carnegie Mellon University and released into the
14 ;;;; public domain. The software is in the public domain and is
15 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
16 ;;;; files for more information.
18 ;;; SB-COLD holds stuff used to build the initial SBCL core file
19 ;;; (including not only the final construction of the core file, but
20 ;;; also the preliminary steps like e.g. building the cross-compiler
21 ;;; and running the cross-compiler to produce target FASL files).
26 #+nil
; change to #+sbcl if desired, but at your own risk!
27 (when (sb-sys:find-dynamic-foreign-symbol-address
"show_gc_generation_throughput")
28 (setf (extern-alien "show_gc_generation_throughput" int
) 1))
30 #+sbcl
; prevent "illegal to redefine standard type: RATIONAL" etc
31 (when (member "SB-XC" (package-nicknames "CL") :test
'string
=)
32 (sb-ext:unlock-package
"CL")
33 (rename-package "CL" "COMMON-LISP" '("CL"))
34 (sb-ext:lock-package
"CL"))
36 (in-package "SB-COLD")
38 (defun parse-make-host-parallelism (str)
39 (multiple-value-bind (value1 end
) (parse-integer str
:junk-allowed t
)
41 (let ((value2 (if (and value1
42 (< end
(1- (length str
))) ; ~ /,[\d]+/
43 (eql (char str end
) #\
,))
44 (parse-integer str
:start
(1+ end
)))))
45 ;; If only 1 integer, assume same parallelism for both passes.
48 ;; 0 means no parallelism. 1 means use at most one subjob,
49 ;; just in case you want to test the controlling loop.
50 (when (eql value1
0) (setq value1 nil
))
51 (when (eql value2
0) (setq value2 nil
))
52 ;; Parallelism on pass 1 works only if LOAD does not compile.
53 ;; Otherwise it's slower than compiling serially.
54 ;; (And this has only been tested with sb-fasteval, not sb-eval.)
55 (cons (and (find-package "SB-INTERPRETER") value1
)
58 (defvar *make-host-parallelism
* nil
)
59 (defvar *fail-on-warnings
* t
)
60 (defun make-host-1-parallelism () (car *make-host-parallelism
*))
61 (defun make-host-2-parallelism () (cdr *make-host-parallelism
*))
65 (setq *make-host-parallelism
*
66 (let ((envvar (sb-ext:posix-getenv
"SBCL_MAKE_PARALLEL")))
69 (parse-make-host-parallelism envvar
))))
70 (defmacro with-subprocesses
(&rest body
) `(progn ,@body
))
71 (let ((f (multiple-value-bind (sym access
) (find-symbol "OS-EXIT" "SB-SYS")
72 (if (eq access
:external
) sym
'sb-unix
:unix-exit
))))
73 (defun exit-process (arg) (funcall f arg
))
74 (defun exit-subprocess (arg) (funcall f arg
)))
75 ;; Lazily reference sb-posix because it may not be loaded
76 (defun posix-fork () (funcall (intern "FORK" "HOST-SB-POSIX")))
77 (defun getpid () (funcall (intern "UNIX-GETPID" "HOST-SB-UNIX")))
78 (defun posix-wait () (funcall (intern "WAIT" "HOST-SB-POSIX"))))
82 (setq *make-host-parallelism
*
83 (let ((envvar (ext:getenv
"SBCL_MAKE_PARALLEL")))
85 (parse-make-host-parallelism envvar
))))
86 ;; FFI symbols won't exist if libffcall could not be found at build time.
87 (defmacro with-subprocesses
(&rest rest
)
88 (cons (or (find-symbol "WITH-SUBPROCESSES" "POSIX") 'progn
) rest
))
89 ;; clisp doesn't expose fork() and consequently doesn't behave
90 ;; correctly when (EXT:EXIT) is called in a forked child process.
91 (defun exit-process (arg) (ext:exit arg
))
92 #+#.
(cl:if
(cl:find-package
"FFI") '(and) '(or))
93 (progn (ffi:def-call-out exit-subprocess
(:name
"exit") (:arguments
(arg ffi
:int
))
94 (:library
:default
) (:language
:stdc
))
95 (ffi:def-call-out posix-fork
(:name
"fork") (:return-type ffi
:int
)
96 (:library
:default
) (:language
:stdc
)))
97 (defun getpid () (posix:process-id
))
99 (multiple-value-bind (pid status code
) (posix:wait
)
100 (if (eql status
:exited
)
102 (values pid
(- code
))))))
104 ;;; If TRUE, then COMPILE-FILE is being invoked only to process
105 ;;; :COMPILE-TOPLEVEL forms, not to produce an output file.
106 ;;; This is part of the implementation of parallelized make-host-2.
107 (defvar *compile-for-effect-only
* nil
)
109 ;;; prefixes for filename stems when cross-compiling. These are quite arbitrary
110 ;;; (although of course they shouldn't collide with anything we don't want to
111 ;;; write over). In particular, they can be either relative path names (e.g.
112 ;;; "host-objects/" or absolute pathnames (e.g. "/tmp/sbcl-xc-host-objects/").
114 ;;; The cross-compilation process will force the creation of these directories
115 ;;; by executing CL:ENSURE-DIRECTORIES-EXIST (on the xc host Common Lisp).
116 (defvar *host-obj-prefix
*)
117 (defvar *target-obj-prefix
*)
119 (defvar *target-obj-suffix
*
120 ;; Target fasl files are LOADed (actually only quasi-LOADed, in
121 ;; GENESIS) only by SBCL code, and it doesn't care about particular
122 ;; extensions, so we can use something arbitrary.
124 (defvar *target-assem-obj-suffix
*
125 ;; Target fasl files from SB-C:ASSEMBLE-FILE are LOADed via GENESIS.
126 ;; The source files are compiled once as assembly files and once as
127 ;; normal lisp files. In the past, they were kept separate by
128 ;; clever symlinking in the source tree, but that became less clean
129 ;; as ports to host environments without symlinks started appearing.
130 ;; In order to keep them separate, we have the assembled versions
131 ;; with a separate suffix.
134 ;;; a function of one functional argument, which calls its functional argument
135 ;;; in an environment suitable for compiling the target. (This environment
136 ;;; includes e.g. a suitable *READTABLE* that looks in SB-XC:*FEATURES*
137 ;;; when it reads #- and #+ syntax)
138 (declaim (type function
*in-target-compilation-mode-fn
*))
139 (defvar *in-target-compilation-mode-fn
*)
141 ;;; a function with the same calling convention as CL:COMPILE-FILE, to be
142 ;;; used to translate ordinary Lisp source files into target object files
143 (declaim (type function
*target-compile-file
*))
144 (defvar *target-compile-file
*)
146 ;;; designator for a function with the same calling convention as
147 ;;; SB-C:ASSEMBLE-FILE, to be used to translate assembly files into target
149 (defvar *target-assemble-file
*)
153 ;;; Take the file named X and make it into a file named Y. Sorta like
154 ;;; UNIX, and unlike Common Lisp's bare RENAME-FILE, we don't allow
155 ;;; information from the original filename to influence the final
156 ;;; filename. (The reason that it's only sorta like UNIX is that in
157 ;;; UNIX "mv foo bar/" will work, but the analogous
158 ;;; (RENAME-FILE-A-LA-UNIX "foo" "bar/") should fail.)
160 ;;; (This is a workaround for the weird behavior of Debian CMU CL
161 ;;; 2.4.6, where (RENAME-FILE "dir/x" "dir/y") tries to create a file
162 ;;; called "dir/dir/y". If that behavior goes away, then we should be
163 ;;; able to get rid of this function and use plain RENAME-FILE in the
164 ;;; COMPILE-STEM function above. -- WHN 19990321
165 (defun rename-file-a-la-unix (x y
)
167 (let ((path ;; (Note that the TRUENAME expression here is lifted from an
168 ;; example in the ANSI spec for TRUENAME.)
169 (with-open-file (stream y
:direction
:output
)
171 ;; From the ANSI spec: "In this case, the file is closed
172 ;; when the truename is tried, so the truename
173 ;; information is reliable."
176 (rename-file x path
)))
177 (compile 'rename-file-a-la-unix
)
179 (export '(*target-sbcl-version
* *generated-sources-root
*
180 *build-dependent-generated-sources-root
*
181 stem-source-path find-bootstrap-file read-from-file
))
182 (defvar *sources-root
* "")
183 (defvar *generated-sources-root
* "")
184 (defvar *build-dependent-generated-sources-root
* "")
185 (defvar *src-cold-shared-pathname
* *load-pathname
*)
187 ;;; See remark in COMPILE-STEM about strings vs. The Common Lisp Way
188 (defun find-bootstrap-file (namestring &optional build-dependent
)
189 (cond ((char= (char namestring
0) #\^
)
190 ;; If it starts with a "^" then it means "src/cold/..."
191 (let ((this *src-cold-shared-pathname
*)
192 (name (subseq namestring
1)))
193 (make-pathname :host
(pathname-host this
)
194 :device
(pathname-device this
)
195 :directory
(pathname-directory this
)
196 :name
(pathname-name name
)
197 :type
(or (pathname-type name
) (pathname-type this
)))))
198 ((find #\
/ namestring
)
199 ;; Otherwise if it contains a slash, then it's a source file which is either
200 ;; in the tree as checked in, or generated by a prior build step.
202 (if (eql (mismatch "output/" namestring
) 7) ; a generated source
204 *build-dependent-generated-sources-root
*
205 *generated-sources-root
*)
209 ;; Else, it's an optional user-supplied customization file,
210 ;; or a generated data file in the root directory such as "version.lisp-expr"
212 (compile 'find-bootstrap-file
) ; seems in vogue to compile everything in this file
214 ;;; Return an expression read from the file named NAMESTRING.
215 ;;; For user-supplied inputs, protect against more than one expression
216 ;;; appearing in the file. (Our ^build-order.lisp-expr file has more than
217 ;;; one expression in it, so we need to be able to not enforce.)
218 (defun read-from-file (namestring &key
(enforce-single-expr t
) build-dependent
)
219 (with-open-file (s (find-bootstrap-file namestring build-dependent
))
220 (let* ((result (read s
))
221 (eof-result (cons nil nil
)))
222 (unless enforce-single-expr
223 (return-from read-from-file result
))
224 (unless (eq (read s nil eof-result
) eof-result
)
225 (error "more than one expression in file ~S" namestring
))
227 (compile 'read-from-file
)
229 #+sbcl
(let ((ext (find-package "SB-EXT")))
230 ;; prevent things from working by accident when they would not work in
231 ;; ANSI lisp, e.g. ~/print-symbol-with-prefix/ (missing SB-EXT:)
232 (when (member ext
(package-use-list "CL-USER"))
233 (unuse-package ext
"CL-USER")))
237 ;; too much noise, can't see the actual warnings
238 (setq cl
:*compile-print
* nil
239 ext
:*gc-verbose
* nil
))
243 (setq cl
:*compile-print
* nil
)
244 (load (find-bootstrap-file "^muffler"))
245 ;; Let's just say we never care to see these.
246 (declaim (sb-ext:muffle-conditions
247 (satisfies unable-to-optimize-note-p
)
248 (satisfies optional
+key-style-warning-p
)
249 sb-ext
:code-deletion-note
)))
251 ;;;; special read-macros for building the cold system (and even for
252 ;;;; building some of our tools for building the cold system)
254 (load (find-bootstrap-file "^shebang"))
256 ;;; Subfeatures could be assigned as late as the beginning of make-host-2,
257 ;;; but I don't want to introduce another mechanism for delaying reading
258 ;;; of the customizer just because we can.
259 ;;; But it's not well-advertised; does it really merit a customization file?
260 (export 'backend-subfeatures
)
261 (defvar backend-subfeatures
262 (let ((customizer-file-name "customize-backend-subfeatures.lisp"))
263 (when (probe-file customizer-file-name
)
264 (copy-list (funcall (compile nil
(read-from-file customizer-file-name
)) nil
)))))
266 ;;; When cross-compiling, the *FEATURES* set for the target Lisp is
267 ;;; not in general the same as the *FEATURES* set for the host Lisp.
268 ;;; In order to refer to target features specifically, we refer to
269 ;;; SB-XC:*FEATURES* instead of CL:*FEATURES*.
271 ;;; To support building in a read-only filesystem, the 'local-target-features'
272 ;;; file might not be directly located here, since it's a generated file.
273 ;;; In as much as we use files as the means of passing parameters to
274 ;;; our Lisp scripts - because we can't in general assume that we can read
275 ;;; the command-line arguments in any Lisp - it doesn't make sense to have
276 ;;; another file specifying the name of the local-target-features file.
277 ;;; The compromise is to examine a variable specifying a path
278 ;;; (and it can't go in SB-COLD because the package is not made soon enough)
279 (setf sb-xc
:*features
*
280 (let* ((pathname (let ((var 'cl-user
::*sbcl-local-target-features-file
*))
283 "local-target-features.lisp-expr")))
285 (funcall (compile nil
(read-from-file pathname
))
286 (read-from-file "^base-target-features.lisp-expr")))
287 (customizer-file-name "customize-target-features.lisp")
288 (customizer (if (probe-file customizer-file-name
)
290 (read-from-file customizer-file-name
))
292 ;; Bind temporarily so that TARGET-FEATUREP and TARGET-PLATFORM-KEYWORD
293 ;; can see the tentative list.
294 (sb-xc:*features
* (funcall customizer default-features
))
295 (gc (intersection '(:cheneygc
:gencgc
:mark-region-gc
)
297 (arch (target-platform-keyword)))
298 (when (member :mark-region-gc sb-xc
:*features
*)
299 (setf sb-xc
:*features
* (remove :gencgc sb-xc
:*features
*)
300 gc
(remove :gencgc gc
)))
301 (unless (and gc
(not (cdr gc
)))
302 (error "Exactly 1 GC implementation needs to be selected"))
304 ;; all our GCs are generational
305 (when (member gc
'(:gencgc
:mark-region-gc
))
306 (pushnew :generational sb-xc
:*features
*))
307 (when (eq gc
:mark-region-gc
)
308 (setq sb-xc
:*features
* (remove :immobile-space sb-xc
:*features
*)))
309 ;; Win32 conditionally adds :sb-futex in grovel-features.sh
310 ;; Futexes aren't available in all macos versions, but they are available in
311 ;; all versions that support arm, so always enable them there
312 (when (target-featurep '(:and
:sb-thread
(:or
:linux
:freebsd
:openbsd
(:and
:darwin
:arm64
))))
313 (pushnew :sb-futex sb-xc
:*features
*))
314 (when (target-featurep '(:and
:sb-thread
(:or
:arm64
:x86-64
)))
315 (pushnew :system-tlabs sb-xc
:*features
*))
316 (when (target-featurep '(:and
(:or
:permgen
:immobile-space
) :x86-64
))
317 (pushnew :compact-instance-header sb-xc
:*features
*))
318 (when (target-featurep :immobile-space
)
319 (pushnew :immobile-code sb-xc
:*features
*))
320 (when (target-featurep :64-bit
)
321 (push :compact-symbol sb-xc
:*features
*))
322 (when (target-featurep :64-bit
)
323 ;; Considering that a single config file governs rv32 and rv64, I don't
324 ;; know how to make this properly configurable. In theory, 32-bit builds could
325 ;; have a salted hash (gaining 3 bits by making the hash slot raw), but
326 ;; they don't, so in light of things, this is a valid criterion.
327 (push :salted-symbol-hash sb-xc
:*features
*))
328 (when (target-featurep '(:and
:sb-thread
(:or
(:and
:darwin
(:not
(:or
:ppc
:x86
))) :openbsd
)))
329 (push :os-thread-stack sb-xc
:*features
*))
330 (when (target-featurep '(:and
:x86
:int4-breakpoints
))
331 ;; 0xCE is a perfectly good 32-bit instruction,
332 ;; unlike on x86-64 where it is illegal. It's therefore
333 ;; confusing to allow this feature in a 32-bit build.
334 ;; But it's annoying to have a build script that otherwise works
335 ;; for a native x86/x86-64 build except for needing one change.
336 ;; Just print something and go on with life.
337 (setq sb-xc
:*features
* (remove :int4-breakpoints sb-xc
:*features
*))
338 (warn "Removed :INT4-BREAKPOINTS from target features"))
339 (when (target-featurep :x86-64
)
340 (let ((int3-enable (target-featurep :int3-breakpoints
))
341 (int4-enable (target-featurep :int4-breakpoints
))
342 (ud2-enable (target-featurep :ud2-breakpoints
)))
343 (when (or ud2-enable int4-enable
)
344 (setq sb-xc
:*features
* (remove :int3-breakpoints sb-xc
:*features
*))
345 (when (and ud2-enable int4-enable
)
346 (error "UD2-BREAKPOINTS and INT4-BREAKPOINTS are mutually exclusive choices")))
347 (unless (or int3-enable int4-enable ud2-enable
)
348 ;; don't love the name, but couldn't think of a better one
349 (push :sw-int-avoidance sb-xc
:*features
*))))
350 (when (or (target-featurep :arm64
)
351 (and (target-featurep :x86-64
)
352 (member :sse4 backend-subfeatures
)))
353 (push :round-float sb-xc
:*features
*))
354 (when (target-featurep '(:and
:arm64
:darwin
))
355 (push :arm-v8.1 backend-subfeatures
))
357 ;; Putting arch and gc choice first is visually convenient, versus
358 ;; having to parse a random place in the line to figure out the value
359 ;; of a binary choice {cheney vs gencgc} and architecture.
360 ;; De-duplicate the rest of the symbols because the command line
361 ;; can add redundant --with-mumble options.
362 (list* arch gc
(sort (remove-duplicates
363 (remove arch
(remove gc sb-xc
:*features
*)))
366 ;;; Call for effect of signaling an error if no target picked.
367 (target-platform-keyword)
369 ;;; You can get all the way through make-host-1 without either one of these
370 ;;; features, but then 'bit-bash' will fail to cross-compile.
371 (unless (intersection '(:big-endian
:little-endian
) sb-xc
:*features
*)
372 (warn "You'll have bad time without either endian-ness defined"))
374 ;;; Some feature combinations simply don't work, and sometimes don't
375 ;;; fail until quite a ways into the build. Pick off the more obvious
376 ;;; combinations now, and provide a description of what the actual
377 ;;; failure is (not always obvious from when the build fails).
378 (let ((feature-compatibility-tests
379 '(("(and sb-safepoint (not sb-thread))" ":SB-SAFEPOINT requires :SB-THREAD")
380 ("(and sb-thread (not (or riscv ppc ppc64 x86 x86-64 arm64)))"
381 ":SB-THREAD not supported on selected architecture")
382 ("(and mark-region-gc (not (or x86-64 arm64)))"
383 "mark-region is not supported on selected architecture")
384 ("(and (not sb-thread) (or arm64 ppc64))"
385 "The selected architecture requires :SB-THREAD")
386 ("(and gencgc cheneygc)"
387 ":GENCGC and :CHENEYGC are incompatible")
388 ("(and sb-safepoint (not (and (or arm64 x86 x86-64) (or darwin linux win32))))"
389 ":SB-SAFEPOINT not supported on selected arch/OS")
390 ("(not (or elf mach-o win32))"
391 "No execute object file format feature defined")
392 ("(and cons-profiling (not sb-thread))" ":CONS-PROFILING requires :SB-THREAD")
393 ("(and sb-linkable-runtime (not (or arm arm64 x86 x86-64 ppc ppc64)))"
394 ":SB-LINKABLE-RUNTIME not supported on selected architecture")
395 ("(and sb-linkable-runtime (not (or darwin freebsd linux win32)))"
396 ":SB-LINKABLE-RUNTIME not supported on selected operating system")
397 ("(and sb-eval sb-fasteval)"
398 ;; It sorta kinda works to have both, but there should be no need,
399 ;; and it's not really supported.
400 "At most one interpreter can be selected")
401 ("(and compact-instance-header (not (or permgen immobile-space)))"
402 ":COMPACT-INSTANCE-HEADER requires :IMMOBILE-SPACE feature")
403 ("(and immobile-code (not immobile-space))"
404 ":IMMOBILE-CODE requires :IMMOBILE-SPACE feature")
405 ("(and immobile-symbols (not immobile-space))"
406 ":IMMOBILE-SYMBOLS requires :IMMOBILE-SPACE feature")
407 ("(and system-tlabs (not sb-thread))"
408 ":SYSTEM-TLABS requires SB-THREAD")
409 ("(and sb-futex (not sb-thread))"
410 "Can't enable SB-FUTEX on platforms lacking thread support")
411 ;; There is still hope to make multithreading on DragonFly x86-64
412 ("(and sb-thread x86 dragonfly)"
413 ":SB-THREAD not supported on selected architecture")))
414 (failed-test-descriptions nil
))
415 (dolist (test feature-compatibility-tests
)
416 (let ((*readtable
* *xc-readtable
*))
417 (when (read-from-string (concatenate 'string
"#+" (first test
) "T NIL"))
418 (push (second test
) failed-test-descriptions
))))
419 (when failed-test-descriptions
420 (error "Feature compatibility check failed, ~S"
421 (reverse failed-test-descriptions
))))
423 ;;;; cold-init-related PACKAGE and SYMBOL tools
425 ;;; Once we're done with possibly ANSIfying the COMMON-LISP package,
426 ;;; it's probably a mistake if we change it (beyond changing the
427 ;;; values of special variables such as *** and +, anyway). Set up
428 ;;; machinery to warn us when/if we change it.
430 ;;; All code depending on this is itself dependent on #+SB-SHOW.
431 (defvar *cl-snapshot
*)
432 (when (member :sb-show sb-xc
:*features
*)
433 (load (find-bootstrap-file "^snapshot"))
434 (setq *cl-snapshot
* (take-snapshot "COMMON-LISP")))
436 ;;;; master list of source files and their properties
438 ;;; flags which can be used to describe properties of source files
439 (defparameter *expected-stem-flags
*
440 '(;; meaning: This file is needed to generate C headers if doing so
441 ;; independently of make-host-1
443 ;; meaning: This file is not to be compiled when building the
444 ;; cross-compiler which runs on the host ANSI Lisp. ("not host
445 ;; code", i.e. does not execute on host -- but may still be
446 ;; cross-compiled by the host, so that it executes on the target)
448 ;; meaning: This file is not to be compiled as part of the target
449 ;; SBCL. ("not target code" -- but still presumably host code,
450 ;; used to support the cross-compilation process)
452 ;; meaning: The #'COMPILE-STEM argument :TRACE-FILE should be T.
453 ;; When the compiler is SBCL's COMPILE-FILE or something like it,
454 ;; compiling "foo.lisp" will generate "foo.trace" which contains lots
455 ;; of exciting low-level information about representation selection,
456 ;; VOPs used by the compiler, and bits of assembly.
458 ;; meaning: When cold-loading this file while producing the
459 ;; initial cold core, genesis should produce a trace file of the
460 ;; fops (fasl operations) executed.
462 ;; meaning: The #'COMPILE-STEM argument :BLOCK-COMPILE should be
463 ;; T. That is, the entire file will be block compiled. Like
464 ;; :TRACE-FILE, this applies to all COMPILE-FILEs which support
465 ;; something like :BLOCK-COMPILE.
467 ;; meaning: This file is to be processed with the SBCL assembler,
468 ;; not COMPILE-FILE. (Note that this doesn't make sense unless
469 ;; :NOT-HOST is also set, since the SBCL assembler doesn't exist
470 ;; while the cross-compiler is being built in the host ANSI Lisp.)
472 ;; meaning: ignore this flag.
473 ;; This works around nonstandard behavior of "#." in certain hosts.
474 ;; When the evaluated form yields 0 values, ECL and CLISP treat it
475 ;; as though if yielded NIL:
476 ;; * (read-from-string "#(#.(cl:if (cl:eql 1 2) x (values)))")
478 ;; The correct value for the above expression - as obtained in SBCL,
479 ;; CCL, and ABCL - is #() because _any_ reader macro is permitted
480 ;; to produce 0 values. In fact you can demonstrate this by actually
481 ;; implementing your own "#." which conditionally returns 0 values,
482 ;; and seeing that it works in any lisp including the suspect ones.
483 ;; The oft-used idiom of "#+#.(cl:if (test) '(and) '(or)) X"
484 ;; is sufficiently unclear that its worth allowing a spurious NIL
485 ;; just to avoid that ugly mess.
488 (defmacro do-stems-and-flags
((stem flags build-phase
) &body body
)
489 (let ((stem-and-flags (gensym "STEM-AND-FLAGS")))
490 `(dolist (,stem-and-flags
(get-stems-and-flags ,build-phase
))
491 (let ((,stem
(first ,stem-and-flags
))
492 (,flags
(rest ,stem-and-flags
)))
495 ;;; Given a STEM, remap the path components "/{arch}/" and "/asm-target/"
496 ;;; to suitable directories.
497 (defun stem-remap-target (stem)
498 (flet ((try-replacing (this that
)
499 (let ((position (search this stem
)))
502 (subseq stem
0 (1+ position
))
503 (string-downcase that
)
504 (subseq stem
(+ position
(length this
) -
1)))))))
505 (or (try-replacing "/{arch}/" (target-platform-keyword))
506 (try-replacing "/asm-target/" (backend-assembler-target-name))
508 (compile 'stem-remap-target
)
510 ;;; Determine the source path for a stem by remapping from the abstract name
511 ;;; if it contains "/{arch}/" and appending a ".lisp" suffix.
512 ;;; Assume that STEM is source-tree-relative unless it starts with "output/"
513 ;;; in which case it could be elsewhere, if you prefer to keep the sources
514 ;;; devoid of compilation artifacts. (The production of out-of-tree artifacts
515 ;;; is not actually implemented in the generic build, however if your build
516 ;;; system does that by itself, then hooray for you)
517 (defun stem-source-path (stem)
518 (concatenate 'string
(find-bootstrap-file (stem-remap-target stem
)) ".lisp"))
519 (compile 'stem-source-path
)
521 ;;; Determine the object path for a stem/flags/mode combination.
522 (export 'stem-object-path
)
523 (defun stem-object-path (stem flags mode
)
524 (multiple-value-bind (obj-prefix obj-suffix
)
527 ;; On some xc hosts, it's impossible to LOAD a fasl file unless it
528 ;; has the same extension that the host uses for COMPILE-FILE
529 ;; output, so we have to be careful to use the xc host's preferred
531 (values *host-obj-prefix
*
532 (concatenate 'string
"."
533 (pathname-type (compile-file-pathname stem
)))))
535 (values *target-obj-prefix
*
536 (cond ((find :assem flags
) *target-assem-obj-suffix
*)
537 (t *target-obj-suffix
*)))))
538 (concatenate 'string obj-prefix
(stem-remap-target stem
) obj-suffix
)))
539 (compile 'stem-object-path
)
541 (defvar *stems-and-flags
* nil
)
542 ;;; Read the set of files to compile with respect to a build phase, 1 or 2.
543 (defun get-stems-and-flags (build-phase)
544 (when (and *stems-and-flags
* (eql (car *stems-and-flags
*) build-phase
))
545 (return-from get-stems-and-flags
(cdr *stems-and-flags
*)))
546 (let* ((feature (aref #(:sb-xc-host
:sb-xc
) (1- build-phase
)))
548 ;; The build phase feature goes into CL:*FEATURES*, not SB-XC:*FEATURES*
549 ;; because firstly we don't use feature expressions to control the set of
550 ;; files pertinent to the build phase - that is governed by :NOT-{HOST,TARGET}
551 ;; flags, and secondly we can not assume existence of the SB-XC package in
552 ;; warm build. The sole reason for this hack is to allow testing for CMU
553 ;; as the build host in make-host-1 which apparently needs to be allowed
554 ;; to produce warnings as a bug workaround.
555 (let ((cl:*features
* (cons feature cl
:*features
*))
556 (*readtable
* *xc-readtable
*))
557 (read-from-file "^build-order.lisp-expr" :enforce-single-expr nil
))))
558 (setf *stems-and-flags
* (cons build-phase list
)))
559 ;; Now check for duplicate stems and bogus flags.
560 (let ((stems (make-hash-table :test
'equal
)))
561 (do-stems-and-flags (stem flags build-phase
)
562 ;; We do duplicate stem comparison based on the object path in
563 ;; order to cover the case of stems with an :assem flag, which
564 ;; have two entries but separate object paths for each. KLUDGE:
565 ;; We have to bind *target-obj-prefix* here because it's normally
566 ;; set up later in the build process and we don't actually care
567 ;; what it is so long as it doesn't change while we're checking
568 ;; for duplicate stems.
569 (let* ((*target-obj-prefix
* "")
570 (object-path (stem-object-path stem flags
:target-compile
)))
571 (if (gethash object-path stems
)
572 (error "duplicate stem ~S in *STEMS-AND-FLAGS*" stem
)
573 (setf (gethash object-path stems
) t
)))
574 ;; Check for stupid typos in FLAGS list keywords.
575 ;; FIXME: We should make sure that the :assem flag is only used
576 ;; when paired with :not-host.
577 (let ((set-difference (set-difference flags
*expected-stem-flags
*)))
579 (error "found unexpected flag(s) in *STEMS-AND-FLAGS*: ~S"
581 (cdr *stems-and-flags
*))
583 ;;;; tools to compile SBCL sources to create the cross-compiler
585 ;;; a wrapper for compilation/assembly, used mostly to centralize
586 ;;; the procedure for finding full filenames from "stems"
588 ;;; Compile the source file whose basic name is STEM, using some
589 ;;; standard-for-the-SBCL-build-process procedures to generate the
590 ;;; full pathnames of source file and object file. Return the pathname
591 ;;; of the object file for STEM.
593 ;;; STEM and FLAGS are as per DO-STEMS-AND-FLAGS. MODE is one of
594 ;;; :HOST-COMPILE and :TARGET-COMPILE.
595 (defun compile-stem (stem flags mode
)
596 (let* ((src (stem-source-path stem
))
597 (obj (stem-object-path stem flags mode
))
598 ;; Compile-for-effect happens simultaneously with a forked compile,
599 ;; so we need the for-effect output not to stomp on the real output.
601 (concatenate 'string obj
602 (if *compile-for-effect-only
* "-scratch" "-tmp")))
606 #+abcl
; ABCL complains about its own deficiency and then returns T
607 ;; for warnings and failure. "Unable to compile function" is not our problem,
608 ;; but I tried everything to muffle it, and nothing worked; so if it occurs,
609 ;; treat the file as a success despite any actual problems that may exist.
612 ;; Even though COMPILER-UNSUPPORTED-FEATURE-ERROR is a condition class,
613 ;; HANDLER-BIND seems unable to match it. What the hell? Bugs all the way down.
614 (handler-bind ((condition
616 (when (search "Using interpreted form" (princ-to-string c
))
617 (setq compiler-bug t
)))))
618 (multiple-value-bind (fasl warn err
) (apply #'compile-file args
)
619 (if compiler-bug
(values fasl nil nil
) (values fasl warn err
))))))
620 #+ccl
; CCL doesn't like NOTINLINE on unknown functions
622 (handler-bind ((ccl:compiler-warning
624 (when (eq (ccl::compiler-warning-warning-type c
)
625 :unknown-declaration-function
)
626 (muffle-warning c
)))))
627 (apply #'compile-file args
)))
628 #-
(or abcl ccl
) #'compile-file
)
629 (:target-compile
(if (find :assem flags
)
630 *target-assemble-file
*
631 *target-compile-file
*))))
632 (trace-file (if (find :trace-file flags
) t nil
))
633 (block-compile (if (find :block-compile flags
) t
:specified
)))
634 (declare (type function compilation-fn
))
636 (ensure-directories-exist obj
:verbose cl
:*compile-print
*) ; host's value
638 ;; We're about to set about building a new object file. First, we
639 ;; delete any preexisting object file in order to avoid confusing
640 ;; ourselves later should we happen to bail out of compilation
642 (when (and (not *compile-for-effect-only
*) (probe-file obj
))
647 ;; Work around a bug in CLISP 1999-01-08 #'COMPILE-FILE: CLISP
648 ;; mangles relative pathnames passed as :OUTPUT-FILE arguments,
649 ;; but works OK with absolute pathnames.
651 ;; following discussion on cmucl-imp 2002-07
652 ;; "COMPILE-FILE-PATHNAME", it would seem safer to deal with
653 ;; absolute pathnames all the time; it is no longer clear that the
654 ;; original behaviour in CLISP was wrong or that the current
655 ;; behaviour is right; and in any case absolutifying the pathname
656 ;; insulates us against changes of behaviour. -- CSR, 2002-08-09
658 ;; (Note that this idiom is taken from the ANSI
659 ;; documentation for TRUENAME.)
660 (with-open-file (stream tmp-obj
662 ;; Compilation would overwrite the
663 ;; temporary object anyway and overly
664 ;; strict implementations default
666 :if-exists
:supersede
)
669 ;; and some compilers (e.g. OpenMCL) will complain if they're
670 ;; asked to write over a file that exists already (and isn't
671 ;; recognizeably a fasl file), so
672 (when (probe-file tmp-obj
)
673 (delete-file tmp-obj
))
675 ;; Try to use the compiler to generate a new temporary object file.
676 (flet ((report-recompile-restart (stream)
677 (format stream
"Recompile file ~S" src
))
678 (report-continue-restart (stream)
679 (format stream
"Continue, using possibly bogus file ~S" obj
)))
682 (multiple-value-bind (output-truename warnings-p failure-p
)
684 (apply compilation-fn src
687 ;; Block compilation was
688 ;; completely broken from the
689 ;; beginning of SBCL history
690 ;; until version 2.0.2.
692 (or (eq mode
:target-compile
)
693 (and (find-symbol "SPLIT-VERSION-STRING" "HOST-SB-C")
694 (funcall (find-symbol "VERSION>=" "HOST-SB-C")
695 (funcall (find-symbol "SPLIT-VERSION-STRING" "HOST-SB-C")
696 (lisp-implementation-version))
700 ;; If tracing, also print, but don't specify :PRINT unless specifying
701 ;; :TRACE-FILE so that whatever the default is for *COMPILE-PRINT*
702 ;; prevails, insensitively to whether it's the SB-XC: or CL: symbol.
704 '(:trace-file t
:print t
)))
706 :report report-recompile-restart
708 (declare (ignore warnings-p
))
709 (cond ((not output-truename
)
710 (error "couldn't compile ~S" src
))
714 (error "FAILURE-P was set when creating ~S."
717 :report report-recompile-restart
720 :report report-continue-restart
721 (setf failure-p nil
)))
722 ;; Don't leave failed object files lying around.
723 (when (and failure-p
(probe-file tmp-obj
))
724 (delete-file tmp-obj
)
725 (format t
"~&deleted ~S~%" tmp-obj
))))
726 ;; Otherwise: success, just fall through.
729 ;; If we get to here, compilation succeeded, so it's OK to rename
730 ;; the temporary output file to the permanent object file.
731 (cond ((not *compile-for-effect-only
*)
732 (rename-file-a-la-unix tmp-obj obj
))
733 ((probe-file tmp-obj
)
734 (delete-file tmp-obj
))) ; clean up the trash
736 ;; nice friendly traditional return value
738 (compile 'compile-stem
)
740 (defparameter *host-quirks
*
741 (or #+cmu
'(:host-quirks-cmu
)
742 #+ecl
'(:host-quirks-ecl
)
743 #+ccl
'(:host-quirks-ccl
)
744 #+sbcl
'(:host-quirks-sbcl
))) ; not so much a "quirk", but consistent anyway
746 ;;; Execute function FN in an environment appropriate for compiling the
747 ;;; cross-compiler's source code in the cross-compilation host.
748 (defun in-host-compilation-mode (fn)
749 (declare (type function fn
))
750 (let ((sb-xc:*features
* (append '(:sb-xc-host
) *host-quirks
* sb-xc
:*features
*))
751 (*readtable
* *xc-readtable
*))
753 (compile 'in-host-compilation-mode
)
755 ;;; Process a file as source code for the cross-compiler, compiling it
756 ;;; (if necessary) in the appropriate environment, then loading it
757 ;;; into the cross-compilation host Common lisp.
758 (defun host-cload-stem (stem flags
)
760 (with-simple-restart (recompile "Recompile")
761 (let ((compiled-filename (in-host-compilation-mode
763 (compile-stem stem flags
:host-compile
)))))
765 (load compiled-filename
))))))
766 (compile 'host-cload-stem
)
768 ;;; like HOST-CLOAD-STEM, except that we don't bother to compile
769 (defun host-load-stem (stem flags
)
771 (with-simple-restart (recompile "Reload")
772 (return (load (stem-object-path stem flags
:host-compile
))))))
773 (compile 'host-load-stem
)
775 ;;;; tools to compile SBCL sources to create object files which will
776 ;;;; be used to create the target SBCL .core file
778 (defun lpnify-stem (stem)
779 ;; Don't want genfiles path to sneak in - avoid (STEM-SOURCE-PATH ...) here.
780 (let ((string (stem-remap-target stem
)))
781 ;; Distrust that random hosts don't bork up the translation.
782 ;; Simply replace '/' with ';' and be done.
783 (format nil
"SYS:~:@(~A~).LISP" (substitute #\
; #\/ string))))
784 (compile 'lpnify-stem
)
786 ;;; Run the cross-compiler on a file in the source directory tree to
787 ;;; produce a corresponding file in the target object directory tree.
788 (defun target-compile-stem (stem flags
)
790 (or (search "src/pcl" stem
)
791 (search "src/code/alieneval" stem
)
792 (search "src/code/arena" stem
)
793 (search "src/code/avltree" stem
)
794 (search "src/code/brothertree" stem
)
795 (search "src/code/early-classoid" stem
)
796 (search "src/code/type-class" stem
)
797 (search "src/code/class" stem
)
798 (search "src/code/debug" stem
) ; also matches debug-{info,int,var-io}
799 (search "src/code/early-defmethod" stem
)
800 (search "src/code/final" stem
)
801 (search "src/code/format" stem
)
802 (search "src/code/solist" stem
))))
803 (funcall *in-target-compilation-mode-fn
*
805 (progv (list (intern "*SOURCE-NAMESTRING*" "SB-C")
806 (intern "*FORCE-SYSTEM-TLAB*" "SB-C"))
807 (list (lpnify-stem stem
)
810 (with-simple-restart (recompile "Recompile")
811 (return (compile-stem stem flags
:target-compile
)))))))))
812 (compile 'target-compile-stem
)
814 ;;; (This function is not used by the build process, but is intended
815 ;;; for interactive use when experimenting with the system. It runs
816 ;;; the cross-compiler on test files with arbitrary filenames, not
817 ;;; necessarily in the source tree, e.g. in "/tmp".)
818 (defun target-compile-file (filename)
819 (funcall *in-target-compilation-mode-fn
*
821 (funcall *target-compile-file
* filename
))))
822 (compile 'target-compile-file
)
824 (defvar *math-ops-memoization
* (make-hash-table :test
'equal
))
825 (defun math-journal-pathname (direction)
826 ;; Initialy we read from the file in the source tree, but writeback occurs
827 ;; to a new local file. Then if re-reading we read the local copy of the cache.
828 ;; This should allow multiple builds to happen (via make-all-targets.sh) in a
829 ;; single source tree. If exactly one target is built, we can mv the local file
830 ;; on top of the source file. For more than one, we could either merge them
831 ;; or just ignore any modifications.
832 (let* ((base "xfloat-math.lisp-expr")
833 (final (concatenate 'string
"output/" base
))
834 (local (concatenate 'string
*host-obj-prefix
* base
)))
837 (:input
(if (probe-file local
) local final
))
840 (defun count-lines-of (pathname &aux
(n 0))
841 (with-open-file (f pathname
)
842 (loop (let ((line (read-line f nil
)))
843 (if line
(incf n
) (return n
))))))
845 (defmacro with-math-journal
(&body body
)
846 `(let* ((table *math-ops-memoization
*)
847 (memo (cons table
(hash-table-count table
))))
848 (assert (atom table
)) ; prevent nested use of this macro
849 (let ((*math-ops-memoization
* memo
))
851 (when nil
; *compile-verbose*
852 (funcall (intern "SHOW-INTERNED-NUMBERS" "SB-IMPL") *standard-output
*))
853 (when (> (hash-table-count table
) (cdr memo
))
854 (let ((filename (math-journal-pathname :output
)))
855 (with-open-file (stream filename
:direction
:output
856 :if-exists
:supersede
)
857 (funcall (intern "DUMP-MATH-MEMOIZATION-TABLE" "SB-IMPL")
859 ;; Enforce absence of spurious newlines from pretty-printing or whatever
860 ;; If this assertion is wrong on other lisps we can just remove it
861 (assert (= (count-lines-of filename
) (+ (hash-table-count table
) 5)))
862 (format t
"~&; Math journal: wrote ~S (~d entries)"
863 filename
(hash-table-count table
))))))
865 ;;;; One more journal file because the math file isn't enough
866 ;;; Define this before renaming the SB- packages
867 ;;; A non-parallelized compile using a sufficiently new SBCL as the host
868 ;;; will use a paravirtualized implementation of generate-perfect-hash-sexpr
869 ;;; which is to say, it just uses the host; as a side-effect it records
870 ;;; the generated string so that we can replay it for any host
871 ;;; or for parallelized build.
872 (defvar *perfect-hash-generator-mode
* :PLAYBACK
)
873 (defvar *perfect-hash-generator-memo
* nil
)
875 ;;; A separate file is used for each possible value of N-FIXNUM-BITS.
876 ;;; Therefore any particular set of symbols appears at most once per file.
877 (defun perfect-hash-generator-journal (direction)
878 (let* ((bits (symbol-value (intern "N-FIXNUM-BITS" "SB-VM")))
881 (format nil
"xperfecthash~D.lisp-expr" bits
)))))
884 (:output
(if (search "/xbuild/" *host-obj-prefix
*)
885 ;; parallel build writes to a subdirectory
886 (concatenate 'string
*target-obj-prefix
* stem
)
887 ;; normal build writes the file in place
890 (defun perfect-hash-generator-program ()
891 ;; The path depends on what the host is, not what the target is
892 #+unix
"tools-for-build/perfecthash"
893 #+win32
"tools-for-build/perfecthash.exe")
895 #+sbcl
(when (and (probe-file (perfect-hash-generator-program))
896 (find-symbol "RUN-PROGRAM" "SB-EXT"))
897 (pushnew :use-host-hash-generator cl
:*features
*)
898 (setq *perfect-hash-generator-mode
* :RECORD
))
900 ;;; I want this to work using the host-native readtable if sb-cold:*xc-readtable*
901 ;;; isn't established. The caller should bind *READTABLE* to ours if reading
902 ;;; on a non-SBCL host; it's purposely not done here.
903 (defun preload-perfect-hash-generator (pathname)
904 (with-open-file (stream pathname
:if-does-not-exist nil
)
906 (let ((entries (let ((*read-base
* 16)) (read stream
)))
907 (uniqueness-checker (make-hash-table :test
'equalp
))
910 (setq *perfect-hash-generator-memo
*
911 ;; Compute the XOR of all the hashes of each entry as a quick pass/fail
912 ;; when searching, assuming thst EQUALP compares (CAR CONS) before
913 ;; the CDR, which is almost surely, though not necessarily, what it does.
914 (mapcar (lambda (entry)
916 (destructuring-bind (array identifier expression
) entry
917 ;; ARRAY is read as simple-vector, not UB32.
918 ;; (It actually doesn't matter how it's stored in memory)
919 (setq array
(coerce array
'(simple-array (unsigned-byte 32) (*))))
920 ;; assert that the entry was stored in canonical form
921 (assert (equalp array
(sort (copy-seq array
) #'<)))
922 ;; assert that there are not redundant lines.
923 ;; (Changing the pretty-printing from C must not write
924 ;; a distinct line if its key already existed)
925 (let ((existsp (gethash array uniqueness-checker
)))
927 (setf (gethash array uniqueness-checker
)
928 (list expression linenum
)))
930 (warn "~X maps to~%~{~S from line ~D~}~%~S from line ~D~%"
931 array existsp expression linenum
)
933 (let ((digest (reduce #'logxor array
)))
934 (list* (cons digest array
) identifier expression
))))
937 (error "hash generator duplicates: ~D" errors
))))))
938 (compile 'preload-perfect-hash-generator
)
940 (defun emulate-generate-perfect-hash-sexpr (array identifier digest
)
941 (declare #-use-host-hash-generator
(ignore identifier
))
943 (declare (ignorable computed
))
944 ;; Entries are written to disk with hashes sorted in ascending order so that
945 ;; comparing as sets can be done using EQUALP.
946 ;; Sort nondestructively in case something else looks at the value as supplied.
947 (let* ((canonical-array (sort (copy-seq array
) #'<))
948 (match (assoc (cons digest canonical-array
) *perfect-hash-generator-memo
*
951 (return-from emulate-generate-perfect-hash-sexpr
(cddr match
)))
952 (ecase *perfect-hash-generator-mode
*
954 (error "perfect hash file is missing a needed entry for ~x" array
))
956 ;; This will only display anything when we didn't have the data,
957 ;; so it's actually not too "noisy" in a normal build.
958 #+use-host-hash-generator
959 (let ((output (make-string-output-stream))
961 (sb-ext:run-program
(perfect-hash-generator-program)
963 ;; win32 misbehaves with :input string-stream
964 :input
:stream
:output
:stream
967 :use-posix-spawn t
)))
968 (format (sb-ext:process-input process
) "~{~X~%~}" (coerce array
'list
))
969 (close (sb-ext:process-input process
))
970 (loop for char
= (read-char (sb-ext:process-output process
) nil
)
972 do
(write-char char output
))
973 (sb-ext:process-wait process
)
974 (sb-ext:process-close process
)
975 (unless (zerop (sb-ext:process-exit-code process
))
976 (error "Error running perfecthash: exit code ~D"
977 (sb-ext:process-exit-code process
)))
978 (let* ((string (get-output-stream-string output
))
979 ;; don't need the final newline, it looks un-lispy in the file
981 (assert (char= (char string
(1- l
)) #\newline
))
982 (setq computed
(subseq string
0 (1- l
))))
983 (let ((*print-right-margin
* 200) (*print-level
* nil
) (*print-length
* nil
))
984 (format t
"~&Recording perfect hash:~%~S~%~X~%"
986 (setf *perfect-hash-generator-memo
*
987 (nconc *perfect-hash-generator-memo
*
988 (list (list* (cons digest canonical-array
)
993 ;;; Unlike xfloat-math which expresses universal truths, the perfect-hash file
994 ;;; expresses facts about the behavior of a _particular_ SBCL revision.
995 ;;; It was overly challenging to alter the calc-symbol-name-hash algorithm
996 ;;; without being forced to re-run every cross-build to determine what the 32-bit
997 ;;; inputs would be to the perfect hash generator. By storing a representations of
998 ;;; objects that contributed to the key calculation, we can in theory recreate the
999 ;;; perfect hash file for all relevant objects without a rebuild under every
1000 ;;; combination of architectures and features. A few difficulties:
1001 ;;; * Hashes should be emitted in base 16 because the C program wants that,
1002 ;;; but our extended array syntax uses base 10 since it's more natural.
1003 ;;; i..e "#A((16) (unsigned-byte 8) ...)"
1004 ;;; * Packages have to exist when the file is read, so we can't read symbols
1005 ;;; into an architecture-specific package like sb-arm-asm.
1006 ;;; Since symbol-name-hash is based solely on print-name, it's irrelevant what
1007 ;;; the package is, so it's always OK to use keywords.
1009 ;;; I screwed this up multiple differerent ways when developing it.
1010 ;;; For example the symbol ADD was getting printed without a colon, and reparsed
1011 ;;; as 2781 in base 16.
1012 ;;; Finally I hit upon a solution which solves just about everything:
1013 ;;; write the identifying information as a string, which works around nonexistence
1014 ;;; of the following, among others:
1015 ;;; - symbol SB-KERNEL:SIMD-PACK-TYPE for #-x86-64
1016 ;;; - symbol SB-KERNEL:HANDLE-WIN32-EXCEPTION for #-win32
1017 ;;; - package SB-APROF for #-x86-64
1019 ;;; Separating the files by N-FIXNUM-TAG-BITS works to nearly guarantee
1020 ;;; that any particular set of symbols appears once and once only, so you don't
1021 ;;; have to wonder why it appears more than once, and under what circumstance
1022 ;;; the hashes should be different for the same symbols.
1023 ;;; Unfortunately, that was too aspirational. The problem stems from NIL in a
1024 ;;; list of symbols. Since the address of NIL depends on the architecture,
1025 ;;; and not only that, the particular *FEATURES*, we might have different
1026 ;;; hashes for NIL. Like without sb-thread, there will be an alloc-region
1027 ;;; placed in static space below NIL which shifts NIL's address higher,
1028 ;;; which changes its hash.
1029 (defun save-perfect-hashfuns (pathname entries
)
1030 (with-open-file (*standard-output
* pathname
1032 :if-exists
:supersede
1033 :if-does-not-exist
:create
)
1036 (let ((*print-pretty
* t
) (*print-length
* nil
) (*print-level
* nil
)
1037 (*print-lines
* nil
) (*print-right-margin
* 128))
1038 (dolist (entry entries
)
1039 (destructuring-bind ((digest . array
) identifier . string
) entry
1040 (declare (ignore digest
))
1041 (unless (stringp identifier
)
1042 ;; If this entry was read from the file, it's already a string
1044 (let ((*package
* (find-package "SB-KERNEL")))
1045 (write-to-string identifier
:escape
:t
:pretty nil
))))
1046 (format t
"(~X~% ~S~% ~S)~%" array identifier string
))))
1050 (compile 'save-perfect-hashfuns
)
1052 (defun update-perfect-hashfuns (sources destination
)
1053 (flet ((load-file (pathname)
1054 (mapcar (lambda (entry)
1055 (destructuring-bind (array identifier expression
) entry
1056 (setq array
(coerce array
'(simple-array (unsigned-byte 32) (*))))
1057 (assert (equalp array
(sort (copy-seq array
) #'<)))
1058 (let ((digest (reduce #'logxor array
)))
1059 (list* (cons digest array
) identifier expression
))))
1060 (with-open-file (stream pathname
:if-does-not-exist nil
)
1062 (let ((*read-base
* 16)) (read stream
))))))
1065 ((or (= i
(length a1
)) (= i
(length a2
)))
1067 (unless (= (aref a1 i
) (aref a2 i
))
1068 (return (< (aref a1 i
) (aref a2 i
)))))))
1069 (let ((entries (load-file destination
)))
1070 (dolist (source sources
)
1071 (dolist (entry (load-file source
))
1072 (unless (assoc (car entry
) entries
:test
#'equalp
)
1073 (push entry entries
))))
1074 (setq entries
(sort entries
#'compare
:key
#'cdar
))
1075 (save-perfect-hashfuns destination entries
))))
1077 (defun maybe-save-perfect-hashfuns-for-playback ()
1078 ;; Check again for corruption
1079 (let ((uniqueness-checker (make-hash-table :test
'equalp
)))
1080 (dolist (entry *perfect-hash-generator-memo
*)
1081 (let ((array (cdar entry
)))
1082 (assert (not (gethash array uniqueness-checker
)))
1083 (setf (gethash array uniqueness-checker
) t
))))
1084 #+use-host-hash-generator
1085 (when (eq *perfect-hash-generator-mode
* :record
)
1086 (save-perfect-hashfuns (perfect-hash-generator-journal :output
)
1087 *perfect-hash-generator-memo
*))
1090 ;;;; Please avoid writing "consecutive" (un-nested) reader conditionals
1091 ;;;; in this file, whether for the same or different feature test.
1092 ;;;; The following example prints 3 different results in 3 different lisp
1093 ;;;; implementations all of which have feature :linux (and not :nofeat).
1096 (dolist (s '("#-nofeat #+linux a b c d e"
1097 "#-nofeat #-linux a b c d e"
1098 "#+nofeat #-linux a b c d e"
1099 "#+nofeat #+linux a b c d e"
1100 "#+linux #-nofeat a b c d e"
1101 "#-linux #-nofeat a b c d e"
1102 "#-linux #+nofeat a b c d e"
1103 "#+linux #+nofeat a b c d e"
1104 "#+linux #-linux a b c d e"
1105 "#-linux #-linux a b c d e"
1106 "#-linux #+linux a b c d e"
1107 "#+linux #+linux a b c d e"
1108 "#+nofeat #-nofeat a b c d e"
1109 "#-nofeat #-nofeat a b c d e"
1110 "#-nofeat #+nofeat a b c d e"
1111 "#+nofeat #+nofeat a b c d e"))
1112 (format t
"test ~2d: ~a ~s~%"
1114 (let ((stream (make-string-input-stream s
)))
1115 (list (read stream
) (read stream
) (read stream
)))