~ chicken-core (master) /batch-driver.scm
Trap1;;;; batch-driver.scm - Driver procedure for the compiler
2;
3; Copyright (c) 2008-2022, The CHICKEN Team
4; Copyright (c) 2000-2007, Felix L. Winkelmann
5; All rights reserved.
6;
7; Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
8; conditions are met:
9;
10; Redistributions of source code must retain the above copyright notice, this list of conditions and the following
11; disclaimer.
12; Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
13; disclaimer in the documentation and/or other materials provided with the distribution.
14; Neither the name of the author nor the names of its contributors may be used to endorse or promote
15; products derived from this software without specific prior written permission.
16;
17; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
18; OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19; AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
20; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22; SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23; THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24; OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25; POSSIBILITY OF SUCH DAMAGE.
26
27
28(declare
29 (unit batch-driver)
30 (uses extras data-structures pathname expand
31 support compiler-syntax compiler optimizer internal
32 ;; TODO: Backend should be configurable
33 scrutinizer lfa2 c-platform c-backend user-pass))
34
35(module chicken.compiler.batch-driver
36 (compile-source-file)
37
38(import scheme
39 chicken.base
40 chicken.file
41 chicken.fixnum
42 chicken.format
43 chicken.gc
44 chicken.internal
45 chicken.load
46 chicken.pathname
47 chicken.platform
48 chicken.pretty-print
49 chicken.process-context
50 chicken.process-context.posix
51 chicken.string
52 chicken.syntax
53 chicken.port
54 chicken.time
55 chicken.condition
56 chicken.compiler.support
57 chicken.compiler.compiler-syntax
58 chicken.compiler.core
59 chicken.compiler.optimizer
60 chicken.compiler.scrutinizer
61 chicken.compiler.lfa2
62 chicken.compiler.c-platform
63 chicken.compiler.c-backend
64 chicken.compiler.user-pass)
65
66(include "tweaks")
67(include "mini-srfi-1.scm")
68
69(define-constant funny-message-timeout 60000)
70
71;;; Emit collected information from various statistics about the program
72
73(define (print-program-statistics db)
74 (receive
75 (size osize kvars kprocs globs sites entries) (compute-database-statistics db)
76 (when (debugging 's "program statistics:")
77 (printf "; program size: \t~s \toriginal program size: \t~s\n" size osize)
78 (printf "; variables with known values: \t~s\n" kvars)
79 (printf "; known procedures: \t~s\n" kprocs)
80 (printf "; global variables: \t~s\n" globs)
81 (printf "; known call sites: \t~s\n" sites)
82 (printf "; database entries: \t~s\n" entries) ) ) )
83
84;;; Initialize analysis database:
85;;
86;; - Simply marks the symbols directly in the plist.
87;; - Does nothing after the first invocation, but we leave it this way to
88;; have the option to add default entries for each new db.
89
90(define initialize-analysis-database
91 (let ((initial #t))
92 (lambda ()
93 (when initial
94 (for-each
95 (lambda (s)
96 (mark-variable s '##compiler#intrinsic 'standard))
97 standard-bindings)
98 (for-each
99 (lambda (s)
100 (mark-variable s '##compiler#intrinsic 'extended))
101 extended-bindings)
102 (for-each
103 (lambda (s)
104 (mark-variable s '##compiler#intrinsic 'internal))
105 internal-bindings))
106 (set! initial #f))))
107
108;;; Display analysis database:
109
110(define display-analysis-database
111 (let ((names '((captured . cpt) (assigned . set) (boxed . box) (global . glo)
112 (assigned-locally . stl)
113 (contractable . con) (standard-binding . stb) (simple . sim)
114 (inlinable . inl)
115 (collapsable . col) (removable . rem) (constant . con)
116 (inline-target . ilt) (inline-transient . itr)
117 (undefined . und) (replacing . rpg) (unused . uud) (extended-binding . xtb)
118 (inline-export . ilx) (hidden-refs . hrf)
119 (value-ref . vvf)
120 (customizable . cst) (has-unused-parameters . hup) (boxed-rest . bxr) ) )
121 (omit #f))
122 (lambda (db)
123 (unless omit
124 (set! omit
125 (append default-standard-bindings
126 default-extended-bindings
127 internal-bindings) ) )
128 (hash-table-for-each
129 (lambda (sym plist)
130 (let ((val #f)
131 (lval #f)
132 (pvals #f)
133 (csites '())
134 (refs '())
135 (derived-rvars '()))
136 (unless (memq sym omit)
137 (write sym)
138 (let loop ((es plist))
139 (if (pair? es)
140 (begin
141 (case (caar es)
142 ((captured assigned boxed global contractable standard-binding assigned-locally
143 collapsable removable undefined replacing unused simple inlinable inline-export
144 has-unused-parameters extended-binding customizable constant boxed-rest hidden-refs)
145 (printf "\t~a" (cdr (assq (caar es) names))) )
146 ((unknown)
147 (set! val 'unknown) )
148 ((value)
149 (unless (eq? val 'unknown) (set! val (cdar es))) )
150 ((local-value)
151 (unless (eq? val 'unknown) (set! lval (cdar es))) )
152 ((potential-values)
153 (set! pvals (cdar es)))
154 ((replacable home contains contained-in use-expr closure-size rest-parameter
155 captured-variables explicit-rest rest-cdr rest-null? consed-rest-arg)
156 (printf "\t~a=~s" (caar es) (cdar es)) )
157 ((derived-rest-vars)
158 (set! derived-rvars (cdar es)))
159 ((references)
160 (set! refs (cdar es)) )
161 ((call-sites)
162 (set! csites (cdar es)) )
163 (else (bomb "Illegal property" (car es))) )
164 (loop (cdr es)) ) ) )
165 (when (pair? refs) (printf "\trefs=~s" (length refs)))
166 (when (pair? derived-rvars) (printf "\tdrvars=~s" (length derived-rvars)))
167 (when (pair? csites) (printf "\tcss=~s" (length csites)))
168 (cond [(and val (not (eq? val 'unknown)))
169 (printf "\tval=~s" (cons (node-class val) (node-parameters val))) ]
170 [(and lval (not (eq? val 'unknown)))
171 (printf "\tlval=~s" (cons (node-class lval) (node-parameters lval)))])
172 (when (pair? pvals)
173 (for-each
174 (lambda (pval)
175 (printf "\tpval=~s" (cons (node-class pval) (node-parameters pval))))
176 pvals))
177 (newline) ) ) )
178 db) ) ) )
179
180;;; Compile a complete source file:
181
182(define (compile-source-file filename user-supplied-options . options)
183 (define (option-arg p)
184 (if (null? (cdr p))
185 (quit-compiling "missing argument to `-~A' option" (car p))
186 (let ([arg (cadr p)])
187 (if (symbol? arg)
188 (quit-compiling "invalid argument to `~A' option" arg)
189 arg) ) ) )
190 (initialize-compiler)
191 (set! explicit-use-flag (memq 'explicit-use options))
192 (set! emit-debug-info (memq 'debug-info options))
193 (when (memq 'module-registration options)
194 (set! compile-module-registration 'yes))
195 (when (memq 'no-module-registration options)
196 (set! compile-module-registration 'no))
197 (when (memq 'static options)
198 (set! static-extensions #t)
199 (register-feature! 'chicken-compile-static))
200 (let* ((dynamic (memq 'dynamic options))
201 (unit (memq 'unit options))
202 (init-forms `((##core#declare
203 ,@(append
204 default-declarations
205 (if emit-debug-info
206 '((uses debugger-client))
207 '())
208 (if explicit-use-flag
209 '()
210 `((uses ,@default-units)))
211 (if (and static-extensions
212 (not dynamic)
213 (not unit)
214 (not explicit-use-flag)
215 (or (not compile-module-registration)
216 (eq? compile-module-registration 'yes)))
217 '((uses eval-modules))
218 '())))))
219 (import-forms `((import-for-syntax ,@default-syntax-imports)
220 ,@(if explicit-use-flag
221 '()
222 `((import-syntax ,@default-imports)))))
223 (cleanup-forms '(((chicken.base#implicit-exit-handler))))
224 (outfile (cond ((memq 'output-file options)
225 => (lambda (node)
226 (let ((oname (option-arg node)))
227 (if (symbol? oname)
228 (symbol->string oname)
229 oname) ) ) )
230 ((memq 'to-stdout options) #f)
231 (else (make-pathname #f (if filename (pathname-file filename) "out") "c")) ) )
232 ;; Create a temporary file to receive the C code, so that it
233 ;; can atomically be renamed to the actual output file after
234 ;; the C generation.
235 (tmp-outfile (and outfile
236 (conc outfile ".tmp." (current-process-id) (current-seconds))))
237 (opasses (default-optimization-passes))
238 (time0 #f)
239 (time-breakdown #f)
240 (forms '())
241 (inline-output-file #f)
242 (profile (or (memq 'profile options)
243 (memq 'accumulate-profile options)
244 (memq 'profile-name options)))
245 (profile-name
246 (and-let* ((pn (memq 'profile-name options))) (cadr pn)))
247 (hsize (memq 'heap-size options))
248 (kwstyle (memq 'keyword-style options))
249 (loop/dispatch (memq 'clustering options))
250 (a-only (memq 'analyze-only options))
251 (do-scrutinize #t)
252 (do-lfa2 (memq 'lfa2 options))
253 (dumpnodes #f)
254 (start-time #f)
255 (upap #f)
256 (ssize (or (memq 'nursery options) (memq 'stack-size options)))
257 (module-name
258 (and-let* ((m (memq 'module options)))
259 (option-arg m))))
260
261 (define (cputime) (current-process-milliseconds))
262
263 (define (dribble fstr . args)
264 (debugging 'p (apply sprintf fstr args)))
265
266 (define (print-header mode dbgmode)
267 (debugging 'p "pass" mode)
268 (and (memq dbgmode debugging-chicken)
269 (begin
270 (printf "[~a]~%" mode)
271 #t) ) )
272
273 (define (print-node mode dbgmode n)
274 (when (print-header mode dbgmode)
275 (if dumpnodes
276 (dump-nodes n)
277 (pretty-print (build-expression-tree n)) ) ) )
278
279 (define (print-db mode dbgmode db pass)
280 (when (print-header mode dbgmode)
281 (printf "(iteration ~s)~%" pass)
282 (display-analysis-database db) ) )
283
284 (define (print-expr mode dbgmode xs)
285 (when (print-header mode dbgmode)
286 (for-each
287 (lambda (x)
288 (pretty-print x)
289 (newline))
290 xs) ) )
291
292 (define (string-trim str)
293 (let loop ((front 0)
294 (back (string-length str)))
295 (cond ((= front back) "")
296 ((char-whitespace? (string-ref str front))
297 (loop (add1 front) back))
298 ((char-whitespace? (string-ref str (sub1 back)))
299 (loop front (sub1 back)))
300 (else (substring str front back)))))
301
302 (define (string->extension-name str)
303 (let ((str (string-trim str)))
304 (if (and (positive? (string-length str))
305 (char=? #\( (string-ref str 0)))
306 (handle-exceptions ex
307 (##sys#error "invalid import specification" str)
308 (with-input-from-string str read))
309 (string->symbol str))))
310
311 (define (arg-val str)
312 (let* ((len (string-length str))
313 (len1 (- len 1)) )
314 (or (if (< len 2)
315 (string->number str)
316 (case (string-ref str len1)
317 ((#\m #\M) (* (string->number (substring str 0 len1)) (* 1024 1024)))
318 ((#\k #\K) (* (string->number (substring str 0 len1)) 1024))
319 (else (string->number str)) ) )
320 (quit-compiling "invalid numeric argument ~S" str) ) ) )
321
322 (define (collect-options opt)
323 (let loop ([opts options])
324 (cond [(memq opt opts) => (lambda (p) (cons (option-arg p) (loop (cddr p))))]
325 [else '()] ) ) )
326
327 (define (begin-time)
328 (when time-breakdown (set! time0 (cputime))) )
329
330 (define (end-time pass)
331 (when time-breakdown
332 (printf "milliseconds needed for ~a: \t~s~%"
333 pass
334 (inexact->exact (round (- (cputime) time0)) ) )))
335
336 (define (analyze pass node . args)
337 (let-optionals args ((no 0) (contf #t))
338 (let ((db (analyze-expression node)))
339 (when upap
340 (upap pass db node
341 (cut db-get db <> <>)
342 (cut db-put! db <> <> <>)
343 no contf) )
344 db) ) )
345
346 (define (chop-separator str)
347 (let ((len (sub1 (string-length str))))
348 (if (and (> len 0)
349 (memq (string-ref str len) '(#\\ #\/)))
350 (substring str 0 len)
351 str) ) )
352
353 (when unit
354 (set! unit-name (string->symbol (option-arg unit))))
355 (when (or unit-name dynamic)
356 (set! standalone-executable #f))
357 (when (memq 'ignore-repository options)
358 (set! ##sys#dload-disabled #t)
359 (repository-path #f))
360 (set! enable-specialization (memq 'specialize options))
361 (set! debugging-chicken
362 (append-map
363 (lambda (do)
364 (map (lambda (c) (string->symbol (string c)))
365 (string->list do) ) )
366 (collect-options 'debug) ) )
367 (when (memq 'h debugging-chicken)
368 (print-debug-options)
369 (exit))
370 (set! dumpnodes (memq '|D| debugging-chicken))
371 (set! import-libraries
372 (map (lambda (il)
373 (cons (string->symbol il)
374 (string-append il ".import.scm")))
375 (collect-options 'emit-import-library)))
376 (when (and (memq 'emit-all-import-libraries options)
377 (not a-only))
378 (set! all-import-libraries #t))
379 (when enable-specialization
380 (set! do-scrutinize #t))
381 (when (memq 't debugging-chicken) (##sys#start-timer))
382 (when (memq 'b debugging-chicken) (set! time-breakdown #t))
383 (when (memq 'raw options)
384 (set! explicit-use-flag #t)
385 (set! init-forms '())
386 (set! import-forms '())
387 (set! cleanup-forms '()))
388 (when (memq 'no-lambda-info options)
389 (set! emit-closure-info #f) )
390 (when (memq 'no-compiler-syntax options)
391 (set! compiler-syntax-enabled #f))
392 (when (memq 'local options)
393 (set! local-definitions #t))
394 (when (memq 'inline-global options)
395 (set! enable-inline-files #t)
396 (set! inline-locally #t))
397 (when (memq 'verbose options)
398 (set! verbose-mode #t)
399 (set! ##sys#notices-enabled #t))
400 (when (memq 'strict-types options)
401 (set! strict-variable-types #t)
402 (set! enable-specialization #t))
403 (when (memq 'no-warnings options)
404 (dribble "Warnings are disabled")
405 (set! ##sys#warnings-enabled #f)
406 (set! do-scrutinize #f)) ; saves some processing time
407 (when (memq 'optimize-leaf-routines options) (set! optimize-leaf-routines #t))
408 (when (memq 'unsafe options)
409 (set! unsafe #t) )
410 (when (memq 'setup-mode options)
411 (set! ##sys#setup-mode #t))
412 (when (memq 'regenerate-import-libraries options)
413 (set! preserve-unchanged-import-libraries #f))
414 (when (memq 'disable-interrupts options) (set! insert-timer-checks #f))
415 (when (memq 'fixnum-arithmetic options) (set! number-type 'fixnum))
416 (when (memq 'block options) (set! block-compilation #t))
417 (when (memq 'emit-external-prototypes-first options)
418 (set! external-protos-first #t))
419 (when (memq 'inline options) (set! inline-locally #t))
420 (and-let* ((elf (memq 'emit-link-file options)))
421 (set! emit-link-file (option-arg elf)))
422 (and-let* ((ifile (memq 'emit-inline-file options)))
423 (set! inline-locally #t) ; otherwise this option makes no sense
424 (set! local-definitions #t)
425 (set! inline-output-file (option-arg ifile)))
426 (and-let* ((tfile (memq 'emit-types-file options)))
427 (set! types-output-file (option-arg tfile)))
428 (and-let* ([inlimit (memq 'inline-limit options)])
429 (set! inline-max-size
430 (let ([arg (option-arg inlimit)])
431 (or (string->number arg)
432 (quit-compiling
433 "invalid argument to `-inline-limit' option: `~A'" arg) ) ) ) )
434 (and-let* ((ulimit (memq 'unroll-limit options)))
435 (set! unroll-limit
436 (let ((arg (option-arg ulimit)))
437 (or (string->number arg)
438 (quit-compiling
439 "invalid argument to `-unroll-limit' option: `~A'" arg) ) ) ) )
440 (when (memq 'case-insensitive options)
441 (dribble "Identifiers and symbols are case insensitive")
442 (register-feature! 'case-insensitive)
443 (case-sensitive #f) )
444 (when kwstyle
445 (let ([val (option-arg kwstyle)])
446 (cond [(string=? "prefix" val) (keyword-style #:prefix)]
447 [(string=? "none" val) (keyword-style #:none)]
448 [(string=? "suffix" val) (keyword-style #:suffix)]
449 [else (quit-compiling
450 "invalid argument to `-keyword-style' option")] ) ) )
451 (when (memq 'no-parentheses-synonyms options)
452 (dribble "Disabled support for parentheses synonyms")
453 (parentheses-synonyms #f) )
454 (when (memq 'r7rs-syntax options)
455 (dribble "Disabled the CHICKEN extensions to R7RS syntax")
456 (case-sensitive #f)
457 (keyword-style #:none)
458 (parentheses-synonyms #f))
459 (set! ##sys#read-error-with-line-number #t)
460 (set! ##sys#include-pathnames
461 (append (map chop-separator (collect-options 'include-path))
462 ##sys#include-pathnames) )
463 (when (and outfile filename (string=? outfile filename))
464 (quit-compiling "source- and output-filename are the same") )
465 (when (memq 'keep-shadowed-macros options)
466 (set! undefine-shadowed-macros #f) )
467 (when (memq 'no-argc-checks options)
468 (set! no-argc-checks #t) )
469 (when (memq 'no-bound-checks options)
470 (set! no-bound-checks #t) )
471 (when (memq 'no-procedure-checks options)
472 (set! no-procedure-checks #t) )
473 (when (memq 'no-procedure-checks-for-toplevel-bindings options)
474 (set! no-global-procedure-checks #t) )
475 (when (memq 'no-procedure-checks-for-usual-bindings options)
476 (for-each
477 (lambda (v)
478 (mark-variable v '##compiler#always-bound-to-procedure)
479 (mark-variable v '##compiler#always-bound) )
480 default-standard-bindings)
481 (for-each
482 (lambda (v)
483 (mark-variable v '##compiler#always-bound-to-procedure)
484 (mark-variable v '##compiler#always-bound) )
485 default-extended-bindings) )
486 (when (memq 'p debugging-chicken) (load-verbose #t))
487
488 ;; Handle feature options:
489 (for-each
490 register-feature!
491 (append-map (cut string-split <> ", ") (collect-options 'feature)))
492 (for-each
493 unregister-feature!
494 (append-map (cut string-split <> ",") (collect-options 'no-feature)))
495
496 ;; Load extensions:
497 (set! ##sys#features (cons #:compiler-extension ##sys#features))
498 (let ([extends (collect-options 'extend)])
499 (dribble "Loading compiler extensions...")
500 (for-each
501 (lambda (e)
502 (let ((f (##sys#resolve-include-filename e #f #t #f)))
503 (when (not f) (quit-compiling "cannot load extension: ~a" e))
504 (load f)))
505 extends) )
506 (set! ##sys#features (delete #:compiler-extension ##sys#features eq?))
507 (set! ##sys#features (cons '#:compiling ##sys#features))
508 (set! upap (user-post-analysis-pass))
509
510 ;; Handle units added with the "-uses" flag.
511 (let ((uses (append-map
512 (lambda (u) (map string->symbol (string-split u ", ")))
513 (collect-options 'uses))))
514 (unless (null? uses)
515 (set! init-forms
516 (append init-forms `((##core#declare (uses . ,uses)))))))
517
518 ;; Mark linked libraries so they will be compiled as unit dependencies.
519 (let ((link (append-map
520 (lambda (l) (map string->symbol (string-split l ", ")))
521 (collect-options 'link))))
522 (set! linked-libraries (lset-union/eq? linked-libraries link)))
523
524 ;; Append required extensions to imports:
525 (set! import-forms
526 (append
527 import-forms
528 (map (lambda (r) `(import ,(string->extension-name r)))
529 (collect-options 'require-extension))))
530
531 (when (memq 'compile-syntax options)
532 (set! ##sys#enable-runtime-macros #t) )
533 (set! target-heap-size
534 (and hsize
535 (arg-val (option-arg hsize))))
536 (set! target-stack-size
537 (and ssize
538 (arg-val (option-arg ssize))))
539 (set! emit-trace-info (not (memq 'no-trace options)))
540 (set! disable-stack-overflow-checking (memq 'disable-stack-overflow-checks options))
541 (set! bootstrap-mode (feature? #:chicken-bootstrap))
542 (when (memq 'm debugging-chicken) (set-gc-report! #t))
543 (cond ((memq 'no-usual-integrations options)
544 (set! do-scrutinize #f))
545 (else
546 (set! standard-bindings default-standard-bindings)
547 (set! extended-bindings default-extended-bindings) ))
548 (dribble "debugging info: ~A"
549 (if emit-trace-info
550 "calltrace"
551 "none") )
552 (when profile
553 (let ((acc (eq? 'accumulate-profile (car profile))))
554 (when (and acc (not profile-name))
555 (quit-compiling
556 "you need to specify -profile-name if using accumulated profiling runs"))
557 (set! emit-profile #t)
558 (set! profiled-procedures 'all)
559 (set! init-forms
560 (append
561 init-forms
562 default-profiling-declarations
563 (if acc
564 '((set! ##sys#profile-append-mode #t))
565 '() ) ) )
566 (dribble "generating ~aprofiled code" (if acc "accumulative " "")) ))
567
568 ;;XXX hardcoded "modules.db" is bad (also used in chicken-install.scm)
569 (load-identifier-database "modules.db")
570
571 (cond ((memq 'version options)
572 (print-version #t)
573 (newline) )
574 ((or (memq 'help options) (memq '-help options) (memq 'h options) (memq '-h options))
575 (print-usage))
576 ((memq 'release options)
577 (display (chicken-version))
578 (newline) )
579 ((not filename)
580 (print-version #t)
581 (display "\nEnter `chicken -help' for information on how to use the compiler,\n")
582 (display "or try `csc' for a more convenient interface.\n")
583 (display "\nRun `csi' to start the interactive interpreter.\n"))
584 (else
585
586 ;; Display header:
587 (dribble "compiling `~a' ..." filename)
588 (debugging 'r "options" options)
589 (debugging 'r "debugging options" debugging-chicken)
590 (debugging 'r "target heap size" target-heap-size)
591 (debugging 'r "target stack size" target-stack-size)
592 (set! start-time (cputime))
593
594 ;; Read toplevel expressions:
595 (set! ##sys#line-number-database (make-vector line-number-database-size '()))
596 (let ([prelude (collect-options 'prelude)]
597 [postlude (collect-options 'postlude)]
598 [files (append
599 (collect-options 'prologue)
600 (list filename)
601 (collect-options 'epilogue) ) ] )
602
603 (let ([proc (user-read-pass)])
604 (cond [proc
605 (dribble "User read pass...")
606 (set! forms (proc prelude files postlude)) ]
607 [else
608 (do ([files files (cdr files)])
609 ((null? files)
610 (set! forms
611 (append (map string->expr prelude)
612 (reverse forms)
613 (map string->expr postlude) ) ) )
614 (let* ((f (car files))
615 (in (check-and-open-input-file f)) )
616 (fluid-let ((##sys#current-source-filename f))
617 (let loop ()
618 (let ((x (chicken.syntax#read-with-source-info in))) ; OBSOLETE - after bootstrapping we can get rid of this explicit namespacing
619
620 (cond ((eof-object? x)
621 (close-checked-input-file in f) )
622 (else
623 (set! forms (cons x forms))
624 (loop)))))))) ] ) ) )
625
626 ;; Start compilation passes:
627 (let ([proc (user-preprocessor-pass)])
628 (when proc
629 (dribble "User preprocessing pass...")
630 (set! forms (map proc forms))))
631
632 (print-expr "source" '|1| forms)
633 (begin-time)
634 ;; Canonicalize s-expressions
635 (let* ((init0 (map canonicalize-expression init-forms))
636 (exps0 (map (lambda (x)
637 (fluid-let ((##sys#current-source-filename filename))
638 (canonicalize-expression x)))
639 (let ((forms (append import-forms forms)))
640 (if (not module-name)
641 forms
642 `((##core#module
643 ,(string->symbol module-name) ()
644 ,@forms))))))
645 (uses0 (map (lambda (u)
646 (canonicalize-expression `(##core#require ,u)))
647 (##sys#fast-reverse used-libraries)))
648 (exps (append
649 (map (lambda (ic) `(set! ,(cdr ic) ',(car ic))) immutable-constants)
650 init0
651 uses0
652 (if unit-name `((##core#provide ,unit-name)) '())
653 (if emit-profile
654 (profiling-prelude-exps (and (not unit-name)
655 (or profile-name #t)))
656 '() )
657 exps0
658 (if standalone-executable
659 cleanup-forms
660 '((##core#undefined))))))
661
662 (unless (null? import-libraries)
663 (quit-compiling
664 "No module definition found for import libraries to emit: ~A"
665 ;; ~S would be confusing: separate with a comma
666 (string-intersperse
667 (map (lambda (il) (->string (car il)))
668 import-libraries) ", ")))
669
670 (when (pair? compiler-syntax-statistics)
671 (with-debugging-output
672 'S
673 (lambda ()
674 (print "applied compiler syntax:")
675 (for-each
676 (lambda (cs) (printf " ~a\t\t~a~%" (car cs) (cdr cs)))
677 compiler-syntax-statistics))))
678 (when (debugging '|N| "real name table:")
679 (display-real-name-table) )
680 (when (debugging 'n "line number database:")
681 (##sys#display-line-number-database) )
682
683 (set! ##sys#line-number-database line-number-database-2)
684 (set! line-number-database-2 #f)
685
686 (end-time "canonicalization")
687 (print-expr "canonicalized" '|2| exps)
688
689 (when (memq 'check-syntax options) (exit))
690
691 ;; User-defined pass (s-expressions)
692 (let ([proc (user-pass)])
693 (when proc
694 (dribble "User pass...")
695 (begin-time)
696 (set! exps (map proc exps))
697 (end-time "user pass") ) )
698
699 ;; Convert s-expressions to node tree
700 (let ((node0 (build-toplevel-procedure
701 (build-node-graph
702 (canonicalize-begin-body exps))))
703 (db #f))
704 (print-node "initial node tree" '|T| node0)
705 (initialize-analysis-database)
706
707 ;; collect requirements and load inline files
708 (let ((extensions required-extensions))
709 (when enable-inline-files
710 (for-each
711 (lambda (id)
712 (and-let* ((ifile (##sys#resolve-include-filename
713 (symbol->string id) '(".inline") #t #f)))
714 (dribble "Loading inline file ~a ..." ifile)
715 (load-inline-file ifile)))
716 extensions))
717 (let ((ifs (collect-options 'consult-inline-file)))
718 (unless (null? ifs)
719 (set! inline-locally #t)
720 (for-each
721 (lambda (ilf)
722 (dribble "Loading inline file ~a ..." ilf)
723 (load-inline-file ilf) )
724 ifs)))
725 ;; Perform scrutiny and optionally specialization
726 (when (or do-scrutinize enable-specialization)
727 ;;XXX hardcoded database file name
728 (unless (memq 'ignore-repository options)
729 (unless (load-type-database "types.db"
730 enable-specialization)
731 (quit-compiling
732 "default type-database `types.db' not found")))
733 (for-each
734 (lambda (fn)
735 (or (load-type-database fn enable-specialization #f)
736 (quit-compiling "type-database `~a' not found" fn)))
737 (collect-options 'consult-types-file))
738 (for-each
739 (lambda (id)
740 (load-type-database
741 (make-pathname #f (symbol->string id) "types")
742 enable-specialization))
743 extensions)
744 (begin-time)
745 (set! first-analysis #f)
746 (set! db (analyze 'scrutiny node0))
747 (print-db "analysis" '|0| db 0)
748 (end-time "pre-analysis (scrutiny)")
749 (begin-time)
750 (debugging 'p "performing scrutiny")
751 (scrutinize node0 db
752 do-scrutinize enable-specialization
753 strict-variable-types block-compilation)
754 (end-time "scrutiny")
755 (when enable-specialization
756 (print-node "specialization" '|P| node0))
757 (set! first-analysis #t) ) )
758
759 ;; TODO: Move this so that we don't need to export these
760 (set! ##sys#line-number-database #f)
761 (set! constant-table #f)
762 (set! inline-table #f)
763 ;; Analyze toplevel assignments
764 (unless unsafe
765 (scan-toplevel-assignments (first (node-subexpressions node0))) )
766
767 (begin-time)
768 ;; Convert to CPS
769 (let ([node1 (perform-cps-conversion node0)])
770 (end-time "cps conversion")
771 (print-node "cps" '|3| node1)
772
773 ;; Optimization loop:
774 (let loop ((i 1)
775 (node2 node1)
776 (progress #t)
777 (l/d #f)
778 (l/d-done #f))
779 (begin-time)
780 ;; Analyze node tree for optimization
781 (let ([db (analyze 'opt node2 i progress)])
782 (when first-analysis
783 (when (memq 'u debugging-chicken)
784 (dump-undefined-globals db))
785 (when (memq 'd debugging-chicken)
786 (dump-defined-globals db))
787 (when (memq 'v debugging-chicken)
788 (dump-global-refs db))
789 ;; do this here, because we must make sure we have a db
790 (and-let* ((tfile (or (and (eq? types-output-file #t)
791 (pathname-replace-extension filename "types"))
792 (and (string? types-output-file)
793 types-output-file))))
794 (dribble "generating type file `~a' ..." tfile)
795 (emit-types-file filename tfile db block-compilation)))
796 (set! first-analysis #f)
797 (end-time "analysis")
798 (print-db "analysis" '|4| db i)
799
800 (when (memq 's debugging-chicken)
801 (print-program-statistics db))
802
803 ;; Optimize (once)
804 (cond (progress
805 (debugging 'p "optimization pass" i)
806 (begin-time)
807 (receive (node2 progress-flag)
808 (if l/d
809 (determine-loop-and-dispatch node2 db)
810 (perform-high-level-optimizations
811 node2 db block-compilation
812 inline-locally inline-max-size
813 unroll-limit
814 inline-substitutions-enabled))
815 (end-time "optimization")
816 (print-node "optimized-iteration" '|5| node2)
817 (cond (progress-flag
818 (loop (add1 i) node2 #t #f l/d))
819 ((and (not l/d-done) loop/dispatch)
820 (debugging 'p "clustering enabled")
821 (loop (add1 i) node2 #t #t #t))
822 ((not inline-substitutions-enabled)
823 (debugging 'p "rewritings enabled")
824 (set! inline-substitutions-enabled #t)
825 (loop (add1 i) node2 #t #f l/d-done) )
826 (optimize-leaf-routines
827 (begin-time)
828 (let ([db (analyze 'leaf node2)])
829 (end-time "analysis")
830 (begin-time)
831 (let ((progress
832 (transform-direct-lambdas! node2 db)))
833 (end-time "leaf routine optimization")
834 (loop (add1 i)
835 node2
836 progress
837 #f
838 l/d-done) ) ) )
839 (else
840 (loop (add1 i) node2 #f #f l/d-done)) ) ) )
841
842 (else
843 ;; Secondary flow-analysis
844 (when do-lfa2
845 (begin-time)
846 (debugging 'p "doing lfa2")
847 (let ((floatvars (perform-secondary-flow-analysis node2 db)))
848 (end-time "secondary flow analysis")
849 (unless (null? floatvars)
850 (begin-time)
851 (debugging 'p "doing unboxing")
852 (set! node2 (perform-unboxing node2 floatvars)))
853 (end-time "unboxing")))
854 (print-node "optimized" '|7| node2)
855 ;; inlining into a file with interrupts enabled would
856 ;; change semantics
857 (when (and inline-output-file insert-timer-checks)
858 (let ((f inline-output-file))
859 (dribble "generating global inline file `~a' ..." f)
860 (emit-global-inline-file
861 filename f db block-compilation
862 inline-max-size
863 (map foreign-stub-id foreign-lambda-stubs)) ) )
864 (begin-time)
865 ;; Closure conversion
866 (set! node2 (perform-closure-conversion node2 db))
867 (end-time "closure conversion")
868 (print-db "final-analysis" '|8| db i)
869 (when (and ##sys#warnings-enabled
870 (> (- (cputime) start-time) funny-message-timeout))
871 (display "(don't worry - still compiling...)\n") )
872 (print-node "closure-converted" '|9| node2)
873 (when a-only (exit 0))
874 (begin-time)
875 ;; Preparation
876 (receive (node literals lliterals lambda-table dbg-info)
877 (prepare-for-code-generation node2 db)
878 (end-time "preparation")
879 (begin-time)
880
881 ;; generate link file
882 (when emit-link-file
883 (let ((exts required-extensions))
884 (dribble "generating link file `~a' ..." emit-link-file)
885 (with-output-to-file emit-link-file (cut pp exts))))
886
887 ;; Code generation
888 (let ((out (if tmp-outfile
889 (open-output-file tmp-outfile)
890 (current-output-port))) )
891 (when tmp-outfile
892 (dribble "generating `~A' ..." tmp-outfile))
893 (generate-code literals lliterals lambda-table out filename
894 user-supplied-options dynamic db dbg-info)
895 (when tmp-outfile
896 (close-output-port out)
897 (rename-file tmp-outfile outfile #t)))
898 (end-time "code generation")
899 (when (memq 't debugging-chicken)
900 (##sys#display-times (##sys#stop-timer)))
901 (compiler-cleanup-hook)
902 (dribble "compilation finished.") ) ) ) ) ) ) ) ) ) ) ) )
903)