;;; compile.ss ;;; Copyright 1984-2017 Cisco Systems, Inc. ;;; ;;; Licensed under the Apache License, Version 2.0 (the "License"); ;;; you may not use this file except in compliance with the License. ;;; You may obtain a copy of the License at ;;; ;;; http://www.apache.org/licenses/LICENSE-2.0 ;;; ;;; Unless required by applicable law or agreed to in writing, software ;;; distributed under the License is distributed on an "AS IS" BASIS, ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ;;; See the License for the specific language governing permissions and ;;; limitations under the License. ;;; use fixnum arithmetic in code building & output routines (let () (import (nanopass)) (include "types.ss") (include "base-lang.ss") (include "expand-lang.ss") ; for tracing: #;(define-syntax do-trace (syntax-rules () ((_ . r) (trace-output . r)))) ; no tracing: (define-syntax do-trace (syntax-rules () ((_ . r) r))) (define trace-output (lambda (fun . args) (when ($assembly-output) (fprintf ($assembly-output) "~s ====>~%" ($procedure-name fun))) (let ([x (apply fun args)]) (when ($assembly-output) (parameterize ([print-graph #t]) (pretty-print x ($assembly-output)) (newline ($assembly-output)))) x))) (define cheat? (lambda (x) (nanopass-case (Lsrc Expr) x [,pr #t] [(quote ,d) #t] [(if ,e0 ,e1 ,e2) (and (cheat? e0) (cheat? e1) (cheat? e2))] [(seq ,e1 ,e2) (and (cheat? e1) (cheat? e2))] [(call ,preinfo ,e ,e* ...) (and (andmap cheat? e*) (cheat? e))] [else #f]))) (define cheat-eval (rec compile (lambda (x) (nanopass-case (Lsrc Expr) x [,pr ($top-level-value (primref-name pr))] [(quote ,d) d] [(if ,e0 ,e1 ,e2) (compile (if (compile e0) e1 e2))] [(seq ,e1 ,e2) (compile e1) (compile e2)] [(call ,preinfo ,e ,e* ...) (#2%apply (compile e) (map compile e*))] [else ($oops #f "unexpected form ~s" x)])))) (define c-compile (lambda (x) (with-output-language (Lsrc Expr) ($c-make-closure ; pretending main is a library routine to avoid argument-count check (let ([x `(case-lambda ,(make-preinfo-lambda #f #f (lookup-libspec main)) (clause () 0 ,x))]) ($np-compile x #f)))))) (define c-set-code-quad! (foreign-procedure "(cs)s_set_code_quad" (scheme-object scheme-object scheme-object) void)) (define lookup-c-entry-index (foreign-procedure "(cs)lookup_c_entry" (iptr) scheme-object)) (define-who (c-mkcode x) (define (mkcode x) (record-case x [(object) (x) x] [(entry) (i) (lookup-c-entry-index i)] [(library) (x) ($lookup-library-entry (libspec-index x) #t)] [(library-code) (x) ($closure-code ($lookup-library-entry (libspec-index x) #t))] [(closure) func ; call mkcode on code record first or we might set func-closure field multiple times (let ([cp (mkcode ($c-func-code-record func))]) ; i.e., the remainder must be atomic wrt mkcode (or ($c-func-closure func) (let ([p ($make-closure (constant code-data-disp) cp)]) (set-$c-func-closure! func p) p)))] [(code) (func subtype free name arity-mask size code-list info pinfo*) (or ($c-func-code-object func) (let ([p ($make-code-object subtype free name arity-mask size info pinfo*)]) (set-$c-func-code-object! func p) (let mkc0 ([c* code-list] [a (constant code-data-disp)] [r* '()] [ra 0] [x* '()]) (if (null? c*) ($make-relocation-table! p (reverse r*) (reverse x*)) (let ([c (car c*)]) (record-case c [(word) n ($set-code-word! p a n) (mkc0 (cdr c*) (fx+ a 2) r* ra x*)] [(byte) n ($set-code-byte! p a n) (mkc0 (cdr c*) (fx+ a 1) r* ra x*)] [(long) n ($set-code-long! p a n) (mkc0 (cdr c*) (fx+ a 4) r* ra x*)] [(quad) n ($set-code-quad! p a n) (mkc0 (cdr c*) (fx+ a 8) r* ra x*)] [(code-top-link) () (constant-case ptr-bits [(64) ($set-code-quad! p a a) (mkc0 (cdr c*) (fx+ a 8) r* ra x*)] [(32) ($set-code-long! p a a) (mkc0 (cdr c*) (fx+ a 4) r* ra x*)])] [(abs) (n x) (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-abs) n (fx- a ra))]) (constant-case ptr-bits [(64) (mkc0 (cdr c*) (fx+ a 8) (cons r r*) a x*)] [(32) (mkc0 (cdr c*) (fx+ a 4) (cons r r*) a x*)])))] [else (constant-case architecture [(x86) (record-case c [(rel) (n x) (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-rel) n (fx- a ra))]) (mkc0 (cdr c*) (fx+ a 4) (cons r r*) a x*)))] [else (c-assembler-output-error c)])] [(arm32) (record-case c [(arm32-abs) (n x) ; on ARMV7 would be 8: 4-byte movi, 4-byte movt (let ([a1 (fx- a 12)]) ; 4-byte ldr, 4-byte bra, 4-byte value (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-arm32-abs) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [(arm32-call) (n x) ; on ARMV7 would be 12: 4-byte movi, 4-byte movt, 4-byte blx (let ([a1 (fx- a 16)]) ; 4-byte ldr, 4-byte bra, 4-byte value, 4-byte blx (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-arm32-call) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [(arm32-jump) (n x) ; on ARMV7 would be 12: 4-byte movi, 4-byte movt, 4-byte bx (let ([a1 (fx- a 16)]) ; 4-byte ldr, 4-byte bra, 4-byte value, 4-byte bx (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-arm32-jump) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [else (c-assembler-output-error c)])] [(ppc32) (record-case c [(ppc32-abs) (n x) (let ([a1 (fx- a 8)]) (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-ppc32-abs) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [(ppc32-call) (n x) (let ([a1 (fx- a 16)]) (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-ppc32-call) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [(ppc32-jump) (n x) (let ([a1 (fx- a 16)]) (let ([x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-ppc32-jump) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*))))] [else (c-assembler-output-error c)])] [(x86_64) (record-case c [(x86_64-jump) (n x) (let ([a1 (fx- a 12)] [x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-x86_64-jump) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*)))] [(x86_64-call) (n x) (let ([a1 (fx- a 12)] [x* (cons (mkcode x) x*)]) (let ([r ($reloc (constant reloc-x86_64-call) n (fx- a1 ra))]) (mkc0 (cdr c*) a (cons r r*) a1 x*)))] [else (c-assembler-output-error c)])] [else (c-assembler-output-error c)])])))) p))] [else (c-assembler-output-error x)])) ; rationale for the critical section: ; (1) the code objects we create here may be mutually recursive, and we ; need for them all to be in the same generation. ; (2) code objects are created without relocation tables, and linked ; after relocation tables are added, potentially confusing the ; collector. this could be addressed by maintaining a LINKED flag ; in the code-object header. ; (3) we record code modifications as code objects are allocated, then ; flush once at the end to avoid multiple flushes. ; rationale for the dynamic-wind: ; we have to flush the instruction cache even if mkcode errors out or is ; interrupted with a noncontinuable interrupt so that no code modifications ; are recorded for code objects that have been dropped and for which the ; memory containing them has been returned to the O/S. (critical-section (dynamic-wind void (lambda () (mkcode x)) $flush-instruction-cache))) (define c-build-fasl (lambda (x t a?) (let build ([x x]) (record-case x [(object) (x) ($fasl-enter x t a?)] [(closure) func ($fasl-bld-graph x t a? (lambda (x t a?) (build ($c-func-code-record func))))] [(code) stuff ($fasl-bld-graph x t a? (lambda (x t a?) (record-case x [(code) (func subtype free name arity-mask size code-list info pinfo*) ($fasl-enter name t a?) ($fasl-enter arity-mask t a?) ($fasl-enter info t a?) ($fasl-enter pinfo* t a?) (for-each (lambda (x) (record-case x [(abs) (n x) (build x)] [else (constant-case architecture [(x86) (record-case x [(rel) (n x) (build x)] [else (void)])] [(x86_64) (record-case x [(x86_64-jump x86_64-call) (n x) (build x)] [else (void)])] [(arm32) (record-case x [(arm32-abs arm32-call arm32-jump) (n x) (build x)] [else (void)])] [(ppc32) (record-case x [(ppc32-abs ppc32-call ppc32-jump) (n x) (build x)] [else (void)])])])) code-list)])))])))) (include "fasl-helpers.ss") (define c-assembler-output-error (lambda (x) ($oops 'compile-internal "invalid assembler output ~s" x))) (define (c-faslobj x t p a?) (let faslobj ([x x]) (record-case x [(object) (x) ($fasl-out x p t a?)] [(entry) (i) (put-u8 p (constant fasl-type-entry)) (put-uptr p i)] [(library) (x) (put-u8 p (constant fasl-type-library)) (put-uptr p (libspec-index x))] [(library-code) (x) (put-u8 p (constant fasl-type-library-code)) (put-uptr p (libspec-index x))] [(closure) func ($fasl-wrf-graph x p t a? (lambda (x p t a?) (put-u8 p (constant fasl-type-closure)) (put-uptr p (constant code-data-disp)) (faslobj ($c-func-code-record func))))] [(code) (func subtype free name arity-mask size code-list info pinfo*) ($fasl-wrf-graph x p t a? (lambda (x p t a?) (put-u8 p (constant fasl-type-code)) (put-u8 p subtype) (put-uptr p free) (put-uptr p size) ($fasl-out name p t a?) ($fasl-out arity-mask p t a?) ($fasl-out info p t a?) ($fasl-out pinfo* p t a?) (let prf0 ([c* code-list] [a (constant code-data-disp)] [r* '()] [ra 0] [x* '()]) (if (null? c*) (begin (let ([actual-size (- a (constant code-data-disp))]) (unless (= actual-size size) ($oops 'c-faslcode "wrote ~s bytes, expected ~s bytes" actual-size size))) (put-uptr p (fold-left (lambda (m r) (fx+ m (if (reloc-long? r) 3 1))) 0 r*)) (for-each (lambda (r x) (let ([item-offset (reloc-item-offset r)]) (put-u8 p (let* ([k (fxsll (reloc-type r) 2)] [k (if (eqv? item-offset 0) k (fxlogor k 2))]) (if (reloc-long? r) (fxlogor k 1) k))) (put-uptr p (reloc-code-offset r)) (unless (eqv? item-offset 0) (put-uptr p item-offset)) (faslobj x))) (reverse r*) (reverse x*))) (let ([c (car c*)]) (record-case c [(word) n (put16 p n) (prf0 (cdr c*) (fx+ a 2) r* ra x*)] [(byte) n (put8 p n) (prf0 (cdr c*) (fx+ a 1) r* ra x*)] [(long) n (put32 p n) (prf0 (cdr c*) (fx+ a 4) r* ra x*)] [(quad) n (put64 p n) (prf0 (cdr c*) (fx+ a 8) r* ra x*)] [(code-top-link) () (constant-case ptr-bits [(64) (put64 p a) (prf0 (cdr c*) (fx+ a 8) r* ra x*)] [(32) (put32 p a) (prf0 (cdr c*) (fx+ a 4) r* ra x*)])] [(abs) (n x) (let ([r ($reloc (constant reloc-abs) n (fx- a ra))]) (constant-case ptr-bits [(64) (put64 p 0) (prf0 (cdr c*) (fx+ a 8) (cons r r*) a (cons x x*))] [(32) (put32 p 0) (prf0 (cdr c*) (fx+ a 4) (cons r r*) a (cons x x*))]))] [else (constant-case architecture [(x86) (record-case c [(rel) (n x) (put32 p 0) (let ([r ($reloc (constant reloc-rel) n (fx- a ra))]) (prf0 (cdr c*) (fx+ a 4) (cons r r*) a (cons x x*)))] [else (c-assembler-output-error c)])] [(arm32) (record-case c [(arm32-abs) (n x) ; on ARMV7 would be 8: 4-byte movi, 4-byte movt (let ([a1 (fx- a 12)]) ; 4-byte ldr, 4-byte bra, 4-byte value (let ([r ($reloc (constant reloc-arm32-abs) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [(arm32-call) (n x) ; on ARMV7 would be 12: 4-byte movi, 4-byte movt, 4-byte blx (let ([a1 (fx- a 16)]) ; 4-byte ldr, 4-byte bra, 4-byte value, 4-byte blx (let ([r ($reloc (constant reloc-arm32-call) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [(arm32-jump) (n x) ; on ARMV7 would be 12: 4-byte movi, 4-byte movt, 4-byte bx (let ([a1 (fx- a 16)]) ; 4-byte ldr, 4-byte bra, 4-byte value, 4-byte bx (let ([r ($reloc (constant reloc-arm32-jump) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [else (c-assembler-output-error c)])] [(ppc32) (record-case c [(ppc32-abs) (n x) (let ([a1 (fx- a 8)]) (let ([r ($reloc (constant reloc-ppc32-abs) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [(ppc32-call) (n x) (let ([a1 (fx- a 16)]) (let ([r ($reloc (constant reloc-ppc32-call) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [(ppc32-jump) (n x) (let ([a1 (fx- a 16)]) (let ([r ($reloc (constant reloc-ppc32-jump) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [else (c-assembler-output-error c)])] [(x86_64) (record-case c [(x86_64-jump) (n x) (let ([a1 (fx- a 12)]) ; 10-byte moviq followed by 2-byte jmp (let ([r ($reloc (constant reloc-x86_64-jump) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [(x86_64-call) (n x) (let ([a1 (fx- a 12)]) ; 10-byte moviq followed by 2-byte call (let ([r ($reloc (constant reloc-x86_64-call) n (fx- a1 ra))]) (prf0 (cdr c*) a (cons r r*) a1 (cons x x*))))] [else (c-assembler-output-error c)])] [else (c-assembler-output-error c)])]))))))] [else (c-assembler-output-error x)]))) (define (c-print-fasl x p situation) (let ([t ($fasl-table)] [a? (let ([flags (fxlogor (if (generate-inspector-information) (constant annotation-debug) 0) (if (eq? ($compile-profile) 'source) (constant annotation-profile) 0))]) (and (not (fx= flags 0)) flags))]) (c-build-fasl x t a?) ($fasl-start p t situation (lambda (p) (c-faslobj x t p a?))))) (define-record-type visit-chunk (nongenerative) (fields chunk)) (define-record-type revisit-chunk (nongenerative) (fields chunk)) (define-who (host-machine-type) (let ([m (machine-type)]) (let lookup ([ra* (constant machine-type-alist)]) (if (null? ra*) ($oops who "unrecognized machine type ~s" m) (if (eq? (cdar ra*) m) (caar ra*) (lookup (cdr ra*))))))) (define with-whacked-optimization-locs (lambda (x1 th) (define ht (make-eq-hashtable)) (define-pass whack! : Lexpand (ir f) -> * () (Outer : Outer (ir) -> * () [,inner (Inner ir)] [(group ,[] ,[]) (values)] [(visit-only ,[]) (values)] [(revisit-only ,[]) (values)] [else (values)]) (Inner : Inner (ir) -> * () [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) (for-each f db*) (values)] [else (values)])) (whack! x1 (lambda (db) (when db (eq-hashtable-set! ht db (unbox db)) (set-box! db '())))) (th) (whack! x1 (lambda (db) (when db (set-box! db (eq-hashtable-ref ht db '()))))))) (define check-prelex-flags (lambda (x after) (when ($enable-check-prelex-flags) ($pass-time 'cpcheck-prelex-flags (lambda () (do-trace $cpcheck-prelex-flags x after)))))) (define compile-file-help (lambda (op hostop wpoop source-table machine sfd do-read outfn) (parameterize ([$target-machine machine] [$sfd sfd] [$current-mso ($current-mso)] [$block-counter 0] [optimize-level (optimize-level)] [debug-level (debug-level)] [run-cp0 (run-cp0)] [cp0-effort-limit (cp0-effort-limit)] [cp0-score-limit (cp0-score-limit)] [cp0-outer-unroll-limit (cp0-outer-unroll-limit)] [generate-inspector-information (generate-inspector-information)] [generate-procedure-source-information (generate-procedure-source-information)] [$compile-profile ($compile-profile)] [generate-interrupt-trap (generate-interrupt-trap)] [$optimize-closures ($optimize-closures)] [enable-cross-library-optimization (enable-cross-library-optimization)] [generate-covin-files (generate-covin-files)]) (emit-header op (constant scheme-version) (constant machine-type)) (when hostop (emit-header hostop (constant scheme-version) (host-machine-type))) (when wpoop (emit-header wpoop (constant scheme-version) (host-machine-type))) (let cfh0 ([n 1] [rrcinfo** '()] [rlpinfo** '()] [rfinal** '()]) (let ([x0 ($pass-time 'read do-read)]) (if (eof-object? x0) (compile-file-help2 op (reverse rrcinfo**) (reverse rlpinfo**) (reverse rfinal**)) (let () (define source-info-string (and (or ($assembly-output) (expand-output) (expand/optimize-output)) (with-output-to-string (lambda () (printf "expression #~s" n) (when (and (annotation? x0) (fxlogtest (annotation-flags x0) (constant annotation-debug))) (let ((s (annotation-source x0))) (call-with-values (lambda () ((current-locate-source-object-source) s #t #t)) (case-lambda [() (void)] [(path line char) (printf " on line ~s" line)])))))))) (when ($assembly-output) (when source-info-string (fprintf ($assembly-output) "~%;; ~a\n" source-info-string)) (parameterize ([print-graph #t]) (pretty-print (if (annotation? x0) (annotation-stripped x0) x0) ($assembly-output))) (flush-output-port ($assembly-output))) (let ([x1 ($pass-time 'expand (lambda () (expand x0 (if (eq? (subset-mode) 'system) ($system-environment) (interaction-environment)) #t #t outfn)))]) (check-prelex-flags x1 'expand) ($uncprep x1 #t) ; populate preinfo sexpr fields (check-prelex-flags x1 'uncprep) (when source-table ($insert-profile-src! source-table x1)) (when wpoop ; cross-library optimization locs might be set by cp0 during the expander's compile-time ; evaluation of library forms. since we have no need for the optimization information in ; the wpo file, we temporarily whack the optimization locs while writing the wpo file. (with-whacked-optimization-locs x1 (lambda () ($with-fasl-target (host-machine-type) (lambda () (parameterize ([$target-machine (machine-type)]) (let ([t ($fasl-table)]) ($fasl-enter x1 t (constant annotation-all)) ($fasl-start wpoop t (constant fasl-type-visit-revisit) (lambda (p) ($fasl-out x1 p t (constant annotation-all))))))))))) (let-values ([(rcinfo* lpinfo* final*) (compile-file-help1 x1 source-info-string)]) (when hostop ; the host library file contains expander output possibly augmented with ; cross-library optimization information inserted by cp0. this write must come ; after cp0, at least, so that cp0 has a chance to insert that information. ($with-fasl-target (host-machine-type) (lambda () (parameterize ([$target-machine (machine-type)]) (let ([t ($fasl-table)]) ($fasl-enter x1 t (constant annotation-all)) ($fasl-start hostop t (constant fasl-type-visit-revisit) (lambda (p) ($fasl-out x1 p t (constant annotation-all))))))))) (cfh0 (+ n 1) (cons rcinfo* rrcinfo**) (cons lpinfo* rlpinfo**) (cons final* rfinal**))))))))))) (define library/program-info? (lambda (x) (or (program-info? x) (library-info? x)))) (define-who compile-file-help1 (lambda (x1 source-info-string) (define-who expand-Lexpand (lambda (e) ; we might want to export expand-Inner from syntax.ss instead of $build-install-library/ct-code ; and $build-install-library/rt-code (define-pass expand-Inner : Lexpand (ir) -> Lexpand () (Inner : Inner (ir) -> Inner () [,lsrc lsrc] ; NB: workaround for nanopass tag snafu [(program ,uid ,body) ($build-invoke-program uid body)] [(library/ct ,uid (,export-id* ...) ,import-code ,visit-code) ($build-install-library/ct-code uid export-id* import-code visit-code)] [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) ($build-install-library/rt-code uid dl* db* dv* de* body)] [else ir])) (with-output-language (Lsrc Expr) (define (lambda-chunk lsrc) ; pretending main is a library routine to avoid argument-count check `(case-lambda ,(make-preinfo-lambda #f #f (lookup-libspec main)) (clause () 0 ,lsrc))) (define (visit lsrc e* rchunk*) (define (rchunks) (cons (make-visit-chunk (lambda-chunk lsrc)) rchunk*)) (if (null? e*) (rchunks) (let f ([e (car e*)] [e* (cdr e*)]) (nanopass-case (Lexpand Outer) e [(group ,outer1 ,outer2) (f outer1 (cons outer2 e*))] [(visit-only ,lsrc2) (visit `(seq ,lsrc ,lsrc2) e* rchunk*)] [else (common e e* (rchunks))])))) (define (revisit lsrc e* rchunk*) (define (rchunks) (cons (make-revisit-chunk (lambda-chunk lsrc)) rchunk*)) (if (null? e*) (rchunks) (let f ([e (car e*)] [e* (cdr e*)]) (nanopass-case (Lexpand Outer) e [(group ,outer1 ,outer2) (f outer1 (cons outer2 e*))] [(revisit-only ,lsrc2) (revisit `(seq ,lsrc ,lsrc2) e* rchunk*)] [else (common e e* (rchunks))])))) (define (visit-revisit lsrc e* rchunk*) (define (rchunks) (cons (lambda-chunk lsrc) rchunk*)) (if (null? e*) (rchunks) (let f ([e (car e*)] [e* (cdr e*)]) (nanopass-case (Lexpand Outer) e [(group ,outer1 ,outer2) (f outer1 (cons outer2 e*))] [,lsrc2 (visit-revisit `(seq ,lsrc ,lsrc2) e* rchunk*)] [else (common e e* (rchunks))])))) (define (unwrap-inner e) (nanopass-case (Lexpand Inner) e [(library/ct-info ,linfo/ct) linfo/ct] [(library/rt-info ,linfo/rt) linfo/rt] [(program-info ,pinfo) pinfo] [else e])) (define (common e e* rchunk*) (nanopass-case (Lexpand Outer) e [(visit-only ,lsrc) (visit lsrc e* rchunk*)] [(revisit-only ,lsrc) (revisit lsrc e* rchunk*)] [,lsrc (visit-revisit lsrc e* rchunk*)] [else (let ([rchunk* (cons (nanopass-case (Lexpand Outer) e [(visit-only ,inner) (make-visit-chunk (unwrap-inner inner))] [(revisit-only ,inner) (make-revisit-chunk (unwrap-inner inner))] [(recompile-info ,rcinfo) rcinfo] [,inner (unwrap-inner inner)] [else (sorry! who "unexpected Outer ~s" e)]) rchunk*)]) (if (null? e*) rchunk* (start (car e*) (cdr e*) rchunk*)))])) (define (start e e* rchunk*) (nanopass-case (Lexpand Outer) e [(group ,outer1 ,outer2) (start outer1 (cons outer2 e*) rchunk*)] [else (common e e* rchunk*)])) (reverse (start (expand-Inner e) '() '()))))) (when (expand-output) (when source-info-string (fprintf (expand-output) "~%;; expand output for ~a\n" source-info-string)) (pretty-print ($uncprep x1) (expand-output)) (flush-output-port (expand-output))) (let loop ([chunk* (expand-Lexpand x1)] [rx2b* '()] [rfinal* '()] [rlpinfo* '()] [rrcinfo* '()]) (if (null? chunk*) (begin (when (expand/optimize-output) (when source-info-string (fprintf (expand/optimize-output) "~%;; expand/optimize output for ~a\n" source-info-string)) (let ([e* (map (lambda (x2b) (define (finish x2b) ($uncprep (cond [(recompile-info? x2b) (with-output-language (Lexpand Outer) `(recompile-info ,x2b))] [(library/ct-info? x2b) (with-output-language (Lexpand Inner) `(library/ct-info ,x2b))] [(library/rt-info? x2b) (with-output-language (Lexpand Inner) `(library/rt-info ,x2b))] [(program-info? x2b) (with-output-language (Lexpand Inner) `(program-info ,x2b))] [else (nanopass-case (Lsrc Expr) x2b [(case-lambda ,preinfo (clause () ,interface ,body)) body] [else (sorry! 'compile-file-help "unexpected optimizer output ~s" x2b)])]))) (if (pair? x2b) (case (car x2b) [(visit-stuff) `(eval-when (visit) ,(finish (cdr x2b)))] [(revisit-stuff) `(eval-when (revisit) ,(finish (cdr x2b)))] [else (sorry! who "unrecognized stuff ~s" x2b)]) (finish x2b))) rx2b*)]) (pretty-print (if (fx= (length e*) 1) (car e*) `(begin ,@(reverse e*))) (expand/optimize-output)) (flush-output-port (expand/optimize-output)))) (values (reverse rrcinfo*) (reverse rlpinfo*) (reverse rfinal*))) (let ([x1 (car chunk*)] [chunk* (cdr chunk*)]) (define finish-compile (lambda (x1 f) (if (library/program-info? x1) (loop chunk* (cons (f x1) rx2b*) rfinal* (cons (f `(object ,x1)) rlpinfo*) rrcinfo*) (let* ([waste (check-prelex-flags x1 'before-cpvalid)] [x2 ($pass-time 'cpvalid (lambda () (do-trace $cpvalid x1)))] [waste (check-prelex-flags x2 'cpvalid)] [x2a (let ([cpletrec-ran? #f]) (let ([x ((run-cp0) (lambda (x) (set! cpletrec-ran? #t) (let* ([x ($pass-time 'cp0 (lambda () (do-trace $cp0 x)))] [waste (check-prelex-flags x 'cp0)] [x ($pass-time 'cpletrec (lambda () (do-trace $cpletrec x)))] [waste (check-prelex-flags x 'cpletrec)]) x)) x2)]) (if cpletrec-ran? x (let ([x ($pass-time 'cpletrec (lambda () (do-trace $cpletrec x)))]) (check-prelex-flags x 'cpletrec) x))))] [x2b ($pass-time 'cpcheck (lambda () (do-trace $cpcheck x2a)))] [waste (check-prelex-flags x2b 'cpcheck)] [x2b ($pass-time 'cpcommonize (lambda () (do-trace $cpcommonize x2b)))] [waste (check-prelex-flags x2b 'cpcommonize)] [x7 (do-trace $np-compile x2b #t)] [x8 ($c-make-closure x7)]) (loop chunk* (cons (f x2b) rx2b*) (cons (f x8) rfinal*) rlpinfo* rrcinfo*))))) (cond [(recompile-info? x1) (loop chunk* (cons x1 rx2b*) rfinal* rlpinfo* (cons x1 rrcinfo*))] [(visit-chunk? x1) (finish-compile (visit-chunk-chunk x1) (lambda (x) `(visit-stuff . ,x)))] [(revisit-chunk? x1) (finish-compile (revisit-chunk-chunk x1) (lambda (x) `(revisit-stuff . ,x)))] [else (finish-compile x1 values)])))))) (define compile-file-help2 (lambda (op rcinfo** lpinfo** final**) (define (libreq-hash x) (symbol-hash (libreq-uid x))) (define (libreq=? x y) (eq? (libreq-uid x) (libreq-uid y))) (let ([import-ht (make-hashtable libreq-hash libreq=?)] [include-ht (make-hashtable string-hash string=?)]) (for-each (lambda (rcinfo*) (for-each (lambda (rcinfo) (for-each (lambda (x) (hashtable-set! import-ht x #t)) (recompile-info-import-req* rcinfo)) (for-each (lambda (x) (hashtable-set! include-ht x #t)) (recompile-info-include-req* rcinfo))) rcinfo*)) rcinfo**) (let ([import-req* (vector->list (hashtable-keys import-ht))] [include-req* (vector->list (hashtable-keys include-ht))]) ; the first entry is always a recompile-info record with recompile information for the entire object file ($pass-time 'pfasl (lambda () (c-print-fasl `(object ,(make-recompile-info import-req* include-req*)) op (constant fasl-type-visit-revisit)) (for-each (lambda (final*) (for-each (lambda (x) (record-case x [(visit-stuff) x (c-print-fasl x op (constant fasl-type-visit))] [(revisit-stuff) x (c-print-fasl x op (constant fasl-type-revisit))] [else (c-print-fasl x op (constant fasl-type-visit-revisit))])) final*)) ; inserting #t after lpinfo as an end-of-header marker (append lpinfo** (cons (list `(object #t)) final**))))))))) (define (new-extension new-ext fn) (let ([old-ext (path-extension fn)]) (format "~a.~a" (if (or (string=? old-ext "") (string=? old-ext new-ext)) fn (path-root fn)) new-ext))) (module (with-object-file with-host-file with-wpo-file with-coverage-file) (define call-with-port/cleanup (lambda (ofn op p) (on-reset (delete-file ofn #f) (on-reset (close-port op) (p op)) (close-port op)))) (define with-object-file (lambda (who ofn p) (call-with-port/cleanup ofn ($open-file-output-port who ofn (file-options replace)) p))) (define with-host-file (lambda (who ofn p) (if ofn (call-with-port/cleanup ofn ($open-file-output-port who ofn (file-options replace)) p) (p #f)))) (define with-wpo-file (lambda (who ofn p) (if (generate-wpo-files) (let ([ofn (new-extension "wpo" ofn)]) (call-with-port/cleanup ofn ($open-file-output-port who ofn (file-options replace)) p)) (p #f)))) (define with-coverage-file (lambda (who ofn p) (if (generate-covin-files) (let ([ofn (new-extension "covin" ofn)]) (call-with-port/cleanup ofn ($open-file-output-port who ofn (file-options compressed replace) (buffer-mode block) (current-transcoder)) (lambda (op) (let ([source-table (make-source-table)]) (p source-table) (put-source-table op source-table))))) (p #f))))) (set! $compile-host-library (lambda (who iofn) (let ([ip ($open-file-input-port who iofn)]) (on-reset (close-port ip) (let loop ([rx1* '()] [rcinfo* '()] [rother* '()]) (let ([x1 (fasl-read ip)]) (cond [(eof-object? x1) (close-port ip) (unless (null? rx1*) (unless (null? rother*) ($oops 'compile-library "unexpected value ~s read from file ~s that also contains ~s" (car rother*) iofn (car rx1*))) (with-object-file who iofn (lambda (op) (emit-header op (constant scheme-version) (constant machine-type)) (let loop ([x1* (reverse rx1*)] [rrcinfo** (list rcinfo*)] [rlpinfo** '()] [rfinal** '()]) (if (null? x1*) (compile-file-help2 op (reverse rrcinfo**) (reverse rlpinfo**) (reverse rfinal**)) (let-values ([(rcinfo* lpinfo* final*) (let ([x1 (car x1*)]) (if (recompile-info? x1) (values (list x1) '() '()) (compile-file-help1 (car x1*) "host library")))]) (loop (cdr x1*) (cons rcinfo* rrcinfo**) (cons lpinfo* rlpinfo**) (cons final* rfinal**))))))))] [(recompile-info? x1) (loop rx1* (cons x1 rcinfo*) rother*)] [(Lexpand? x1) (loop (cons x1 rx1*) rcinfo* rother*)] [else (loop rx1* rcinfo* (cons x1 rother*))]))))))) (let () (define-record-type node (nongenerative) (fields (mutable depend*) (mutable use-count)) (protocol (lambda (new) (lambda () (new #f 0))))) (define-record-type program-node (nongenerative) (sealed #t) (parent node) (fields pinfo (mutable ir)) (protocol (lambda (pargs->new) (lambda (pinfo) ((pargs->new) pinfo #f))))) (define program-node-uid (lambda (node) (program-info-uid (program-node-pinfo node)))) (define program-node-invoke-req* (lambda (node) (program-info-invoke-req* (program-node-pinfo node)))) (define-record-type library-node (nongenerative) (parent node) (fields binary? (mutable ctinfo) (mutable rtinfo) (mutable ctir) (mutable rtir) (mutable visible?) fn) (protocol (lambda (pargs->new) (lambda (binary? ctinfo rtinfo visible? fn) (safe-assert (or ctinfo rtinfo)) ((pargs->new) binary? ctinfo rtinfo #f #f visible? fn))))) (define library-node-path (lambda (node) (library-info-path (or (library-node-ctinfo node) (library-node-rtinfo node))))) (define library-node-uid (lambda (node) (library-info-uid (or (library-node-ctinfo node) (library-node-rtinfo node))))) (define library-node-version (lambda (node) (library-info-version (or (library-node-ctinfo node) (library-node-rtinfo node))))) (define library-node-invoke-req* (lambda (node) (library/rt-info-invoke-req* (library-node-rtinfo node)))) (define library-node-import-req* (lambda (node) (library/ct-info-import-req* (library-node-ctinfo node)))) (define read-input-file (lambda (who ifn) (call-with-port ($open-file-input-port who ifn) (lambda (ip) (on-reset (close-port ip) (let ([hash-bang-line (let ([start-pos (port-position ip)]) (if (and (eqv? (get-u8 ip) (char->integer #\#)) (eqv? (get-u8 ip) (char->integer #\!)) (let ([b (lookahead-u8 ip)]) (or (eqv? b (char->integer #\space)) (eqv? b (char->integer #\/))))) (let-values ([(op get-bv) (open-bytevector-output-port)]) (put-u8 op (char->integer #\#)) (put-u8 op (char->integer #\!)) (let loop () (let ([b (get-u8 ip)]) (unless (eof-object? b) (put-u8 op b) (unless (eqv? b (char->integer #\newline)) (loop))))) (get-bv)) (begin (set-port-position! ip start-pos) #f)))]) (if ($compiled-file-header? ip) (let loop ([rls '()]) (let ([x (fasl-read ip)]) (cond [(eof-object? x) (values hash-bang-line (reverse rls))] [(Lexpand? x) (loop (cons x rls))] [else ($oops who "unexpected wpo file object ~s" x)]))) ($oops who "input file is source ~s" ifn)))))))) (define find-library (lambda (who path what library-ext*) (with-values ($library-search who path (library-directories) library-ext*) (lambda (src-path lib-path lib-exists?) (and lib-exists? (begin (when (and src-path (time library-node-path] [else uid]))) (define read-library (lambda (path libs-visible?) (cond [(find-library who path "wpo" (map (lambda (ext) (cons (car ext) (string-append (path-root (cdr ext)) ".wpo"))) (library-extensions))) => (lambda (fn) (let*-values ([(hash-bang-line ir*) (read-input-file who fn)] [(no-program node* ignore-rcinfo*) (process-ir*! ir* fn #f libs-visible?)]) (values fn node*)))] [(find-library who path "so" (library-extensions)) => (lambda (fn) (values fn (read-binary-file path fn libs-visible?)))] [else ($oops who "unable to locate expanded library file for library ~s" path)]))) (define read-binary-file (lambda (path fn libs-visible?) (call-with-port ($open-file-input-port who fn) (lambda (ip) (on-reset (close-port ip) (if ($compiled-file-header? ip) (let ([libs-in-file '()]) (let loop! () (let ([x (fasl-read ip)]) (if (eof-object? x) (begin (for-each (lambda (node) (unless (library-node-ctinfo node) ($oops who "missing compile-time information for ~s" (library-node-path node))) (unless (library-node-rtinfo node) ($oops who "missing run-time information for ~s" (library-node-path node)))) libs-in-file) libs-in-file) (begin (cond [(recompile-info? x)] [(procedure? x)] [(library/ct-info? x) (let ([node (record-ct-lib! x #t fn libs-visible?)]) (when node (set! libs-in-file (cons node libs-in-file))))] [(library/rt-info? x) (let ([node (record-rt-lib! x #t fn libs-visible?)]) (when node (set! libs-in-file (cons node libs-in-file))))] [(program-info? x) ($oops who "found program while looking for library ~s in ~a" path fn)] ; NB: this is here to support the #t inserted by compile-file-help2 after header information [(eq? x #t)] [else ($oops who "unexpected value ~s read from ~a" x fn)]) (loop!)))))) ($oops who "malformed binary input file ~s" fn))))))) (define process-ir*! (lambda (ir* ifn capture-program? libs-visible?) (define outer-who who) (let ([libs-in-file '()] [maybe-program #f] [rcinfo* '()]) (define-pass process-ir! : Lexpand (ir) -> * () (Outer : Outer (ir situation) -> * () [(recompile-info ,rcinfo) (set! rcinfo* (cons rcinfo rcinfo*)) (values)] [(group ,[] ,[]) (values)] [(visit-only ,[inner 'visit ->]) (values)] [(revisit-only ,[inner 'revisit ->]) (values)]) (Inner : Inner (ir situation) -> * () [,lsrc ($oops outer-who "expected program or library form, but encountered top-level expression ~s processing file ~a" ($uncprep lsrc) ifn)] [(library/ct-info ,linfo/ct) (let ([node (record-ct-lib! linfo/ct #f ifn libs-visible?)]) (when node (set! libs-in-file (cons node libs-in-file)))) (values)] [(library/rt-info ,linfo/rt) (let ([node (record-rt-lib! linfo/rt #f ifn libs-visible?)]) (when node (set! libs-in-file (cons node libs-in-file)))) (values)] [(program-info ,pinfo) (unless capture-program? ($oops outer-who "found program while reading library wpo file ~a" ifn)) (when (eq? situation 'visit) ($oops outer-who "encountered visit-only program while processing file ~s" ifn)) (when maybe-program ($oops outer-who "found multiple programs in entry file ~a" ifn)) (set! maybe-program (make-program-node pinfo)) (values)]) (Program : Program (ir situation) -> * () [(program ,uid ,body) (unless capture-program? ($oops outer-who "found program while reading library wpo file ~a" ifn)) (when (eq? situation 'visit) ($oops outer-who "encountered visit-only program while processing file ~s" ifn)) (unless maybe-program ($oops outer-who "unable to locate program descriptor for ~s" uid)) (unless (eq? uid (program-node-uid maybe-program)) ($oops outer-who "expected code for program uid ~s, but found code for program uid ~s" (program-node-uid maybe-program) uid)) (program-node-ir-set! maybe-program ir) (values)]) (ctLibrary : ctLibrary (ir situation) -> * () [(library/ct ,uid (,export-id* ...) ,import-code ,visit-code) (when (eq? situation 'revisit) ($oops outer-who "encountered revisit-only compile-time library ~s while processing file ~s" (lookup-path uid) ifn)) (record-ct-lib-ir! uid ir) (values)]) (rtLibrary : rtLibrary (ir situation) -> * () [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) (when (eq? situation 'visit) ($oops outer-who "encountered visit-only run-time library ~s while processing file ~s" (lookup-path uid) ifn)) (record-rt-lib-ir! uid ir) (values)]) (when capture-wpo? (set! wpo* (cons ir wpo*))) (Outer ir 'load)) (for-each process-ir! ir*) (for-each (lambda (node) (unless (library-node-ctinfo node) ($oops who "missing compile-time information for ~s" (library-node-path node))) (unless (library-node-rtinfo node) ($oops who "missing run-time information for ~s" (library-node-path node))) (unless (library-node-ctir node) ($oops who "missing compile-time code for ~s" (library-node-path node))) (unless (library-node-rtir node) ($oops who "missing run-time code for ~s" (library-node-path node)))) libs-in-file) (values maybe-program libs-in-file rcinfo*)))) (define record-ct-lib! (lambda (linfo/ct binary? ifn libs-visible?) (let* ([uid (library-info-uid linfo/ct)] [cell (symbol-hashtable-cell libs uid #f)] [node (cdr cell)]) (if node (if (library-node-ctinfo node) ($oops who "encountered library ~s in ~a, but had already encountered it in ~a" (library-info-path linfo/ct) ifn (library-node-fn node)) (begin (library-node-ctinfo-set! node linfo/ct) #f)) (let ([node (make-library-node binary? linfo/ct #f (or libs-visible? binary?) ifn)]) (set-cdr! cell node) node))))) (define record-rt-lib! (lambda (linfo/rt binary? ifn libs-visible?) (let* ([uid (library-info-uid linfo/rt)] [cell (symbol-hashtable-cell libs uid #f)] [node (cdr cell)]) (if node (if (library-node-rtinfo node) ($oops who "encountered library ~s in ~a, but had already encountered it in ~a" (library-info-path linfo/rt) ifn (library-node-fn node)) (begin (library-node-rtinfo-set! node linfo/rt) #f)) (let ([node (make-library-node binary? #f linfo/rt (or libs-visible? binary?) ifn)]) (set-cdr! cell node) node))))) (define record-ct-lib-ir! (lambda (uid ir) (let ([node (symbol-hashtable-ref libs uid #f)]) (unless node ($oops "missing descriptor for compile-time library code ~s" uid)) (library-node-ctir-set! node ir)))) (define record-rt-lib-ir! (lambda (uid ir) (let ([node (symbol-hashtable-ref libs uid #f)]) (unless node ($oops "missing descriptor for run-time library code ~s" uid)) (library-node-rtir-set! node ir)))) (define chase-library (lambda (req libs-visible?) (let ([a (symbol-hashtable-cell libs (libreq-uid req) #f)]) (cond [(cdr a) => (lambda (node) (when libs-visible? (unless (library-node-visible? node) (library-node-visible?-set! node #t) (chase-library-dependencies! node))))] [else (let ([path (libreq-path req)]) (let-values ([(fn node*) (read-library path libs-visible?)]) (unless (symbol-hashtable-ref libs (libreq-uid req) #f) ($oops who "~s does not define expected compilation instance of library ~s" fn path)) (for-each chase-library-dependencies! node*)))])))) (define find-dependencies (lambda (req* maybe-import-req*) (let ([dep* (map (lambda (req) (let ([node (symbol-hashtable-ref libs (libreq-uid req) #f)]) (node-use-count-set! node (fx+ (node-use-count node) 1)) node)) req*)]) (if maybe-import-req* (fold-right (lambda (req dep*) (let ([node (symbol-hashtable-ref libs (libreq-uid req) #f)]) (if node (begin (node-use-count-set! node (fx+ (node-use-count node) 1)) (cons node dep*)) dep*))) dep* maybe-import-req*) dep*)))) (define chase-program-dependencies! (lambda (node) (for-each (lambda (req) (chase-library req libs-visible?)) (program-node-invoke-req* node)) (node-depend*-set! node (find-dependencies (program-node-invoke-req* node) #f)))) (define chase-library-dependencies! (lambda (node) (if (library-node-visible? node) (for-each (lambda (req) (unless ($system-library? (libreq-path req)) (chase-library req (library-node-visible? node)))) (library-node-import-req* node)) (for-each (lambda (req) (chase-library req (library-node-visible? node))) (library-node-invoke-req* node))) (unless (node-depend* node) (node-depend*-set! node (find-dependencies (library-node-invoke-req* node) (and (library-node-visible? node) (library-node-import-req* node))))))) (let-values ([(maybe-program node* rcinfo*) (process-ir*! ir* ifn capture-program? libs-visible?)]) (when capture-program? (unless maybe-program ($oops who "missing entry program in file ~a" ifn)) (unless (program-node-ir maybe-program) ($oops who "loading ~a did not define expected program pieces" ifn)) (chase-program-dependencies! maybe-program)) (for-each chase-library-dependencies! node*) (let-values ([(visible* invisible*) (partition library-node-visible? (vector->list (hashtable-values libs)))]) (values maybe-program visible* invisible* rcinfo* wpo*)))))) (define topological-sort (lambda (program-entry library-entry*) (define topological-sort (lambda (dep* node*) (if (null? dep*) node* (let* ([dep (car dep*)] [use-count (node-use-count dep)]) (node-use-count-set! dep (fx- use-count 1)) (if (fx= use-count 1) (topological-sort (cdr dep*) (topological-sort (node-depend* dep) (cons dep node*))) (topological-sort (cdr dep*) node*)))))) (fold-right (lambda (entry node*) (topological-sort (node-depend* entry) (cons entry node*))) (if program-entry (topological-sort (node-depend* program-entry) '()) '()) (filter (lambda (node) (fx= (node-use-count node) 0)) library-entry*)))) (define void-pr (lookup-primref 3 'void)) (with-output-language (Lsrc Expr) (define build-install-library/ct-code (lambda (node) (nanopass-case (Lexpand ctLibrary) (library-node-ctir node) [(library/ct ,uid (,export-id* ...) ,import-code ,visit-code) (if (library-node-visible? node) ($build-install-library/ct-code uid export-id* import-code visit-code) void-pr)]))) (define build-void (let ([void-rec `(quote ,(void))]) (lambda () void-rec))) (define gen-var (lambda (sym) (make-prelex sym 0 #f #f))) (define build-let (lambda (ids exprs body) `(call ,(make-preinfo) ,(build-lambda ids body) ,exprs ...))) (define build-lambda (lambda (ids body) `(case-lambda ,(make-preinfo-lambda) (clause (,ids ...) ,(length ids) ,body)))) (define build-call (lambda (e . e*) `(call ,(make-preinfo) ,e ,e* ...))) (define-syntax build-primcall ; written as a macro to give lookup-primref a chance to lookup the primref at expansion time (syntax-rules () [(_ ?name ?arg ...) (build-call (lookup-primref 3 ?name) ?arg ...)])) (define-syntax build-primref (syntax-rules () [(_ ?level ?name) (lookup-primref ?level ?name)])) (define build-install-library/rt-code (lambda (node thunk) (build-primcall '$install-library/rt-code `(quote ,(library-node-uid node)) thunk))) (define-pass patch : Lsrc (ir env) -> Lsrc () (definitions (define with-initialized-ids (lambda (old-id* proc) (let ([new-id* (map (lambda (old-id) (let ([new-id (make-prelex (prelex-name old-id) (let ([flags (prelex-flags old-id)]) (fxlogor (fxlogand flags (constant prelex-sticky-mask)) (fxsll (fxlogand flags (constant prelex-is-mask)) (constant prelex-was-flags-offset)))) (prelex-source old-id) #f)]) (prelex-operand-set! old-id new-id) new-id)) old-id*)]) (let-values ([v* (proc new-id*)]) (for-each (lambda (old-id) (prelex-operand-set! old-id #f)) old-id*) (apply values v*))))) (define build-ref (case-lambda [(x) (build-ref #f x)] [(src x) (let ([x (prelex-operand x)]) (safe-assert (prelex? x)) (if (prelex-referenced x) (set-prelex-multiply-referenced! x #t) (set-prelex-referenced! x #t)) `(ref ,src ,x))]))) (Expr : Expr (ir) -> Expr () [(ref ,maybe-src ,x) (build-ref maybe-src x)] [(call ,preinfo ,pr (quote ,d)) (guard (eq? (primref-name pr) '$top-level-value) (symbol? d)) (cond [(symbol-hashtable-ref env d #f) => (lambda (x) (build-ref (preinfo-src preinfo) x))] [else ir])] [(set! ,maybe-src ,x ,[e]) (let ([x (prelex-operand x)]) (safe-assert (prelex? x)) (set-prelex-assigned! x #t) `(set! ,maybe-src ,x ,e))] [(letrec ([,x* ,e*] ...) ,body) (with-initialized-ids x* (lambda (x*) `(letrec ([,x* ,(map Expr e*)] ...) ,(Expr body))))] [(letrec* ([,x* ,e*] ...) ,body) (with-initialized-ids x* (lambda (x*) `(letrec* ([,x* ,(map Expr e*)] ...) ,(Expr body))))]) (CaseLambdaClause : CaseLambdaClause (ir) -> CaseLambdaClause () [(clause (,x* ...) ,interface ,body) (with-initialized-ids x* (lambda (x*) `(clause (,x* ...) ,interface ,(Expr body))))])) (define build-top-level-set!* (lambda (node) (nanopass-case (Lexpand rtLibrary) (library-node-rtir node) [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) (fold-right (lambda (dl db dv body) (if dl `(seq ,(build-primcall '$set-top-level-value! `(quote ,dl) `(cte-optimization-loc ,db (ref #f ,dv))) ,body) body)) (build-void) dl* db* dv*)]))) (define make-patch-env (lambda (cluster*) (let ([patch-env (make-hashtable symbol-hash eq?)]) (for-each (lambda (cluster) (for-each (lambda (node) (unless (library-node-binary? node) (nanopass-case (Lexpand rtLibrary) (library-node-rtir node) [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) (for-each (lambda (label var) (when label (symbol-hashtable-set! patch-env label var))) dl* dv*)]))) cluster)) cluster*) patch-env))) (define build-combined-program-ir (lambda (program node*) `(seq ,(build-primcall 'for-each (build-primref 3 '$mark-pending!) `(quote ,(map library-node-uid (remp library-node-binary? node*)))) ,(patch (fold-right (lambda (node combined-body) (if (library-node-binary? node) `(seq ,(build-primcall '$invoke-library `(quote ,(library-node-path node)) `(quote ,(library-node-version node)) `(quote ,(library-node-uid node))) ,combined-body) (nanopass-case (Lexpand rtLibrary) (library-node-rtir node) [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) `(letrec* ([,dv* ,de*] ...) (seq ,body (seq ,(build-install-library/rt-code node (if (library-node-visible? node) (build-lambda '() (build-top-level-set!* node)) void-pr)) ,combined-body)))]))) (nanopass-case (Lexpand Program) (program-node-ir program) [(program ,uid ,body) body]) node*) (make-patch-env (list node*)))))) (define build-combined-library-ir (lambda (cluster*) (define build-mark-invoked! (lambda (node) (build-primcall '$mark-invoked! `(quote ,(library-node-uid node))))) (define build-cluster (lambda (node* cluster-body) (fold-right (lambda (node cluster-body) (nanopass-case (Lexpand rtLibrary) (library-node-rtir node) [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) `(letrec* ([,dv* ,de*] ...) (seq ,body (seq ,(if (library-node-visible? node) `(seq ,(build-top-level-set!* node) ,(build-mark-invoked! node)) (build-mark-invoked! node)) ,cluster-body)))])) cluster-body node*))) (patch ; example: D imports C; C imports A, B; B imports A; A imports nothing ; have wpos for D, A, B; obj for C ; (let ([lib-f (void)]) ; (set! lib-f ; (lambda (idx) ; (letrec ([A-local ---] ...) ; A-body ; (begin ($top-level-set! A-export A-local) ...) ; (letrec ([B-local ---] ...) ; B-body ; (begin ($top-level-set! B-export B-local) ...) ; (let ([t (lambda (idx) ; (letrec ([D-local ---] ...) ; D-body ; (begin ($top-level-set! D-export B-local) ...) ; (set! lib-f (lambda (idx) (void)))))]) ; (if (eqv? idx 0) ; (set! lib-f t) ; (t idx))))))) ; ($install-library/rt-code 'A-uid (lambda () (lib-f 0))) ; ($install-library/rt-code 'B-uid (lambda () (lib-f 0))) ; ($install-library/rt-code 'D-uid (lambda () (lib-f 1))) ; (void)) (let ([lib-f (gen-var 'lib-f)]) (let ([cluster-idx* (enumerate cluster*)]) (build-let (list lib-f) (list (build-void)) `(seq (set! #f ,lib-f ,(let f ([cluster* cluster*] [cluster-idx* cluster-idx*]) (let ([idx (gen-var 'idx)]) (build-lambda (list idx) (build-cluster (car cluster*) (let ([cluster* (cdr cluster*)]) (if (null? cluster*) (let ([idx (gen-var 'idx)]) `(set! #f ,lib-f ,(build-lambda (list idx) (build-void)))) (let ([t (gen-var 't)]) (build-let (list t) (list (f cluster* (cdr cluster-idx*))) `(if ,(build-primcall 'eqv? `(ref #f ,idx) `(quote ,(car cluster-idx*))) (set! #f ,lib-f (ref #f ,t)) ,(build-call `(ref #f ,t) `(ref #f ,idx)))))))))))) ,(fold-right (lambda (cluster cluster-idx body) (fold-right (lambda (node body) `(seq ,(build-install-library/rt-code node (if (library-node-visible? node) (build-lambda '() (build-call `(ref #f ,lib-f) `(quote ,cluster-idx))) void-pr)) ,body)) body cluster)) (build-void) cluster* cluster-idx*))))) (make-patch-env cluster*))))) (with-output-language (Lexpand Outer) (define add-recompile-info (lambda (rcinfo* body) (fold-left (lambda (body rcinfo) `(group (recompile-info ,rcinfo) ,body)) body rcinfo*))) (define requirements-join (lambda (req* maybe-collected-invoke-req*) (define (->libreq node) (make-libreq (library-node-path node) (library-node-version node) (library-node-uid node))) (if maybe-collected-invoke-req* (let f ([invoke-req* maybe-collected-invoke-req*]) (if (null? invoke-req*) req* (let* ([invoke-req (car invoke-req*)] [uid (library-node-uid invoke-req)]) (if (memp (lambda (req) (eq? (libreq-uid req) uid)) req*) (f (cdr invoke-req*)) (cons (->libreq invoke-req) (f (cdr invoke-req*))))))) req*))) (define add-library/rt-records (lambda (maybe-ht node* body) (fold-left (lambda (body node) (if (library-node-binary? node) body (let* ([info (library-node-rtinfo node)] [uid (library-info-uid info)]) `(group (revisit-only (library/rt-info ,(make-library/rt-info (library-info-path info) (library-info-version info) uid (library-node-visible? node) (requirements-join (library/rt-info-invoke-req* info) (and maybe-ht (symbol-hashtable-ref maybe-ht uid #f)))))) ,body)))) body node*))) (define add-library/ct-records (lambda (maybe-ht visit-lib* body) (fold-left (lambda (body visit-lib) (if (library-node-binary? visit-lib) body (let* ([info (library-node-ctinfo visit-lib)] [uid (library-info-uid info)]) `(group (visit-only (library/ct-info ,(make-library/ct-info (library-info-path info) (library-info-version info) uid (library-node-visible? visit-lib) (requirements-join (library/ct-info-import-req* info) (and maybe-ht (symbol-hashtable-ref maybe-ht uid #f))) (library/ct-info-visit-visit-req* info) (library/ct-info-visit-req* info)))) ,body)))) body visit-lib*))) (define add-program-record (lambda (node body) `(group (revisit-only (program-info ,(make-program-info (program-node-uid node) ; NB: possibly list direct or indirect binary library reqs here (program-node-invoke-req* node)))) ,body))) (define add-visit-lib-install* (lambda (visit-lib* body) (fold-left (lambda (body visit-lib) (if (library-node-binary? visit-lib) body `(group (visit-only ,(build-install-library/ct-code visit-lib)) ,body))) body visit-lib*))) (define build-cluster* (lambda (node* ht) (define (add-deps! node deps) (symbol-hashtable-set! ht (library-node-uid node) deps)) (define (s-entry/binary node* rcluster* deps) (if (null? node*) (reverse rcluster*) (let ([node (car node*)]) (if (library-node-binary? node) (s-entry/binary (cdr node*) rcluster* (cons node deps)) (begin (add-deps! node deps) (s-source (cdr node*) (list node) rcluster* (list node))))))) (define (s-source node* rnode* rcluster* deps) (if (null? node*) (reverse (cons (reverse rnode*) rcluster*)) (let ([node (car node*)]) (if (library-node-binary? node) (s-entry/binary (cdr node*) (cons (reverse rnode*) rcluster*) (cons node deps)) (begin (add-deps! node deps) (s-source (cdr node*) (cons node rnode*) rcluster* deps)))))) (s-entry/binary node* '() '()))) (define build-program-body (lambda (program-entry node* visit-lib* invisible* rcinfo*) (add-recompile-info rcinfo* (add-library/rt-records #f node* (add-library/ct-records #f visit-lib* (add-library/ct-records #f invisible* (add-program-record program-entry (add-visit-lib-install* visit-lib* (add-visit-lib-install* invisible* `(revisit-only ,(build-combined-program-ir program-entry node*))))))))))) (define build-library-body (lambda (node* visit-lib* rcinfo*) (let* ([collected-req-ht (make-hashtable symbol-hash eq?)] [cluster* (build-cluster* node* collected-req-ht)]) (add-recompile-info rcinfo* (add-library/rt-records collected-req-ht node* (add-library/ct-records collected-req-ht visit-lib* (add-visit-lib-install* visit-lib* `(revisit-only ,(build-combined-library-ir cluster*)))))))))) (define finish-compile (lambda (who msg ifn ofn hash-bang-line x1) (with-object-file who ofn (lambda (op) (with-coverage-file who ofn (lambda (source-table) (when hash-bang-line (put-bytevector op hash-bang-line)) (parameterize ([$target-machine (constant machine-type-name)] ; dummy sfd for block-profile optimization [$sfd (make-source-file-descriptor ifn #xc7 #xc7c7)] [$block-counter 0]) (when source-table ($insert-profile-src! source-table x1)) (emit-header op (constant scheme-version) (constant machine-type)) (let-values ([(rcinfo* lpinfo* final*) (compile-file-help1 x1 msg)]) (compile-file-help2 op (list rcinfo*) (list lpinfo*) (list final*)))))))))) (define write-wpo-file (lambda (who ofn ir*) (with-wpo-file who ofn (lambda (wpoop) (when wpoop (emit-header wpoop (constant scheme-version) (host-machine-type)) ($with-fasl-target (host-machine-type) (lambda () (parameterize ([$target-machine (machine-type)]) (let ([t ($fasl-table)]) (let ([x (fold-left (lambda (outer ir) (with-output-language (Lexpand Outer) `(group ,outer ,ir))) (car ir*) (cdr ir*))]) ($fasl-enter x t (constant annotation-all)) ($fasl-start wpoop t (constant fasl-type-visit-revisit) (lambda (p) ($fasl-out x p t (constant annotation-all)))))))))))))) (define build-required-library-list (lambda (node* visit-lib*) (let ([ht (make-hashtable symbol-hash eq?)]) (fold-left (lambda (ls node) (if (and (library-node-binary? node) (not (symbol-hashtable-contains? ht (library-node-uid node)))) (cons (library-node-path node) ls) ls)) (fold-left (lambda (ls node) (if (library-node-binary? node) (begin (symbol-hashtable-set! ht (library-node-uid node) #t) (cons (library-node-path node) ls)) ls)) '() node*) visit-lib*)))) ;; TODO: Add automatic recompliation ala scheme import/load-library (set-who! compile-whole-program (rec compile-whole-program (case-lambda [(ifn ofn) (compile-whole-program ifn ofn #f)] [(ifn ofn libs-visible?) (unless (string? ifn) ($oops who "~s is not a string" ifn)) (unless (string? ofn) ($oops who "~s is not a string" ofn)) (let*-values ([(hash-bang-line ir*) (read-input-file who ifn)] [(program-entry lib* invisible* rcinfo* no-wpo*) (build-graph who ir* ifn #t #f libs-visible?)]) (safe-assert program-entry) (safe-assert (null? no-wpo*)) (let ([node* (topological-sort program-entry lib*)]) (finish-compile who "whole program" ifn ofn hash-bang-line (build-program-body program-entry node* lib* invisible* rcinfo*)) (build-required-library-list node* lib*)))]))) (set-who! compile-whole-library (lambda (ifn ofn) (unless (string? ifn) ($oops who "~s is not a string" ifn)) (unless (string? ofn) ($oops who "~s is not a string" ofn)) (let*-values ([(hash-bang-line ir*) (read-input-file who ifn)] [(no-program lib* invisible* rcinfo* wpo*) (build-graph who ir* ifn #f (generate-wpo-files) #t)]) (safe-assert (not no-program)) (safe-assert (null? invisible*)) (safe-assert (or (not (generate-wpo-files)) (not (null? wpo*)))) (when (null? lib*) ($oops "did not find libraries in input file ~s" ifn)) (let ([node* (topological-sort #f lib*)]) (write-wpo-file who ofn wpo*) (finish-compile who "whole library" ifn ofn hash-bang-line (build-library-body node* lib* rcinfo*)) (build-required-library-list node* lib*)))))) (set! $c-make-code (lambda (func subtype free name arity-mask size code-list info pinfo*) (let ([code `(code ,func ,subtype ,free ,(if (symbol? name) (symbol->string name) (and (string? name) name)) ,arity-mask ,size ,code-list ,info ,pinfo*)]) (set-$c-func-code-record! func code) code))) (set! $c-make-closure (lambda (func) (or ($c-func-closure-record func) (let ([x `(closure . ,func)]) (set-$c-func-closure-record! func x) x)))) (set-who! compile (rec compile (case-lambda [(x0) (compile x0 (if (eq? (subset-mode) 'system) ($system-environment) (interaction-environment)))] [(x0 env-spec) (define-pass expand-Lexpand : Lexpand (ir) -> Lsrc () (Inner : Inner (ir) -> Expr () [,lsrc lsrc] [(program ,uid ,body) ($build-invoke-program uid body)] [(library/ct ,uid (,export-id* ...) ,import-code ,visit-code) ($build-install-library/ct-code uid export-id* import-code visit-code)] [(library/rt ,uid (,dl* ...) (,db* ...) (,dv* ...) (,de* ...) ,body) ($build-install-library/rt-code uid dl* db* dv* de* body)] [else (sorry! who "unexpected Lexpand record ~s" ir)]) (Outer : Outer (ir) -> Expr () [(group ,[e1] ,[e2]) `(seq ,e1 ,e2)] [,inner (Inner inner)] [else (sorry! who "unexpected Lexpand record ~s" ir)])) (unless (environment? env-spec) ($oops who "~s is not an environment" env-spec)) ((parameterize ([$target-machine (constant machine-type-name)] [$sfd #f]) (let* ([x1 (expand-Lexpand ($pass-time 'expand (lambda () (expand x0 env-spec #t))))] [waste ($uncprep x1 #t)] ; populate preinfo sexpr fields [waste (when (and (expand-output) (not ($noexpand? x0))) (pretty-print ($uncprep x1) (expand-output)) (flush-output-port (expand-output)))] [x2 ($pass-time 'cpvalid (lambda () ($cpvalid x1)))] [x2a (let ([cpletrec-ran? #f]) (let ([x ((run-cp0) (lambda (x) (set! cpletrec-ran? #t) (let ([x ($pass-time 'cp0 (lambda () ($cp0 x)))]) ($pass-time 'cpletrec (lambda () ($cpletrec x))))) x2)]) (if cpletrec-ran? x ($pass-time 'cpletrec (lambda () ($cpletrec x))))))] [x2b ($pass-time 'cpcheck (lambda () ($cpcheck x2a)))] [x2b ($pass-time 'cpcommonize (lambda () ($cpcommonize x2b)))]) (when (and (expand/optimize-output) (not ($noexpand? x0))) (pretty-print ($uncprep x2b) (expand/optimize-output)) (flush-output-port (expand/optimize-output))) (if (and (compile-interpret-simple) (not ($assembly-output)) (cheat? x2b)) (lambda () (cheat-eval x2b)) ($compile-backend x2b)))))]))) (set! $compile-backend (lambda (x2) (c-mkcode (c-compile x2)))) (let () (define emit-boot-header (lambda (op machine bootfiles) (emit-header op (constant scheme-version) (constant machine-type) (map path-root (map path-last bootfiles))) (when (null? bootfiles) (parameterize ([$target-machine machine] [$sfd #f]) (c-print-fasl ($np-boot-code 'error-invoke) op (constant fasl-type-visit-revisit)) (c-print-fasl ($np-boot-code 'invoke) op (constant fasl-type-visit-revisit)) ($fasl-base-rtd #!base-rtd op))))) (define do-make-boot-file (lambda (who outfn machine bootfile* infn*) (unless (string? outfn) ($oops who "~s is not a string" outfn)) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (unless (and (list? bootfile*) (andmap string? bootfile*)) ($oops who "~s is not a list of strings" bootfile*)) (for-each (lambda (infn) (unless (string? infn) ($oops who "~s is not a string" infn))) infn*) (with-object-file who outfn (lambda (op) (with-coverage-file who outfn (lambda (source-table) (unless (and (eq? who 'make-boot-file) (null? bootfile*)) (emit-boot-header op machine bootfile*)) (for-each (lambda (infn) (let ([ip ($open-file-input-port who infn)]) (on-reset (close-port ip) (if ($compiled-file-header? ip) (begin (let* ([bufsiz (file-buffer-size)] [buf (make-bytevector bufsiz)]) (let loop () (let ([n (get-bytevector-n! ip buf 0 bufsiz)]) (unless (eof-object? n) (put-bytevector op buf 0 n) (loop))))) (when source-table (guard (c [else (void)]) (let ([ip ($open-file-input-port who (new-extension "covin" infn) (file-options compressed) (buffer-mode block) (current-transcoder))]) (on-reset (close-port ip) (get-source-table! ip source-table)) (close-port ip))))) (let ([sfd ($source-file-descriptor infn ip)]) ; whack ip so close-port calls close the text port (set! ip (transcoded-port ip (current-transcoder))) (compile-file-help op #f #f source-table machine sfd ($make-read ip sfd 0) outfn)))) (close-port ip))) infn*))))))) (define do-make-boot-header ; create boot loader (invoke) for entry into Scheme from C (lambda (who out machine bootfiles) (unless (string? out) ($oops who "~s is not a string" out)) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (for-each (lambda (x) (unless (string? x) ($oops who "~s is not a string" x))) bootfiles) (with-object-file who out (lambda (op) (emit-boot-header op machine bootfiles))))) (set-who! make-boot-file (lambda (outfn bootfile* . infn*) (do-make-boot-file who outfn (machine-type) bootfile* infn*))) (set-who! $make-boot-file (lambda (outfn machine bootfile* . infn*) (do-make-boot-file who outfn machine bootfile* infn*))) (set-who! make-boot-header ; exported interface: machine-type implicit and requires one or more ; subordinate boot files (lambda (out bootfile . bootfiles) (do-make-boot-header who out (machine-type) (cons bootfile bootfiles)))) (set-who! $make-boot-header ; create boot loader (invoke) for entry into Scheme from C (lambda (out machine . bootfiles) (do-make-boot-header who out machine bootfiles)))) (let () (define (libreq-hash x) (symbol-hash (libreq-uid x))) (define (libreq=? x y) (eq? (libreq-uid x) (libreq-uid y))) (define do-concatenate-object-files (lambda (who outfn infn*) (unless (string? outfn) ($oops who "~s is not a string" outfn)) (for-each (lambda (infn) (unless (string? infn) ($oops who "~s is not a string" infn))) infn*) (let ([import-ht (make-hashtable libreq-hash libreq=?)] [include-ht (make-hashtable string-hash string=?)]) (let in-loop ([infn* infn*] [rip* '()]) (if (null? infn*) (let ([ip* (reverse rip*)]) (with-object-file who outfn (lambda (op) (emit-header op (constant scheme-version) (constant machine-type)) (c-print-fasl `(object ,(make-recompile-info (vector->list (hashtable-keys import-ht)) (vector->list (hashtable-keys include-ht)))) op (constant fasl-type-visit-revisit)) (for-each (lambda (ip) (let loop () ;; NB: This loop consumes one entry past the last library/program info record, ;; which we presume is the #t end-of-header marker. (let ([ty (lookahead-u8 ip)]) (unless (eof-object? ty) ;; perhaps should verify ty here. (let ([x (fasl-read ip)]) (when (or (library-info? x) (program-info? x)) (c-print-fasl `(object ,x) op ty) (loop))))))) ip*) ;; inserting #t after lpinfo as an end-of-header marker (c-print-fasl `(object #t) op (constant fasl-type-visit-revisit)) (let* ([bufsiz (file-buffer-size)] [buf (make-bytevector bufsiz)]) (for-each (lambda (ip) (let loop () (let ([n (get-bytevector-n! ip buf 0 bufsiz)]) (unless (eof-object? n) (put-bytevector op buf 0 n) (loop)))) (close-port ip)) ip*))))) (let* ([fn (car infn*)] [ip ($open-file-input-port who fn)]) (on-reset (close-port ip) ;; NB: Does not currently support files beginning with a #! line. Add that here if desired. (unless ($compiled-file-header? ip) ($oops who "missing header for compiled file ~s" fn)) (let ([rcinfo (fasl-read ip)]) (unless (recompile-info? rcinfo) ($oops who "expected recompile info at start of ~s, found ~a" fn rcinfo)) (for-each (lambda (x) ;; NB: this could be enhanced to perform additional checks for compatible versions (hashtable-set! import-ht x x)) (recompile-info-import-req* rcinfo)) (for-each (lambda (x) (hashtable-set! include-ht x #t)) (recompile-info-include-req* rcinfo)) (in-loop (cdr infn*) (cons ip rip*)) )))))))) (set-who! concatenate-object-files (lambda (outfn infn0 . infn*) (do-concatenate-object-files who outfn (cons infn0 infn*)))) ) (set-who! compile-port (rec compile-port (case-lambda [(ip op) (compile-port ip op #f)] [(ip op sfd) (compile-port ip op sfd #f)] [(ip op sfd wpoop) (compile-port ip op sfd wpoop #f)] [(ip op sfd wpoop covop) (compile-port ip op sfd wpoop covop (constant machine-type-name))] [(ip op sfd wpoop covop machine) (compile-port ip op sfd wpoop covop machine #f)] [(ip op sfd wpoop covop machine hostop) (unless (and (input-port? ip) (textual-port? ip)) ($oops who "~s is not a textual input port" ip)) (unless (and (output-port? op) (binary-port? op)) ($oops who "~s is not a binary output port" op)) (when ($port-flags-set? op (constant port-flag-compressed)) ($compressed-warning who op)) (when sfd (unless (source-file-descriptor? sfd) ($oops who "~s is not a source-file descriptor or #f" sfd))) (when wpoop (unless (and (output-port? wpoop) (binary-port? wpoop)) ($oops who "~s is not a binary output port or #f" wpoop)) (when ($port-flags-set? wpoop (constant port-flag-compressed)) ($compressed-warning who wpoop))) (when covop (unless (and (output-port? covop) (textual-port? covop)) ($oops who "~s is not a textual output port or #f" covop))) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (when hostop (unless (and (output-port? hostop) (binary-port? hostop)) ($oops who "~s is not a binary output port or #f" hostop)) (when ($port-flags-set? hostop (constant port-flag-compressed)) ($compressed-warning who hostop))) (let ([source-table (and covop (make-source-table))]) (let ([fp (and (port-has-port-position? ip) (let ([fp (port-position ip)]) (if ($port-flags-set? ip (constant port-flag-char-positions)) fp (and (eqv? fp 0) fp))))]) (compile-file-help op hostop wpoop source-table machine sfd ($make-read ip sfd fp) #f) (when covop (put-source-table covop source-table))))]))) (set-who! compile-to-port (rec compile-to-port (case-lambda [(sexpr* op) (compile-to-port sexpr* op #f)] [(sexpr* op sfd) (compile-to-port sexpr* op sfd #f)] [(sexpr* op sfd wpoop) (compile-to-port sexpr* op sfd wpoop #f)] [(sexpr* op sfd wpoop covop) (compile-to-port sexpr* op sfd wpoop covop (constant machine-type-name))] [(sexpr* op sfd wpoop covop machine) (compile-to-port sexpr* op sfd wpoop covop machine #f)] [(sexpr* op sfd wpoop covop machine hostop) (define do-compile-to-port (lambda () (let ([source-table (and covop (make-source-table))]) (compile-file-help op hostop wpoop source-table machine sfd (lambda () (if (null? sexpr*) (eof-object) (let ([x (car sexpr*)]) (set! sexpr* (cdr sexpr*)) x))) (port-name op)) (when covop (put-source-table covop source-table))))) (unless (list? sexpr*) ($oops who "~s is not a proper list" sexpr*)) (unless (and (output-port? op) (binary-port? op)) ($oops who "~s is not a binary output port" op)) (when ($port-flags-set? op (constant port-flag-compressed)) ($compressed-warning who op)) (when sfd (unless (source-file-descriptor? sfd) ($oops who "~s is not a source-file descriptor or #f" sfd))) (when wpoop (unless (and (output-port? wpoop) (binary-port? wpoop)) ($oops who "~s is not a binary output port or #f" wpoop)) (when ($port-flags-set? wpoop (constant port-flag-compressed)) ($compressed-warning who wpoop))) (when covop (unless (and (output-port? covop) (textual-port? covop)) ($oops who "~s is not a textual output port or #f" covop))) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (when hostop (unless (and (output-port? hostop) (binary-port? hostop)) ($oops who "~s is not a binary output port or #f" hostop)) (when ($port-flags-set? hostop (constant port-flag-compressed)) ($compressed-warning who hostop))) (if (and (= (length sexpr*) 1) (pair? (car sexpr*)) (eq? (caar sexpr*) 'top-level-program)) (let ([library-collector (make-parameter '())]) (parameterize ([$require-libraries library-collector]) (do-compile-to-port)) (library-collector)) (do-compile-to-port))]))) (let () (define (in&out in) (let ([ext (path-extension in)]) (cond [(string=? ext "") (values (format "~a.ss" in) (format "~a.so" in))] [(string=? ext "so") (values in (format "~a.so" in))] [else (values in (format "~a.so" (path-root in)))]))) (define (do-compile-to-file who out hostout machine sfd do-read) (with-object-file who out (lambda (op) (with-host-file who hostout (lambda (hostop) (with-wpo-file who out (lambda (wpoop) (with-coverage-file who out (lambda (source-table) (compile-file-help op hostop wpoop source-table machine sfd do-read out)))))))))) (define (do-compile-file who in out hostout machine r6rs?) (unless (string? in) ($oops who "~s is not a string" in)) (unless (string? out) ($oops who "~s is not a string" out)) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (when (compile-file-message) (printf "compiling ~a with output to ~a~@[ (host output to ~a)~]\n" in out hostout)) (let ([ip ($open-file-input-port who in)]) (on-reset (close-port ip) (let ([sfd ($source-file-descriptor in ip)]) ; whack existing ip so close-port calls close the text port (set! ip (transcoded-port ip (current-transcoder))) (when r6rs? ($set-port-flags! ip (constant port-flag-r6rs))) (let ([fp (let ([start-pos (port-position ip)]) (if (and (eqv? (read-char ip) #\#) (eqv? (read-char ip) #\!) (memv (read-char ip) '(#\space #\/))) (let loop ([fp 3]) (let ([c (read-char ip)]) (if (eof-object? c) fp (let ([fp (+ fp 1)]) (if (char=? c #\newline) fp (loop fp)))))) (begin (set-port-position! ip start-pos) 0)))]) (do-compile-to-file who out hostout machine sfd ($make-read ip sfd fp))))) (close-port ip))) (define (do-compile-script who in out machine r6rs?) (define ($make-read-program ip sfd fp) (let ([do-read ($make-read ip sfd fp)]) (lambda () (let f ([form* '()]) (let ([x (do-read)]) (if (eof-object? x) (if (null? form*) x `(top-level-program ,@(reverse form*))) (f (cons x form*)))))))) (unless (string? in) ($oops who "~s is not a string" in)) (unless (string? out) ($oops who "~s is not a string" out)) (unless (symbol? machine) ($oops who "~s is not a symbol" machine)) (unless (eq? machine (constant machine-type-name)) ($oops who "compiler for ~s is not loaded" machine)) (when (compile-file-message) (printf "compiling ~a with output to ~a\n" in out)) (let ([ip ($open-file-input-port who in)]) (on-reset (close-port ip) (let ([sfd ($source-file-descriptor in ip)]) ; whack existing ip so close-port calls close the text port (set! ip (transcoded-port ip (current-transcoder))) (when r6rs? ($set-port-flags! ip (constant port-flag-r6rs))) (let ([start-pos (port-position ip)]) (if (and (eqv? (read-char ip) #\#) (eqv? (read-char ip) #\!) (memv (lookahead-char ip) '(#\space #\/))) ; copy #! line (with-object-file who out (lambda (op) (with-wpo-file who out (lambda (wpoop) (with-coverage-file who out (lambda (source-table) (put-u8 op (char->integer #\#)) (put-u8 op (char->integer #\!)) (when wpoop (put-u8 wpoop (char->integer #\#))) (when wpoop (put-u8 wpoop (char->integer #\!))) (let ([fp (let loop ([fp 2]) (let ([c (read-char ip)]) (when (eof-object? c) ($oops who "unexpected eof reading script header on ~s" in)) (let ([n (char->integer c)]) (unless (fx< n 256) ($oops who "integer code for ~s script header character ~s is too large to copy to output port" in c)) (put-u8 op n) (when wpoop (put-u8 wpoop n))) (let ([fp (+ fp 1)]) (if (char=? c #\newline) fp (loop fp)))))]) (compile-file-help op #f wpoop source-table machine sfd ((if r6rs? $make-read-program $make-read) ip sfd fp) out)))))))) ; no #! line (with-object-file who out (lambda (op) (set-port-position! ip start-pos) (with-wpo-file who out (lambda (wpoop) (with-coverage-file who out (lambda (source-table) (compile-file-help op #f wpoop source-table machine sfd ((if r6rs? $make-read-program $make-read) ip sfd 0) out))))))))))) (close-port ip)) (unless-feature windows (chmod out #o755))) (set-who! compile-file (case-lambda [(in out machine) (do-compile-file who in out #f machine #f)] [(in out) (do-compile-file who in out #f (constant machine-type-name) #f)] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) (do-compile-file who in out #f (constant machine-type-name) #f))])) (set-who! compile-library (let () (define do-compile-library (lambda (in out machine) (do-compile-file who in out (and (not (eq? machine (machine-type))) (format "~a.~s" (path-root out) (machine-type))) machine #t))) (case-lambda [(in out machine) (do-compile-library in out machine)] [(in out) (do-compile-library in out (constant machine-type-name))] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) (do-compile-library in out (constant machine-type-name)))]))) (set-who! compile-script (case-lambda [(in out machine) (do-compile-script who in out machine #f)] [(in out) (do-compile-script who in out (constant machine-type-name) #f)] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) (do-compile-script who in out (constant machine-type-name) #f))])) (set-who! compile-program (let () (define (do-compile-program in out machine) (let ([library-collector (make-parameter '())]) (parameterize ([$require-libraries library-collector]) (do-compile-script who in out machine #t)) (library-collector))) (case-lambda [(in out machine) (do-compile-program in out machine)] [(in out) (do-compile-program in out (constant machine-type-name))] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) (do-compile-program in out (constant machine-type-name)))]))) (set-who! maybe-compile-file (case-lambda [(in out) (unless (string? in) ($oops who "~s is not a string" in)) (unless (string? out) ($oops who "~s is not a string" out)) ($maybe-compile-file who in out compile-file) (void)] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) ($maybe-compile-file who in out compile-file)) (void)])) (set-who! maybe-compile-library (case-lambda [(in out) (unless (string? in) ($oops who "~s is not a string" in)) (unless (string? out) ($oops who "~s is not a string" out)) ($maybe-compile-file who in out (compile-library-handler)) (void)] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) ($maybe-compile-file who in out (compile-library-handler))) (void)])) (set-who! maybe-compile-program (case-lambda [(in out) (unless (string? in) ($oops who "~s is not a string" in)) (unless (string? out) ($oops who "~s is not a string" out)) ($maybe-compile-file who in out (compile-program-handler)) (void)] [(in) (unless (string? in) ($oops who "~s is not a string" in)) (let-values ([(in out) (in&out in)]) ($maybe-compile-file who in out (compile-program-handler))) (void)])) (set-who! compile-to-file (rec compile-to-file (case-lambda [(sexpr* out) (compile-to-file sexpr* out #f)] [(sexpr* out sfd) (unless (list? sexpr*) ($oops who "~s is not a proper list" sexpr*)) (unless (string? out) ($oops who "~s is not a string" out)) (when sfd (unless (source-file-descriptor? sfd) ($oops who "~s is not a source-file descriptor or #f" sfd))) (let ([library? (and (= (length sexpr*) 1) (pair? (car sexpr*)) (eq? (caar sexpr*) 'library))] [program? (and (= (length sexpr*) 1) (pair? (car sexpr*)) (eq? (caar sexpr*) 'top-level-program))]) (define (go) (do-compile-to-file who out (and library? (not (eq? (constant machine-type-name) (machine-type))) (format "~a.~s" (path-root out) (machine-type))) (constant machine-type-name) sfd (lambda () (if (null? sexpr*) (eof-object) (let ([x (car sexpr*)]) (set! sexpr* (cdr sexpr*)) x))))) (if program? (let ([library-collector (make-parameter '())]) (parameterize ([$require-libraries library-collector]) (go)) (library-collector)) (go)))])))) );let